package aalgTest;

import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;

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

import aalg.algebra.DlDomainParameters;
import aalg.algebra.IElement;
import aalg.algebra.IGroup;
import aalg.algebra.IMonoid;
import aalg.algebra.InvertibleSubGroup;
import aalg.algebra.MultiGroup;
import aalg.algebra.MultiMonoidElement;
import aalg.algebra.PrimeFieldElement;

public class GroupTest {
	
	
	private <T extends IElement> BigInteger calculateGroupOrder(final IGroup<T> group) {
		BigInteger elementCount = BigInteger.ZERO;
		
		for (T element: group.getElements()) {
			elementCount = elementCount.add(BigInteger.ONE);
		}
		
		return elementCount;
	}
	
	private <T extends IElement> void testInverse(final IGroup<T> group) {
		for (T element: group.getElements()) {
			final T inverse = group.getInverse(element);
			
			if (inverse != null) {
				Assert.assertEquals (group.getIdentity(), group.operation(element, inverse));
			}
		}
	}
	
	private <T extends IElement> void clasifyElements (final InvertibleSubGroup<T> group, final Set<T> invertibleElements, final Set<T> nonInvertibleElements) {
		final IMonoid<T> monoid = group.getBaseMonoid();
		
		for (T element: monoid.getElements()) {
			if (group.containsElement(element))
				invertibleElements.add(element);
			else
				nonInvertibleElements.add(element);
		}
	}
	
	private <T extends IElement> void closureTest(final InvertibleSubGroup<T> group) {
		final Set<T> invertibleElements = new HashSet<T>();
		final Set<T> nonInvertibleElements = new HashSet<T>();
		clasifyElements (group, invertibleElements, nonInvertibleElements);
		
		System.out.println ("Invertible: " + invertibleElements.size() + " non invertible: " + nonInvertibleElements.size());
		
	}
	
	private <T extends IElement> void fieldSizeTest(final IGroup<T> group) {
		final BigInteger calculatedSize = calculateGroupOrder(group);
		final BigInteger givenSize = group.getOrder();
		
		Assert.assertEquals(givenSize, calculatedSize);
	}
	
	@Test
	public void testMultiGroup() {
		final BigInteger p = BigInteger.valueOf(3);
		
		final MultiGroupCreator creator = new MultiGroupCreator(p);
		final DlDomainParameters<MultiMonoidElement<PrimeFieldElement>> domainParameters = creator.createGroup();
		final InvertibleSubGroup<MultiMonoidElement<PrimeFieldElement>> group = (InvertibleSubGroup<MultiMonoidElement<PrimeFieldElement>>) domainParameters.getGroup();
				
		testInverse(group);
		fieldSizeTest(group);
		closureTest(group);
	}
}
