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 org.apache.commons.functor.BinaryFunction;
020
021/**
022 * A base class for numeric ranges. The elements within this range must be a
023 * <b>Number</b> and <b>Comparable</b>.
024 *
025 * @param <T> the type of numbers and step that are both a number and comparable
026 * @see org.apache.commons.functor.range.IntegerRange
027 * @see org.apache.commons.functor.range.LongRange
028 * @see org.apache.commons.functor.range.FloatRange
029 * @see org.apache.commons.functor.range.DoubleRange
030 * @see org.apache.commons.functor.range.CharacterRange
031 * @since 0.1
032 * @version $Revision$ $Date$
033 */
034public abstract class NumericRange<T extends Number & Comparable<T>> extends AbstractRange<T, T> {
035
036    /**
037     * Construct a new {@link NumericRange}.
038     * @param leftEndpoint left endpoint
039     * @param rightEndpoint right endpoint
040     * @param step increment step
041     * @param nextValue function to implement the taking of a step
042     */
043    protected NumericRange(Endpoint<T> leftEndpoint, Endpoint<T> rightEndpoint, T step,
044            BinaryFunction<T, T, T> nextValue) {
045        super(leftEndpoint, rightEndpoint, step, nextValue);
046    }
047
048    /**
049     * {@inheritDoc}
050     */
051    public boolean contains(T obj) {
052        if (obj == null) {
053            return false;
054        }
055        double leftValue = this.getLeftEndpoint().getValue().doubleValue();
056        double rightValue = this.getRightEndpoint().getValue().doubleValue();
057        boolean includeLeft = this.getLeftEndpoint().getBoundType() == BoundType.CLOSED;
058        boolean includeRight = this.getRightEndpoint().getBoundType() == BoundType.CLOSED;
059        double step = this.getStep().doubleValue();
060        double value = obj.doubleValue();
061
062        double firstValue = 0;
063        double lastValue = 0;
064
065        if (step < 0.0) {
066            firstValue = includeLeft ? leftValue : leftValue + step;
067            lastValue = includeRight ? rightValue : Math.nextUp(rightValue);
068            if (value > firstValue || value < lastValue) {
069                return false;
070            }
071        } else {
072            firstValue = includeLeft ? leftValue : leftValue + step;
073            lastValue = includeRight ? rightValue : rightValue
074                                                    - (rightValue - Math
075                                                        .nextUp(rightValue));
076            if (value < firstValue || value > lastValue) {
077                return false;
078            }
079        }
080        return ((value - firstValue) / step + 1) % 1.0 == 0.0;
081    }
082
083}