001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.lang;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021
022/**
023 * A simple mutable double value.
024 *
025 * <p>
026 * This class extends {@link Holder}&lt;{@link Double}&gt; and provides a convenient way to pass mutable
027 * double references to lambdas, inner classes, or methods.
028 *
029 * <h5 class='section'>Notes:</h5><ul>
030 *    <li class='note'>
031 *       This class is <b>not thread-safe</b>.
032 * </ul>
033 *
034 * <h5 class='section'>Example:</h5>
035 * <p class='bjava'>
036 *    <jc>// Create a double value to track a running sum</jc>
037 *    DoubleHolder <jv>sum</jv> = DoubleHolder.<jsm>create</jsm>();
038 *
039 *    <jc>// Use in a lambda to accumulate values</jc>
040 *    list.forEach(<jv>x</jv> -&gt; {
041 *       <jv>sum</jv>.set(<jv>sum</jv>.get() + <jv>x</jv>.doubleValue());
042 *    });
043 *
044 *    <jsm>log</jsm>(<js>"Total: "</js> + <jv>sum</jv>.get());
045 * </p>
046 *
047 * <h5 class='section'>See Also:</h5><ul>
048 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsLang">Lang Package</a>
049 * </ul>
050 */
051@SuppressWarnings({
052   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
053})
054public class DoubleHolder extends Holder<Double> {
055
056   /**
057    * Creates a new double value initialized to <c>0.0</c>.
058    *
059    * <h5 class='section'>Example:</h5>
060    * <p class='bjava'>
061    *    DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>create</jsm>();
062    *    <jsm>assertEquals</jsm>(0.0, <jv>value</jv>.get());
063    * </p>
064    *
065    * @return A new double value.
066    */
067   public static DoubleHolder create() {
068      return of(0.0);
069   }
070
071   /**
072    * Creates a new double value with the specified initial value.
073    *
074    * <h5 class='section'>Example:</h5>
075    * <p class='bjava'>
076    *    DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>of</jsm>(3.14159);
077    *    <jsm>assertEquals</jsm>(3.14159, <jv>value</jv>.get());
078    * </p>
079    *
080    * @param value The initial value.  Can be <jk>null</jk>.
081    * @return A new double value.
082    */
083   public static DoubleHolder of(Double value) {
084      return new DoubleHolder(value);
085   }
086
087   /**
088    * Constructor.
089    *
090    * @param value The initial value.  Can be <jk>null</jk>.
091    */
092   public DoubleHolder(Double value) {
093      super(value);
094   }
095
096   /**
097    * Checks if the current value equals the specified value within the given precision.
098    *
099    * <p>
100    * This method handles <jk>null</jk> values safely and performs precision-based equality comparison
101    * using absolute difference. The comparison returns <jk>true</jk> if the absolute difference between
102    * the two values is less than or equal to the specified precision.
103    *
104    * <h5 class='section'>Example:</h5>
105    * <p class='bjava'>
106    *    DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>of</jsm>(3.14159);
107    *    <jsm>assertTrue</jsm>(<jv>value</jv>.is(3.14, 0.01));      <jc>// Within 0.01</jc>
108    *    <jsm>assertFalse</jsm>(<jv>value</jv>.is(3.14, 0.001));    <jc>// Not within 0.001</jc>
109    *
110    *    <jc>// Handles null values</jc>
111    *    DoubleHolder <jv>empty</jv> = DoubleHolder.<jsm>create</jsm>();
112    *    <jv>empty</jv>.set(<jk>null</jk>);
113    *    <jsm>assertFalse</jsm>(<jv>empty</jv>.is(3.14, 0.01));     <jc>// Null doesn't match any value</jc>
114    *
115    *    <jc>// Floating-point precision example</jc>
116    *    DoubleHolder <jv>calc</jv> = DoubleHolder.<jsm>of</jsm>(0.1 + 0.2);  <jc>// Actually 0.30000000000000004</jc>
117    *    <jsm>assertTrue</jsm>(<jv>calc</jv>.is(0.3, 0.000001));    <jc>// Handles rounding errors</jc>
118    * </p>
119    *
120    * @param other The value to compare with.
121    * @param precision The maximum allowed difference for equality. Must be non-negative.
122    * @return <jk>true</jk> if the absolute difference between the values is less than or equal to precision.
123    * @throws IllegalArgumentException if precision is negative.
124    */
125   public boolean is(double other, double precision) {
126      req(precision >= 0, "Precision must be non-negative");
127      var v = get();
128      if (v == null) {
129         return false;
130      }
131      return Math.abs(v - other) <= precision;
132   }
133
134   /**
135    * Checks if the current value matches any of the specified values within the given precision.
136    *
137    * <p>
138    * This method handles <jk>null</jk> values safely and performs precision-based equality comparison
139    * for each value using absolute difference.
140    *
141    * <h5 class='section'>Example:</h5>
142    * <p class='bjava'>
143    *    DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>of</jsm>(3.14159);
144    *    <jsm>assertTrue</jsm>(<jv>value</jv>.isAny(0.01, 2.5, 3.15, 5.0));   <jc>// Matches 3.15 within 0.01</jc>
145    *    <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.001, 1.0, 2.0, 5.0));  <jc>// No match within 0.001</jc>
146    *
147    *    <jc>// Empty array returns false</jc>
148    *    <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.01));
149    * </p>
150    *
151    * @param precision The maximum allowed difference for equality. Must be non-negative and is the first parameter.
152    * @param values The values to compare to.  Must not be <jk>null</jk>.
153    * @return <jk>true</jk> if the current value matches any of the specified values within the precision.
154    * @throws IllegalArgumentException if precision is negative.
155    */
156   public boolean isAny(double precision, double...values) {
157      req(precision >= 0, "Precision must be non-negative");
158      reqnn("values", values);
159      var v = get();
160      if (v == null)
161         return false;
162      for (var value : values)
163         if (Math.abs(v - value) <= precision)
164            return true;
165      return false;
166   }
167}