Calculator::withSpecifiedPayments()   A
last analyzed

Complexity

Conditions 3
Paths 1

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 1
nop 4
dl 0
loc 24
ccs 13
cts 13
cp 1
crap 3
rs 9.536
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Nyholm\EffectiveInterest;
6
7
/**
8
 * @author Tobias Nyholm <[email protected]>
9
 */
10
final class Calculator
11
{
12
    /**
13
     * @var NewtonRaphson
14
     */
15
    private $newton;
16
17
    /**
18
     * @param NewtonRaphson $newton
19
     */
20 8
    public function __construct(NewtonRaphson $newton = null)
21
    {
22 8
        $this->newton = $newton ?? new NewtonRaphson();
23 8
    }
24
25
    /**
26
     * Get the interest when you know all the payments and their dates. Use this function when you have
27
     * administration fees at the first payment and/or when payments are irregular.
28
     *
29
     * @param int    $principal
30
     * @param string $startDate in format 'YYYY-mm-dd'
31
     * @param array  $payments  array with payment dates and values ['YYYY-mm-dd'=>int]
32
     * @param float  $guess     A guess what the interest may be. Between zero and one. Example 0.045
33
     *
34
     * @return float
35
     */
36 6
    public function withSpecifiedPayments(int $principal, string $startDate, array $payments, float $guess): float
37
    {
38 6
        list($values, $days) = $this->preparePayments($principal, $startDate, $payments);
39
40
        $fx = function ($x) use ($days, $values) {
41 6
            $sum = 0;
42 6
            foreach ($days as $idx => $day) {
43 6
                $sum += $values[$idx] * pow(1 + $x, ($days[0] - $day) / 365);
44
            }
45
46 6
            return $sum;
47 6
        };
48
49
        $fdx = function ($x) use ($days, $values) {
50 6
            $sum = 0;
51 6
            foreach ($days as $idx => $day) {
52 6
                $sum += (1 / 365) * ($days[0] - $day) * $values[$idx] * pow(1 + $x, (($days[0] - $day) / 365) - 1);
53
            }
54
55 6
            return $sum;
56 6
        };
57
58 6
        return $this->newton->run($fx, $fdx, $guess);
59
    }
60
61
    /**
62
     * Get the effective interest when the monthly payments are exactly the same.
63
     *
64
     * @param int   $principal      The total loan amount (Principal)
65
     * @param int   $payment        The monthly payment
66
     * @param int   $numberOfMonths The number of months
67
     * @param float $guess          A guess of what the interest might be. Interest as a number between zero and one. Example 0.045
68
     *
69
     * @return float
70
     */
71 2
    public function withEqualPayments(int $principal, int $payment, int $numberOfMonths, float $guess): float
72
    {
73 View Code Duplication
        $fx = function ($x) use ($principal, $payment, $numberOfMonths) {
74 2
            return  $payment - $payment * pow(1 + $x, -1 * $numberOfMonths) - $x * $principal;
75 2
        };
76
77 2 View Code Duplication
        $fdx = function ($x) use ($principal, $payment, $numberOfMonths) {
78 2
            return  $numberOfMonths * $payment * pow(1 + $x, -1 * $numberOfMonths - 1) - $principal;
79 2
        };
80
81 2
        return 12 * $this->newton->run($fx, $fdx, $guess);
82
    }
83
84
    /**
85
     * Prepare payment data by separating dates from values and prefix the array with the principal.
86
     *
87
     * @param int    $principal
88
     * @param string $startDate
89
     * @param array  $payments
90
     *
91
     * @return array
92
     */
93 6
    private function preparePayments(int $principal, string $startDate, array $payments): array
94
    {
95 6
        $values = [-1 * $principal];
96 6
        $days = [1];
97 6
        $startDate = new \DateTimeImmutable($startDate);
98
99 6
        foreach ($payments as $date => $payment) {
100 6
            $values[] = $payment;
101 6
            $days[] = 1 + $startDate->diff(new \DateTime($date))->days;
102
        }
103
104 6
        return [$values, $days];
105
    }
106
}
107