package solver;

import java.util.ArrayList;
import java.util.List;

import org.ojalgo.optimisation.Expression;
import org.ojalgo.optimisation.ExpressionsBasedModel;
import org.ojalgo.optimisation.Variable;

import svm.DualConstraint;
import svm.DualQPSolver;
import svm.Mode;
import svm.SVMStructKernel;
import base.Example;
import base.Structure;

public class OJAlgoSolver<X extends Structure, Y extends Structure> extends DualQPSolver<X, Y>
{
	private static final double TOL = 1E-6;

	public OJAlgoSolver(Mode m)
	{
		super(m);
	}

	@Override
	public double solve(List<DualConstraint<X, Y>> constraints, List<Example<X, Y>> data, ArrayList<Double> alphas, double C, SVMStructKernel<X, Y> svm, int iter)
	{
		Variable[] varAlpha = new Variable[constraints.size()];
		for (int i = 0; i < varAlpha.length; i++)
		{
			varAlpha[i] = Variable.make("alpha" + i).lower(0);
			varAlpha[i].setValue(i < alphas.size() ? alphas.get(i) : 0);
		}
		Variable varBeta = Variable.make("beta").lower(0);

		ExpressionsBasedModel model = new ExpressionsBasedModel(varAlpha);
		model.addVariable(varBeta);

		Expression constraint = model.addExpression("constr");
		for (int i = 0; i < varAlpha.length; i++)
			constraint.setLinearFactor(varAlpha[i], 1);
		constraint.setLinearFactor(varBeta, 1);
		constraint.level(C);

		Expression objective = model.addExpression("obj");

		for (int i = 0; i < constraints.size(); i++)
		{
			objective.setLinearFactor(varAlpha[i], constraints.get(i).loss);
			for (int j = 0; j < constraints.size(); j++)
			{
				objective.setQuadraticFactor(varAlpha[i], varAlpha[j], -0.5 * H.get(i).get(j));
			}
		}
		objective.weight(1);

		model.maximise();

		alphas.clear();
		for (int i = 0; i < varAlpha.length; i++)
		{
			alphas.add(varAlpha[i].getValue().doubleValue());
			if (alphas.get(i) > 0.0000001)
				constraints.get(i).lastUsed = iter;
			// System.out.print(alphas.get(i)+",\t");
		}

		if (varBeta.getValue().doubleValue() > TOL)
			return 0;
		double sslack = 0;
		// double alphasum = 0;
		double maxAlpha = 0;
		for (int i = 0; i < constraints.size(); i++)
		{
			// alphasum+=alphas.get(i);
			if (alphas.get(i) > TOL)
			{
				double slack = constraints.get(i).loss;
				for (int j = 0; j < constraints.size(); j++)
				{
					slack -= alphas.get(j) * H.get(i).get(j);
				}
				if (alphas.get(i) > maxAlpha)
				{
					sslack = slack;
					maxAlpha = alphas.get(i);
				}
				// System.out.println("possible slack: " + slack);

			}
		}
		// System.out.println("How abut dem alphas and betas: " +alphasum+" \t "+beta.get(GRB.DoubleAttr.X));

		return sslack;
	}

}
