@@ -6,22 +6,66 @@ struct QuantileTests {
66 @Test (
77 " Valid data returns correct quantile " ,
88 arguments: [
9- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . inverseEmpiricalCDF, 6 ) ,
10- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . inverseEmpiricalCDF, 1 ) ,
11- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . averagedInverseEmpiricalCDF, 6 ) ,
12- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . averagedInverseEmpiricalCDF, 1 ) ,
13- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . closestOrOddIndexed, 6 ) ,
14- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . closestOrOddIndexed, 1 ) ,
15- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . lerpInverseEmpiricalCDF, 6 ) ,
16- ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . lerpInverseEmpiricalCDF, 1 ) ,
17- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] , 0.5 , . inverseEmpiricalCDF, 5 ) ,
18- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] , 0.5 , . inverseEmpiricalCDF, 5 ) ,
19- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] , 0.5 , . averagedInverseEmpiricalCDF, 5.5 ) ,
20- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] , 0.5 , . averagedInverseEmpiricalCDF, 5 ) ,
21- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] , 0.5 , . closestOrOddIndexed, 5 ) ,
22- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] , 0.5 , . closestOrOddIndexed, 4 ) ,
23- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] , 0.5 , . lerpInverseEmpiricalCDF, 5 ) ,
24- ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] , 0.5 , . lerpInverseEmpiricalCDF, 4.5 ) ,
9+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . inverseEmpiricalCDF, 1 ) ,
10+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.25 , . inverseEmpiricalCDF, 3 ) ,
11+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.5 , . inverseEmpiricalCDF, 4 ) ,
12+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.75 , . inverseEmpiricalCDF, 4 ) ,
13+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . inverseEmpiricalCDF, 6 ) ,
14+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . averagedInverseEmpiricalCDF, 1 ) ,
15+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.25 , . averagedInverseEmpiricalCDF, 2.5 ) ,
16+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.5 , . averagedInverseEmpiricalCDF, 3.5 ) ,
17+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.75 , . averagedInverseEmpiricalCDF, 4 ) ,
18+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . averagedInverseEmpiricalCDF, 6 ) ,
19+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . closest, 1 ) ,
20+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.25 , . closest, 2 ) ,
21+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.5 , . closest, 3 ) ,
22+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.75 , . closest, 4 ) ,
23+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . closest, 6 ) ,
24+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0 , . lerpInverseEmpiricalCDF, 1 ) ,
25+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.25 , . lerpInverseEmpiricalCDF, 2.25 ) ,
26+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.5 , . lerpInverseEmpiricalCDF, 3.5 ) ,
27+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 0.75 , . lerpInverseEmpiricalCDF, 4 ) ,
28+ ( [ 1 , 4 , 2 , 6 , 4 , 3 ] , 1 , . lerpInverseEmpiricalCDF, 6 ) ,
29+ ( [ 7 , 3 , 10 , 1 , 8 , 4 , 9 , 2 , 5 , 6 ] , 0.5 , . inverseEmpiricalCDF, 6 ) ,
30+ ( [ 5 , 9 , 2 , 7 , 1 , 8 , 3 , 6 , 4 ] , 0.5 , . inverseEmpiricalCDF, 5 ) ,
31+ ( [ 7 , 3 , 10 , 1 , 8 , 4 , 9 , 2 , 5 , 6 ] , 0.5 , . averagedInverseEmpiricalCDF, 5.5 ) ,
32+ ( [ 5 , 9 , 2 , 7 , 1 , 8 , 3 , 6 , 4 ] , 0.5 , . averagedInverseEmpiricalCDF, 5 ) ,
33+ ( [ 7 , 3 , 10 , 1 , 8 , 4 , 9 , 2 , 5 , 6 ] , 0.5 , . closest, 5 ) ,
34+ ( [ 5 , 9 , 2 , 7 , 1 , 8 , 3 , 6 , 4 ] , 0.5 , . closest, 5 ) ,
35+ ( [ 7 , 3 , 10 , 1 , 8 , 4 , 9 , 2 , 5 , 6 ] , 0.5 , . lerpInverseEmpiricalCDF, 5.5 ) ,
36+ ( [ 5 , 9 , 2 , 7 , 1 , 8 , 3 , 6 , 4 ] , 0.5 , . lerpInverseEmpiricalCDF, 5 ) ,
37+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.25 , . inverseEmpiricalCDF, 6 ) ,
38+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.5 , . inverseEmpiricalCDF, 11 ) ,
39+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.75 , . inverseEmpiricalCDF, 16 ) ,
40+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.25 , . averagedInverseEmpiricalCDF, 5.5 ) ,
41+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.5 , . averagedInverseEmpiricalCDF, 10.5 ) ,
42+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.75 , . averagedInverseEmpiricalCDF, 15.5 ) ,
43+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.25 , . closest, 6 ) ,
44+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.5 , . closest, 11 ) ,
45+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.75 , . closest, 15 ) ,
46+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.25 , . lerpInverseEmpiricalCDF, 5.75 ) ,
47+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.5 , . lerpInverseEmpiricalCDF, 10.5 ) ,
48+ ( [ 15 , 3 , 8 , 20 , 1 , 12 , 7 , 18 , 5 , 10 , 14 , 2 , 9 , 16 , 4 , 11 , 19 , 6 , 13 , 17 ] , 0.75 , . lerpInverseEmpiricalCDF, 15.25 ) ,
49+ ( [ 1 , 2 ] , 0 , . inverseEmpiricalCDF, 1 ) ,
50+ ( [ 1 , 2 ] , 1 , . inverseEmpiricalCDF, 2 ) ,
51+ ( [ 1 , 2 ] , 0.25 , . inverseEmpiricalCDF, 2 ) ,
52+ ( [ 1 , 2 ] , 0.75 , . inverseEmpiricalCDF, 2 ) ,
53+ ( [ 1 , 2 ] , 0 , . averagedInverseEmpiricalCDF, 1 ) ,
54+ ( [ 1 , 2 ] , 1 , . averagedInverseEmpiricalCDF, 2 ) ,
55+ ( [ 1 , 2 ] , 0.25 , . averagedInverseEmpiricalCDF, 1.5 ) ,
56+ ( [ 1 , 2 ] , 0.75 , . averagedInverseEmpiricalCDF, 1.5 ) ,
57+ ( [ 1 , 2 ] , 0 , . closest, 1 ) ,
58+ ( [ 1 , 2 ] , 1 , . closest, 2 ) ,
59+ ( [ 1 , 2 ] , 0.25 , . closest, 1 ) ,
60+ ( [ 1 , 2 ] , 0.75 , . closest, 2 ) ,
61+ ( [ 1 , 2 ] , 0 , . lerpInverseEmpiricalCDF, 1 ) ,
62+ ( [ 1 , 2 ] , 1 , . lerpInverseEmpiricalCDF, 2 ) ,
63+ ( [ 1 , 2 ] , 0.25 , . lerpInverseEmpiricalCDF, 1.25 ) ,
64+ ( [ 1 , 2 ] , 0.75 , . lerpInverseEmpiricalCDF, 1.75 ) ,
65+ ( [ 5 ] , 0.5 , . inverseEmpiricalCDF, 5 ) ,
66+ ( [ 5 ] , 0.5 , . averagedInverseEmpiricalCDF, 5 ) ,
67+ ( [ 5 ] , 0.5 , . closest, 5 ) ,
68+ ( [ 5 ] , 0.5 , . lerpInverseEmpiricalCDF, 5 ) ,
2569 ] as [ ( [ Int ] , Double , QuantileEstimationMethod , Double ) ]
2670 )
2771 func validData( data: [ Int ] , probability: Double , method: QuantileEstimationMethod , expectedQuantile: Double ) async {
@@ -33,11 +77,59 @@ struct QuantileTests {
3377 arguments: [
3478 ( [ ] , . inverseEmpiricalCDF) ,
3579 ( [ ] , . averagedInverseEmpiricalCDF) ,
36- ( [ ] , . closestOrOddIndexed ) ,
80+ ( [ ] , . closest ) ,
3781 ( [ ] , . lerpInverseEmpiricalCDF) ,
3882 ] as [ ( [ Int ] , QuantileEstimationMethod ) ]
3983 )
4084 func invalidData( data: [ Int ] , method: QuantileEstimationMethod ) async {
4185 #expect( data. quantile ( probability: 1 , of: \. self) . isNaN)
4286 }
87+
88+ @Test (
89+ " Collection with NaN values returns NaN " ,
90+ arguments: [
91+ ( [ . nan, 1.0 , 2.0 ] , 0.5 , . inverseEmpiricalCDF) ,
92+ ( [ 1.0 , . nan, 2.0 ] , 0.5 , . inverseEmpiricalCDF) ,
93+ ( [ 1.0 , 2.0 , . nan] , 0.5 , . inverseEmpiricalCDF) ,
94+ ( [ . nan, 1.0 , 2.0 ] , 0.5 , . averagedInverseEmpiricalCDF) ,
95+ ( [ . nan, 1.0 , 2.0 ] , 0.5 , . closest) ,
96+ ( [ . nan, 1.0 , 2.0 ] , 0.5 , . lerpInverseEmpiricalCDF) ,
97+ ( [ . nan, 1.0 , 2.0 ] , 0.0 , . inverseEmpiricalCDF) ,
98+ ( [ . nan, 1.0 , 2.0 ] , 1.0 , . inverseEmpiricalCDF) ,
99+ ] as [ ( [ Double ] , Double , QuantileEstimationMethod ) ]
100+ )
101+ func nanInCollection( data: [ Double ] , probability: Double , method: QuantileEstimationMethod ) async {
102+ #expect( data. quantile ( probability: probability, of: \. self, method: method) . isNaN)
103+ }
104+
105+ @Test (
106+ " Infinity values without conflicting arithmetic return the correct quantile " ,
107+ arguments: [
108+ ( [ 1.0 , 2.0 , . infinity] , 0.75 , . inverseEmpiricalCDF, Double . infinity) ,
109+ ( [ 1.0 , . infinity] , 0.5 , . inverseEmpiricalCDF, Double . infinity) ,
110+ ( [ - . infinity, 1.0 , 2.0 ] , 0.0 , . inverseEmpiricalCDF, - . infinity) ,
111+ ( [ 1.0 , 2.0 , . infinity] , 1.0 , . inverseEmpiricalCDF, Double . infinity) ,
112+ ( [ 1.0 , . infinity] , 0.75 , . closest, Double . infinity) ,
113+ ( [ - . infinity, 1.0 ] , 0.25 , . closest, - . infinity) ,
114+ ( [ 1.0 , . infinity] , 0.5 , . averagedInverseEmpiricalCDF, Double . infinity) ,
115+ ( [ - . infinity, 1.0 ] , 0.5 , . averagedInverseEmpiricalCDF, - . infinity) ,
116+ ( [ 1.0 , . infinity] , 0.5 , . lerpInverseEmpiricalCDF, Double . infinity) ,
117+ ] as [ ( [ Double ] , Double , QuantileEstimationMethod , Double ) ]
118+ )
119+ func infinityWellBehaved( data: [ Double ] , probability: Double , method: QuantileEstimationMethod , expectedQuantile: Double ) async {
120+ let result = data. quantile ( probability: probability, of: \. self, method: method)
121+ #expect( result == expectedQuantile)
122+ }
123+
124+ @Test (
125+ " Conflicting infinities under arithmetic return NaN " ,
126+ arguments: [
127+ ( [ - . infinity, . infinity] , 0.5 , . averagedInverseEmpiricalCDF) ,
128+ ( [ - . infinity, 1.0 ] , 0.5 , . lerpInverseEmpiricalCDF) ,
129+ ( [ - . infinity, . infinity] , 0.5 , . lerpInverseEmpiricalCDF) ,
130+ ] as [ ( [ Double ] , Double , QuantileEstimationMethod ) ]
131+ )
132+ func conflictingInfinitiesReturnNaN( data: [ Double ] , probability: Double , method: QuantileEstimationMethod ) async {
133+ #expect( data. quantile ( probability: probability, of: \. self, method: method) . isNaN)
134+ }
43135}
0 commit comments