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 float value.
023 *
024 * <p>
025 * This class extends {@link Value}&lt;{@link Float}&gt; and provides a convenient way to pass mutable
026 * float 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 float value to track a running sum</jc>
036 *    FloatValue <jv>sum</jv> = FloatValue.<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>.floatValue());
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 FloatValue extends Value<Float> {
051
052   /**
053    * Creates a new float value initialized to <c>0.0f</c>.
054    *
055    * <h5 class='section'>Example:</h5>
056    * <p class='bjava'>
057    *    FloatValue <jv>value</jv> = FloatValue.<jsm>create</jsm>();
058    *    <jsm>assertEquals</jsm>(0.0f, <jv>value</jv>.get());
059    * </p>
060    *
061    * @return A new float value.
062    */
063   public static FloatValue create() {
064      return of(0.0f);
065   }
066
067   /**
068    * Creates a new float value with the specified initial value.
069    *
070    * <h5 class='section'>Example:</h5>
071    * <p class='bjava'>
072    *    FloatValue <jv>value</jv> = FloatValue.<jsm>of</jsm>(3.14f);
073    *    <jsm>assertEquals</jsm>(3.14f, <jv>value</jv>.get());
074    * </p>
075    *
076    * @param value The initial value.
077    * @return A new float value.
078    */
079   public static FloatValue of(Float value) {
080      return new FloatValue(value);
081   }
082
083   /**
084    * Constructor.
085    *
086    * @param value The initial value.
087    */
088   public FloatValue(Float 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    *    FloatValue <jv>value</jv> = FloatValue.<jsm>of</jsm>(3.14f);
103    *    <jsm>assertTrue</jsm>(<jv>value</jv>.is(3.14f, 0.01f));    <jc>// Exact match within 0.01</jc>
104    *    <jsm>assertTrue</jsm>(<jv>value</jv>.is(3.15f, 0.02f));    <jc>// Within 0.02</jc>
105    *    <jsm>assertFalse</jsm>(<jv>value</jv>.is(3.15f, 0.001f));  <jc>// Not within 0.001</jc>
106    *
107    *    <jc>// Handles null values</jc>
108    *    FloatValue <jv>empty</jv> = FloatValue.<jsm>create</jsm>();
109    *    <jv>empty</jv>.set(<jk>null</jk>);
110    *    <jsm>assertFalse</jsm>(<jv>empty</jv>.is(3.14f, 0.01f));   <jc>// Null doesn't match any value</jc>
111    *
112    *    <jc>// Precision-based comparison</jc>
113    *    FloatValue <jv>pi</jv> = FloatValue.<jsm>of</jsm>(3.14159f);
114    *    <jsm>assertTrue</jsm>(<jv>pi</jv>.is(3.14f, 0.002f));      <jc>// Within 0.002 precision</jc>
115    * </p>
116    *
117    * @param other The value to compare with.
118    * @param precision The maximum allowed difference for equality. Must be non-negative.
119    * @return <jk>true</jk> if the absolute difference between the values is less than or equal to precision.
120    * @throws IllegalArgumentException if precision is negative.
121    */
122   public boolean is(float other, float precision) {
123      assertArg(precision >= 0, "Precision must be non-negative");
124      var v = get();
125      if (v == null) {
126         return false;
127      }
128      return Math.abs(v - other) <= precision;
129   }
130
131   /**
132    * Checks if the current value matches any of the specified values within the given precision.
133    *
134    * <p>
135    * This method handles <jk>null</jk> values safely and performs precision-based equality comparison
136    * for each value using absolute difference.
137    *
138    * <h5 class='section'>Example:</h5>
139    * <p class='bjava'>
140    *    FloatValue <jv>value</jv> = FloatValue.<jsm>of</jsm>(3.14f);
141    *    <jsm>assertTrue</jsm>(<jv>value</jv>.isAny(0.01f, 2.5f, 3.15f, 5.0f));   <jc>// Matches 3.15f within 0.01</jc>
142    *    <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.001f, 1.0f, 2.0f, 5.0f));  <jc>// No match within 0.001</jc>
143    *
144    *    <jc>// Empty array returns false</jc>
145    *    <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.01f));
146    * </p>
147    *
148    * @param precision The maximum allowed difference for equality. Must be non-negative and is the first parameter.
149    * @param values The values to compare to.
150    * @return <jk>true</jk> if the current value matches any of the specified values within the precision.
151    * @throws IllegalArgumentException if precision is negative.
152    */
153   public boolean isAny(float precision, float...values) {
154      assertArg(precision >= 0, "Precision must be non-negative");
155      assertArgNotNull("values", values);
156      var v = get();
157      if (v == null)
158         return false;
159      for (var value : values)
160         if (Math.abs(v - value) <= precision)
161            return true;
162      return false;
163   }
164}