unit uTSimRes;

interface

uses
  Math,
  xMatDefs,
  uauxiliares,
  uConstantesSimSEE,
  SysUtils, Classes;

const
  CLargoBufferForRead = 10240; //  el largo estandar es 128, el máximo admisible 65355

const
  strUsarArchivoDeCorrida =
    '{$carpetaCorrida}simres_{$semillaSim}_{$escenario}_d*.xlt';
  OLD_strUsarArchivoDeCorrida =
    '{$carpetaCorrida}simres_{$semillaSim}x{$nCronicasSim}_{$escenario}.xlt';


type
  TResumenActor = class
  public
    Clase: string;
    nombre, infoAd: string;
    constructor Create(const nombre, clase, infoAd: string);
  end;

  TDAOfResumenActor = array of TResumenActor;

  TEncabezadoSimRes = class
  public
    version_SimSEE: string;
    cnt_Lineas: integer;
    cnt_LineaPrimerCronica: integer;
    dtIniSim, FechaIni, FechaFin: TDateTime;
    nCronicas: integer;
    nPasos, nPostes, nActores, nCols: integer;
    durpos: TDAofNReal;
    durPaso_: NReal;
    invDurPaso: NReal;
    resumenActores: TDAOfResumenActor;
    actoresPorColumnas: TStringList;
    unidadesPorColumnas: TStringList;
    variablesPorColumnas: TStringList;
    iCronica, rSeed: integer;
    constructor Create(var f: textfile);
    procedure Free; virtual;

    // auxiliares
    procedure SplitNomVar(const s: string; var nomActor, nomVar, unidades: string);
    procedure JoinNomVar(const nomActor, nomVar: string; var s: string);
  private
    procedure readln(var f: textfile; var linea: string);
  end;



  { TResultadoSim }
  TResultadoSim = class
  public
    archiSimRes: string;
    BufferForRead: array[0.. CLargoBufferForRead - 1] of byte;
    f: textfile;
    iCronica, rSeed: integer;
    rec: TDAOfNReal;
    //    iCronica_base: integer;

    encabezado_: TEncabezadoSimRes;



    {$IFDEF pruebasDistSimRes3}
    procedure imprimir;
    {$ENDIF}

    constructor Create(const simres: string);

    procedure reset; virtual;
    procedure First; virtual;
    // lee el siguiente record
    procedure Next; virtual;
    function EOF: boolean; virtual;
    function nCols: integer;
    function nombreColumna(iColumna: integer): string;
    procedure nomActUniVarColumna(iColumna: integer;
      var nomActor, nomVar, unidades: string);

    //Retornan el numero de la columna identificada por nomActor, nomVar y unidades
    //La primer versión recibe el id joineado con JoinNomVar, la segunda recibe
    //los valores por separado
    function columnaActUniVar(const idActUniVar: string): integer; overload;
    function columnaActUniVar(const nomActor, nomVar: string): integer; overload;

    procedure Free; virtual;

    // retorna el número de paso correspondiente a una fecha
    // el número de paso va de 1 a NPasos.
    function kPasoInicioParaFecha(fecha: TDateTime): integer;
    function fechaDeInicioDelPaso(kPaso: integer): TDateTime;

    function suma(const indices: array of integer): NReal;
    function maximo(const indices: array of integer): NReal;
    procedure sumaConSigno(const indices: array of integer;
      var sumaPos, sumaNeg: NReal);
    function combinar(const indices: TMatOfNInt; coefs: TDAofNReal): NReal;
    function promedio(const indices: array of integer): NReal;

    // indices debe tener los indices de las columnas de la variable
    // para cada uno de los postes ordenados en forma creciente
    // retorna la suma producto de las variables por la duración de los postes
    function sumaproductocondurpos(const indices: array of integer): NReal;

    procedure sumaproductocondurposConSigno(const indices: array of integer;
      var sumaPos, sumaNeg: NReal);

    // Similar a la anterior, pero solamente suma hasta el poste kPosteHasta inclusive
    // Por ejemplo si kposteHasta= 3 se realizará la suma
    // producto solamente para los postes 1, 2 y 3.
    // Esto sirve para tener la suma por ejemplo de las HORAS FIRMES que se
    // definen de acuerdo al reglamento como las horas fuera del valle.
    function sumaproductoconDurposHasta(const indices: array of integer;
      kPosteHasta: integer): NReal;

    // indices debe tener los indices de las columnas de la variable
    // para cada uno de los postes ordenados en forma creciente
    // retorna el promedio ponderado de las variables por la duración de los postes
    // (v1 * durpos[1] + v2 * durpos[2] + ... + vn * durpos[n]) / sum(durpos)
    function promedioPonderadoPorDurpos(const indices: array of integer): NReal;

    // sum( min(A[k],TopeDeA)*DurPos[k] )
    // en recorte devuelve la suma de los recortes de aplicar el tope
    function sumaProductoConDurposTopeado(const indicesA: array of integer;
      TopeDeA: NReal; var Recorte: NReal): NReal;

    // sum( min(A[k],TopeDeA)*B[k]*DurPos[k] )  útil para cálculo de ingresos
    // en recorte devuelve la suma de los recortes de aplicar el tope
    function sumaDobleProductoConDurposTopeado(
      const indicesA, indicesB: array of integer; TopeDeB: NReal;
      var Recorte: NReal; modoComparativo, flg_acumProducto : boolean): NReal;

    // operaciones multitramo
    function suma_m(const indices: array of TDAOfNInt): NReal;
    function promedio_m(const indices: array of TDAOfNInt): NReal;
    function sumaProductoConDurpos_m(const indices: array of TDAOfNInt): NReal;
    function promedioPonderadoCondurPos_m(const indices: array of TDAOfNInt): NReal;
    function maximo_m(const indices: array of TDAOfNInt): NReal;

    // busca el actor y la variable y retorna el array de índices
    // correspondientes a los postes 1, .... NPostes
    function getIndices(const actor, variable: string): TDAOfNInt;
    function ncronicas: integer; virtual;
    function npasos: integer; virtual;
    function durpaso_: NReal; virtual;

  private
    r_next: string;
    procedure readln(var f: textfile; var linea: string);
  end;



  { TResultadosSim_MultiARchivo }
  TResultadosSim_MultiARchivo = class(TResultadoSim)
  private
    function parche_Inteto_LEER_Sim_SEMxNCRONICAS_OldStyle(
      var archisSimRes_lst: TStringList; const archisComodin: string): integer;
  public
    Carpeta: string;
    archisSimRes_lst: TStringList;
    arr_encabezados: array of TEncabezadoSimRes;
    nCronicasTotales: integer;
    iFile: integer; // archivo activo.

    constructor Create(const archisComodin: string);

    procedure reset; override;
    procedure First; override;
    // lee el siguiente record
    procedure Next; override;
    function EOF: boolean; override;
    procedure Free; override;
    function ncronicas: integer; override;
    function npasos: integer; override;
    function durpaso_: NReal; override;
  end;

//Retorna el texto que haya hasta el proximo tab o el final de linea
function nextTab(var s: string): string;



// Recibe una mascara de archivo de corrida del tipo:
// C:\SimSEE\rundir\NOMBRE_SALA\simres_31_base_*.xlt
// y retorna la lista de nombres de archivos archivos a procesar.
// En Carpeta retorna el camino a los archivos.
function ArchisSim_lst(var carpeta: string; archiCorrida: string): TStringList;

// Recibe una lista de archivos de resultados de simulación y los
// ordena por orden consecutivo de los ordinales de las crónicas.
// retorna la cantidad total de crónicas.
// Si hay repetidos o huecos retona - (menos)
// la cantidad de crónicas.
// Si la lista contiene nombres no identificables como archivos de Simulación
// tira una Excepción.
function SortAndCountCronicas(archisSim: TStringList): integer;

implementation

// simres_31_base_d00001a00013h4.xlt
function get_kCronicas(var kCronicaDesde, kCronicaHasta: integer;
  archi: string): boolean;
var
  s, sCron: string;
  res: boolean;
  resCod: integer;
  i: integer;
begin
  s := archi;
  kCronicaDesde := -1;
  kCronicaHasta := -1;
  res := False;

  i := pos('_d', s);
  while i > 0 do
  begin
    res := True;
    Delete(s, 1, i + 1);
    i := pos('_d', s);
  end;
  if not res then
  begin
    Result := False;
    exit;
  end;

  i := pos('a', s);
  if i <> 6 then
  begin
    Result := False;
    exit;
  end;
  sCron := copy(s, 1, 5);
  Delete(s, 1, 6);
  i := 1;
  while (sCron[i] = '0') and (i < 5) do
    Inc(i);
  Delete(sCron, 1, i - 1);
  val(sCron, kCronicaDesde, resCod);
  if resCod <> 0 then
  begin
    Result := False;
    exit;
  end;

  i := pos('h', s);
  if i <> 6 then
  begin
    Result := False;
    exit;
  end;
  sCron := copy(s, 1, 5);
  Delete(s, 1, 6);
  i := 1;
  while (sCron[i] = '0') and (i < 5) do
    Inc(i);
  Delete(sCron, 1, i - 1);
  val(sCron, kCronicaHasta, resCod);
  if resCod <> 0 then
  begin
    Result := False;
    exit;
  end;
  Result := True;
end;


// Recibe una lista de archivos de resultados de simulación y los
// ordena por orden consecutivo de los ordinales de las crónicas.
// retorna la cantidad total de crónicas.
// Si hay repetidos o huecos retona - (menos)
// la cantidad de crónicas.
// Si la lista contiene nombres no identificables como archivos de Simulación
// tira una Excepción.
function SortAndCountCronicas(archisSim: TStringList): integer;
var
  k: integer;
  kCronDesde, kCronHasta: integer;
  jCronDesde, jCronHasta: integer;
  nCronicas: integer;
  flg_BloquesConsecutivos: boolean;
