package aalg.algebra;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;

/**
 * Group of all invertible elements of given base monoid.
 * @author Ing. Petr Ležák
 *
 * @param <T> type of element
 */
public class InvertibleSubGroup<T extends IElement> implements IGroup<T> {

	private final IMonoid<T> baseMonoid;   // Underlying monoid
	private final BigInteger order;   // Number of invertible elements
	
	/**
	 * Creates group above given monoid.
	 * @param baseMonoid base monoid
	 * @param order order of the inversible subgroup
	 */
	public InvertibleSubGroup(final IMonoid<T> baseMonoid, final BigInteger order) {
		this.baseMonoid = baseMonoid;
		this.order = order;
	}
	
	@Override
	public T operation (final T a, final T b) {
		return baseMonoid.operation(a, b);
	}

	@Override
	public T getIdentity() {
		return baseMonoid.getIdentity();
	}

	@Override
	public T repeatOperation(final T base, final BigInteger repeatCount) {
		return baseMonoid.repeatOperation(base, repeatCount);
	}

	@Override
	public T readElement(final InputStream stream) throws IOException {
		return baseMonoid.readElement(stream);
	}

	@Override
	public void writeElement(final T element, final OutputStream stream) throws IOException {
		baseMonoid.writeElement(element, stream);
	}

	@Override
	public Iterable<T> getElements() {
		return new FilteringIterable<T>(baseMonoid.getElements(), this);
	}

	@Override
	public BigInteger getOrder() {
		return order;
	}

	@Override
	public boolean containsElement(final T element) {
		return baseMonoid.containsElement(element) && baseMonoid.getInverse(element) != null;
	}

	@Override
	public T getInverse(final T x) {
		return baseMonoid.getInverse(x);
	}
	
	/**
	 * Returns base monoid.
	 * @return base monoid
	 */
	public IMonoid<T> getBaseMonoid() {
		return baseMonoid;
	}

	@Override
	public T elementFromString(final String string) {
		return baseMonoid.elementFromString(string);
	}

	@Override
	public BigInteger getElementIndex(final T element) {
		return baseMonoid.getElementIndex(element);
	}

}
