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

dafny.DafnySequence Maven / Gradle / Ivy

The newest version!
// Copyright by the contributors to the Dafny Project
// SPDX-License-Identifier: MIT

package dafny;

import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;

import dafny.TypeDescriptor;

public abstract class DafnySequence implements Iterable {
    /*
    Invariant: forall 0<=i DafnySequence of(TypeDescriptor type, T ... elements) {
        Array array;
        if (!type.isPrimitive()) {
            array = Array.wrap(type, elements.clone());
        } else {
            // Need to unbox each individual element.  Slow, but it should be
            // unusual to have a large sequence display of unknown type.
            array = Array.newArray(type, elements.length);
            for (int i = 0; i < elements.length; i++) {
                array.set(i, elements[i]);
            }
        }
        return DafnySequence.fromArray(type, array);
    }

    public static DafnySequence of(byte ... elements) {
        return DafnySequence.fromArray(TypeDescriptor.BYTE, Array.wrap(elements));
    }

    public static DafnySequence of(short ... elements) {
        return DafnySequence.fromArray(TypeDescriptor.SHORT, Array.wrap(elements));
    }

    public static DafnySequence of(int ... elements) {
        return DafnySequence.fromArray(TypeDescriptor.INT, Array.wrap(elements));
    }

    public static DafnySequence of(long ... elements) {
        return DafnySequence.fromArray(TypeDescriptor.LONG, Array.wrap(elements));
    }

    public static DafnySequence of(boolean ... elements) {
        return DafnySequence.fromArray(TypeDescriptor.BOOLEAN, Array.wrap(elements));
    }

    public static DafnySequence of(char ... elements) {
        return DafnySequence.fromArray(TypeDescriptor.CHAR, Array.wrap(elements));
    }

    @SuppressWarnings("unchecked")
    public static  DafnySequence empty(TypeDescriptor type) {
        if (type == TypeDescriptor.CHAR) {
            return (DafnySequence) asString("");
        }
        return ArrayDafnySequence. empty(type);
    }

    public static  DafnySequence fromArray(TypeDescriptor type, Array elements) {
        return fromRawArray(type, elements.unwrap());
    }

    @SuppressWarnings("unchecked")
    public static  DafnySequence fromRawArray(TypeDescriptor type, Object elements) {
        if (type == TypeDescriptor.CHAR) {
            return (DafnySequence) asString(new String((char[]) elements));
        }
        return new ArrayDafnySequence<>(Array.wrap(type, elements).copy());
    }

    /**
     * Return a sequence backed by the given array without making a defensive
     * copy.  Only safe if the array never changes afterward.
     */
    public static  DafnySequence unsafeWrapArray(Array elements) {
        return new ArrayDafnySequence<>(elements, true);
    }

    public static  DafnySequence unsafeWrapRawArray(TypeDescriptor type, Object elements) {
        return new ArrayDafnySequence<>(Array.wrap(type, elements));
    }

    public static  DafnySequence fromArrayRange(TypeDescriptor type, Array elements, int lo, int hi) {
        return new ArrayDafnySequence(elements.copyOfRange(lo, hi));
    }

    public static  DafnySequence fromRawArrayRange(TypeDescriptor type, Object elements, int lo, int hi) {
        return fromArrayRange(type, Array.wrap(type, elements), lo, hi);
    }

    public static  DafnySequence fromList(TypeDescriptor type, List l) {
        assert l != null: "Precondition Violation";
        return new ArrayDafnySequence(Array.fromList(type, l));
    }

    public static DafnySequence asString(String s){
        return new StringDafnySequence(s);
    }

