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.AssertionUtils.*;
020
021/**
022 * A simple mutable double value.
023 *
024 * <p>
025 * This class extends {@link Value}&lt;{@link Double}&gt; and provides a convenient way to pass mutable
026 * double references to lambdas, inner classes, or methods.
027 *
028 * <h5 class='section'>Notes:</h5><ul>
029 *    <li class='note'>
030 *       This class is <b>not thread-safe</b>.
031 * </ul>
032 *
033 * <h5 class='section'>Example:</h5>
034 * <p class='bjava'>
035 *    <jc>// Create a double value to track a running sum</jc>
036 *    DoubleValue <jv>sum</jv> = DoubleValue.<jsm>create</jsm>();
037 *
038 *    <jc>// Use in a lambda to accumulate values</jc>
039 *    list.forEach(<jv>x</jv> -&gt; {
040 *       <jv>sum</jv>.set(<jv>sum</jv>.get() + <jv>x</jv>.doubleValue());
041 *    });
042 *
043 *    <jsm>log</jsm>(<js>"Total: "</js> + <jv>sum</jv>.get());
044 * </p>
045 *
046 * <h5 class='section'>See Also:</h5><ul>
047 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsLang">Lang Package</a>
048 * </ul>
049 */
050public class DoubleValue extends Value<Double> {
051
052   /**
053    * Creates a new double value initialized to <c>0.0</c>.
054    *
055    * <h5 class='section'>Example:</h5>
056    * <p class='bjava'>
057    *    DoubleValue <jv>value</jv> = DoubleValue.<jsm>create</jsm>();
058    *    <jsm>assertEquals</jsm>(0.0, <jv>value</jv>.get());
059    * </p>
060    *
061    * @return A new double value.
062    */
063   public static DoubleValue create() {
064      return of(0.0);
065   }
066
067   /**
068    * Creates a new double value with the specified initial value.
069    *
070    * <h5 class='section'>Example:</h5>
071    * <p class='bjava'>
072    *    DoubleValue <jv>value</jv> = DoubleValue.<jsm>of</jsm>(3.14159);
073    *    <jsm>assertEquals</jsm>(3.14159, <jv>value</jv>.get());
074    * </p>
075    *
076    * @param value The initial value.
077    * @return A new double value.
078    */
079   public static DoubleValue of(Double value) {
080      return new DoubleValue(value);
081   }
082
083   /**
084    * Constructor.
085    *
086    * @param value The initial value.
087    */
088   public DoubleValue(Double value) {
089      super(value);
090   }
091
092   /**
093    * Checks if the current value equals the specified value within the given precision.
094    *
095    * <p>
096    * This method handles <jk>null</jk> values safely and performs precision-based equality comparison
097    * using absolute difference. The comparison returns <jk>true</jk> if the absolute difference between
098    * the two values is less than or equal to the specified precision.
099    *
100    * <h5 class='section'>Example:</h5>
101    * <p class='bjava'>
102    *    DoubleValue <jv>value</jv> = DoubleValue.<jsm>of</jsm>(3.14159);
103    *    <jsm>assertTrue</jsm>(<jv>value</jv>.is(3.14, 0.01));      <jc>// Within 0.01</jc>
104    *    <jsm>assertFalse</jsm>(<jv>value</jv>.is(3.14, 0.001));    <jc>// Not within 0.001</jc>
105    *
106    *    <jc>// Handles null values</jc>
107    *    DoubleValue <jv>empty</jv> = DoubleValue.<jsm>create</jsm>();
108    *    <jv>empty</jv>.set(<jk>null</jk>);
109    *    <jsm>assertFalse</jsm>(<jv>empty</jv>.is(3.14, 0.01));     <jc>// Null doesn't match any value</jc>
110    *
111    *    <jc>// Floating-point precision example</jc>
112    *    DoubleValue <jv>calc</jv> = DoubleValue.<jsm>of</jsm>(0.1 + 0.2);  <jc>// Actually 0.30000000000000004</jc>
113    *    <jsm>assertTrue</jsm>(<jv>calc</jv>.is(0.3, 0.000001));    <jc>// Handles rounding errors</jc>
114    * </p>
115    *
116    * @param other The value to compare with.
117    * @param precision The maximum allowed difference for equality. Must be non-negative.
118    * @return <jk>true</jk> if the absolute difference between the values is less than or equal to precision.
119    * @throws IllegalArgumentException if precision is negative.
120    */
121   public boolean is(double other, double precision) {
122      assertArg(precision >= 0, "Precision must be non-negative");
123      var v = get();
124      if (v == null) {
125         return false;
126      }
127      return Math.abs(v - other) <= precision;
128   }
129
130   /**
131    * Checks if the current value matches any of the specified values within the given precision.
132    *
133    * <p>
134    * This method handles <jk>null</jk> values safely and performs precision-based equality comparison
135    * for each value using absolute difference.
136    *
137    * <h5 class='section'>Example:</h5>
138    * <p class='bjava'>
139    *    DoubleValue <jv>value</jv> = DoubleValue.<jsm>of</jsm>(3.14159);
140    *    <jsm>assertTrue</jsm>(<jv>value</jv>.isAny(0.01, 2.5, 3.15, 5.0));   <jc>// Matches 3.15 within 0.01</jc>
141    *    <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.001, 1.0, 2.0, 5.0));  <jc>// No match within 0.001</jc>
142    *
143    *    <jc>// Empty array returns false</jc>
144    *    <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.01));
145    * </p>
146    *
147    * @param precision The maximum allowed difference for equality. Must be non-negative and is the first parameter.
148    * @param values The values to compare to.
149    * @return <jk>true</jk> if the current value matches any of the specified values within the precision.
150    * @throws IllegalArgumentException if precision is negative.
151    */
152   public boolean isAny(double precision, double...values) {
153      assertArg(precision >= 0, "Precision must be non-negative");
154      assertArgNotNull("values", values);
155      var v = get();
156      if (v == null)
157         return false;
158      for (var value : values)
159         if (Math.abs(v - value) <= precision)
160            return true;
161      return false;
162   }
163}