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

classdef autodiff
	properties (SetAccess=public)  
		% {{{ 
		isautodiff   = false;
		dependents   = {};
		independents = {};
		driver       = 'fos_forward';
		obufsize     = NaN;
		lbufsize     = NaN;
		cbufsize     = NaN;
		tbufsize     = NaN;
		gcTriggerRatio = NaN;
		gcTriggerMaxSize = NaN;
		end
		%}}}
	methods
     	function createxml(obj,fid) % {{{
            fprintf(fid, '<!-- autodiff -->\n');            
                    
            % automatic differentiation parameters 
            fprintf(fid,'%s\n%s\n%s\n','<frame key="1" label="automatic differentiation parameters">','<section name="autodiff" />');                    
                fprintf(fid,'%s%s%s\n%s\n%s%s%s\n%s\n%s\n','  <parameter key ="isautodiff" type="',class(obj.isautodiff),'" optional="false">','     <section name="autodiff" />','         <option value="',convert2str(obj.isautodiff),'" type="string" default="true"></option>','     <help> indicates if the automatic differentiation is activated </help>','  </parameter>');
             
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="dependents" type="',class(obj.dependents),'" default="',convert2str(obj.dependents),'">','     <section name="autodiff" />','     <help> list of dependent variables </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="independents" type="',class(obj.independents),'" default="',convert2str(obj.independents),'">','     <section name="autodiff" />','     <help> list of independent variables </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="driver" type="',class(obj.driver),'" default="',convert2str(obj.driver),'">','     <section name="autodiff" />','     <help> ADOLC driver (''fos_forward'' or ''fov_forward'') </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="obufsize" type="',class(obj.obufsize),'" default="',convert2str(obj.obufsize),'">','     <section name="autodiff" />','     <help> Number of operations per buffer (==OBUFSIZE in usrparms.h)  </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="lbufsize" type="',class(obj.lbufsize),'" default="',convert2str(obj.lbufsize),'">','     <section name="autodiff" />','     <help> Number of locations per buffer (==LBUFSIZE in usrparms.h) </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="cbufsize" type="',class(obj.cbufsize),'" default="',convert2str(obj.cbufsize),'">','     <section name="autodiff" />','     <help> Number of values per buffer (==CBUFSIZE in usrparms.h) </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="tbufsize" type="',class(obj.tbufsize),'" default="',convert2str(obj.tbufsize),'">','     <section name="autodiff" />','     <help> Number of taylors per buffer (&amp;lt;=TBUFSIZE in usrparms.h) </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="gcTriggerRatio" type="',class(obj.gcTriggerRatio),'" default="',convert2str(obj.gcTriggerRatio),'">','     <section name="autodiff" />','     <help> free location block sorting/consolidation triggered if the ratio between allocated and used locations exceeds gcTriggerRatio </help>','  </parameter>');
                fprintf(fid,'%s%s%s%s%s\n%s\n%s\n%s\n','  <parameter key ="gcTriggerRatio" type="',class(obj.gcTriggerRatio),'" default="',convert2str(obj.gcTriggerRatio),'">','     <section name="autodiff" />','     <help> free location block sorting/consolidation triggered if the allocated locations exceed gcTriggerMaxSize </help>','  </parameter>');
            
            fprintf(fid,'%s\n%s\n','</frame>');    
        
        end % }}}
		function obj = autodiff(varargin) % {{{
			switch nargin
				case 0
					obj=setdefaultparameters(obj);
				otherwise
					error('constructor not supported');
			end
		end % }}}
		function obj = setdefaultparameters(obj) % {{{
		obj.obufsize     = 524288;
		obj.lbufsize     = 524288;
		obj.cbufsize     = 524288;
		obj.tbufsize     = 524288;
		obj.gcTriggerRatio=2.0;
		obj.gcTriggerMaxSize=65536;
		end % }}}
		function md = checkconsistency(obj,md,solution,analyses) % {{{

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

			%Driver value:
			md = checkfield(md,'fieldname','autodiff.driver','values',{'fos_forward','fov_forward','fov_forward_all','fos_reverse','fov_reverse','fov_reverse_all'});
			
			%buffer values: 
			md = checkfield(md,'fieldname','autodiff.obufsize','>=',16);
			md = checkfield(md,'fieldname','autodiff.lbufsize','>=',16);
			md = checkfield(md,'fieldname','autodiff.cbufsize','>=',16);
			md = checkfield(md,'fieldname','autodiff.tbufsize','>=',16);
			md = checkfield(md,'fieldname','autodiff.gcTriggerRatio','>=',0);
			md = checkfield(md,'fieldname','autodiff.gcTriggerMaxSize','>=',65536);

			%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'')');
			fielddisplay(obj,'obufsize','Number of operations per buffer (==OBUFSIZE in usrparms.h)');
			fielddisplay(obj,'lbufsize','Number of locations per buffer (==LBUFSIZE in usrparms.h)');
			fielddisplay(obj,'cbufsize','Number of values per buffer (==CBUFSIZE in usrparms.h)');
			fielddisplay(obj,'tbufsize','Number of taylors per buffer (<=TBUFSIZE in usrparms.h)');
			fielddisplay(obj,'gcTriggerRatio','free location block sorting/consolidation triggered if the ratio between allocated and used locations exceeds gcTriggerRatio');
			fielddisplay(obj,'gcTriggerMaxSize','free location block sorting/consolidation triggered if the allocated locations exceed gcTriggerMaxSize');
		end % }}}
		function marshall(obj,md,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

			%buffer sizes {{{
			WriteData(fid,'object',obj,'fieldname','obufsize','format','Double');
			WriteData(fid,'object',obj,'fieldname','lbufsize','format','Double');
			WriteData(fid,'object',obj,'fieldname','cbufsize','format','Double');
			WriteData(fid,'object',obj,'fieldname','tbufsize','format','Double');
			WriteData(fid,'object',obj,'fieldname','gcTriggerRatio','format','Double');
			WriteData(fid,'object',obj,'fieldname','gcTriggerMaxSize','format','Double');
			%}}}
			%process dependent variables {{{
			num_dependent_objects=numel(obj.dependents);
			WriteData(fid,'data',num_dependent_objects,'enum',AutodiffNumDependentObjectsEnum(),'format','Integer');

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

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

					names{i}=dep.name;
					types(i)=dep.typetoscalar();
					indices(i)=dep.index;
				end
				WriteData(fid,'data',names,'enum',AutodiffDependentObjectNamesEnum(),'format','StringArray');
				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
