# Display a number in Toki Pona

Toki Pona is a constructed language with 137ish words, designed to constrain the speaker to expressing ideas in a simple and straightforward manner, reducing ideas to more essential forms.

Often, people attempt to avoid directly expressing numeric quantities in Toki Pona, opting to express them in more practical terms. if one has a very large amount of money ("mani mute mute"), does it matter if it is $3,532,123 or$3,532,124?

However, Toki Pona does have a basic additive number system, (as well as some others that people have proposed but are not widely used) which is capable of expressing exact quantities.

Toki Pona uses the follow words for numbers:

word value
ala 0
wan 1
tu 2
luka 5
mute 20
ale 100

The quantity expressed by a series of these words is the sum of all of their values.

A quantity must be expressed in as few words as possible, with the words ordered from greatest value to least. for instance, simply summing the values of the words, 6 could be expressed "tu tu tu", "luka wan", "wan luka", "wan wan wan wan wan wan", or some other variation. however, a valid program will only generate "luka wan" for 6.

# Challenge

Write a program which takes as input a non-negative integer and outputs this integer expressed in the Toki Pona number system.

Standard rules apply. Spaces are required.

# Examples

input output
0 ala
1 wan
2 tu
28 mute luka tu wan
137 ale mute luka luka luka tu
1000 ale ale ale ale ale ale ale ale ale ale
• To clarify, the output must be ordered descending by value? Sep 2 at 23:22
• that is correct. Sep 2 at 23:23
• Are spaces mandatory? Sep 2 at 23:59
• trailing spaces are fine Sep 3 at 19:11
• I wonder why 3,532,123 is not in the test cases...
– Stef
Sep 4 at 11:59

# Python 2, 81 bytes

lambda n:n/100*'ale '+n/20%5*'mute '+n/5%4*'luka '+n%5/2*'tu '+n%5%2*'wan'or'ala'


Try it online!

The naive approach is surprisingly effective. The overhead of an iterative or recursive method seems too high, though I'd like to be proven wrong.

• My best attempt at a recursive strategy comes to 124 bytes if anyone is interested in improving on that
– Jo King
Sep 3 at 7:44
• Shouldn't 100 be 'ale' not 'ale '? Sep 3 at 7:57
• @rak1507: Spaces between words are mandatory, so this solution puts a space after any word that may be followed by another. Sep 3 at 9:23
• Yes, I know, I'm just wondering whether or not trailing spaces are allowed. Sep 3 at 10:00

# Jelly, 41 bytes

“dÞ¦£‘Fd0¦¥ƒFJx$ȯWị“&ḍṘṭ.½6²EmSFʂ5ȥṙẠ»Ḳ¤K  Try it online! ### How? “dÞ¦£‘Fd0¦¥ƒFJx$ȯWị“...»Ḳ¤K - Link: non-negative integer, n
“dÞ¦£‘                      - [100,20,5,2]
ƒ                - reduce starting with n, using:
¥                 -   last two links as a dyad:
F                     -     flatten i.e. n->[n] or [....,[q,r]]->[...,q,r]
d0¦                  -     divmod the tail by (the next divisor)
F               - flatten
Jx$- repeat indices - e.g. [0,3,2,2,0] -> [2,2,2,3,3,4,4] W - wrap -> [n] ȯ - logical OR (if n was 0 we now get [0] in place of []) “...»Ḳ¤ - ["ale","mute","luka","tu","wan","ala"] ị - index into (1-indexed and modular) K - join with spaces  # Pip, 63 bytes Yay?{x:y//aYy%ab.sRLx}MZ[h20 5 2o]"ale mute luka tu wan"^s"ala"  Takes the number as a command-line argument. Try it online! ### Explanation Yay? ; Copy the command-line argument from local variable a into global variable y Ya ; If y is nonzero (truthy), y? ; Take this function (explained below) {x:y//aYy%ab.sRLx} ; and map it to pairs of items from MZ ; this list of numbers [100 20 5 2 1] ; and this string of words, split on spaces "ale mute luka tu wan" ^ s ; Else (if y is zero), output ala "ala"  The mapping function gets a number as its first argument a and the corresponding word as its second argument b: { ; Set x to y (the input number) int-divided by a (the Toki Pona number) x: y//a ; Update y to be y mod a Y y%a ; Concatenate b (the Toki Pona word) with space... b . s ; ... and return a list of x copies of that string RL x }  # Vyxals, 38 bytes 521f20p₁p(nḋ÷)WṪ«øßṀ∞HĊ∴ǑTṄ‟7«⌈ð+*‛Ż₅⟑  Try it Online!  ‛Ż₅⟑ # Handle the base case 0 -> ala 521f20p₁p # Create the list 100,20,5,2,1 (n ) # Iterate over, pushing each time ḋ÷ # Divmod ToS (initially input) by current number, and iterate - remainder becomes new ToS WṪ # Wrap, getting all the divisions * # Dot product with «...«⌈ð+ # Words, each with a space appended  # Retina 0.8.2, 60 bytes .+$*
1{100}
ale
1{20}
mute
1{5}
luka
11
tu
1
wan
^$ala  Try it online! Link includes test cases. Explanation: .+$*


