001/*
002 * Units of Measurement Implementation for Java SE
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM.
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-363 nor the names of its 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 tec.uom.se.function;
031
032import java.util.Objects;
033import java.util.function.BinaryOperator;
034
035import javax.measure.Quantity;
036import javax.measure.Unit;
037
038import tec.uom.se.quantity.Quantities;
039
040/**
041 * @author Otavio
042 * @author Werner
043 * @version 1.0
044 * @since 1.0
045 * @param <Q>
046 */
047public class QuantitySummaryStatistics<Q extends Quantity<Q>> {
048
049  private final Quantity<Q> empty;
050
051  private long count;
052
053  private Quantity<Q> min;
054
055  private Quantity<Q> max;
056
057  private Quantity<Q> sum;
058
059  private Quantity<Q> average;
060
061  private final BinaryOperator<Quantity<Q>> minFunctions = QuantityFunctions.min();
062
063  private final BinaryOperator<Quantity<Q>> maxFunctions = QuantityFunctions.max();
064
065  /**
066   * Creates a new instance, targeting the given {@link javax.measure.Unit}.
067   * 
068   * @param unit
069   *          the target unit, not null.
070   */
071  QuantitySummaryStatistics(Unit<Q> unit) {
072    empty = Quantities.getQuantity(0, unit);
073    setQuantity(empty);
074  }
075
076  /**
077   * Records another value into the summary information.
078   * 
079   * @param quantity
080   *          the input quantity value to be added, not null.
081   */
082  public void accept(Quantity<Q> quantity) {
083
084    Objects.requireNonNull(quantity);
085
086    if (isEmpty()) {
087      setQuantity(quantity.to(empty.getUnit()));
088      count++;
089    } else {
090      doSummary(quantity.to(empty.getUnit()));
091    }
092  }
093
094  /**
095   * Combines the state of another {@code QuantitySummaryStatistics} into this one.
096   * 
097   * @param quantitySummary
098   *          another {@code QuantitySummaryStatistics}, not null.
099   */
100  public QuantitySummaryStatistics<Q> combine(QuantitySummaryStatistics<Q> quantitySummary) {
101    Objects.requireNonNull(quantitySummary);
102
103    if (!equals(quantitySummary)) {
104      return this;
105    }
106
107    min = minFunctions.apply(min, quantitySummary.min.to(empty.getUnit()));
108    max = maxFunctions.apply(max, quantitySummary.max.to(empty.getUnit()));
109    sum = sum.add(quantitySummary.sum);
110    count += quantitySummary.count;
111    average = sum.divide(count);
112    return this;
113  }
114
115  private void doSummary(Quantity<Q> moneraty) {
116    min = minFunctions.apply(min, moneraty);
117    max = maxFunctions.apply(max, moneraty);
118    sum = sum.add(moneraty);
119    average = sum.divide(++count);
120  }
121
122  private boolean isEmpty() {
123    return count == 0;
124  }
125
126  private void setQuantity(Quantity<Q> quantity) {
127    min = quantity;
128    max = quantity;
129    sum = quantity;
130    average = quantity;
131  }
132
133  /**
134   * Get the number of items added to this summary instance.
135   * 
136   * @return the number of summarized items, >= 0.
137   */
138  public long getCount() {
139    return count;
140  }
141
142  /**
143   * Get the minimal quantity found within this summary.
144   * 
145   * @return the minimal quantity
146   */
147  public Quantity<Q> getMin() {
148    return min;
149  }
150
151  /**
152   * Get the minimal quantity found within this summary converted to unit
153   * 
154   * @param unit
155   *          to convert
156   * @return the minimal quantity converted to this unit
157   */
158  public Quantity<Q> getMin(Unit<Q> unit) {
159    return min.to(unit);
160  }
161
162  /**
163   * Get the maximal amount found within this summary.
164   * 
165   * @return the maximal quantity
166   */
167  public Quantity<Q> getMax() {
168    return max;
169  }
170
171  /**
172   * Get the maximal amount found within this summary converted to unit
173   * 
174   * @param unit
175   *          to convert
176   * @return the maximal quantity converted to this unit
177   */
178  public Quantity<Q> getMax(Unit<Q> unit) {
179    return max.to(unit);
180  }
181
182  /**
183   * Get the sum of all amounts within this summary.
184   * 
185   * @return the total amount
186   */
187  public Quantity<Q> getSum() {
188    return sum;
189  }
190
191  /**
192   * Get the sum of all amounts within this summary converted to unit
193   * 
194   * @param unit
195   *          to convert
196   * @return the total amount converted to this unit
197   */
198  public Quantity<Q> getSum(Unit<Q> unit) {
199    return sum.to(unit);
200  }
201
202  /**
203   * Get the quantity average of all amounts added.
204   * 
205   * @return the quantity average quantity
206   */
207  public Quantity<Q> getAverage() {
208    return average;
209  }
210
211  /**
212   * Get the quantity average of all amounts added converted to unit
213   * 
214   * @param unit
215   *          to convert
216   * @return the average quantity converted to this unit
217   */
218  public Quantity<Q> getAverage(Unit<Q> unit) {
219    return average.to(unit);
220  }
221
222  /**
223   * convert the summary to this unit measure
224   * 
225   * @param unit
226   *          to convert the summary
227   * @return the summary converted to this unit
228   */
229  public QuantitySummaryStatistics<Q> to(Unit<Q> unit) {
230    QuantitySummaryStatistics<Q> summary = new QuantitySummaryStatistics<>(unit);
231    summary.average = average.to(unit);
232    summary.count = count;
233    summary.max = max.to(unit);
234    summary.min = min.to(unit);
235    summary.sum = sum.to(unit);
236    return summary;
237  }
238
239  /**
240   * will equals when the unit were equals
241   */
242  @Override
243  public boolean equals(Object obj) {
244    if (QuantitySummaryStatistics.class.isInstance(obj)) {
245      @SuppressWarnings("rawtypes")
246      QuantitySummaryStatistics other = QuantitySummaryStatistics.class.cast(obj);
247      return Objects.equals(empty.getUnit(), other.empty.getUnit());
248    }
249    return false;
250  }
251
252  @Override
253  public int hashCode() {
254    return empty.getUnit().hashCode();
255  }
256
257  @Override
258  public String toString() {
259    final StringBuilder sb = new StringBuilder();
260    sb.append("[currency: ").append(empty.getUnit()).append(",");
261    sb.append("count:").append(count).append(",");
262    sb.append("min:").append(min).append(",");
263    sb.append("max:").append(max).append(",");
264    sb.append("sum:").append(sum).append(",");
265    sb.append("average:").append(average).append("]");
266    return sb.toString();
267  }
268}