1 package org.opentrafficsim.core.distributions;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import org.djutils.exceptions.Throw;
8
9 import nl.tudelft.simulation.jstats.distributions.DistUniform;
10 import nl.tudelft.simulation.jstats.streams.StreamInterface;
11
12
13
14
15
16
17
18
19
20
21 public class Distribution<O> implements Generator<O>, Serializable
22 {
23
24 private static final long serialVersionUID = 20160301L;
25
26
27 private final List<FrequencyAndObject<O>> generators = new ArrayList<>();
28
29
30 private double cumulativeTotal;
31
32
33 private final DistUniform random;
34
35
36
37
38
39
40
41
42 public Distribution(final List<FrequencyAndObject<O>> generators, final StreamInterface stream)
43 {
44 this(stream);
45 Throw.whenNull(generators, "generators");
46
47
48 this.generators.addAll(generators);
49 fixProbabilities();
50 }
51
52
53
54
55
56
57 public Distribution(final StreamInterface stream)
58 {
59 Throw.whenNull(stream, "stream");
60 this.random = new DistUniform(stream, 0, 1);
61 }
62
63
64
65
66
67 private void fixProbabilities()
68 {
69 if (0 == this.generators.size())
70 {
71 return;
72 }
73 this.cumulativeTotal = 0;
74 for (FrequencyAndObject<O> generator : this.generators)
75 {
76 double frequency = generator.frequency();
77 this.cumulativeTotal += frequency;
78 }
79 }
80
81 @Override
82 public final O draw()
83 {
84 Throw.when(0 == this.generators.size(), IllegalStateException.class, "Cannot draw from empty collection");
85 Throw.when(0 == this.cumulativeTotal, IllegalStateException.class, "Sum of frequencies or probabilities must be > 0");
86
87 double randomValue = this.random.draw() * this.cumulativeTotal;
88 for (FrequencyAndObject<O> fAndO : this.generators)
89 {
90 double frequency = fAndO.frequency();
91 if (frequency >= randomValue)
92 {
93 return fAndO.object();
94 }
95 randomValue -= frequency;
96 }
97
98 FrequencyAndObject<O> useThisOne = this.generators.get(0);
99 for (FrequencyAndObject<O> fAndO : this.generators)
100 {
101 if (fAndO.frequency() > 0)
102 {
103 useThisOne = fAndO;
104 break;
105 }
106 }
107 return useThisOne.object();
108 }
109
110
111
112
113
114
115
116 public final Distribution<O> add(final FrequencyAndObject<O> generator)
117 {
118 Throw.whenNull(generator, "generator");
119 return add(this.generators.size(), generator);
120 }
121
122
123
124
125
126
127
128
129
130 public final Distribution<O> add(final int index, final FrequencyAndObject<O> generator)
131 {
132 Throw.whenNull(generator, "generator");
133 this.generators.add(index, generator);
134 fixProbabilities();
135 return this;
136 }
137
138
139
140
141
142
143
144 public final Distribution<O> remove(final int index)
145 {
146 this.generators.remove(index);
147 fixProbabilities();
148 return this;
149 }
150
151
152
153
154
155
156
157
158
159 public final Distribution<O> set(final int index, final FrequencyAndObject<O> generator)
160 {
161 Throw.whenNull(generator, "generator");
162 this.generators.set(index, generator);
163 fixProbabilities();
164 return this;
165 }
166
167
168
169
170
171
172
173
174
175 public final Distribution<O> modifyFrequency(final int index, final double frequency)
176 {
177 Throw.when(index < 0 || index >= this.size(), IndexOutOfBoundsException.class, "Index %s out of range (0..%d)", index,
178 this.size() - 1);
179 return set(index, new FrequencyAndObject<O>(frequency, this.generators.get(index).object()));
180 }
181
182
183
184
185
186 public final Distribution<O> clear()
187 {
188 this.generators.clear();
189 return this;
190 }
191
192
193
194
195
196
197
198 public final FrequencyAndObject<O> get(final int index)
199 {
200 return this.generators.get(index);
201 }
202
203
204
205
206
207 public final int size()
208 {
209 return this.generators.size();
210 }
211
212 @Override
213 public final int hashCode()
214 {
215 final int prime = 31;
216 int result = 1;
217 long temp;
218 temp = Double.doubleToLongBits(this.cumulativeTotal);
219 result = prime * result + (int) (temp ^ (temp >>> 32));
220 result = prime * result + ((this.generators == null) ? 0 : this.generators.hashCode());
221 result = prime * result + ((this.random == null) ? 0 : this.random.hashCode());
222 return result;
223 }
224
225 @Override
226 @SuppressWarnings("checkstyle:needbraces")
227 public final boolean equals(final Object obj)
228 {
229 if (this == obj)
230 return true;
231 if (obj == null)
232 return false;
233 if (getClass() != obj.getClass())
234 return false;
235 Distribution<?> other = (Distribution<?>) obj;
236 if (Double.doubleToLongBits(this.cumulativeTotal) != Double.doubleToLongBits(other.cumulativeTotal))
237 return false;
238 if (this.generators == null)
239 {
240 if (other.generators != null)
241 return false;
242 }
243 else if (!this.generators.equals(other.generators))
244 return false;
245 if (this.random == null)
246 {
247 if (other.random != null)
248 return false;
249 }
250 else if (!this.random.equals(other.random))
251 return false;
252 return true;
253 }
254
255 @Override
256 public final String toString()
257 {
258 StringBuilder result = new StringBuilder();
259 result.append("Distribution [");
260 String separator = "";
261 for (FrequencyAndObject<O> fAndO : this.generators)
262 {
263 result.append(separator + fAndO.frequency() + "->" + fAndO.object());
264 separator = ", ";
265 }
266 result.append(']');
267 return result.toString();
268 }
269
270 }