-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#18 Improved roman numerals conversions and validation
- Loading branch information
Showing
14 changed files
with
810 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Mathematicator\Numbers\Converter; | ||
|
||
|
||
use Brick\Math\BigInteger; | ||
use Brick\Math\BigNumber; | ||
use Brick\Math\Exception\RoundingNecessaryException; | ||
use Brick\Math\RoundingMode; | ||
use Mathematicator\Numbers\Exception\NumberFormatException; | ||
use Mathematicator\Numbers\Exception\OutOfSetException; | ||
use Nette\StaticClass; | ||
use Stringable; | ||
|
||
/** | ||
* Convert integer to basic roman numerals (original ancient set) | ||
* | ||
* @see https://en.wikipedia.org/wiki/Roman_numerals | ||
*/ | ||
class IntToRomanBasic | ||
{ | ||
use StaticClass; | ||
|
||
/** | ||
* @param BigNumber|int|string|Stringable $input | ||
* @return string | ||
* @throws OutOfSetException | ||
*/ | ||
public static function convert($input): string | ||
{ | ||
try { | ||
$int = BigInteger::of((string) $input); | ||
} catch (RoundingNecessaryException $e) { | ||
throw new OutOfSetException($input . ' (not integer)'); | ||
} | ||
|
||
if ($int->isLessThan(0)) { | ||
throw new OutOfSetException((string) $input, 'integers >= 0'); | ||
} | ||
|
||
$out = ''; | ||
|
||
$conversionTable = RomanToInt::getConversionTable(0); | ||
|
||
foreach ($conversionTable as $roman => $value) { | ||
$matches = $int->dividedBy($value, RoundingMode::DOWN)->toInt(); | ||
$out .= str_repeat($roman, $matches); | ||
$int = $int->mod($value); | ||
} | ||
|
||
return $out; | ||
} | ||
|
||
|
||
/** | ||
* @param string $romanNumber | ||
* @return BigInteger | ||
* @throws NumberFormatException | ||
*/ | ||
public static function reverse($romanNumber): BigInteger | ||
{ | ||
return RomanToInt::convert($romanNumber); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Mathematicator\Numbers\Converter; | ||
|
||
|
||
use Brick\Math\BigInteger; | ||
use Brick\Math\BigNumber; | ||
use Brick\Math\BigRational; | ||
use Mathematicator\Numbers\Exception\OutOfSetException; | ||
use Nette\StaticClass; | ||
use Stringable; | ||
|
||
/** | ||
* Convert rational number to basic roman fractions (original ancient set) | ||
* | ||
* @see https://en.wikipedia.org/wiki/Roman_numerals | ||
*/ | ||
final class RationalToRoman | ||
{ | ||
use StaticClass; | ||
|
||
/** @var string[] */ | ||
protected static $fractionConversionTable = [ | ||
'1/12' => '·', | ||
'2/12' => '··', | ||
'3/12' => '···', | ||
'4/12' => '····', | ||
'5/12' => '·····', | ||
'6/12' => 'S', | ||
'7/12' => 'S·', | ||
'8/12' => 'S··', | ||
'9/12' => 'S···', | ||
'10/12' => 'S····', | ||
'11/12' => 'S·····', | ||
]; | ||
|
||
|
||
/** | ||
* @param BigNumber|int|string|Stringable $input | ||
* @return string | ||
* @throws OutOfSetException | ||
* @see https://en.wikipedia.org/wiki/Roman_numerals (Fractions) | ||
*/ | ||
public static function convert($input): string | ||
{ | ||
$rationalNumber = BigRational::of((string) $input)->simplified(); | ||
|
||
if ($rationalNumber->isLessThan('1/12')) { | ||
throw new OutOfSetException((string) $input . ' (less than 1/12)', 'integers >= 0, fractions /12'); | ||
} | ||
|
||
$denominatorOriginal = $rationalNumber->getDenominator(); | ||
|
||
if (in_array((string) $denominatorOriginal, ['1', '2', '3', '4', '6', '12'], true) && $denominatorOriginal->isLessThanOrEqualTo(12)) { | ||
$toFinalMultiplier = BigInteger::of(12)->dividedBy($denominatorOriginal)->toInt(); | ||
$numeratorMultiplied = $rationalNumber->getNumerator()->multipliedBy($toFinalMultiplier); | ||
|
||
$intFinal = $numeratorMultiplied->quotient(12); | ||
$numeratorFinal = $numeratorMultiplied->mod(12)->toInt(); | ||
|
||
$out = ''; | ||
|
||
// Integer part | ||
if ($intFinal->isGreaterThan(0)) { | ||
$out .= IntToRoman::convert($intFinal); | ||
} | ||
|
||
|
||
// Fraction part | ||
if ($numeratorFinal > 0) { | ||
$out .= self::$fractionConversionTable["$numeratorFinal/12"]; | ||
} | ||
|
||
return $out; | ||
} else { | ||
throw new OutOfSetException((string) $input); | ||
} | ||
} | ||
} |
Oops, something went wrong.