/* Copyright 2009-2014 David Hadka
 *
 * This file is part of the MOEA Framework.
 *
 * The MOEA Framework 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.
 *
 * The MOEA Framework 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 the MOEA Framework.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.moeaframework.problem.WFG;

import java.util.Arrays;

/* This code is based on the Walking Fish Group implementation.
 * 
 * Copyright 2005 The Walking Fish Group (WFG).
 *
 * This material is provided "as is", with no warranty expressed or implied.
 * Any use is at your own risk. Permission to use or copy this software for
 * any purpose is hereby granted without fee, provided this notice is
 * retained on all copies. Permission to modify the code and to distribute
 * modified code is granted, provided a notice that the code was modified is
 * included with the above copyright notice.
 *
 * http://www.wfg.csse.uwa.edu.au/
 */
class Shapes {

	/**
	 * Private constructor to prevent instantiation.
	 */
	private Shapes() {
		super();
	}

	/**
	 * Construct a vector of length M-1, with values "1,0,0,..." if "degenerate"
	 * is true, otherwise with values "1,1,1,..." if "degenerate" is false.
	 */
	private static int[] WFG_create_A(int M, boolean degenerate) {
		assert M >= 2;

		if (degenerate) {
			int[] A = new int[M - 1];
			A[0] = 1;
			return A;
		} else {
			int[] A = new int[M - 1];
			Arrays.fill(A, 1);
			return A;
		}
	}

	/**
	 * Given the vector "x" (the last value of which is the sole distance
	 * parameter), and the shape function results in "h", calculate the scaled
	 * fitness values for a WFG problem.
	 */
	private static double[] WFG_calculate_f(double[] x, double[] h) {
		assert Misc.vector_in_01(x);
		assert Misc.vector_in_01(h);
		assert x.length == h.length;

		int M = h.length;

		double[] S = new double[M];

		for (int m = 1; m <= M; m++) {
			S[m - 1] = m * 2.0;
		}

		return FrameworkFunctions.calculate_f(1.0, x, h, S);
	}

	public static double[] WFG1_shape(double[] t_p) {
		assert Misc.vector_in_01(t_p);
		assert t_p.length >= 2;

		int M = t_p.length;

		int[] A = WFG_create_A(M, false);
		double[] x = FrameworkFunctions.calculate_x(t_p, A);

		double[] h = new double[M];

		for (int m = 1; m <= M - 1; m++) {
			h[m - 1] = ShapeFunctions.convex(x, m);
		}
		h[M - 1] = ShapeFunctions.mixed(x, 5, 1.0);

		return WFG_calculate_f(x, h);
	}

	public static double[] WFG2_shape(double[] t_p) {
		assert Misc.vector_in_01(t_p);
		assert t_p.length >= 2;

		int M = t_p.length;

		int[] A = WFG_create_A(M, false);
		double[] x = FrameworkFunctions.calculate_x(t_p, A);

		double[] h = new double[M];

		for (int m = 1; m <= M - 1; m++) {
			h[m - 1] = ShapeFunctions.convex(x, m);
		}
		h[M - 1] = ShapeFunctions.disc(x, 5, 1.0, 1.0);

		return WFG_calculate_f(x, h);
	}

	public static double[] WFG3_shape(double[] t_p) {
		assert Misc.vector_in_01(t_p);
		assert t_p.length >= 2;

		int M = t_p.length;

		int[] A = WFG_create_A(M, true);
		double[] x = FrameworkFunctions.calculate_x(t_p, A);

		double[] h = new double[M];

		for (int m = 1; m <= M; m++) {
			h[m - 1] = ShapeFunctions.linear(x, m);
		}

		return WFG_calculate_f(x, h);
	}

	public static double[] WFG4_shape(double[] t_p) {
		assert Misc.vector_in_01(t_p);
		assert t_p.length >= 2;

		int M = t_p.length;

		int[] A = WFG_create_A(M, false);
		double[] x = FrameworkFunctions.calculate_x(t_p, A);

		double[] h = new double[M];

		for (int m = 1; m <= M; m++) {
			h[m - 1] = ShapeFunctions.concave(x, m);
		}

		return WFG_calculate_f(x, h);
	}

	public static double[] I1_shape(double[] t_p) {
		assert Misc.vector_in_01(t_p);
		assert t_p.length >= 2;

		int M = t_p.length;

		int[] A = WFG_create_A(M, false);
		double[] x = FrameworkFunctions.calculate_x(t_p, A);

		double[] h = new double[M];

		for (int m = 1; m <= M; m++) {
			h[m - 1] = ShapeFunctions.concave(x, m);
		}

		double[] S = new double[M];
		Arrays.fill(S, 1.0);

		return FrameworkFunctions.calculate_f(1.0, x, h, S);
	}

}
