PHP Classes

File: Misc/Numeralo.php

Recommend this page to a friend!
  Classes of Stefan Jibrail Froelich  >  PHP Roman Numeral to Number  >  Misc/Numeralo.php  >  Download  
File: Misc/Numeralo.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP Roman Numeral to Number
Convert numbers between Roman and decimal system
Author: By
Last change: fixed issues with floats
Date: 7 years ago
Size: 4,753 bytes
 

Contents

Class file image Download
<?php
/**
 * @package Whisppa
 * @subpackage Misc
 * @license http://opensource.org/licenses/MIT MIT License
 * @author Stefan (frostymarvelous) Froelich <sfroelich@gmail.com>
 * @copyright Copyright (c) 2015, Stefan (frostymarvelous) Froelich
 */
namespace Whisppa\Misc;


/**
 * This class allows you to convert from Roman numerals to natural numbers and vice versa.
 * I decided to do this as a fun challenge after reading http://thedailywtf.com/articles/Roman-Enumeration
 * Took me about 30 minutes to come up with, research and code the solution.
 * It can convert numbers up to 3,999,999 because I couldn't find any numerals for 5,000,000 above.
 * Due to my inability to get the correct accented characters 5000 above, I resulted to using the pipe (|) to represent accent.
 */
class Numeralo
{
   
/**
    * @var string[] A notation map to represent the common Roman numeral values.
    * @static
    */
   
protected static $NOTATION =
    [
       
'|', //one
       
'[', //five
       
']', //ten
   
];
   
   
/**
    * @var \ArrayObject[] A map of Roman numerals based on place value. Each item ends with the first numeral in the next place value.
    * @static
    */
   
protected static $NUMERALS_BY_PLACE_VALUE =
    [
        [
'I', 'V', 'X',], //ones
       
['X', 'L', 'C',], //tens
       
['C', 'D', 'M',], // hundreds
       
['M', 'V|', 'X|',], //thousands
       
['X|', 'L|', 'C|',], //tens of thousands
       
['C|', 'D|', 'M|',], //hundreds of thousands
       
['M|', '~', '~',], // millions. there are no values for the last two that I could find
   
];
   
   
/**
    * @var string[] sA map of numbers and their representative Roman numerals in notation format. This map allows us to make any numeral by replacing the the notation with the place value equivalent.
    * @static
    */
   
protected static $NUMBER_TO_NOTATION =
    [
       
'0' => '',
       
'1' => '|',
       
'2' => '||',
       
'3' => '|||',
       
'4' => '|[',
       
'5' => '[',
       
'6' => '[|',
       
'7' => '[||',
       
'8' => '[|||',
       
'9' => '|]',
    ];
   
   
/**
    * @var int[] A map of the major Roman numerals and the number equivalent.
    * @static
    */
   
protected static $NUMERALS_TO_NUMBER =
    [
       
'I' => 1,
       
'V' => 5,
       
'X' => 10,
       
'L' => 50,
       
'C' => 100,
       
'D' => 500,
       
'M' => 1000,
       
'V|' => 5000,
       
'X|' => 10000,
       
'L|' => 50000,
       
'C|' => 100000,
       
'D|' => 500000,
       
'M|' => 1000000,
    ];
   
   
/**
    * Converts natural numbers to Roman numerals.
    *
    * @static
    * @param int|string $number a number or numeric string less than 3,999,999
    * @throws \InvalidArgumentException if the provided $number argument is not numeric or greater than 3,999,999.
    * @return string Roman numeral equivalent
    */
   
public static function number_to_numerals($number) {
        if(!
is_numeric($number))
            throw new \
InvalidArgumentException('Only numbers allowed');
        if(
$number > 3999999)
            throw new \
InvalidArgumentException('Number cannot be greater than 3,999,999');
       
       
//floats are not supported
       
$number = (int) $number;
       
       
$numerals = '';
       
$number_string = strrev((string) $number);
       
$length = strlen($number_string);
       
        for(
$i = 0; $i < $length; $i++) {
           
$char = $number_string[$i];
           
           
$num_map = self::$NUMERALS_BY_PLACE_VALUE[$i];
           
$numerals = str_replace(self::$NOTATION, $num_map, self::$NUMBER_TO_NOTATION[$char]) . $numerals;
        }
           
        return
$numerals;
    }
   
   
/**
    * Converts Roman numerals to natural numbers.
    *
    * @static
    * @param string $numerals the Roman numerals to be converted
    * @throws \InvalidArgumentException if the provided $numerals argument contains invalid characters.
    * @return int the equivalent number
    */
   
public static function numerals_to_number($numerals) {
       
$number = 0;
       
$numeral_string = strrev((string) $numerals);
       
$length = strlen($numeral_string);
       
       
$prev_number = false;
       
$is_accented = false;
       
        for(
$i = 0; $i < $length; $i++) {
           
$char = $numeral_string[$i];
           
            if(
$char == '|') //check if it is an accent character
           
{
               
$is_accented = true;
                continue;
//skip this iteration and process it in the next one as the accent applies to the next char
           
}
            else if(
$is_accented)
            {
               
$char .= '|';
               
$is_accented = false;
            }
           
           
//TODO Make a check using maybe regex at the beginning of the method.
           
if(!isset(self::$NUMERALS_TO_NUMBER[$char]))
                throw new \
InvalidArgumentException("Invalid character '{$char}' in numeral string");
           
           
           
$num = self::$NUMERALS_TO_NUMBER[$char];
           
           
//this is where the magic happens
            //if the previous number divided by 5 or 10 is equal to the current number, then we subtract eg. 9 = IX. I = 1, X = 10, 10/10 = 1
           
if($prev_number)
            {
                if((
$prev_number / 5) == $num || ($prev_number / 10) == $num)
                   
$number -= $num;
                else
                   
$number += $num;
            }
            else
               
$number += $num;
           
           
           
$prev_number = $num;
        }
       
        return
$number;
       
    }
   
   
}