//
// C++ Interface: itpp_ext
//
// Description:
//
//
// Author: smidl <smidl@utia.cas.cz>, (C) 2008
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef FN_H
#define FN_H

#include "../bdmerror.h"
#include "../base/bdmbase.h"

namespace bdm {

//! class representing function \f$f(x) = a\f$, here \c rv is empty
class constfn : public fnc {
	//! value of the function
	vec val;

public:
	//vec eval() {return val;};
	//! inherited
	vec eval ( const vec &cond ) {
		return val;
	};
	//!Default constructor
	constfn ( const vec &val0 ) : fnc(), val ( val0 ) {
		dimy = val.length();
		dimc = 0;
	};
};

//! Class representing function \f$f(x) = Ax+B\f$
class linfn: public fnc {
	//! Identification of \f$x\f$
	RV rv;
	//! Matrix A
	mat A;
	//! vector B
	vec B;
public :
	vec eval ( const vec &cond ) {
		bdm_assert_debug ( cond.length() == A.cols(), "linfn::eval Wrong cond." );
		return A * cond + B;
	}

//		linfn evalsome ( ivec &rvind );
	//!default constructor
	linfn ( ) : fnc(), A ( ), B () { };
	//! Set values of \c A and \c B
	void set_parameters ( const mat &A0 , const vec &B0 ) {
		A = A0;
		B = B0;
	};
	void from_setting(const Setting &set){
		UI::get(A,set,"A",UI::compulsory);
		UI::get(B,set,"B",UI::compulsory);
	}
	void validate(){
		dimy = A.rows();
		dimc = A.cols();
	}
	
};
UIREGISTER(linfn);


/*!
\brief Class representing a differentiable function of two variables \f$f(x,u)\f$.

Function of two variables.

TODO:
1) Technically, it could have a common parent (e.g. \c fnc ) with other functions. For now, we keep it as it is.
2) It could be generalized into multivariate form, (which was original meaning of \c fnc ).
*/
class diffbifn: public fnc {
protected:
	//! Indentifier of the first rv.
	RV rvx;
	//! Indentifier of the second rv.
	RV rvu;
	//! cache for rvx.count()
	int dimx;
	//! cache for rvu.count()
	int dimu;
public:
	//! Evaluates \f$f(x0,u0)\f$ (VS: Do we really need common eval? )
	vec eval ( const vec &cond ) {
		bdm_assert_debug ( cond.length() == ( dimx + dimu ), "linfn::eval Wrong cond." );
		if ( dimu > 0 ) {
			return eval ( cond ( 0, dimx - 1 ), cond ( dimx, dimx + dimu - 1 ) );//-1 = end (in matlab)
		} else {
			return eval ( cond ( 0, dimx - 1 ), vec ( 0 ) );//-1 = end (in matlab)
		}

	}

	//! Evaluates \f$f(x0,u0)\f$
	virtual vec eval ( const vec &x0, const vec &u0 ) {
		return zeros ( dimy );
	};
	//! Evaluates \f$A=\frac{d}{dx}f(x,u)|_{x0,u0}\f$ and writes result into \c A . @param full denotes that even unchanged entries are to be rewritten. When, false only the changed elements are computed. @param x0 numeric value of \f$x\f$, @param u0 numeric value of \f$u\f$ @param A a place where the result will be stored.
	virtual void dfdx_cond ( const vec &x0, const vec &u0, mat &A , bool full = true ) {};
	//! Evaluates \f$A=\frac{d}{du}f(x,u)|_{x0,u0}\f$ and writes result into \c A . @param full denotes that even unchanged entries are to be rewritten. When, false only the changed elements are computed.	@param x0 numeric value of \f$x\f$, @param u0 numeric value of \f$u\f$ @param A a place where the result will be stored.
	virtual void dfdu_cond ( const vec &x0, const vec &u0, mat &A, bool full = true ) {};
	//!Default constructor (dimy is not set!)
	diffbifn () : fnc() {};
	//! access function
	int _dimx() const {
		return dimx;
	}
	//! access function
	int _dimu() const {
		return dimu;
	}
};

//! Class representing function \f$f(x,u) = Ax+Bu\f$
//TODO can be generalized into multilinear form!
class bilinfn: public diffbifn {
	mat A;
	mat B;
public :
	//!\name Constructors
	//!@{

	bilinfn () : diffbifn (), A(), B() { }

	bilinfn ( const mat &A0, const mat &B0 ) {
		set_parameters ( A0, B0 );
	}

	//! Alternative initialization
	void set_parameters ( const mat &A0, const mat &B0 ) {
		bdm_assert ( A0.rows() == B0.rows(), "bilinfn matrices must have the same number of rows" );
		A = A0;
		B = B0;
		dimy = A.rows();
		dimx = A.cols();
		dimu = B.cols();
	}
	//!@}

	//!\name Mathematical operations
	//!@{
	inline vec eval ( const  vec &x0, const vec &u0 ) {
		bdm_assert_debug ( x0.length() == dimx, "bilinfn::eval Wrong xcond." );
		bdm_assert_debug ( u0.length() == dimu, "bilinfn::eval Wrong ucond." );
		return A*x0 + B*u0;
	}

	void dfdx_cond ( const vec &x0, const vec &u0, mat &F, bool full ) {
		bdm_assert_debug ( ( F.cols() == A.cols() ) && ( F.rows() == A.rows() ), "Allocated F is not compatible." );
		if ( full ) F = A; 	//else : nothing has changed no need to regenerate
	}

	void dfdu_cond ( const vec &x0, const vec &u0, mat &F,  bool full = true ) {
		bdm_assert_debug ( ( F.cols() == B.cols() ) && ( F.rows() == B.rows() ), "Allocated F is not compatible." );
		if ( full ) F = B;	//else : nothing has changed no need to regenerate
	}
	//!@}
};

} //namespace
#endif // FN_H