    public static DafnySequence asUnicodeString(String s) {
        int[] codePoints = new int[s.codePointCount(0, s.length())];
        int charIndex = 0;
        for (int codePointIndex = 0; codePointIndex < codePoints.length; codePointIndex++) {
            char c1 = s.charAt(charIndex++);
            if (Character.isHighSurrogate(c1)) {
                if (charIndex >= s.length()) {
                    throw new IllegalArgumentException();
                }
                char c2 = s.charAt(charIndex++);
                if (!Character.isLowSurrogate(c2)) {
                    throw new IllegalArgumentException();
                }
                codePoints[codePointIndex] = Character.toCodePoint(c1, c2);
            } else {
                codePoints[codePointIndex] = c1;
            }
        }
        return new ArrayDafnySequence<>(Array.wrap(TypeDescriptor.UNICODE_CHAR, codePoints));
    }

    public static DafnySequence fromBytes(byte[] bytes) {
        return unsafeWrapBytes(bytes.clone());
    }

    /**
     * Return a sequence backed by the given byte array without making a
     * defensive copy.  Only safe if the array never changes afterward.
     */
    public static DafnySequence unsafeWrapBytes(byte[] bytes) {
        return unsafeWrapArray(Array.wrap(bytes));
    }

    public static  DafnySequence Create(TypeDescriptor type, BigInteger length, Function init) {
        int len = length.intValueExact();
        Array values = Array.newArray(type, len);
        for(int i = 0; i < len; i++) {
            values.set(i, init.apply(BigInteger.valueOf(i)));
        }
        return fromArray(type, values);
    }

    @SuppressWarnings("unchecked")
    public static  TypeDescriptor> _typeDescriptor(TypeDescriptor elementType) {
        return TypeDescriptor.referenceWithDefault(
                (Class>) (Class) DafnySequence.class,
                DafnySequence.empty(elementType));
    }

    public Array toArray() {
        return Array.fromList(elementType(), asList());
    }

    public Object toRawArray() {
        return toArray().unwrap();
    }

    public static byte[] toByteArray(DafnySequence seq) {
        return Array.unwrapBytes(seq.toArray());
    }

    public static int[] toIntArray(DafnySequence seq) {
        return Array.unwrapInts(seq.toArray());
    }

    public abstract TypeDescriptor elementType();

    // Determines if this DafnySequence is a prefix of other
    public  boolean isPrefixOf(DafnySequence other) {
        assert other != null : "Precondition Violation";
        if (other.length() < length()) return false;
        for (int i = 0; i < length(); i++) {
            if (!java.util.Objects.equals(this.select(i), other.select(i))) return false;
        }
        return true;
    }

    // Determines if this DafnySequence is a proper prefix of other
    public  boolean isProperPrefixOf(DafnySequence other) {
        assert other != null : "Precondition Violation";
        return length() < other.length() && isPrefixOf(other);
    }

    // This is just a convenience to implement infrequently-used operations or
    // non-performance-critical operations.  Uses of it may be specialized in
    // subclasses as needed.
    protected List asList() {
        return new AbstractList() {
            @Override
            public T get(int index) {
                return select(index);
            }

            @Override
            public int size() {
                return length();
            }

            @Override
            public Iterator iterator() {
                return DafnySequence.this.iterator();
            }
        };
    }

    @SuppressWarnings("unchecked")
    public static  DafnySequence concatenate(DafnySequence th, DafnySequence other) {
        assert th != null : "Precondition Violation";
        assert other != null : "Precondition Violation";

        if (th.isEmpty()) {
            return (DafnySequence)other;
        } else if (other.isEmpty()) {
            return (DafnySequence)th;
        } else {
            return new ConcatDafnySequence((DafnySequence)th, (DafnySequence)other);
        }
    }

    /**
     * Build a new sequence of the same type, and a known length, by copying
     * from a number of existing ones.
     *
     * Not public; only meant to be used by {@link ConcatDafnySequence}.
     */
    abstract Copier newCopier(int length);

    /** @see #newCopier(int) */
    interface Copier {
        public void copyFrom(DafnySequence source);

        public NonLazyDafnySequence result();
    }

    public abstract T select(int i);

    public T selectUnsigned(byte i) {
        return select(Byte.toUnsignedInt(i));
    }

