unit uPostOpers;

{$DEFINE MULTIORDENAR_QUICKSORT}

interface

uses
{$IFDEF WINDOWS}
  Dialogs,
{$ENDIF}
  uHistoVarsOps, xMatDefs, Math, uSimResGlobs, uAuxiliares,
  SysUtils;

type
  TClaseDePostOper = class of TPostOper;

  TPostOper = class
  public
    res: TCronVar;
    //Resuelve los valores que tengan que ver con los índices o el
    //encabezado de la simulacion.
    //Para las PostOpers debe inicializar los resultados de manera que tengan
    //las dimensiones necesarias para alojar sus resultados, p ej en una suma
    //de CronVars las mismas dimensiones que los parametros, en un
    //cambioPasoDeTiempo la cantidad de cónicas del parámetro y la cantidad de
    //pasos del parametro * (viejaDurPaso / nuevaDurPaso)
    procedure prepararse; virtual; abstract;
    procedure Evaluar; virtual; abstract;
    class function tipo: string; virtual; abstract;
    function referenciaCronVar(cronVar: TCronVar): boolean; virtual;
    function parametrosCronVar: TDAOfCronVar; virtual;
    function nombresParametrosCronVar: string; virtual;
    function parametrosAdicionales: string; virtual;
    function serialize: string; virtual;
  end;

  TPostOperUnReal = class(TPostOper)
  public
    aReal: NReal;
    function serialize: string; override;
  end;


  TPostOperUnaCronVar = class(TPostOper)
  public
    param1: TCronVar;
    function referenciaCronVar(cronVar: TCronVar): boolean; override;
    function parametrosCronVar: TDAOfCronVar; override;
    function nombresParametrosCronVar: string; override;
    function serialize: string; override;
  end;

  TPostOperUnaCronVarUnReal = class(TPostOperUnaCronVar)
  public
    aReal: NReal;
    function serialize: string; override;
  end;

  TPostOperDosCronVars = class(TPostOperUnaCronVar)
  public
    param2: TCronVar;
    function referenciaCronVar(cronVar: TCronVar): boolean; override;
    function parametrosCronVar: TDAOfCronVar; override;
    function nombresParametrosCronVar: string; override;
    procedure prepararse; override;
    function serialize: string; override;
  end;

  TPostOperMultiCronVar = class(TPostOper)
  public
    params: TDAOfCronVar;
    function referenciaCronVar(cronVar: TCronVar): boolean; override;
    procedure prepararse; override;
    function parametrosCronVar: TDAOfCronVar; override;
    function nombresParametrosCronVar: string; override;
  end;


  TPostOper_CrearConstanteReal = class(TPostOperUnReal)
  public
    constructor Create(res: TCronVar; aReal: NReal);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
  end;


  TPostOper_minEntreCronVarYReal = class(TPostOperUnaCronVarUnReal)
  public
    constructor Create(res, param1: TCronVar; aReal: NReal);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
  end;

  TPostOper_maxEntreCronVarYReal = class(TPostOperUnaCronVarUnReal)
  public
    constructor Create(res, param1: TCronVar; aReal: NReal);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
  end;

  TPostOper_cronVarMasReal = class(TPostOperUnaCronVarUnReal)
  public
    constructor Create(res, param1: TCronVar; aReal: NReal);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
  end;

  TPostOper_cronVarPorReal = class(TPostOperUnaCronVarUnReal)
  public
    constructor Create(res, param1: TCronVar; aReal: NReal);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
  end;

  TPostOper_restaCronVars = class(TPostOperDosCronVars)
  public
    constructor Create(res, param1, param2: TCronVar);
    procedure Evaluar; override;
    class function tipo: string; override;
  end;

  (* Hace la combinación lineal ponderarda de un conjunto de variables
  combinarCronVars resVar N coef1 coef2 .... coefN cronVar1 cronVar2 ... cronVarN
  *)
  TPostOper_combinarCronVars = class(TPostOperMultiCronVar)
  public
    coeficientes: TDAOfNReal;
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar;
      coeficientes: TDAOfNReal);
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
    function serialize: string; override;
  end;

  (* Hace la combinación lineal ponderarda de un conjunto de variables
  combinarCronVars resVar N coef1 coef2 .... coefN cronVar1 cronVar2 ... cronVarN
  Pero considerando cada una con un desplazamiento en el tiempo.
  Cuando al aplicar un desplazamiento, se sale de la serie, ese valor no se considera
  en las sumas por lo tanto, los bordes del resultado pueden no tener el aporte
  de una serie que al desplazarla no cubre el borde. Por ejemplo si res[k]= x[k]+y[k+1]
  res[0]= x[0]+y[1]
  ...
  res[k]= x[k]+y[k+1]
  ...
  res[n-1]=x[n-1]+y[n]
  res[n]= x[n] + 0

  como podemos ver en este caso, el último valor de las series resultado está
  calculado con el aporte de sólo una de las series.
  *)
  TPostOper_combinarDespCronVars = class(TPostOperMultiCronVar)
  public
    coeficientes: TDAOfNReal;
    desplazamientos: TDAOfNInt;
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar;
      coeficientes: TDAOfNReal; desplazamientos: TDAOfNInt);
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
    function serialize: string; override;
  end;



    (* rch@201501240821
       Ordena tramos temporales de un conjunto de CronVars en base
    a una de ellas identificada como la Monotonizante.
    Sirve para porder en salas de paso horario tramos diarios o semanales
    para comparar con los resultados de las salas de pasos diarios o semanales
    en que la ejecución se realiza en postes.
    *)
  TPostOper_MonotonizarCronVars = class(TPostOperMultiCronVar)
  public
    NPasosDelCajon: integer;
    flg_Decreciente: boolean;
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar;
      NPasosDelCajon: integer; flg_Decreciente: boolean);
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
    function serialize: string; override;
  end;



  (* res[iPaso][iCronica]:= q^iPaso * param1[iPaso][iCronica] *)
  TPostOper_aplicarActualizador = class(TPostOperUnaCronVarUnReal)
  public
    constructor Create(res, param1: TCronVar; q: NReal);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
  end;

  TPostOper_multiplicacionCronVars = class(TPostOperDosCronVars)
  public
    constructor Create(res, param1, param2: TCronVar);
    procedure Evaluar; override;
    class function tipo: string; override;
  end;

  TPostOper_divisionCronVars = class(TPostOperDosCronVars)
  public
    constructor Create(res, param1, param2: TCronVar);
    procedure Evaluar; override;
    class function tipo: string; override;
  end;

  // Tipo de cambio de paso de tiempo
  TTipoCPT = (TCPT_Promedio, TCPT_Minimo{, TCPT_Primero}, TCPT_Suma, TCPT_Maximo,
    {TCPT_Ultimo,} TCPT_I_esimo);

  { TPostOper_cambioPasoDeTiempo }

  TPostOper_cambioPasoDeTiempo = class(TPostOperUnaCronVar)
  public

    horasPasoNuevo: NReal;
    tipoCPT: TTipoCPT;
    jesimaEntrada: integer;

    constructor Create(res, param1: TCronVar; horasPasoNuevo: NReal;
      xTipoCPT: TTipoCPT; x_jesimaEntrada: integer = -1); overload;

    procedure prepararse; override;
    procedure Evaluar; override;

    procedure Evaluar_PromSum;

    class function tipo: string; override;
    function parametrosAdicionales: string; override;
    function serialize: string; override;
  end;

  TPostOper_CVaR = class(TPostOperUnaCronVar)
  public
    p1, p2: NReal;
    PreOrdenar: integer; // -1: Decreciente, 0: No Ordenar, 1: Creciente
    constructor Create(res, param1: TCronVar; p1, p2: NReal; PreOrdenar: integer);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
    function serialize: string; override;
  end;


  TPostOper_acumularCronVar = class(TPostOperUnaCronVar)
  public
    constructor Create(res, param1: TCronVar);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
  end;


  TPostOper_acumularConPisoYTecho = class(TPostOperMultiCronVar)
  public
    Ingreso, Piso, Techo: TCronVar; // punteros a los params
    ValIni: NReal;

    constructor Create(res, ingreso, Piso, Techo: TCronVar; ValIni: NReal);

    procedure SetParametros(res, ingreso, Piso, Techo: TCronVar; ValIni: NReal);

    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
    function serialize: string; override;
  end;


  TPostOper_potenciaFirmeHidraulica = class(TPostOperMultiCronVar)
  public
    constructor Create(res: TCronVar; params: TDAOfCronVar);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    procedure Free;
    function serialize: string; override;
  end;

  TPostOper_maximo = class(TPostOperMultiCronVar)
  public
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar);
    procedure Evaluar; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;

  TPostOper_MultiOrdenar = class(TPostOperMultiCronVar)
  public
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar);
    procedure Evaluar; override;
    procedure prepararse; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;


  TPostOper_MultiPromedioMovil = class(TPostOperMultiCronVar)
  public
    NPasosPM: integer;
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar; NPasosPM: integer);
    procedure Evaluar; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;

  { TPostOper_Recronizar
  Reconizar( a, n ) tiene como resultado una CronVar Res tal que
  Res.NCronicas = n * a.NCronias
  Comenzando con el paso 1 de a, se tonan n valores de cada crónica y
  se ponen como valores de las nuevas crónicas.
  }

  TPostOper_Recronizar = class(TPostOperUnaCronVar)
  public
    NPasosRecronizacion, NCronRes, NPasosRes: integer;
    DurPaso_Horas_Res: NReal;
    constructor Create(res: TCronVar; aCronVar: TCronVar; NPasosRecronizacion: integer);
    procedure Evaluar; override;
    procedure prepararse; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;

  { TPostOper_Concatenar  }

  TPostOper_Concatenar = class(TPostOperUnaCronVar)
  public
    NCronRes, NPasosRes: integer;
    DurPaso_Horas_Res: NReal;
    constructor Create(res: TCronVar; aCronVar: TCronVar);
    procedure Evaluar; override;
    procedure prepararse; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;

  { TPostOper_ParalelizarCronVars }
  TPostOper_ParalelizarCronVars = class(TPostOperMultiCronVar)
  public
    NCronRes, NPasosRes: integer;
    DurPaso_Horas_Res: NReal;
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar);
    procedure Prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;

  { TPostOper_Enventanar

  Res := Enventanar( a, pIni, pFin )

  Carga en Res las crónicas de a comenzando en aquella
  correspondiente a una probabilidad acumulada pIni y finalizando
  en aquella con probabilidad acumulada pFin.

  La Probabilidad Acumulada se mide como 0 para la primer crónica y 1
  para la última  crónica.

  Res.nCronicas = (kCronFin - kCronIni +1 )

  }

  TPostOper_Enventanar = class(TPostOperUnaCronVar)
  public
    kIni, kFin: integer;
    pIni, pFin: NReal;
    constructor Create(res: TCronVar; aCronVar: TCronVar; pIni, pFin: NReal);
    procedure Evaluar; override;
    procedure prepararse; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;

  { TPostOper_Transponer

  Res:= Transponer( a )
  Transpone la matriz
  }

  TPostOper_Transponer = class(TPostOperUnaCronVar)
  public
    constructor Create(res: TCronVar; aCronVar: TCronVar);
    procedure Evaluar; override;
    procedure prepararse; override;
    class function tipo: string; override;
  end;


  TSentidoAcumCron = (SAC_DeIzquierdaADerecha, SAC_DeDerechaAIzquierda);

  { TPostOper_AcumCron
  Res:= AcumCron( a, flgPromediar, Sentido )
  Para cada paso de tiempo carga en el resultado el acumulado de los
  valores de "a" del mismo paso.
  Dependiendo del parámetro de dirección el acumulado
  se hace de izquierda a derecha (de la primera a la última crónica)
  o de derecha a izquierda (de la última crónica a la primera).
  El parámetro flgPromediar indica si al acumular, se divide por
  la cantidad de valores acumulados o no.
  }

  TPostOper_AcumCron = class(TPostOperUnaCronVar)
  public
    flgPromediar: boolean;
    sentido: TSentidoAcumCron;
    constructor Create(res: TCronVar; aCronVar: TCronVar;
      flgPromediar: boolean; sentido: TSentidoAcumCron);
    procedure Evaluar; override;
    procedure prepararse; override;
    class function tipo: string; override;
    function serialize: string; override;
  end;


