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