package fr.umlv.tpnotesept;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.junit.Test;

public class SetsTest {
  @Test
  public void immutableIntersectionSet1() {
    HashSet<String> set1 = new HashSet<>();
    HashSet<Object> set2 = new HashSet<>();
    Sets.immutableIntersectionSet(set1, set2);
  }
  
  @Test
  public void immutableIntersectionSet2() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Double> set2 = new HashSet<>();
    Sets.immutableIntersectionSet(set1, set2);
  }
  
  @Test
  public void immutableIntersectionSet3() {
    HashSet<Integer> set1 = new HashSet<>(Arrays.asList(2, 3, 4, 5));
    HashSet<Integer> set2 = new HashSet<>(Arrays.asList(2, 5, 7));
    Set<Integer> set = Sets.immutableIntersectionSet(set1, set2);
    assertTrue(set.contains(2));
    assertTrue(set.contains(5));
   
    assertFalse(set.contains(3));
    assertFalse(set.contains(4));
    assertFalse(set.contains(7));
    
    assertTrue(set.size() == 2);
  }
  
  @Test
  public void immutableSetAsView() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>(Arrays.asList(42, 77));
    Set<Integer> set = Sets.immutableIntersectionSet(set1, set2);
    
    assertTrue(set.isEmpty());
    set1.add(42);
    
    assertTrue(set.size() == 1);
    assertTrue(set.contains(42));
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void immutableSetAdd() {
    Set<Object> set = Sets.immutableIntersectionSet(
        Collections.<Object>emptySet(),
        Collections.<Object>emptySet());
    
    set.add("foo");
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void immutableSetRemove() {
    Set<Object> set = Sets.immutableIntersectionSet(
        new HashSet<>(Arrays.<Object>asList("foo")),
        new HashSet<>(Arrays.<Object>asList("foo")));
    
    set.remove("foo");
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void immutableSetClear() {
    Set<Object> set = Sets.immutableIntersectionSet(
        new HashSet<>(Arrays.<Object>asList("foo")),
        new HashSet<>(Arrays.<Object>asList("foo")));
    
    set.clear();
  }
  
  @Test
  public void immutableSetForEach() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    for(int i=0; i<10_000; i++) {
      set1.add(i);
      set2.add(i);
    }
    Set<Integer> set = Sets.immutableIntersectionSet(set1, set2);
    HashSet<Integer> newSet = new HashSet<>();
    for(Integer i: set) {
      newSet.add(i);
    }
    assertEquals(set1, newSet);
    assertEquals(set2, newSet);
  }
  
  @Test(timeout=500)
  public void immutableSetFastEnough() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    for(int i=0; i<10_000; i++) {
      set1.add(i);
      set2.add(i);
    }
    Set<Integer> set = Sets.immutableIntersectionSet(set1, set2);
    for(int i=0; i<10_000; i++) {
      assertTrue(set.contains(i));
    }
  }
  
  @Test
  public void mutableIntersectionSet() {
    HashSet<Integer> set1 = new HashSet<>(Arrays.asList(2, 3, 4, 5));
    HashSet<Integer> set2 = new HashSet<>(Arrays.asList(2, 5, 7));
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    assertTrue(set.contains(2));
    assertTrue(set.contains(5));
   
    assertFalse(set.contains(3));
    assertFalse(set.contains(4));
    assertFalse(set.contains(7));
    
    assertTrue(set.size() == 2);
  }
  
  @Test
  public void mutableIntersectionSetAsView() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>(Arrays.asList(42, 77));
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    
    assertTrue(set.isEmpty());
    set1.add(42);
    
    assertTrue(set.size() == 1);
    assertTrue(set.contains(42));
  }
  
  @Test
  public void mutableSetForEach() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    for(int i=0; i<10_000; i++) {
      set1.add(i);
      set2.add(i);
    }
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    HashSet<Integer> newSet = new HashSet<>();
    for(Integer i: set) {
      newSet.add(i);
    }
    assertEquals(set1, newSet);
    assertEquals(set2, newSet);
  }
  
  @Test(timeout=500)
  public void mutableSetFastEnough() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    for(int i=0; i<10_000; i++) {
      set1.add(i);
      set2.add(i);
    }
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    for(int i=0; i<10_000; i++) {
      assertTrue(set.contains(i));
    }
  }
  
  @Test
  public void mutableRemove() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    for(int i=0; i<10_000; i++) {
      set1.add(i);
      set2.add(i);
    }
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    for(int i=0; i<10_000; i++) {
      set.remove(i);
    }
    assertTrue(set1.isEmpty());
    assertTrue(set2.isEmpty());
  }
  
  @Test
  public void mutableRemove2() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    for(int i=0; i<10_000; i++) {
      set1.add(i);
      set2.add(i);
    }
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    for(Iterator<Integer> it = set.iterator(); it.hasNext();) {
      it.next();
      it.remove();
    }
    assertTrue(set1.isEmpty());
    assertTrue(set2.isEmpty());
  }
  
  @Test(expected=IllegalStateException.class)
  public void mutableRemove3() {
    HashSet<Integer> set1 = new HashSet<>(Arrays.asList(888));
    HashSet<Integer> set2 = new HashSet<>(Arrays.asList(888));
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    Iterator<Integer> it = set.iterator();
    it.remove();
  }
  
  @Test
  public void mutableAdd() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    for(Integer i: set) {
      assertTrue(set.add(i));
    }
    assertEquals(set1, set2);
  }
  
  @Test
  public void mutableAdd2() {
    HashSet<Integer> set1 = new HashSet<>();
    HashSet<Integer> set2 = new HashSet<>();
    
    Set<Integer> set = Sets.mutableIntersectionSet(set1, set2);
    assertTrue(set.add(777));
    assertFalse(set.add(777));
    
    set1.remove(777);
    assertTrue(set.add(777));
    assertFalse(set.add(777));
    
    set2.remove(777);
    assertTrue(set.add(777));
    assertFalse(set.add(777));
  }
}