begin
  if archisSim.Count = 0 then
  begin
    Result := 0;
    exit;
  end;

  archisSim.Sort;

  kCronDesde := -1;
  kCronHasta := -1;
  jCronDesde := -1;
  jCronHasta := -1;

  k := 0;
  flg_BloquesConsecutivos := True;
  if not get_kCronicas(kCronDesde, kCronHasta, archisSim[k]) then
    raise Exception.Create('SortByCronica Lista de Archivos Formato no reconocible(' +
      archisSim[k] + ')');

  nCronicas := kCronHasta - kCronDesde + 1;

  for k := 1 to archisSim.Count - 1 do
  begin
    if not get_kCronicas(jCronDesde, jCronHasta, archisSim[k]) then
      raise Exception.Create('SortByCronica Lista de Archivos Formato no reconocible(' +
        archisSim[k] + ')');
    nCronicas := nCronicas + (jCronHasta - jCronDesde + 1);
    flg_BloquesConsecutivos :=
      flg_BloquesConsecutivos and (jCronDesde = (kCronHasta + 1));
    kCronDesde := jCronDesde;
    kCronHasta := jCronHasta;
  end;

  if flg_BloquesConsecutivos then
    Result := nCronicas
  else
    Result := -nCronicas;

end;


function ArchisSim_lst(var carpeta: string; archiCorrida: string): TStringList;
var
  res: TStringList;
  Info: TSearchRec;

begin
  carpeta := ExtractFilePath(archiCorrida);
  res := TStringList.Create;
  if FindFirst(archiCorrida, faArchive, Info) = 0 then
  begin
    repeat
      res.add(info.Name);
      Writeln(info.Name);
    until FindNext(info) <> 0;
  end;
  FindClose(Info);
  Result := res;
end;



function IsoStrToDateTime(const s: string): TDateTime;
var
  //  anio, mes, dia, hora: string;
  anio, mes, dia, hora, minuto: integer;
begin
  //  s[11]:= ' ';
(*
  anio:= copy( s, 1, 4 );
  mes:= copy( s, 6,2 );
  dia:= copy( s, 9, 2 );
  hora:= copy( s, 12, 5 );
*)

  anio := StrToInt(copy(s, 1, 4));
  mes := StrToInt(copy(s, 6, 2));
  dia := StrToInt(copy(s, 9, 2));
  if (length(s) > 12) then
  begin
    hora := StrToInt(copy(s, 12, 2));
    minuto := StrToInt(copy(s, 15, 2));
  end
  else
  begin
    hora := 0;
    minuto := 0;
  end;
{$IFDEF FPC}
  Result := ComposeDateTime(EncodeDate(anio, mes, dia), EncodeTime(
    hora, minuto, 0, 0));
{$ELSE}
  Result := EncodeDateTime(anio, mes, dia, hora, minuto, 0, 0);
{$ENDIF}
end;


procedure TEncabezadoSimRes.readln(var f: textfile; var linea: string);
begin
  system.readln(f, linea);
  Inc(cnt_Lineas);
end;


procedure TEncabezadoSimRes.SplitNomVar(const s: string;
  var nomActor, nomVar, unidades: string);
var
  posComa: integer;
begin
  posComa := Pos('.', s);
  nomActor := Copy(s, 0, posComa - 1);
  nomVar := Copy(s, posComa + 2, Length(s) - posComa + 2);
  posComa := Pos('[', nomVar);
  unidades := Copy(nomVar, posComa + 2, length(nomVar) - posComa + 2);
  nomVar := copy(nomVar, 0, posComa - 1);
end;

procedure TEncabezadoSimRes.JoinNomVar(const nomActor, nomVar: string; var s: string);
begin
  s := nomActor + '_' + nomVar;
end;

constructor TEncabezadoSimRes.Create(var f: textfile);
var
  i: integer;
  linea, nomActor, tipoActor, infoActor: string;
  encabActores: string;
  encabUnidades: string;
  encabVariables: string;
  unidades_str: string;