Convert to unary.

1{100}
ale
1{20}
mute
1{5}
luka
11
tu
1
wan


Greedily match the appropriate numbers. wan doesn't need a space since it can only appear once.

^$ala  Fix 0 separately. # Ruby, 92 bytes ->n{n<1?"ala":[100,20,5,2,1].map{|i|n-=i*j=n/i;"#{%w{luka wan tu mute ale}[i%12%5]} "*j}*""}  Try it online! Saved some bytes by iterating directly through 100,20,5,2,1 and using the formula i%12%5 to generate a unique index in the range 0..4for each in order to lookup the word. I'm pleasantly surprised that n-=i*(j=n/i) works as intended without brackets. # Ruby, 105 bytes ->n{n<1?a=["ala"]:5.times{|i|a=*a,"#{%w{ale mute luka tu wan}[i]} "*j=n/k=[100,20,5,2,1][i];n-=j*k} a*""}  Try it online! # JavaScript (V8), 109 96 bytes x=>(g=s=>n=>(s+" ").repeat(x/n,x%=n))ale(100)+gmute(20)+gluka(5)+gtu(2)+gwan(1)||"ala"  Try it online! • that [r="repeat"] and [r] trick is rather cursed to be honest and I love it Sep 2 at 23:32 # Scratch, 46 bytes set [output v] to [] // setup set [count v] to [1] if <(input) = [0]> then set [output v] to [ala] // 0 in toki pona else repeat [5] // every number word repeat ([floor v] of ((input) / (item (count) of [value v] // repeats every word in order set [output v] to (join (output) (item (count) of [number v] // the script that generates the number set [input v] to ((input) - (([floor v] of ((input) / (item (count) of [value v]))) * (item (count) of [value v]) // the script that updates the loop by finding a number end change [count v] by [1] // it makes sure that it will move on to the next word  Try it online! # JavaScript (Node.js), 90 bytes (n,r,t='0ala1wan2tu5luka20mute100ale'.match(n+'(\\D+)|$')[1])=>t?r?t+' '+f(r):t:f(n-1,-~r)


Try it online!

# JavaScript (Node.js), 91 bytes

n=>'100ale 20mute 5luka 2tu 1wan'.replace(/(\d+)(\D+)/g,(_,a,b)=>b.repeat(n/a,n%=a))||'ala'


Try it online!

This solution may introduce a trailing space, while the first one won't.

# APL (Dyalog Unicode), 69 bytes

{×⍵:1↓∊(∊(⊂0 2∘⊤)@4⊢0 5 4 5⊤⍵)/' '(=⊂⊢)' ale mute luka tu wan'⋄'ala'}


Try it online!

