1<?php 2 3namespace Mpdf\Conversion; 4 5/** 6 * @link https://github.com/JeroenDeDauw/RomanNumbers 7 * @license GNU GPL v2+ 8 */ 9class DecToRoman 10{ 11 12 private $symbolMap; 13 14 public function __construct(array $symbolMap = []) 15 { 16 if ($symbolMap !== []) { 17 $this->symbolMap = $symbolMap; 18 } else { 19 $this->symbolMap = [['I', 'V'], ['X', 'L'], ['C', 'D'], ['M']]; 20 } 21 } 22 23 public function convert($number) 24 { 25 $this->ensureNumberIsAnInteger($number); 26 $this->ensureNumberIsWithinBounds($number); 27 28 return $this->constructRomanString($number); 29 } 30 31 private function ensureNumberIsAnInteger($number) 32 { 33 if (!is_int($number)) { 34 throw new \InvalidArgumentException('Can only translate integers to roman'); 35 } 36 } 37 38 private function ensureNumberIsWithinBounds($number) 39 { 40 if ($number < 1) { 41 throw new \OutOfRangeException('Numbers under one cannot be translated to roman'); 42 } 43 44 if ($number > $this->getUpperBound()) { 45 throw new \OutOfBoundsException('The provided number is to big to be fully translated to roman'); 46 } 47 } 48 49 public function getUpperBound() 50 { 51 $symbolGroupCount = count($this->symbolMap); 52 $valueOfOne = pow(10, $symbolGroupCount - 1); 53 54 $hasFiveSymbol = array_key_exists(1, $this->symbolMap[$symbolGroupCount - 1]); 55 56 return $valueOfOne * ($hasFiveSymbol ? 9 : 4) - 1; 57 } 58 59 private function constructRomanString($number) 60 { 61 $romanNumber = ''; 62 63 for ($i = 0; $i < count($this->symbolMap); $i++) { 64 $divisor = pow(10, $i + 1); 65 $remainder = $number % $divisor; 66 $digit = $remainder / pow(10, $i); 67 68 $number -= $remainder; 69 $romanNumber = $this->formatDigit($digit, $i) . $romanNumber; 70 71 if ($number === 0) { 72 break; 73 } 74 } 75 76 return $romanNumber; 77 } 78 79 private function formatDigit($digit, $orderOfMagnitude) 80 { 81 if ($digit === 0) { 82 return ''; 83 } 84 85 if ($digit === 4 || $digit === 9) { 86 return $this->formatFourOrNine($digit, $orderOfMagnitude); 87 } 88 89 $romanNumber = ''; 90 91 if ($digit >= 5) { 92 $digit -= 5; 93 $romanNumber .= $this->getFiveSymbol($orderOfMagnitude); 94 } 95 96 $romanNumber .= $this->formatOneToThree($orderOfMagnitude, $digit); 97 98 return $romanNumber; 99 } 100 101 private function formatFourOrNine($digit, $orderOfMagnitude) 102 { 103 $firstSymbol = $this->getOneSymbol($orderOfMagnitude); 104 $secondSymbol = $digit === 4 105 ? $this->getFiveSymbol($orderOfMagnitude) 106 : $this->getTenSymbol($orderOfMagnitude); 107 108 return $firstSymbol . $secondSymbol; 109 } 110 111 private function formatOneToThree($orderOfMagnitude, $digit) 112 { 113 return str_repeat($this->getOneSymbol($orderOfMagnitude), $digit); 114 } 115 116 private function getOneSymbol($orderOfMagnitude) 117 { 118 return $this->symbolMap[$orderOfMagnitude][0]; 119 } 120 121 private function getFiveSymbol($orderOfMagnitude) 122 { 123 return $this->symbolMap[$orderOfMagnitude][1]; 124 } 125 126 private function getTenSymbol($orderOfMagnitude) 127 { 128 return $this->symbolMap[$orderOfMagnitude + 1][0]; 129 } 130 131} 132