// JavaScript Functions for Cryptography
// Started: 11-04-05, PM
// Updated: 11-04-05, PM
// (c) 2005, P. Mathys

// ASCII text string to number (0...255) conversion
// e.g., Hello --> 72,101,108,108,111
// Converts text string without modification
function txtToNum(txt){
  var txtNum = new Array()
  for (var i=0; i<txt.length; i++){
    txtNum[i] = txt.charCodeAt(i)
  }
  return txtNum
}

// Number (0...255) to ASCII text string conversion
// e.g., 72,101,108,108,111 --> Hello
// Converts array of numbers without modification
function numToTxt(txtNum){
  var txt = ''
  for (var i=0; i<txtNum.length; i++){
    txt += String.fromCharCode(txtNum[i])
  }
  return txt
}

// ASCII text string to number (0...25) conversion
// e.g., Hello --> 7,4,11,11,14
// Keeps only letters A...Z, (all uppercase)
function txtToNum26(txt){
  var txtNum = new Array()
  var aux
  var j = 0
  txt = txt.toUpperCase()
  for (var i=0; i<txt.length; i++){
    aux = txt.charCodeAt(i)
    if (aux>='A'.charCodeAt(0)&aux<='Z'.charCodeAt(0)){
      txtNum[j++] = aux - 'A'.charCodeAt(0)
    }
  }
  return txtNum
}

// Number (0...25) to ASCII text string conversion
// e.g., 7,4,11,11,14 --> HELLO
// Adds 65 ('A') to array of numbers before conversion
function numToTxt26(txtNum){
  var txt = ''
  for (var i=0; i<txtNum.length; i++){
    txt += String.fromCharCode(txtNum[i]+'A'.charCodeAt(0))
  }
  return txt
}

// ASCII text string to number (-32,0...25) conversion
// e.g., Hi there -->
// Keeps only letters A...Z (all uppercase), and (single) spaces
// LF converts to space
function txtToNum26SP(txt){
  var txtNum = new Array()
  var makeSP = ' \n'
  var makeSPnum = new Array()
  var aux
  var j = 0
  txt = txt.toUpperCase()
  for (var k=0; k<makeSP.length; k++){
    makeSPnum[k] = makeSP.charCodeAt(k)
    }
  for (var i=0; i<txt.length; i++){
    aux = txt.charCodeAt(i)
    if (aux>='A'.charCodeAt(0)&aux<='Z'.charCodeAt(0)){
      txtNum[j++] = aux - 'A'.charCodeAt(0)
    } else {
      for (var k=0; k<makeSPnum.length; k++){
        if (aux==makeSPnum[k]) {
          if (j>0){
            if (txtNum[j-1]!=(' '.charCodeAt(0) - 'A'.charCodeAt(0))){
              txtNum[j++] = ' '.charCodeAt(0) - 'A'.charCodeAt(0)
            }
          }
        }
      }
    }
  }
  return txtNum
}

// ASCII text string to number (-20,0...25) conversion
// Keeps only letters A...Z (all uppercase), and hyphens ('-')
function txtToNum26dash(txt){
  var txtNum = new Array()
  var aux
  var j = 0
  txt = txt.toUpperCase()
  for (var i=0; i<txt.length; i++){
    aux = txt.charCodeAt(i)
    if (aux>='A'.charCodeAt(0)&aux<='Z'.charCodeAt(0)){
      txtNum[j++] = aux - 'A'.charCodeAt(0)
    } else {
      if (aux=='-'.charCodeAt(0)){
        txtNum[j++] = aux - 'A'.charCodeAt(0)
      }
    }
  }
  return txtNum
}

// Complete number sequence in txtNum so that it is permutation
// of 26 letter alphabet (0...25)
function complete26(txtNum){
  var flags = genConstVec(0,26)    // Flags to keep track of numbers seen
  var txtNum26 = new Array()
  var j = 0
  var aux
  for (var i=0; i<txtNum.length; i++){
    aux = Math.round(txtNum[i])
    if (aux=='-'.charCodeAt(0)-'A'.charCodeAt(0)){
      return txtNum
    }
    if (aux>=0&aux<26){
      if (flags[aux]==0){
        txtNum26[j++] = aux
        flags[aux] += 1
      }
    }
  }
  for (var i=0; i<flags.length; i++){
    if (flags[i]==0){
      txtNum26[j++] = i
      flags[i] += 1
    }
  }
  return txtNum26
}

// Check if number sequence in txtNum is permutation
// of 26 letter alphabet (0...25)
function check26(txtNum){
  var flags = genConstVec(0,26)    // Flags to keep track of numbers seen
  var aux
  if (txtNum.length!=26){
    return 0
  }
  for (var i=0; i<txtNum.length; i++){
    aux = txtNum[i]
    if (aux!=Math.round(txtNum[i])){
      return 0
    }
    if (aux<0|aux>=26){
      return 0
    }
    flags[aux] += 1
  }
  for (var i=0; i<flags.length; i++){
    if (flags[i]!=1){
      return 0
    }
  }
  return 1
}

// Convert ASCII text in 'txt' to blocks of length
// 'blockLen' (separated by spaces) and lines of maximum
// length 'lineLen' (separated by \n)
// e.g. txtBlocks('THISISATESTOFAFUNCTION',5,72)
// --> THISI SATES TOFAF UNCTI ON
function txtBlocks(txt,blockLen,lineLen) {
  var txtOut = ''
  var bpl = Math.floor((lineLen+1)/(blockLen+1))  // blocks per line
  var j = 0    // counts chars in block
  var k = 0    // counts blocks on line
  for (var i=0; i<txt.length; i++){
    txtOut += txt.charAt(i)
    j += 1
    if (j % blockLen == 0) {
      k += 1
      if (k<bpl){
        txtOut += ' '
      } else {
        txtOut += '\n'
        k = 0
      }
    }
  }
  return txtOut
}

// computes N modulo m such that N = -1  =>  N = m-1
function modUlo(N,m){
  if (N<0){
    var q = Math.ceil(Math.abs(N)/m)
    N = N + q*m
  }
  return N % m
}

// Computes (a+b) modulo m for arrays a, b
function addMod(a,b,m){
  var len = a.length
  if (b.length<len){
    len = b.length
  }
  var c = new Array()
  for (var i=0; i<len; i++){
    c[i] = modUlo(a[i]+b[i],m)
  }
  return c
}

// Computes (a-b) modulo m for arrays a, b
function subMod(a,b,m){
  var len = a.length
  if (b.length<len){
    len = b.length
  }
  var c = new Array()
  for (var i=0; i<len; i++){
    c[i] = modUlo(a[i]-b[i],m)
  }
  return c
}

// Caesar cipher, adds key K modulo 26 to characters
// in numeric (0...26) form
function cipherCaesar26(txtNum,K){
  var cipherNum = new Array()
  for (var i=0; i<txtNum.length; i++){
    cipherNum[i] = modUlo((txtNum[i] + K),26)
  }
  return cipherNum
}

// Simple substitution cipher
// If content of txtNum[i] is j, then cipherNum = keyLtrsNum[j]
// If j is outside range [0...25] then cipherNum = -20
function cipherSubst1(txtNum,keyLtrsNum){
  var cipherNum = new Array()
  var j
  for (var i=0; i<txtNum.length; i++){
    j = Math.round(txtNum[i])
    if (j>=0&j<26){
      cipherNum[i] = keyLtrsNum[j]
    } else {
      cipherNum[i] = -20
    }
  }
  return cipherNum
}

function revert26(keyLtrsNum){
  var keyLtrsNumR = genConstVec('-'.charCodeAt(0)-'A'.charCodeAt(0),26)
  var aux
  for (var i=0; i<keyLtrsNum.length; i++){
    aux = keyLtrsNum[i]
    if (aux==Math.round(aux)&aux>=0&aux<26){
      keyLtrsNumR[aux] = i
    }
  }
  return keyLtrsNumR
}

// Makes histogram from data in vector 'y'
// Vector 'x' specifies bin centers (from left to right)
function histoGram(y,x){
  var N = new Array()
  var boundary = new Array()
  for (var j=0; j<x.length; j++){
    N[j] = 0         // initialize
  }
  for (var j=0; j<x.length-1; j++){
    boundary[j] = (x[j]+x[j+1])/2
  }
  for (var i=0; i<y.length; i++){
    if (y[i]>=boundary[0]&y[i]<boundary[N.length-2]){
      for (var j=1; j<N.length-1; j++){
        if (y[i]>=boundary[j-1]&y[i]<boundary[j]){
          N[j] += 1
          break
        }
      }
    } else {
      if (y[i]<boundary[0]){
        N[0] += 1
      }
      if (y[i]>=boundary[N.length-2]){
        N[N.length-1] += 1
      }
    }
  }
  return N
}

// Generate vector from 'a' to 'b' with increments 'inc'
function genVec(a,b,inc){
  var x = new Array()
  if ((b<a&inc>0)|(b>a&inc<0)|(inc==0)){
    return x
  }
  var i = 0
  x[i] = a
  if (a<b){
    while ((x[i]+inc)<=b){
      x[i+1] = x[i++]+inc
    }
  } else {
    while ((x[i]+inc)>=b){
      x[i+1] = x[i++]+inc
    }
  }
  return x
}

// Generate constant vector (filled with 'filld')
// of length 'len'
function genConstVec(filld,len){
  var x = new Array()
  if (len<=0){
    return x
  }
  for (var i=0; i<len; i++){
    x[i] = filld
  }
  return x
}

// Computes relative frequency of characters (A...Z represented
// as numbers 0...25) in 'txtNum' vector
function relfrqTxt26(txtNum){
  var M = 26
  var pvec = histoGram(txtNum,genVec(0,M-1,1))
  for (var i=0; i<M; i++){
    pvec[i] = pvec[i]/txtNum.length
  }
  return pvec
}

// Find maximum value in vector x
function maxX(x){
  var maxi = x[0]
  for (var i=0; i<x.length; i++){
    if (x[i]>maxi){
      maxi = x[i]
    }
  }
  return maxi
}

// Find minimum value in vector x
function minX(x){
  var mini = x[0]
  for (var i=0; i<x.length; i++){
    if (x[i]<mini){
      mini = x[i]
    }
  }
  return mini
}

// Vector 'x' to string conversion for display
// 'w' is width of each entry
// 'sep' is column separator symbol
function vecToStr(x,w,sep){
  var str = ''
  var aux
  var ovrflow = '*'
  while (ovrflow.length<w){
    ovrflow = '*' + ovrflow
  }
  for (var i=0; i<x.length; i++){
    aux = '' + x[i]
    if (aux.length>w){
      aux = ovrflow
    } else {
      while (aux.length<w){
        aux = ' ' + aux
      }
    }
    str += aux
    str += sep
  }
  return str.substring(0,str.length-sep.length)
}

// Vector 'x' to string conversion with 'ld' and 'rd' digits
// to the left and right of the decimal point and separation
// character(s) 'sep' between elements
// 'ld' includes minus sign for negative numbers
// Total width per entry (excl sep) is ld if rd==0, ld+rd+1
// otherwise
function vecToStrNum(x,ld,rd,sep){
//  var maxi = maxX(x)
  var mini = minX(x)
  var w = ld + rd       // width of each item, excluding sep
  var lrd = w
  if (mini<0){
    lrd -= 1
    ld -= 1
  }
  if (rd>0){
    w += 1              // decimal point
  }
  var ovrflow = '*'
  while (ovrflow.length<w){
    ovrflow = '*' + ovrflow
  }
  var str = ''
  var aux
  var auxn
  var L
  for (var i=0; i<x.length; i++){
    auxn = Math.round(Math.pow(10,rd)*x[i])
    aux = '' + Math.abs(auxn)
    if (aux.length>lrd|(auxn<0&ld<0)){
      str += ovrflow
    } else {
      while (aux.length<rd){
        aux = '0' + aux
      }
      L = aux.length
      if (rd>0){
        aux = aux.substring(0,L-rd) + '.' + aux.substring(L-rd,L)
      }
      if (ld>0&rd>0&aux.length<rd+2){
        aux = '0' + aux
      }
      if (auxn<0){
        aux = '-' + aux
      }
      while (aux.length<w){
        aux = ' ' + aux
      }
      str += aux
    }
    if (i<x.length-1){
      str += sep
    }
  }
  return str
}

// mkRow(d,width) is a row object (array) constructor
// It fills a row of length 'width' with data 'd'
// that can be either numeric or string
function mkRow(d,width){
  this.Col = new Array()      // Each entry corresponds to a column in a 2-D array
  for (var j=0; j<width; j++){
    this.Col[j] = d
  }
}

// mk2D(d,width,height) is a 2-dim array constructor
// It fills array with 'height' rows and 'width' columns
// with data 'd' (numeric or string)
function mk2D(d,width,height){
  for (var i=0; i<height; i++){
    this[i] = new mkRow(d,width)
  }
  this.rows = height
  this.cols = width
}

// Array to 2-dim array conversion, horizontal
// Converts array to (horizontal) row vector
function arrayTo2Dh(x){
  var A = new mk2D(0,x.length,1)
  A[0].Col = x
  return A
}

// Array to 2-dim array conversion (reshape), R rows
// Converts array to 2-dim array with R rows
// and Math.floor(x.length/R) columns
function arrayTo2Drows(x,R){
  var C = Math.floor(x.length/R)
  var A = new mk2D(0,C,R)
  for (var i=0; i<R; i++){
    for (var j=0; j<C; j++){
      A[i].Col[j] = x[i*C+j]
    }
  }
  return A
}

// Converts 2-dim array to regular JavaScript array (vector)
function twoDToArray(A){
  var x = new Array()
  for (var i=0; i<A.rows; i++){
    for (var j=0; j<A.cols; j++){
      x[i*A.cols+j] = A[i].Col[j]
    }
  }
  return x
}

// Array to 2-dim array conversion, vertical
// Converts array to (vertical) column vector
function arrayTo2Dv(x){
  var A = new mk2Dv(0,1,x.length)
  for (var i=0; i<x.length; i++){
    A[i].Col[0] = x[i]
  }
  return A
}

// Matrix (2-dim array) transpose
function transpose2D(A){
  var AT = new mk2D(0,A.rows,A.cols)
  for (var i=0; i<AT.rows; i++){
    for (var j=0; j<AT.cols; j++){
      AT[i].Col[j] = A[j].Col[i]
    }
  }
  return AT
}

// horizJoin(A,B) joins 2-dim arrays A, B horizontally
// and returns  C = [A B]
// C.rows = min{A.rows,B.rows}
function horizJoin(A,B){
  var width = A.cols + B.cols
  var height = A.rows
  if (B.rows<A.rows){
    height = B.rows
  }
  var C = new mk2D(0,width,height)
  for (var i=0; i<height; i++){
    C[i].Col = A[i].Col.concat(B[i].Col)
  }
  return C
}

// vertJoin(A,B) joins 2-dim arrays A, B vertically
// and returns  C = [A; B]
// C.cols = min{A.cols,B.cols}
function vertJoin(A,B){
  var height = A.rows + B.rows
  var width = A.cols
  if (B.cols<A.cols){
    width = B.cols
  }
  var C = new mk2D(0,width,height)
  for (var i=0; i<A.rows; i++){
    C[i].Col = A[i].Col.slice(0,width)
  }
  for (var i=0; i<B.rows; i++){
    C[i+A.rows].Col = B[i].Col.slice(0,width)
  }
  return C
}

// 2-D array to text string conversion for display
// (array constructed using var A = new mk2D(d,width,height))
// 'w' is width of each entry
// 'sepCol' is column separator symbol
// 'sepRow' is row separator symbol
function array2DToStr(A,w,sepCol,sepRow){
  var str = ''
  var aux
  var ovrflow = '*'
  while (ovrflow.length<w){
    ovrflow = '*' + ovrflow
  }
  for (var i=0; i<A.rows; i++){
    for (var j=0; j<A.cols; j++){
      aux = '' + A[i].Col[j]
      if (aux.length>w){
        aux = ovrflow
      } else {
        while (aux.length<w){
          aux = ' ' + aux
        }
      }
      str += aux
      str += sepCol
    }
    str = str.substring(0,str.length-sepCol.length)
    str += sepRow
  }
  return str
}

// 2-D array to text string conversion with 'ld' and 'rd' digits
// to the left and right of the decimal point and separation
// character(s) 'sepCol' between elements in same row and
// separation character(s) 'sepRow' between rows
// 'ld' includes minus sign for negative numbers
// Total width per entry (excl sepCol) is ld if rd==0,
// ld+rd+1 otherwise
// (2-D array constructed using var A = new mk2D(d,width,height))
function array2DToStrNum(A,ld,rd,sepCol,sepRow){
  var str = ''
  for (var i=0; i<A.rows; i++){
    str += vecToStrNum(A[i].Col,ld,rd,sepCol)
    str += sepRow
  }
  return str
}

function plotXY(x,y,width,height){
  var wL = 12                // Left width (for YLabels)
  var hB = 2                 // Bottom height (for XLabels)
  actwide = width - wL       // Actual width for plot
  acthigh = height - hB      // Actual height for plot
  var xx = new Array()
  var yy = new Array()
  var inc
  if (actwide>=x.length){
    inc = Math.floor(actwide/x.length)
    for (var i=0; i<x.length; i++){
      for (var j=0; j<inc; j++){
        xx[i*inc+j] = x[i]
        yy[i*inc+j] = y[i]
      }
    }
    xx = xx.slice(0,xx.length-inc+1)
  } else {
    inc = Math.floor(x.length/actwide)
    for (var i=0; i<actwide; i++){
      xx[i] = x[i*inc]
      yy[i] = y[i*inc]
    }
    inc = 1
  }
  actwide = xx.length
  var xmin = minX(xx)
  var xmax = maxX(xx)
  var ymin = minX(yy)
  var ymax = maxX(yy)
  var horz = xmax-xmin       // x-width
  var vert = ymax-ymin       // y-width
  var deltax = horz/(actwide-1)
  var deltay = vert/(acthigh-1)
  var yqb = new Array()      // y quantization boundaries
  for (var i=0; i<acthigh-1; i++){
    yqb[(acthigh-2)-i] = (i+0.5)*deltay+ymin
  }
  var A = new mk2D(' ',actwide,acthigh)   // Array for plot
  for (var j=0; j<actwide; j+=inc){
    if (yy[j] >= yqb[0]){
      A[0].Col[j] = '*'
    }
  }
  for (var i=1; i<acthigh-1; i++){
    for (var j=0; j<actwide; j+=inc){
      if (yy[j] >= yqb[i] & yy[j] < yqb[i-1]){
        A[i].Col[j] = '*'
      }
    }
  }
  for (var j=0; j<actwide; j+=inc){
    if (yy[j] < yqb[acthigh-2]){
      A[acthigh-1].Col[j] = '*'
    }
  }
  var str
  var aux
  var yskip = 4
  var L = new mk2D(' ',wL,acthigh)
  for (var i=0; i<acthigh; i+=yskip){
    aux = i*deltay + ymin
    str = num2Str(aux,5,4)
    str = str + ' - '
    while (str.length<wL){
      str = ' ' + str
    }
    for (var j=0; j<wL; j++){
      L[acthigh-1-i].Col[j] = str.substring(j,j+1)
    }
  }
  var xskip = wL-2
  var B = new mk2D(' ',width,hB)
  for (var j=wL; j<width-Math.floor(xskip/2); j+=xskip){
    aux = (j-wL)*deltax + xmin
    str = num2Str(aux,5,4)
    B[0].Col[j] = '|'
    for (var k=0; k<str.length; k++){
      B[1].Col[j+k-Math.floor(str.length/2)] = str.substring(k,k+1)
    }
  }
  var P = vertJoin(horizJoin(L,A),B)
  return P
}

// Number to string function
// String is of form -1.29e-5
// dmant is digits for mantissa (including '-' sign, decimal point)
// dexp is digits for exponent (including 'e' and '-' sign)
function num2Str(num,dmant,dexp){
  var aux1
  var aux2
  var aux3
  var str
  var str1
  var str2
  var ovrflow = '*'
  while (ovrflow.length<dexp-1){
    ovrflow = '*'+ ovrflow
  }
  if (num==0){
    str = '0'
  } else {
    //aux1 = Math.round(M.pow(10,dmant-3)*num)
    //aux2 = Math.log(Math.abs(aux1))/Math.log(10))
    //aux2 = aux2 - (dmant-3)
    aux2 = Math.floor(Math.log(Math.abs(num))/Math.log(10))     // Exponent
    aux1 = num*Math.pow(10,-aux2)                  // Mantissa
    str1 = '' + Math.round(Math.pow(10,dmant-3)*Math.abs(aux1))
    if (str1.length>dmant-2){
      aux1 = aux1/10
      aux2 = aux2 + 1
      str1 = '' + Math.round(Math.pow(10,dmant-3)*Math.abs(aux1))
    }
    while (str1.length<dmant-2){str1 = '0' + str1}
    str1 = str1.substring(0,1) + '.' + str1.substring(1,dmant-2)
    if (aux1<0){str1 = '-' + str1}
    str2 = '' + Math.abs(aux2)
    if (aux2<0){str2 = '-' + str2}
    if (str2.length>dexp-1){str2 = ovrflow}
    str = str1 + 'e' + str2
  }
  return str
}

