)abbrev category ARR2CAT TwoDimensionalArrayCategory
++ Two dimensional array categories and domains
++ Author:
++ Date Created: 27 October 1989
++ Keywords: array, data structure
++ Examples:
++ References:
TwoDimensionalArrayCategory(R, Row, Col) : Category == Definition where
  ++ TwoDimensionalArrayCategory is a general array category which
  ++ allows different representations and indexing schemes.
  ++ Rows and columns may be extracted with rows returned as objects
  ++ of type Row and columns returned as objects of type Col.
  ++ The index of the 'first' row may be obtained by calling the
  ++ function 'minRowIndex'.  The index of the 'first' column may
  ++ be obtained by calling the function 'minColIndex'.  The index of
  ++ the first element of a 'Row' is the same as the index of the
  ++ first column in an array and vice versa.
  R   : Type
  Row : IndexedAggregate(Integer, R)
  Col : IndexedAggregate(Integer, R)

  PI ==> PositiveInteger
  NNI ==> NonNegativeInteger
  LNNI ==> List(NNI)
  LI ==> List(Integer)
  SI ==> Segment(Integer)
  LSI ==> List(SI)

  Definition == Join(HomogeneousAggregate(R), _
      shallowlyMutable, finiteAggregate) with

    if R has Comparable then Comparable

--% Array creation

    new : (NonNegativeInteger, NonNegativeInteger, R) -> %
      ++ new(m, n, r) is an m-by-n array all of whose entries are r
    qnew : (NonNegativeInteger, NonNegativeInteger) -> %
      ++ qnew(m, n) is is an m-by-n uninitilized array
    fill! : (%, R) -> %
      ++ fill!(m, r) fills m with r's

--% Size inquiries

    minRowIndex : % -> Integer
      ++ minRowIndex(m) returns the index of the 'first' row of the array m
    maxRowIndex : % -> Integer
      ++ maxRowIndex(m) returns the index of the 'last' row of the array m
    minColIndex : % -> Integer
      ++ minColIndex(m) returns the index of the 'first' column of the array m
    maxColIndex : % -> Integer
      ++ maxColIndex(m) returns the index of the 'last' column of the array m
    nrows : % -> NonNegativeInteger
      ++ nrows(m) returns the number of rows in the array m
    ncols : % -> NonNegativeInteger
      ++ ncols(m) returns the number of columns in the array m