×⍵ if ⍵ is positive:
' '(=⊂⊢)' ale ...' split into a list of substrings, where each starts with a space.
0 5 4 5⊤⍵ Mixed radix decode, this results in a length 4 vector, where the first three are the 100, 20 and 5 places, and the fourth encodes the 1 and 2 places
∊(⊂0 2∘⊤)@4 split the last into two numbers
/ repeat each string from the previously generated list this many times
1↓∊ join into a single string and remove leading space

⋄'ala' if the input was 0, return this string

# Python 3, 143142 132 bytes

def c(s):
d,x={100:'ale',20:'mute',5:'luka',2:'tu',1:'wan'},''
if s==0:x+='ala'
else:
for i in d:
x+=s//i*(d[i]+' ')
s%=i
print(x)


Try it online!

Previous submission:

# Python 3, 143 bytes

def c(s):
if s==0:
print('ala')
else:
d = {100:'ale',20:'mute',5:'luka',2:'tu',1:'wan'}
for i in d:
print(s//i*d[i],end=' ')
s%=i


Try it online!

Thank you Jo King for pointing out non inclusion of whitespace
emanresu A, have provided the links now
Thank you Kevin Cruijssen and Jo King♦ for the if s==0 contraction

Jo King♦'s solution:

# Python 3, 116 bytes

def c(s,d={100:'ale',20:'mute',5:'luka',2:'tu',1:'wan'},x=''):
for i in d:x+=s//i*(d[i]+' ');s%=i
print(x or'ala')


Try it online!

• Nice answer! Could you provide a TIO link please, or some other online testing environment, so people can easily test this themselves? Sep 3 at 8:07
• 139 bytes by switching the if-else, so you just use if s: instead of if s==0:. And welcome to CGCC! If you haven't seen them yet, tips for golfing in Python and tips for golfing in <all languages> might be interesting to read through. Enjoy your stay. :) Sep 3 at 12:25
• You can also move the declarations to the function header, inline the for to save on whitespace, and change the s==0 check into a check that x is not empty in the print. 116 bytes
– Jo King
Sep 3 at 12:56

# Python 3, 191 bytes

def f(x):
k=[[]]+[0]*x
for i in range(1,1+x):
for j,q in[[1,"wan"],[2,"tu"],[5,"luka"],[20,"mute"],[100,"ale"]]:
if j<=i:p=k[i-j]+[q];k[i]=min(k[i]or p,p,key=len)
return k[x]or["ala"]


Try it online!

This is definitely not the shortest approach but it works in linear time using dynamic programming which was my intent. Even then, there is almost certainly a shorter linear approach, and more so probably a shorter dynamic programming method anyway.

Edit: never mind, I'm pretty sure this solution is straight up constant time. You may as well just go upvote that instead; I haven't golfed in a while and tunneled on the wrong approach.

• You don't need DP because 1, 2, 5, 20, 100 is a "nice" number system, i.e. greedy approach (simply cutting down from high to low) works just fine. Sep 2 at 23:32
• @Bubbler Ah, didn't realize, thanks for pointing that out. I will fix (well, it's not broken, but just silly) it later when I have time if I remember. Sep 2 at 23:33

# Charcoal, 56 bytes

Ｎθ¿¬θala«Ｆ⪪”→"jρ§φ|XºM″℅^§¶⬤]r⊗x｜Ｋ” ¿Σι≧÷Ｉιφ«×⁺ι ÷θφ≧﹪φθ


Try it online! Link is to verbose version of code. Explanation:

Ｎθ


Input n.

¿¬θala«


If n is zero then just print ala, otherwise:

Ｆ⪪”→"jρ§φ|XºM″℅^§¶⬤]r⊗x｜Ｋ”


Split the compressed string 10 ale 5 mute 4 luka 2 tu 2 wan on spaces and loop through the substrings.

¿Σι


If this substring is numeric, ...

≧÷Ｉιφ«


... integer divide the predefined variable 1000 by its value, otherwise:

×⁺ι ÷θφ


Integer divide the input by the predefined variable and print the space-suffixed current string that many times.

≧﹪φθ


Reduce the input modulo the predefined variable.

# 05AB1E (legacy), 39 bytes

