% export2latex(EPLdata, filename, options)
%
% This function may be used to export numeric data in a latex file for later
% use with, e.g., NumericPlots.sty.
% If the data is provided as an 1xK array, each entry of the array will be exported
% to the file <filename>. Each entry of the array must be a structure which contains
% x, y and ident. x and y must be arrays of the same size MxN. If M>1, each column
% of the array is exported to the file and may later be used by \Data<ident><rm>, where
% <rm> is the roman representation of the column number. If M=1, the data may later
% be used with the latex command \Data<ident>
%
% EPLdata(1, i).x = x-matrix
% EPLdata(1, i).y = y-matrix
% EPLdata(1, i).ident = identifier (has to be a string without numbers ->
% latex command name)
% EPLdata(1, i).descr = description (optional)
% EPLdata(1, i).group = GroupNr (optional) use numbers from 1 to n to define groups. For
% each group, the maximum and minimum values will be written to the output file and may be
% accessed with identifier Group<RomanNumber> where RomanNumber is the roman represenation
% of the group number. All data without group or with group number 0 is put into the group
% Dummy
% EPLdata(1, i).precision (optional) = number of decimal places to be written
% filename = filename (with path, without extension)
% options (optional) define which output is required
%  - options.DataBoundaries [true]: if true, the min/max values of the data
%    will be written to the output file.
%  - options.AxisBoundaries [false]: if true, values which may be set as
%    min/max values for the axis are written to the output file. These are
%    the min/max values -/+ a gap.
%  - options.AxisBoundariesGap [10]: defines the gap used for the axis
%    min/max values in percent of the total range of the data.
%  - options.SuppressWarning [false]: suppresses the warning about max/min values being to
%    close together
%  - options.precision [empty]: how many decimal places should be printed for x and y
%    values. Will be calculated automatically if left empty.
%  - options.NaNsplit [false]: if true, the data will be split at NaN
%    values. See \multilistplot for how to plot them.
%
% function roman.m required to convert a number to its roman representation.
%

% author: Thomas K�nig
% date: 2010/04/01
% ed. by Pfeffer Andreas July 2010 ->Axis nearly tight -> xMaxAxis,xMinAxis,... added
% 2010/08/03 Thomas K�nig: Options added
%
% Copyright 2010 Thomas K�nig, Alexander Michel
%
% This file is part of NumericPlots.
%
% NumericPlots is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% any later version.
%
% NumericPlots is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with NumericPlots.  If not, see <http://www.gnu.org/licenses/>.


