{xDEFINE PERTURBADO}
{xDEFINE CHEQUEOMEM}
{xDEFINE ESTABILIZAR_FRAMEINICIAL}
{$DEFINE SPXMEJORCAMINO}
{$DEFINE DUMP_TEXT_SIMRES}
{$DEFINE DUMP_TEXT_OPTRES}
unit ugestorsalasmh_OLD;

{$IFDEF FPC}
{$MODE Delphi}
{$ENDIF}
interface

uses
{$IFDEF CHEQUEOMEM}
  udbgutil,
{$ENDIF}
  Classes, SysUtils,
  uglobs,
  uconstantesSimSEE,
  xmatdefs,
{$IFDEF MIPSimplex}
 {$IFDEF SPXMEJORCAMINO}
  umipsimplex_mejorcamino,
 {$ELSE}
  umipsimplex,
 {$ENDIF}
{$ELSE}
  usimplex,
{$ENDIF}
  umadresuniformes,

  //  upreprocesador,
  //  uEsclavizador,
  //  uEsclavizadorSobreMuestreado,
{$IFDEF WIN32}
  ipcthrd,
  {$IFDEF FPC-LCL}
    {$IFNDEF APLICACION_CONSOLA}
  LCLIntf, lcltype,
    {$ENDIF}
  {$ELSE}
  Windows,
  {$ENDIF}
{$ELSE}
  uEmuladorWinIPC, uKeyDir,
{$ENDIF}
  //  uEsclavizadorSubMuestreado,
  winLinuxUtils,
  uAuxiliares,
  usalasdejuego,
  uversiones;

type
  {
  TGestorSalaMH esta clase es utilizada para dar "multiplicidad" a una sala para el procesamiento
  en paralelo en varios hilos.

  }
  TGestorSalaMH = class
  private
    //Variables para cálculo multi core
    SoyElPrincipal: boolean; // a TRUE si es el hilo principal (que es el que orquesta)
    oldEstrellaIni, oldEstrellaFin: integer;
    estrellaIniCalc, estrellaFinCalc: integer;  //rango de estrellas a calcular por el
    //hilo principal

    listaOptimizadores: TList;
    //Lista con los hilos TRobotOptimizadorMultiCore
    listaEventosTX: TList;              //Lista con los eventos por los que esperan
    //los hilos para continuar la ejecución
    //del próximo paso
{$IFDEF DUMP_TEXT_OPTRES}
    escritor: TThread;
    //castear a TRobotEscritorOptimizadorMultiCore para usar
    //      semPasoEscritura: TMutex;           //semáforo para acceder a la variable global
    //del paso que va escribiendo el escritor
{$ENDIF}
    semCntCalculando: TMutex;           //semáforo para acceder al contador de
    //hilos calculando
    semTareas: TMutex;
    //semáforo para acceder a las tareas que aún quedan por calcular
{$IFDEF WIN32}
{$IFDEF DUMP_TEXT_OPTRES}
    evPasoCompletoEscritorTx,
{$ENDIF}
    evPasoCompletoCalculoRx: TEvent;
{$ELSE}
{$IFDEF DUMP_TEXT_OPTRES}
    evPasoCompletoEscritorTx: TEventTX;
{$ENDIF}
    evPasoCompletoCalculoRx: TEventRX;
    //evento para notificar al hilo principal
{$ENDIF}
    //que se termino de calcular el frame del paso
    pasosAutomaticos: boolean;
    //Fin variables para cálculo multi core
  public
    sala: TSalaDeJuego;
    nCores: integer;                    //cantidad de Nucleos en el equipo.
    //Por defecto es 1
    nHilosForzados: integer;
    //-1 significa libre, otro valor la cantidad de hilos a usar
    nTareasForzadas: integer;
    //-1 significa libre, otro valor la cantidad de tareas a calcular por paso
    nTareasCalculadasEstePaso: integer;
    reenganchar: boolean;
    //true si un hilo no calculo ninguna estrella en un paso
    nTareas: integer;                   //Número de particiones del frame
    nHilos: integer;                    //Número de hilos realizando los cálculos

    constructor Create(sala: TSalaDeJuego); { Guarda el puntero a la sala y obtiene la cantidad de núcles en el sistema.
      Pero no hace más nada }

    constructor Clonar(gestorSala: TGestorSalaMH; abortarEnError: boolean);
    procedure cargarSala(archiSala, nombre_escenario_activo: string;
      abortarEnError: boolean);
    //Copia las variables que puedan ser modificadas en el formulario del simulador
    //entre que se carga la sala y que se le da optimizar
    procedure copiarVarsTiempoEjecucion(gestorSala: TGestorSalaMH);

    procedure darPaso;

    //      procedure asignarSemillasIniciales(semillaDelPaso: Integer);

    procedure calcularRangoEstrellasMultiCore(estrellaIni, estrellaFin: integer);

    procedure llenarRangoEstrellas(estrellaIni: integer;
      costosFuturos: TDAOfNReal; kpaso: integer);

    //si la sala esta en un paso mayor o igual que kpaso, copia los valores en
    //el costo futuro de kpaso + 1, queda en el paso kpaso y retorna 1
    //si la sala esta en un paso menor que kpaso no copia los valores,
    //queda en el paso que estaba y retorna 0
    //el paso resultante es en el que se va a calcular el próximo rango
    function llenarRangosEstrellasEIrAPaso(estrellasIni: TDAofNInt;
      costosFuturos: TDAOfDAOfNReal; kpaso: integer): integer;

    function getRangoEstrellas(estrellaIni, estrellaFin, paso: integer): TDAOfNReal;
    function irAPaso(nuevoPaso: integer): integer;

    procedure inicializarVariablesMultiCore(darPasoAuto: boolean);
    procedure liberarVariablesMultiCore;
    procedure asignarEstrellas(estrellaIni, estrellaFin: integer);
    procedure asignarEstadosFuentesMadreUniforme(
      const estado: TEstadoMadresUniformes);


    procedure OptimizarMultiCore;

    procedure guardarResultadosOpt(dir: string);
  end;

implementation

uses
  uRobotCalculoOptimizadorMultiCore_OLD, uRobotEscritorOptimizadorMultiCore;

constructor TGestorSalaMH.Create(sala: TSalaDeJuego);
begin
  inherited Create;
  self.SoyElPrincipal := sala <> nil;
  self.sala := sala;
  self.nCores := winLinuxUtils.GetSystemCoreCount;
  self.nHilosForzados := -1;
  self.nHilos := 0;
  self.nTareasForzadas := -1;
  self.pasosAutomaticos := True;
end;

constructor TGestorSalaMH.Clonar(gestorSala: TGestorSalaMH; abortarEnError: boolean);
begin
  inherited Create;
  self.cargarSala(gestorSala.sala.archiSala,
    gestorSala.sala.EscenarioActivo.nombre, abortarEnError);
  self.nCores := gestorSala.nCores;
  Self.nHilosForzados := gestorSala.nHilosForzados;
  self.nTareasForzadas := gestorSala.nTareasForzadas;
  self.nHilos := gestorSala.nHilos;
  self.pasosAutomaticos := gestorSala.pasosAutomaticos;
  self.SoyElPrincipal := False;
end;

procedure TGestorSalaMH.cargarSala(archiSala, nombre_escenario_activo: string;
  abortarEnError: boolean);
begin
  if sala <> nil then
    sala.Free;
  sala := TSalaDeJuego.cargarSala(archiSala, nombre_escenario_activo, abortarEnError);
{  self.nCores:= winLinuxUtils.GetSystemCoreCount;
  self.nHilosForzados:= -1;
  self.nHilos:= 0;
  self.pasosAutomaticos:= true;
  self.sala.escribirOptActores:= false;}
end;

procedure TGestorSalaMH.copiarVarsTiempoEjecucion(gestorSala: TGestorSalaMH);
begin

  self.sala.globs.NMAX_ITERACIONESDELPASO_OPT :=
    gestorSala.sala.globs.NMAX_ITERACIONESDELPASO_OPT;
  self.sala.globs.NMAX_ITERACIONESDELPASO_SIM :=
    gestorSala.sala.globs.NMAX_ITERACIONESDELPASO_SIM;

  self.sala.globs.SortearOpt := gestorSala.sala.globs.SortearOpt;
  self.sala.escribirOptActores := gestorSala.sala.escribirOptActores;
  self.sala.globs.TasaDeActualizacion := gestorSala.sala.globs.TasaDeActualizacion;
  self.sala.globs.NCronicasOpt := gestorSala.sala.globs.NCronicasOpt;
  self.sala.globs.semilla_inicial_opt := gestorSala.sala.globs.semilla_inicial_opt;
  self.sala.globs.semilla_inicial_sim := gestorSala.sala.globs.semilla_inicial_sim;