begin
  cnt_Lineas := 0;
  //Version simulador
  Readln(f, linea);
  //Inicio simulación:
  //Los archivos viejos no incluían la versión del simulador
  if pos('Versión del simulador:', linea) = 1 then
  begin
    delete( linea, 1, length( 'Versión del simulador:' ) );
    version_SimSEE:=trim( linea );
    Readln(f, linea);
  end
  else
    self.version_SimSEE:= '';
  nextTab(linea);
  dtIniSim := IsoStrToDateTime(linea);

  //FechaIni FechaFin
  Readln(f, linea);
  nextTab(linea);
  FechaIni := IsoStrToDateTime(nextTab(linea));
  nextTab(linea);
  FechaFin := IsoStrToDateTime(nextTab(linea));
  //NCronicas
  Readln(f, linea);
  nexttab(linea);
  nCronicas := NextInt(linea);
  //NPasos
  Readln(f, linea);
  nexttab(linea);
  nPasos := NextInt(linea);
  //NPostes
  Readln(f, linea);
  nexttab(linea);
  nPostes := NextInt(linea);
  SetLength(durPos, nPostes);
  //Durpos
  Readln(f, linea);
  nexttab(linea);
  for i := 0 to nPostes - 1 do
    durPos[i] := NextFloat(linea);
  durPaso_ := vsum(durPos);
  invDurPaso := 1 / durPaso_;
  //NActores
  Readln(f, linea);
  nexttab(linea);
  nActores := NextInt(linea);
  SetLength(resumenActores, nActores);
  readln(f, linea);
  for i := 0 to nActores - 1 do
  begin
    Readln(f, linea);
    nomActor := nextTab(linea);
    tipoActor := nextTab(linea);
    infoActor := nextTab(linea);
    resumenActores[i] := TResumenActor.Create(nomActor, tipoActor, infoActor);
  end;

  (********* INICIO PRIMERA CRONICA ******)
  cnt_LineaPrimerCronica := cnt_Lineas + 1;

  readln(f, linea);
  //Cronica RandSeed
  Readln(f, linea);
  nexttab(linea);
  iCronica := nextInt(linea);
  nexttab(linea);
  rSeed := nextInt(linea);

  //Actores
  Readln(f, encabActores);
  linea := Copy(encabActores, 0, length(encabActores));

  actoresPorColumnas := TStringList.Create;
  nCols := 0;
  while linea <> '' do
  begin
    Inc(nCols);
    actoresPorColumnas.Add(nextTab(linea));
  end;

  //Unidades
  readln(f, encabUnidades);
  linea := Copy(encabUnidades, 0, length(encabUnidades));
  unidadesPorColumnas := TStringList.Create;
  unidadesPorColumnas.Capacity := actoresPorColumnas.Count;

  while linea <> '' do
  begin
    unidades_str := nextTab(linea);
    if pos('[', unidades_str) = 0 then
      unidades_str := '[' + unidades_str + ']';
    unidadesPorColumnas.Add(unidades_str);
  end;

  //Variables
  readln(f, encabVariables);
  linea := Copy(encabVariables, 0, length(encabVariables));
  variablesPorColumnas := TStringList.Create;
  variablesPorColumnas.Capacity := actoresPorColumnas.Count;

  while linea <> '' do
    variablesPorColumnas.Add(nextTab(linea));

  //Postes
  readln(f, linea);
end;


procedure TEncabezadoSimRes.Free;
var
  k: integer;
begin
  setlength(durpos, 0);
  for k := 0 to high(resumenActores) do
    resumenActores[k].Free;
  setlength(resumenActores, 0);
  actoresPorColumnas.Free;
  unidadesPorColumnas.Free;
  variablesPorColumnas.Free;
  inherited Free;
end;
//------------------------
//Métodos de TResumenActor
//========================
constructor TResumenActor.Create(const nombre, clase, infoAd: string);
begin
  inherited Create();
  self.nombre := nombre;
  self.infoAd := infoAd;
  self.Clase := clase;
end;

//--------------------------
//Métodos de DTResultadoSim
//==========================

procedure TResultadoSim.readln(var f: textfile; var linea: string);
begin
  linea := r_next;
  system.readln(f, r_next);
end;


function TResultadoSim.kPasoInicioParaFecha(fecha: TDateTime): integer;
var
  df: double;
begin
  if fecha <= encabezado_.FechaIni then
    Result := 1
  else if fecha >= encabezado_.FechaFin then
    Result := encabezado_.nPasos + 1
  else
  begin
    df := fecha - encabezado_.FechaIni;
    Result := trunc(df / (encabezado_.durPaso_ / 24.0) + 0.0001) + 1;
  end;
end;

function TResultadoSim.fechaDeInicioDelPaso(kPaso: integer): TDateTime;
begin
  Result := encabezado_.FechaIni + (kPaso - 1) * encabezado_.durPaso_ / 24.0;
end;

{$IFDEF pruebasDistSimRes3}
procedure TResultadoSim.imprimir;
var
  i: integer;
begin
  writeln(archiSimRes);
  for i := 0 to 10 do
    Write(rec[i], ' ');

  writeln;
end;

{$ENDIF}

constructor TResultadoSim.Create(const simres: string);
var
  oldFileMode: integer;
  ts: string;
begin
  {$IFDEF MULTI_ARCHIVO}
  writeln(simres);
  {$ENDIF}
  if FileExists(simres) then
  begin

    inherited Create;
    archiSimRes := simres;
    assignfile(f, simres);

    oldFileMode := filemode;
    filemode := 0;
    system.reset(f);
    filemode := oldfilemode;

    setTextBuf(f, BufferForRead, CLargoBufferForRead);

    encabezado_ := TEncabezadoSimRes.Create(f);
    setlength(rec, encabezado_.nCols);
    iCronica := encabezado_.iCronica;
    rSeed := encabezado_.rSeed;

    r_next := '';
    readln(f, ts); // solo para llenar r_next
  end
  else
    raise Exception.Create('TResultadoSim.Create: error, no se encuentra el archivo '
      + simres);
end;

procedure TResultadoSim.reset;
var
  k: integer;
  r: string;
begin
  system.reset(f);
  for k := 1 to encabezado_.cnt_LineaPrimerCronica do
    readln(f, r);
  //iCronica:= encabezado_.iCronica;
  //rSeed:= encabezado_.rSeed;
  Next;
end;

