## Copyright (C) 1996-2015 Piotr Held
##
## This file is part of Octave.
##
## Octave 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 (at your option) any
## later version.
##
## Octave 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 Octave; see the file COPYING.  If not,
## see <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn{Function File} {@var{output} =} upoembed (@var{olens},@var{orbit_data}, @var{delay})
## @deftypefnx{Function File} {@var{output} =} upoembed (@var{olens},@var{orbit_data}, @var{delay},  @var{paramName}, @var{paramValue}, @dots{})
##
## Creates delay coordinates for upo output.
##
## @strong{Inputs}
##
## @table @var
## @item olens
## This vector contains the periods that are generated by upo.
## @item orbit_data
## The orbit data that is generated by upo.
## @item delay
## The delay used to get the delay coordinates.
## @end table
##
## @strong{Parameter}
## @table @var
## @item m
## The embedding dimension used [default = 2].
## @item p
## The period of the orbit to be extracted. This may be a vector
## [default = extract all orbit periods].
## @end table
##
## @strong{Output}
##
## A cell that contains the delay vectors for each orbit. The orbits 
## are in the same order as they are in @var{olens}. Can be converted to 
## matrix using @code{str2mat (output)}.
##
## @seealso{upo}
##
## @strong{Algorithms}
##
## The algorithms for this functions have been taken from the TISEAN package.
## @end deftypefn

## Author: Piotr Held <pjheld@gmail.com>.
## This function is based on upoembed of TISEAN 3.0.1
## https://github.com/heggus/Tisean"

function output = upoembed (olens, orbit_data, delay, varargin)

  # Input validation
  if (nargin < 3)
    print_usage;
  endif

  isPositiveIntVector = @(x) isreal(x) && isvector (x) && ...
                             all (x > 0) && all (x-round(x) == 0);
  if (isPositiveIntVector (olens) == false)
    error ('Octave:invalid-input-arg', "olens must be a realvector");
  endif

  if ((isvector (orbit_data) == false) || (isreal(orbit_data) == false))
    error ('Octave:invalid-input-arg', "orbit_data must be a realvector");
  endif

  if (sum (olens) != length (orbit_data))
    error ('Octave:invalid-input-arg', "The sum of the periods of orbits \
is not equal to the number of orbit data");
  endif

  isPositiveIntScalar = @(x) isreal(x) && isscalar (x) && ...
                             (x > 0) && (x-round(x) == 0);
  if (isPositiveIntScalar (delay) == false)
    error ('Octave:invalid-input-arg', "delay must be a positive \
integer scalar value");
  endif

  # Assign default values
  m        = 2;
  porbit   = 1;

  #### Parse the input
  p = inputParser ();
  p.FunctionName = "upoembed";

  isNumericScalar = @(x) isreal(x) && isscalar (x);

  p.addParamValue ("m", m, isPositiveIntScalar);
  p.addParamValue ("p", porbit, isPositiveIntVector);

  p.parse (varargin{:});

  # Assing inputs
  m        = p.Results.m;
  porbit   = p.Results.p;

  ## Additional data validation 
  if (!ismember ('p', p.UsingDefaults) && !(any(ismember (porbit, olens))))
      error ('Octave:invalid-input-arg', "parameter p must be one of the \
values of olens");
  endif

  ## Generating indexes of orbits to use
  if (!ismember ('p', p.UsingDefaults))
    idx = find(ismember(olens, porbit)).';
  else
    idx = 1:length(olens);
  endif


  ## Create delay vectors
  j         = (m:-1:1);
  delay_vec = @(x) orbit_data(sum(olens(1:idx(x)-1))+ ...
                              mod(((1:olens(idx(x))+1).').-...
                              (j-1).*delay -1 +m.*olens(idx(x)),...
                              olens(idx(x)))+1);
  output    = arrayfun (delay_vec,(1:length(idx)).','UniformOutput', false);

  ## Below is unvectorized code (for reference only)
  ## Does not account for parameter 'p'
  ## The return is in a single matrix instead of a cell array
  #  for i=1:length(olens)
  #    for k=1:olens(i)+1;
  #      for j=m:-1:1;
  #       output(sum(olens(1:i-1)+1)+k,m+1-j) = ...
  #       orbit_data(sum(olens(1:i-1))+mod(k.-(j-1).*delay-1+m*olens(i),olens(i))+1);
  #      endfor
  #    endfor
  #  endfor

endfunction

%!shared olens, odata
%! olens = [6;6;2;1];
%! odata = [0.568860233; 0.320431054; 1.01113260; -0.396884680; 1.06592703; -0.735762954; 0.468448341; 0.791814446; 0.219137505; 1.15340614; -0.777471960; 0.447908580; 0.982830405; -0.487029105; 0.634082019];

%!fail ("upoembed (olens, odata, 1, 'p',3)");
%!xtest ("upoembed (olens, odata, 1, 'p',6)");

%!test
%! "res was generated using 'upoembed -d1' on 'olens' and 'odata' from TISEAN";
%! res = [-0.735762954, 0.568860233; 0.568860233, 0.320431054; 0.320431054, 1.01113260; 1.01113260, -0.396884680; -0.396884680, 1.06592703; 1.06592703, -0.735762954; -0.735762954, 0.568860233; 0.447908580, 0.468448341; 0.468448341, 0.791814446; 0.791814446, 0.219137505; 0.219137505, 1.15340614; 1.15340614, -0.777471960; -0.777471960, 0.447908580; 0.447908580, 0.468448341; -0.487029105, 0.982830405; 0.982830405, -0.487029105; -0.487029105, 0.982830405; 0.634082019, 0.634082019; 0.634082019, 0.634082019];
%! out = upoembed(olens,odata,1,'m',2);
%! out = cell2mat(out);
%! assert (out,res,1e-6);

%!test
%! "res was generated using 'upoembed -p6 -d1' from TISEAN on olens and odata";
%! res = [-0.735762954, 0.568860233; 0.568860233, 0.320431054; 0.320431054, 1.01113260; 1.01113260, -0.396884680; -0.396884680, 1.06592703; 1.06592703, -0.735762954; -0.735762954, 0.568860233; ; ; 0.447908580, 0.468448341; 0.468448341, 0.791814446; 0.791814446, 0.219137505; 0.219137505, 1.15340614; 1.15340614, -0.777471960; -0.777471960, 0.447908580; 0.447908580, 0.468448341];
%! out = upoembed(olens,odata,1,'p',6);
%! out = cell2mat(out);
%! assert (out,res,1e-6);

%!test
%! "TEST FOR WHEN 'P' IS A VECTOR";
%! res = [-0.735762954, 0.568860233; 0.568860233, 0.320431054; 0.320431054, 1.01113260; 1.01113260, -0.396884680; -0.396884680, 1.06592703; 1.06592703, -0.735762954; -0.735762954, 0.568860233; ; ; 0.447908580, 0.468448341; 0.468448341, 0.791814446; 0.791814446, 0.219137505; 0.219137505, 1.15340614; 1.15340614, -0.777471960; -0.777471960, 0.447908580; 0.447908580, 0.468448341;  0.634082019, 0.634082019;0.634082019, 0.634082019];
%! out = upoembed(olens,odata,1,'p',[6 1]);
%! out = cell2mat(out);
%! assert (out,res,1e-6);
