# Spell out numbers in French

There was a challenge to convert a number to its numeral in English, but that was too straightforward. Given a number 0–100, your task is to output the corresponding numeral in French. The French numeral system has a more complex logic behind it compared to the English one:

Number  Numeral
---------------
0      zéro (note the accent)
1      un
2      deux
3      trois
4      quatre
5      cinq
6      six
7      sept
8      huit
9      neuf
10      dix
11      onze
12      douze
13      treize
14      quatorze
15      quinze
16      seize
17      dix-sept (literally ten seven)
18      dix-huit
19      dix-neuf
20      vingt
21      vingt et un (no hyphens)
22      vingt-deux
...
30      trente
...
40      quarante
...
50      cinquante
...
60      soixante
...
70      soixante-dix (literally sixty ten)
71      soixante et onze
...
80      quatre-vingts (note the s; literally four twenties)
81      quatre-vingt-un (note the hyphens)
...
90      quatre-vingt-dix
91      quatre-vingt-onze
...
99      quatre-vingt-dix-neuf (4*20+10+9)
100     cent


For a complete list, follow http://quizlet.com/996950/ (http://www.webcitation.org/6RNppaJx0).

## Further rules/explanations

• There will always be a hyphen between words EXCEPT when the number ends in 1.
• When the number ends in 1, the word et (meaning and) is added before the un or onze. (31 = trente et un)
• However, 81 and 91 are formatted the same as the other numbers. (81 = quatre-vingt-un)
• At 60, the system switches from base 10 to base 20.
• Loopholes that are forbidden by default are not allowed.
• Using an external source such as a website, as well as any libraries, APIs, functions, or the like that convert numbers to numerals or translate to French are not allowed.
• This is , so the answer with the fewest bytes wins.
• If available, please link to an online compiler to allow for easy testing of your code.

## Input

• Input shall be taken from however your language takes input. (STDIN, command line, etc.)
• Input will be a single string, consisting of a whole number between 0 and 100 inclusive (leading zeroes optional).
• You can assume the input will always be well-formed.

## Output

• The result shall be output to the most convenient method for your language. (STDOUT, dialog box, etc.)
• Case does not matter in the output.

## Examples

Input  Output
-------------
0      zéro
18     dix-huit
51     cinquante et un
80     quatre-vingts
91     quatre-vingt-onze
99     quatre-vingt-dix-neuf

• Note that french speaking Belgians use "septante", "octante" and "nonante" instead of soixante-dix, quatre-vingts, quatre-vingt-dix. It should be used to in the ancient Belgian colony. – Emmanuel Jul 31 '14 at 9:16
• +1 for the genius of doing this in French. As a German colleague of mine said: "Germans say the numbers backwards. But to understand the French, you need a calculator." – Level River St Jul 31 '14 at 12:28
• It looks like zéro is the only accented number in the range 0-100. This is not a standard ASCII character. I assume any format (code page 437, UTF-8, etc) that can output this character is acceptable. – Level River St Jul 31 '14 at 12:36
• @Martin The rules state "You can assume the input will always be well-formed," so input > 100 can result in any behavior. – NinjaBearMonkey Jul 31 '14 at 14:39
• There is absolutely no logic behind the french system. I'm sure there is a mistake in 99% of the french checks, this is even more complicated after 100. The conjugaison is also a nightmare. Trust me, I'm french. – Michael M. Jul 31 '14 at 18:05

# JavaScript (ES6) 318 321

Edit Bug fix (managing leading 0s) and golfed more

Credit for the camel case trick @Core1024

With input/output via popup

alert((n=prompt(),w='ZéroUnDeuxTroisQuatreCinqSixSeptHuitNeufDixOnzeDouzeTreizeQuatorzeQuinzeSeizeDix-septDix-huitDix-neufVingtTrenteQuaranteCinquanteSoixante'.match(/[A-Z][^A-Z]+/g),
u=n%10,s=u-1|n>80?d='-':' et ',n>99?'Cent':n<21?w[n|0]:n<70?w[18+n/10|0]+(u?s+w[u]:''):(n<80?w[24]:w[4]+d+w[20])+(n-80?s+w[n%20]:'s')))


As a testable function

F=n=>(
w='ZéroUnDeuxTroisQuatreCinqSixSeptHuitNeufDixOnzeDouzeTreizeQuatorzeQuinzeSeizeDix-septDix-huitDix-neufVingtTrenteQuaranteCinquanteSoixante'
.match(/[A-Z][^A-Z]+/g),
u=n%10,s=u-1|n>80?d='-':' et ',
n>99?'Cent':
n<21?w[n|0]:
n<70?w[18+n/10|0]+(u?s+w[u]:''):
(n<80?w[24]:w[4]+d+w[20])+(n-80?s+w[n%20]:'s')
)