implementation

uses
  uAuxiliaresEscrituraSimRes;

{ TPostOper_Transponer }

constructor TPostOper_Transponer.Create(res: TCronVar; aCronVar: TCronVar);
begin
  inherited Create;
  Self.param1 := aCronVar;
  Self.res := res;
end;

procedure TPostOper_Transponer.Evaluar;
var
  kPaso, kCron: integer;
begin
  for kPaso := 0 to param1.nPasos - 1 do
    for kCron := 0 to param1.nCronicas - 1 do
      res.SetVal(kCron, kPaso, param1.GetVal(kPaso, kCron));
end;

procedure TPostOper_Transponer.prepararse;
begin
  res.inicializar(param1.nPasos, param1.nCronicas, param1.dtIni, param1.DurPaso_horas);
end;

class function TPostOper_Transponer.tipo: string;
begin
  Result := 'Transponer';
end;

{ TPostOper_AcumCron }

constructor TPostOper_AcumCron.Create(res: TCronVar; aCronVar: TCronVar;
  flgPromediar: boolean; sentido: TSentidoAcumCron);
begin
  inherited Create;
  Self.param1 := aCronVar;
  Self.res := res;
  Self.flgPromediar := flgPromediar;
  Self.sentido := sentido;
end;

procedure TPostOper_AcumCron.Evaluar;
var
  acum: NReal;
  iPaso, jCron, jCronRes: integer;
begin
  case sentido of
    SAC_DeIzquierdaADerecha:
    begin
      for iPaso := 0 to param1.Npasos - 1 do
      begin
        acum := 0.0;
        for jCron := 0 to param1.NCronicas - 1 do
        begin
          acum := acum + param1[iPaso, jCron];
          if flgPromediar then
            res[iPaso, jCron] := acum / (jCron + 1)
          else
            res[iPaso, jCron] := acum;
        end;
      end;
    end;
    SAC_DeDerechaAIzquierda:
    begin
      for iPaso := 0 to param1.Npasos - 1 do
      begin
        acum := 0.0;
        for jCron := param1.NCronicas - 1 downto 0 do
        begin
          acum := acum + param1[iPaso, jCron];
          jCronRes := param1.NCronicas - 1 - jCron;
          if flgPromediar then
            res[iPaso, jCronRes] := acum / (jCronRes + 1)
          else
            res[iPaso, jCronRes] := acum;
        end;
      end;
    end;
  end;
end;

