Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Numeric
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
5 / 5
21
100.00% covered (success)
100.00%
1 / 1
 __construct
n/a
0 / 0
n/a
0 / 0
1
 convertBase
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
7
 arabicToRoman
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 romanToArabic
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 numericToAlpha
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 alphaToNumeric
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
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 */
13declare(strict_types=1);
14
15namespace 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 */
25final 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}