Spell That Number

Spell That Number

January 13, 2020
Programming

fivethirtyeight.com is Nate Silver’s site of charts and statistics that I visit periodically looking at a certain graph in hope it’s going to change. They also publish occasional riddles and last week’s riddle seemed interesting enough.

Here is the short version: spell a number in English and find its value by adding the value of all letters (a=1, b=2, c=3 and so on) – they call this number the “Gemantria” of the word. Find the largest number that is smaller than its value.

The problem of spelling a number didn’t look complicated but a short Google search gave me only awful implementations so I decided to roll my own. Below is the result:

Spelling a Number #

The function spell takes care of spelling a number. For numbers less than 20 it just takes their names from a table. Numbers up to 100 get their names by combining the tens name (“twenty”, “thirty”, etc.) with the units name. Hundreds get treated the same way, then thousands, millions, billions and so on and so forth.

The function calls itself recursively and in general it’s a nice little programming exercise.

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

void spell (unsigned long long num, char* buffer)
{
  if (num < 20)
  {
    //numbers from zero to 20 are each spelled differently
    const char *nums[] = { 
      "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
      "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
      "sixteen", "seventeen", "eighteen", "nineteen" };
    strcat (buffer, nums[num]);
    return;
  }

  int ilog = (int)log10 ((double)num); //2 for hundreds, 3 for thousands, etc.

  //highest power of 10 smaller than the number
  unsigned long long exp = (unsigned long long) pow (10, ilog);
  if (ilog < 2)
  {
    //numbers from 21 to 99 spelled as "thirty-six"
    const char *tens[] = {
      "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
    strcat (buffer, tens[num / exp - 2]);
    num %= exp;
    if (num)
    {
      strcat (buffer, "-");
      spell (num, buffer);
    }
  }
  else if (ilog < 3)
  {
    //numbers up to 1000 are still special
    //(all the rest get a name for every 3 decimal places) 
    spell (num / exp, buffer);
    strcat (buffer, " hundred");
    num %= exp;
    if (num)
    {
      strcat (buffer, " ");
      spell (num, buffer);
    }
  }
  else
  {
    //names for powers of 1000. You can extend it with names from:
    //https://en.wikipedia.org/wiki/Names_of_large_numbers
    const char *thpows[] = { "thousand", "million", "billion", "trillion" };
    int pwr = ilog / 3 - 1; //index in 'thpows' array

    if (pwr >= _countof (thpows))
    {
      strcpy (buffer, "HUGE!!!");
      return;
    }
    // highest power of 1000 smaller than the number
    exp = (unsigned long long)pow (10, (pwr+1) * 3);
    spell (num / exp, buffer);
    strcat (buffer, " ");
    strcat (buffer, thpows[pwr]);
    num %= exp;
    if (num)
    {
      strcat (buffer, " ");
      spell (num, buffer);
    }
  }
}

//Compute 'Gemantria' value for a string
int gemantria (const char* ptr)
{
  int v = 0;
  int c;
  while (c= tolower(*ptr++))
  {
    if (c > 'a' && c < 'z')
      v += c - 'a' + 1;
  }
  return v;
}

//If invoked without an argument, solves the riddle; otherwise just spells the
//number passed as argument.
int main (int argc, char **argv)
{
  char buffer[1024];
  buffer[0] = 0;
  unsigned long long num = 0;
  int v = 0;
  if (argc > 1)
    num = atoll (argv[1]);
  else
  {
    //large enough starting value
    int i = 100000;
    do
    {
      buffer[0] = 0;
      spell (--i, buffer);
      v = gemantria (buffer);
    } while (i >= v);
    printf ("%d = %s value=%d\n", i, buffer, v);
    return 0;
  }
  spell (num, buffer);
  printf ("%lld = %s", num, buffer);
  return 0;
}

Riddles #

What about the the original question? The answer is 279! Run the program without any arguments to get the answer. It’s larger than 42 but equally important.

To finish, here is another riddle: the alphanumeric value of a number grows much slower than the number itself. Can you find a best fitting function that approximates the alphanumeric value?

Points of Interest #

None whatsoever! As far as I can see there is no practical application of this code. It was however a fun thing to do in a wintry Sunday evening.