//package gr.forth.ics.util; 
 
/** 
 * Implementation of disjoint-set data structure, providing merge(merge)/find operations, in 
 * order to be able to track whether two elements belong in the same set or not, and 
 * to efficiently merge two sets. 
 * 
 * <p>The set that contains a particular element {@code e} (of type {@code Partition} 
 * is accessed by {@code e.find()}. 
 * Note that the returned set is merely an element itself, which is used as a representative of 
 * the set. So, if two elements have the same representative, they belong to the same set. 
 * Otherwise, they belong to different sets. Note that the elements of a particular set cannot be 
 * queried directly. Thus, perhaps confusingly, a {@code Partition} represents both a single 
 * element <em>and</em> potentially a set of elements, in the particular partition happens 
 * to be chosen as the representative of its set. 
 * 
 * <p>A <em>partition</em> is initially {@link #singleton(Object) created as a singleton}. 
 * It accepts an arbitrary (user-defined) value (of type {@code <E>}) 
 * that is permanently associated with the created partition, and can be accessed by {@link #value()}. 
 * A created partition is independent from any other created partition. This means that the return value 
 * of {@link #find()} is unique, and method {@link #areMerged(Partition, Partition)} 
 * always returns false when given such a partition as an argument. 
 * 
 * <p>A partition {@code p1} may be <em>merged</em> with another partition {@code p2} by 
 * {@code p1.merge(p2);} (or, equivalently, {@code p2.merge(p1);}). From that point on, 
 * this invariant is created: {@code p1.find() == p2.find()} which remains true regardless of 
 * other future merge operations. Similarly, {@code areMerged(p1, p2)} will always return {@code true}. 
 * 
 * <p>All methods throw {@code NullPointerException} if given {@code null} arguments. 
 * 
 * @param <E> the type of the arbitrary (user-defined) element associated with a partition 
 * @author Andreou Dimitris, email: jim.andreou (at) gmail (dot) com 
 * @see <a href="http://en.wikipedia.org/wiki/Disjoint-set_data_structure"> 
 * Disjoint-set data structure article on Wikipedia</a> 
 */ 
public class Partition<E> { 
    private Partition<?> parent; 
    private int rank; 
    private final E value; 
     
    private Partition(E value) { 
        this.value = value; 
        this.parent = this; 
    } 
 
    /** 
     * Creates a partition of a singleton set, containing only itself. The created 
     * partition is permanently associated with the specified value, which can 
     * be accessed by {@linkplain #value()}. 
     * 
     * @param <E> the type of the user-defined value 
     * @param value the value to associate with the created partition 
     * @return the created partition 
     */ 
    public static <E> Partition<E> singleton(E value) { 
        return new Partition<E>(value); 
    } 
 
    /** 
     * Returns the value associated with this partition upon creation. 
     * 
     * @return the value associated with this partition upon creation 
     */ 
    public E value() { 
        return value; 
    } 
 
    /** 
     * Merges this partition with another one. This has the following implications: 
     * <ul> 
     * <li>{@code this.find() == other.find()} will be true, forever 
     * <li>{@code Partition.areMerged(this, other)} will return true, forever 
     * </ul> 
     * @param other the partition to merge this partition with 
     * @return a partition representing the merged partitions. It will be either equal to either 
     *      {@code this.find()} or {@code other.find()}, i.e. one representative of the partitions 
     *      which will be elected to represent the union 
     */ 
    public Partition<?> merge(Partition<?> other) { 
        Partition<?> root1 = other.find(); 
        Partition<?> root2 = find(); 
        if (root1.rank < root2.rank) { 
            root1.parent = root2; 
            return root2; 
        } else if (root1.rank > root2.rank) { 
            root2.parent = root1; 
            return root1; 
        } else { 
            root2.parent = root1; 
            root1.rank++; 
            return root1; 
        } 
    } 
 
    /** 
     * Returns the unique representative of the set that this element belongs to. The 
     * returned instance can be compared with the representative of another element, 
     * to check whether the two elements belong to the same set (when the two 
     * representatives will be identical) 
     *  
     * @return the unique representative of the set that this partition belongs to 
     */ 
    public Partition<?> find() { 
        //A single-pass path compressing algorithm 
        Partition<?> current = this; 
        Partition<?> last = this; 
        while (current.parent != current) { 
            last.parent = current.parent; //initially a no-op 
            last = current; 
            current = current.parent; 
        } 
        return current; 
    } 
 
    /** 
     * Checks whether the two elements have been merged. 
     * 
     * <p>Equivalent to {@code partition1.find() == partition2.find()}. 
     * @param partition1 the first element 
     * @param partition2 the second element 
     * @return whether the two partitions have been merged 
     */ 
    public static boolean areMerged(Partition<?> partition1, Partition<?> partition2) { 
        return partition1.find() == partition2.find(); 
    } 
} 
 
package gr.forth.ics.util; 
 
import junit.framework.TestCase; 
 
/** 
 * 
 * @author Andreou Dimitris, email: jim.andreou (at) gmail (dot) com 
 */ 
public class PartitionTest extends TestCase { 
     
    public PartitionTest(String testName) { 
        super(testName); 
    } 
 
    public void testValue() { 
        Partition<String> p = Partition.singleton("test"); 
        assertEquals("test", p.value()); 
    } 
     
    public void testFind() { 
        Partition<String> p1 = Partition.singleton("a"); 
        assertSame(p1, p1.find()); 
    } 
 
    public void testUnionWithEqualRank() { 
        Partition<String> p1 = Partition.singleton("a"); 
        Partition<String> p2 = Partition.singleton("b"); 
        assertNotSame(p1.find(), p2.find()); 
 
        p1.merge(p2); 
        assertSame(p1.find(), p2.find()); 
    } 
 
    public void testUnionWithLessRank() { 
        Partition<?> pSmall = Partition.singleton("a"); 
        Partition<?> pBig = Partition.singleton("b").merge(Partition.singleton("c")); 
         
        Partition<?> pAll = pSmall.merge(pBig); 
 
        //the small is attached to the big 
        assertSame(pSmall.find(), pBig); 
        assertSame(pBig.find(), pBig); 
        assertSame(pAll, pBig); 
    } 
 
    public void testUnionWithMoreRank() { 
        Partition<?> pSmall = Partition.singleton("a"); 
        Partition<?> pBig = Partition.singleton("b").merge(Partition.singleton("c")); 
 
        Partition<?> pAll = pBig.merge(pSmall); 
 
        //the small is attached to the big 
        assertSame(pSmall.find(), pBig); 
        assertSame(pBig.find(), pBig); 
        assertSame(pAll, pBig); 
    } 
} 
 
    
     
     
     
     
     
     
     
  
  |