Nathan Fiscaletti

If you can make sense of bad documentation effectively, you're already better than 90% of engineers.

Home

Creating a custom numeric base in PHP

Published May 02, 2019

Recently I wanted to create URL’s that used words instead of integers for specific posts, however, these URL’s needed to conform to three points.

  1. Be completely unique.
  2. Be repeatable with the same seed.
  3. Be reversible back to their seed.

The idea of a numeric base (or “radix”) is pretty simple, you have x number of digits to work with (in the most common base, base-10, you have 10 digits to work with: 0-9.) When you reach x in the current numeral place, that numeral loops back to 0 and the numeral place to the left has 1 added to it. Although my description of a base may not be the best, you can read more about it here: Number Bases - Math is Fun

Most numeric bases up to base-36 use the alphabet to supplement the missing numerals. For example, base-16 uses [0-1][A-F], this way it has the 16 “digits” it requires to create a number in base-16 format.

This is accomplished in PHP using a library I wrote that not only allows for this type of normal “base” expression, but also allows for some more complicated usage that technically speaking go outside of the constraints of a “base”.

Find the library here

General Usage

Using the library is incredibly simple. To create a base simply use the following:

$base5 = new Base(5);

You can then convert to and from the base using ->parse(int) and ->toBase10($parsedValue):

$b5value = $base5->parse(99);
echo '99 in base5 is '.$b5value.PHP_EOL;

echo 'converted back to base-10 is: '.$base5->toBase10($b5value).PHP_EOL;

You can also convert to other bases using the following:

$base2 = new Base(2);
$base3 = new Base(3);
$base5 = new Base(5);

// Convert from base10 to base2
$b2val = $base2->parse(1325);
echo "b10(1325)\t==\tb2($b2val)".PHP_EOL;

// Convert from base2 to base5
$b5val = $base2->convert($b2val, $base5);
echo "b2($b2val)\t==\tb5($b5val)".PHP_EOL;

// Convert from base5 to base3
$b3val = $base5->convert($b5val, $base3);
echo "b5($b5val)\t==\tb3($b3val)".PHP_EOL;

You can also do mathematical expressions with a base fairly easily.

$base2 = new Base(2);
$base2_13 = $base2->parse(13);
$base2_10 = $base2->parse(10);
$base2_2 = $base2->parse(2);

$result = $base2->math(
    '(#a * #b) / #c', 
    [
        'a' => $base2_13, // $base2_13 is 1101
        'b' => $base2_10, // $base2_10 is 1010
        'c' => $base2_2   // $base2_2  is 10
    ]
);

echo 'in base 2:  (' . $base2_13 . ' * ' . $base2_10 . ') / ' . $base2_2 . ' = ' . $result . PHP_EOL;
echo 'in base 10: (13 * 10) / 2 = ' . $base2->toBase10($result) . PHP_EOL;

The output would look something like this:

in base 2:  (1101 * 1010) / 10 = 1000001
in base 10: (13 * 10) / 2 = 65

You can create any base up to base-36, after that you must supply a custom library.

Custom Libraries

So, my thought was to supplement all digits within my base with a library of words taken from a text file. My base would be the number of words stored in the file. If the file had 88 words in it, it would be base-88, however wouldn’t rely on any numbers or the alphabet and instead would use each word from the document as a “digit”.

Let’s say I have the following text document

hello
there
friend

This would be base-3, where hello would represent 0, there, would represent 1, and friend would represent 2.

So, using the following code I could convert a number to base-3 using this special library.

$library = file('words.txt', FILE_IGNORE_NEW_LINES);
$base = new Base(count($library));
$base->setLibrary($library);
echo $base->parse(954);

Which would result in ThereHelloFriendFriendThereHelloHello

You can convert back to base 10 using the ->toBase10 member function of the $base instance.

Using Multiple Libraries

You can also use separate libraries for different places of the resulting number. In this example, I use one library for the 1s place, but another for all other numbers.

$library1 = file("./adjectives.txt", FILE_IGNORE_NEW_LINES);
$library2 = file("./nouns.txt", FILE_IGNORE_NEW_LINES);

$baseCustom = new Base(count($library1));
$baseCustom->setLibrary($library1);

// 0 = 1s place, 1 = 10s place, ...
$baseCustom->putLibrary($library2, 0);

echo $baseCustom->parse(9984134);

Note: When using multiple libraries like this, they must conform to the same base. (Have the same number of “digits” defined in them.)

This “multiple libraries” feature was added specifically because I wanted to be able to have a format of “AdjectiveAdjectiveNoun” for my final URLs.

So, a resulting URL might look something like https://website.com/AbidingEsotericAnthropology instead of https://website.com/99653422

This would allow me to maintain uniqueness, reversibility and repeatability while still having easy to remember URLs. In order to avoid the URLs becoming long strings of words, you would need to have a higher base. For example, I use a base of around 5,000. Meaning I can have up to 25 million (5,000²) combinations before I would even exceed two words.

Constraints

If you use a custom library they are constrained to three things.

  • They must use only lower case entries as the library uses capitalization to denote a new “digit”.
  • Each entry in the list must be unique
  • If you are using multiple libraries for different places in the number, the libraries must be of equal lengths.