View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.commons.lang;
18  
19  import static org.apache.juneau.commons.utils.CollectionUtils.*;
20  import static org.apache.juneau.junit.bct.BctAssertions.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import org.apache.juneau.*;
24  import org.junit.jupiter.api.*;
25  
26  class DoubleValue_Test extends TestBase {
27  
28  	//-----------------------------------------------------------------------------------------------------------------
29  	// Basic tests
30  	//-----------------------------------------------------------------------------------------------------------------
31  
32  	@Test
33  	void a01_create() {
34  		var v = DoubleValue.create();
35  		assertEquals(0.0, v.get());
36  	}
37  
38  	@Test
39  	void a02_of() {
40  		var v = DoubleValue.of(3.14159);
41  		assertEquals(3.14159, v.get(), 0.00001);
42  	}
43  
44  	@Test
45  	void a03_constructor() {
46  		var v = new DoubleValue(2.71828);
47  		assertEquals(2.71828, v.get(), 0.00001);
48  	}
49  
50  	@Test
51  	void a04_constructor_withNull() {
52  		var v = new DoubleValue(null);
53  		assertNull(v.get());
54  	}
55  
56  	//-----------------------------------------------------------------------------------------------------------------
57  	// Inherited Value<Double> methods
58  	//-----------------------------------------------------------------------------------------------------------------
59  
60  	@Test
61  	void b01_set() {
62  		var v = DoubleValue.create();
63  		v.set(5.5);
64  		assertEquals(5.5, v.get(), 0.00001);
65  	}
66  
67  	@Test
68  	void b02_setIfEmpty() {
69  		var v = new DoubleValue(null);
70  		v.setIfEmpty(1.5);
71  		assertEquals(1.5, v.get(), 0.00001);
72  
73  		v.setIfEmpty(2.5);
74  		assertEquals(1.5, v.get(), 0.00001); // Should not change
75  	}
76  
77  	@Test
78  	void b03_orElse() {
79  		var v = new DoubleValue(null);
80  		assertEquals(9.9, v.orElse(9.9), 0.00001);
81  
82  		v.set(4.2);
83  		assertEquals(4.2, v.orElse(9.9), 0.00001);
84  	}
85  
86  	@Test
87  	void b04_map() {
88  		var v = DoubleValue.of(5.0);
89  		var v2 = v.map(x -> x * 2);
90  		assertEquals(10.0, v2.get(), 0.00001);
91  	}
92  
93  	@Test
94  	void b05_ifPresent() {
95  		var v = DoubleValue.of(7.5);
96  		var sb = new StringBuilder();
97  		v.ifPresent(x -> sb.append(x));
98  		assertEquals("7.5", sb.toString());
99  	}
100 
101 	@Test
102 	void b06_isPresent() {
103 		var v = new DoubleValue(null);
104 		assertFalse(v.isPresent());
105 
106 		v.set(1.0);
107 		assertTrue(v.isPresent());
108 	}
109 
110 	@Test
111 	void b07_isEmpty() {
112 		var v = new DoubleValue(null);
113 		assertEmpty(v);
114 
115 		v.set(1.0);
116 		assertNotEmpty(v);
117 	}
118 
119 	@Test
120 	void b08_getAndSet() {
121 		var v = DoubleValue.of(1.5);
122 		assertEquals(1.5, v.getAndSet(2.5), 0.00001);
123 		assertEquals(2.5, v.get(), 0.00001);
124 	}
125 
126 	@Test
127 	void b09_getAndUnset() {
128 		var v = DoubleValue.of(3.5);
129 		assertEquals(3.5, v.getAndUnset(), 0.00001);
130 		assertNull(v.get());
131 	}
132 
133 	//-----------------------------------------------------------------------------------------------------------------
134 	// Use case scenarios
135 	//-----------------------------------------------------------------------------------------------------------------
136 
137 	@Test
138 	void c01_trackingPreciseSum() {
139 		var sum = DoubleValue.create();
140 
141 		l(1.111, 2.222, 3.333, 4.444).forEach(x -> {
142 			sum.set(sum.get() + x);
143 		});
144 
145 		assertEquals(11.11, sum.get(), 0.00001);
146 	}
147 
148 	@Test
149 	void c02_trackingStatistics() {
150 		var sum = DoubleValue.create();
151 		var count = DoubleValue.create();
152 
153 		l(10.5, 20.3, 15.7, 30.1).forEach(x -> {
154 			sum.set(sum.get() + x);
155 			count.set(count.get() + 1);
156 		});
157 
158 		double average = sum.get() / count.get();
159 		assertEquals(19.15, average, 0.00001);
160 	}
161 
162 	@Test
163 	void c03_trackingMaxValue() {
164 		var max = DoubleValue.of(Double.MIN_VALUE);
165 
166 		l(5.5, 12.3, 3.8, 20.1, 1.2).forEach(x -> {
167 			if (x > max.get()) {
168 				max.set(x);
169 			}
170 		});
171 
172 		assertEquals(20.1, max.get(), 0.00001);
173 	}
174 
175 	@Test
176 	void c04_compoundInterestCalculation() {
177 		var principal = DoubleValue.of(1000.0);
178 		double rate = 0.05; // 5% interest
179 
180 		// Apply interest 3 times
181 		for (var i = 0; i < 3; i++) {
182 			principal.set(principal.get() * (1 + rate));
183 		}
184 
185 		assertEquals(1157.625, principal.get(), 0.001);
186 	}
187 
188 	//-----------------------------------------------------------------------------------------------------------------
189 	// is(double, double) - precision-based equality
190 	//-----------------------------------------------------------------------------------------------------------------
191 
192 	@Test
193 	void d01_is_exactMatch() {
194 		var v = DoubleValue.of(3.14159);
195 		assertTrue(v.is(3.14159, 0.0));
196 		assertTrue(v.is(3.14159, 0.00001));
197 	}
198 
199 	@Test
200 	void d02_is_withinPrecision() {
201 		var v = DoubleValue.of(3.14159);
202 		assertTrue(v.is(3.14, 0.01));
203 		assertTrue(v.is(3.15, 0.01));
204 		assertFalse(v.is(3.14, 0.001));
205 		assertFalse(v.is(3.15, 0.001));
206 	}
207 
208 	@Test
209 	void d03_is_nullValue() {
210 		var v = new DoubleValue(null);
211 		assertFalse(v.is(3.14, 0.01));
212 		assertFalse(v.is(0.0, 0.01));
213 	}
214 
215 	@Test
216 	void d04_is_zeroPrecision() {
217 		var v = DoubleValue.of(5.0);
218 		assertTrue(v.is(5.0, 0.0));
219 		assertFalse(v.is(5.0001, 0.0));
220 	}
221 
222 	@Test
223 	void d05_is_largePrecision() {
224 		var v = DoubleValue.of(100.0);
225 		assertTrue(v.is(50.0, 100.0));
226 		assertTrue(v.is(150.0, 100.0));
227 		assertFalse(v.is(201.0, 100.0));
228 	}
229 
230 	@Test
231 	void d06_is_floatingPointRoundingError() {
232 		var v = DoubleValue.of(0.1 + 0.2); // Actually 0.30000000000000004
233 		assertFalse(v.is(0.3, 0.0)); // Exact match fails
234 		assertTrue(v.is(0.3, 0.000001)); // But within small precision
235 	}
236 
237 	@Test
238 	void d07_is_negativeValues() {
239 		var v = DoubleValue.of(-5.5);
240 		assertTrue(v.is(-5.5, 0.0));
241 		assertTrue(v.is(-5.4, 0.2));
242 		assertTrue(v.is(-5.6, 0.2));
243 		assertFalse(v.is(-5.0, 0.4));
244 	}
245 
246 	@Test
247 	void d08_is_negativePrecision_throwsException() {
248 		var v = DoubleValue.of(3.14);
249 		assertThrows(IllegalArgumentException.class, () -> v.is(3.14, -0.01));
250 	}
251 
252 	@Test
253 	void d09_is_boundaryValue() {
254 		var v = DoubleValue.of(10.0);
255 		assertTrue(v.is(10.5, 0.5)); // Exactly at boundary
256 		assertFalse(v.is(10.51, 0.5)); // Just outside boundary
257 	}
258 
259 	@Test
260 	void d10_is_verySmallNumbers() {
261 		var v = DoubleValue.of(0.0000001);
262 		assertTrue(v.is(0.0000002, 0.00000015));
263 		assertFalse(v.is(0.0000002, 0.00000005));
264 	}
265 
266 	@Test
267 	void d11_is_afterSet() {
268 		var v = DoubleValue.of(1.0);
269 		assertTrue(v.is(1.0, 0.01));
270 
271 		v.set(2.0);
272 		assertFalse(v.is(1.0, 0.01));
273 		assertTrue(v.is(2.0, 0.01));
274 	}
275 
276 	@Test
277 	void d12_is_scientificNotation() {
278 		var v = DoubleValue.of(1.23e10);
279 		assertTrue(v.is(1.23e10, 0.0));
280 		assertTrue(v.is(1.24e10, 1e8));
281 		assertFalse(v.is(1.24e10, 1e7));
282 	}
283 
284 	//-----------------------------------------------------------------------------------------------------------------
285 	// isAny(double, double...) - precision-based matching
286 	//-----------------------------------------------------------------------------------------------------------------
287 
288 	@Test
289 	void e01_isAny_withinPrecision() {
290 		var v = DoubleValue.of(3.14159);
291 		assertTrue(v.isAny(0.01, 2.5, 3.15, 5.0));  // Matches 3.15 within 0.01
292 		assertTrue(v.isAny(0.01, 3.14, 3.15));       // Matches both
293 		assertFalse(v.isAny(0.001, 1.0, 2.0, 5.0));  // No match within 0.001
294 	}
295 
296 	@Test
297 	void e02_isAny_exactMatch() {
298 		var v = DoubleValue.of(5.0);
299 		assertTrue(v.isAny(0.0, 5.0, 6.0, 7.0));
300 		assertTrue(v.isAny(0.0, 1.0, 5.0));
301 		assertFalse(v.isAny(0.0, 1.0, 2.0, 3.0));
302 	}
303 
304 	@Test
305 	void e03_isAny_nullValue() {
306 		var v = new DoubleValue(null);
307 		assertFalse(v.isAny(0.01, 1.0, 2.0, 3.0));
308 		assertFalse(v.isAny(0.0, 0.0, 1.0));
309 	}
310 
311 	@Test
312 	void e04_isAny_emptyArray() {
313 		var v = DoubleValue.of(3.14);
314 		assertFalse(v.isAny(0.01));  // Only precision, no values
315 	}
316 
317 	@Test
318 	void e05_isAny_zeroPrecision() {
319 		var v = DoubleValue.of(5.0);
320 		assertTrue(v.isAny(0.0, 5.0, 6.0));
321 		assertFalse(v.isAny(0.0, 5.0001, 6.0));
322 	}
323 
324 	@Test
325 	void e06_isAny_largePrecision() {
326 		var v = DoubleValue.of(100.0);
327 		assertTrue(v.isAny(100.0, 50.0, 150.0, 200.0));
328 		assertTrue(v.isAny(100.0, 0.0, 201.0));  // 0.0 matches (100.0 - 0.0 = 100.0 <= 100.0)
329 		assertTrue(v.isAny(100.0, 0.0, 202.0));  // 0.0 matches (100.0 - 0.0 = 100.0 <= 100.0)
330 		assertFalse(v.isAny(99.0, 0.0, 202.0));  // Neither matches (100.0 - 0.0 = 100.0 > 99.0, 100.0 - 202.0 = 102.0 > 99.0)
331 	}
332 
333 	@Test
334 	void e07_isAny_floatingPointRoundingError() {
335 		var v = DoubleValue.of(0.1 + 0.2); // Actually 0.30000000000000004
336 		assertFalse(v.isAny(0.0, 0.3, 0.4)); // Exact match fails
337 		assertTrue(v.isAny(0.000001, 0.3, 0.4)); // But within small precision
338 	}
339 
340 	@Test
341 	void e08_isAny_negativeValues() {
342 		var v = DoubleValue.of(-5.5);
343 		assertTrue(v.isAny(0.2, -5.4, -5.6, -5.0));
344 		assertTrue(v.isAny(0.0, -5.5, -6.0));
345 		assertFalse(v.isAny(0.4, -5.0, -6.0));
346 	}
347 
348 	@Test
349 	void e09_isAny_negativePrecision_throwsException() {
350 		var v = DoubleValue.of(3.14);
351 		assertThrows(IllegalArgumentException.class, () -> v.isAny(-0.01, 3.14, 3.15));
352 	}
353 
354 	@Test
355 	void e10_isAny_boundaryValue() {
356 		var v = DoubleValue.of(10.0);
357 		assertTrue(v.isAny(0.5, 9.5, 10.5, 11.0)); // 9.5 and 10.5 are exactly at boundary
358 		assertTrue(v.isAny(0.5, 9.5, 10.51, 11.0)); // 9.5 matches (10.0 - 9.5 = 0.5 <= 0.5)
359 		assertFalse(v.isAny(0.49, 9.5, 10.51, 11.0)); // None match (10.0 - 9.5 = 0.5 > 0.49, 10.0 - 10.51 = 0.51 > 0.49)
360 	}
361 
362 	@Test
363 	void e11_isAny_verySmallNumbers() {
364 		var v = DoubleValue.of(0.0000001);
365 		assertTrue(v.isAny(0.00000015, 0.0000002, 0.0000003));
366 		assertFalse(v.isAny(0.00000005, 0.0000002, 0.0000003));
367 	}
368 
369 	@Test
370 	void e12_isAny_afterSet() {
371 		var v = DoubleValue.of(1.0);
372 		assertTrue(v.isAny(0.01, 1.0, 2.0));
373 
374 		v.set(2.0);
375 		assertFalse(v.isAny(0.01, 1.0, 1.5));
376 		assertTrue(v.isAny(0.01, 2.0, 2.5));
377 	}
378 }
379