To Test In FireFox console or FireBug

for (i = 0; i < 100; console.log(r),i+= 10)
for (j=0, r=''; j < 10; j++)
r+=(i+j)+':'+F(i+j+'')+", "; // specific: input is a string
F('100')


Test Output

0:Zéro, 1:Un, 2:Deux, 3:Trois, 4:Quatre, 5:Cinq, 6:Six, 7:Sept, 8:Huit, 9:Neuf,
10:Dix, 11:Onze, 12:Douze, 13:Treize, 14:Quatorze, 15:Quinze, 16:Seize, 17:Dix-sept, 18:Dix-huit, 19:Dix-neuf,
20:Vingt, 21:Vingt et Un, 22:Vingt-Deux, 23:Vingt-Trois, 24:Vingt-Quatre, 25:Vingt-Cinq, 26:Vingt-Six, 27:Vingt-Sept, 28:Vingt-Huit, 29:Vingt-Neuf,
30:Trente, 31:Trente et Un, 32:Trente-Deux, 33:Trente-Trois, 34:Trente-Quatre, 35:Trente-Cinq, 36:Trente-Six, 37:Trente-Sept, 38:Trente-Huit, 39:Trente-Neuf,
40:Quarante, 41:Quarante et Un, 42:Quarante-Deux, 43:Quarante-Trois, 44:Quarante-Quatre, 45:Quarante-Cinq, 46:Quarante-Six, 47:Quarante-Sept, 48:Quarante-Huit, 49:Quarante-Neuf,
50:Cinquante, 51:Cinquante et Un, 52:Cinquante-Deux, 53:Cinquante-Trois, 54:Cinquante-Quatre, 55:Cinquante-Cinq, 56:Cinquante-Six, 57:Cinquante-Sept, 58:Cinquante-Huit, 59:Cinquante-Neuf,
60:Soixante, 61:Soixante et Un, 62:Soixante-Deux, 63:Soixante-Trois, 64:Soixante-Quatre, 65:Soixante-Cinq, 66:Soixante-Six, 67:Soixante-Sept, 68:Soixante-Huit, 69:Soixante-Neuf,
70:Soixante-Dix, 71:Soixante et Onze, 72:Soixante-Douze, 73:Soixante-Treize, 74:Soixante-Quatorze, 75:Soixante-Quinze, 76:Soixante-Seize, 77:Soixante-Dix-sept, 78:Soixante-Dix-huit, 79:Soixante-Dix-neuf,
80:Quatre-Vingts, 81:Quatre-Vingt-Un, 82:Quatre-Vingt-Deux, 83:Quatre-Vingt-Trois, 84:Quatre-Vingt-Quatre, 85:Quatre-Vingt-Cinq, 86:Quatre-Vingt-Six, 87:Quatre-Vingt-Sept, 88:Quatre-Vingt-Huit, 89:Quatre-Vingt-Neuf,
90:Quatre-Vingt-Dix, 91:Quatre-Vingt-Onze, 92:Quatre-Vingt-Douze, 93:Quatre-Vingt-Treize, 94:Quatre-Vingt-Quatorze, 95:Quatre-Vingt-Quinze, 96:Quatre-Vingt-Seize, 97:Quatre-Vingt-Dix-sept, 98:Quatre-Vingt-Dix-huit, 99:Quatre-Vingt-Dix-neuf,
"Cent"

• Cool trick with the camel case! – tomsmeding Aug 1 '14 at 9:07
• @tomsmeding really cool so obviously not devised by me (added credit in post) – edc65 Aug 1 '14 at 9:24
• @edc65 Do you mind if I try to improve my Ruby answer with that? I didn't even see that case-insensitive output is allowed when I wrote mine. ^^ – Martin Ender Aug 1 '14 at 13:16
• @MartinBüttner of course you can – edc65 Aug 1 '14 at 13:24

b=(words"zéro un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize vingt trente quarante cinquante soixante"!!)
a!b=a++"-"++b
f 0=b 0
f 71=f 60++" et onze"
f 80=f 4!b 17++"s"
f 100="cent"
f x|x<17=b x|x<20=b 10!b(x-10)|x>80=b 4!b 17!f(x-80)|m==1=f(x-1)++" et un"|x>60=f 60!f(x-60)|m==0=b(15+div x 10)|1<2=f(x-m)!f m where m=mod x 10
main=interact$f.read  Ungolfed base :: Int -> String -- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 base i = words "zéro un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize vingt trente quarante cinquante soixante" !! i hyphen :: String -> String -> String a hyphen b = a ++ "-" ++ b say :: Int -> String say 0 = base 0 say 71 = say 60 ++ " et onze" say 80 = say 4 hyphen base 17 ++ "s" say 100 = "cent" say x | x < 17 = base x | x < 20 = base 10 hyphen base (x - 10) | x > 80 = base 4 hyphen base 17 hyphen say (x - 80) | m == 1 = say (x - 1) ++ " et un" | x > 60 = say 60 hyphen say (x - 60) | m == 0 = base (div x 10 + 15) | otherwise = say (x - m) hyphen say m where m = mod x 10 main = putStr.say.read=<<getLine  Functional programming languages are quite suitable for this job. • Have an upvote, you were faster than me. You might want to replace your main with main=interact$f.read and save a few characters. – gxtaillon Jul 31 '14 at 20:40
• @gxtaillon Yes, I forgot interact. Updated. – Ray Aug 1 '14 at 15:36

