PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / trunk
GiveWP – Donation Plugin and Fundraising Platform vtrunk
4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / vendor / moneyphp / money / src / Calculator / GmpCalculator.php
give / vendor / moneyphp / money / src / Calculator Last commit date
BcMathCalculator.php 1 year ago GmpCalculator.php 4 years ago PhpCalculator.php 4 years ago
GmpCalculator.php
323 lines
1 <?php
2
3 namespace Money\Calculator;
4
5 use Money\Calculator;
6 use Money\Money;
7 use Money\Number;
8
9 /**
10 * @author Frederik Bosch <f.bosch@genkgo.nl>
11 */
12 final class GmpCalculator implements Calculator
13 {
14 /**
15 * @var string
16 */
17 private $scale;
18
19 /**
20 * @param int $scale
21 */
22 public function __construct($scale = 14)
23 {
24 $this->scale = $scale;
25 }
26
27 /**
28 * {@inheritdoc}
29 */
30 public static function supported()
31 {
32 return extension_loaded('gmp');
33 }
34
35 /**
36 * {@inheritdoc}
37 */
38 public function compare($a, $b)
39 {
40 $aNum = Number::fromNumber($a);
41 $bNum = Number::fromNumber($b);
42
43 if ($aNum->isDecimal() || $bNum->isDecimal()) {
44 $integersCompared = gmp_cmp($aNum->getIntegerPart(), $bNum->getIntegerPart());
45 if ($integersCompared !== 0) {
46 return $integersCompared;
47 }
48
49 $aNumFractional = $aNum->getFractionalPart() === '' ? '0' : $aNum->getFractionalPart();
50 $bNumFractional = $bNum->getFractionalPart() === '' ? '0' : $bNum->getFractionalPart();
51
52 return gmp_cmp($aNumFractional, $bNumFractional);
53 }
54
55 return gmp_cmp($a, $b);
56 }
57
58 /**
59 * {@inheritdoc}
60 */
61 public function add($amount, $addend)
62 {
63 return gmp_strval(gmp_add($amount, $addend));
64 }
65
66 /**
67 * {@inheritdoc}
68 */
69 public function subtract($amount, $subtrahend)
70 {
71 return gmp_strval(gmp_sub($amount, $subtrahend));
72 }
73
74 /**
75 * {@inheritdoc}
76 */
77 public function multiply($amount, $multiplier)
78 {
79 $multiplier = Number::fromNumber($multiplier);
80
81 if ($multiplier->isDecimal()) {
82 $decimalPlaces = strlen($multiplier->getFractionalPart());
83 $multiplierBase = $multiplier->getIntegerPart();
84
85 if ($multiplierBase) {
86 $multiplierBase .= $multiplier->getFractionalPart();
87 } else {
88 $multiplierBase = ltrim($multiplier->getFractionalPart(), '0');
89 }
90
91 $resultBase = gmp_strval(gmp_mul(gmp_init($amount), gmp_init($multiplierBase)));
92
93 if ('0' === $resultBase) {
94 return '0';
95 }
96
97 $result = substr($resultBase, $decimalPlaces * -1);
98 $resultLength = strlen($result);
99 if ($decimalPlaces > $resultLength) {
100 return '0.'.str_pad('', $decimalPlaces - $resultLength, '0').$result;
101 }
102
103 return substr($resultBase, 0, $decimalPlaces * -1).'.'.$result;
104 }
105
106 return gmp_strval(gmp_mul(gmp_init($amount), gmp_init((string) $multiplier)));
107 }
108
109 /**
110 * {@inheritdoc}
111 */
112 public function divide($amount, $divisor)
113 {
114 $divisor = Number::fromNumber($divisor);
115
116 if ($divisor->isDecimal()) {
117 $decimalPlaces = strlen($divisor->getFractionalPart());
118
119 if ($divisor->getIntegerPart()) {
120 $divisor = new Number($divisor->getIntegerPart().$divisor->getFractionalPart());
121 } else {
122 $divisor = new Number(ltrim($divisor->getFractionalPart(), '0'));
123 }
124
125 $amount = gmp_strval(gmp_mul(gmp_init($amount), gmp_init('1'.str_pad('', $decimalPlaces, '0'))));
126 }
127
128 list($integer, $remainder) = gmp_div_qr(gmp_init($amount), gmp_init((string) $divisor));
129
130 if (gmp_cmp($remainder, '0') === 0) {
131 return gmp_strval($integer);
132 }
133
134 $divisionOfRemainder = gmp_strval(
135 gmp_div_q(
136 gmp_mul($remainder, gmp_init('1'.str_pad('', $this->scale, '0'))),
137 gmp_init((string) $divisor),
138 GMP_ROUND_MINUSINF
139 )
140 );
141
142 if ($divisionOfRemainder[0] === '-') {
143 $divisionOfRemainder = substr($divisionOfRemainder, 1);
144 }
145
146 return gmp_strval($integer).'.'.str_pad($divisionOfRemainder, $this->scale, '0', STR_PAD_LEFT);
147 }
148
149 /**
150 * {@inheritdoc}
151 */
152 public function ceil($number)
153 {
154 $number = Number::fromNumber($number);
155
156 if ($number->isInteger()) {
157 return (string) $number;
158 }
159
160 if ($number->isNegative()) {
161 return $this->add($number->getIntegerPart(), '0');
162 }
163
164 return $this->add($number->getIntegerPart(), '1');
165 }
166
167 /**
168 * {@inheritdoc}
169 */
170 public function floor($number)
171 {
172 $number = Number::fromNumber($number);
173
174 if ($number->isInteger()) {
175 return (string) $number;
176 }
177
178 if ($number->isNegative()) {
179 return $this->add($number->getIntegerPart(), '-1');
180 }
181
182 return $this->add($number->getIntegerPart(), '0');
183 }
184
185 /**
186 * {@inheritdoc}
187 */
188 public function absolute($number)
189 {
190 return ltrim($number, '-');
191 }
192
193 /**
194 * {@inheritdoc}
195 */
196 public function round($number, $roundingMode)
197 {
198 $number = Number::fromNumber($number);
199
200 if ($number->isInteger()) {
201 return (string) $number;
202 }
203
204 if ($number->isHalf() === false) {
205 return $this->roundDigit($number);
206 }
207
208 if (Money::ROUND_HALF_UP === $roundingMode) {
209 return $this->add(
210 $number->getIntegerPart(),
211 $number->getIntegerRoundingMultiplier()
212 );
213 }
214
215 if (Money::ROUND_HALF_DOWN === $roundingMode) {
216 return $this->add($number->getIntegerPart(), '0');
217 }
218
219 if (Money::ROUND_HALF_EVEN === $roundingMode) {
220 if ($number->isCurrentEven()) {
221 return $this->add($number->getIntegerPart(), '0');
222 }
223
224 return $this->add(
225 $number->getIntegerPart(),
226 $number->getIntegerRoundingMultiplier()
227 );
228 }
229
230 if (Money::ROUND_HALF_ODD === $roundingMode) {
231 if ($number->isCurrentEven()) {
232 return $this->add(
233 $number->getIntegerPart(),
234 $number->getIntegerRoundingMultiplier()
235 );
236 }
237
238 return $this->add($number->getIntegerPart(), '0');
239 }
240
241 if (Money::ROUND_HALF_POSITIVE_INFINITY === $roundingMode) {
242 if ($number->isNegative()) {
243 return $this->add(
244 $number->getIntegerPart(),
245 '0'
246 );
247 }
248
249 return $this->add(
250 $number->getIntegerPart(),
251 $number->getIntegerRoundingMultiplier()
252 );
253 }
254
255 if (Money::ROUND_HALF_NEGATIVE_INFINITY === $roundingMode) {
256 if ($number->isNegative()) {
257 return $this->add(
258 $number->getIntegerPart(),
259 $number->getIntegerRoundingMultiplier()
260 );
261 }
262
263 return $this->add(
264 $number->getIntegerPart(),
265 '0'
266 );
267 }
268
269 throw new \InvalidArgumentException('Unknown rounding mode');
270 }
271
272 /**
273 * @param $number
274 *
275 * @return string
276 */
277 private function roundDigit(Number $number)
278 {
279 if ($number->isCloserToNext()) {
280 return $this->add(
281 $number->getIntegerPart(),
282 $number->getIntegerRoundingMultiplier()
283 );
284 }
285
286 return $this->add($number->getIntegerPart(), '0');
287 }
288
289 /**
290 * {@inheritdoc}
291 */
292 public function share($amount, $ratio, $total)
293 {
294 return $this->floor($this->divide($this->multiply($amount, $ratio), $total));
295 }
296
297 /**
298 * {@inheritdoc}
299 */
300 public function mod($amount, $divisor)
301 {
302 // gmp_mod() only calculates non-negative integers, so we use absolutes
303 $remainder = gmp_mod($this->absolute($amount), $this->absolute($divisor));
304
305 // If the amount was negative, we negate the result of the modulus operation
306 $amount = Number::fromNumber($amount);
307
308 if ($amount->isNegative()) {
309 $remainder = gmp_neg($remainder);
310 }
311
312 return gmp_strval($remainder);
313 }
314
315 /**
316 * @test
317 */
318 public function it_divides_bug538()
319 {
320 $this->assertSame('-4.54545454545455', $this->getCalculator()->divide('-500', 110));
321 }
322 }
323