001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Units of Measurement project.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products
017 *    derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package tech.units.indriya.function;
031
032import static tech.uom.lib.common.function.QuantityFunctions.*;
033
034import java.util.Objects;
035import java.util.function.BinaryOperator;
036
037import javax.measure.Quantity;
038import javax.measure.Unit;
039
040import tech.units.indriya.quantity.Quantities;
041
042/**
043 * @author Otavio
044 * @author Werner
045 * @version 1.2
046 * @since 1.0
047 * @param <Q>
048 */
049public class QuantitySummaryStatistics<Q extends Quantity<Q>> {
050
051  private final Quantity<Q> empty;
052
053  private long count;
054
055  private Quantity<Q> min;
056
057  private Quantity<Q> max;
058
059  private Quantity<Q> sum;
060
061  private Quantity<Q> average;
062
063  private final BinaryOperator<Quantity<Q>> minFunctions = min();
064
065  private final BinaryOperator<Quantity<Q>> maxFunctions = max();
066
067  /**
068   * Creates a new instance, targeting the given {@link javax.measure.Unit}.
069   * 
070   * @param unit
071   *          the target unit, not null.
072   */
073  QuantitySummaryStatistics(Unit<Q> unit) {
074    empty = Quantities.getQuantity(0, unit);
075    setQuantity(empty);
076  }
077
078  /**
079   * Records another value into the summary information.
080   * 
081   * @param quantity
082   *          the input quantity value to be added, not null.
083   */
084  public void accept(Quantity<Q> quantity) {
085
086    Objects.requireNonNull(quantity);
087
088    if (isEmpty()) {
089      setQuantity(quantity.to(empty.getUnit()));
090      count++;
091    } else {
092      doSummary(quantity.to(empty.getUnit()));
093    }
094  }
095
096  /**
097   * Combines the state of another {@code QuantitySummaryStatistics} into this one.
098   * 
099   * @param quantitySummary
100   *          another {@code QuantitySummaryStatistics}, not null.
101   */
102  public QuantitySummaryStatistics<Q> combine(QuantitySummaryStatistics<Q> quantitySummary) {
103    Objects.requireNonNull(quantitySummary);
104
105    if (!equals(quantitySummary)) {
106      return this;
107    }
108
109    min = minFunctions.apply(min, quantitySummary.min.to(empty.getUnit()));
110    max = maxFunctions.apply(max, quantitySummary.max.to(empty.getUnit()));
111    sum = sum.add(quantitySummary.sum);
112    count += quantitySummary.count;
113    average = sum.divide(count);
114    return this;
115  }
116
117  private void doSummary(Quantity<Q> moneraty) {
118    min = minFunctions.apply(min, moneraty);
119    max = maxFunctions.apply(max, moneraty);
120    sum = sum.add(moneraty);
121    average = sum.divide(++count);
122  }
123
124  private boolean isEmpty() {
125    return count == 0;
126  }
127
128  private void setQuantity(Quantity<Q> quantity) {
129    min = quantity;
130    max = quantity;
131    sum = quantity;
132    average = quantity;
133  }
134
135  /**
136   * Get the number of items added to this summary instance.
137   * 
138   * @return the number of summarized items, >= 0.
139   */
140  public long getCount() {
141    return count;
142  }
143
144  /**
145   * Get the minimal quantity found within this summary.
146   * 
147   * @return the minimal quantity
148   */
149  public Quantity<Q> getMin() {
150    return min;
151  }
152
153  /**
154   * Get the minimal quantity found within this summary converted to unit
155   * 
156   * @param unit
157   *          to convert
158   * @return the minimal quantity converted to this unit
159   */
160  public Quantity<Q> getMin(Unit<Q> unit) {
161    return min.to(unit);
162  }
163
164  /**
165   * Get the maximal amount found within this summary.
166   * 
167   * @return the maximal quantity
168   */
169  public Quantity<Q> getMax() {
170    return max;
171  }
172
173  /**
174   * Get the maximal amount found within this summary converted to unit
175   * 
176   * @param unit
177   *          to convert
178   * @return the maximal quantity converted to this unit
179   */
180  public Quantity<Q> getMax(Unit<Q> unit) {
181    return max.to(unit);
182  }
183
184  /**
185   * Get the sum of all amounts within this summary.
186   * 
187   * @return the total amount
188   */
189  public Quantity<Q> getSum() {
190    return sum;
191  }
192
193  /**
194   * Get the sum of all amounts within this summary converted to unit
195   * 
196   * @param unit
197   *          to convert
198   * @return the total amount converted to this unit
199   */
200  public Quantity<Q> getSum(Unit<Q> unit) {
201    return sum.to(unit);
202  }
203
204  /**
205   * Get the quantity average of all amounts added.
206   * 
207   * @return the quantity average quantity
208   */
209  public Quantity<Q> getAverage() {
210    return average;
211  }
212
213  /**
214   * Get the quantity average of all amounts added converted to unit
215   * 
216   * @param unit
217   *          to convert
218   * @return the average quantity converted to this unit
219   */
220  public Quantity<Q> getAverage(Unit<Q> unit) {
221    return average.to(unit);
222  }
223
224  /**
225   * convert the summary to this unit measure
226   * 
227   * @param unit
228   *          to convert the summary
229   * @return the summary converted to this unit
230   */
231  public QuantitySummaryStatistics<Q> to(Unit<Q> unit) {
232    QuantitySummaryStatistics<Q> summary = new QuantitySummaryStatistics<>(unit);
233    summary.average = average.to(unit);
234    summary.count = count;
235    summary.max = max.to(unit);
236    summary.min = min.to(unit);
237    summary.sum = sum.to(unit);
238    return summary;
239  }
240
241  /**
242   * will equals when the unit were equals
243   */
244  @Override
245  public boolean equals(Object obj) {
246    if (QuantitySummaryStatistics.class.isInstance(obj)) {
247      @SuppressWarnings("rawtypes")
248      QuantitySummaryStatistics other = QuantitySummaryStatistics.class.cast(obj);
249      return Objects.equals(empty.getUnit(), other.empty.getUnit());
250    }
251    return false;
252  }
253
254  @Override
255  public int hashCode() {
256    return empty.getUnit().hashCode();
257  }
258
259  @Override
260  public String toString() {
261    final StringBuilder sb = new StringBuilder();
262    sb.append("[unit: ").append(empty.getUnit()).append(",");
263    sb.append("count:").append(count).append(",");
264    sb.append("min:").append(min).append(",");
265    sb.append("max:").append(max).append(",");
266    sb.append("sum:").append(sum).append(",");
267    sb.append("average:").append(average).append("]");
268    return sb.toString();
269  }
270}