{$N-}
program Hilb;
{

  The program performs simultaneous solution by Gauss-Jordan
  elimination.

  --------------------------------------------------
  From: Pascal Programs for Scientists and Engineers

  Alan R. Miller, Sybex
  n x n inverse hilbert matrix
  solution is 1 1 1 1 1
  double precision version
  --------------------------------------------------

  INSTRUCTIONS
  1.  Compile and run the program using the $N- (Numeric Processing :
      Software) compiler directive.
  2.  if you have a math coprocessor in your computer, compile and run the
      program using the $N+ (Numeric Processing : Hardware) compiler
      directive.  Compare the speed and precision of the results to those
      of example 1.
}

const
  maxr = 10;
  maxc = 10;

type
{$IFOPT N+}                        { use extended type if using 80x87 }
  real  = extended;
{$ENDIF}
  ary   = array[1..maxr] of real;
  arys  = array[1..maxc] of real;
  ary2s = array[1..maxr, 1..maxc] of real;

var
  y          : arys;
  coef       : arys;
  a, b       : ary2s;
  n, m, i, j : integer;
  error      : boolean;

procedure gaussj
  (var b     : ary2s;  (* square matrix of coefficients *)
    y        : arys;  (* constant vector *)
    var coef : arys;  (* solution vector *)
    ncol     : integer;  (* order of matrix *)
    var error: boolean); (* true if matrix singular *)

(*  Gauss Jordan matrix inversion and solution *)
(*  Adapted from McCormick  *)
(*  Feb  8, 81 *)
(*   B(N,N) coefficient matrix, becomes inverse *)
(*   Y(N)   original constant vector *)
(*   W(N,M) constant vector(s) become solution vector *)
(*   DETERM is the determinant *)
(*   ERROR = 1 if singular *)
(*   INDEX(N,3) *)
(*   NV is number of constant vectors *)

var
  w    : array[1..maxc, 1..maxc] of real;
  index: array[1..maxc, 1..3] of integer;
  i, j, k, l, nv, irow, icol, n, l1   : integer;
  determ, pivot, hold, sum, t, ab, big: real;

procedure swap(var a, b: real);

var
  hold: real;

begin  (* swap *)
  hold := a;
  a := b;
  b := hold
end  (* procedure swap *);


begin     (* Gauss-Jordan main program *)
  error := false;
  nv := 1 (* single constant vector *);
  n := ncol;
  for i := 1 to n do
    begin
      w[i, 1] := y[i] (* copy constant vector *);
      index[i, 3] := 0
    end;
  determ := 1.0;
  for i := 1 to n do
    begin
      (* search for largest element *)
      big := 0.0;
      for j := 1 to n do
        begin
          if index[j, 3] <> 1 then
            begin
              for k := 1 to n do
                begin
                  if index[k, 3] > 1 then
                    begin
                      writeln(' ERROR: matrix singular');
                      error := true;
                      exit;         (* abort *)
                    end;
                  if index[k, 3] < 1 then
                    if abs(b[j, k]) > big then
                      begin
                        irow := j;
                        icol := k;
                        big := abs(b[j, k])
                      end
                end (* k loop *)
            end
        end (* j loop *);
      index[icol, 3] := index[icol, 3] + 1;
      index[i, 1] := irow;
      index[i, 2] := icol;

  (* interchange rows to put pivot on diagonal *)
  if irow <> icol then
    begin
      determ := - determ;
      for l := 1 to n do
        swap(b[irow, l], b[icol, l]);
      if nv > 0 then
        for l := 1 to nv do
          swap(w[irow, l], w[icol, l])
    end; (* if irow <> icol *)

      (* divide pivot row by pivot column *)
      pivot := b[icol, icol];
      determ := determ * pivot;
      b[icol, icol] := 1.0;
      for l := 1 to n do
        b[icol, l] := b[icol, l] / pivot;
      if nv > 0 then
        for l := 1 to nv do
          w[icol, l] := w[icol, l] / pivot;
      (*  reduce nonpivot rows *)
      for l1 := 1 to n do
        begin
          if l1 <> icol then
            begin
              t := b[l1, icol];
              b[l1, icol] := 0.0;
              for l := 1 to n do
                b[l1, l] := b[l1, l] - b[icol, l] * t;
              if nv > 0 then
                for l := 1 to nv do
                  w[l1, l] := w[l1, l] - w[icol, l] * t;
            end   (* if l1 <> icol *)
        end
    end (* i loop *);

  if error then exit;
  (* interchange columns *)
  for i := 1 to n do
    begin
      l := n - i + 1;
      if index[l, 1] <> index[l, 2] then
        begin
          irow := index[l, 1];
          icol := index[l, 2];
          for k := 1 to n do
            swap(b[k, irow], b[k, icol])
        end (* if index *)
    end  (* i loop *);
  for k := 1 to n do
    if index[k, 3] <> 1 then
      begin
        writeln(' ERROR: matrix singular');
        error := true;
        exit;   (* abort *)
      end;
  for i := 1 to n do
    coef[i] := w[i, 1];
end (* procedure gaussj *);


procedure get_data(var a : ary2s;
                   var y : arys;
                   var n, m : integer);

(* setup n-by-n hilbert matrix *)

var
  i, j : integer;

begin
  for i := 1 to n do
    begin
      a[n,i] := 1.0/(n + i - 1);
      a[i,n] := a[n,i]
    end;
  a[n,n] := 1.0/(2*n -1);
  for i := 1 to n do
    begin
      y[i] := 0.0;
      for j := 1 to n do
        y[i] := y[i] + a[i,j]
    end;
  writeln;
  if n < 7 then
    begin
      for i:= 1 to n  do
        begin
          for j:= 1 to m do
            write( a[i,j] :7:5, '  ');
          writeln( ' : ', y[i] :7:5)
        end;
      writeln
    end  (* if n<7 *)
end (* procedure get_data *);

procedure write_data;

(* print out the answers *)

var
  i : integer;

begin
  for i := 1 to m do
    write( coef[i] :13:9);
  writeln;
end (* write_data *);


begin  (* main program *)
  a[1,1] := 1.0;
  n := 2;
  m := n;
  repeat
    get_data (a, y, n, m);
    for i := 1 to n do
      for j := 1 to n do
        b[i,j] := a[i,j] (* setup work array *);
    gaussj (b, y, coef, n, error);
    if not error then write_data;
    n := n+1;
    m := n
  until n > maxr;
end.
