############################################################################
#
#	File:     matrix2.icn
#
#	Subject:  Procedures for matrix transposition and scalar multiplication
#
#	Authors:  Arthur C. Eschenlauer
#
#	Date:     November 1, 2005
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
# transpose_matrix(M) : L  - produces a matrix R that is the transpose of M:
#                               R[j][i] = M[i][j]
#
# numeric_matrix(M) : L    - produces a matrix R that is a copy of M except
#                            each element has been subjected to the
#                            numeric(x) function; if numeric fails for any
#                            element, numeric_matrix fails:
#                               R[i][j] = numeric(M[i][j])
#
# scale_matrix(M,mult) : L - produces a new matrix R each of whose elements
#                            is mult times larger than its peer in M:
#                                R[i][j] := mult * M[i][j]
# scale_matrix(mult,M) : L - is a synonym for scale_matrix(M,mult).
#
# floor_matrix(M,min) : L  - produces a matrix R that is a copy of M except 
#                            each element is increased to min if necessary:
#                                R[i][j] := min <= M[i][j] | min
# floor_matrix(min,M) : L  - is a synonym for floor_matrix(M,min).
#
# ceil_matrix(M,max) : L   - produces a matrix R that is a copy of M except 
#                            each element is increased to max if necessary:
#                                R[i][j] := max <= M[i][j] | max
# ceil_matrix(max,M) : L   - is a synonym for ceil_matrix(M,max).
#
# sumsquares_matrix(M) : n - produces the sum of the squares
#                            of all terms in a matrix
#                                sum(for every i,j) (M[i][j])^2
#
# sumsquaresdiff_matrix(M1,M2) : n - produces the sum of the squares of all
#                            terms in the difference between two matrices
#                                sum(for every i,j) (M1[i][j] - M2[i][j])^2
#
# normalize_rows(M,t) : L  - produce a row-scaled matrix such that, 
#                            for every row i, the sum of the values in
#                            all columns is 1
#                                R[i][j] /:= sum(for every J) M[i][J]
#                            t is a required minimum magnitude
#                            for row sums to avoid divide-by-zero errors
# normalize_rows(t,M) : L  - synonym for normalize_rows(M,t) 
#
# normalize_columns(M,t) : L - produce a column-scaled matrix such that, 
#                              for every column i, the sum of the values
#                              in all rows is 1
#                              such that their sum is 1
#                                R[i][j] /:= sum(for every I) M[I][j]
#                              t is a required minimum magnitude for
#                              column sums to avoid divide-by-zero errors
# normalize_columns(t,M) : L - synonym for normalize_columns(M,t) 
#
# sprintf_matrix(f,M)      - produces a matrix R of strings whose elements
#                            are formatted (by the IPL sprintf routine)
#                            from the elements of M:
#                                R[i][j] := sprintf(f,M[i,j])
#
############################################################################
#
#  Links: matrix, printf
#
############################################################################

link matrix
link printf

# transpose_matrix(M)  - produces a new matrix R that is the transpose of M:
#                          R[j][i] = M[i][j]
procedure transpose_matrix(M)
  local R, row, rowcnt, colcnt, i, j
  # sanity checks
  type(M) == "list"      | fail
  type(M[1]) == "list"   | fail
  rowcnt := *M           | fail
  colcnt := *M[1]        | fail
  every i := 2 to rowcnt
    do *M[i] = colcnt    | fail
  R := list( )                # create list of rows
  every i := 1 to colcnt do {
    put( R, row := list( ) )  # create list of column values
    every j := 1 to rowcnt do # populate column values
      put( row, M[j][i] )
  }
  return R
end

# numeric_matrix(M)    - produces a new matrix R that is a copy of M except
#                        each element has been subjected to the numeric(x)
#                        function; if numeric fails for any element, 
#                        numeric_matrix fails:
#                          R[i][j] = numeric(M[i][j])
procedure numeric_matrix(M)
  local R, row, rowcnt, colcnt, i, j
  # sanity checks
  type(M) == "list"      | fail
  type(M[1]) == "list"   | fail
  rowcnt := *M           | fail
  colcnt := *M[1]        | fail
  every i := 2 to rowcnt
    do *M[i] = colcnt    | fail
  R := list( )                # create list of rows
  every i := 1 to rowcnt do {
    put( R, row := list( ) )  # create list of column values
    every j := 1 to colcnt do # populate column values
      put( row, numeric(M[i][j]) | fail )
  }
  return R
end

# scale_matrix(M,mult) - produces a new matrix R each of whose elements is
#                        mult times larger than its peer in M:
#                          R[i][j] := mult * M[i][j]
# scale_matrix(mult,M) - is a synonym for scale_matrix(M,mult).
procedure scale_matrix(mult,M)
  local R, i, j
  # handle synonymous invocation
  if numeric(M) & type(mult) == "list" then M :=: mult
  # sanity checks
  mult := numeric(mult) | fail
  type(M) == "list"     | fail
  type(M[1]) == "list"  | fail
  R := copy_matrix(M)   | fail # create a copy of input matrix
  every i := 1 to *R do        # for each row
    every j := 1 to *R[1] do   #   for each column
                               #     scale the column value
      R[i][j] := numeric(R[i][j]) * mult | fail
  return R
end