// Makes plot array P from probability vector pvec
// of the 26 characters of the alphabet
// width is number of columns of P, height is number of
// rows of P
function stemTxt26(pvec,width,height){
  var wL = 8               // Left width (for YLabels)
  var hB = 3               // Bottom height (for XLabels)
  actwide = width - wL -1  // Actual width for plot
  acthigh = height - hB    // Actual height for plot
  var x = genVec(0,25,1)
  var xLnum = new Array()  // x-label numbers
  var xx = new Array()
  var yy = new Array()
  if (actwide>=x.length){
    inc = Math.floor(actwide/x.length)
    for (var i=0; i<x.length; i++){
      xLnum[i] = x[i]
      for (var j=0; j<inc; j++){
        xx[i*inc+j] = x[i]
        yy[i*inc+j] = pvec[i]
      }
    }
    xx = xx.slice(0,xx.length-inc+1)
  } else {
    inc = Math.floor(x.length/actwide)
    for (var i=0; i<actwide; i++){
      xLnum[i] = x[i*inc]
      xx[i] = x[i*inc]
      yy[i] = pvec[i*inc]
    }
    inc = 1
  }
  actwide = xx.length + 1
  var xmin = minX(xx)
  var xmax = maxX(xx)
  var ymin = minX(yy)
  if (ymin>0){ymin = 0}
  var ymax = maxX(yy)
  var horz = xmax-xmin       // x-width
  var vert = ymax-ymin       // y-width
  var deltax = horz/(actwide-1)
  var deltay = vert/(acthigh-1)
  var yqb = new Array()      // y quantization boundaries
  for (var i=0; i<acthigh-1; i++){
    yqb[(acthigh-2)-i] = (i+0.5)*deltay+ymin
  }
  var A = new mk2D(' ',actwide,acthigh)   // Array for plot
  for (var j=0; j<actwide; j+=inc){
    if (yy[j] >= yqb[0]){
      A[0].Col[j] = 'o'
    }
  }
  for (var i=1; i<acthigh-1; i++){
    for (var j=0; j<actwide; j+=inc){
      if (yy[j] >= yqb[i]){
        A[i].Col[j] = '|'
      }
      if (yy[j] >= yqb[i] & yy[j] < yqb[i-1]){
        A[i].Col[j] = 'o'
      }
    }
  }
  for (var j=0; j<actwide; j+=inc){
    if (yy[j] < yqb[acthigh-2]){
      A[acthigh-1].Col[j] = 'o'
    } else {
      A[acthigh-1].Col[j] = '|'
    }
  }
  var str
  var aux
  var yskip = 4
  var dmant = 5
  var L = new mk2D(' ',wL,acthigh)
  for (var i=0; i<acthigh; i+=yskip){
    aux = i*deltay + ymin
    if (aux==0){
      str = '0'
    }else{
      str = '' + Math.round(Math.pow(10,dmant-2)*aux)
      while (str.length<dmant-1){
        str = '0' + str
      }
      str = str.substring(0,1) + '.' + str.substring(1,dmant-1)
    }
    str = str + ' - '
    while (str.length<wL){
      str = ' ' + str
    }
    for (var j=0; j<wL; j++){
      L[acthigh-1-i].Col[j] = str.substring(j,j+1)
    }
  }
  var B = new mk2D(' ',width,hB)
  var ovrflow = '*'
  while (ovrflow.length<inc){ovrflow = '*' + ovrflow}
  for (var j=0; j<actwide; j++){
    B[1].Col[j*inc+wL] = String.fromCharCode(xLnum[j]+'A'.charCodeAt(0))
    if (inc>1){
      aux = Math.floor(pvec[j]*Math.pow(10,inc-1))
      str = '' + aux
      if (str.length>inc){
        str = ovrflow
      } else {
        while (str.length<inc-1){str = '0' + str}
        str = '.' + str
      }
    }
    for (var k=0; k<str.length; k++){
      B[2].Col[j*inc+wL-Math.floor(inc/2)+k] = str.substring(k,k+1)
    }
  }
  var P = vertJoin(horizJoin(L,A),B)
  return P
}

// Returns 2-dim array with addition table modulo m
function addModTbl(m){
  var A = new mk2D(0,m,m)
  for (var i=0; i<m; i++){
    for (var j=0; j<m; j++){
      A[i].Col[j] = (i+j)%m
    }
  }
  return A
}

// Returns 2-dim array with multiplication table modulo m
function multModTbl(m){
  var M = new mk2D(0,m,m)
  for (var i=0; i<m; i++){
    for (var j=0; j<m; j++){
      M[i].Col[j] = (i*j)%m
    }
  }
  return M
}

// Random number generator using linear congruential method
// rand01Seed = (a*rand01Seed + c)  mod m
// for a=3141593, c=1048139, m=2^(26)-27=67108837 (prime)
// The returned value is in the range [0...1) (uniform)
// Note that 'seed' is an integer in the range 0...67108836
// Larger integers are taken modulo 67108837
// Example how to initialize:
//   var today = new Date()
//   rand01(today.getTime()%1000)

var rand01Seed = 0
function rand01(seed){
  var a = 3141593
  var c = 1048139
  var m = 67108837
  if (rand01.arguments.length>0){
    rand01Seed = modUlo(seed,m)
  } else {
    rand01Seed = (a*rand01Seed + c) % m
  }
  return rand01Seed/m
}


