# 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? Commented Sep 2, 2021 at 23:22
• that is correct. Commented Sep 2, 2021 at 23:23
• trailing spaces are fine Commented Sep 3, 2021 at 19:11
• I wonder why 3,532,123 is not in the test cases...
– Stef
Commented Sep 4, 2021 at 11:59
• mi pilin pona tan ni: jan pi toki pona li lon lipu ni :)
– qarz
Commented Apr 7, 2023 at 14:40

# 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
Commented Sep 3, 2021 at 7:44
• Shouldn't 100 be 'ale' not 'ale '? Commented Sep 3, 2021 at 7:57
• @rak1507: Spaces between words are mandatory, so this solution puts a space after any word that may be followed by another. Commented Sep 3, 2021 at 9:23
• Yes, I know, I'm just wondering whether or not trailing spaces are allowed. Commented Sep 3, 2021 at 10:00

# Jelly,  41  39 bytes

-2 thanks to Unrelated String! (Golfed the method of "coin counting".)

;%\:ɗ“dÞ¦£‘Jx$ȯWị“&ḍṘṭ.½6²EmSFʂ5ȥṙẠ»Ḳ¤K  Try it online! ### How? ;%\:ɗ“dÞ¦£‘Jx$ȯWị“...»Ḳ¤K - Link: non-negative integer, n
“dÞ¦£‘               - [100,20,5,2]
;                         -   (n) concatenate ([100,20,5,2]) -> [n,100,20,5,2]
\                       -   cumulative reduce by:
%                        -     modulo -> [n, n%100, n%100%20, n%100%20%5, n%100%20%5%2]
:                      -     integer divide ([100,20,5,2]) -> [n//100, n%100//20, n%100%20//5, n%100%20%5//2, n%100%20%5%2]
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  • -2 Commented Apr 6, 2023 at 22:45 • Good stuff @UnrelatedString, thanks! Commented Apr 7, 2023 at 13:59 # 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 }  # 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! # 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. # 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 Commented Sep 2, 2021 at 23:32 # Scratch, 46 blocks 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? Commented Sep 3, 2021 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. :) Commented Sep 3, 2021 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
Commented Sep 3, 2021 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. Commented Sep 2, 2021 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. Commented Sep 2, 2021 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 ?. Commented Sep 6, 2021 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
Commented Sep 6, 2021 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 ? //. Commented Sep 6, 2021 at 23:37
• You can move the ?, though to get rid of the whitespace, e.g. ./100%.?. Commented Sep 6, 2021 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
Commented Sep 7, 2021 at 14:52

# C (gcc), 118 109 bytes

f(i){printf(i?(i-=99)>0?"ale ":(i+=80)>0?"mute ":(i+=15)>0?"luka ":(i+=3)>0?"tu ":"wan ":"ala");--i<1?:f(i);}


Try it online!

New best for C.

Thanks for Jo King finding 4 bytes for me.

• (i-=1) and (i+=1) can be --i and ++i respectively. You can also save one more byte by removing the "%s ", by moving the space to the other strings except for ala
– Jo King
Commented Sep 17, 2021 at 13:31

# 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
Commented Sep 3, 2021 at 7:30

i;*v=L"\1\2\5\24d";f(n){n||puts("ala");for(i=4;~i;)n/v[i]?printf("wan \0$tu \0$$luka \0mute \0ale "+6*i),n-=v[i]:i--;}  Try it online! Saved 4 bytes thanks to ErikF!!! Saved 14 17 bytes thanks to AZTECCO!!! Saved 3 bytes thanks to ceilingcat!!! • 134 bytes by removing the char Commented Sep 3, 2021 at 14:22 • @ErikF Nice one, of course we don't need the char because printf will erase the type anyway and use that argument as a char*. Commented Sep 3, 2021 at 17:45 • @AZTECCO Brilliant - thanks! :D Commented Sep 3, 2021 at 17:46 • @AZTECCO Again, briliant - thanks! :D Commented Sep 3, 2021 at 20:35 • @ceilingcat Awesome - thanks! :D Commented Sep 3, 2021 at 20:36 # x86-16 machine code, PC DOS, 69 bytes 00000000: be1c 01ba 2101 85db 740b 83c2 06ac 983b ....!...t......; 00000010: d872 f72b d8b4 09cd 2175 f3c3 6414 0502 .|.+....!u..d... 00000020: 0161 6c61 2024 2061 6c65 2024 206d 7574 .ala$ ale $mut 00000030: 6520 246c 756b 6120 2474 7520 2420 2077 e$luka $tu$  w
00000040: 616e 2024 20                             an $ Listing: BE 011C MOV SI, OFFSET V ; quantity values to [SI] BA 0121 MOV DX, OFFSET W ; quantity words to [DX] 85 DB TEST BX, BX ; check for zero case 74 0B JZ QOUT ; if zero, display 'ala' and exit QNEXT: 83 C2 06 ADD DX, 6 ; skip to next output string index AC LODSB ; next quantity to check into AL QLOOP: 98 CBW ; clear AH 3B D8 CMP BX, AX ; is input less than this quantity? 72 F7 JB QNEXT ; if so, check next lowest 2B D8 SUB BX, AX ; otherwise reduce input by quantity QOUT: B4 09 MOV AH, 9 ; display quantity string CD 21 INT 21H ; write to stdout 75 F3 JNZ QLOOP ; loop if value has not been reduced to zero C3 RET ; return to caller/DOS V DB 100, 20, 5, 2, 1 W DB 'ala$ ','ale $','mute$','luka $','tu$  ','wan $'  Callable function, input value in BX output to DOS STDOUT. Maybe not the most elegant or byte efficient way to handle output of the variable-length strings... Sample output using DEBUG (note input values are in hex): # 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 # Python 3, 117 bytes lambda x:S(S(S(S(S(x*Q,Q*100,"ale "),Q*20,"mute "),Q*5,"luka "),Q*2,"tu ",),Q,"wan ")[:-1]or"ala" S,Q=str.replace,"q"  Try it online! An obvious find and replace one. Confuses the syntax highlighter - something or"string" should be parsed the same as something or "string", not something o r"string". # Scala, 303 bytes Try it online! def f(_n:Int)={ var n=_n if(n<1)List("ala")else{ var a=List[String]() List(100,20,5,2,1).zipWithIndex.foreach{case(k,i)=> val j=n/k a++=(s"${List("ale","mute","luka","tu","wan")(i)} "*j).split(" ")
n -= j*k
}
a.mkString
}
}


Ungolfed version

object Main extends App {
def f(_n: Int): String = {
var n=_n
if (n < 1) {
val a = List("ala")
a.mkString("")
} else {
var a = List[String]()
val denominations = List(100, 20, 5, 2, 1)
for ((k, i) <- denominations.zipWithIndex) {
val j = n / k
val word = List("ale", "mute", "luka", "tu", "wan")(i)

$_="@{[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! # 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.

# Raku, 76 bytes

->\n {~flat(<ale mute luka tu wan>Zxx(n/100,n/20%5,n/5%4,n%5/2,n%2))||"ala"}


Try it online!