All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.osgl.util.LazyRange Maven / Gradle / Ivy

The newest version!
package org.osgl.util;

/*-
 * #%L
 * Java Tool
 * %%
 * Copyright (C) 2014 - 2017 OSGL (Open Source General Library)
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import org.osgl.$;
import org.osgl.exception.InvalidArgException;
import org.osgl.exception.NotAppliedException;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Iterator;

/**
 * Implement {@link C.Range} using {@link LazySeq}.
 */
public class LazyRange extends LazySeq
implements C.Range, Serializable {

    private final ELEMENT to;

    private final Comparator order;

    private final $.Func2 step;

    protected final int ordering;

    private final int size;

    protected final $.F1 next;

    protected final $.F1 prev;

    public LazyRange(final ELEMENT from, final ELEMENT to, final $.Func2 step) {
        this(from, to, $.F.NATURAL_ORDER, step);
    }

    public LazyRange(final ELEMENT from, final ELEMENT to, final Comparator order,
                     final $.Func2 step
    ) {
        E.NPE(from, to, order, step);

        ordering = N.sign(order.compare(from, to));
        boolean eq = $.eq(from, to);
        E.invalidArgIf(eq, "[from] shall not be equals to [to]");

        // check if step align with order
        ELEMENT next = step.apply(from, -ordering);
        int ordering2 = order.compare(from, next);
        if (N.sign(ordering2) != N.sign(ordering)) {
            E.invalidArg("step function doesn't align to the direction between [from] and [to]");
        }

        // find out the size of the range
        if (from instanceof Number) {
            int n0 = ((Number)from).intValue();
            int n1 = ((Number)to).intValue();
            int n2 = ((Number)next).intValue();

            int distance = n1 - n0;
            int unit = n2 - n0;
            int mod = distance%unit;
            if (mod > 0) {
                size = (distance + mod) / unit - 1;
            } else {
                size = distance / unit;
            }
        } else {
            size = -1;
        }

        this.to = to;
        this.head = from;
        this.order = order;
        this.step = step;

        $.F2 f2 = $.f2(step());
        this.next = f2.curry(-ordering);
        this.prev = f2.curry(ordering);
        this.tail = new $.F0>() {
            @Override
            public C.Sequence apply() throws NotAppliedException, $.Break {
                if ($.eq(from, to)) {
                    return Nil.seq();
                } else {
                    return of(LazyRange.this.next.apply(from), to);
                }
            }
        };

        this.setFeature(C.Feature.LIMITED);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }

        if (obj instanceof C.Range) {
            C.Range that = (C.Range) obj;
            return $.eq(that.from(), from()) && $.eq(that.to(), to()) && $.eq(that.order(), order()) && $.eq(that.step(), step());
        }

        return false;
    }

    @Override
    public int hashCode() {
        return $.hc(from(), to(), order(), step());
    }

    @Override
    public String toString() {
        return new StringBuilder("[").append(from()).append(",").append(to()).append(")").toString();
    }

    @Override
    public int size() throws UnsupportedOperationException {
        if (size < 0) {
            throw new UnsupportedOperationException();
        } else {
            return size;
        }
    }

    protected LazyRange of(ELEMENT from, ELEMENT to) {
        return new LazyRange(from, to, order, step);
    }

    public final ELEMENT from() {
        return head();
    }

    @Override
    public final ELEMENT to() {
        return to;
    }

    @Override
    public Comparator order() {
        return order;
    }

    @Override
    public $.Func2 step() {
        return step;
    }

    @Override
    public C.Range merge(C.Range r2) throws InvalidArgException {
        if ($.ne(step(), r2.step()) || $.ne(order(), r2.order())) {
            throw E.invalidArg("r2 and this range does not have the same step or order operator");
        }
        int ordering2 = N.sign(order.compare(r2.from(), r2.to()));
        if (ordering2 != ordering) {
            throw E.invalidArg("r2 and this range doesn't have the same ordering direction");
        }
        ELEMENT from1 = from(), to1 = step().apply(to, -1), from2 = r2.from(), to2 = r2.step().apply(r2.to(), -1);
        boolean fromInThis = contains(from2), toInThis = contains(to2);
        if (fromInThis && toInThis) {
            return this;
        }
        boolean fromInThat = r2.contains(from1), toInThat = r2.contains(to1);
        if (fromInThat && toInThat) {
            return r2;
        }
        if ((fromInThis && toInThat) || ($.eq(to(), from2))) {
            return of(from1, r2.to());
        }
        if ((toInThis && fromInThat) || ($.eq(from1, r2.to()))) {
            return of(from2, to);
        }
        throw E.invalidArg("r2 and this range cannot be merged together");
    }

    @Override
    public ELEMENT last() throws UnsupportedOperationException {
        return prev.apply(to);
    }

    @Override
    public C.Range tail() throws UnsupportedOperationException {
        ELEMENT from = next.apply(from());
        if ($.eq(from, to)) {
            return Nil.range();
        }
        return of(next.apply(from()), to);
    }

    @Override
    public C.Range head(int n) {
        return take(n);
    }

    @Override
    public C.Range tail(int n) throws UnsupportedOperationException {
        E.illegalArgumentIf(n <= 0, "n must be a positive int");
        return of(step().apply(to, -n), to);
    }

    @Override
    public C.Range take(int n) {
        E.invalidArgIf(n <= 0, "n must be a positive int");
        ELEMENT from = from();
        return of(from, step().apply(from, n));
    }

    @Override
    public C.Range drop(int n) {
        E.invalidArgIf(n <= 0, "n must be a positive int");
        ELEMENT from = from();
        return of(step().apply(from, n), to);
    }

    @Override
    public C.Range reverse() throws UnsupportedOperationException {
        return of(prev.apply(to), prev.apply(from()));
    }

    @Override
    public Iterator reverseIterator() {
        return reverse().iterator();
    }

    @Override
    public  R reduceRight(R identity, $.Func2 accumulator) {
        return reverse().reduceLeft(identity, accumulator);
    }

    @Override
    public LazyRange accept($.Visitor visitor) {
        super.accept(visitor);
        return this;
    }

    @Override
    public LazyRange forEach($.Visitor visitor) {
        return accept(visitor);
    }

    @Override
    public LazyRange each($.Visitor visitor) {
        return accept(visitor);
    }

    @Override
    public LazyRange acceptLeft($.Visitor visitor) {
        super.acceptLeft(visitor);
        return this;
    }

    @Override
    public LazyRange acceptRight($.Visitor visitor) {
        reverse().acceptLeft(visitor);
        return this;
    }

    @Override
    public $.Option reduceRight($.Func2 accumulator) {
        return reverse().reduceLeft(accumulator);
    }

    @Override
    public $.Option findLast($.Function predicate) {
        return reverse().findFirst(predicate);
    }

    @Override
    public boolean contains(ELEMENT t) {
        E.NPE(t);

        if (0 == ordering) {
            return $.eq(to, t);
        }

        ELEMENT from = from();

        if ($.eq(from, t)) {
            return true;
        }

        int withFrom = order.compare(t, from);
        if (ordering < 0 && withFrom < 0) {
            return false;
        }
        int withTo = order.compare(t, to);
        return withFrom * withTo < 0;
    }

    @Override
    public boolean containsAll(C.Range range) {
        E.NPE(range);
        return contains(range.from()) && contains(prev.apply(range.to()));
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        throw new InvalidObjectException("Proxy required");
    }

    private static class SerializationProxy implements Serializable {
        ELEMENT from;
        ELEMENT to;
        Comparator order;
        $.Func2 step;

        SerializationProxy(LazyRange r) {
            from = r.from();
            to = r.to();
            order = r.order;
            step = r.step;
        }

        private Object readResolve() {
            return new LazyRange(from, to, order, step);
        }

        private static final long serialVersionUID = 21864874113505L;
    }

    private Object writeReplace() {
        return new SerializationProxy(this);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy