## Copyright (C) 2011 Martin Šíra
##

## -*- texinfo -*-
## @deftypefn {Function File} @var{result}=runmulticore (@var{method}, @var{functionhandle}, @var{parametercell}, @var{procno}, @var{tmpdir}, @var{verbose})
## Evaluate a function on multiple cores by means of either 'general' or 'multicore' octave forge packages, or test calculations by serial computing.
## Function enables to easly change between methods for easy debugging or running at different machines/octaves with different installed pacakges.
## @table @samp
## @item @var{method} 
##    Function used in multicore calculations. To use function 'parcellfun' from package 'general', set to "parcellfun", to use function 'startmulticoremaster' from package 'multicore', set to "multicore", to use serial computing, set to 'cellfun'.
## @item @var{functionhandle} 
##    Handle of the function to calculate (with at sign at the beginning).
## @item @var{parametercell} 
##    Cell with parameters for function @var{functionhandle}.
## @item @var{procno}
##    When specified, number of parallel processes will be limited. If ommited, set to 0, negative value or Inf, number of processes will be equal to number of computer cores.
## @item @var{tmpdir}
##    When specified, temporary directory for function 'startmulticoremaster' will be set, otherwise temporary directory is given by octave function 'tempdir'. Not usefull for function 'parcellfun'.
## @item @var{result}
##    Cell of the same size as @var{parametercell} with all results.
## @item @var{verbose}
##    Verbose level. If equal to zero, messages will be supressed.
## @end table
##
## Example: compare calculation times:
## @example
## for i=1:10
##      parametercell{i}=i;
## endfor
## tic
## result=runmulticore('cellfun', @factorial, parametercell, 0, '~', 0);
## toc
## tic
## result=runmulticore('parcellfun', @factorial, parametercell, 0, '~', 0);
## toc
## tic
## result=runmulticore('multicore', @factorial, parametercell, 0, '~', 0);
## toc
## @end example
## @end deftypefn

function result=runmulticore(method, functionhandle, parametercell, procno, tmpdir, verbose)
        % check arguments: --------------------------- %{{{1
        % correct number of arguments?
        if (nargin < 3 | nargin > 6)
                print_usage("");
        endif
        % correct first parameter?
        if ~(strcmp(method,"cellfun") | strcmp(method,"parcellfun") | strcmp(method,"multicore"))
                error("parralel computing method not specified!");
        endif
        % verbose level specified?
        if (nargin < 6)
                verbose=0;
        endif
        % temporary directory specified?
        if (nargin < 5)
                tmpdir=tempdir;
        endif
        % number of processes specified?
        if (nargin < 4)
                procno=inf;
        endif

        % determine number of processes: --------------------------- %{{{1
        if ( procno==inf | procno<1 )
                % detect number of cpus:
                % correctly should be: (doesn't work in octave 3.2):
                %cpus=nproc
                % else use following:
                if isunix
                        % unix os
                        pid = fopen("/proc/cpuinfo"); 
                        procno = length(strfind(char(fread(pid)'),"processor")); 
                        fclose(pid);
                else 
                        % expecting windows:
                        [status output]=system("echo %number_of_processors%"); 
                        procno=str2num(output);
                endif
        endif

        % calculate task: --------------------------- %{{{1
        % cellfun: --------------------------- %{{{2
        if strcmp(method,"cellfun") 
                % use non parallel computing by means of cellfun:
                result=cellfun(functionhandle, parametercell);
                % convert resulted struct to cell (because input is cell):
                resulttmp=cell(size(parametercell));
                for i=1:size(result,2)
                        resulttmp{i}=result(i);
                endfor
                result=resulttmp;
        % parcellfun: --------------------------- %{{{2
        elseif strcmp(method,"parcellfun") 
                % use package general and function parcellfun by J.Hajek:
                result=parcellfun(procno, functionhandle, parametercell, "VerboseLevel", verbose);
                % convert resulted struct to cell (because input is cell):
                resulttmp=cell(size(parametercell));
                for i=1:size(result,2)
                        resulttmp{i}=result(i);
                endfor
                result=resulttmp;
        % multicore --------------------------- %{{{2
        elseif strcmp(method,"multicore") 
                % use package multicore and function startmulticoremaster by Markus Buehren
                % run slave processess:
                fidIn=[];
                fidOut=[];
                fidPid=[];
                for i=1:procno-1
                        [curfidIn, curfidOut, curfidPid] = popen2 ("octave", "-q");
                        fidIn=[fidIn curfidIn];
                        fidOut=[fidOut curfidOut];
                        fidPid=[fidPid curfidPid];
                        input_str = ['startmulticoreslave(''',tmpdir,''');'];
                        fputs (fidIn, input_str);
                        fputs (fidIn, sprintf('\n'));
                endfor
                result = startmulticoremaster(functionhandle, parametercell, tmpdir, inf);

                % Close the secondary process
                % should be used: 
                % correctly should be: (doesn't work in octave 3.2):
                %pclose(fidPid(i))
                % else use following (leave zombies):
                for i=1:procno-1
                        fclose(fidIn(i));
                        fclose(fidOut(i));
                        [err, msg] = kill (fidPid(i), 9);
                endfor
        endif
endfunction

% --------------------------- tests: %{{{1

%!shared res, result, method, functionhandle, parametercell, procno, tmpdir, verbose
%! for i=1:10
%!      parametercell{i}=i;
%! endfor
%! functionhandle=@factorial;
%! procno=0;
%! tmpdir='~';
%! verbose=0;
%! result={};
%! result=runmulticore('cellfun', functionhandle, parametercell, procno, tmpdir, verbose);
%! res=0;
%! for i=1:10
%!      res=res+result{i};
%! endfor
%!assert(res,4037913)
%! result={};
%! result=runmulticore('parcellfun', functionhandle, parametercell, procno, tmpdir, verbose);
%! res=0;
%! for i=1:10
%!      res=res+result{i};
%! endfor
%!assert(res,4037913)
%! result={};
%! result=runmulticore('multicore', functionhandle, parametercell, procno, tmpdir, verbose);
%! res=0;
%! for i=1:10
%!      res=res+result{i};
%! endfor
%!assert(res,4037913)

% vim settings modeline: vim: foldmarker=%{{{,%}}} fdm=marker fen ft=octave
