unit uRangos;

interface

uses
  classes, SysUtils;

type
  TRango = class
    public
      minValor, maxValor: Integer;

      Constructor Create(minValor, maxValor: Integer);
      function toString: String;
      function largoRango: Integer;
  end;

  {TListaDeRangos es una lista ordenada de objetos de tipo TRango que sean disjuntos.
  Al crearla se especifican valores mínimo y máximo que pueden tener los rangos.
  Al ingresar un nuevo rango se inserta en el lugar que le corresponde de la lista.
  Ejemplo: si el rango total es [0, 149] y queremos tener los rangos [10, 20] y [80, 130]
  La lista tendría minValor:= 0, maxValor:= 149 y dos objetos de tipo trango con
  uno con minValor:= 10 y maxValor:= 20 y otro con minValor:= 80 y maxValor:= 130
  en ese orden.
  }
  TListaDeRangos = class(TList)
    public
      minValor, maxValor: Integer;

      Constructor Create(minValor, maxValor: Integer);
      //Retorna los rangos en [minValor, maxValor] que no estan en la lista
      function complemento: TListaDeRangos;
      //Inserta el nuevo rango en la lista de rangos. Si el rango esta directamente 
      //al lado de uno de los rangos presentes los dos rangos se sustituyen por
      //uno que contiene a los 2
      //Es decir si la lista tiene [10, 20] y [80, 130] y se quiere agregar [21, 79]
      //la lista queda con un único rango [10, 130]
      procedure add(rango: TRango); reintroduce;
      function toString : String;
      function largoRango: Integer;
      procedure Free;
  end;

  TDAOfListaDeRangos = array of TListaDeRangos;

procedure testRango;

implementation

Constructor TRango.Create(minValor, maxValor: Integer);
begin
  inherited Create;
  self.minValor:= minValor;
  self.maxValor:= maxValor;
end;

function TRango.toString: String;
begin
  result:= '[' + IntToStr(minValor) + ', ' + IntToStr(maxValor) +']';
end;

function TRango.largoRango: Integer;
begin
  result:= maxValor - minValor + 1;
end;

Constructor TListaDeRangos.Create(minValor, maxValor: Integer);
begin
  inherited Create;
  self.minValor:= minValor;
  self.maxValor:= maxValor;
end;

function TListaDeRangos.complemento: TListaDeRangos;
var
  i, iRango: Integer;
  res: TListaDeRangos;
begin
  res:= TListaDeRangos.Create(self.minValor, Self.maxValor);

  iRango:= 0;
  i:= self.minValor;
  while iRango < Count do
  begin
    if i < TRango(Items[iRango]).minValor then
    begin
      res.add(TRango.Create(i, TRango(Items[iRango]).minValor - 1));
    end;
    i:= TRango(Items[iRango]).maxValor + 1;
    inc(iRango);
  end;
  if i < Self.maxValor then
    res.add(TRango.Create(i, maxValor));
  result:= res;
end;

procedure TListaDeRangos.add(rango: TRango);
var
  iRango: Integer;
begin
  iRango:= 0;
  while iRango < Self.Count do
  begin
    if rango.maxValor <= TRango(items[iRango]).minValor then
      break
    else
      inc(iRango);
  end;
  Insert(iRango, rango);
end;

function TListaDeRangos.toString : String;
var
  i: Integer;
  res: String;
begin
  res:= '[' + IntToStr(minValor) + '..';
  for i:= 0 to Count - 1 do
    res:= res + TRango(Items[i]).toString + '..';
  res:= res + IntToStr(maxValor) + ']';
  result:= res;
end;

function TListaDeRangos.largoRango: Integer;
begin
  result:= maxValor - minValor + 1;
end;

procedure TListaDeRangos.Free;
var
  i: Integer;
begin
  for i:= 0 to Count - 1 do
    TRango(Items[i]).Free;
  inherited Free;
end;

procedure testRango;
var
  lista, comp: TListaDeRangos;
begin
  lista:= TListaDeRangos.Create(0, 150);
  writeln(lista.toString);
  //[0..150]

  lista.add(TRango.Create(10, 20));
  writeln('Agrego [10, 20]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[10, 20]..150]

  lista.add(TRango.Create(80, 130));
  writeln('Agrego [80, 130]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[10, 20]..[80, 130]..150]

  lista.add(TRango.Create(3, 5));
  writeln('Agrego [3, 5]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[3, 5]..[10, 20]..[80, 130]..150]

  lista.add(TRango.Create(0, 2));
  writeln('Agrego [0, 2]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[0, 5]..[10, 20]..[80, 130]..150]

  lista.add(TRango.Create(131, 145));
  writeln('Agrego [131, 145]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[0, 5]..[10, 20]..[80, 145]..150]

  lista.add(TRango.Create(21, 79));
  writeln('Agrego [21, 79]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[0, 5]..[10, 145]..150]


  lista.add(TRango.Create(9, 9));
  writeln('Agrego [9, 9]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[0, 5]..[9, 145]..150]

  lista.add(TRango.Create(6, 6));
  writeln('Agrego [6, 6]= ', lista.toString);
  comp:= lista.complemento;
  writeln('Complemento= ', comp.toString);
  writeln;
  comp.Free;
  //[0..[0, 6]..[9, 145]..150]

  lista.Free;
end;

end.
