//  LZ09.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package jmetal.problems.LZ09;

import java.util.Vector;

/**
 * Base class to implement the problems of the LZ09 benchmark, which is
 * defined in:
 * H. Li and Q. Zhang. Multiobjective optimization problems with complicated 
 * pareto sets, MOEA/D and NSGA-II. IEEE Transactions on Evolutionary 
 * Computation, 12(2):284-302, April 2009.
 */
public class LZ09 {
	
	public LZ09 (int nvar, int nobj, int ptype, int dtype, int ltype) {
		this.nvar = nvar ;
		this.nobj = nobj ;
		this.ltype = ltype ;
		this.dtype = dtype ;
	  this.ptype = ptype ;
	}

	int nvar  ;
	int nobj  ;
	int ltype ;
  int dtype ;
  int ptype ;
  
	// control the PF shape
	void alphaFunction(double alpha[], Vector<Double> x, int dim, int type) {
		if (dim == 2) {
			if (type == 21) {
				alpha[0] = x.elementAt(0);
				alpha[1] = 1 - Math.sqrt(x.elementAt(0));
			}

			if (type == 22) {
				alpha[0] = x.elementAt(0);
				alpha[1] = 1 - x.elementAt(0) * x.elementAt(0);
			}

			if (type == 23) {
				alpha[0] = x.elementAt(0);
				alpha[1] = 1 - Math.sqrt(alpha[0]) - alpha[0]
				           * Math.sin(10 * alpha[0] * alpha[0] * Math.PI);
			}

			if (type == 24) {
				alpha[0] = x.elementAt(0);
				alpha[1] = 1 - x.elementAt(0) - 0.05 * Math.sin(4 * Math.PI * x.elementAt(0));
			}
		} else {
			if (type == 31) {
				alpha[0] = Math.cos(x.elementAt(0) * Math.PI / 2) * Math.cos(x.elementAt(1) * Math.PI / 2);
				alpha[1] = Math.cos(x.elementAt(0) * Math.PI / 2) * Math.sin(x.elementAt(1) * Math.PI / 2);
				alpha[2] = Math.sin(x.elementAt(0) * Math.PI / 2);
			}

			if (type == 32) {
				alpha[0] = 1 - Math.cos(x.elementAt(0) * Math.PI / 2)
				* Math.cos(x.elementAt(1) * Math.PI / 2);
				alpha[1] = 1 - Math.cos(x.elementAt(0) * Math.PI / 2)
				* Math.sin(x.elementAt(1) * Math.PI / 2);
				alpha[2] = 1 - Math.sin(x.elementAt(0) * Math.PI / 2);
			}

			if (type == 33) {
				alpha[0] = x.elementAt(0);
				alpha[1] = x.elementAt(1);
				alpha[2] = 3
				- (Math.sin(3 * Math.PI * x.elementAt(0)) + Math.sin(3 * Math.PI * x.elementAt(1))) - 2
				* (x.elementAt(0) + x.elementAt(1));
			}

			if (type == 34) {
				alpha[0] = x.elementAt(0) * x.elementAt(1);
				alpha[1] = x.elementAt(0) * (1 - x.elementAt(1));
				alpha[2] = (1 - x.elementAt(0));
			}
		}
	} // alphaFunction

	// control the distance
	double betaFunction(Vector<Double> x, int type) {
		double beta;
		beta = 0;
		int dim = x.size();

		if (dim == 0)
			beta = 0;

		if (type == 1) {
			beta = 0;
			for (int i = 0; i < dim; i++) {
				beta += x.elementAt(i) * x.elementAt(i);
			}
			beta = 2.0 * beta / dim;
		}

		if (type == 2) {
			beta = 0;
			for (int i = 0; i < dim; i++) {
				beta += Math.sqrt(i + 1) * x.elementAt(i) * x.elementAt(i);
			}
			beta = 2.0 * beta / dim;
		}

		if (type == 3) {
			double sum = 0, xx;
			for (int i = 0; i < dim; i++) {
				xx = 2 * x.elementAt(i);
				sum += (xx * xx - Math.cos(4 * Math.PI * xx) + 1);
			}
			beta = 2.0 * sum / dim;
		}

		if (type == 4) {
			double sum = 0, prod = 1, xx;
			for (int i = 0; i < dim; i++) {
				xx = 2 * x.elementAt(i);
				sum += xx * xx;
				prod *= Math.cos(10 * Math.PI * xx / Math.sqrt(i + 1));
			}
			beta = 2.0 * (sum - 2 * prod + 2) / dim;
		}

		return beta;
	} // betaFunction


//	control the PS shape of 2-d instances
	double psfunc2(double x, double t1, int dim, int type, int css) {
		// type:  the type of curve 
		// css:   the class of index
		double beta;
		beta = 0.0;

		dim++;

		if(type==21){
			double xy   = 2*(x - 0.5);
			beta = xy - Math.pow(t1, 0.5*(nvar + 3*dim - 8)/(nvar - 2));
		}	

		if(type==22){
			double theta = 6*Math.PI*t1 + dim*Math.PI/nvar;  
			double xy    = 2*(x - 0.5);
			beta = xy - Math.sin(theta);
		}

		if(type==23){
			double theta = 6*Math.PI*t1 + dim*Math.PI/nvar;
			double ra    = 0.8*t1;
			double xy    = 2*(x - 0.5);
			if(css==1)
				beta = xy - ra*Math.cos(theta);
			else{
				beta = xy - ra*Math.sin(theta);
			}
		}

		if(type==24){
			double theta = 6*Math.PI*t1 + dim*Math.PI/nvar;
			double xy    = 2*(x - 0.5);
			double ra    = 0.8*t1;
			if(css==1)
				beta = xy - ra*Math.cos(theta/3);
			else{
				beta = xy - ra*Math.sin(theta);
			}
		}

		if(type==25){
			double rho   = 0.8;
			double phi   = Math.PI*t1;
			double theta = 6*Math.PI*t1 + dim*Math.PI/nvar;
			double xy    = 2*(x - 0.5);
			if(css==1)
				beta = xy - rho*Math.sin(phi)*Math.sin(theta);
			else if(css==2)
				beta = xy - rho*Math.sin(phi)*Math.cos(theta);
			else
				beta = xy - rho*Math.cos(phi);			
		}

		if(type==26){
			double theta = 6*Math.PI*t1 + dim*Math.PI/nvar;
			double ra    = 0.3*t1*(t1*Math.cos(4*theta) + 2);
			double xy    = 2*(x - 0.5);
			if(css==1)
				beta = xy - ra*Math.cos(theta);
			else{
				beta = xy - ra*Math.sin(theta);
			}
		}

		return beta;
	}


//	control the PS shapes of 3-D instances
	double psfunc3(double x, double t1, double t2, int dim, int type){
		// type:  the type of curve 
		// css:   the class of index
		double beta;
		beta = 0.0 ;

		dim++;

		if(type==31){
			double xy  = 4*(x - 0.5);
			double rate = 1.0*dim/nvar;
			beta = xy - 4*(t1*t1*rate + t2*(1.0-rate)) + 2;
		}

		if(type==32){
			double theta = 2*Math.PI*t1 + dim*Math.PI/nvar;
			double xy    = 4*(x - 0.5);
			beta = xy - 2*t2*Math.sin(theta);	
		}

		return beta;
	}
	

