/******************************************************************************* * Copyright (c) 2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse public static 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 org.rascalmpl.value.ISet; import org.rascalmpl.value.ISetWriter; 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.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 final class SetFunctions { private final static TypeFactory TF = TypeFactory.getInstance(); public static boolean contains(IValueFactory vf, ISet set1, IValue e) { for (IValue v : set1) { if (v.isEqual(e)) { return true; } } return false; } public static ISet insert(IValueFactory vf, ISet set1, IValue e) { ISetWriter sw = vf.setWriter(); sw.insertAll(set1); sw.insert(e); return sw.done(); } public static ISet intersect(IValueFactory vf, ISet set1, ISet set2) { if (set1 == set2) return set1; ISetWriter w = vf.setWriter(); for (IValue v : set1) { if (set2.contains(v)) { w.insert(v); } } return w.done(); } public static ISet union(IValueFactory vf, ISet set1, ISet set2) { if (set1 == set2) return set1; ISetWriter w = vf.setWriter(); w.insertAll(set1); w.insertAll(set2); return w.done(); } public static ISet subtract(IValueFactory vf, ISet set1, ISet set2) { if (set1 == set2) return vf.set(); ISetWriter sw = vf.setWriter(); for (IValue a : set1) { if (!set2.contains(a)) { sw.insert(a); } } return sw.done(); } public static ISet delete(IValueFactory vf, ISet set1, IValue v) { ISetWriter w = vf.setWriter(); boolean deleted = false; for (Iterator<IValue> iterator = set1.iterator(); iterator.hasNext();) { IValue e = iterator.next(); if (!deleted && e.isEqual(v)) { deleted = true; // skip first occurrence } else { w.insert(e); } } return w.done(); } public static boolean isSubsetOf(IValueFactory vf, ISet set1, ISet set2) { for (IValue elem : set1) { if (!set2.contains(elem)) { return false; } } return true; } public static int hashCode(IValueFactory vf, ISet set1) { int hash = 0; Iterator<IValue> iterator = set1.iterator(); while (iterator.hasNext()) { IValue element = iterator.next(); hash ^= element.hashCode(); } return hash; } public static boolean equals(IValueFactory vf, ISet set1, Object other) { if (other == set1) return true; if (other == null) return false; if (other instanceof ISet) { ISet set2 = (ISet) other; if (set1.getType() != set2.getType()) return false; if (hashCode(vf, set1) != hashCode(vf, set2)) return false; if (set1.size() == set2.size()) { for (IValue v1 : set1) { if (set2.contains(v1) == false) return false; } return true; } } return false; } /* * NOTE: it's actually difficult to support isEqual semantics for sets if * it is not supported by the underlying container. */ public static boolean isEqual(IValueFactory vf, ISet set1, IValue other) { // return equals(vf, set1, other); if (other == set1) return true; if (other == null) return false; if (other instanceof ISet) { ISet set2 = (ISet) other; if (set1.size() == set2.size()) { for (IValue v1 : set1) { // function contains() calls isEqual() but used O(n) time if (contains(vf, set2, v1) == false) return false; } return true; } } return false; } public static ISet product(IValueFactory vf, ISet set1, ISet set2) { ISetWriter w = vf.setWriter(); for (IValue t1 : set1) { for (IValue t2 : set2) { ITuple t3 = vf.tuple(t1, t2); w.insert(t3); } } return w.done(); } // public static ISet compose(IValueFactory vf, ISet set1, ISet set2) // throws FactTypeUseException { // if (set1.getElementType() == TF.voidType()) // return set1; // if (set2.getElementType() == TF.voidType()) // return set2; // // if (set1.getElementType().getArity() != 2 // || set2.getElementType().getArity() != 2) { // throw new IllegalOperationException( // "Incompatible types for composition.", // set1.getElementType(), set2.getElementType()); // } // // if (!set1.getElementType().getFieldType(1) // .comparable(set2.getElementType().getFieldType(0))) // return vf.set(); // // ISetWriter w = vf.setWriter(); // // if (set1.getElementType().getFieldType(1) // .comparable(set2.getElementType().getFieldType(0))) { // for (IValue v1 : set1) { // ITuple tuple1 = (ITuple) v1; // for (IValue t2 : set2) { // ITuple tuple2 = (ITuple) t2; // // if (tuple1.get(1).isEqual(tuple2.get(0))) { // w.insert(vf.tuple(tuple1.get(0), tuple2.get(1))); // } // } // } // } // return w.done(); // } public static ISet compose(IValueFactory vf, ISet set1, ISet set2) throws FactTypeUseException { if (set1.getElementType() == TF.voidType()) return set1; if (set2.getElementType() == TF.voidType()) return set2; if (set1.getElementType().getArity() != 2 || set2.getElementType().getArity() != 2) { throw new IllegalOperationException( "Incompatible types for composition.", set1.getElementType(), set2.getElementType()); } if (!set1.getElementType().getFieldType(1) .comparable(set2.getElementType().getFieldType(0))) return vf.set(); // Index ShareableHashMap<IValue, ShareableValuesList> rightSides = new ShareableHashMap<>(); Iterator<IValue> otherRelationIterator = set2.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 Type[] newTupleFieldTypes = new Type[]{set1.getElementType().getFieldType(0), set2.getElementType().getFieldType(1)}; Type tupleType = TF.tupleType(newTupleFieldTypes); ISetWriter resultWriter = vf.setWriter(tupleType); Iterator<IValue> relationIterator = set1.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}; resultWriter.insert(vf.tuple(tupleType, newTupleData)); }while(valuesIterator.hasNext()); } } return resultWriter.done(); } public static ISet carrier(IValueFactory vf, ISet set1) { ISetWriter w = vf.setWriter(); for (IValue t : set1) { w.insertAll((ITuple) t); } return w.done(); } private static ShareableValuesHashSet computeClosureDelta(IValueFactory vf, ISet rel1, Type tupleType) { 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 = rel1.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 final ShareableValuesHashSet newTuples = new ShareableValuesHashSet(); 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(newTuples.add(vf.tuple(tupleType, 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 newTuples; } // public static ISet closure(IValueFactory vf, ISet set1) // throws FactTypeUseException { // // will throw exception if not binary and reflexive // set1.getType().closure(); // // ISet tmp = set1; // // int prevCount = 0; // // while (prevCount != tmp.size()) { // prevCount = tmp.size(); // tmp = tmp.union(compose(vf, tmp, tmp)); // } // // return tmp; // } public static ISet closure(IValueFactory vf, ISet rel1) { if (rel1.getElementType() == TF.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 = TF.tupleType(tupleElementType, tupleElementType); java.util.Set<IValue> closureDelta = computeClosureDelta(vf, rel1, tupleType); // NOTE: type is already known, thus, using a SetWriter degrades performance ISetWriter resultWriter = vf.setWriter(tupleType); resultWriter.insertAll(rel1); resultWriter.insertAll(closureDelta); return resultWriter.done(); } // public static ISet closureStar(IValueFactory vf, ISet set1) // throws FactTypeUseException { // set1.getType().closure(); // // an exception will have been thrown if the type is not acceptable // // ISetWriter reflex = vf.setWriter(); // // for (IValue e : carrier(vf, set1)) { // reflex.insert(vf.tuple(new IValue[] { e, e })); // } // // return closure(vf, set1).union(reflex.done()); // } // TODO: Currently untested in PDB. public static ISet closureStar(IValueFactory vf, ISet rel1) { if (rel1.getElementType() == TF.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 = TF.tupleType(tupleElementType, tupleElementType); // calculate ShareableValuesHashSet closureDelta = computeClosureDelta(vf, rel1, tupleType); ISet carrier = carrier(vf, rel1); // aggregate result // NOTE: type is already known, thus, using a SetWriter degrades performance ISetWriter resultWriter = vf.setWriter(rel1.getElementType()); resultWriter.insertAll(rel1); resultWriter.insertAll(closureDelta); Iterator<IValue> carrierIterator = carrier.iterator(); while (carrierIterator.hasNext()) { IValue element = carrierIterator.next(); resultWriter.insert(vf.tuple(tupleType, element, element)); } return resultWriter.done(); } private static boolean isBinary(ISet rel1){ return rel1.getElementType().getArity() == 2; } public static ISet domain(IValueFactory vf, ISet set1) { int columnIndex = 0; ISetWriter w = vf.setWriter(); for (IValue elem : set1) { ITuple tuple = (ITuple) elem; w.insert(tuple.get(columnIndex)); } return w.done(); } public static ISet range(IValueFactory vf, ISet set1) { int columnIndex = set1.getType().getArity() - 1; ISetWriter w = vf.setWriter(); for (IValue elem : set1) { ITuple tuple = (ITuple) elem; w.insert(tuple.get(columnIndex)); } return w.done(); } public static ISet project(IValueFactory vf, ISet set1, int... fields) { ISetWriter w = vf.setWriter(); for (IValue v : set1) { w.insert(((ITuple) v).select(fields)); } return w.done(); } public static ISet projectByFieldNames(IValueFactory vf, ISet set1, String... fields) { int[] indexes = new int[fields.length]; int i = 0; if (set1.getType().getFieldTypes().hasFieldNames()) { for (String field : fields) { indexes[i++] = set1.getType().getFieldTypes() .getFieldIndex(field); } return project(vf, set1, indexes); } throw new IllegalOperationException("select with field names", set1.getType()); } }