#include <alberta.h>
#if (defined(ALBERTA_VER) && (ALBERTA_VER==2))
//# include <alberta_inlines.h>
//#include <alberta_util_inlines.h>
# include "alberta1_wrapper.h"
#endif

 #include "newalberta.hh"
 /****************************************************************************/
 #include "do_on_el.hh"
 #include "do_elfunc_on_el.hh"
//#include "public_do_elfunc_on_el.hh"
#include "prototype_fem_elfunc.hh"
#include "fem_elfunc.hh"
 #include "fem_base.hh"
 #include "traverse.hh"
 #include "est.hh"
 #include "ellipt_est.hh"
 #include "parab_est.hh"
 #include "sys_est.hh"
#include "build_stat.hh"
#include "operator_parts.hh"
#include "build_diff.hh"
#include "build_sys.hh"
/****************************************************************************/
 /*                                                                          */
 /* error estimator for (nonlinear) heat equation:                           */
 /*              u_t - div A grad u + f(x,t,u,grad u) = 0                    */
 /*                                                                          */
 /* eta_h = C[0]*||h^2 ((U - Uold)/tau - div A grad U + f(x,t,U,grad U))||   */
 /*           + C[1]*|||h^1.5 [A grad U]|||                                  */
 /* eta_c = C[2]*||(Uold - Uold_coarse)/tau||                                */
 /* eta_t = C[3]*||U - Uold||                                                */
 /*                                                                          */
 /* heat_est() return value is the TIME DISCRETIZATION ESTIMATE, eta_t       */
 /*                                                                          */

 /*--------------------------------------------------------------------------*/
 /* element residual:  C2*h_S^2*|| U_t -div A nabla u^h + r ||_L^2(S)        */
 /*--------------------------------------------------------------------------*/
 /* time residual:  C4*|| U - Uold ||_L^2(S)                                 */
 /*--------------------------------------------------------------------------*/
 /* quadrature:     C5*h_S^2*||nabla(R)||_L^2(S)                             */
 /*--------------------------------------------------------------------------*/
 /* source:         C6*||f - In fn||_L^2(S)                                  */
 /*--------------------------------------------------------------------------*/


 /*--------------------------------------------------------------------------*/
 /* element residual:  C0*h_S^2*|| U_t -div A nabla u^h + r ||_L^2(S)        */
 /*--------------------------------------------------------------------------*/
 /* time residual:  C3*|| U - Uold ||_L^2(S)  neu aus estimator.c                               */
 /*--------------------------------------------------------------------------*/

 /*--------------------------------------------------------------------------*/

REAL sys_est::calc(/*const REAL* const lambda,*/ const int qp)
{int comp;
   REAL val=0.0;
	REAL_DD *const D2uhqp_begin 	=	D2uhqp;
	uh_el 	=   u_begin   ;
	uh_old_el=    u_old_begin ;
  	uh_qp 	=	uh_qp_begin;
  	uh_old_qp 	=	uh_old_qp_begin;
 	for (comp=0; comp < ncomps; comp++)
 	{
		prob_data_ptr = op_arg_infos[current_line*ncomps +comp]->prob_data_ptr;
 		quad_fast_uh=	quad_fasts[comp];
			//increment pointers
		if (op_arg_infos[current_line*ncomps + comp]->prob_data_ptr->massfunc
		  	||(op_arg_infos[current_line*ncomps + comp]->prob_data_ptr->flags & MASS_CONST))
 //Kriterium ok?	Zumindest gleich wie in set_mass_op_info
				uh_old_qp =	&uh_old_qp_begin[comp*quad_fast_uh->n_points];
		else					
			uh_old_qp = 	uh_qp;
    		val += ellipt_est::calc(/*lambda,*/ qp);

		D2uhqp	+=	quad_fast_uh->n_points;
		uh_qp 	+=	quad_fast_uh->n_points;
 	} 
 //Set to diagonal element. Stimmt nicht fuer versch. FE-spaces!! Baustelle
 	quad_fast_uh=	quad_fasts[current_line];
	uh_el 	=   u_begin   + current_line*sol_bas_fcts->n_bas_fcts ;
	uh_qp 	=	uh_qp_begin + current_line*quad_fast_uh->n_points ;
	uh_old_qp =	uh_old_qp_begin + current_line*quad_fast_uh->n_points ;
	D2uhqp= D2uhqp_begin + current_line*quad_fast_uh->n_points;
	if (common_source_contrib) 
//9.11		val -= common_source_contrib->fem_elfunc::calc(qp, current_line);
		//Baustelle: Evaluation at lambda. alles andere ist unsicher.
		val -= common_source_contrib->calc(	quad_fast_uh->quad->lambda[qp], current_line);
//		val -= common_source_contrib->public_do_elfunc_on_el::calc(qp, current_line);
    
   return(val);
}
int sys_est::set_at_el(const EL_INFO * const el_info, REAL eval_time)
{	
	int   col_comp, comp;
	REAL_DD *const D2uhqp_begin 	=	D2uhqp;
	uh_el 	=   u_begin   ;
	uh_old_el=    u_old_begin ;
  	uh_qp 	=	uh_qp_begin;
  	uh_old_qp 	=	uh_old_qp_begin;
  	D2uhqp	= 	D2uhqp_begin ;

//wie   build_sys::set_at_el(el_info, lambda_det_ptr);
	   fill_el_geom_cache(el_info, 0U); // scheinbar noetig??
   	   fill_el_geom_cache(el_info, FILL_EL_DET|FILL_EL_LAMBDA);
   	const_fe_space->bas_fcts->get_dof_indices(el_info->el
	               ,err_estimates[0]->fe_space->admin, dof);               //Baustelle.
	for (comp=0; comp< ncomps;  comp++)
	{
		current_line	= comp;		//weiss nich, ob das muss
		uh_ptr =	u_h[comp];
		quad_fast_uh	= quad_fasts[comp];
   		sol_bas_fcts->get_real_vec(el_info->el, u_h[comp],    uh_el);
       		sol_bas_fcts->get_real_vec(el_info->el, u_old[comp], uh_old_el);
	//		uh_at_qp(quad_fasts[col_comp], &(uh_el[sol_index]), 	&uh_qp[qp_index]); //so!!
	//		uh_at_qp(quad_fasts[col_comp], &(uh_old_el[sol_index]), &uh_old_qp[qp_index]);
	//     		D2_uh_at_qp(quad_fast, el_info->el_geom_cache.Lambda, uh_el, D2uhqp);

		prob_data_ptr = op_arg_infos[comp*ncomps + comp]->prob_data_ptr;
		est_el_res_contrib::set_at_el( el_info,	  eval_time);
		for (col_comp=0; col_comp< ncomps;  col_comp++)
			{if (comp != col_comp)		// off-diags, Teilklon von est_el_res_contrib::set_at_el

				{
				prob_data_ptr = op_arg_infos[comp*ncomps +col_comp]->prob_data_ptr;
			 	if(prob_data_ptr->order0func) 
	 				prob_data_ptr->order0func->set_at_el(el_info, eval_time);
  			 	if (prob_data_ptr->f_sources)
		  			prob_data_ptr->f_sources->set_at_el( el_info, eval_time);
				}
			}
		uh_el 		+=	sol_bas_fcts->n_bas_fcts; 
		uh_old_el 	+=	sol_bas_fcts->n_bas_fcts;
		uh_qp 		+=	quad_fasts[comp]->n_points;
		uh_old_qp 	+=	quad_fasts[comp]->n_points;
		D2uhqp		+=	quad_fasts[comp]->n_points;

	}
	uh_el 	=   u_begin   ;
	uh_old_el=    u_old_begin ;
  	uh_qp 	=	uh_qp_begin;
  	uh_old_qp 	=	uh_old_qp_begin;
  	D2uhqp	= 	D2uhqp_begin ;
	if (common_source_contrib) 
		common_source_contrib->set_at_el( el_info, eval_time);
//		common_source_contrib->public_do_elfunc_on_el::set_at_el( el_info, &op_arg_info->lambda_det);
  return(0);
}


int sys_est::calc(const EL_INFO *el_info)
{
	int row_comp=0;
	REAL_DD *const D2uhqp_begin 	=	D2uhqp;
//	lambda_det_ptr= &op_arg_info->lambda_det;
		uh_el 	=   u_begin   ;
		uh_old_el=    u_old_begin ;
		uh_qp 	=	uh_qp_begin;
		uh_old_qp =	uh_old_qp_begin;
  		D2uhqp	= 	D2uhqp_begin ;
	for (row_comp=0; row_comp< ncomps; row_comp++)
	{  
		current_line	= row_comp;
		uh_ptr =	u_h[row_comp];
		sol_bas_fcts =	u_h[row_comp]->fe_space->bas_fcts;
 		est_vec		= err_estimates[row_comp]->vec;
 		est_c_vec	= err_c_estimates[row_comp]->vec;
      mark_vec 	= marks[row_comp]->vec; 
//		test_quad_fast = test_quad_fasts[row_comp];
		ellipt_est::calc(el_info); //Zeilenweise,
/*			welches dann sys::calc() ruft, welches ueber die Spalten laeuft */
//reihe voll, fangen bei Loesung Komp. 0 wieder an (ist mehr ne Sicherung):
		uh_el 	=   u_begin   ;
		uh_old_el=    u_old_begin ;
		uh_qp 	=	uh_qp_begin;
		uh_old_qp =	uh_old_qp_begin;
  		D2uhqp	= 	D2uhqp_begin ;
	}
//wichtig
//  if (common_source_contrib)
//anders!   	common_source_contrib->el_fct(el_info);
//dirichlet-Rand in den Matrizen??
 return 0;
}


 /****************************************************************************/
 
sys_est::sys_est(const char *const name, const fem_base* discrete_prob, 
				  const REAL  *const time,const  REAL *const timestep, int degree)
	:	parab_est( name, discrete_prob/*, time,*/, timestep 
				, degree)
				, u_old_begin(uh_old_el)
 				,u_begin(uh_el)
				, uh_qp_begin(uh_qp)
				, uh_old_qp_begin(uh_old_qp)
{  FUNCNAME("sys_est constructor");
   TEST_EXIT(const_fe_space,"no const_fe_space\n");
//  get_C(name); macht parab_est!!
  INFO(info_level, 4,
	"estimation  C0: %e, C1: %e, C2: %e, C3: %e\n", C0,C1,C2,C3);
   D2uhqp = new REAL_DD[quad_fasts[0]->n_points*ncomps];
   //Baustelle! Groesse bedarf der sorgfaeltigen berechnung.
   MSG("constuction completed\n");
}

