package unify;

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

import org.junit.Test;

import de.dhbwstuttgart.typeinference.unify.TypeUnify;
import de.dhbwstuttgart.typeinference.unify.interfaces.IFiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;
import de.dhbwstuttgart.typeinference.unify.model.PairOperator;
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
import de.dhbwstuttgart.typeinference.unify.model.UnifyType;
import de.dhbwstuttgart.typeinference.unify.model.TypeParams;
import junit.framework.Assert;

public class UnifyTest extends TypeUnify{
	
	/**
	 * Testing the unification for cases with (n)one pair and without generics.
	 */
	@Test 
	public void unifyTestTrivial() {
		/*
		 * INIT
		 */
		
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();

		UnifyType number = tf.getSimpleType("Number");
		UnifyType object = tf.getSimpleType("Object");
		UnifyType integer = tf.getSimpleType("Integer");
		UnifyType doubl = tf.getSimpleType("Double");
		
		fcb.add(number, object);
		fcb.add(integer, number);
		fcb.add(doubl, number);

		IFiniteClosure fc = fcb.getCollectionExample();
		
		/*
		 * Test 1: 
		 * 
		 * unify({ }) = { } 
		 */
		
		Set<UnifyPair> eq = new HashSet<UnifyPair>();
		Set<Set<UnifyPair>> expected = new HashSet<>();
		expected.add(new HashSet<>());
		Set<Set<UnifyPair>> actual = unify(eq, fc);
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 2:
		 * 
		 * (a <. Number)
		 */
		
		UnifyType tphA = tf.getPlaceholderType("a");
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, number, PairOperator.SMALLERDOT));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, number, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, integer, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, doubl, PairOperator.EQUALSDOT));
		
		actual = unify(eq, fc);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 3:
		 * 
		 * (Integer <. a)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(integer, tphA, PairOperator.SMALLERDOT));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, integer, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, number, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, object, PairOperator.EQUALSDOT));
		
		actual = unify(eq, fc);
		
		Assert.assertEquals(expected, actual);
		
		
		/*
		 * Test 4:
		 * 
		 * (a <.? Number)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, number, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, number, PairOperator.EQUALSDOT));

		actual = unify(eq, fc);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 5:
		 * 
		 * (a <.? ? super Integer)
		 */
		
		UnifyType supInteger = tf.getSuperType(integer);
		UnifyType supNumber = tf.getSuperType(number);
		UnifyType supObject = tf.getSuperType(object);
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, supInteger, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, integer, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, number, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, object, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, supInteger, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, supNumber, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, supObject, PairOperator.EQUALSDOT));
		
		actual = unify(eq, fc);
		System.out.println("? super Integer");
		System.out.println(actual);
		actual = filterGeneratedTPHsMultiple(actual);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 6:
		 * 
		 * (Number <.? a)
		 * 
		 */
		
		eq = new HashSet<>();
		UnifyType extNumber = tf.getExtendsType(number);
		UnifyType extObject = tf.getExtendsType(object);
		UnifyType supDouble = tf.getSuperType(doubl);
		eq.add(new UnifyPair(number, tphA, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, number, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, extNumber, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, extObject, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, supInteger, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, supDouble, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, supNumber, PairOperator.EQUALSDOT));
		
		actual = unify(eq, fc);
		actual = filterGeneratedTPHsMultiple(actual);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 7:
		 * 
		 * (? extends Number <.? a)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(extNumber, tphA, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, extNumber, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, extObject, PairOperator.EQUALSDOT));
		
		actual = unify(eq, fc);
		actual = filterGeneratedTPHsMultiple(actual);
		Assert.assertEquals(expected, actual);
		
		
		/*
		 * Test 8:
		 * 
		 * (a <.? ? extends Number)
		 */
		
		UnifyType extInteger = tf.getExtendsType(integer);
		UnifyType extDouble = tf.getExtendsType(doubl);
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, extNumber, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, extNumber, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, extInteger, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, extDouble, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, doubl, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, integer, PairOperator.EQUALSDOT));
		addAsSet(expected, new UnifyPair(tphA, number, PairOperator.EQUALSDOT));
		
		actual = unify(eq, fc);
		actual = filterGeneratedTPHsMultiple(actual);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 8:
		 * 
		 * (Integer <. Number)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(integer, number, PairOperator.SMALLERDOT));
		
		expected = new HashSet<>();
		expected.add(new HashSet<>());
		
		actual = unify(eq, fc);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 9:
		 * 
		 * (Integer <.? Number)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(integer, number, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		
		actual = unify(eq, fc);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 10:
		 * 
		 * (a <. b)
		 */
		
		UnifyType tphB = tf.getPlaceholderType("b");
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, tphB, PairOperator.SMALLERDOT));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, tphB, PairOperator.SMALLERDOT));
		
		actual = unify(eq, fc);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 11:
		 * 
		 * (a <.? b)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, tphB, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, tphB, PairOperator.SMALLERDOTWC));
		
		actual = unify(eq, fc);
		
		Assert.assertEquals(expected, actual);
		
		/*
		 * Test 12:
		 * 
		 * (void <. void)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, tf.getSimpleType("void"), PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tf.getSimpleType("void"), tphA, PairOperator.SMALLERDOT));
		
		expected = new HashSet<>();
		addAsSet(expected, new UnifyPair(tphA, tf.getSimpleType("void"), PairOperator.EQUALSDOT));
		
		//Test funktioniert momentan auch ohne ein "void" in der Finite Closure
		//fcb.add(tf.getSimpleType("void"), tf.getSimpleType("Object"));
		
		IFiniteClosure voidFC = fcb.getFiniteClosure();
		
		actual = unify(eq, voidFC);
		
		Assert.assertEquals(expected, actual);
	}
	
	@Test
	public void unifyTestSimple() {
		/*
		 * INIT
		 */
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();

		UnifyType number = tf.getSimpleType("Number");
		UnifyType object = tf.getSimpleType("Object");
		UnifyType integer = tf.getSimpleType("Integer");
		UnifyType doubl = tf.getSimpleType("Double");
		
		//fcb.add(number, object);
		fcb.add(integer, number);
		//fcb.add(doubl, number);

		IFiniteClosure fc = fcb.getCollectionExample();
		
		/*
		 * Test 1: 
		 * 
		 * (Vector<a> <. Vector<? extends b>)
		 * (List<b> <. List<? extends Number>) 
		 * 
		 * Expected: 
		 * {(b = Number), (a = Number)}, {(b = Number), (a = Integer)}, {(b = Number), (a = Integer)}
		 * (b = Integer), 
		 */
		
		UnifyType tphA = tf.getPlaceholderType("a");
		UnifyType tphB = tf.getPlaceholderType("b");
		UnifyType extB = tf.getExtendsType(tphB);
		UnifyType extNum = tf.getExtendsType(number);
		
		Set<UnifyPair> eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tf.getSimpleType("Vector", tphA), tf.getSimpleType("Vector", extB), PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tf.getSimpleType("List", tphB), tf.getSimpleType("List", extNum), PairOperator.SMALLERDOT));
		
		Set<Set<UnifyPair>> expected = new HashSet<>();
		Set<Set<UnifyPair>> actual = unify(eq, fc);
		
		//System.out.println(actual);
		//Assert.assertEquals(actual, expected);
	
		
		/*
		 * Test 2:
		 * 
		 * Vector<? extends a> <. List<? extends Number>
		 * 
		 */
		
		UnifyType extA = tf.getExtendsType(tphA);
		
		eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tf.getSimpleType("Vector", extA), tf.getSimpleType("Vector", extNum), PairOperator.SMALLERDOT));

		expected = new HashSet<>();
		actual = unify(eq, fc);
		
		//System.out.println(actual);
		//Assert.assertEquals(actual, expected);
		
		/*
		 * Test 3:
		 * 
		 * Vector<? extends Number> <. List<? extends a>
		 * 
		 */
		
		eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tf.getSimpleType("Vector", extNum), tf.getSimpleType("Vector", extA), PairOperator.SMALLERDOT));

		expected = new HashSet<>();
		actual = unify(eq, fc);

		System.out.println(actual);
		//Assert.assertEquals(actual, expected);
		
		/*
		 * Test 4:
		 * 
		 * LinkedList <. Deque <. Queue <. Collection
		 * 
		 * Vector<Number> <. List<a>
		 * List<a> <. AbstractList<b>
		 * ? extends Number <.? b
		 */
		
		eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tf.getSimpleType("LinkedList", number), tf.getSimpleType("Deque", tphA), PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tf.getSimpleType("Deque", tphA), tf.getSimpleType("Queue", tphB), PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(extNum, tphB, PairOperator.SMALLERDOTWC));
		
		expected = new HashSet<>();
		actual = unify(eq, fc);
		
		//System.out.println(actual);
		//Assert.assertEquals(actual, expected);
	}
	
	/**
	 * These are tests that specifically test cases where the old unify algorithm was incomplete.
	 */
	@Test
	public void unifyTestExtension() {
		/*
		 * INIT
		 */
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();

		UnifyType number = tf.getSimpleType("Number");
		UnifyType object = tf.getSimpleType("Object");
		UnifyType integer = tf.getSimpleType("Integer");
		UnifyType doubl = tf.getSimpleType("Double");
		UnifyType mygeneric = tf.getSimpleType("MyGeneric", "T");
		
		
		fcb.add(mygeneric, object);
		fcb.add(integer, number);
		//fcb.add(doubl, number);

		IFiniteClosure fc = fcb.getCollectionExample();
		
		
		/*
		 * Test 1:
		 * This is a Test for the extension of case 1 in the cartesian product of step 4.
		 * 
		 * (a <. Vector<b>)
		 * (List<Integer> <. List<b>)
		 * 
		 * Expected:
		 * (b = Integer), (a = Vector<Integer>)
		 * (b = ? extends Integer), (a = Vector<Integer>),
		 * (b = ? extends Integer), (a = Vector<? extends Integer>),
		 * (b = ? super Integer), (a = Vector<Integer>)
		 * (b = ? super Integer), (a = Vector<Number>)
		 * (b = ? super Integer), (a = Vector<? super Integer>)
		 * (b = ? super Integer), (a = Vector<? super Number>)
		 * (b = ? extends Number), (a = Vector<Integer>)
		 * (b = ? extends Number), (a = Vector<Number>)
		 * (b = ? extends Number), (a = Vector<? extends Integer>)
		 * (b = ? extends Number), (a = Vector<? extends Number>)
		 */
		
		UnifyType tphA = tf.getPlaceholderType("a");
		UnifyType tphB = tf.getPlaceholderType("b");
		UnifyType extB = tf.getExtendsType(tphB);
		UnifyType extNum = tf.getExtendsType(number);
		
		Set<UnifyPair> eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tphA, tf.getSimpleType("Stack", tphB), PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tf.getSimpleType("List", integer), tf.getSimpleType("List", tphB), PairOperator.SMALLERDOT));
		
		Set<Set<UnifyPair>> expected = new HashSet<>();
		Set<Set<UnifyPair>> actual = unify(eq, fc);
		
		System.out.println(actual);
		//Assert.assertEquals(actual, expected);
		
		/*
		 * Test 2:
		 * 
		 * This is a test for th extension of case 2 of the cartesian product of step 4.
		 * 
		 * (a <.? ? ext b)
		 * (b =. Number)
		 */
		
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, extB, PairOperator.SMALLERDOTWC));
		eq.add(new UnifyPair(tphB, number, PairOperator.EQUALSDOT));
		
		expected = new HashSet<>();

		actual = unify(eq, fc);
		
		System.out.println("Case 2");
		System.out.println(actual);
		
		/*
		 * Test 3:
		 * This is a test for the extension of case 3 of the cartesian product of step 4.
		 * 
		 * (a <.? ? sup b)
		 * (b = Number)
		 */
		
		UnifyType supB = tf.getSuperType(tphB);
		eq = new HashSet<>();
		eq.add(new UnifyPair(tphA, supB, PairOperator.SMALLERDOTWC));
		eq.add(new UnifyPair(tphB, number, PairOperator.EQUALSDOT));
		
		expected = new HashSet<>();

		actual = unify(eq, fc);
		
		
		System.out.println("Case 3");
		System.out.println(actual);
		//Assert.assertEquals(expected, actual);
		
		/*
		 * Case 4 has no extension
		 */
		
		/*
		 * Test 5:
		 * This is a test for the extension of case 5 of the cartesian product of step 4.
		 * 
		 * Vector<b> <. a
		 * b =. Number
		 */

		eq = new HashSet<>();
		eq.add(new UnifyPair(tf.getSimpleType("HashSet", tphB), tphA, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphB, number, PairOperator.EQUALSDOT));
		
		expected = new HashSet<>();

		actual = unify(eq, fc);
		
		System.out.println(actual);
		//Assert.assertEquals(expected, actual);
		
		/*
		 * Test 6:
		 * This is a test for the extension of case 6 of the cartesian product of step 4.
		 * 
		 * ? extends b <.? a
		 * b =. Integer
		 */

		eq = new HashSet<>();
		eq.add(new UnifyPair(extB, tphA, PairOperator.SMALLERDOTWC));
		eq.add(new UnifyPair(tphB, integer, PairOperator.EQUALSDOT));
		
		expected = new HashSet<>();

		actual = unify(eq, fc);
		
		System.out.println(actual);
		//Assert.assertEquals(expected, actual);
		
		/*
		 * Test 7:
		 * This is a test for the extension of case 7 of the cartesian product of step 4.
		 * 
		 * ? sup b <.? a
		 * b =. Number
		 */

		eq = new HashSet<>();
		eq.add(new UnifyPair(supB, tphA, PairOperator.SMALLERDOTWC));
		eq.add(new UnifyPair(tphB, number, PairOperator.EQUALSDOT));
		
		expected = new HashSet<>();

		actual = unify(eq, fc);
		
		System.out.println("Case 7");
		System.out.println(actual);
		//Assert.assertEquals(expected, actual);
		
		
		/*
		 * Test 8:
		 * This is a test for the extension of case 8 of the cartesian product of step 4.
		 * 
		 * MyGeneric<? extends b> <.? a
		 * b =. Integer
		 */

		eq = new HashSet<>();
		eq.add(new UnifyPair(tf.getSimpleType("MyGeneric", extB), tphA, PairOperator.SMALLERDOTWC));
		eq.add(new UnifyPair(tphB, number, PairOperator.EQUALSDOT));
		
		expected = new HashSet<>();

		actual = unify(eq, fc);
		
		System.out.println("Case 8:");
		System.out.println(actual);
		//Assert.assertEquals(expected, actual);
		
	}
	
	@Test
	public void unifyTestComplex() {
		/*
		 * INIT
		 */
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();

		UnifyType tphT = tf.getPlaceholderType("T");
		UnifyType vector = tf.getSimpleType("Vector", tphT);
		UnifyType number = tf.getSimpleType("Number");
		UnifyType integer = tf.getSimpleType("Integer");
		UnifyType object = tf.getSimpleType("Object");
		UnifyType matrix = tf.getSimpleType("Matrix");
		UnifyType vectorvectorint = tf.getSimpleType("Vector", tf.getSimpleType("Vector", integer));

		
		fcb.add(integer, number);
		fcb.add(matrix, vectorvectorint);
		fcb.add(vector, object);
		
		IFiniteClosure fc = fcb.getFiniteClosure();
		
		Set<UnifyPair> eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tf.getSimpleType("Matrix"), tf.getSimpleType("Vector", tf.getPlaceholderType("a")), PairOperator.SMALLERDOT));

		Set<Set<UnifyPair>> expected = new HashSet<>();
		Set<Set<UnifyPair>> actual = unify(eq, fc);
		
		System.out.println("Test Matrix:");
		System.out.println(actual);
	}	

	@Test
	public void unifyTestVoid() {
		/*
		 * Constraints Set mit "void" Typen
		 * T1 < Integer
		 * Integer < Integer
		 * T1 < T2
		 * T3 < Bool
		 * void < T4
		 * OL < Object
		 * void < void
		 * ol < T5
		 * T5 < ol
		 */
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();

		UnifyType tphT1 = tf.getPlaceholderType("T1");
		UnifyType tphT2 = tf.getPlaceholderType("T2");
		UnifyType tphT3 = tf.getPlaceholderType("T3");
		UnifyType tphT4 = tf.getPlaceholderType("T4");
		UnifyType tphT5 = tf.getPlaceholderType("T5");

		UnifyType integer = tf.getSimpleType("java.lang.Integer");
		UnifyType voidType = tf.getSimpleType("void");
		UnifyType bool = tf.getSimpleType("java.lang.Boolean");
		UnifyType object = tf.getSimpleType("Object");
		UnifyType main = tf.getSimpleType("Main");
		UnifyType ol = tf.getSimpleType("OL");
		
		fcb.add(integer, object);
		fcb.add(main, object);
		fcb.add(bool, object);
		fcb.add(voidType, voidType);
		fcb.add(ol, object);
		
		IFiniteClosure fc = fcb.getFiniteClosure();
		
		Set<UnifyPair> eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tphT1, integer, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(integer, integer, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT1, tphT2, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT2, integer, PairOperator.SMALLERDOT));
		
		eq.add(new UnifyPair(tphT3, bool, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(voidType, tphT4, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(ol, object, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(voidType, voidType, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(ol, tphT5, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT5, ol, PairOperator.SMALLERDOT));

		Set<Set<UnifyPair>> expected = new HashSet<>();
		Set<UnifyPair> solution = new HashSet<UnifyPair>();
		solution.add(new UnifyPair(tphT3, bool, PairOperator.EQUALSDOT));
		solution.add(new UnifyPair(tphT5, ol, PairOperator.EQUALSDOT));
		solution.add(new UnifyPair(tphT2, integer, PairOperator.EQUALSDOT));
		solution.add(new UnifyPair(tphT1, integer, PairOperator.EQUALSDOT));
		solution.add(new UnifyPair(tphT4, voidType, PairOperator.EQUALSDOT));
		expected.add(solution);
		
		
		Set<Set<UnifyPair>> actual = unify(eq, fc);
		
		System.out.println("Test Void:");
		System.out.println(actual);
		
		Assert.assertEquals(expected, actual);
	}

	@Test
	public void unifyTestOverloading(){
		/*
		 * Constraints Set mit "void" Typen
		 * OL < T1
		 * T1 < T1
		 * T1 < T2
		 * T1 < OL
		 */
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();

		UnifyType tphT1 = tf.getPlaceholderType("T1");
		UnifyType tphT2 = tf.getPlaceholderType("T2");

		UnifyType integer = tf.getSimpleType("java.lang.Integer");
		UnifyType voidType = tf.getSimpleType("void");
		UnifyType bool = tf.getSimpleType("java.lang.Boolean");
		UnifyType object = tf.getSimpleType("Object");
		UnifyType main = tf.getSimpleType("Main");
		UnifyType ol = tf.getSimpleType("OL");
		
		fcb.add(integer, object);
		fcb.add(main, object);
		fcb.add(bool, object);
		fcb.add(voidType, voidType);
		fcb.add(ol, object);
		
		IFiniteClosure fc = fcb.getFiniteClosure();
		
		Set<UnifyPair> eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(ol, tphT1, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT1, tphT1, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT1, tphT2, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT1, ol, PairOperator.SMALLERDOT));

		Set<UnifyPair> expectedSolution = new HashSet<UnifyPair>();
		expectedSolution.add(new UnifyPair(tphT1, ol, PairOperator.EQUALSDOT));
		
		
		Set<Set<UnifyPair>> actual = unify(eq, fc);
		
		System.out.println("Test Overloading:");
		System.out.println(actual);
		
		for(Set<UnifyPair> actualSolution : actual){
			Assert.assertTrue(actualSolution.containsAll(expectedSolution));
		}
	}
	
	@Test
	public void unifyTestSubclasses() {
		/*
		 * TPH1 < Integer
		 * Integer < Integer
		 * TPH1 < TPH2
		 * TPH2 < Integer
		 * 
		 * Einzige korrekte Lösung:
		 * TPH1 = Integer, TPH2 = Integer
		 */
		TypeFactory tf = new TypeFactory();
		FiniteClosureBuilder fcb = new FiniteClosureBuilder();

		UnifyType tphT1 = tf.getPlaceholderType("T1");
		UnifyType tphT2 = tf.getPlaceholderType("T2");
		UnifyType tphT3 = tf.getPlaceholderType("T3");
		
		UnifyType integer = tf.getSimpleType("java.lang.Integer");
		UnifyType bool = tf.getSimpleType("java.lang.Boolean");
		UnifyType object = tf.getSimpleType("java.lang.Object");
		UnifyType main = tf.getSimpleType("Main");
		
		fcb.add(integer, object);
		fcb.add(main, object);
		fcb.add(bool, object);
		
		IFiniteClosure fc = fcb.getFiniteClosure();
		
		Set<UnifyPair> eq = new HashSet<UnifyPair>();
		eq.add(new UnifyPair(tphT1, integer, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(integer, integer, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT1, tphT2, PairOperator.SMALLERDOT));
		eq.add(new UnifyPair(tphT2, integer, PairOperator.SMALLERDOT));

		Set<Set<UnifyPair>> expected = new HashSet<>();
		Set<UnifyPair> solution = new HashSet<UnifyPair>();
		solution.add(new UnifyPair(tphT1, integer, PairOperator.EQUALSDOT));
		solution.add(new UnifyPair(tphT2, integer, PairOperator.EQUALSDOT));
		expected.add(solution);
		
		Set<Set<UnifyPair>> actual = unify(eq, fc);
		
		System.out.println("Test Subclass:");
		System.out.println(actual);
		
		Assert.assertEquals(expected, actual);
	}
	
	@Test
	public void applyTypeUnificationRulesTest() {
		
	}
	
	@Test
	public void calculatePairSetsTest() {
		
	}
	
//	@Test
//	public void permuteParamsTest() {
//		TypeFactory tf = new TypeFactory();
//		ArrayList<Set<UnifyType>> candidates = new ArrayList<>();
//		
//		UnifyType p11 = tf.getPlaceholderType("p11");
//		UnifyType p12 = tf.getExtendsType(tf.getSimpleType("p12"));
//		UnifyType p13 = tf.getSimpleType("p13");
//		UnifyType p21 = tf.getPlaceholderType("p21");
//		UnifyType p22 = tf.getPlaceholderType("p22");
//		UnifyType p31 = tf.getSimpleType("p31", "T");
//		
//		Set<UnifyType> p1 = new HashSet<>();
//		p1.add(p11);
//		p1.add(p12);
//		p1.add(p13);
//		
//		Set<UnifyType> p2 = new HashSet<>();
//		p2.add(p21);
//		p2.add(p22);
//		
//		Set<UnifyType> p3 = new HashSet<>();
//		p3.add(p31);
//		
//		candidates.add(p1);
//		candidates.add(p2);
//		candidates.add(p3);
//		
//		
//		/*
//		 * Expected Result:
//		 *  {<x, y, z> | x in { p11, p12, p13}, y in { p21, p22 }, z in { p31 }}
//		 */
//		Set<TypeParams> expected = Arrays.stream(new TypeParams[] {
//				new TypeParams(p11, p21, p31),
//				new TypeParams(p11, p22, p31),
//				new TypeParams(p12, p21, p31),
//				new TypeParams(p12, p22, p31),
//				new TypeParams(p13, p21, p31),
//				new TypeParams(p13, p22, p31)
//		}).collect(Collectors.toSet());
//		
//		Set<TypeParams> actual = permuteParams(candidates);
//		
//		Assert.assertEquals(expected, actual);
//	}
	
	private Set<Set<UnifyPair>> filterGeneratedTPHsMultiple(Set<Set<UnifyPair>> set) {
		return set.stream().map(x -> filterGeneratedTPHs(x)).collect(Collectors.toSet());
	}
	
	private Set<UnifyPair> filterGeneratedTPHs(Set<UnifyPair> set) {
		return set.stream().filter(x -> !((x.getRhsType() instanceof PlaceholderType) && ((PlaceholderType) x.getRhsType()).isGenerated())).
				filter(x -> !((x.getLhsType() instanceof PlaceholderType) && ((PlaceholderType) x.getLhsType()).isGenerated())).collect(Collectors.toSet());
	}
	
	private void addAsSet(Set<Set<UnifyPair>> addTo, UnifyPair... mPairs) {
		addTo.add(new HashSet<>(Arrays.stream(mPairs).collect(Collectors.toSet())));
	}
}