///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2006, Eric D. Friedman All Rights Reserved.
// Copyright (c) 2009, Rob Eden All Rights Reserved.
// Copyright (c) 2009, Jeff Randall All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
///////////////////////////////////////////////////////////////////////////////


package gnu.trove.set.hash;

import junit.framework.TestCase;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;



/**
 * Created: Sat Nov  3 10:33:15 2001
 *
 * @author Eric D. Friedman
 * @author Rob Eden
 * @author Jeff Randall
 * @version $Id: THashSetTest.java,v 1.1.2.3 2010/03/02 04:09:50 robeden Exp $
 */

public class THashSetTest extends TestCase {

    public THashSetTest( String name ) {
        super( name );
    }


    public void setUp() throws Exception {
        super.setUp();
    }


    public void tearDown() throws Exception {
        super.tearDown();
    }


    public void testConstructors() throws Exception {
        Set<String> set = new THashSet<String>();
        assertNotNull( set );

        String[] strings = {"a", "b", "c", "d"};
        set.addAll( Arrays.asList( strings ) );

        Set<String> copy = new THashSet<String>( set );
        assertTrue( "set not a copy: " + set + ", " + copy, set.equals( copy ) );

        Set<String> another = new THashSet<String>( 20 );
        another.addAll( Arrays.asList( strings ) );
        assertTrue( "set not equal: " + set + ", " + copy, set.equals( another ) );

        another = new THashSet<String>( 2, 1.0f );
        another.addAll( Arrays.asList( strings ) );
        assertTrue( "set not equal: " + set + ", " + copy, set.equals( another ) );
    }


    public void testIsEmpty() throws Exception {
        Set<String> s = new THashSet<String>();
        assertTrue( "new set wasn't empty", s.isEmpty() );

        s.add( "One" );
        assertTrue( "set with element reports empty", !s.isEmpty() );
        s.clear();
        assertTrue( "cleared set reports not-empty", s.isEmpty() );
    }


    public void testContains() throws Exception {
        Set<String> s = new THashSet<String>();
        String o = "testContains";
        s.add( o );
        assertTrue( "contains failed", s.contains( o ) );
    }


    @SuppressWarnings({"ForLoopReplaceableByForEach"})
    public void testContainsAll() throws Exception {
        Set<String> s = new THashSet<String>();
        String[] o = {"Hello World", "Goodbye World", "Hello Goodbye"};
        s.addAll( Arrays.asList( o ) );
        for ( int i = 0; i < o.length; i++ ) {
            assertTrue( o[i], s.contains( o[i] ) );
        }
        assertTrue( "containsAll failed: " + s,
                s.containsAll( Arrays.asList( o ) ) );

        String[] more = {"Hello World", "Goodbye World", "Hello Goodbye", "Not There"};
        assertFalse( "containsAll failed: " + s,
                s.containsAll( Arrays.asList( more ) ) );
    }


    public void testRetainAll() throws Exception {
        Set<String> set = new THashSet<String>();
        String[] strings = {"Hello World", "Goodbye World", "Hello Goodbye", "Remove Me"};
        set.addAll( Arrays.asList( strings ) );
        for ( String string : strings ) {
            assertTrue( string, set.contains( string ) );
        }

        String[] retain = {"Hello World", "Goodbye World", "Hello Goodbye"};
        assertTrue( "retainAll failed: " + set,
                set.retainAll( Arrays.asList( retain ) ) );
        assertTrue( "containsAll failed: " + set,
                set.containsAll( Arrays.asList( retain ) ) );
    }


    public void testRemoveAll() throws Exception {
        Set<String> set = new THashSet<String>();
        String[] strings = {"Hello World", "Goodbye World", "Hello Goodbye", "Keep Me"};
        set.addAll( Arrays.asList( strings ) );
        for ( String string : strings ) {
            assertTrue( string, set.contains( string ) );
        }

        String[] remove = {"Hello World", "Goodbye World", "Hello Goodbye"};
        assertTrue( "removeAll failed: " + set,
                set.removeAll( Arrays.asList( remove ) ) );
        assertTrue( "removeAll failed: " + set,
                set.containsAll( Arrays.asList( "Keep Me" ) ) );

        for ( String element : remove ) {
            assertFalse( element + " still in set: " + set, set.contains( element ) );
        }
        assertEquals( 1, set.size() );
    }


    public void testAdd() throws Exception {
        Set<String> s = new THashSet<String>();
        assertTrue( "add failed", s.add( "One" ) );
        assertTrue( "duplicated add succeded", !s.add( "One" ) );
    }


    public void testRemove() throws Exception {
        Set<String> s = new THashSet<String>();
        s.add( "One" );
        s.add( "Two" );
        assertTrue( "One was not added", s.contains( "One" ) );
        assertTrue( "One was not removed", s.remove( "One" ) );
        assertTrue( "One was not removed", !s.contains( "One" ) );
        assertTrue( "Two was removed", s.contains( "Two" ) );
        assertEquals( 1, s.size() );
    }