    public T selectUnsigned(short i) {
        return select(Short.toUnsignedInt(i));
    }

    public T selectUnsigned(int i) {
        return select(Integer.toUnsignedLong(i));
    }

    public T select(long i) {
        return select(BigInteger.valueOf(i));
    }

    public T selectUnsigned(long i) {
        return select(Helpers.unsignedToBigInteger(i));
    }

    public T select(BigInteger i) {
        return select(i.intValue());
    }

    public abstract int length();

    public boolean isEmpty() {
        return this.length() == 0;
    }

    public final int cardinalityInt() {
        return length();
    }

    public abstract  DafnySequence update(int i, R t);

    public static  DafnySequence update(DafnySequence seq, BigInteger b, R t) {
        return seq.update(b.intValue(), t);
    }

    public static  DafnySequence update(DafnySequence seq, int idx, R t) {
        return seq.update(idx, t);
    }

    public static  DafnySequence update(DafnySequence seq, long idx, R t) {
        return seq.update((int)idx, t);
    }

    @SuppressWarnings("unchecked")
    public boolean contains(Object t) {
        assert t != null : "Precondition Violation";
        return asList().indexOf((T)t) != -1;
    }

    // Returns the subsequence of values [lo..hi)
    public abstract DafnySequence subsequence(int lo, int hi);


    // Returns the subsequence of values [lo..length())
    public final DafnySequence drop(int lo) {
        assert lo >= 0 && lo <= length() : "Precondition Violation";
        return subsequence(lo, length());
    }

    public DafnySequence dropUnsigned(byte lo) {
        return drop(Byte.toUnsignedInt(lo));
    }

    public DafnySequence dropUnsigned(short lo) {
        return drop(Short.toUnsignedInt(lo));
    }

    public DafnySequence dropUnsigned(int lo) {
        return drop(Integer.toUnsignedLong(lo));
    }

    public DafnySequence drop(long lo) {
        return drop(BigInteger.valueOf(lo));
    }

    public DafnySequence dropUnsigned(long lo) {
        return drop(Helpers.unsignedToBigInteger(lo));
    }

    public DafnySequence drop(BigInteger lo) {
        return drop(lo.intValue());
    }


    // Returns the subsequence of values [0..hi)
    public final DafnySequence take(int hi) {
        assert hi >= 0 && hi <= length() : "Precondition Violation";
        return subsequence(0, hi);
    }

    public DafnySequence takeUnsigned(byte hi) {
        return take(Byte.toUnsignedInt(hi));
    }

    public DafnySequence takeUnsigned(short hi) {
        return take(Short.toUnsignedInt(hi));
    }

    public DafnySequence takeUnsigned(int hi) {
        return take(Integer.toUnsignedLong(hi));
    }

    public DafnySequence take(long hi) {
        return take(BigInteger.valueOf(hi));
    }

    public DafnySequence takeUnsigned(long hi) {
        return take(Helpers.unsignedToBigInteger(hi));
    }

    public DafnySequence take(BigInteger hi) {
        return take(hi.intValue());
    }

    public final DafnySequence> slice(List l) {
        assert l != null : "Precondition Violation";
        ArrayList> list = new ArrayList<>();
        int curr = 0;
        for (Integer i : l) {
            assert i != null : "Precondition Violation";
            list.add(subsequence(curr, curr + i));
            curr += i;
        }

        TypeDescriptor eexx = elementType();
        TypeDescriptor> ssxx = _typeDescriptor(eexx);
        return fromList(ssxx, list);
    }

    public DafnyMultiset asDafnyMultiset() {
        return new DafnyMultiset<>(asList());
    }

    @Override
    public abstract Iterator iterator();

    @Override
    public Spliterator spliterator() {
        // TODO Override these with faster versions in subclasses if needed
        return asList().spliterator();
    }