end;

(*
procedure TGestorSalaMH.asignarSemillasIniciales(semillaDelPaso: Integer);
var
  i: Integer;
begin
  self.sala.globs.fijarSemillaAleatoria(semillaDelPaso);
  if listaOptimizadores <> NIL then
  begin
    for i:= 0 to listaOptimizadores.Count - 1 do
      TRobotCalculoOptimizadorMultiCore(listaOptimizadores[i]).gestorSala.sala.globs.fijarSemillaAleatoria(semillaDelPaso);
  end;
end;
  *)

procedure TGestorSalaMH.calcularRangoEstrellasMultiCore(estrellaIni,
  estrellaFin: integer);
var
  i: integer;
  fuiElUltimo: boolean;
begin
  sala.globs.procNot(sala.globs.procNot_opt_InicioCalculosDeEtapa);
  if (oldEstrellaIni <> estrellaIni) or (oldEstrellaFin <> estrellaFin) then
  begin
    oldEstrellaIni := estrellaIni;
    oldEstrellaFin := estrellaFin;

    asignarEstrellas(estrellaIni, estrellaFin);
  end;
  fuiElUltimo := False;
  uRobotCalculoOptimizadorMultiCore_OLD.iProximaTarea := 0;

  //No mutuoexcluyo porque se que no hay nadie
  uRobotCalculoOptimizadorMultiCore_OLD.cnt_RobotsCalculando :=
    listaOptimizadores.Count + 1;


  for i := 0 to listaEventosTX.Count - 1 do
  begin
{$IFDEF WIN32}
    TEvent(listaEventosTX[i]).Signal;
{$ELSE}
    TEventTX(listaEventosTX[i]).Signal;
{$ENDIF}
  end;

  if sala.globs.abortarSim or not semTareas.Get(toSpinlocks) then
  begin
    writeln('TGestorSalaMH.calcularRangoEstrellasMultiCore: No obtuve semTareas 1');
    sala.globs.abortarSim := True;
  end
  else
  begin
    while iProximaTarea < Length(tareas) do
    begin
      sala.globs.iTareaEjecutando := iProximaTarea;
      iProximaTarea := iProximaTarea + 1;
      semTareas.Release;
      self.estrellaIniCalc := tareas[sala.globs.iTareaEjecutando].estrellaIni;
      self.estrellaFinCalc := tareas[sala.globs.iTareaEjecutando].estrellaFin;

      sala.calcularRangoEstrellas(estrellaIniCalc, estrellaFinCalc, False, False);
      nTareasCalculadasEstePaso := nTareasCalculadasEstePaso + 1;
      if sala.globs.abortarSim or not semTareas.Get(toSpinlocks) then
      begin
        writeln('TGestorSalaMH.calcularRangoEstrellasMultiCore: No obtuve semTareas 2');
        sala.globs.abortarSim := True;
        break;
      end;
    end; //while de las tareas
    semTareas.Release;
  end;

  if sala.globs.abortarSim or not semCntCalculando.Get(6000) then
  begin
    writeln('TGestorSalaMH.calcularRangoEstrellasMultiCore: No obtuve semCntCalculando');
    sala.globs.abortarSim := True;
  end;

  if cnt_RobotsCalculando = 1 then
    fuiElUltimo := True
  else
    cnt_RobotsCalculando := cnt_RobotsCalculando - 1;
  //    writeln('hiloPrincipal:', cntCalculando);
  semCntCalculando.Release;

  if sala.globs.abortarSim or (not fuiElUltimo and not
    evPasoCompletoCalculoRx.Wait(6000000)) then //Tiempo para completar el cálculo
  begin
    writeln('TGestorSalaMH.calcularRangoEstrellasMultiCore: No me despertaron de evPasoCompleto');
    sala.globs.abortarSim := True;
  end;

  if sala.globs.abortarSim then
  begin
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotCalculoOptimizadorMultiCore(listaOptimizadores[i]).Terminate;
{$IFDEF DUMP_TEXT_OPTRES}
    escritor.Terminate;
{$ENDIF}
  end;
end;

procedure TGestorSalaMH.llenarRangoEstrellas(estrellaIni: integer;
  costosFuturos: TDAOfNReal; kpaso: integer);
