Four is magic
You are encouraged to solve this task according to the task description, using any language you may know.
 Task
Write a subroutine, function, whatever it may be called in your language, that takes an integer number and returns an English text sequence starting with the English cardinal representation of that integer, the word 'is' and then the English cardinal representation of the count of characters that made up the first word, followed by a comma.
Continue the sequence by using the previous count word as the first word of the next phrase, append 'is' and the cardinal count of the letters in that word.
Continue until you reach four. Since four has four characters, finish by adding the words 'four is magic' and a period. All integers will eventually wind up at four.
For instance, suppose your are given the integer 3. Convert 3 to Three, add is , then the cardinal character count of three, or five, with a comma to separate if from the next phrase. Continue the sequence five is four, (five has four letters), and finally, four is magic.
Three is five, five is four, four is magic.
For reference, here are outputs for 0 through 9.
Zero is four, four is magic. One is three, three is five, five is four, four is magic. Two is three, three is five, five is four, four is magic. Three is five, five is four, four is magic. Four is magic. Five is four, four is magic. Six is three, three is five, five is four, four is magic. Seven is five, five is four, four is magic. Eight is five, five is four, four is magic. Nine is four, four is magic.
 Some task guidelines

 You may assume the input will only contain integer numbers.
 Cardinal numbers between 20 and 100 may use either hyphens or spaces as word separators but they must use a word separator. (23 is twenty three or twentythree not twentythree.)
 Cardinal number conversions should follow the English short scale. (billion is 1e9, trillion is 1e12, etc.)
 Cardinal numbers should not include commas. (20140 is twenty thousand one hundred forty not twenty thousand, one hundred forty.)
 When converted to a string, 100 should be one hundred, not a hundred or hundred, 1000 should be one thousand, not a thousand or thousand.
 When converted to a string, there should be no and in the cardinal string. 130 should be one hundred thirty not one hundred and thirty.
 When counting characters, count all of the characters in the cardinal number including spaces and hyphens. One hundred fiftyone should be 21 not 18.
 The output should follow the format "N is K, K is M, M is ... four is magic." (unless the input is 4, in which case the output should simply be "four is magic.")
 The output can either be the return value from the function, or be displayed from within the function.
 You are encouraged, though not mandated to use proper sentence capitalization.
 You may optionally support negative numbers. 7 is negative seven.
 Show the output here for a small representative sample of values, at least 5 but no more than 25. You are free to choose which which numbers to use for output demonstration.
You can choose to use a library, (module, external routine, whatever) to do the cardinal conversions as long as the code is easily and freely available to the public.
If you roll your own, make the routine accept at minimum any integer from 0 up to 999999. If you use a premade library, support at least up to unsigned 64 bit integers. (or the largest integer supported in your language if it is less.)
Four is magic is a popular codegolf task. This is not code golf. Write legible, idiomatic and well formatted code.
 Related tasks
AWK
<lang AWK>
 syntax: GAWK f FOUR_IS_MAGIC.AWK
BEGIN {
init_numtowords() n = split("1 0 1 2 3 4 5 6 7 8 9 11 21 1995 1000000 1234567890 1100100100100",arr," ") for (i=1; i<=n; i++) { a = arr[i] printf("%s: ",a) do { if (a == 4) { break } a = numtowords(a) b = numtowords(length(a)) printf("%s is %s, ",a,b) a = length(a) } while (b !~ /^four$/) printf("four is magic.\n") } exit(0)
}
 source: The AWK Programming Language, page 75
function numtowords(n, minus,str) {
if (n < 0) { n = n * 1 minus = "minus " } if (n == 0) { str = "zero" } else { str = intowords(n) } gsub(/ /," ",str) gsub(/ $/,"",str) return(minus str)
} function intowords(n) {
n = int(n) if (n >= 1000000000000) { return intowords(n/1000000000000) " trillion " intowords(n%1000000000000) } if (n >= 1000000000) { return intowords(n/1000000000) " billion " intowords(n%1000000000) } if (n >= 1000000) { return intowords(n/1000000) " million " intowords(n%1000000) } if (n >= 1000) { return intowords(n/1000) " thousand " intowords(n%1000) } if (n >= 100) { return intowords(n/100) " hundred " intowords(n%100) } if (n >= 20) { return tens[int(n/10)] " " intowords(n%10) } return(nums[n])
} function init_numtowords() {
split("one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen",nums," ") split("ten twenty thirty forty fifty sixty seventy eighty ninety",tens," ")
} </lang>
 Output:
