Page MenuHomeDevCentral

D1599.id4085.diff
No OneTemporary

D1599.id4085.diff

diff --git a/src/Culture/Rome/RomanNumerals.php b/src/Culture/Rome/RomanNumerals.php
new file mode 100644
--- /dev/null
+++ b/src/Culture/Rome/RomanNumerals.php
@@ -0,0 +1,96 @@
+<?php
+declare(strict_types=1);
+
+namespace Keruald\OmniTools\Culture\Rome;
+
+use InvalidArgumentException;
+
+final class RomanNumerals {
+
+ public static function fromHinduArabic (int $number) : string {
+ self::assertStrictlyPositiveNumber($number);
+
+ if ($number > 1000) {
+ return self::computeFromKiloHinduArabic($number);
+ }
+
+ $table = self::getHinduArabicTable();
+
+ return $table[$number] ?? self::computeFromHinduArabic($number);
+ }
+
+ /**
+ * Provides a canonical table with hindu arabic numerals as keys,
+ * and roman numerals as values.
+ */
+ public static function getHinduArabicTable () : array {
+ return [
+ 1 => 'i',
+ 2 => 'ii',
+ 3 => 'iii',
+ 4 => 'iv',
+ 5 => 'v',
+ 6 => 'vi',
+ 7 => 'vii',
+ 8 => 'viii',
+ 9 => 'ix',
+ 10 => 'x',
+ 50 => 'l',
+ 100 => 'c',
+ 500 => 'd',
+ 1000 => 'm',
+ ];
+ }
+
+ private static function getComputeHinduArabicTable () : iterable {
+ // limit => number to subtract (as a [roman, hindu arabic] array)
+ yield 21 => ['x', 10];
+ yield 30 => ['xx', 20];
+ yield 40 => ['xxx', 30];
+ yield 50 => ['xl', 40];
+ yield 60 => ['l', 50];
+ yield 70 => ['lx', 60];
+ yield 80 => ['lxx', 70];
+ yield 90 => ['lxxx', 80];
+ yield 100 => ['xc', 90];
+ yield 200 => ['c', 100];
+ yield 300 => ['cc', 200];
+ yield 400 => ['ccc', 300];
+ yield 500 => ['cd', 400];
+ yield 600 => ['d', 500];
+ yield 700 => ['dc', 600];
+ yield 800 => ['dcc', 700];
+ yield 900 => ['dccc', 800];
+ yield 1000 => ['cm', 900];
+ }
+
+ private static function computeFromHinduArabic (int $number) : string {
+ foreach (self::getComputeHinduArabicTable() as $limit => $term) {
+ if ($number < $limit) {
+ return $term[0] . self::fromHinduArabic($number - $term[1]);
+ }
+ }
+
+ throw new \LogicException("This should be unreachable code.");
+ }
+
+ private static function computeFromKiloHinduArabic (int $number) : string {
+ $thousandAmount = (int)floor($number / 1000);
+ $remainder = $number % 1000;
+
+ $roman = str_repeat('m', $thousandAmount);
+ if ($remainder > 0) {
+ $roman .= self::fromHinduArabic($remainder);
+ }
+
+ return $roman;
+ }
+
+ private static function assertStrictlyPositiveNumber (int $number) : void {
+ if ($number < 1) {
+ throw new InvalidArgumentException(
+ "Can only convert strictly positive numbers"
+ );
+ }
+ }
+}
diff --git a/tests/Culture/Rome/RomanNumeralsTest.php b/tests/Culture/Rome/RomanNumeralsTest.php
new file mode 100644
--- /dev/null
+++ b/tests/Culture/Rome/RomanNumeralsTest.php
@@ -0,0 +1,43 @@
+<?php
+declare(strict_types=1);
+
+namespace Keruald\OmniTools\Tests\Culture\Rome;
+
+use Keruald\OmniTools\Culture\Rome\RomanNumerals;
+use PHPUnit\Framework\TestCase;
+use InvalidArgumentException;
+
+class RomanNumeralsTest extends TestCase {
+
+ /**
+ * @dataProvider provideRomanAndHinduArabicNumerals
+ */
+ public function testFromHindiArabicNumeral (
+ string $roman,
+ int $hinduArabic
+ ) : void {
+ $this->assertEquals(
+ $roman,
+ RomanNumerals::fromHinduArabic($hinduArabic)
+ );
+ }
+
+ public function provideRomanAndHinduArabicNumerals () : iterable {
+ yield ['i', 1];
+ yield ['xi', 11];
+ yield ['xlii', 42];
+ yield ['mcmxcix', 1999];
+ yield ['mm', 2000];
+ }
+
+ public function testFromHindiArabicNumeralWithNegativeNumbers () : void {
+ $this->expectException(InvalidArgumentException::class);
+ RomanNumerals::fromHinduArabic(-1);
+ }
+
+ public function testFromHindiArabicNumeralWithZero () : void {
+ $this->expectException(InvalidArgumentException::class);
+ RomanNumerals::fromHinduArabic(0);
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Dec 21, 01:56 (17 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2308399
Default Alt Text
D1599.id4085.diff (4 KB)

Event Timeline