1 package org.djutils.stats.summarizers.quantileaccumulator;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6
7 import org.djutils.exceptions.Throw;
8 import org.djutils.stats.summarizers.Tally;
9
10
11
12
13
14 public class FullStorageAccumulator implements QuantileAccumulator
15 {
16
17 private List<Double> accumulator = new ArrayList<>();
18
19
20 private boolean isSorted = true;
21
22
23
24
25 public FullStorageAccumulator()
26 {
27
28 }
29
30
31 @Override
32 public double register(final double value)
33 {
34 Throw.when(Double.isNaN(value), IllegalArgumentException.class, "accumulator can not accumlate NaN value");
35 this.accumulator.add(value);
36 this.isSorted = false;
37 return value;
38 }
39
40
41
42
43 private void ensureSorted()
44 {
45 if (!this.isSorted)
46 {
47 Collections.sort(this.accumulator);
48 this.isSorted = true;
49 }
50 }
51
52
53 @Override
54 public double getQuantile(final Tally tally, final double probability)
55 {
56 Throw.whenNull(tally, "tally cannot be null");
57 Throw.when(probability < 0 || probability > 1, IllegalArgumentException.class,
58 "Probability should be between 0.0 and 1.0 (inclusive); got {}", probability);
59 ensureSorted();
60 double doubleIndex = (this.accumulator.size() - 1) * probability;
61 int index = Math.min((int) Math.floor(doubleIndex), this.accumulator.size() - 1);
62 double v0 = this.accumulator.get(index);
63 if (index >= this.accumulator.size() - 1)
64 {
65 return v0;
66 }
67 double v1 = this.accumulator.get(index + 1);
68 return v1 * (doubleIndex - index) + v0 * (1.0 - (doubleIndex - index));
69 }
70
71
72 @Override
73 public double getCumulativeProbability(final Tally tally, final double quantile)
74 {
75 Throw.when(Double.isNaN(quantile), IllegalArgumentException.class, "quantile may not be NaN");
76 ensureSorted();
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 if (this.accumulator.size() == 0)
93 {
94 return Double.NaN;
95 }
96 int lowerBound = 0;
97 int upperBound = this.accumulator.size();
98
99 while (lowerBound < upperBound)
100 {
101 int guess = (lowerBound + upperBound) / 2;
102 if (this.accumulator.get(guess) < quantile)
103 {
104 lowerBound = guess + 1;
105 }
106 else
107 {
108 upperBound = guess;
109 }
110 }
111 int b = lowerBound;
112 upperBound = this.accumulator.size();
113 while (b < upperBound)
114 {
115 int guess = (b + upperBound) / 2;
116 double value = this.accumulator.get(guess);
117 if (value > quantile)
118 {
119 upperBound = guess;
120 }
121 else
122 {
123 b = guess + 1;
124 }
125 }
126 upperBound--;
127 if (upperBound < 0)
128 {
129 return 0.0;
130 }
131 if (lowerBound >= this.accumulator.size())
132 {
133 return 1.0;
134 }
135 double adjust = 0;
136 if (upperBound >= lowerBound)
137 {
138 adjust = 1;
139 }
140 if (upperBound < lowerBound)
141 {
142 adjust = 1;
143 }
144 return (adjust + upperBound + lowerBound) / this.accumulator.size() / 2;
145 }
146
147
148 @Override
149 public void initialize()
150 {
151 this.accumulator.clear();
152 }
153
154
155 @Override
156 public String toString()
157 {
158 return "FullStorageAccumulator [accumulator size=" + this.accumulator.size() + ", isSorted=" + this.isSorted + "]";
159 }
160
161 }