package aalg.algebra;


/**
 * Solver of equation system that calculates vector E.
 * @author Ing. Petr Ležák
 *
 * @param <F> type of field item
 */
public class EquationsVectorE<F extends IElement> {

	private final IField<F> field;   // Underlying field
	private final int size;   // Number of parts of multimonoid
	private final Matrix<F> equationMatrix;   // Left side matrix of equation system
	private final VectorImpl<F> equationRightSide;   // Right side of equation system
	private VectorImpl<F> parameterValues;   // Selected parameter values
	private EquationSystemSolution<F> solution;   // General (parametric) solution of equation system
	
	/**
	 * Creates solver.
	 * @param field underlying field
	 * @param size number of parts of multimonoid
	 */
	public EquationsVectorE(final IField<F> field, final int size) {
		this.field = field;
		this.size = size;
		
		equationMatrix = new Matrix<F>(field, size, size, Density.SPARSE);
		equationRightSide = new DenseVector<F>(field, size);
	}

	
	/**
	 * Creates equation system for given matrix L and cube M.
	 * @param matrixL matrix L
	 * @param cubeM cube M
	 */
	public void createEquations(final Matrix<F> matrixL, final Cube<F> cubeM) {
		final F zero = field.getAdditiveIdentity();
		
		equationMatrix.fill(zero);
		equationRightSide.fill(zero);
		
		for (int i = 0; i < size; i++) {
			for (int k = 0; k < size; k++) {
				equationMatrix.set(i, k, cubeM.get (i, i, k));
			}
			
			equationRightSide.set(i, field.subtract(field.getMultiplicativeIdentity(), matrixL.get(i, i)));
		}
	}
	
	/**
	 * Calculates general solution of equation system.
	 * @return true if equations have solution, false if not
	 */
	public boolean solveEquations() {
		solution = equationMatrix.solveEquationSystem(equationRightSide);
		
		if (solution != null) {
			parameterValues = new DenseVector<F> (field, solution.getParameterCount());
			return true;
		}
		else
			return false;
	}

	/**
	 * Returns number of equation solution parameters.
	 * @return number of equation solution parameters
	 */
	public int getParameterCount() {
		return solution.getParameterCount();
	}

	/**
	 * Sets value of given parameter.
	 * @param index index of parameter
	 * @param value value of parameter
	 */
	public void setParameterValue(final int index, final F value) {
		parameterValues.set(index, value);
	}

	/**
	 * Transforms solution of equation system to vector E.
	 * @param cubeM solved vector E
	 */
	public void solutionToVectorE(final IVector<F> vectorE) {
		vectorE.assign (solution.getSolution(parameterValues));
	}

	/**
	 * Returns vector of parameters. This vector may be read to get parameter values
	 * or filled to set parameter values.
	 * @return vector of parameters
	 */
	public VectorImpl<F> getParameters() {
		return parameterValues;
	}

}
