Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 4a8cf662973e8b18c30f160b15bac955e53dcdc8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
/*
 * Copyright (c) OSGi Alliance (2002, 2016). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.osgi.util.measurement;

/**
 * Represents a value with an error, a unit and a time-stamp.
 * 
 * <p>
 * A {@code Measurement} object is used for maintaining the tuple of value,
 * error, unit and time-stamp. The value and error are represented as doubles
 * and the time is measured in milliseconds since midnight, January 1, 1970 UTC.
 * 
 * <p>
 * Mathematic methods are provided that correctly calculate taking the error
 * into account. A runtime error will occur when two measurements are used in an
 * incompatible way. E.g., when a speed (m/s) is added to a distance (m). The
 * measurement class will correctly track changes in unit during multiplication
 * and division, always coercing the result to the most simple form. See
 * {@link Unit} for more information on the supported units.
 * 
 * <p>
 * Errors in the measurement class are absolute errors. Measurement errors
 * should use the P95 rule. Actual values must fall in the range value +/- error
 * 95% or more of the time.
 * 
 * <p>
 * A {@code Measurement} object is immutable in order to be easily shared.
 * 
 * <p>
 * Note: This class has a natural ordering that is inconsistent with equals. See
 * {@link #compareTo(Object)}.
 * 
 * @Immutable
 * @author $Id$
 */
public class Measurement implements Comparable<Object> {
	private final double		value;
	private final double		error;
	private final long			time;
	private final Unit			unit;
	private transient String	name;
	private transient int		hashCode;

	/**
	 * Create a new {@code Measurement} object.
	 * 
	 * @param value The value of the {@code Measurement}.
	 * @param error The error of the {@code Measurement}.
	 * @param unit The {@code Unit} object in which the value is measured. If
	 *        this argument is {@code null}, then the unit will be set to
	 *        {@link Unit#unity}.
	 * @param time The time measured in milliseconds since midnight, January 1,
	 *        1970 UTC.
	 */
	public Measurement(double value, double error, Unit unit, long time) {
		this.value = value;
		this.error = Math.abs(error);
		this.unit = (unit != null) ? unit : Unit.unity;
		this.time = time;
		name = null;
		hashCode = 0;
	}

	/**
	 * Create a new {@code Measurement} object with a time of zero.
	 * 
	 * @param value The value of the {@code Measurement}.
	 * @param error The error of the {@code Measurement}.
	 * @param unit The {@code Unit} object in which the value is measured. If
	 *        this argument is {@code null}, then the unit will be set to
	 *        {@link Unit#unity}.
	 */
	public Measurement(double value, double error, Unit unit) {
		this(value, error, unit, 0l);
	}

	/**
	 * Create a new {@code Measurement} object with an error of 0.0 and a time
	 * of zero.
	 * 
	 * @param value The value of the {@code Measurement}.
	 * @param unit The {@code Unit} in which the value is measured. If this
	 *        argument is {@code null}, then the unit will be set to
	 *        {@link Unit#unity}.
	 */
	public Measurement(double value, Unit unit) {
		this(value, 0.0d, unit, 0l);
	}

	/**
	 * Create a new {@code Measurement} object with an error of 0.0, a unit of
	 * {@link Unit#unity} and a time of zero.
	 * 
	 * @param value The value of the {@code Measurement}.
	 */
	public Measurement(double value) {
		this(value, 0.0d, null, 0l);
	}

	/**
	 * Returns the value of this {@code Measurement} object.
	 * 
	 * @return The value of this {@code Measurement} object as a double.
	 */
	public final double getValue() {
		return value;
	}

	/**
	 * Returns the error of this {@code Measurement} object. The error is always
	 * a positive value.
	 * 
	 * @return The error of this {@code Measurement} as a double.
	 */
	public final double getError() {
		return error;
	}

	/**
	 * Returns the {@code Unit} object of this {@code Measurement} object.
	 * 
	 * @return The {@code Unit} object of this {@code Measurement} object.
	 * 
	 * @see Unit
	 */
	public final Unit getUnit() {
		return unit;
	}

	/**
	 * Returns the time at which this {@code Measurement} object was taken. The
	 * time is measured in milliseconds since midnight, January 1, 1970 UTC, or
	 * zero when not defined.
	 * 
	 * @return The time at which this {@code Measurement} object was taken or
	 *         zero.
	 */
	public final long getTime() {
		return time;
	}

