Jump to content

Four is magic

From Rosetta Code
Task
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 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



11l

Translation of: Nim
V Small = [‘zero’, ‘one’, ‘two’, ‘three’, ‘four’,
           ‘five’, ‘six’, ‘seven’, ‘eight’, ‘nine’,
           ‘ten’, ‘eleven’, ‘twelve’, ‘thirteen’, ‘fourteen’,
           ‘fifteen’, ‘sixteen’, ‘seventeen’, ‘eighteen’, ‘nineteen’]

V Tens = [‘’, ‘’, ‘twenty’, ‘thirty’, ‘forty’, ‘fifty’, ‘sixty’, ‘seventy’, ‘eighty’, ‘ninety’]

V Illions = [‘’, ‘ thousand’, ‘ million’, ‘ billion’, ‘ trillion’, ‘ quadrillion’, ‘ quintillion’]

F say(Int64 =n) -> String
   V result = ‘’
   I n < 0
      result = ‘negative ’
      n = -n

   I n < 20
      result ‘’= Small[Int(n)]

   E I n < 100
      result ‘’= Tens[Int(n I/ 10)]
      V m = n % 10
      I m != 0
         result ‘’= ‘-’Small[Int(m)]

   E I n < 1000
      result ‘’= Small[Int(n I/ 100)]‘ hundred’
      V m = n % 100
      I m != 0
         result ‘’= ‘ ’say(m)

   E
      V sx = ‘’
      V i = 0
      L n > 0
         V m = n % 1000
         n I/= 1000
         I m != 0
            V ix = say(m)‘’Illions[i]
            I sx.len > 0
               ix ‘’= ‘ ’sx
            sx = ix
         i++
      result ‘’= sx

   R result

F fourIsMagic(=n)
   V s = say(n).capitalize()
   V result = s
   L n != 4
      n = s.len
      s = say(n)
      result ‘’= ‘ is ’s‘, ’s
   R result‘ is magic.’

