# Four is magic

*is a*

**Four is magic****draft**programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

- Four is magic.

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**twenty-three**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 fifty-one**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 pre-made 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 code-golf task. **This is not code golf.** Write legible, idiomatic and well formatted code.

- Related tasks

## Kotlin[edit]

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.

// version 1.1.4-3

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()

}

}

- 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. Seventy-five 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 thirty-seven is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixty-four is thirty-one, thirty-one is ten, ten is three, three is five, five is four, four is magic. Nine quintillion two hundred twenty-three quadrillion three hundred seventy-two trillion thirty-six billion eight hundred fifty-four million seven hundred seventy-five thousand eight hundred seven is one hundred ninety-six, one hundred ninety-six is twenty-two, twenty-two is ten, ten is three, three is five, five is four, four is magic.

## Perl 6[edit]

Lingua::EN::Numbers::Cardinal module available from the Perl 6 ecosystem.

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;

- 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. Seventy-five is twelve, twelve is six, six is three, three is five, five is four, four is magic. Three hundred thirty-seven is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixty-four is thirty-one, thirty-one is ten, ten is three, three is five, five is four, four is magic. Nine billion eight hundred seventy-six million five hundred forty-three thousand two hundred nine is ninety-seven, ninety-seven is twelve, twelve is six, six is three, three is five, five is four, four is magic. One hundred fifteen quattuorvigintillion seven hundred ninety-two trevigintillion eighty-nine duovigintillion two hundred thirty-seven unvigintillion three hundred sixteen vigintillion one hundred ninety-five novemdecillion four hundred twenty-three octodecillion five hundred seventy septendecillion nine hundred eighty-five sexdecillion eight quindecillion six hundred eighty-seven quattuordecillion nine hundred seven tredecillion eight hundred fifty-three duodecillion two hundred sixty-nine undecillion nine hundred eighty-four decillion six hundred sixty-five nonillion six hundred forty octillion five hundred sixty-four septillion thirty-nine sextillion four hundred fifty-seven quintillion five hundred eighty-four quadrillion seven trillion nine hundred thirteen billion one hundred twenty-nine million six hundred thirty-nine thousand nine hundred thirty-six is eight hundred sixty-nine, eight hundred sixty-nine is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.

## Python[edit]

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.

import random

from collections import OrderedDict

numbers = { # taken from https://en.wikipedia.org/wiki/Names_of_large_numbers#cite_ref-a_14-3

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')

- 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.

## REXX[edit]

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.

/*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.*/

#[email protected].#; 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. */

The **$SPELL#.REX** routine can be found here ───► $SPELL#.REX.

- output when using the default inputs:

Negative one hundred sixty-four is thirty-one, thirty-one 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. Seventy-five 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 thirty-seven is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic. Nine quintillion two hundred twenty-three quadrillion three hundred seventy-two trillion thirty-six billion eight hundred fifty-four million seven hundred seventy-five thousand eight hundred seven is one hundred ninety-six, one hundred ninety-six is twenty-two, twenty-two is ten, ten is three, three is five, five is four, four is magic.

## zkl[edit]

Limitiation: zkl only has 64 bit signed integars.

Uses the nth function from Spelling_of_ordinal_numbers#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,*]

}

foreach n in (T(0,4,6,11,13,75,337,-164,9876543209)){

println(fourIsMagic(n),"\n")

}

- 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. Seventy-five is twelve, twelve is six, six is three, three is five, five is four, four is magic. Three hundred thirty-seven is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic. Negative one hundred sixty-four is thirty-one, thirty-one is ten, ten is three, three is five, five is four, four is magic. Nine billion eight hundred seventy-six million five hundred forty-three thousand two hundred nine is ninety-seven, ninety-seven is twelve, twelve is six, six is three, three is five, five is four, four is magic.