unit uPostOpers;

{$DEFINE MULTIORDENAR_QUICKSORT}

interface

uses
{$IFDEF WINDOWS}
  Dialogs,
{$ENDIF}
  uHistoVarsOps, xMatDefs, Math, uSimResGlobs, uAuxiliares, matreal,
  ucursorfila,
  WRFFTI01, autoco,
{$IFDEF CRONVAR_PAGINADAS}
  uvectorpaginado,
{$ENDIF}
  SysUtils;

type
  TClaseDePostOper = class of TPostOper;

  { TPostOper }

  TPostOper = class

    //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 modificaCronVar(cronVar: TCronVar): boolean; virtual; abstract;
    function referenciaCronVar(cronVar: TCronVar): boolean; virtual; abstract;

    // retorna array con las cronvar usadas como parámetro
    function parametrosCronVar: TDAOfCronVar; virtual;
    function res_nombre: string; virtual; abstract;
    function nombresParametrosCronVar: string; virtual;
    function parametrosAdicionales: string; virtual;
    function serialize: string; virtual;
  end;




  { TPostOper_res }

  TPostOper_res = class(TPostOper)
    res: TCronVar;
    function res_nombre: string; override;
    function modificaCronVar(cronVar: TCronVar): boolean; override;
    function referenciaCronVar(cronVar: TCronVar): boolean; override;
    function serialize: string; override;
  end;

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


  TPostOperUnaCronVar = class(TPostOper_res)
  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;

  { TPostOperDosCronVarsUnReal }

  TPostOperDosCronVarsUnReal = class(TPostOperDosCronVars)
  public
    aReal: NReal;
    function serialize: string; override;
  end;



  TPostOperMultiCronVar = class(TPostOper_res)
  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_FiltroVentanaHann }

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


  { TPostOper_EspectroDePotencia }

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


  { TPostOper_restaCronVars }

  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 }

  TPostOper_MonotonizarCronVars = class(TPostOperMultiCronVar)
  public
    NPasosDelCajon: integer;
    flg_Decreciente: boolean;
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar;
      NPasosDelCajon: integer; flg_Decreciente: boolean);
    procedure prepararse; override;
    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_XYt }

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

  { TPostOper_CovarianzaEntreCronVars }

  TPostOper_CovarianzaEntreCronVars = class(TPostOperDosCronVars)
  public
    corrBessel: integer;
    Normalizar: integer;
    DesplazamientoMaximo: integer;
    constructor Create(res, param1, param2: TCronVar; corrBessel, Normalizar: integer);
    procedure prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function parametrosAdicionales: string; override;
    function serialize: string; override;
  end;

  TPostOper_CovarianzaCadenciaAnual = class(TPostOperDosCronVarsUnReal)
  public
    DesplazamientoMaximo: integer;
    constructor Create(res, param1, param2: TCronVar; overlap: NReal);
    procedure prepararse; override;
    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;

  // ATENCIÓN !!!!!
  // OJO!!! no alterar el oden ni inserta ni borrar elementos. SOLO
  // se puede agregar al final. Las plantillas sr3 guardan el Ordinal, si se cambia
  // algo se está cambiando lo que ya está guardado.
  // Tipo de cambio de paso de tiempo
  TTipoCPT = (TCPT_Promedio, TCPT_Minimo, TCPT_Suma, TCPT_Maximo,
    TCPT_I_esimo {, TCPT_Primero, TCPT_Ultimo });

  { 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;



  { TPostOper_AproLin }

  TPostOper_AproLin = class(TPostOperMultiCronVar)
    NCoeficientesHiperplano: integer;
    funcionAAjustar: TCronVar;
    lambda: NReal;
    constructor Create(res: TCronVar; acronvars: TDAOfCronVar;
      aFuncionAAjustar: TCronVar; aLambda: NReal);
    procedure Prepararse; override;
    procedure Evaluar; override;
    class function tipo: string; override;
    function serialize: string; override;
    function parametrosAdicionales: string; override;
  end;


implementation

uses
  uAuxiliaresEscrituraSimRes;

{ TPostOper }
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;
end;

{ TPostOper_EspectroDePotencia }

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

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

procedure TPostOper_EspectroDePotencia.Evaluar;
var
  m: TMatOfNReal;
  a: TDAofNReal;
  kcron: integer;
  afft_base: TWRFFT_base;

  procedure getcol(k: integer);
  var
    jpaso: integer;
  begin
    for jpaso := 0 to high(a) do
      a[jpaso] := m[jpaso][k];
  end;

  procedure setcol(k: integer);
  var
    jpaso: integer;
  begin
    for jpaso := 0 to high(a) do
      m[jpaso][k] := a[jpaso];
  end;

begin
  param1.GetCursorFila.CopyToDM(m);
  setlength(a, length(m));
  afft_base := TWRFFT_base.Create(length(a));
  for kcron := 0 to res.nCronicas - 1 do
  begin
    getcol(kcron);
    h2f(a, afft_base);
    setcol(kcron);
  end;
  afft_base.Free;
  res.GetCursorFila.CopyFromDM(m);
  Free_MatOfNreal(m);
end;

class function TPostOper_EspectroDePotencia.tipo: string;
begin
  Result := 'EspectroDePotencia';
end;

function TPostOper_EspectroDePotencia.parametrosAdicionales: string;
begin
  Result := inherited parametrosAdicionales;
end;

{ TPostOper_FiltroVentanaHann }

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

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

procedure TPostOper_FiltroVentanaHann.Evaluar;
var
  i, j: integer;
  w, vhann: NReal;
  cr, cp1: TCronVarCursorFila;

begin
  cr := res.GetCursorFila;
  cp1 := param1.GetCursorFila;
  w := 2 * pi / (res.NCronicas - 1);
  for i := 0 to res.nPasos - 1 do
  begin
    cp1.Enter(i);
    cr.Enter(i);
    vhann := 0.5 * (1 - cos(w * i));
    for j := 0 to res.NCronicas - 1 do
      cr[j] := cp1[j] * vhann;
    cr.leave;
    cp1.leave;
  end;
end;

class function TPostOper_FiltroVentanaHann.tipo: string;
begin
  Result := 'FiltroVentanaHann';
end;

function TPostOper_FiltroVentanaHann.parametrosAdicionales: string;
begin
  Result := inherited parametrosAdicionales;
end;

{ TPostOper_AproLin }

constructor TPostOper_AproLin.Create(res: TCronVar; acronvars: TDAOfCronVar;
  aFuncionAAjustar: TCronVar; aLambda: NReal);
begin
  self.params := acronvars;
  self.res := res;
  self.funcionAAjustar := aFuncionAAjustar;
  self.lambda := aLambda;
  NCoeficientesHiperplano := Length(acronvars) + 1; // +1 por el termino independiente
  res.nCronicas := NCoeficientesHiperplano;

end;

procedure TPostOper_AproLin.Prepararse;
begin
  res.inicializar(NCoeficientesHiperplano, params[0].nPasos, params[0].dtIni,
    params[0].DurPaso_horas);
end;

procedure TPostOper_AproLin.Evaluar;
var

  X, Xt: TMatR;
  XXt: TMatR;
  XYt: TMatR;
  Yt: TMatR;
  lambdaI: TMatR;

  resBool: boolean;
  exp10: integer;

  vectores_A: TDAofVectR;
  NPasos, NCronicas, kCronica, NVariables, kVariable, kPaso, kColumna, kFila: integer;
  det, CasiCero: NReal;

  cx: array of TCronVarCursorFila;
  cf, cr: TCronVarCursorFila;

begin

  NCronicas := params[0].nCronicas;
  NPasos := params[0].nPasos;
  NVariables := NCoeficientesHiperplano - 1;


  X := TMatR.Create_Init(NVariables + 1, NCronicas);
  Yt := TMatR.Create_Init(NCronicas, 1);

  XXt := TMatR.Create_Init(NVariables + 1, NVariables + 1);
  XYt := TMatR.Create_Init(NVariables + 1, 1);

  //en vectores_A tenemos vectores A  ( a0, a1, ... , a{NVariables+1} )
  setlength(vectores_A, NPasos);
  setlength(cx, NVariables);
  // Creamos los cursores de lectura
  cf := TCronVarCursorFila.Create(funcionAAjustar.GetCursorFila.vector, CMC_Read);
  SetLength(cx, NVariables);

  for kVariable := 0 to NVariables - 1 do
    cx[kVariable] := TCronVarCursorFila.Create(
      params[kVariable].GetCursorFila.vector, CMC_Read);

  cr := TCronVarCursorFila.Create(res.GetCursorFila.vector, CMC_Write);

  // para todos los pasos hacemos un hiperplano
  for kPaso := 0 to NPasos - 1 do
  begin

    // resetea las matrices
    XXt.Ceros;
    XYt.Ceros;

    // armamos el vector Y (columna) con los costos futuros de cada cronica
    // en la matriz X (termino independiente  + estado por cronica en filas) ponemos los 1 en la primera columna

    cf.Enter(kPaso);
    for kCronica := 1 to NCronicas do
    begin
      Yt.pon_e(kCronica, 1, cf[kCronica - 1]);
      X.pon_e(1, kCronica, 1);
    end;

    // armamos la matriz X recorriendo las filas (variables) y columnas (cronicas)

    for kVariable := 2 to NVariables + 1 do
      // no va desde 1 porque en el 1 esta el 1 del a0 (termino independiente)
    begin
      cx[kVariable - 2].Enter(kPaso);
      for kCronica := 1 to NCronicas do
        X.pon_e(kVariable, kCronica, cx[kVariable - 2][kCronica - 1]);
      cx[kVariable - 2].Leave;
    end;

    Xt := X.Crear_Transpuesta;

    XXt.Mult(X, Xt);
    XYt.Mult(X, Yt);


    {X.WriteArchiXLT('X.xlt');
    Xt.WriteArchiXLT('Xt.xlt');
    XXt.WriteArchiXLT('XXt.xlt');
    Yt.WriteArchiXLT('Yt.xlt');
    XYt.WriteArchiXLT('XYt.xlt'); }

    // Solucion min error cuad + lambda*||coef hiperplan||

    lambdaI := TMatR.Create_identidad(XXt.nc);

    lambdaI.PorReal(lambda);
    XXt.Suma(XXt, lambdaI);

    CasiCero := 1E-6;
    // Esto resuelve por minimos cuadrados
    det := XXt.Escaler(XYt, resBool, exp10);
    det := det * power(10, exp10);

    //XYt.WriteArchiXLT('A.xlt');

    vectores_A[kPaso] := TVectR.Create_InitVal(NCoeficientesHiperplano);

    cr.Enter(kPaso);
    if (not resBool) or (abs(det) < CasiCero) then
    begin
      vectores_A[kPaso].pon_e(1, Yt.Columna(1).promedio);

      for kVariable := 1 to NVariables + 1 do
        cr[kVariable - 1] := vectores_A[kPaso].e(kVariable);

      //raise Exception.Create('No se puede resolver por mínimos cuadrados!!!');
    end
    else
    begin
      XYt.CopyColVect(vectores_A[kPaso], 1);

      for kVariable := 1 to NVariables + 1 do
        cr[kVariable - 1] := vectores_A[kPaso].e(kVariable);
    end;
    cr.Leave;
    Xt.Free;
    lambdaI.Free;
  end; // for kpaso


  // libero TODO
  for kPaso := 0 to NPasos - 1 do
    vectores_A[kPaso].Free;
  SetLength(vectores_A, 0);

  //lambdaI.Free; liberada en el for

  X.Free;
  //Xt.Free; liberada en el for

  XXt.Free;
  XYt.Free;

  Yt.Free;

  for kVariable := 0 to NVariables - 1 do
    cx[kVariable].Free;
  cf.Free;
  cr.Free;
  setlength(cx, 0);

end;

class function TPostOper_AproLin.tipo: string;
begin
  Result := 'AproLin';
end;

function TPostOper_AproLin.serialize: string;
begin
  Result := inherited serialize + #9 + FloatToStr(lambda) + #9 +
    TDAOfCronVarToStringConTamanio(params) + #9 + encomille(funcionAAjustar.nombre);
  // AproLin VariableResultado Lambda {NCoeficientesHiperplano - 1} Variable1 Variable2 ... VariableN FuncionAAjustar

end;

function TPostOper_AproLin.parametrosAdicionales: string;
begin
  Result := 'funcAjus= ' + funcionAAjustar.nombre;
end;



{ TPostOperDosCronVarsUnReal }

function TPostOperDosCronVarsUnReal.serialize: string;
begin
  Result := inherited serialize + #9 + FloatToStr(aReal);
end;

{ 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;
  cr, cp: TCronVarCursorFila;
begin
  cr := res.GetCursorFila;
  cp := param1.GetCursorFila;

  for kPaso := 0 to param1.nPasos - 1 do
  begin
    cp.Enter(kPaso);
    for kCron := 0 to param1.nCronicas - 1 do
    begin
      cr.Enter(kCron);
      cr[kPaso] := cp[kCron];
      cr.Leave;
    end;
    cp.Leave;
  end;
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;
  cr, cp: TCronVarCursorFila;
begin
  cr := res.GetCursorFila;
  cp := param1.GetCursorFila;

  case sentido of
    SAC_DeIzquierdaADerecha:
    begin
      for iPaso := 0 to param1.Npasos - 1 do
      begin
        cr.Enter(iPaso);
        cp.Enter(iPaso);
        acum := 0.0;
        for jCron := 0 to param1.NCronicas - 1 do
        begin
          acum := acum + cp[jCron];
          if flgPromediar then
            cr[jCron] := acum / (jCron + 1)  //???? QUé será esto?????
          else
            cr[jCron] := acum;
        end;
        cp.Leave;
        cr.Leave;
      end;
    end;
    SAC_DeDerechaAIzquierda:
    begin
      for iPaso := 0 to param1.Npasos - 1 do
      begin
        cr.Enter(iPaso);
        cp.Enter(iPaso);
        acum := 0.0;
        for jCron := param1.NCronicas - 1 downto 0 do
        begin
          acum := acum + cp[jCron];
          jCronRes := param1.NCronicas - 1 - jCron;
          if flgPromediar then
            cr[jCronRes] := acum / (jCronRes + 1)
          else
            cr[jCronRes] := acum;
        end;
        cp.Leave;
        cr.Leave;
      end;
    end;
  end; // del case

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
  kPasoRes, kPaso, kCron: integer;
begin
  for kPaso := 0 to param1.nPasos - 1 do
  begin
    param1.Enter(kpaso);
    for kCron := kIni to kFin do
    begin
      kPasoRes := kCron - kini;
      res.Enter(kPasoRes);
      res[kCron] := param1[kCron];
      res.Leave;
    end;
    param1.Leave;
  end;
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;
  cp, cr: TCronVarCursorFila;
  kPasoPar: integer;
begin

  cp := param1.GetCursorFila;
  cr := res.GetCursorFila;

  for kPasoRes := 0 to NPasosRes - 1 do
  begin
    cr.Enter(kPasoRes);
    kCronRes := 0;
    for i := 0 to self.NPasosRecronizacion - 1 do
    begin
      kPasoPar := kPasoRes * NPasosRecronizacion + i;
      cp.Enter(kPasoPar);
      for j := 0 to param1.nCronicas - 1 do
      begin
        cr[kCronRes] := cp[j];
        Inc(kCronRes);
      end;
      cp.Leave;
    end;
    cr.Leave;
  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;
  cr, cp: TCronVarCursorFila;
  aval: NREal;
begin
  kCronRes := 0;
  kPasoRes := 0;

  cr := res.GetCursorFila;
  cp := param1.GetCursorFila;

  for i := 0 to param1.nPasos - 1 do
  begin
    cp.enter(i);
    for j := 0 to param1.nCronicas - 1 do
    begin
      kPasoRes := j * param1.nPasos + i;
      cr.enter(kPasoRes);
      aval := cp[j];
      cr[0] := aval;
      cr.leave;
      Inc(kPasoRes);
    end;
    cp.leave;
  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 * length( params );
  Self.DurPaso_Horas_Res := params[0].DurPaso_horas / length( params );
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;

  cr: TCronVarCursorFila;
  cp: array of TCronVarCursorFila;

begin
  hPasos := params[0].nPasos - 1;
  hCronicas := params[0].nCronicas - 1;

  cr := res.GetCursorFila;
  setlength(cp, length(params));
  for ivar := 0 to high(params) do
    cp[ivar] := params[ivar].GetCursorFila;

  for iPaso := 0 to hPasos do
  begin
    for ivar := 0 to high(self.params) do
    begin
      cr.Enter(ivar + iPaso * length(params));
      cp[ivar].Enter(iPaso);
      for jCronica := 0 to hCronicas do
        cr[jCronica] := cp[ivar][jCronica];
      cp[ivar].Leave;
      cr.Leave;
    end;
  end;

  setlength(cp, 0);

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_res
//====================

function TPostOper_res.res_nombre: string;
begin
  Result := res.nombre;
end;

function TPostOper_res.modificaCronVar(cronVar: TCronVar): boolean;
begin
  Result := res = cronVar;
end;

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


function TPostOper_res.serialize: string;
begin
  Result := inherited serialize + #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.Enter(0);
  res[0] := aReal;
  res.Leave;
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;
  bReal: NReal;
begin
  for i := 0 to res.nPasos - 1 do
  begin
    res.Enter(i);
    param1.Enter(i);
    for j := 0 to res.nCronicas - 1 do
      res[j] := min(param1[j], aReal);
    res.Leave;
    param1.Leave;
  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;
  aVal: NReal;
begin
  for i := 0 to res.nPasos - 1 do
  begin
    res.Enter(i);
    param1.Enter(i);
    for j := 0 to res.nCronicas - 1 do
    begin
      aVal := param1[j];
      if aReal > aVal then
        res[j] := aReal
      else
        res[j] := aVal;
    end;
    res.Leave;
    param1.Leave;
  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;
begin
  for i := 0 to res.nPasos - 1 do
  begin
    res.Enter(i);
    param1.Enter(i);
    for j := 0 to res.nCronicas - 1 do
      res[j] := param1[j] + aReal;
    res.Leave;
    param1.Leave;
  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;
begin
  for i := 0 to res.nPasos - 1 do
  begin
    res.Enter(i);
    param1.Enter(i);
    for j := 0 to res.nCronicas - 1 do
      res[j] := param1[j] * aReal;
    res.Leave;
    param1.Leave;
  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;
  cr, cp1, cp2: TCronVarCursorFila;
begin
  cr := res.GetCursorFila;
  cp1 := param1.GetCursorFila;
  cp2 := param2.GetCursorFila;

  for i := 0 to res.nPasos - 1 do
  begin
    cp1.Enter(i);
    cp2.Enter(i);
    cr.Enter(i);
    for j := 0 to res.NCronicas - 1 do
      cr[j] := cp1[j] - cp2[j];
    cr.leave;
    cp2.leave;
    cp1.leave;
  end;
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;

begin
  for iPaso := 0 to res.nPasos - 1 do
  begin
    res.Enter(iPaso);
    for ivar := 0 to high(params) do
      params[ivar].Enter(iPaso);

    for jCronica := 0 to res.nCronicas - 1 do
    begin
      a := 0;
      for ivar := 0 to high(self.params) do
        a := a + params[ivar][jCronica] * coeficientes[ivar];
      res[jCronica] := a;
    end;

    res.Leave;
    for ivar := 0 to high(params) do
      params[ivar].Leave;
  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: integer;
begin
  hPasos := res.nPasos - 1;
  for iPaso := 0 to hPasos do
  begin
    res.Enter(iPaso);
    for ivar := 0 to high(params) do
    begin
      iPasoDesp := iPaso + desplazamientos[ivar];
      if (iPasoDesp >= 0) and (iPasoDesp <= hPasos) then
        params[ivar].Enter(iPasoDesp);
    end;

    for jCronica := 0 to res.nCronicas - 1 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][jCronica] * coeficientes[ivar];
      end;
      res[jCronica] := a;
    end;

    res.Leave;
    for ivar := 0 to high(params) do
    begin
      iPasoDesp := iPaso + desplazamientos[ivar];
      if (iPasoDesp >= 0) and (iPasoDesp <= hPasos) then
        params[ivar].Leave;
    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.prepararse;
begin
  inherited prepararse;
  if NPasosDelCajon > res.nPasos then
    NPasosDelCajon := res.nPasos;
end;

procedure TPostOper_MonotonizarCronVars.Evaluar;
var
  nTramos: integer;
  iTramo: integer;
  iCronica, iPaso: integer;
  ivar: integer;
  ar: TDAOfNReal;
  indx: TDAOfNint;

  acv: TCronVarCursorFila;

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
        res.Enter(iTramo * NPasosDelCajon + iPaso);
        ar[iPaso] := res[iCronica];
        indx[iPaso] := iPaso;
        res.Leave;
      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
      begin
        res.Enter(iTramo * NPasosDelCajon + iPaso);
        res[iCronica] := ar[iPaso];
        res.Leave;
      end;

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

        // Guardamos los valores en el orden impuesto por la Monotonizante
        for iPaso := 0 to NPasosDelCajon - 1 do
        begin
          acv.Enter(iTramo * NPasosDelCajon + iPaso);
          acv[iCronica] := ar[indx[iPaso]];
          acv.Leave;
        end;
      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
    res.Enter(iPaso);
    param1.Enter(iPaso);
    for jCronica := 0 to hCronicas do
      res[jCronica] := qa * param1[jCronica];
    qa := qa * aReal;
    param1.Leave;
    res.Leave;
  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
  begin
    res.Enter(i);
    param1.Enter(i);
    param2.Enter(i);
    for j := 0 to res.nCronicas - 1 do
      res[j] := param1[j] * param2[j];
    res.Leave;
    param1.Leave;
    param2.Leave;
  end;
end;


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

//-------------------------------------------
//Métodos de TPostOper_XYt
//===========================================


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

procedure TPostOper_XYt.prepararse;
begin
  res.inicializar(param2.nPasos, param1.nPasos, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_XYt.Evaluar;
var
  iPaso: integer;
  iPaso1: integer;
  iPaso2: integer;
  jCronica: integer;

  cr: TCronVarCursorFila;
  cp1, cp2: TCronVarCursorFila;
  aVal: NReal;

begin
  cr := res.GetCursorFila;
  cp1 := param1.GetCursorFila;
  cp2 := param2.GetCursorFila;

  for iPaso1 := 0 to param1.nPasos - 1 do
  begin
    cr.Enter(iPaso1);
    cp1.Enter(iPaso1);
    for iPaso2 := 0 to param2.nPasos - 1 do
    begin
      cp2.Enter(iPaso2);
      aVal := 0;
      for jCronica := 0 to param1.nCronicas - 1 do
        aVal := aval + cp1[jCronica] * cp2[jCronica];
      cr[iPaso] := aVal;
      cp2.Leave;
    end;
    cp1.Leave;
    cr.Leave;
  end;

end;

class function TPostOper_XYt.tipo: string;
begin
  Result := 'XYt';
end;

//-------------------------------------------
//Métodos de TPostOper_CovarianzaEntreCronVars
//===========================================

constructor TPostOper_CovarianzaEntreCronVars.Create(res, param1, param2: TCronVar;
  corrBessel, Normalizar: integer);
begin
  inherited Create;
  self.res := res;
  self.param1 := param1;
  self.param2 := param2;
  self.corrBessel := corrBessel;
  self.Normalizar := Normalizar;
end;

procedure TPostOper_CovarianzaEntreCronVars.prepararse;
var
  NPasosRes: integer;
begin
  DesplazamientoMaximo := param1.nPasos div 2;
  NPasosRes := 2 * DesplazamientoMaximo + 1;

  if (res.nPasos <> -1) and (res.nPasos <> NPasosRes) then
    raise Exception.Create('TPostOper_CovarianzaEntreCronVars: La CronVar de resultado' +
      'debe tener tamanio [2*(NPasos div 2),1], Ingrese una variable vacia');
  if (param1.nPasos <> param2.nPasos) or (param1.nCronicas <> param2.nCronicas) then
    raise Exception.Create('TPostOper_CovarianzaEntreCronVars: Las CronVar de entrada' +
      'deben tener el mismo tamanio');

  res.inicializar(2, NPasosRes, param1.dtIni, param1.DurPaso_horas);
end;

procedure TPostOper_CovarianzaEntreCronVars.Evaluar;
var
  iPaso, jCronica, cnt: integer;
  desv_param1, desv_param2: NReal;
  covar, ve_param1_tot, ve_param2_tot: NReal;

  cr: TCronVarCursorFila;
  cp1, cp2: TCronVarCursorFila;


  procedure calc_cov_normalizada;
  var
    iPaso1: integer;
    iPaso2: integer;
    iPasoRes: integer;
    jCronica: integer;
    kDesp: integer;
    fila1, fila2: TDAofNReal;
  begin
    //desv estandar
    desv_param1 := 0;
    desv_param2 := 0;
    for iPaso1 := 0 to param1.nPasos - 1 do
    begin
     // fila1 := cp1.vector[iPaso1];
      //fila2 := cp2.vector[iPaso1];
      cp1.Enter(iPaso1);
      fila1 := cp1.vector[iPaso1];
      cp1.Leave;
      cp2.Enter(iPaso1);
      for jCronica := 0 to param1.nCronicas - 1 do
      begin
        desv_param1 := desv_param1 + sqr(fila1[jCronica] - ve_param1_tot);
        desv_param2 := desv_param2 + sqr(cp2[jCronica] - ve_param2_tot);
      end;
    //  cp1.Leave;
      cp2.Leave;
    end;

    desv_param1 := sqrt(desv_param1 / cnt);
    desv_param2 := sqrt(desv_param2 / cnt);

    for kDesp := -DesplazamientoMaximo to DesplazamientoMaximo do
    begin
      iPasoRes := kDesp + DesplazamientoMaximo;
      cr.Enter(iPasoRes);
      cr[0] := kDesp;  //imprimo en la primer columna el desplazamiento
      cnt := 0;
      covar := 0;
      for iPaso1 := 0 to (param1.nPasos - 1) do
      begin
        cp1.Enter(iPaso1);
        fila1 := cp1.vector[iPaso1];
        cp1.Leave;
        iPaso2 := iPaso1 - kDesp;
        if (iPaso2 > 0) and (iPaso2 < param2.nPasos) then
        begin
          cp2.Enter(iPaso2);
          fila2 := cp2.vector[iPaso2];
          cnt := cnt + 1;
          for jCronica := 0 to param1.nCronicas - 1 do
          begin
            covar := covar + ((fila1[jCronica] - ve_param1_tot) / (desv_param1)) *
              ((fila2[jCronica] - ve_param2_tot) / (desv_param2));
            cnt := cnt + 1;
          end;
         cp2.Leave;
        end;
        //cp1.Leave;
      end;
      cr[1] := covar / (cnt - corrBessel);
      cr.Leave;
    end;
  end;

  procedure calc_cov_sin_normalizar;
  var
    iPaso1: integer;
    iPaso2: integer;
    jCronica: integer;
    kDesp: integer;
    iPasoRes: integer;
    fila1, fila2: TDAofNReal;
  begin

    for kDesp := -DesplazamientoMaximo to DesplazamientoMaximo do
    begin
      iPasoRes := kDesp + DesplazamientoMaximo;
      cr.Enter(iPasoRes);
      cr[0] := kDesp;  //imprimo en la primer columna el desplazamiento
      cnt := 0;
      covar := 0;
      for iPaso1 := 0 to (param1.nPasos - 1) do
      begin
        //cp1.Enter(iPaso1);
        fila1 := cp1.vector[iPaso1];
        iPaso2 := iPaso1 - kDesp;
        if (iPaso2 > 0) and (iPaso2 < param2.nPasos) then
        begin
          //cp2.Enter(iPaso2);
          fila2 := cp2.vector[iPaso2];
          cnt := cnt + 1;
          for jCronica := 0 to param1.nCronicas - 1 do
          begin
            covar := covar + ((fila1[jCronica] - ve_param1_tot)) *
              ((fila2[jCronica] - ve_param2_tot));
            cnt := cnt + 1;
          end;
          cp2.Leave;
        end;
        cp1.Leave;
      end;
      cr[1] := covar / (cnt - corrBessel);
      cr.Leave;
    end;
  end;

begin

  cr := res.GetCursorFila;
  cp1 := param1.GetCursorFila;
  cp2 := param2.GetCursorFila;

  //covarianza  res[iPaso+k, 1] = <A[iPaso, jCronica], B[iPaso-k, jCronica]>m

  //valor esperado
  ve_param1_tot := 0;
  ve_param2_tot := 0;
  for iPaso := 0 to param1.nPasos - 1 do
  begin
    cp1.Enter(iPaso);
    cp2.Enter(iPaso);
    for jCronica := 0 to param1.nCronicas - 1 do
    begin
      ve_param1_tot := ve_param1_tot + cp1[jCronica];
      ve_param2_tot := ve_param2_tot + cp2[jCronica];
    end;
    cp1.Leave;
    cp2.Leave;
  end;
  cnt := param1.nPasos * param1.nCronicas;

  ve_param1_tot := ve_param1_tot / cnt;
  ve_param2_tot := ve_param2_tot / cnt;

{
  //ve para paso y cronica
  ve_param1_tot := 0;
  ve_param2_tot := 0;
  for iPaso := 0 to param1.nPasos - 1 do
  begin
    ve_param1_tot := ve_param1_tot + vePaso_param1[iPaso] / param1.nPasos;
    ve_param2_tot := ve_param2_tot + vePaso_param2[iPaso] / param2.nPasos;
  end;
 }

  if Normalizar = 1 then
    calc_cov_normalizada
  else
    calc_cov_sin_normalizar;

end;


class function TPostOper_CovarianzaEntreCronVars.tipo: string;
begin
  Result := 'CovarianzaEntreCronVars';
end;

function TPostOper_CovarianzaEntreCronVars.parametrosAdicionales: string;
begin
  Result := 'Bessel:' + IntToStr(corrBessel) + ', Normalizar: ' + IntToStr(Normalizar);
end;

function TPostOper_CovarianzaEntreCronVars.serialize: string;
begin
  Result := inherited serialize + #9 + IntToStr(corrBessel) + #9 + IntToStr(Normalizar);
end;

//-------------------------------------------
//Métodos de TPostOper_CovarianzaCadenciaAnual
//===========================================

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

procedure TPostOper_CovarianzaCadenciaAnual.prepararse;
var
  NPasosRes: integer;
begin

  DesplazamientoMaximo := param1.nPasos div 2;
  NPasosRes := 2 * DesplazamientoMaximo + 1;

  if (res.nPasos <> -1) and (res.nPasos <> NPasosRes) then
    raise Exception.Create('TPostOper_CovarianzaEntreCronVars: La CronVar de resultado' +
      'debe tener tamanio [2*(NPasos div 2),1], Ingrese una variable vacia');
  if (param1.nPasos <> param2.nPasos) or (param1.nCronicas <> param2.nCronicas) then
    raise Exception.Create('TPostOper_CovarianzaEntreCronVars: Las CronVar de entrada' +
      'deben tener el mismo tamanio');

  res.inicializar(53, NPasosRes, param1.dtIni, param1.DurPaso_horas);

end;

procedure TPostOper_CovarianzaCadenciaAnual.Evaluar;
var
  iPaso, iPaso1, iPaso2, jCronica, kDesp, cnt: integer;
  ve_param1, ve_param2: TDAofNReal;
  cnt_semanal, covar_semanal, desv_param1, desv_param2: TDAofNReal;
  DiasDelAnioMedio, durPasoHoras: NReal;
  kSemana, kSemana_Ini, kSemanaOverlap, kSemana2: integer;
  anio, mes, dia: word;
  dtPDA: TDateTime;
  overlap_MaxIter, kOverlap: integer;

  cr: TCronVarCursorFila;
  cp1, cp2: TCronVarCursorFila;
  fila1, fila2: TDAofNReal;
  f: textfile;
  archi: string;
begin

  cr := res.GetCursorFila;
  cp1 := param1.GetCursorFila;
  cp2 := param2.GetCursorFila;


  DiasDelAnioMedio := 365.2425;
  DecodeDate(param1.dtIni, anio, mes, dia);
  dtPDA := EncodeDate(anio, 1, 1);
  kSemana_Ini := (trunc((param1.dtIni - dtPDA) / DiasDelAnioMedio * 52) mod 52);
  durPasoHoras := param1.DurPaso_horas;
  overlap_MaxIter := trunc(aReal / 2);

  SetLength(ve_param1, 52);
  SetLength(ve_param2, 52);
  SetLength(desv_param1, 52);
  SetLength(desv_param2, 52);

  SetLength(cnt_semanal, 52);
  SetLength(covar_semanal, 52);

  for kSemana := 0 to 52 - 1 do
  begin
    cnt_semanal[kSemana] := 0;
    covar_semanal[kSemana] := 0;
    ve_param1[kSemana] := 0;
    ve_param2[kSemana] := 0;
    desv_param1[kSemana] := 0;
    desv_param2[kSemana] := 0;
  end;


  //valor esperado
  for iPaso := 0 to param1.nPasos - 1 do
  begin
    cp1.Enter(iPaso);
    fila1 := cp1.vector[iPaso];
    cp1.Leave;
    cp2.Enter(iPaso);
    kSemana := round(iPaso * durPasoHoras / 24 / 7);
    kSemana := round(frac((kSemana_Ini + kSemana) / 52.0) * 52.0);
    for jCronica := 0 to param1.nCronicas - 1 do
    begin
      ve_param1[kSemana] := ve_param1[kSemana] + fila1[jCronica];
      ve_param2[kSemana] := ve_param2[kSemana] + cp2[jCronica];
      cnt_semanal[kSemana] := cnt_semanal[kSemana] + 1;
    end;
    //cp1.Leave;
    cp2.Leave;
  end;

  for kSemana := 0 to 52 - 1 do
  begin
    ve_param1[kSemana] := ve_param1[kSemana]/cnt_semanal[kSemana];
    ve_param2[kSemana] := ve_param2[kSemana]/cnt_semanal[kSemana];
    cnt_semanal[kSemana] := 0;
  end;

  //desv estandar
  for iPaso := 0 to param1.nPasos - 1 do
  begin
    cp1.Enter(iPaso);
    fila1 := cp1.vector[iPaso];
    cp1.Leave;
    cp2.Enter(iPaso);
    kSemana := round(iPaso * durPasoHoras / 24 / 7);
    kSemana := round(frac((kSemana_Ini + kSemana) / 52.0) * 52.0);
    for jCronica := 0 to param1.nCronicas - 1 do
    begin
      desv_param1[kSemana] := desv_param1[kSemana] + sqr(fila1[jCronica] - ve_param1[kSemana]);
      desv_param2[kSemana] := desv_param2[kSemana] + sqr(cp2[jCronica] - ve_param2[kSemana]);
      cnt_semanal[kSemana] := cnt_semanal[kSemana] + 1;
    end;
    //cp1.Leave;
    cp2.Leave;
  end;

  for kSemana := 0 to 52 - 1 do
  begin
    desv_param1[kSemana] := sqrt(desv_param1[kSemana] / cnt_semanal[kSemana]);
    desv_param2[kSemana] := sqrt(desv_param2[kSemana] / cnt_semanal[kSemana]);
    cnt_semanal[kSemana] := 0;
  end;
 {$IFDEF escribirprueba}
  archi := '/home/vani/ADME/iN34_cys_AO/ADME_LP_2019_2046_TestCEGH_iN34_Feb2020/pasos.xlt';
 AssignFile(f, archi);
 if FileExists(archi) then
   Append(f)
 else
   Rewrite(f);
         writeln(f,
         'kDesp: '  + #9 +
         'kSemana' + #9 +
        'iPaso: '  + #9 +
        'kSemana2: ' +  #9 +
        'iPaso2: ');
 {$ENDIF}
  for kDesp := -DesplazamientoMaximo to DesplazamientoMaximo do
  begin
    if (kDesp = 0) or (kDesp = -1) then
       kSemana:= 1;
    for iPaso1 := 0 to param1.nPasos - 1 do
    begin
      kSemana := round(iPaso1 * durPasoHoras / 24 / 7);
      kSemana := round(frac((kSemana_Ini + kSemana) / 52.0) * 52.0);
      iPaso2 := iPaso1 - kDesp;
      if (iPaso2 >= 0) and (iPaso2 < param2.nPasos) then
      begin
        kSemana2 := round(iPaso2 * durPasoHoras / 24 / 7);
        kSemana2 := round(frac((kSemana_Ini + kSemana2) / 52.0) * 52.0);
        {$IFDEF escribirprueba}
        writeln(f,
        IntToStr(kDesp) + #9  +
        FloatToStr(kSemana) + #9 +
        IntToStr(iPaso1) + #9 +
        FloatToStr(kSemana2) + #9 +
        IntToStr(iPaso2));
        {$ENDIF}
        cp1.Enter(iPaso1);
        fila1 := cp1.vector[iPaso1];
        cp1.Leave;
        cp2.Enter(iPaso2);
        for jCronica := 0 to param1.nCronicas - 1 do
        begin
          for kOverlap := -overlap_MaxIter to overlap_MaxIter do
          begin
            kSemanaOverlap := kSemana + kOverlap;

            if (kSemanaOverlap > 51) then
              kSemanaOverlap := kSemanaOverlap - 52;
            if (kSemanaOverlap < 0) then
              kSemanaOverlap := kSemanaOverlap + 52;

              covar_semanal[kSemanaOverlap] :=
              covar_semanal[kSemanaOverlap] +
              ((fila1[jCronica] - ve_param1[kSemanaOverlap]) / (desv_param1[kSemanaOverlap])) *
              ((cp2[jCronica] - ve_param2[kSemana2]) / (desv_param2[kSemana2]));


            cnt_semanal[kSemanaOverlap] := cnt_semanal[kSemanaOverlap] + 1;
          end;
        end;
        //cp1.Leave;
        cp2.Leave;
      end;
    end;

    cr.Enter(kDesp + DesplazamientoMaximo);
    cr[0] := kDesp; //imprimo en la primer columna el desplazamiento
    for kSemana := 0 to 52 - 1 do
    begin
      cr[kSemana + 1] := covar_semanal[kSemana] / cnt_semanal[kSemana];
      cnt_semanal[kSemana] := 0;
      covar_semanal[kSemana] := 0;
    end;
    cr.Leave;
  end;
 {$IFDEF escribirprueba}
 closefile(f);
 {$ENDIF}

end;



class function TPostOper_CovarianzaCadenciaAnual.tipo: string;
begin
  Result := 'CovarianzaCadenciaAnual';
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
  writeln('Division:  ' + res.nombre + ' = ' + param1.nombre + ' / ' + param2.nombre);
  for i := 0 to res.nPasos - 1 do
  begin
    res.Enter(i);
    param1.Enter(i);
    param2.Enter(i);
    for j := 0 to res.nCronicas - 1 do
    begin
      p := param1[j];
      q := param2[j];
      if abs(q) <= AsumaCero then
      begin
        if abs(p) <= AsumaCero then
          res[j] := 0
        else
          raise Exception.Create('PosOper_DivisionCronVar dividiendo por CERO!');
      end
      else
        res[j] := p / q;
    end;
    res.Leave;
    param1.Leave;
    param2.Leave;
  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;
  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, res.nPasos);

  // borramos el resultado
  for kPaso := 0 to res.nPasos - 1 do
  begin
    res.Enter(kPaso);
    for iCronica := 0 to res.nCronicas - 1 do
      res[iCronica] := 0;
    res.Leave;
    // 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
      param1.Enter(kPaso);

      calcularAlfa;

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

      res.Enter(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
                res[iCronica] := param1[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 (param1[iCronica] > res[iCronica]) then
                res[iCronica] := param1[iCronica];
            flg[j1] := True;
          end;

          if (beta >= 0.001) and (j2 < res.nPasos) then
          begin
            res.Leave;
            res.Enter(j2);
            for iCronica := 0 to param1.nCronicas - 1 do
              res[iCronica] := param1[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 (Param1[iCronica] < res[iCronica]) then
                res[iCronica] := param1[iCronica];
            flg[j1] := True;
          end;

          if (beta >= 0.001) and (j2 < res.nPasos) then
          begin
            res.Leave;
            res.Enter(j2);
            for iCronica := 0 to param1.nCronicas - 1 do
              res[iCronica] := param1[iCronica];
            flg[j2] := True;
          end;
        end
      end;   // del case
      res.Leave;
    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;
  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
    res.Enter(kpaso);
    for iCronica := 0 to res.nCronicas - 1 do
      Res[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
      param1.Enter(kPaso);
      if j1 > (res.nPasos - 1) then
        break;
      res.Enter(j1);
      if j1 = j2 then
      begin
        for iCronica := 0 to param1.nCronicas - 1 do
          Res[iCronica] := Res[iCronica] + Param1[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 <= (res.nPasos - 1)) then
        begin
          for iCronica := 0 to param1.nCronicas - 1 do
            Res[iCronica] := Res[iCronica] + Param1[iCronica] * alfa;

          Res.Enter(j2);
          for iCronica := 0 to param1.nCronicas - 1 do
            Res[iCronica] := Res[iCronica] + Param1[iCronica] * beta;
        end
        else
          for iCronica := 0 to param1.nCronicas - 1 do
            Res[iCronica] := Res[iCronica] + Param1[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);

      param1.Enter(kPaso);
      if j1 > (res.nPasos - 1) then
        break;
      res.Enter(j1);

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

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

      m_Aporte := alfa * m_Aporte_fijo;
      for iCronica := 0 to param1.nCronicas - 1 do
        Res[iCronica] := Res[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
          res.Enter(j);
          for iCronica := 0 to param1.nCronicas - 1 do
            Res[iCronica] := Res[iCronica] + m_Aporte_fijo;
        end;
        m_Resto := m_Resto - nFijos * m_Aporte_fijo;
      end;
      if (j2 > j1) and (abs(beta) > AsumaCero) then
      begin
        res.Enter(j2);
        m_Aporte := beta * m_Aporte_fijo;
        for iCronica := 0 to param1.nCronicas - 1 do
          Res[iCronica] := Res[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
      res.Enter(kPaso);
      for iCronica := 0 to res.nCronicas - 1 do
        Res[iCronica] := Res[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
  iCronica, 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;

  setlength(filakParam1, param1.nCronicas);

  for kPaso := 0 to res.nPasos - 1 do
  begin
    param1.Enter(kPaso);
    param1.GetCursorFila.CopyToDA(filakParam1);
    if PreOrdenar <> 0 then
    begin
      if PreOrdenar > 0 then
        QuickSort_Creciente(filakParam1)
      else
        QuickSort_Decreciente(filaKParam1);
    end;

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

    res.Enter(kPaso);
    res[0] := ma / njs;
  end;

  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;
  acums: TDAOfNReal;
begin
  Res.Enter(0);
  param1.Enter(0);

  setlength(acums, param1.nCronicas);

  for j := 0 to res.nCronicas - 1 do
  begin
    acums[j] := Param1[j];
    Res[j] := acums[j];
  end;

  for i := 1 to param1.nPasos - 1 do
  begin
    res.Enter(i);
    param1.Enter(i);
    for j := 0 to param1.nCronicas - 1 do
    begin
      acums[j] := acums[j] + Param1[j];
      Res[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, aVal: NReal;
  acum: TDAOfNreal;
begin
  setlength(acum, res.nCronicas);

  for j := 0 to res.nCronicas - 1 do
    acum[j] := ValIni;

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

  setlength(acum, 0);
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;

  PSum: TDAOfNReal;
  av: TDAOfNReal;
  auxsum: NReal;
  iCron: integer;
  j95: integer;
  jMinError: integer;
  P95Sum: NReal;
  NCentrales: integer;
  res_nCols: integer;
  nCronicas_entrada: integer;
begin
  NCentrales := length(params);

  (*
  PSum := TCronVar.Create('PSum');
  PSum.inicializar(auxcv.nCronicas, auxcv.nPasos, auxcv.dtIni, auxcv.DurPaso_horas);
    *)
  res_nCols := res.nCronicas;
  nCronicas_entrada:= params[0].nCronicas;

  setlength(PSum, nCronicas_entrada );

  for iPaso := 0 to params[0].nPasos - 1 do
  begin
    res.Enter(iPaso);
    for iCentral := 0 to NCentrales - 1 do
      params[iCentral].Enter(iPaso);

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

    // ahora ordenamos las potencias de cada central en forma decreciente
    for iCentral := 0 to NCentrales - 1 do
      QuickSort_Decreciente( pointer(params[iCentral].GetCursorFila.pdatos_),
        nCronicas_entrada );

    // 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_entrada - 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_entrada - 1 do
    begin
      sumaPots := 0;
      for iCentral := 0 to NCentrales - 1 do
        sumaPots := sumaPots + params[iCentral][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.
  *)

   if jMinError < 0 then jMinError := 0; // solo para que funcione con una crónica.

    // ahora cargamos en los resultados, la potencia reconocida para cada
    // central.
    sumaPots := 0;
    for iCentral := 0 to NCentrales - 1 do
    begin
      res[iCentral] := params[iCentral][jMinError];
      sumaPots := sumaPots + res[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[NCentrales] := P95Sum;
    for iCentral := 0 to NCentrales - 1 do
      params[iCentral].Leave;
    res.Leave;
  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;
  aVal: NReal;

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
    res.Enter(iPaso);
    for ivar := 0 to high(params) do
      params[ivar].Enter(iPaso);

    for jCronica := 0 to hCronicas do
    begin
      max := params[0][jCronica];
      for ivar := 1 to high(params) do
      begin
        aVal := params[ivar][jCronica];
        if aVal > max then
          max := aVal;
      end;
      res[jCronica] := max;
    end;

    res.Leave;
    for ivar := 0 to high(params) do
      params[ivar].Leave;

  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;
  av: TDAofNReal;
begin

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

  setlength(idx, res.nCronicas);
  setlength(buff, length(idx));

  for iPaso := 0 to hPasos - 1 do
  begin
    res.Enter(iPaso);
    for ivar := 0 to high(params) do
      params[ivar].Enter(iPaso);

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

    res.GetCursorFila.CopyToDA(av);
    QuickSort_Creciente(av, idx);
    res.GetCursorFila.CopyFromDA(av);

    for ivar := 0 to high(self.params) do
    begin
      params[ivar].GetCursorFila.CopyToDA(tmp_buff);
      for jCronica := 0 to high(idx) do
        buff[jCronica] := tmp_buff[idx[jCronica]];
      params[ivar].GetCursorFila.CopyFromDA(buff);
    end;

    for ivar := 0 to high(params) do
      params[ivar].Leave;

  end;
  setlength(idx, 0);
  setlength(buff, 0);
  setlength(tmp_buff, 0);
  setlength(av, 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: TDAofNReal;
  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
  setlength(aprom, res.nCronicas);


  for iPaso := 0 to hPasos do
  begin
    res.Enter(iPaso);
    vclear(aprom);
    jPaso := 0;
    while (((iPaso - jPaso) >= 0) and (jPaso < NPasosPM)) do
    begin
      for ivar := 0 to high(params) do
      begin
        params[ivar].Enter(iPaso - jPaso);
        for jCronica := 0 to hCronicas do
          aprom[jCronica] := aprom[jCronica] + params[iVar][jCronica];
        //.GetVal(jCronica);
        //params[ivar].Leave; @080719xc Se rompía cuando se realizaba la PostOper sobre la misma CronVar
      end;
      Inc(jPaso);
    end;
    for jCronica := 0 to hCronicas do
      res[jCronica] := aprom[jCronica] / NPasosPM / length(Params);
    res.Leave;
    for ivar := 0 to high(params) do
      params[ivar].Leave;
  end;
  setlength(aprom, 0);
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.
