Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
53 / 53 |
|
100.00% |
5 / 5 |
CRAP | |
100.00% |
1 / 1 |
Numeric | |
100.00% |
53 / 53 |
|
100.00% |
5 / 5 |
21 | |
100.00% |
1 / 1 |
__construct | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
convertBase | |
100.00% |
27 / 27 |
|
100.00% |
1 / 1 |
7 | |||
arabicToRoman | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
romanToArabic | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
numericToAlpha | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
alphaToNumeric | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Utils\Converter |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Utils\Converter; |
16 | |
17 | /** |
18 | * Numeric converter. |
19 | * |
20 | * @package phpOMS\Utils\Converter |
21 | * @license OMS License 2.0 |
22 | * @link https://jingga.app |
23 | * @since 1.0.0 |
24 | */ |
25 | final class Numeric |
26 | { |
27 | /** |
28 | * Romans association. |
29 | * |
30 | * @var array<string, int> |
31 | * @since 1.0.0 |
32 | */ |
33 | public const ROMANS = [ |
34 | 'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, |
35 | 'XC' => 90, 'L' => 50, 'XL' => 40, 'X' => 10, |
36 | 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1, |
37 | ]; |
38 | |
39 | /** |
40 | * Constructor. |
41 | * |
42 | * @since 1.0.0 |
43 | * @codeCoverageIgnore |
44 | */ |
45 | private function __construct() |
46 | { |
47 | } |
48 | |
49 | /** |
50 | * Convert base. |
51 | * |
52 | * @param string $numberInput Input number |
53 | * @param string $fromBaseInput Input layout (e.g. 0123456789ABCDEF) |
54 | * @param string $toBaseInput Output layout (e.g. 0123456789ABCDEF) |
55 | * |
56 | * @return string |
57 | * |
58 | * @since 1.0.0 |
59 | */ |
60 | public static function convertBase(string $numberInput, string $fromBaseInput, string $toBaseInput) : string |
61 | { |
62 | if ($fromBaseInput === $toBaseInput) { |
63 | return $numberInput; |
64 | } |
65 | |
66 | $fromBase = \str_split($fromBaseInput, 1); |
67 | $toBase = \str_split($toBaseInput, 1); |
68 | $number = \str_split($numberInput, 1); |
69 | $fromLen = \strlen($fromBaseInput); |
70 | $toLen = \strlen($toBaseInput); |
71 | $numberLen = \strlen($numberInput); |
72 | $newOutput = ''; |
73 | |
74 | if ($toBaseInput === '0123456789') { |
75 | $newOutput = '0'; |
76 | |
77 | for ($i = 1; $i <= $numberLen; ++$i) { |
78 | $newOutput = \bcadd( |
79 | $newOutput, |
80 | \bcmul( |
81 | (string) \array_search($number[$i - 1], $fromBase), |
82 | \bcpow((string) $fromLen, (string) ($numberLen - $i)) |
83 | ) |
84 | ); |
85 | } |
86 | |
87 | return $newOutput; |
88 | } |
89 | |
90 | $base10 = (int) ($fromBaseInput !== '0123456789' ? self::convertBase($numberInput, $fromBaseInput, '0123456789') : $numberInput); |
91 | |
92 | if ($base10 < \strlen($toBaseInput)) { |
93 | return $toBase[$base10]; |
94 | } |
95 | |
96 | while ($base10 !== '0') { |
97 | $newOutput = $toBase[(int) \bcmod((string) $base10, (string) $toLen)] . $newOutput; |
98 | $base10 = \bcdiv((string) $base10, (string) $toLen, 0); |
99 | } |
100 | |
101 | return $newOutput; |
102 | } |
103 | |
104 | /** |
105 | * Convert arabic to roman. |
106 | * |
107 | * Be aware that there is no standard for larger roman numbers. |
108 | * |
109 | * @param int $arabic Arabic number |
110 | * |
111 | * @return string |
112 | * |
113 | * @since 1.0.0 |
114 | */ |
115 | public static function arabicToRoman(int $arabic) : string |
116 | { |
117 | $result = ''; |
118 | |
119 | while ($arabic > 0) { |
120 | foreach (self::ROMANS as $rom => $arb) { |
121 | if ($arabic >= $arb) { |
122 | $arabic -= $arb; |
123 | $result .= $rom; |
124 | break; |
125 | } |
126 | } |
127 | } |
128 | |
129 | return $result; |
130 | } |
131 | |
132 | /** |
133 | * Convert roman to arabic. |
134 | * |
135 | * @param string $roman Roman number |
136 | * |
137 | * @return int |
138 | * |
139 | * @since 1.0.0 |
140 | */ |
141 | public static function romanToArabic(string $roman) : int |
142 | { |
143 | $result = 0; |
144 | |
145 | foreach (self::ROMANS as $key => $value) { |
146 | while (\str_starts_with($roman, $key)) { |
147 | $result += $value; |
148 | $temp = \substr($roman, \strlen($key)); |
149 | |
150 | if ($temp !== false) { |
151 | $roman = $temp; |
152 | } |
153 | } |
154 | } |
155 | |
156 | return $result; |
157 | } |
158 | |
159 | /** |
160 | * Convert numeric to alpha. |
161 | * |
162 | * This can be used for alpha lists such as e.g. word uses. |
163 | * |
164 | * @param int $number Number to convert |
165 | * |
166 | * @return string |
167 | * |
168 | * @since 1.0.0 |
169 | */ |
170 | public static function numericToAlpha(int $number) : string |
171 | { |
172 | $alpha = ''; |
173 | |
174 | for ($i = 1; $number >= 0 && $i < 10; ++$i) { |
175 | $alpha = \chr(0x41 + (int) ($number % \pow(26, $i) / \pow(26, $i - 1))) . $alpha; |
176 | $number -= \pow(26, $i); |
177 | } |
178 | |
179 | return $alpha; |
180 | } |
181 | |
182 | /** |
183 | * Convert alpha to numeric. |
184 | * |
185 | * @param string $alpha Alpha to convert |
186 | * |
187 | * @return int |
188 | * |
189 | * @since 1.0.0 |
190 | */ |
191 | public static function alphaToNumeric(string $alpha) : int |
192 | { |
193 | $numeric = 0; |
194 | $length = \strlen($alpha); |
195 | |
196 | for ($i = 0; $i < $length; ++$i) { |
197 | $numeric += \pow(26, $i) * (\ord($alpha[$length - $i - 1]) - 0x40); |
198 | } |
199 | |
200 | return (int) $numeric - 1; |
201 | } |
202 | } |