L(n) [Int64(0), 4, 6, 11, 13, 75, 100, 337, -164, 7FFF'FFFF'FFFF'FFFF]
   print(fourIsMagic(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.
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.

8086 Assembly

puts:		equ	9h	; MS-DOS syscall to print a string
		cpu	8086
		bits	16
		org	100h
section		.text
		;;;	Read number from the MS-DOS command line
		;;;	The task says numbers up to 999999 need to be 
		;;;	supported, so we can't get away with using MUL.
		mov	cl,[80h]	; Is there an argument?
		test	cl,cl
		jnz	havearg
		mov	ah,puts		; If not, print "no input"
		mov	dx,errinput
		int	21h
		ret			; And stop.
havearg:	mov	si,82h		; Start of argument string
		xor	ch,ch		; CX = argument length
		dec	cx		; Minus one (space before argument)
		xor	ax,ax		; Accumulator starts out at 0
		xor	dx,dx
numloop:	mov	bp,ax		; DX:AX *= 10
		mov	di,dx
		add	ax,ax		; ... *2 
		adc	dx,dx		
		add	ax,ax		; ... *4
		adc	dx,dx
		add	ax,bp		; ... *5
		adc	dx,di
		add	ax,ax		; ... *10
		adc	dx,dx
		mov	bx,ax
		lodsb			; Get digit
		sub	al,'0'
		xor	ah,ah
		add	ax,bx		; Add digit
		adc	dx,0
		loop	numloop		; Next digit if there is one
		;;;	DX:AX now contains the binary representation of
		;;;	the decimal input.
		cmp	dx,0Fh		; Check that DX:AX <= 999999
		jb	donum
		cmp	ax,4240h	; 0F4240h = 1000000
		jb	donum
		mov	ah,puts		; Otherwise, print error message
		mov	dx,errhigh
		int	21h
		ret
		;;;	DX:AX = current number
donum:		push	dx		; Keep number
		push	ax 
		mov	di,numstring	; Create the string for the number
		call	cardinal
		mov	[di],byte '$'
		xor	[numstring],byte 32	; Capitalize first letter
.print:		mov	dx,numstring	; Print the string
		mov	ah,puts
		int	21h
		mov	dx,is		; print ' is ',
		int	21h
		pop	ax		; Retrieve number
		pop	dx
		test	dx,dx		; DX:AX = 4 = magic
		jnz	.nomagic	; DX <> 0 = not magic
		cmp	ax,4		; If AX=4 then magic
		je	.magic
.nomagic:	sub	di,numstring	; Calculate length of string
		xor	dx,dx		; Set DX:AX to DI
		mov	ax,di
		push	dx		; Store new number on stack
		push	ax
		mov	di,numstring	; Make string for new number
		call	cardinal
		mov	[di],byte '$'
		mov	dx,numstring	; Print the string
		mov	ah,puts
		int	21h
		mov	dx,commaspace	; Print comma and space
		int	21h
		jmp 	.print		; Then use next number as input
.magic:		mov	dx,magic 	; print "magic.",
		mov	ah,puts
		int	21h
		ret			; and stop
		;;;	Subroutine: assuming 0 <= DX:AX <= 999999, write
		;;;	cardinal representation at ES:DI.
cardinal:	mov	bp,ax
		or	bp,dx
		jz	.zero		; If it is zero, return 'Zero'
		mov	bp,1000		; Otherwise, get 1000s part
		div	bp
		test	ax,ax		; Above 1000?
		jz	.hundreds_dx	; If not, just find hundreds
		push	dx		; Otherwise, save <1000s part,
		call	.hundreds	; get string for how many thousands,
		mov	si,thousand	; Then add ' thousand',
		call	stradd
		pop	dx		; Restore <1000 part,
		test	dx,dx		; Even thousands?
		jnz	.hundreds_spc	; Then add hundreds
		ret			; Otherwise we're done
.hundreds_spc:	mov	al,' '		; Add space betweeen thousand and rest
		stosb
.hundreds_dx:	mov	ax,dx		
.hundreds:	mov	bp,100		; Get hundreds part
		xor	dx,dx
		div	bp		; AX=100s
		test	ax,ax		; If zero, no hundreds
		jz	.tens_dx
		dec	ax		; Otherwise, look up in singles
		shl	ax,1		; table,
		mov	bx,ax
		mov	si,[single+bx]
		call	stradd		; Add to the output string,
		mov	si,hundred	; Add ' hundred',
		call	stradd
		test	dx,dx		; Is there any more?
		jne	.tens_spc	; If so, add tens
		ret			; Otherwise we're done
.tens_spc:	mov	al,' '		; Add space between 'hundred' and tens
		stosb
.tens_dx:	mov	ax,dx		; Tens in AX (from hundreds)
.tens:		aam			; AH=10s digit, AL=1s digit
		test	ah,ah		; If 10s digit is 0, single digit
		jz	.ones
		cmp	ah,1		; If 10s digit is 1, teens
		jz	.teens
		mov	bl,ah		; Look up tens digit in tens table
		sub	bl,2
		shl	bl,1
		xor	bh,bh
		mov	si,[tens+bx]	; Add to the output string
		call	stradd
		test	al,al		; Ones digit left?
		jne	.ones_dash	; If so, add dash and ones digit
		ret			; Otherwise we're done
.ones_dash:	mov	[di],byte '-'
		inc	di
.ones:		mov	bl,al		; Look up ones digit in ones table
		dec	bl
		shl	bl,1
		xor	bh,bh
		mov	si,[single+bx]
		jmp	stradd
.teens:		mov	bl,al		; Look up ones digit in teens table
		shl	bl,1
		xor	bh,bh
		mov	si,[teens+bx]
		jmp	stradd 
.zero:		mov	si,zero
		;;;	Copy $-terminated string at DS:SI to ES:DI, except
		;;;	the terminator.
stradd:		push	ax		; Keep AX register
.loop:		lodsb			; Get byte from DS:SI
		cmp 	al,'$'		; Are we there yet?
		je	.out		; If so, stop
		stosb			; Otherwise, store at ES:DI
		jmp	.loop
.out:		pop	ax
		ret
section		.data
single:		dw	one,two,three,four
		dw	five,six,seven,eight,nine
teens:		dw	ten,eleven,twelve,thirteen,fourteen
		dw	fifteen,sixteen,seventeen,eighteen,nineteen 
tens:		dw	twenty,thirty,forty,fifty
		dw	sixty,seventy,eighty,ninety
zero:		db	'zero$'
one:		db	'one$'
two:		db	'two$'
three:		db	'three$'
four:		db	'four$'
five:		db	'five$'
six:		db	'six$'
seven:		db	'seven$'
eight:		db	'eight$'
nine:		db	'nine$'
ten:		db	'ten$'
eleven:		db	'eleven$'
twelve:		db	'twelve$'
thirteen:	db	'thirteen$'
fourteen:	db	'fourteen$'
fifteen:	db	'fifteen$'
sixteen:	db	'sixteen$'
seventeen:	db	'seventeen$'
eighteen:	db	'eighteen$'
nineteen:	db	'nineteen$'
twenty:		db	'twenty$'
thirty:		db	'thirty$'
forty:		db	'forty$'
fifty:		db	'fifty$'
sixty:		db	'sixty$'
seventy:	db	'seventy$'
eighty:		db	'eighty$'
ninety:		db	'ninety$'
hundred:	db	' hundred$'
thousand:	db	' thousand$'
is:		db	' is $'
magic:		db	'magic.$'
commaspace:	db	', $'
errinput:	db	'No input$'
errhigh:	db	'Max input 999999$'
section		.bss
numstring:	resb	1024
Output:
C:\>magic 0
Zero is four, four is magic.
C:\>magic 1
One is three, three is five, five is four, four is magic.
C:\>magic 2
Two is three, three is five, five is four, four is magic.
C:\>magic 3
Three is five, five is four, four is magic.
C:\>magic 4
Four is magic.
C:\>magic 123
One hundred twenty-three is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.
C:\>magic 123456
One hundred twenty-three thousand four hundred fifty-six is fifty-six, fifty-six is nine, nine is four, four is magic.
C:\>magic 999999
Nine hundred ninety-nine thousand nine-hundred ninety-nine is fifty-eight, fifty-eight is eleven, eleven is six, six is three, three is five, five is four, four is magic.

ALGOL 68

Assumes LONG INT is at least 64 bit, as in ALGOL 68 Genie, if not, adjust the NUMBER mode and TOINT operator to suit or remove the larger test cases.
Uses a modified version of the NAME operator in the Spelling of ordinal numbers task.

BEGIN # construct number names from a number                                  #
    MODE NUMBER = LONG INT;        # adjust to suit, must be at least 64 bits #
    OP TOINT = ( NUMBER n )INT: SHORTEN n;                   # adjust to suit #
    []STRING units
           = ( "one", "two",  "three", "four", "five", "six", "seven", "eight", "nine" );
    []STRING teens
           = ( "ten",     "eleven",  "twelve",    "thirteen", "fourteen"
             , "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
             );
    []STRING tens
           = ( "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" );
    []STRING power suffix
           = ( "thousand", "million", "billion", "trillion", "quadrillion", "quintillion" );
    # returns n converted to a number name, n must be in range 1-99           #
    PROC two digits = ( INT n )STRING:
         IF   n < 10
         THEN units[ n ]
         ELIF n < 20
         THEN teens[ n - 9 ]
         ELIF INT last1 = n MOD 10;
              last1 = 0
         THEN tens[ ( n OVER 10 ) - 1 ]
         ELSE tens[ ( n OVER 10 ) - 1 ] + " " + units[ last1 ]
         FI # two digits # ;
    # returns n converted to a number name, n must be in range 1-999          #
    PROC three digits = ( INT n )STRING:
         IF   n < 100
         THEN two digits( n )
         ELIF STRING hundreds = units[ n OVER 100 ] + " hundred";
              INT    last2 = n MOD 100;
              last2 = 0
         THEN hundreds
         ELSE hundreds + " " + two digits( last2 )
         FI # three digits # ;
    # returns the "name" of n                                                 #
    OP NAME = ( NUMBER n )STRING:
       IF   n < 0 THEN "negative " + NAME - n
       ELIF n = 0 THEN "zero"
       ELSE # have a positive number to name                                  #
            NUMBER   v         := n;
            STRING   result    := "";
            INT      power pos := 0;
            WHILE v /= 0 DO
                IF   INT v999 = TOINT ( v MOD 1000 );
                     v999 /= 0
                THEN STRING component := three digits( v999 );
                     IF power pos > 0 THEN
                         component +:= " " + power suffix[ power pos ]
                     FI;
                     IF result /= "" THEN component +:= " " FI;
                     component +=: result
                FI;
                power pos +:= 1;
                v OVERAB 1000
            OD;
            result
       FI # NAME # ;
    # returns the "x is y, y is z, ... four is magic" sequence derived from n #
    OP MAGIC = ( NUMBER n )STRING:
       BEGIN
          NUMBER   v      := n;
          STRING   result := "";
          WHILE v /= 4 DO
              IF result /= "" THEN result +:= ", " FI;
              STRING v name = NAME v;
              v       := ( UPB v name - LWB v name ) + 1;
              result +:= v name + " is " + NAME v
          OD;
          IF result /= "" THEN result +:= ", " FI;
          result + "four is magic"
       END # MAGIC # ;
    # test cases                                                              #
    []NUMBER t = ( 0, 1, 2, 3, 4, 5, 100, 101, 272, 1701, 1968, 4077, 90 210
                 , - 100 001, 987 654, - NUMBER( 8 007 006 005 004 003 )
                 );
    FOR n FROM LWB t TO UPB t DO
        print( ( whole( t[ n ], -18 ), ": ", MAGIC t[ n ], newline ) )
    OD
END
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
one hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic
one hundred one is fifteen, fifteen is seven, seven is five, five is four, four is magic
two hundred seventy two is twenty three, twenty three is twelve, twelve is six, six is three, three is five, five is four, four is magic
one thousand seven hundred one is thirty, thirty is six, six is three, three is five, five is four, four is magic
one thousand nine hundred sixty eight is thirty seven, thirty seven is twelve, twelve is six, six is three, three is five, five is four, four is magic
four thousand seventy seven is twenty seven, twenty seven is twelve, twelve is six, six is three, three is five, five is four, four is magic
ninety thousand two hundred ten is thirty one, thirty one is ten, ten is three, three is five, five is four, four is magic
negative one hundred thousand one is thirty three, thirty three is twelve, twelve is six, six is three, three is five, five is four, four is magic
nine hundred eighty seven thousand six hundred fifty four is fifty seven, fifty seven is eleven, eleven is six, six is three, three is five, five is four, four is magic
negative eight quadrillion seven trillion six billion five million four thousand three is eighty six, eighty six is ten, ten is three, three is five, five is four, four is magic

APL

Works with: Dyalog APL
magic{
    t20'one' 'two' 'three' 'four' 'five' 'six' 'seven' 'eight' 'nine'
    t20t20,'ten' 'eleven' 'twelve' 'thirteen' 'fourteen' 'fifteen' 'sixteen'
    t20t20,'seventeen' 'eighteen' 'nineteen'
    tens'twenty' 'thirty' 'forty' 'fifty' 'sixty' 'seventy' 'eighty' 'ninety'
    spell{
        =0:'zero'
        {
            =0:''
            <20:⍵t20
            <100:tens[(÷10)-1],((0≠≢r)/'-'),r10|
            <1000:(÷100),' hundred',((0≠≢r)/' '),r100|
            <1e6:(÷1000),' thousand',((0≠≢r)/' '),r1000|
            <1e9:(÷1e6),' million',((0≠≢r)/' '),r1e6|
            <1e12:(÷1e9),' billion',((0≠≢r)/' '),r1e9|
            <1e15:(÷1e12),' trillion',((0≠≢r)/' '),r1e12|
            <1e18:(÷1e15),' quadrillion',((0≠≢r)/' '),r1e15|
            <1e21:(÷1e18),' quintillion',((0≠≢r)/' '),r1e18|
            'Overflow' ⎕SIGNAL 11
        }
    }
    1(819)@1{
        nspell 
        =4:n,' is magic.'
        n,' is ',(spell n),', ',n
    }
}
Output:
      magic 0
Zero is four, four is magic.
      magic 4
Four is magic.
      magic 10
Ten is three, three is five, five is four, four is magic.
      magic 1234567
One million two hundred thirty-four thousand five hundred sixty-seven is sixty-n
      ine, sixty-nine is ten, ten is three, three is five, five is four, four is
       magic.
      magic 2*64
Eighteen quintillion four hundred forty-six quadrillion seven hundred forty-four
       trillion seventy-three billion seven hundred nine million five hundred fi
      fty-one thousand six hundred sixteen is one hundred ninety, one hundred ni
      nety is eighteen, eighteen is eight, eight is five, five is four, four is
      magic.

AppleScript

(* Uses a Foundation number formatter for brevity. *)
use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"

on getNumberFormatter(localeID, numberStyle)
    set formatter to current application's class "NSNumberFormatter"'s new()
    tell formatter to setLocale:(current application's class "NSLocale"'s localeWithLocaleIdentifier:(localeID))
    tell formatter to setNumberStyle:(numberStyle)
    
    return formatter
end getNumberFormatter

on join(listOfText, delimiter)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to delimiter
    set txt to listOfText as text
    set AppleScript's text item delimiters to astid
    
    return txt
end join

on fourIsMagic(n)
    set n to n as number
    if (n is 4) then return "Four is magic."
    
    set formatter to getNumberFormatter("en_US", current application's NSNumberFormatterSpellOutStyle)
    
    set nName to (formatter's stringFromNumber:(n)) as text
    if (nName begins with "minus") then
        set nName to "Negative " & text from word 2 to -1 of nName
    else -- Crude ID-based capitalisation. Good enough for English number names.
        set nName to character id ((id of character 1 of nName) - 32) & text 2 thru -1 of nName
    end if
    
    set output to {}
    repeat until (n is 4)
        set n to (count nName)
        set lenName to (formatter's stringFromNumber:(n)) as text
        set end of output to nName & " is " & lenName
        set nName to lenName
    end repeat
    set end of output to "four is magic."
    
    return join(output, ", ")
end fourIsMagic

local tests, output, n
set tests to {-19, 0, 4, 25, 32, 111, 1.234565789E+9}
set output to {}
repeat with n in tests
    set end of output to fourIsMagic(n)
end repeat
return join(output, linefeed)
Output:
"Negative nineteen is seventeen, seventeen is nine, nine is four, four is magic.
Zero is four, four is magic.
Four is magic.
Twenty-five is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Thirty-two is ten, ten is three, three is five, five is four, four is magic.
One hundred eleven is eighteen, eighteen is eight, eight is five, five is four, four is magic.
One billion two hundred thirty-four million five hundred sixty-five thousand seven hundred eighty-nine is one hundred two, one hundred two is fifteen, fifteen is seven, seven is five, five is four, four is magic."

AutoHotkey

Based on Number names

Four_is_magic(num){
	nubmer := num
	while (num <> 4)
		result .= (res := spell(num)) " is " spell(num := StrLen(res)) ", "
	return PrettyNumber(nubmer) "  " result "four is magic!"
}
Spell(n) { ; recursive function to spell out the name of a max 36 digit integer, after leading 0s removed
	Static p1=" thousand ",p2=" million ",p3=" billion ",p4=" trillion ",p5=" quadrillion ",p6=" quintillion "
		 , p7=" sextillion ",p8=" septillion ",p9=" octillion ",p10=" nonillion ",p11=" decillion "
		 , t2="twenty",t3="thirty",t4="forty",t5="fifty",t6="sixty",t7="seventy",t8="eighty",t9="ninety"
		 , o0="zero",o1="one",o2="two",o3="three",o4="four",o5="five",o6="six",o7="seven",o8="eight"
		 , o9="nine",o10="ten",o11="eleven",o12="twelve",o13="thirteen",o14="fourteen",o15="fifteen"
		 , o16="sixteen",o17="seventeen",o18="eighteen",o19="nineteen"
	If (11 < d := (StrLen(n)-1)//3)		; #of digit groups of 3
		Return "Number too big"
	If (d)								; more than 3 digits
		Return Spell(SubStr(n,1,-3*d)) p%d% ((s:=SubStr(n,1-3*d)) ? ", " Spell(s) : "")
	i := SubStr(n,1,1)
	If (n > 99)							; 3 digits
		Return o%i% " hundred" ((s:=SubStr(n,2)) ? " " Spell(s) : "")
	If (n > 19)							; n = 20..99
		Return t%i% ((o:=SubStr(n,2)) ? "-" o%o% : "")
	Return o%n%							; n = 0..19
}
PrettyNumber(n) { ; inserts thousands separators into a number string 
    Return RegExReplace(n, "\B(?=((\d{3})+$))", ",")
}

Examples:

for i, num in StrSplit("7,54,235,8463,95723,485723,5472539,15750268,853956201,2736452849,94837286837,636478294710", ",")
	result .= Four_is_magic(num) "`n"
MsgBox % result

Outputs:

7  seven is five, five is four, four is magic!
54  fifty-four is ten, ten is three, three is five, five is four, four is magic!
235  two hundred thirty-five is twenty-three, twenty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic!
8,463  eight thousand , four hundred sixty-three is forty-one, forty-one is nine, nine is four, four is magic!
95,723  ninety-five thousand , seven hundred twenty-three is forty-nine, forty-nine is ten, ten is three, three is five, five is four, four is magic!
485,723  four hundred eighty-five thousand , seven hundred twenty-three is sixty-two, sixty-two is nine, nine is four, four is magic!
5,472,539  five million , four hundred seventy-two thousand , five hundred thirty-nine is seventy-five, seventy-five is twelve, twelve is six, six is three, three is five, five is four, four is magic!
15,750,268  fifteen million , seven hundred fifty thousand , two hundred sixty-eight is seventy-two, seventy-two is eleven, eleven is six, six is three, three is five, five is four, four is magic!
853,956,201  eight hundred fifty-three million , nine hundred fifty-six thousand , two hundred one is eighty-five, eighty-five is eleven, eleven is six, six is three, three is five, five is four, four is magic!
2,736,452,849  two billion , seven hundred thirty-six million , four hundred fifty-two thousand , eight hundred forty-nine is one hundred seven, one hundred seven is seventeen, seventeen is nine, nine is four, four is magic!
94,837,286,837  ninety-four billion , eight hundred thirty-seven million , two hundred eighty-six thousand , eight hundred thirty-seven is one hundred nineteen, one hundred nineteen is twenty, twenty is six, six is three, three is five, five is four, four is magic!
636,478,294,710  six hundred thirty-six billion , four hundred seventy-eight million , two hundred ninety-four thousand , seven hundred ten is one hundred twenty-two, one hundred twenty-two is twenty-two, twenty-two is ten, ten is three, three is five, five is four, four is magic!

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," ")
}
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.

C

Library: GLib
#include <stdint.h>
#include <stdio.h>
#include <glib.h>

typedef struct named_number_tag {
    const char* name;
    uint64_t number;
} named_number;

const named_number named_numbers[] = {
    { "hundred", 100 },
    { "thousand", 1000 },
    { "million", 1000000 },
    { "billion", 1000000000 },
    { "trillion", 1000000000000 },
    { "quadrillion", 1000000000000000ULL },
    { "quintillion", 1000000000000000000ULL }
};

const named_number* get_named_number(uint64_t n) {
    const size_t names_len = sizeof(named_numbers)/sizeof(named_number);
    for (size_t i = 0; i + 1 < names_len; ++i) {
        if (n < named_numbers[i + 1].number)
            return &named_numbers[i];
    }
    return &named_numbers[names_len - 1];
}

size_t append_number_name(GString* str, uint64_t n) {
    static const char* small[] = {
        "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
        "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
        "sixteen", "seventeen", "eighteen", "nineteen"
    };
    static const char* tens[] = {
        "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
    };
    size_t len = str->len;
    if (n < 20) {
        g_string_append(str, small[n]);
    }
    else if (n < 100) {
        g_string_append(str, tens[n/10 - 2]);
        if (n % 10 != 0) {
            g_string_append_c(str, '-');
            g_string_append(str, small[n % 10]);
        }
    } else {
        const named_number* num = get_named_number(n);
        uint64_t p = num->number;
        append_number_name(str, n/p);
        g_string_append_c(str, ' ');
        g_string_append(str, num->name);
        if (n % p != 0) {
            g_string_append_c(str, ' ');
            append_number_name(str, n % p);
        }
    }
    return str->len - len;
}

GString* magic(uint64_t n) {
    GString* str = g_string_new(NULL);
    for (unsigned int i = 0; ; ++i) {
        size_t count = append_number_name(str, n);
        if (i == 0)
            str->str[0] = g_ascii_toupper(str->str[0]);
        if (n == 4) {
            g_string_append(str, " is magic.");
            break;
        }
        g_string_append(str, " is ");
        append_number_name(str, count);
        g_string_append(str, ", ");
        n = count;
    }
    return str;
}

void test_magic(uint64_t n) {
    GString* str = magic(n);
    printf("%s\n", str->str);
    g_string_free(str, TRUE);
}

int main() {
    test_magic(5);
    test_magic(13);
    test_magic(78);
    test_magic(797);
    test_magic(2739);
    test_magic(4000);
    test_magic(7893);
    test_magic(93497412);
    test_magic(2673497412U);
    test_magic(10344658531277200972ULL);
    return 0;
}
Output:
Five is four, four is magic.
Thirteen is eight, eight is five, five is four, four is magic.
Seventy-eight is thirteen, thirteen is eight, eight is five, five is four, four is magic.
Seven hundred ninety-seven is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic.
Two thousand seven hundred thirty-nine is thirty-eight, thirty-eight is twelve, twelve is six, six is three, three is five, five is four, four is magic.
Four thousand is thirteen, thirteen is eight, eight is five, five is four, four is magic.
Seven thousand eight hundred ninety-three is forty-one, forty-one is nine, nine is four, four is magic.
Ninety-three million four hundred ninety-seven thousand four hundred twelve is seventy-five, seventy-five is twelve, twelve is six, six is three, three is five, five is four, four is magic.
Two billion six hundred seventy-three million four hundred ninety-seven thousand four hundred twelve is one hundred, one hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Ten quintillion three hundred forty-four quadrillion six hundred fifty-eight trillion five hundred thirty-one billion two hundred seventy-seven million two hundred thousand nine hundred seventy-two is one hundred ninety-seven, one hundred ninety-seven is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.

C++

Negative numbers are not supported.

#include <iostream>
#include <string>
#include <cctype>
#include <cstdint>

typedef std::uint64_t integer;

const char* small[] = {
    "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
    "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
    "sixteen", "seventeen", "eighteen", "nineteen"
};

const char* tens[] = {
    "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
};

struct named_number {
    const char* name_;
    integer number_;
};

const named_number named_numbers[] = {
    { "hundred", 100 },
    { "thousand", 1000 },
    { "million", 1000000 },
    { "billion", 1000000000 },
    { "trillion", 1000000000000 },
    { "quadrillion", 1000000000000000ULL },
    { "quintillion", 1000000000000000000ULL }
};

const named_number& get_named_number(integer n) {
    constexpr size_t names_len = std::size(named_numbers);
    for (size_t i = 0; i + 1 < names_len; ++i) {
        if (n < named_numbers[i + 1].number_)
            return named_numbers[i];
    }
    return named_numbers[names_len - 1];
}

std::string cardinal(integer n) {
    std::string result;
    if (n < 20)
        result = small[n];
    else if (n < 100) {
        result = tens[n/10 - 2];
        if (n % 10 != 0) {
            result += "-";
            result += small[n % 10];
        }
    } else {
        const named_number& num = get_named_number(n);
        integer p = num.number_;
        result = cardinal(n/p);
        result += " ";
        result += num.name_;
        if (n % p != 0) {
            result += " ";
            result += cardinal(n % p);
        }
    }
    return result;
}

inline char uppercase(char ch) {
    return static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
}

std::string magic(integer n) {
    std::string result;
    for (unsigned int i = 0; ; ++i) {
        std::string text(cardinal(n));
        if (i == 0)
            text[0] = uppercase(text[0]);
        result += text;
        if (n == 4) {
            result += " is magic.";
            break;
        }
        integer len = text.length();
        result += " is ";
        result += cardinal(len);
        result += ", ";
        n = len;
    }
    return result;
}

void test_magic(integer n) {
    std::cout << magic(n) << '\n';
}

int main() {
    test_magic(5);
    test_magic(13);
    test_magic(78);
    test_magic(797);
    test_magic(2739);
    test_magic(4000);
    test_magic(7893);
    test_magic(93497412);
    test_magic(2673497412U);
    test_magic(10344658531277200972ULL);
    return 0;
}
Output:
Five is four, four is magic.
Thirteen is eight, eight is five, five is four, four is magic.
Seventy-eight is thirteen, thirteen is eight, eight is five, five is four, four is magic.
Seven hundred ninety-seven is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic.
Two thousand seven hundred thirty-nine is thirty-eight, thirty-eight is twelve, twelve is six, six is three, three is five, five is four, four is magic.
Four thousand is thirteen, thirteen is eight, eight is five, five is four, four is magic.
Seven thousand eight hundred ninety-three is forty-one, forty-one is nine, nine is four, four is magic.
Ninety-three million four hundred ninety-seven thousand four hundred twelve is seventy-five, seventy-five is twelve, twelve is six, six is three, three is five, five is four, four is magic.
Two billion six hundred seventy-three million four hundred ninety-seven thousand four hundred twelve is one hundred, one hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Ten quintillion three hundred forty-four quadrillion six hundred fifty-eight trillion five hundred thirty-one billion two hundred seventy-seven million two hundred thousand nine hundred seventy-two is one hundred ninety-seven, one hundred ninety-seven is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.

Clojure

Translation of: UNIX Shell
(require '[clojure.edn :as edn])
(def names { 0 "zero"      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"     1000000          "million"      1000000000 "billion"
            1000000000000 "trillion"     1000000000000000 "quadrillion"
            1000000000000000000          "quintillion" })

(def powers-of-10 (reverse (sort (filter #(clojure.string/ends-with? (str %) "00") (keys names)))))

(defn name-of [n]
  (let [p (first (filter #(>= n %) powers-of-10))]
    (cond
      (not (nil? p))
        (let [quotient  (quot n p)
              remainder (rem  n p)]
              (str (name-of quotient) " " (names p) (if (> remainder 0) (str " " (name-of remainder)))))

      (and (nil? p) (> n 20))
        (let [remainder  (rem n 10)
              tens       (- n remainder)]
              (str (names tens) (if (> remainder 0) (str " " (name-of remainder)))))

      true
        (names n))))

(defn four-is-magic
  ([n] (four-is-magic n ""))
  ([n prefix]
   (let [name ((if (empty? prefix) clojure.string/capitalize identity) (name-of n))
         new-prefix (str prefix (if (not (empty? prefix)) ", "))]
   (if (= n 4)
    (str new-prefix name " is magic.")
    (let [len (count name)]
      (four-is-magic len (str new-prefix name " is " (name-of len))))))))

(defn report [n]
  (println (str n ": " (four-is-magic n))))

(defn -main [& args]
  (doall (map (comp report edn/read-string) args)))


(if (not= "repl" *command-line-args*)
  (apply -main *command-line-args*))
Output:
$ clj four-is-magic.clj 3252003274489856000 1114111 42 23 {0..9}
3252003274489856000: Three quintillion two hundred fifty two quadrillion three trillion two hundred seventy four billion four hundred eighty nine million eight hundred fifty six thousand is one hundred sixty five, one hundred sixty five is twenty two, twenty two is ten, ten is three, three is five, five is four, four is magic.
1114111: One million one hundred fourteen thousand one hundred eleven is sixty, sixty is five, five is four, four is magic.
42: Forty two is nine, nine is four, four is magic.
23: Twenty three is twelve, twelve is six, six is three, three is five, five 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.

Common Lisp

Same test cases as the AWK sample.

(defun integer-to-text (int)
  (format nil "~@(~A~)" (with-output-to-string (out)
                          (loop for n = int then (length c)
                                for c = (format nil "~R" n)
                                while (/= n 4)
                                do (format out "~A is ~R, " c (length c))
                                finally (format out "four is magic.")))))
(loop for n
       in '( -1 0 1 2 3 4 5 6 7 8 9 11 21 1995 1000000 1234567890 1100100100100 )
       do (print (integer-to-text n)))
Output:
"Minus one is nine, nine 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." 
"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." 
"Eleven is six, six is three, three is five, five is four, four is magic." 
"Twenty-one is ten, ten is three, three is five, five is four, four is magic." 
"One thousand, nine hundred and ninety-five is forty-two, forty-two is nine, nine is four, four is magic." 
"One million is eleven, eleven is six, six is three, three is five, five is four, four is magic." 
"One billion, two hundred and thirty-four million, five hundred and sixty-seven thousand, eight hundred and ninety is one hundred and thirteen, one hundred and thirteen is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic." 
"One trillion, one hundred billion, one hundred million, one hundred thousand, one hundred is eighty-nine, eighty-nine is eleven, eleven is six, six is three, three is five, five is four, four is magic." 

Delphi

program Four_is_magic;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

// https://rosettacode.org/wiki/Number_names#Delphi
const
  smallies: array[1..19] of string = ('one', 'two', 'three', 'four', 'five',
    'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen',
    'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen');
  tens: array[2..9] of string = ('twenty', 'thirty', 'forty', 'fifty', 'sixty',
    'seventy', 'eighty', 'ninety');

function domaxies(number: int64): string;
const
  maxies: array[0..5] of string = (' thousand', ' million', ' billion',
    ' trillion', ' quadrillion', ' quintillion');
begin
  domaxies := '';
  if number >= 0 then
    domaxies := maxies[number];
end;

function doHundreds(number: int64): string;
begin
  Result := '';
  if number > 99 then
  begin
    Result := smallies[number div 100];
    Result := Result + ' hundred';
    number := number mod 100;
    if number > 0 then
      Result := Result + ' and ';
  end;
  if number >= 20 then
  begin
    Result := Result + tens[number div 10];
    number := number mod 10;
    if number > 0 then
      Result := Result + '-';
  end;
  if (0 < number) and (number < 20) then
    Result := Result + smallies[number];
end;

function spell(number: int64): string;
var
  scaleFactor: int64;
  maxieStart, h: int64;
begin
  if number = 0 then
    exit('zero');

  scaleFactor := 1000000000000000000;
  Result := '';
  if number < 0 then
  begin
    number := -number;
    Result := 'negative ';
  end;

  maxieStart := 5;
  if number < 20 then
    exit(smallies[number]);
  while scaleFactor > 0 do
  begin
    if number > scaleFactor then
    begin
      h := number div scaleFactor;
      Result := Result + doHundreds(h) + domaxies(maxieStart);
      number := number mod scaleFactor;
      if number > 0 then
        Result := Result + ', ';
    end;
    scaleFactor := scaleFactor div 1000;
    dec(maxieStart);
  end;
end;
//****************************************************\\

const
  numbers: array of Int64 = [0, 4, 6, 11, 13, 75, 100, 337, -164, int64.MaxValue];

function fourIsMagic(n: int64): string;
var
  s: string;
begin
  s := spell(n);
  s[1] := upcase(s[1]);
  var t := s;

  while n <> 4 do
  begin
    n := s.Length;
    s := spell(n);
    t := t + ' is ' + s + ', ' + s;
  end;
  t := t + ' is magic.';
  exit(t);
end;

begin
//  writeln(spell(4));
  for var n in numbers do
    writeln(fourIsMagic(n));
  readln;
end.
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 and thirty-seven is thirty, thirty is six, six is three, three is five, five is four, four is magic.
Negative one hundred and sixty-four is thirty-five, thirty-five is eleven, eleven is six, six is three, three is five, five is four, four is magic.
Nine quintillion, two hundred and twenty-three quadrillion, three hundred and seventy-two trillion, thirty-six billion, eight hundred and fifty-four million, seven hundred and seventy-five thousand, eight hundred and seven is two hundred and twenty-two, two hundred and twenty-two is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic.

EasyLang

Translation of: Go
small$[] = [ "zero" "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" ]
illions$[] = [ "" " thousand" " million" " billion" " trillion" " quadrillion" " quintillion" ]
func$ say n .
   if n < 0
      t$ = "negative "
      n = -n
   .
   if n < 20
      t$ &= small$[n + 1]
   elif n < 100
      t$ &= tens$[n div 10 + 1]
      s = n mod 10
      if s > 0
         t$ &= "-" & small$[s + 1]
      .
   elif n < 1000
      t$ &= small$[n div 100 + 1] & " hundred"
      s = n mod 100
      if s > 0
         t$ &= " " & say s
      .
   else
      i = 1
      while n > 0
         p = n mod 1000
         n = n div 1000
         if p > 0
            ix$ = say p & illions$[i]
            if sx$ <> ""
               ix$ &= " " & sx$
            .
            sx$ = ix$
         .
         i += 1
      .
      t$ &= sx$
   .
   return t$
.
# 
func$ toupper c$ .
   c = strcode c$
   if c >= 97 and c <= 122
      c$ = strchar (c - 32)
   .
   return c$
.
func$ four_is_magic n .
   s$ = say n
   s$ = toupper substr s$ 1 1 & substr s$ 2 99999
   t$ = s$
   while n <> 4
      n = len s$
      s$ = say n
      t$ &= " is " & s$ & ", " & s$
   .
   t$ &= " is magic."
   return t$
.
for n in [ 6 13 75 111 337 99999999 ]
   print four_is_magic n
.
Output:
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 eleven is eighteen, eighteen is eight, eight 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.
Ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-nine is seventy-eight, seventy-eight is thirteen, thirteen is eight, eight is five, five is four, four is magic.

F#

The Function

//Express an Integer in English Language. Nigel Galloway: September 19th., 2018
let fN=[|[|"";"one";"two";"three";"four";"five";"six";"seven";"eight";"nine"|];
         [|"ten";"eleven";"twelve";"thirteen";"fourteen";"fifteen";"sixteen";"seventeen";"eighteen";"nineteen"|];
         [|"";"";"twenty";"thirty";"fourty";"fifty";"sixty";"seventy";"eighty";"ninety"|]|]
let rec I2α α β=match α with |α when α<20     ->β+fN.[α/10].[α%10]
                             |α when α<100    ->I2α (α%10) (β+fN.[2].[α/10]+if α%10>0 then " " else "")
                             |α when α<1000   ->I2α (α-(α/100)*100) (β+fN.[0].[α/100]+" hunred"+if α%100>0 then " and " else "")
                             |α when α<1000000->I2α (α%1000) (β+(I2α (α/1000) "")+" thousand"+if α%100=0 then "" else if (α-(α/1000)*1000)<100 then " and " else " ")

The Task

let rec printI2α=function |0->printf "naught->"; printI2α 6
                          |4->printfn "four is magic"
                          |n when n<0->let g = I2α -n "minus " in printf "%s->" g; printI2α (g.Length)
                          |n         ->let g = I2α n "" in printf "%s->" g; printI2α (g.Length)
let N=System.Random()
List.init 25 (fun _->N.Next 999999) |> List.iter printI2α
Output:
seven hundred and fifty thousand nine hundred and eighty eight->sixty->five->four is magic
nine hundred and fifty four thousand two hundred and twenty two->sixty one->nine->four is magic
three hundred and seventy two thousand nine hundred and thirty one->sixty four->ten->three->five->four is magic
six hundred and three thousand six hundred and eighteen->fifty three->eleven->six->three->five->four is magic
two hundred and forty nine thousand three hundred and eighty eight->sixty five->ten->three->five->four is magic
four hundred and sixty two thousand four hundred and ninety nine->sixty two->nine->four is magic
six hundred and fifty thousand eight hundred and seventy five->fifty nine->ten->three->five->four is magic
six hundred and ninety three thousand two hundred and seventy nine->sixty four->ten->three->five->four is magic
one hundred and thirty three thousand four hundred and seventy six->sixty four->ten->three->five->four is magic
seven hundred and thirty two thousand nine hundred and fifteen->sixty->five->four is magic
seven hundred and seven thousand five hundred and forty one->fifty eight->eleven->six->three->five->four is magic
twenty five thousand six hundred and two->thirty nine->eleven->six->three->five->four is magic
seven hundred and sixty nine thousand two hundred and sixty four->sixty two->nine->four is magic
eight hundred and ninety five thousand eight hundred and two->fifty eight->eleven->six->three->five->four is magic
four hundred and eleven thousand one hundred and four->fifty one->nine->four is magic
four hundred and ninety five thousand eight hundred and eighty one->sixty four->ten->three->five->four is magic
six hundred and fifty six thousand one hundred and eighty seven->sixty one->nine->four is magic
five hundred and twenty two thousand seven hundred and fifty->fifty eight->eleven->six->three->five->four is magic
three hundred and forty four thousand and ninety two->fifty two->nine->four is magic
three hundred and forty one thousand seven hundred and forty four->sixty five->ten->three->five->four is magic
eight hundred and eighty four thousand two hundred and fifty->fifty eight->eleven->six->three->five->four is magic
six hundred and forty thousand seven hundred and sixteen->fifty five->ten->three->five->four is magic
six hundred and eight thousand three hundred and five->fifty one->nine->four is magic
three hundred and ninety nine thousand two hundred and sixty eight->sixty four->ten->three->five->four is magic
six hundred and ninety two thousand two hundred and seventy five->sixty two->nine->four is magic

Some particular values:

printI2α 0 -> naught->six->three->five->four is magic
printI2α 4 -> four is magic
printI2α 999999 -> nine hundred and ninety nine thousand nine hundred and ninety nine->sixty four->ten->three->five->four is magic
printI2α -23 -> minus twenty three->eighteen->eight->five->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.

USING: ascii formatting io kernel make math.text.english regexp
sequences ;
IN: rosetta-code.four-is-magic

! Strip " and " and "," from the output of Factor's number>text
! word with a regular expression.
: number>english ( n -- str )
    number>text R/ and |,/ "" re-replace ;

! Return the length of the input integer's text form.
! e.g. 1 -> 3
: next-len ( n -- m ) number>english length ;

! Given a starting integer, return the sequence of lengths
! terminating with 4.
! e.g. 1 -> { 1 3 5 4 }
: len-chain ( n -- seq )
    [ [ dup 4 = ] [ dup , next-len ] until , ] { } make ;

! Convert a non-four number to its phrase form.
! e.g. 6 -> "six is three, "
: non-four ( 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." ] [ non-four ] if ;
    
: say-magic ( n -- )
    len-chain [ phrase ] map concat capitalize print ;
    
{ 1 4 -11 100 112719908181724 -612312 } [ say-magic ] each
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 eighty-one thousand seven hundred twenty-four is one hundred forty-three, one hundred forty-three is twenty-three, twenty-three 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 fifty-seven, fifty-seven is eleven, eleven is six, six is three, three is five, five is four, four is magic.

Fortran

MODULE FOUR_IS_MAGIC
    IMPLICIT NONE
    CHARACTER(8), DIMENSION(20) :: SMALL_NUMS
    CHARACTER(7), DIMENSION(8) :: TENS
    CHARACTER(7) :: HUNDRED
    CHARACTER(8) :: THOUSAND
    CHARACTER(8) :: MILLION
    CHARACTER(8) :: BILLION
    CHARACTER(9) :: TRILLION
    CHARACTER(11) :: QUADRILLION
    CHARACTER(11) :: QUINTILLION
    CHARACTER(1) :: SEPARATOR
    CHARACTER(1) :: SPACE


    CONTAINS

    SUBROUTINE INIT_ARRAYS

        SMALL_NUMS(1) = "zero"
        SMALL_NUMS(2) = "one"
        SMALL_NUMS(3) = "two"
        SMALL_NUMS(4) = "three"
        SMALL_NUMS(5) = "four"
        SMALL_NUMS(6) = "five"
        SMALL_NUMS(7) = "six"
        SMALL_NUMS(8) = "seven"
        SMALL_NUMS(9) = "eight"
        SMALL_NUMS(10) = "nine"
        SMALL_NUMS(11) = "ten"
        SMALL_NUMS(12) = "eleven"
        SMALL_NUMS(13) = "twelve"
        SMALL_NUMS(14) = "thirteen"
        SMALL_NUMS(15) = "fourteen"
        SMALL_NUMS(16) = "fifteen"
        SMALL_NUMS(17) = "sixteen"
        SMALL_NUMS(18) = "seventeen"
        SMALL_NUMS(19) = "eighteen"
        SMALL_NUMS(20) = "nineteen"

        TENS(1) = "twenty"
        TENS(2) = "thirty"
        TENS(3) = "forty"
        TENS(4) = "fifty"
        TENS(5) = "sixty"
        TENS(6) = "seventy"
        TENS(7) = "eight"
        TENS(8) = "ninety"

        HUNDRED = "hundred"
        THOUSAND = "thousand"
        MILLION = "million"
        BILLION = "billion"
        TRILLION = "trillion"
        QUADRILLION = "quadrillion"
        QUINTILLION = "quintillion"
        SEPARATOR = "-"
        SPACE = " "

    END SUBROUTINE INIT_ARRAYS

    RECURSIVE FUNCTION STRING_REPRESENTATION(NUM) RESULT(NUM_AS_STR)
        INTEGER(16), INTENT(IN) :: NUM
        CHARACTER(1000) :: NUM_AS_STR
        INTEGER(16), DIMENSION(9) :: COMPONENTS

        CALL INIT_ARRAYS()

        COMPONENTS = GET_COMPONENTS(NUM)

        NUM_AS_STR = TRIM(ADJUSTL(GET_SUBSET(COMPONENTS(9), QUINTILLION)))
        NUM_AS_STR = TRIM(NUM_AS_STR) // SPACE // TRIM(ADJUSTL(GET_SUBSET(COMPONENTS(8), QUADRILLION)))
        NUM_AS_STR = TRIM(NUM_AS_STR) // SPACE // TRIM(ADJUSTL(GET_SUBSET(COMPONENTS(7), TRILLION)))
        NUM_AS_STR = TRIM(NUM_AS_STR) // SPACE // TRIM(ADJUSTL(GET_SUBSET(COMPONENTS(6), BILLION)))
        NUM_AS_STR = TRIM(NUM_AS_STR) // SPACE // TRIM(ADJUSTL(GET_SUBSET(COMPONENTS(5), MILLION)))
        NUM_AS_STR = TRIM(NUM_AS_STR) // SPACE // TRIM(ADJUSTL(GET_SUBSET(COMPONENTS(4), THOUSAND)))
        NUM_AS_STR = TRIM(NUM_AS_STR) // SPACE // TRIM(ADJUSTL(GET_SUBSET(COMPONENTS(3), HUNDRED)))


        IF (COMPONENTS(2) .EQ. 1) THEN
            NUM_AS_STR = TRIM(ADJUSTL(NUM_AS_STR)) // SPACE // TRIM(ADJUSTL(SMALL_NUMS(10 + COMPONENTS(1) + 1)))
        ELSE

            IF (COMPONENTS(1) .GT. 0) THEN
                IF (COMPONENTS(2) .GT. 0) THEN
                    NUM_AS_STR = TRIM(ADJUSTL(NUM_AS_STR)) // SPACE // TRIM(ADJUSTL(TENS(COMPONENTS(2) - 1))) // SEPARATOR
                    NUM_AS_STR = TRIM(ADJUSTL(NUM_AS_STR)) // TRIM(ADJUSTL(SMALL_NUMS(COMPONENTS(1) + 1)))
                ELSE
                    NUM_AS_STR = TRIM(ADJUSTL(NUM_AS_STR)) // SPACE // TRIM(ADJUSTL(SMALL_NUMS(COMPONENTS(1) + 1)))
                ENDIF
            ELSE IF (COMPONENTS(2) .GT. 0) THEN
                NUM_AS_STR = TRIM(ADJUSTL(NUM_AS_STR)) // SPACE // TRIM(ADJUSTL(TENS(COMPONENTS(2) - 1)))
            ENDIF
        ENDIF

    END FUNCTION STRING_REPRESENTATION

    FUNCTION GET_COMPONENTS(NUM)
        INTEGER(16), INTENT(IN) :: NUM
        INTEGER(16), DIMENSION(9) :: GET_COMPONENTS
        INTEGER(16) :: I_UNITS
        INTEGER(16) :: I_TENS
        INTEGER(16) :: I_HUNDREDS
        INTEGER(16) :: I_THOUSANDS
        INTEGER(16) :: I_MILLIONS
        INTEGER(16) :: I_BILLIONS
        INTEGER(16) :: I_TRILLIONS
        INTEGER(16) :: I_QUADRILLIONS
        INTEGER(16) :: I_QUINTILLIONS
        REAL(16) DIVIDE_TEMP

        I_UNITS = NUM

        DIVIDE_TEMP = (I_UNITS - MOD(I_UNITS, 1000000000000000000))/1000000000000000000
        I_QUINTILLIONS = FLOOR(DIVIDE_TEMP)

        IF (I_QUINTILLIONS .NE. 0) THEN
           I_UNITS = I_UNITS - I_QUINTILLIONS*1000000000000000000
        ENDIF


        DIVIDE_TEMP = (I_UNITS - MOD(I_UNITS, 1000000000000000))/1000000000000000
        I_QUADRILLIONS = FLOOR(DIVIDE_TEMP)

        IF (I_QUADRILLIONS .NE. 0) THEN
           I_UNITS = I_UNITS - I_QUADRILLIONS*1000000000000000
        ENDIF

        DIVIDE_TEMP = (I_UNITS - MOD(I_UNITS, 1000000000000))/1000000000000
        I_TRILLIONS = FLOOR(DIVIDE_TEMP)

        IF (I_TRILLIONS .NE. 0) THEN
           I_UNITS = I_UNITS - I_TRILLIONS*1000000000000
        ENDIF

        DIVIDE_TEMP = (I_UNITS - MOD(I_UNITS, 1000000000))/1000000000
        I_BILLIONS = FLOOR(DIVIDE_TEMP)

        IF (I_BILLIONS .NE. 0) THEN
           I_UNITS = I_UNITS - I_BILLIONS*1000000000
        ENDIF

        DIVIDE_TEMP = (I_UNITS - MOD(I_UNITS, 1000000))/1000000
        
        I_MILLIONS = FLOOR(DIVIDE_TEMP)

        IF (I_MILLIONS .NE. 0) THEN
           I_UNITS = I_UNITS - I_MILLIONS*1000000
        ENDIF

        DIVIDE_TEMP = (I_UNITS - MOD(I_UNITS, 1000))/1000

        I_THOUSANDS = FLOOR(DIVIDE_TEMP)

        IF (I_THOUSANDS .NE. 0) THEN
           I_UNITS = I_UNITS - I_THOUSANDS*1000
        ENDIF

        DIVIDE_TEMP = I_UNITS/1E2
        I_HUNDREDS = FLOOR(DIVIDE_TEMP)

        IF (I_HUNDREDS .NE. 0) THEN
           I_UNITS = I_UNITS - I_HUNDREDS*1E2
        ENDIF

        DIVIDE_TEMP = I_UNITS/10.
        I_TENS =  FLOOR(DIVIDE_TEMP)

        IF (I_TENS .NE. 0) THEN
           I_UNITS = I_UNITS - I_TENS*10
        ENDIF

        GET_COMPONENTS(1) = I_UNITS
        GET_COMPONENTS(2) = I_TENS
        GET_COMPONENTS(3) = I_HUNDREDS
        GET_COMPONENTS(4) = I_THOUSANDS
        GET_COMPONENTS(5) = I_MILLIONS
        GET_COMPONENTS(6) = I_BILLIONS
        GET_COMPONENTS(7) = I_TRILLIONS
        GET_COMPONENTS(8) = I_QUADRILLIONS
        GET_COMPONENTS(9) = I_QUINTILLIONS

    END FUNCTION GET_COMPONENTS

    FUNCTION GET_SUBSET(COUNTER, LABEL) RESULT(OUT_STR)
        CHARACTER(*), INTENT(IN) :: LABEL
        INTEGER(16), INTENT(IN) :: COUNTER
        CHARACTER(100) :: OUT_STR

        OUT_STR = ""

        IF (COUNTER .GT. 0) THEN
            IF (COUNTER .LT. 20) THEN
                OUT_STR = SPACE // TRIM(ADJUSTL(SMALL_NUMS(COUNTER + 1)))
            ELSE
                OUT_STR = SPACE // TRIM(ADJUSTL(STRING_REPRESENTATION(COUNTER)))
            ENDIF
            OUT_STR = TRIM(ADJUSTL(OUT_STR)) // SPACE // TRIM(LABEL)
        ENDIF

    END FUNCTION GET_SUBSET


    SUBROUTINE FIND_MAGIC(NUM)
        INTEGER(16), INTENT(IN) :: NUM
        INTEGER(16) :: CURRENT, LEN_SIZE
        CHARACTER(1000) :: CURRENT_STR, CURRENT_STR_LEN
        CHARACTER(1000) :: OUT_STR

        CURRENT = NUM
        OUT_STR = ""

        DO WHILE (CURRENT .NE. 4)
            CURRENT_STR = STRING_REPRESENTATION(CURRENT)
            LEN_SIZE = LEN_TRIM(ADJUSTL(CURRENT_STR))
            CURRENT_STR_LEN = STRING_REPRESENTATION(LEN_SIZE)
            OUT_STR = TRIM(ADJUSTL(OUT_STR)) // SPACE // TRIM(ADJUSTL(CURRENT_STR))
            OUT_STR = TRIM(ADJUSTL(OUT_STR)) // " is " // TRIM(ADJUSTL(CURRENT_STR_LEN)) // ","
            CURRENT = LEN_SIZE
        ENDDO

        WRITE(*,*) TRIM(ADJUSTL(OUT_STR)) // SPACE // "four is magic."
    END SUBROUTINE FIND_MAGIC
        

END MODULE FOUR_IS_MAGIC

PROGRAM TEST_NUM_NAME
        USE FOUR_IS_MAGIC
        IMPLICIT NONE

        INTEGER(2) I
        INTEGER(16), DIMENSION(10) :: TEST_NUMS = (/5, 13, 78, 797, 2739, 4000, 7893, 93497412, 2673497412, 10344658531277200972/)
        CHARACTER(1000) :: NUM_NAME

        DO I=1, SIZE(TEST_NUMS)
            CALL FIND_MAGIC(TEST_NUMS(I))
        ENDDO
END PROGRAM
 five is four, four is magic.
 thirteen is eight, eight is five, five is four, four is magic.
 seventy-eight is thirteen, thirteen is eight, eight is five, five is four, four is magic.
 seven hundred ninety-seven is twenty-six, twenty-six is ten, ten is three, three is five, five is four, four is magic.
 two thousand seven hundred thirty-nine is thirty-eight, thirty-eight is twelve, twelve is six, six is three, three is five, five is four, four is magic.
 four thousand is thirteen, thirteen is eight, eight is five, five is four, four is magic.
 seven thousand eight hundred ninety-three is forty-one, forty-one is nine, nine is four, four is magic.
 ninety-three million four hundred ninety-seven thousand four hundred twelve is seventy-five, seventy-five is twelve, twelve is six, six is three, three is five, five is four, four is magic.
 two billion six hundred seventy-three million four hundred ninety-seven thousand four hundred twelve is one hundred, one hundred is eleven, eleven is six, six is three, three is five, five is four, four is magic.
 ten quintillion three hundred forty-four quadrillion six hundred fifty-eight trillion five hundred thirty-one billion two hundred seventy-seven million two hundred thousand nine hundred seventy-two is one hundred ninety-seven, one hundred ninety-seven is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.

FreeBASIC

Translation of: Phix
#define floor(x) ((x*2.0-0.5) Shr 1)

Dim Shared veintes(1 To 20) As String*9 => _
{"zero", "one", "two", "three", "four", "five", "six", _
"seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", _
"fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"}

Dim Shared decenas(1 To 8) As String*7 => _
{"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"}

Type myorder
    var1 As Double
    var2 As String*8
End Type
Dim Shared orders(1 To 4) As myorder => _
{(10^12,"trillion"), (10^9,"billion"), (10^6,"million"), (10^3,"thousand")}

Function centenas(n As Integer) As String
    If n < 20 Then
        Return veintes((n Mod 20)+1)
    Elseif (n Mod 10) = 0 Then
        Return decenas((floor(n/10) Mod 10)-1)
    End If
    Return decenas((floor(n/10) Mod 10)-1) & "-" & veintes((n Mod 10)+1)
End Function

Function miles(n As Integer) As String
    If n < 100 Then
        Return centenas(n)
    Elseif (n Mod 100) = 0 Then
        Return veintes((floor(n/100) Mod 20)+1) & " centenas"
    End If
    Return veintes((floor(n/100) Mod 20)+1) & " centenas " & centenas(n Mod 100)
End Function

Function triplet(n As Integer) As String
    Dim As Integer order, high, low
    Dim As String nombre, res = ""
    For i As Integer = 1 To Ubound(orders)
        order = orders(i).var1 
        nombre = orders(i).var2
        high = floor(n/order)
        low = (n Mod order)
        If high <> 0 Then res &= miles(high) & " " & nombre
        n = low
        If low = 0 Then Exit For : End If
        If Len(res) And high <> 0 Then res &= " "
    Next i
    If n <> 0 Or res="" Then
        res &= miles(floor(n))
    End If
    Return res
End Function

Function deletrear(n As Integer) As String
    Dim As String res = ""
    If n < 0 Then
        res = "negative "
        n = -n
    End If
    res &= triplet(n)
    
    Return res
End Function

Function fourIsMagic(n As Integer) As String
    Dim As String s = deletrear(n)
    s = Mid(Ucase(s), 1, 1) & Mid(s, 2, Len(s))
    Dim As String t = s
    While n <> 4
        n = Len(s)
        s = deletrear(n)
        t &= " is " & s & ", " & s
    Wend
    t &= " is magic."
    Return t
End Function

Dim As Longint tests(1 To 21) = _
{-21, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, _
34, 123, 456, 1024, 1234, 12345, 123456, 1010101}

For i As Integer = 1 To Ubound(tests)
    Print Using "#######: &"; tests(i); fourIsMagic(tests(i))
Next i
Sleep
Output:
    -21: Negative twenty-one is nineteen, nineteen is eight, eight is five, five is four, four is magic.
     -1: Negative one is twelve, twelve is six, six is three, three is five, five 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.
     12: Twelve is six, six is three, three is five, five is four, four is magic.
     34: Thirty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.
    123: One centenas twenty-three is twenty-five, twenty-five is eleven, eleven is six, six is three, three is five, five is four, four is magic.
    456: Four centenas fifty-six is twenty-three, twenty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic.
   1024: One thousand twenty-four is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.
   1234: One thousand two centenas thirty-four is thirty-seven, thirty-seven is twelve, twelve is six, six is three, three is five, five is four, four is magic.
  12345: Twelve thousand three centenas forty-five is forty-one, forty-one is nine, nine is four, four is magic.
 123456: One centenas twenty-three thousand four centenas fifty-six is fifty-eight, fifty-eight is eleven, eleven is six, six is three, three is five, five is four, four is magic.
1010101: One million ten thousand one centenas one is forty-one, forty-one is nine, nine is four, four is magic.


FutureBasic

include "NSLog.incl"

local fn FourIsMagic( number as CFNumberRef ) as CFStringRef
  CFMutableStringRef    result = fn MutableStringNew
  NumberFormatterRef formatter = fn NumberFormatterWithStyle( NSNumberFormatterSpellOutStyle )
  NumberFormatterSetLocale( formatter, fn LocaleWithIdentifier( @"en_EN" )  )
  CFStringRef numberString = fn NumberFormatterStringFromNumber( formatter, number )
  MutableStringAppendString( result, fn StringCapitalizedString( numberString ) )
  
  while ( fn StringIsEqual( numberString, @"four" ) == NO )
    numberString = fn NumberFormatterStringFromNumber( formatter, fn NumberWithInteger( len(numberString) ) )
    MutableStringAppendString( result, fn StringWithFormat( @" is %@, %@", numberString, numberString ) )
  wend
  MutableStringAppendString( result, @" is magic." )
end fn = result

NSInteger   i
CFNumberRef testInput
CFArrayRef  testNumbers : testNumbers = @[@23, @1000000000, @20140, @100, @130, @151, @-7]

NSLog( @"Outputs 0 through 9:\n" )
for i = 0 to 9
  NSLog( @"%@", fn FourIsMagic( fn NumberWithInteger( i ) ) )
next

NSLog( @"\nOther number tests:\n" )
for testInput in testNumbers
  NSLog( @"%@", fn FourIsMagic( testInput ) )
next

HandleEvents
Output:
Outputs 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.

Other number tests:

Twenty-Three 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 thirty-three, thirty-three 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 Fifty-One is twenty-one, twenty-one is ten, ten is three, three is five, five is four, four is magic.
Minus Seven 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.

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 right-to-left
		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
}
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.

Haskell

Negative numbers are supported.

module Main where

import Data.List (find)
import Data.Char (toUpper)

firstNums :: [String]
firstNums =
  [ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
    "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
  ]

tens :: [String]
tens = ["twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

biggerNumbers :: [(Int, String)]
biggerNumbers =
  [(100, "hundred"), (1000, "thousand"), (1000000, "million"), (1000000000, "billion"), (1000000000000, "trillion")]

cardinal :: Int -> String
cardinal n
  | n' < 20 =
    negText ++ firstNums !! n'
  | n' < 100 =
    negText ++ tens !! (n' `div` 10 - 2) ++ if n' `mod` 10 /= 0 then "-" ++ firstNums !! (n' `mod` 10) else ""
  | otherwise =
    let (num, name) =
          maybe
            (last biggerNumbers)
            fst
            (find (\((num_, _), (num_', _)) -> n' < num_') (zip biggerNumbers (tail biggerNumbers)))
        smallerNum = cardinal (n' `div` num)
     in negText ++ smallerNum ++ " " ++ name ++ if n' `mod` num /= 0 then " " ++ cardinal (n' `mod` num) else ""
  where
    n' = abs n
    negText = if n < 0 then "negative " else ""

capitalized :: String -> String
capitalized (x : xs) = toUpper x : xs
capitalized [] = []

magic :: Int -> String
magic =
  go True
  where
    go first num =
      let cardiNum = cardinal num
       in (if first then capitalized else id) cardiNum ++ " is "
            ++ if num == 4
              then "magic."
              else cardinal (length cardiNum) ++ ", " ++ go False (length cardiNum)

main :: IO ()
main = do
  putStrLn $ magic 3
  putStrLn $ magic 15
  putStrLn $ magic 4
  putStrLn $ magic 10
  putStrLn $ magic 20
  putStrLn $ magic (-13)
  putStrLn $ magic 999999
Output:
Three is five, five is four, four is magic.
Fifteen is seven, seven is five, five is four, four is magic.
Four is magic.
Ten is three, three is five, five is four, four is magic.
Twenty is six, six is three, three is five, five is four, four is magic.
Negative thirteen is seventeen, seventeen is nine, nine is four, four is magic.
Nine hundred ninety-nine thousand nine hundred ninety-nine is fifty-eight, fifty-eight is eleven, eleven is six, six is three, three is 
five, five is four, four is magic.

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

Java

public class FourIsMagic {

    public static void main(String[] args) {
        for ( long n : new long[] {6, 60, 89, 300, 670, 2000, 2467, 20000, 24500,200000, 230000, 246571, 2300000, 2465712, 20000000, 24657123, 230000000, 245000000, -246570000, 123456789712345l, 8777777777777777777L, Long.MAX_VALUE}) {
            String magic = fourIsMagic(n);
            System.out.printf("%d = %s%n", n, toSentence(magic));
        }
    }
    
    private static final String toSentence(String s) {
        return s.substring(0,1).toUpperCase() + s.substring(1) + ".";
    }
    
    private static final String[] nums = new String[] {
            "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", 
            "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
    };
    
    private static final String[] tens = new String[] {"zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};

    private static final String fourIsMagic(long n) {
        if ( n == 4 ) {
            return numToString(n) + " is magic";
        }
        String result = numToString(n);
        return result + " is " + numToString(result.length()) + ", " + fourIsMagic(result.length());
    }
    
    private static final String numToString(long n) {
        if ( n < 0 ) { 
            return "negative " + numToString(-n);
        }
        int index = (int) n;
        if ( n <= 19 ) {
            return nums[index];
        }
        if ( n <= 99 ) {
            return tens[index/10] + (n % 10 > 0 ? " " + numToString(n % 10) : "");
        }
        String label = null;
        long factor = 0;
        if ( n <= 999 ) {
            label = "hundred";
            factor = 100;
        }
        else if ( n <= 999999) {
            label = "thousand";
            factor = 1000;
        }
        else if ( n <= 999999999) {
            label = "million";
            factor = 1000000;
        }
        else if ( n <= 999999999999L) {
            label = "billion";
            factor = 1000000000;
        }
        else if ( n <= 999999999999999L) {
            label = "trillion";
            factor = 1000000000000L;
        }
        else if ( n <= 999999999999999999L) {
            label = "quadrillion";
            factor = 1000000000000000L;
        }
        else {
            label = "quintillion";
            factor = 1000000000000000000L;
        }
        return numToString(n / factor) + " " + label + (n % factor > 0 ? " " + numToString(n % factor ) : "");
    }

}
Output:
6 = Six is three, three is five, five is four, four is magic.
60 = Sixty is five, five is four, four is magic.
89 = Eighty nine is eleven, eleven is six, six is three, three is five, five is four, four is magic.
300 = Three hundred is thirteen, thirteen is eight, eight is five, five is four, four is magic.
670 = Six hundred seventy is nineteen, nineteen is eight, eight is five, five is four, four is magic.
2000 = Two thousand is twelve, twelve is six, six is three, three is five, five is four, four is magic.
2467 = Two thousand four hundred sixty seven is thirty seven, thirty seven is twelve, twelve is six, six is three, three is five, five is four, four is magic.
20000 = Twenty thousand is fifteen, fifteen is seven, seven is five, five is four, four is magic.
24500 = Twenty four thousand five hundred is thirty three, thirty three is twelve, twelve is six, six is three, three is five, five is four, four is magic.
200000 = Two hundred thousand is twenty, twenty is six, six is three, three is five, five is four, four is magic.
230000 = Two hundred thirty thousand is twenty seven, twenty seven is twelve, twelve is six, six is three, three is five, five is four, four is magic.
246571 = Two hundred forty six thousand five hundred seventy one is fifty five, fifty five is ten, ten is three, three is five, five is four, four is magic.
2300000 = Two million three hundred thousand is thirty four, thirty four is eleven, eleven is six, six is three, three is five, five is four, four is magic.
2465712 = Two million four hundred sixty five thousand seven hundred twelve is sixty five, sixty five is ten, ten is three, three is five, five is four, four is magic.
20000000 = Twenty million is fourteen, fourteen is eight, eight is five, five is four, four is magic.
24657123 = Twenty four million six hundred fifty seven thousand one hundred twenty three is seventy seven, seventy seven is thirteen, thirteen is eight, eight is five, five is four, four is magic.
230000000 = Two hundred thirty million is twenty six, twenty six is ten, ten is three, three is five, five is four, four is magic.
245000000 = Two hundred forty five million is thirty, thirty is six, six is three, three is five, five is four, four is magic.
-246570000 = Negative two hundred forty six million five hundred seventy thousand is sixty eight, sixty eight is eleven, eleven is six, six is three, three is five, five is four, four is magic.
123456789712345 = One hundred twenty three trillion four hundred fifty six billion seven hundred eighty nine million seven hundred twelve thousand three hundred forty five is one hundred fifty three, one 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.
8777777777777777777 = Eight quintillion seven hundred seventy seven quadrillion seven hundred seventy seven trillion seven hundred seventy seven billion seven hundred seventy seven million seven hundred seventy seven thousand seven hundred seventy seven is two hundred thirty one, two hundred thirty one is twenty two, twenty two is ten, ten is three, three is five, five is four, four is magic.
9223372036854775807 = 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.

JavaScript

Utilizing the new BigInt type (added in ECMAScript 2019), and akin to the Python example, this implementation supports numbers as high as at least 10^3003, and could support even higher values by adding items to the dictionary.

To test whether a particular JavaScript interpreter implements BigInt, we can evaluate a boolean expression like:

Object.getOwnPropertyNames(this).includes('BigInt')
const reverseOrderedNumberToTextMap = (function () {
    const rawNumberToTextMapping = { // Ported over from the Python solution.
        [1n]: "one",
        [2n]: "two",
        [3n]: "three",
        [4n]: "four",
        [5n]: "five",
        [6n]: "six",
        [7n]: "seven",
        [8n]: "eight",
        [9n]: "nine",
        [10n]: "ten",
        [11n]: "eleven",
        [12n]: "twelve",
        [13n]: "thirteen",
        [14n]: "fourteen",
        [15n]: "fifteen",
        [16n]: "sixteen",
        [17n]: "seventeen",
        [18n]: "eighteen",
        [19n]: "nineteen",
        [20n]: "twenty",
        [30n]: "thirty",
        [40n]: "forty",
        [50n]: "fifty",
        [60n]: "sixty",
        [70n]: "seventy",
        [80n]: "eighty",
        [90n]: "ninety",
        [100n]: "hundred",
        [1000n]: "thousand",
        [10n ** 6n]: "million",
        [10n ** 9n]: "billion",
        [10n ** 12n]: "trillion",
        [10n ** 15n]: "quadrillion",
        [10n ** 18n]: "quintillion",
        [10n ** 21n]: "sextillion",
        [10n ** 24n]: "septillion",
        [10n ** 27n]: "octillion",
        [10n ** 30n]: "nonillion",
        [10n ** 33n]: "decillion",
        [10n ** 36n]: "undecillion",
        [10n ** 39n]: "duodecillion",
        [10n ** 42n]: "tredecillion",
        [10n ** 45n]: "quattuordecillion",
        [10n ** 48n]: "quinquadecillion",
        [10n ** 51n]: "sedecillion",
        [10n ** 54n]: "septendecillion",
        [10n ** 57n]: "octodecillion",
        [10n ** 60n]: "novendecillion",
        [10n ** 63n]: "vigintillion",
        [10n ** 66n]: "unvigintillion",
        [10n ** 69n]: "duovigintillion",
        [10n ** 72n]: "tresvigintillion",
        [10n ** 75n]: "quattuorvigintillion",
        [10n ** 78n]: "quinquavigintillion",
        [10n ** 81n]: "sesvigintillion",
        [10n ** 84n]: "septemvigintillion",
        [10n ** 87n]: "octovigintillion",
        [10n ** 90n]: "novemvigintillion",
        [10n ** 93n]: "trigintillion",
        [10n ** 96n]: "untrigintillion",
        [10n ** 99n]: "duotrigintillion",
        [10n ** 102n]: "trestrigintillion",
        [10n ** 105n]: "quattuortrigintillion",
        [10n ** 108n]: "quinquatrigintillion",
        [10n ** 111n]: "sestrigintillion",
        [10n ** 114n]: "septentrigintillion",
        [10n ** 117n]: "octotrigintillion",
        [10n ** 120n]: "noventrigintillion",
        [10n ** 123n]: "quadragintillion",
        [10n ** 153n]: "quinquagintillion",
        [10n ** 183n]: "sexagintillion",
        [10n ** 213n]: "septuagintillion",
        [10n ** 243n]: "octogintillion",
        [10n ** 273n]: "nonagintillion",
        [10n ** 303n]: "centillion",
        [10n ** 306n]: "uncentillion",
        [10n ** 309n]: "duocentillion",
        [10n ** 312n]: "trescentillion",
        [10n ** 333n]: "decicentillion",
        [10n ** 336n]: "undecicentillion",
        [10n ** 363n]: "viginticentillion",
        [10n ** 366n]: "unviginticentillion",
        [10n ** 393n]: "trigintacentillion",
        [10n ** 423n]: "quadragintacentillion",
        [10n ** 453n]: "quinquagintacentillion",
        [10n ** 483n]: "sexagintacentillion",
        [10n ** 513n]: "septuagintacentillion",
        [10n ** 543n]: "octogintacentillion",
        [10n ** 573n]: "nonagintacentillion",
        [10n ** 603n]: "ducentillion",
        [10n ** 903n]: "trecentillion",
        [10n ** 1203n]: "quadringentillion",
        [10n ** 1503n]: "quingentillion",
        [10n ** 1803n]: "sescentillion",
        [10n ** 2103n]: "septingentillion",
        [10n ** 2403n]: "octingentillion",
        [10n ** 2703n]: "nongentillion",
        [10n ** 3003n]: "millinillion"
    };

    return new Map(Object.entries(rawNumberToTextMapping)
                         .sort((a, b) => BigInt(a[0]) > BigInt(b[0]) ? -1 : 1)
                         .map(numberAndText => [BigInt(numberAndText[0]), numberAndText[1]]));
})();


function getCardinalRepresentation(number)
{
    if (number == 0n)
    {
        return "zero";
    }

    function* generateCardinalRepresentationTokens(number)
    {
        if (number <= 0n)
        {
            yield "negative";
            number *= -1n;
        }

        for (const [currentEntryNumber, currentEntryText] of reverseOrderedNumberToTextMap.entries())
        {
            if (number >= currentEntryNumber)
            {
                if (currentEntryNumber >= 100n)
                {
                    yield* generateCardinalRepresentationTokens(number / currentEntryNumber);
                }

                yield currentEntryText;
                number -= currentEntryNumber;
            }
        }
    }


    return [...generateCardinalRepresentationTokens(number)].join(" ");
}

function* generateFourIsMagicParts(number)
{
    if (typeof number != "bigint")
    {
        number = BigInt(number);
    }

    if (number == 4n)
    {
        yield "four is magic";
    }
    else
    {
        const cardinalRepresentation = getCardinalRepresentation(number);
        yield `${cardinalRepresentation} is ${getCardinalRepresentation(BigInt(cardinalRepresentation.length))}`;
        yield* generateFourIsMagicParts(cardinalRepresentation.length);
    }

}

function capitalizeFirstLetter(str)
{
    return str.replace(/^([a-z])/, chr => chr.toUpperCase());
}

function fourIsMagic(number)
{
    return capitalizeFirstLetter(`${[...generateFourIsMagicParts(number)].join(", ")}.`);
}

[
    0,
    -150,
    210,
    10n ** 2703n + 1225n,
    4,
    -4,
    10n ** 3003n + 42n
].map(fourIsMagic).join("\n\n");
Output:
Zero is four, four is magic.

Negative one hundred fifty is twenty six, twenty six is ten, ten is three, three is five, five is four, four is magic.

Two hundred ninety twenty is twenty five, twenty five is eleven, eleven is six, six is three, three is five, five is four, four is magic.

One nongentillion one thousand two hundred ninety thirty five is sixty one, sixty one is nine, nine is four, four is magic.

Four is magic.

Negative four is thirteen, thirteen is eight, eight is five, five is four, four is magic.

One millinillion forty two is twenty six, twenty six is ten, ten is three, three is five, five is four, four is magic.

jq

Adapted from Wren

Works with: jq

Also works with gojq, the Go implementation of jq

Also works with fq, a Go implementation of a large subset of jq

def small: ["zero", "one", "two", "three", "four", "five", "six",  "seven", "eight", "nine", "ten", "eleven",
            "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"];

def tens: ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];

def illions: ["", " thousand", " million", " billion"," trillion", " quadrillion", " quintillion"];

def say:
  {n: ., t: ""}
  | if .n < 0
    then .t = "negative " | .n = -.n
    else . end
  | if .n < 20
    then .t += small[.n]
    elif .n < 100
    then .t += tens[(.n/10)|floor]
    | .s = .n % 10
    | if (.s > 0) then .t +=  "-" + small[.s] else . end
    elif .n < 1000
    then .t += small[(.n/100)|floor] + " hundred"
    | .s = .n % 100
    | if .s > 0 then .t += " " + (.s|say) else . end
    else .sx = ""
    | .i = 0
    | until(.n == 0;
        .p = .n % 1000
        | .n = (.n / 1000 |floor)
        | if (.p > 0)
          then .ix = (.p|say) + illions[.i]
          | if (.sx != "") then .ix +=  " " + .sx else . end
          | .sx = .ix
          else .
	  end
        | .i += 1 )
   | .t += .sx
   end
   | .t;

def capitalize:
  .[:1] as $x
  | ($x | ascii_upcase) as $X
  | if $x == $X then . else $X + .[1:] end;
  
def fourIsMagic:
  {n: ., s: (say | capitalize)}
  | .t = .s
  | until(.n == 4; 
      .n = (.s|length)
      | .s = (.n | say)
      | .t +=  " is " + .s + ", " + .s )
  | .t + " is magic." ;

(0, 4, 6, 11, 13, 75, 100, 337, -164,  9007199254740991)
| fourIsMagic

Invocation: jq -rn -f four-is-magic.jq

Output:

As for Wren.

Julia

# The num2text routines are from the "Number names" task, updated for Julia 1.0

const stext = ["one", "two", "three", "four", "five",
               "six", "seven", "eight", "nine"]
const teentext = ["eleven", "twelve", "thirteen", "fourteen",
                  "fifteen", "sixteen", "seventeen",
                  "eighteen", "nineteen"]
const tenstext = ["ten", "twenty", "thirty", "forty", "fifty",
                  "sixty", "seventy", "eighty", "ninety"]
const ordstext = ["million", "billion", "trillion",
                  "quadrillion", "quintillion", "sextillion",
                  "septillion", "octillion", "nonillion",
                  "decillion", "undecillion", "duodecillion",
                  "tredecillion", "quattuordecillion", "quindecillion",
                  "sexdecillion", "septendecillion", "octodecillion",
                  "novemdecillion", "vigintillion"]
 
function normalize_digits!(a)
    while  0 < length(a) && a[end] == 0
        pop!(a)
    end
    return length(a)
end
 
function digits2text!(d, use_short_scale=true)
    ndig = normalize_digits!(d)
    0 < ndig || return ""
    if ndig < 7
        s = ""
        if 3 < ndig
            t = digits2text!(d[1:3])
            s = digits2text!(d[4:end])*" thousand"
            0 < length(t) || return s
            if occursin("and", t)
                return s*" "*t
            else
                return s*" and "*t
            end
        end
        if ndig == 3
            s *= stext[pop!(d)]*" hundred"
            ndig = normalize_digits!(d)
            0 < ndig || return s
            s *= " and "
        end
        1 < ndig || return s*stext[pop!(d)]
        j, i = d
        j != 0 || return s*tenstext[i]
        i != 1 || return s*teentext[j]
        return s*tenstext[i]*"-"*stext[j]
    end
    s = digits2text!(d[1:6])
    d = d[7:end]
    dgrp = use_short_scale ? 3 : 6
    ord = 0
    while(dgrp < length(d))
        ord += 1
        t = digits2text!(d[1:dgrp])
        d = d[(dgrp+1):end]
        0 < length(t) || continue
        t = t*" "*ordstext[ord]
        if length(s) == 0
            s = t
        else
            s = t*" "*s
        end
    end
    ord += 1
    t = digits2text!(d)*" "*ordstext[ord]
    0 < length(s) || return t
    return t*" "*s
end
 
function num2text(n, use_short_scale=true)
    -1 < n || return "minus "*num2text(-n, use_short_scale)
    0 < n || return "zero"
    toobig = use_short_scale ? big(10)^66 : big(10)^126
    n < toobig || return "too big to say"
    return digits2text!(digits(n, base=10), use_short_scale)
end
  
 
function magic(n)
    str = uppercasefirst(num2text(n))
    n = length(str)
    while true
        numtext = num2text(n)
        str *= " is " * numtext
        if numtext == "four"
            break
        end
        str *= ", " * numtext
        n = length(numtext)
    end
    println(str[1:7] == "Four is" ? "Four is magic." : "$str, four is magic.")
end

 
for n in [0, 4, 6, 11, 13, 75, 337, -164, 9_876_543_209]
    magic(n)
end
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 and thirty-seven is thirty, thirty is six, six is three, three is five, five is four, four is magic. Minus one hundred and sixty-four is thirty-two, thirty-two is ten, ten is three, three is five, five is four, four is magic. Nine billion eight hundred and seventy-six million five hundred and forty-three thousand two hundred and nine is one hundred and nine, one hundred and nine is twenty, twenty 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.

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

Lua

-- Four is magic, in Lua, 6/16/2020 db
local oneslist = { [0]="", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }
local teenlist = { [0]="ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }
local tenslist = { [0]="", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }
local lionlist = { [0]="", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion", "septillion", "octillion", "nonillion", "decillion" }
local abs, floor = math.abs, math.floor

local function numname(num)
  if (num == 0) then return "zero" end
  local absnum, lion, result = abs(num), 0, ""
  local function dashed(s) return s=="" and s or "-"..s end
  local function spaced(s) return s=="" and s or " "..s end
  while (absnum > 0) do
    local word, ones, tens, huns = "", absnum%10, floor(absnum/10)%10, floor(absnum/100)%10
    if (tens==0) then word = oneslist[ones]
    elseif (tens==1) then word = teenlist[ones]
    else word = tenslist[tens] .. dashed(oneslist[ones]) end
    if (huns > 0) then word = oneslist[huns] .. " hundred" .. spaced(word) end
    if (word ~= "") then result = word .. spaced(lionlist[lion]) .. spaced(result) end
    absnum = floor(absnum / 1000)
    lion = lion + 1
  end
  if (num < 0) then result = "negative " .. result end
  return result
end

local function fourismagic(num)
  local function fim(num)
    local name = numname(num)
    if (num == 4) then
      return name .. " is magic."
    else
      local what = numname(#name)
      return name .. " is " .. what .. ", " .. fim(#name)
    end
  end
  local result = fim(num):gsub("^%l", string.upper)
  return result
end

local numbers = { -21,-1, 0,1,2,3,4,5,6,7,8,9, 12,34,123,456,1024,1234,12345,123456,1010101 }
for _, num in ipairs(numbers) do
  print(num, fourismagic(num))
end
Output:
-21     Negative twenty-one is nineteen, nineteen is eight, eight is five, five is four, four is magic.
-1      Negative one is twelve, twelve is six, six is three, three is five, five 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.
12      Twelve is six, six is three, three is five, five is four, four is magic.
34      Thirty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.
123     One hundred twenty-three is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.
456     Four hundred fifty-six is twenty-two, twenty-two is ten, ten is three, three is five, five is four, four is magic.
1024    One thousand twenty-four is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic.
1234    One thousand two hundred thirty-four is thirty-six, thirty-six is ten, ten is three, three is five, five is four, four is magic.
12345   Twelve thousand three hundred forty-five is forty, forty is five, five is four, four is magic.
123456  One hundred twenty-three thousand four hundred fifty-six is fifty-six, fifty-six is nine, nine is four, four is magic.
1010101 One million ten thousand one hundred one is forty, forty is five, five is four, four is magic.

M2000 Interpreter

module Four_is_magic {
	numname=lambda ->{
		flush
		data "", "one", "two", "three", "four"
		data "five", "six", "seven", "eight", "nine", "ten"
		data "eleven", "twelve", "thirteen", "fourteen", "fifteen"
		data "sixteen", "seventeen","eighteen", "nineteen"
		dim lows(), tens(), lev()
		lows()=array([])
		data "", "", "twenty", "thirty", "forty","fifty", "sixty"
		data "seventy", "eighty", "ninety"
		tens()=array([])
		lev()=("", "thousand", "million", "billion")
		=lambda lows(), tens(), lev() (n as long) -> {
			if n=0 then ="zero": exit
			long i, tr : boolean t : string ret, prefix
			if n < 0 then prefix= "negative ": n-! else prefix=""
			while n>0 {push n mod 1000: n|div 1000}
			t=stack.size>1
			while not empty {
			 	tripn="" : read tr : if tr=0 then continue
				lt= tr mod 100 : h= tr div 100
				if lt<20 then
					tripn+=lows(lt)
				else
					tripn=tens(lt div 10)+if$(lt mod 10 >0 ->"-"+lows(lt mod 10),"")
				end if
				if h>0 then tripn = lows(h)+" hundred " + tripn
				if empty then if t and h = 0 then tripn = " and " + tripn
				ret+=tripn+" "+lev(stack.size)+" "
			}
			=prefix+trim$(ret)
		}
	}()
	TitleStr=lambda (s as string) ->{
		=ucase$(left$(s,1))+mid$(s, 2)
	}
	magic=lambda numname, TitleStr (n as integer)-> {
		first$=numname(n)
		count$=numname(len(first$))
		while first$<>"four"
			data first$+" is "+count$
			swap first$, count$
			count$=numname(len(first$))
		end while
		data "four is magic."
		=TitleStr(array([])#str$(", "))
	}
	document doc$
	for i=0 to 9
		doc$ = magic(i)+{
		}
	next
	doc$ = magic(23)+{
	}
	doc$ = magic(130)+{
	}
	doc$ = magic(151)+{
	}
	doc$ = magic(-7)+{
	}
	doc$ = magic(20140)+{
	}
	report doc$
	clipboard doc$
}
Four_is_magic
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.
Twenty-three is twelve, twelve 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 fifty-one is twenty-one, twenty-one is ten, ten is three, three is five, five is four, four is magic.
Negative seven is fourteen, fourteen is eight, eight is five, five is four, four is magic.
Twenty thousand one hundred forty is thirty-three, thirty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic.

Mathematica /Wolfram Language

Define a simple function which generates the output, using FixedPointList to iterate until a magic number is reached.

magic[num_] := Capitalize[ StringRiffle[ Partition[
    FixedPointList[IntegerName[StringLength[#], "Cardinal"] &, IntegerName[num, "Cardinal"]], 
    2, 1] /. {n_, n_} :> {n, "magic"}, ", ", " is "] <> "."]

Call the function a few times to show the expected output:

Output:
In[1]:= magic[0]
Out[1]= "Zero is four, four is magic."

In[2]:= magic[1]
Out[2]= "One is three, three is five, five is four, four is magic."

In[3]:= magic[10]
Out[3]= "Ten is three, three is five, five is four, four is magic."

In[4]:= magic[999999]
Out[4]= "Nine hundred ninety-nine thousand nine hundred ninety-nine is fifty-eight, fifty-eight is eleven, eleven is six, six is three, three is five, five is four, four is magic."

In[5]:= magic[RandomInteger[10^6]]
Out[5]= "One hundred ninety-four thousand sixty-four is forty-three, forty-three is eleven, eleven is six, six is three, three is five, five is four, four is magic."

Nim

Translation of: Go
import strutils

const

  Small = ["zero",    "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"]

  Illions = ["", " thousand", " million", " billion", " trillion", " quadrillion", " quintillion"]

#---------------------------------------------------------------------------------------------------

func say(n: int64): string =

  var n = n

  if n < 0:
    result = "negative "
    n = -n

  if n < 20:
    result &= Small[n]

  elif n < 100:
    result &= Tens[n div 10]
    let m = n mod 10
    if m != 0: result &= '-' & Small[m]

  elif n < 1000:
    result &= Small[n div 100] & " hundred"
    let m = n mod 100
    if m != 0: result &= ' ' & m.say()

  else:
    # Work from right to left.
    var sx = ""
    var i = 0
    while n > 0:
      let m = n mod 1000
      n = n div 1000
      if m != 0:
        var ix = m.say() & Illions[i]
        if sx.len > 0: ix &= " " & sx
        sx = ix
      inc i
    result &= sx

#---------------------------------------------------------------------------------------------------

func fourIsMagic(n: int64): string =
  var n = n
  var s = n.say().capitalizeAscii()
  result = s
  while n != 4:
    n = s.len.int64
    s = n.say()
    result &= " is " & s & ", " & s
  result &= " is magic."


#———————————————————————————————————————————————————————————————————————————————————————————————————

for n in [int64 0, 4, 6, 11, 13, 75, 100, 337, -164, int64.high]:
  echo fourIsMagic(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.
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

Translation of: Raku
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;
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.

Phix

Note that on 32-bit Phix integers/atoms are only accurate to 9,007,199,254,740,992 (a hardware limit of 64-bit floating point registers) and on 64-bit the limit is 18,446,744,073,709,551,616 (ditto 80-bit floating points) so if you need more than that this will need to be reworked to use gmp or similar.

with javascript_semantics
--<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)
    string res = ""
    for i=1 to length(orders) do
        {atom order, string name} = orders[i]
        atom 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
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.
Twenty-three 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 thirty-three, thirty-three 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 fifty-one is twenty-one, twenty-one is ten, ten is three, three is five, five is four, four is magic.
Nine hundred ninety-nine thousand nine hundred ninety-nine is fifty-eight, fifty-eight 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.

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. 

q

C:``one`two`three`four`five`six`seven`eight`nine`ten,
  `eleven`twelve`thirteen`fourteen`fifteen`sixteen`seventeen`eighteen`nineteen                             / cardinal numbers <20

T:``ten`twenty`thirty`forty`fifty`sixty`seventy`eighty`ninety                                              / tens
M:``thousand`million`billion`trillion`quadrillion`quintillion`sextillion`septillion                        / magnitudes

st:{                                                                                                       / stringify <1000
  $[x<20; C x;
    x<100; (T;C)@'10 vs x;
    {C[y],`hundred,$[z=0;`;x z]}[.z.s] . 100 vs x] } 

s:{$[x=0; "zero"; {" "sv string except[;`]raze x{$[x~`;x;x,y]}'M reverse til count x} st each 1000 vs x]}  / stringify

fim:{@[;0;upper],[;"four is magic.\n"] raze 1_{y," is ",x,", "}prior s each(count s@)\[x]}                 / four is magic

1 raze fim each 0 4 8 16 25 89 365 2586 25865 369854 40000000001;                                          / tests
Output:
Zero is four, four is magic.
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.
Forty billion one is seventeen, seventeen is nine, nine is four, four is magic.

Comment

In q the same syntax applies a function to an argument or a list to its indexes. A consequence is that, with the Converge iterator \ the lengths alone form a finite-state machine which can generate the convergence.

q)show sl:count each string C  / string lengths
0 3 3 5 4 4 3 5 5 4 3 6 6 8 8 7 7 9 8 8
q)sl\[18]
18 8 5 4
q)sl\[19]
19 8 5 4

R

# provided by neonira

integerToText <- function(value_n_1) {
  english_words_for_numbers <- list(
    simples = c(
      'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
      'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
    ),
    tens = c('twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'),
    powers = c(
      'hundred'     = 100,
      'thousand'    = 1000,
      'million'     = 1000000,
      'billion'     = 1000000000,
      'trillion'    = 1000000000000,
      'quadrillion' = 1000000000000000,
      'quintillion' = 1000000000000000000
    )
  )

  buildResult <- function(x_s) {
    if (value_n_1 < 0) return(paste('minus', x_s))
    x_s
  }
  withDash <- function(a_s, b_s) paste(a_s, b_s, sep = '-')
  val <- abs(value_n_1)
  if (val < 20L) return(buildResult(english_words_for_numbers$simples[val + 1L]))
  if (val < 100L) {
    tens <- val %/% 10L - 1L
    reminder <- val %% 10L
    if (reminder == 0L) return(buildResult(english_words_for_numbers$ten[tens]))
    return(buildResult(withDash(english_words_for_numbers$ten[tens], Recall(reminder))))
  }

  index <- l <- length(english_words_for_numbers$powers)
  for(power in seq_len(l)) {
    if (val < english_words_for_numbers$powers[power]) {
      index <- power - 1L
      break
    }
  }

  f <- Recall(val %/% english_words_for_numbers$powers[index])
  reminder <- val %% english_words_for_numbers$powers[index]
  if (reminder == 0L) return(buildResult(paste(f, names(english_words_for_numbers$powers)[index])))
  buildResult(paste(f, names(english_words_for_numbers$powers)[index],  Recall(reminder)))
}

magic <- function(value_n_1) {
  text <- vector('character')
  while(TRUE) {
    r <- integerToText(value_n_1)
    nc <- nchar(r)
    complement <- ifelse(value_n_1 == 4L, "is magic", paste("is", integerToText(nc)))
    text[length(text) + 1L] <- paste(r, complement)
    if (value_n_1 == 4L) break
    value_n_1 <- nc
  }

  buildSentence <- function(x_s) paste0(toupper(substr(x_s, 1L, 1L)), substring(x_s, 2L), '.')
  buildSentence(paste(text, collapse = ', '))
}
Output:
0
"Zero is four, four is magic."

199 
"One hundred ninety-nine is twenty-three, twenty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic."

4567
"Four thousand five hundred sixty-seven is thirty-eight, thirty-eight is twelve, twelve is six, six is three, three is five, five is four, four is magic."

Racket

#lang racket

(require rackunit)

(define smalls
  (map symbol->string
       '(zero one two three four five six seven eight nine ten eleven twelve
         thirteen fourteen fifteen sixteen seventeen eighteen nineteen)))
 
(define tens
  (map symbol->string
       '(zero ten twenty thirty forty fifty sixty seventy eighty ninety)))
 
(define larges
  (map symbol->string
       '(thousand million billion trillion quadrillion quintillion sextillion
         septillion octillion nonillion decillion undecillion duodecillion
         tredecillion quattuordecillion quindecillion sexdecillion
         septendecillion octodecillion novemdecillion vigintillion)))
 
(define (number->words n)
  (define (step div suffix separator [subformat number->words])
    (define-values [q r] (quotient/remainder n div))
    (define S (if suffix (~a (subformat q) " " suffix) (subformat q)))
    (if (zero? r) S (~a S separator (number->words r))))
  (cond [(< n 0) (~a "negative " (number->words (- n)))]
        [(< n 20) (list-ref smalls n)]
        [(< n 100) (step 10 #f "-" (curry list-ref tens))]
        [(< n 1000) (step 100 "hundred" " ")]
        [else (let loop ([N 1000000] [D 1000] [unit larges])
                (cond [(null? unit)
                       (error 'number->words "number too big: ~e" n)]
                      [(< n N) (step D (car unit) " ")]
                      [else (loop (* 1000 N) (* 1000 D) (cdr unit))]))]))
 
(define (first-cap s)
  (~a (string-upcase (substring s 0 1)) (substring s 1)))
 
(define (magic word [acc null])
  (if (equal? word "four")
      (string-join (reverse (cons "four is magic." acc)) ", \n")
      (let* ([word-len (string-length word)]
             [words (number->words word-len)])
        (magic words
               (cons (string-append word " is " words) acc)))))

(define (number-magic n) 
  (first-cap (magic (number->words n))))
 
(for ([n (append (range 11)
                 '(-10 23 172 20140 100 130 999999 876000000
                       874143425855745733896030))])
  (displayln n)
  (displayln (number-magic n))
  (newline))
Output:
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.

10
Ten is three, 
three is five, 
five is four, 
four is magic.

-10
Negative ten is twelve, 
twelve is six, 
six is three, 
three is five, 
five is four, 
four is magic.

23
Twenty-three is twelve, 
twelve is six, 
six is three, 
three is five, 
five is four, 
four is magic.

172
One hundred seventy-two is twenty-three, 
twenty-three is twelve, 
twelve is six, 
six is three, 
three is five, 
five is four, 
four is magic.

20140
Twenty thousand one hundred forty is thirty-three, 
thirty-three is twelve, 
twelve is six, 
six is three, 
three is five, 
five is four, 
four is magic.

100
One hundred is eleven, 
eleven is six, 
six is three, 
three is five, 
five is four, 
four is magic.

130
One hundred thirty is eighteen, 
eighteen is eight, 
eight is five, 
five is four, 
four is magic.

999999
Nine hundred ninety-nine thousand nine hundred ninety-nine is fifty-eight, 
fifty-eight is eleven, 
eleven is six, 
six is three, 
three is five, 
five is four, 
four is magic.

876000000
Eight hundred seventy-six million is thirty-three, 
thirty-three is twelve, 
twelve is six, 
six 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.

Raku

(formerly Perl 6)

Works with: Rakudo version 2017.09

Lingua::EN::Numbers module available from the Raku ecosystem.

use Lingua::EN::Numbers; # Version 2.4.0 or higher

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.

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.

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

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.

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)

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.

Ruby

Using a 'refinement' to the Integer class, a way to a way to extend a class locally.

module NumberToWord
  
  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'}.reverse_each.to_h
  
  refine Integer do
    def to_english
      return 'zero' if i.zero?
      words =  self < 0 ? ['negative'] : []
      i = self.abs
      NUMBERS.each do |k, v|
        if k <= i then
          times = i/k
          words << times.to_english if k >= 100
          words << v
          i -= times * k
        end
        return words.join(" ") if i.zero?
      end
    end
  end
    
end
  
using  NumberToWord
  
def magic4(n)
  words = []
  until n == 4
    s = n.to_english
    n = s.size
    words << "#{s} is #{n.to_english}"
  end
  words << "four is magic."
  words.join(", ").capitalize
end
  
[0, 4, 6, 11, 13, 75, 337, -164, 9_876_543_209].each{|n| puts magic4(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.

Rust

fn main() {
    magic(4);
    magic(2_340);
    magic(765_000);
    magic(27_000_001);
    magic(999_123_090);
    magic(239_579_832_723_441);
    magic(std::u64::MAX);
}

fn magic(num: u64) {
    if num == 4 {
        println!("four is magic!");
        println!();
        return;
    }
    let name = number_name(num);
    let len = name.len() as u64;
    print!("{} is {}, ", name, number_name(len));
    magic(len);
}


const LOW: &'static [&'static str] = &[
    "zero", "one", "two", "three", "four", "five",
    "six", "seven", "eight","nine", "ten",
    "eleven", "twelve", "thirteen", "fourteen", "fifteen",
    "sixteen", "seventeen", "eighteen", "nineteen"
];
const MED: &'static [&'static str] = &[
    "twenty", "thirty", "forty", "fifty",
    "sixy", "seventy", "eighty", "ninety"
];
const HIGH: &'static [&'static str] = &[
    "thousand", "million", "billion",
    "trillion", "quadrillion", "quintillion"
];

fn number_name(num: u64) -> String {
    if num < 20 {
        return LOW[num as usize].to_string();
    }
    if num < 100 {
        let index = ((num / 10) - 2) as usize;
        let tens = MED[index].to_string();
        let remainder = num % 10;
        if remainder > 0 {
            return format!("{}-{}", tens, number_name(remainder));
        }
        return tens;
    }
    if num < 1000 {
        let hundreds = LOW[(num / 100) as usize];
        let remainder = num % 100;
        if remainder > 0 {
            return format!("{} hundred {}", hundreds, number_name(remainder));
        }
        return format!("{} hundred", hundreds);
    }

    let mut remainder = num % 1000;
    let mut cur = if remainder > 0 { number_name(remainder) } else { "".to_string() };
    let mut n = num / 1000;

    for noun in HIGH.iter() {
        if n > 0 {
            remainder = n % 1000;
            if remainder > 0 {
                // this condition resolves double space issues
                cur =
                    if cur.len() > 0 { format!("{} {} {}", number_name(remainder), noun, cur ) }
                    else { format!("{} {}", number_name(remainder), noun) }
            }
            n /= 1000;
        }
    }
    return cur;
}
Output:
four is magic!

two thousand three hundred forty is thirty-two, thirty-two is ten, ten is three, three is five, five is four, four is magic!

seven hundred sixy-five thousand is thirty-two, thirty-two is ten, ten is three, three is five, five is four, four is magic!

twenty-seven million one is twenty-four, twenty-four is eleven, eleven is six, six is three, three is five, five is four, four is magic!

nine hundred ninety-nine million one hundred twenty-three thousand ninety is seventy-three, seventy-three is thirteen, thirteen is eight, eight is five, five is four, four is magic!

two hundred thirty-nine trillion five hundred seventy-nine billion eight hundred thirty-two million seven hundred twenty-three thousand four hundred forty-one is one hundred fifty-eight, one hundred fifty-eight is twenty-three, twenty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic!

eighteen quintillion four hundred forty-six quadrillion seven hundred forty-four trillion seventy-three billion seven hundred nine million five hundred fifty-one thousand six hundred fifteen is one hundred ninety, one hundred ninety is eighteen, eighteen is eight, eight is five, five is four, four is magic!

Sidef

Translation of: Perl
func cardinal(n) {
    static lingua_en = frequire("Lingua::EN::Numbers")
    lingua_en.num2en(n) - / and|,/g
}

func four_is_magic(n) {
    var str = ""
    loop {
       str += (cardinal(n) + " is ")
       if (n == 4) {
           str += "magic."
           break
       } else {
           n = cardinal(n).len
           str += (cardinal(n) + ", ")
       }
   }
   str.tc
}

[0, 4, 6, 11, 13, 75, 337, -164, 9_876_543_209].each { |n|
    say four_is_magic(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.

Swift

import Foundation

func fourIsMagic(_ number: NSNumber) -> String {
    let formatter = NumberFormatter()
    formatter.numberStyle = .spellOut
    formatter.locale = Locale(identifier: "en_EN")

    var result: [String] = []

    var numberString = formatter.string(from: number)!
    result.append(numberString.capitalized)

    while numberString != "four" {
        numberString = formatter.string(from: NSNumber(value: numberString.count))!
        result.append(contentsOf: [" is ", numberString, ", ", numberString])
    }

    result.append(" is magic.")
    return result.joined()
}

for testInput in [23, 1000000000, 20140, 100, 130, 151, -7] { 
    print(fourIsMagic(testInput as NSNumber))
}
Output:
Twenty-Three 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 thirty-three, thirty-three 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 Fifty-One is twenty-one, twenty-one is ten, ten is three, three is five, five is four, four is magic.
Minus Seven is eleven, eleven is six, six is three, three is five, five is four, four is magic.

UNIX Shell

Works with: Bash version 4+
#!/usr/bin/env bash
name_of() {
  # return the English name for a numeric value
  local -i n=$1

  if (( n < 0 )); then
    printf 'negative %s\n' "$(name_of $(( -n )))"
    return 0
  fi

  # Names for numbers that fit in a bash integer
  local -A names=([0]=zero [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 [1000000]=million [1000000000]=billion
                  [1000000000000]=trillion [1000000000000000]=quadrillion
                  [1000000000000000000]=quintillion)

  # The powers of 10 above 10, in descending order
  local powers_of_10=($(printf '%s\n' "${!names[@]}" | sort -nr | grep '00$'))

  # find the largest power of 10 that is smaller than n
  local -i i=0
  local -i p=${powers_of_10[i]}
  while ((  p > n  && i < ${#powers_of_10[@]}-1 )); do
    i=i+1
    p=${powers_of_10[$i]}
  done

  # if we found one, split on it and construct quotient 'name' remainder
  if (( n >= p )); then
    local -i quotient=n/p
    local -i remainder=n%p
    local remname=
    if (( remainder > 0 )); then
        remname=$(name_of $remainder)
    fi
    printf '%s %s\n' "$(name_of $quotient)" "${names[$p]}${remname:+ $remname}"
  elif (( n > 20 )); then
    # things are a little different under 100, since the multiples of
    # 10 have their own names
    local -i remainder=n%10
    local -i tens=n-remainder
    local remname=
    if (( remainder > 0 )); then
      remname=-$(name_of $remainder)
    fi
    printf '%s\n' "${names[$tens]}${remname:+$remname}"
  else
    printf '%s\n' "${names[$n]}"
  fi

  return 0
}

# Convert numbers into the length of their names 
# Display the series of values in name form until
# the length turns into four; then terminate with "four is magic."
# Again, takes a second argument, this time a prefix, to
# facilitate tail recursion.
four_is_magic() {
  local -i n=$1 
  local prefix=$2
  local name=$(name_of $n)

  # capitalize the first entry
  if [[ -z $prefix ]]; then
    name=${name^}
  fi

  # Stop at 4, otherwise count the length of the name and recurse
  if (( $n == 4 )); then
     printf '%s%s is magic.\n' "${prefix:+$prefix, }" "$name"
  else
     local -i len=${#name}
     four_is_magic "$len" "${prefix:+$prefix, }$name is $(name_of $len)"
  fi
}
Output:
# sadly the number of Rubik's Cube permutations won't fit into a bash int,
# but this is that value minus the first digit (40 quintillion)
$ four_is_magic 3252003274489856000
Three quintillion two hundred fifty-two quadrillion three trillion two hundred seventy-four billion four hundred eighty-nine million eight hundred fifty-six thousand is one hundred sixty-five, one hundred sixty-five is twenty-two, twenty-two is ten, ten is three, three is five, five is four, four is magic.

# Max Unicode code point
$ four_is_magic 1114111
One million one hundred fourteen thousand one hundred eleven is sixty, sixty is five, five is four, four is magic.

# Some usual suspects
$ four_is_magic 42
Forty-two is nine, nine is four, four is magic.

$ four_is_magic 23
Twenty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic.

# the units
for (( i=0; i<10; i++ )); do
  four_is_magic $i
done
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.

V (Vlang)

Translation of: go
import math
fn main() {
	for n in [i64(0), 4, 6, 11, 13, 75, 100, 337, -164,
		math.max_i64,
     ] {
		println(four_is_magic(n))
	}
}
 
fn four_is_magic(nn i64) string {
    mut n := nn
	mut s := say(n)
	s = s[..1].to_upper() + s[1..]
	mut t := s
	for n != 4 {
		n = i64(s.len)
		s = say(n)
		t += " is " + s + ", " + s
	}
	t += " is magic."
	return t
}
 
const small = ["zero", "one", "two", "three", "four", "five", "six",
	"seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen",
	"fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]
const tens = ["", "", "twenty", "thirty", "forty",
	"fifty", "sixty", "seventy", "eighty", "ninety"]
const illions = ["", " thousand", " million", " billion",
	" trillion", " quadrillion", " quintillion"]
 
fn say(nn i64) string {
	mut t := ''
    mut n := nn
	if n < 0 {
		t = "negative "
		// Note, for math.MinInt64 this leaves n negative.
		n = -n
	}
	match true {
	    n < 20 {
            t += small[n]
        }
        n < 100 {
            t += tens[n/10]
            s := n % 10
            if s > 0 {
                t += "-" + small[s]
            }
        }
        n < 1000 {
            t += small[n/100] + " hundred"
            s := n % 100
            if s > 0 {
                t += " " + say(s)
            }
        }
        else {
            // work right-to-left
            mut sx := ""
            for i := 0; n > 0; i++ {
                p := n % 1000
                n /= 1000
                if p > 0 {
                    mut ix := say(p) + illions[i]
                    if sx != "" {
                        ix += " " + sx
                    }
                    sx = ix
                }
            }
		    t += sx
        }
	}
	return t
}
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 quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one is one hundred forty-seven, one hundred forty-seven is twenty-three, twenty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic.

Wren

Translation of: Go
Library: Wren-str

This reuses the say function from the Number names task.

Note that it is not safe to use this script for numbers with an absolute magnitude >= 2^53 as integers cannot be expressed exactly by Wren's Num type beyond that limit.

import "./str" for Str

var small = ["zero", "one", "two", "three", "four", "five", "six",  "seven", "eight", "nine", "ten", "eleven",
             "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]

var tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

var illions = ["", " thousand", " million", " billion"," trillion", " quadrillion", " quintillion"]

var say
say = Fn.new { |n|
    var t = ""
    if (n < 0) {
        t = "negative "
        n = -n
    }
    if (n < 20) {
        t = t + small[n]
    } else if (n < 100) {
        t = t + tens[(n/10).floor]
        var s = n % 10
        if (s > 0) t = t + "-" + small[s]
    } else if (n < 1000) {
        t = t + small[(n/100).floor] + " hundred"
        var s = n % 100
        if (s > 0) t = t + " " + say.call(s)
    } else {
        var sx = ""
        var i = 0
        while (n > 0) {
            var p = n % 1000
            n = (n/1000).floor
            if (p > 0) {
                var ix = say.call(p) + illions[i]
                if (sx != "") ix = ix + " " + sx
                sx = ix
            }
            i = i + 1
        }
        t = t + sx
    }
    return t
}

var fourIsMagic = Fn.new { |n|
    var s = Str.capitalize(say.call(n))
    var t = s
    while (n != 4) {
        n = s.count
        s = say.call(n)
        t = t + " is " + s + ", " + s
    }
    return t + " is magic."
}

for (n in [0, 4, 6, 11, 13, 75, 100, 337, -164,  9007199254740991]) {
    System.print(fourIsMagic.call(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.
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 quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one is one hundred forty-seven, one hundred forty-seven is twenty-three, twenty-three is twelve, twelve is six, six is three, three is five, five is four, four is magic.

zkl

Translation of: Raku

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.
Cookies help us deliver our services. By using our services, you agree to our use of cookies.