	void objective(Vector<Double> x_var, Vector <Double> y_obj)
	{
		// 2-objective case
		if(nobj==2)
		{
			if(ltype==21||ltype==22||ltype==23||ltype==24||ltype==26)
			{
				double g = 0, h = 0, a, b;
				Vector <Double> aa = new Vector();
				Vector <Double> bb = new Vector();
				for(int n=1;n<nvar;n++)
				{

					if(n%2==0){
						a = psfunc2(x_var.elementAt(n),x_var.elementAt(0),n,ltype,1);  // linkage
						aa.addElement(a);
					}
					else
					{
						b = psfunc2(x_var.elementAt(n),x_var.elementAt(0),n,ltype,2);
						bb.addElement(b);
					}	

				}
				
				g = betaFunction(aa, dtype);
				h = betaFunction(bb, dtype);

				double alpha [] = new double[2] ;
				alphaFunction(alpha,x_var,2,ptype);  // shape function
				y_obj.set(0, alpha[0] + h);
				y_obj.set(1, alpha[1] + g); 
				aa.clear(); 
				bb.clear();
			}
			
			if(ltype==25)
			{
				double g = 0, h = 0, a, b;
				double e = 0, c;
				Vector <Double> aa = new Vector() ;
				Vector <Double> bb = new Vector() ;
				for(int n=1;n<nvar;n++){
					if(n%3==0){
						a = psfunc2(x_var.elementAt(n),x_var.elementAt(0),n,ltype,1); 
						aa.addElement(a);
					}
					else if(n%3==1)
					{
						b = psfunc2(x_var.elementAt(n),x_var.elementAt(0),n,ltype,2);
						bb.addElement(b);
					}	
					else{
						c = psfunc2(x_var.elementAt(n),x_var.elementAt(0),n,ltype,3);
						if(n%2==0)    aa.addElement(c);			
						else          bb.addElement(c);
					}
				}		
				g = betaFunction(aa,dtype);          // distance function
				h = betaFunction(bb,dtype);
				double alpha[] = new double[2];
				alphaFunction(alpha,x_var,2,ptype);  // shape function
				y_obj.set(0, alpha[0] + h);
				y_obj.set(1, alpha[1] + g); 
				aa.clear(); 
				bb.clear();
			}
		}
		

		// 3-objective case
		if(nobj==3)
		{
			if(ltype==31||ltype==32)
			{
				double g = 0, h = 0, e = 0, a;
				Vector <Double> aa  = new Vector() ;
				Vector <Double> bb = new Vector();
				Vector <Double> cc = new Vector();
				for(int n=2;n<nvar;n++)
				{
					a = psfunc3(x_var.elementAt(n),x_var.elementAt(0),x_var.elementAt(1),n,ltype);
					if(n%3==0)	    aa.addElement(a);
					else if(n%3==1)	bb.addElement(a);
					else            cc.addElement(a);
				}

				g = betaFunction(aa,dtype);
				h = betaFunction(bb,dtype);
				e = betaFunction(cc,dtype);

				double alpha[] = new double[3];
				alphaFunction(alpha,x_var,3,ptype);  // shape function
				y_obj.set(0, alpha[0] + h);
				y_obj.set(1 ,alpha[1] + g); 
				y_obj.set(2, alpha[2] + e); 
				aa.clear(); 
				bb.clear();
				cc.clear();
			}
		}
	}
}
