unit uauxiliares;

{$MODE DELPHI}

{$IFDEF LINUX}
{xDEFINE REDEFINIR_RDLN}
{$ENDIF}

interface

uses
{$IFDEF LINUX}
  BaseUnix, Unix,
  errors,
{$ELSE}
  Windows,

{$ENDIF}
  SysUtils,
  process,
  syncobjs,
  zipper,
  xmatdefs, Classes;


{***********************************}
{ Constantes y Funciones auxiliares }
{-----------------------------------}
const
  { valor mínimo admisible }
  potEpsilon = 0.1;
  cpEpsilon = 0.01;

{$IFNDEF LCL}
  //Constantes para resultados de MessageBox
  idOk = 1;
  idCancel = 2;
  idAbort = 3;
  idRetry = 4;
  idIgnore = 5;
  idYes = 6;
  idNo = 7;
  IDCLOSE = 8;
  IDHELP = 9;
  IDTRYAGAIN = 10;
  IDCONTINUE = 11;
{$ENDIF}

{$IFDEF FPC-LCL}
{$IFDEF WINDOWS}
  ID_OK = idOk;
  ID_CANCEL = idCancel;
  ID_ABORT = idAbort;
  ID_RETRY = idRetry;
  ID_IGNORE = idIgnore;
  ID_YES = idYes;
  ID_NO = idNo;
  ID_CLOSE = IDCLOSE;
  ID_HELP = IDHELP;
{$ENDIF}
{$ENDIF}

type
  TEstacion = (verano, otonio, invierno, primavera);
  TFunCompare = function( a, b: Pointer ): integer;
  TFunIntercambio = function( ka, kb: integer ): boolean of object;

resourcestring
  rs_ArrayErrorDeFormato_DebeComenzarCon =
    'Formato de array inválido debe comenzar con';
  rs_ArrayErrorDeFormato_CantidadDeElementos =
    'Formato de array inválido debe tener la cantidad de elementos seguida de';
  rs_ArrayErrorDeFormato_CantidadDeElementosNoEntera =
    'Formato de array inválido. La cantidad de elementos tienen que ser un entero.';
  rs_ElValorEspecificadoEs = 'El valor ingresado es';
  rs_CaracteresEntreParentesisRectos = 'Caractéres entre paréntesis rectos';
  rs_SI = 'SI';
  rs_NO = 'NO';


