| 1441 |
ariadna |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
|
|
|
4 |
|
|
|
5 |
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
|
|
6 |
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
|
|
7 |
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
|
|
8 |
|
|
|
9 |
class Arabic
|
|
|
10 |
{
|
|
|
11 |
use ArrayEnabled;
|
|
|
12 |
|
|
|
13 |
private const ROMAN_LOOKUP = [
|
|
|
14 |
'M' => 1000,
|
|
|
15 |
'D' => 500,
|
|
|
16 |
'C' => 100,
|
|
|
17 |
'L' => 50,
|
|
|
18 |
'X' => 10,
|
|
|
19 |
'V' => 5,
|
|
|
20 |
'I' => 1,
|
|
|
21 |
];
|
|
|
22 |
|
|
|
23 |
/**
|
|
|
24 |
* Recursively calculate the arabic value of a roman numeral.
|
|
|
25 |
*/
|
|
|
26 |
private static function calculateArabic(array $roman, int &$sum = 0, int $subtract = 0): int
|
|
|
27 |
{
|
|
|
28 |
$numeral = array_shift($roman);
|
|
|
29 |
if (!isset(self::ROMAN_LOOKUP[$numeral])) {
|
|
|
30 |
throw new Exception('Invalid character detected');
|
|
|
31 |
}
|
|
|
32 |
|
|
|
33 |
$arabic = self::ROMAN_LOOKUP[$numeral];
|
|
|
34 |
if (count($roman) > 0 && isset(self::ROMAN_LOOKUP[$roman[0]]) && $arabic < self::ROMAN_LOOKUP[$roman[0]]) {
|
|
|
35 |
$subtract += $arabic;
|
|
|
36 |
} else {
|
|
|
37 |
$sum += ($arabic - $subtract);
|
|
|
38 |
$subtract = 0;
|
|
|
39 |
}
|
|
|
40 |
|
|
|
41 |
if (count($roman) > 0) {
|
|
|
42 |
self::calculateArabic($roman, $sum, $subtract);
|
|
|
43 |
}
|
|
|
44 |
|
|
|
45 |
return $sum;
|
|
|
46 |
}
|
|
|
47 |
|
|
|
48 |
/**
|
|
|
49 |
* ARABIC.
|
|
|
50 |
*
|
|
|
51 |
* Converts a Roman numeral to an Arabic numeral.
|
|
|
52 |
*
|
|
|
53 |
* Excel Function:
|
|
|
54 |
* ARABIC(text)
|
|
|
55 |
*
|
|
|
56 |
* @param string|string[] $roman Should be a string, or can be an array of strings
|
|
|
57 |
*
|
|
|
58 |
* @return array|int|string the arabic numberal contrived from the roman numeral
|
|
|
59 |
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
|
|
60 |
* with the same dimensions
|
|
|
61 |
*/
|
|
|
62 |
public static function evaluate(mixed $roman): array|int|string
|
|
|
63 |
{
|
|
|
64 |
if (is_array($roman)) {
|
|
|
65 |
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $roman);
|
|
|
66 |
}
|
|
|
67 |
|
|
|
68 |
// An empty string should return 0
|
|
|
69 |
$roman = substr(trim(strtoupper((string) $roman)), 0, 255);
|
|
|
70 |
if ($roman === '') {
|
|
|
71 |
return 0;
|
|
|
72 |
}
|
|
|
73 |
|
|
|
74 |
// Convert the roman numeral to an arabic number
|
|
|
75 |
$negativeNumber = $roman[0] === '-';
|
|
|
76 |
if ($negativeNumber) {
|
|
|
77 |
$roman = trim(substr($roman, 1));
|
|
|
78 |
if ($roman === '') {
|
|
|
79 |
return ExcelError::NAN();
|
|
|
80 |
}
|
|
|
81 |
}
|
|
|
82 |
|
|
|
83 |
try {
|
|
|
84 |
$arabic = self::calculateArabic(mb_str_split($roman, 1, 'UTF-8'));
|
|
|
85 |
} catch (Exception) {
|
|
|
86 |
return ExcelError::VALUE(); // Invalid character detected
|
|
|
87 |
}
|
|
|
88 |
|
|
|
89 |
if ($negativeNumber) {
|
|
|
90 |
$arabic *= -1; // The number should be negative
|
|
|
91 |
}
|
|
|
92 |
|
|
|
93 |
return $arabic;
|
|
|
94 |
}
|
|
|
95 |
}
|