function ret = export2latex(EPLdata, filename, options)
   if size(EPLdata, 1)>1
       error('EPLdata must be of size [1, n]');
   end

   % define standard options
   if ((nargin<3) || ~isfield(options, 'DataBoundaries'))
       % DataBoundaries should be in the output file (xMin, xMax, yMin,
       % yMax)
       options.DataBoundaries = true;
   end
   if ~isfield(options, 'AxisBoundaries')
       % axis boundaries (xMinAxis, xMaxAxis, yMinAxis, yMaxAxis)
       options.AxisBoundaries = false;
   end
   if ~isfield(options, 'AxisBoundariesGap')
       % gap for the axis boundaries in percent of full scale
       options.AxisBoundariesGap = 10;
   end
   if ~isfield(options, 'SuppressWarning')
       options.SuppressWarning = false;
   end
   if ~isfield(options, 'NaNsplit')
       options.NaNsplit = false;
   end

   % open file if possible
   [pathstr, ~, ext] = fileparts(filename);
   if ~isempty(pathstr) && ~exist(pathstr, 'dir')
       mkdir(pathstr);
   end
   if strcmp(ext, '.tex')
       fname = fullfile(filename);
   else
       fname = fullfile([filename, '.tex']);
   end
   [fid, msg] = fopen(fname, 'wt');
   if fid<0
       display(msg);
       error('Matlab:FileError', 'Could not open the file %s.', fname);
   end

   % write file header
   fprintf(fid, '%% EPLdata written by export2latex\n');
   fprintf(fid, '%% date: %s\n\n', date);
   fprintf(fid, '\\def\\ExpDate{%s}\n', date);
   fclose(fid);

   % process data
   for i=1:size(EPLdata,2)
       % Ident='';
       My = size(EPLdata(i).y, 1);
       Mx = size(EPLdata(i).x, 1);
       if ( Mx>1 && Mx~=My )
           error('Matlab:export2latex', 'EPLdata.x and EPLdata.y must have the same size or EPLdata.x must have one column only');
       end
       if (any(any(isnan(EPLdata(i).x))) || any(any(isnan(EPLdata(i).y)))) && ~options.NaNsplit
           error('Matlab:export2latex', 'EPLdata.x or EPLdata.y contain NaN which Latex cannot process. Consider setting options.NaNsplit = true.');
       end
       if ( Mx==1 )
           idx_x = ones(1, My);
       else
           idx_x = 1:Mx;
       end

       % data header
       if(isfield(EPLdata(1,i), 'descr'))
           fid = fopen(fname, 'at');
           fprintf(fid, '\\expandafter\\def\\csname Descr%s\\endcsname{%s}\n', EPLdata(1,i).ident, EPLdata(1,i).descr);
           fclose(fid);
       end
       if ~isfield(EPLdata(1,i), 'group')
           EPLdata(1,i).group = 0;
       end
       if isempty(EPLdata(1,i).group)
           EPLdata(1,i).group = 0;
       end

       % xMax, xMin, Dx
       fid = fopen(fname, 'at');
       xMax = max(max(EPLdata(1,i).x));
       xMin = min(min(EPLdata(1,i).x));

       % axis boundaries
       xMinAxis = xMin-(xMax-xMin)*options.AxisBoundariesGap/100; % -10% axis nearly tight
       xMaxAxis = xMax+(xMax-xMin)*options.AxisBoundariesGap/100; % +10% axis nearly tight


       if (abs(xMax-xMin)<1e-16)
           xMax = 1.0;
           xMin = -1.0;
           if ~options.SuppressWarning
               fprintf('export2latex - Warning: |xMax-xMin|<1e-16\n');
           end
       end
       DxV = (xMax-xMin)/5;
       NrUnsigX = 0;
       while (fix(DxV*10^(NrUnsigX)) == 0)
           NrUnsigX = NrUnsigX+1;
       end

       % yMin, yMax, Dy
       yMax = max(max(EPLdata(1,i).y));
       yMin = min(min(EPLdata(1,i).y));

       % axis boundaries
       yMinAxis = yMin-(yMax-yMin)*options.AxisBoundariesGap/100; % -10% axis nearly tight
       yMaxAxis = yMax+(yMax-yMin)*options.AxisBoundariesGap/100; % +10% axis nearly tight

       % save min/max values for groupes
       group(i) = EPLdata(1,i).group+1;
       groupXmax(i) = xMax;
       groupXmin(i) = xMin;
       groupYmax(i) = yMax;
       groupYmin(i) = yMin;

       if (abs(yMax-yMin)<1e-16)
           yMax = 1.0;
           yMin = -1.0;
           if ~options.SuppressWarning
               fprintf('export2latex - Warning: |yMax-yMin|<1e-16\n');
           end
       end
       DyV = (yMax-yMin)/5;

       DxVAxis = (xMaxAxis-xMinAxis)/5;
       DyVAxis = (yMaxAxis-yMinAxis)/5;
       NrUnsigYAxis = 0;
       while (fix(DyVAxis*10^(NrUnsigYAxis)) == 0)
           NrUnsigYAxis = NrUnsigYAxis+1;
       end
       NrUnsigXAxis = 0;
       while (fix(DxVAxis*10^(NrUnsigXAxis)) == 0)
           NrUnsigXAxis = NrUnsigXAxis+1;
       end

       % check how many significant numbers DyV has. This number will be used for the precision of all Min/Max values so
       % latex prints all of them with the same number of decimal places.
       NrUnsigY = 0;
       while (fix(DyV*10^(NrUnsigY)) == 0)
           NrUnsigY = NrUnsigY+1;
       end

       if options.DataBoundaries
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXmin\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], EPLdata(1,i).ident, floor(xMin*10^NrUnsigX)/(10^NrUnsigX));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXmax\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], EPLdata(1,i).ident, ceil(xMax*10^NrUnsigX)/(10^NrUnsigX));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYmin\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], EPLdata(1,i).ident, floor(yMin*10^NrUnsigY)/(10^NrUnsigY));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYmax\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], EPLdata(1,i).ident, ceil(yMax*10^NrUnsigY)/(10^NrUnsigY));
       end
       fprintf(fid, ['\\expandafter\\def\\csname Data%sDxV\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], EPLdata(1,i).ident, DxV);
       fprintf(fid, ['\\expandafter\\def\\csname Data%sDyV\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], EPLdata(1,i).ident, DyV);
       if options.AxisBoundaries
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXminAxis\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], EPLdata(1,i).ident, floor(xMinAxis*10^NrUnsigX)/(10^NrUnsigX));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXmaxAxis\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], EPLdata(1,i).ident, ceil(xMaxAxis*10^NrUnsigX)/(10^NrUnsigX));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYminAxis\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], EPLdata(1,i).ident, floor(yMinAxis*10^NrUnsigY)/(10^NrUnsigY));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYmaxAxis\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], EPLdata(1,i).ident, ceil(yMaxAxis*10^NrUnsigY)/(10^NrUnsigY));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sDxV\\endcsname{%1.', num2str(NrUnsigXAxis), 'f}\n'], EPLdata(1,i).ident, DxVAxis);
           fprintf(fid, ['\\expandafter\\def\\csname Data%sDyV\\endcsname{%1.', num2str(NrUnsigYAxis), 'f}\n'], EPLdata(1,i).ident, DyVAxis);
       end
       fclose(fid);

       % write actual data
       for j=1:My
           % what precision is needed to output the data?
           yyMax = max(EPLdata(1,i).y(idx_x(j),:));
           yyMin = min(EPLdata(1,i).y(idx_x(j),:));
           if (abs(yyMax-yyMin)<1e-16)
               yNrPrecision = 1;
           else
               yNrPrecision = ceil(log10(10000/(yyMax-yyMin)));
           end
           xxMax = max(EPLdata(1,i).x(idx_x(j),:));
           xxMin = min(EPLdata(1,i).x(idx_x(j),:));
           if (abs(xxMax-xxMin)<1e-16)
               xNrPrecision = 1;
           else
               xNrPrecision = ceil(log10(10000/(xxMax-xxMin)));
           end
           if isfield(EPLdata(1,i), 'precision')
               NrPrecision = EPLdata(1,i).precision;
               if isempty(NrPrecision)
                   NrPrecision = max(yNrPrecision, xNrPrecision);
               end
           else
               NrPrecision = max(yNrPrecision, xNrPrecision);
           end

           % write the data to the file
           xData = EPLdata(1,i).x(idx_x(j),:);
           yData = EPLdata(1,i).y(j,:);
           if My > 1; Ident = roman(num2str(j)); else Ident=''; end;

           if options.NaNsplit
               % look for the ranges where the data exists (~isnan)
               yData(isnan(xData)) = NaN;

               KeepLooking = true;
               nrRanges = 0;
               Ranges = zeros(0);
               b = 0;
               while KeepLooking
                   a = find(~isnan(yData((b+1):end)), 1, 'first') + b;
                   if ~isempty(a)
                       b = find(isnan(yData(a:end)), 1, 'first') + a-1;
                       if isempty(b)
                           b = length(yData);
                           KeepLooking = false;
                       end
                       nrRanges = nrRanges+1;
                       Ranges(nrRanges, :) = [a, b-1];
                   else
                       KeepLooking = false;
                   end
               end

               % data does not contain NaN:
               if nrRanges<1
                   nrRanges = 1;
                   Ranges(1, :) = [1, length(yData)];
               end

               fid = fopen(fname, 'at');
               fprintf(fid, '\\expandafter\\def\\csname Data%s%sNrRanges\\endcsname{%i}\n', EPLdata(1,i).ident, Ident, nrRanges);
               fclose(fid);
               for iRange = 1:nrRanges
                   fid = fopen(fname, 'at');
                   fprintf(fid, '\\expandafter\\def\\csname Data%s%s%i\\endcsname{\n', EPLdata(1,i).ident, Ident, iRange);
                   fclose(fid);
                   dlmwrite(fname, [xData(Ranges(iRange,1):Ranges(iRange,2)); yData(Ranges(iRange,1):Ranges(iRange,2))]', '-append', 'delimiter', ' ', 'precision', ['%.', num2str(NrPrecision), 'f']);
                   fid = fopen(fname, 'at');
                   fprintf(fid, '}\n\n');
                   fclose(fid);
               end

           else
               % options.NaN = false

               fid = fopen(fname, 'at');
               fprintf(fid, '\\expandafter\\def\\csname Data%s%s\\endcsname{\n', EPLdata(1,i).ident, Ident);
               fclose(fid);
               dlmwrite(fname, [xData; yData]', '-append', 'delimiter', ' ', 'precision', ['%.', num2str(NrPrecision), 'f']);
               fid = fopen(fname, 'at');
               fprintf(fid, '}\n\n');
               fclose(fid);
           end
       end
   end

   % write group values
   fid = fopen(fname, 'at');
   for i = 1:max(group)
       xMax = max(groupXmax(group==i));
       xMin = min(groupXmin(group==i));
       yMax = max(groupYmax(group==i));
       yMin = min(groupYmin(group==i));
       if (abs(yMax-yMin)<1e-16)
           yMax = 1.0;
           yMin = -1.0;
       end
       if (abs(xMax-xMin)<1e-16)
           xMax = 1.0;
           xMin = -1.0;
       end


       % axis boundaries
       xMinAxis = xMin-(xMax-xMin)*options.AxisBoundariesGap/100;
       xMaxAxis = xMax+(xMax-xMin)*options.AxisBoundariesGap/100;
       yMinAxis = yMin-(yMax-yMin)*options.AxisBoundariesGap/100;
       yMaxAxis = yMax+(yMax-yMin)*options.AxisBoundariesGap/100;

       DxV = (xMax-xMin)/5;
       DyV = (yMax-yMin)/5;
       DxVAxis = (xMaxAxis-xMinAxis)/5;
       DyVAxis = (yMaxAxis-yMinAxis)/5;

       % check how many significant numbers DyV has. This number will be used for the precision of all Min/Max values so
       % latex prints all of them with the same number of decimal places.
       NrUnsigY = 0;
       while (fix(DyV*10^(NrUnsigY)) == 0)
           NrUnsigY = NrUnsigY+1;
       end
       NrUnsigX = 0;
       while (fix(DxV*10^(NrUnsigX)) == 0)
           NrUnsigX = NrUnsigX+1;
       end
       NrUnsigYAxis = 0;
       while (fix(DyVAxis*10^(NrUnsigYAxis)) == 0)
           NrUnsigYAxis = NrUnsigYAxis+1;
       end
       NrUnsigXAxis = 0;
       while (fix(DxVAxis*10^(NrUnsigXAxis)) == 0)
           NrUnsigXAxis = NrUnsigXAxis+1;
       end

       if i>1.5
           ident = strcat('Group', roman(num2str(i-1)));
       else
           ident = 'GroupDummy';
       end

       if options.DataBoundaries
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXmin\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], ident, floor(xMin*10^NrUnsigX)/(10^NrUnsigX));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXmax\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], ident, ceil(xMax*10^NrUnsigX)/(10^NrUnsigX));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYmin\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], ident, floor(yMin*10^NrUnsigY)/(10^NrUnsigY));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYmax\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], ident, ceil(yMax*10^NrUnsigY)/(10^NrUnsigY));
       end
       fprintf(fid, ['\\expandafter\\def\\csname Data%sDxV\\endcsname{%1.', num2str(NrUnsigX), 'f}\n'], ident, DxV);
       fprintf(fid, ['\\expandafter\\def\\csname Data%sDyV\\endcsname{%1.', num2str(NrUnsigY), 'f}\n'], ident, DyV);
       if options.AxisBoundaries
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXminAxis\\endcsname{%1.', num2str(NrUnsigXAxis), 'f}\n'], ident, floor(xMinAxis*10^NrUnsigXAxis)/(10^NrUnsigXAxis));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sXmaxAxis\\endcsname{%1.', num2str(NrUnsigXAxis), 'f}\n'], ident, ceil(xMaxAxis*10^NrUnsigXAxis)/(10^NrUnsigXAxis));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYminAxis\\endcsname{%1.', num2str(NrUnsigYAxis), 'f}\n'], ident, floor(yMinAxis*10^NrUnsigYAxis)/(10^NrUnsigYAxis));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sYmaxAxis\\endcsname{%1.', num2str(NrUnsigYAxis), 'f}\n'], ident, ceil(yMaxAxis*10^NrUnsigYAxis)/(10^NrUnsigYAxis));
           fprintf(fid, ['\\expandafter\\def\\csname Data%sDxVAxis\\endcsname{%1.', num2str(NrUnsigXAxis), 'f}\n'], ident, DxVAxis);
           fprintf(fid, ['\\expandafter\\def\\csname Data%sDyVAxis\\endcsname{%1.', num2str(NrUnsigYAxis), 'f}\n'], ident, DyVAxis);
       end

   end
   fclose(fid);
   ret = 1;
end