-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNumber.php
More file actions
120 lines (94 loc) · 2.99 KB
/
Number.php
File metadata and controls
120 lines (94 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?php
declare(strict_types=1);
namespace TinyBlocks\Math\Internal;
use TinyBlocks\Math\Internal\Exceptions\InvalidNumber;
final class Number
{
private const float ZERO = 0.0;
private const string SIGN = '?<sign>[\-\+]';
private const string POINT = '?<point>\.';
private const string INTEGRAL = '?<integral>[0-9]+';
private const string EXPONENT = '?<exponent>[\-\+]?[0-9]+';
private const string NUMERATOR = '?<numerator>[0-9]+';
private const string FRACTIONAL = '?<fractional>[0-9]+';
private const string DENOMINATOR = '?<denominator>[0-9]+';
private const string VALID_NUMBER = '/^(%s)?(?:(?:(%s)?(%s)?(%s)?(?:[eE](%s))?)|(?:(%s)\/?(%s)))$/';
private int $match;
private array $matches = [];
private function __construct(public readonly string $value)
{
$pattern = sprintf(
self::VALID_NUMBER,
self::SIGN,
self::INTEGRAL,
self::POINT,
self::FRACTIONAL,
self::EXPONENT,
self::NUMERATOR,
self::DENOMINATOR
);
$this->match = preg_match($pattern, $this->value, $this->matches);
if ($this->isInvalidNumber()) {
throw new InvalidNumber(value: $this->value);
}
}
public static function from(float|string $value): Number
{
return new Number(value: (string)$value);
}
public function getExponent(): ?string
{
return $this->match(key: 'exponent');
}
public function getFractional(): ?string
{
return $this->match(key: 'fractional');
}
public function isZero(): bool
{
return $this->value == self::ZERO;
}
public function isNegative(): bool
{
return $this->value < self::ZERO;
}
public function isPositiveOrZero(): bool
{
return $this->isZero() || !$this->isNegative();
}
public function isNegativeOrZero(): bool
{
return $this->isZero() || $this->isNegative();
}
public function isLessThan(Number $other): bool
{
return $this->value < $other->value;
}
public function isGreaterThan(Number $other): bool
{
return $this->value > $other->value;
}
public function isLessThanOrEqual(Number $other): bool
{
return $this->value <= $other->value;
}
public function isGreaterThanOrEqual(Number $other): bool
{
return $this->value >= $other->value;
}
public function toFloatWithScale(Scale $scale): float
{
$value = (float)$this->value;
return $scale->hasAutomaticScale() ? $value : (float)number_format($value, $scale->value, '.', '');
}
private function match(string $key): ?string
{
$math = $this->matches[$key] ?? null;
return $math == '' ? null : $math;
}
private function isInvalidNumber(): bool
{
$integral = $this->match(key: 'integral');
return ($this->match !== 1) || (is_null($integral) && is_null($this->getFractional()));
}
}