/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.utils.datastructures.persistent; import gnu.trove.procedure.TObjectProcedure; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Random; import java.util.Set; public class ImmutableSet> { @SuppressWarnings({ "rawtypes" }) static final ImmutableSet EMPTY = new EmptyImmutableSet(); private static class EmptyImmutableSet> extends ImmutableSet { public EmptyImmutableSet() { isBlack = true; } protected org.simantics.utils.datastructures.persistent.ImmutableSet addRec(T obj) { return new ImmutableSet(obj); } protected org.simantics.utils.datastructures.persistent.ImmutableSet removeRec(T obj) { return null; } public boolean contains(T obj) { return false; } @Override public boolean forEach(TObjectProcedure arg0) { return true; } @Override void print(int arg0) { } } ImmutableSet left; T key; ImmutableSet right; boolean isBlack; protected ImmutableSet( ImmutableSet left, T key, ImmutableSet right, boolean isBlack) { this.left = left; this.right = right; this.key = key; this.isBlack = isBlack; } @SuppressWarnings("unchecked") public ImmutableSet(T key) { this(EMPTY, key, EMPTY, false); } @SuppressWarnings("unchecked") public static > ImmutableSet of(T ... array) { if(array.length == 0) return EMPTY; ImmutableSet ret = new ImmutableSet(array[0]); for(int i=1;i> ImmutableSet of(Collection c) { Iterator it = c.iterator(); if(!it.hasNext()) return EMPTY; ImmutableSet ret = new ImmutableSet(it.next()); while(it.hasNext()) ret = ret.add(it.next()); return ret; } private ImmutableSet() { } public boolean contains(T obj) { int cmp = obj.compareTo(key); if(cmp < 0) return left.contains(obj); else if(cmp > 0) return right.contains(obj); else return true; } protected ImmutableSet addRec(T obj) { int cmp = obj.compareTo(key); if(cmp < 0) { ImmutableSet newLeft = left.addRec(obj); if(newLeft == left) return this; if(isBlack) return balance(newLeft, key, right); else return red(newLeft, key, right); } else if(cmp > 0) { ImmutableSet newRight = right.addRec(obj); if(newRight == right) return this; if(isBlack) return balance(left, key, newRight); else return red(left, key, newRight); } else return this; } /** * Assumes this is a black nonempty node. * * Removes obj from the tree. The returned tree has always * one black node less in every branch (even if nothing is * removed). * * @param obj * @return */ protected ImmutableSet removeRec(T obj) { int cmp = obj.compareTo(key); if(cmp < 0) { ImmutableSet newLeft = left.removeRec(obj); if(newLeft == null) return null; else if(left.isBlack) return balleft(newLeft, key, right); else return red(newLeft, key, right); } else if(cmp > 0) { ImmutableSet newRight = right.removeRec(obj); if(newRight == null) return null; else if(right.isBlack) return balright(left, key, newRight); else return red(left, key, newRight); } else return append(left, right); } /** * Assumes a and b have the same black depth and keys in a are strictly less * than keys in b. Creates a new tree with the same black depth as a and b. * * @param * @param a * @param b * @return */ protected static > ImmutableSet append(ImmutableSet a, ImmutableSet b) { if(a==EMPTY) return b; if(b==EMPTY) return a; if(a.isBlack) { if(b.isBlack) { ImmutableSet middle = append(a.right, b.left); if(middle.isBlack) return balleft(a.left, a.key, black(middle, b.key, b.right)); else return red(black(a.left, a.key, middle.left), middle.key, black(middle.right, b.key, b.right)); } else return red(append(a, b.left), b.key, b.right); } else { if(b.isBlack) return red(a.left, a.key, append(a.right, b)); else { ImmutableSet middle = append(a.right, b.left); if(middle.isBlack) return red(a.left, a.key, red(middle, b.key, b.right)); else return red(red(a.left, a.key, middle.left), middle.key, red(middle.right, b.key, b.right)); } } } public T getFirst() { if(left == EMPTY) return key; else return left.getFirst(); } static private > ImmutableSet balance( ImmutableSet left, T key, ImmutableSet right) { if(!left.isBlack) { if(!left.left.isBlack) return red( toBlack(left.left), left.key, black(left.right, key, right) ); else if(!left.right.isBlack) return red( black(left.left, left.key, left.right.left), left.right.key, black(left.right.right, key, right) ); } if(!right.isBlack) { if(!right.left.isBlack) return red( black(left, key, right.left.left), right.left.key, black(right.left.right, right.key, right.right) ); else if(!right.right.isBlack) return red( black(left, key, right.left), right.key, toBlack(right.right) ); } return black(left, key, right); } static private > ImmutableSet black( ImmutableSet left, T key, ImmutableSet right) { return new ImmutableSet(left, key, right, true); } static private > ImmutableSet red( ImmutableSet left, T key, ImmutableSet right) { return new ImmutableSet(left, key, right, false); } static private > ImmutableSet toBlack(ImmutableSet tree) { if(tree.isBlack) return tree; else return black(tree.left, tree.key, tree.right); } static private > ImmutableSet toRed(ImmutableSet tree) { if(tree.isBlack) return red(tree.left, tree.key, tree.right); else return tree; } static private > ImmutableSet balleft( ImmutableSet left, T key, ImmutableSet right) { if(left.isBlack) { if(right.isBlack) return balance(left, key, toRed(right)); else return red(black(left, key, right.left.left), right.left.key, balance(right.left.right, right.key, toRed(right.right))); } else return red(toBlack(left), key, right); } static private > ImmutableSet balright( ImmutableSet left, T key, ImmutableSet right) { if(right.isBlack) { if(left.isBlack) return balance(toRed(left), key, right); else return red(balance(toRed(left.left), left.key, left.right.left), left.right.key, black(left.right.right, key, right)); } else return red(left, key, toBlack(right)); } public ImmutableSet add(T obj) { ImmutableSet tree = addRec(obj); tree.isBlack = true; return tree; } public ImmutableSet remove(T obj) { ImmutableSet tree = removeRec(obj); if(tree == null) return this; if(tree.isBlack) return tree; else return black(tree.left, tree.key, tree.right); } public boolean forEach(TObjectProcedure procedure) { if(left.forEach(procedure)) if(procedure.execute(key)) return right.forEach(procedure); return false; } public ImmutableSet addAll(Iterable c) { ImmutableSet ret = this; for(T t : c) ret = ret.add(t); return ret; } static class AddAll> implements TObjectProcedure { ImmutableSet result; public AddAll(ImmutableSet result) { this.result = result; } @Override public boolean execute(T arg0) { result = result.add(arg0); return true; } } public ImmutableSet addAll(ImmutableSet c) { if(this==EMPTY) return c; if(c==EMPTY) return this; AddAll proc = new AddAll(this); c.forEach(proc); return proc.result; } /************************************************************************** * * Testing * ************************************************************************** */ void print(int indentation) { left.print(indentation + 1); for(int i=0;i> ImmutableSet empty() { return EMPTY; } public void validate() { validateRec(); if(!isBlack) System.out.println("Root is not black"); } @SuppressWarnings("unchecked") public static void main(String[] args) { ImmutableSet set = ImmutableSet.EMPTY; final Set set2 = new HashSet(); Random random = new Random(); for(int i=0;i<10000;++i) { int r1 = random.nextInt(1000); int r2 = random.nextInt(1000); set = set.add(r1); set = set.remove(r2); set2.add(r1); set2.remove(r2); set.validate(); for(Integer v : set2) if(!set.contains(v)) System.out.println("set2 has more elements"); set.forEach(new TObjectProcedure() { @Override public boolean execute(Integer arg0) { if(!set2.contains(arg0)) System.out.println("set has more elements"); return true; } }); } /* map.forEach(new TObjectProcedure() { @Override public boolean execute(Integer arg0) { System.out.println(arg0); return true; } }); */ } }