procedure TResultadoSim.First;
begin
  reset;
  Next;
end;

function TResultadoSim.EOF: boolean;
begin
  Result := system.EOF(f);
end;

procedure TResultadoSim.Next;
var
  linea: string;
  k: integer;
begin
  readln(f, linea);

  if linea = '' then
  begin
    readln(f, linea);
    nexttab(linea);
    iCronica := nextInt(linea);
(*
    writeln('i ' + IntToStr(iCronica - iCronica_base) + ' ibase ' +
      IntToStr(iCronica_base));
  *)
    nexttab(linea);
    rSeed := nextInt(linea);

    readln(f, linea);
    readln(f, linea);
    readln(f, linea);
    readln(f, linea);
    readln(f, linea);
  end;

  //writeln(copy( linea,0,15 ));
(**
  rec[0] := NextInt(linea);
  rec[1] := IsoStrToDateTime(nexttab(linea));
  for k := 2 to encabezado_.nCols - 1 do
    rec[k] := nextFloat(linea);
    **)
  rec[0] := SysUtils.StrToInt(nextTab(linea));
  rec[1] := IsoStrToDateTime(nexttab(linea));
  for k := 2 to encabezado_.nCols - 1 do
    rec[k] := SysUtils.StrToFloat(nextTab(linea));

end;



function TResultadoSim.nCols: integer;
begin
  Result := encabezado_.nCols;
end;


function TResultadoSim.nombreColumna(iColumna: integer): string;
var
  res: string;
begin
  encabezado_.JoinNomVar(encabezado_.actoresPorColumnas[iColumna],
    encabezado_.VariablesPorColumnas[iColumna],
    res);
  Result := res;
end;

procedure TResultadoSim.nomActUniVarColumna(iColumna: integer;
  var nomActor, nomVar, unidades: string);
begin
  nomActor := encabezado_.ActoresPorColumnas[iColumna];
  nomVar := encabezado_.VariablesPorColumnas[iColumna];
  unidades := encabezado_.UnidadesPorColumnas[iColumna];
end;

function TResultadoSim.columnaActUniVar(const idActUniVar: string): integer;
var
  nomAct, nomVar, unidades: string;
begin
  encabezado_.SplitNomVar(idActUniVar, nomAct, nomVar, unidades);
  Result := columnaActUniVar(nomAct, nomVar);
end;

function TResultadoSim.columnaActUniVar(const nomActor, nomVar: string): integer;
var
  res, i: integer;
begin
  res := -1;
  for i := 0 to encabezado_.actoresPorColumnas.Count - 1 do
    if (((encabezado_.actoresPorColumnas[i] = nomActor) or
      ((nomActor = '-') and (encabezado_.actoresPorColumnas[i] = 'Sala'))) and
      (encabezado_.variablesPorColumnas[i] = nomVar)) then
    begin
      res := i;
      break;
    end;
  Result := res;
end;

function TResultadoSim.getIndices(const actor, variable: string): TDAOfNInt;
var
  kPoste: integer;
  res: TDAOfNInt;
  svar: string;
begin
  setlength(res, encabezado_.nPostes);
  for kPoste := 0 to encabezado_.nPostes - 1 do
  begin
    svar := variable + '_P' + IntToStr(kPoste + 1);
    res[kPoste] := columnaActUniVar(actor, svar);
    if res[kPoste] < 0 then
    begin
      setlength(res, 1);
      res[0] := columnaActUniVar(actor, variable);
      if res[0] < 0 then
        if variable <> 'CFaux' then
          raise Exception.Create('No encontré variable: ' +
            variable + ' del actor: ' + actor)
        else
          raise Exception.Create('No encontré variable: ' +
            variable + ' del actor: ' + actor +
            '. Asegurese de haber ejecutado la simulación seleccionando un Costo Futuro Auxiliar.');
      break;
    end;
  end;
  Result := res;
end;


function TResultadoSim.ncronicas: integer;
begin
  Result := encabezado_.nCronicas;
end;

function TResultadoSim.npasos: integer;
begin
  Result := encabezado_.nPasos;
end;

function TResultadoSim.durpaso_: NReal;
begin
  Result := encabezado_.durpaso_;
end;


function TResultadoSim.promedio(const indices: array of integer): NReal;
var
  res: NReal;
  k: integer;
begin
  res := 0;
  for k := low(indices) to high(indices) do
    res := res + self.rec[indices[k]];
  Result := res / length(indices);
end;

function TResultadoSim.promedio_m(const indices: array of TDAOfNInt): NReal;
var
  kparte: integer;
  res: NReal;
  largoTotal: integer;
begin
  res := 0;
  largoTotal := 0;
  for kparte := 0 to high(indices) do
  begin
    largoTotal := largoTotal + length(indices[kparte]);
    res := res + suma(indices[kparte]);
  end;
  Result := res / largoTotal;
end;


function TResultadoSim.suma(const indices: array of integer): NReal;
var
  res: NReal;
  k: integer;
begin
  res := 0;
  for k := low(indices) to high(indices) do
    res := res + self.rec[indices[k]];
  Result := res;
end;

