%AUTODIFF class definition
%
%   Usage:
%      autodiff=autodiff();

classdef autodiff
	properties (SetAccess=public) 
		isautodiff   = false;
		dependents   = {};
		independents = {};
		driver       = 'fos_forward';
	end
	methods
		function obj = autodiff(varargin) % {{{
			switch nargin
				case 0
					obj=setdefaultparameters(obj);
				otherwise
					error('constructor not supported');
			end
		end % }}}
		function obj = setdefaultparameters(obj) % {{{

		end % }}}
		function md = checkconsistency(obj,md,solution,analyses) % {{{

			%Early return 
			if ~obj.isautodiff, return; end

			%Driver value:
			md = checkfield(md,'autodiff.driver','values',{'fos_forward','fov_forward','fov_forward_all','fos_reverse','fov_reverse','fov_reverse_all'});

			%go through our dependents and independents and check consistency: 
			for i=1:numel(obj.dependents),
				dep=obj.dependents{i};
				md=checkconsistency(dep,md,solution,analyses);
			end
			for i=1:numel(obj.independents),
				indep=obj.independents{i};
				md=checkconsistency(indep,md,i,solution,analyses,obj.driver);
			end

		end % }}}
		function disp(obj) % {{{
			disp(sprintf('      automatic differentiation parameters:'));
			fielddisplay(obj,'isautodiff','indicates if the automatic differentiation is activated');
			fielddisplay(obj,'dependents','list of dependent variables');
			fielddisplay(obj,'independents','list of independent variables');
			fielddisplay(obj,'driver','ADOLC driver (''fos_forward'' or ''fov_forward'')');
		end % }}}
		function marshall(obj,fid) % {{{

			WriteData(fid,'object',obj,'fieldname','isautodiff','format','Boolean');
			WriteData(fid,'object',obj,'fieldname','driver','format','String');

			%early return
			if ~obj.isautodiff,
				WriteData(fid,'data',false,'enum',AutodiffMassFluxSegmentsPresentEnum(),'format','Boolean');
				WriteData(fid,'data',false,'enum',AutodiffKeepEnum(),'format','Boolean');
				return;
			end

			%process dependent variables {{{
			num_dependent_objects=numel(obj.dependents);
			WriteData(fid,'data',num_dependent_objects,'enum',AutodiffNumDependentObjectsEnum(),'format','Integer');

			if(num_dependent_objects),
				names=zeros(num_dependent_objects,1);
				types=zeros(num_dependent_objects,1);
				indices=zeros(num_dependent_objects,1);

				for i=1:num_dependent_objects,
					dep=obj.dependents{i};

					names(i)=StringToEnum(dep.name);
					types(i)=dep.typetoscalar();
					indices(i)=dep.index;
				end
				WriteData(fid,'data',names,'enum',AutodiffDependentObjectNamesEnum(),'format','IntMat','mattype',3);
				WriteData(fid,'data',types,'enum',AutodiffDependentObjectTypesEnum(),'format','IntMat','mattype',3);
				WriteData(fid,'data',indices,'enum',AutodiffDependentObjectIndicesEnum(),'format','IntMat','mattype',3);
			end
			%}}}
			%process independent variables {{{
			num_independent_objects=numel(obj.independents);
			WriteData(fid,'data',num_independent_objects,'enum',AutodiffNumIndependentObjectsEnum(),'format','Integer');

			if(num_independent_objects),
				names=zeros(num_independent_objects,1);
				types=zeros(num_independent_objects,1);

				for i=1:num_independent_objects,
					indep=obj.independents{i};

					names(i)=StringToEnum(indep.name);
					types(i)=indep.typetoscalar();
				end
				WriteData(fid,'data',names,'enum',AutodiffIndependentObjectNamesEnum(),'format','IntMat','mattype',3);
				WriteData(fid,'data',types,'enum',AutodiffIndependentObjectTypesEnum(),'format','IntMat','mattype',3);
			end
			%}}}
			%if driver is fos_forward, build index:  {{{
			if strcmpi(obj.driver,'fos_forward'),
				index=0;

				for i=1:num_independent_objects,
					indep=obj.independents{i};
					if ~isnan(indep.fos_forward_index),
						index=index+indep.fos_forward_index;
						break;
					else
						if strcmpi(indep.type,'scalar'),
							index=index+1;
						else
							index=index+indep.nods;
						end
					end
				end
				index=index-1; %get c-index numbering going
				WriteData(fid,'data',index,'enum',AutodiffFosForwardIndexEnum(),'format','Integer');
			end
			%}}}
			%if driver is fos_reverse, build index:  {{{
			if strcmpi(obj.driver,'fos_reverse'),
				index=0;

				for i=1:num_dependent_objects,
					dep=obj.dependents{i};
					if ~isnan(dep.fos_reverse_index),
						index=index+dep.fos_reverse_index;
						break;
					else
						if strcmpi(dep.type,'scalar'),
							index=index+1;
						else
							index=index+dep.nods;
						end
					end
				end
				index=index-1; %get c-index numbering going
				WriteData(fid,'data',index,'enum',AutodiffFosReverseIndexEnum(),'format','Integer');
			end
			%}}}
			%if driver is fov_forward, build indices:  {{{
			if strcmpi(obj.driver,'fov_forward'),
				indices=0;

				for i=1:num_independent_objects,
					indep=obj.independents{i};
					if ~isempty(indep.fos_forward_index),
						indices=indices+indep.fov_forward_indices;
						break;
					else
						if strcmpi(indep.type,'scalar'),
							indices=indices+1;
						else
							indices=indices+indep.nods;
						end
					end
				end
				indices=indices-1; %get c-indices numbering going
				WriteData(fid,'data',indices,'enum',AutodiffFovForwardIndicesEnum(),'format','IntMat','mattype',3);
			end
			%}}}
			%deal with mass fluxes:  {{{
			mass_flux_segments=cell(0,1);
			for i=1:num_dependent_objects,
				dep=obj.dependents{i};
				if strcmpi(dep.name,'MassFlux'),
					mass_flux_segments{end+1,1}=dep.segments;
				end
			end
			if ~isempty(mass_flux_segments), 
				WriteData(fid,'data',mass_flux_segments,'enum',MassFluxSegmentsEnum(),'format','MatArray');
				flag=true;
			else
				flag=false;
			end
			WriteData(fid,'data',flag,'enum',AutodiffMassFluxSegmentsPresentEnum(),'format','Boolean');
			%}}}
			%deal with trace keep on: {{{
			keep=false;

			%From ADOLC userdoc: 
			% The optional integer argument keep of trace on determines whether the numerical values of all active variables are 
			% recorded in a buffered temporary array or file called the taylor stack. This option takes effect if keep = 1 and 
			% prepares the scene for an immediately following gradient evaluation by a call to a routine implementing the reverse 
			% mode as described in the Section 4 and Section 5. 
			%

			if length(obj.driver)<=3,
				keep=false; %there is no "_reverse" string within the driver string: 
			else
				if strncmpi(obj.driver(4:end),'_reverse',8),
					keep=true;
				else
					keep=false;
				end
			end
			WriteData(fid,'data',keep,'enum',AutodiffKeepEnum(),'format','Boolean');
			%}}}

		end % }}}
	end
end