    @Override
    public final boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof DafnySequence)) {
            return false;
        }
        @SuppressWarnings("unchecked")
        DafnySequence other = (DafnySequence) obj;
        return this.equalsNonLazy(other.force());
    }

    /**
     * Compare for equality to the given sequence, assuming that it is not
     * null, not == to this, and not lazy.
     */
    protected boolean equalsNonLazy(NonLazyDafnySequence other) {
        return asList().equals(other.asList());
    }

    @Override
    public abstract int hashCode();

    @Override
    public String toString() {
        return asList().toString();
    }

    @SuppressWarnings("unchecked")
    public String verbatimString() {
        if (elementType() == TypeDescriptor.UNICODE_CHAR) {
            // This is slow, but the override in ArrayDafnySequence will almost
            // always be used instead
            int[] codePoints = new int[length()];
            int i = 0;
            for(Integer ch: (List) asList())
            {
                codePoints[i++] = ch;
            }
            return new String(codePoints, 0, codePoints.length);
        } else {
            // This is slow, but the override in StringDafnySequence will almost
            // always be used instead
            StringBuilder builder = new StringBuilder(length());
            for(Character ch: (List) asList())
            {
                builder.append(ch);
            }
            return builder.toString();
        }
    }

    public Iterable Elements() {
        return this;
    }

    public HashSet UniqueElements() {
        return new HashSet<>(asList());
    }

    /**
     * @return The sequence representing this sequence's actual value.
     * That's usually just the sequence itself, but not if the
     * sequence is lazily computed.
     *
     * @see ConcatDafnySequence
     */
    protected abstract NonLazyDafnySequence force();
}

abstract class NonLazyDafnySequence extends DafnySequence {
    @Override
    protected final NonLazyDafnySequence force() {
        return this;
    }
}

final class ArrayDafnySequence extends NonLazyDafnySequence {
    private final Array seq;
    @SuppressWarnings("unused")
    private boolean unsafe; // for debugging purposes

    // NOTE: Input array is *shared*; must be a copy if it comes from a public input
    ArrayDafnySequence(TypeDescriptor elementType, Object elements, boolean unsafe) {
        this(Array.wrap(elementType, elements), unsafe);
    }

    ArrayDafnySequence(TypeDescriptor elementType, Object elements) {
        this(Array.wrap(elementType, elements));
    }

    ArrayDafnySequence(Array elements, boolean unsafe) {
        this.seq = elements;
        this.unsafe = unsafe;
    }

    ArrayDafnySequence(Array array) {
        this(array, false);
    }

    Array unwrap() {
        return seq;
    }

    @Override
    public Array toArray() {
        return seq.copy();
    }

    public static  ArrayDafnySequence empty(TypeDescriptor type) {
        return new ArrayDafnySequence(type, type.newArray(0));
    }

    @Override
    public TypeDescriptor elementType() {
        return seq.elementType();
    }

    @Override
    @SuppressWarnings("unchecked")
    public  ArrayDafnySequence update(int i, R t) {
        assert t != null : "Precondition Violation";
        //todo: should we allow i=length, and return a new sequence with t appended to the sequence?
        assert 0 <= i && i < length(): "Precondition Violation";
        Array newArray = (Array)seq.copy();
        newArray.set(i, t);
        return new ArrayDafnySequence<>(newArray);
    }

    public ArrayDafnySequence subsequence(int lo, int hi) {
        assert lo >= 0 && hi >= 0 && hi >= lo : "Precondition Violation";

        return new ArrayDafnySequence<>(seq.copyOfRange(lo, hi));
    }

    @Override
    Copier newCopier(final int length) {
        return new Copier() {
            private final Array newArray = Array.newArray(seq.elementType(), length);
            private int nextIndex = 0;

            @Override
            public void copyFrom(DafnySequence source) {
                source = source.force();
                if (source instanceof ArrayDafnySequence) {
                    Array sourceArray = ((ArrayDafnySequence) source).seq;
                    sourceArray.copy(0, newArray, nextIndex, sourceArray.length());
                    nextIndex += sourceArray.length();
                } else {
                    for (T t : source) {
                        newArray.set(nextIndex++, t);
                    }
                }
            }

            @Override
            public NonLazyDafnySequence result() {
                return new ArrayDafnySequence(newArray);
            }
        };
    }

