package lnsc;
import java.util.*;

/** <P> Abstract class containing the basic implementation for simple
 *  univariate single-real-valued <code>FunctionalUnit</code>. These functions
 *  will have the form <code>f'(x) = factor*f(x) + offset</code> where
 *  <code>f(x)</code> needs to be implemented. </P>
 *
 * <P> This class encapsulates all the methods and slots required to create a
 * functional unit of 1 input and 1 output from the class
 * <code>AbstractFunctionUnit</code>.  It is the fastest way to create such
 * units. Only 3 simple things are needed by subclasses: </P>
 *  <ol>
 *      <li>In the constructor, the field <code>m_IsDifferentiable</code> must
 *          be filled appropriately. (<code>m_IsStateless</code> is assumed
 *          <code>true</code>)</li>
 *      <li>Only the simple methods <code>function(double)</code>, and
 *          <code>functionDerivative(double)</code> if the function is
 *          differentiable, need to be implemented.
 *      <li>Since <code>FunctionalUnit</code> are <code>Serializable</code> and
 *          <code>Cloneable</code>, any required extra code to make these
 *          interfaces work properly should be added. It is necessary to at
 *          least set the <code>private static serialVersionUID</code> variable
 *          appropriately for the <code>Seriablizable</code> interface. For
 *          complex object, the <code>Cloneable</code> interface can rely on
 *          <code>Tools.copyObject(Serializable)</code>. </li>
 *  </ol>
 *
 * <P>Moreover, every function derived from <code>AbstractSimpleUnit</code> can
 * also be re-scaled and offset to match any specific output range. The output
 * computed by the original <code>function(double)</code> can be multiplied by
 * <code>factor</code> and then translated by adding <code>offset</code>. The
 * <code>functionDerivative(double)</code> is adjusted accordingly. By default
 * <code>factor = 1.0</code> and <code>offset = 0.0</code>. </P>
 *
 *  @author Francois Rivest
 *  @version 1.0
 *  @since 1.0
 */
public abstract class AbstractSimpleUnit extends AbstractFunctionalUnit
{
	/*********************************************************************/
    //Serial Version UID

	/** Serial version UID. */
	static final long serialVersionUID = 5215067520421445273L;

	/*********************************************************************/
	//Private fields

	/** Factor property data. */
	private double m_Factor = 1.0;

	/** Offset property data. */
	private double m_Offset = 0.0;

	/*********************************************************************/
	//Constructors

	/** Creates a simple unit with Factor=1.0 and Offset=0.0. */
	public AbstractSimpleUnit()
	{
		m_InputCount = 1;
		m_OutputCount = 1;
		m_IsStateless = true;
	}

	/** Creates a simple unit with given factor and offset.
	 *  @param      newFactor           Function factor.
	 *  @param      newOffset           Function offset.
	*/
	public AbstractSimpleUnit(double newFactor, double newOffset)
	{
		this();
		setFactor(newFactor);
		setOffset(newOffset);
	}

	/*********************************************************************/
	//

	/** Univariate real-valued function to implement.
	 *  @param      x                   The input.
	 *  @return     The output (that will be scaled and offset).
	 */
	protected abstract double function(double x);

	/** Derivative of the univariate real-valued function implemented.
	 *  It has to be implemented only if the function is differentiable,
	 *  otherwise an empty method returning 0.0 should be provided.
	 *  @param      x                   The input.
	 *  @return     The derivative (that will be scaled).
	 */
	protected abstract double functionDerivative(double x);

	/** Second derivative of the univariate real-valued function implemented.
	 *  It has to be implemented only if the function is twice differentiable,
	 *  otherwise an empty method returning 0.0 should be provided.
	 *  @param      x                   The input.
	 *  @return     The second derivative (that will be scaled).
	 */
	protected abstract double functionSecondDerivative(double x);

	/*********************************************************************/
	//Properties

	/** Returns the value added to the scaled output.
	 *  @return     Value added to the scaled output.
	 */
	public final double getOffset() {return m_Offset;}

	/** Sets the value added to the scaled output.
	 *  @param      newOffset               Value added to the scaled output.
	 */
	public final void setOffset(double newOffset) {m_Offset = newOffset;}

	/** Returns the scaling factor applied to the internal output.
	 *  @return     Factor applied to the internal output.
	 */
	public final double getFactor() {return m_Factor;}

	/** Sets the factor applied to the internal output.
	 *  @param      newFactor               Factor applied to the internal
	 *                                      output.
	 */
	public final void setFactor(double newFactor) {m_Factor = newFactor;}

	/*********************************************************************/
	//FunctionalUnit implementation

	public final FunctionalUnit.ProcessPatternResult processPattern(double[] inputPattern, boolean computeDerivative, boolean computeSecondDerivative)
	{
		//param checking
		FunctionalUnit.ProcessPatternResult ret = preProcessPattern(inputPattern, computeDerivative, computeSecondDerivative);
		//compute output pattern
		ret.outputPattern[0] = m_Factor * function(inputPattern[0]) + m_Offset;
		//compute derivative
		if (computeDerivative)
		{
			ret.derivative[0][0] = m_Factor * functionDerivative(inputPattern[0]);
		}
		//compute derivative
		if (computeSecondDerivative)
		{
			ret.secondDerivative[0][0][0] = m_Factor * functionSecondDerivative(inputPattern[0]);
		}
		//return
		return ret;
	}

	/*********************************************************************/
	//toString method

	public String toString()
	 {
		String ret = super.toString() + "\n";
		ret += "Abstract Class: AbstractSimpleUnit\n";
		ret += "\tOffset: " + Double.toString(m_Offset) + "\n";
		ret += "\tFactor: " + Double.toString(m_Factor);
		return ret;
	 }

}