т20 5Y)vy‰}).•Ð‰#¬êAš’ŒÞƒÜ•#ð«*J¨'ãà‚à


Uses the legacy version of 05AB1E (built in Python), because the new version of 05AB1E (built in Elixir) can't take the maximum on strings for $$\n=0\$$. This would be 40 bytes in the new version with an if/else construction.

Explanation:

т                  # Push 100
20                # Push 20
5              # Push 5
Y             # Push 2
)            # Wrap all values into a list: [100,20,5,2]
v           # Foreach y over these values:
y‰         #  Divmod the (implicit) input or previous value by y
#  Pop and push the n//y and n%y separated to the stack
})          # After the loop: wrap all values on the stack into a list
.•Ð‰#¬êAš’ŒÞƒÜ•    # Push compressed string "ale mute luka tu wan "
#   # Split it on spaces: ["ale","mute","luka","tu","wan",""]
ð« # Append a space to each
*                  # Repeat these strings the previous calculated values amount of times
J                 # Join this list of strings to a single string
¨                # Remove the trailing space
'ãà‚           '# Pair it with dictionary string "ala"
à           # Pop and push the maximum string of this pair
# (after which it is output implicitly as result)


See this 05AB1E tip of mine (sections How to use the dictionary? and How to compress strings not part of the dictionary?) to understand why .•Ð‰#¬êAš’ŒÞƒÜ• is "ale mute luka tu wan " and 'ãà is "ala".

# MathGolf, 44 42 bytes

k♀I52ΓÉ‼/%]╖)☺ÿmuteÿluka╢→Γùwan▐ +m*y╡╖) ╙


Try it online.

Explanation:

k             # Push the input-integer
♀            # Push 100
I           # Push 20
5          # Push 5
2         # Push 2
Γ        # Wrap the top four values into a list: [100,20,5,2]
É       # Foreach over this list, using three characters as inner code-block:
‼      #  Apply the following two characters separated to the stack
/     #   Integer-divide
%     #   Modulo
]      # After the loop, wrap all (five) values on the stack into a list
╖)☺           # Push compressed string "ale"
ÿmute         # Push string "mute"
ÿluka         # Push string "luka"
╢→            # Push compressed string "tu"
Γ        # Wrap all four values into a list: ["ale","mute","luka","tu"]
ùwan▐   # Append "wan": ["ale","mute","luka","tu","wan"]
+ # Append a space to each: ["ale ","mute ","luka ","tu ","wan "]
m*            # Repeat each string the values amount of times
y           # Join this list of strings together
╡          # Remove the trailing space
╖)        # Push compressed string "ala"
╙      # Only leave the maximum of the top two strings on the stack
# (after which the stack joined together is output implicitly as result)


# TI-Basic, 163160 152 bytes