1: minus one is nine, nine is four, four is magic. 0: zero is four, four is magic. 1: one is three, three is five, five is four, four is magic. 2: two is three, three is five, five is four, four is magic. 3: three is five, five is four, four is magic. 4: four is magic. 5: five is four, four is magic. 6: six is three, three is five, five is four, four is magic. 7: seven is five, five is four, four is magic. 8: eight is five, five is four, four is magic. 9: nine is four, four is magic. 11: eleven is six, six is three, three is five, five is four, four is magic. 21: twenty one is ten, ten is three, three is five, five is four, four is magic. 1995: one thousand nine hundred ninety five is thirty seven, thirty seven is twelve, twelve is six, six is three, three is five, five is four, four is magic. 1000000: one million is eleven, eleven is six, six is three, three is five, five is four, four is magic. 1234567890: one billion two hundred thirty four million five hundred sixty seven thousand eight hundred ninety is ninety eight, ninety eight is twelve, twelve is six, six is three, three is five, five is four, four is magic. 1100100100100: one trillion one hundred billion one hundred million one hundred thousand one hundred is eighty five, eighty five is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Factor
Factor's math.text.english
vocabulary does most of the heavy lifting. Since number>text
produces " and " and "," in its output, they are removed with a regular expression.
<lang factor>USING: ascii formatting io kernel make math.text.english regexp
sequences ;
IN: rosettacode.fourismagic
! Strip " and " and "," from the output of Factor's number>text ! word with a regular expression.
 number>english ( n  str )
number>text R/ and ,/ "" rereplace ;
! Return the length of the input integer's text form. ! e.g. 1 > 3
 nextlen ( n  m ) number>english length ;
! Given a starting integer, return the sequence of lengths ! terminating with 4. ! e.g. 1 > { 1 3 5 4 }
 lenchain ( n  seq )
[ [ dup 4 = ] [ dup , nextlen ] until , ] { } make ;
! Convert a nonfour number to its phrase form. ! e.g. 6 > "six is three, "
 nonfour ( n  str )
number>english dup length number>english "%s is %s, " sprintf ;
! Convert any number to its phrase form. ! e.g. 4 > "four is magic."
 phrase ( n  str )
dup 4 = [ drop "four is magic." ] [ nonfour ] if ;
 saymagic ( n  )
lenchain [ phrase ] map concat capitalize print ;
{ 1 4 11 100 112719908181724 612312 } [ saymagic ] each</lang>
 Output:
