//==============================================================================
//	
//	Copyright (c) 2013-
//	Authors:
//	* Ernst Moritz Hahn <emhahn@cs.ox.ac.uk> (University of Oxford)
//	
//------------------------------------------------------------------------------
//	
//	This file is part of PRISM.
//	
//	PRISM is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation; either version 2 of the License, or
//	(at your option) any later version.
//	
//	PRISM 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 General Public License for more details.
//	
//	You should have received a copy of the GNU General Public License
//	along with PRISM; if not, write to the Free Software Foundation,
//	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//	
//==============================================================================

package param;

import java.util.HashMap;

/**
 * Generates new functions, stores valid ranges of parameters, etc.
 * 
 * @see Function
 * @author Ernst Moritz Hahn <emhahn@cs.ox.ac.uk> (University of Oxford)
 */
abstract class FunctionFactory {
	/** names of parameters */
	protected String[] parameterNames;
	/** lower bounds of parameters */
	protected BigRational[] lowerBounds;
	/** upper bounds of parameters */
	protected BigRational[] upperBounds;
	/** maps variable name to index in {@code parameterNames},
	 * {@code lowerBounds} and {@code upperBounds}
	 */
	protected HashMap<String, Integer> varnameToInt;
	
	/**
	 * Creates a new function factory.
	 * {@code parameterNames}, {@code lowerBounds}, {@code upperBounds} all
	 * must have the same length. Each index into these arrays represents
	 * respectively the name, lower and upper bound of a given variable.
	 * After this function factory has been created, the number of variables,
	 * their names and bounds are fixed and cannot be changed anymore.
	 * 
	 * @param parameterNames names of parameters
	 * @param lowerBounds lower bounds of parameters
	 * @param upperBounds upper bounds of parameters
	 */
	FunctionFactory(String[] parameterNames, BigRational[] lowerBounds, BigRational[] upperBounds) {
		this.parameterNames = parameterNames;
		this.lowerBounds = lowerBounds;
		this.upperBounds = upperBounds;
		this.varnameToInt = new HashMap<String, Integer>();
		for (int var = 0; var < parameterNames.length; var++) {
			varnameToInt.put(parameterNames[var], var);
		}
	}

	/**
	 * Returns a function representing the number one.
	 * @return function representing the number one
	 */
	abstract Function getOne();
	
	/**
	 * Returns a function representing the number zero.
	 * @return function representing the number zero
	 */
	abstract Function getZero();

	/**
	 * Returns a function representing not-a-number.
	 * @return function representing not-a-number
	 */
	abstract Function getNaN();
	
	/**
	 * Returns a function representing positive infinity.
	 * @return function representing the positive infinity
	 */
	abstract Function getInf();
	
	/**
	 * Returns a function representing negative infinity.
	 * @return function representing the negative infinity
	 */
	abstract Function getMInf();
	
	/**
	 * Returns a new function which represents the same value as the
	 * {@code BigRational} {@code bigRat}.
	 * 
	 * @param bigRat value to create a function of
	 * @return function representing the same value as {@code bigRat}
	 */
	abstract Function fromBigRational(BigRational bigRat);
	
	/**
	 * Returns a function representing a single variable. 
	 * 
	 * @param var the variable to create a function of
	 * @return function consisting only in one variable
	 */
	abstract Function getVar(int var);


	/**
	 * Returns a function representing a single variable. 
	 * 
	 * @param var name of the variable to create a function of
	 * @return function consisting only in one variable
	 */
	Function getVar(String var) {
		return getVar(varnameToInt.get(var));
	}
	
	/**
	 * Returns name of variable with the given index.
	 * 
	 * @param var index of the variable to obtain name of
	 * @return name of {@code var}
	 */
	String getParameterName(int var) {
		return parameterNames[var];
	}

	/**
	 * Returns lower bound of variable with the given index.
	 * 
	 * @param var index of the variable to obtain lower bound of
	 * @return lower bound of {@code var}
	 */
	BigRational getLowerBound(int var) {
		return lowerBounds[var];
	}
	
	/**
	 * Returns upper bound of variable with the given index.
	 * 
	 * @param var index of the variable to obtain upper bound of
	 * @return upper bound of {@code var}
	 */
	BigRational getUpperBound(int var) {
		return upperBounds[var];
	}
	
	/**
	 * Returns number of variables used in this function factory.
	 * @return
	 */
	public int getNumVariables() {
		return parameterNames.length;
	}
	
	/**
	 * Returns a function representing the value of the given number.
	 * 
	 * @param from number to create function of
	 * @return function representing the number {@code from}
	 */
	Function fromLong(long from) {
		return fromBigRational(new BigRational(from));
	}
}