    @Override
    protected List asList() {
        return new AbstractList() {
            @Override
            public T get(int index) {
                return seq.get(index);
            }

            @Override
            public T set(int index, T element) {
                T prev = seq.get(index);
                seq.set(index, element);
                return prev;
            }

            @Override
            public int size() {
                return length();
            }
        };
    }

    @Override
    public T select(int i) {
        return (T) seq.get(i);
    }

    @Override
    public int length() {
        return seq.length();
    }

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

    @Override
    protected boolean equalsNonLazy(NonLazyDafnySequence other) {
        if (other instanceof ArrayDafnySequence) {
            return seq.shallowEquals(((ArrayDafnySequence) other).seq);
        } else {
            return super.equalsNonLazy(other);
        }
    }

    @Override
    public int hashCode() {
        return asList().hashCode();
    }

    @Override
    public String verbatimString() {
        if (elementType() == TypeDescriptor.UNICODE_CHAR) {
            return new String((int[]) seq.unwrap(), 0, seq.length());
        } else {
            return new String((char[]) seq.unwrap());
        }
    }
}

final class StringDafnySequence extends NonLazyDafnySequence {
    private final String string;

    StringDafnySequence(String string) {
        this.string = string;
    }

    @Override
    public Array toArray() {
        return Array.wrap(string.toCharArray());
    }

    @Override
    public TypeDescriptor elementType() {
        return TypeDescriptor.CHAR;
    }

    @Override
    public Character select(int i) {
        return string.charAt(i);
    }

    @Override
    public int length() {
        return string.length();
    }

    @Override
    @SuppressWarnings("unchecked")
    public  DafnySequence update(int i, R t) {
        // assume R == Character
        assert t != null : "Precondition Violation";
        StringBuilder sb = new StringBuilder(string);
        sb.setCharAt(i, (Character)t);
        return (DafnySequence)new StringDafnySequence(sb.toString());
    }

    @Override
    public boolean contains(Object t) {
        assert t != null : "Precondition Violation";
        return string.indexOf((Character)t) != -1;
    }

    @Override
    public DafnySequence subsequence(int lo, int hi) {
        return new StringDafnySequence(string.substring(lo, hi));
    }

    @Override
    Copier newCopier(int length) {
        return new Copier() {
            private final StringBuilder sb = new StringBuilder(length);

            @Override
            public void copyFrom(DafnySequence source) {
                source = source.force();
                if (source instanceof StringDafnySequence) {
                    sb.append(((StringDafnySequence) source).string);
                } else {
                    for (char c : source) {
                        sb.append(c);
                    }
                }
            }

            @Override
            public NonLazyDafnySequence result() {
                return new StringDafnySequence(sb.toString());
            }
        };
    }

    @Override
    public Iterator iterator() {
        final int n = string.length();
        return new Iterator() {
            int i = 0;

            @Override
            public boolean hasNext() {
                return i < n;
            }

            @Override
            public Character next() {
                return string.charAt(i++);
            }
        };
    }

    @Override
    public boolean equalsNonLazy(NonLazyDafnySequence obj) {
        if (obj instanceof StringDafnySequence) {
            return string.equals(((StringDafnySequence) obj).string);
        } else {
            return super.equalsNonLazy(obj);
        }
    }

    @Override
    public int hashCode() {
        return string.hashCode();
    }

    @Override
    public String verbatimString() {
        return string;
    }

    @Override
    public String toString() {
        return string;
    }
}

abstract class LazyDafnySequence extends DafnySequence {
    @Override
    public Array toArray() {
        return force().toArray();
    }

