Translate numbers to French

The French spell out numbers in a peculiar way.

• 1-16 are "normal"
• 17-19 are spelled out 10+7. 10+8, 10+9.
• 20-69 are "normal" (OK, OK! Not really, but they are in this challenge)
• 70-79 are 60+10, 60+11 ... 60+10+7, 60+10+8, 60+10+9.
• 80-99 are 4*20, 4*20+1, 4*20+2 ... 4*20+16, 4*20+10+7, 4*20+10+8, 4*20+10+9

Challenge:

Take a positive integer in the range [1,100], and output it the "French way". You must output it exactly as it's shown below, with * and +, so 97 is 4*20+10+7, not [4 20 10 7] or something else.

Test cases:

1  - 1
.. - ..
16 - 16
17 - 10+7
18 - 10+8
19 - 10+9
20 - 20
21 - 21
.. - ..
69 - 69
70 - 60+10
71 - 60+11
72 - 60+12
77 - 60+10+7
78 - 60+10+8
79 - 60+10+9
80 - 4*20
81 - 4*20+1
82 - 4*20+2
.. - ..
90 - 4*20+10
91 - 4*20+11
96 - 4*20+16
97 - 4*20+10+7
98 - 4*20+10+8
99 - 4*20+10+9
100 - 100
• Every language I know has a transition in the "teens", like the one from 16 to 10+7 above. (In English it happens between 12 and 10+3, with a little more morphological disguise.) I've always been a little overobsessed with the fact that different languages make that transition at different numbers. – Greg Martin Apr 26 '17 at 22:23
• Why should "vingt-deux" be 22 when "dix-huit" is 10+8? – Titus Apr 27 '17 at 0:54
• Luckily, this is a programming puzzle site and not a linguistic trivia site. Otherwise people might get annoyed when OP makes silly mistakes. Phew! – Stewie Griffin Apr 27 '17 at 8:41
• @StewieGriffin People still got annoyed. – Leaky Nun Apr 27 '17 at 8:47
• As a french I do find it quite fine :D. – Walfrat Apr 27 '17 at 12:51

Excel, 153 149 Bytes

=IF(OR(A1<17,A1>99,AND(A1>19,A1<70)),A1,IF(A1<20,"10+"&A1-10,IF(A1<80,"60","4*20")&IF(A1=80,,IF(MOD(A1,20)>16,"+10+"&MOD(A1,20)-10,"+"&MOD(A1,20)))))

I'm sure this could be better, I struggled to find an efficient way to account for #80.

edit: Consolidated the 'Normal' cases better to save 4 bytes. #80 still sucks.

Can't find a specific answer on here, not sure the rules of code-golf tbh. Can I use multiple cells in Excel, and add the byte count of each?

ie. For an input in cell A1

A2: 11 Bytes

=MOD(A1,20)

A3 (result): 125 Bytes

=IF(OR(A1<17,A1>99,AND(A1>19,A1<70)),A1,IF(A1<20,"10+"&A1-10,IF(A1<80,"60","4*20")&IF(A1=80,,IF(A2>16,"+10+"&A2-10,"+"&A2))))

For a total of 136?

• I think it should be allowed to write code in multiple cells. IMHO it's like having intermediate variables or functions in other programming languages. – pajonk Apr 27 '17 at 9:19
• I feel that there should be some penalty of using multiple cells, just like there's a penalty of using functions in other languages (that of typing the boilerplate to declare the function). Perhaps the tersest supported encoding (ie CSV), so the necessary number of commas and (if required) quotes? – Muzer Apr 27 '17 at 9:49
• I'm not aware of any format that excel files can be saved in with a recognizable output. CSV files do not by default support functions like these, and will break up any function that uses a comma. If saved as pure text in one column with a new line between cells, it could be copied directly into excel and function. In this case, 1 byte would be added for each additional cell. – qoou Apr 27 '17 at 21:57
• Save a byte by converting IF(A1=80,,IF(MOD(A1,20)>16,"+10+"&MOD(A1,20)-10,"+"&MOD(A1,20))) to IFS(A1=80,,MOD(A1,20)>16,"+10+"&MOD(A1,20)-10,1,"+"&MOD(A1,20)) – Greedo Apr 28 '17 at 10:10
• In Libreoffice calc, you can skip the ) at the end, can you do the same in excel? So you can save 5 "bytes" (there are really UCS2-Chars, so if you say Byte == octet, you must count it double). And you must change the , in ; – 12431234123412341234123 Apr 28 '17 at 10:44

