File : lin-ops.adb


with Messages;
use Messages;

package body Lin.Ops is

  function "-"(U: Ivec) return Ivec is
    W: IVec(U'Range);
  begin
    for I in U'Range loop
      W(I) := -U(I);
    end loop;
    return W;
  end "-";

  function "+"(U,V: IVec) return IVec is
    W: IVec(U'Range);
  begin
    for I in U'Range loop
      W(I) := U(I)+V(I);
    end loop;
    return W;
  end "+";

  function "-"(U,V: IVec) return IVec is
    W: IVec(U'Range);
  begin
    for I in U'Range loop
      W(I) := U(I)-V(I);
    end loop;
    return W;
  end "-";

  function "-"(U: Rvec) return Rvec is
    W: RVec(U'Range);
  begin
    for I in U'Range loop
      W(I) := -U(I);
    end loop;
    return W;
  end "-";

  function "+"(U,V: RVec) return RVec is
    W: RVec(U'Range);
  begin
    for I in U'Range loop
      W(I) := U(I)+V(I);
    end loop;
    return W;
  end "+";

  function "-"(U,V: RVec) return RVec is
    W: RVec(U'Range);
  begin
    for I in U'Range loop
      W(I) := U(I)-V(I);
    end loop;
    return W;
  end "-";

  function "*"(U,V: RVec) return Rep is
    R: Rep := Zero;
  begin
    for I in U'Range loop
      R := R+U(I)*V(I);
    end loop;
    return R;
  end "*";

  function "-"(A: RMat) return RMat is
    B: RMat(A'Range(1),A'Range(2));
  begin
    for I in A'Range(1) loop
      for J in A'Range(2) loop
        B(I,J) := -A(I,J);
      end loop;
    end loop;
    return B;
  end "-";

  function "+"(A,B: RMat) return RMat is
    C: RMat(A'Range(1),A'Range(2));
  begin
    for I in A'Range(1) loop
      for J in A'Range(2) loop
        C(I,J) := A(I,J)+B(I,J);
      end loop;
    end loop;
    return C;
  end "+";

  function "-"(A,B: RMat) return RMat is
    C: RMat(A'Range(1),A'Range(2));
  begin
    for I in A'Range(1) loop
      for J in A'Range(2) loop
        C(I,J) := A(I,J)-B(I,J);
      end loop;
    end loop;
    return C;
  end "-";

  function "*"(A: RMat; U: RVec) return RVec is
    R: Rep;
    V: RVec(A'Range(1));
  begin
    for I in V'Range loop
      R := Zero;
      for J in U'Range loop
        R := R+A(I,J)*U(J);
      end loop;
      V(I) := R;
    end loop;
    return V;
  end "*";

  function "*"(A,B: RMat) return RMat is
    R: Rep;
    C: RMat(A'Range(1),B'Range(2));
  begin
    for I in C'Range(1) loop
      for J in C'Range(2) loop
        R := Zero;
        for K in B'Range(1) loop
          R := R+A(I,K)*B(K,J);
        end loop;
        C(I,J) := R;
      end loop;
    end loop;
    return C;
  end "*";

  function "-"(A: QMat) return Qmat is
    D: constant Positive := A.P'Last;
    B: QMat(D);
  begin
    for I in 1 .. D loop
      for J in 1 .. D loop
        B.P(I,J) := -A.P(I,J);
      end loop;
    end loop;
    B.Q := A.Q;
    return B;
  end "-";

  function "*"(A: Qmat; U: IVec) return IVec is
    D: constant Positive := A.P'Last;
    R: Integer;
    V: IVec(1 .. D);
  begin
    for I in 1 .. D loop
      R := 0;
      for J in 1 .. D loop
        R := R+A.P(I,J)*U(J);
      end loop;
      if (R mod A.Q) /= 0 then
        Message("Lin.Ops.*",Not_Implemented);
      end if;
      V(I) := R/A.Q;
    end loop;
    return V;
  end "*";

  function Gcd(P: Positive; Q: Natural) return Positive is
    Pt: Positive;
    Qt,R: Natural;
  begin
    if Q>P then Pt := Q; Qt := P;
    else Pt := P; Qt := Q; end if;
    while Qt>0 loop
      R := Pt mod Qt;
      Pt := Qt;
      Qt := R;
    end loop;
    return Pt;
  end Gcd;

  procedure Cleanup(A: in out Qmat) is
    D: constant Positive := A.P'Last;
    G: Positive := A.Q;
  begin
    for I in 1 .. D loop
      for J in 1 .. D loop
        G := Gcd(G,abs(A.P(I,J)));
      end loop;
    end loop;
    for I in 1 .. D loop
      for J in 1 .. D loop
        A.P(I,J) := A.P(I,J)/G;
      end loop;
    end loop;
    A.Q := A.Q/G;
  end Cleanup;

  function "*"(A,B: QMat) return QMat is
    D: constant Positive := A.P'Last;
    R: Integer;
    C: QMat(D);
  begin
    for I in 1 .. D loop
      for J in 1 .. D loop
        R := 0;
        for K in 1 .. D loop
          R := R+A.P(I,K)*B.P(K,J);
        end loop;
        C.P(I,J) := R;
      end loop;
    end loop;
    C.Q := A.Q*B.Q;
    if C.Q>1 then Cleanup(C); end if;
    return C;
  end "*";

  function Sqr(A: QMat) return QMat is
    D: constant Positive := A.P'Last;
    R: Integer;
    C: QMat(D);
  begin
    for I in 1 .. D loop
      for J in 1 .. D loop
        R := 0;
        for K in 1 .. D loop
          R := R+A.P(I,K)*A.P(K,J);
        end loop;
        C.P(I,J) := R;
      end loop;
    end loop;
    C.Q := A.Q*A.Q;
    if C.Q>1 then Cleanup(C); end if;
    return C;
  end Sqr;

  procedure Divide(A: in out QMat; N: in Positive) is
  begin
    A.Q := N*A.Q;
  end Divide;

   procedure dgefa(n: in Integer; a: in out RMat; ipvt: out IVec) is
-- translated from linpack routine dgefa, http://www.netlib.org/linpack/
      l: Integer;
      t: Rep;
   begin
      for k in 1 .. n-1 loop
         t := abs(a(n,k));
         l := n;
         for m in reverse k .. n-1 loop
            if (abs(a(m,k)) >= t) then
               t := abs(a(m,k));
               l := m;
            end if;
         end loop;
         ipvt(k) := l;
         if (a(l,k) /= Zero) then
            if (l /= k) then
               t := a(l,k);
               a(l,k) := a(k,k);
               a(k,k) := t;
            end if;
            t := -One/a(k,k);
            for m in k+1 .. n loop
               a(m,k) := t*a(m,k);
            end loop;
            for j in k+1 .. n loop
               t := a(l,j);
               if (l /= k) then
                  a(l,j) := a(k,j);
                  a(k,j) := t;
               end if;
               for m in k+1 .. n loop
                  a(m,j) := a(m,j) + t*a(m,k);
               end loop;
            end loop;
         end if;
      end loop;
      ipvt(n) := n;
   end dgefa;

  procedure Det(N: in Integer; A: in out RMat; R: out Rep) is
    Ipvt: IVec(1 .. N);
  begin
    Dgefa(N,A,Ipvt);
    R := One;
    for I in 1 .. N loop
      if Ipvt(I)=I then
        R := R*A(I,I);
      else
        R := -R*A(I,I);
      end if;
    end loop;
  end Det;

end Lin.Ops;