--% Part extractions

    elt : (%, Integer, Integer) -> R
      ++ elt(m, i, j) returns the element in the ith row and jth
      ++ column of the array m
      ++ error check to determine if indices are in proper ranges
    qelt : (%, Integer, Integer) -> R
      ++ qelt(m, i, j) returns the element in the ith row and jth
      ++ column of the array m
      ++ NO error check to determine if indices are in proper ranges
    elt : (%, Integer, Integer, R) -> R
      ++ elt(m, i, j, r) returns the element in the ith row and jth
      ++ column of the array m, if m has an ith row and a jth column,
      ++ and returns r otherwise
    row : (%, Integer) -> Row
      ++ row(m, i) returns the ith row of m
      ++ error check to determine if index is in proper ranges
    column : (%, Integer) -> Col
      ++ column(m, j) returns the jth column of m
      ++ error check to determine if index is in proper ranges
    parts : % -> List R
      ++ parts(m) returns a list of the elements of m in row major order
    listOfLists : % -> List List R
       ++ \spad{listOfLists(m)} returns the rows of the array m as a list
       ++ of lists.
    subMatrix : (%, Integer, Integer, Integer, Integer) -> %
       ++ \spad{subMatrix(x, i1, i2, j1, j2)} extracts the submatrix
       ++ \spad{[x(i, j)]} where the index i ranges from \spad{i1} to \spad{i2}
       ++ and the index j ranges from \spad{j1} to \spad{j2}.
    elt : (%, Integer, LI) -> %
       ++ \spad{elt(x, row, colList)} returns an 1-by-n array consisting
       ++ of elements of x, where \spad{n = # colList}.
       ++ If \spad{colList = [j<1>, j<2>, ..., j<n>]}, then the \spad{(k, l)}th
       ++ entry of \spad{elt(x, row, colList)} is \spad{x(row, j<l>)}.
    elt : (%, LI, Integer) -> %
       ++ \spad{elt(x, rowList, col)} returns an m-by-1 array consisting
       ++ of elements of x, where \spad{m = # rowList}.
       ++ If \spad{rowList = [i<1>, i<2>, ..., i<m>]}, then the \spad{(k, l)}th
       ++ entry of \spad{elt(x, rowList, col)} is \spad{x(i<k>, col)}.
    elt : (%, LI, LI) -> %
       ++ \spad{elt(x, rowList, colList)} returns an m-by-n array consisting
       ++ of elements of x, where \spad{m = # rowList} and \spad{n = # colList}.
       ++ If \spad{rowList = [i<1>, i<2>, ..., i<m>]} and \spad{colList =
       ++ [j<1>, j<2>, ..., j<n>]}, then the \spad{(k, l)}th entry of
       ++ \spad{elt(x, rowList, colList)} is \spad{x(i<k>, j<l>)}.
    elt : (%, SI, SI) -> %
       ++ \spad{elt(x, s1, s2)} is equivalent to
       ++ \spad{elt(x, expand(s1), expand(s2))} but should be more
       ++ convenient and more efficient.
    elt : (%, LI, SI) -> %
       ++ \spad{elt(x, rowList, s)} is equivalent to
       ++ \spad{elt(x, rowList, expand(s))} but should be more
       ++ convenient and more efficient.
    elt : (%, SI, LI) -> %
       ++ \spad{elt(x, s, colList)} is equivalent to
       ++ \spad{elt(x, expand(s), colList)} but should be more
       ++ convenient and more efficient.
    elt : (%, Integer, LSI) -> %
       ++ \spad{elt(x, row, ls2)} is equivalent to \spad{elt(x, row, l2)}
       ++ where l2 is obtained appending expansions of elements of ls2,
       ++ but should be more convenient and more efficient.
    elt : (%, LSI, Integer) -> %
       ++ \spad{elt(x, ls1, col)} is equivalent to \spad{elt(x, l1, col)}
       ++ where l1 is obtained appending expansions of elements of ls1,
       ++ but should be more convenient and more efficient.
    setelt! : (%, Integer, LSI, %) -> %
       ++ \spad{setelt!(x, row, ls2)} is equivalent to
       ++ \spad{setelt!(x, row, l2)} where l2 is obtained appending
       ++ expansions of elements of ls2, but should be more convenient
       ++ and more efficient.
    setelt! : (%, LSI, Integer, %) -> %
       ++ \spad{setelt!(x, ls1, col)} is equivalent to
       ++ \spad{setelt!(x, l1, col)} where l1 is obtained appending
       ++ expansions of elements of ls1, but should be more convenient
       ++ and more efficient.
    -- Works by coercing single integers to segments of integers
    -- elt : (%, LI, LSI) -> %
    -- elt : (%, LSI, LI) -> %
    elt : (%, SI, LSI) -> %
       ++ \spad{elt(x, s1, ls2)} is equivalent to \spad{elt(x, l1, l2)}
       ++ where li is obtained appending expansions of elements of lsi,
       ++ but should be more convenient and more efficient.
    elt : (%, LSI, SI) -> %
       ++ \spad{elt(x, ls1, s2)} is equivalent to \spad{elt(x, l1, l2)}
       ++ where li is obtained appending expansions of elements of lsi,
       ++ but should be more convenient and more efficient.
    elt : (%, LSI, LSI) -> %
       ++ \spad{elt(x, ls1, ls2)} is equivalent to \spad{elt(x, l1, l2)}
       ++ where li is obtained appending expansions of elements of lsi,
       ++ but should be more convenient and more efficient.
    rowSlice : % -> Segment(Integer)
       ++ \spad{rowSlice(m)} returns a segment s such that for
       ++ m the access m(s, j) gives the j-th column.
    colSlice : % -> Segment(Integer)
       ++ \spad{colSlice(m)} returns a segment s such that for
       ++ m the access m(i, s) gives the i-th row.

--% Part assignments

    setelt! : (%, Integer, Integer, R) -> R
      ++ setelt!(m, i, j, r) sets the element in the ith row and jth
      ++ column of m to r
      ++ error check to determine if indices are in proper ranges
    qsetelt! : (%, Integer, Integer, R) -> R
      ++ qsetelt!(m, i, j, r) sets the element in the ith row and jth
      ++ column of m to r
      ++ NO error check to determine if indices are in proper ranges
    setRow! : (%, Integer, Row) -> %
      ++ setRow!(m, i, v) sets to ith row of m to v
    setColumn! : (%, Integer, Col) -> %
      ++ setColumn!(m, j, v) sets to jth column of m to v
    setelt! : (%, Integer, LI, %) -> %
       ++ \spad{setelt!(x, row, colList)} assigns to an 1-by-n selection
       ++ of the array, where \spad{n = # colList}.
    setelt! : (%, LI, Integer, %) -> %
       ++ \spad{setelt!(x, rowList, col)} assigns to an m-by-1 selection
       ++ of the array, where \spad{m = # rowList}.
    setelt! : (%, List Integer, List Integer, %) -> %
       ++ \spad{setelt!(x, rowList, colList, y)} destructively alters
       ++ the array x.  If y is \spad{m}-by-\spad{n},
       ++ \spad{rowList = [i<1>, i<2>, ..., i<m>]}
       ++ and \spad{colList = [j<1>, j<2>, ..., j<n>]}, then
       ++ \spad{x(i<k>, j<l>)}
       ++ is set to \spad{y(k, l)} for \spad{k = 1, ..., m} and
       ++ \spad{l = 1, ..., n}.
    setelt! : (%, SI, SI, %) -> %
       ++ \spad{setelt!(x, s1, s2)} is equivalent to
       ++ \spad{setelt!(x, expand(s1), expand(s2))} but should be more
       ++ convenient and more efficient.
    setelt! : (%, LI, SI, %) -> %
       ++ \spad{setelt!(x, l1, s2)} is equivalent to
       ++ \spad{setelt!(x, l1, expand(s2))} but should be more
       ++ convenient and more  efficient.
    setelt! : (%, SI, LI, %) -> %
       ++ \spad{setelt!(x, s1, l2)} is equivalent to
       ++ \spad{setelt!(x, expand(s1), l2)} but should be more
       ++ convenient and more  efficient.
    -- Works by coercing single integers to segments of integers
    -- setelt! : (%, LI, LSI, %) -> %
    -- setelt! : (%, LSI, LI, %) -> %
    setelt! : (%, SI, LSI, %) -> %
       ++ \spad{setelt!(x, s1, ls2)} is equivalent to \spad{setelt!(x, l1, l2)}
       ++ where li is obtained appending expansions of elements of lsi,
       ++ but should be more convenient and more efficient.
    setelt! : (%, LSI, SI, %) -> %
       ++ \spad{setelt!(x, ls1, s2)} is equivalent to \spad{setelt!(x, l1, l2)}
       ++ where li is obtained appending expansions of elements of lsi,
       ++ but should be more convenient and more efficient.
    setelt! : (%, LSI, LSI, %) -> %
       ++ \spad{setelt!(x, ls1, ls1)} is equivalent to
       ++ \spad{setelt!(x, l1, l2)} where li is obtained appending
       ++ expansions of elements of lsi, but should be more convenient
       ++ and more efficient.
    setsubMatrix! : (%, Integer, Integer, %) -> %
       ++ \spad{setsubMatrix(x, i1, j1, y)} destructively alters the
       ++ array x. Here \spad{x(i, j)} is set to \spad{y(i-i1+1, j-j1+1)} for
       ++ \spad{i = i1, ..., i1-1+nrows y} and \spad{j = j1, ..., j1-1+ncols y}.

    -- manipulations

    swapRows! : (%, Integer, Integer) -> %
       ++ \spad{swapRows!(m, i, j)} interchanges the \spad{i}th and \spad{j}th
       ++ rows of m. This destructively alters the array.
    swapColumns! : (%, Integer, Integer) -> %
       ++ \spad{swapColumns!(m, i, j)} interchanges the \spad{i}th and
       ++ \spad{j}th columns of m. This destructively alters the array.
    transpose : % -> %
       ++ \spad{transpose(m)} returns the transpose of the array m.
    squareTop : % -> %
       ++ \spad{squareTop(m)} returns an n-by-n array consisting of the first
       ++ n rows of the m-by-n array m. Error: if
       ++ \spad{m < n}.
    horizConcat : (%, %) -> %
       ++ \spad{horizConcat(x, y)} horizontally concatenates two arrays with
       ++ an equal number of rows. The entries of y appear to the right
       ++ of the entries of x.  Error: if the arrays
       ++ do not have the same number of rows.
    horizConcat : (List %) -> %
      ++ \spad{horizConcat(l)} horizontally concatenates all members of l
      ++ Error: if the arrays  do not have the same number of rows.
    vertConcat : (%, %) -> %
       ++ \spad{vertConcat(x, y)} vertically concatenates two arrays with an
       ++ equal number of columns. The entries of y appear below
       ++ of the entries of x.  Error: if the arrays
       ++ do not have the same number of columns.
    vertConcat : (List %) -> %
      ++ \spad{vertConcat(l)} vertically concatenates all members of l
      ++ Error: if the arrays do not have the same number of columns.
    blockConcat : (List List %) -> %
      ++ \spad{blockConcat(ll)} concatenates arrays row and
      ++ column wise, building a array from blocks. The order
      ++ is row major as in \spad{matrix}.

    vertSplit : (%, PI) -> List %
      ++ \spad{vertSplit(a, n)} splits a into n arrays
      ++ of equal size row wise.
      ++ Error: if number of rows of a is not divisible by n.
    vertSplit : (%, LNNI) -> List %
      ++ \spad{vertSplit(a, [n1, ..., ni])} splits a into
      ++ arrays having n1, ..., ni rows.
      ++ Error: if number of rows of a is different than
      ++ n1+ ... + ni.
    horizSplit : (%, PI) -> List %
      ++ \spad{horizSplit(a, n)} splits a into n arrays
      ++ of equal size column wise.
      ++ Error: if number of columns of a is not divisible by n.
    horizSplit : (%, LNNI) -> List %
      ++ \spad{horizSplit(a, [n1, n2, ..., ni])} splits a into
      ++ arrays having n1, ..., ni columns.
      ++ Error: if number of columns of a is different than
      ++ n1 + ... + ni.
    blockSplit : (%, PI, PI) -> List List %
      ++ \spad{blockSplit(a, n, m)} splits a into n*m
      ++ subarrays of equal size row and column wise, dividing
      ++ a into blocks.
      ++ Error: if number of rows of a is not divisible by n
      ++ or number of columns of a is not divisible by m.
    blockSplit : (%, LNNI, LNNI) -> List List %
      ++ \spad{blockSplit(a, [n1,...,ni], [m1,...,mi])} splits a
      ++ into multiple subarraus row and column wise, such that
      ++ element at position k, l has nk rows and ml columns.
      ++ Error: if number of rows of a is different than
      ++ n1 + ... + ni or  number of columns of a is different
      ++ than m1 + ... + mj


    -- Map and Zip

    map : (R -> R, %) -> %
      ++ map(f, a) returns \spad{b}, where \spad{b(i, j) = f(a(i, j))} for all \spad{i, j}
    map! : (R -> R, %) -> %
      ++ map!(f, a)  assign \spad{a(i, j)} to \spad{f(a(i, j))} for all \spad{i, j}
    map : ((R, R) -> R, %, %) -> %
      ++ map(f, a, b) returns \spad{c}, where \spad{c(i, j) = f(a(i, j), b(i, j))}
      ++ for all \spad{i, j}
    map : ((R, R) -> R, %, %, R) -> %
      ++ map(f, a, b, r) returns \spad{c}, where \spad{c(i, j) = f(a(i, j), b(i, j))} when both
      ++ \spad{a(i, j)} and \spad{b(i, j)} exist;
      ++ else \spad{c(i, j) = f(r, b(i, j))} when \spad{a(i, j)} does not exist;
      ++ else \spad{c(i, j) = f(a(i, j), r)} when \spad{b(i, j)} does not exist;
      ++ otherwise \spad{c(i, j) = f(r, r)}.

   add

    minr ==> minRowIndex
    maxr ==> maxRowIndex
    minc ==> minColIndex
    maxc ==> maxColIndex
    mini ==> minIndex
    maxi ==> maxIndex


--% Predicates

    import from Integer

    any?(f, m) ==
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          f(qelt(m, i, j)) => return true
      false

    every?(f, m) ==
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          not f(qelt(m, i, j)) => return false
      true

    size?(m, n) == nrows(m) * ncols(m) = n
    less?(m, n) == nrows(m) * ncols(m) < n
    more?(m, n) == nrows(m) * ncols(m) > n

    if R has Comparable then

      smaller?(m1, m2) ==
          mri1 := minRowIndex(m1)
          mri2 := minRowIndex(m2)
          mri1 < mri2 => true
          mri2 < mri1 => false
          minr := mri1
          mri1 := maxRowIndex(m1)
          mri2 := maxRowIndex(m2)
          mri1 < mri2 => true
          mri2 < mri1 => false
          maxr := mri1
          mci1 := minColIndex(m1)
          mci2 := minColIndex(m2)
          mci1 < mci2 => true
          mci2 < mci1 => false
          minc := mci1
          mci1 := maxColIndex(m1)
          mci2 := maxColIndex(m2)
          mci1 < mci2 => true
          mci2 < mci1 => false
          maxc := mci1
          for i in minr..maxr repeat
              for j in minc..maxc repeat
                  el1 := m1(i, j)
                  el2 := m2(i, j)
                  smaller?(el1, el2) => return true
                  if not(el1 = el2) then return false
          false

--% Size inquiries

    # m == nrows(m) * ncols(m)

--% Part extractions

    elt(m, i, j, r) ==
      i < minRowIndex(m) or i > maxRowIndex(m) => r
      j < minColIndex(m) or j > maxColIndex(m) => r
      qelt(m, i, j)

    count(f : R -> Boolean, m : %) ==
      num : NonNegativeInteger := 0
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          if f(qelt(m, i, j)) then num := num + 1
      num

    parts m ==
      entryList : List R := []
      for i in maxRowIndex(m)..minRowIndex(m) by -1 repeat
        for j in maxColIndex(m)..minColIndex(m) by -1 repeat
          entryList := concat(qelt(m, i, j), entryList)
      entryList

    listOfLists x ==
        ll : List List R := []
        for i in maxr(x)..minr(x) by -1 repeat
            l : List R := []
            for j in maxc(x)..minc(x) by -1 repeat
                l := cons(qelt(x, i, j), l)
            ll := cons(l, ll)
        ll

    subMatrix(x, i1, i2, j1, j2) ==
        (i2 + 1 < i1) => error "subMatrix: bad row indices"
        (j2 + 1 < j1) => error "subMatrix: bad column indices"
        rows := qcoerce(i2 - i1 + 1)@NonNegativeInteger
        cols := qcoerce(j2 - j1 + 1)@NonNegativeInteger
        y := qnew(rows, cols)
        rows = 0 or cols = 0 => y
        (i1 < minr(x)) or (i2 > maxr(x)) =>
            error "subMatrix: index out of range"
        (j1 < minc(x)) or (j2 > maxc(x)) =>
            error "subMatrix: index out of range"
        for i in minr(y)..maxr(y) for k in i1..i2 repeat
            for j in minc(y)..maxc(y) for l in j1..j2 repeat
                qsetelt!(y, i, j, qelt(x, k, l))
        y

    elt(x : %, row : Integer, colList : LI) ==
        (row < minr(x)) or (row > maxr(x)) =>
            error "elt: index out of range"
        for ej in colList repeat
            (ej < minc(x)) or (ej > maxc(x)) =>
                error "elt: index out of range"
        y := qnew(1, # colList)
        for ej in colList for j in minc(y)..maxc(y) repeat
            qsetelt!(y, 1, j, qelt(x, row, ej))
        y

    elt(x : %, rowList : LI, col : Integer) ==
        for ei in rowList repeat
            (ei < minr(x)) or (ei > maxr(x)) =>
                error "elt: index out of range"
        (col < minc(x)) or (col > maxc(x)) =>
            error "elt: index out of range"
        y := qnew(# rowList, 1)
        for ei in rowList for i in minr(y)..maxr(y) repeat
            qsetelt!(y, i, 1, qelt(x, ei, col))
        y

    elt(x : %, rowList : List Integer, colList : List Integer) ==
        for ei in rowList repeat
            (ei < minr(x)) or (ei > maxr(x)) =>
                error "elt: index out of range"
        for ej in colList repeat
            (ej < minc(x)) or (ej > maxc(x)) =>
                error "elt: index out of range"
        y := qnew(# rowList, # colList)
        for ei in rowList for i in minr(y)..maxr(y) repeat
            for ej in colList for j in minc(y)..maxc(y) repeat
                qsetelt!(y, i, j, qelt(x, ei, ej))
        y

    check_seg(s : SI, lb : Integer, ub : Integer) : NonNegativeInteger ==
        ii := incr(s)
        i1 := low(s)
        i2 := high(s)
        -- Empty segment:
        (ii > 0 and i2 + 1 < i1) or (ii < 0 and i1 + 1 < i2) =>
            error "check_seg: bad indices"
        (i1 > i2 and ii > 0) or (i2 > i1 and ii < 0) => 0
        -- Regular segment:
        0 < ii  =>
            (i2 + 1 < i1) => error "check_seg: index out of range"
            cc := qcoerce(i2 - i1 + ii)@NonNegativeInteger
            cc < ii => cc
            i1 < lb or ub < i2 =>
                error "check_seg: index out of range"
            ii = 1 => cc
            qcoerce(cc quo ii)@NonNegativeInteger
        ii < 0 =>
            ii := -ii
            (i1 + 1 < i2) or i2 < lb or ub < i1 =>
                error "check_seg: index out of range"
            cc := qcoerce(i1 - i2 + ii)@NonNegativeInteger
            cc <  ii => cc
            i2 < lb or ub < i1 =>
                error "check_seg: index out of range"
            ii = 1 => cc
            qcoerce(cc quo ii)@NonNegativeInteger
        error "chec_seg: zero increment"

    elt(x : %, rowList : LI, sc : SI) : % ==
        lc := low(sc)
        uc := high(sc)
        ic := incr(sc)
        nr := # rowList
        nc := check_seg(sc, minc(x), maxc(x))
        y := qnew(nr, nc)
        nr = 0 or nc = 0 => y
        for i in minr(y)..maxr(y) for k in rowList repeat
            for j in minc(y)..maxc(y) for l in lc..uc by ic repeat
                qsetelt!(y, i, j, qelt(x, k, l))
        y

    elt(x : %, sr : SI, colList : LI) : % ==
        lr := low(sr)
        ur := high(sr)
        ir := incr(sr)
        nr := check_seg(sr, minr(x), maxr(x))
        nc := # colList
        y := qnew(nr, nc)
        nr = 0 or nc = 0 => y
        for i in minr(y)..maxr(y) for k in lr..ur by ir repeat
            for j in minc(y)..maxc(y) for l in colList repeat
                qsetelt!(y, i, j, qelt(x, k, l))
        y

    elt(x : %, sr : SI, sc : SI) : % ==
        lr := low(sr)
        ur := high(sr)
        lc := low(sc)
        uc := high(sc)
        ir := incr(sr)
        ic := incr(sc)
        ir = 1 and ic = 1 => subMatrix(x, lr, ur, lc, uc)
        nr := check_seg(sr, minr(x), maxr(x))
        nc := check_seg(sc, minc(x), maxc(x))
        y := qnew(nr, nc)
        nr = 0 or nc = 0 => y
        for i in minr(y)..maxr(y) for k in lr..ur by ir repeat
            for j in minc(y)..maxc(y) for l in lc..uc by ic repeat
                qsetelt!(y, i, j, qelt(x, k, l))
        y

    check_segs(ls : LSI, lb : Integer, ub : Integer) : NonNegativeInteger ==
        res : NonNegativeInteger := 0
        for s in ls repeat
            res := res + check_seg(s, lb, ub)
        res

    elt(x : %, row : Integer, lsc : LSI) : % ==
        nc := check_segs(lsc, minc(x), maxc(x))
        y := qnew(1, nc)
        nc = 0 => y
        j := minc(y)
        for sc in lsc repeat
            for l in low(sc)..high(sc) by incr(sc) repeat
                qsetelt!(y, 1, j, qelt(x, row, l))
                j := j + 1
        y

    elt(x : %, lsr : LSI, col : Integer) : % ==
        nr := check_segs(lsr, minr(x), maxr(x))
        y := qnew(nr, 1)
        nr = 0 => y
        i := minr(y)
        for sr in lsr repeat
            for k in low(sr)..high(sr) by incr(sr) repeat
                qsetelt!(y, i, 1, qelt(x, k, col))
                i := i + 1
        y

    elt(x : %, sr : SI, lsc : LSI) : % ==
        lr := low(sr)
        ur := high(sr)
        ir := incr(sr)
        nr := check_seg(sr, minr(x), maxr(x))
        nc := check_segs(lsc, minc(x), maxc(x))
        y := qnew(nr, nc)
        nr = 0 or nc = 0 => y
        j := minc(y)
        for sc in lsc repeat
            for l in low(sc)..high(sc) by incr(sc) repeat
                for i in minr(y)..maxr(y) for k in lr..ur by ir repeat
                    qsetelt!(y, i, j, qelt(x, k, l))
                j := j + 1
        y

    elt(x : %, lsr : LSI, sc : SI) : % ==
        lc := low(sc)
        uc := high(sc)
        ic := incr(sc)
        nr := check_segs(lsr, minr(x), maxr(x))
        nc := check_seg(sc, minc(x), maxc(x))
        y := qnew(nr, nc)
        nr = 0 or nc = 0 => y
        i := minr(y)
        for sr in lsr repeat
            for k in low(sr)..high(sr) by incr(sr) repeat
                for j in minc(y)..maxc(y) for l in lc..uc by ic repeat
                    qsetelt!(y, i, j, qelt(x, k, l))
                i := i + 1
        y

    elt(x : %, lsr : LSI, lsc : LSI) : % ==
        nr := check_segs(lsr, minr(x), maxr(x))
        nc := check_segs(lsc, minc(x), maxc(x))
        y := qnew(nr, nc)
        nr = 0 or nc = 0 => y
        i := minr(y)
        for sr in lsr repeat
            lr := low(sr)
            ur := high(sr)
            ir := incr(sr)
            for k in lr..ur by ir repeat
                j := minc(y)
                for sc in lsc repeat
                    for l in low(sc)..high(sc) by incr(sc) repeat
                        qsetelt!(y, i, j, qelt(x, k, l))
                        j := j + 1
                i := i + 1
        y

    rowSlice(x :%) : Segment(Integer) ==
        minr(x)..maxr(x)

    colSlice(x :%) : Segment(Integer) ==
        minc(x)..maxc(x)

    -- Setting parts

    setelt!(x : %, row : Integer, colList : LI, y : %) ==
        (row < minr(x)) or (row > maxr(x)) =>
            error "setelt!: index out of range"
        for ej in colList repeat
            (ej < minc(x)) or (ej > maxc(x)) =>
                error "setelt!: index out of range"
        ((nrows y) ~= 1) or ((# colList) ~= (ncols y)) =>
            error "setelt!: matrix has bad dimensions"
        rowiy := minr(y)
        for ej in colList for j in minc(y)..maxc(y) repeat
            qsetelt!(x, row, ej, qelt(y, rowiy, j))
        y

    setelt!(x : %, rowList : LI, col : Integer, y : %) ==
        for ei in rowList repeat
            (ei < minr(x)) or (ei > maxr(x)) =>
                error "setelt!: index out of range"
        (col < minc(x)) or (col > maxc(x)) =>
            error "setelt!: index out of range"
        ((# rowList) ~= (nrows y)) or ((ncols y) ~= 1) =>
            error "setelt!: matrix has bad dimensions"
        coliy := minc(y)
        for ei in rowList for i in minr(y)..maxr(y) repeat
            qsetelt!(x, ei, col, qelt(y, i, coliy))
        y

    setelt!(x : %, rowList : List Integer, colList : List Integer, y : %) ==
        for ei in rowList repeat
            (ei < minr(x)) or (ei > maxr(x)) =>
                error "setelt!: index out of range"
        for ej in colList repeat
            (ej < minc(x)) or (ej > maxc(x)) =>
                error "setelt!: index out of range"
        ((# rowList) ~= (nrows y)) or ((# colList) ~= (ncols y)) =>
            error "setelt!: matrix has bad dimensions"
        for ei in rowList for i in minr(y)..maxr(y) repeat
            for ej in colList for j in minc(y)..maxc(y) repeat
                qsetelt!(x, ei, ej, qelt(y, i, j))
        y

    setelt!(x : %, sr : SI, sc : SI, y : %) ==
        lr := low(sr)
        ur := high(sr)
        lc := low(sc)
        uc := high(sc)
        ir := incr(sr)
        ic := incr(sc)
        -- ir = 1 and ic = 1 => subMatrix(x, lr, ur, lc, uc)
        nr := check_seg(sr, minr(x), maxr(x))
        nc := check_seg(sc, minc(x), maxc(x))
        nrows(y) ~= nr or ncols(y) ~= nc =>
            error "setelt!: matrix has bad dimensions"
        nr = 0 or nc = 0 => y
        for i in minr(y)..maxr(y) for k in lr..ur by ir repeat
            for j in minc(y)..maxc(y) for l in lc..uc by ic repeat
                qsetelt!(x, k, l, qelt(y, i, j))
        y

    setelt!(x : %, rowList : LI, sc : SI, y : %) ==
        lc := low(sc)
        uc := high(sc)
        ic := incr(sc)
        nr := # rowList
        nc := check_seg(sc, minc(x), maxc(x))
        nrows(y) ~= nr or ncols(y) ~= nc =>
            error "setelt!: matrix has bad dimensions"
        nr = 0 or nc = 0 => y
        for i in minr(y)..maxr(y) for k in rowList repeat
            for j in minc(y)..maxc(y) for l in lc..uc by ic repeat
                qsetelt!(x, k, l, qelt(y, i, j))
        y

    setelt!(x : %, sr : SI, colList : LI, y : %) ==
        lr := low(sr)
        ur := high(sr)
        ir := incr(sr)
        nr := check_seg(sr, minr(x), maxr(x))
        nc := # colList
        nrows(y) ~= nr or ncols(y) ~= nc =>
            error "setelt!: matrix has bad dimensions"
        nr = 0 or nc = 0 => y
        for i in minr(y)..maxr(y) for k in lr..ur by ir repeat
            for j in minc(y)..maxc(y) for l in colList repeat
                qsetelt!(x, k, l, qelt(y, i, j))
        y

    setelt!(x : %, row : Integer, lsc : LSI, y : %) ==
        nc := check_segs(lsc, minc(x), maxc(x))
        nrows(y) ~= 1 or ncols(y) ~= nc =>
            error "setelt!: matrix has bad dimensions"
        nc = 0 => y
        i := minr(y)
        j := minc(y)
        for sc in lsc repeat
            for l in low(sc)..high(sc) by incr(sc) repeat
                qsetelt!(x, row, l, qelt(y, i, j))
                j := j + 1
        y

    setelt!(x : %, lsr : LSI, col : Integer, y : %) ==
        nr := check_segs(lsr, minr(x), maxr(x))
        nrows(y) ~= nr or ncols(y) ~= 1 =>
            error "setelt!: matrix has bad dimensions"
        nr = 0 => y
        i := minr(y)
        j := minc(y)
        for sr in lsr repeat
            for k in low(sr)..high(sr) by incr(sr) repeat
                qsetelt!(x, k, col, qelt(y, i, j))
                i := i + 1
        y

    setelt!(x : %, sr : SI, lsc : LSI, y : %) ==
        lr := low(sr)
        ur := high(sr)
        ir := incr(sr)
        nr := check_seg(sr, minr(x), maxr(x))
        nc := check_segs(lsc, minc(x), maxc(x))
        nrows(y) ~= nr or ncols(y) ~= nc =>
            error "setelt!: matrix has bad dimensions"
        nr = 0 or nc = 0 => y
        j := minc(y)
        for sc in lsc repeat
            for l in low(sc)..high(sc) by incr(sc) repeat
                for i in minr(y)..maxr(y) for k in lr..ur by ir repeat
                    qsetelt!(x, k, l, qelt(y, i, j))
                j := j + 1
        y

    setelt!(x : %, lsr : LSI, sc : SI, y : %) ==
        lc := low(sc)
        uc := high(sc)
        ic := incr(sc)
        nr := check_segs(lsr, minr(x), maxr(x))
        nc := check_seg(sc, minc(x), maxc(x))
        nrows(y) ~= nr or ncols(y) ~= nc =>
            error "setelt!: matrix has bad dimensions"
        nr = 0 or nc = 0 => y
        i := minr(y)
        for sr in lsr repeat
            for k in low(sr)..high(sr) by incr(sr) repeat
                for j in minc(y)..maxc(y) for l in lc..uc by ic repeat
                    qsetelt!(x, k, l, qelt(y, i, j))
                i := i + 1
        y

    setelt!(x : %, lsr : LSI, lsc : LSI, y : %) ==
        nr := check_segs(lsr, minr(x), maxr(x))
        nc := check_segs(lsc, minc(x), maxc(x))
        nrows(y) ~= nr or ncols(y) ~= nc =>
            error "setelt!: matrix has bad dimensions"
        nr = 0 or nc = 0 => y
        i := minr(y)
        for sr in lsr repeat
            lr := low(sr)
            ur := high(sr)
            ir := incr(sr)
            for k in lr..ur by ir repeat
                j := minc(y)
                for sc in lsc repeat
                    for l in low(sc)..high(sc) by incr(sc) repeat
                        qsetelt!(x, k, l, qelt(y, i, j))
                        j := j + 1
                i := i + 1
        y

    setsubMatrix!(x, i1, j1, y) ==
        i2 := i1 + nrows(y) - 1
        j2 := j1 + ncols(y) - 1
        (i1 < minr(x)) or (i2 > maxr(x)) =>
            error "setsubMatrix!: inserted matrix too big, "
                   "use subMatrix to restrict it"
        (j1 < minc(x)) or (j2 > maxc(x)) =>
            error "setsubMatrix!: inserted matrix too big, "
                   "use subMatrix to restrict it"
        for i in minr(y)..maxr(y) for k in i1..i2 repeat
            for j in minc(y)..maxc(y) for l in j1..j2 repeat
                qsetelt!(x, k, l, qelt(y, i, j))
        x

    -- Manipulations

    swapRows!(x, i1, i2) ==
        (i1 < minr(x)) or (i1 > maxr(x)) or (i2 < minr(x)) or _
            (i2 > maxr(x)) => error "swapRows!: index out of range"
        i1 = i2 => x
        for j in minc(x)..maxc(x) repeat
            r := qelt(x, i1, j)
            qsetelt!(x, i1, j, qelt(x, i2, j))
            qsetelt!(x, i2, j, r)
        x

    swapColumns!(x, j1, j2) ==
        (j1 < minc(x)) or (j1 > maxc(x)) or (j2 < minc(x)) or _
            (j2 > maxc(x)) => error "swapColumns!: index out of range"
        j1 = j2 => x
        for i in minr(x)..maxr(x) repeat
            r := qelt(x, i, j1)
            qsetelt!(x, i, j1, qelt(x, i, j2))
            qsetelt!(x, i, j2, r)
        x

    transpose(x : %) ==
        ans := qnew(ncols x, nrows x)
        for i in minr(ans)..maxr(ans) repeat
            for j in minc(ans)..maxc(ans) repeat
                qsetelt!(ans, i, j, qelt(x, j, i))
        ans

    squareTop x ==
        nrows x < (cols := ncols x) =>
            error "squareTop: number of columns exceeds number of rows"
        ans := qnew(cols, cols)
        for i in minr(x)..(minr(x) + cols - 1) repeat
            for j in minc(x)..maxc(x) repeat
                qsetelt!(ans, i, j, qelt(x, i, j))
        ans

    horizConcat(x, y) == horizConcat([x, y])

    horizConcat(la : List %) ==
        empty?(la) =>
            error "horizConcat: empty list"
        a1 := first(la)
        nr := nrows(a1)
        nc := ncols(a1)
        for a in rest(la) repeat
            nr ~= nrows(a) =>
                error "horizConcat: array must have same number of rows"
            nc := nc + ncols(a)
        ans := qnew(nr, nc)
        for i in minr(a1)..maxr(a1) repeat
            l := minc(ans)
            for a in la repeat
                for j in minc(a)..maxc(a) repeat
                    qsetelt!(ans, i, l, qelt(a, i, j))
                    l := l + 1
        ans

    vertConcat(x, y) == vertConcat([x, y])

    vertConcat(la : List %) ==
        empty?(la) =>
            error "vertConcat: empty list"
        a1 := first(la)
        nr := nrows(a1)
        nc := ncols(a1)
        for a in rest(la) repeat
            nc ~= ncols(a) =>
                error "vertConcat: array must have same number of columns"
            nr := nr + nrows(a)
        ans := qnew(nr, nc)
        l :=  minr(ans)
        for a in la repeat
            for i in minr(a)..maxr(a) repeat
                for j in minc(a)..maxc(a) repeat
                    qsetelt!(ans, l, j, qelt(a, i, j))
                l := l + 1
        ans

    blockConcat(LLA: List List %) : % ==
        vertConcat([horizConcat(LA) for LA in LLA])

    vertSplit(A : %, r : PI) : List % ==
      dr := nrows(A) exquo r
      dr case "failed" => error "split does not result in an equal division"
      mir := minr A
      mic := minc A
      mac := maxc A
      [ subMatrix(A, mir+i*dr, mir+(i+1)*dr-1, mic, mac) for i in 0..(r-1) ]

    vertSplit(A : %, lr : LNNI) : List % ==
        reduce("+", lr) ~= nrows(A) =>
            error "split does not result in proper partition"
        l : List NNI := cons(1, scan(_+, lr, 1$NNI)$ListFunctions2(NNI, NNI))
        mir := minr(A) -1   -- additional shift because l starts at 1
        mic := minc A
        mac := maxc A
        [subMatrix(A, mir+l(i-1), mir+l(i)-1, mic, mac) for i in 2..#l]

    horizSplit(A : %, c : PI) : List % ==
      dc := ncols(A) exquo c
      dc case "failed" => error "split does not result in an equal division"
      mir := minr A
      mar := maxr A
      mic := minc A
      [ subMatrix(A, mir, mar, mic+i*dc, mic+(i+1)*dc-1) for i in 0..(c-1) ]


    horizSplit(A : %, lc : LNNI) : List % ==
        reduce("+", lc) ~= ncols(A) =>
            error "split does not result in proper partition"
        l : List NNI := cons(1, scan(_+, lc, 1$NNI)$ListFunctions2(NNI, NNI))
        mir := minr A
        mar := maxr A
        mic := minc(A) -1   -- additional shift because l starts at 1
        [subMatrix(A, mir, mar, mic+l(i-1), mic+l(i)-1) for i in 2..#l]

    blockSplit(A : %, nr : PI, nc : PI) : List List % ==
      -- The map version does not work with OpenAxiom.
      --map( (X:M):(List M) +-> horizSplit(X, nc), vertSplit(A, nr) )$ListFunctions2(M, List M)
      [ horizSplit(X, nc) for X in vertSplit(A, nr) ]

    blockSplit(A : %, lr : LNNI, lc : LNNI) : List List % ==
      --map( (X:M):(List M) +-> horizSplit(X, lc), vertSplit(A, lr) )$ListFunctions2(M, List M)
      [horizSplit(X, lc) for X in vertSplit(A, lr)]

--% Creation

    copy m ==
      ans := qnew(nrows m, ncols m)
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          qsetelt!(ans, i, j, qelt(m, i, j))
      ans

    fill!(m, r) ==
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          qsetelt!(m, i, j, r)
      m

    map(f : R -> R, m : %) : % ==
      ans := qnew(nrows m, ncols m)
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          qsetelt!(ans, i, j, f(qelt(m, i, j)))
      ans

    map!(f, m) ==
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          qsetelt!(m, i, j, f(qelt(m, i, j)))
      m

    map(f, m, n) ==
      (nrows(m) ~= nrows(n)) or (ncols(m) ~= ncols(n)) =>
        error "map: arguments must have same dimensions"
      ans := qnew(nrows m, ncols m)
      for i in minRowIndex(m)..maxRowIndex(m) repeat
        for j in minColIndex(m)..maxColIndex(m) repeat
          qsetelt!(ans, i, j, f(qelt(m, i, j), qelt(n, i, j)))
      ans

    map(f, m, n, r) ==
      -- FIXME: what if minRowIndex differ?
      maxRow := max(maxRowIndex m, maxRowIndex n)
      maxCol := max(maxColIndex m, maxColIndex n)
      ans := qnew(max(nrows m, nrows n), max(ncols m, ncols n))
      for i in minRowIndex(m)..maxRow repeat
        for j in minColIndex(m)..maxCol repeat
          qsetelt!(ans, i, j, f(elt(m, i, j, r), elt(n, i, j, r)))
      ans

    setRow!(m, i, v) ==
      i < minRowIndex(m) or i > maxRowIndex(m) =>
        error "setRow!: index out of range"
      for j in minColIndex(m)..maxColIndex(m) _
        for k in minIndex(v)..maxIndex(v) repeat
          qsetelt!(m, i, j, v.k)
      m

    setColumn!(m, j, v) ==
      j < minColIndex(m) or j > maxColIndex(m) =>
        error "setColumn!: index out of range"
      for i in minRowIndex(m)..maxRowIndex(m) _
        for k in minIndex(v)..maxIndex(v) repeat
          qsetelt!(m, i, j, v.k)
      m

    if R has _= : (R, R) -> Boolean then

      m = n ==
        eq?(m, n) => true
        (nrows(m) ~= nrows(n)) or (ncols(m) ~= ncols(n)) => false
        for i in minRowIndex(m)..maxRowIndex(m) repeat
          for j in minColIndex(m)..maxColIndex(m) repeat
            not (qelt(m, i, j) = qelt(n, i, j)) => return false
        true

      member?(r, m) ==
        for i in minRowIndex(m)..maxRowIndex(m) repeat
          for j in minColIndex(m)..maxColIndex(m) repeat
            qelt(m, i, j) = r => return true
        false

      count(r : R, m : %) == count(x +-> x = r, m)

    if Row has shallowlyMutable and Row has LinearAggregate(R) then

      row(m, i) ==
        i < minRowIndex(m) or i > maxRowIndex(m) =>
          error "row: index out of range"
        (nc := ncols m) = 0 => empty()
        mci := minColIndex(m)
        v : Row := new(nc, qelt(m, i, mci))
        for j in mci..maxColIndex(m) _
          for k in minIndex(v)..maxIndex(v) repeat
            qsetelt!(v, k, qelt(m, i, j))
        v

    if Col has shallowlyMutable and Col has LinearAggregate(R) then

      column(m, j) ==
        j < minColIndex(m) or j > maxColIndex(m) =>
          error "column: index out of range"
        (nr := nrows m) = 0 => empty()
        mri := minRowIndex(m)
        v : Col := new(nr, qelt(m, mri, j))
        for i in mri..maxRowIndex(m) _
          for k in minIndex(v)..maxIndex(v) repeat
            qsetelt!(v, k, qelt(m, i, j))
        v

    if R has CoercibleTo(OutputForm) then

      coerce(m : %) ==
        l : List List OutputForm
        l := [[qelt(m, i, j) :: OutputForm _
                  for j in minColIndex(m)..maxColIndex(m)] _
                  for i in minRowIndex(m)..maxRowIndex(m)]
        matrix l

)abbrev domain IIARRAY2 InnerIndexedTwoDimensionalArray
InnerIndexedTwoDimensionalArray(R, mnRow, mnCol, Row, Col) : _
       Exports == Implementation where
  ++ This is an internal type which provides an implementation of
  ++ 2-dimensional arrays as PrimitiveArray's of PrimitiveArray's.
  R : Type
  mnRow, mnCol : Integer
  Row : FiniteLinearAggregate R
  Col : FiniteLinearAggregate R

  Exports ==> TwoDimensionalArrayCategory(R, Row, Col)

  Implementation ==> add

    Qsize ==> MATRIX_SIZE$Lisp
    Qnew ==> MAKE_MATRIX$Lisp
    Qnew1 ==> MAKE_MATRIX1$Lisp
    Qnrows ==> ANROWS$Lisp
    Qncols ==> ANCOLS$Lisp
    Qelt2 ==> QAREF2O$Lisp
    Qsetelt2 ==> QSETAREF2O$Lisp



--% Primitive array creation

    empty() == Qnew(0, 0)

    qnew(rows, cols) == Qnew(rows, cols)

    new(rows, cols, a) == Qnew1(rows, cols, a)

--% Size inquiries

    minRowIndex m == mnRow
    minColIndex m == mnCol
    maxRowIndex m == nrows m + mnRow - 1
    maxColIndex m == ncols m + mnCol - 1

    nrows m == Qnrows(m)

    ncols m == Qncols(m)

--% Part selection/assignment

    qelt(m, i, j) == Qelt2(m, i, j, mnRow, mnCol)

    elt(m : %, i : Integer, j : Integer) ==
      i < minRowIndex(m) or i > maxRowIndex(m) =>
        error "elt: index out of range"
      j < minColIndex(m) or j > maxColIndex(m) =>
        error "elt: index out of range"
      qelt(m, i, j)

    qsetelt!(m, i, j, r) == Qsetelt2(m, i, j, r, mnRow, mnCol)

    setelt!(m : %, i : Integer, j : Integer, r : R) ==
      i < minRowIndex(m) or i > maxRowIndex(m) =>
        error "setelt!: index out of range"
      j < minColIndex(m) or j > maxColIndex(m) =>
        error "setelt!: index out of range"
      qsetelt!(m, i, j, r)

    if R has SetCategory then
        latex(m : %) : String ==
          s : String := "\left[ \begin{array}{"
          j : Integer
          for j in minColIndex(m)..maxColIndex(m) repeat
            s := concat(s,"c")$String
          s := concat(s,"} ")$String
          i : Integer
          for i in minRowIndex(m)..maxRowIndex(m) repeat
            for j in minColIndex(m)..maxColIndex(m) repeat
              s := concat(s, latex(qelt(m, i, j))$R)$String
              if j < maxColIndex(m) then s := concat(s, " & ")$String
            if i < maxRowIndex(m) then s := concat(s, " \\ ")$String
          concat(s, "\end{array} \right]")$String

)abbrev domain IARRAY2 IndexedTwoDimensionalArray
IndexedTwoDimensionalArray(R, mnRow, mnCol) : Exports == Implementation where
  ++ An IndexedTwoDimensionalArray is a 2-dimensional array where
  ++ the minimal row and column indices are parameters of the type.
  ++ Rows and columns are returned as IndexedOneDimensionalArray's with
  ++ minimal indices matching those of the IndexedTwoDimensionalArray.
  ++ The index of the 'first' row may be obtained by calling the
  ++ function 'minRowIndex'.  The index of the 'first' column may
  ++ be obtained by calling the function 'minColIndex'.  The index of
  ++ the first element of a 'Row' is the same as the index of the
  ++ first column in an array and vice versa.
  R : Type
  mnRow, mnCol : Integer
  Row ==> IndexedOneDimensionalArray(R, mnCol)
  Col ==> IndexedOneDimensionalArray(R, mnRow)

  Exports ==> TwoDimensionalArrayCategory(R, Row, Col)

  Implementation ==>
    InnerIndexedTwoDimensionalArray(R, mnRow, mnCol, Row, Col)

)abbrev domain ARRAY2 TwoDimensionalArray
TwoDimensionalArray(R) : Exports == Implementation where
  ++ A TwoDimensionalArray is a two dimensional array with
  ++ 1-based indexing for both rows and columns.
  R : Type
  Row ==> OneDimensionalArray R
  Col ==> OneDimensionalArray R

  Exports ==> TwoDimensionalArrayCategory(R, Row, Col)

  Implementation ==>
     InnerIndexedTwoDimensionalArray(R, 1, 1, Row, Col) add
         Qelt2 ==> QAREF2O$Lisp
         Qsetelt2 ==> QSETAREF2O$Lisp
         -- qelt and qsetelt! are logically unnecessary, but good for
         -- performance
         qelt(m, i, j) == Qelt2(m, i, j, 1@Integer, 1@Integer)
         qsetelt!(m, i, j, r) == Qsetelt2(m, i, j, r, 1@Integer, 1@Integer)

--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.
