Completed
Push — master ( 8f7d6a...a27e32 )
by Hannes
02:14
created

AbstractTable::getAgeIndex()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 1
crap 3
1
<?php
2
3
/*
4
 * This file is part of the Runalyze Age Grade.
5
 *
6
 * (c) RUNALYZE <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Runalyze\AgeGrade\Table;
13
14
use Runalyze\AgeGrade\AgeGrade;
15
16
/**
17
 * Classes extending AbstractTable must provide correctly sized $OpenStandard and $AgeFactors.
18
 */
19
abstract class AbstractTable implements TableInterface
20
{
21
    /** @var int */
22
    protected $NumDistances;
23
24
    /** @var int */
25
    protected $NumAges;
26
27
    /** @var array available distances [km] */
28
    protected $Distances = [
29
        5.0, 6.0, 6.437376, 8.0, 8.04672, 10, 12, 15, 16.09344, 20, 21.0975, 25, 30, 42.195, 50, 80.46736, 100, 160.9344, 200,
30
    ];
31
32
    /** @var array available ages [from, to] in [years] */
33
    protected $AgeRange = [5, 100];
34
35
    /** @var array open standard times for all available distances in [s] */
36
    protected $OpenStandard = [];
37
38
    /** @var array for each age an array with factors for all available distances in [0.0 .. 1.0] */
39
    protected $AgeFactors = [];
40
41 14
    public function __construct()
42
    {
43 14
        $this->NumDistances = count($this->Distances);
44 14
        $this->NumAges = 1 + $this->AgeRange[1] - $this->AgeRange[0];
0 ignored issues
show
Documentation Bug introduced by
It seems like 1 + $this->AgeRange[1] - $this->AgeRange[0] can also be of type double. However, the property $NumAges is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
45 14
    }
46
47
    /**
48
     * @return array distances with exact age standards [km]
49
     */
50 1
    public function getAvailableDistances()
51
    {
52 1
        return $this->Distances;
53
    }
54
55
    /**
56
     * @return array [from, to] in [years]
57
     */
58 1
    public function getAvailableAgeRange()
59
    {
60 1
        return $this->AgeRange;
61
    }
62
63
    /**
64
     * @return float [km]
65
     */
66 1
    public function getMinimalDistance()
67
    {
68 1
        return $this->Distances[0];
69
    }
70
71
    /**
72
     * @param  int   $age           [years]
73
     * @param  float $distance      [km]
74
     * @param  int   $timeInSeconds [s]
75
     * @return float age grade in [0.0 .. 1.0]
76
     */
77 6
    public function getAgePerformance($age, $distance, $timeInSeconds)
78
    {
79 6
        return $this->getAgeStandard($age, $distance) / $timeInSeconds;
80
    }
81
82
    /**
83
     * @param  int      $age           [years]
84
     * @param  float    $distance      [km]
85
     * @param  int      $timeInSeconds [s]
86
     * @return AgeGrade
87
     */
88 2
    public function getAgeGrade($age, $distance, $timeInSeconds)
89
    {
90 2
        $ageStandard = $this->getAgeStandard($age, $distance);
91 2
        $ageFactor = $this->getAgeFactor($age, $distance);
92
93 2
        $ageGrade = new AgeGrade($ageStandard / $timeInSeconds);
94 2
        $ageGrade->setOriginalResult($distance, $timeInSeconds);
95 2
        $ageGrade->setTableData($ageStandard, $ageFactor);
96
97 2
        return $ageGrade;
98
    }
99
100
    /**
101
     * @param  int   $age      [years]
102
     * @param  float $distance [km]
103
     * @return int   age standard by WMA [s]
104
     */
105 10
    public function getAgeStandard($age, $distance)
106
    {
107 10
        list($distanceIndex, $fraction) = $this->getDistanceIndexWithFraction($distance);
108 10
        $ageFactor = $this->getAgeFactor($age, $distance);
109
110 10
        if (1.0 === $fraction) {
111 9
            return $this->OpenStandard[$distanceIndex] / $ageFactor;
112
        }
113
114 3
        return ((1 - $fraction) * $this->OpenStandard[$distanceIndex - 1] + $fraction * $this->OpenStandard[$distanceIndex]) / $ageFactor;
115
    }
116
117
    /**
118
     * @param  int   $age      [years]
119
     * @param  float $distance [km]
120
     * @return float age grade factor in [0.0 .. 1.0]
121
     */
122 10
    protected function getAgeFactor($age, $distance)
123
    {
124 10
        $ageIndex = $this->getAgeIndex($age);
125 10
        list($distanceIndex, $fraction) = $this->getDistanceIndexWithFraction($distance);
126
127 10
        if ($distance <= $this->Distances[0] || $distance >= $this->Distances[$this->NumDistances - 1]) {
128 6
            return $this->AgeFactors[$ageIndex][$distanceIndex];
129
        }
130
131 6
        return (1 - $fraction) * $this->AgeFactors[$ageIndex][$distanceIndex - 1] + $fraction * $this->AgeFactors[$ageIndex][$distanceIndex];
132
    }
133
134
    /**
135
     * @param  int $age [years]
136
     * @return int age index for internal table
137
     */
138 10
    protected function getAgeIndex($age)
139
    {
140 10
        list($min, $max) = $this->AgeRange;
141
142 10
        if ($age < $min) {
143 1
            $age = $min;
144 10
        } elseif ($age > $max) {
145 1
            $age = $max;
146 1
        }
147
148 10
        return (int) $age - $min;
149
    }
150
151
    /**
152
     * @param  float $distance [km]
153
     * @return array [index, fraction] distance index for internal table
154
     *                        fraction belongs to the returned index, (1 - fraction) to (index - 1)
155
     */
156 10
    protected function getDistanceIndexWithFraction($distance)
157
    {
158 10
        if ($distance <= $this->Distances[0]) {
159 4
            return [0, 1.0];
160
        }
161
162 10
        if ($distance >= $this->Distances[$this->NumDistances - 1]) {
163 4
            return [$this->NumDistances - 1, 1.0];
164
        }
165
166 6
        for ($i = 0; $i < $this->NumDistances; ++$i) {
167 6
            if ($this->Distances[$i] >= $distance) {
168 6
                break;
169
            }
170 6
        }
171
172 6
        $i = min($i, $this->NumDistances - 1);
173 6
        $fraction = ($distance - $this->Distances[$i - 1]) / ($this->Distances[$i] - $this->Distances[$i - 1]);
174
175 6
        return [$i, $fraction];
176
    }
177
}
178