Task: Four is magic


C++: #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;
}









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