function TResultadoSim.maximo(const indices: array of integer): NReal;
var
  res: NReal;
  k: integer;
begin
  res := rec[indices[low(indices)]];
  for k := low(indices)+1 to high(indices) do
    if rec[indices[k]] > res then
      res := rec[indices[k]];
  Result := res;
end;

procedure TResultadoSim.sumaConSigno(const indices: array of integer;
  var sumaPos, sumaNeg: NReal);
var
  k: integer;
  v: NReal;
begin
  sumaPos := 0;
  sumaNeg := 0;
  for k := low(indices) to high(indices) do
  begin
    v := self.rec[indices[k]];
    if v >= 0 then
      sumaPos := sumaPos + v
    else
      sumaNeg := sumaNeg + v;
  end;
end;

function TResultadoSim.combinar(const indices: TMatOfNInt; coefs: TDAofNReal): NReal;
var
  res: NReal;
  k, j: integer;
begin
  res := 0;
  for k := low(indices) to high(indices) do
    for j := low(indices[k]) to high(indices[k]) do
      res := res + self.rec[indices[k][j]] * coefs[k];
  Result := res;
end;

function TResultadoSim.suma_m(const indices: array of TDAOfNInt): NReal;
var
  kparte: integer;
  res: NReal;
begin
  res := 0;
  for kparte := 0 to high(indices) do
    res := res + suma(indices[kparte]);
  Result := res;
end;

function TResultadoSim.sumaproductocondurpos(const indices: array of integer): NReal;
var
  res: NReal;
  kPoste: integer;
  kI: integer;
begin
  res := 0;
  for kPoste := 0 to encabezado_.nPostes - 1 do
  begin
    kI := min(kPoste, high(indices));
    res := res + rec[indices[kI]] * encabezado_.durpos[kPoste];
  end;
  Result := res;
end;

procedure TResultadoSim.sumaproductocondurposConSigno(const indices: array of integer;
  var sumaPos, sumaNeg: NReal);
var
  kPoste, kI: integer;
  v: NReal;
begin
  sumaPos := 0;
  sumaNeg := 0;
  for kPoste := 0 to encabezado_.nPostes - 1 do
  begin
    kI := min(kPoste, high(indices));
    v := rec[indices[kI]] * encabezado_.durpos[kPoste];
    if v >= 0 then
      sumaPos := sumaPos + v
    else
      sumaNeg := sumaNeg + v;
  end;
end;

function TResultadoSim.sumaproductoconDurposHasta(const indices: array of integer;
  kPosteHasta: integer): NReal;
var
  res: NReal;
  kPoste: integer;
  kI: integer;
begin
  res := 0;
  for kPoste := 0 to kposteHasta - 1 do
  begin
    kI := min(kPoste, high(indices));
    res := res + rec[indices[kI]] * encabezado_.durpos[kPoste];
  end;
  Result := res;
end;

function TResultadoSim.promedioPonderadoPorDurpos(
  const indices: array of integer): NReal;
var
  res: NReal;
  kPoste, kI: integer;
begin
  res := 0;
  for kPoste := 0 to encabezado_.nPostes - 1 do
  begin
    kI := min(kPoste, high(indices));
    res := res + rec[indices[kI]] * encabezado_.durpos[kPoste];
  end;

  Result := res * encabezado_.invDurPaso;
end;

// sum( min(A[k],TopeDeA)*DurPos[k] )
// en recorte devuelve la suma de los recortes de aplicar el tope
function TResultadoSim.sumaProductoConDurposTopeado(const indicesA: array of integer;
  TopeDeA: NReal; var Recorte: NReal): NReal;
var
  res: NReal;
  kPoste, kA: integer;
  a: NReal;
begin
  res := 0;
  recorte := 0;
  for kPoste := 0 to encabezado_.nPostes - 1 do
  begin
    kA := min(kPoste, high(indicesA));
    a := self.rec[indicesA[kA]];
    if a <= TopeDeA then
    begin
      res := res + a * encabezado_.durpos[kPoste];
    end
    else
    begin
      res := res + TopeDeA * encabezado_.durpos[kPoste];
      recorte := recorte + (a - TopeDeA) * encabezado_.durpos[kPoste];
    end;
  end;
  Result := res;
end;

function TResultadoSim.sumaDobleProductoConDurposTopeado(
  const indicesA, indicesB: array of integer; TopeDeB: NReal;
  var Recorte: NReal; modoComparativo, flg_acumProducto: boolean): NReal;
var
  res: NReal;
  kPoste: integer;
  valB: NReal;
  kA, kB: integer;