    @Override
    public TypeDescriptor elementType() {
        return force().elementType();
    }

    @Override
    protected List asList() {
        return force().asList();
    }

    @Override
    public T select(int i) {
        return force().select(i);
    }

    @Override
    public int length() {
        return force().length();
    }

    @Override
    public  DafnySequence update(int i, R t) {
        return force().update(i, t);
    }

    @Override
    public DafnySequence subsequence(int lo, int hi) {
        return force().subsequence(lo, hi);
    }

    @Override
    Copier newCopier(int length) {
        return force().newCopier(length);
    }

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

    @Override
    public String toString() {
        return force().toString();
    }

    @Override
    public String verbatimString() {
        return force().verbatimString();
    }

    @Override
    protected boolean equalsNonLazy(NonLazyDafnySequence other) {
        return force().equalsNonLazy(other);
    }

    @Override
    public int hashCode() {
        return force().hashCode();
    }
}

final class ConcatDafnySequence extends LazyDafnySequence {
    // INVARIANT: Either these are both non-null and ans is null or both are
    // null and ans is non-null.
    // Under concurrent access this is more complicated though:
    // the only safe pattern is to read left and right into temporary variables first
    // and judge based on their values whether ans is populated or not.
    // The opposite order is subject to race conditions:
    // if you observe ans as null first and then attempt to read left or right,
    // another thread could complete force() in-between and clear left and right
    // before you get to them!
    private volatile DafnySequence left, right;
    private NonLazyDafnySequence ans = null;
    private final int length;

    ConcatDafnySequence(DafnySequence left, DafnySequence right) {
        this.left = left;
        this.right = right;
        this.length = left.length() + right.length();
    }

    @Override
    protected NonLazyDafnySequence force() {
        if (ans == null) {
            ans = computeElements();
            // Allow left and right to be garbage-collected
            left = null;
            right = null;
        }

        return ans;
    }

    @Override
    public int length() {
        return length;
    }

    private NonLazyDafnySequence computeElements() {
        // Somewhat arbitrarily, the copier will be created by the leftmost
        // sequence.  This is fine unless native Java code is uncareful and
        // has created ArrayDafnySequences of boxed primitive types.
        Copier copier;

        // Treat this instance as the root of a tree, where a
        // ConcatDafnySequence is an internal node (with its left and right
        // fields as children) and any other DafnySequence is a leaf node,
        // and prepare to perform a non-recursive in-order traversal.  (We could
        // use recursion, but there could easily be enough sequences being
        // concatenated to exhaust the system stack.)
        Deque> toVisit = new ArrayDeque<>();

        // Another thread may have already completed force() at this point.
        DafnySequence leftBuffer = left;
        DafnySequence rightBuffer = right;
        if (leftBuffer == null || rightBuffer == null) {
            return ans;
        }

        toVisit.push(rightBuffer);
        DafnySequence first = leftBuffer;
        while (first instanceof ConcatDafnySequence) {
            ConcatDafnySequence cfirst = (ConcatDafnySequence) first;
            leftBuffer = cfirst.left;
            rightBuffer = cfirst.right;
            if (leftBuffer == null || rightBuffer == null) {
                break;
            } else {
                toVisit.push(rightBuffer);
                first = leftBuffer;
            }
        }
        toVisit.push(first);

        copier = first.newCopier(this.length);

        while (!toVisit.isEmpty()) {
            DafnySequence seq = toVisit.pop();

            if (seq instanceof ConcatDafnySequence) {
                ConcatDafnySequence cseq = (ConcatDafnySequence) seq;

                leftBuffer = cseq.left;
                rightBuffer = cseq.right;
                if (leftBuffer == null || rightBuffer == null) {
                    copier.copyFrom(cseq.ans);
                } else {
                    toVisit.push(rightBuffer);
                    toVisit.push(leftBuffer);
                }
            } else {
                copier.copyFrom(seq);
            }
        }

        return copier.result();
    }
}