procedure TPostOper_AcumCron.prepararse;
begin
  res.inicializar(param1.nCronicas, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

class function TPostOper_AcumCron.tipo: string;
begin
  Result := 'AcumCron';
end;

function TPostOper_AcumCron.serialize: string;
begin
  Result := inherited serialize + #9 + BoolToStr(Self.flgPromediar) +
    #9 + IntToStr(Ord(sentido));
end;

{ TPostOper_Enventanar }

constructor TPostOper_Enventanar.Create(res: TCronVar; aCronVar: TCronVar;
  pIni, pFin: NReal);
begin

  if (pIni < 0) or (pIni > 1) then
    raise Exception.Create('TPostOper_Enventanar.Create: No se cumple 0<=pIni<=1');

  if (pFin < 0) or (pFin > 1) then
    raise Exception.Create('TPostOper_Enventanar.Create: No se cumple 0<=pFin<=1');

  inherited Create;
  Self.param1 := aCronVar;
  Self.res := res;

  if pIni <= pFin then
  begin
    Self.pIni := pIni;
    Self.pFin := pFin;
  end
  else
  begin
    Self.pIni := pFin;
    Self.pFin := pIni;
  end;

end;



procedure TPostOper_Enventanar.Evaluar;
var
  kPaso, kCron: integer;
begin
  for kPaso := 0 to param1.nPasos - 1 do
    for kCron := kIni to kFin do
      res.v_[kpaso, kCron - kini] := param1.v_[kPaso, kCron];
end;

procedure TPostOper_Enventanar.prepararse;
begin
  Self.kIni := trunc((param1.nCronicas - 1) * pIni + 0.5);
  Self.kFin := trunc((param1.nCronicas - 1) * pFin + 0.5);
  res.inicializar(kFin - kIni + 1, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;


class function TPostOper_Enventanar.tipo: string;
begin
  Result := 'Enventanar';
end;

function TPostOper_Enventanar.serialize: string;
begin
  Result := inherited serialize + #9 + FloatToStr(pIni) + #9 + FloatToStr(pFin);
end;

{ TPostOper_Recronizar }

constructor TPostOper_Recronizar.Create(res: TCronVar; aCronVar: TCronVar;
  NPasosRecronizacion: integer);
begin
  inherited Create;
  Self.param1 := aCronVar;
  Self.res := res;
  Self.NPasosRecronizacion := NPasosRecronizacion;

  Self.NCronRes := param1.nCronicas * Self.NPasosRecronizacion;
  Self.NPasosRes := param1.nPasos div Self.NPasosRecronizacion;
  Self.DurPaso_Horas_Res := param1.DurPaso_horas * Self.NPasosRecronizacion;

end;

procedure TPostOper_Recronizar.Evaluar;
var
  kPasoRes, kCronRes, i, j: integer;
begin
  for kPasoRes := 0 to NPasosRes - 1 do
  begin
    kCronRes := 0;
    for i := 0 to self.NPasosRecronizacion - 1 do
      for j := 0 to param1.nCronicas - 1 do
      begin
        res.SetVal(kPasoRes, kCronRes,
          param1.GetVal(kPasoRes * NPasosRecronizacion + i, j));
        Inc(kCronRes);
      end;
  end;
end;

procedure TPostOper_Recronizar.prepararse;
begin
  res.inicializar(NCronRes, NPasosRes, param1.dtIni, DurPaso_Horas_Res);
end;

class function TPostOper_Recronizar.tipo: string;
begin
  Result := 'Recronizar';
end;

function TPostOper_Recronizar.serialize: string;
begin
  Result := inherited serialize + #9 + IntToStr(Self.NPasosRecronizacion);
end;


{ Métodos de TPostOper_Concatenar }


constructor TPostOper_Concatenar.Create(res: TCronVar; aCronVar: TCronVar);
begin
  inherited Create;
  Self.param1 := aCronVar;
  Self.res := res;

  Self.NCronRes := 1;  // es una cronica sola
  Self.NPasosRes := param1.nPasos * param1.nCronicas;
  // va a contener todas las entradas de la cronVar
  Self.DurPaso_Horas_Res := param1.DurPaso_horas;
  // * Self.NCronicasConcatenacion;   // Por si se quiere cambiar en otro momento

end;

procedure TPostOper_Concatenar.Evaluar;
var
  kPasoRes, kCronRes, i, j: integer;
begin
  kCronRes := 0;
  kPasoRes := 0;
  for j := 0 to param1.nCronicas - 1 do
  begin
    for i := 0 to param1.nPasos - 1 do
    begin
      res.SetVal(kPasoRes, kCronRes,
        param1.GetVal(i, j));
      Inc(kPasoRes);
    end;
  end;
end;

procedure TPostOper_Concatenar.prepararse;
begin
  res.inicializar(NCronRes, NPasosRes, param1.dtIni, DurPaso_Horas_Res);
end;

class function TPostOper_Concatenar.tipo: string;
begin
  Result := 'Concatenar';
end;

function TPostOper_Concatenar.serialize: string;
begin
  Result := inherited serialize + #9 + IntToStr(Self.NPasosRes);
end;

//-------------------------------------
//Métodos de TPostOper_ParalelizarCronVars
//=====================================

constructor TPostOper_ParalelizarCronVars.Create(res: TCronVar;
  acronvars: TDAOfCronVar);
begin
  self.params := acronvars;
  self.res := res;

  Self.NCronRes := params[0].nCronicas;
  Self.NPasosRes := params[0].nPasos * (high(self.params) + 1);
  Self.DurPaso_Horas_Res := params[0].DurPaso_horas;
end;

procedure TPostOper_ParalelizarCronVars.prepararse;
var
  ivar: integer;
begin
  res.inicializar(NCronRes, NPasosRes, params[0].dtIni, DurPaso_Horas_Res);
  for ivar := 1 to high(self.params) do
  begin
    if params[ivar].nPasos <> params[ivar - 1].nPasos then
      raise Exception.Create(
        'TPostOper_ParalelizarCronVars, NO es posible usar cronVar con diferente número de Pasos en esta operación.');
    if params[ivar].nCronicas <> params[ivar - 1].nCronicas then
      raise Exception.Create(
        'TPostOper_ParalelizarCronVars, NO es posible usar cronVar con diferente cantidad de Cronicas en esta operación.');
  end;
end;

// Esta funcion Encadena variables cronicas, manteniendo el orden de las cronicas.
// Se implento para Encadenar los valores horarios de una sala de paso semanal.
// La cronica resultante tiene los valores por hora ordenados cronologicamente para todas las cronicas
// cron1sem1hora1  cron2sem1hora1  ...  cronNsem1hora1
// cron1sem1hora2  cron2sem1hora2  ...  cronNsem1hora2
// ...             ...             ...  ...
// cron1sem1horaM  cron2sem1horaM  ...  cronNsem1horaM
// cron1semShora1  cron2semShora1  ...  cronNsemShora1
// cron1semShora2  cron2semOhora2  ...  cronNsemShora2
// ...             ...             ...  ...
// ...             ...             ...  ...
// ...             ...             ...  ...
// cron1semShoraM  cron2semShoraM  ...  cronNsemShoraM
procedure TPostOper_ParalelizarCronVars.Evaluar;
// A futuro se van a hacer dos funciones para esto
var
  iPaso, jCronica: integer;
  ivar: integer;
  hPasos, hCronicas: integer;
begin
  hPasos := params[0].nPasos - 1;
  hCronicas := params[0].nCronicas - 1;
  for ivar := 0 to high(self.params) do
    for iPaso := 0 to hPasos do
      for jCronica := 0 to hCronicas do
        res[ivar + iPaso * length(params), jCronica] :=
          params[ivar][iPaso, jCronica];
end;

class function TPostOper_ParalelizarCronVars.tipo: string;
begin
  Result := 'paralelizarCronVars';
end;


function TPostOper_ParalelizarCronVars.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfCronVarToStringConTamanio(params);
  // + #9 + TDAOfCronVarToString(params);

end;


//--------------------
//Métodos de TPostOper
//====================

function TPostOper.referenciaCronVar(cronVar: TCronVar): boolean;
begin
  Result := res = cronVar;
end;

function TPostOper.parametrosCronVar: TDAOfCronVar;
var
  res: TDAOfCronVar;
begin
  setlength(res, 0);
  Result := res;
end;

function TPostOper.nombresParametrosCronVar: string;
begin
  Result := '';
end;


function TPostOper.parametrosAdicionales: string;
begin
  Result := '-';
end;

function TPostOper.serialize: string;
begin
  Result := tipo + #9 + encomille(res.nombre);
end;


//-------------------------------
// Métodos de TPosOperUnReal
//===============================
function TPostOperUnReal.serialize: string;
begin

  Result := inherited serialize + #9 + FloatToStr(aReal);
end;


//------------------------------
//Métodos de TPostOperUnaCronVar
//==============================

function TPostOperUnaCronVar.referenciaCronVar(cronVar: TCronVar): boolean;
begin
  Result := inherited referenciaCronVar(cronVar) or (self.param1 = cronVar);
end;

function TPostOperUnaCronVar.parametrosCronVar: TDAOfCronVar;
var
  res: TDAOfCronVar;
begin
  SetLength(res, 1);
  res[0] := param1;
  Result := res;
end;

function TPostOperUnaCronVar.nombresParametrosCronVar: string;
begin
  Result := param1.nombre;
end;



function TPostOperUnaCronVar.serialize: string;
begin
  Result := inherited serialize + #9 + encomille(param1.nombre);
end;


//------------------------------
//Métodos de TPostOperUnaCronVarUnReal
//==============================
function TPostOperUnaCronVarUnReal.serialize: string;
begin
  Result := inherited serialize + #9 + FloatToStr(aReal);
end;


//-------------------------------
//Métodos de TPostOperDosCronVars
//===============================

function TPostOperDosCronVars.referenciaCronVar(cronVar: TCronVar): boolean;
begin
  Result := inherited referenciaCronVar(cronVar) or (self.param2 = cronVar);
end;

function TPostOperDosCronVars.parametrosCronVar: TDAOfCronVar;
var
  res: TDAOfCronVar;
begin
  SetLength(res, 2);
  res[0] := param1;
  res[1] := param2;
  Result := res;
end;

function TPostOperDosCronVars.nombresParametrosCronVar: string;
begin
  Result := inherited nombresParametrosCronVar + ', ' + param2.nombre;
end;

procedure TPostOperDosCronVars.prepararse;
var
  res_Npasos, res_NCronicas: integer;
  res_dtIni: TDateTime;
  res_DurPaso_horas: NReal;
begin
  res_NPasos := max(param1.nPasos, param2.nPasos);
  res_NCronicas := max(param1.nCronicas, param2.nCronicas);
  res_dtIni := max(param1.dtIni, param2.dtIni);
  res_DurPaso_horas := max(param1.DurPaso_horas, param2.DurPaso_horas);
  res.inicializar(res_nCronicas, res_nPasos, res_dtIni, res_DurPaso_horas);
end;

function TPostOperDosCronVars.serialize: string;
begin
  Result := inherited serialize + #9 + encomille(param2.nombre);
end;


//--------------------------------
//Métodos de TPostOperMultiCronVar
//================================

function TPostOperMultiCronVar.referenciaCronVar(cronVar: TCronVar): boolean;
var
  res: boolean;
  i: integer;
begin
  res := inherited referenciaCronVar(cronVar);
  if not res then
  begin
    for i := 0 to high(params) do
      if cronVar = params[i] then
      begin
        res := True;
        break;
      end;
  end;
  Result := res;
end;

function TPostOperMultiCronVar.parametrosCronVar: TDAOfCronVar;
begin
  Result := params;
end;

procedure TPostOperMultiCronVar.prepararse;
var
  res_Npasos, res_NCronicas: integer;
  res_dtIni: TDateTime;
  res_DurPaso_horas: NReal;
  aCronVar: TCronVar;
  ivar: integer;
begin
  res_NPasos := 0;
  res_NCronicas := 0;
  res_dtIni := 0;
  res_DurPaso_horas := 0;
  for ivar := 0 to high(self.params) do
  begin
    aCronVar := params[ivar];
    res_NPasos := max(res_NPasos, aCronVar.nPasos);
    res_NCronicas := max(res_NCronicas, aCronVar.nCronicas);
    res_dtIni := max(res_dtIni, aCronVar.dtIni);
    res_DurPaso_horas := max(res_DurPaso_horas, aCronVar.DurPaso_horas);
  end;
  res.inicializar(res_nCronicas, res_nPasos, res_dtIni, res_DurPaso_horas);
end;


function TPostOperMultiCronVar.nombresParametrosCronVar: string;
var
  res: string;
  i: integer;
begin
  res := '';
  for i := 0 to high(params) - 1 do
    res := res + params[i].nombre + ', ';
  res := res + params[high(params)].nombre;
  Result := res;
end;


//-----------------------------------------
//Métodos de TPostOper_minEntreCronVarYReal
//=========================================
constructor TPostOper_CrearConstanteReal.Create(res: TCronVar; aReal: NReal);
begin
  inherited Create;
  self.res := res;
  self.aReal := aReal;
end;

procedure TPostOper_CrearConstanteReal.prepararse;
begin
  res.inicializar(1, 1, 0, 0);
end;

procedure TPostOper_CrearConstanteReal.Evaluar;
begin
  res[0, 0] := aReal;
end;

class function TPostOper_CrearConstanteReal.tipo: string;
begin
  Result := 'CrearConstateReal';
end;


function TPostOper_CrearConstanteReal.parametrosAdicionales: string;
begin
  Result := 'aReal= ' + FloatToStrF(aReal, ffGeneral, 16, 10);
end;


//-----------------------------------------
//Métodos de TPostOper_minEntreCronVarYReal
//=========================================

constructor TPostOper_minEntreCronVarYReal.Create(res, param1: TCronVar; aReal: NReal);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.aReal := aReal;
end;

procedure TPostOper_minEntreCronVarYReal.prepararse;
begin
  res.inicializar(param1.nCronicas, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_minEntreCronVarYReal.Evaluar;
var
  i, j: integer;
  filaResi, filaParam1i: TDAofNReal;
begin
  for i := 0 to res.nPasos - 1 do
  begin
    filaResi := res.v_[i];
    filaParam1i := param1.v_[i];
    for j := 0 to high(filaResi) do
      if aReal < filaParam1i[j] then
        filaResi[j] := aReal
      else
        filaResi[j] := filaParam1i[j];
  end;
end;

class function TPostOper_minEntreCronVarYReal.tipo: string;
begin
  Result := 'minEntreCronVarYReal';
end;

function TPostOper_minEntreCronVarYReal.parametrosAdicionales: string;
begin
  Result := 'real= ' + FloatToStrF(aReal, ffGeneral, 16, 10);
end;


//-----------------------------------------
//Métodos de TPostOper_maxEntreCronVarYReal
//=========================================

constructor TPostOper_maxEntreCronVarYReal.Create(res, param1: TCronVar; aReal: NReal);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.aReal := aReal;
end;

procedure TPostOper_maxEntreCronVarYReal.prepararse;
begin
  res.inicializar(param1.nCronicas, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_maxEntreCronVarYReal.Evaluar;
var
  i, j: integer;
  filaResi, filaParam1i: TDAofNReal;
begin
  for i := 0 to res.nPasos - 1 do
  begin
    filaResi := res.v_[i];
    filaParam1i := param1.v_[i];
    for j := 0 to high(filaResi) do
      if aReal > filaParam1i[j] then
        filaResi[j] := aReal
      else
        filaResi[j] := filaParam1i[j];
  end;
end;

class function TPostOper_maxEntreCronVarYReal.tipo: string;
begin
  Result := 'maxEntreCronVarYReal';
end;

function TPostOper_maxEntreCronVarYReal.parametrosAdicionales: string;
begin
  Result := 'aReal= ' + FloatToStrF(aReal, ffGeneral, 16, 10);
end;


//-----------------------------------
//Métodos de TPostOper_CronVarMasReal
//===================================

constructor TPostOper_CronVarMasReal.Create(res, param1: TCronVar; aReal: NReal);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.aReal := aReal;
end;

procedure TPostOper_CronVarMasReal.prepararse;
begin
  res.inicializar(param1.nCronicas, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_CronVarMasReal.Evaluar;
var
  i, j: integer;
  filaResi, filaParam1i: TDAofNReal;
begin
  for i := 0 to high(res.v_) do
  begin
    filaResi := res.v_[i];
    filaParam1i := param1.v_[i];
    for j := 0 to high(filaResi) do
      filaResi[j] := filaParam1i[j] + aReal;
  end;
end;


class function TPostOper_CronVarMasReal.tipo: string;
begin
  Result := 'cronVarMasReal';
end;

function TPostOper_CronVarMasReal.parametrosAdicionales: string;
begin
  Result := 'aReal= ' + FloatToStrF(aReal, ffGeneral, 16, 10);
end;


//-----------------------------------
//Métodos de TPostOper_CronVarPorReal
//===================================

constructor TPostOper_CronVarPorReal.Create(res, param1: TCronVar; aReal: NReal);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.aReal := aReal;
end;

procedure TPostOper_CronVarPorReal.prepararse;
begin
  res.inicializar(param1.nCronicas, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_CronVarPorReal.Evaluar;
var
  i, j: integer;
  filaResi, filaParam1i: TDAofNReal;
begin
  for i := 0 to high(res.v_) do
  begin
    filaResi := res.v_[i];
    filaParam1i := param1.v_[i];
    for j := 0 to high(filaResi) do
      filaResi[j] := filaParam1i[j] * aReal;
  end;
end;


class function TPostOper_CronVarPorReal.tipo: string;
begin
  Result := 'cronVarPorReal';
end;

function TPostOper_CronVarPorReal.parametrosAdicionales: string;
begin
  Result := 'aReal= ' + FloatToStrF(aReal, ffGeneral, 16, 10);
end;


//----------------------------------
//Métodos de TPostOper_RestaCronVars
//==================================

constructor TPostOper_RestaCronVars.Create(res, param1, param2: TCronVar);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.param2 := param2;
end;


procedure TPostOper_RestaCronVars.Evaluar;
var
  i, j: integer;
begin
  for i := 0 to res.nPasos - 1 do
    for j := 0 to res.NCronicas - 1 do
      res[i, j] := Param1[i, j] - Param2[i, j];
end;

class function TPostOper_RestaCronVars.tipo: string;
begin
  Result := 'restaCronVars';
end;

//-------------------------------------
//Métodos de TPostOper_combinarCronVars
//=====================================

constructor TPostOper_combinarCronVars.Create(res: TCronVar;
  acronvars: TDAOfCronVar; coeficientes: TDAOfNReal);
begin
  self.params := acronvars;
  self.coeficientes := coeficientes;
  self.res := res;
end;

procedure TPostOper_combinarCronVars.Evaluar;
var
  iPaso, jCronica: integer;
  ivar: integer;
  a: NReal;
  hPasos, hCronicas: integer;
begin
  hPasos := res.nPasos - 1;
  hCronicas := res.nCronicas - 1;

  for iPaso := 0 to hPasos do
  begin
    for jCronica := 0 to hCronicas do
    begin
      a := 0;
      for ivar := 0 to high(self.params) do
        a := a + params[ivar][iPaso, jCronica] * coeficientes[ivar];
      res[iPaso, jCronica] := a;
    end;
  end;
end;

class function TPostOper_combinarCronVars.tipo: string;
begin
  Result := 'combinarCronVars';
end;

function TPostOper_combinarCronVars.parametrosAdicionales: string;
begin
  Result := 'coeficientes= ' + TDAOfNRealToStringSinTamanio(coeficientes, 16, 10, ', ');
end;


function TPostOper_combinarCronVars.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfNRealToTabbedStringConTamanio(
    coeficientes, 8, 3) + #9 + TDAOfCronVarToString(params);

end;

//-----------------------------------------
//Métodos de TPostOper_combinarDespCronVars
//=========================================

constructor TPostOper_combinarDespCronVars.Create(res: TCronVar;
  acronvars: TDAOfCronVar; coeficientes: TDAOfNReal; desplazamientos: TDAOfNInt);
begin
  self.params := acronvars;
  self.coeficientes := coeficientes;
  self.desplazamientos := desplazamientos;
  self.res := res;
end;


procedure TPostOper_combinarDespCronVars.Evaluar;
var
  iPaso, jCronica, iPasoDesp: integer;
  ivar: integer;
  a: NReal;
  hPasos, hCronicas: integer;
begin
  hPasos := res.nPasos - 1;    // cantidad de pasos de tiempo
  hCronicas := res.nCronicas - 1; // cantidad de crónicas
  for iPaso := 0 to hPasos do
  begin
    for jCronica := 0 to hCronicas do
    begin
      a := 0;
      for ivar := 0 to high(self.params) do
      begin
        iPasoDesp := iPaso + desplazamientos[ivar];
        if (iPasoDesp >= 0) and (iPasoDesp <= hPasos) then
          a := a + params[ivar][iPasoDesp, jCronica] * coeficientes[ivar];
      end;
      res[iPaso, jCronica] := a;
    end;
  end;
end;

class function TPostOper_combinarDespCronVars.tipo: string;
begin
  Result := 'combinarDespCronVars';
end;

function TPostOper_combinarDespCronVars.parametrosAdicionales: string;
begin
  Result := 'coeficientes= ' + TDAOfNRealToStringSinTamanio(coeficientes, 16, 10, ', ') +
    ', desplazamientos= ' + TDAOfNIntToStringSinTamanio(desplazamientos, ', ');
end;

function TPostOper_combinarDespCronVars.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfNRealToTabbedStringConTamanio(
    coeficientes, 8, 3) + #9 + TDAOfNIntToTabbedString(desplazamientos) +
    #9 + TDAOfCronVarToString(params);
end;

//----------------------------------------
//Métodos de TPostOper_MonotonizarCronVars
//========================================

constructor TPostOper_MonotonizarCronVars.Create(res: TCronVar;
  acronvars: TDAOfCronVar; NPasosDelCajon: integer; flg_Decreciente: boolean);
begin
  self.params := acronvars; // estas son las Monotonizadas
  self.res := res; // esta es la Monotonizante
  self.NPasosDelCajon := NPasosDelCajon;
  self.flg_Decreciente := flg_Decreciente;

end;

procedure TPostOper_MonotonizarCronVars.Evaluar;
var
  nTramos: integer;
  iTramo: integer;
  iPaso: integer;
  ivar: integer;
  ar: TDAOfNReal;
  indx: TDAOfNint;
  acv: TCronVar;
begin
  nTramos := res.nPasos div NPasosDelCajon;
  setlength(ar, NPasosDelCajon);
  setlength(indx, NPasosDelCajon);
  for iCronica := 0 to res.nCronicas - 1 do
  begin
    for iTramo := 0 to nTramos - 1 do
    begin
      // copiamos el tramo de la Monotizante
      // e inicializamos el indx
      for iPaso := 0 to NPasosDelCajon - 1 do
      begin
        ar[iPaso] := res[iTramo * NPasosDelCajon + iPaso, iCronica];
        indx[iPaso] := iPaso;
      end;

      // creamos el orden
      if flg_Decreciente then
        QuickSort_Decreciente(ar, indx)
      else
        QuickSort_Creciente(ar, indx);

      // Guardamos el tramo ordenado en la Monotizante
      for iPaso := 0 to NPasosDelCajon - 1 do
        res[iTramo * NPasosDelCajon + iPaso, iCronica] := ar[iPaso];

      // Imponemos el mismo orden al resto de las CronVars.
      for ivar := 0 to high(self.params) do
      begin
        acv := params[ivar];
        // Copiamos los valores en el orden original
        for iPaso := 0 to NPasosDelCajon - 1 do
          ar[iPaso] := acv[iTramo * NPasosDelCajon + iPaso, iCronica];

        // Guardamos los valores en el orden impuesto por la Monotonizante
        for iPaso := 0 to NPasosDelCajon - 1 do
          acv[iTramo * NPasosDelCajon + iPaso, iCronica] := ar[indx[iPaso]];
      end;
    end;
  end;
end;



class function TPostOper_MonotonizarCronVars.tipo: string;
begin
  Result := 'monotonizar';
end;

function TPostOper_MonotonizarCronVars.parametrosAdicionales: string;
var
  res: string;
begin
  res := TDAOfCronVarToString(params) + ', ' + 'NPasos= ' + IntToStr(NPasosDelCajon);
  if flg_Decreciente then
    Result := res + ', DEC'
  else
    Result := res + ', INC';
end;

function TPostOper_MonotonizarCronVars.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfCronVarToStringConTamanio(params) +
    #9 + IntToStr(NPasosDelCajon) + #9 + BoolToStr(flg_Decreciente);
end;




//----------------------------------------
//Métodos de TPostOper_aplicarActualizador
//========================================

constructor TPostOper_aplicarActualizador.Create(res, param1: TCronVar; q: NReal);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.aReal := q;
end;

procedure TPostOper_aplicarActualizador.prepararse;
begin
  res.inicializar(param1.nCronicas, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_aplicarActualizador.Evaluar;
var
  iPaso, jCronica: integer;
  qa: NReal;
  hPasos, hCronicas: integer;
begin
  hPasos := res.nPasos - 1;
  hCronicas := res.nCronicas - 1;
  qa := 1;
  for iPaso := 0 to hPasos do
  begin
    for jCronica := 0 to hCronicas do
      res[iPaso, jCronica] := qa * param1[iPaso, jCronica];
    qa := qa * aReal;
  end;
end;

class function TPostOper_aplicarActualizador.tipo: string;
begin
  Result := 'aplicarActualizador';
end;

function TPostOper_aplicarActualizador.parametrosAdicionales: string;
begin
  Result := 'q= ' + FloatToStrF(aReal, ffGeneral, 16, 10);
end;


//-------------------------------------------
//Métodos de TPostOper_multiplicacionCronVars
//===========================================

constructor TPostOper_multiplicacionCronVars.Create(res, param1, param2: TCronVar);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  Self.param2 := param2;
end;


procedure TPostOper_multiplicacionCronVars.Evaluar;
var
  i, j: integer;
begin
  for i := 0 to res.nPasos - 1 do
    for j := 0 to res.nCronicas - 1 do
      res[i, j] := Param1[i, j] * Param2[i, j];
end;


class function TPostOper_multiplicacionCronVars.tipo: string;
begin
  Result := 'multiplicacionCronVars';
end;




//-------------------------------------
//Métodos de TPostOper_DivisionCronVars
//=====================================

constructor TPostOper_DivisionCronVars.Create(res, param1, param2: TCronVar);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.param2 := param2;
end;


procedure TPostOper_DivisionCronVars.Evaluar;
var
  i, j: integer;
  p, q: NReal;
begin
  for i := 0 to res.nPasos - 1 do
  begin
    for j := 0 to res.nCronicas - 1 do
    begin
      p := Param1[i, j];
      q := Param2[i, j];
      if abs(q) <= AsumaCero then
      begin
        if abs(p) <= AsumaCero then
          res[i, j] := 0
        else
          raise Exception.Create('PosOper_DivisionCronVar dividiendo por CERO!');
      end
      else
        res[i, j] := p / q;
    end;
  end;
end;

class function TPostOper_DivisionCronVars.tipo: string;
begin
  Result := 'divisionCronVars';
end;


//---------------------------------------
//Métodos de TPostOper_cambioPasoDeTiempo
//=======================================

constructor TPostOper_cambioPasoDeTiempo.Create(res, param1: TCronVar;
  horasPasoNuevo: NReal; xTipoCPT: TTipoCPT; x_jesimaEntrada: integer);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.horasPasoNuevo := horasPasoNuevo;
  self.tipoCPT := xTipoCPT;
  self.jesimaEntrada := x_jesimaEntrada;
end;

procedure TPostOper_cambioPasoDeTiempo.prepararse;
var
  nPasosRes: integer;
begin
  if res = param1 then
    raise Exception.Create(
      'TPostOper_cambioPasoDeTiempo, NO es posible usar la misma cronVar a la salida y a la entrada de esta operación.'
      );

  nPasosRes := trunc((param1.DurPaso_horas / horasPasoNuevo) * param1.nPasos);
  if nPasosRes <= 0 then
    raise Exception.Create(
      'TPostOper_cambioPasoDeTiempo, el resultado tendría CERO datos. Revise los parámetros.'
      );
  res.inicializar(param1.nCronicas, nPasosRes, param1.dtIni, horasPasoNuevo);
end;


procedure TPostOper_cambioPasoDeTiempo.Evaluar;
var
  j1, j2: integer;
  jr1, jr2: NReal;
  iCronica, kPaso: integer;
  filaKRes, filaK2Res, filaKParam1: TDAofNReal;
  flg: TDAOfBoolean;
  h1sobreh2: NReal;
  h2sobreh1: NReal;
  alfa, beta: NReal;
  jCol: integer;



  procedure calcularAlfa;
  var
    AsumaCero: NReal;
  begin
    // jr1 y jr2 son el inicio y el final del paso dentro del paso nuevo
    jr1 := kPaso * h1sobreh2;
    jr2 := (kPaso + 1) * h1sobreh2;

    // j1 indice en el que inicia y j2 indice en el que finaliza

    // si j1=j2 el paso de tiempo entra completo en el nuevo paso
    // si j1<j2 el paso no esta incluido en un solo paso nuevo
    // BOLAZO! si j1>j2 el beta del paso de tiempo sobra, para hacer el cálculo se simula que j2:=j1+1.

    j1 := Trunc(jr1);
    j2 := Trunc(jr2);

    Assert(j1 <= j2,
      'OJO, esto no puede ser TPostOper_CambioPasoDeTiempo.Evaluar calcularAlfa j1 > j2 ');
    if j1 = j2 then
      alfa := 1
    else
      alfa := (j2 - jr1) * h2sobreh1;

    beta := 1 - alfa;

    AsumaCero := 1e-10;
    if not ((alfa >= -AsumaCero) and (alfa <= (1 + AsumaCero)) and
      (beta >= -AsumaCero) and (beta <= (1 + AsumaCero))) then
      raise Exception.Create(
        'TPostOper_cambioPasoDeTiempo.Evaluar->Error, alfa o beta fuera de rango');
  end;

begin
  if tipoCPT in [TCPT_Promedio, TCPT_Suma] then
  begin
    Evaluar_PromSum;
    exit;
  end;

  h1sobreh2 := param1.DurPaso_horas / horasPasoNuevo;
  h2sobreh1 := horasPasoNuevo / param1.DurPaso_horas;

  SetLength(flg, Length(res.v_));

  // borramos el resultado
  for kPaso := 0 to res.nPasos - 1 do
  begin
    filaKRes := res.v_[kpaso];
    for iCronica := 0 to res.nCronicas - 1 do
      filaKRes[iCronica] := 0;

    // Flag de control para las operaciones
    flg[kPaso] := False;
  end;

  if horasPasoNuevo >= param1.DurPaso_horas then
  begin  // aumentando la duración del paso
    jCol := 0;

    // recorremos los casilleros de la CronVar de entrada
    for kPaso := 0 to param1.nPasos - 1 do
    begin

      // el casillero kPaso se distribuye en los casilleros
      // del resultado que van de j1 hasta j2 inclusive

      //Obtengo el kPaso de tiempo
      filaKParam1 := param1.v_[kPaso];

      calcularAlfa;


      // yo pa mi que esto no puede pasar
      if j1 > high(res.v_) then
        break; // No hay info suficiente para completar un paso

      filaKRes := res.v_[j1];

      case tipoCPT of

        TCPT_I_esimo:
        begin
          if alfa > 1e-10 then
          begin
            if (jCol = jesimaEntrada) then
            begin
              for iCronica := 0 to param1.nCronicas - 1 do
                filaKRes[iCronica] := filaKParam1[iCronica];
              flg[j1] := True;
            end;
            jCol := jCol + 1;
          end
          else
            jCol := 0;
        end;

        TCPT_Maximo:
        begin
          // j1=j2 el bloque pertenecerá al mismo bloque de tiempo y
          // flg[j1]=False indica que no fue asignado o sea que es el primero
          if (alfa >= 0.001) then
          begin
            for iCronica := 0 to param1.nCronicas - 1 do
              if (flg[j1] = False) or
                (filaKParam1[iCronica] > filaKRes[iCronica]) then
                filaKRes[iCronica] := filaKParam1[iCronica];
            flg[j1] := True;
          end;

          if (beta >= 0.001) and (j2 < length(res.v_)) then
          begin
            filaK2Res := res.v_[j2];
            for iCronica := 0 to param1.nCronicas - 1 do
              filaK2Res[iCronica] := filaKParam1[iCronica];
            flg[j2] := True;
          end;
        end;

        TCPT_Minimo:
        begin
          // j1=j2 el bloque pertenecerá al mismo bloque de tiempo y
          // flg[j1]=False indica que no fue asignado o sea que es el primero
          if (alfa >= 0.001) then
          begin
            for iCronica := 0 to param1.nCronicas - 1 do
              if (flg[j1] = False) or (filaKParam1[iCronica] < filaKRes[iCronica]) then
                filaKRes[iCronica] := filaKParam1[iCronica];
            flg[j1] := True;
          end;

          if (beta >= 0.001) and (j2 < length(res.v_)) then
          begin
            filaK2Res := res.v_[j2];
            for iCronica := 0 to param1.nCronicas - 1 do
              filaK2Res[iCronica] := filaKParam1[iCronica];
            flg[j2] := True;
          end;
        end
      end;
    end;
  end
  else
  begin
    raise Exception.Create(
      'CambioPasoDeTiempo ...las operaciones Primero, Ultimo, Minimo y Maximo no adminten reducir el paso en la implementación actual.'
      );
  end;
end;



procedure TPostOper_cambioPasoDeTiempo.Evaluar_PromSum;
var
  j1, j2: integer;
  jr1, jr2: NReal;
  iCronica, kPaso: integer;
  filaKRes, filaK2Res, filaKParam1: TDAofNReal;
  h1sobreh2: NReal;
  h2sobreh1: NReal;
  alfa, beta: NReal;
  m_Resto, m_Aporte, m_Aporte_fijo: NReal;
  j, nFijos: integer;

begin
  h1sobreh2 := param1.DurPaso_horas / horasPasoNuevo;
  h2sobreh1 := horasPasoNuevo / param1.DurPaso_horas;

  // borramos el resultado
  for kPaso := 0 to res.nPasos - 1 do
  begin
    filaKRes := res.v_[kpaso];
    for iCronica := 0 to res.nCronicas - 1 do
      filaKRes[iCronica] := 0;
  end;

  if horasPasoNuevo >= param1.DurPaso_horas then
  begin  // aumentando la duración del paso
    // recorremos los casilleros de la CronVar de entrada
    for kPaso := 0 to param1.nPasos - 1 do
    begin
      jr1 := kPaso * h1sobreh2;
      jr2 := (kPaso + 1) * h1sobreh2;
      j1 := Trunc(jr1);
      j2 := Trunc(jr2);

      // el casillero kPaso se distribuye en los casilleros
      // del resultado que van de j1 hasta j2 inclusive
      filaKParam1 := param1.v_[kPaso];
      if j1 > high(res.v_) then
        break;
      filaKRes := res.v_[j1];
      if j1 = j2 then
      begin
        for iCronica := 0 to param1.nCronicas - 1 do
          filaKRes[iCronica] := filaKRes[iCronica] + filaKParam1[iCronica];
      end
      else
      begin
        alfa := (j2 - jr1) * h2sobreh1;
        beta := 1 - alfa; //(jr2 - j2) * h2sobreh1;
        AsumaCero := 1e-10;
        if not ((alfa >= -AsumaCero) and (alfa <= (1 + AsumaCero)) and
          (beta >= -AsumaCero) and (beta <= (1 + AsumaCero))) then
          raise Exception.Create(
            'TPostOper_cambioPasoDeTiempo.Evaluar->Error, alfa o beta fuera de rango');
        if (j2 <= high(res.v_)) then
        begin
          //Cuando entre por aca y termine el for la fila j1 del resultado va a estar
          //terminada de calcular
          filaK2Res := res.v_[j2];
          for iCronica := 0 to param1.nCronicas - 1 do
          begin
            filaKRes[iCronica] := filaKRes[iCronica] + filaKParam1[iCronica] * alfa;
            filaK2Res[iCronica] := filaK2Res[iCronica] + filaKParam1[iCronica] * beta;
          end;
        end
        else
          for iCronica := 0 to param1.nCronicas - 1 do
            filaKRes[iCronica] := filaKRes[iCronica] + filaKParam1[iCronica] * alfa;
      end;
    end;
  end
  else
  begin
    // reduciendo la duración del paso
    // h1 > h2
    // rch@201411250753
    // agrego esta posibilidad en al v_ADME_105

    for kPaso := 0 to param1.nPasos - 1 do
    begin
      jr1 := kPaso * h1sobreh2;
      jr2 := (kPaso + 1) * h1sobreh2;
      j1 := Trunc(jr1);
      j2 := Trunc(jr2);

      filaKParam1 := param1.v_[kPaso];
      if j1 > high(res.v_) then
        break;
      filaKRes := res.v_[j1];

      // Como h1 > h2, jr2 > jr1 +1 pues h1sobreh2 > 1
      alfa := (j1 + 1 - jr1);
      beta := (jr2 - j2);

      m_Resto := filaKParam1[iCronica];
      m_Aporte_fijo := m_Resto * h2sobreh1;

      m_Aporte := alfa * m_Aporte_fijo;
      for iCronica := 0 to param1.nCronicas - 1 do
        filaKRes[iCronica] := filaKRes[iCronica] + m_Aporte;
      m_Resto := m_Resto - m_Aporte;


      if j1 = j2 then
        raise Exception.Create(
          'Error j1=j2 en cambioPasoDeTiempo con h2 < h1 NO PUEDE SER ');

      nFijos := j2 - j1 - 1;
      if nFijos > 0 then
      begin
        for j := j1 + 1 to j2 - 1 do
        begin
          filaKRes := res.v_[j];
          for iCronica := 0 to param1.nCronicas - 1 do
            filaKRes[iCronica] := filaKRes[iCronica] + m_Aporte_fijo;
        end;
        m_Resto := m_Resto - nFijos * m_Aporte_fijo;
      end;
      if (j2 > j1) and (abs(beta) > AsumaCero) then
      begin
        filaKRes := res.v_[j2];
        m_Aporte := beta * m_Aporte_fijo;
        for iCronica := 0 to param1.nCronicas - 1 do
          filaKRes[iCronica] := filaKRes[iCronica] + m_Aporte;
        m_Resto := m_Resto - m_Aporte;
      end;
    end;
  end;

  if tipoCPT = TCPT_Promedio then
  begin
    for kPaso := 0 to res.nPasos - 1 do
    begin
      filaKRes := res.v_[kPaso];
      for iCronica := 0 to res.nCronicas - 1 do
        filaKRes[iCronica] := filaKRes[iCronica] * h1sobreh2;
    end;
  end;
end;



class function TPostOper_cambioPasoDeTiempo.tipo: string;
begin
  Result := 'cambioPasoDeTiempo';
end;

function TPostOper_cambioPasoDeTiempo.parametrosAdicionales: string;
begin
  Result := 'horasPasoNuevo= ' + FloatToStrF(horasPasoNuevo, ffGeneral, 16, 10) +
    ', tipoCPT= ' + IntToStr(Ord(tipoCPT));
  if tipoCPT = TCPT_I_esimo then
    Result := Result + ', iesimaEntrada= ' + IntToStr(jesimaEntrada);
end;


function TPostOper_cambioPasoDeTiempo.serialize: string;
begin
  Result := inherited serialize + #9 + FloatToStr(horasPasoNuevo) +
    #9 + IntToStr(Ord(tipoCPT)) + #9 + IntToStr(jesimaEntrada);
end;




//---------------------------------------
//Métodos de TPostOper_CVaR
//=======================================

constructor TPostOper_CVaR.Create(res, param1: TCronVar; p1, p2: NReal;
  PreOrdenar: integer);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.p1 := p1;
  self.p2 := p2;
  self.PreOrdenar := PreOrdenar;
end;

procedure TPostOper_CVaR.prepararse;
begin
  res.inicializar(1, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_CVaR.Evaluar;
var
  j1, j2, njs: integer;
  kPaso, j: integer;
  ma: NReal;
  filakParam1: TDAOfNReal;
begin

  j1 := trunc((param1.nCronicas - 1) * p1 + 0.5);
  if j1 < 0 then
    j1 := 0
  else if j1 > (param1.nCronicas - 1) then
    j1 := param1.nCronicas - 1;

  j2 := trunc((param1.nCronicas - 1) * p2 + 0.5);
  if j2 < 0 then
    j2 := 0
  else if j2 > (param1.nCronicas - 1) then
    j2 := param1.nCronicas - 1;

  if j2 < j1 then
  begin
    j := j1;
    j1 := j2;
    j2 := j;
  end;

  njs := j2 - j1 + 1;

  if PreOrdenar <> 0 then
    setlength(filakParam1, param1.nCronicas);

  for kPaso := 0 to high(res.v_) do
  begin
    if PreOrdenar <> 0 then
    begin
      vcopy(filakParam1, param1.v_[kPaso]);
      if PreOrdenar > 0 then
        QuickSort_Creciente(filakParam1)
      else
        QuickSort_Decreciente(filaKParam1);
    end
    else
      filaKParam1 := param1.v_[kPaso];

    ma := 0;
    for iCronica := j1 to j2 do
      ma := ma + filaKParam1[iCronica];

    res.v_[kPaso, 0] := ma / njs;
  end;

  if PreOrdenar <> 0 then
    setlength(filakParam1, 0);

end;

class function TPostOper_CVaR.tipo: string;
begin
  Result := 'CVaR';
end;

function TPostOper_CVaR.parametrosAdicionales: string;
begin
  Result := 'p1:' + FloatToStr(p1) + ', p2: ' + FloatToStr(p2) +
    ', preOrdenar:' + IntToStr(preOrdenar);
end;


function TPostOper_CVaR.serialize: string;
begin
  Result := inherited serialize + #9 + FloatToStr(p1) + #9 + FloatToStr(
    p2) + #9 + IntToStr(preOrdenar);
end;



//------------------------------------
//Métodos de TPostOper_acumularCronVar
//====================================

constructor TPostOper_acumularCronVar.Create(res, param1: TCronVar);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
end;

procedure TPostOper_acumularCronVar.prepararse;
begin
  res.inicializar(param1.nCronicas, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_acumularCronVar.Evaluar;
var
  i, j: integer;
  filaIRes, filaIParam1: TDAofNReal;
  acums: TDAOfNReal;
begin
  filaIRes := res.v_[0];
  filaIParam1 := param1.v_[0];

  setlength(acums, param1.nCronicas);

  for j := 0 to param1.nCronicas - 1 do
  begin
    acums[j] := filaIParam1[j];
    filaIRes[j] := acums[j];
  end;

  for i := 1 to param1.nPasos - 1 do
  begin
    filaIRes := res.v_[i];
    filaIParam1 := param1.v_[i];
    for j := 0 to param1.nCronicas - 1 do
    begin
      acums[j] := acums[j] + filaIParam1[j];
      filaIRes[j] := acums[j];
    end;
  end;

  setlength(acums, 0);
end;

class function TPostOper_acumularCronVar.tipo: string;
begin
  Result := 'acumularCronVar';
end;




//------------------------------------
//Métodos de TPostOper_acumularConPisoYTecho
//====================================

procedure TPostOper_acumularConPisoYTecho.SetParametros(res, ingreso,
  Piso, Techo: TCronVar; ValIni: NReal);
begin
  self.res := res;
  params[0] := Ingreso;
  params[1] := Piso;
  params[2] := Techo;
  Self.Ingreso := Ingreso;
  Self.Techo := Techo;
  self.Piso := Piso;
  self.ValIni := ValIni;
end;

constructor TPostOper_acumularConPisoYTecho.Create(res, ingreso, Piso, Techo: TCronVar;
  ValIni: NReal);
begin
  inherited Create;
  setlength(params, 3);
  SetParametros(res, ingreso, Piso, Techo, ValIni);
end;



// Función auxiliar.
// Calcula VSig como Vant+Aporte Neto controlando que no pase
// de Techo y Piso, pero si se pasa intenta corregir con el aporte.
// Si Vant > Techo no se admiten Aportes Positivos.
// Si Vant < Piso no se adminte Aportes Negativos.

function VSig(Vant: NReal; var AporteNeto: NReal; Piso, Techo: NReal): NReal;
var
  Vs: NReal;
  diff: NREal;
begin
  if (VAnt >= Techo) and (AporteNeto >= 0) then
  begin
    AporteNeto := 0;
    Result := Vant;
    exit;
  end;

  if (VAnt <= Piso) and (AporteNeto <= 0) then
  begin
    AporteNeto := 0;
    Result := Vant;
    exit;
  end;


  Vs := Vant + AporteNeto;
  if Vs > Techo then
  begin
    if AporteNeto > 0 then
    begin
      diff := Vs - Techo;
      if AporteNeto >= diff then
      begin
        AporteNeto := AporteNeto - diff;
        Vs := Techo;
      end
      else
      begin
        AporteNeto := 0;
        Vs := VAnt;
      end;
    end;
  end
  else if Vs < Piso then
  begin
    if AporteNeto < 0 then
    begin
      diff := Vs - Piso;
      if AporteNeto <= diff then
      begin
        AporteNeto := AporteNeto - diff;
        Vs := Piso;
      end
      else
      begin
        AporteNeto := 0;
        Vs := Vant;
      end;
    end;
  end;

  Result := Vs;
end;


procedure TPostOper_acumularConPisoYTecho.Evaluar;
var
  i, j: integer;
  aIngresoNeto: NReal;
begin
  for j := 0 to res.nCronicas - 1 do
  begin
    aIngresoNeto := Ingreso[0, j];
    res[0, j] := VSig(ValIni, aIngresoNeto, Piso[0, j], Techo[0, j]);
    Ingreso[0, j] := aIngresoNeto;
  end;

  for i := 1 to res.nPasos - 1 do
    for j := 0 to res.nCronicas - 1 do
    begin
      aIngresoNeto := Ingreso[i, j];
      res[i, j] := VSig(res[i - 1, j], aIngresoNeto, Piso[i, j], Techo[i, j]);
      Ingreso[i, j] := aIngresoNeto;
    end;
end;

class function TPostOper_acumularConPisoYTecho.tipo: string;
begin
  Result := 'acumularConPisoYTecho';
end;

function TPostOper_acumularConPisoYTecho.parametrosAdicionales: string;
begin
  Result := 'ValIni:' + FloatToStr(ValIni);
end;


function TPostOper_acumularConPisoYTecho.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfCronVarToString(params) +
    #9 + FloatToStr(ValIni);
end;


//--------------------------------------------
//Métodos de TPostOper_potenciaFirmeHidraulica
//============================================

constructor TPostOper_potenciaFirmeHidraulica.Create(res: TCronVar;
  params: TDAOfCronVar);
begin
  inherited Create;
  self.res := res;
  self.params := params;
end;

procedure TPostOper_potenciaFirmeHidraulica.prepararse;
begin
  //se asume que todos los parametros tienen la misma cantidad de pasos y crónicas,
  //fecha inicial y tiempo entre los pasos
  //El resultado tendra una columna con la potencia firme reconocida de cada generador
  //pasado como parametro y una mas con la suma de las potencias firmes reconocidas
  res.inicializar(
    length(params) + 1,
    params[0].nPasos, params[0].dtIni, params[0].DurPaso_horas);
end;

procedure TPostOper_potenciaFirmeHidraulica.Evaluar;
var
  iPaso, iCentral: integer;
  sumaPots, error, minError: NReal;

  auxcv: TCronVar;
  PSum: TDAOfNReal;
  auxsum: NReal;
  iCron: integer;
  j95: integer;
  jMinError: integer;
  P95Sum: NReal;
  NCentrales: integer;
  nCronicas: integer;
begin
  auxcv := params[0];
  NCentrales := length(params);

  (*
  PSum := TCronVar.Create('PSum');
  PSum.inicializar(auxcv.nCronicas, auxcv.nPasos, auxcv.dtIni, auxcv.DurPaso_horas);
    *)
  nCronicas := auxcv.nCronicas;
  setlength(PSum, nCronicas);

  for iPaso := 0 to params[0].nPasos - 1 do
  begin
    // Cargamos en PSum la suma de las potencias de las centrales.
    for iCron := 0 to nCronicas - 1 do
    begin
      auxsum := 0;
      for iCentral := 0 to NCentrales - 1 do
      begin
        auxcv := params[iCentral];
        auxsum := auxsum + auxcv.v_[iPaso, iCron];
      end;
      PSum[iCron] := auxsum;
    end;

    // ahora ordenamos las potencias de cada central en forma decreciente
    for iCentral := 0 to NCentrales - 1 do
    begin
      auxcv := params[iCentral];
      QuickSort_Decreciente(auxcv.v_[iPaso]);
    end;
    // ordenamos la suma de potencias en forma decreciente.
    QuickSort_Decreciente(PSum);

    // registramos la posición de la prob. de excedencia de 95% de la suma
    // de potencias y su valor.
    j95 := trunc(0.95 * (nCronicas - 1));
    P95Sum := PSum[j95];

    // ahora buscamos la suma de potencias de las centrales que con igual prob.
    // de excedencia en cada una, diste lo menos posible de la P95Sum determinada
    // en el paso anterior. Así jminError quedará con un índice que indica la
    // probabilidad de excedencia que al ser considerada la potencia de cada
    // central por separado con esa probabilidad, la suma de dichas potencias
    // es la más cercana a la potencia de probabilidad de excedenica de 95%
    // del conjunto de las centrales.
    minError := P95Sum;
    jminError := -1;
    for iCron := 0 to nCronicas - 1 do
    begin
      sumaPots := 0;
      for iCentral := 0 to NCentrales - 1 do
        sumaPots := sumaPots + params[iCentral].v_[iPaso, iCron];
      error := abs(sumaPots - P95Sum);
      if (error < minError) then
      begin
        jminError := iCron;
        minError := error;
      end;
    end;

(*
    if (jminError > j95) then
      raise Exception.Create('Violación del principio de Brandino SumaP95 > P95Suma ');
    // exigirle al conjunto una probabilidad de excedencia dada es un problema más
    // estricto que exigirle a cada central por separado la misma probabilidad de
    // excedencia.
  *)

    // ahora cargamos en los resultados, la potencia reconocida para cada
    // central.
    sumaPots := 0;
    for iCentral := 0 to NCentrales - 1 do
    begin
      res.v_[iPaso, iCentral] := params[iCentral].v_[iPaso, jMinError];
      sumaPots := sumaPots + res.v_[iPaso, iCentral];
    end;
    //    res.v[iPaso][res.nCronicas - 1]:= sumaPots;
    // en la última columna del resultado cargamos la potencia firme del
    // conjunto de las centrales. (observar que no es sumaPots)
    res.v_[iPaso, NCentrales] := P95Sum;
  end;

  setlength(PSum, 0);
end;

class function TPostOper_potenciaFirmeHidraulica.tipo: string;
begin
  Result := 'potenciaFirmeHidraulica';
end;

procedure TPostOper_potenciaFirmeHidraulica.Free;
begin
  SetLength(params, 0);
  inherited Free;
end;

function TPostOper_potenciaFirmeHidraulica.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfCronVarToStringConTamanio(params);
end;

//---------------------------
//Métodos de TPostOper_maximo
//===========================

constructor TPostOper_maximo.Create(res: TCronVar; acronvars: TDAOfCronVar);
begin
  inherited Create;
  self.params := acronvars;
  self.res := res;
end;

procedure TPostOper_maximo.Evaluar;
var
  iPaso, jCronica: integer;
  ivar: integer;
  max: NReal;
  hPasos, hCronicas: integer;
begin
  hPasos := res.nPasos - 1;    // cantidad de pasos de tiempo
  hCronicas := res.nCronicas - 1; // cantidad de crónicas
  for iPaso := 0 to hPasos do
  begin
    for jCronica := 0 to hCronicas do
    begin
      max := params[0][0, 0];
      for ivar := 0 to high(self.params) do
        if params[ivar][iPaso, jCronica] > max then
          max := params[ivar][iPaso, jCronica];
      res[iPaso, jCronica] := max;
    end;
  end;
end;

class function TPostOper_maximo.tipo: string;
begin
  Result := 'maximo';
end;


function TPostOper_maximo.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfCronVarToStringConTamanio(params);
end;


//----------------------------------
//Métodos de TPostOper_MultiOrdenar
//==================================

constructor TPostOper_MultiOrdenar.Create(res: TCronVar; acronvars: TDAOfCronVar);
begin
  inherited Create;
  self.params := acronvars;
  self.res := res;
end;


{$IFDEF MULTIORDENAR_QUICKSORT}
procedure TPostOper_MultiOrdenar.Evaluar;
var
  iPaso, jCronica: integer;
  ivar: integer;
  hPasos: integer;
  idx: TDAofNInt;
  buff, tmp_buff: TDAofNReal;

begin
  hPasos := res.nPasos;    // cantidad de pasos de tiempo

  setlength(idx, length(res.v_[0]));
  setlength(buff, length(idx));
  for iPaso := 0 to hPasos - 1 do
  begin

    for jCronica := 0 to high(idx) do
      idx[jCronica] := jCronica;

    QuickSort_Creciente(res.v_[iPaso], idx);

    for ivar := 0 to high(self.params) do
    begin
      tmp_buff := params[ivar].v_[iPaso];
      for jCronica := 0 to high(idx) do
        buff[jCronica] := tmp_buff[idx[jCronica]];
      params[ivar].v_[iPaso] := buff;
      buff := tmp_buff;
    end;
  end;
  setlength(idx, 0);
  setlength(buff, 0);
end;

{$ELSE}


procedure TPostOper_MultiOrdenar.Evaluar;
var
  iPaso, jCronica: integer;
  ivar: integer;
  min: NReal;
  hPasos, hCronicas: integer;
  k, kmin: integer;
  aval: NReal;

begin
  hPasos := res.nPasos;    // cantidad de pasos de tiempo
  hCronicas := res.nCronicas; // cantidad de crónicas

  for iPaso := 0 to hPasos - 1 do
  begin
    writeln('kpaso: ', iPaso);

    for jCronica := 0 to hCronicas - 1 do
    begin
      min := res.v_[iPaso, jCronica];
      kmin := jCronica;
      for k := jCronica + 1 to hCronicas - 1 do
        if res.v_[iPaso, k] < min then
        begin
          kmin := k;
          min := res.v_[iPaso, k];
        end;
      if (kmin <> jCronica) then
      begin

        aval := res.v_[iPaso, jCronica];
        res.v_[iPaso, jCronica] := res.v_[iPaso, kmin];
        res.v_[iPaso, kmin] := aval;
        for ivar := 0 to high(self.params) do
        begin
          aval := params[ivar].v_[iPaso, jCronica];
          params[ivar].v_[iPaso, jCronica] := params[ivar].v_[iPaso, kmin];
          params[ivar].v_[iPaso, kmin] := aval;
        end;
      end;
    end;
  end;
end;

{$ENDIF}

procedure TPostOper_MultiOrdenar.prepararse;
begin
  // Se supone que la variable resultado es la Ordenadora
end;


class function TPostOper_MultiOrdenar.tipo: string;
begin
  Result := 'MultiOrdenar';
end;


function TPostOper_MultiOrdenar.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfCronVarToStringConTamanio(params);
end;




//----------------------------------
//Métodos de TPostOper_MultiPromedioMovil
//==================================

constructor TPostOper_MultiPromedioMovil.Create(res: TCronVar;
  acronvars: TDAOfCronVar; NPasosPM: integer);
begin
  inherited Create;
  self.params := acronvars;
  self.res := res;
  self.NPasosPM := NPasosPM;
end;


procedure TPostOper_MultiPromedioMovil.Evaluar;
var
  iPaso, jCronica: integer;
  ivar: integer;
  aprom: NReal;
  hPasos, hCronicas: integer;
  jPaso: integer;

begin
  hPasos := res.nPasos - 1;    // cantidad de pasos de tiempo -1
  hCronicas := res.nCronicas - 1; // cantidad de crónicas -1
  for iPaso := 0 to hPasos do
  begin
    for jCronica := 0 to hCronicas do
    begin
      aprom := 0;
      jPaso := 0;
      while (((iPaso - jPaso) >= 0) and (jPaso < NPasosPM)) do
      begin
        for iVar := 0 to High(Params) do
          aprom := aprom + params[iVar][iPaso - jPaso, jCronica];
        Inc(jPaso);
      end;
      res[iPaso, jCronica] := aprom / NPasosPM / length(Params);
    end;
  end;
end;

class function TPostOper_MultiPromedioMovil.tipo: string;
begin
  Result := 'MultiPromedioMovil';
end;


function TPostOper_MultiPromedioMovil.serialize: string;
begin
  Result := inherited serialize + #9 + TDAOfCronVarToStringConTamanio(
    params) + #9 + FloatToStr(NPasosPM);
end;


end.
