
dafny.DafnySequence Maven / Gradle / Ivy
// 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();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy