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.commons.functor.range;
018
019import java.util.Iterator;
020
021import org.apache.commons.functor.BinaryFunction;
022import org.apache.commons.lang3.Validate;
023
024/**
025 * A generator for a range of float.
026 *
027 * @since 1.0
028 * @version $Revision$ $Date$
029 */
030public class FloatRange extends NumericRange<Float> {
031
032    // attributes
033    // ---------------------------------------------------------------
034
035    /**
036     * Calculate default step.
037     */
038    public static final BinaryFunction<Float, Float, Float> DEFAULT_STEP = new BinaryFunction<Float, Float, Float>() {
039
040        public Float evaluate(Float left, Float right) {
041            return left > right ? -1.0f : 1.0f;
042        }
043    };
044
045    // constructors
046    // ---------------------------------------------------------------
047    /**
048     * Create a new FloatRange.
049     *
050     * @param from start
051     * @param to end
052     */
053    public FloatRange(Number from, Number to) {
054        this(from.floatValue(), to.floatValue());
055    }
056
057    /**
058     * Create a new FloatRange.
059     *
060     * @param from start
061     * @param to end
062     * @param step increment
063     */
064    public FloatRange(Number from, Number to, Number step) {
065        this(from.floatValue(), to.floatValue(), step.floatValue());
066    }
067
068    /**
069     * Create a new FloatRange.
070     *
071     * @param from start
072     * @param to end
073     */
074    public FloatRange(float from, float to) {
075        this(from, to, DEFAULT_STEP.evaluate(from, to).floatValue());
076    }
077
078    /**
079     * Create a new FloatRange.
080     *
081     * @param from start
082     * @param to end
083     * @param step increment
084     */
085    public FloatRange(float from, float to, float step) {
086        this(from, DEFAULT_LEFT_BOUND_TYPE, to, DEFAULT_RIGHT_BOUND_TYPE, step);
087    }
088
089    /**
090     * Create a new FloatRange.
091     *
092     * @param from start
093     * @param to end
094     * @throws NullPointerException if either {@link Endpoint} is {@code null}
095     */
096    public FloatRange(Endpoint<Float> from, Endpoint<Float> to) {
097        this(from, to, DEFAULT_STEP.evaluate(from.getValue(), to.getValue()));
098    }
099
100    /**
101     * Create a new FloatRange.
102     *
103     * @param from start
104     * @param to end
105     * @param step increment
106     * @throws NullPointerException if either {@link Endpoint} is {@code null}
107     */
108    public FloatRange(Endpoint<Float> from, Endpoint<Float> to, float step) {
109        super(from, to, Float.valueOf(step), new BinaryFunction<Float, Float, Float>() {
110
111            public Float evaluate(Float left, Float right) {
112                return Float.valueOf(left.floatValue() + right.floatValue());
113            }
114        });
115        final float f = from.getValue();
116        final float t = to.getValue();
117
118        Validate.isTrue(f == t || Math.signum(step) == Math.signum(t - f),
119            "Will never reach '%s' from '%s' using step %s", t, f, step);
120    }
121
122    /**
123     * Create a new FloatRange.
124     *
125     * @param from start
126     * @param leftBoundType type of left bound
127     * @param to end
128     * @param rightBoundType type of right bound
129     * @throws NullPointerException if either bound type is {@code null}
130     */
131    public FloatRange(float from, BoundType leftBoundType, float to,
132                        BoundType rightBoundType) {
133        this(from, leftBoundType, to, rightBoundType, DEFAULT_STEP.evaluate(from, to));
134    }
135
136    /**
137     * Create a new FloatRange.
138     *
139     * @param from start
140     * @param leftBoundType type of left bound
141     * @param to end
142     * @param rightBoundType type of right bound
143     * @param step increment
144     * @throws NullPointerException if either bound type is {@code null}
145     */
146    public FloatRange(float from, BoundType leftBoundType, float to,
147                      BoundType rightBoundType, float step) {
148        this(new Endpoint<Float>(from, leftBoundType), new Endpoint<Float>(to, rightBoundType), step);
149    }
150
151    /**
152     * {@inheritDoc}
153     */
154    protected Iterator<Float> createIterator() {
155        return new Iterator<Float>() {
156            private float currentValue;
157
158            {
159                currentValue = leftEndpoint.getValue();
160
161                if (leftEndpoint.getBoundType() == BoundType.OPEN) {
162                    this.currentValue += step;
163                }
164            }
165
166            public void remove() {
167                throw new UnsupportedOperationException();
168            }
169
170            public Float next() {
171                final float step = getStep();
172                final float r = currentValue;
173                currentValue += step;
174                return Float.valueOf(r);
175            }
176
177            public boolean hasNext() {
178                final int cmp = Float.compare(currentValue, rightEndpoint.getValue());
179
180                if (cmp == 0) {
181                    return rightEndpoint.getBoundType() == BoundType.CLOSED;
182                }
183                if (step > 0f) {
184                    return cmp < 0;
185                }
186                return cmp > 0;
187            }
188        };
189    }
190
191}