dafny.DafnySequence Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of DafnyRuntime Show documentation
Show all versions of DafnyRuntime Show documentation
Runtime for Dafny programs compiled to Java
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 extends T> th, DafnySequence extends T> 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 extends R> seq, BigInteger b, R t) {
return seq.update(b.intValue(), t);
}
public static DafnySequence update(DafnySequence extends R> seq, int idx, R t) {
return seq.update(idx, t);
}
public static DafnySequence update(DafnySequence extends R> 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 extends DafnySequence extends T>> 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();
}
}