# floor_matrix(M,min)  - produces a new matrix R that is a copy of M except 
#                        each element is increased to min if necessary:
#                          R[i][j] := min <= M[i][j] | min
procedure floor_matrix(min,M)
  local R, i, j, r
  # handle synonymous invocation
  if numeric(M) & type(min) == "list" then M :=: min
  # sanity checks
  min := numeric(min)  | fail
  type(M) == "list"    | fail
  type(M[1]) == "list" | fail
  R := copy_matrix(M)  | fail  # create copy of input matrix
  every i := 1 to *R do        # for each row
    every j := 1 to *R[1] do { #   for each column
                               #     adjust column value if less than min
      r := numeric(R[i][j]) | fail
      R[i][j] := r < min | r
    }
  return R
end
# floor_matrix(min,M)  - is a synonym for floor_matrix(M,min).

# ceil_matrix(M,max)  - produces a new matrix R that is a copy of M except 
#                        each element is increased to max if necessary:
#                          R[i][j] := max <= M[i][j] | max
procedure ceil_matrix(max,M)
  local R, i, j, r
  # handle synonymous invocation
  if numeric(M) & type(max) == "list" then M :=: max
  # sanity checks
  max := numeric(max)  | fail
  type(M) == "list"    | fail
  type(M[1]) == "list" | fail
  R := copy_matrix(M)  | fail  # create copy of input matrix
  every i := 1 to *R do        # for each row
    every j := 1 to *R[1] do { #   for each column
                               #     adjust column value if less than max
      r := numeric(R[i][j]) | fail
      R[i][j] := r > max | r
    }
  return R
end
# ceil_matrix(max,M)  - is a synonym for ceil_matrix(M,max).

# sumsquares_matrix(M) - produces the sum of the squares
#   of all terms in a matrix
#       sum( for every i,j ) (M[i][j])^2
procedure sumsquares_matrix(M)
  local r, r1, i, j
  # sanity checks
  type(M)    == "list"  | fail
  type(M[1]) == "list"  | fail
  # compute the sum of squares
  r := 0
  every i := 1 to *M do        # for each row
    every j := 1 to *M[1] do { #   for each column
                               #     sumsquare the column value
      r1 := M[i][j]
      r +:= r1 * r1
    }
  return r
end

# sumsquaresdiff_matrix(M1,M2) - produces the sum of the squares
#   of all terms in the difference between two matrices
#       sum( for every i,j ) (M1[i][j] - M2[i][j])^2
procedure sumsquaresdiff_matrix(M1,M2)
  local r, r1, r2, i, j, scratch
  # sanity checks
  type(M1)    == type(M2)    == "list"  | fail
  type(M1[1]) == type(M2[1]) == "list"  | fail
  ( *M1 = *M2, *M1[1] = *M2[1] )        | fail
  # compute the sum of squares
  r := 0
  every i := 1 to *M1 do        # for each row
    every j := 1 to *M1[1] do { #   for each column
                                #     sumsquare the column value
      r1 := M1[i][j] ; r2 := r1 - M2[i][j]
      r +:= r2 * r2
    }
  return r
end

# normalize_rows(M,t) : L  - produce a row-scaled matrix such that, 
#                            for every row i, the sum of the values in
#                            all columns is 1
#                                R[i][j] /:= sum(for every J) M[i][J]
#                            t is a required minimum magnitude
#                            for row sums to avoid divide-by-zero errors
# normalize_rows(t,M) : L  - synonym for normalize_rows(M,t) 
procedure normalize_rows(M,threshold)
  local R, rowsum, rowcnt, colcnt, i, j
  # handle synonymous invocation
  if numeric(M) & type(threshold) == "list" then M :=: threshold
  # sanity checks
  type(M)    == "list"  | fail
  type(M[1]) == "list"  | fail
  \threshold            | fail
  R := copy_matrix( M ) | fail
  rowcnt := *R
  colcnt := *R[1]
  every i := 1 to rowcnt do { # for each column
    rowsum := 0
    every j := 1 to colcnt do rowsum +:= R[i][j]
    if not -threshold < rowsum < threshold
      then
        every j := 1 to colcnt do R[i][j] /:= rowsum
  }
  return R
end

# normalize_columns(M,t) : L - produce a column-scaled matrix such that, 
#                              for every column i, the sum of the values
#                              in all rows is 1
#                              such that their sum is 1
#                                R[i][j] /:= sum(for every I) M[I][j]
#                              t is a required minimum magnitude for
#                              column sums to avoid divide-by-zero errors
# normalize_columns(t,M) : L - synonym for normalize_columns(M,T) 
procedure normalize_columns(M,threshold)
  local R, colsum, rowcnt, colcnt, i, j
  # handle synonymous invocation
  if numeric(M) & type(threshold) == "list" then M :=: threshold
  # sanity checks
  type(M)    == "list"  | fail
  type(M[1]) == "list"  | fail
  \threshold            | fail
  R := copy_matrix( M ) | fail
  rowcnt := *R
  colcnt := *R[1]
  every j := 1 to colcnt do { # for each column
    colsum := 0
    every i := 1 to rowcnt do colsum +:= R[i][j]
    if not -threshold < colsum < threshold
      then
        every i := 1 to rowcnt do R[i][j] /:= colsum
  }
  return R
end

# sprintf_matrix(format,M) - produces a matrix R of strings formatted
#                            by sprintf
procedure sprintf_matrix( fmt, M )
  local R, row, rowcnt, colcnt, i, j
  # sanity checks
  type(M) == "list"      | fail
  type(M[1]) == "list"   | fail
  rowcnt := *M           | fail
  colcnt := *M[1]        | fail
  every i := 2 to rowcnt
    do *M[i] = colcnt    | fail
  R := list( )                # create list of rows
  every i := 1 to rowcnt do {
    put( R, row := list( ) )  # create list of column values
    every j := 1 to colcnt do # populate column values
      put( row, sprintf( fmt, M[i][j] ) | fail )
  }
  return R
end