Retina, 52 48 bytes

4 bytes saved thanks to Neil

^7\B
60+1
^9\B
81
^8\B
4*20+
1(?=7|8|9)
10+
\+0

Try it online! or verify all inputs (provided by Neil)

Explanation

^7\B
60+1
^9\B
81
^8\B
4*20+

First we handle the translation of 70, 80, and 90. In these first 3 stages, a 7 at the start with another character following it is replaced by 60+1. Similarly, 9 is replaced by 81, and 8 by 4*20+1. The replacement of 9 is essentially changing it to "eighty-ten" and such, so that the 8 is then handled by the next replacement, which saves bytes over writing 4*20+1 twice.

1(?=7|8|9)
10+

This handles the cases of 17, 18, and 19, by replacing the 1 in each with 10+.

\+0

Finally, there should never be a +0 at the end, so delete it if it's there.

• Surely instead if look behinds and lookaheads you can use capture groups – Downgoat Apr 26 '17 at 21:18
• Doesn't work for 7-9, but I don't think you need that lookbehind: Try it online! – Neil Apr 26 '17 at 21:26
• @Neil I realized that while I was gone :P But thanks for the new version! – Business Cat Apr 26 '17 at 22:15
• @Downgoat I could replace the lookahead with a capturing group but it wouldn't save any bytes since $1 is just as long as ?=. – Business Cat Apr 26 '17 at 22:18 Python 2, 98 bytes f=lambda x:[x*(x%100<70),'10+'+x-10][16<x<20]or['60+'+f(x-60),'4*20'+x/81*('+'+f(x-80))][x/80] Try it online! JavaScript (ES6), 73 71 bytes f=n=>n>16&n<20?'10+'+n%10:n<70|n>99?n:n%20?f(n-n%20)+'+'+f(n%20):'4*20' Bonus version that prints the numbers as they are actually spelled for an extra 2 bytes: f=n=>n<17|n>99?n:n<20?'10+'+n%10:n%20?f(n-n%20)+'+'+f(n%20):n-80?n:'4*20' • fails for a lot of inputs; actually it only works for 1..20, 30, 40, 50, 60, 80 and 100. – Titus Apr 27 '17 at 0:00 • @Titus I think you're misunderstanding most of the outputs. 23, for instance, is supposed to output 23, not 20+3. – ETHproductions Apr 27 '17 at 0:37 • Save two bytes with (m=n%20) – Titus Apr 27 '17 at 2:53 • @Titus Thanks, but I already tried that, and it doesn't work on 70-99 because m gets reset to 0 in the f(n-n%20) call. (It's a global variable) – ETHproductions Apr 27 '17 at 11:05 • You can save a byte by changing n<70|n>99 to n%100<70. Also, could you add a test-compiler? – Kevin Cruijssen May 2 '17 at 10:03 R, 110 bytes i=scan() r=i%%10;paste0(ifelse(i>16&i<20,"10+",ifelse(i>69&i<80,"60+10+",ifelse(i>16&i<20,"4*20+",i-r/10))),r) • Try (i-r)/10 instead of floor(i/10). And i>15 should be i>16. – Titus Apr 26 '17 at 22:51 PHP, 99 bytes (I wanna be happy version) a straight port of ETHproductions´ JS, 4 bytes golfed. Prints the numbers as asked for by the OP. function f($n){return$n<17|$n>19?$n>60&$n<100?($m=$n%20)?f($n-$m)."+".f($m):'4*20':$n:"10+".$n%10;} breakdown function f($n){return
$n<17|$n>19
?$n>69&$n<100
?($m=$n%20)
?f($n-$m)."+".f($m) # 70..79, 81..99 :'4*20' # 80 :$n                     # 1..16, 20..69
:"10+".$n%10 # 17..19 ; } I wanna be right version, 114 98 bytes new approach inspired by ETHproductions, prints the numbers as they are actually spelled out. function f($n){return$n>16&$n<100?$n-80?($m=$n%($n>60?20:10))?f($n-$m)."+".f($m):$n-$m:'4*20':$n;}

