package aalg.algebra;


/**
 * Validator that validates if parameters really forms multimonoid.
 * @author Ing. Petr Ležák
 *
 * @param <F> type of field element
 */
public class MultiMonoidValidator<F extends IElement> {

	private final int size;
	private final IField<F> field;

	private final IVector<F> vectorE;
	private final IVector<F> vectorK;
	private final Matrix<F> matrixL;
	private final Cube<F> cubeM;

	public MultiMonoidValidator(final IField<F> field, final int size, final IVector<F> vectorE, final IVector<F> vectorK, final Matrix<F> matrixL, final Cube<F> cubeM) {
		this.field = field;
		this.size = size;
		this.vectorE = vectorE;
		this.vectorK = vectorK;
		this.matrixL = matrixL;
		this.cubeM = cubeM;
	}
	
	public boolean validateSymmetryM() {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				for (int k = 0; k < size; k++) {
					final F left = cubeM.get(i, j, k);
					final F right = cubeM.get(i, k, j);
					
					if (!left.equals(right))
						return false;
				}
			}
		}
		
		return true;
	}

	public boolean validateEquationM() {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				for (int k = 0; k < size; k++) {
					for (int l = 0; l < size; l++) {
						F left = field.getAdditiveIdentity();
						F right = field.getAdditiveIdentity();
						
						for (int m = 0; m < size; m++) {
							left = field.add(left, field.multiply(cubeM.get(i, l, m), cubeM.get(m, j, k)));
							right = field.add(right, field.multiply(cubeM.get(i, j, m), cubeM.get(m, l, k)));
						}
						
						if (!left.equals(right))
							return false;
					}
				}
			}
		}
		
		return true;
	}

	public boolean validateEquationML1() {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				for (int l = 0; l < size; l++) {
					F left = field.getAdditiveIdentity();
					F right = field.getAdditiveIdentity();
					
					for (int m = 0; m < size; m++) {
						left = field.add(left, field.multiply(cubeM.get(i, l, m), matrixL.get(m, j)));
						right = field.add(right, field.multiply(cubeM.get(i, j, m), matrixL.get(m, l)));
					}
					
					if (!left.equals(right))
						return false;
				}
			}
		}
		
		return true;
	}

	public boolean validateEquationML2() {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				for (int k = 0; k < size; k++) {
					F left = field.getAdditiveIdentity();
					F right = field.getAdditiveIdentity();
					
					for (int m = 0; m < size; m++) {
						left = field.add(left, field.multiply(cubeM.get(m, j, k), matrixL.get(i, m)));
						right = field.add(right, field.multiply(cubeM.get(i, j, m), matrixL.get(m, k)));
					}
					
					if (!left.equals(right))
						return false;
				}
			}
		}
		
		return true;
	}

	public boolean validateEquationLMK() {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				F left = field.getAdditiveIdentity();
				F right = matrixL.get(i, j);
	
				for (int m = 0; m < size; m++) {
					left = field.add(left, field.multiply(matrixL.get(i, m), matrixL.get(m, j)));
					right = field.add(right, field.multiply(cubeM.get(i, j, m), vectorK.get(m)));
				}
					
				if (!left.equals(right))
					return false;
			}
		}
		
		return true;
	}
	
	public boolean validateEquationLME() {
		for (int i = 0; i < size; i++) {
			F left = field.getMultiplicativeIdentity();
			F right = matrixL.get(i, i);

			for (int k = 0; k < size; k++) {
				right = field.add(right, field.multiply(cubeM.get(i, i, k), vectorE.get(k)));
			}
				
			if (!left.equals(right))
				return false;
		}
		
		return true;
	}

	public boolean validateEquationKLE() {
		for (int i = 0; i < size; i++) {
			F left = vectorK.get(i);

			for (int k = 0; k < size; k++) {
				left = field.add(left, field.multiply(matrixL.get(i, k), vectorE.get(k)));
			}
				
			if (!left.equals(field.getAdditiveIdentity()))
				return false;
		}
		
		return true;
	}

	public boolean validateMonoid() {
		if (!validateSymmetryM())
			return false;

		if (!validateEquationM())
			return false;

		if (!validateEquationML1())
			return false;

		if (!validateEquationML2())
			return false;
		
		if (!validateEquationLMK())
			return false;
		
		if (!validateEquationLME())
			return false;

		if (!validateEquationKLE())
			return false;

		return true;
	}

}
