/*******************************************************************************
* Copyright (c) 2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI
* * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
* * Paul Klint - Paul.Klint@cwi.nl - CWI
*
* Based on code by:
*
* * Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.rascalmpl.value.impl.func;
import java.util.Iterator;
import java.util.Random;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.exceptions.FactTypeUseException;
import org.rascalmpl.value.exceptions.IllegalOperationException;
import org.rascalmpl.value.impl.util.collections.ShareableValuesHashSet;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
public final class ListFunctions {
private final static TypeFactory TF = TypeFactory.getInstance();
public static IList sublist(IValueFactory vf, IList list1, int offset, int length) {
if (offset < 0 || length < 0 || offset + length > list1.length()) {
throw new IndexOutOfBoundsException();
}
IListWriter w = vf.listWriter();
for (int i = offset; i < offset + length; i++) {
w.append(list1.get(i));
}
return w.done();
}
public static IList insert(IValueFactory vf, IList list1, IValue e) {
IListWriter w = vf.listWriter();
w.appendAll(list1);
w.insert(e);
return w.done();
}
public static IList put(IValueFactory vf, IList list1, int i, IValue e)
throws IndexOutOfBoundsException {
IListWriter w = vf.listWriter();
w.appendAll(list1);
w.replaceAt(i, e);
return w.done();
}
// public static IList replace(IValueFactory vf, IList list1, int first,
// int second, int end, IList repl) {
// IListWriter w = vf.listWriter();
// if (first < end) {
// for (int i = 0; i < first; i++) {
// w.append(list1.get(i));
// }
// w.appendAll(repl);
// for (int i = end; i < list1.length(); i++) {
// w.append(list1.get(i));
// }
// } else {
// for (int i = list1.length() - 1; i > first; i--) {
// w.insert(list1.get(i));
// }
// for (IValue v : repl) {
// w.insert(v);
// }
// for (int i = end; i >= 0; i--) {
// w.insert(list1.get(i));
// }
// }
// return w.done();
// }
public static IList replace(IValueFactory vf, IList list1, int first,
int second, int end, IList repl) throws FactTypeUseException,
IndexOutOfBoundsException {
IListWriter result = vf.listWriter();
int rlen = repl.length();
int increment = Math.abs(second - first);
if (first < end) {
int listIndex = 0;
// Before begin
while (listIndex < first) {
result.append(list1.get(listIndex++));
}
int replIndex = 0;
boolean wrapped = false;
// Between begin and end
while (listIndex < end) {
result.append(repl.get(replIndex++));
if (replIndex == rlen) {
replIndex = 0;
wrapped = true;
}
listIndex++; // skip the replaced element
for (int j = 1; j < increment && listIndex < end; j++) {
result.append(list1.get(listIndex++));
}
}
if (!wrapped) {
while (replIndex < rlen) {
result.append(repl.get(replIndex++));
}
}
// After end
int dlen = list1.length();
while (listIndex < dlen) {
result.append(list1.get(listIndex++));
}
} else {
// Before begin (from right to left)
int listIndex = list1.length() - 1;
while (listIndex > first) {
result.insert(list1.get(listIndex--));
}
// Between begin (right) and end (left)
int replIndex = 0;
boolean wrapped = false;
while (listIndex > end) {
result.insert(repl.get(replIndex++));
if (replIndex == repl.length()) {
replIndex = 0;
wrapped = true;
}
listIndex--; // skip the replaced element
for (int j = 1; j < increment && listIndex > end; j++) {
result.insert(list1.get(listIndex--));
}
}
if (!wrapped) {
while (replIndex < rlen) {
result.insert(repl.get(replIndex++));
}
}
// Left of end
while (listIndex >= 0) {
result.insert(list1.get(listIndex--));
}
}
return result.done();
}
public static IList append(IValueFactory vf, IList list1, IValue e) {
IListWriter w = vf.listWriter();
w.appendAll(list1);
w.append(e);
return w.done();
}
public static boolean contains(IValueFactory vf, IList list1, IValue e) {
for (IValue v : list1) {
if (v.isEqual(e)) {
return true;
}
}
return false;
}
public static IList delete(IValueFactory vf, IList list1, IValue v) {
IListWriter w = vf.listWriter();
boolean deleted = false;
for (Iterator<IValue> iterator = list1.iterator(); iterator.hasNext();) {
IValue e = iterator.next();
if (!deleted && e.isEqual(v)) {
deleted = true; // skip first occurrence
} else {
w.append(e);
}
}
return w.done();
}
public static IList delete(IValueFactory vf, IList list1, int index) {
IListWriter w = vf.listWriter();
int currentIndex = 0;
boolean deleted = false;
for (Iterator<IValue> iterator = list1.iterator(); iterator.hasNext(); currentIndex++) {
IValue e = iterator.next();
if (!deleted && index == currentIndex) {
deleted = true; // skip first occurrence
} else {
w.append(e);
}
}
return w.done();
}
public static IList reverse(IValueFactory vf, IList list1) {
IListWriter w = vf.listWriter();
for (IValue e : list1) {
w.insert(e);
}
return w.done();
}
public static IList shuffle(IValueFactory vf, IList list1, Random rand) {
IListWriter w = vf.listWriter();
w.appendAll(list1); // add everything
// we use Fisher–Yates shuffle (or Knuth shuffle)
// unbiased and linear time (incase of random access)
for (int i = list1.length() - 1; i >= 1; i--) {
w.replaceAt(i, w.replaceAt(rand.nextInt(i + 1), w.get(i)));
}
return w.done();
}
public static IList concat(IValueFactory vf, IList list1, IList list2) {
IListWriter w = vf.listWriter();
w.appendAll(list1);
w.appendAll(list2);
return w.done();
}
public static int hashCode(IValueFactory vf, IList list1) {
int hash = 0;
Iterator<IValue> iterator = list1.iterator();
while (iterator.hasNext()) {
IValue element = iterator.next();
hash = (hash << 1) ^ element.hashCode();
}
return hash;
}
public static boolean equals(IValueFactory vf, IList list1, Object other) {
if (other == list1)
return true;
if (other == null)
return false;
if (other instanceof IList) {
IList list2 = (IList) other;
if (list1.isEmpty() && list2.isEmpty()) {
return true;
}
if (list1.getType() != list2.getType())
return false;
if (hashCode(vf, list1) != hashCode(vf, list2))
return false;
if (list1.length() == list2.length()) {
final Iterator<IValue> it1 = list1.iterator();
final Iterator<IValue> it2 = list2.iterator();
while (it1.hasNext() && it2.hasNext()) {
// call to Object.equals(Object)
if (it1.next().equals(it2.next()) == false)
return false;
}
assert (!it1.hasNext() && !it2.hasNext());
return true;
}
}
return false;
}
public static boolean isEqual(IValueFactory vf, IList list1, IValue other) {
// return equals(vf, list1, other);
if (other == list1)
return true;
if (other == null)
return false;
if (other instanceof IList) {
IList list2 = (IList) other;
if (list1.length() == list2.length()) {
final Iterator<IValue> it1 = list1.iterator();
final Iterator<IValue> it2 = list2.iterator();
while (it1.hasNext() && it2.hasNext()) {
// call to IValue.isEqual(IValue)
if (it1.next().isEqual(it2.next()) == false)
return false;
}
assert (!it1.hasNext() && !it2.hasNext());
return true;
}
}
return false;
}
public static IList product(IValueFactory vf, IList list1, IList list2) {
IListWriter w = vf.listWriter();
for (IValue t1 : list1) {
for (IValue t2 : list2) {
IValue values[] = { t1, t2 };
ITuple t3 = vf.tuple(values);
w.insert(t3);
}
}
return (IList) w.done();
}
public static IList intersect(IValueFactory vf, IList list1, IList list2) {
IListWriter w = vf.listWriter();
for (IValue v : list1) {
if (list2.contains(v)) {
w.append(v);
}
}
return w.done();
}
public static IList subtract(IValueFactory vf, IList list1, IList list2) {
IListWriter w = vf.listWriter();
for (IValue v : list1) {
if (list2.contains(v)) {
list2 = list2.delete(v);
} else
w.append(v);
}
return w.done();
}
public static boolean isSubListOf(IValueFactory vf, IList list1, IList list2) {
int j = 0;
nextValue: for (IValue elm : list1) {
while (j < list2.length()) {
if (elm.isEqual(list2.get(j))) {
j++;
continue nextValue;
} else
j++;
}
return false;
}
return true;
}
public static IList closure(IValueFactory vf, IList list1) {
// will throw exception if not binary and reflexive
list1.getType().closure();
IList tmp = list1;
int prevCount = 0;
ShareableValuesHashSet addedTuples = new ShareableValuesHashSet();
while (prevCount != tmp.length()) {
prevCount = tmp.length();
IList tcomp = compose(vf, tmp, tmp);
IListWriter w = vf.listWriter();
for (IValue t1 : tcomp) {
if (!tmp.contains(t1)) {
if (!addedTuples.contains(t1)) {
addedTuples.add(t1);
w.append(t1);
}
}
}
tmp = tmp.concat(w.done());
addedTuples.clear();
}
return tmp;
}
public static IList closureStar(IValueFactory vf, IList list1) {
list1.getType().closure();
// an exception will have been thrown if the type is not acceptable
IListWriter reflex = vf.listWriter();
for (IValue e : carrier(vf, list1)) {
reflex.insert(vf.tuple(new IValue[] { e, e }));
}
return closure(vf, list1).concat(reflex.done());
}
public static IList compose(IValueFactory vf, IList list1, IList list2) {
Type otherTupleType = list2.getType().getFieldTypes();
if (list1.getElementType() == TF.voidType())
return list1;
if (otherTupleType == TF.voidType())
return list2;
if (list1.getElementType().getArity() != 2
|| otherTupleType.getArity() != 2)
throw new IllegalOperationException("compose",
list1.getElementType(), otherTupleType);
// Relaxed type constraint:
if (!list1.getElementType().getFieldType(1)
.comparable(otherTupleType.getFieldType(0)))
throw new IllegalOperationException("compose",
list1.getElementType(), otherTupleType);
IListWriter w = vf.listWriter();
for (IValue v1 : list1) {
ITuple tuple1 = (ITuple) v1;
for (IValue t2 : list2) {
ITuple tuple2 = (ITuple) t2;
if (tuple1.get(1).isEqual(tuple2.get(0))) {
w.append(vf.tuple(tuple1.get(0), tuple2.get(1)));
}
}
}
return w.done();
}
public static IList carrier(IValueFactory vf, IList rel1) {
IListWriter w = vf.listWriter();
java.util.HashSet<IValue> cache = new java.util.HashSet<>();
for (IValue v : rel1) {
ITuple t = (ITuple) v;
for (IValue e : t) {
if (!cache.contains(e)) {
cache.add(e);
w.append(e);
}
}
}
return w.done();
}
public static IList domain(IValueFactory vf, IList rel1) {
int columnIndex = 0;
IListWriter w = vf.listWriter();
java.util.HashSet<IValue> cache = new java.util.HashSet<>();
for (IValue elem : rel1) {
ITuple tuple = (ITuple) elem;
IValue e = tuple.get(columnIndex);
if (!cache.contains(e)) {
cache.add(e);
w.append(e);
}
}
return w.done();
}
public static IList range(IValueFactory vf, IList rel1) {
int columnIndex = rel1.getType().getArity() - 1;
IListWriter w = vf.listWriter();
java.util.HashSet<IValue> cache = new java.util.HashSet<>();
for (IValue elem : rel1) {
ITuple tuple = (ITuple) elem;
IValue e = tuple.get(columnIndex);
if (!cache.contains(e)) {
cache.add(e);
w.append(e);
}
}
return w.done();
}
public static IList project(IValueFactory vf, IList list1, int... fields) {
IListWriter w = vf.listWriter();
for (IValue v : list1) {
w.append(((ITuple) v).select(fields));
}
return w.done();
}
public static IList projectByFieldNames(IValueFactory vf, IList list1, String... fields) {
int[] indexes = new int[fields.length];
int i = 0;
if (list1.getType().getFieldTypes().hasFieldNames()) {
for (String field : fields) {
indexes[i++] = list1.getType().getFieldTypes()
.getFieldIndex(field);
}
return project(vf, list1, indexes);
}
throw new IllegalOperationException("select with field names",
list1.getType());
}
}