/*******************************************************************************
* Copyright (c) 2009 Centrum Wiskunde en Informatica (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:
* Arnold Lankamp - interfaces and implementation
* Michael Steindorfer - performance improvements
*******************************************************************************/
package org.rascalmpl.value.impl.fast;
import java.util.Iterator;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.exceptions.IllegalOperationException;
import org.rascalmpl.value.impl.util.collections.ShareableValuesHashSet;
import org.rascalmpl.value.impl.util.collections.ShareableValuesList;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.util.RotatingQueue;
import org.rascalmpl.value.util.ShareableHashMap;
import org.rascalmpl.value.util.ValueIndexedHashMap;
public class RelationalFunctionsOnSet {
protected final static TypeFactory typeFactory = TypeFactory.getInstance();
protected final static Type voidType = typeFactory.voidType();
public static int arity(ISet rel) {
return rel.getElementType().getArity();
}
// // TODO: Currently untested in PDB.
// public static ISet union(ISet rel1, ISet rel2){
// ShareableValuesHashSet newData;
// Iterator<IValue> setIterator;
//
// Set thisSet = (Set) rel1;
// Set otherSet = (Set) rel2;
//
// if(otherSet.size() <= rel1.size()){
// newData = new ShareableValuesHashSet(thisSet.data);
// setIterator = otherSet.iterator();
// }else{
// newData = new ShareableValuesHashSet(otherSet.data);
// setIterator = rel1.iterator();
// }
//
// while(setIterator.hasNext()){
// newData.add(setIterator.next());
// }
//
// Type newElementType = thisSet.elementType.lub(otherSet.elementType);
// return new SetWriter(newElementType, newData).done();
// }
// // TODO: Currently untested in PDB.
// public static ISet intersect(ISet rel1, ISet rel2){
// ShareableValuesHashSet commonData = new ShareableValuesHashSet();
// Iterator<IValue> setIterator;
//
// ISet theOtherSet;
//
// if(rel2.size() <= rel1.size()){
// setIterator = rel2.iterator();
// theOtherSet = rel1;
// }else{
// setIterator = rel1.iterator();
// theOtherSet = rel2;
// }
//
// Type newElementType = TypeFactory.getInstance().voidType();
// while(setIterator.hasNext()){
// IValue value = setIterator.next();
// if(theOtherSet.contains(value)){
// newElementType = newElementType.lub(value.getType());
// commonData.add(value);
// }
// }
//
// return new SetWriter(newElementType, commonData).done();
// }
// // TODO: Currently untested in PDB.
// public static ISet subtract(ISet rel1, ISet rel2){
// ShareableValuesHashSet newData = new ShareableValuesHashSet(((Set)rel1).data);
//
// Iterator<IValue> setIterator = rel2.iterator();
// while(setIterator.hasNext()){
// newData.remove(setIterator.next());
// }
//
// Type newElementType = TypeFactory.getInstance().voidType();
// for(IValue el : newData)
// newElementType = newElementType.lub(el.getType());
//
// return new SetWriter(newElementType, newData).done();
// }
private static ShareableValuesHashSet computeCarrier(ISet rel1) {
ShareableValuesHashSet newData = new ShareableValuesHashSet();
Iterator<IValue> relationIterator = ((Set)rel1).data.iterator();
while(relationIterator.hasNext()){
ITuple tuple = (ITuple) relationIterator.next();
Iterator<IValue> tupleIterator = tuple.iterator();
while(tupleIterator.hasNext()){
newData.add(tupleIterator.next());
}
}
return newData;
}
public static ISet carrier(ISet rel1) {
ShareableValuesHashSet newData = computeCarrier(rel1);
Type type = determainMostGenericTypeInTuple(rel1);
return new SetWriter(type, newData).done();
}
// TODO: Currently untested in PDB.
public static ISet domain(ISet rel1){
ShareableValuesHashSet newData = new ShareableValuesHashSet();
Iterator<IValue> relationIterator = ((Set)rel1).data.iterator();
while(relationIterator.hasNext()){
ITuple tuple = (ITuple) relationIterator.next();
newData.add(tuple.get(0));
}
Type type = rel1.getElementType().getFieldType(0);
return new SetWriter(type, newData).done();
}
// TODO: Currently untested in PDB.
public static ISet range(ISet rel1){
ShareableValuesHashSet newData = new ShareableValuesHashSet();
int last = rel1.getElementType().getArity() - 1;
Iterator<IValue> relationIterator = ((Set)rel1).data.iterator();
while(relationIterator.hasNext()){
ITuple tuple = (ITuple) relationIterator.next();
newData.add(tuple.get(last));
}
Type type = rel1.getElementType().getFieldType(last);
return new SetWriter(type, newData).done();
}
public static ISet compose(ISet rel1, ISet rel2){
Type otherTupleType = rel2.getElementType();
if (rel1.getElementType() == voidType) {
return rel1;
}
if(otherTupleType == voidType) {
return rel2;
}
if(rel1.getElementType().getArity() != 2 || otherTupleType.getArity() != 2) {
throw new IllegalOperationException("compose", rel1.getElementType(), otherTupleType);
}
if(!rel1.getElementType().getFieldType(1).comparable(otherTupleType.getFieldType(0))) {
return new SetWriter().done();
}
// Index
ShareableHashMap<IValue, ShareableValuesList> rightSides = new ShareableHashMap<>();
Iterator<IValue> otherRelationIterator = rel2.iterator();
while(otherRelationIterator.hasNext()){
ITuple tuple = (ITuple) otherRelationIterator.next();
IValue key = tuple.get(0);
ShareableValuesList values = rightSides.get(key);
if(values == null){
values = new ShareableValuesList();
rightSides.put(key, values);
}
values.append(tuple.get(1));
}
// Compute
ShareableValuesHashSet newData = new ShareableValuesHashSet();
Type[] newTupleFieldTypes = new Type[]{rel1.getElementType().getFieldType(0), otherTupleType.getFieldType(1)};
Type tupleType = typeFactory.tupleType(newTupleFieldTypes);
Iterator<IValue> relationIterator = ((Set)rel1).data.iterator();
while(relationIterator.hasNext()){
ITuple thisTuple = (ITuple) relationIterator.next();
IValue key = thisTuple.get(1);
ShareableValuesList values = rightSides.get(key);
if(values != null){
Iterator<IValue> valuesIterator = values.iterator();
do{
IValue value = valuesIterator.next();
IValue[] newTupleData = new IValue[]{thisTuple.get(0), value};
newData.add(Tuple.newTuple(tupleType, newTupleData));
}while(valuesIterator.hasNext());
}
}
return new SetWriter(tupleType, newData).done();
}
private static ShareableValuesHashSet computeClosure(ISet rel1, Type tupleType){
ShareableValuesHashSet allData = new ShareableValuesHashSet(((Set)rel1).data);
RotatingQueue<IValue> iLeftKeys = new RotatingQueue<>();
RotatingQueue<RotatingQueue<IValue>> iLefts = new RotatingQueue<>();
ValueIndexedHashMap<RotatingQueue<IValue>> interestingLeftSides = new ValueIndexedHashMap<>();
ValueIndexedHashMap<ShareableValuesHashSet> potentialRightSides = new ValueIndexedHashMap<>();
// Index
Iterator<IValue> allDataIterator = allData.iterator();
while(allDataIterator.hasNext()){
ITuple tuple = (ITuple) allDataIterator.next();
IValue key = tuple.get(0);
IValue value = tuple.get(1);
RotatingQueue<IValue> leftValues = interestingLeftSides.get(key);
ShareableValuesHashSet rightValues;
if(leftValues != null){
rightValues = potentialRightSides.get(key);
}else{
leftValues = new RotatingQueue<>();
iLeftKeys.put(key);
iLefts.put(leftValues);
interestingLeftSides.put(key, leftValues);
rightValues = new ShareableValuesHashSet();
potentialRightSides.put(key, rightValues);
}
leftValues.put(value);
rightValues.add(value);
}
int size = potentialRightSides.size();
int nextSize = 0;
// Compute
do{
ValueIndexedHashMap<ShareableValuesHashSet> rightSides = potentialRightSides;
potentialRightSides = new ValueIndexedHashMap<>();
for(; size > 0; size--){
IValue leftKey = iLeftKeys.get();
RotatingQueue<IValue> leftValues = iLefts.get();
RotatingQueue<IValue> interestingLeftValues = null;
IValue rightKey;
while((rightKey = leftValues.get()) != null){
ShareableValuesHashSet rightValues = rightSides.get(rightKey);
if(rightValues != null){
Iterator<IValue> rightValuesIterator = rightValues.iterator();
while(rightValuesIterator.hasNext()){
IValue rightValue = rightValuesIterator.next();
if(allData.add(Tuple.newTuple(tupleType, new IValue[]{leftKey, rightValue}))){
if(interestingLeftValues == null){
nextSize++;
iLeftKeys.put(leftKey);
interestingLeftValues = new RotatingQueue<>();
iLefts.put(interestingLeftValues);
}
interestingLeftValues.put(rightValue);
ShareableValuesHashSet potentialRightValues = potentialRightSides.get(rightKey);
if(potentialRightValues == null){
potentialRightValues = new ShareableValuesHashSet();
potentialRightSides.put(rightKey, potentialRightValues);
}
potentialRightValues.add(rightValue);
}
}
}
}
}
size = nextSize;
nextSize = 0;
}while(size > 0);
return allData;
}
public static ISet closure(ISet rel1) {
if(rel1.getElementType() == voidType) return rel1;
if(!isBinary(rel1)) {
throw new IllegalOperationException("closure", rel1.getType());
}
Type tupleElementType = rel1.getElementType().getFieldType(0).lub(rel1.getElementType().getFieldType(1));
Type tupleType = typeFactory.tupleType(tupleElementType, tupleElementType);
return new SetWriter(rel1.getElementType(), computeClosure(rel1, tupleType)).done();
}
// TODO: Currently untested in PDB.
public static ISet closureStar(ISet rel1) {
if (rel1.getElementType() == voidType) {
return rel1;
}
if (!isBinary(rel1)) {
throw new IllegalOperationException("closureStar", rel1.getType());
}
Type tupleElementType = rel1.getElementType().getFieldType(0).lub(rel1.getElementType().getFieldType(1));
Type tupleType = typeFactory.tupleType(tupleElementType, tupleElementType);
ShareableValuesHashSet closure = computeClosure(rel1, tupleType);
ShareableValuesHashSet carrier = computeCarrier(rel1);
Iterator<IValue> carrierIterator = carrier.iterator();
while(carrierIterator.hasNext()){
IValue element = carrierIterator.next();
closure.add(Tuple.newTuple(tupleType, new IValue[]{element, element}));
}
return new SetWriter(rel1.getElementType(), closure).done();
}
// TODO: Currently untested in PDB.
public static ISet project(ISet rel1, int... indexes){
ShareableValuesHashSet newData = new ShareableValuesHashSet();
Iterator<IValue> dataIterator = ((Set)rel1).data.iterator();
while(dataIterator.hasNext()){
ITuple tuple = (ITuple) dataIterator.next();
newData.add(tuple.select(indexes));
}
Type type = rel1.getElementType().select(indexes);
return new SetWriter(type, newData).done();
}
// TODO: Currently untested in PDB.
public static ISet projectByFieldNames(ISet rel1, String... fields){
if(!rel1.getElementType().hasFieldNames()) throw new IllegalOperationException("select with field names", rel1.getType());
ShareableValuesHashSet newData = new ShareableValuesHashSet();
Iterator<IValue> dataIterator = ((Set)rel1).data.iterator();
while(dataIterator.hasNext()){
ITuple tuple = (ITuple) dataIterator.next();
newData.add(tuple.selectByFieldNames(fields));
}
Type type = rel1.getElementType().select(fields);
return new SetWriter(type, newData).done();
}
private static Type determainMostGenericTypeInTuple(ISet rel1){
Type result = rel1.getElementType().getFieldType(0);
for(int i = rel1.getElementType().getArity() - 1; i > 0; i--){
result = result.lub(rel1.getElementType().getFieldType(i));
}
return result;
}
private static boolean isBinary(ISet rel1){
return rel1.getElementType().getArity() == 2;
}
}