)abbrev package IMATLIN InnerMatrixLinearAlgebraFunctions
++ Author: Clifton J. Williamson, P.Gianni
++ Date Created: 13 November 1989
++ Basic Operations:
++ Related Domains: IndexedMatrix(R, minRow, minCol), Matrix(R),
++    RectangularMatrix(n, m, R), SquareMatrix(n, R)
++ Also See:
++ AMS Classifications:
++ Keywords: matrix, canonical forms, linear algebra
++ Examples:
++ References:
++ Description:
++   \spadtype{InnerMatrixLinearAlgebraFunctions} is an internal package
++   which provides standard linear algebra functions on domains in
++   \spad{MatrixCategory}
InnerMatrixLinearAlgebraFunctions(R, Row, Col, M) : _
             Exports == Implementation where
  R   : DivisionRing
  Row : FiniteLinearAggregate R
  Col : FiniteLinearAggregate R
  M   : MatrixCategory(R, Row, Col)
  I ==> Integer

  Exports ==> with
    rowEchelon : M -> M
      ++ \spad{rowEchelon(m)} returns the row echelon form of the matrix m.
    rank : M -> NonNegativeInteger
      ++ \spad{rank(m)} returns the rank of the matrix m.
    nullity : M -> NonNegativeInteger
      ++ \spad{nullity(m)} returns the mullity of the matrix m. This is the
      ++ dimension of the null space of the matrix m.
    if Col has shallowlyMutable then
      nullSpace : M -> List Col
        ++ \spad{nullSpace(m)} returns a basis for the null space of the
        ++ matrix m.
    if R has Field then
      determinant : M -> R
        ++ \spad{determinant(m)} returns the determinant of the matrix m.
        ++ an error message is returned if the matrix is not square.
      generalizedInverse : M -> M
        ++ \spad{generalizedInverse(m)} returns the generalized
        ++ (Moore--Penrose) inverse of the matrix m, i.e. the matrix h such
        ++ that m*h*m=h, h*m*h=m, m*h and h*m are both symmetric matrices.
    inverse : M -> Union(M, "failed")
      ++ \spad{inverse(m)} returns the inverse of the matrix m.
      ++ If the matrix is not invertible, "failed" is returned.
      ++ Error: if the matrix is not square.
    row_operation_base : (x : M, i : I, j : I, k : I, maxC : I, pp : R
                      ) -> Void
      ++ row_operation_base should be local, but conditional
    row_operation_modular : (x : M, i : I, j : I, k : I, maxC : I, pp : R, _
                  p : I) -> Void
      ++ row_operation_modular should be local, but conditional


  Implementation ==> add

    rowAllZeroes? : (M, I) -> Boolean
    rowAllZeroes?(x, i) ==
      -- determines if the ith row of x consists only of zeroes
      -- internal function: no check on index i
      for j in minColIndex(x)..maxColIndex(x) repeat
        qelt(x, i, j) ~= 0 => return false
      true

    colAllZeroes? : (M, I) -> Boolean
    colAllZeroes?(x, j) ==
      -- determines if the ith column of x consists only of zeroes
      -- internal function: no check on index j
      for i in minRowIndex(x)..maxRowIndex(x) repeat
        qelt(x, i, j) ~= 0 => return false
      true

    Ops ==>

        row_operation_base(x : M, i : I, j : I, k : I, maxC : I, pp : R
                          ) : Void ==
            for k1 in j..maxC repeat
                qsetelt!(x, k, k1, qelt(x, k, k1) - pp*qelt(x, i, k1))

        row_operation_modular(x : M, i : I, j : I, k : I, maxC : I, pp : R, _
                      p : I) : Void ==
            pp1 := p -(pp pretend I)
            js := qconvert(j)@SingleInteger
            ms := qconvert(maxC)@SingleInteger
            for k1 in js..ms repeat
                qsetelt!(x, k, k1, QSMULADDMOD64_32(pp1,
                           qelt(x, i, k1), qelt(x, k, k1), p)$Lisp)

        rowEchelon y ==
            -- row echelon form via Gaussian elimination
            -- Choose most efficient version of row operations
            row_op : (M, I, I, I, I, R) -> Void :=
                cc := characteristic()$R
                cc > 0 and cc < 1000000000 and _
                  R is PrimeField(cc::PositiveInteger) => _
                    (x : M, i : I, j : I, k : I, maxC : I, pp : R) : Void +-> _
                      row_operation_modular(x, i, j, k, maxC, pp, cc)
                row_operation_base
            x := copy y
            minR := minRowIndex x; maxR := maxRowIndex x
            minC := minColIndex x; maxC := maxColIndex x
            i := minR
            n : I := minR - 1
            for j in minC..maxC repeat
                i > maxR => return x
                n := minR - 1
                -- n = smallest k such that k >= i and x(k, j) ~= 0
                for k in i..maxR repeat
                    if qelt(x, k, j) ~= 0 then (n := k; break)
                n = minR - 1 => "no non-zeroes"
                -- put nth row in ith position
                if i ~= n then swapRows!(x, i, n)
                -- divide ith row by its first non-zero entry
                b := inv qelt(x, i, j)
                qsetelt!(x, i, j, 1)
                for k in (j+1)..maxC repeat
                    qsetelt!(x, i, k, b * qelt(x, i, k))
                -- perform row operations so that jth column has only one 1
                for k in minR..maxR repeat
                      if k ~= i and not((pp := qelt(x, k, j)) = 0) then
                          row_op(x, i, j + 1, k, maxC, pp)
                          qsetelt!(x, k, j, 0)
                -- increment i
                i := i + 1
            x

        if Col has shallowlyMutable then

            nullSpace y ==
                x := rowEchelon y
                minR := minRowIndex x; maxR := maxRowIndex x
                minC := minColIndex x; maxC := maxColIndex x
                nrow := nrows x; ncol := ncols x
                basis : List Col := []
                rk := nrow; row := maxR
                -- compute rank = # rows - # rows of all zeroes
                while rk > 0 and rowAllZeroes?(x, row) repeat
                    rk := (rk - 1) :: NonNegativeInteger
                    row := (row - 1) :: NonNegativeInteger
                -- if maximal rank, return empty list
                ncol <= nrow and rk = ncol => []
                -- if rank = 0, return standard basis vectors
                rk = 0 =>
                    for j in minC..maxC repeat
                        w : Col := new(ncol, 0)
                        qsetelt!(w, j, 1)
                        basis := cons(w, basis)
                    basis
                -- v contains information about initial 1's in the rows of x
                -- if the ith row has an initial 1 in the jth column, then
                -- v.j = i; v.j = minR - 1, otherwise
                v : IndexedOneDimensionalArray(I, minC) := new(ncol, minR - 1)
                for i in minR..(minR + rk - 1) repeat
                    for j in minC.. while qelt(x, i, j) = 0 repeat j
                    qsetelt!(v, j, i)
                j := maxC; l := minR + ncol - 1
                while j >= minC repeat
                    w : Col := new(ncol, 0)
                    -- if there is no row with an initial 1 in the jth column,
                    -- create a basis vector with a 1 in the jth row
                    if qelt(v, j) = minR - 1 then
                        colAllZeroes?(x, j) =>
                            qsetelt!(w, l, 1)
                            basis := cons(w, basis)
                        for k in minC..(j-1) for ll in minR..(l-1) repeat
                            if qelt(v, k) ~= minR - 1 then
                                qsetelt!(w, ll, -qelt(x, qelt(v, k), j))
                        qsetelt!(w, l, 1)
                        basis := cons(w, basis)
                    j := j - 1; l := l - 1
                basis

        determinant y ==
            (ndim := nrows y) ~= (ncols y) =>
                error "determinant: matrix must be square"
            -- Gaussian Elimination
            ndim = 0 => 1
            ndim = 1 => qelt(y, minRowIndex y, minColIndex y)
            x := copy y
            minR := minRowIndex x; maxR := maxRowIndex x
            minC := minColIndex x; maxC := maxColIndex x
            ans : R := 1
            for i in minR..(maxR - 1) for j in minC..(maxC - 1) repeat
                if qelt(x, i, j) = 0 then
                    rown := minR - 1
                    for k in (i+1)..maxR repeat
                        qelt(x, k, j) ~= 0 => (rown := k; break)
                    if rown = minR - 1 then return 0
                    swapRows!(x, i, rown); ans := -ans
                ans := qelt(x, i, j) * ans; b := -inv qelt(x, i, j)
                for l in (j+1)..maxC repeat
                    qsetelt!(x, i, l, b * qelt(x, i, l))
                for k in (i+1)..maxR repeat
                    if not((b := qelt(x, k, j)) = 0) then
                       for l in (j+1)..maxC repeat
                           qsetelt!(x, k, l, qelt(x, k, l) + b * qelt(x, i, l))
            qelt(x, maxR, maxC) * ans

    -- condition to allow optimization of array accessors
    if M is Matrix(R) then

        Ops

    else

        Ops


    rank x ==
        y :=
            (rk := nrows x) > (rh := ncols x) =>
                rk := rh
                transpose x
            copy x
        y := rowEchelon y; i := maxRowIndex y
        while rk > 0 and rowAllZeroes?(y, i) repeat
            i := i - 1
            rk := (rk - 1)::NonNegativeInteger
        rk::NonNegativeInteger

    nullity x == (ncols x - rank x)::NonNegativeInteger

    if R has Field then
        generalizedInverse(x) ==
            SUP := SparseUnivariatePolynomial R
            FSUP := Fraction SUP
            VFSUP := Vector FSUP
            MATCAT2 := MatrixCategoryFunctions2(R, Row, Col, M,
                         FSUP, VFSUP, VFSUP, Matrix FSUP)
            MATCAT22 := MatrixCategoryFunctions2(FSUP, VFSUP, VFSUP,
                                                 Matrix FSUP, R, Row, Col, M)
            y := map((r1 : R) : FSUP +-> coerce(
                           coerce(r1)$SUP)$FSUP, x)$MATCAT2
            ty := transpose y
            yy := ty*y
            nc := ncols yy
            var := monomial(1, 1)$SUP ::(Fraction SUP)
            yy := inverse(yy+scalarMatrix(ncols yy, var))::Matrix(FSUP)*ty
            map((z1 : FSUP) : R +-> elt(z1, 0), yy)$MATCAT22

    inv_gen(x : M) : Union(M, "failed") ==
      (ndim := nrows x) ~= (ncols x) =>
        error "inverse: matrix must be square"
      ndim = 0 => x
      AB : M := zero(ndim, ndim + ndim)
      minR := minRowIndex x; maxR := maxRowIndex x
      minC := minColIndex x; maxC := maxColIndex x
      kmin := minRowIndex AB; kmax := kmin + ndim - 1
      lmin := minColIndex AB; lmax := lmin + ndim - 1
      for i in minR..maxR for k in kmin..kmax repeat
        for j in minC..maxC for l in lmin..lmax repeat
          qsetelt!(AB, k, l, qelt(x, i, j))
        qsetelt!(AB, k, lmin + ndim + k - kmin, 1)
      AB := rowEchelon AB
      elt(AB,kmax,lmax) = 0 => "failed"
      subMatrix(AB, kmin, kmax, lmin + ndim, lmax + ndim)

    if R has Field and M is Matrix(R) then

        inverse x ==
            nrows(x) = 2 and ncols(x) = 2 =>
                ans2 : M := zero(2, 2)
                zero?(det :=  x(1,1)*x(2,2)-x(1,2)*x(2,1)) => "failed"
                detinv := inv det
                ans2(1, 1) := x(2, 2)*detinv
                ans2(1, 2) := -x(1, 2)*detinv
                ans2(2, 1) := -x(2, 1)*detinv
                ans2(2, 2) := x(1, 1)*detinv
                ans2
            inv_gen(x)

    else

        inverse x == inv_gen(x)

)abbrev package MATCAT2 MatrixCategoryFunctions2
++ Author: Clifton J. Williamson
++ Date Created: 21 November 1989
++ Basic Operations:
++ Related Domains: IndexedMatrix(R, minRow, minCol), Matrix(R),
++    RectangularMatrix(n, m, R), SquareMatrix(n, R)
++ Also See:
++ AMS Classifications:
++ Keywords:
++ Keywords: matrix, map, reduce
++ Examples:
++ References:
++ Description:
++   \spadtype{MatrixCategoryFunctions2} provides functions between two matrix
++   domains.  The functions provided are \spadfun{map} and \spadfun{reduce}.
MatrixCategoryFunctions2(R1, Row1, Col1, M1, R2, Row2, Col2, M2) : _
         Exports == Implementation where
  R1   : Join(SemiRng, AbelianMonoid)
  Row1 : FiniteLinearAggregate R1
  Col1 : FiniteLinearAggregate R1
  M1   : MatrixCategory(R1, Row1, Col1)
  R2   : Join(SemiRng, AbelianMonoid)
  Row2 : FiniteLinearAggregate R2
  Col2 : FiniteLinearAggregate R2
  M2   : MatrixCategory(R2, Row2, Col2)

  Exports ==> with
    map : (R1 -> R2, M1) -> M2
      ++ \spad{map(f, m)} applies the function f to the elements
      ++ of the matrix m.
    map : (R1 -> Union(R2,"failed"),M1) -> Union(M2,"failed")
      ++ \spad{map(f, m)} applies the function f to the elements
      ++ of the matrix m.
    reduce : ((R1, R2) -> R2, M1, R2) -> R2
      ++ \spad{reduce(f, m, r)} traverses m row by row passing m(i, j)
      ++ as first argument to f.  The second argument to f works as
      ++ accumulator, value from previous call is passed to next call.
      ++ On first call r is passed as second argument to f.
      ++ Return value is the last result returned by f.

  Implementation ==> add
    minr ==> minRowIndex
    maxr ==> maxRowIndex
    minc ==> minColIndex
    maxc ==> maxColIndex

    map(f : (R1->R2), m : M1) : M2 ==
      ans : M2 := new(nrows m, ncols m, 0)
      for i in minr(m)..maxr(m) for k in minr(ans)..maxr(ans) repeat
        for j in minc(m)..maxc(m) for l in minc(ans)..maxc(ans) repeat
          qsetelt!(ans, k, l, f qelt(m, i, j))
      ans

    map(f:(R1 -> (Union(R2,"failed"))),m:M1):Union(M2,"failed") ==
      ans : M2 := new(nrows m, ncols m, 0)
      for i in minr(m)..maxr(m) for k in minr(ans)..maxr(ans) repeat
        for j in minc(m)..maxc(m) for l in minc(ans)..maxc(ans) repeat
          (r := f qelt(m,i,j)) = "failed" => return "failed"
          qsetelt!(ans, k, l, r::R2)
      ans

    reduce(f, m, ident) ==
      s := ident
      for i in minr(m)..maxr(m) repeat
       for j in minc(m)..maxc(m) repeat
         s := f(qelt(m, i, j), s)
      s

)abbrev package RMCAT2 RectangularMatrixCategoryFunctions2
++ Author: Clifton J. Williamson
++ Date Created: 21 November 1989
++ Basic Operations:
++ Related Domains: IndexedMatrix(R, minRow, minCol), Matrix(R),
++    RectangularMatrix(n, m, R), SquareMatrix(n, R)
++ Also See:
++ AMS Classifications:
++ Keywords:
++ Keywords: matrix, map, reduce
++ Examples:
++ References:
++ Description:
++ \spadtype{RectangularMatrixCategoryFunctions2} provides functions between
++ two matrix domains.  The functions provided are \spadfun{map} and \spadfun{reduce}.