begin
  vcopyTramoDesplazando(sala.globs.CF.constelacion.fCosto[kpaso],
    estrellaIni, costosFuturos, 0, Length(costosFuturos));
end;

function TGestorSalaMH.llenarRangosEstrellasEIrAPaso(estrellasIni: TDAofNInt;
  costosFuturos: TDAOfDAOfNReal; kpaso: integer): integer;
var
  i: integer;
begin
  //lleno si me dicen irAPaso(mi paso -1) o menos
  if sala.globs.kPaso_ - 1 >= kpaso then
  begin
    for i := 0 to high(estrellasIni) do
      //fcosto se indexa desde 1
      vcopyTramoDesplazando(sala.globs.CF.constelacion.fCosto[kPaso + 1],
        estrellasIni[i], costosFuturos[i], 0, Length(costosFuturos[i]));
  end;
  Result := irAPaso(kpaso);
end;

function TGestorSalaMH.getRangoEstrellas(estrellaIni, estrellaFin, paso: integer):
TDAOfNReal;
var
  res: TDAofNReal;
begin
  SetLength(res, estrellaFin - estrellaIni + 1);
  vcopyTramoDesplazando(res, 0, sala.globs.CF.constelacion.fCosto[paso],
    estrellaIni, estrellaFin - estrellaIni + 1);
  Result := res;
end;

function TGestorSalaMH.irAPaso(nuevoPaso: integer): integer;
var
  i: integer;
begin
  if not pasosAutomaticos then
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotCalculoOptimizadorMultiCore(listaOptimizadores[i]).gestorSala.irAPaso(
        nuevoPaso);

  if sala.globs.EstadoDeLaSala = CES_OPTIMIZANDO then
  begin
    if sala.globs.kPaso_ > nuevoPaso then
    begin
      sala.globs.SwapAuxs;
      sala.globs.Fijar_kPaso(nuevoPaso);
      sala.globs.procNot(sala.globs.procNot_opt_FinCalculosDeEtapa);
      //Writeln('nTareasCalculadasEstePaso= ', nTareasCalculadasEstePaso);
      if nTareasCalculadasEstePaso > 0 then
      begin
        reenganchar := False;
        nTareasCalculadasEstePaso := 0;
      end
      else
        reenganchar := True;
      Result := 1;
    end
    else if sala.globs.kpaso_ = nuevoPaso then
      Result := 2
    else
      Result := 0;
  end
  else //if globs.EstadoDeLaSala = CES_SIMULANDO
  begin
    if sala.globs.kPaso_ < nuevoPaso then
    begin
      sala.globs.Fijar_kPaso(nuevoPaso);
      sala.globs.SwapAuxs;
      //      globs.procNot( globs.procNot_opt_FinCalculosDeEtapa);
      Result := 1;
    end
    else if sala.globs.kPaso_ = nuevoPaso then
      Result := 2
    else
      Result := 0;
  end;
end;

procedure TGestorSalaMH.darPaso;
var
  i: integer;
begin

  sala.globs.SwapAuxs;

  sala.globs.Fijar_kPaso(sala.globs.kPaso_ - 1);
  if not pasosAutomaticos then
    //Los robots no tienen optimizadores en su listaOptimizadores
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotCalculoOptimizadorMultiCore(listaOptimizadores[i]).gestorSala.darPaso;

  //Writeln('nTareasCalculadasEstePaso= ', nTareasCalculadasEstePaso);
  if nTareasCalculadasEstePaso > 0 then
  begin
    reenganchar := False;
    nTareasCalculadasEstePaso := 0;
  end
  else
    reenganchar := True;
end;

procedure TGestorSalaMH.inicializarVariablesMultiCore(darPasoAuto: boolean);
var
  i: integer;
  robot: TRobotCalculoOptimizadorMultiCore;
