Passed
Push — master ( 74fc3b...25ef3a )
by Alec
04:14
created

MoneyFunctions::add()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * User: alec
4
 * Date: 26.11.18
5
 * Time: 14:35
6
 */
7
8
namespace AlecRabbit\Money;
9
10
use AlecRabbit\Money\Contracts\CalculatorInterface;
11
12
trait MoneyFunctions
13
{
14
    /** @var CalculatorInterface */
15
    private $calculator;
16
17
18
    /**
19
     * @param Money $first
20
     * @param Money ...$collection
21
     *
22
     * @return Money
23
     */
24 3
    public static function min(Money $first, Money ...$collection): Money
25
    {
26 3
        $min = $first;
27
28 3
        foreach ($collection as $money) {
29 2
            if ($money->lessThan($min)) {
30 2
                $min = $money;
31
            }
32
        }
33
34 3
        return $min;
35
    }
36
37
    /**
38
     * Checks whether the value represented by this object is less than the other.
39
     *
40
     * @param Money $other
41
     *
42
     * @return bool
43
     */
44 5
    public function lessThan(Money $other): bool
45
    {
46 5
        return $this->compare($other) === -1;
47
    }
48
49
    /**
50
     * @param Money $first
51
     * @param Money ...$collection
52
     *
53
     * @return Money
54
     */
55 3
    public static function max(Money $first, Money ...$collection): Money
56
    {
57 3
        $max = $first;
58
59 3
        foreach ($collection as $money) {
60 2
            if ($money->greaterThan($max)) {
61 2
                $max = $money;
62
            }
63
        }
64
65 3
        return $max;
66
    }
67
68
    /**
69
     * Checks whether the value represented by this object is greater than the other.
70
     *
71
     * @param Money $other
72
     *
73
     * @return bool
74
     */
75 5
    public function greaterThan(Money $other): bool
76
    {
77 5
        return $this->compare($other) === 1;
78
    }
79
80
    /**
81
     * @param Money $first
82
     * @param Money ...$collection
83
     *
84
     * @return Money
85
     */
86 6
    public static function sum(Money $first, Money ...$collection): Money
87
    {
88 6
        return $first->add(...$collection);
89
    }
90
91
    /**
92
     * Returns a new Money object that represents
93
     * the sum of this and an other Money object.
94
     *
95
     * @param Money ...$addends
96
     *
97
     * @return Money
98
     */
99 28
    public function add(Money ...$addends): Money
100
    {
101 28
        $amount = $this->amount;
102 28
        $calculator = $this->calculator;
103
104 28
        foreach ($addends as $addend) {
105 25
            $this->assertSameCurrency($addend);
0 ignored issues
show
Bug introduced by
It seems like assertSameCurrency() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

105
            $this->/** @scrutinizer ignore-call */ 
106
                   assertSameCurrency($addend);
Loading history...
106
107 25
            $amount = $calculator->add($amount, $addend->amount);
0 ignored issues
show
Bug introduced by
The property amount is declared private in AlecRabbit\Money\Money and cannot be accessed from this context.
Loading history...
108
        }
109 28
        return new Money($amount, $this->currency);
110
    }
111
112
    /**
113
     * @param Money $first
114
     * @param Money ...$collection
115
     *
116
     * @return Money
117
     */
118 5
    public static function avg(Money $first, Money ...$collection): Money
119
    {
120 5
        return $first->add(...$collection)->divide(\func_num_args());
121
    }
122
123
    /**
124
     * Returns a new Money object that represents
125
     * the divided value by the given factor.
126
     *
127
     * @param float|int|string $divisor
128
     *
129
     * @return Money
130
     */
131 16
    public function divide($divisor): Money
132
    {
133 16
        $this->assertOperand($divisor);
0 ignored issues
show
Bug introduced by
It seems like assertOperand() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

133
        $this->/** @scrutinizer ignore-call */ 
134
               assertOperand($divisor);
Loading history...
134
135 11
        if ($this->calculator->compare((string)$divisor, '0') === 0) {
136 1
            throw new \InvalidArgumentException('Division by zero.');
137
        }
138
139 10
        $quotient = $this->calculator->divide($this->amount, $divisor);
140
        return
141 10
            $this->newInstance($quotient);
0 ignored issues
show
Bug introduced by
It seems like newInstance() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
            $this->/** @scrutinizer ignore-call */ 
142
                   newInstance($quotient);
Loading history...
142
    }
143
144
    /**
145
     * Checks whether the value represented by this object equals to the other.
146
     *
147
     * @param Money $other
148
     *
149
     * @return bool
150
     */
151 8
    public function equals(Money $other): bool
152
    {
153 8
        return $this->isSameCurrency($other) && $this->amount === $other->amount;
0 ignored issues
show
Bug introduced by
The property amount is declared private in AlecRabbit\Money\Money and cannot be accessed from this context.
Loading history...
Bug introduced by
It seems like isSameCurrency() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
        return $this->/** @scrutinizer ignore-call */ isSameCurrency($other) && $this->amount === $other->amount;
Loading history...
154
    }
155
156
    /**
157
     * @param Money $other
158
     *
159
     * @return bool
160
     */
161 3
    public function greaterThanOrEqual(Money $other): bool
162
    {
163 3
        return $this->compare($other) >= 0;
164
    }
165
166
    /**
167
     * @param Money $other
168
     *
169
     * @return bool
170
     */
171 3
    public function lessThanOrEqual(Money $other): bool
172
    {
173 3
        return $this->compare($other) <= 0;
174
    }
175
176
    /**
177
     * Returns a new Money object that represents
178
     * the multiplied value by the given factor.
179
     *
180
     * @param float|int|string $multiplier
181
     *
182
     * @return Money
183
     */
184 9
    public function multiply($multiplier): Money
185
    {
186 9
        $this->assertOperand($multiplier);
187
188 4
        $product = $this->calculator->multiply($this->amount, $multiplier);
189
190
        return
191 4
            $this->newInstance($product);
192
    }
193
194
    /**
195
     * Returns a new Money object that represents
196
     * the remainder after dividing the value by
197
     * the given factor.
198
     *
199
     * @param Money $divisor
200
     *
201
     * @return Money
202
     */
203 4
    public function mod(Money $divisor): Money
204
    {
205 4
        $this->assertSameCurrency($divisor);
206
207 4
        return new Money($this->calculator->mod($this->amount, $divisor->amount), $this->currency);
0 ignored issues
show
Bug introduced by
The property amount is declared private in AlecRabbit\Money\Money and cannot be accessed from this context.
Loading history...
208
    }
209
210
    /**
211
     * Allocate the money among N targets.
212
     *
213
     * @param int $n
214
     *
215
     * @param int|null $precision
216
     * @return Money[]
217
     *
218
     */
219 7
    public function allocateTo(int $n, ?int $precision = null): array
220
    {
221 7
        if ($n <= 0) {
222 2
            throw new \InvalidArgumentException('Number to allocateTo must be greater than zero.');
223
        }
224
225 5
        return $this->allocate(array_fill(0, $n, 1), $precision);
226
    }
227
228
    /**
229
     * Allocate the money according to a list of ratios.
230
     *
231
     * @param array $ratios
232
     *
233
     * @param int|null $precision
234
     * @return Money[]
235
     */
236 37
    public function allocate(array $ratios, ?int $precision = null): array
237
    {
238 37
        $precision = $precision ?? 2;
239 37
        if (0 === $allocations = \count($ratios)) {
240 1
            throw new \InvalidArgumentException('Cannot allocate to none, ratios cannot be an empty array.');
241
        }
242
243 36
        $remainder = $this->amount;
244 36
        $results = [];
245 36
        $total = array_sum($ratios);
246
247 36
        if ($total <= 0) {
248 1
            throw new \InvalidArgumentException('Sum of ratios must be greater than zero.');
249
        }
250
251 35
        foreach ($ratios as $ratio) {
252 35
            if ($ratio < 0) {
253 1
                throw new \InvalidArgumentException('Ratio must be zero or positive.');
254
            }
255
256 35
            $share = $this->calculator->share($this->amount, $ratio, $total, $precision);
257 35
            $results[] = $this->newInstance($share);
258 35
            $remainder = $this->calculator->subtract($remainder, $share);
259
        }
260 34
        switch ($this->calculator->compare($remainder, '0')) {
261
            case -1:
262 13
                for ($i = $allocations - 1; $i >= 0; $i--) {
263 13
                    if (!$ratios[$i]) {
264 1
                        continue;
265
                    }
266 13
                    $results[$i]->setAmount($this->calculator->add($results[$i]->amount, $remainder));
267 13
                    break;
268
                }
269 13
                break;
270 21
            case 1:
271 7
                for ($i = 0; $i < $allocations; $i++) {
272 7
                    if (!$ratios[$i]) {
273 1
                        continue;
274
                    }
275 7
                    $results[$i]->setAmount($this->calculator->add($results[$i]->amount, $remainder));
276 7
                    break;
277
                }
278 7
                break;
279
            default:
280 14
                break;
281
        }
282 34
        return $results;
283
    }
284
285
    /**
286
     * @param Money $money
287
     *
288
     * @return string
289
     */
290 2
    public function ratioOf(Money $money): string
291
    {
292 2
        if ($money->isZero()) {
293 1
            throw new \InvalidArgumentException('Cannot calculate a ratio of zero.');
294
        }
295
296 1
        return $this->calculator->divide($this->amount, $money->amount);
0 ignored issues
show
Bug introduced by
The property amount is declared private in AlecRabbit\Money\Money and cannot be accessed from this context.
Loading history...
297
    }
298
299
    /**
300
     * Checks if the value represented by this object is zero.
301
     *
302
     * @return bool
303
     */
304 30
    public function isZero(): bool
305
    {
306 30
        return $this->calculator->compare($this->amount, '0') === 0;
307
    }
308
309
    /**
310
     * @return Money
311
     */
312 7
    public function absolute(): Money
313
    {
314 7
        return $this->newInstance($this->calculator->absolute($this->amount));
315
    }
316
317
    /**
318
     * @return Money
319
     */
320 8
    public function negative(): Money
321
    {
322 8
        return $this->newInstance(0)->subtract($this);
323
    }
324
325
    /**
326
     * Returns a new Money object that represents
327
     * the difference of this and an other Money object.
328
     *
329
     * @param Money ...$subtrahends
330
     *
331
     * @return Money
332
     */
333 20
    public function subtract(Money ...$subtrahends): Money
334
    {
335 20
        $amount = $this->amount;
336 20
        $calculator = $this->calculator;
337
338 20
        foreach ($subtrahends as $subtrahend) {
339 20
            $this->assertSameCurrency($subtrahend);
340
341 20
            $amount = $calculator->subtract($amount, $subtrahend->amount);
0 ignored issues
show
Bug introduced by
The property amount is declared private in AlecRabbit\Money\Money and cannot be accessed from this context.
Loading history...
342
        }
343
344 20
        return new Money($amount, $this->currency);
345
    }
346
347
    /**
348
     * Checks if the value represented by this object is not negative.
349
     *
350
     * @return bool
351
     */
352 6
    public function isNotNegative(): bool
353
    {
354
        return
355 6
            !$this->isNegative();
356
    }
357
358
    /**
359
     * Checks if the value represented by this object is negative.
360
     *
361
     * @return bool
362
     */
363 13
    public function isNegative(): bool
364
    {
365 13
        return $this->calculator->compare($this->amount, '0') === -1;
366
    }
367
368
    /**
369
     * Checks if the value represented by this object is not zero.
370
     *
371
     * @return bool
372
     */
373 6
    public function isNotZero(): bool
374
    {
375
        return
376 6
            !$this->isZero();
377
    }
378
379
    /**
380
     * Checks if the value represented by this object is not positive.
381
     *
382
     * @return bool
383
     */
384 17
    public function isNotPositive(): bool
385
    {
386
        return
387 17
            !$this->isPositive();
388
    }
389
390
    /**
391
     * Checks if the value represented by this object is positive.
392
     *
393
     * @return bool
394
     */
395 23
    public function isPositive(): bool
396
    {
397 23
        return $this->calculator->compare($this->amount, '0') === 1;
398
    }
399
400
    abstract public function compare(Money $other): int;
401
402
}