1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.functor.generator.range;
19  
20  import org.apache.commons.functor.BinaryFunction;
21  import org.apache.commons.functor.UnaryProcedure;
22  import org.apache.commons.lang3.Validate;
23  
24  /**
25   * A generator for a range of float.
26   *
27   * @since 1.0
28   * @version $Revision: $ $Date: $
29   */
30  public class FloatRange extends NumericRange<Float> {
31  
32      // attributes
33      // ---------------------------------------------------------------
34      /**
35       * Left limit.
36       */
37      private final Endpoint<Float> leftEndpoint;
38  
39      /**
40       * Right limit.
41       */
42      private final Endpoint<Float> rightEndpoint;
43  
44      /**
45       * Increment step.
46       */
47      private final float step;
48  
49      /**
50       * Calculate default step.
51       */
52      public static final BinaryFunction<Float, Float, Float> DEFAULT_STEP = new BinaryFunction<Float, Float, Float>() {
53  
54          public Float evaluate(Float left, Float right) {
55              return left > right ? -1.0f : 1.0f;
56          }
57      };
58  
59      // constructors
60      // ---------------------------------------------------------------
61      /**
62       * Create a new FloatRange.
63       *
64       * @param from start
65       * @param to end
66       */
67      public FloatRange(Number from, Number to) {
68          this(from.floatValue(), to.floatValue());
69      }
70  
71      /**
72       * Create a new FloatRange.
73       *
74       * @param from start
75       * @param to end
76       * @param step increment
77       */
78      public FloatRange(Number from, Number to, Number step) {
79          this(from.floatValue(), to.floatValue(), step.floatValue());
80      }
81  
82      /**
83       * Create a new FloatRange.
84       *
85       * @param from start
86       * @param to end
87       */
88      public FloatRange(float from, float to) {
89          this(from, to, DEFAULT_STEP.evaluate(from, to).floatValue());
90      }
91  
92      /**
93       * Create a new FloatRange.
94       *
95       * @param from start
96       * @param to end
97       * @param step increment
98       */
99      public FloatRange(float from, float to, float step) {
100         this(from, DEFAULT_LEFT_BOUND_TYPE, to, DEFAULT_RIGHT_BOUND_TYPE, step);
101     }
102 
103     /**
104      * Create a new FloatRange.
105      *
106      * @param from start
107      * @param leftBoundType type of left bound
108      * @param to end
109      * @param rightBoundType type of right bound
110      * @param step increment
111      */
112     public FloatRange(float from, BoundType leftBoundType, float to,
113                       BoundType rightBoundType, float step) {
114         this.leftEndpoint = Validate
115             .notNull(new Endpoint<Float>(from, leftBoundType),
116                      "Left Endpoint argument must not be null");
117         this.rightEndpoint = Validate
118             .notNull(new Endpoint<Float>(to, rightBoundType),
119                      "Right Endpoint argument must not be null");
120         this.step = step;
121         if (from != to && Math.signum(step) != Math.signum(to - from)) {
122             throw new IllegalArgumentException("Will never reach " + to
123                                                + " from " + from
124                                                + " using step " + step);
125         }
126     }
127 
128     /**
129      * Create a new FloatRange.
130      *
131      * @param from start
132      * @param to end
133      * @param step increment
134      */
135     public FloatRange(Endpoint<Float> from, Endpoint<Float> to, float step) {
136         this.leftEndpoint = Validate
137             .notNull(from, "Left Endpoint argument must not be null");
138         this.rightEndpoint = Validate
139             .notNull(to, "Right Endpoint argument must not be null");
140         this.step = step;
141         if (from != to
142             && Math.signum(step) != Math.signum(to.getValue().doubleValue()
143                                              - from.getValue().doubleValue())) {
144             throw new IllegalArgumentException("Will never reach " + to
145                                                + " from " + from
146                                                + " using step " + step);
147         }
148     }
149 
150     // methods
151     // ---------------------------------------------------------------
152     /**
153      * {@inheritDoc}
154      */
155     public Endpoint<Float> getLeftEndpoint() {
156         return this.leftEndpoint;
157     }
158 
159     /**
160      * {@inheritDoc}
161      */
162     public Endpoint<Float> getRightEndpoint() {
163         return this.rightEndpoint;
164     }
165 
166     /**
167      * {@inheritDoc}
168      */
169     public Float getStep() {
170         return this.step;
171     }
172 
173     /**
174      * {@inheritDoc}
175      */
176     public void run(UnaryProcedure<? super Float> proc) {
177         final float step = this.getStep();
178         final boolean includeLeftValue = this.getLeftEndpoint()
179             .getBoundType() == BoundType.CLOSED;
180         final boolean includeRightValue = this.getRightEndpoint()
181             .getBoundType() == BoundType.CLOSED;
182         final float leftValue = this.getLeftEndpoint().getValue();
183         final float rightValue = this.getRightEndpoint().getValue();
184         if (step < 0) {
185             final float from = includeLeftValue ? leftValue : leftValue + step;
186             if (includeRightValue) {
187                 for (float i = from; i >= rightValue; i += step) {
188                     proc.run(i);
189                 }
190             } else {
191                 for (float i = from; i > rightValue; i += step) {
192                     proc.run(i);
193                 }
194             }
195         } else {
196             final float from = includeLeftValue ? leftValue : leftValue + step;
197             if (includeRightValue) {
198                 for (float i = from; i <= rightValue; i += step) {
199                     proc.run(i);
200                 }
201             } else {
202                 for (float i = from; i < rightValue; i += step) {
203                     proc.run(i);
204                 }
205             }
206         }
207     }
208 
209     /**
210      * {@inheritDoc}
211      */
212     @Override
213     public String toString() {
214         return "FloatRange<" + this.leftEndpoint.toLeftString() + ", "
215                 + this.rightEndpoint.toRightString() + ", " + this.step + ">";
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     @Override
222     public boolean equals(Object obj) {
223         if (obj == this) {
224             return true;
225         }
226         if (!(obj instanceof FloatRange)) {
227             return false;
228         }
229         FloatRange that = (FloatRange) obj;
230         return this.leftEndpoint.equals(that.leftEndpoint)
231                 && this.rightEndpoint.equals(that.rightEndpoint)
232                 && this.step == that.step;
233     }
234 
235     /**
236      * {@inheritDoc}
237      */
238     @Override
239     public int hashCode() {
240         int hash = "FloatRange".hashCode();
241         hash <<= 2;
242         hash ^= this.leftEndpoint.getValue().hashCode();
243         hash <<= 2;
244         hash ^= this.rightEndpoint.getValue().hashCode();
245         hash <<= 2;
246         hash ^= Float.valueOf(this.step).hashCode();
247         return hash;
248     }
249 
250 }