begin
  if nHilosForzados = -1 then
    nHilos := nCores
  else
    nHilos := nHilosForzados;

  sala.globs.Alerta('Optimización multihilo, Nucleos: ' + IntToStr(
    nCores) + ', Hilos: ' + IntToStr(nHilos));
  sala.globs.deshabilitarAlertas;
  //Para que se asignen las estrellas en el primer paso
  oldEstrellaIni := -1;
  oldEstrellaFin := -1;
  pasosAutomaticos := darPasoAuto;

  uRobotEscritorOptimizadorMultiCore.abortar_calculo := False;
  uRobotEscritorOptimizadorMultiCore.ultimoPasoCalculado := sala.globs.NPasos + 1;
{$IFDEF DUMP_TEXT_OPTRES}
  escritor := TRobotEscritorOptimizadorMultiCore.Create(self,
    sala.globs.CF.constelacion.fCosto);
{$ENDIF}
  //Inicializo variables de sincronizacion
  //Usaremos ev paso completo para que los robots avisen que se terminaron de
  //construir. En particular nos interesa saber que los TEventRX de los robots
  //estan creados para poder crear los TEventTX para despertarlos
  cnt_RobotsCalculando := nHilos - 1;
{$IFDEF Win32}
  semCntCalculando := TMutex.Create(nomSemCntCalculando);
  semTareas := TMutex.Create(nomSemTareas);
  evPasoCompletoCalculoRx := TEvent.Create(nombreEvPasoCompletoCalculo, False);
{$IFDEF DUMP_TEXT_OPTRES}
  //semPasoEscritura:= TMutex.Create(nomSemPasoEscritura);
  evPasoCompletoEscritorTx := TEvent.Create(nomEvPasoCompletoEscritor, False);
{$ENDIF}
{$ELSE}
  if not FileExists(nomSemCntCalculando) then
    CrearArchiKeyString(nomSemCntCalculando);
  if not FileExists(nomSemTareas) then
    CrearArchiKeyString(nomSemTareas);
{  if not FileExists(nomSemPasoEscritura) then
    CrearArchiKey(nomSemPasoEscritura);}
  if not FileExists(nombreEvPasoCompletoCalculo) then
    CrearArchiKeyString(nombreEvPasoCompletoCalculo);
  if not FileExists(nomEvPasoCompletoEscritor) then
    CrearArchiKeyString(nomEvPasoCompletoEscritor);
  if not FileExists(nombreEvHiloProximoPaso) then
    CrearArchiKeyString(nombreEvHiloProximoPaso);

  semCntCalculando := TMutex.Create(nomSemCntCalculando, 1);
  semTareas := TMutex.Create(nomSemTareas, 1);
  evPasoCompletoCalculoRx := TEventRX.Create(nombreEvPasoCompletoCalculo, 1);

{$IFDEF DUMP_TEXT_OPTRES}
  //  semPasoEscritura:= TMutex.Create(nomSemPasoEscritura, 1);
  evPasoCompletoEscritorTx := TEventTX.Create(nomEvPasoCompletoEscritor, 1);
{$ENDIF}
{$ENDIF}

  listaOptimizadores := TList.Create;
  listaOptimizadores.Capacity := nHilos - 1;
  listaEventosTX := TList.Create;
  if nHilos > 1 then
  begin
    listaEventosTX.Capacity := listaOptimizadores.Capacity;
    //el -2 es porque un hilo va a ser el principal.
    //En la lista van a quedar nHilos -1 robots y va a estar tambien el hilo principal
    for i := 0 to nHilos - 2 do
    begin
      robot := TRobotCalculoOptimizadorMultiCore.Create(self,
        sala.globs.CF.constelacion.fCosto, darPasoAuto, i);
      listaOptimizadores.Add(robot);
    end;

    if evPasoCompletoCalculoRx.Wait(60000) then  // espero a que se creen los robots
    begin
      //Se hace en dos pasadas porque los eventos RX deben estar creados antes que los
      //eventos TX
      for i := 0 to nHilos - 2 do
      begin
        robot := listaOptimizadores[i];
    {$IFDEF WIN32}
        listaEventosTX.Add(TEvent.Create(nombreEvHiloProximoPaso +
          IntToStr(robot.iRobotEnLaLista), False));
    {$ELSE}
        listaEventosTX.Add(TEventTX.Create(nombreEvHiloProximoPaso,
          robot.iRobotEnLaLista));
    {$ENDIF}
      end;
    end
    else
      sala.globs.abortarSim := True;
  end;
  nTareasCalculadasEstePaso := 0;
  self.reenganchar := False;
  sala.globs.habilitarAlertas;
end;

procedure TGestorSalaMH.liberarVariablesMultiCore;
var
  i: integer;
begin
  if listaOptimizadores <> nil then
  begin
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotCalculoOptimizadorMultiCore(listaOptimizadores[i]).Terminate;

    if listaEventosTX <> nil then
    begin
      for i := 0 to listaEventosTX.Count - 1 do
      begin
    {$IFDEF WIN32}
        TEvent(listaEventosTX[i]).Signal;
        TEvent(listaEventosTX[i]).Free;
    {$ELSE}
        TEventTX(listaEventosTX[i]).Signal;
        TEventTX(listaEventosTX[i]).Free;
    {$ENDIF}
        TRobotCalculoOptimizadorMultiCore(listaOptimizadores[i]).WaitFor;
        TRobotCalculoOptimizadorMultiCore(listaOptimizadores[i]).Free;
      end;
      listaEventosTX.Free;
    end;
    listaOptimizadores.Free;
  end;
{$IFDEF DUMP_TEXT_OPTRES}
  escritor.Free;
{$ENDIF}

  if semCntCalculando <> nil then
    semCntCalculando.Free;
  if semTareas <> nil then
    semTareas.Free;
  if evPasoCompletoCalculoRx <> nil then
    evPasoCompletoCalculoRx.Free;
{$IFDEF DUMP_TEXT_OPTRES}
  //  if semPasoEscritura <> NIL then semPasoEscritura.Free;
  if evPasoCompletoEscritorTx <> nil then
    evPasoCompletoEscritorTx.Free;
{$ENDIF}

(*
{$IFNDEF WIN32}
  if FileExists(nomSemCntCalculando) then
    DeleteFile(nomSemCntCalculando);
  if FileExists(nomEvPasoCompleto) then
    DeleteFile(nomEvPasoCompleto);
  if FileExists(nombreEvHiloProximoPaso) then
    DeleteFile(nombreEvHiloProximoPaso);
{$ENDIF}
*)
end;

procedure TGestorSalaMH.asignarEstrellas(estrellaIni, estrellaFin: integer);
var
  i, iEstrIni, iEstrFin: integer;
  nEstrellas, nEstrellasPorTarea, nTareasConUnaEstrellaMas: integer;
begin
  nEstrellas := estrellaFin - estrellaIni + 1;
  //TODO
  if nTareasForzadas <> -1 then
    nTareas := nTareasForzadas
  else
    //  nTareas:= nEstrellas;
    //  nTareas:= 16 * nHilos;
    //    nTareas:= 4 * nHilos;
    //  nTareas:= 2 * nHilos;
    nTareas := nHilos;

  nEstrellasPorTarea := nEstrellas div nTareas;
  nTareasConUnaEstrellaMas := nEstrellas mod nTareas;

  SetLength(tareas, nTareas);

  iEstrIni := estrellaIni;
  if nTareasConUnaEstrellaMas > 0 then
  begin
    iEstrFin := estrellaIni + nEstrellasPorTarea;
    for i := 0 to nTareasConUnaEstrellaMas - 1 do
    begin
      tareas[i].estrellaIni := iEstrIni;
      tareas[i].estrellaFin := iEstrFin;

      iEstrIni := iEstrFin + 1;
      iEstrFin := iEstrIni + nEstrellasPorTarea;
    end;
    iEstrIni := nTareasConUnaEstrellaMas * (nEstrellasPorTarea + 1);
    iEstrFin := iEstrIni + nEstrellasPorTarea - 1;
  end
  else
    iEstrFin := estrellaIni + nEstrellasPorTarea - 1;

  for i := nTareasConUnaEstrellaMas to nTareas - 1 do
  begin
    tareas[i].estrellaIni := iEstrIni;
    tareas[i].estrellaFin := iEstrFin;

    iEstrIni := iEstrFin + 1;
    iEstrFin := iEstrIni + nEstrellasPorTarea - 1;
  end;
end;

procedure TGestorSalaMH.asignarEstadosFuentesMadreUniforme(
  const estado: TEstadoMadresUniformes);
var
  i: integer;
begin
  self.sala.globs.MadresUniformes.setEstado(estado);
  if listaOptimizadores <> nil then
  begin
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotCalculoOptimizadorMultiCore(
        listaOptimizadores[i]).gestorSala.asignarEstadosFuentesMadreUniforme(estado);
  end;
end;