const
  Separadores = [' ', ',', ';', #9, #10, #13];
  SeparadoresSinEspacio = [',', ';', #9, #10, #13];
  SeparadoresDeLineaYTab = [#9, #10, #13];
  Digitos = ['0'..'9'];
  Alfanumericos = ['0'..'9', 'a' .. 'z', 'A'.. 'Z', '_', '$'];

// Esta genérica para trancar y destrancar todo lo que precisemos.
var
  SeccionCriticaGenerica: TCriticalSection;

  // Estas variables son modificadas al hacer SetSeparadoresGlobales o Locales
  ShortDateFormat_flg_has_MMM_: boolean; // indica que en ShortDateFormat se usó MMM lo que implica
                 // que no se puede usar el DateToStr ni DateTimeToStr de sysutils pues
                 // da error. Se utiliza en ufechas para la función DateToStr y DateTimeToStr
  ShortDateFormat_pos_Dia: integer; // 1, 2 o 3 indica la posición de D en el fomato
  ShortDateFormat_pos_Mes: integer; // 1, 2 o 3 indica la posición de M en el fomato
  ShortDateFormat_pos_Anio: integer; // 1, 2 o 3 indica la posición de Y en el fomato


var // usada por nexpal
  Separadores_Activos: set of char;

function boolToSiNo(b: boolean): string;
function MesOfSem(isem: integer): integer;
function EstacionOfSem(isem: integer): TEstacion;
function EstacionOfMes(imes: integer): TEstacion;

{ si x < cpEpsilon; result:= cpEpsilon sino result:= x }
procedure cpSupEpsilon(var x: NReal);

{ si x < potEpsilon; result:= potEpsilon sino result:= x }
procedure potSupEpsilon(var x: NReal);

{ retorna TRUE si paso esta en [paso1, paso2] }
function PasoEnRango(paso1, paso, paso2: integer): boolean;

{ retorna el paso correspondiente al par (ano,sem).
result:= ano*52+sem }
function AnoSemToPaso(ano, sem: integer): integer;

{$IFDEF REDEFINIR_RDLN}
procedure readln(var f: Text; var r: string);
{$ENDIF}

// Lee líneas hasta encontrar la proxima no vacía y la devuelve.
// Si llega el fin del archivo retorna ''
function ProximaLineaNoVacia(var f: Text): string;

{
 retorna la palabra hasta el separador. Si no encuentra el separador
 retonra FALSE y en sres el string vacío.
 si lo encuentra retorna TRUE y en sres la palabra y en s borra
 el prinicio del string hasta el separador "sep" inclusive.
 OJO, si no encuentra "sep" retorna "sres" y "s" vacío y result=false.
}
function getPalHastaSep(out sres: string; var s: string; const sep: string): boolean;

{
 Retorna la próxima palabra hasta "sep" o el fin del string si no encuentra sep.
 De "s" se quita la palabra y el separador.
}
function getNextPalSep(var s: string; const sep: string): string;

{
 Parsea el string s en palabras separadas por el string "sep"
 Retorna una lista con las palabras encontradas.
 Por defecto el separador es un blanco.
}
function getPalabras_lst(s: string; const sep: string = ' '): TStringList;

{
 Parsea el string s en palabras separadas por el string "sep"
 Retorna un array con las palabras encontradas.
 Por defecto el separador es un blanco.
}
function getPalabras_arr(s: string; const sep: string = ' '): TDAofString;
{
Busca un directorio y si no existe lo crea, devuelve el mismo string
de entrada.
}
function CrearSiNoExiste(carpeta: string): string;

function NextBool(var s: string): boolean;
function NextFloat(var s: string; sep: string = '' ): NReal;
function NextInt(var s: string; sep: string = '' ): integer;
function NextPal(var s: string): string; overload;
function NextPalSinEspacio(var s: string): string;
//Busca igual que NextPal, pero ignora los espacios, ',' y ';'
function NextStr(var s: string): string;


// quita la primer linea de la lista y la devuelve
function NextLine(var sl: TStringList): string; overload;

// busca hasta el #10 y retorna el comienzo quitandolo de s.
// si el comienzo termina con #13 se lo quita.
function NextLine(var s: string): string; overload;

// elimina las primeras NSkip palabras de línea y retorna la siguiente
// palabra.
// Si sep = '' utiliza nextpal, si sip <> '' utiliza getNextPalSep
function NextPal_SkipN(var linea: string; NSkip: integer; sep: string = ''): string;

function NextStrParam(var s: string; Nombre: string): string; overload;
function NextFloatParam(var s: string; Nombre: string): NReal; overload;
function NextIntParam(var s: string; Nombre: string): integer; overload;

// estas funciones leen una línea y esperan Nombre [separador] valor
function ReadlnStrParam(var f: textfile; Nombre: string): string; overload;
function ReadlnFloatParam(var f: textfile; Nombre: string): NReal; overload;
function ReadlnIntParam(var f: textfile; Nombre: string): integer; overload;

function TextToDArrOfNReal(s: string; sepDec: char): TDAOfNReal;
function TextToTMatOfNReal(lineas: TStrings; sepDec: char): TMatOfNReal;

function InsertionSort_Creciente(const A: TDAOfNReal): TDAofNReal; overload;
function InsertionSort_Decreciente(const A: TDAOfNReal): TDAofNReal; overload;

procedure QuickSort_Creciente(var A: array of integer); overload;
procedure QuickSort_Creciente(var A: TDAOfNReal); overload;

// Ordena un array de referencias a Objetos en base a la función compareFunc donde
// compaeFunc( a, b ) = -1 si a < b; 0 si a = b y +1 si a > b
// la funcion IntercambioFunc, si se suministra es llamada para realizar los intercambios
// y debe retornar TRUE si luego de ser llamada, los objetos deben ser intercambiados en
// el array A. Si la función se encaga de intercambiar los elementos debe retornar FALSE, para
// que no se realice el intercambio dentro del procedimiento (al retornar de la llamada).
procedure QuickSort_Creciente(var A: TDAOfPtr; compareFunc: TFunCompare; InterCambioFunc: TFunIntercambio = nil ); overload;


// ordena el vecto A en forma creciente y hace los mismos cambios sobre el
// vector idxs.
procedure QuickSort_Creciente(var A: TDAOfNReal; var idxs: TDAOfNInt); overload;

procedure QuickSort_Decreciente(var A: array of integer); overload;

procedure QuickSort_Decreciente(var A: TDAOfNReal); overload;
procedure QuickSort_Decreciente(pA: pNReal; N: integer); overload;


// ordena el vecto A en forma decreciente y hace los mismos cambios sobre el
// vector idxs.
procedure QuickSort_Decreciente(var A: TDAOfNReal; var idxs: TDAOfNInt); overload;

//busca un string s en una lista TStringList y devuelve el primer lugar donde esta
//si no esta en la lista devuelve -1
function searchinlist(s: string; lista: TStringList): integer;


// Retorna A + (B-A)*alfa
function ponder(A, B, alfa: NReal): NReal;

// Interpola en un vector entre dos valores extremos.
function interpolar(valores: TDAOfNReal; xmin, xmax: NReal; x: Nreal): NReal;

function pertenece(valor: cardinal; const arreglo: TDAofNCardinal): boolean; overload;
function pertenece(valor: integer; const arreglo: TDAofNInt): boolean; overload;
function pertenece(valor: NReal; const arreglo: TDAofNReal): boolean; overload;
function pertenece(valor: cardinal; const arreglo: TDAofNCardinal;
  var posicion: integer): boolean; overload;
function pertenece(valor: integer; const arreglo: TDAofNInt;
  var posicion: integer): boolean; overload;
function pertenece(valor: NReal; const arreglo: TDAofNReal;
  var posicion: integer): boolean; overload;

//Convierten un arreglo a string separando los valores con separador con el formato
//[length(arreglo); valor0  valor1  ... valorn]
//Si no se especifica separador se usa #9. Si no se especifica precision se
//usa 8 y decimales 3
function TDAOfNRealToString(const arreglo: TDAOfNReal): string; overload;
function TDAOfNRealToString(const arreglo: TDAOfNReal;
  precision, decimales: integer): string; overload;
function TDAOfNRealToString(const arreglo: array of double;
  precision, decimales: integer; separador: string; wall: char = ';'): string; overload;

//Convierten un arreglo a string separando los valores con separador con el formato
//[valor0  valor1  ... valorn]
function TDAOfNRealToStringSinTamanio(const arreglo: TDAOfNReal;
  precision, decimales: integer; separador: string): string; overload;

//Convierte un arreglo a string con el formato
//valor0  valor1  ... valorn
//arreglo debe tener al menos 1 elemento
function TDAOfNRealToTabbedString(const arreglo: TDAOfNReal;
  precision, decimales: integer): string;

//Convierte un arreglo a string con el formato
//Length(arreglo) valor0  valor1  ... valorn
function TDAOfNRealToTabbedStringConTamanio(const arreglo: TDAOfNReal;
  precision, decimales: integer): string;

//Convierten un arreglo a string separando los valores con separador con el formato
//[length(arreglo); valor0  valor1  ... valorn]
function TDAOfNIntToString(const arreglo: array of integer; separador: string;
  wall: char = ';'): string;

function TDAOfNIntToStringSinTamanio(const arreglo: TDAofNInt;
  separador: string): string;
//Convierte un arreglo a string con el formato
//valor0  valor1  ... valorn
//arreglo debe tener al menos 1 elemento
function TDAOfNIntToTabbedString(const arreglo: TDAofNInt): string;

//Convierte un arreglo a string con el formato
//Length(arreglo) valor0  valor1  ... valorn
function TDAOfNIntToTabbedStringConTamanio(const arreglo: TDAofNInt): string;

//Convierten un arreglo a string separando los valores con separador con el formato
//[length(arreglo); valor0  valor1  ... valorn]
function TDAOfBooleanToString(const arreglo: array of boolean;
  separador: string; useBoolStrs: boolean; wall: char = ';'): string;

//Convierte una TStringList a string separando los valores con separador con el formato
//[stringList.Count; valor0  valor1  ... valorn]
function TStringListToString(const stringList: TStringList; separador: string;
  wall: char = ';'): string;

//Convierte un TDAOfString a string separando los valores con separador con el formato
//[length(TDAOfString); valor0  valor1  ... valorn]
function TDAOfStringToString(const DAOfString: array of string;
  separador: string; wall: char = ';'): string;

function nroOcurrencias(subCadena, cadena: string): integer;

function quitarElementoTDAOfNCardinal(const arreglo: TDAofNCardinal;
  elem: cardinal): TDAofNCardinal;

function clonarTList(original: TList): TList;

//Parsean un arreglo escrito con TDAOfXXXToString
function parseDAOfNreal( const xval: string): TDAOfNReal;
function parseDAOfNInt( const xval: string): TDAOfNInt;
function parseDAOfBoolean( const xval: string ): TDAOfBoolean;
function parseDAOfString( const xval: string ): TDAOfString;
function parseStringList( const xval: string ): TStringList;
procedure parsearReferencia(r: string; var claseDelReferido, nombreDelReferido: string);

procedure StrToArchi(nombreArchi, str: string);

// elimina del array los elementos con trim( ) = ''
procedure clear_vacias(var palabras: TDAOfString);


{$IFNDEF LCL}
procedure ShowMessage(s: string);
function MessageBox(msg: PAnsiChar; Caption: PAnsiChar): integer;
{$ENDIF}

function TextFileToStr(const filename: string): string;


// para leer o salvar la sala hay que poner los globales
// esto es solo para arreglar un problema con las versiones <= 45
// que se salvan con fecha a la dd/MM/yyyy en lugar de ISO.
procedure setSeparadoresGlobales_FechaUruguaya_V45_;

// para leer o salvar la sala hay que poner los globales
procedure setSeparadoresGlobales;

// para trabajar en el editor usamos los locales
procedure setSeparadoresLocales;

// Pone comillas al texto si hay espacios o tabuladores o si es el string vacío
function encomille(s: string): string;

// si s no es alfanumerico le agrega llaves {s}
function ponllavesid(s: string): string;


function RunChild(apl: string; params: array of string;
  EsperarFinDelHijo: boolean = False): boolean;

// Lanza una aplicación y la desvincula del proceso para que se pueda cerrar y
// que la aplicación quede corriendo.
// Para que la aplicación pueda ejecutar sin consola debe cerrar sus entradas y salidas
procedure LanzarApl(Apl: string; params: array of string);

{$IFDEF WINDOWS}
procedure RunShellExecute(const prog, params:string);
{$ENDIF}


function RunChildAndWAIT(apl: string; params: array of string): boolean;


// corre el comando apl con los parametros pasados
// y le escribe la entrada en su entrada standar.
// Se captura la salida y se retorna como un TStringList
function RunPipe(apl: string; paramsLst: TStrings; entrada: TStringList;
  finDeLinea: string = #10): TStringList;


// copia un archivo
function cp(archivoOrigen, archivoDestino: string): boolean;

// elimina la BOM (Byte Order Mask) si existe al inicio de la línea.
// por seguridad siempre que leemos un archivo de texto debería
// llamar esto sobre la primera línea para sacarnos los problemas.
procedure eliminar_BOM(var linea: string);

// Retorna true si el primer caracter de pal no es '0' ni 'F' ni  'f'
function StrToBool(pal: string): boolean;


// Codifica una cadena para su transmisión vía HTTP
function HTTPEncode(const AStr: string): string;

// Decodifica una cadena codificada con HTTPEncode
function HTTPDecode(const AStr: string): string;

// Busca los archivos que cumplen el pattern dentro del path.
function getFileList(path: string; pattern: string): TStringList;


// Zipea una lista de archivos.
// 1  - Zippeo correctamente
// 0  - El nombre del zip ya existe y flg_override = false
// -1 - Lista vacia
// -2 - No encontro alguno de los archivos
// -3 - No pudo zipear alguno de los archivos
// Si zipName no tiene ".zip" se lo agrega al final.
function zipearListaDeArchivos(lst: TStringList; zipName: string;
  flg_override: boolean = False): integer;

// descomprime el contenido en la carpeta con mismo nombre que el archivo, sin
// la extensión.
procedure Unzip(archi_zip: string);


// result:= valon in [Miniomo, Maximo]
function EnIntervalo(valor, minimo, maximo: NReal): boolean;

//Devuelve un vector con los valores de vec1 seguidos de los valores de vec2
function concatenate(vec1, vec2: TDAofNReal): TDAofNReal;

procedure readlnS( f: TStream; out r: string );
procedure writelnS( f: TSTream; const r: string = '' );
procedure writeS( f: TSTream; const r: string );

// sustituye los caracteres que no sean alfanumericos por "_"
function PurificarAlfaNum( s: string; sustChar: char = '_' ): string;


implementation




var
  cnt_GlobsSet: integer;
  cnt_GlobsSet_UY45: integer;
  local_ThousandSeparator: char;
  local_DecimalSeparator: char;
  local_DateSeparator: char;
  local_ShortDateFormat: string;
  local_LongDateFormat: string;

  local_ShortDateFormat_flg_has_MMM: boolean; // indica que en ShortDateFormat se usó MMM lo que implica
                 // que no se puede usar el DateToStr ni DateTimeToStr de sysutils pues
                 // da error. Se utiliza en ufechas para la función DateToStr y DateTimeToStr
  local_ShortDateFormat_pos_Dia: integer; // 1, 2 o 3 indica la posición de D en el fomato
  local_ShortDateFormat_pos_Mes: integer; // 1, 2 o 3 indica la posición de M en el fomato
  local_ShortDateFormat_pos_Anio: integer; // 1, 2 o 3 indica la posición de Y en el fomato

function zipearListaDeArchivos(lst: TStringList; zipName: string;
  flg_override: boolean = False): integer;
var
  fzip: TZipper;
  r: string;
  i: integer;

begin

  Result := 1;

  fzip := TZipper.Create;
  if pos('.zip', zipname) = 0 then
    fzip.FileName := zipname + '.zip'
  else
    fzip.FileName := zipname;

  if FileExists(fzip.FileName) then
  begin
    if flg_override then
      deletefile(fzip.FileName)
    else
    begin
      fzip.Free;
      Result := 0;
      Exit;
    end;
  end;

  if lst.Count = 0 then
  begin
    fzip.Free;
    Result := -1;
    Exit;
  end;

  for i := 0 to lst.Count - 1 do
  begin
    if not FileExists(lst.Strings[i]) then
    begin
      Result := -2;
      Break;
      fzip.Free;
      Exit;
    end
    else
    begin
      r := ExtractFileName(lst.Strings[i]);
      fzip.Entries.AddFileEntry(lst.Strings[i], r);
    end;
  end;

  try
    fzip.ZipAllFiles;
  except
    Result := -3;
  end;
  fzip.Free;
end;


procedure Unzip(archi_zip: string);
var
  funzip: TUnZipper;
  carpeta: string;

begin
  funzip := TUnZipper.Create;
  carpeta := ExtractFilePath(archi_zip);
  //  carpeta:= copy( archi_zip, 1, length( archi_zip ) - 4 );
  funzip.OutputPath := carpeta;
  funzip.UnZipAllFiles(archi_zip);
  funzip.Free;
end;

function EnIntervalo(valor, minimo, maximo: NReal): boolean;
begin
  Result := (minimo <= valor) and (valor <= maximo);
end;

function concatenate(vec1, vec2: TDAofNReal): TDAofNReal;
var
  i, j: integer;
  largo1, largo2: integer;
  aux: TDAofNReal;
begin
  largo1 := Length(vec1);
  largo2 := Length(vec2);
  SetLength(aux, largo1 + largo2);

  for i := 0 to largo1 - 1 do
  begin
    aux[i] := vec1[i];
  end;

  for j := largo1 to largo1 + largo2 - 1 do
  begin
    i := j - largo1;
    aux[j] := vec2[i];
  end;

  Result := aux;
end;

{ Lee una línea sin importar si es UNIX o DOS }
procedure readlnS( f: TStream; out r: string);
var
  c: char;
  NotEOF: boolean;
begin
  r := '';
  c := 'm';
  NotEOF:= true;
  while (c <> #10) and NotEOF do
  begin
    if f.Read(c, 1) = 1 then
      r := r + c
    else
      NotEOF:= true;
  end;

  if length(r) > 0 then
    if c = #10 then
      Delete(r, length(r), 1);
  if length(r) > 0 then
    if r[length(r)] = #13 then
      Delete(r, length(r), 1);
end;


procedure writeS( f: TSTream; const r: string );
var
  rs: shortstring;
begin
  rs:= r;
  f.Write( rs[1], length( rs ) );
end;

function PurificarAlfaNum(s: string; sustChar: char): string;
var
  k: integer;
  res: string;
begin
  res:=s;
  for k:= 1 to length( res ) do
    if not (res[k] in Alfanumericos) then
      res[k]:= sustChar;
  result:= res;
end;


procedure writelnS( f: TSTream; const r: string);
begin
  writeS(f, r+#13#10 );
end;


function StrToBool(pal: string): boolean;
var
  c: char;
begin
  if length(pal) = 0 then
  begin
    Result := False;
    exit;
  end;
  c := UpCase(pal[1]);
  Result := not ((c = '0') or (c = 'F') or (c = 'f'));
end;


// para leer o salvar la sala hay que poner los globales
procedure setSeparadoresGlobales_FechaUruguaya_V45_;
begin
  Inc(cnt_GlobsSet_UY45);
  SysUtils.DefaultFormatSettings.DateSeparator := '/';
  SysUtils.DefaultFormatSettings.ShortDateFormat := 'dd/MM/yyyy';
  SysUtils.DefaultFormatSettings.LongDateFormat := 'dd/MM/yyyy HH:mm:ss';

  ShortDateFormat_flg_has_MMM_:= false;
  ShortDateFormat_pos_Dia:=1;
  ShortDateFormat_pos_Mes:=2;
  ShortDateFormat_pos_Anio:=3;

end;

procedure setSeparadoresGlobales;
begin
  SeccionCriticaGenerica.Enter;
  Inc(cnt_GlobsSet);
  SysUtils.DefaultFormatSettings.ThousandSeparator := ' ';
  SysUtils.DefaultFormatSettings.DecimalSeparator := '.';
  if (cnt_GlobsSet_UY45 = 0) then
  begin
    SysUtils.DefaultFormatSettings.DateSeparator := '-';
    SysUtils.DefaultFormatSettings.ShortDateFormat := 'yyyy-MM-dd';
    SysUtils.DefaultFormatSettings.LongDateFormat := 'yyyy-MM-dd HH:mm:ss';
    ShortDateFormat_flg_has_MMM_:= false;
    ShortDateFormat_pos_Dia:=3;
    ShortDateFormat_pos_Mes:=2;
    ShortDateFormat_pos_Anio:=1;
  end;
  SeccionCriticaGenerica.Leave;
end;

procedure setSeparadoresLocales;
begin
  SeccionCriticaGenerica.Enter;
  Dec(cnt_GlobsSet);
  if cnt_GlobsSet = 0 then
  begin
    cnt_GlobsSet_UY45 := 0;
    SysUtils.DefaultFormatSettings.ThousandSeparator := local_ThousandSeparator;
    SysUtils.DefaultFormatSettings.DecimalSeparator := local_DecimalSeparator;
    SysUtils.DefaultFormatSettings.DateSeparator := local_DateSeparator;
    SysUtils.DefaultFormatSettings.ShortDateFormat := local_ShortDateFormat;
    SysUtils.DefaultFormatSettings.LongDateFormat := local_LongDateFormat;

    ShortDateFormat_flg_has_MMM_:= local_ShortDateFormat_flg_has_MMM;
    ShortDateFormat_pos_Dia:= local_ShortDateFormat_pos_Dia;
    ShortDateFormat_pos_Mes:= local_ShortDateFormat_pos_Mes;
    ShortDateFormat_pos_Anio:=local_ShortDateFormat_pos_Anio;
  end;
  SeccionCriticaGenerica.Leave;
end;


function cp(archivoOrigen, archivoDestino: string): boolean;
var
  f, fs: file;
  buff: array[0..102410 - 1] of byte;
  // ojo no pasarse de rosca que esto está en el stack.
  n: integer;
  res: integer;
  oldFileMode: integer;
begin
  oldFileMode := filemode;
  filemode := 0; // readonl
  Assign(f, archivoOrigen);
  reset(f, 1);
  filemode := 1;
  Assign(fs, archivoDestino);
  rewrite(fs, 1);
  filemode := OldFileMode;
  n := 0;
  res := 0;
  repeat
    blockread(f, buff{%H-}, 102410, n);
    blockwrite(fs, buff, n, res);
  until (n = 0) or (n <> res);
  Close(fs);
  Close(f);
  Result := n = res;
end;




function encomille(s: string): string;
begin
  if (pos(' ', s) <> 0) or (pos(#9, s) <> 0) or (length(s) = 0) then
    Result := '"' + s + '"'
  else
    Result := s;
end;

function ponllavesid(s: string): string;
var
  k: integer;
  flgPonerLlaves: boolean;

begin
  flgPonerLlaves := False;
  for k := 1 to length(s) do
  begin
    if not (s[k] in Alfanumericos) then
    begin
      flgPonerLLaves := True;
      break;
    end;
  end;
  if flgPonerLlaves then
    Result := '{' + s + '}'
  else
    Result := s;
end;


function boolToSiNo(b: boolean): string;
begin
  if b then
    Result := rs_SI
  else
    Result := rs_NO;
end;

{$IFNDEF LCL}
procedure ShowMessage(s: string);
begin
  writeln;
  writeln('**********ATENCION *********');
  writeln(s);
  writeln('----------------------------');
  writeln('Presione ENTER para continuar ... ');
  system.readln;
end;

function MessageBox(msg: PAnsiChar; Caption: PAnsiChar): integer;
var
  i: integer;
  input: char;
begin
  writeln;
  Write('**********');
  Write(Caption);
  writeln('*********');
  writeln(msg);
  Write('----------');
  for i := 0 to length(Caption) - 1 do
    Write('-');
  writeln('---------');
  input := #0;
  while (input <> 's') and (input <> 'n') do
  begin
    writeln('(S)i/(N)o: > ');
    Read(input);
    input := LowerCase(input);
  end;
  if input = 's' then
    Result := idYes
  else
    Result := idNo;
end;

{$ENDIF}

function TextFileToStr(const filename: string): string;
var
  lineas: TStringList;
  s: string;
begin
  lineas := TStringList.Create;
  lineas.LoadFromFile(filename);
  s := lineas.Text;
  lineas.Free;
  Result := s;
end;

function ponder(A, B, alfa: NReal): NReal;
begin
  Result := A + (B - A) * alfa;
end;

function InsertionSort_Creciente(const A: TDAOfNReal): TDAofNReal;
var
  i, j, tope, posNuevoElem: integer;
  valor: NReal;
  res: TDAofNReal;
begin
  SetLength(res, length(A));
  tope := 0;
  for i := 0 to high(A) do
  begin
    posNuevoElem := tope;
    valor := A[i];
    for j := 0 to tope - 1 do
    begin
      if res[j] > valor then
      begin
        posNuevoElem := j;
        break;
      end;
    end;

    for j := tope downto posNuevoElem + 1 do
      res[j] := res[j - 1];
    res[posNuevoElem] := valor;
    Inc(tope);
  end;
  Result := res;
end;

function InsertionSort_Decreciente(const A: TDAOfNReal): TDAofNReal;
var
  i, j, tope, posNuevoElem: integer;
  valor: NReal;
  res: TDAofNReal;
begin
  SetLength(res, length(A));
  tope := 0;
  for i := 0 to high(A) do
  begin
    posNuevoElem := tope;
    valor := A[i];
    for j := 0 to tope - 1 do
    begin
      if res[j] < valor then
      begin
        posNuevoElem := j;
        break;
      end;
    end;

    for j := tope downto posNuevoElem + 1 do
      res[j] := res[j - 1];
    res[posNuevoElem] := valor;
    Inc(tope);
  end;
  Result := res;
end;

procedure QuickSort_Creciente(var A: array of integer);

  procedure Quick_Sort(var A: array of integer; iLo, iHi: integer);
  var
    Lo, Hi, Mid, T: integer;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while A[Lo] < Mid do
        Inc(Lo);
      while A[Hi] > Mid do
        Dec(Hi);
      if Lo <= Hi then
      begin
        T := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := T;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, Lo, iHi);
  end;

begin
  if (length(A) > 0) then
    Quick_Sort(A, Low(A), High(A));
end;

// ordena el vecto A en forma creciente y hace los mismos cambios sobre el
// vector idxs.
procedure QuickSort_Creciente(var A: TDAOfNReal; var idxs: TDAOfNInt); overload;

  procedure Quick_Sort(var A: TDAOfNReal; var idxs: array of integer; iLo, iHi: integer);
  var
    Lo, Hi: integer;
    T, Mid: NReal;
    Tidx: integer;

  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while A[Lo] < Mid do
        Inc(Lo);
      while A[Hi] > Mid do
        Dec(Hi);
      if Lo <= Hi then
      begin
        T := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := T;

        Tidx := idxs[Lo];
        idxs[Lo] := idxs[Hi];
        idxs[Hi] := Tidx;

        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, idxs, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, idxs, Lo, iHi);
  end;

begin
  Quick_Sort(A, idxs, Low(A), High(A));
end;


procedure QuickSort_Creciente(var A: TDAOfNReal);

  procedure Quick_Sort(var A: TDAOfNReal; iLo, iHi: integer);
  var
    Lo, Hi: integer;
    T, Mid: NReal;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while A[Lo] < Mid do
        Inc(Lo);
      while A[Hi] > Mid do
        Dec(Hi);
      if Lo <= Hi then
      begin
        T := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := T;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, Lo, iHi);
  end;

begin
  Quick_Sort(A, Low(A), High(A));
end;



procedure QuickSort_Creciente(var A: TDAOfPtr; compareFunc: TFunCompare; InterCambioFunc: TFunIntercambio );

  procedure Quick_Sort(var A: TDAOfPtr; iLo, iHi: integer);
  var
    Lo, Hi: integer;
    T, Mid: Pointer;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while CompareFunc( A[Lo],  Mid ) < 0 do
        Inc(Lo);
      while CompareFunc( A[Hi],  Mid ) > 0 do
        Dec(Hi);
      if Lo <= Hi then
      begin
        if ( @IntercambioFunc = nil ) or IntercambioFunc( Lo, Hi ) then
        begin
          T := A[Lo];
          A[Lo] := A[Hi];
          A[Hi] := T;
        end;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, Lo, iHi);
  end;

begin
  Quick_Sort(A, Low(A), High(A));
end;



//**************** DECRECIENTES
procedure QuickSort_Decreciente(var A: array of integer);

  procedure Quick_Sort(var A: array of integer; iLo, iHi: integer);
  var
    Lo, Hi, Mid, T: integer;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while A[Lo] > Mid do
        Inc(Lo);
      while A[Hi] < Mid do
        Dec(Hi);
      if Lo <= Hi then
      begin
        T := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := T;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, Lo, iHi);
  end;

begin
  Quick_Sort(A, Low(A), High(A));
end;


procedure QuickSort_Decreciente(var A: TDAOfNReal);

  procedure Quick_Sort(var A: TDAOfNReal; iLo, iHi: integer);
  var
    Lo, Hi: integer;
    T, Mid: NReal;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while A[Lo] > Mid do
        Inc(Lo);
      while A[Hi] < Mid do
        Dec(Hi);
      if Lo <= Hi then
      begin
        T := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := T;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, Lo, iHi);
  end;

begin
  Quick_Sort(A, Low(A), High(A));
end;



// Ordena N Reales en forma decreciente comenzando por la posición
// de A[0]
procedure QuickSort_Decreciente(pA: pNReal; N: integer);

  procedure Quick_Sort(var A: TLAOfNReal; iLo, iHi: integer);
  var
    Lo, Hi: integer;
    T, Mid: NReal;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while A[Lo] > Mid do
        Inc(Lo);
      while A[Hi] < Mid do
        Dec(Hi);
      if Lo <= Hi then
      begin
        T := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := T;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, Lo, iHi);
  end;

begin
  Quick_Sort(TLAOfNReal(pointer(pA)^), 0, N - 1);
end;



procedure QuickSort_Decreciente(var A: TDAOfNReal; var idxs: TDAOfNInt);

  procedure Quick_Sort(var A: TDAOfNReal; var idxs: TDAOfNInt; iLo, iHi: integer);
  var
    Lo, Hi: integer;
    T, Mid: NReal;
    Tidx: integer;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A[(Lo + Hi) div 2];
    repeat
      while A[Lo] > Mid do
        Inc(Lo);
      while A[Hi] < Mid do
        Dec(Hi);
      if Lo <= Hi then
      begin
        T := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := T;

        Tidx := idxs[Lo];
        idxs[Lo] := idxs[Hi];
        idxs[Hi] := Tidx;

        Inc(Lo);
        Dec(Hi);

      end;
    until Lo > Hi;
    if Hi > iLo then
      Quick_Sort(A, idxs, iLo, Hi);
    if Lo < iHi then
      Quick_Sort(A, idxs, Lo, iHi);
  end;

begin
  Quick_Sort(A, idxs, Low(A), High(A));
end;

function searchinlist(s: string; lista: TStringList): integer;
var
  i: integer;
begin
  Result := -1;
  for i := 0 to lista.Count - 1 do
  begin
    if lista[i] = s then
    begin
      Result := i;
      break;
    end;
  end;

end;

function TextToDArrOfNReal(s: string; sepDec: char): TDAOfNReal;
var
  i: integer;
  tmpArr, tmpArr2: TDAOfNReal;
  cnt: integer;
  pal: string;
  r: NReal;
begin
  if sepDec <> '.' then
  begin
    i := pos('.', s);
    while i > 0 do
    begin
      Delete(s, i, 1);
      i := pos('.', s);
    end;

    for i := 1 to length(s) do
      if s[i] = sepDec then
        s[i] := '.';
  end;

  setlength(tmpArr, 100);

  cnt := 0;

  pal := nextpal(s);
  while pal <> '' do
  begin
    try
      r := StrToFloat(pal);
    except
      raise Exception.Create('OJO, no logré interpretar : (' + pal +
        ') como un número.');
    end;

    if cnt >= length(tmpArr) then
    begin
      setlength(tmpArr2, length(tmpArr) + 100);
      for i := 0 to high(tmpArr) do
        tmpArr2[i] := tmpArr[i];
      setlength(tmpArr, 0);
      tmpArr := tmpArr2;
    end;
    tmpArr[cnt] := r;
    Inc(cnt);
    pal := nextpal(s);
  end;
  Result := copy(tmpArr, 0, cnt);
  setlength(tmpArr, 0);
end;

function TextToTMatOfNReal(lineas: TStrings; sepDec: char): TMatOfNReal;
var
  i, nNoVacias: integer;
  res: TMatOfNReal;
begin
  SetLength(res, lineas.Count);
  nNoVacias := 0;
  for i := 0 to lineas.Count - 1 do
  begin
    if lineas[i] <> '' then
    begin
      res[nNoVacias] := TextToDArrOfNReal(lineas[i], sepDec);
      nNoVacias := nNoVacias + 1;
    end;
  end;
  if nNoVacias <> lineas.Count then
    res := copy(res, 0, nNoVacias - 1);
  Result := res;
end;

{$IFDEF REDEFINIR_RDLN}
{ Lee una l¡nea sin importar si es UNIX o DOS }
procedure readln(var f: Text; var r: string);
var
  c: char;
begin
  r := '';
  c := 'm';
  while not EOF(f) and (c <> #10) do
  begin
    Read(f, c);
    r := r + c;
  end;
  if length(r) > 0 then
    if c = #10 then
      Delete(r, length(r), 1);
  if length(r) > 0 then
    if r[length(r)] = #13 then
      Delete(r, length(r), 1);
end;
{$ENDIF}

// Lee líneas hasta encontrar la proxima no vacía y la devuelve.
// Si llega el fin del archivo retorna ''
function ProximaLineaNoVacia(var f: Text): string;
var
  linea: string;
begin
  Readln(f, linea);
  linea := trim(linea);
  while not EOF(f) and (linea = '') do
  begin
    Readln(f, linea);
    linea := trim(linea);
  end;
  Result := linea;
end;


{ saca la primer palabra del string s}
function NextPal(var s: string): string;
var
  k1, k2: integer;
  ts: string;
begin
  k1 := 1;
  while (k1 <= Length(s)) and (s[k1] in Separadores_Activos) do
    Inc(k1);
  k2 := k1;
  while (k2 <= Length(s)) and not (s[k2] in Separadores_Activos) do
    Inc(k2);

  if k1 <= length(s) then
  begin
    ts := copy(s, k1, k2 - k1);
    if k2 < length(s) then
      Delete(s, 1, k2)
    else
      s := '';
  end
  else
  begin
    ts := '';
    s := '';
  end;

  Result := ts;
end;

{ saca la primer palabra del string s no considera el espacio como separador}
function NextPalSinEspacio(var s: string): string;
var
  k1, k2: integer;
  ts: string;
begin
  k1 := 1;
  while (k1 <= Length(s)) and (s[k1] in SeparadoresSinEspacio) do
    Inc(k1);
  k2 := k1;
  while (k2 <= Length(s)) and not (s[k2] in SeparadoresSinEspacio) do
    Inc(k2);

  if k1 <= length(s) then
  begin
    ts := copy(s, k1, k2 - k1);
    if k2 < length(s) then
      Delete(s, 1, k2)
    else
      s := '';
  end
  else
  begin
    ts := '';
    s := '';
  end;

  Result := ts;
end;

function NextStr(var s: string): string;
var
  k1, k2: integer;
  ts: string;
begin
  k1 := 1;
  while (k1 <= Length(s)) and (s[k1] in SeparadoresDeLineaYTab) do
    Inc(k1);
  k2 := k1;
  while (k2 <= Length(s)) and not (s[k2] in SeparadoresDeLineaYTab) do
    Inc(k2);

  if k1 <= length(s) then
  begin
    ts := copy(s, k1, k2 - k1);
    if k2 < length(s) then
      Delete(s, 1, k2)
    else
      s := '';
  end
  else
  begin
    ts := '';
    s := '';
  end;

  Result := ts;
end;


// quita la primer linea de la lista y la devuelve
function NextLine(var sl: TStringList): string;
begin
  if sl.Count > 0 then
  begin
    Result := sl[0];
    sl.Delete(0);
  end
  else
    Result := '';
end;

function getPalHastaSep(out sres: string; var s: string; const sep: string): boolean;
var
  i: integer;
begin
  s := trim(s);
  i := pos(sep, s);
  if i > 0 then
  begin
    sres := copy(s, 1, i - 1);
    sres := trim(sres);
    Delete(s, 1, i - 1 + length(sep));
    Result := True;
  end
  else
  begin
    sres := s;
    s := '';
    Result := False;
  end;
end;

function getNextPalSep(var s: string; const sep: string): string;
var
  i: integer;
  sres: string;
begin
  s := trim(s);
  i := pos(sep, s);
  if i > 0 then
  begin
    sres := copy(s, 1, i - 1);
    Delete(s, 1, i - 1 + length(sep));
  end
  else
  begin
    sres := s;
    s := '';
  end;
  Result := trim(sres);
end;



function getPalabras_lst(s: string; const sep: string = ' '): TStringList;
var
  res: TStringList;
  ss: string;
  pal: string;
begin
  res := TStringList.Create;
  ss := s;
  while (ss <> '') do
  begin
    pal := getNextPalSep(ss, sep);
    if (pal <> '') or (sep <> ' ') then
      res.add(pal);
  end;
  Result := res;
end;

function getPalabras_arr(s: string; const sep: string = ' '): TDAofString;
var
  ls: TStringList;
  res: TDAOfString;
  k: integer;
begin
  ls := getPalabras_lst(s, sep);
  setlength(res, ls.Count);
  for k := 0 to ls.Count - 1 do
    res[k] := ls[k];
  ls.Free;
  Result := res;
end;

function CrearSiNoExiste(carpeta: string): string;
begin
  if not DirectoryExists(carpeta) then
    try
      mkdir(carpeta)
    except
      on E: Exception do
        raise Exception.Create('Error al intentar crear la carpeta: ' + carpeta);
    end;
  Result := carpeta;
end;



function NextLine(var s: string): string;
var
  res: string;
begin
  if not getPalHastaSep(res, s, #10) then
  begin
    res := s;
    s := '';
  end;
  if length(res) > 0 then
    if res[length(res)] = #13 then
      Delete(res, length(res), 1);

  Result := res;
end;


function NextPal_SkipN(var linea: string; NSkip: integer; sep: string): string;
var
  i: integer;
begin
  if sep = '' then
  begin
    for i := 1 to NSkip do
      nextpal(linea);
    Result := nextpal(linea);
  end
  else
  begin
    for i := 1 to NSkip do
      getNextPalSep(linea, sep );
    Result := getNextpalSep(linea, sep);
  end;
end;

function NextInt(var s: string; sep: string): integer;
begin
  if sep = '' then
    Result := StrToInt(NextPal(s))
  else
    Result:= StrToInt( getNextPalSep( s, sep ) );
end;

function NextBool(var s: string): boolean;
begin
  Result := StrToBool(NextPal(s));
end;

function NextFloat(var s: string; sep: string): NReal;
begin
  if sep = '' then
    Result := StrToFloat(NextPal(s))
  else
    Result:= StrToFloat( getNextPalSep( s, sep ) );
end;

function NextStrParam(var s: string; Nombre: string): string;
var
  ts: string;
begin
  ts := nextPal(s);
  if pos(Nombre, ts) > 0 then
    Result := nextPal(s)
  else
    raise Exception.Create('NextParam( ' + Nombre + ' ) Leí: ' + ts + ' y s:' + s);
end;

function NextFloatParam(var s: string; Nombre: string): NReal;
var
  sval: string;
begin
  sval := NextStrParam(s, nombre);
  Result := StrToFloat(sval);
end;

function NextIntParam(var s: string; Nombre: string): integer; overload;
var
  sval: string;
begin
  sval := NextStrParam(s, nombre);
  Result := StrToInt(sval);
end;

function ReadlnStrParam(var f: textfile; Nombre: string): string;
var
  r: string;
begin
  readln(f, r);
  Result := nextStrParam(r, Nombre);
end;

function ReadlnFloatParam(var f: textfile; Nombre: string): NReal;
var
  r: string;
begin
  readln(f, r);
  Result := nextFloatParam(r, Nombre);
end;

function ReadlnIntParam(var f: textfile; Nombre: string): integer;
var
  r: string;
begin
  readln(f, r);
  Result := nextIntParam(r, Nombre);
end;


function MesOfSem(isem: integer): integer;
var
  m: integer;
begin
  m := trunc(isem / 52.0 * 12.0) + 1;
  if m > 12 then
    MesOfSem := 12
  else
    MesOfSem := m;
end;


function EstacionOfMes(imes: integer): TEstacion;
var
  r: TEstacion;
begin
  case imes of
    12, 1, 2: r := verano;
    3, 4, 5: r := otonio;
    6, 7, 8: r := invierno;
    9, 10, 11: r := primavera;
    else
      raise Exception.Create('auxiliares.pas, EstacionOfMes (imes fuera de rango )');
  end;
  EstacionOfMes := r;
end;

function EstacionOfSem(isem: integer): TEstacion;
begin
  EstacionOfSem := EstacionOfMes(MesOfSem(isem));
end;

procedure cpSupEpsilon(var x: NReal);
begin
  if x < cpEpsilon then
    x := cpEpsilon;
end;

procedure potSupEpsilon(var x: NReal);
begin
  if x < potEpsilon then
    x := potEpsilon;
end;

{ retorna TRUE si paso está en [paso1, paso2] }
function PasoEnRango(paso1, paso, paso2: integer): boolean;
begin
  PasoEnRango := (paso1 <= paso) and (paso <= paso2);
end;

{ retorna el paso correspondiente al par ano,sem. }
function AnoSemToPaso(ano, sem: integer): integer;
begin
  AnoSemToPaso := ano * 52 + sem;
end;

function interpolar(valores: TDAOfNReal; xmin, xmax: NReal; x: Nreal): NReal;
var
  rix: NReal;
  ix: integer;
  dx: NReal;
begin
  dx := xmax - xmin;
  if dx <= AsumaCero then
    raise Exception.Create('ERROR, interpolar rango absurdo, xmin: ' +
      FloatToStr(xmin) + ' xmax: ' + FloatToStr(xmax));
  rix := (x - xmin) / dx * high(valores);
  ix := trunc(rix);
  if ix >= high(valores) then
    Result := valores[high(valores)]
  else
  begin
    rix := frac(rix);
    Result := valores[ix] * (1 - rix) + valores[ix + 1] * rix;
  end;
end;

function pertenece(valor: cardinal; const arreglo: TDAofNCardinal): boolean;
var
  i: integer;
  res: boolean;
begin
  res := False;
  for i := 0 to high(arreglo) do
    if arreglo[i] = valor then
    begin
      res := True;
      break;
    end;
  Result := res;
end;

function pertenece(valor: integer; const arreglo: TDAofNInt): boolean;
var
  i: integer;
  res: boolean;
begin
  res := False;
  for i := 0 to high(arreglo) do
    if arreglo[i] = valor then
    begin
      res := True;
      break;
    end;
  Result := res;
end;

function pertenece(valor: NReal; const arreglo: TDAofNReal): boolean;
var
  i: integer;
  res: boolean;
begin
  res := False;
  for i := 0 to high(arreglo) do
    if arreglo[i] = valor then
    begin
      res := True;
      break;
    end;
  Result := res;
end;

function pertenece(valor: cardinal; const arreglo: TDAofNCardinal;
  var posicion: integer): boolean;
var
  i: integer;
begin
  posicion := -1;
  for i := 0 to high(arreglo) do
    if arreglo[i] = valor then
    begin
      posicion := i;
      break;
    end;
  Result := posicion <> -1;
end;

function pertenece(valor: integer; const arreglo: TDAofNInt;
  var posicion: integer): boolean;
var
  i: integer;
begin
  posicion := -1;
  for i := 0 to high(arreglo) do
    if arreglo[i] = valor then
    begin
      posicion := i;
      break;
    end;
  Result := posicion <> -1;
end;

function pertenece(valor: NReal; const arreglo: TDAofNReal;
  var posicion: integer): boolean;
var
  i: integer;
begin
  posicion := -1;
  for i := 0 to high(arreglo) do
    if arreglo[i] = valor then
    begin
      posicion := i;
      break;
    end;
  Result := posicion <> -1;
end;

function TDAOfNRealToString(const arreglo: TDAOfNReal): string;
begin
  Result := TDAOfNRealToString(arreglo, 12, 3, #9);
end;

function TDAOfNRealToString(const arreglo: TDAOfNReal;
  precision, decimales: integer): string;
begin
  Result := TDAOfNRealToString(arreglo, precision, decimales, #9);
end;

function TDAOfNRealToString(const arreglo: array of double;
  precision, decimales: integer; separador: string; wall: char): string;
var
  k: integer;
  res: string;
begin
  res := '[' + IntToStr(length(arreglo)) + wall + ' ';
  if length(arreglo) > 0 then
  begin
    res := res + FloatToStrF(arreglo[0], ffGeneral, precision, decimales);
    for k := 1 to high(arreglo) do
      res := res + separador + FloatToStrF(arreglo[k], ffGeneral, precision, decimales);
  end;
  res := res + ']';
  Result := res;
end;

function TDAOfNRealToStringSinTamanio(const arreglo: TDAOfNReal;
  precision, decimales: integer; separador: string): string; overload;
var
  k: integer;
  res: string;
begin
  res := '[';
  if length(arreglo) > 0 then
  begin
    res := res + FloatToStrF(arreglo[0], ffGeneral, precision, decimales);
    for k := 1 to high(arreglo) do
      res := res + separador + FloatToStrF(arreglo[k], ffGeneral, precision, decimales);
  end;
  res := res + ']';
  Result := res;
end;

function TDAOfNRealToTabbedString(const arreglo: TDAOfNReal;
  precision, decimales: integer): string;
var
  i: integer;
  res: string;
begin
  res := FloatToStrF(arreglo[0], ffGeneral, precision, decimales);
  for i := 1 to high(arreglo) do
    res := res + #9 + FloatToStrF(arreglo[i], ffGeneral, precision, decimales);
  Result := res;
end;

function TDAOfNRealToTabbedStringConTamanio(const arreglo: TDAOfNReal;
  precision, decimales: integer): string;
var
  i: integer;
  res: string;
begin
  res := IntToStr(Length(arreglo));
  for i := 0 to High(arreglo) do
    res := res + #9 + FloatToStrF(arreglo[i], ffGeneral, precision, decimales);
  Result := res;
end;

function TDAOfNIntToString(const arreglo: array of integer; separador: string;
  wall: char): string;
var
  k: integer;
  res: string;
begin
  res := '[' + IntToStr(length(arreglo)) + wall + ' ';
  if length(arreglo) > 0 then
  begin
    res := res + IntToStr(arreglo[0]);
    for k := 1 to high(arreglo) do
      res := res + separador + IntToStr(arreglo[k]);
  end;
  res := res + ']';
  Result := res;
end;

function TDAOfNIntToStringSinTamanio(const arreglo: TDAofNInt;
  separador: string): string;
var
  k: integer;
  res: string;
begin
  res := '[';
  if length(arreglo) > 0 then
  begin
    res := res + IntToStr(arreglo[0]);
    for k := 1 to high(arreglo) do
      res := res + separador + IntToStr(arreglo[k]);
  end;
  res := res + ']';
  Result := res;
end;

function TDAOfNIntToTabbedString(const arreglo: TDAofNInt): string;
var
  i: integer;
  res: string;
begin
  res := IntToStr(arreglo[0]);
  for i := 1 to high(arreglo) do
    res := res + #9 + IntToStr(arreglo[i]);
  Result := res;
end;

function TDAOfNIntToTabbedStringConTamanio(const arreglo: TDAofNInt): string;
var
  i: integer;
  res: string;
begin
  res := IntToStr(Length(arreglo));
  for i := 0 to High(arreglo) do
    res := res + #9 + IntToStr(arreglo[i]);
  Result := res;
end;

function TDAOfBooleanToString(const arreglo: array of boolean;
  separador: string; useBoolStrs: boolean; wall: char): string;
var
  k: integer;
  res: string;
begin
  res := '[' + IntToStr(length(arreglo)) + wall + ' ';
  if length(arreglo) > 0 then
  begin
    res := res + BoolToStr(arreglo[0], useBoolStrs);
    for k := 1 to high(arreglo) do
      res := res + separador + BoolToStr(arreglo[k], useBoolStrs);
  end;
  res := res + ']';
  Result := res;
end;

function TStringListToString(const stringList: TStringList; separador: string;
  wall: char): string;
var
  k: integer;
  res: string;
begin
  res := '[' + IntToStr(stringList.Count) + wall + ' ';
  if stringList.Count > 0 then
  begin
    res := res + stringList[0];
    for k := 1 to stringList.Count - 1 do
      res := res + separador + stringList[k];
  end;
  res := res + ']';
  Result := res;
end;

function TDAOfStringToString(const DAOfString: array of string;
  separador: string; wall: char): string;
var
  k: integer;
  res: string;
  len: integer;
begin
  len := Length(DAOfString);
  res := '[' + IntToStr(Len) + wall + ' ';
  if Len > 0 then
  begin
    res := res + DAOfString[0];
    for k := 1 to len - 1 do
      res := res + separador + DAOfString[k];
  end;
  res := res + ']';
  Result := res;
end;

function nroOcurrencias(subCadena, cadena: string): integer;
var
  i, j, res: integer;
begin
  res := 0;
  i := 1;
  while i < Length(cadena) - Length(subCadena) do
  begin
    j := 1;
    while (j < Length(subCadena)) and (subCadena[j] = cadena[i + j]) do
      j := j + 1;

    if j = Length(subCadena) then
    begin
      res := res + 1;
      i := i + j;
    end
    else
      i := i + 1;
  end;
  Result := res;
end;

function quitarElementoTDAOfNCardinal(const arreglo: TDAofNCardinal;
  elem: cardinal): TDAofNCardinal;
var
  res: TDAofNCardinal;
  i, iRes, n: integer;
begin
  n := Length(arreglo);
  if n > 0 then
  begin
    iRes := 0;
    SetLength(res, n - 1);
    for i := 0 to high(arreglo) do
      if arreglo[i] <> elem then
      begin
        res[iRes] := arreglo[i];
        iRes := iRes + 1;
      end;
  end
  else
    res := nil;
  Result := res;
end;

function clonarTList(original: TList): TList;
var
  res: TList;
  i: integer;
begin
  res := TList.Create;
  res.Capacity := original.Capacity;
  for i := 0 to original.Count - 1 do
    res.Add(original[i]);
  Result := res;
end;


function parseDAOfNreal( const xval: string): TDAOfNReal;
var
  k: integer;
  s: string;
  pal: string = '';
  N: integer;
  A: TDAOfNreal;
begin
  s := trim(xval);
  if s = '' then
  begin
    setlength(A, 0);
    result:= A;
    exit;
  end;
  if not getPalHastaSep(pal, s, '[') then
    raise Exception.Create(rs_ArrayErrorDeFormato_DebeComenzarCon + ' [ . ');
  if (not getPalHastaSep(pal, s, '|') and not getPalHastaSep(pal, s, ';')) then
    raise Exception.Create(rs_ArrayErrorDeFormato_CantidadDeElementos +
      ' | o ; : [n|, [n; ');
  try
    N := StrToInt(trim(pal));
  except
    raise Exception.Create(rs_ArrayErrorDeFormato_CantidadDeElementosNoEntera +
      ' ' + rs_ElValorEspecificadoEs + ': [' + pal + '] (' +
      rs_CaracteresEntreParentesisRectos + ')');
  end; // try
  if N > 0 then
  begin
    setlength(A, N);
    for k := 0 to N - 2 do
    begin
      if not getPalHastaSep(pal, s, ',') then
        raise Exception.Create(
          'Formato de array inválido. Imposible encontrar la coma que marca el fin del elemento número: '
          + IntToStr(k + 1));
      try
        A[k] := StrToFloat(pal);
      except
        raise Exception.Create('Formato de array inválido. No pude convertir: [' +
          pal + '] a un número real procesando el elemento número: ' +
          IntToStr(k + 1));
      end; // try
    end;
    k := N - 1; // el último elemento lo procesamos aparte pues finaliza con ]
    if not getPalHastaSep(pal, s, ']') then
      raise Exception.Create(
        'Formato de array inválido. Imposible encontrar la coma que marca el fin del último elemento del array.');
    try
      A[k] := StrToFloat(pal);
    except
      raise Exception.Create('Formato de array inválido. No pude convertir: [' +
        pal + '] a un número real procesando el elemento número: ' + IntToStr(k + 1));
    end; // try
  end
  else
  begin
    if not getPalHastaSep(pal, s, ']') then
      raise Exception.Create(
        'Formato de array inválido. Imposible encontrar la coma que marca el fin del último elemento del array.');
    A := nil;
  end;
  result:= A;
end;

function parseDAOfNInt( const xval: string): TDAOfNInt;
var
  k: integer;
  s: string;
  pal: string = '';
  N: integer;
  A: TDAOfNInt;
begin
  s := trim(xval);
  if s = '' then
  begin
    setlength(A, 0);
    result:= A;
    exit;
  end;

  if not getPalHastaSep(pal, s, '[') then
    raise Exception.Create('Formato de array inválido debe comenzar con [ . ');
  if (not getPalHastaSep(pal, s, '|') and not getPalHastaSep(pal, s, ';')) then
    raise Exception.Create(
      'Formato de array inválido debe tener la cantidad de elementos seguida de | o ; : [n|, [n; ');
  try
    N := StrToInt(pal);
  except
    raise Exception.Create(
      'Formato de array inválido. La cantidad de elementos tienen que ser un entero. Es: ['
      + pal + '] (lo que está entre paréntesis rectos)');
  end; // try
  setlength(A, N);
  for k := 0 to N - 2 do
  begin
    if not getPalHastaSep(pal, s, ',') then
      raise Exception.Create(
        'Formato de array inválido. Imposible encontrar la coma que marca el fin del elemento número: '
        + IntToStr(k + 1));
    try
      A[k] := StrToInt(pal);
    except
      raise Exception.Create('Formato de array inválido. No pude convertir: [' +
        pal + '] a un número entero procesando el elemento número: ' +
        IntToStr(k + 1));
    end; // try
  end;
  k := N - 1; // el último elemento lo procesamos aparte pues finaliza con ]
  if not getPalHastaSep(pal, s, ']') then
    raise Exception.Create(
      'Formato de array inválido. Imposible encontrar el ] que marca el fin del ultimo elemento del array.');
  if N > 0 then
  begin
    try
      A[k] := StrToInt(pal);
    except
      raise Exception.Create('Formato de array inválido. No pude convertir: [' +
        pal + '] a un número entero procesando el elemento número: ' +
        IntToStr(k + 1));
    end; // try
  end;
  result:= A;
end;

function parseDAOfBoolean( const xval: string ): TDAOfBoolean;
var
  k: integer;
  s: string;
  pal: string = '';
  N: integer;
  A: TDAOfBoolean;
begin
  s := xval;
  if s = '' then
  begin
    setlength(A, 0);
    result:= A;
    exit;
  end;

  if not getPalHastaSep(pal, s, '[') then
    raise Exception.Create('Formato de array inválido debe comenzar con [ . ');
  if (not getPalHastaSep(pal, s, '|') and not getPalHastaSep(pal, s, ';')) then
    raise Exception.Create(
      'Formato de array inválido debe tener la cantidad de elementos seguida de | o ; : [n|, [n; ');
  try
    N := StrToInt(pal);
  except
    raise Exception.Create(
      'Formato de array inválido. La cantidad de elementos tienen que ser un entero. Es: ['
      + pal + '] (lo que está entre paréntesis rectos)');
  end; // try
  setlength(A, N);
  if N > 0 then
  begin
    for k := 0 to N - 2 do
    begin
      if not getPalHastaSep(pal, s, ',') then
        raise Exception.Create(
          'Formato de array inválido. Imposible encontrar la coma que marca el fin del elemento número: '
          + IntToStr(k + 1));
      try
        A[k] := StrToBool(pal);
      except
        raise Exception.Create('Formato de array inválido. No pude convertir: [' +
          pal + '] a un número entero procesando el elemento número: ' +
          IntToStr(k + 1));
      end; // try
    end;
    k := N - 1; // el último elemento lo procesamos aparte pues finaliza con ]
    if not getPalHastaSep(pal, s, ']') then
      raise Exception.Create(
        'Formato de array inválido. Imposible encontrar el ] que marca el fin del ultimo elemento del array.');
    try
      A[k] := StrToBool(pal);
    except
      raise Exception.Create('Formato de array inválido. No pude convertir: [' +
        pal + '] a un número entero procesando el elemento número: ' +
        IntToStr(k + 1));
    end; // try
  end;
  result:= A;
end;




function parseStringList( const xval: string): TStringList;
var
  k: integer;
  s: string;
  pal: string = '';
  N: integer;
  A: TStringList;
begin
  s := xval;
  if not getPalHastaSep(pal, s, '[') then
    raise Exception.Create('Formato de array inválido debe comenzar con [ . ');
  if (not getPalHastaSep(pal, s, '|') and not getPalHastaSep(pal, s, ';')) then
    raise Exception.Create(
      'Formato de array inválido debe tener la cantidad de elementos seguida de | o ; : [n|, [n; ');
  try
    N := StrToInt(pal);
  except
    raise Exception.Create(
      'Formato de array inválido. La cantidad de elementos tienen que ser un entero. Es: ['
      + pal + '] (lo que está entre paréntesis rectos)');
  end; // try

  A := TStringList.Create;
  A.Capacity := N;

  { dfusco@20150612
    Se agrega el filtro de N>0 para que no se retorne el StringList con un
    elemento vacío. }
  if N > 0 then
  begin
    for k := 0 to N - 2 do
    begin
      if not getPalHastaSep(pal, s, ',') then
        raise Exception.Create(
          'Formato de array inválido. Imposible encontrar la coma que marca el fin del elemento número: '
          + IntToStr(k + 1));
      A.add(pal);
    end;
    //   k:= N-1; // el último elemento lo procesamos aparte pues finaliza con ]
    if not getPalHastaSep(pal, s, ']') then
      raise Exception.Create(
        'Formato de array inválido. Imposible encontrar el ] que marca el fin del ultimo elemento del array.');
    A.add(pal);
  end;
  result:= A;
end;

function parseDAOfString( const xval: string): TDAOfString;
var
  l: TStringList;
  k: integer;
  A: TDAOfString;
begin
  l := nil;
  l:= parseStringList( xval );
  if l <> nil then
  begin
    setlength(A, l.Count);
    for k := 0 to l.Count - 1 do
      a[k] := l[k];
    l.Free;
  end
  else
    setlength(a, 0);
  result:= A;
end;

procedure StrToArchi(nombreArchi, str: string);
var
  archi: TextFile;
begin
  AssignFile(archi, nombreArchi);
  Rewrite(archi);
  WriteLn(archi, str);
  CloseFile(archi);
end;

procedure clear_vacias(var palabras: TDAOfString);
var
  k, j: integer;
  pal: string;
begin
  if palabras = nil then
    setlength(palabras, 0);

  k := 0;
  j := 0;
  for k := 0 to length(palabras) - 1 do
  begin
    pal := trim(palabras[k]);
    if pal <> '' then
    begin
      palabras[j] := pal;
      Inc(j);
    end;
  end;
  if j < length(palabras) then
    setlength(palabras, j);
end;


procedure parsearReferencia(r: string; var claseDelReferido, nombreDelReferido: string);
var
  i: integer;
begin
  if trim(r) = '' then
  begin
    claseDelReferido := '?';
    nombreDelReferido := '?';
    exit;
  end;
  if pos('<', r) <> 1 then
    raise Exception.Create('ReadRefToCosa, Error(falta:<): Formato inválido: ' +
      r + '.'{ Procesando línea: '+IntToStr( klinea )});
  System.Delete(r, 1, 1); // borramos el < de apertura
  i := pos('>', r);
  if i = 0 then
    raise Exception.Create('ReadRefToCosa, Error(falta:>): Formato inválido: ' +
      r + '.'{ Procesando línea: '+IntToStr( klinea )});
  if i < length(r) then
    raise Exception.Create('ReadRefToCosa, Error( Hay caracteres después del > )' +
      r + '.'{ Procesando línea: '+IntToStr( klinea )});

  System.Delete(r, i, 1); // borramos el > de cierre
  i := pos('.', r);
  if i = 0 then
    raise Exception.Create('ReadRefToCosa, Error(falta:.): Formato inválido: ' +
      r + '.'{ Procesando línea: '+IntToStr( klinea )});
  claseDelReferido := copy(r, 1, i - 1);
  nombreDelReferido := copy(r, i + 1, Length(r) - (i));
end;


{$IFDEF WINDOWS}
function RunChild(apl: string; params: array of string;
  EsperarFinDelHijo: boolean): boolean;
var
  proc_info: TProcessInformation;
  startinfo: TStartupInfo;
  ExitCode: longword = 0;
  aplic, cmdline: string;
  k: integer;
  ext: string;
begin
  ext := LowerCase(ExtractFileExt(apl));
  {$IFDEF WINDOWS}
  if ext = '.exe' then
    aplic := apl + #0
  else
    aplic := apl + '.exe'#0;
  {$ELSE}
  aplic:= apl+#0;
  {$ENDIF}

  cmdline := apl;
  if length(params) > 0 then
    for k := 0 to high(params) do
      cmdline := cmdline + ' ' + params[k];
  cmdline := cmdline + #0;

  // Inicializamos las estructuras
  FillChar(proc_info{%H-}, sizeof(TProcessInformation), 0);
  FillChar(startinfo{%H-}, sizeof(TStartupInfo), 0);
  startinfo.cb := sizeof(TStartupInfo);

  // Intentamos crear el proceso
  // if CreateProcess(@aplic[1], @cmdline[1], nil, nil, False, <-- esto estaba antes y no me andaba DV@20171127
  if CreateProcess(nil, PChar(cmdline), nil, nil, False, NORMAL_PRIORITY_CLASS,
    nil, nil, startinfo, proc_info) <> False then
  begin
    if EsperarFinDelHijo then
    begin
      // El proceso se creó exitosamente
      // Ahora esperemos a que termine...
      while WaitForSingleObject(proc_info.hProcess, 10000) = WAIT_TIMEOUT do
        writeln('.');

      // Proceso finalizado. Ahora debemos cerrarlo.
      GetExitCodeProcess(proc_info.hProcess, ExitCode);  // Opcional
      FileClose(proc_info.hThread); { *Converted from CloseHandle*  }
      FileClose(proc_info.hProcess); { *Converted from CloseHandle*  }
    end;
    Result := True;
  end
  else
  begin
    Result := False;
  end;//if
end;

function RunChildAndWAIT(apl: string; params: array of string): boolean;
begin
  Result := RunChild(apl, params, True);
end;

{$ELSE}

procedure DoProcesoHijo(apl: ansistring; params: array of string);
var
  i, error: integer;
  paramsPChar, iter: PPChar;
begin
  writeln('DoProcesoHijo, Apl: ', Apl);
  for i := 0 to high(params) do
    writeln('i: ', i, ' val: ', params[i]);

  Apl := Apl + #0;
  GetMem(paramsPChar, (length(params) + 2) * SizeOf(PChar));
  iter := paramsPChar;
  iter^ := @(Apl[1]);
  Inc(iter);
  for i := 0 to High(params) do
  begin
    params[i] := params[i] + #0;
    iter^ := @(params[i][1]);
    Inc(iter);
  end;
  iter^ := nil;
  fpexecv(Apl, paramsPChar);

  //si vuelvo de fpexecv hubo error
  error := fpgeterrno;
  writeln('uEmuladorWinIPC.DoProcesoHijo: error= ', error, ', ', strError(error));

  freemem(paramsPChar, (length(params) + 2) * SizeOf(PChar));
  fpexit(0); // halt;
end;

function RunChild(apl: string; params: array of string;
  EsperarFinDelHijo: boolean): boolean;
var
  pid: integer;
  status: integer;
  aplx: ansistring;
begin
  // init child process
  pid := fpfork();
  Result := pid >= 0;
  if pid = -1 then
    exit;

  if pid = 0 then
  begin
    // in child process - init grandchild
    aplx := apl;
    doProcesoHijo(aplx, params);
  end
  else
  // in parent process - use waitpit to query for child process
  if EsperarFinDelHijo then
    fpwaitpid(pid, @status, 0);
end;


function RunChildAndWAIT(apl: string; params: array of string): boolean;
begin
  Result := RunChild(apl, params, True);
end;

{$ENDIF}

{$IFDEF WINDOWS}
procedure RunShellExecute(const prog,params:string);
begin
  //  ( Handle, nil/'open'/'edit'/'find'/'explore'/'print',   // 'open' isn't always needed
  //      path+prog, params, working folder,
  //        0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min)   // for SW_ constants : uses ... Windows ...
//  if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //success
  if ShellExecute(0, nil,PChar('cmd'),PChar('/c '+prog+' '+params),PChar(extractfilepath(prog)),1) >32 then; //success
  // return values 0..32 are errors
end;
{$ENDIF}


procedure LanzarApl(Apl: string; params: array of string);
var
  Process: TProcess;
  I: integer;
begin
  Process := TProcess.Create(nil);
  try
    Process.InheritHandles := False;
    Process.Options := [];
    Process.ShowWindow := swoShow;

    // Copy default environment variables including DISPLAY variable for GUI application to work
    for I := 1 to GetEnvironmentVariableCount do
      Process.Environment.Add(GetEnvironmentString(I));

    for I := 0 to high(params) do
      Process.Parameters.Add(Params[I]);
    Process.Executable := Apl;
    Process.Execute;
  finally
    Process.Free;
  end;
end;

// corre el comando apl con los parametros pasados
// y le escribe la entrada en su entrada standar.
// Se captura la salida y se retorna como un TStringList
function RunPipe(apl: string; paramsLst: TStrings; entrada: TStringList;
  finDeLinea: string): TStringList;
var
  ap: TProcess;
  Buffer: array[0..1024] of char;
  ReadCount: integer;
  ReadSize: integer;
  i: integer;
  s: AnsiString;
  res: TStringList;
  directorioPrograma: string;

begin

  directorioPrograma := ExtractFileDir(apl);

  ap := TProcess.Create(nil);
  ap.Options := [poUsePipes, poStderrToOutPut];
  ap.Executable := apl;
  ap.Parameters := paramsLst;
  ap.CurrentDirectory := directorioPrograma;

  ap.Execute;
  s:= '';

  if entrada <> nil then
  begin
    for i := 0 to entrada.Count - 1 do
    begin
      s := entrada[i] + finDeLinea;
      ap.Input.Write(s[1], length(s));
    end;
  end;



  while ap.Running or (ap.Output.NumBytesAvailable > 0) do
  begin
    if ap.Output.NumBytesAvailable > 0 then
    begin
      // make sure that we don't read more data than we have allocated
      // in the buffer
      ReadSize := ap.Output.NumBytesAvailable;
      if ReadSize > SizeOf(Buffer) then
        ReadSize := SizeOf(Buffer);
      // now read the output into the buffer
      ReadCount := ap.Output.Read({%H-}Buffer[0], ReadSize);
     // setlength(s, ReadCount);
      s := s + copy(Buffer, 0, ReadCount);
     // writeln( s );
    end;
  end;

  res := TStringList.Create;
  res.Delimiter:= finDeLinea[1];
  res.Text:= s;
  Result := res;

  // Close the input on the SecondProcess
  // so it finishes processing it's data
  ap.CloseInput;

  // and wait for it to complete
  // be carefull what command you run because it may not exit when
  // it's input is closed and the following line may loop forever
  while ap.Running do
  begin
    writeln( '.' );
    Sleep(10);
  end;

  // free our process objects
  ap.Free;
end;

procedure DeleteDir(const DirName: string);
var
  Path: string;
  F: TSearchRec;

begin
  Path := DirName + '\*.*';
  if FindFirst(Path, faAnyFile, F) = 0 then
  begin
    try
      repeat
        if (F.Attr and faDirectory <> 0) then
        begin
          if (F.Name <> '.') and (F.Name <> '..') then
          begin
            DeleteDir(DirName + '\' + F.Name);
          end;
        end
        else
          DeleteFile(DirName + '\' + F.Name);
      until FindNext(F) <> 0;
    finally
      FindClose(F);
    end;
  end;
  RemoveDir(DirName);
end;



procedure eliminar_BOM(var linea: string);
var
  i: integer;
  n: integer;
  buscando: boolean;
  c: integer;
begin
  buscando := True;
  i := 1;
  n := length(linea);
  while (buscando and (i <= n)) do
  begin
    c := Ord(linea[i]);
    if (c >= 32) and (c <= 126) then
      buscando := False
    else
      Inc(i);
  end;
  if not buscando then
  begin
    if (i > 1) then
      Delete(linea, 1, i - 1);
  end
  else
    linea := '';
end;




function HTTPDecode(const AStr: string): string;

var
  S, SS, R: PChar;
  H: string[3];
  L, C: integer;

begin
  L := Length(Astr);
  SetLength(Result, L);
  if (L = 0) then
    exit;
  S := PChar(AStr);
  SS := S;
  R := PChar(Result);
  while (S - SS) < L do
  begin
    case S^ of
      '+': R^ := ' ';
      '%':
      begin
        Inc(S);
        if ((S - SS) < L) then
        begin
          if (S^ = '%') then
            R^ := '%'
          else
          begin
            H := '$00';
            H[2] := S^;
            Inc(S);
            if (S - SS) < L then
            begin
              H[3] := S^;
              Val(H, PByte(R)^, C);
              if (C <> 0) then
                R^ := ' ';
            end;
          end;
        end;
      end;
      else
        R^ := S^;
    end;
    Inc(R);
    Inc(S);
  end;
  SetLength(Result, R - PChar(Result));
end;

function getFileList(path: string; pattern: string): TStringList;
var
  oldDir: string;
  Info: TSearchRec;
begin
  try
    if DirectoryExists(path) then
    begin
      oldDir := GetCurrentDir;
      ChDir(path);
      chDir(oldDir);
    end
    else
      Exit;
  except
    raise Exception.Create('No se pudo acceder al directorio: ' + path);
  end;

  Result := TStringList.Create;
  if FindFirst(pattern, faAnyFile, Info) = 0 then
  begin
    repeat
      Result.Add(info.Name);
    until FindNext(info) <> 0;
  end;
  FindClose(Info);

end;

function HTTPEncode(const AStr: string): string;

const
  HTTPAllowed = ['A'..'Z', 'a'..'z', '*', '@', '.', '_', '-',
    '0'..'9', '$', '!', '''', '(', ')'];

var
  SS, S, R: PChar;
  H: string[2];
  L: integer;

begin
  L := Length(AStr);
  SetLength(Result, L * 3); // Worst case scenario
  if (L = 0) then
    exit;
  R := PChar(Result);
  S := PChar(AStr);
  SS := S; // Avoid #0 limit !!
  while ((S - SS) < L) do
  begin
    if S^ in HTTPAllowed then
      R^ := S^
    else if (S^ = ' ') then
      R^ := '+'
    else
    begin
      R^ := '%';
      H := HexStr(Ord(S^), 2);
      Inc(R);
      R^ := H[1];
      Inc(R);
      R^ := H[2];
    end;
    Inc(R);
    Inc(S);
  end;
  SetLength(Result, R - PChar(Result));
end;

procedure load_localsettings;
var
  s: string;
  i: integer;
  k, iPos: integer;
  C: Char;

  cnt: integer;

procedure setifcero(
  var local_ShortDateFormat_pos: integer;
  var cnt: integer; ipos: integer );
begin
  if local_ShortDateFormat_pos = 0 then
  begin
    inc( cnt );
    local_ShortDateFormat_pos:= ipos;
  end;
end;

begin
  local_ThousandSeparator := SysUtils.DefaultFormatSettings.ThousandSeparator;
  local_DecimalSeparator := SysUtils.DefaultFormatSettings.DecimalSeparator;
  local_DateSeparator := SysUtils.DefaultFormatSettings.DateSeparator;
  local_ShortDateFormat := SysUtils.DefaultFormatSettings.ShortDateFormat;
  local_LongDateFormat := SysUtils.DefaultFormatSettings.LongDateFormat;

  if pos('yyyy', local_ShortDateFormat) = 0 then
  begin
    i := pos('y', local_ShortDateFormat);
    if i > 0 then
      while pos('yyyy', local_ShortDateFormat) = 0 do
      begin
        s := copy(local_ShortDateFormat, 1, i) + 'y';
        if i < length(local_ShortDateFormat) then
          local_ShortDateFormat :=
            s + copy(local_ShortDateFormat, i + 1, length(local_ShortDateFormat) - i)
        else
          local_ShortDateFormat := s;
        i := pos('y', local_ShortDateFormat);
      end;
  end;

  if pos('/', local_ShortDateFormat) > 0 then
    local_DateSeparator := '/'
  else if pos('-', local_ShortDateFormat) > 0 then
    local_DateSeparator := '-';

  local_ShortDateFormat_flg_has_MMM:=  pos( 'MMM', local_ShortDateFormat ) > 0;

  s:= UpperCase( local_ShortDateFormat );

  iPos:= 1;
  cnt:= 0;
  k:= 1;

  while ( cnt < 3 ) and ( k < length( local_ShortDateFormat ) ) do
  begin
    C:= s[k];
    case C of
      'D': setifcero( local_ShortDateFormat_pos_Dia, cnt, ipos);
      'M': setifcero( local_ShortDateFormat_pos_Mes, cnt, iPos);
      'Y': setifcero( local_ShortDateFormat_pos_Anio, cnt, iPos);
    else
      if C = local_DateSeparator then
       inc( iPos );
    end;
    inc( k );
  end;


  ShortDateFormat_flg_has_MMM_:= local_ShortDateFormat_flg_has_MMM;
  ShortDateFormat_pos_Dia:= local_ShortDateFormat_pos_Dia;
  ShortDateFormat_pos_Mes:= local_ShortDateFormat_pos_Mes;
  ShortDateFormat_pos_Anio:=local_ShortDateFormat_pos_Anio;

end;

initialization
  SeccionCriticaGenerica := TCriticalSection.Create;
  load_localsettings;

  cnt_GlobsSet := 0;
  cnt_GlobsSet_UY45 := 0;
  Separadores_Activos := Separadores;

finalization
  SeccionCriticaGenerica.Free;

end.
