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