breakdown

function f($n){return$n>16&$n<100 ?$n-80
?($m=$n%($n>60?20:10)) ?f($n-$m)."+".f($m) # $n%$m>0
:$n-$m              # 10,20,30,40,50,60
:'4*20'                 # 80
:\$n                         # 1..16, 100
;}

Python 2, 130 108 bytes

22 bytes saved thanks to @mathjunkie

f=lambda x:79<x<100and('4*20'+('+'+f(x-80))*(x>80))or 69<x<100and'60+'+f(x-60)or 16<x<20and'10+'+x-10orx

Try it online!

• 108 bytes: TIO – math junkie Apr 27 '17 at 1:42
• You need to count f= because you used it inside the lambda. – Leaky Nun Apr 27 '17 at 2:35
• @LeakyNun fixed – Uriel Apr 27 '17 at 15:13

Batch, 220 217 bytes

@set/pn=
@set s=
@if %n% gtr 99 goto g
@if %n% gtr 79 set s=+4*20&set/an-=80
@if %n% gtr 69 set s=+60&set/an-=60
@if %n% gtr 16 if %n% lss 20 set s=%s%+10&set/an-=10
:g
@if %n% gtr 0 set s=%s%+%n%
@echo %s:~1%

Takes input on STDIN. Generating and removing the leading + saves 1 byte over special-casing 80. Edit: Saved 3 bytes thanks to @ConorO'Brien.

• You can save 3 bytes by removing @echo off and prefixing all statements except for hte loop statement with @ – Conor O'Brien May 2 '17 at 18:21
• @ConorO'Brien Huh, I wonder why I forgot to do that this time... – Neil May 2 '17 at 18:26

Jelly, 55 bytes

⁹
’,ṃ60Ṁ€
⁹%80“4*20”,
Dj⁾0+µ¹e?“×ØŒ‘
%ȷ2:“FP‘S‘ŀḟ0Ç€j”+

Try it online! or see a test suite

No doubt there is a shorter way!

How?

+ - Link 1, helper for 1-69&100: number s=0, number n
⁹ - link's right argument, n

’,ṃ60Ṁ€ - Link 2, helper for 70-79: number s=1, number n
’       - decrement s -> 0
,      - pair -> [0,n]
ṃ60   - base decompress (vectorises) using [1,2,...60]  effectively -> [60,[1,n%60]]
Ṁ€ - maximum for €ach effectively -> [60,n%60]

⁹%80“4*20”, - Link 3, helper for 80-99: number s=2, number n
⁹           - link's right argument, n
%80        - mod 80
“4*20”  - literal ['4','*','2','0']
, - pair -> [['4','*','2','0'],n]

Dj⁾0+µ¹e?“×ØŒ‘ - Link 4, reformat 17-19: element v (may be char list or number)
?      - if
e       - v exists in
“×ØŒ‘ - literal [17,18,19]
- then:
D              -   convert to decimal list  e.g. [1,7]
⁾0+          -   literal ['0','+']
j             -   join                          [1,'0','+',7]
µ         - else:
¹        -   identity, v

%ȷ2:“FP‘S‘ŀḟ0Ç€j”+ - Main link: number n in [1,100]
ȷ2                - literal 100
%                  - mod (convert 100 to 0)
“FP‘           - literal [70,80]
:               - integer division (vectorises)
S          - sum (0 for 1-69&100; 1 for 70-79; 2 for 80-99)
‘         - increment (...1, 2 or 3)
ŀ        - call link at index (1, 2 or 3) as a dyad(sum, n)
ḟ0      - filter out zeros (remove 0 from 4*20+0)
Ç€    - call the last link (4) as a monad for each
”+ - literal '+'
j   - join