	/**
	 * Returns a new {@code Measurement} object that is the product of this
	 * object multiplied by the specified object.
	 * 
	 * @param m The {@code Measurement} object that will be multiplied with this
	 *        object.
	 * @return A new {@code Measurement} that is the product of this object
	 *         multiplied by the specified object. The error and unit of the new
	 *         object are computed. The time of the new object is set to the
	 *         time of this object.
	 * @throws ArithmeticException If the {@code Unit} objects of this object
	 *         and the specified object cannot be multiplied.
	 * @see Unit
	 */
	public Measurement mul(Measurement m) {
		double mvalue = m.value;
		return new Measurement(value * mvalue, Math.abs(value) * m.error + error * Math.abs(mvalue), unit.mul(m.unit), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the product of this
	 * object multiplied by the specified value.
	 * 
	 * @param d The value that will be multiplied with this object.
	 * @param u The {@code Unit} of the specified value.
	 * @return A new {@code Measurement} object that is the product of this
	 *         object multiplied by the specified value. The error and unit of
	 *         the new object are computed. The time of the new object is set to
	 *         the time of this object.
	 * @throws ArithmeticException If the units of this object and the specified
	 *         value cannot be multiplied.
	 * @see Unit
	 */
	public Measurement mul(double d, Unit u) {
		return new Measurement(value * d, error * Math.abs(d), unit.mul(u), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the product of this
	 * object multiplied by the specified value.
	 * 
	 * @param d The value that will be multiplied with this object.
	 * @return A new {@code Measurement} object that is the product of this
	 *         object multiplied by the specified value. The error of the new
	 *         object is computed. The unit and time of the new object is set to
	 *         the unit and time of this object.
	 */
	public Measurement mul(double d) {
		return new Measurement(value * d, error * Math.abs(d), unit, time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the quotient of this
	 * object divided by the specified object.
	 * 
	 * @param m The {@code Measurement} object that will be the divisor of this
	 *        object.
	 * @return A new {@code Measurement} object that is the quotient of this
	 *         object divided by the specified object. The error and unit of the
	 *         new object are computed. The time of the new object is set to the
	 *         time of this object.
	 * @throws ArithmeticException If the {@code Unit} objects of this object
	 *         and the specified object cannot be divided.
	 * @see Unit
	 */
	public Measurement div(Measurement m) {
		double mvalue = m.value;
		return new Measurement(value / mvalue, (Math.abs(value) * m.error + error * Math.abs(mvalue)) / (mvalue * mvalue), unit.div(m.unit), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the quotient of this
	 * object divided by the specified value.
	 * 
	 * @param d The value that will be the divisor of this object.
	 * @param u The {@code Unit} object of the specified value.
	 * @return A new {@code Measurement} that is the quotient of this object
	 *         divided by the specified value. The error and unit of the new
	 *         object are computed. The time of the new object is set to the
	 *         time of this object.
	 * @throws ArithmeticException If the {@code Unit} objects of this object
	 *         and the specified object cannot be divided.
	 * @see Unit
	 */
	public Measurement div(double d, Unit u) {
		return new Measurement(value / d, error / Math.abs(d), unit.div(u), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the quotient of this
	 * object divided by the specified value.
	 * 
	 * @param d The value that will be the divisor of this object.
	 * @return A new {@code Measurement} object that is the quotient of this
	 *         object divided by the specified value. The error of the new
	 *         object is computed. The unit and time of the new object is set to
	 *         the {@code Unit} and time of this object.
	 */
	public Measurement div(double d) {
		return new Measurement(value / d, error / Math.abs(d), unit, time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the sum of this object
	 * added to the specified object.
	 * 
	 * The error and unit of the new object are computed. The time of the new
	 * object is set to the time of this object.
	 * 
	 * @param m The {@code Measurement} object that will be added with this
	 *        object.
	 * @return A new {@code Measurement} object that is the sum of this and m.
	 * @see Unit
	 * @throws ArithmeticException If the {@code Unit} objects of this object
	 *         and the specified object cannot be added.
	 */
	public Measurement add(Measurement m) {
		return new Measurement(value + m.value, error + m.error, unit.add(m.unit), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the sum of this object
	 * added to the specified value.
	 * 
	 * @param d The value that will be added with this object.
	 * @param u The {@code Unit} object of the specified value.
	 * @return A new {@code Measurement} object that is the sum of this object
	 *         added to the specified value. The unit of the new object is
	 *         computed. The error and time of the new object is set to the
	 *         error and time of this object.
	 * @throws ArithmeticException If the {@code Unit} objects of this object
	 *         and the specified value cannot be added.
	 * @see Unit
	 */
	public Measurement add(double d, Unit u) {
		return new Measurement(value + d, error, unit.add(u), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the sum of this object
	 * added to the specified value.
	 * 
	 * @param d The value that will be added with this object.
	 * @return A new {@code Measurement} object that is the sum of this object
	 *         added to the specified value. The error, unit, and time of the
	 *         new object is set to the error, {@code Unit} and time of this
	 *         object.
	 */
	public Measurement add(double d) {
		return new Measurement(value + d, error, unit, time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the subtraction of the
	 * specified object from this object.
	 * 
	 * @param m The {@code Measurement} object that will be subtracted from this
	 *        object.
	 * @return A new {@code Measurement} object that is the subtraction of the
	 *         specified object from this object. The error and unit of the new
	 *         object are computed. The time of the new object is set to the
	 *         time of this object.
	 * @throws ArithmeticException If the {@code Unit} objects of this object
	 *         and the specified object cannot be subtracted.
	 * @see Unit
	 */
	public Measurement sub(Measurement m) {
		return new Measurement(value - m.value, error + m.error, unit.sub(m.unit), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the subtraction of the
	 * specified value from this object.
	 * 
	 * @param d The value that will be subtracted from this object.
	 * @param u The {@code Unit} object of the specified value.
	 * @return A new {@code Measurement} object that is the subtraction of the
	 *         specified value from this object. The unit of the new object is
	 *         computed. The error and time of the new object is set to the
	 *         error and time of this object.
	 * @throws ArithmeticException If the {@code Unit} objects of this object
	 *         and the specified object cannot be subtracted.
	 * @see Unit
	 */
	public Measurement sub(double d, Unit u) {
		return new Measurement(value - d, error, unit.sub(u), time);
	}

	/**
	 * Returns a new {@code Measurement} object that is the subtraction of the
	 * specified value from this object.
	 * 
	 * @param d The value that will be subtracted from this object.
	 * @return A new {@code Measurement} object that is the subtraction of the
	 *         specified value from this object. The error, unit and time of the
	 *         new object is set to the error, {@code Unit} object and time of
	 *         this object.
	 */
	public Measurement sub(double d) {
		return new Measurement(value - d, error, unit, time);
	}

	/**
	 * Returns a {@code String} object representing this {@code Measurement}
	 * object.
	 * 
	 * @return a {@code String} object representing this {@code Measurement}
	 *         object.
	 */
	@Override
	public String toString() {
		String result = name;
		if (result == null) {
			StringBuilder sb = new StringBuilder();
			sb.append(value);
			if (error != 0.0d) {
				sb.append(" +/- ");
				sb.append(error);
			}
			String u = unit.toString();
			if (u.length() > 0) {
				sb.append(" ");
				sb.append(u);
			}
			result = sb.toString();
			name = result;
		}
		return result;
	}

	/**
	 * Compares this object with the specified object for order. Returns a
	 * negative integer, zero, or a positive integer if this object is less
	 * than, equal to, or greater than the specified object.
	 * 
	 * <p>
	 * Note: This class has a natural ordering that is inconsistent with equals.
	 * For this method, another {@code Measurement} object is considered equal
	 * if there is some {@code x} such that
	 * 
	 * <pre>
	 * getValue() - getError() &lt;= x &lt;= getValue() + getError()
	 * </pre>
	 * 
	 * for both {@code Measurement} objects being compared.
	 * 
	 * @param obj The object to be compared.
	 * @return A negative integer, zero, or a positive integer if this object is
	 *         less than, equal to, or greater than the specified object.
	 * 
	 * @throws ClassCastException If the specified object is not of type
	 *         {@code Measurement}.
	 * @throws ArithmeticException If the unit of the specified
	 *         {@code Measurement} object is not equal to the {@code Unit}
	 *         object of this object.
	 */
	@Override
	public int compareTo(Object obj) {
		if (this == obj) {
			return 0;
		}
		Measurement that = (Measurement) obj;
		if (!unit.equals(that.unit)) {
			throw new ArithmeticException("Cannot compare " + this + " and " + that);
		}
		int result = Double.compare(value, that.value);
		if (result == 0) {
			return 0;
		}
		if (result < 0) {
			if (Double.compare(value + error, that.value - that.error) >= 0) {
				return 0;
			}
			return -1;
		}
		if (Double.compare(value - error, that.value + that.error) <= 0) {
			return 0;
		}
		return 1;
	}

	/**
	 * Returns a hash code value for this object.
	 * 
	 * @return A hash code value for this object.
	 */
	@Override
	public int hashCode() {
		int h = hashCode;
		if (h == 0) {
			long bits = Double.doubleToLongBits(value);
			h = 31 * 17 + ((int) (bits ^ (bits >>> 32)));
			bits = Double.doubleToLongBits(error);
			h = 31 * h + ((int) (bits ^ (bits >>> 32)));
			h = 31 * h + unit.hashCode();
			hashCode = h;
		}
		return h;
	}

	/**
	 * Returns whether the specified object is equal to this object. Two
	 * {@code Measurement} objects are equal if they have same value, error and
	 * {@code Unit}.
	 * 
	 * <p>
	 * Note: This class has a natural ordering that is inconsistent with equals.
	 * See {@link #compareTo(Object)}.
	 * 
	 * @param obj The object to compare with this object.
	 * @return {@code true} if this object is equal to the specified object;
	 *         {@code false} otherwise.
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof Measurement)) {
			return false;
		}
		Measurement that = (Measurement) obj;
		return (Double.compare(value, that.value) == 0) && (Double.compare(error, that.error) == 0) && unit.equals(that.unit);
	}
}

Back to the top