begin
  res := 0;
  recorte := 0;
  for kPoste := 0 to encabezado_.nPostes - 1 do
  begin
    kA := min(kPoste, high(indicesA));
    kB := min(kPoste, high(indicesB));

    valB := self.rec[indicesB[kB]];
    if not modoComparativo then
    begin
      if valB > TopeDeB then
      begin
        res := res + self.rec[indicesA[kA]] * TopeDeB * encabezado_.durpos[kPoste];
        recorte := recorte + self.rec[indicesA[kA]] * (valB - TopeDeB) *
          encabezado_.durpos[kPoste];
      end
      else
        res := res + self.rec[indicesA[kA]] * valB * encabezado_.durpos[kPoste];
    end
    else
    begin
      // rch@20160215 Agrego funcionalidad MODO COMPARATIVO
      //usa  el tope como un comparador y carga en Recorte la Energía asociada
      // rch@20130907 agrego fncionalidad de flg_acumProducto en modo comparativo
      if valB < TopeDeB then
      begin
        if flg_acumProducto then
         res := res + self.rec[indicesA[kA]] * encabezado_.durpos[kPoste]* valB
        else
         res := res + self.rec[indicesA[kA]] * encabezado_.durpos[kPoste];
      end
      else
      begin
        if flg_acumProducto then
         recorte := recorte + self.rec[indicesA[kA]] * encabezado_.durpos[kPoste] * valB
        else
         recorte := recorte + self.rec[indicesA[kA]] * encabezado_.durpos[kPoste] ;
      end;
    end;
  end;
  Result := res;
end;


function TResultadoSim.sumaProductoConDurpos_m(
  const indices: array of TDAOfNInt): NReal;
var
  kIndice: integer;
  res: NReal;
begin
  res := 0;
  for kIndice := 0 to high(indices) do
    res := res + sumaproductocondurpos(indices[kIndice]);
  Result := res;
end;


function TResultadoSim.promedioPonderadoCondurPos_m(
  const indices: array of TDAOfNInt): NReal;
var
  kIndice: integer;
  res: NReal;
begin
  res := 0;
  for kIndice := 0 to high(indices) do
    res := res + sumaProductoConDurpos(indices[kIndice]);
  Result := res * encabezado_.invDurPaso;
end;




function TResultadoSim.maximo_m(const indices: array of TDAOfNInt): NReal;
var
  i: integer;
  res, maxI: NReal;
begin
  //  res := -MaxNReal;
  res := 0;
  for i := 0 to High(indices) do
  begin
    maxI := maximo(indices[i]);
    res := res + maxI;
    //  if maxI > res then   res := maxI;
  end;
  Result := res;
end;

procedure TResultadoSim.Free;
begin
  setlength(rec, 0);
  //encabezado_.Free;
  closefile(f);
  inherited Free;
end;

function nextTab(var s: string): string;
var
  posTab: integer;
  res: string;