One is three, three is five, five is four, four is magic. Four is magic. Negative eleven is fifteen, fifteen is seven, seven is five, five is four, four is magic. One hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic. One hundred twelve trillion seven hundred nineteen billion nine hundred eight million one hundred eightyone thousand seven hundred twentyfour is one hundred fortythree, one hundred fortythree is twentythree, twentythree is twelve, twelve is six, six is three, three is five, five is four, four is magic. Negative six hundred twelve thousand three hundred twelve is fiftyseven, fiftyseven is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Go
Uses the say
function from the
Number names task.
<lang go>package main
import ( "fmt" "math" "strings" )
func main() { for _, n := range [...]int64{ 0, 4, 6, 11, 13, 75, 100, 337, 164, math.MaxInt64, } { fmt.Println(fourIsMagic(n)) } }
func fourIsMagic(n int64) string { s := say(n) s = strings.ToUpper(s[:1]) + s[1:] t := s for n != 4 { n = int64(len(s)) s = say(n) t += " is " + s + ", " + s } t += " is magic." return t }
// Following is from https://rosettacode.org/wiki/Number_names#Go
var small = [...]string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"} var tens = [...]string{"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"} var illions = [...]string{"", " thousand", " million", " billion", " trillion", " quadrillion", " quintillion"}
func say(n int64) string { var t string if n < 0 { t = "negative " // Note, for math.MinInt64 this leaves n negative. n = n } switch { case n < 20: t += small[n] case n < 100: t += tens[n/10] s := n % 10 if s > 0 { t += "" + small[s] } case n < 1000: t += small[n/100] + " hundred" s := n % 100 if s > 0 { t += " " + say(s) } default: // work righttoleft sx := "" for i := 0; n > 0; i++ { p := n % 1000 n /= 1000 if p > 0 { ix := say(p) + illions[i] if sx != "" { ix += " " + sx } sx = ix } } t += sx } return t }</lang>
 Output:
Zero is four, four is magic. Four is magic. Six is three, three is five, five is four, four is magic. Eleven is six, six is three, three is five, five is four, four is magic. Thirteen is eight, eight is five, five is four, four is magic. Seventyfive is twelve, twelve is six, six is three, three is five, five is four, four is magic. One hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic. Three hundred thirtyseven is twentysix, twentysix is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixtyfour is thirtyone, thirtyone is ten, ten is three, three is five, five is four, four is magic. Nine quintillion two hundred twentythree quadrillion three hundred seventytwo trillion thirtysix billion eight hundred fiftyfour million seven hundred seventyfive thousand eight hundred seven is one hundred ninetysix, one hundred ninetysix is twentytwo, twentytwo is ten, ten is three, three is five, five is four, four is magic.
J
<lang J> names =. 'one';'two';'three';'four';'five';'six';'seven';'eight';'nine';'ten';'eleven';'twelve';'thirteen';'fourteen';'fifteen';'sixteen';'seventeen';'eighteen';'nineteen'
tens =. ;'twenty';'thirty';'forty';'fifty';'sixty';'seventy';'eighty';'ninety'
NB. selects the xth element from list y lookup =: >@{:@{.
NB. string formatting addspace =: ((' '"_, ]) ` ]) @. (<&0 @ {: @ $)
NB. numbers in range 1 to 19 s1 =: lookup&names
NB. numbers in range 20 to 99 s2d=: (lookup&tens @ <. @ %&10) , addspace @ (s1 @ (10&))
NB. numbers in range 100 to 999 s3d =: s1 @ (<.@%&100), ' hundred', addspace @ s2d @ (100&)
NB. numbers in range 1 to 999 s123d =: s1 ` s2d ` s3d @. (>& 19 + >&99)
NB. numbers in range 1000 to 999999 s456d =: (s123d @<.@%&1000), ' thousand', addspace @ s123d @ (1000&)
NB. stringify numbers in range 1 to 999999 stringify =: s123d ` s456d @. (>&999)
NB. takes an int and returns an int of the length of the string of the input lengthify =: {: @ $ @ stringify
NB. determines the string that should go after ' is ' what =: ((stringify @ lengthify), (', '"_)) ` ('magic'"_) @. (=&4)
runonce =: stringify , ' is ', what
run =: runonce, ((run @ lengthify) ` ("_) @. (=&4))
doall =: run"0
inputs =: 4 8 16 25 89 365 2586 25865 369854
doall inputs
</lang>
 Output:
four is magic eight is five, five is four, four is magic sixteen is seven, seven is five, five is four, four is magic twenty five is eleven, eleven is six, six is three, three is five, five is four, four is magic eighty nine is eleven, eleven is six, six is three, three is five, five is four, four is magic three hundred sixty five is twenty four, twenty four is eleven, eleven is six, six is three, three is five, five is four, four is magic two thousand five hundred eighty six is thirty six, thirty six is ten, ten is three, three is five, five is four, four is magic twenty five thousand eight hundred sixty five is forty five, forty five is ten, ten is three, three is five, five is four, four is magic three hundred sixty nine thousand eight hundred fifty four is fifty eight, fifty eight is eleven, eleven is six, six is three, three is five, five is four, four is magic
Kotlin
This uses the code I wrote for the Number names task, appropriately adjusted to deal with this task. Input is limited to signed 64 bit integers as Kotlin doesn't currently support unsigned types. <lang scala>// version 1.1.43
val names = mapOf(
1 to "one", 2 to "two", 3 to "three", 4 to "four", 5 to "five", 6 to "six", 7 to "seven", 8 to "eight", 9 to "nine", 10 to "ten", 11 to "eleven", 12 to "twelve", 13 to "thirteen", 14 to "fourteen", 15 to "fifteen", 16 to "sixteen", 17 to "seventeen", 18 to "eighteen", 19 to "nineteen", 20 to "twenty", 30 to "thirty", 40 to "forty", 50 to "fifty", 60 to "sixty", 70 to "seventy", 80 to "eighty", 90 to "ninety"
) val bigNames = mapOf(
1_000L to "thousand", 1_000_000L to "million", 1_000_000_000L to "billion", 1_000_000_000_000L to "trillion", 1_000_000_000_000_000L to "quadrillion", 1_000_000_000_000_000_000L to "quintillion"
)
fun numToText(n: Long): String {
if (n == 0L) return "zero" val neg = n < 0L val maxNeg = n == Long.MIN_VALUE var nn = if (maxNeg) (n + 1) else if (neg) n else n val digits3 = IntArray(7) for (i in 0..6) { // split number into groups of 3 digits from the right digits3[i] = (nn % 1000).toInt() nn /= 1000 }
fun threeDigitsToText(number: Int) : String { val sb = StringBuilder() if (number == 0) return "" val hundreds = number / 100 val remainder = number % 100 if (hundreds > 0) { sb.append(names[hundreds], " hundred") if (remainder > 0) sb.append(" ") } if (remainder > 0) { val tens = remainder / 10 val units = remainder % 10 if (tens > 1) { sb.append(names[tens * 10]) if (units > 0) sb.append("", names[units]) } else sb.append(names[remainder]) } return sb.toString() }
val strings = Array<String>(7) { threeDigitsToText(digits3[it]) } var text = strings[0] var big = 1000L for (i in 1..6) { if (digits3[i] > 0) { var text2 = strings[i] + " " + bigNames[big] if (text.length > 0) text2 += " " text = text2 + text } big *= 1000 } if (maxNeg) text = text.dropLast(5) + "eight" if (neg) text = "negative " + text return text
}
fun fourIsMagic(n: Long): String {
if (n == 4L) return "Four is magic." var text = numToText(n).capitalize() val sb = StringBuilder() while (true) { val len = text.length.toLong() if (len == 4L) return sb.append("$text is four, four is magic.").toString() val text2 = numToText(len) sb.append("$text is $text2, ") text = text2 }
}
fun main(args: Array<String>) {
val la = longArrayOf(0, 4, 6, 11, 13, 75, 100, 337, 164, 9_223_372_036_854_775_807L) for (i in la) { println(fourIsMagic(i)) println() }
}</lang>
 Output:
Zero is four, four is magic. Four is magic. Six is three, three is five, five is four, four is magic. Eleven is six, six is three, three is five, five is four, four is magic. Thirteen is eight, eight is five, five is four, four is magic. Seventyfive is twelve, twelve is six, six is three, three is five, five is four, four is magic. One hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic. Three hundred thirtyseven is twentysix, twentysix is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixtyfour is thirtyone, thirtyone is ten, ten is three, three is five, five is four, four is magic. Nine quintillion two hundred twentythree quadrillion three hundred seventytwo trillion thirtysix billion eight hundred fiftyfour million seven hundred seventyfive thousand eight hundred seven is one hundred ninetysix, one hundred ninetysix is twentytwo, twentytwo is ten, ten is three, three is five, five is four, four is magic.
Perl
<lang perl>use Lingua::EN::Numbers qw(num2en);
sub cardinal {
my($n) = @_; (my $en = num2en($n)) =~ s/\ and,//g; $en;
}
sub magic {
my($int) = @_; my $str; while () { $str .= cardinal($int) . " is "; if ($int == 4) { $str .= "magic.\n"; last } else { $int = length cardinal($int); $str .= cardinal($int) . ", "; } } ucfirst $str;
}
print magic($_) for 0, 4, 6, 11, 13, 75, 337, 164, 9_876_543_209;</lang>
 Output:
Zero is four, four is magic. Four is magic. Six is three, three is five, five is four, four is magic. Eleven is six, six is three, three is five, five is four, four is magic. Thirteen is eight, eight is five, five is four, four is magic. Seventyfive is twelve, twelve is six, six is three, three is five, five is four, four is magic. Three hundred thirtyseven is twentysix, twentysix is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixtyfour is thirtyone, thirtyone is ten, ten is three, three is five, five is four, four is magic. Nine billion eight hundred seventysix million five hundred fortythree thousand two hundred nine is ninetyseven, ninetyseven is twelve, twelve is six, six is three, three is five, five is four, four is magic.
Perl 6
Lingua::EN::Numbers::Cardinal module available from the Perl 6 ecosystem.
<lang perl6>use Lingua::EN::Numbers::Cardinal;
sub card ($n) { cardinal($n).subst(/','/, , :g) }
sub magic (Int $int is copy) {
my $string; loop { $string ~= "{ card($int) } is "; if $int = ($int == 4) ?? 0 !! card($int).chars { $string ~= "{ card($int) }, " } else { $string ~= "magic.\n"; last } } $string.tc
}
.&magic.say for 0, 4, 6, 11, 13, 75, 337, 164, 9876543209, 2**256;</lang>
 Output:
Zero is four, four is magic. Four is magic. Six is three, three is five, five is four, four is magic. Eleven is six, six is three, three is five, five is four, four is magic. Thirteen is eight, eight is five, five is four, four is magic. Seventyfive is twelve, twelve is six, six is three, three is five, five is four, four is magic. Three hundred thirtyseven is twentysix, twentysix is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixtyfour is thirtyone, thirtyone is ten, ten is three, three is five, five is four, four is magic. Nine billion eight hundred seventysix million five hundred fortythree thousand two hundred nine is ninetyseven, ninetyseven is twelve, twelve is six, six is three, three is five, five is four, four is magic. One hundred fifteen quattuorvigintillion seven hundred ninetytwo trevigintillion eightynine duovigintillion two hundred thirtyseven unvigintillion three hundred sixteen vigintillion one hundred ninetyfive novemdecillion four hundred twentythree octodecillion five hundred seventy septendecillion nine hundred eightyfive sexdecillion eight quindecillion six hundred eightyseven quattuordecillion nine hundred seven tredecillion eight hundred fiftythree duodecillion two hundred sixtynine undecillion nine hundred eightyfour decillion six hundred sixtyfive nonillion six hundred forty octillion five hundred sixtyfour septillion thirtynine sextillion four hundred fiftyseven quintillion five hundred eightyfour quadrillion seven trillion nine hundred thirteen billion one hundred twentynine million six hundred thirtynine thousand nine hundred thirtysix is eight hundred sixtynine, eight hundred sixtynine is twentyfour, twentyfour is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Phix
Note that on 32bit Phix integers/atoms are only accurate to 9,007,199,254,740,992 (a hardware limit of 64bit floating point registers) so if you need more than that this will need to be reworked to use bigatoms. <lang Phix><adapted from demo\rosetta\number_names.exw, which alas outputs ",", "and", uses "minus" instead of "negative", etc...> constant twenties = {"zero","one","two","three","four","five","six","seven","eight","nine","ten",
"eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"}, decades = {"twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"}
function hundred(integer n)
if n<20 then return twenties[mod(n,20)+1] elsif mod(n,10)=0 then return decades[mod(floor(n/10),10)1] end if return decades[mod(floor(n/10),10)1] & '' & twenties[mod(n,10)+1]
end function
function thousand(integer n)
if n<100 then return hundred(n) elsif mod(n,100)=0 then return twenties[mod(floor(n/100),20)+1]&" hundred" end if return twenties[mod(floor(n/100),20)+1] & " hundred " & hundred(mod(n,100))
end function
constant orders = {{power(10,12),"trillion"},
{power(10,9),"billion"}, {power(10,6),"million"}, {power(10,3),"thousand"}}
function triplet(integer n) atom order, high, low string name, res = ""
for i=1 to length(orders) do {order,name} = orders[i] high = floor(n/order) low = mod(n,order) if high!=0 then res &= thousand(high)&' '&name end if n = low if low=0 then exit end if if length(res) and high!=0 then res &= " " end if end for if n!=0 or res="" then res &= thousand(floor(n)) end if return res
end function
function spell(integer n) string res = ""
if n<0 then res = "negative " n = n end if res &= triplet(n) return res
end function </adapted from number_names.exw>
function fourIsMagic(atom n)
string s = spell(n) s[1] = upper(s[1]) string t = s while n!=4 do n = length(s) s = spell(n) t &= " is " & s & ", " & s end while t &= " is magic.\n" return t
end function
constant tests = {7, 1, 0, 1, 2, 3, 4, 23, 1e9, 20140, 100, 130, 151, 999999} for i=1 to length(tests) do
puts(1,fourIsMagic(tests[i]))
end for</lang>
 Output:
Negative seven is fourteen, fourteen is eight, eight is five, five is four, four is magic. Negative one is twelve, twelve is six, six is three, three is five, five is four, four is magic. Zero is four, four is magic. One is three, three is five, five is four, four is magic. Two is three, three is five, five is four, four is magic. Three is five, five is four, four is magic. Four is magic. Twentythree is twelve, twelve is six, six is three, three is five, five is four, four is magic. One billion is eleven, eleven is six, six is three, three is five, five is four, four is magic. Twenty thousand one hundred forty is thirtythree, thirtythree is twelve, twelve is six, six is three, three is five, five is four, four is magic. One hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic. One hundred thirty is eighteen, eighteen is eight, eight is five, five is four, four is magic. One hundred fiftyone is twentyone, twentyone is ten, ten is three, three is five, five is four, four is magic. Nine hundred ninetynine thousand nine hundred ninetynine is fiftyeight, fiftyeight is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Python
Python 3 version. Should work for integers up to at least 10^3003. It can be extended easily to arbitrary integers by adding to the numbers dict.
<lang python>import random from collections import OrderedDict
numbers = { # taken from https://en.wikipedia.org/wiki/Names_of_large_numbers#cite_refa_143
1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten', 11: 'eleven', 12: 'twelve', 13: 'thirteen', 14: 'fourteen', 15: 'fifteen', 16: 'sixteen', 17: 'seventeen', 18: 'eighteen', 19: 'nineteen', 20: 'twenty', 30: 'thirty', 40: 'forty', 50: 'fifty', 60: 'sixty', 70: 'seventy', 80: 'eighty', 90: 'ninety', 100: 'hundred', 1000: 'thousand', 10 ** 6: 'million', 10 ** 9: 'billion', 10 ** 12: 'trillion', 10 ** 15: 'quadrillion', 10 ** 18: 'quintillion', 10 ** 21: 'sextillion', 10 ** 24: 'septillion', 10 ** 27: 'octillion', 10 ** 30: 'nonillion', 10 ** 33: 'decillion', 10 ** 36: 'undecillion', 10 ** 39: 'duodecillion', 10 ** 42: 'tredecillion', 10 ** 45: 'quattuordecillion', 10 ** 48: 'quinquadecillion', 10 ** 51: 'sedecillion', 10 ** 54: 'septendecillion', 10 ** 57: 'octodecillion', 10 ** 60: 'novendecillion', 10 ** 63: 'vigintillion', 10 ** 66: 'unvigintillion', 10 ** 69: 'duovigintillion', 10 ** 72: 'tresvigintillion', 10 ** 75: 'quattuorvigintillion', 10 ** 78: 'quinquavigintillion', 10 ** 81: 'sesvigintillion', 10 ** 84: 'septemvigintillion', 10 ** 87: 'octovigintillion', 10 ** 90: 'novemvigintillion', 10 ** 93: 'trigintillion', 10 ** 96: 'untrigintillion', 10 ** 99: 'duotrigintillion', 10 ** 102: 'trestrigintillion', 10 ** 105: 'quattuortrigintillion', 10 ** 108: 'quinquatrigintillion', 10 ** 111: 'sestrigintillion', 10 ** 114: 'septentrigintillion', 10 ** 117: 'octotrigintillion', 10 ** 120: 'noventrigintillion', 10 ** 123: 'quadragintillion', 10 ** 153: 'quinquagintillion', 10 ** 183: 'sexagintillion', 10 ** 213: 'septuagintillion', 10 ** 243: 'octogintillion', 10 ** 273: 'nonagintillion', 10 ** 303: 'centillion', 10 ** 306: 'uncentillion', 10 ** 309: 'duocentillion', 10 ** 312: 'trescentillion', 10 ** 333: 'decicentillion', 10 ** 336: 'undecicentillion', 10 ** 363: 'viginticentillion', 10 ** 366: 'unviginticentillion', 10 ** 393: 'trigintacentillion', 10 ** 423: 'quadragintacentillion', 10 ** 453: 'quinquagintacentillion', 10 ** 483: 'sexagintacentillion', 10 ** 513: 'septuagintacentillion', 10 ** 543: 'octogintacentillion', 10 ** 573: 'nonagintacentillion', 10 ** 603: 'ducentillion', 10 ** 903: 'trecentillion', 10 ** 1203: 'quadringentillion', 10 ** 1503: 'quingentillion', 10 ** 1803: 'sescentillion', 10 ** 2103: 'septingentillion', 10 ** 2403: 'octingentillion', 10 ** 2703: 'nongentillion', 10 ** 3003: 'millinillion'
} numbers = OrderedDict(sorted(numbers.items(), key=lambda t: t[0], reverse=True))
def string_representation(i: int) > str:
""" Return the english string representation of an integer """ if i == 0: return 'zero'
words = ['negative'] if i < 0 else [] working_copy = abs(i)
for key, value in numbers.items(): if key <= working_copy: times = int(working_copy / key)
if key >= 100: words.append(string_representation(times))
words.append(value) working_copy = times * key
if working_copy == 0: break
return ' '.join(words)
def next_phrase(i: int):
""" Generate all the phrases """ while not i == 4: # Generate phrases until four is reached str_i = string_representation(i) len_i = len(str_i)
yield str_i, 'is', string_representation(len_i)
i = len_i
# the last phrase yield string_representation(i), 'is', 'magic'
def magic(i: int) > str:
phrases = []
for phrase in next_phrase(i): phrases.append(' '.join(phrase))
return f'{", ".join(phrases)}.'.capitalize()
if __name__ == '__main__':
for j in (random.randint(0, 10 ** 3) for i in range(5)): print(j, ':\n', magic(j), '\n')
for j in (random.randint(10 ** 24, 10 ** 24) for i in range(2)): print(j, ':\n', magic(j), '\n')</lang>
 Output:
475 : Four hundred seventy five is twenty five, twenty five is eleven, eleven is six, six is three, three is five, five is four, four is magic. 968 : Nine hundred sixty eight is twenty four, twenty four is eleven, eleven is six, six is three, three is five, five is four, four is magic. 304 : Three hundred four is eighteen, eighteen is eight, eight is five, five is four, four is magic. 544 : Five hundred forty four is twenty three, twenty three is twelve, twelve is six, six is three, three is five, five is four, four is magic. 394 : Three hundred ninety four is twenty five, twenty five is eleven, eleven is six, six is three, three is five, five is four, four is magic. 49587779907680717664396 : Negative forty nine sextillion five hundred eighty seven quintillion seven hundred seventy nine quadrillion nine hundred seven trillion six hundred eighty billion seven hundred seventeen million six hundred sixty four thousand three hundred ninety six is two hundred fifty one, two hundred fifty one is twenty one, twenty one is ten, ten is three, three is five, five is four, four is magic. 874143425855745733896030 : Eight hundred seventy four sextillion one hundred forty three quintillion four hundred twenty five quadrillion eight hundred fifty five trillion seven hundred forty five billion seven hundred thirty three million eight hundred ninety six thousand thirty is two hundred fifty three, two hundred fifty three is twenty three, twenty three is twelve, twelve is six, six is three, three is five, five is four, four is magic.
Racket
<lang racket>#lang racket
(define numbernames
(list "zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine" "ten" "eleven" "twelve" "thirteen" "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" "nineteen"))
(define numbersalist
'((20 . twenty) (30 . thirty) (40 . forty) (50 . fifty) (60 . sixty) (70 . seventy) (80 . eighty) (90 . ninety) (100 . hundred) (1000 . thousand) (#e1E6 . million) (#e1E9 . billion) (#e1E12 . trillion) (#e1E15 . quadrillion) (#e1E18 . quintillion) (#e1E21 . sextillion) (#e1E24 . septillion) (#e1E27 . octillion) (#e1E30 . nonillion) (#e1E33 . decillion) (#e1E36 . undecillion) (#e1E39 . duodecillion) (#e1E42 . tredecillion) (#e1E45 . quattuordecillion) (#e1E48 . quinquadecillion) (#e1E51 . sedecillion) (#e1E54 . septendecillion) (#e1E57 . octodecillion) (#e1E60 . novendecillion) (#e1E63 . vigintillion) (#e1E66 . unvigintillion) (#e1E69 . duovigintillion) (#e1E72 . tresvigintillion) (#e1E75 . quattuorvigintillion) (#e1E78 . quinquavigintillion) (#e1E81 . sesvigintillion) (#e1E84 . septemvigintillion) (#e1E87 . octovigintillion) (#e1E90 . novemvigintillion) (#e1E93 . trigintillion) (#e1E96 . untrigintillion) (#e1E99 . duotrigintillion) (#e1E102 . trestrigintillion) (#e1E105 . quattuortrigintillion) (#e1E108 . quinquatrigintillion) (#e1E111 . sestrigintillion) (#e1E114 . septentrigintillion) (#e1E117 . octotrigintillion) (#e1E120 . noventrigintillion) (#e1E123 . quadragintillion) (#e1E153 . quinquagintillion) (#e1E183 . sexagintillion) (#e1E213 . septuagintillion) (#e1E243 . octogintillion) (#e1E273 . nonagintillion) (#e1E303 . centillion) (#e1E306 . uncentillion) (#e1E309 . duocentillion) (#e1E312 . trescentillion) (#e1E333 . decicentillion) (#e1E336 . undecicentillion) (#e1E363 . viginticentillion) (#e1E366 . unviginticentillion) (#e1E393 . trigintacentillion) (#e1E423 . quadragintacentillion) (#e1E453 . quinquagintacentillion) (#e1E483 . sexagintacentillion) (#e1E513 . septuagintacentillion) (#e1E543 . octogintacentillion) (#e1E573 . nonagintacentillion) (#e1E603 . ducentillion) (#e1E903 . trecentillion) (#e1E1203 . quadringentillion) (#e1E1503 . quingentillion) (#e1E1803 . sescentillion) (#e1E2103 . septingentillion) (#e1E2403 . octingentillion) (#e1E2703 . nongentillion) (#e1E3003 . millinillion)))
(define (number>words n)
(define (n>list n acc) (define (nameof n) (symbol>string(cdr (assoc n numbersalist)))) (define (consname n) (cons (nameof n) acc)) (cond [(and (zero? n) (pair? acc)) (reverse acc)] [(< n 20) (reverse (list (listref numbernames n)))] [(< n 100) (letvalues (([q r] (quotient/remainder n 10))) (n>list r (consname (* q 10))))] [else (let*values (([val] (argmax values (filtermap (compose (λ (val) (and (< val n) val)) car) numbersalist))) ([q r] (quotient/remainder n val))) (n>list r (append (cons (nameof val) (reverse (n>list q null))) acc)))])) (stringjoin (n>list n null)))
(define (firstcap s)
(stringappend (stringupcase (substring s 0 1)) (substring s 1)))
(define (numbermagic n)
(firstcap (magic (number>words n) null)))
(define (magic word accumulator)
(if (equal? word "four") (stringjoin (reverse (cons "four is magic." accumulator)) ", ") (let ((wordlen (stringlength word))) (magic (number>words wordlen) (cons (stringappend word " is " (number>words wordlen)) accumulator)))))
(module+ test
(define testnumbers (append (range 11) '(23 172 20140 100 130 876000000 874143425855745733896030))) (foreach (λ (n) (displayln (numbermagic n))) testnumbers))</lang>
 Output:
Zero is four, four is magic. One is three, three is five, five is four, four is magic. Two is three, three is five, five is four, four is magic. Three is five, five is four, four is magic. Four is magic. Five is four, four is magic. Six is three, three is five, five is four, four is magic. Seven is five, five is four, four is magic. Eight is five, five is four, four is magic. Nine is four, four is magic. Ten is three, three is five, five is four, four is magic. Three is five, five is four, four is magic. Two is three, three is five, five is four, four is magic. Twenty thousand one hundred forty is three, three is five, five is four, four is magic. Ten is three, three is five, five is four, four is magic. One hundred thirty is eighteen, eighteen is eight, eight is five, five is four, four is magic. Six million is eleven, eleven is six, six is three, three is five, five is four, four is magic. Four sextillion three quintillion five quadrillion five trillion five billion three million six thousand thirty is eleven, eleven is six, six is three, three is five, five is four, four is magic.
REXX
The numbers used for the default were taken from the Kotlin example.
Numbers are limited to 3,003 decimal digits, the maximum number that the $SPELL# REXX program will handle. <lang rexx>/*REXX pgm converts a # to English into the phrase: a is b, b is c, ... four is magic. */ numeric digits 3003 /*be able to handle gihugic numbers. */ parse arg x /*obtain optional numbers from the C.L.*/ if x= then x=164 0 4 6 11 13 75 100 337 9223372036854775807 /*use these defaults?*/ @.=. /*stemmed array used for memoization. */
do j=1 for words(x) /*process each of the numbers in list. */ say 4_is( word(x, j) ) /*display phrase that'll be returned. */ say /*display a blank line between outputs.*/ end /*j*/
exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ 4_is: procedure expose @.; parse arg #,,$ /*obtain the start number.*/
if #\==4 then do until L==4 /*Not 4? Process number.*/ @.#=$spell#(# 'quiet minus negative') /*spell number in English.*/ #=@.#; L=length(#) /*get the length of spelt#*/ if @.L==. then @.L=$spell#(L 'quiet') /*¬spelt before? Spell it.*/ $=$ # "is" @.L',' /*add phrase to the answer*/ #=L /*use the new number, ··· */ end /*until*/ /* ··· which will be spelt*/ $=strip($ 'four is magic.') /*finish the sentence with the finale. */ parse var $ first 2 other; upper first /*capitalize the first letter of output*/ return first  other /*return the sentence to the invoker. */</lang>
The $SPELL#.REX routine can be found here ───► $SPELL#.REX.
 output when using the default inputs:
Negative one hundred sixtyfour is thirtyone, thirtyone is ten, ten is three, three is five, five is four, four is magic. Zero is four, four is magic. Four is magic. Six is three, three is five, five is four, four is magic. Eleven is six, six is three, three is five, five is four, four is magic. Thirteen is eight, eight is five, five is four, four is magic. Seventyfive is twelve, twelve is six, six is three, three is five, five is four, four is magic. One hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic. Three hundred thirtyseven is twentysix, twentysix is ten, ten is three, three is five, five is four, four is magic. Nine quintillion two hundred twentythree quadrillion three hundred seventytwo trillion thirtysix billion eight hundred fiftyfour million seven hundred seventyfive thousand eight hundred seven is one hundred ninetysix, one hundred ninetysix is twentytwo, twentytwo is ten, ten is three, three is five, five is four, four is magic.
Ring
<lang ring> /* Checking numbers from 0 to 10 */ for c = 0 to 10 See checkmagic(c) + NL next
/* The functions */
Func CheckMagic numb CardinalN = "" Result = "" if isnumber(numb) = false or numb < 0 or numb > 999_999_999_999_999 Return "ERROR: Number entered is incorrect" ok if numb = 4 Result = "Four is magic." else While True if CardinalN = "four" Result += "four is magic" exit ok strnumb = StringNumber(numb) CardinalN = StringNumber(len(strnumb)) Result += strnumb + " is " + CardinalN + ", " numb = len(strnumb) End Result += "." Result = upper(Result[1]) + Right(Result, len(Result) 1) ok
Return Result
Func StringNumber cnumb
NumStr = [:n0 = "zero", :n1 = "one", :n2 = "two", :n3 = "three", :n4 = "four", :n5 = "five", :n6 = "six", :n7 = "seven", :n8 = "eight", :n9 = "nine", :n10 = "ten", :n11 = "eleven", :n12 = "twelve", :n13 = "thirteen", :n14 = "fourteen", :n15 = "fifteen", :n16 = "sixteen", :n17 = "seventeen", :n18 = "eighteen", :n19 = "nineteen", :n20 = "twenty", :n30 = "thirty", :n40 = "fourty", :n50 = "fifty", :n60 = "sixty", :n70 = "seventy", :n80 = "eighty", :n90 = "ninety"]
numLev = [:l1 = "", :l2 = "thousand", :l3 = "million", :l4 = "billion", :l5 = "trillion"]
Result = ""
if cnumb > 0 decimals(0) snumb = string((cnumb)) lnumb = [""] fl = floor(len(snumb) / 3) if fl > 0 for i = 1 to fl lnumb[i] = right(snumb, 3) snumb = left(snumb, len(snumb) 3) lnumb + "" next if (len(snumb) % 3) > 0 lnumb[len(lnumb)] = snumb else del(lnumb, len(lnumb)) ok else lnumb[1] = snumb ok for l = len(lnumb) to 1 step 1 bnumb = lnumb[l] bResult = "" if number(bnumb) != 0 for n = len(bnumb) to 1 step 1 if (len(bnumb) = 3 and n = 2) or (len(bnumb) = 2 and n = 1) if number(bnumb[n]) > 1 eval("bResult = NumStr[:n" + bnumb[n] + "0] + ' ' + bResult") elseif number(bnumb[n]) = 1 eval("bResult = NumStr[:n" + bnumb[n] + bnumb[n+1] + "] + ' ' + bResult") ok else if len(bnumb) = 3 and n = 1 and number(bnumb[1]) > 0 if trim(bResult) != "" bResult = " " + bResult ok if number(bnumb[1]) > 1 bResult = "hundreds" + bResult else bResult = "hundred" + bResult ok if left(trim(bResult), 7) = "hundred" bResult = bResult + " " ok ok if (len(bnumb) = 3 and n = 1 and number(bnumb[1]) = 0) OR (len(bnumb) = n and number(bnumb[n]) = 0) OR (len(bnumb) = 3 and number(bnumb[2]) = 1) OR (len(bnumb) = 2 and number(bnumb[1]) = 1) loop ok eval("bResult = NumStr[:n" + bnumb[n] + "] + ' ' + bResult") ok next Result = Result + bResult if l > 1 if number(bnumb) > 1 eval("Result = Result + numLev[:l" + l + "] + 's ' ") else eval("Result = Result + numLev[:l" + l + "] + ' ' ") ok ok ok next else Result = Result + NumStr[:n0] ok
Return trim(Result) </lang> Output:
Zero is four, four is magic. One is three, three is five, five is four, four is magic. Two is three, three is five, five is four, four is magic. Three is five, five is four, four is magic. Four is magic. Five is four, four is magic. Six is three, three is five, five is four, four is magic. Seven is five, five is four, four is magic. Eight is five, five is four, four is magic. Nine is four, four is magic. Ten is three, three is five, five is four, four is magic.
zkl
Limitiation: zkl only has 64 bit signed integars.
Uses the nth function from Spelling_of_ordinal_numbers#zkl
<lang zkl>fcn fourIsMagic(int){
if(int==0) return("Zero is four, four is magic."); string:=""; while(1){ c:=nth(int,False); string+="%s is ".fmt(c); if(int = ( if(int==4) 0 else c.len() )){
string+="%s, ".fmt(nth(int,False));
}else{ string+="magic.";
break;
} } string[0].toUpper() + string[1,*]
}</lang> <lang zkl>foreach n in (T(0,4,6,11,13,75,337,164,9876543209)){
println(fourIsMagic(n),"\n")
}</lang>
 Output:
Zero is four, four is magic. Four is magic. Six is three, three is five, five is four, four is magic. Eleven is six, six is three, three is five, five is four, four is magic. Thirteen is eight, eight is five, five is four, four is magic. Seventyfive is twelve, twelve is six, six is three, three is five, five is four, four is magic. Three hundred thirtyseven is twentysix, twentysix is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixtyfour is thirtyone, thirtyone is ten, ten is three, three is five, five is four, four is magic. Nine billion eight hundred seventysix million five hundred fortythree thousand two hundred nine is ninetyseven, ninetyseven is twelve, twelve is six, six is three, three is five, five is four, four is magic.