## Ruby, 333 bytes

l=['']+%w{un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize}
d=%w{vingt trente quarante cinquante soixante _ quatre-vingt}+['']*2
n=gets.to_i
v=n%20
t=n%10
puts n<1?'zéro':n>99?'cent':d[(n<70?n:n-v)/10-2]+(n<21||t<1&&n<61?'':v<1??s:t==1&&n<80?' et ':?-)+(n>60||n<20?v<17?l[v]:'dix-'+l[t]:l[t])


It's mostly just two look up tables and a bunch of ternary operators that encode all of the weird rules and tell you which lookup table to use when. Let me know if you want to know more. ;)

• Hate to be nitpicky, but 80 should output quatre-vingts, with an s on the end. – NinjaBearMonkey Jul 31 '14 at 14:51
• @hsl fixed and shortened a bit. – Martin Ender Jul 31 '14 at 20:26

# Python - 344 (348)(380)(445)(537) bytes

Thanks to grc, Ray and isaacg for their help in the golfing process.

The code consists of the initial dictionary definition and a list comprehension that fills in the blanks with the junction of the dictionary's elements.

You can check the code online at repl.it

r=range
def f(a):b=a/60*10+10;d[a]=d[a-a%b]+(' et ','-')[a%10!=1or a>80]+d[a%b]
d=dict(zip(r(17)+r(20,70,10)+[80,100],'zéro un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize vingt trente quarante cinquante soixante quatre-vingt cent'.split()))
[f(v)for v in r(100)if(v in d)<1]
d[80]+='s'
print d[input()]


My latest attempts to golf this code have been to forgo the generation process and with that reduction refine the function to just generate the requested number on the spot. However, since the 60 and 80's numbers need uncalculated elements, the struggle has been to create such a function while decreasing code.

• You could use r=range, this trick for lines 2 & 3, and only one space for indentation. And you can save 65 bytes with d=dict(zip(r(17)+r(20,70,10)+[80,100],'zéro un deux ... quatre-vingt cent'.split())) – grc Jul 31 '14 at 6:00
• The r=range trick I was aware of, but forgot to add it when I wrote the 1st revision. Everything else was relatively new to me, so thanks are in order. A note though, the code doesn't work on Python 3.x unless the print statement is turned into a function and the r functions in the dict are turned into lists. – Doktoro Reichard Jul 31 '14 at 17:57
• In python 2 you don't need int(input()), input() is enough. – Ray Jul 31 '14 at 19:02
• Some more tips: I think if you put b=a/60*10+10 in f(a) then you could use a-a%b and a%b for your dictionary keys. Also, you don't need the space in 1 or, and the 4th line can be shortened to [f(v)for v in r(100)if v not in d]. I haven't tried any of this though. – grc Aug 1 '14 at 4:54
• if v not in d -> if(v in d)<1 saves a character. If you semicolon separate the two parts of your function, you can put it all on one line. – isaacg Aug 1 '14 at 20:22

# Python - 392 bytes

It has a list with base numbers which it uses to generate the other numbers. Most of the generation logic is in the list comprehension on line 2, using list indexing for conditionals. Once the list is generated it then looks up the inputted number and prints it.

Edit: Shortened from 426 bytes using grc's tip.

a='_un_deux_trois_quatre_cinq_six_sept_huit_neuf_dix_onze_douze_treize_quatorze_quinze_seize_dix-sept_dix-huit_dix-neut'.split('_')
a+=[[['vingt','trente'],['quarante','cinquante'],['soixante']*2,[a[4]+'-vingt']*2][b][c>9]+['','-',' et '][(c%[10,20][b>1]>0)+(c%10==1)*(b<3)]+a[c%[10,20][b>1]]for b in[0,1,2,3]for c in range(20)]
a[0]='zéro'
a[80]+='s'
a+=['cent']
print(a[int(input())])

