package aalgTest;

import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
import org.junit.Assert;
import aalg.algebra.IElement;
import aalg.algebra.IGroup;

public class FiniteGroupTester<T extends IElement> {
	
	private final IGroup<T> group;
	private final Set<T> elementSet;
	private final T identity;
	
	public FiniteGroupTester(final IGroup<T> group) {
		this.group = group;
		this.elementSet = new HashSet<T>();
		this.identity = group.getIdentity();
	}
	
	private void fillElements() {
		clearElements();
		
		for (T element: group.getElements()) {
			if (!elementSet.add (element))
				Assert.fail("group.getElements() returns diplicate elements");
		}
	}

	public void clearElements() {
		elementSet.clear();
	}

	private void testInverse() {
		for (T element: elementSet) {
			final T inverse = group.getInverse(element);
			
			Assert.assertTrue("Not closed for inverse", elementSet.contains(inverse));
			Assert.assertEquals("Wrong inverse", identity, group.operation(element, inverse)); 
		}
	}

	private void testCommutativityAndClosure() {
		for (T elementA: elementSet) {
			for (T elementB: elementSet) {
				final T resultAB = group.operation(elementA, elementB);
				final T resultBA = group.operation(elementB, elementA);
				
				Assert.assertTrue("Group is not closed for operation", elementSet.contains(resultAB));
				Assert.assertEquals("Group is not commutative", resultAB, resultBA);
			}
		}
	}

	private void testAssociativity() {
		for (T elementA: elementSet) {
			for (T elementB: elementSet) {
				for (T elementC: elementSet) {
					final T result1 = group.operation(elementA, group.operation (elementB, elementC));
					final T result2 = group.operation(group.operation (elementA, elementB), elementC);
					
					Assert.assertEquals("Group is not associative", result1, result2);
				}
			}
		}
	}
	
	private void testOrder() {
		final BigInteger expectedOrder = BigInteger.valueOf(elementSet.size());
		final BigInteger givenOrder = group.getOrder();
		
		Assert.assertEquals("Wrong order of group", expectedOrder, givenOrder);
	}
	
	public void testGroup() {
		fillElements();
		
		testCommutativityAndClosure();
		testAssociativity();
		testInverse();
		testOrder();
		
		clearElements();
	}
	
}