procedure TGestorSalaMH.OptimizarMultiCore;
begin
  try
    if sala.inicializarOptimizacion(nil, nil) = 0 then
      exit; // No hay variables de estado = no hay optimización
    if sala.globs.EstadoDeLaSala = CES_OPTIMIZANDO then
    begin
      // inicializa las variables y crea un robot por thread
      inicializarVariablesMultiCore(True);

      sala.globs.procNot(sala.globs.procNot_opt_InicioOptimizacion);
      while (sala.globs.kPaso_ > 0) and (not sala.globs.abortarSim) do
      begin
        calcularRangoEstrellasMultiCore(0, sala.globs.CF.nEstrellasPorPuntoT - 1);

{$IFDEF DUMP_TEXT_OPTRES}
        uRobotEscritorOptimizadorMulticore.ultimoPasoCalculado := sala.globs.kpaso_;
        if TRobotEscritorOptimizadorMultiCore(escritor).pasoAEscribir >=
          uRobotEscritorOptimizadorMultiCore.ultimoPasoCalculado then
          evPasoCompletoEscritorTx.Signal;
{$ENDIF}
        darPaso;
        sala.globs.procNot(sala.globs.procNot_opt_FinCalculosDeEtapa);
      end;
      sala.FinOptimizacion;
    end;
    if not sala.globs.abortarSim then
    begin
{$IFNDEF DUMP_TEXT_OPTRES}
      guardarResultadosOpt(sala.dirResultadosCorrida);
{$ELSE}
      //evPasoCompletoEscritorTx.Signal;
      escritor.WaitFor;
{$ENDIF}
      sala.globs.EstadoDeLaSala := CES_OPTIMIZACION_TERMINADA;
    end
    else
      sala.globs.EstadoDeLaSala := CES_OPTIMIZACION_ABORTADA;
    liberarVariablesMultiCore;
  except
    sala.globs.EstadoDeLaSala := CES_OPTIMIZACION_ABORTADA;
    sala.globs.abortarSim := True;
    liberarVariablesMultiCore;
    raise;
  end;
end;




procedure TGestorSalaMH.guardarResultadosOpt(dir: string);
var
  archi: string;
  fsal: TextFile;
  costosFuturosDelPaso: TDAofNReal;
  i, k: integer;
  nPasos: integer;
  linea: string;
begin
  if dir[Length(dir)] = DirectorySeparator then
  begin
    if sala.globs.SortearOpt then
      archi := dir + 'optres_' + IntToStr(sala.globs.semilla_inicial_opt) +
        'x' + IntToStr(sala.globs.NCronicasOpt) + '.xlt'
    else
      archi := dir + 'optres_VE.xlt';
  end
  else
  begin
    if sala.globs.SortearOpt then
      archi := dir + DirectorySeparator + 'optres_' + IntToStr(sala.globs.semilla_inicial_opt) +
        'x' + IntToStr(sala.globs.NCronicasOpt) + 'Sorteos.xlt'
    else
      archi := dir + DirectorySeparator + 'optres_VE.xlt';
  end;

  sala.globs.Alerta('Escribiendo resultados en: ' + archi);
  assignfile(fsal, archi);
  rewrite(fsal);
  try
    writeln(fsal, 'Versión del simulador:'#9, vSimSEESimulador);
    writeln(fsal, 'fActPaso:', #9, FloatToStrF(sala.globs.fActPaso,
      ffGeneral, 12, 10));
    sala.globs.CF.constelacion.PrintDefsToText(fsal, True);
    nPasos := sala.globs.calcNPasosOpt;

    Write(fsal, 'paso\estado'#9'Fecha');
    for k := 1 to sala.globs.CF.nEstrellasPorPuntoT do
      Write(fsal, #9, k);
    writeln(fsal);

    for i := 0 to nPasos do
    begin
      sala.globs.Fijar_kPaso(nPasos - (i - 1));
      costosFuturosDelPaso := sala.globs.CF.constelacion.fCosto[nPasos - (i - 1)];
      linea := IntToStr(sala.globs.kpaso_) + #9 +
        sala.globs.FechaInicioDelpaso.AsISOStr + #9 +
        FloatToStrF(costosFuturosDelPaso[0], ffGeneral, 6, 2);
      for k := 1 to high(costosFuturosDelPaso) do
        linea := linea + #9 + FloatToStrF(costosFuturosDelPaso[k], ffGeneral, 6, 2);
      writeln(fsal, linea);
    end;
    sala.globs.CF.StoreInArchi(dir + DirectorySeparator + 'CF.bin');
  finally
    CloseFile(fsal);
  end;
end;


end.