• You can save 23 bytes with a='_un_deux_trois ... quinze_seize'.split('_') – grc Jul 31 '14 at 5:37
• There's a typo: ciquante, a n is missing. – A.L Jul 31 '14 at 10:07
• You byte count is actually 420, less than stated. – Ray Jul 31 '14 at 19:04

# Python 3, (503 bytes)

Compress the table using bzip2 and then use ascii85 encoding to store the result. The table is:

zéro
un
deux
trois
quatre
cinq
...
cent


Very naive method, but it's not that bad.

# Golfed

import bz2,base64 as B
print(bz2.decompress(B.a85decode('6<\\%_0gSqh;d"=$\\VU:fOjTBn&3p:MiVu^S+:%s4!Q6o8\\8%r<Bp,5\\LT&Q+19!OmJC@3n\'bD<]UHekq<8OP<;]9BZ,;>836X4<[@KJ,)FsD^8j9Q=]O]&/8\'rjSK&0Sh0W[ru0E0!!M-tL69NZF6N\'Lc#$Q=?S_P0+uEZP"[H;%Ucch??nYC76\'k<)isZIBqqOKi(,IHp""^8d/EqRpc_I<IRj[\'4KB/."%5,"pjr&27q+&t.6J+ik=Jdd2A)j]\'jt5ts0>:sr9.@E>V0F9L?9r&pX\'E.NUP:r&?>\'*(gKmd;/1QkUb*1&JhfWiE7Kl,P,o1go+.3O&l))Y,$/PO)%"al^4H2,n-l\\PuM!W1rBB9t.,U>DhAs83burMn(%%-qHG<gr+^')).decode().split('\n')[int(input())])  ## Ungolfed import bz2, base64 s = '6<\\%_0gSqh;d"=$\\VU:fOjTBn&3p:MiVu^S+:%s4!Q6o8\\8%r<Bp,5\\LT&Q+19!OmJC@3n\'bD<]UHekq<8OP<;]9BZ,;>836X4<[@KJ,)FsD^8j9Q=]O]&/8\'rjSK&0Sh0W[ru0E0!!M-tL69NZF6N\'Lc#$Q=?S_P0+uEZP"[H;%Ucch??nYC76\'k<)isZIBqqOKi(,IHp""^8d/EqRpc_I<IRj[\'4KB/."%5,"pjr&27q+&t.6J+ik=Jdd2A)j]\'jt5ts0>:sr9.@E>V0F9L?9r&pX\'E.NUP:r&?>\'*(gKmd;/1QkUb*1&JhfWiE7Kl,P,o1go+.3O&l))Y,$/PO)%"al^4H2,n-l\\PuM!W1rBB9t.,U>DhAs83burMn(%%-qHG<gr+^'
table = bz2.decompress(base64.a85decode(s)).decode().split('\n')
num = int(input())
print(table[num])


## Bonus

Can you find the word "Vim" in the compressed string ?

• I quite like Sh0W. Couldn't find Vim, nor can you. – tomsmeding Aug 1 '14 at 9:06
• @tomsmeding What a pity! Vim disappeared after my last edit. – Ray Aug 1 '14 at 9:37

# Bash, 456 440 421 408

Assumes valid input (integer from 0 to 100 with any number of leading zeroes).

v=sed 's/0*//'<<<$1 f=('' dix vingt trente quarante cinquante soixante soixante-dix quatre-vingts quatre-vingt-dix) s=('' ' et un' -deux -trois -quatre -cinq -six -sept -huit -neuf) o=${f[${v: -2:1}]}${s[${v: -1:1}]} [ "${o:0:1}" = \  ]&&o=un
((v>99))&&o=cent
sed 's/^-//
s/s-/-/
s/s et /-/
s/dix et un/onze/
s/di.*ux/douze/
s/d.*s$/treize/ s/d.*re/quatorze/ s/d.*q/quinze/ s/di.*ix/seize/'<<<${o:-zéro}


# JavaScript 459 (No Camel Casing)

@edc65 can't take that from you... ;)

A="0un0deux0trois0quatre0cinq0six0sept0huit0neuf0dix0onze0douze0treize0quatorze0quinze0seize0dix-sept0dix-huit0dix-neuf".split(0);S="soixante";Q=A[4]+"-vingt";T=10;V=20;N=59;for(b=5;1<b--;)for(c=V;c--;)X=b*V+c,A[X]=[,["vingt","trente"],["quarante","cinquante"],[S,S],[Q,Q]][b][c/T|0]+(X%T?X>N?X%V==T?"-dix":"":"":"")+(1>X%T?"":(1==X%(X>N?V:T)|71==X)&81!=X?" et ":"-")+(X>N&X%V==T?"-dix":A[c%(X>N?V:T)]);A[0]="zéro";A[80]+="s";A[100]="cent";alert(A[prompt()])