package unify;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.Assert;
import org.junit.Test;

import de.dhbwstuttgart.typeinference.unify.interfaces.IFiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.MPair;
import de.dhbwstuttgart.typeinference.unify.model.PairOperator;
import de.dhbwstuttgart.typeinference.unify.model.FiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.UnifyType;

public class FiniteClosureTest {
	
	@Test
	public void testSmaller() {
		TypeFactory tf = new TypeFactory();

		UnifyType integer = tf.getSimpleType("Integer");
		UnifyType number = tf.getSimpleType("Number");
		
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();
		fcb.add(integer, number);
		fcb.add(tf.getSimpleType("MyMap", "T"), tf.getSimpleType("HashMap", tf.getSimpleType("Integer")));
		fcb.add(tf.getSimpleType("HashMap", "T"), tf.getSimpleType("Collection"));
		IFiniteClosure fc = fcb.getCollectionExample();

		/*
		 * Test Case 1:
		 * 
		 * smaller(Set<Integer>) = { HashSet<Integer>, Set<Integer, TreeSet<Integer>, LinkedHashSet<Integer> }
		 */
		
		UnifyType setInt = tf.getSimpleType("Set", integer);
		UnifyType hashSetInt = tf.getSimpleType("HashSet", integer);
		UnifyType treeSetInt = tf.getSimpleType("TreeSet", integer);
		UnifyType linkedHashSetInt = tf.getSimpleType("LinkedHashSet", integer);
		
		Set<UnifyType> expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				setInt, hashSetInt, linkedHashSetInt, treeSetInt,
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setInt));
		
		/*
		 * Test Case 2:
		 * 
		 * smaller(Set<? ext Integer>) = 
		 * { HashSet<Integer>, Set<Integer>, TreeSet<Integer>, LinkedHashSet<Integer>,
		 *   HashSet<? ext Integer>, Set<? ext Integer>, TreeSet<? ext Integer>, LinkedHashSet<? ext Integer> }
		 */
		
		UnifyType extInt = tf.getExtendsType(integer);
		UnifyType hashSetExtInt = tf.getSimpleType("HashSet", extInt);
		UnifyType treeSetExtInt = tf.getSimpleType("TreeSet", extInt);
		UnifyType linkedHashSetExtInt = tf.getSimpleType("LinkedHashSet", extInt);
		UnifyType setExtInt = tf.getSimpleType("Set", extInt);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
					setInt, hashSetInt, linkedHashSetInt, treeSetInt,
					hashSetExtInt, treeSetExtInt, linkedHashSetExtInt, setExtInt,
			}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setExtInt));
		
		/*
		 * Test Case 3:
		 * 
		 * smaller(Set<? ext Number>) = 
	 	 * { HashSet<Integer>, Set<Integer>, TreeSet<Integer>, LinkedHashSet<Integer>,
	 	 *   HashSet<Number>, Set<Number>, TreeSet<Number>, LinkedHashSet<Number>,
		 *   HashSet<? ext Integer>, Set<? ext Integer>, TreeSet<? ext Integer>, LinkedHashSet<? ext Integer> 
		 *   HashSet<? ext Number, Set<? ext Number, TreeSet<? ext Number, LinkedHashSet<? ext Number> }
		 */
		
		UnifyType hashSetNum = tf.getSimpleType("HashSet", number);
		UnifyType treeSetNum = tf.getSimpleType("TreeSet", number);
		UnifyType linkedHashSetNum = tf.getSimpleType("LinkedHashSet", number);
		UnifyType setNum = tf.getSimpleType("Set", number);
		
		UnifyType extNum = tf.getExtendsType(number);
		UnifyType hashSetExtNum = tf.getSimpleType("HashSet", extNum);
		UnifyType treeSetExtNum = tf.getSimpleType("TreeSet", extNum);
		UnifyType linkedHashSetExtNum = tf.getSimpleType("LinkedHashSet", extNum);
		UnifyType setExtNum = tf.getSimpleType("Set", extNum);
		
		expectedResult =  new HashSet<>(Arrays.stream(new UnifyType[] {
				setInt, hashSetInt, linkedHashSetInt, treeSetInt,
				setNum, hashSetNum, linkedHashSetNum, treeSetNum,
				setExtInt, hashSetExtInt, linkedHashSetExtInt, treeSetExtInt,
				setExtNum, hashSetExtNum, linkedHashSetExtNum, treeSetExtNum
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setExtNum));
		
		/*
		 * Test Case 4:
		 * smaller(Set<T>) = 		 
		 * { HashSet<T>, Set<T>, TreeSet<T>, LinkedHashSet<T> }
		 */
		
		UnifyType t = tf.getPlaceholderType("T");
		UnifyType setT = tf.getSimpleType("Set", t);
		UnifyType hashSetT = tf.getSimpleType("HashSet", t);
		UnifyType treeSetT = tf.getSimpleType("TreeSet", t);
		UnifyType linkedHashSetT = tf.getSimpleType("LinkedHashSet", t);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
					setT, hashSetT, treeSetT, linkedHashSetT
			}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setT));
		
		/*
		 * Test Case 5 
		 * 
		 * smaller(Set<? super Integer> = 
		 * { Set<Integer>, HashSet<Integer>, TreeSet<Integer>, LinkedHashSet<Integer>,
		 *   Set<? super Integer>, HashSet<? super Integer>, TreeSet<? super Integer>, LinkedHashSet<? super Integer>,
		 *   Set<? super Number>, HashSet<? super Number>, TreeSet<? super Number>, LinkedHashSet<? super Number> }
		 */

		UnifyType superNum = tf.getSuperType(number);
		UnifyType superInt = tf.getSuperType(integer);
		
		UnifyType setSupInt = tf.getSimpleType("Set", superInt);
		UnifyType hashSetSupInt = tf.getSimpleType("HashSet", superInt);
		UnifyType linkedHashSetSupInt = tf.getSimpleType("LinkedHashSet", superInt);
		UnifyType treeSetSupInt = tf.getSimpleType("TreeSet", superInt);
		UnifyType setSupNum = tf.getSimpleType("Set", superNum);
		UnifyType hashSetSupNum = tf.getSimpleType("HashSet", superNum);
		UnifyType linkedHashSetSupNum = tf.getSimpleType("LinkedHashSet", superNum);
		UnifyType treeSetSupNum = tf.getSimpleType("TreeSet", superNum);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				setSupInt, hashSetSupInt, linkedHashSetSupInt, treeSetSupInt,
				setSupNum, hashSetSupNum, linkedHashSetSupNum, treeSetSupNum,
				setInt, hashSetInt, linkedHashSetInt, treeSetInt,
				setNum, hashSetNum, linkedHashSetNum, treeSetNum
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setSupInt));
		
		/*
		 * Test Case 6:
		 * TODO probleme wenn Set<? extends T> weil T auch in der Klassendeklaration class Set<T> verwendet wird.
		 * smaller(Set<? ext T1) = 
		 * { HashSet<T1>, Set<T1>, TreeSet<T1>, LinkedHashSet<T1>,
		 *   HashSet<? ext T1>, Set<? ext T1>, TreeSet<? ext T1>, LinkedHashSet<? ext T1> }
		 * 
		 */
		UnifyType t1 = tf.getPlaceholderType("T1");
		UnifyType extT1 = tf.getExtendsType(t1);
		UnifyType setExtT1 = tf.getSimpleType("Set", extT1);
		UnifyType hashSetExtT1 = tf.getSimpleType("HashSet", extT1);
		UnifyType treeSetExtT1 = tf.getSimpleType("TreeSet", extT1);
		UnifyType linkedHashSetExtT1 = tf.getSimpleType("LinkedHashSet", extT1);
		UnifyType setT1 = tf.getSimpleType("Set", t1);
		UnifyType hashSetT1 = tf.getSimpleType("HashSet", t1);
		UnifyType treeSetT1 = tf.getSimpleType("TreeSet", t1);
		UnifyType linkedHashSetT1 = tf.getSimpleType("LinkedHashSet", t1);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				setT1, hashSetT1, treeSetT1, linkedHashSetT1,
				setExtT1, hashSetExtT1, treeSetExtT1, linkedHashSetExtT1
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setExtT1));
		
		/*
		 * Test Case 7:
		 * 
		 * smaller(Set<NotInFc>) = 		 
		 * { HashSet<NotInFc>, Set<NotInFc>, TreeSet<NotInFc>, LinkedHashSet<NotInFc>,
		 *   HashSet<? ext NotInFc>, Set<? ext NotInFc>, TreeSet<? ext NotInFc>, LinkedHashSet<? ext NotInFc> }
		 */
		
		UnifyType notInFc = tf.getSimpleType("notInFC");
		UnifyType setNotInFc = tf.getSimpleType("Set", notInFc);
		UnifyType hashSetNotInFc = tf.getSimpleType("HashSet", notInFc);
		UnifyType treeSetNotInFc = tf.getSimpleType("TreeSet", notInFc);
		UnifyType linkedHashSetNotInFc = tf.getSimpleType("LinkedHashSet", notInFc);
		UnifyType hashSetExtNotInFc = tf.getSimpleType("HashSet", notInFc);
		UnifyType treeSetExtNotInFc = tf.getSimpleType("TreeSet", notInFc);
		UnifyType linkedHashSetExtNotInFc = tf.getSimpleType("LinkedHashSet", notInFc);
		UnifyType setExtNotInFc = tf.getSimpleType("Set", notInFc);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				setNotInFc, hashSetNotInFc, treeSetNotInFc, linkedHashSetNotInFc,
				setExtNotInFc, hashSetExtNotInFc, treeSetExtNotInFc, linkedHashSetExtNotInFc
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setNotInFc));
		
		/*
		 * Test Case 8:
		 * 
		 * smaller(Set<? super NotInFC>) = 
		 * { Set<NotInFC>, HashSet<NotInFC>, LinkedHashSet<NotInFC>, TreeSet<NotInFC>,
		 *   Set<? super NotInFC>, HashSet<? super NotInFC>, LinkedHashSet<? super NotInFC>, TreeSet<? super NotInFc> }
		 */
		
		UnifyType superNotInFc = tf.getSuperType(notInFc);
		UnifyType setSuperNotInFc = tf.getSimpleType("Set", superNotInFc);
		UnifyType hashSetSuperNotInFc = tf.getSimpleType("HashSet", superNotInFc);
		UnifyType treeSetSuperNotInFc = tf.getSimpleType("TreeSet", superNotInFc);
		UnifyType linkedHashSetSuperNotInFc = tf.getSimpleType("LinkedHashSet", superNotInFc);	
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				setNotInFc, hashSetNotInFc, treeSetNotInFc, linkedHashSetNotInFc,
				setSuperNotInFc, hashSetSuperNotInFc, treeSetSuperNotInFc, linkedHashSetSuperNotInFc
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(setSuperNotInFc));
		
		/*
		 * Test Case 8:
		 * 
		 * smaller(NotInFc<? extends Number>) = 
		 * { NotInFc<Integer>, NotInFc<Number>, NotInFc<? extends Number>, NotInFc<? extends Integer> }
		 */
		
		UnifyType notInFcExtNumber = tf.getSimpleType("NotInFc", extNum);
		UnifyType notInFcInteger = tf.getSimpleType("NotInFc", integer);
		UnifyType notInFcNumber = tf.getSimpleType("NotInFc", number);
		UnifyType notInFcExtInt = tf.getSimpleType("NotInFc", extInt);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				notInFcExtNumber, notInFcInteger, notInFcNumber, notInFcExtInt
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, fc.smaller(notInFcExtNumber));
		
		/*
		 * Test Case 9:
		 * 
		 * smaller(NotInFc<? super AlsoNotInFc> = 
		 * { NotInFc<AlsoNotInFc>, NotInFc<? super AlsoNotInFc> }
		 */
		
		UnifyType alsoNotInFc = tf.getSimpleType("AlsoNotInFc");
		UnifyType notInFcAlsoNotInFc = tf.getSimpleType("NotInFc", alsoNotInFc);
		UnifyType notInFcSupAlsoNotInFc = tf.getSimpleType("NotInFc", tf.getSuperType(alsoNotInFc));
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				notInFcAlsoNotInFc, notInFcSupAlsoNotInFc
		}).collect(Collectors.toSet()));
		
		Set<UnifyType> actual = fc.smaller(notInFcSupAlsoNotInFc);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 10:
		 * 
		 * smaller(TreeMap<? extends Number, ? super Integer>) =
		 * { TreeMap<? extends Number, ? super Integer>, TreeMap<? extends Number, ? super Number>, TreeMap<? extends Number, Integer>, TreeMap<? extends Number, Number>
		 *   TreeMap<Number, ? super Integer>, TreeMap<Number, ? super Number>, TreeMap<Number, Integer>, TreeMap<Number, Number>,
		 *   TreeMap<Integer, ? super Integer, TreeMap<Integer, ? super Number>, TreeMap<Integer, Integer>, TreeMap<Integer, Number>
		 *   TreeMap<? extends Integer, ? super Integer>, TreeMap<? extends Integer, ? super Number>, TreeMap<? extends Integer, Integer>, TreeMap<? extends Integer, Number> }
		 */
		
		UnifyType treeMapExtNumSupInt = tf.getSimpleType("TreeMap", extNum, superInt);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				treeMapExtNumSupInt, tf.getSimpleType("TreeMap", extNum, superNum), tf.getSimpleType("TreeMap", extNum, integer), tf.getSimpleType("TreeMap", extNum, number),
				tf.getSimpleType("TreeMap", number, superInt), tf.getSimpleType("TreeMap", number, superNum), tf.getSimpleType("TreeMap", number, integer), tf.getSimpleType("TreeMap", number, number),
				tf.getSimpleType("TreeMap", integer, superInt), tf.getSimpleType("TreeMap", integer, superNum), tf.getSimpleType("TreeMap", integer, integer), tf.getSimpleType("TreeMap", integer, number),
				tf.getSimpleType("TreeMap", extInt, superInt), tf.getSimpleType("TreeMap", extInt, superNum), tf.getSimpleType("TreeMap", extInt, integer), tf.getSimpleType("TreeMap", extInt, number)
		}).collect(Collectors.toSet()));
		
		actual = fc.smaller(treeMapExtNumSupInt);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 11:
		 * 
		 * smaller(SortedMap<Number, T>) =  { SortedMap<Number, T>, NavigableMap<Number, T>, TreeMap<Number, T> }
		 */
		
		UnifyType sortedMapNumberT = tf.getSimpleType("SortedMap", number, t);
		UnifyType navigableMapNumberT = tf.getSimpleType("NavigableMap", number, t);
		UnifyType treeMapNumberT = tf.getSimpleType("TreeMap", number, t);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				sortedMapNumberT, navigableMapNumberT, treeMapNumberT
		}).collect(Collectors.toSet()));
		
		actual = fc.smaller(sortedMapNumberT);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 12:
		 * 
		 * MyMap<K> <* TreeMap<K, List<K>>
		 * 
		 * smaller(TreeMap<Number, List<Number>) = { TreeMap<Number, List<Number>>, MyMap<Number> }
		 */
		
		fcb = new FiniteClosureBuilder();
		UnifyType k = tf.getPlaceholderType("K");
		UnifyType myMap = tf.getSimpleType("MyMap", k);
		fcb.add(myMap, tf.getSimpleType("TreeMap", k, tf.getSimpleType("List", k)));
		fcb.add(integer, number);
		fc = fcb.getCollectionExample();
		
		UnifyType treeMapNumberListNumber = tf.getSimpleType("TreeMap", number, tf.getSimpleType("List", number));
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				treeMapNumberListNumber,
				tf.getSimpleType("MyMap", number)
		}).collect(Collectors.toSet()));
		
		actual = fc.smaller(treeMapNumberListNumber);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 13:
		 * 
		 * MyMap<K> <* TreeMap<K, List<K>>
		 * 
		 * smaller(TreeMap<? extends Number, List<Integer>) = 
		 * { TreeMap<? extends Integer>, List<Integer>>, 
		 *   TreeMap<? extends Number, List<Integer>>,
		 *   TreeMap<Number, List<Integer>>,
		 *   TreeMap<Integer, List<Integer>>,
		 *   MyMap<Integer> }
		 */

		UnifyType listInteger = tf.getSimpleType("List", integer);
		UnifyType treeMapExtNumberListInteger = tf.getSimpleType("TreeMap", extNum, listInteger);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				treeMapExtNumberListInteger,
				tf.getSimpleType("TreeMap", extInt, listInteger),
				tf.getSimpleType("TreeMap", number, listInteger),
				tf.getSimpleType("TreeMap", integer, listInteger),
				tf.getSimpleType("MyMap", integer)
		}).collect(Collectors.toSet()));
		
		actual = fc.smaller(treeMapExtNumberListInteger);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 14
		 *  
		 * MyMap<K> <* TreeMap<K, List<K>>
		 * 
		 * smaller(TreeMap<? extends Number, List<? extends Number>) = 
		 * { TreeMap<? extends Number>, List<? extends Number>>, 
		 *   TreeMap<? extends Integer, List<? extends Number>>,
		 *   TreeMap<Number, List<? extends Number>>,
		 *   TreeMap<Integer, List<? extends Number>>,
		 *   MyMap<Integer> 
		 *   MyMap<Number> 
		 *   MyMap<? extends Integer> 
		 *   MyMap<? extends Number> 
		 */
		
		UnifyType listExtNum = tf.getSimpleType("List", extNum);
		UnifyType treeMapExtNumListExtNum = tf.getSimpleType("TreeMap", extNum, listExtNum);
		UnifyType myMapInt = tf.getSimpleType("MyMap", integer);
		UnifyType myMapNumber = tf.getSimpleType("MyMap", number);
		UnifyType myMapExtInt = tf.getSimpleType("MyMap", extInt);
		UnifyType myMapExtNum = tf.getSimpleType("MyMap", extNum);

		actual = fc.smaller(treeMapExtNumListExtNum);
		
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				tf.getSimpleType("TreeMap", extNum, listExtNum),
				tf.getSimpleType("TreeMap", extInt, listExtNum),
				tf.getSimpleType("TreeMap", number, listExtNum),
				tf.getSimpleType("TreeMap", integer, listExtNum),
				myMapInt, myMapNumber, myMapExtInt, myMapExtNum
		}).collect(Collectors.toSet()));
		
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 15:
		 * 
		 * MyMap<K> <* TreeMap<K, List<K>>
		 * 
		 * smaller(NavigableSet<? extends Integer, ? extends List<? extends Integer>>) = 
		 * { Permutationen der List,
		 *   Permutationen der List in TreeSets,
		 *   MyMap<Integer> und MyMap<? extends Integer>
		 * }
		 */
		
		UnifyType navSet = tf.getSimpleType("NavigableMap", extInt, tf.getExtendsType(tf.getSimpleType("List", extInt)));
		
		actual = fc.smaller(navSet);
		
		Assert.assertEquals(82, actual.size());
		Assert.assertTrue(actual.contains(myMapExtInt));
		Assert.assertTrue(actual.contains(myMapInt));	
	}
	
	@Test
	public void testGreater() {
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();
		UnifyType k = tf.getPlaceholderType("K");
		UnifyType integer = tf.getSimpleType("Integer");
		UnifyType number = tf.getSimpleType("Number");
		UnifyType myMap = tf.getSimpleType("MyMap", k);
		UnifyType myIntMap = tf.getSimpleType("MyIntMap");
		UnifyType collection = tf.getSimpleType("Collection");
		UnifyType sortedSet =tf.getSimpleType("SortedSet", "T");
		UnifyType extInt = tf.getExtendsType(integer);
		UnifyType extNum = tf.getExtendsType(number);
		UnifyType supInt = tf.getSuperType(integer);
		UnifyType supNum = tf.getSuperType(number);
		fcb.add(myMap, tf.getSimpleType("Map", k, tf.getSimpleType("List", k)));
		fcb.add(myIntMap, tf.getSimpleType("MyMap", integer));
		fcb.add(sortedSet, tf.getSimpleType("Set", "T")); // sortedSet < Set missing in collection example
		fcb.add(integer, number);
		IFiniteClosure fc = fcb.getCollectionExample();
		
		/*
		 * Test Case 1:
		 * 
		 * greater(SortedSet<Integer>) = 
		 * { SortedSet<Integer>, Set<Integer>, Collection
		 *   SortedSet<? extends Integer>, SortedSet<? super Integer>, SortedSet<? extends Number>,
		 *   Set<? extends Integer>, Set<? super Integer>, Set<? extends Number> }
		 */

		UnifyType sortedSetInteger = tf.getSimpleType("SortedSet", integer);
		Set<UnifyType> expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				sortedSetInteger, tf.getSimpleType("Set", integer), collection,
				tf.getSimpleType("SortedSet", extInt), tf.getSimpleType("SortedSet", supInt),
				tf.getSimpleType("SortedSet", extNum), tf.getSimpleType("Set", extInt),
				tf.getSimpleType("Set", supInt), tf.getSimpleType("Set", extNum)
		}).collect(Collectors.toSet()));
		
		Set<UnifyType> actual = fc.greater(sortedSetInteger);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 2:
		 * 
		 * greater(SortedSet<? extends Integer>) = 
		 * { SortedSet<? extends Integer>, SortedSet<? extends Number>,
		 *   Set<? extends Integer>, Set<? extends Number>, Collection }
		 */
		
		UnifyType sortedSetExtInt = tf.getSimpleType("SortedSet", extInt);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				sortedSetExtInt, tf.getSimpleType("SortedSet", extNum), collection,
				tf.getSimpleType("Set", extInt), tf.getSimpleType("Set", extNum)
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(sortedSetExtInt);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 3:
		 * 
		 * TODO hier extends und super? (siehe test case 4 bei smaller)
		 * greater(SortedSet<T>) = 
		 * { SortedSet<T>, SortedSet<? extends T>, SortedSet<? super T>,
		 *   Set<T>, Set<? extends T>, Set<? super T>, Collection }
		 */
		
		/*
		 * Test Case 4:
		 * 
		 * greater(SortedSet<? super Number>) = 
		 * { SortedSet<? super Number>, SortedSet<? super Integer>
		 *   Set<? super Number>, Set<? super Integer>, Collection } 
		 */
		
		UnifyType sortedSetSupNum = tf.getSimpleType("SortedSet", supNum);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				sortedSetSupNum, tf.getSimpleType("SortedSet", supInt), collection,
				tf.getSimpleType("Set", supNum), tf.getSimpleType("Set", supInt)
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(sortedSetSupNum);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 5:
		 * 
		 * TODO nicht unifizierbar bei T wenn Set<T> deklariert wurde. Können die  beiden T's verschieden sein?
		 * greater(SortedSet<? extends T1>) = 
		 * { SortedSet<? extends T1>, Set<? extends T1>, Collection }
		 */
		
		UnifyType extT = tf.getExtendsType(tf.getPlaceholderType("T1"));
		UnifyType sortedSetExtT = tf.getSimpleType("SortedSet", extT);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				sortedSetExtT, tf.getSimpleType("Set",  extT), collection,
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(sortedSetExtT);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 6:
		 * 
		 * greater(SortedSet<NotInFc>) = 
		 * { SortedSet<NotInFc>, SortedSet<? extends NotInFC>, SortedSet<? super NotInFc>,
		 *   Set<NotInFc>, Set<? extends NotInFC>, Set<? super NotInFC>, Collection }
		 */
		
		UnifyType notInFc = tf.getSimpleType("NotInFc");
		UnifyType extNotInFc = tf.getExtendsType(notInFc);
		UnifyType supNotInFc = tf.getSuperType(notInFc);
		UnifyType sortedSetNotInFc= tf.getSimpleType("SortedSet", notInFc);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				sortedSetNotInFc, tf.getSimpleType("SortedSet", extNotInFc),  tf.getSimpleType("SortedSet", supNotInFc),
				tf.getSimpleType("Set", notInFc), tf.getSimpleType("Set", extNotInFc), tf.getSimpleType("Set", supNotInFc),
				collection
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(sortedSetNotInFc);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 7:
		 * 
		 * greater(SortedSet<? super NotInFc) = 
		 * { SortedSet<? super NotInFc>, Set<? super NotInFc>, Collection }
		 */
		
		UnifyType sortedSetSupNotInFc= tf.getSimpleType("SortedSet", supNotInFc);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				sortedSetSupNotInFc, tf.getSimpleType("Set", supNotInFc), collection
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(sortedSetSupNotInFc);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 8:
		 * 
		 * greater(NotInFc<Integer>) = 
		 * { NotInFc<Integer>, NotInFc<? super Integer>, NotInFC<? extends Integer>,
		 *   NotInFc<? extends Number> }
		 */
		
		UnifyType notInFcInteger = tf.getSimpleType("NotInFc", integer);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				notInFcInteger, tf.getSimpleType("NotInFc", supInt),
				tf.getSimpleType("NotInFc", extInt), tf.getSimpleType("NotInFc", extNum)
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(notInFcInteger);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 9:
		 * greater(NotInFc<AlsoNotInFc>) =
		 * { NotInFc<AlsoNotInFc>, NotInFc<? extends AlsoNotInFc, NotInFc<? super AlsoNotInFc> }
		 */
		
		UnifyType notInFcAlsoNotInFc = tf.getSimpleType("NotInFc", tf.getSimpleType("AlsoNotInFc"));
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				notInFcAlsoNotInFc, tf.getSimpleType("NotInFc", tf.getExtendsType(tf.getSimpleType("AlsoNotInFc"))),
				tf.getSimpleType("NotInFc", tf.getSuperType(tf.getSimpleType("AlsoNotInFc")))
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(notInFcAlsoNotInFc);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 10:
		 * greater(Map<? extends Integer, Integer>) = 
		 * { Map<? extends Integer, Integer>, Map<? extends Integer, ? extends Integer>,
		 *   Map<? extends Integer, ? super Integer>, Map<? extends Integer, ? extends Number>,
		 *   Map<? extends Number, Integer>, Map<? extends Number, ? extends Integer>, 
		 *   Map<? extends Number, ? super Integer>, Map<? extends Number, ? extends Number> }
		 */
		
		UnifyType mapExtIntInt = tf.getSimpleType("Map", extInt, integer);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				mapExtIntInt, tf.getSimpleType("Map", extInt, extInt),
				tf.getSimpleType("Map", extInt, supInt), tf.getSimpleType("Map", extInt, extNum),
				tf.getSimpleType("Map", extNum, integer), tf.getSimpleType("Map", extNum, extInt),
				tf.getSimpleType("Map", extNum, supInt), tf.getSimpleType("Map", extNum, extNum)
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(mapExtIntInt);
		Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 11:
		 * 
		 * MyIntMap < MyMap<Integer>
         * MyMap<K> < Map<K, List<K>>
		 * 
		 * greater(MyIntMap) = 
		 * { MyMap<Integer>, MyMap<? extends Integer>, MyMap<? extends Number>, MyMap<? super Integer>,
		 *   Map<Integer, List<Integer>, Map<? extends Integer>, List<Integer>,
		 *   Map<? extends Number>, List<Integer>, Map<? super Integer, List<Integer>, MyIntMap }
		 */

		UnifyType listInteger = tf.getSimpleType("List", integer);
		expectedResult = new HashSet<>(Arrays.stream(new UnifyType[] {
				myIntMap, tf.getSimpleType("MyMap", integer), tf.getSimpleType("MyMap", extInt),
				tf.getSimpleType("MyMap", extNum), tf.getSimpleType("MyMap", supInt),
				tf.getSimpleType("Map", integer, listInteger), tf.getSimpleType("Map", extInt, listInteger),
				tf.getSimpleType("MyMap", extNum, listInteger), tf.getSimpleType("MyMap", supInt, listInteger)
		}).collect(Collectors.toSet()));
		
		actual = fc.greater(myIntMap);
		//Assert.assertEquals(expectedResult, actual);
		
		/*
		 * Test Case 12:
		 * 
         * MyIntMap < MyMap<Integer>
         * MyMap<K> < Map<K, List<K>>
         * 
         * TODO
         * D<? super Number> d = null;
		 * A<? super Number, List<? super Number>> a = null;
		 * a = d; ist nicht möglich!
         * 
		 * greater(MyMap<? super Number>) = 
		 * { MyMap<? super Number>, MyMap<? super Integer>,
		 *   Map<? super Number, List<? super Number>>, 
		 *   Map<? super Integer>, List<? super Number>> }
		 */
		
		/*
		 * Test Case 13:
		 * 
		 * greater(SortedMap<? super Number>, ? super List<? extends Integer>>) = 
		 * 
		 */
	}
	
	@Test
	public void testGrArg() {
		IFiniteClosure fc = new FiniteClosureBuilder().getCollectionExample();
		TypeFactory tf = new TypeFactory();
		
		System.out.println("\n\n----- GrArg Test -----");
		System.out.println("GrArg(List<T>) = " + fc.grArg(tf.getSimpleType("List", "T")));
		System.out.println("GrArg(? extends List<T>) = " + fc.grArg(tf.getExtendsType(tf.getSimpleType("List", "T"))));
		System.out.println("GrArg(? super List<T>) = " + fc.grArg(tf.getSuperType(tf.getSimpleType("List", "T"))));
	}
	
	
	@Test
	public void testSmArg() {
		IFiniteClosure fc = new FiniteClosureBuilder().getCollectionExample();
		TypeFactory tf = new TypeFactory();
		
		System.out.println("\n\n----- SmArg Test -----");
		System.out.println("SmArg(List<T>) = " + fc.smArg(tf.getSimpleType("List", "T")));
		System.out.println("SmArg(? extends List<T>) = " + fc.smArg(tf.getExtendsType(tf.getSimpleType("List", "T"))));
		System.out.println("SmArg(? super List<T>) = " + fc.smArg(tf.getSuperType(tf.getSimpleType("List", "T"))));
	}
	
	@Test
	public void testGetGenericType() {
		// TODO
	}
	
	private void printDiff(Set<UnifyType> expected, Set<UnifyType> actual) {
		System.out.println("Diff:");
	    System.out.println("In expected but not in actual:");
	    Set<UnifyType> expected1 = new HashSet<>(expected);
	    expected1.removeAll(actual);
	    System.out.println(expected1);
	}
}