RectangularMatrixCategoryFunctions2(m, n, R1, Row1, Col1, M1, R2, Row2, Col2, M2) : _
         Exports == Implementation where
  m, n  : NonNegativeInteger
  R1   : Join(SemiRng, AbelianMonoid)
  Row1 : DirectProductCategory(n, R1)
  Col1 : DirectProductCategory(m, R1)
  M1   : RectangularMatrixCategory(m, n, R1, Row1, Col1)
  R2   : Join(SemiRng, AbelianMonoid)
  Row2 : DirectProductCategory(n, R2)
  Col2 : DirectProductCategory(m, R2)
  M2   : RectangularMatrixCategory(m, n, R2, Row2, Col2)

  Exports ==> with
    map : (R1 -> R2, M1) -> M2
      ++ \spad{map(f, m)} applies the function \spad{f} to the elements of the
      ++ matrix \spad{m}.
    reduce : ((R1, R2) -> R2, M1, R2) -> R2
      ++ \spad{reduce(f, m, r)} returns a matrix \spad{n} where
      ++ \spad{n[i, j] = f(m[i, j], r)} for all indices spad{i} and \spad{j}.

  Implementation ==> add
    minr ==> minRowIndex
    maxr ==> maxRowIndex
    minc ==> minColIndex
    maxc ==> maxColIndex

    map(f, mat) ==
      ans : M2 := new(m, n, 0)$Matrix(R2) pretend M2
      for i in minr(mat)..maxr(mat) for k in minr(ans)..maxr(ans) repeat
        for j in minc(mat)..maxc(mat) for l in minc(ans)..maxc(ans) repeat
          qsetelt!(ans pretend Matrix R2, k, l, f qelt(mat, i, j))
      ans

    reduce(f, mat, ident) ==
      s := ident
      for i in minr(mat)..maxr(mat) repeat
       for j in minc(mat)..maxc(mat) repeat
         s := f(qelt(mat, i, j), s)
      s

)abbrev package IMATQF InnerMatrixQuotientFieldFunctions
++ Author: Clifton J. Williamson
++ Date Created: 22 November 1989
++ Basic Operations:
++ Related Domains: IndexedMatrix(R, minRow, minCol), Matrix(R), RectangularMatrix(n, m, R), SquareMatrix(n, R)
++ Also See:
++ AMS Classifications:
++ Keywords: matrix, inverse, integral domain
++ Examples:
++ References:
++ Description:
++   \spadtype{InnerMatrixQuotientFieldFunctions} provides functions on matrices
++   over an integral domain which involve the quotient field of that integral
++   domain.  The functions rowEchelon and inverse return matrices with
++   entries in the quotient field.
InnerMatrixQuotientFieldFunctions(R, Row, Col, M, QF, Row2, Col2, M2) : _
         Exports == Implementation where
  R    : IntegralDomain
  Row  : FiniteLinearAggregate R
  Col  : FiniteLinearAggregate R
  M    : MatrixCategory(R, Row, Col)
  QF   : QuotientFieldCategory R
  Row2 : FiniteLinearAggregate QF
  Col2 : FiniteLinearAggregate QF
  M2   : MatrixCategory(QF, Row2, Col2)
  IMATLIN ==> InnerMatrixLinearAlgebraFunctions(QF, Row2, Col2, M2)
  MATCAT2 ==> MatrixCategoryFunctions2(R, Row, Col, M, QF, Row2, Col2, M2)
  CDEN    ==> InnerCommonDenominator(R, QF, Col, Col2)

  Exports ==> with
      rowEchelon : M -> M2
        ++ \spad{rowEchelon(m)} returns the row echelon form of the matrix m.
        ++ the result will have entries in the quotient field.
      inverse : M -> Union(M2,"failed")
        ++ \spad{inverse(m)} returns the inverse of the matrix m.
        ++ If the matrix is not invertible, "failed" is returned.
        ++ Error: if the matrix is not square.
        ++ Note: the result will have entries in the quotient field.
      if Col2 has shallowlyMutable then
        nullSpace : M -> List Col
          ++ \spad{nullSpace(m)} returns a basis for the null space of the
          ++ matrix m.
  Implementation ==> add

    qfMat : M -> M2
    qfMat m == map((r1 : R) : QF +-> r1 :: QF, m)$MATCAT2

    rowEchelon m == rowEchelon(qfMat m)$IMATLIN
    inverse m ==
      (inv := inverse(qfMat m)$IMATLIN) case "failed" => "failed"
      inv :: M2

    if Col2 has shallowlyMutable then
      nullSpace m ==
        [clearDenominator(v)$CDEN for v in nullSpace(qfMat m)$IMATLIN]

)abbrev package MATLIN MatrixLinearAlgebraFunctions
++ Author: Clifton J. Williamson, P.Gianni
++ Date Created: 13 November 1989
++ Basic Operations:
++ Related Domains: IndexedMatrix(R, minRow, minCol), Matrix(R),
++    RectangularMatrix(n, m, R), SquareMatrix(n, R)
++ Also See:
++ AMS Classifications:
++ Keywords: matrix, canonical forms, linear algebra
++ Examples:
++ References:
++ Description:
++   \spadtype{MatrixLinearAlgebraFunctions} provides functions to compute
++   inverses and canonical forms.
MatrixLinearAlgebraFunctions(R, Row, Col, M) : Exports == Implementation where
  R   : CommutativeRing
  Row : FiniteLinearAggregate R
  Col : FiniteLinearAggregate R
  M   : MatrixCategory(R, Row, Col)
  I ==> Integer

  Exports ==> with

    determinant : M -> R
      ++ \spad{determinant(m)} returns the determinant of the matrix m.
      ++ an error message is returned if the matrix is not square.
    minordet : M -> R
      ++ \spad{minordet(m)} computes the determinant of the matrix m using
      ++ minors. Error: if the matrix is not square.
    elRow1!       :   (M, I, I)         ->  M
      ++ elRow1!(m, i, j) swaps rows i and j of matrix m : elementary operation
      ++ of first kind
    elRow2!       :  (M, R, I, I)        ->  M
      ++ elRow2!(m, a, i, j)  adds to row i a*row(m, j) : elementary operation of
      ++ second kind. (i ~=j)
    elColumn2!    :  (M, R, I, I)        ->  M
      ++ elColumn2!(m, a, i, j)  adds to column i a*column(m, j) : elementary
      ++ operation of second kind. (i ~=j)

    if R has IntegralDomain then
      rank : M -> NonNegativeInteger
        ++ \spad{rank(m)} returns the rank of the matrix m.
      nullity : M -> NonNegativeInteger
        ++ \spad{nullity(m)} returns the mullity of the matrix m. This is
        ++ the dimension of the null space of the matrix m.
      nullSpace : M -> List Col
        ++ \spad{nullSpace(m)} returns a basis for the null space of the
        ++ matrix m.
      fractionFreeGauss! : M -> M
        ++ \spad{fractionFreeGauss(m)} performs the fraction
        ++ free gaussian  elimination on the matrix m.
      invertIfCan : M -> Union(M,"failed")
        ++ \spad{invertIfCan(m)} returns the inverse of m over R
      adjoint : M -> Record(adjMat : M, detMat : R)
        ++ \spad{adjoint(m)} returns the ajoint matrix of m (i.e. the matrix
        ++ n such that m*n = determinant(m)*id) and the detrminant of m.

    if R has EuclideanDomain then
      rowEchelon : M -> M
        ++ \spad{rowEchelon(m)} returns the row echelon form of the matrix m.

      normalizedDivide : (R, R) -> Record(quotient : R, remainder : R)
        ++ normalizedDivide(n, d) returns a normalized quotient and
        ++ remainder such that consistently unique representatives
        ++ for the residue class are chosen, e.g. positive remainders

    if R has Field then
      inverse : M -> Union(M,"failed")
        ++ \spad{inverse(m)} returns the inverse of the matrix.
        ++ If the matrix is not invertible, "failed" is returned.
        ++ Error: if the matrix is not square.

  Implementation ==> add

    rowAllZeroes? : (M, I) -> Boolean
    rowAllZeroes?(x, i) ==
      -- determines if the ith row of x consists only of zeroes
      -- internal function: no check on index i
      for j in minColIndex(x)..maxColIndex(x) repeat
        qelt(x, i, j) ~= 0 => return false
      true

    colAllZeroes? : (M, I) -> Boolean
    colAllZeroes?(x, j) ==
      -- determines if the ith column of x consists only of zeroes
      -- internal function: no check on index j
      for i in minRowIndex(x)..maxRowIndex(x) repeat
        qelt(x, i, j) ~= 0 => return false
      true

    minorDet:(M,I,List I,I,PrimitiveArray(Union(R,"uncomputed")))-> R
    minorDet(x, m, l, i, v) ==
      z := v.m
      z case R => z
      ans : R := 0; rl : List I := []
      j := first l; l := rest l; pos := true
      minR := minRowIndex x; minC := minColIndex x;
      repeat
        if qelt(x, j + minR, i + minC) ~= 0 then
          ans :=
            md := minorDet(x, m - 2^(j :: NonNegativeInteger), _
                           concat!(reverse rl, l), i + 1, v) *_
                           qelt(x, j + minR, i + minC)
            pos => ans + md
            ans - md
        empty?(l) =>
          v.m := ans
          return ans
        pos := not pos; rl := cons(j, rl); j := first l; l := rest l

    minordet x ==
      (ndim := nrows x) ~= (ncols x) =>
        error "determinant: matrix must be square"
      -- minor expansion with (s---loads of) memory
      ndim = 0 => 1
      n1 : I := ndim - 1
      v : PrimitiveArray(Union(R,"uncomputed")) :=
           new((2^ndim - 1) :: NonNegativeInteger,"uncomputed")
      minR := minRowIndex x; maxC := maxColIndex x
      for i in 0..n1 repeat
        qsetelt!(v, (2^i - 1), qelt(x, i + minR, maxC))
      minorDet(x, 2^ndim - 2, [i for i in 0..n1], 0, v)

       -- elementary operation of first kind: exchange two rows --
    elRow1!(m : M, i : I, j : I) : M ==
      vec := row(m, i)
      setRow!(m, i, row(m, j))
      setRow!(m, j, vec)
      m

             -- elementary operation of second kind: add to row i--
                         -- a*row j  (i~=j) --
    elRow2!(m : M, a : R, i : I, j : I) : M ==
      vec := map((r1 : R) : R +-> a*r1, row(m, j))
      vec := map("+",row(m,i),vec)
      setRow!(m, i, vec)
      m
             -- elementary operation of second kind: add to column i --
                           -- a*column j (i~=j) --
    elColumn2!(m : M, a : R, i : I, j : I) : M ==
      vec := map((r1 : R) : R +-> a*r1, column(m, j))
      vec := map("+",column(m,i),vec)
      setColumn!(m, i, vec)
      m

    if R has IntegralDomain then
      -- Fraction-Free Gaussian Elimination
      fractionFreeGauss! x  ==
        (ndim := nrows x) = 1 => x
        ans := b := 1$R
        minR := minRowIndex x; maxR := maxRowIndex x
        minC := minColIndex x; maxC := maxColIndex x
        i := minR
        for j in minC..maxC repeat
          if qelt(x, i, j) = 0 then -- candidate for pivot = 0
            rown := minR - 1
            for k in (i+1)..maxR repeat
              if qelt(x, k, j) ~= 0 then
                 rown := k -- found a pivot
                 break
            if rown > minR - 1 then
               swapRows!(x, i, rown)
               ans := -ans
          (c := qelt(x,i,j)) = 0 =>  "next j" -- try next column
          for k in (i+1)..maxR repeat
            if qelt(x, k, j) = 0 then
              for l in (j+1)..maxC repeat
                qsetelt!(x, k, l, (c * qelt(x, k, l) exquo b) :: R)
            else
              pv := qelt(x, k, j)
              qsetelt!(x, k, j, 0)
              for l in (j+1)..maxC repeat
                val := c * qelt(x, k, l) - pv * qelt(x, i, l)
                qsetelt!(x, k, l, (val exquo b) :: R)
          b := c
          (i := i + 1) > maxR => break
        if ans ~= 1 then
          lasti := i-1
          for j in 1..maxC repeat x(lasti, j) := -x(lasti, j)
        x

      --
      lastStep(x : M)  : M ==
        ndim := nrows x
        minR := minRowIndex x; maxR := maxRowIndex x
        minC := minColIndex x; maxC := minC+ndim -1
        exCol := maxColIndex x
        det := x(maxR, maxC)
        maxR1 := maxR-1
        maxC1 := maxC+1
        minC1 := minC+1
        iRow := maxR
        iCol := maxC-1
        for i in maxR1..1 by -1 repeat
          for j in maxC1..exCol repeat
            ss := +/[x(i, iCol+k)*x(i+k, j) for k in 1..(maxR-i)]
            x(i, j) := _exquo((det * x(i, j) - ss), x(i, iCol))::R
          iCol := iCol-1
        subMatrix(x, minR, maxR, maxC1, exCol)

      invertIfCan(y) ==
        (nr := nrows y) ~= (ncols y) =>
            error "invertIfCan: matrix must be square"
        nr = 0 => y
        adjRec := adjoint y
        (den := recip(adjRec.detMat)) case "failed" => "failed"
        den::R * adjRec.adjMat

      adjoint(y) ==
        (nr := nrows y) ~= (ncols y) => error "adjoint: matrix must be square"
        nr = 0 => [y, 1]
        maxR := maxRowIndex y
        maxC := maxColIndex y
        x := horizConcat(copy y, scalarMatrix(nr, 1$R))
        ffr := fractionFreeGauss!(x)
        det := ffr(maxR, maxC)
        [lastStep(ffr), det]


    if R has Field then

      VR      ==> Vector R
      IMATLIN ==> InnerMatrixLinearAlgebraFunctions(R, Row, Col, M)
      MMATLIN ==> InnerMatrixLinearAlgebraFunctions(R, VR, VR, Matrix R)
      FLA2    ==> FiniteLinearAggregateFunctions2(R, VR, R, Col)
      MAT2    ==> MatrixCategoryFunctions2(R, Row, Col, M, R, VR, VR, Matrix R)

      rowEchelon  y == rowEchelon(y)$IMATLIN
      rank        y == rank(y)$IMATLIN
      nullity     y == nullity(y)$IMATLIN
      determinant y == determinant(y)$IMATLIN
      inverse     y == inverse(y)$IMATLIN
      if Col has shallowlyMutable then
        nullSpace y == nullSpace(y)$IMATLIN
      else
        nullSpace y ==
          [map((r1 : R) : R +-> r1, v)$FLA2
               for v in nullSpace(map((r1 : R) : R +-> r1, y)$MAT2)$MMATLIN]

    else if R has IntegralDomain then
      QF      ==> Fraction R
      Row2    ==> Vector QF
      Col2    ==> Vector QF
      M2      ==> Matrix QF
      IMATQF  ==> InnerMatrixQuotientFieldFunctions(R, Row, Col, M, QF, Row2, Col2, M2)

      nullSpace m == nullSpace(m)$IMATQF

      determinant y ==
        (ndim := nrows y) ~= (ncols y) =>
            error "determinant: matrix must be square"
        ndim = 0 => 1
        fm := fractionFreeGauss!(copy y)
        fm(maxRowIndex fm, maxColIndex fm)

      rank x ==
        y :=
          (rk := nrows x) > (rh := ncols x) =>
              rk := rh
              transpose x
          copy x
        y := fractionFreeGauss! y
        i := maxRowIndex y
        while rk > 0 and rowAllZeroes?(y, i) repeat
          i := i - 1
          rk := (rk - 1) :: NonNegativeInteger
        rk :: NonNegativeInteger

      nullity x == (ncols x - rank x) :: NonNegativeInteger

      if R has EuclideanDomain then

        if R has IntegerNumberSystem then
            normalizedDivide(n : R, d : R) : Record(quotient : R, remainder : R) ==
               qr := divide(n, d)
               qr.remainder >= 0 => qr
               d > 0 =>
                  qr.remainder := qr.remainder + d
                  qr.quotient := qr.quotient - 1
                  qr
               qr.remainder := qr.remainder - d
               qr.quotient := qr.quotient + 1
               qr
        else
            normalizedDivide(n : R, d : R) : Record(quotient : R, remainder : R) ==
               divide(n, d)

        rowEchelon y ==
          x := copy y
          minR := minRowIndex x; maxR := maxRowIndex x
          minC := minColIndex x; maxC := maxColIndex x
          n := minR - 1
          i := minR
          for j in minC..maxC repeat
            if i > maxR then break
            n := minR - 1
            xnj : R
            for k in i..maxR repeat
              if not zero?(xkj := qelt(x, k, j)) and ((n = minR - 1) _
                     or sizeLess?(xkj, xnj)) then
                n := k
                xnj := xkj
            n = minR - 1 => "next j"
            swapRows!(x, i, n)
            for k in (i+1)..maxR repeat
              qelt(x,k,j) = 0 => "next k"
              aa := extendedEuclidean(qelt(x, i, j), qelt(x, k, j))
              (a, b, d) := (aa.coef1, aa.coef2, aa.generator)
              b1 := (qelt(x, i, j) exquo d) :: R
              a1 := (qelt(x, k, j) exquo d) :: R
              -- a*b1+a1*b = 1
              for k1 in (j+1)..maxC repeat
                val1 := a * qelt(x, i, k1) + b * qelt(x, k, k1)
                val2 := -a1 * qelt(x, i, k1) + b1 * qelt(x, k, k1)
                qsetelt!(x, i, k1, val1); qsetelt!(x, k, k1, val2)
              qsetelt!(x, i, j, d); qsetelt!(x, k, j, 0)

            un := unitNormal qelt(x, i, j)
            qsetelt!(x, i, j, un.canonical)
            if un.associate ~= 1 then for jj in (j+1)..maxC repeat
                qsetelt!(x, i, jj, un.associate * qelt(x, i, jj))

            xij := qelt(x, i, j)
            for k in minR..(i-1) repeat
              qelt(x,k,j) = 0 => "next k"
              qr := normalizedDivide(qelt(x, k, j), xij)
              qsetelt!(x, k, j, qr.remainder)
              for k1 in (j+1)..maxC repeat
                qsetelt!(x, k, k1, qelt(x, k, k1) - qr.quotient * qelt(x, i, k1))
            i := i + 1
          x

    else determinant x == minordet x

--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-- This file and MATRIX SPAD must be compiled in bootstrap mode.