Pyth, 61 56 bytes

L?}b}17 19++T\+eb|bk?}/QTr6T.s+?<Q80"60+""4*20+"y%Q20\+y

Test it online!

Thanks to Leaky Nun for a 5 byte improvement!

Explanation:

| Implicit: Q=eval(input())
L                    | Define y(b):
?}b}17 19           |  If b is in the inclusive range from 17 to 19:
++T\+eb    |   Return "10+" + (last digit of b)
|b  |  Else: if b!=0: return b
k |   Else: return an empty string (Leaves a trailing '+' for case 80)
_____________________|________________
?}/QTr6T                              | If 70<=Q<100:
+                           |  Concatenate the next two expressions:
?<Q80                      |   If Q<80:
"60+"                 |    Evaluate to "60+"
"4*20+"          |    Else: Evaluate to "4*20+"
y%Q20     |   y(Q%20)
.s                       \+   |  Strip off trailing '+', if present (for case 80)
y  | Else: return y(Q)
(Q)| Trailing Q is implicitly added
• *-Q100>Q69}/QTr6T – Leaky Nun Apr 27 '17 at 2:28
• @]b}17 19}b}17 19 – Leaky Nun Apr 27 '17 at 2:31
• +"10+"ebj\+,Teb – Leaky Nun Apr 27 '17 at 2:32
• @LeakyNun Thanks for the help with golfing! I've made the changes you've suggested. – K Zhang Apr 27 '17 at 21:26

Python3, 127 bytes

m,r=map,range;l=[*r(1,17),*m("10+{}".format,(7,8,9))];f=[0,*l,*r(20,61),*m("60+{}".format,l),"4*20",*m("4*20+{}".format,l),100]

Each array element contains its representation:

for i in range(1,101):
print(i, f[i])

The code does not actually create a function, just an array -- I don't know if that's allowed. Otherwise, I'd have to make this 139 bytes by adding f=[...].__getitem__.

• Welcome to PPCG! I believe there was a discussion on meta about submitting arrays as mappings from integers to objects, but I can't seem to find it at the moment. I'll let you know if I do (and what the outcome of that discussion was). Either way, you won't need f=, because unnamed functions (i.e. expressions that evaluate to the submitted function) are fine unless the name is needed for something like recursion. – Martin Ender Apr 28 '17 at 11:06
• There is no clear consensus, but the marginally top voted answer suggests to allow your solution. – Martin Ender Apr 28 '17 at 13:41

Java 7, 9796 109 bytes

String c(int i){return i>16&i<20?"10+"+(i-10):i%100<70?i+"":i<80?"60+"+c(i-60):"4*20"+(i<81?"":"+"+c(i-80));}

+13 bytes for bug-fixing case 80.. :(

Explanation:

String c(int i){      // Method with integer parameter and String return-type
return i>16&i<20?   //  If 17..19:
"10+"+(i-10)      //   Return "10+" + i-10
:i%100<70?         //  Else if 1..16; 20..69; 100:
i+""              //   Return i
:i<80?             //  Else if 70..79:
"60+"+c(i-60)     //   Return "60+" + recursive-call with i-60
:                  //  Else (80..99):
"4*20"+           //   Return "4*20" +
(i<81?           //   If 80:
""              //    nothing
:                //   Else (81..99)
"+"+c(i-80));   //    recursive-call with i-80
}                     // End of method

Test code:

Try it here.

class M{
static String c(int i){return i>16&i<20?"10+"+(i-10):i%100<70?i+"":i<80?"60+"+c(i-60):"4*20"+(i<81?"":"+"+c(i-80));}

public static void main(String[] a){
for (int i = 1; i <= 100; i++) {
System.out.println(c(i));
}
}
}