001/*
002 * Units of Measurement Common Library for Java
003 * Copyright (c) 2005-2023, Werner Keil and others.
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.uom.lib.common.function;
031
032import java.util.Comparator;
033import java.util.Objects;
034import java.util.function.BinaryOperator;
035import java.util.function.Function;
036import java.util.function.Predicate;
037import javax.measure.Quantity;
038import javax.measure.Unit;
039import tech.uom.lib.common.util.NaturalQuantityComparator;
040
041/**
042 * Utility methods for operating on {@link Unit units} and {@link Quantity quantities} with Java
043 * {@linkplain FunctionalInterface functional interfaces} like {@linkplain Function}.
044 *
045 * @author Otavio
046 * @author Werner
047 * @version 1.0.3
048 * @since 2.0
049 */
050@SuppressWarnings("rawtypes")
051public final class QuantityFunctions {
052
053    private QuantityFunctions() {
054        throw new Error("no instances");
055    }
056
057    /**
058     * Creates a comparator to sort by number, ignoring the unit.
059     *
060     * @param <Q> the type of quantity
061     * @return <p>
062     * <b>Given:</b>
063     * <p>
064     * <code>
065     * Quantity&lt;Time&gt; day = timeFactory.create(1, Units.DAY);<br>
066     * Quantity&lt;Time&gt; hours = timeFactory.create(18, Units.HOUR);<br>
067     * Quantity&lt;Time&gt; minutes = timeFactory.create(15, Units.HOUR);<br>
068     * Quantity&lt;Time&gt; seconds = timeFactory.create(100, Units.HOUR);<br>
069     * </code>
070     * <p>
071     * will return: <code>day, hours, minutes, seconds</code>
072     * </p>
073     * @throws NullPointerException if any of the values to compare is {@code null}
074     */
075    public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNumber() {
076        return (q1, q2) -> Double.compare(q1.getValue().doubleValue(), q2.getValue().doubleValue());
077    }
078
079    /**
080     * Creates a comparator to sort by number descending, ignoring the unit.
081     *
082     * @param <Q> the type of quantity
083     * @return <p>
084     * <b>Given:</b>
085     * <p>
086     * <code>
087     * Quantity&lt;Time&gt; day = timeFactory.create(1, Units.DAY);<br>
088     * Quantity&lt;Time&gt; hours = timeFactory.create(18, Units.HOUR);<br>
089     * Quantity&lt;Time&gt; minutes = timeFactory.create(15, Units.HOUR);<br>
090     * Quantity&lt;Time&gt; seconds = timeFactory.create(100, Units.HOUR);<br>
091     * </code>
092     * <p>
093     * will return: <code>seconds, hours, minutes, day</code>
094     * </p>
095     * @throws NullPointerException if any of the values to compare is {@code null}
096     */
097    public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNumberDesc() {
098        Comparator<Quantity<Q>> sortNumber = sortNumber();
099        return sortNumber.reversed();
100    }
101
102    /**
103     * Creates a comparator to sort by name, ignoring the value.
104     *
105     * @param <Q> the type of quantity
106     * @return <p>
107     * <b>Given:</b>
108     * <p>
109     * <code>
110     * Quantity&lt;Time&gt; day = timeFactory.create(1, Units.DAY);<br>
111     * Quantity&lt;Time&gt; hours = timeFactory.create(18, Units.HOUR);<br>
112     * Quantity&lt;Time&gt; minutes = timeFactory.create(15, Units.HOUR);<br>
113     * Quantity&lt;Time&gt; seconds = timeFactory.create(100, Units.HOUR);<br>
114     * </code>
115     * <p>
116     * will return: <code>day, hours, minutes, seconds</code>
117     * </p>
118     * @throws NullPointerException if any of the values to compare is {@code null}
119     */
120    public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortSymbol() {
121        return (q1, q2) -> q1.getUnit().getSymbol().compareTo(q2.getUnit().getSymbol());
122    }
123
124    /**
125     * Creates a comparator to sort by name descending, ignoring the value.
126     *
127     * @param <Q> the type of quantity
128     * @return <p>
129     * <b>Given:</b>
130     * </p>
131     * <code>
132     * Quantity&lt;Time&gt; day = timeFactory.create(1, Units.DAY);<br>
133     * Quantity&lt;Time&gt; hours = timeFactory.create(18, Units.HOUR);<br>
134     * Quantity&lt;Time&gt; minutes = timeFactory.create(15, Units.HOUR);<br>
135     * Quantity&lt;Time&gt; seconds = timeFactory.create(100, Units.HOUR);<br>
136     * </code>
137     * <p>
138     * will return: <code>seconds, minutes, hour,  day</code>
139     * </p>
140     * @throws NullPointerException if any of the values to compare is {@code null}
141     */
142    public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortSymbolDesc() {
143        Comparator<Quantity<Q>> sortSymbol = sortSymbol();
144        return sortSymbol.reversed();
145    }
146
147    /**
148     * Creates a comparator to sort by natural order, looking to both the unit and the value.
149     *
150     * @param <Q> the type of quantity
151     * @return <p>
152     * <b>Given:</b>
153     * </p>
154     * <code>
155     * Quantity&lt;Time&gt; day = timeFactory.create(1, Units.DAY);<br>
156     * Quantity&lt;Time&gt; hours = timeFactory.create(18, Units.HOUR);<br>
157     * Quantity&lt;Time&gt; minutes = timeFactory.create(15, Units.HOUR);<br>
158     * Quantity&lt;Time&gt; seconds = timeFactory.create(100, Units.HOUR);<br>
159     * </code>
160     * <p>
161     * will return: <code>seconds, minutes, hours, day</code>
162     * </p>
163     * @throws NullPointerException if any of the values to compare is {@code null}
164     */
165    @SuppressWarnings("unchecked")
166    public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNatural() {
167        return new NaturalQuantityComparator();
168    }
169
170    /**
171     * Creates a comparator to sort by natural order descending, looking to both the unit and the value.
172     *
173     * @param <Q> the type of quantity
174     * @return <p>
175     * <b>Given:</b>
176     * </p>
177     * <code>
178     * Quantity&lt;Time&gt; day = timeFactory.create(1, Units.DAY);<br>
179     * Quantity&lt;Time&gt; hours = timeFactory.create(18, Units.HOUR);<br>
180     * Quantity&lt;Time&gt; minutes = timeFactory.create(15, Units.HOUR);<br>
181     * Quantity&lt;Time&gt; seconds = timeFactory.create(100, Units.HOUR);<br>
182     * </code>
183     * <p>
184     * will return: <code>day, hour, minute, second</code>
185     * </p>
186     * @throws NullPointerException if any of the values to compare is {@code null}
187     */
188    public static <Q extends Quantity<Q>> Comparator<Quantity<Q>> sortNaturalDesc() {
189        Comparator<Quantity<Q>> sortNatural = sortNatural();
190        return sortNatural.reversed();
191    }
192
193    /**
194     * Creates a BinaryOperator to calculate the minimum Quantity
195     *
196     * @param <Q> the type of quantity
197     * @return the min BinaryOperator, not null.
198     */
199    public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> min() {
200
201        return (q1, q2) -> {
202            double d1 = q1.getValue().doubleValue();
203            double d2 = q2.to(q1.getUnit()).getValue().doubleValue();
204            double min = Double.min(d1, d2);
205            if (min == d1) {
206                return q1;
207            }
208            return q2;
209        };
210    }
211
212    /**
213     * Creates a BinaryOperator to calculate the maximum Quantity
214     *
215     * @param <Q> the type of quantity
216     * @return the max BinaryOperator, not null.
217     */
218    public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> max() {
219
220        return (q1, q2) -> {
221            double d1 = q1.getValue().doubleValue();
222            double d2 = q2.to(q1.getUnit()).getValue().doubleValue();
223            double min = Double.max(d1, d2);
224            if (min == d1) {
225                return q1;
226            }
227            return q2;
228        };
229    }
230
231    /**
232     * Creates a BinaryOperator to sum.
233     *
234     * @param <Q> the type of quantity
235     * @return the sum BinaryOperator
236     */
237    public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> sum() {
238        return Quantity::add;
239    }
240
241    /**
242     * Creates a BinaryOperator to sum converting to unit
243     *
244     * @param unit unit to be converting
245     * @param <Q> the type of quantity
246     * @return the sum BinaryOperator converting to unit
247     */
248    public static <Q extends Quantity<Q>> BinaryOperator<Quantity<Q>> sum(Unit<Q> unit) {
249        return (q1, q2) -> q1.to(unit).add(q2.to(unit));
250    }
251
252    /**
253     * Predicate to filter to one or more units
254     *
255     * @param units - units to be filtered (optional)
256     * @param <Q> the type of quantity
257     * @return A predicate to filter one or more units
258     */
259    @SafeVarargs
260    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> fiterByUnit(Unit<Q>... units) {
261
262        if (Objects.isNull(units) || units.length == 0) {
263            return q -> true;
264        }
265        Predicate<Quantity<Q>> predicate = null;
266        for (Unit<Q> u : units) {
267            if (Objects.isNull(predicate)) {
268                predicate = q -> q.getUnit().equals(u);
269            } else {
270                predicate = predicate.or(q -> q.getUnit().equals(u));
271            }
272        }
273        return predicate;
274    }
275
276    /**
277     * Predicate to filter excluding these units
278     *
279     * @param units - units to be filtered (optional)
280     * @param <Q> the type of quantity
281     * @return A predicate to filter to not be these units
282     */
283    @SafeVarargs
284    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> fiterByExcludingUnit(Unit<Q>... units) {
285        if (Objects.isNull(units) || units.length == 0) {
286            return q -> true;
287        }
288        return fiterByUnit(units).negate();
289    }
290
291    /**
292     * creates a Filter to greater than number, ignoring units
293     *
294     * @param value - the value to be used in Predicate
295     * @param <Q> the type of quantity
296     * @return the Predicate greater than this number, ignoring units
297     */
298    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThan(Number value) {
299        return q -> q.getValue().doubleValue() > value.doubleValue();
300    }
301
302    /**
303     * creates a filter to greater than the quantity measure
304     *
305     * @param quantity - the measure to be used in filter
306     * @param <Q> the type of quantity
307     * @return the Predicate greater than this measure
308     */
309    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThan(Quantity<Q> quantity) {
310        return q -> q.to(quantity.getUnit()).getValue().doubleValue() > quantity.getValue().doubleValue();
311    }
312
313    /**
314     * creates a Filter to greater or equals than number, ignoring units
315     *
316     * @param value - the value to be used in Predicate
317     * @param <Q> the type of quantity
318     * @return the Predicate greater or equals than this number, ignoring units
319     */
320    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThanOrEqualTo(Number value) {
321        return q -> q.getValue().doubleValue() >= value.doubleValue();
322    }
323
324    /**
325     * creates a filter to greater or equals than the quantity measure
326     *
327     * @param quantity - the measure to be used in filter
328     * @param <Q> the type of quantity
329     * @return the Predicate greater or equals than this measure
330     */
331    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isGreaterThanOrEqualTo(Quantity<Q> quantity) {
332        return q -> q.to(quantity.getUnit()).getValue().doubleValue() >= quantity.getValue().doubleValue();
333    }
334
335    /**
336     * creates a Filter to lesser than number, ignoring units
337     *
338     * @param value - the value to be used in Predicate
339     * @param <Q> the type of quantity
340     * @return the Predicate greater than this number, ignoring units
341     */
342    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThan(Number value) {
343        return q -> q.getValue().doubleValue() < value.doubleValue();
344    }
345
346    /**
347     * creates a filter to lesser than the quantity measure
348     *
349     * @param quantity - the measure to be used in filter
350     * @param <Q> the type of quantity
351     * @return the Predicate lesser than this measure
352     */
353    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThan(Quantity<Q> quantity) {
354        return q -> q.to(quantity.getUnit()).getValue().doubleValue() < quantity.getValue().doubleValue();
355    }
356
357    /**
358     * creates a Filter to lesser or equals than number, ignoring units
359     *
360     * @param value - the value to be used in Predicate
361     * @param <Q> the type of quantity
362     * @return the Predicate lesser or equals than this number, ignoring units
363     */
364    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThanOrEqualTo(Number value) {
365        return q -> q.getValue().doubleValue() <= value.doubleValue();
366    }
367
368    /**
369     * creates a filter to lesser or equals than the quantity measure
370     *
371     * @param quantity - the measure to be used in filter
372     * @param <Q> the type of quantity
373     * @return the Predicate lesser or equals than this measure
374     */
375    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isLessThanOrEqualTo(Quantity<Q> quantity) {
376        return q -> q.to(quantity.getUnit()).getValue().doubleValue() <= quantity.getValue().doubleValue();
377    }
378
379    /**
380     * creates a Filter to between, lesser or equals and greater or equals, than number, ignoring units
381     *
382     * @param min - the min value to be used in Predicate
383     * @param max - the max value to be used in Predicate
384     * @param <Q> the type of quantity
385     * @return the Predicate lesser or equals than this number, ignoring units
386     */
387    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isBetween(Number min, Number max) {
388        Predicate<Quantity<Q>> minFilter = isGreaterThanOrEqualTo(min);
389        Predicate<Quantity<Q>> maxFilter = isLessThanOrEqualTo(max);
390        return minFilter.and(maxFilter);
391    }
392
393    /**
394     * creates a filter to between, lesser or equals and greater or equals, than the quantity measure
395     *
396     * @param min - the min value to be used in Predicate
397     * @param max - the max value to be used in Predicate
398     * @param <Q> the type of quantity
399     * @return the Predicate lesser or equals than this measure
400     */
401    public static <Q extends Quantity<Q>> Predicate<Quantity<Q>> isBetween(Quantity<Q> min, Quantity<Q> max) {
402        return isGreaterThanOrEqualTo(min).and(isLessThanOrEqualTo(max));
403    }
404
405    /**
406     * creates a function to group quantities by their {@link Unit unit}.
407     *
408     * @param <Q> the type of quantity
409     * @return the Function of {@link Quantity} grouped by {@link Unit}
410     */
411    public static <Q extends Quantity<Q>> Function<Quantity<Q>, Unit<Q>> groupByUnit() {
412        return Quantity::getUnit;
413    }
414}