File : lin-ops.adb



package body Lin.Ops is

  function CompareR(U,V: Trip) return Integer is
--- 1 means U<V, -1 means U>V
  begin
    if U.R<V.R then return  1; end if;
    if U.R>V.R then return -1; end if;
    return 0;
  end CompareR;

  function CompareIJ(U,V: Trip) return Integer is
--- 1 means U<V, -1 means U>V
  begin
    if U.I<V.I then return  1; end if;
    if U.I>V.I then return -1; end if;
    if U.J<V.J then return  1; end if;
    if U.J>V.J then return -1; end if;
    return 0;
  end CompareIJ;

  function CompareJI(U,V: Trip) return Integer is
--- 1 means U<V, -1 means U>V
  begin
    if U.J<V.J then return  1; end if;
    if U.J>V.J then return -1; end if;
    if U.I<V.I then return  1; end if;
    if U.I>V.I then return -1; end if;
    return 0;
  end CompareJI;

  procedure Sort(W: in out Sparse; Compare: in TripCompare) is
--- translated from numerical recipes
--- elements in sorted W are increasing
    L,J,Ir,I: Integer;
    Rw: Trip;
  begin
    Ir := W(0).I;
    if Ir<2 then return; end if;
    L := Ir/2+1;
    loop
      if L>1 then
        L := L-1;
        Rw := W(L);
      else
        Rw := W(Ir);
        W(Ir) := W(1);
        Ir := Ir-1;
        if Ir=1 then
          W(1) := Rw;
          return;
        end if;
      end if;
      I := L;
      J := L+L;
      while J<=Ir loop
        if J<Ir and then Compare(W(J),W(J+1))>0 then
          J := J+1;
        end if;
        if Compare(Rw,W(J))>0 then
          W(I) := W(J);
          I := J;
          J := J+J;
        else
          J := Ir+1;
        end if;
      end loop;
      W(I) := Rw;
    end loop;
  end Sort;

  procedure Cleanup(W: in out Sparse; Force: in Boolean := False) is
  begin
    if not Force and then W(0).J=0 then return; end if;
    if W(0).I=0 then return; end if;
    declare
      Changed: Boolean := False;
      L: Integer := 0;
      Keep: array(W'Range) of Boolean;
    begin
      Sort(W,CompareIJ'Access);
      for J in reverse 1 .. Length(W) loop
        if W(J).R=Zero then
          Keep(J) := False;
          Changed := True;
        elsif J>1 and then CompareIJ(W(J),W(J-1))=0 then
          W(J-1).R := W(J-1).R+W(J).R;
          Keep(J) := False;
          Changed := True;
        else
          Keep(J) := True;
        end if;
      end loop;
      if Changed then
        for J in 1 .. Length(W) loop
          if Keep(J) then
            L := L+1;
            W(L) := W(J);
          end if;
        end loop;
        W(0).I := L;
      end if;
      W(0).J := 0;
    end;
  end Cleanup;

end Lin.Ops;