1. matrix.e

Test.
--
--
--
--
--Matrix.e file by A. P. Adamson, Jan. 1997, for use with EUPHORIA language,
--based in part on the matrix Ada programs in matrix directory of
--Walnut Creek Ada CDRom and its predecessors.
--Send error notes to me at        euclid at horandata.net
--Additional routines can also be added. If you have one you like, send it
--to me or post it to the news group.
--A modest amount of checking has been done but there may still be errors.
--Use at your own risk. I make no claim for the correctness of the routines.
--
--I define row vector as:     sequence  {1,2,3,...} and
--I define column vector as:  sequence  {{1},{2},{3},...}
--
--The following functions are implemented in the order shown.
--
--global function makeRowVector(integer iIn)
--global function makeColumnVector(integer iIn)
--global function make2DMatrix(integer rows, integer cols)
--global function make3DMatrix(integer rows, integer cols, integer layers)
--global function transpose( sequence matIn)
--global function dotRowRowVec (sequence seqIn)
--global function dotRowColVec (sequence seqIn)
--global function vectorsCrossProduct (sequence seqIn)
--global function matrixProduct (sequence seqIn)
--global function matrixTimesRowVector(sequence seqIn)
--global function matrixTimesColumnVector(sequence seqIn)
--global function rowVectorTimesMatrix(sequence seqIn)
--global function columnVectorTimesMatrix(sequence seqIn)
--global function matrixTrace(sequence matIn)
--global function abs(atom numIn)
--global function matrixPower(sequence seqIn)
--global function rot2DVectorCW90 (sequence seqIn)
--global function rot2DVectorCCW90 (sequence seqIn)
--global function reverseXin2DVector(sequence seqIn)
--global function reverseYin2DVector(sequence seqIn)
--global function getValueACrossB(sequence seqIn)
--global function getVectorLength(sequence seqIn)
--global function getCosIncludedAngleBetweenAB(sequence seqIn)
--global function crout(sequence seqIn, sequence vecIn)
--global function simultaneousInvert(sequence seqIn, sequence vecIn)
--
global function makeRowVector(integer iIn)
    return repeat(0,iIn)
end function
--
-- ******************************************************************--
--
global function makeColumnVector(integer iIn)
    return repeat(repeat(0,1),iIn)
end function
--
-- ******************************************************************--
--
global function make2DMatrix(integer rows, integer cols)
    return repeat(repeat(0,cols), rows)
end function
--
-- ******************************************************************--
--
global function make3DMatrix(integer rows, integer cols, integer layers)
    return repeat(repeat(repeat(0,cols), rows),layers)
end function
--
-- ******************************************************************--
--
global function transpose( sequence matIn)
   --   This function performs the tranpose of a vector or 2 dimensional
   --   matrix matIn.
   --   (A) {1,2,3,4} becomes a column vector  {{1},{2},{3},{4}}
   --   (B) {{1},{2},{3},{4}} becomes a column vector {1,2,3,4}
   --   (C) {{1,2,3,4},{-1,3,-1,0}} becomes {{1,-1},{2,3},{3,-2},{4,0}}
   --   etc
--
    sequence matOut
    if atom(matIn[1]) then                                      --case A
       matOut = make2DMatrix(length(matIn),1)
       for i = 1 to length(matIn) by 1 do
            matOut[i][1] = matIn[i]
       end for
       return matOut
    elsif
       length(matIn[1]) = 1 then                                --case B
       matOut = makeRowVector(length(matIn))
       for i = 1 to length(matIn) by 1 do
            matOut[i] = matIn[i][1]
       end for
       return matOut
    else
       matOut = make2DMatrix(length(matIn[1]),length(matIn))    --case C
       for i = 1 to length(matIn) by 1 do
            for j = 1 to length(matIn[1]) by 1 do
                matOut[j][i] = matIn[i][j]
            end for
          end for
    end if
    return matOut
end function
--
-- ******************************************************************--
--
  global function dotRowRowVec (sequence seqIn)
    --seqIn is of the form {{a, b, c}, {d, e, f}}  but more than 3
            --terms cam be used
    --   This function performs the inner (dot) product of two vectors A
    --   and B resulting in a floating point number.
    --   Comparability of dimensions is checked.
--
    atom flo
    flo = 0.0
    if (length(seqIn[1]) = length(seqIn[2]) and
                          atom(seqIn[1][1]) and
                          atom(seqIn[2][1]))
    then
        for i = 1 to length(seqIn[1]) do
          flo = flo + seqIn[1][i]  * seqIn[2][i]
        end for
    return flo
    else
    puts (1, "vector lengths unequal\n")
    return 0
    end if
  end function
--
-- ******************************************************************--
--
  global function dotRowColVec (sequence seqIn)
    --seqIn is of the form {{a, b, c}, {{d}, {e}, {f}}}  but more than 3
            --terms cam be used
--
    --   This function performs the inner (dot) product of two vectors A,
    --   a row vwctor and B, a column vector resulting in a floating point
    --   number.
    --   Comparability of dimensions is checked.
--
    atom flo
    flo = 0.0
    if (length(seqIn[1]) = length(seqIn[2]) and
                           atom(seqIn[1][1]) and
                           length(seqIn[2][1] = 1))
    then
        for i = 1 to length(seqIn[1]) do
          flo = flo + seqIn[1][i]  * seqIn[2][i][1]
        end for
    return flo
    else
    puts (1, "vector lengths unequal\n")
    return 0
    end if
  end function
--
-- *******************************************************************
--
  global function   vectorsCrossProduct (sequence seqIn)
    --   This function performs the cross product of two 3 element vectors
    --   resulting in a 3 element vector.
    --   Usage, v = vectorsCrossProduct(seqIn) where
    --   seqIn is of the form {{{number},{number},{number}},
    --                         {{number},{number},{number}}}
    --   Compatability of dimensions is checked.
--
  sequence sOut
    sOut = makeRowVector(3)
    if (length(seqIn[1]) = 3 and
        length(seqIn[2]) = 3 and
          atom(seqIn[1][1])  and
          atom(seqIn[2][1])  )
     then
        sOut[1] = seqIn[1][2] * seqIn[2][3] - seqIn[1][3] * seqIn[2][2]
        sOut[2] = seqIn[1][3] * seqIn[2][1] - seqIn[1][1] * seqIn[2][3]
        sOut[3] = seqIn[1][1] * seqIn[2][2] - seqIn[1][2] * seqIn[2][1]
--
    end if
    return sOut
    end function
-- ******************************************************************--
--
--  functions "+, -, *, /" to term by term add, subtract, multiply, divide
--  2 vectors (or 2 matricees, or a number and a matrix or vector) is
--  inherent in Euphoria.  C = A + B
--
-- ******************************************************************--
--
  global function matrixProduct (sequence seqIn)
  --seqIn is of the form seqIn[1] = mat1In with mat1In having m rows
  --and n columns
  --                     seqIn[2] = mat2In with mat2In having n rows
  --and q columns, ie, the number of columns in the first matrix equals
  --the number of rows in the second matrix.
  --m, n, q must be > 1.
  --The output matrix will have m rows and q columns.
  --   Comparability of dimensions is checked.
--
  atom sum
  sequence vecOut
  sum = 0
    if (length(seqIn[1][1]) != length(seqIn[2])) then
      puts(1,"Error in dimensions of input matricees")
      return 1=0
    else
  vecOut = make2DMatrix(length(seqIn[1]),length(seqIn[2][2]) )
     for i = 1 to length(seqIn[1]) by 1 do
      for j =1 to length(seqIn[2][1]) by 1 do
       sum = 0
       for k = 1 to length(seqIn[1][1]) by 1 do
        sum = sum + (seqIn[1][i][k] * seqIn[2][k][j])
      end for
        vecOut[i][j] = sum
        sum = 0
    end for
  end for
    end if
    return vecOut
  end function
--
-- **********************************************************************
--
  global function matrixTimesRowVector(sequence seqIn)
--
    --   This function performs the multiplication of a matrix (seqIn[1])
    --   and a row vector (seqIn[2] resulting in a row vector.
    --   seqIn format is:
    --   {{matIn},{vecIn}} where matIn is a 2 dimensional matrix and
    --                           vecIn is a 1 dimensional vector of
    --   the same order and form {1,2,3...}. vecOut form is the same as vecIn.
    --   Comparability of dimensions is checked.
    -- **********************************************************************
--
    sequence vecOut
    atom sum
    vecOut = makeRowVector(length(seqIn[2]))
    if length(seqIn[1][1]) != length(seqIn[2])
    then
        return 1=0
    else
    for i = 1 to length(seqIn[1]) do
      sum  = 0.0
      for k = 1 to length(seqIn[2]) do
        sum = sum + seqIn[1][i][k] * seqIn[2][k]
      vecOut[i] = sum
end for
end for
    end if
    return vecOut
  end function
--
-- **********************************************************************
  global function matrixTimesColumnVector(sequence seqIn)
--
    -- **********************************************************************
    --   This function performs the multiplication of a matrix (seqIn[1])
    --   and a column vector (seqIn[2] resulting in a column vector.
    --   seqIn format is:
    --   {{matIn},{vecIn}} where matIn is a 2 dimensional matrix and
    --                           vecIn is a 1 dimensional vector of
    --   the same order and form {{1},{2},{3}...}. vecOut form is the
    --   same as vecIn.
    --   Comparability of dimensions is checked.
    -- **********************************************************************
--
    sequence vecOut
    atom sum
    vecOut = makeRowVector(length(seqIn[2]))
    if length(seqIn[1][1]) != length(seqIn[2])
    then
        return 1=0
    else
    for i = 1 to length(seqIn[1]) do
      sum  = 0.0
      for k = 1 to length(seqIn[2]) do
        sum = sum + seqIn[1][i][k] * seqIn[2][k][1]
      vecOut[i] = sum
end for
end for
    end if
    return vecOut
  end function
--
-- **********************************************************************
--
  global function rowVectorTimesMatrix(sequence seqIn)
--
    -- **********************************************************************
    --   This function performs the multiplication of a row vector
    --   (seqIn[1] and a matrix (seqIn[2]) resulting in a row vector.
    --   seqIn format is: {{vecIn},{matIn}} where matIn is a 2 dimensional
    --   matrix and vecIn is a 1 dimensional vector of form {1,2,3} and of
    --   the same order. vecOut form is the same as vecIn.
    --   Comparability of dimensions is checked.
    -- **********************************************************************
--
    sequence vecOut
    atom sum
--
    vecOut = makeRowVector(length(seqIn[1]))
--
    if length(seqIn[1]) != length(seqIn[2][1])
    then
        return 1=0
    else
    for i = 1 to length(seqIn[2]) do
      sum  = 0.0
      for k = 1 to length(seqIn[1]) do
        sum = sum + seqIn[2][k][i] * seqIn[1][i]
      vecOut[i] = sum
end for
end for
    end if
    return vecOut
  end function
--***************************************************************************
--
  global function columnVectorTimesMatrix(sequence seqIn)
--
    -- **********************************************************************
    --   This function performs the multiplication of a  (column) vector
    --   (seqIn[1] and a matrix (seqIn[2]) resulting in a (column) vector.
    --   seqIn format is: {{vecIn},{matIn}} where matIn is a 2 dimensional
    --   matrix and vecIn is a 1 dimensional vector of the form {{1},{2},{3}}
    --   and of the same order. vecOut form is the same as vecIn.
    --   Comparability of dimensions is checked.
    -- **********************************************************************
--
    sequence vecOut
    atom sum
--
    vecOut = makeColumnVector(length(seqIn[1]))
--
    if length(seqIn[1]) != length(seqIn[2][1])
    then
        return 1=0
    else
    for i = 1 to length(seqIn[2]) do
      sum  = 0.0
      for k = 1 to length(seqIn[1]) do
        sum = sum + seqIn[2][k][i] * seqIn[1][i][1]
      vecOut[i][1] = sum
end for
end for
    end if
    return vecOut
  end function
--***************************************************************************
--
global function matrixTrace(sequence matIn)
--matIn is a 2 dimensional square matrix, format {{1,2,3},{},...}
--This function returns the "trace", ie the sum of the major diagonal terms
--
atom sum
sum = 0
for i = 1 to length(matIn[1]) by 1 do
sum = sum + matIn[i][i]
end for
    return sum
end function
--
--************************************************************************
--
  global function abs(atom numIn)
  if numIn < 0 then return -numIn else return numIn  end if
  end function
--
--************************************************************************
--
  global function matrixPower(sequence seqIn)
    sequence A, B, C, L, M
    integer rows, cols, P, I_PIVOT, J_PIVOT
    atom BIG_ENTRY, TEMP, EPSILON
    A = seqIn[1]
    rows = length(seqIn[1])
    cols = length(seqIn[1][1])
    P = seqIn[2]
    B = make2DMatrix(rows,rows)
    C = make2DMatrix(rows,rows)
    L = makeRowVector(rows)
    M = makeRowVector(rows)
    -- *******************************************************************
    --     This function performs the square matrix operation of " matrix A
    --   raise to integer power P ".  When  P is negative , say P = -N ,
    --   matrixPower(A, -N) = (inverse(A))**N , that is, the inverse of A
    --   raise to power N .  In this case, matrix A must be non-singular.
    --   Exceptions will be raised if the matrix A is not a square matrix,
    --   or if matrix A is singular.
    --     The form of seqIn is {square matrix, power} where seqIn[1] is
    --   the input matrix and power is an integer, + or -. If power = -1,
    --   the returned matrix is the inverse of the matrix in.
--
    --          I tested the inversion operation for matrix size capability
    --  on a  Pentium 120, 32MHZ, DOS6.22 and found:
    --          80 X 80   OK with emm386 active   OK, 100 NG
    --          240 X 240 OK with emm386 inactive OK,85 seconds for inversion.
    --          Locked up for 300 X 300
--
    --          Bob Craig tested on Pentium 150, WIN 95, 32MHZ and found:
    --          500 X 500   worked OK, 707 seconds for the inversion step
    --  The size capability is no doubt due to config.sys details.
--
    -- *******************************************************************
--
    if rows != cols then
    puts(1,"Input matrix is not square")
    return 1=0
    end if
--
    if P = 0 then
          -- B = identity matrix
      for I = 1 to rows do
        for J = 1 to rows do
          if I != J then
            B[I][J] = 0.0
          else
            B[I][J] = 1.0
          end if
        end for
      end for
      return B
    end if
--
    B = A
--
    if P > 0 then
      --& B = A multiplied itself for P times
            for c = 1  to P-1 by 1 do
            B = matrixProduct({A, B})
            end for
      return B
    end if
--
    -- P is negative, find inverse first
    -- initiate the row and column interchange information
--
    for K = 1 to rows do
      L[K] = K   -- row interchage information
      M[K] = K   -- column interchange information
    end for
--
    -- major loop for inverse
--
    for K = 1 to rows do
      -- & search for row and column index I_PIVOT, J_PIVOT
      -- & both in (K .. B'LAST(1) ) for maximum B(I,J)
      -- & in absolute value :BIG_ENTRY
      BIG_ENTRY = 0.0
      -- check matrix singularity
      for I = K to rows do
        for J = K to rows do
          if abs(B[I][J]) > abs(BIG_ENTRY) then
            BIG_ENTRY = B[I][J]
            I_PIVOT = I
            J_PIVOT = J
          end if
        end for
      end for
--
      if K = 1 then
        if BIG_ENTRY = 0.0 then
          puts(1, "SINGULAR Matrix") return 1=0
        else
          EPSILON = rows * (abs(BIG_ENTRY)) * 0.000001
        end if
      else
        if abs(BIG_ENTRY) < EPSILON then
          puts(1, "SINGULAR Matrix")
          end if
      end if
--
      -- interchange row and column
--
      --& interchange K-th and I_PIVOT-th rows
      if I_PIVOT != K then
        for J = 1 to rows  do
          TEMP = B[I_PIVOT][J]
          B[I_PIVOT][J] = B[K][J]
          B[K][J] = TEMP
        end for
        L[K] = I_PIVOT
      end if
      --& interchange K-th and J_PIVOT-th columns
      if J_PIVOT !=K then
        for I = 1 to rows do
          TEMP = B[I][J_PIVOT]
          B[I][J_PIVOT] = B[I][K]
          B[I][K] = TEMP
        end for
        M[K] = J_PIVOT
      end if
--
      --& divide K-th column by minus pivot (-BIG_ENTRY)
--
      for I = 1 to rows do
        if I != K then
          B[I][K] = B[I][K] / (-BIG_ENTRY)
        end if
      end for
--
      -- reduce matrix row by row
--
      for I = 1 to rows do
        if I != K then
          for J = 1 to rows do
            if J != K then
              B[I][J] = B[I][J] + B[I][K] * B[K][J]
            end if
          end for
        end if
      end for
--
      --& divide K-th row by pivot
--
      for J = 1 to rows do
        if J != K then
          B[K][J] = B[K][J] / BIG_ENTRY
        end if
      end for
      B[K][K] = 1.0 / BIG_ENTRY
--
    end for   -- end of major inverse loop
--
    -- final column and row interchange to obtain
    -- inverse of A, i.e. A**(-1)
--
  for K = rows to 1 by -1 do
      -- column interchage
      J_PIVOT = L[K]
      if J_PIVOT != K then
        --& intechange B(I,J_PIVOT) and B(I,K) for each row I
      for I = 1 to rows do
          TEMP = B[I][J_PIVOT]
          B[I][J_PIVOT] = B[I][K]
          B[I][K] = TEMP
        end for
      end if
      -- row interchage
      I_PIVOT = M[K]
      if I_PIVOT != K then
        --& INTECHANGE B(I_PIVOT,J) and B(K,J) for each column J
        for J = 1 to rows do
          TEMP = B[I_PIVOT][J]
          B[I_PIVOT][J] = B[K][J]
          B[K][J] = TEMP
        end for
      end if
    end for
--
    -- inverse of A is obtained and stored in B
    -- now ready to handle the negative power
--
    -- & C = B**(-P)
    if P = -1 then
      return B
    end if
--
    C = B
--
     for I = P+1 to -1 by -1 do
 --C = C * B
 C = matrixProduct({C, B})
    end for
    return C
  end function
--****************************************************************************
--
  global function rot2DVectorCW90 (sequence seqIn)
    --**********************************************************************
******
    --    This function rotates a 2D vector 90 degrees cw and returns the
    --    rotated vector. seqIn format is   {2,4}
    --**********************************************************************
******
    return {seqIn[2],-seqIn[1]}
  end function
--
    --**********************************************************************
******
  global function rot2DVectorCCW90 (sequence seqIn)
    --**********************************************************************
******
    --    This function rotates a 2D vector 90 degrees ccw and returns the
    --    rotated vector. seqIn format is   {2,4}
    --**********************************************************************
******
    return {-seqIn[2],seqIn[1]}
  end function
    --**********************************************************************
******
--
  global function reverseXin2DVector(sequence seqIn)
    --************************************************************************
    --    This function rotates a 2D vector 180 degrees about the Y axis.
    --    seqIn is of the form    {2,4}
    --************************************************************************
    return {-seqIn[1], seqIn[2]}
  end function
--****************************************************************************
--
  global function reverseYin2DVector(sequence seqIn)
    --**********************************************************************
******
    --    Rotates 2D vector 180 degrees about the X axis. seqIn form is
    --          {2,4}
    --**********************************************************************
******
    return {seqIn[1], -seqIn[2]}
  end function
    --************************************************************************
--
  global function getValueACrossB(sequence seqIn)
  --A : VEC2T; B : VEC2T) return FLOAT is
    --************************************************************************
  --Gets magnitude of A cross B for 2 2D vectors.
    --************************************************************************
  sequence A, B
  A = seqIn[1]
  B = seqIn[2]
  return  A[1] * B[2] - A[2] * B[1]
  end function
--
-- ******************************************************************--
global function getVectorLength(sequence seqIn)
--seqIn is of the form {2,3,4}, assumed to be x, y, z coordinates of a vector
--in x,y,z space with x, y, z mutually perpendicular ()more than 3 coords
--can be used
atom a
a = 0
--return sqrt(seqIn[1] * seqIn[1] + seqIn[2] * seqIn[2] + seqIn[3] * seqIn[3])
for i = 1  to length(seqIn) by 1 do
a = a + seqIn[i] * seqIn[i]
end for
return sqrt(a)
end function
--
--************************************************************************
--
    global function getCosIncludedAngleBetweenAB(sequence seqIn)
  --seqIn is of form {vecA , vecB}
  --Gets cos(THETA) where THETA is angle between 2  vectors.
    --************************************************************************
sequence A, B
atom lenA, lenB
A = seqIn[1]
B = seqIn[2]
lenA =  getVectorLength(A)
lenB  = getVectorLength(B)
if lenA = 0 or lenB = 0 then puts(1,"One vec has 0 length!")
    return 1=0
else
    return  ((dotRowRowVec({A,B})) / ( getVectorLength(A)   *
getVectorLength(B)))
end if
  end function
--
    --************************************************************************
--
--following function solves a simultaneous equation set via Crout's
--reduction. It should take half the time of a solution via inversion.
--Actually it takes 4 times as long????
--include matrix.e
--
global function crout(sequence seqIn, sequence vecIn)
--
--For a    n by n    simultaneous equation system such as:
--    x1 + 2*x2        - x4 =  1
--    x1 +   x2 -   x3 + x4 =  4
--  2*x1 -   x2 + 2*x3      =  6
--    x1 +   x2        - x4 = -1
--
--Seqin is the square matrix    1  2  0 -1
--                              1  1 -1  1
--                              2 -1  2  0
--                              1  1  0 -1
--Vecin is the column vector    {{1},{4},{6},{-1}}
--
--They will be manipulated by the crout reduction and forward and backward
--substitution to give the solution to the equations, a new column vector,
--which is returned.
--Reference, Matricees for Engineers, by Alan D. Kraus, page 118
--
--The code fragment is:
  sequence U, L, Y, X
  integer mord
  atom suml, sumu
  mord = length(seqIn)
U = make2DMatrix(mord,mord)     --upper triangle
L = make2DMatrix(mord,mord)     --lower triangle
Y = makeRowVector(mord)         --Y = U * X
X = makeRowVector(mord)         --X = answer vector
--code for finding U and L follows
  for k = 1 to mord do
    U[k][k] = U[k][k] + 1.0
    for i = k to mord do
        if k = 1 then
            L[i][k] = seqIn[i][k]
            for j = k+1 to mord do
                if k  != j then
                    U[k][j] = (1.0/L[k][k]) * (seqIn[k][j])
                end if
            end for
        else
            suml = 0.0
            for m = 1 to k-1 do
                suml = suml + L[i][m] * U[m][k]
                L[i][k] = seqIn[i][k] - suml
            end for
        end if
        for j = k to mord do
            sumu = 0.0
            for m = 1 to k-1 do
                sumu = sumu + L[k][m] * U[m][j]
            end for
            U[k][j] = (1.0/L[k][k]) * (seqIn[k][j] - sumu)
        end for
    end for
end for
--U and L are set by above
--
--Code Fragment for substitutions, set Y
    for i = 1 to mord do    --value Y per 5.20, page 125   for g
        if i = 1 then
            Y[i] = (1.0 / L[i][i]) * vecIn[i][1]
        else
            suml = 0.0
            for m = 1 to i - 1 do
                suml = suml + L[i][m] * Y[m]
            end for
            Y[i] = (1.0 / L[i][i]) * (vecIn[i][1] - suml)
                end if
                end for
--
--Now use back subs to set X
for k =  mord to 1 by -1 do    --value X per 5.21, page 125
   sumu = 0.0
     for m = k+1 to mord do
       sumu = sumu + U[k][m] * X[m]
     end for
  X[k] = Y[k] - sumu
end for
    return X
end function
--
--************************************************************************
--
global function simultaneousInvert(sequence seqIn, sequence vecIn)
--
--For a    n by n    simultaneous equation system such as:
--    x1 + 2*x2        - x4 =  1
--    x1 +   x2 -   x3 + x4 =  4
--  2*x1 -   x2 + 2*x3      =  6
--    x1 +   x2        - x4 = -1
--
--seqIn is the square matrix    1  2  0 -1
--                              1  1 -1  1
--                              2 -1  2  0
--                              1  1  0 -1
--
--seqIn = {{1,2,0,-1,},{1,1,-1,1},{2,-1,2,0},{1,1,0,-1}}
--vecIn is the column vector    {{1},{4},{6},{-1}}
--
--The solutions will be determined via inversion of A and multiplication
--by B with the result returned. It is a new column vector containing the
--solutions for x. Reference, Matricees for Engineers, by Alan D. Kraus
return matrixProduct({matrixPower({seqIn, -1}), vecIn})
end function
--
--************************************************************************
--end MATRIX_PACKAGE;

new topic     » topic index » view message » categorize

2. matrix.e

Please delete the word test from the first line of matrix.e.....sorry for
the error.
Arthur P. Adamson, The Engine Man, euclid at mail.horandata.net

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu