|
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]; |
|
|
|
|
|
|
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
|
|
|
|
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
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. 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.