/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package edu.mit.csail.sdg.alloy4compiler.sim;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import edu.mit.csail.sdg.alloy4.ConstList;
import edu.mit.csail.sdg.alloy4.ErrorAPI;
import edu.mit.csail.sdg.alloy4.ErrorType;
import edu.mit.csail.sdg.alloy4.Util;
import edu.mit.csail.sdg.alloy4.ConstList.TempList;
/** Immutable; represents a tupleset. */
public final class SimTupleset implements Iterable<SimTuple> {
// if (min<max && !next) this == tuples + min..max
// else if (min<max && next) this == tuples + { (min,min+1)...(max-1,max) }
// else this == tuples
/** The list of tuples.
* <br> <b>Invariant:</b> If nonempty, it must contain only same-arity tuples.
* <br> <b>Invariant:</b> It must not contain duplicate tuples.
*/
private final ConstList<SimTuple> tuples;
private final int min;
private final int max;
private final boolean next;
/** Construct a tupleset with the given 4 values (Note: caller MUST make sure there are no duplicates, even between (min,max) and tuples, and that all tuples are of same arity!) */
private SimTupleset(Collection<SimTuple> tuples, int min, int max, boolean next) {
this.tuples = ConstList.make(tuples);
this.min = min;
this.max = max;
this.next = next;
}
/** Construct a tupleset containing the given list of tuples (Note: caller MUST make sure there are no duplicates, and all tuples are of same arity!) */
private SimTupleset(Collection<SimTuple> tuples) {
this.tuples = ConstList.make(tuples);
this.min = 0;
this.max = 0;
this.next = false;
}
/** The tupleset containing no tuples. */
public static final SimTupleset EMPTY = new SimTupleset(new TempList<SimTuple>(0).makeConst());
/** Construct the set containing integers between min and max (inclusively). */
public static SimTupleset make(int min, int max) {
if (min>max) return EMPTY;
if (min==max) return new SimTupleset(Util.asList(SimTuple.make(SimAtom.make(min))), 0, 0, false);
return new SimTupleset(EMPTY.tuples, min, max, false);
}
/** Construct the set containing (min,min+1)...(max-1,max) */
public static SimTupleset makenext(int min, int max) {
return min>=max ? EMPTY : (new SimTupleset(EMPTY.tuples, min, max, true));
}
/** Construct a tupleset containing the given tuple. */
public static SimTupleset make(SimTuple tuple) {
return new SimTupleset(Util.asList(tuple));
}
/** Make a tupleset containing the given atom. */
public static SimTupleset make(String atom) {
return make(SimTuple.make(atom));
}
/** Make a tupleset containing a deep copy of the given list of tuples (Note: caller MUST make sure there are no duplicates, and all tuples are of same arity!) */
public static SimTupleset make(Collection<SimTuple> tuples) {
return tuples.size()==0 ? EMPTY : new SimTupleset(tuples);
}
/** If this tupleset is empty, then return 0, else return the arity of every tuple in this tupleset. */
public int arity() {
return min<max ? (next ? 2 : 1) : (tuples.size()==0 ? 0 : tuples.get(0).arity());
}
/** Returns the i-th tuple, or null if no such tuple. */
private SimTuple get(long i) {
if (i<0) return null;
long ans = (min<max) ? (max - (long)min) : 0;
if (min<max && !next) ans++;
if (i<ans) {
SimAtom a = SimAtom.make(min+i);
if (next) return SimTuple.make(a, SimAtom.make((min+i)+1)); else return SimTuple.make(a);
}
i = i - ans;
if (i<tuples.size()) return tuples.get((int)i); else return null;
}
/** Returns true if this is empty. */
public boolean empty() {
return min>=max && tuples.size()==0;
}
/** Returns the number of tuples in this tupleset (this answer may be truncated if it cannot fit in a 32-bit integer) */
public int size() {
return (int)longsize();
}
/** Returns the number of tuples in this tupleset (this answer will never overflow) */
public long longsize() {
long ans = (min<max) ? (max - (long)min) : 0;
if (min<max && !next) ans++;
return ans + tuples.size();
}
/** Returns true if this tupleset contains the given tuple. */
public boolean has(SimTuple that) {
if (arity()!=that.arity()) return false;
if (min<max && !next) {
Integer a = that.get(0).toInt(null);
if (a!=null && min<=a && a<=max) return true;
}
if (min<max && next) {
Integer a = that.get(0).toInt(null), b = that.get(1).toInt(null);
if (a!=null && b!=null && a<b && a.intValue()==b.intValue()-1 && min<=a && b<=max) return true;
}
return tuples.contains(that);
}
/** Returns true if this tupleset is unary and contains the given atom. */
public boolean has(SimAtom that) {
if (arity()!=1) return false;
if (min<max && !next) {
Integer a = that.toInt(null);
if (a!=null && min<=a && a<=max) return true;
}
for(int i=tuples.size()-1; i>=0; i--) if (tuples.get(i).get(0)==that) return true;
return false;
}
/** Returns an arbitrary atom from an arbitrary tuple.
* @throws - ErrorAPI if this tupleset is empty
*/
public SimAtom getAtom() throws ErrorAPI {
if (tuples.size()>0) return tuples.get(0).get(0);
if (min>=max) throw new ErrorAPI("This tupleset is empty");
return SimAtom.make(min);
}
/** Returns an arbitrary tuple.
* @throws - ErrorAPI if this tupleset is empty
*/
public SimTuple getTuple() throws ErrorAPI {
if (tuples.size()>0) return tuples.get(0);
if (min>=max) throw new ErrorAPI("This tupleset is empty");
SimAtom a = SimAtom.make(min);
if (next) return SimTuple.make(a, SimAtom.make(min+1)); else return SimTuple.make(a);
}
/** Return the union of this and that; (if this tupleset and that tupleset does not have compatible arity, then we return this tupleset as is).
* <br/> Note: the tuples in the result will be ordered as follows:
* first comes the tuples in "this" in original order,
* then the tuples that are in "that" but not in "this".
*/
public SimTupleset union(SimTupleset that) {
if (this.empty() || this==that) return that;
if (that.empty() || arity()!=that.arity()) return this;
TempList<SimTuple> ans = null; // when null, it means we haven't found any new tuple to add yet
for(SimTuple x: that) if (!has(x)) {
if (ans == null) ans = new TempList<SimTuple>(tuples);
ans.add(x);
}
return ans==null ? this : new SimTupleset(ans.makeConst(), min, max, next);
}
/** Return the union of this and that; (if this tupleset and that tuple does not have compatible arity, then we return this tupleset as is).
* <br/> Note: if this operation is a no-op, we guarantee we'll return this SimTupleset as is.
*/
public SimTupleset union(SimTuple that) {
if (empty()) return make(that);
if (arity()!=that.arity() || has(that)) return this;
TempList<SimTuple> ans = new TempList<SimTuple>(tuples.size()+1);
ans.addAll(tuples).add(that);
return new SimTupleset(ans.makeConst(), min, max, next);
}
//================================================================================================================//
/** Return the tupleset where each tuple is truncated to the first N atoms; if n is zero or negative, we return the emptyset; if n >= this.arity, we return this as is. */
public SimTupleset head(int n) {
if (n<=0 || empty()) return EMPTY; else if (arity() <= n) return this;
if (min<max) { // if we get here, than arity must be 2, and n must be 1.
TempList<SimTuple> ans = new TempList<SimTuple>(tuples.size());
for(SimTuple x: tuples) {
Integer a = x.head().toInt(null);
if (a!=null && a>=min && a<max) continue;
SimTuple y = SimTuple.make(x.head());
if (!ans.contains(y)) ans.add(y);
}
return new SimTupleset(ans.makeConst(), min, max-1, false);
}
TempList<SimTuple> ans = new TempList<SimTuple>(tuples.size());
for(SimTuple x: this) { SimTuple y = x.head(n); if (!ans.contains(y)) ans.add(y); }
return new SimTupleset(ans.makeConst());
}
/** Return the tupleset where each tuple is truncated to the last N atoms; if n is zero or negative, we return the emptyset; if n >= this.arity, we return this as is. */
public SimTupleset tail(int n) {
if (n<=0 || empty()) return EMPTY; else if (arity() <= n) return this;
if (min<max) { // if we get here, than arity must be 2, and n must be 1.
TempList<SimTuple> ans = new TempList<SimTuple>(tuples.size());
for(SimTuple x: tuples) {
Integer a = x.tail().toInt(null);
if (a!=null && a>min && a<=max) continue;
SimTuple y = SimTuple.make(x.tail());
if (!ans.contains(y)) ans.add(y);
}
return new SimTupleset(ans.makeConst(), min+1, max, false);
}
TempList<SimTuple> ans = new TempList<SimTuple>(tuples.size());
for(SimTuple x: this) { SimTuple y = x.tail(n); if (!ans.contains(y)) ans.add(y); }
return new SimTupleset(ans.makeConst());
}
/** Returns a read-only iterator over the tuples. */
public Iterator<SimTuple> iterator() {
return new Iterator<SimTuple>() {
private long n = longsize();
private long i = 0;
public SimTuple next() { if (i>=n) throw new NoSuchElementException(); else {i++; return get(i-1);} }
public boolean hasNext() { return i<n; }
public void remove() { throw new UnsupportedOperationException(); }
};
}
/** Write this SimTupleset as { (".." ".." "..") (".." ".." "..") (".." ".." "..") } */
void write(BufferedOutputStream out) throws IOException {
boolean first = true;
out.write('{');
for(SimTuple x: this) {
if (first) first=false; else out.write(' ');
x.write(out);
}
out.write('}');
}
/** Read a { (".." ".." "..") (".." ".." "..") (".." ".." "..") } tupleset. */
static SimTupleset read(BufferedInputStream in) throws IOException {
while(true) {
int c = in.read();
if (c<0) throw new IOException("Unexpected EOF");
if (c>0 && c<=' ') continue; // skip whitespace
if (c=='{') break; else throw new IOException("Expecting start of tupleset");
}
LinkedHashSet<SimTuple> list = new LinkedHashSet<SimTuple>();
while(true) {
int c = in.read();
if (c<0) throw new IOException("Unexpected EOF");
if (c>0 && c<=' ') continue; // skip whitespace
if (c=='}') break;
if (c!='(') throw new IOException("Expecting start of tuple");
list.add(SimTuple.read(in));
c = in.read();
if (c<0) throw new IOException("Unexpected EOF");
if (c=='}') break;
if (!(c<=' ')) throw new IOException("Expecting \')\' or white space after a tuple.");
}
return make(list);
}
/** Returns the index position if the list of tuples contains the tuple (a,b) (or return -1 if not found). */
private static int find(TempList<SimTuple> tuples, SimAtom a, SimAtom b) {
if (tuples.size() == 0 || tuples.get(0).arity() != 2) return -1;
for(int i=tuples.size()-1; i >= 0; i--) {
SimTuple x = tuples.get(i);
if (x.head()==a && x.tail()==b) return i;
}
return -1;
}
/** {@inheritDoc} */
@Override public String toString() {
StringBuilder sb = null;
for(SimTuple x: this) {
if (sb==null) sb = new StringBuilder("{"); else sb.append(", ");
x.toString(sb);
}
return sb==null ? "{}" : (sb.append("}").toString());
}
/** Returns a hashcode consistent with the equals() method. */
@Override public int hashCode() {
int ans = 0;
for(SimTuple t: this) ans = ans + t.hashCode();
return ans;
}
/** Returns true if this contains the same tuples as that. */
@Override public boolean equals(Object that) {
return this==that || (that instanceof SimTupleset && equals((SimTupleset)that));
}
/** Returns true if this contains the same tuples as that. */
public boolean equals(SimTupleset that) {
// since SimTupleset must not contain duplicates, and this.size()==that.size(), comparing one way is sufficient
return this==that || (that!=null && longsize()==that.longsize() && in(that));
}
/** Returns true if this is a subset of that. */
public boolean in(SimTupleset that) {
if (empty() || this==that) return true;
if (longsize()>that.longsize() || arity()!=that.arity()) return false;
for(SimTuple t: this) if (!that.has(t)) return false;
return true;
}
/** Sum up all the integer atoms in this tupleset; (if this tupleset's arity is not 1, then we return 0) */
public int sum() {
if (arity() != 1) return 0;
Integer zero = 0;
int ans = 0;
for(SimTuple t: this) ans = ans + t.get(0).toInt(zero);
return ans;
}
/** Return the identity over this tupleset; (if this tupleset's arity is not 1, then we return an emptyset)
* <br/> Note: the result's tuple order is the same as this tupleset's tuple order.
*/
public SimTupleset iden() {
if (arity() != 1) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>(size());
for(SimTuple x: this) ans.add(SimTuple.make(x.head(), x.head())); // since "this" has no duplicate tuples, "ans" will not have duplicate tuples either
return new SimTupleset(ans.makeConst());
}
/** Return the relational override of this and that; (if this tupleset and that tuple does not have compatible arity, then we return this tupleset as is).
* <br/> Note: in general, the tuples may be ordered arbitrarily in the result.
* <br/> Note: if this operation is a no-op, we guarantee we'll return this SimTupleset as is.
*/
public SimTupleset override(final SimTuple that) {
if (arity()==1) return union(that);
if (empty()) return SimTupleset.make(that);
if (arity()!=that.arity()) return this;
boolean added = false, same = false;
TempList<SimTuple> ans = new TempList<SimTuple>(size());
SimAtom head = that.get(0);
for(SimTuple x: this) {
if (x.get(0)!=head) { ans.add(x); continue; }
if (x.equals(that)) same = true;
if (!added) { ans.add(that); added=true; }
}
if (!added) ans.add(that); else if (same && longsize()==ans.size()) return this;
return new SimTupleset(ans.makeConst());
}
/** Return the relational override of this and that; (if this tupleset and that tupleset does not have compatible arity, then we return this tupleset as is).
* <br/> Note: in general, the tuples may be ordered arbitrarily in the result.
*/
public SimTupleset override(SimTupleset that) throws ErrorAPI {
if (arity()==1) return union(that);
if (this.empty() || this==that) return that;
if (that.empty() || this.arity()!=that.arity()) return this;
if (that.longsize()==1) return override(that.getTuple()); // very common case, so let's optimize it
TempList<SimTuple> ans = new TempList<SimTuple>(size());
for(SimTuple x: this) if (!that.has(x)) ans.add(x);
ans.addAll(that);
return new SimTupleset(ans.makeConst());
}
/** Return this minus that; (if this tupleset and that tupleset does not have compatible arity, then we return this tupleset as is).
* <br/> Note: The resulting tuples will keep their original order.
*/
public SimTupleset difference(SimTupleset that) {
if (this.empty() || this==that) return EMPTY;
if (that.empty() || arity()!=that.arity()) return this;
TempList<SimTuple> ans = new TempList<SimTuple>(size()-1);
for(SimTuple x: this) if (!that.has(x)) ans.add(x);
return ans.size()==longsize() ? this : (ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst()));
}
/** Return this minus that; (if this tupleset and that tuple does not have compatible arity, then we return this tupleset as is).
* <br/> Note: The resulting tuples will keep their original order.
* <br/> Note: if this operation is a no-op, we guarantee we'll return this SimTupleset as is.
*/
public SimTupleset difference(SimTuple that) {
if (empty() || arity()!=that.arity()) return this;
TempList<SimTuple> ans = new TempList<SimTuple>(size()-1);
for(SimTuple x: this) {
if (that==null || !x.equals(that)) ans.add(x); else that=null;
}
return that!=null ? this : (ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst()));
}
/** Return this minus any tuple that contains the given atom.
* <br/> Note: The resulting tuples will keep their original order.
*/
public SimTupleset removeAll(SimAtom that) {
if (empty()) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>(size()-1);
again:
for(SimTuple x: this) {
for(int i=x.arity()-1; i>=0; i--) if (x.get(i)==that) continue again;
ans.add(x);
}
return ans.size()==longsize() ? this : (ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst()));
}
/** Return the transpose of this tupleset; (if this tupleset's arity is not 2, we'll return an empty set instead) */
public SimTupleset transpose() {
if (empty() || arity()!=2) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>(size());
for(SimTuple x: this) ans.add(SimTuple.make(x.tail(), x.head())); // since "this" has no duplicate tuples, "ans" will not have duplicate tuples either
return new SimTupleset(ans.makeConst());
}
/** Return the cartesian product of this and that. */
public SimTupleset product(SimTupleset that) {
if (empty() || that.empty()) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>(size() * that.size());
for(SimTuple a: this) for(SimTuple b: that) {
ans.add(a.product(b)); // We don't have to check for duplicates, because we assume every tuple in "this" has same arity, and every tuple in "that" has same arity
}
return new SimTupleset(ans.makeConst());
}
/** Return the relational join between this and that (throws ErrorType if this.arity==1 and that.arity==1) */
public SimTupleset join(SimTupleset that) throws ErrorType {
if (empty() || that.empty()) return EMPTY;
if (arity()==1 && that.arity()==1) throw new ErrorType("Cannot join two unary relations.");
TempList<SimTuple> ans = new TempList<SimTuple>();
for(SimTuple a: this) for(SimTuple b: that) if (a.tail()==b.head()) {
SimTuple c = a.join(b);
if (!ans.contains(c)) ans.add(c);
}
return ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst());
}
/** Return the intersection of this and that. */
public SimTupleset intersect(SimTupleset that) {
if (this==that) return this; else if (empty() || that.empty()) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>(size() < that.size() ? size() : that.size());
for(SimTuple x: that) if (has(x)) ans.add(x);
if (ans.size()==0) return EMPTY;
if (ans.size()==this.longsize()) return this;
if (ans.size()==that.longsize()) return that; else return new SimTupleset(ans.makeConst());
}
/** Return true if the intersection of this and that is nonempty. */
public boolean intersects(SimTupleset that) {
if (empty()) return false;
if (this==that) return true;
for(SimTuple x: that) if (has(x)) return true;
return false;
}
/** Returns this<:that (NOTE: if this.arity!=1, then we return the empty set) */
public SimTupleset domain(SimTupleset that) {
if (arity()!=1 || that.empty()) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>(that.size());
for(SimTuple x: that) if (has(x.head())) ans.add(x);
return ans.size()==that.longsize() ? that : (ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst()));
}
/** Returns this:>that (NOTE: if that.arity!=1, then we return the empty set) */
public SimTupleset range(SimTupleset that) {
if (that.arity()!=1 || this.empty()) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>(this.size());
for(SimTuple x: this) if (that.has(x.head())) ans.add(x);
return ans.size()==this.longsize() ? this : (ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst()));
}
/** Returns the closure of this tupleset (NOTE: if this.arity!=2, we will return an empty set) */
public SimTupleset closure() {
if (arity()!=2) return EMPTY;
TempList<SimTuple> ar = new TempList<SimTuple>(size());
ar.addAll(this);
while(true) {
int n = ar.size();
for(int i=0; i<n; i++) {
SimTuple left = ar.get(i);
if (left.head()==left.tail()) continue; // whatever "right" is, "left.right" won't add any new tuple to the final answer
for(int j=0; j<n; j++) if (i!=j) { // whatever "left" is, "left.left" won't add any new tuple to the final answer
SimTuple right = ar.get(j);
if (right.head()==right.tail()) continue; // whatever "left" is, "left.right" won't add any new tuple to the final answer
if (left.tail()==right.head() && find(ar, left.head(), right.tail())<0) ar.add(SimTuple.make(left.head(), right.tail()));
}
}
if (n == ar.size()) return ar.size()==longsize() ? this : new SimTupleset(ar.makeConst()); // if we went through the loop without making any change, we're done
}
}
/** Return the set of tuples which begins with the given tuple (where we remove the "matching leading part") */
public SimTupleset beginWith(SimTuple x) {
int shift = arity() - x.arity();
if (shift <= 0) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>();
again:
for(SimTuple r: this) {
for(int i=0; i<x.arity(); i++) if (r.get(i) != x.get(i)) continue again;
ans.add(r.tail(shift));
}
return ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst());
}
/** Return the set of tuples which ends with the given tuple (where we remove the "matching trailing part") */
public SimTupleset endWith(SimTuple x) {
int shift = arity() - x.arity();
if (shift <= 0) return EMPTY;
TempList<SimTuple> ans = new TempList<SimTuple>();
again:
for(SimTuple r: this) {
for(int i=0; i<x.arity(); i++) if (r.get(i+shift) != x.get(i)) continue again;
ans.add(r.head(shift));
}
return ans.size()==0 ? EMPTY : new SimTupleset(ans.makeConst());
}
/** Returns a modifiable copy of the list of all i-th atom from all tuples in some arbitrary order (0 is first atom, 1 is second atom...)
* @throws - ErrorAPI if this tupleset contains at least one tuple whose length is less than or equal to i
*/
public List<SimAtom> getAllAtoms(int column) throws ErrorAPI {
if (empty()) return new ArrayList<SimAtom>(0);
if (column<0 || column>=arity()) throw new ErrorAPI("This tupleset does not have an \""+column+"th\" column.");
IdentityHashMap<SimAtom,Boolean> ans = new IdentityHashMap<SimAtom,Boolean>();
for(SimTuple x: this) ans.put(x.get(column), Boolean.TRUE);
return new ArrayList<SimAtom>(ans.keySet());
}
/** Return true if this is a total ordering over "elem", with "first" being the first element of the total order. */
public boolean totalOrder(SimTupleset elem, SimTupleset first) throws ErrorAPI {
if (elem.empty()) return false;
if (elem.longsize()==1) return elem.arity()==1 && first.equals(elem) && empty();
if (first.longsize()!=1 || first.arity()!=1 || elem.arity()!=1 || arity()!=2 || longsize()!=elem.longsize()-1) return false;
SimAtom e = first.getAtom();
List<SimAtom> elems = elem.getAllAtoms(0);
if (longsize() > Integer.MAX_VALUE) throw new OutOfMemoryError();
List<SimTuple> next = new ArrayList<SimTuple>(size());
for(SimTuple x: this) next.add(x);
while(true) {
// "e" must be in elems; remove it from elems
for(int n=elems.size(), i=0; ; i++)
if (i>=n) return false;
else if (elems.get(i)==e) { elems.set(i, elems.get(n-1)); elems.remove(n-1); break; }
// if "e" was the last element, then "next" must be empty as well
if (elems.size()==0) return next.size()==0;
// remove (e,e') from next and let e' be the new e
// (if there was a cycle, we would eventually detect that since the repeated element would no longer be in "elems")
for(int n=next.size(), i=0; ; i++)
if (i>=n) return false;
else if (e==next.get(i).head()) { e=next.get(i).tail(); next.set(i, next.get(n-1)); next.remove(n-1); break; }
}
}
/** Return an iterator over all subset x of this where x.size<=1 */
public Iterator<SimTupleset> loneOf() {
return new Iterator<SimTupleset>() {
private long i = (-1); // -1 if we haven't started yet; otherwise it is the next element to return
public SimTupleset next() {
if (i<0) {i++; return EMPTY;} else if (i>=longsize()) throw new NoSuchElementException();
SimTupleset ans = make(get(i));
i++;
return ans;
}
public boolean hasNext() { return i < longsize(); }
public void remove() { throw new UnsupportedOperationException(); }
};
}
/** Return an iterator over all subset x of this where x.size==1 */
public Iterator<SimTupleset> oneOf() {
Iterator<SimTupleset> ans = loneOf();
ans.next(); // here, we depend on our knowledge that loneOf() will always return the emptyset first, so we can skip it up front
return ans;
}
/** Return an iterator over all subset x of this */
public Iterator<SimTupleset> setOf() {
if (longsize() > Integer.MAX_VALUE) throw new OutOfMemoryError();
return new Iterator<SimTupleset>() {
private boolean in[] = new boolean[size()]; // indicates whether each tuple should appear in the upcoming tupleset; if null, it means no more results
public SimTupleset next() {
if (in==null) throw new NoSuchElementException();
TempList<SimTuple> ans = new TempList<SimTuple>();
for(int i=0; i<in.length; i++) if (in[i]) ans.add(get(i));
for(int i=0; ; i++) if (i==in.length) {in=null;break;} else if (!in[i]) {in[i]=true; break;} else {in[i]=false;}
return new SimTupleset(ans.makeConst());
}
public boolean hasNext() { return in!=null; }
public void remove() { throw new UnsupportedOperationException(); }
};
}
/** Return an iterator over all subset x of this where x.size>=1 */
public Iterator<SimTupleset> someOf() {
Iterator<SimTupleset> ans = setOf();
ans.next(); // here, we depend on our knowledge that setOf() will always return the emptyset first, so we can skip it up front
return ans;
}
}