"ale mutelukatu  wan→Str2
{ᴇ2,20,5,2,1→A
{3,4,4,2,3→B
"ala→Str1
Input N
If N:Then
For(I,1,5
While N≥ʟA(I
N-ʟA(I→N
Str1+" "+sub(Str2,4I-3,ʟB(I→Str1
End
End
sub(Ans,5,length(Ans)-4


-3 bytes for not storing the final output to Str1.
-8 bytes thanks to MarcMush.

The output is stored in Ans.

# jq, 78 77 bytes

./100%.?*"ale "+./20%5*"mute "+./5%4*"luka "+.%5/2%5*"tu "+.%5%2*"wan"//"ala"


Try it online!

A translation of dingledooper's Python approach.

In jq, / division is floaty but % seems to floor the result: 123.789%100 is 23. Thus we write something like .%5/2%5 to get the amount of "tu"s.

Slightly clever: ./100%. will cause an error when the input is zero, and the ? catches this and replaces with null, to which the // alternative-operator then provides "ala" as a fallback output. This is actually pretty useful because ""//"ala" is not "ala" (i.e. "" is not falsey in jq.)

Michael Chatiskatzi saved a byte, by moving ? around so that I don't need whitespace between ? and //. Thanks!

• Nice answer! You missed a whitespace after ?. Sep 6 at 7:08
• hmmm, removing that space was an error on jqplay but seems to work on TIO's version of jq. I might keep it for now
– Lynn
Sep 6 at 11:20
• You are right. TIO uses version 1.5, where ? // is equivalent to ?//. jqplay, however, uses jq 1.6. In this version a new operator was introduced: Destructuring Alternative Operator ?//, which is no longer the same as ? //. Sep 6 at 23:37
• You can move the ?, though to get rid of the whitespace, e.g. ./100%.?. Sep 6 at 23:43
• Oh neat, thanks. I'm surprised that works — I guess the error is "propagated" up until a // is found or something?
– Lynn
Sep 7 at 14:52

# Python 3.8 (pre-release), 125 bytes

f=lambda n,b=1,l={1:'wan',2:'tu',5:'luka',20:'mute',100:'ale'}:n and(l[(a:=max(b for b in l if b<=n))]+' '+f(n-a,0))or'ala'*b


Try it online!

Definitely not the shortest approach, but is the only recursive Python solution AFAIK.

## Explanation

Takes in n, the number to translate, and two other utilities: b is used to indicate whether ala should be appended: it starts at 1 (if the original n is 0, then output ala, but is explicitly passed as 0 to exclude ala once n hits zero in a recursive call.

An additional dictionary l is produced, with the numbers being mapped to the Toki Pona translations. In the body of the function, an and expression produces the translation with maxima if n is truthy, if n is zero then it jumps to the or expression and outputs ala or '' depending on the value of b. This ends the recursion.

If n is at least 1, we return the dictionary value with the maximal key below or equal to n, add a space, and then recall f with a reduced n accordingly.

# Python 3, 154 138 bytes

def f(n,p={100:'ale',20:'mute',5:'luka',2:'tu',1:'wan'},o=''):
while n:t=[i for i in p if i<=n][0];n-=t;o+=p[t]+' '
return o[:-1]or'ala'


Try it online!

-16 bytes thanks to Jo King.

This is an adaptation of dingledooper's answer for Python 3 and with no space at the end:

# Python 3, 93 bytes

lambda n:(n//100*'ale '+n//20%5*'mute '+n//5%4*'luka '+n%5//2*'tu '+n%5%2*'wan ')[:-1]or'ala'


Try it online!

• You can shave some whitespace off your first solution by moving the variable declaration to the function header and one lining the while loop, as well as remove the 0:'ala' part from the dictionary. Try it online!
– Jo King
Sep 3 at 7:30

# Perl 5, 85 bytes

$_="@{[map{$c=$F[0]/$_;$F[0]%=$_;(/\D+/g)x$c}qw(100ale 20mute 5luka 2tu 1wan)]}"||ala  Try it online! # Haskell, 111 bytes t@((x,w):y)#n|n>=x=w++' ':t#(n-x)|1<2=y#n _#n="" f 0="ala" f n=zip[100,20,5,2,1](words"ale mute luka tu wan")#n  Try it online! # Excel, 116 bytes ## Formula, 83 bytes =IF(A1,CONCAT(REPT(C1:C4,MOD(A1,D1:D4)/D2:D5))&IF(MOD(MOD(A1,5),2),"wan",""),"ala")  ## C1:D5, 33 bytes C1: "ale " D1: =9^9 C2: "mute " D2: 100 C3: "luka " D3: 20 C4: "tu " D4: 5 D5: 2  Link to Spreadsheet # PHP, 108 104 bytes foreach([100=>ale,20=>mute,5=>luka,2=>tu,1=>wan]as$v=>$w)for(;$argn>=$v;$argn-=$v)$o.="$w ";echo$o?:ala;


Try it online!

I'm so super rusty at PHP on CG, but here it is just for fun... more or less a port of my other answer.

Iterates through the each word's value and reduces the input number by that amount. Keeps reducing as long as the result is positive, each time adding the word to the output. Nothing fancy here, just the only operation used is subtraction.