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
|
|
|
* @param int $sum |
27
|
|
|
* @param int $subtract |
28
|
|
|
* |
29
|
|
|
* @return int |
30
|
|
|
*/ |
31
|
17 |
|
private static function calculateArabic(array $roman, &$sum = 0, $subtract = 0) |
32
|
|
|
{ |
33
|
17 |
|
$numeral = array_shift($roman); |
34
|
17 |
|
if (!isset(self::ROMAN_LOOKUP[$numeral])) { |
35
|
1 |
|
throw new Exception('Invalid character detected'); |
36
|
|
|
} |
37
|
|
|
|
38
|
16 |
|
$arabic = self::ROMAN_LOOKUP[$numeral]; |
39
|
16 |
|
if (count($roman) > 0 && isset(self::ROMAN_LOOKUP[$roman[0]]) && $arabic < self::ROMAN_LOOKUP[$roman[0]]) { |
40
|
12 |
|
$subtract += $arabic; |
41
|
|
|
} else { |
42
|
16 |
|
$sum += ($arabic - $subtract); |
43
|
16 |
|
$subtract = 0; |
44
|
|
|
} |
45
|
|
|
|
46
|
16 |
|
if (count($roman) > 0) { |
47
|
15 |
|
self::calculateArabic($roman, $sum, $subtract); |
48
|
|
|
} |
49
|
|
|
|
50
|
16 |
|
return $sum; |
51
|
|
|
} |
52
|
|
|
|
53
|
17 |
|
/** |
54
|
|
|
* ARABIC. |
55
|
17 |
|
* |
56
|
|
|
* Converts a Roman numeral to an Arabic numeral. |
57
|
|
|
* |
58
|
17 |
|
* Excel Function: |
59
|
|
|
* ARABIC(text) |
60
|
17 |
|
* |
61
|
|
|
* @param mixed $roman Should be a string, or can be an array of strings |
62
|
17 |
|
* |
63
|
|
|
* @return array|int|string the arabic numberal contrived from the roman numeral |
64
|
|
|
* If an array of numbers is passed as the argument, then the returned result will also be an array |
65
|
|
|
* with the same dimensions |
66
|
|
|
*/ |
67
|
|
|
public static function evaluate(mixed $roman) |
68
|
|
|
{ |
69
|
|
|
if (is_array($roman)) { |
70
|
|
|
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $roman); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
// An empty string should return 0 |
74
|
|
|
$roman = substr(trim(strtoupper((string) $roman)), 0, 255); |
75
|
|
|
if ($roman === '') { |
76
|
|
|
return 0; |
77
|
|
|
} |
78
|
|
|
|
79
|
18 |
|
// Convert the roman numeral to an arabic number |
80
|
|
|
$negativeNumber = $roman[0] === '-'; |
81
|
18 |
|
if ($negativeNumber) { |
82
|
17 |
|
$roman = substr($roman, 1); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
try { |
86
|
18 |
|
$arabic = self::calculateArabic(str_split($roman)); |
87
|
18 |
|
} catch (Exception) { |
88
|
1 |
|
return ExcelError::VALUE(); // Invalid character detected |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
if ($negativeNumber) { |
92
|
17 |
|
$arabic *= -1; // The number should be negative |
93
|
17 |
|
} |
94
|
2 |
|
|
95
|
|
|
return $arabic; |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|