begin
  posTab := Pos(#9, s);
  if posTab <> 0 then
  begin
    res := Copy(s, 1, posTab - 1);
    Delete(s, 1, posTab);
  end
  else
  begin
    res := copy(s, 1, length(s));
    s := '';
  end;
  Result := trim(res);
end;

{
rch@201608240900
testear archivos comodines ???
Si es un nombre de archivo comodin
}

{
rch@201309292254
Si el archivo existe retorna TRUE, si no existe se fija
si tiene sin sustituir el $escenario y si es así busca si
existe un archivo con el mismo nombre de la sala y con _Base y otro
}

function Test_FileExists(var archi: string): boolean;
var
  Info: TSearchRec;
  Count: longint;
  ip, ip2: integer;
  s: string;
  old_dir: string;
  archi_dir: string;
  archi_name: string;
  archi_encontrado: string;
  time_encontrado: longint;

begin

  if fileExists(archi) then
  begin
    Result := True;
    exit;
  end;

  ip := pos('{$escenario}', archi);
  if ip = 0 then
  begin
    Result := False;
    exit;
  end;

  old_dir := getcurrentdir;

  archi_dir := ExtractFileDir(archi);
  archi_name := ExtractFileName(archi);
  setcurrentdir(archi_dir);

  ip := pos('{$escenario}', archi_name);
  ip2 := ip + length('{$escenario}');
  s := copy(archi_name, 1, ip - 1) + '*' + copy(archi_name, ip2,
    length(archi_name) - ip2 + 1);

  time_encontrado := -1;

  Count := 0;
  if FindFirst(s, faAnyFile, Info) = 0 then
  begin
    repeat
      Inc(Count);
      if (Count = 1) or (Info.Time > time_encontrado) then
      begin
        archi_encontrado := info.Name;
        time_encontrado := info.time;
      end;
    until FindNext(info) <> 0;
  end;
  FindClose(Info);
  setcurrentdir(old_dir);
  if Count = 0 then
    Result := False
  else
  begin
    archi := archi_dir + DirectorySeparator + archi_encontrado;
    Result := True;
  end;

end;


// Intenta determinar si existen resultados de corrida con la nomenclatura antigua
// de nombres de archivos y retorna el número de crónicas.
// Si no hay archivos o si hay más de uno con la misma semilla tira una excepción.
function TResultadosSim_MultiARchivo.parche_Inteto_LEER_Sim_SEMxNCRONICAS_OldStyle(
  var archisSimRes_lst: TStringList; const archisComodin: string): integer;

var
  NCronicas: integer;
  archi: string;
  k, j: integer;
  s, ssemilla: string;
  buscando: boolean;
begin

  NCronicas := 0;


  // intento ver si hay archivos de simulaciones anteriores
  archi := archisComodin;
  k := pos('\simres_', archi) + length('\simres_');
  buscando := True;
  j := k;
  while buscando and (j < length(archi)) do
  begin
    if archi[j] = '_' then
      buscando := False
    else
      Inc(j);
  end;
  if not buscando then
  begin
    ssemilla := copy(archi, k, j - k);
    k := j - 1;
    s := copy(archi, 1, k);
    Delete(archi, 1, k);
    k := pos('_d*', archi);
    archi := s + 'x*' + copy(archi, 1, k - 1) + copy(archi, k + 3, length(archi) - (k+3)+1 );
    archisSimRes_lst.Free;
    archisSimRes_lst := ArchisSim_lst(carpeta, archi);
    if archisSimRes_lst.Count = 0 then
      raise Exception.Create('No se encontraron resultados de simulación para procesar');
    if archisSimRes_lst.Count > 1 then
      raise Exception.Create(
        'Hay demasiados resultados de simulación para la semilla y caso especificados. Elimine resultados o vuelva a simular.');
    // Bien si vamos a poder procesar, determinamos la cantidad de cróncias.

    archi := archisSimRes_lst[0];
    k := pos('simres_' + ssemilla, archi);

    k := k + length('simres_' + ssemilla) + 1;
    j := k;
    buscando := True;
    while buscando and (j <= length(archi)) do
    begin
      if archi[j] = '_' then
        buscando := False
      else
        Inc(j);
    end;
    if buscando then
      raise Exception.Create(
        'No es posible encontrar resultados de simulación ... por favor ejecute la simulación antes de intentar ejecutar el SimRes3.');
    s := copy(archi, k, j - k);
    val(s, nCronicas, k);
    if k <> 0 then
      raise Exception.Create(
        'No es posible encontrar resultados de simulación ... por favor ejecute la simulación antes de intentar ejecutar el SimRes3.');
  end;
  Result := nCronicas;
end;


constructor TResultadosSim_MultiARchivo.Create(const archisComodin: string);
var
  k: integer;
  oldFileMode: byte;
  archi: string;
begin
  //La idea es tener en un array todos los encabezados, para eso abro cada
  //archivo y cargo el encabezado al array.

  archisSimRes_lst := ArchisSim_lst(carpeta, ArchisComodin);
  if archisSimRes_lst.Count > 0 then
    nCronicasTotales := SortAndCountCronicas(archisSimRes_lst)
  else
    // intenta leer resultados de corrdia MONO-Hilo en nomenclatura antigua de nombres de archivo
    nCronicasTotales := parche_Inteto_LEER_Sim_SEMxNCRONICAS_OldStyle( archisSimRes_lst, archisComodin);



  setlength(arr_encabezados, archisSimRes_lst.Count);
  for k := 0 to archisSimRes_lst.Count - 1 do
  begin
    archi := carpeta + archisSimRes_lst[k];
    if Test_FileExists(archi) then
    begin
      assignfile(f, archi);
      oldFileMode := filemode;
      filemode := 0;
      system.reset(f);
      filemode := oldfilemode;
      arr_encabezados[k] := TEncabezadoSimRes.Create(f);
      Close(f);
    end
    else
      raise Exception.Create(
        'TResultadosSim_MultiARchivo.Create: error, no se encuentra el archivo ' +
        archi);
  end;

  iFile := 0;
  //Creo el resultadoSim del primer archivo de la lista.
  inherited Create(carpeta + archisSimRes_lst[0]);
end;

procedure TResultadosSim_MultiARchivo.reset;
begin
  if iFile > 0 then
  begin
    closefile(f);
    Assign(f, archisSimRes_lst[0]);
    iFile := 0;
  end;
  inherited reset;
end;

procedure TResultadosSim_MultiARchivo.First;
begin
  inherited First;
end;

// lee el siguiente record
procedure TResultadosSim_MultiARchivo.Next;
begin
  if inherited EOF then
  begin
    if (iFile < (archisSimRes_lst.Count - 1)) then
    begin
      encabezado_ := arr_encabezados[iFile];
      closefile(f);
      Inc(iFile);
      writeln('file ' + IntToStr(iFile));
      filemode := 0;
      Assign(f, carpeta + archisSimRes_lst[iFile]);
      inherited reset;
    end;
  end
  else
    inherited Next;
end;

function TResultadosSim_MultiARchivo.EOF: boolean;
begin
  Result := (iFile >= (archisSimRes_lst.Count - 1)) and inherited EOF;
end;

procedure TResultadosSim_MultiARchivo.Free;
var
  k: integer;
begin
  for k := 0 to high(arr_encabezados) do
    arr_encabezados[k].Free;
  setlength(arr_encabezados, 0);
  inherited Free;
end;

function TResultadosSim_MultiARchivo.ncronicas: integer;
begin
  Result := nCronicasTotales;
end;

function TResultadosSim_MultiARchivo.npasos: integer;
begin
  Result := arr_encabezados[iFile].npasos;
end;


function TResultadosSim_MultiARchivo.durpaso_: NReal;
begin
  Result := arr_encabezados[iFile].durpaso_;
end;


end.
