diff --git a/composer.json b/composer.json index 0f09d08..be4d09c 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,9 @@ { "minimum-stability": "dev", "prefer-stable": true, + "require": { + "beberlei/assert": "^3.2" + }, "require-dev": { "phpunit/phpunit": "^9.0" }, diff --git a/composer.lock b/composer.lock index 06e4fb5..41e757b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,71 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "73cde71d5e2a36a709acfdfab2ad5dfb", - "packages": [], + "content-hash": "7aa2245203ff9ea2bce7a2e52abd5654", + "packages": [ + { + "name": "beberlei/assert", + "version": "v3.2.7", + "source": { + "type": "git", + "url": "https://github.com/beberlei/assert.git", + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beberlei/assert/zipball/d63a6943fc4fd1a2aedb65994e3548715105abcf", + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "php": "^7" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "*", + "phpstan/phpstan-shim": "*", + "phpunit/phpunit": ">=6.0.0 <8" + }, + "suggest": { + "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" + }, + "type": "library", + "autoload": { + "psr-4": { + "Assert\\": "lib/Assert" + }, + "files": [ + "lib/Assert/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "role": "Lead Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Collaborator" + } + ], + "description": "Thin assertion library for input validation in business models.", + "keywords": [ + "assert", + "assertion", + "validation" + ], + "time": "2019-12-19T17:51:41+00:00" + } + ], "packages-dev": [ { "name": "doctrine/instantiator", diff --git a/src/RomanNumeralsConverter.php b/src/RomanNumeralsConverter.php new file mode 100644 index 0000000..07eabe4 --- /dev/null +++ b/src/RomanNumeralsConverter.php @@ -0,0 +1,50 @@ + 'M', + 900 => 'CM', + 500 => 'D', + 400 => 'CD', + 100 => 'C', + 90 => 'XC', + 50 => 'L', + 40 => 'XL', + 10 => 'X', + 9 => 'IX', + 5 => 'V', + 4 => 'IV', + 3 => 'III', + 2 => 'II', + 1 => 'I', + ]; + + public static function convert(int $input): string + { + Assert::that($input) + ->greaterOrEqualThan(0, 'Cannot convert negative numbers'); + + $letters = ''; + + while ($input > 0) { + foreach (static::$map as $number => $letter) { + if ($input >= $number) { + // Add the appropriate numeral and reduce the value of + // $input accordingly. + $letters .= $letter; + $input = ($input - $number); + break; + } + } + } + + return $letters; + } +} diff --git a/tests/RomanNumeralsConverterTest.php b/tests/RomanNumeralsConverterTest.php new file mode 100644 index 0000000..e3227f9 --- /dev/null +++ b/tests/RomanNumeralsConverterTest.php @@ -0,0 +1,142 @@ + [ + 'number' => 1, + 'expected' => 'I', + ], + 2 => [ + 'number' => 2, + 'expected' => 'II', + ], + 3 => [ + 'number' => 3, + 'expected' => 'III', + ], + 4 => [ + 'number' => 4, + 'expected' => 'IV', + ], + 5 => [ + 'number' => 5, + 'expected' => 'V', + ], + 9 => [ + 'number' => 9, + 'expected' => 'IX', + ], + 10 => [ + 'number' => 10, + 'expected' => 'X', + ], + 15 => [ + 'number' => 15, + 'expected' => 'XV', + ], + 19 => [ + 'number' => 19, + 'expected' => 'XIX', + ], + 20 => [ + 'number' => 20, + 'expected' => 'XX', + ], + 21 => [ + 'number' => 21, + 'expected' => 'XXI', + ], + 40 => [ + 'number' => 40, + 'expected' => 'XL', + ], + 50 => [ + 'number' => 50, + 'expected' => 'L', + ], + 80 => [ + 'number' => 80, + 'expected' => 'LXXX', + ], + 90 => [ + 'number' => 90, + 'expected' => 'XC', + ], + 100 => [ + 'number' => 100, + 'expected' => 'C', + ], + 110 => [ + 'number' => 110, + 'expected' => 'CX', + ], + 400 => [ + 'number' => 400, + 'expected' => 'CD', + ], + 500 => [ + 'number' => 500, + 'expected' => 'D', + ], + 700 => [ + 'number' => 700, + 'expected' => 'DCC', + ], + 900 => [ + 'number' => 900, + 'expected' => 'CM', + ], + 1000 => [ + 'number' => 1000, + 'expected' => 'M', + ], + 1986 => [ + 'number' => 1986, + 'expected' => 'MCMLXXXVI', + ], + 1990 => [ + 'number' => 1990, + 'expected' => 'MCMXC', + ], + 2020 => [ + 'number' => 2020, + 'expected' => 'MMXX', + ], + ]; + } + + /** + * @test + */ + public function it_cannot_convert_negative_numbers(): void + { + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Cannot convert negative numbers'); + + RomanNumeralsConverter::convert(-1); + } +}