1111 */
1212class MathTest extends TestCase
1313{
14+ // ------------------------------------------------------------------------
15+ // Euclidean Distance
16+
1417 /**
1518 * @covers ::euclideanDist
1619 * @dataProvider euclidianDistanceDataProvider
@@ -29,25 +32,28 @@ public function testEuclideanDist(array $a, array $b, float $dist): void
2932 public function euclidianDistanceDataProvider (): \Generator
3033 {
3134 /** @var array<string> $row */
32- foreach ($ this ->openCsv ('euclidean_distances_2d.csv ' ) as $ row ) {
35+ foreach ($ this ->readCsv ('euclidean_distances_2d ' ) as $ row ) {
3336 list ($ x1 , $ y1 , $ x2 , $ y2 , $ dist ) = array_map ('floatval ' , $ row );
3437 yield [[$ x1 , $ y1 ], [$ x2 , $ y2 ], $ dist ];
3538 }
3639
3740 /** @var array<string> $row */
38- foreach ($ this ->openCsv ('euclidean_distances_3d.csv ' ) as $ row ) {
41+ foreach ($ this ->readCsv ('euclidean_distances_3d ' ) as $ row ) {
3942 list ($ x1 , $ y1 , $ z1 , $ x2 , $ y2 , $ z2 , $ dist ) = array_map ('floatval ' , $ row );
4043 yield [[$ x1 , $ y1 , $ z1 ], [$ x2 , $ y2 , $ z2 ], $ dist ];
4144 }
4245 }
4346
47+ // ------------------------------------------------------------------------
48+ // Centroid
49+
4450 /**
4551 * @covers ::centroid
4652 * @dataProvider centroidDataProvider
4753 * @param array<float> $centroid
4854 * @param array<float> ...$points
4955 */
50- public function testFindCentroid (array $ centroid , array ...$ points ): void
56+ public function testCentroid (array $ centroid , array ...$ points ): void
5157 {
5258 $ this ->assertEquals ($ centroid , Math::centroid ($ points ));
5359 }
@@ -58,43 +64,20 @@ public function testFindCentroid(array $centroid, array ...$points): void
5864 public function centroidDataProvider (): \Generator
5965 {
6066 /** @var array<string> $row */
61- foreach ($ this ->openCsv ('centroids_2d.csv ' ) as $ row ) {
67+ foreach ($ this ->readCsv ('centroids_2d ' ) as $ row ) {
6268 list ($ x1 , $ y1 , $ x2 , $ y2 , $ x3 , $ y3 , $ x4 , $ y4 , $ cx , $ cy ) = array_map ('floatval ' , $ row );
6369 yield [[$ cx , $ cy ], [$ x1 , $ y1 ], [$ x2 , $ y2 ], [$ x3 , $ y3 ], [$ x4 , $ y4 ]];
6470 }
6571 }
6672
67- /**
68- * @return \Generator<array<array<float>>>
69- */
70- public function boundariesDataProvider (): \Generator
71- {
72- /** @var array<string> $row */
73- foreach ($ this ->openCsv ('boundaries_2d.csv ' ) as $ row ) {
74- list ($ x1 , $ y1 , $ x2 , $ y2 , $ x3 , $ y3 , $ x4 , $ y4 , $ x5 , $ y5 , $ ax , $ ay , $ bx , $ by ) = array_map ('floatval ' , $ row );
75- yield [[$ ax , $ ay ], [$ bx , $ by ], [$ x1 , $ y1 ], [$ x2 , $ y2 ], [$ x3 , $ y3 ], [$ x4 , $ y4 ], [$ x5 , $ y5 ]];
76- }
77- }
78-
79- /**
80- * @return array<mixed>
81- */
82- public function frandDataProvider (): array
83- {
84- return [
85- ['min ' => 0 , 'max ' => 1 ],
86- ['min ' => 10 , 'max ' => 20 ],
87- ['min ' => 0 , 'max ' => 100 ],
88- ['min ' => -100 , 'max ' => 100 ],
89- ['min ' => -1e6 , 'max ' => 1e6 ],
90- ];
91- }
73+ // ------------------------------------------------------------------------
74+ // Gaussian Noise
9275
9376 /**
9477 * @covers ::gaussianNoise
9578 * @dataProvider gaussianNoiseDataProvider
9679 */
97- public function testGenerateGaussianNoise (float $ mu , float $ sigma = 1 , float $ nb = 1e3 ): void
80+ public function testGaussianNoise (float $ mu , float $ sigma = 1 , float $ nb = 1e3 ): void
9881 {
9982 // let's generate $nb numbers and sum them
10083 for ($ sum = 0 , $ i = 0 ; $ i < $ nb ; $ i ++) {
@@ -125,10 +108,79 @@ public function gaussianNoiseDataProvider(): array
125108 ];
126109 }
127110
128- private static function openCsv (string $ path ): \SplFileObject
111+ // ------------------------------------------------------------------------
112+ // Haversine
113+
114+ /**
115+ * @covers ::haversine
116+ * @dataProvider haversineDataProvider
117+ * @param array{0: float, 1: float} $from
118+ * @param array{0: float, 1: float} $to
119+ */
120+ public function testHaversine (string $ label , array $ from , array $ to , float $ expected ): void
121+ {
122+ $ obtained = Math::haversine ($ from , $ to );
123+
124+ $ this ->assertLessThan (
125+ 1 , // meter
126+ $ obtained - $ expected ,
127+ "Haversine distance for $ label should be around $ expected meters " ,
128+ );
129+ }
130+
131+ public function haversineDataProvider (): \Generator
132+ {
133+ /** @var array<string> $row */
134+ foreach ($ this ->readCsv ('haversine_distances ' ) as $ row ) {
135+ $ label = array_shift ($ row );
136+ $ row = array_map ('floatval ' , $ row );
137+ yield [$ label , [$ row [0 ], $ row [1 ]], [$ row [2 ], $ row [3 ]], $ row [4 ]];
138+ }
139+ }
140+
141+ // ------------------------------------------------------------------------
142+ // GPS Centroid
143+
144+ /**
145+ * @covers ::gpsCentroid
146+ * @uses \Kmeans\Math::haversine
147+ * @dataProvider gpsCentroidDataProvider
148+ * @param array{0: float, 1: float} $expected
149+ * @param array<array{0: float, 1: float}> $points
150+ */
151+ public function testGpsCentroid (string $ label , array $ expected , array $ points ): void
129152 {
130- $ csv = new \SplFileObject (__DIR__ . '/../Data/ ' . $ path );
131- $ csv ->setFlags (\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );
153+ $ obtained = Math::gpsCentroid ($ points );
154+
155+ $ this ->assertLessThan (
156+ 1 ,
157+ Math::haversine ($ expected , $ obtained ),
158+ "Centroid of $ label should be near " . implode (', ' , $ expected ),
159+ );
160+ }
161+
162+ public function gpsCentroidDataProvider (): \Generator
163+ {
164+ /** @var array<string> $row */
165+ foreach ($ this ->readCsv ('gps_centroid ' ) as $ row ) {
166+ $ label = array_shift ($ row );
167+ $ points = array_chunk (array_map ('floatval ' , $ row ), 2 );
168+ yield [$ label , array_shift ($ points ), $ points ];
169+ }
170+ }
171+
172+ // ------------------------------------------------------------------------
173+ // Helpers
174+
175+ private static function readCsv (string $ path ): \SplFileObject
176+ {
177+ $ csv = new \SplFileObject (__DIR__ . "/../Data/ {$ path }.csv " );
178+
179+ $ csv ->setFlags (
180+ \SplFileObject::READ_CSV |
181+ \SplFileObject::SKIP_EMPTY |
182+ \SplFileObject::READ_AHEAD
183+ );
132184
133185 return $ csv ;
134186 }
0 commit comments