    public void testRemoveObjectNotInSet() throws Exception {
        Set<String> set = new THashSet<String>();
        set.add( "One" );
        set.add( "Two" );
        assertTrue( "One was not added", set.contains( "One" ) );
        assertTrue( "One was not removed", set.remove( "One" ) );
        assertTrue( "One was not removed", !set.contains( "One" ) );
        assertTrue( "Two was removed", set.contains( "Two" ) );
        assertFalse( "Three was removed (non-existant)", set.remove( "Three" ) );
        assertEquals( 1, set.size() );
    }


    public void testSize() throws Exception {
        Set<Object> o = new THashSet<Object>();
        assertEquals( "initial size was not 0", 0, o.size() );

        for ( int i = 0; i < 99; i++ ) {
            o.add( new Object() );
            assertEquals( "size did not increase after add", i + 1, o.size() );
        }
    }


    public void testClear() throws Exception {
        Set<String> s = new THashSet<String>();
        s.addAll( Arrays.asList( "one", "two", "three" ) );
        assertEquals( "size was not 3", 3, s.size() );
        s.clear();
        assertEquals( "initial size was not 0", 0, s.size() );
    }


    public void testSerialize() throws Exception {
        Set<String> s = new THashSet<String>();
        s.addAll( Arrays.asList( "one", "two", "three" ) );
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream( baos );
        oos.writeObject( s );

        ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() );
        ObjectInputStream ois = new ObjectInputStream( bais );

        THashSet s2 = (THashSet) ois.readObject();

        assertEquals( s, s2 );
    }


    public void testToArray() {
        Set<String> s = new THashSet<String>();
        String[] str = {"hi", "bye", "hello", "goodbye"};
        s.addAll( Arrays.asList( str ) );
        Object[] res = s.toArray();
        Arrays.sort( str );
        Arrays.sort( res );
        assertTrue( Arrays.equals( str, res ) );
    }


    public void testToArrayWithParams() {
        Set<String> s = new THashSet<String>();
        String[] str = {"hi", "bye", "hello", "goodbye"};
        s.addAll( Arrays.asList( str ) );
        String[] sink = new String[str.length + 2];
        sink[sink.length - 1] = "residue";
        sink[sink.length - 2] = "will be cleared";
        String[] res = s.toArray( sink );

        Set<String> copy = new HashSet<String>();
        copy.addAll( Arrays.asList( res ) );

        Set<String> bogey = new HashSet<String>();
        bogey.addAll( Arrays.asList( str ) );
        bogey.add( "residue" );
        bogey.add( null );
        assertEquals( bogey, copy );
    }


    @SuppressWarnings({"ToArrayCallWithZeroLengthArrayArgument", "SuspiciousToArrayCall"})
    public void testToArrayAnotherType() throws Exception {
        Set<Number> set = new THashSet<Number>();
        Number[] nums = {1138, 42, 86, 99, 101};
        set.addAll( Arrays.asList( nums ) );

        Integer[] to_int_array_zero = set.toArray( new Integer[0] );
        assertTrue( "set and array mismatch: " + set +
                    ", " + Arrays.asList( to_int_array_zero ),
                set.containsAll( Arrays.asList( to_int_array_zero ) ) );

        Integer[] to_int_array_size = set.toArray( new Integer[set.size()] );
        assertTrue( "set and array mismatch: " + set +
                    ", " + Arrays.asList( to_int_array_size ),
                set.containsAll( Arrays.asList( to_int_array_size ) ) );


        Number[] to_num_array_zero = set.toArray( new Number[0] );
        assertTrue( "set and array mismatch: " + set +
                    ", " + Arrays.asList( to_num_array_zero ),
                set.containsAll( Arrays.asList( to_num_array_zero ) ) );

        Number[] to_num_array_size = set.toArray( new Number[set.size()] );
        assertTrue( "set and array mismatch: " + set +
                    ", " + Arrays.asList( to_num_array_size ),
                set.containsAll( Arrays.asList( to_num_array_size ) ) );
    }


    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
    public void testRehashing() throws Exception {
        Set<Integer> s = new THashSet<Integer>();
        for ( int i = 0; i < 10000; i++ ) {
            s.add( new Integer( i ) );
        }
    }


    /**
     * this tests that we throw when people violate the
     * general contract for hashcode on java.lang.Object
     */
    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
    public void testSomeBadlyWrittenObject() {
        Set<Object> s = new THashSet<Object>();
        boolean didThrow = false;
        int i = 0;
        try {
            for (; i < 101; i++ ) {
                s.add( new Crap() );
            }
        }
        catch ( IllegalArgumentException e ) {
            didThrow = true;
        }
        assertTrue( "expected THashSet to throw an IllegalArgumentException", didThrow );
    }


    public void testIterable() {

        Set<String> set = new THashSet<String>();
        set.add( "One" );
        set.add( "Two" );

        for ( String s : set ) {
            assertTrue( s.equals( "One" ) || s.equals( "Two" ) );
        }
    }


    public void testToString() {
        Set<String> set = new THashSet<String>();
        set.add( "One" );
        set.add( "Two" );

        String to_string = set.toString();
        assertTrue( to_string,
			to_string.equals( "{One, Two}" ) || to_string.equals( "{Two, One}" ) );
    }


    public void testEquals() {
        String[] strings = {"hi", "bye", "hello", "goodbye"};
        Set<String> set = new THashSet<String>();
        set.addAll( Arrays.asList( strings ) );
        Set<String> other = new THashSet<String>();
        other.addAll( Arrays.asList( strings ) );

        assertTrue( "sets incorrectly not equal: " + set + ", " + other,
                set.equals( other ) );

        String[] mismatched = {"heyya", "whassup", "seeya", "blargh"};
        Set<String> unequal = new THashSet<String>();
        unequal.addAll( Arrays.asList( mismatched ) );

        assertFalse( "sets incorrectly equal: " + set + ", " + unequal,
                set.equals( unequal ) );

        // Change length, different code branch
        unequal.add( "whee!" );
        assertFalse( "sets incorrectly equal: " + set + ", " + unequal,
                set.equals( unequal ) );
    }


    public void testEqualsNonSet() {
        String[] strings = {"hi", "bye", "hello", "goodbye"};
        Set<String> set = new THashSet<String>();
        set.addAll( Arrays.asList( strings ) );
        List<String> other = new ArrayList<String>();
        other.addAll( Arrays.asList( strings ) );

        assertFalse( "sets incorrectly equals list: " + set + ", " + other,
                set.equals( other ) );

        Map<String, String> map = new HashMap<String, String>();
        for ( String string : strings ) {
            map.put( string, string );
        }
        assertFalse( "sets incorrectly equals map: " + set + ", " + map,
                set.equals( map ) );
    }


    public void testHashcode() {
        String[] strings = {"hi", "bye", "hello", "goodbye"};
        Set<String> set = new THashSet<String>();
        set.addAll( Arrays.asList( strings ) );
        Set<String> other = new THashSet<String>();
        other.addAll( Arrays.asList( strings ) );

        assertTrue( "hashcodes incorrectly not equal: " + set + ", " + other,
                set.hashCode() == other.hashCode() );

        String[] mismatched = {"heyya", "whassup", "seeya", "blargh"};
        Set<String> unequal = new THashSet<String>();
        unequal.addAll( Arrays.asList( mismatched ) );

        assertFalse( "hashcodes unlikely equal: " + set + ", " + unequal,
                set.hashCode() == unequal.hashCode() );
    }


    public void testCompact() {
        int max_size = 10000;
        int reduced_size = 100;

        THashSet<Integer> set = new THashSet<Integer>( max_size, 1.0f );
        for ( int index = 1; index <= max_size; index++ ) {
            set.add( Integer.valueOf( index ) );
        }
        assertEquals( max_size, set.size() );
        int max_length = set._set.length;

        for ( int index = max_size; index > reduced_size; index-- ) {
            set.remove( Integer.valueOf( index ) );
        }
        assertEquals( reduced_size, set.size() );

        set.compact();
        int compacted_length = set._set.length;
        assertFalse( max_length + " should != " + compacted_length,
                max_length == compacted_length );
    }


    public void testDisabledAutoCompact() {
        int max_size = 10000;
        int reduced_size = 100;

        THashSet<Integer> set = new THashSet<Integer>( max_size, 1.0f );
        set.setAutoCompactionFactor( 0.0f );    // Disable
        for ( int index = 1; index <= max_size; index++ ) {
            set.add( Integer.valueOf( index ) );
        }
        assertEquals( max_size, set.size() );
        int max_length = set._set.length;

        for ( int index = max_size; index > reduced_size; index-- ) {
            set.remove( Integer.valueOf( index ) );
        }
        assertEquals( reduced_size, set.size() );
        int uncompacted_length = set._set.length;
        assertEquals( max_length, uncompacted_length );

        set.compact();
        int compacted_length = set._set.length;
        assertFalse( uncompacted_length + " should != " + compacted_length,
                uncompacted_length == compacted_length );
    }


    // in this junk class, all instances hash to the same
    // address, but some objects claim to be equal where
    // others do not.
    public static class Crap {

        public boolean equals( Object other ) {
            return other instanceof Crap;
        }


        public int hashCode() {
            return System.identityHashCode( this );
        }
    }


    public static void main( String[] args ) throws Exception {
        junit.textui.TestRunner.run( new THashSetTest( "testBadlyWrittenObject" ) );
    }
} // THashSetTests
