missed counting

missed counting

use Cdo,
    LinearAlgebra,
    LinearAlgebra.Sparse,
    NumSuch,
    Random;

config param batchsize = 10000;

/*
 A matrix of data with named rows and columns.
 */
class NamedMatrix {
  var D: domain(2),
      SD = CSRDomain(D),
      X: [SD] real,  // the actual data
      rows: BiMap = new BiMap(),
      cols: BiMap = new BiMap();

   proc init() {
     super.init();
   }

   proc init(X) {
     this.D = {X.domain.dim(1), X.domain.dim(2)};
     super.init();
     this.loadX(X);
   }
}

/*
Loads the data from X into the internal array, also called X.  We call them all X to keep it clear.

:arg real[]: Array representing the matrix
 */
proc NamedMatrix.loadX(X:[], shape: 2*int =(-1,-1)) {
  if shape(1) > 0 && shape(2) > 0 {
    this.D = {1..shape(1), 1..shape(2)};
  }
  for (i,j) in X.domain {
    this.SD += (i,j);
    this.X(i,j) = X(i,j);
  }
}

/*
Sets the row names for the matrix X
 */
proc NamedMatrix.setRowNames(rn:[]): string throws {
  if rn.size != X.domain.dim(1).size then throw new Error();
  for i in 1..rn.size {
    this.rows.add(rn[i]);
  }
  return this.rows;
}

/*
Sets the column names for the matrix X
*/
proc NamedMatrix.setColNames(cn:[]): string throws {
  if cn.size != X.domain.dim(2).size then throw new Error();
  for i in 1..cn.size {
    this.cols.add(cn[i]);
  }
  return this.cols;
}

/*
 Gets the value of the (i,j) entry of the matrix X in the NamedMatrix
 */
proc NamedMatrix.get(i: int, j: int) {
   return this.X(i,j);
 }

 /*
 Get the values using the named rows and columns
  */
proc NamedMatrix.get(f: string, t: string) {
    return this.X(rows.get(f), cols.get(t));
 }

/*
 Set the value of this.X(i,j) by index row
 */
proc NamedMatrix.set(i: int, j: int, w: real) {
    if this.SD.member((i,j)) {
      this.X(i,j) = w;
    } else {
      this.SD += (i,j);
      this.X(i,j) = w;
    }
    return w;
}

/*
Set the values using the row and column names
*/
proc NamedMatrix.set(f: string, t: string, w: real) {
  return this.set(rows.get(f), cols.get(t), w);
}

/*
Update the value in X(i,j) with `w`
 */
proc NamedMatrix.update(i: int, j: int, w: real) {
  const x = this.get(i,j);
  return this.set(i,j, x + w);
}

/*
Update the value in X(i,j) with `w` using the row column names
 */
proc NamedMatrix.update(f: string, t: string, w: real) {
  const x = this.get(rows.get(f),cols.get(t));
  return this.set(rows.get(f),cols.get(t), x + w);
}

/*
Find the number of non-zeroes in the matrix
 */
proc NamedMatrix.nnz() {
  var i: int = 0;
  for ij in this.X.domain {
    i += 1;
  }
  return i;
}

/*
Calculates the sparsity of the matrix: Number of entries / frame size
 */
proc NamedMatrix.sparsity() {
  const d = this.X.shape[1]:real * this.X.shape[2]: real;
  return this.nnz():real / d;
}

/*
 Multiplies the current NamedMatrix `X` against the argument `Y`, but first it aligns
 the names of `X.cols` with `Y.rows`.  Returns an appropriately named NamedMatrix
 :arg NamedMatrix Y:
 */
proc NamedMatrix.alignAndMultiply(Y: NamedMatrix) {
    var rcOverlap: domain(string) = this.cols.keys & Y.rows.keys;

    var xSD: sparse subdomain(this.D) dmapped CS(),
        ySD: sparse subdomain(Y.D) dmapped CS(),
        xX: [xSD] real,  // the actual data
        yX: [ySD] real,  // the actual data
        xrows: BiMap = new BiMap(),
        yrows: BiMap = new BiMap(),
        xcols: BiMap = new BiMap(),
        ycols: BiMap = new BiMap();

    for (left_row, right_col) in zip(this.D.dim(2), Y.D.dim(1)) {
      for rc in rcOverlap {
        const j = this.cols.get(rc);
        if this.SD.member(left_row, j) {
          xSD += (left_row, j);
          xX[left_row, j] = this.X[left_row, j];
        }
        if Y.SD.member(j, right_col) {
          ySD += (j, right_col);
          yX[j, right_col] = Y.X[j, right_col];
        }
      }
    }
    const z = xX.dot(yX);
    var n = new NamedMatrix(X=z);
    n.rows = this.rows;
    n.cols = Y.cols;
    return n;
}

/*
 Creates a NamedMatrix from a table in Postgres.  Does not optimize for square matrices.  This assumption
 is that the matrix is sparse.

 :arg string edgeTable: The SQL table holding the values of the matrix.
 :arg string fromField: The table column representing rows, e.g. `i`.
 :arg string toField: The table column representing columns, e.g. 'j'.
 :arg string wField: `default=NONE` the table column containing the values of cell `(i,j)``
 :arg boolean square: Whether the matrix should be built to have the same rows and columns
 */

 proc NamedMatrixFromPG(con: Connection
   , edgeTable: string
   , fromField: string, toField: string, wField: string = "NONE"
   , square=false) {
  if square {
    return NamedMatrixFromPGSquare(con: Connection
      , edgeTable, fromField, toField, wField);
  } else {
    return NamedMatrixFromPGRectangular(con: Connection
      , edgeTable, fromField, toField, wField);
  }
}

proc NamedMatrixFromPGRectangular(con: Connection
  , edgeTable: string
  , fromField: string, toField: string, wField: string = "NONE") {

  var q = """
  SELECT ftr, t
  FROM (
    SELECT distinct(%s) AS ftr, 'r' AS t FROM %s
    UNION ALL
    SELECT distinct(%s) AS ftr, 'c' AS t FROM %s
  ) AS a
  GROUP BY ftr, t
  ORDER BY ftr, t ;
  """;

  var rows: BiMap = new BiMap(),
      cols: BiMap = new BiMap();

  var cursor = con.cursor();
  cursor.query(q, (fromField, edgeTable, toField, edgeTable));

  forall row in cursor {
    if row['t'] == 'r' {
      rows.add(row['ftr']);
    } else if row['t'] == 'c' {
      cols.add(row['ftr']);
    }
  }

  var D: domain(2) = {1..rows.size(), 1..cols.size()},
      SD = CSRDomain(D),
      X: [SD] real;  // the actual data

  var r = """
  SELECT %s, %s
  FROM %s
  ORDER BY %s, %s ;
  """;
  var cursor2 = con.cursor();
  cursor2.query(r, (fromField, toField, edgeTable, fromField, toField));
  const size = cursor2.rowcount(): int;
  var count = 0: int,
      dom = {1..size},
      indices: [dom] (int, int),
      values: [dom] real;

  // This guy is causing problems.  Exterminiate with extreme prejudice
  //forall row in cursor2 {
  forall row in cursor2 with (+ reduce count) {
    count += 1;
    indices[count]=(
       rows.get(row[fromField])
      ,cols.get(row[toField])
      );

    if wField == "NONE" {
      values[count] = 1;
    } else {
      values[count] = row[wField]: real;
    }
  }

  SD.bulkAdd(indices);
  forall (ij, a) in zip(indices, values) {
    X(ij) = a;
  }

  const nm = new NamedMatrix(X=X);
  nm.rows = rows;
  nm.cols = cols;
  return nm;
}

/*
 Build a square version of the matrix.  Still directed, but with the same number of rows/cols
 */
proc NamedMatrixFromPGSquare ( con: Connection
    , edgeTable: string
    , fromField: string, toField: string, wField: string = "NONE") {

    var q = """
    SELECT ftr
    FROM (
      SELECT distinct(%s) AS ftr FROM %s
      UNION ALL
      SELECT distinct(%s) AS ftr FROM %s
    ) AS a
    GROUP BY ftr ORDER BY ftr;
    """;

    var cursor = con.cursor();
    cursor.query(q, (fromField, edgeTable, toField, edgeTable));
    var rows: BiMap = new BiMap();

    forall row in cursor {
    //for row in cursor {
      rows.add(row['ftr']);
    }

    var D: domain(2) = {1..rows.size(), 1..rows.size()},
        SD = CSRDomain(D),
        X: [SD] real;  // the actual data

    var r = """
    SELECT %s, %s
    FROM %s
    ORDER BY %s, %s ;
    """;
    var cursor2 = con.cursor();
    cursor2.query(r, (fromField, toField, edgeTable, fromField, toField));
    var dom1: domain(1) = {1..0},
        dom2: domain(1) = {1..0},
        indices: [dom1] (int, int),
        values: [dom2] real;
    //forall row in cursor2 {
    for row in cursor2 {
      indices.push_back((
         rows.get(row[fromField])
        ,rows.get(row[toField])
        ));

      if wField == "NONE" {
        values.push_back(1);
      } else {
        values.push_back(row[wField]: real);
      }
    }

    SD.bulkAdd(indices);
    forall (ij, a) in zip(indices, values) {
      X(ij) = a;
    }

    const nm = new NamedMatrix(X=X);
    nm.rows = rows;
    nm.cols = rows;
    return nm;
}

proc NamedMatrixFromPG_(con: Connection
  , edgeTable: string
  , fromField: string, toField: string, wField: string = "NONE") {

  var q = """
  SELECT ftr, t, row_number() OVER(PARTITION BY t ORDER BY ftr ) AS ftr_id
  FROM (
    SELECT distinct(%s) AS ftr, 'r' AS t FROM %s
    UNION ALL
    SELECT distinct(%s) AS ftr, 'c' AS t FROM %s
  ) AS a
  GROUP BY ftr, t
  ORDER BY ftr_id, t ;
  """;

  var rows: BiMap = new BiMap(),
      cols: BiMap = new BiMap();

  var cursor = con.cursor();
  cursor.query(q, (fromField, edgeTable, toField, edgeTable));
  for row in cursor {
    if row['t'] == 'r' {
      rows.add(row['ftr'], row['ftr_id']:int);
    } else if row['t'] == 'c' {
      cols.add(row['ftr'], row['ftr_id']:int);
    }
  }

  var D: domain(2) = {1..rows.size(), 1..cols.size()},
      SD = CSRDomain(D),
      X: [SD] real;  // the actual data

  var r = """
  SELECT %s, %s
  FROM %s
  ORDER BY %s, %s ;
  """;
  var cursor2 = con.cursor();
  cursor2.query(r, (fromField, toField, edgeTable, fromField, toField));
  var dom1: domain(1) = {1..0},
      dom2: domain(1) = {1..0},
      indices: [dom1] (int, int),
      values: [dom2] real;

  //forall row in cursor2 {
  for row in cursor2 {
//    writeln("row: ", row[fromField], " -> ", row[toField]);
    indices.push_back((
       rows.get(row[fromField])
      ,cols.get(row[toField])
      ));

    if wField == "NONE" {
      values.push_back(1);
    } else {
      values.push_back(row[wField]: real);
    }
  }
  SD.bulkAdd(indices);
  for (ij, a) in zip(indices, values) {
    X(ij) = a;
  }

  const nm = new NamedMatrix(X=X);
  nm.rows = rows;
  nm.cols = cols;
  return nm;
}

/*

  :arg con: A CDO Connection to Postgres
  :arg edgeTable: The table in PG of edges
  :arg fromField: The field of edgeTable containing the id of the head vertex
  :arg toField: the field of edgeTable containing the id of the tail vertex
  :arg wField: The field of edgeTable containing the weight of the edge
  :arg n: number of distinct vertices. In practice, this may be gives and the number of names
  :arg weights: Boolean on whether to use the weights in the table or a 1 (indicator)
 */
proc wFromPG(con: Connection, edgeTable: string
    , fromField: string, toField: string, wField: string, n: int, weights=true) {
  var q = "SELECT %s, %s, %s FROM %s ORDER BY 1, 2;";
  var cursor = con.cursor();
  cursor.query(q,(fromField, toField, wField, edgeTable));
  const D: domain(2) = {1..n, 1..n};
  var SD: sparse subdomain(D) dmapped CS();
  var X: [SD] real;
  var dom1: domain(1) = {1..0};
  var dom2: domain(1) = {1..0};
  var indices: [dom1] (int, int);
  var values: [dom2] real;
  forall row in cursor {
    indices.push_back((row[fromField]: int,row[toField]: int));
    values.push_back(row[wField]: real);
  }
  SD.bulkAdd(indices);
  forall (ij, a) in zip(indices, values) {
    if weights {
      X(ij) = a;
    } else {
      X(ij) = 1;
    }
  }
  return X;
}

//SERIAL EXTRACTION FUNCTION FOR PERFORMANCE COMPARISONS
proc wFromPG_(con: Connection, edgeTable: string
    , fromField: string, toField: string, wField: string, n: int, weights=true) {
  var q = "SELECT %s, %s, %s FROM %s ORDER BY 1, 2;";
  var cursor = con.cursor();
  cursor.query(q,(fromField, toField, wField, edgeTable));
  const D: domain(2) = {1..n, 1..n};
  var SD: sparse subdomain(D) dmapped CS();
  var W: [SD] real;

  for row in cursor {
    SD += (row[fromField]: int, row[toField]:int);
    if weights {
      W[row[fromField]:int, row[toField]:int] = row[wField]: real;
    } else {
      W[row[fromField]:int, row[toField]:int] = 1;
    }
  }
   return W;
}

/*

 :arg con: A connection to a Postgres database containing a table with <ftr_id>, <vertex_name> pairs
 :arg nameTable: The name of the Postgres table containing the pairs
 :arg nameField: The name of the field in the nameTable containing the names
 :arg idField: The name of the field in the nameTable containing the feature ids

 :returns: An array of strings in order of feature id
 */
proc vNamesFromPG(con: Connection, nameTable: string
  , nameField: string, idField: string ) {

  var cursor = con.cursor();
  var q1 = "SELECT max(%s) AS n FROM %s";
  cursor.query(q1, (idField, nameTable));
  var n:int= cursor.fetchone()['n']: int;
  var vertexNames: [1..n] string;

  var q2 = "SELECT %s, %s FROM %s ORDER BY 1";
  cursor.query(q2, (idField, nameField, nameTable));
  for row in cursor {
      vertexNames[row[idField]:int ] = row[nameField];
  }
  return vertexNames;
}

/*
 Build a random sparse matrix.  Good for testing;
 */
proc generateRandomSparseMatrix(size: int, sparsity: real) {
  const D: domain(2) = {1..size, 1..size};
  var SD: sparse subdomain(D) dmapped CS();
  var R: [SD] real;
  var da: domain(1) = {1..size};
  var array: [da] int = 1..size;
  var dom: domain(1) = {1..0};
  var indices: [dom] 2*int;
  var N = floor(size*(1-sparsity)): int;
  forall i in array {
    forall j in array {
      indices.push_back((i,j));
    }
  }
  shuffle(indices);
  var sparseids = indices[1..N];
  SD.bulkAdd(sparseids);
  forall (i,j) in sparseids {
    R(i,j) = 1;
  }
  //ndices = zip(array1, array2)
  return R;
}

// BATCH PERSISTENCE
 proc persistSparseMatrix(con: Connection, aTable: string
   , fromField: string, toField: string, weightField: string
   , X:[?D] real) {
   const q = "INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s);";
   var cur = con.cursor();
   var count: int = 0;
   var dom: domain(1, int, false) = {1..0};
   var ts: [dom] (string, string, string, string, int, int, real);
   for ij in X.domain {
     ts.push_back((aTable, fromField, toField, weightField, ij(1), ij(2), X(ij)));
     count += 1;
     if count >= batchsize {
       cur.execute(q, ts);
       count = 0;
       var reset: [dom] (string, string, string, string, int, int, real);
       ts = reset;
     }
   }
   cur.execute(q,ts);
 }

//SERIAL PERSISTANCE FUNCTION FOR PERFORMANCE COMPARISONS
proc persistSparseMatrix_(con: Connection, aTable: string
  , fromField: string, toField: string, weightField: string
  , X:[?D] real) {
  const q = "INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s);";
  var cur = con.cursor();
  for ij in X.domain {
    const d: domain(1) = {1..0};
    var t: [d] (string, string, string, string, int, int, real);
    t.push_back((aTable, fromField, toField, weightField, ij(1), ij(2), X(ij)));
    cur.execute(q, t);
  }
}

// PARALLEL PERSISTENCE FUNCTION FOR PERFORMANCE COMPARISONS
proc persistSparseMatrix_P(con: Connection, aTable: string
  , fromField: string, toField: string, weightField: string
  , X:[?D] real) {
  const q = "INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s);";
  var cur = con.cursor();
  forall ij in X.domain {
    const d: domain(1) = {1..0};
    var t: [d] (string, string, string, string, int, int, real);
    t.push_back((aTable, fromField, toField, weightField, ij(1), ij(2), X(ij)));
    cur.execute(q, t);
  }
}

proc sparsity(X) {
  const d = X.shape[1]:real * X.shape[2]: real;
  var i: real = 0.0;
  for ij in X.domain {
    i += 1.0;
  }
  return i / d;
}

use Cdo,
    LinearAlgebra,
    LinearAlgebra.Sparse,
    NumSuch,
    Random;

config param batchsize = 10000;

/*
 A matrix of data with named rows and columns.
 */
class NamedMatrix {
  var D: domain(2),
      SD = CSRDomain(D),
      X: [SD] real,  // the actual data
      rows: BiMap = new BiMap(),
      cols: BiMap = new BiMap();

   proc init() {
     super.init();
   }

   proc init(X) {
     this.D = {X.domain.dim(1), X.domain.dim(2)};
     super.init();
     this.loadX(X);
   }
}

/*
Loads the data from X into the internal array, also called X.  We call them all X to keep it clear.

:arg real[]: Array representing the matrix
 */
proc NamedMatrix.loadX(X:[], shape: 2*int =(-1,-1)) {
  if shape(1) > 0 && shape(2) > 0 {
    this.D = {1..shape(1), 1..shape(2)};
  }
  for (i,j) in X.domain {
    this.SD += (i,j);
    this.X(i,j) = X(i,j);
  }
}

/*
Sets the row names for the matrix X
 */
proc NamedMatrix.setRowNames(rn:[]): string throws {
  if rn.size != X.domain.dim(1).size then throw new Error();
  for i in 1..rn.size {
    this.rows.add(rn[i]);
  }
  return this.rows;
}

/*
Sets the column names for the matrix X
*/
proc NamedMatrix.setColNames(cn:[]): string throws {
  if cn.size != X.domain.dim(2).size then throw new Error();
  for i in 1..cn.size {
    this.cols.add(cn[i]);
  }
  return this.cols;
}

/*
 Gets the value of the (i,j) entry of the matrix X in the NamedMatrix
 */
proc NamedMatrix.get(i: int, j: int) {
   return this.X(i,j);
 }

 /*
 Get the values using the named rows and columns
  */
proc NamedMatrix.get(f: string, t: string) {
    return this.X(rows.get(f), cols.get(t));
 }

/*
 Set the value of this.X(i,j) by index row
 */
proc NamedMatrix.set(i: int, j: int, w: real) {
    if this.SD.member((i,j)) {
      this.X(i,j) = w;
    } else {
      this.SD += (i,j);
      this.X(i,j) = w;
    }
    return w;
}

/*
Set the values using the row and column names
*/
proc NamedMatrix.set(f: string, t: string, w: real) {
  return this.set(rows.get(f), cols.get(t), w);
}

/*
Update the value in X(i,j) with `w`
 */
proc NamedMatrix.update(i: int, j: int, w: real) {
  const x = this.get(i,j);
  return this.set(i,j, x + w);
}

/*
Update the value in X(i,j) with `w` using the row column names
 */
proc NamedMatrix.update(f: string, t: string, w: real) {
  const x = this.get(rows.get(f),cols.get(t));
  return this.set(rows.get(f),cols.get(t), x + w);
}

/*
Find the number of non-zeroes in the matrix
 */
proc NamedMatrix.nnz() {
  var i: int = 0;
  for ij in this.X.domain {
    i += 1;
  }
  return i;
}

/*
Calculates the sparsity of the matrix: Number of entries / frame size
 */
proc NamedMatrix.sparsity() {
  const d = this.X.shape[1]:real * this.X.shape[2]: real;
  return this.nnz():real / d;
}

/*
 Multiplies the current NamedMatrix `X` against the argument `Y`, but first it aligns
 the names of `X.cols` with `Y.rows`.  Returns an appropriately named NamedMatrix
 :arg NamedMatrix Y:
 */
proc NamedMatrix.alignAndMultiply(Y: NamedMatrix) {
    var rcOverlap: domain(string) = this.cols.keys & Y.rows.keys;

    var xSD: sparse subdomain(this.D) dmapped CS(),
        ySD: sparse subdomain(Y.D) dmapped CS(),
        xX: [xSD] real,  // the actual data
        yX: [ySD] real,  // the actual data
        xrows: BiMap = new BiMap(),
        yrows: BiMap = new BiMap(),
        xcols: BiMap = new BiMap(),
        ycols: BiMap = new BiMap();

    for (left_row, right_col) in zip(this.D.dim(2), Y.D.dim(1)) {
      for rc in rcOverlap {
        const j = this.cols.get(rc);
        if this.SD.member(left_row, j) {
          xSD += (left_row, j);
          xX[left_row, j] = this.X[left_row, j];
        }
        if Y.SD.member(j, right_col) {
          ySD += (j, right_col);
          yX[j, right_col] = Y.X[j, right_col];
        }
      }
    }
    const z = xX.dot(yX);
    var n = new NamedMatrix(X=z);
    n.rows = this.rows;
    n.cols = Y.cols;
    return n;
}

/*
 Creates a NamedMatrix from a table in Postgres.  Does not optimize for square matrices.  This assumption
 is that the matrix is sparse.

 :arg string edgeTable: The SQL table holding the values of the matrix.
 :arg string fromField: The table column representing rows, e.g. `i`.
 :arg string toField: The table column representing columns, e.g. 'j'.
 :arg string wField: `default=NONE` the table column containing the values of cell `(i,j)``
 :arg boolean square: Whether the matrix should be built to have the same rows and columns
 */

 proc NamedMatrixFromPG(con: Connection
   , edgeTable: string
   , fromField: string, toField: string, wField: string = "NONE"
   , square=false) {
  if square {
    return NamedMatrixFromPGSquare(con: Connection
      , edgeTable, fromField, toField, wField);
  } else {
    return NamedMatrixFromPGRectangular(con: Connection
      , edgeTable, fromField, toField, wField);
  }
}

proc NamedMatrixFromPGRectangular(con: Connection
  , edgeTable: string
  , fromField: string, toField: string, wField: string = "NONE") {

  var q = """
  SELECT ftr, t
  FROM (
    SELECT distinct(%s) AS ftr, 'r' AS t FROM %s
    UNION ALL
    SELECT distinct(%s) AS ftr, 'c' AS t FROM %s
  ) AS a
  GROUP BY ftr, t
  ORDER BY ftr, t ;
  """;

  var rows: BiMap = new BiMap(),
      cols: BiMap = new BiMap();

  var cursor = con.cursor();
  cursor.query(q, (fromField, edgeTable, toField, edgeTable));

  forall row in cursor {
    if row['t'] == 'r' {
      rows.add(row['ftr']);
    } else if row['t'] == 'c' {
      cols.add(row['ftr']);
    }
  }

  var D: domain(2) = {1..rows.size(), 1..cols.size()},
      SD = CSRDomain(D),
      X: [SD] real;  // the actual data

  var r = """
  SELECT %s, %s
  FROM %s
  ORDER BY %s, %s ;
  """;
  var cursor2 = con.cursor();
  cursor2.query(r, (fromField, toField, edgeTable, fromField, toField));
  const size = cursor2.rowcount(): int;
  var count = 0: int,
      dom = {1..size},
      indices: [dom] (int, int),
      values: [dom] real;

  // This guy is causing problems.  Exterminiate with extreme prejudice
  //forall row in cursor2 {
  forall row in cursor2 with (+ reduce count) {
    indices[count]=(
       rows.get(row[fromField])
      ,cols.get(row[toField])
      );

    if wField == "NONE" {
      values[count] = 1;
    } else {
      values[count] = row[wField]: real;
    }
  }

  SD.bulkAdd(indices);
  forall (ij, a) in zip(indices, values) {
    X(ij) = a;
  }

  const nm = new NamedMatrix(X=X);
  nm.rows = rows;
  nm.cols = cols;
  return nm;
}

/*
 Build a square version of the matrix.  Still directed, but with the same number of rows/cols
 */
proc NamedMatrixFromPGSquare ( con: Connection
    , edgeTable: string
    , fromField: string, toField: string, wField: string = "NONE") {

    var q = """
    SELECT ftr
    FROM (
      SELECT distinct(%s) AS ftr FROM %s
      UNION ALL
      SELECT distinct(%s) AS ftr FROM %s
    ) AS a
    GROUP BY ftr ORDER BY ftr;
    """;

    var cursor = con.cursor();
    cursor.query(q, (fromField, edgeTable, toField, edgeTable));
    var rows: BiMap = new BiMap();

    forall row in cursor {
    //for row in cursor {
      rows.add(row['ftr']);
    }

    var D: domain(2) = {1..rows.size(), 1..rows.size()},
        SD = CSRDomain(D),
        X: [SD] real;  // the actual data

    var r = """
    SELECT %s, %s
    FROM %s
    ORDER BY %s, %s ;
    """;
    var cursor2 = con.cursor();
    cursor2.query(r, (fromField, toField, edgeTable, fromField, toField));
    var dom1: domain(1) = {1..0},
        dom2: domain(1) = {1..0},
        indices: [dom1] (int, int),
        values: [dom2] real;
    //forall row in cursor2 {
    for row in cursor2 {
      indices.push_back((
         rows.get(row[fromField])
        ,rows.get(row[toField])
        ));

      if wField == "NONE" {
        values.push_back(1);
      } else {
        values.push_back(row[wField]: real);
      }
    }

    SD.bulkAdd(indices);
    forall (ij, a) in zip(indices, values) {
      X(ij) = a;
    }

    const nm = new NamedMatrix(X=X);
    nm.rows = rows;
    nm.cols = rows;
    return nm;
}

proc NamedMatrixFromPG_(con: Connection
  , edgeTable: string
  , fromField: string, toField: string, wField: string = "NONE") {

  var q = """
  SELECT ftr, t, row_number() OVER(PARTITION BY t ORDER BY ftr ) AS ftr_id
  FROM (
    SELECT distinct(%s) AS ftr, 'r' AS t FROM %s
    UNION ALL
    SELECT distinct(%s) AS ftr, 'c' AS t FROM %s
  ) AS a
  GROUP BY ftr, t
  ORDER BY ftr_id, t ;
  """;

  var rows: BiMap = new BiMap(),
      cols: BiMap = new BiMap();

  var cursor = con.cursor();
  cursor.query(q, (fromField, edgeTable, toField, edgeTable));
  for row in cursor {
    if row['t'] == 'r' {
      rows.add(row['ftr'], row['ftr_id']:int);
    } else if row['t'] == 'c' {
      cols.add(row['ftr'], row['ftr_id']:int);
    }
  }

  var D: domain(2) = {1..rows.size(), 1..cols.size()},
      SD = CSRDomain(D),
      X: [SD] real;  // the actual data

  var r = """
  SELECT %s, %s
  FROM %s
  ORDER BY %s, %s ;
  """;
  var cursor2 = con.cursor();
  cursor2.query(r, (fromField, toField, edgeTable, fromField, toField));
  var dom1: domain(1) = {1..0},
      dom2: domain(1) = {1..0},
      indices: [dom1] (int, int),
      values: [dom2] real;

  //forall row in cursor2 {
  for row in cursor2 {
//    writeln("row: ", row[fromField], " -> ", row[toField]);
    indices.push_back((
       rows.get(row[fromField])
      ,cols.get(row[toField])
      ));

    if wField == "NONE" {
      values.push_back(1);
    } else {
      values.push_back(row[wField]: real);
    }
  }
  SD.bulkAdd(indices);
  for (ij, a) in zip(indices, values) {
    X(ij) = a;
  }

  const nm = new NamedMatrix(X=X);
  nm.rows = rows;
  nm.cols = cols;
  return nm;
}

/*

  :arg con: A CDO Connection to Postgres
  :arg edgeTable: The table in PG of edges
  :arg fromField: The field of edgeTable containing the id of the head vertex
  :arg toField: the field of edgeTable containing the id of the tail vertex
  :arg wField: The field of edgeTable containing the weight of the edge
  :arg n: number of distinct vertices. In practice, this may be gives and the number of names
  :arg weights: Boolean on whether to use the weights in the table or a 1 (indicator)
 */
proc wFromPG(con: Connection, edgeTable: string
    , fromField: string, toField: string, wField: string, n: int, weights=true) {
  var q = "SELECT %s, %s, %s FROM %s ORDER BY 1, 2;";
  var cursor = con.cursor();
  cursor.query(q,(fromField, toField, wField, edgeTable));
  const D: domain(2) = {1..n, 1..n};
  var SD: sparse subdomain(D) dmapped CS();
  var X: [SD] real;
  var dom1: domain(1) = {1..0};
  var dom2: domain(1) = {1..0};
  var indices: [dom1] (int, int);
  var values: [dom2] real;
  forall row in cursor {
    indices.push_back((row[fromField]: int,row[toField]: int));
    values.push_back(row[wField]: real);
  }
  SD.bulkAdd(indices);
  forall (ij, a) in zip(indices, values) {
    if weights {
      X(ij) = a;
    } else {
      X(ij) = 1;
    }
  }
  return X;
}

//SERIAL EXTRACTION FUNCTION FOR PERFORMANCE COMPARISONS
proc wFromPG_(con: Connection, edgeTable: string
    , fromField: string, toField: string, wField: string, n: int, weights=true) {
  var q = "SELECT %s, %s, %s FROM %s ORDER BY 1, 2;";
  var cursor = con.cursor();
  cursor.query(q,(fromField, toField, wField, edgeTable));
  const D: domain(2) = {1..n, 1..n};
  var SD: sparse subdomain(D) dmapped CS();
  var W: [SD] real;

  for row in cursor {
    SD += (row[fromField]: int, row[toField]:int);
    if weights {
      W[row[fromField]:int, row[toField]:int] = row[wField]: real;
    } else {
      W[row[fromField]:int, row[toField]:int] = 1;
    }
  }
   return W;
}

/*

 :arg con: A connection to a Postgres database containing a table with <ftr_id>, <vertex_name> pairs
 :arg nameTable: The name of the Postgres table containing the pairs
 :arg nameField: The name of the field in the nameTable containing the names
 :arg idField: The name of the field in the nameTable containing the feature ids

 :returns: An array of strings in order of feature id
 */
proc vNamesFromPG(con: Connection, nameTable: string
  , nameField: string, idField: string ) {

  var cursor = con.cursor();
  var q1 = "SELECT max(%s) AS n FROM %s";
  cursor.query(q1, (idField, nameTable));
  var n:int= cursor.fetchone()['n']: int;
  var vertexNames: [1..n] string;

  var q2 = "SELECT %s, %s FROM %s ORDER BY 1";
  cursor.query(q2, (idField, nameField, nameTable));
  for row in cursor {
      vertexNames[row[idField]:int ] = row[nameField];
  }
  return vertexNames;
}

/*
 Build a random sparse matrix.  Good for testing;
 */
proc generateRandomSparseMatrix(size: int, sparsity: real) {
  const D: domain(2) = {1..size, 1..size};
  var SD: sparse subdomain(D) dmapped CS();
  var R: [SD] real;
  var da: domain(1) = {1..size};
  var array: [da] int = 1..size;
  var dom: domain(1) = {1..0};
  var indices: [dom] 2*int;
  var N = floor(size*(1-sparsity)): int;
  forall i in array {
    forall j in array {
      indices.push_back((i,j));
    }
  }
  shuffle(indices);
  var sparseids = indices[1..N];
  SD.bulkAdd(sparseids);
  forall (i,j) in sparseids {
    R(i,j) = 1;
  }
  //ndices = zip(array1, array2)
  return R;
}

// BATCH PERSISTENCE
 proc persistSparseMatrix(con: Connection, aTable: string
   , fromField: string, toField: string, weightField: string
   , X:[?D] real) {
   const q = "INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s);";
   var cur = con.cursor();
   var count: int = 0;
   var dom: domain(1, int, false) = {1..0};
   var ts: [dom] (string, string, string, string, int, int, real);
   for ij in X.domain {
     ts.push_back((aTable, fromField, toField, weightField, ij(1), ij(2), X(ij)));
     count += 1;
     if count >= batchsize {
       cur.execute(q, ts);
       count = 0;
       var reset: [dom] (string, string, string, string, int, int, real);
       ts = reset;
     }
   }
   cur.execute(q,ts);
 }

//SERIAL PERSISTANCE FUNCTION FOR PERFORMANCE COMPARISONS
proc persistSparseMatrix_(con: Connection, aTable: string
  , fromField: string, toField: string, weightField: string
  , X:[?D] real) {
  const q = "INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s);";
  var cur = con.cursor();
  for ij in X.domain {
    const d: domain(1) = {1..0};
    var t: [d] (string, string, string, string, int, int, real);
    t.push_back((aTable, fromField, toField, weightField, ij(1), ij(2), X(ij)));
    cur.execute(q, t);
  }
}

// PARALLEL PERSISTENCE FUNCTION FOR PERFORMANCE COMPARISONS
proc persistSparseMatrix_P(con: Connection, aTable: string
  , fromField: string, toField: string, weightField: string
  , X:[?D] real) {
  const q = "INSERT INTO %s (%s, %s, %s) VALUES (%s, %s, %s);";
  var cur = con.cursor();
  forall ij in X.domain {
    const d: domain(1) = {1..0};
    var t: [d] (string, string, string, string, int, int, real);
    t.push_back((aTable, fromField, toField, weightField, ij(1), ij(2), X(ij)));
    cur.execute(q, t);
  }
}

proc sparsity(X) {
  const d = X.shape[1]:real * X.shape[2]: real;
  var i: real = 0.0;
  for ij in X.domain {
    i += 1.0;
  }
  return i / d;
}


Forgot to adjust for new readline arguments

use IO;
use AdvancedIters;

extern proc memcpy(x : [], b, len:int);

config const tableSize = 1 << 16;
config const lineSize = 61;

var tonum : [1..128] int;
tonum[0x41] = 0; // A
tonum[0x43] = 1; // C
tonum[0x54] = 2; // T
tonum[0x47] = 3; // G

var tochar : [0..3] string;
tochar[0] = "A";
tochar[1] = "C";
tochar[2] = "T";
tochar[3] = "G";

inline proc hash(str : [] uint(8), beg:int, sizeRange:range(?) ) {
  var data : uint = 0;
  for i in sizeRange {
    data <<= 2;
    data |= tonum[str[beg+i]];
  }
  return data;
}

proc decode(data : uint, size : int) {
  var ret : string;
  var d = data;
  for i in 1..size {
    ret = tochar[(d & 3) : uint(8)] + ret;
    d >>= 2;
  }
  return ret;
}

proc calculate(data : [] uint(8), size : int) {
  var freqDom : domain(uint);
  var freqs : [freqDom] int;

  const ntasks = defaultNumTasks(0);
  var lock : sync bool;
  lock = true;
  const sizeRange = 0..size-1;
  coforall tid in 1..ntasks {
    var curDom : domain(uint);
    var curArr : [curDom] int;
    for i in tid .. data.size-size by ntasks {
      curArr[hash(data, i, sizeRange)] += 1;
    }
    lock;
    for (k,v) in zip(curDom, curArr) do freqs[k] += v;
    lock = true;
  }

  return freqs;
}

proc write_frequencies(data : [] uint(8), size : int) {
  var sum = data.size  - size;
  var freqs = calculate(data, size);

  // sort by frequencies
  var arr : [1..freqs.size] (int, uint);
  for (a, k, v) in zip(arr, freqs.domain, freqs) do
    a = (-v,k); // '-' for descending order
  QuickSort(arr);

  for (c, e) in arr {
    writef("%s %.3dr\n", decode(e, size), (100.0 * -c) / sum);
  }
}

proc write_count(data : [] uint(8), str : string) {
  var freqs = calculate(data, str.length);
  var d = hash(str.toBytes(), 1, 0..str.length-1);
  writeln(freqs[d], "\t", decode(d, str.length));
}

proc string.toBytes() var {
   var b : [1..this.length] uint(8);
   memcpy(b, this, this.length);
   return b;
}

inline proc startsWithThree(data : []) {
  return data[1] == 0x3E && data[2] == 0x54 && data[3] == 0x48;
}

proc main() {
  // Open stdin and a binary reader channel
  const inFile = openfd(0);
  const fileLen = inFile.length();
  var myin = inFile.reader(kind=ionative,locking=false);

  // Read line-by-line until we see a line beginning with '>TH'
  var tempdata : [1..lineSize] uint(8);
  var numRead = 0;
  var total = 0;
  while myin.readline(tempdata, numRead) && !startsWithThree(tempdata) { total += numRead; }

  // Read in the rest of the file
  var dataDom = {1..fileLen-total};
  var data : [dataDom] uint(8);
  var idx = 1;
  while myin.readline(data, numRead, idx, inclusive=false) { idx += numRead; }

  // Resize our array to the amount actually read
  dataDom = {1..idx};

  // Make everything uppercase
  forall d in data do d ^= 0x20;

  write_frequencies(data, 1);
  writeln();
  write_frequencies(data, 2);
  writeln();
  write_count(data, "GGT");
  write_count(data, "GGTA");
  write_count(data, "GGTATT");
  write_count(data, "GGTATTTTAATT");
  write_count(data, "GGTATTTTAATTTATAGT");
}

use IO;
use AdvancedIters;

extern proc memcpy(x : [], b, len:int);

config const tableSize = 1 << 16;
config const lineSize = 61;

var tonum : [1..128] int;
tonum[0x41] = 0; // A
tonum[0x43] = 1; // C
tonum[0x54] = 2; // T
tonum[0x47] = 3; // G

var tochar : [0..3] string;
tochar[0] = "A";
tochar[1] = "C";
tochar[2] = "T";
tochar[3] = "G";

inline proc hash(str : [] uint(8), beg:int, sizeRange:range(?) ) {
  var data : uint = 0;
  for i in sizeRange {
    data <<= 2;
    data |= tonum[str[beg+i]];
  }
  return data;
}

proc decode(data : uint, size : int) {
  var ret : string;
  var d = data;
  for i in 1..size {
    ret = tochar[(d & 3) : uint(8)] + ret;
    d >>= 2;
  }
  return ret;
}

proc calculate(data : [] uint(8), size : int) {
  var freqDom : domain(uint);
  var freqs : [freqDom] int;

  const ntasks = defaultNumTasks(0);
  var lock : sync bool;
  lock = true;
  const sizeRange = 0..size-1;
  coforall tid in 1..ntasks {
    var curDom : domain(uint);
    var curArr : [curDom] int;
    for i in tid .. data.size-size by ntasks {
      curArr[hash(data, i, sizeRange)] += 1;
    }
    lock;
    for (k,v) in zip(curDom, curArr) do freqs[k] += v;
    lock = true;
  }

  return freqs;
}

proc write_frequencies(data : [] uint(8), size : int) {
  var sum = data.size  - size;
  var freqs = calculate(data, size);

  // sort by frequencies
  var arr : [1..freqs.size] (int, uint);
  for (a, k, v) in zip(arr, freqs.domain, freqs) do
    a = (-v,k); // '-' for descending order
  QuickSort(arr);

  for (c, e) in arr {
    writef("%s %.3dr\n", decode(e, size), (100.0 * -c) / sum);
  }
}

proc write_count(data : [] uint(8), str : string) {
  var freqs = calculate(data, str.length);
  var d = hash(str.toBytes(), 1, 0..str.length-1);
  writeln(freqs[d], "\t", decode(d, str.length));
}

proc string.toBytes() var {
   var b : [1..this.length] uint(8);
   memcpy(b, this, this.length);
   return b;
}

inline proc startsWithThree(data : []) {
  return data[1] == 0x3E && data[2] == 0x54 && data[3] == 0x48;
}

proc main() {
  // Open stdin and a binary reader channel
  const inFile = openfd(0);
  const fileLen = inFile.length();
  var myin = inFile.reader(kind=ionative,locking=false);

  // Read line-by-line until we see a line beginning with '>TH'
  var tempdata : [1..lineSize] uint(8);
  var numRead = 0;
  var total = 0;
  while myin.readline(tempdata, numRead) && !startsWithThree(tempdata) { total += numRead; }

  // Read in the rest of the file
  var dataDom = {1..fileLen-total};
  var data : [dataDom] uint(8);
  var idx = 1;
  while myin.readline(data, numRead, idx, false) { idx += numRead; }

  // Resize our array to the amount actually read
  dataDom = {1..idx};

  // Make everything uppercase
  forall d in data do d ^= 0x20;

  write_frequencies(data, 1);
  writeln();
  write_frequencies(data, 2);
  writeln();
  write_count(data, "GGT");
  write_count(data, "GGTA");
  write_count(data, "GGTATT");
  write_count(data, "GGTATTTTAATT");
  write_count(data, "GGTATTTTAATTTATAGT");
}


Really fix FP representation issues with basicUpdate.chpl.

I must have had a major brain fart when I updated this test last week
(I'll blame it on the stress of the release).  This should make this
test pass consistently.

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@19998 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

config const n = 100;
config type myType = real;

var T: [1..n] real;
[i in 1..n] T[i] = if _isIntegralType(myType) then i else i*0.5;

proc foo(type myType) {
  var A = T:myType;
  var x: sync myType = 0:myType;
  forall i in 1..n do
    x += A[i];
  var sum: myType;
  sum = + reduce A;
  if x.readXX() != sum then
    writeln("ERROR: sums do not match for ", typeToString(myType),
            " (should be ", sum, ", result is ", x.readXX(), ")");
}

foo(int(32));
foo(int(64));
foo(uint(32));
foo(uint(64));
foo(real(32));
foo(real(64));

config const n = 100;
config type myType = real;

var T: [1..n] real;
[i in 1..n] T[i] = i*1.01;

proc foo(type myType) {
  var A = T:myType;
  var x: sync myType = 0:myType;
  forall i in 1..n do
    x += A[i];
  var sum = + reduce A;
  if x.readXX() != sum then
    writeln("ERROR: sums do not match for ", typeToString(myType),
            " (should be ", sum, ", result is ", x.readXX(), ")");
}

foo(int(32));
foo(int(64));
foo(uint(32));
foo(uint(64));
foo(real(32));
foo(real(64));


Cast histoSerial iterator indices so they don't overflow the iterator

The iterator attempts to do something like "for 0:uint..length:uint by -1" and
so the last iteration will result in overflow since 0 -1 for uints will
overflow. This just casts the uint32 to int64s to prevent that

config var numIterations = 1;
config const printTimers = 0;

proc main(args: [] string) {
  use Time;
  var inputFilename: string;
  var outputFilename: string;
  var inputTime, outputTime, computeTime, totalTime: Timer;
  if args.numElements == 4 {
    inputFilename = args[1];
    outputFilename = args[2];
    numIterations = args[3]:int;
  } else {
    writeln("num args: ", args.numElements);
    writeln(args);
    halt("Expected 4 args: a.out infile, outfile, numIterations");
  }

  var imgWidth, imgHeight, histoWidth, histoHeight: uint(32);
  totalTime.start();
  inputTime.start();
  var f = open(inputFilename, iomode.r);
  var r = f.reader(kind=ionative);

  r.read(imgWidth);
  r.read(imgHeight);
  r.read(histoWidth);
  r.read(histoHeight);

  var img: [0..#imgWidth*imgHeight] uint(32);
  var histo: [0..#histoWidth*histoHeight] uint(8);

  r.read(img);
  r.close();
  f.close();
  inputTime.stop();

  computeTime.start();
  for it in 0..#numIterations {
    histo = 0;
    const maxUint8 = max(uint(8));
    for val in img {
      if histo[val] < maxUint8 then histo[val] += 1;
    }
  }
  computeTime.stop();

  outputTime.start();
  dumpHisto(histo, histoHeight, histoWidth, outputFilename);
  outputTime.stop();
  totalTime.stop();
  if printTimers >= 3 {
    writeln("IO: ", inputTime.elapsed() + outputTime.elapsed());
    writeln(" -Input: ", inputTime.elapsed());
    writeln(" -Output: ", outputTime.elapsed());
  }
  if printTimers >= 2 then writeln("Compute: ", computeTime.elapsed());
  if printTimers >= 1 then writeln("Timer Wall Time: ", totalTime.elapsed());
}

record RGB {
  var B, G, R: uint(8);
}

proc HSVtoRGB(h: real, s: real, v: real) {
  var value: RGB;

  if s == 0 {
    return value;
  }

  var hh = h / 60;
  var i:int = floor(hh):int;
  var f = hh - i;
  var p = v*(1-s);
  var q = v*(1-s*f);
  var t = v*(1-s*(1-f));
  var r,g,b: real;
  select i {
    when 0 { r = v; g = t; b = p; }
    when 1 { r = q; g = v; b = p; }
    when 2 { r = p; g = v; b = t; }
    when 3 { r = p; g = q; b = v; }
    when 4 { r = t; g = p; b = v; }
    otherwise { r = v; g = p; b = q; }
  }
  value.R = (r*255):uint(8);
  value.G = (g*255):uint(8);
  value.B = (b*255):uint(8);

  return value;
}

proc dumpHisto(histo: [] uint(8), height: uint(32), width: uint(32), outFilename: string) {
  var pixMap: [0..#height*width] RGB;
  for y in 0..#height {
    for x in 0..#width {
      const pos = y*width + x;
      var value = histo[pos];
      if value == 0 then
        (pixMap[pos].R, pixMap[pos].G, pixMap[pos].B) = (0:uint(8),0:uint(8),0:uint(8));
      else
        pixMap[pos] = HSVtoRGB(0.0, 1.0, cbrt(1+63.0*value/max(uint(8)))/4);
    }
  }
  createBMP(pixMap, height, width, outFilename);
}

record bmpFileHeader {
  var filesz: uint(32);
  var creator1: uint(16);
  var creator2: uint(16);
  var bmpOffset: uint(32);
}

record bmpDibHeader {
  var headerSz: uint(32);
  var width: uint(32);
  var height: uint(32);
  var nplanes: uint(16);
  var bitspp: uint(16);
  var compressType: uint(32);
  var bmpByteSz: uint(32);
  var hres: uint(32);
  var vres: uint(32);
  var ncolors: uint(32);
  var nimpcolors: uint(32);
}

record bmpImage {
  var magic: 2*uint(8);
  var fileHeader: bmpFileHeader;
  var dibHeader: bmpDibHeader;
}

extern proc sizeof(type T): uint(32);

proc createBMP(bitmap:[] RGB, height: uint(32), width: uint(32), filename: string) {
  var image: bmpImage;
  var paddedWidth = 4*(((width*24)+31)/32) - width*sizeof(RGB);
  var pad: [0..#paddedWidth] uint(8);

  image.magic(1) = ascii('B');
  image.magic(2) = ascii('M');

  image.fileHeader.filesz = 2 + sizeof(bmpFileHeader) + sizeof(bmpDibHeader) + height*width*sizeof(RGB);
  image.fileHeader.creator1 = 0;
  image.fileHeader.creator2 = 0;
  image.fileHeader.bmpOffset = 2 + sizeof(bmpFileHeader) + sizeof(bmpDibHeader);

  image.dibHeader.headerSz = sizeof(bmpDibHeader);
  image.dibHeader.width = width;
  image.dibHeader.height = height;
  image.dibHeader.nplanes = 1;
  image.dibHeader.bitspp = 24;
  image.dibHeader.compressType = 0;
  image.dibHeader.bmpByteSz = width*height*sizeof(RGB);
  image.dibHeader.hres = 0;
  image.dibHeader.vres = 0;
  image.dibHeader.ncolors = 0;
  image.dibHeader.nimpcolors = 0;

  var f = open(filename, iomode.cw);
  var w = f.writer(kind=ionative);

  w.write(image.magic(1));
  w.write(image.magic(2));
  w.write(image.fileHeader);
  w.write(image.dibHeader);

  for h in 0:int..(height-1):int by -1 {
    w.write(bitmap[h*width..#width]);
    w.write(pad);
  }

  w.close();
  f.close();
}

config var numIterations = 1;
config const printTimers = 0;

proc main(args: [] string) {
  use Time;
  var inputFilename: string;
  var outputFilename: string;
  var inputTime, outputTime, computeTime, totalTime: Timer;
  if args.numElements == 4 {
    inputFilename = args[1];
    outputFilename = args[2];
    numIterations = args[3]:int;
  } else {
    writeln("num args: ", args.numElements);
    writeln(args);
    halt("Expected 4 args: a.out infile, outfile, numIterations");
  }

  var imgWidth, imgHeight, histoWidth, histoHeight: uint(32);
  totalTime.start();
  inputTime.start();
  var f = open(inputFilename, iomode.r);
  var r = f.reader(kind=ionative);

  r.read(imgWidth);
  r.read(imgHeight);
  r.read(histoWidth);
  r.read(histoHeight);

  var img: [0..#imgWidth*imgHeight] uint(32);
  var histo: [0..#histoWidth*histoHeight] uint(8);

  r.read(img);
  r.close();
  f.close();
  inputTime.stop();

  computeTime.start();
  for it in 0..#numIterations {
    histo = 0;
    const maxUint8 = max(uint(8));
    for val in img {
      if histo[val] < maxUint8 then histo[val] += 1;
    }
  }
  computeTime.stop();

  outputTime.start();
  dumpHisto(histo, histoHeight, histoWidth, outputFilename);
  outputTime.stop();
  totalTime.stop();
  if printTimers >= 3 {
    writeln("IO: ", inputTime.elapsed() + outputTime.elapsed());
    writeln(" -Input: ", inputTime.elapsed());
    writeln(" -Output: ", outputTime.elapsed());
  }
  if printTimers >= 2 then writeln("Compute: ", computeTime.elapsed());
  if printTimers >= 1 then writeln("Timer Wall Time: ", totalTime.elapsed());
}

record RGB {
  var B, G, R: uint(8);
}

proc HSVtoRGB(h: real, s: real, v: real) {
  var value: RGB;

  if s == 0 {
    return value;
  }

  var hh = h / 60;
  var i:int = floor(hh):int;
  var f = hh - i;
  var p = v*(1-s);
  var q = v*(1-s*f);
  var t = v*(1-s*(1-f));
  var r,g,b: real;
  select i {
    when 0 { r = v; g = t; b = p; }
    when 1 { r = q; g = v; b = p; }
    when 2 { r = p; g = v; b = t; }
    when 3 { r = p; g = q; b = v; }
    when 4 { r = t; g = p; b = v; }
    otherwise { r = v; g = p; b = q; }
  }
  value.R = (r*255):uint(8);
  value.G = (g*255):uint(8);
  value.B = (b*255):uint(8);

  return value;
}

proc dumpHisto(histo: [] uint(8), height: uint(32), width: uint(32), outFilename: string) {
  var pixMap: [0..#height*width] RGB;
  for y in 0..#height {
    for x in 0..#width {
      const pos = y*width + x;
      var value = histo[pos];
      if value == 0 then
        (pixMap[pos].R, pixMap[pos].G, pixMap[pos].B) = (0:uint(8),0:uint(8),0:uint(8));
      else
        pixMap[pos] = HSVtoRGB(0.0, 1.0, cbrt(1+63.0*value/max(uint(8)))/4);
    }
  }
  createBMP(pixMap, height, width, outFilename);
}

record bmpFileHeader {
  var filesz: uint(32);
  var creator1: uint(16);
  var creator2: uint(16);
  var bmpOffset: uint(32);
}

record bmpDibHeader {
  var headerSz: uint(32);
  var width: uint(32);
  var height: uint(32);
  var nplanes: uint(16);
  var bitspp: uint(16);
  var compressType: uint(32);
  var bmpByteSz: uint(32);
  var hres: uint(32);
  var vres: uint(32);
  var ncolors: uint(32);
  var nimpcolors: uint(32);
}

record bmpImage {
  var magic: 2*uint(8);
  var fileHeader: bmpFileHeader;
  var dibHeader: bmpDibHeader;
}

extern proc sizeof(type T): uint(32);

proc createBMP(bitmap:[] RGB, height: uint(32), width: uint(32), filename: string) {
  var image: bmpImage;
  var paddedWidth = 4*(((width*24)+31)/32) - width*sizeof(RGB);
  var pad: [0..#paddedWidth] uint(8);

  image.magic(1) = ascii('B');
  image.magic(2) = ascii('M');

  image.fileHeader.filesz = 2 + sizeof(bmpFileHeader) + sizeof(bmpDibHeader) + height*width*sizeof(RGB);
  image.fileHeader.creator1 = 0;
  image.fileHeader.creator2 = 0;
  image.fileHeader.bmpOffset = 2 + sizeof(bmpFileHeader) + sizeof(bmpDibHeader);

  image.dibHeader.headerSz = sizeof(bmpDibHeader);
  image.dibHeader.width = width;
  image.dibHeader.height = height;
  image.dibHeader.nplanes = 1;
  image.dibHeader.bitspp = 24;
  image.dibHeader.compressType = 0;
  image.dibHeader.bmpByteSz = width*height*sizeof(RGB);
  image.dibHeader.hres = 0;
  image.dibHeader.vres = 0;
  image.dibHeader.ncolors = 0;
  image.dibHeader.nimpcolors = 0;

  var f = open(filename, iomode.cw);
  var w = f.writer(kind=ionative);

  w.write(image.magic(1));
  w.write(image.magic(2));
  w.write(image.fileHeader);
  w.write(image.dibHeader);

  for h in 0..height-1 by -1 {
    w.write(bitmap[h*width..#width]);
    w.write(pad);
  }

  w.close();
  f.close();
}


delete worers after indexing

delete worers after indexing


Remove on clauses wrapping the deletion of listNodes.

These on clauses are unnecessary since r19694.

We noticed this while Brad was reviewing my change for replacing the
sync var ref counts.

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@19816 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

class listNode {
  type eltType;
  var data: eltType;
  var next: listNode(eltType);
}

record list {
  type eltType;
  var first: listNode(eltType);
  var last: listNode(eltType);
  var length: int;

  proc destroy() {
    var current = first;
    while (current != nil) {
      var next = current.next;
      delete current;
      current = next;
    }
  }

  proc remove(x: eltType) {
    var tmp = first,
        prev: first.type = nil;
    while tmp != nil && tmp.data != x {
      prev = tmp;
      tmp = tmp.next;
    }
    if tmp != nil {
      if prev != nil then
        prev.next = tmp.next;
      if first == tmp then
        first = tmp.next;
      if last == tmp then
        last = prev;
      delete tmp;
      length -= 1;
    }
  }

  iter these() {
    var tmp = first;
    while tmp != nil {
      yield tmp.data;
      tmp = tmp.next;
    }
  }

  proc append(e : eltType) {
    if last {
      last.next = new listNode(eltType, e);
      last = last.next;
    } else {
      first = new listNode(eltType, e);
      last = first;
    }
    length += 1;
  }

  proc append(e: eltType, es: eltType ...?k) {
    append(e);
    for param i in 1..k do
      append(es(i));
  }

  proc prepend(e : eltType) {
    first = new listNode(eltType, e, first);
    if last == nil then
      last = first;
    length += 1;
  }

  proc concat(l: list(eltType)) {
    for e in l do
      append(e);
  }

  proc writeThis(f: Writer) {
    var first: bool = true;
    for e in this {
      if !first then
        f.write(" ");
      else
        first = false;
      f.write(e);
    }
  }
}

proc makeList(x ...?k) {
  var s: list(x(1).type);
  for param i in 1..k do
    s.append(x(i));
  return s;
}

class listNode {
  type eltType;
  var data: eltType;
  var next: listNode(eltType);
}

record list {
  type eltType;
  var first: listNode(eltType);
  var last: listNode(eltType);
  var length: int;

  proc destroy() {
    var current = first;
    while (current != nil) {
      var next = current.next;
      on current do delete current;
      current = next;
    }
  }

  proc remove(x: eltType) {
    var tmp = first,
        prev: first.type = nil;
    while tmp != nil && tmp.data != x {
      prev = tmp;
      tmp = tmp.next;
    }
    if tmp != nil {
      if prev != nil then
        prev.next = tmp.next;
      if first == tmp then
        first = tmp.next;
      if last == tmp then
        last = prev;
      on tmp do delete tmp;
      length -= 1;
    }
  }

  iter these() {
    var tmp = first;
    while tmp != nil {
      yield tmp.data;
      tmp = tmp.next;
    }
  }

  proc append(e : eltType) {
    if last {
      last.next = new listNode(eltType, e);
      last = last.next;
    } else {
      first = new listNode(eltType, e);
      last = first;
    }
    length += 1;
  }

  proc append(e: eltType, es: eltType ...?k) {
    append(e);
    for param i in 1..k do
      append(es(i));
  }

  proc prepend(e : eltType) {
    first = new listNode(eltType, e, first);
    if last == nil then
      last = first;
    length += 1;
  }

  proc concat(l: list(eltType)) {
    for e in l do
      append(e);
  }

  proc writeThis(f: Writer) {
    var first: bool = true;
    for e in this {
      if !first then
        f.write(" ");
      else
        first = false;
      f.write(e);
    }
  }
}

proc makeList(x ...?k) {
  var s: list(x(1).type);
  for param i in 1..k do
    s.append(x(i));
  return s;
}


Fix a race in Cyclic array and domain leader iterators.

The variable splitRanges was incorrectly getting used by multiple threads when
using more than one task per locale. Moved the definition of splitRanges into
the coforall loop so each task gets its own copy instead of all writing to the
same copy.

This should fix the random failures of testCyclicDist.chpl.

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@16444 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

//
// naive routine for dividing numLocales into rank factors
//
def _factor(param rank: int, value) {
  var factors: rank*int;
  for param i in 1..rank do
    factors(i) = 1;
  if value >= 1 {
    var iv = value;
    var factor = 1;
    while iv > 1 {
      for i in 2..iv {
        if iv % i == 0 {
          var j = 1;
          for i in 2..rank {
            if factors(i) < factors(j) then
              j = i;
          }
          factors(j) *= i;
          iv = iv / i;
          break;
        }
      }
    }
  }
  for i in 1..rank do
    for j in i+1..rank do
      if factors(i) < factors(j) then
        factors(i) <=> factors(j);
  return factors;
}

def _tupleOfZero(param size: int, type t) {
  var tmp: size * t;
  return tmp;
}

config param debugCyclicDist = false;
config param verboseCyclicDistWriters = false;

class Cyclic: BaseDist {
  param rank: int = 1;
  type idxType = int(64);

  const lowIdx: rank*idxType;
  const targetLocDom: domain(rank);
  const targetLocs: [targetLocDom] locale;

  const locDist: [targetLocDom] LocCyclic(rank, idxType);

  const tasksPerLocale = 1;
  var pid: int = -1;

  def Cyclic(param rank: int,
             type idxType = int(64),
             low: rank*idxType = _tupleOfZero(rank, idxType),
             targetLocales: [] locale = thisRealm.Locales,
             tasksPerLocale=0) {
    if rank == 1  {
      targetLocDom = [0..#targetLocales.numElements];
      targetLocs = targetLocales;
    } else if targetLocales.rank == 1 {
      const factors = _factor(rank, targetLocales.numElements);
      var ranges: rank*range;
      for param i in 1..rank {
        ranges(i) = 0..#factors(i);
      }
      targetLocDom = [(...ranges)];
      for (loc1, loc2) in (targetLocs, targetLocales) {
        loc1 = loc2;
      }
    } else {
      if targetLocales.rank != rank then
        compilerError("locales array rank must be one or match distribution rank");
      var ranges: rank*range;
      for param i in 1..rank do {
        var thisRange = targetLocales.domain.dim(i);
        ranges(i) = 0..#thisRange.length;
      }
      targetLocDom = [(...ranges)];
      targetLocs = reshape(targetLocales, targetLocDom);
    }

    for param i in 1..rank do
      lowIdx(i) = mod(low(i), targetLocDom.dim(i).length);

    if tasksPerLocale == 0 then
      this.tasksPerLocale = min reduce targetLocales.numCores;
    else
      this.tasksPerLocale = tasksPerLocale;
    coforall locid in targetLocDom {
      on targetLocs(locid) {
        locDist(locid) = new LocCyclic(rank, idxType, locid, this);
      }
    }
    if debugCyclicDist then
      for loc in locDist do writeln(loc);
  }

  def Cyclic(param rank, type idxType, other: Cyclic(rank, idxType)) {
    targetLocDom = other.targetLocDom;
    targetLocs = other.targetLocs;
    lowIdx = other.lowIdx;
    locDist = other.locDist;
    tasksPerLocale = other.tasksPerLocale;
  }

  def clone() return new Cyclic(rank=rank, idxType=idxType, other=this);
}

def Cyclic.getChunk(inds, locid) {
  var sliceBy: rank*range(eltType=idxType, stridable=true);
  var locidtup: rank*int;
  if rank == 1 then
    locidtup(1) = locid;
  else
    locidtup = locid;
  for param i in 1..rank {
    var distStride = targetLocDom.dim(i).length;
    var loclowidx = mod(lowIdx(i) + locidtup(i), distStride);
    var lowmod = mod(inds.dim(i).low, distStride);
    var offset = loclowidx - lowmod;
    if offset < 0 then
      sliceBy(i) = inds.dim(i).low + (distStride + offset)..inds.dim(i).high by distStride;
    else
      sliceBy(i) = inds.dim(i).low + offset..inds.dim(i).high by distStride;
  }
  return inds((...sliceBy));
  //
  // WANT:
  //var distWhole = locDist(locid).myChunk.getIndices();
  //return inds((...distWhole));
}

def Cyclic.supportsPrivatization() param return true;

def Cyclic.getPrivatizeData() return 0;

def Cyclic.privatize(privatizeData) {
  return new Cyclic(rank=rank, idxType=idxType, other=this);
}

def Cyclic.getReprivatizeData() return 0;

def Cyclic.reprivatize(other, reprivatizeData) {
  targetLocDom = other.targetLocDom;
  targetLocs = other.targetLocs;
  locDist = other.locDist;
  lowIdx = other.lowIdx;
  tasksPerLocale = other.tasksPerLocale;
}

def Cyclic.newArithmeticDom(param rank: int, type idxType, param stridable: bool) {
  if idxType != this.idxType then
    compilerError("Cyclic domain index type does not match distribution's");
  if rank != this.rank then
    compilerError("Cyclic domain rank does not match distribution's");
  var dom = new CyclicDom(rank=rank, idxType=idxType, dist = this, stridable=stridable);
  dom.setup();
  return dom;
}

def Cyclic.writeThis(x: Writer) {
  x.writeln(typeToString(this.type));
  x.writeln("------");
  for locid in targetLocDom do
    x.writeln(" [", locid, "=", targetLocs(locid), "] owns chunk: ", locDist(locid).myChunk);
}

def Cyclic.ind2locInd(i: idxType) {
  const numLocs:idxType = targetLocDom.numIndices:idxType;
  // this is wrong if i is less than lowIdx
  //return ((i - lowIdx(1)) % numLocs):int;
  // this works even if i is less than lowIdx
  return mod(mod(i, numLocs) - mod(lowIdx(1), numLocs), numLocs):int;
}

def Cyclic.ind2locInd(ind: rank*idxType) {
  var x: rank*int;
  for param i in 1..rank {
    var dimLen = targetLocDom.dim(i).length;
    //x(i) = ((ind(i) - lowIdx(i)) % dimLen):int;
    x(i) = mod(mod(ind(i), dimLen) - mod(lowIdx(i), dimLen), dimLen):int;
  }
  if rank == 1 then
    return x(1);
  else
    return x;
}

def Cyclic.ind2loc(i: idxType) where rank == 1 {
  return targetLocs(ind2locInd(i));
}

def Cyclic.ind2loc(i: rank*idxType) {
  return targetLocs(ind2locInd(i));
}

class LocCyclic {
  param rank: int;
  type idxType;

  const myChunk: domain(rank, idxType, true);

  def LocCyclic(param rank, type idxType, locid, dist: Cyclic(rank, idxType)) {
    var locidx: rank*int;
    var lowIdx = dist.lowIdx;

    if rank == 1 then
      locidx(1) = locid;
    else
      locidx = locid;

    var tuple: rank*range(idxType, stridable=true);

    for param i in 1..rank {
      const lower = min(idxType)..(lowIdx(i)+locidx(i)) by -dist.targetLocDom.dim(i).length;
      const upper = (lowIdx(i) + locidx(i))..max(idxType) by dist.targetLocDom.dim(i).length;
      const lo = lower.low, hi = upper.high;
      tuple(i) = lo..hi by dist.targetLocDom.dim(i).length;
    }
    myChunk = [(...tuple)];
  }
}

class CyclicDom : BaseArithmeticDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const dist: Cyclic(rank, idxType);

  var locDoms: [dist.targetLocDom] LocCyclicDom(rank, idxType, stridable);

  const whole: domain(rank, idxType, stridable);

  var pid: int = -1;
}

def CyclicDom.setup() {
  coforall localeIdx in dist.targetLocDom {
    on dist.targetLocs(localeIdx) {
      locDoms(localeIdx) = new LocCyclicDom(rank, idxType, stridable, this,
                                            dist.getChunk(whole, localeIdx));
    }
  }
}

def CyclicDom.buildArray(type eltType) {
  var arr = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=this);
  arr.setup();
  return arr;
}

def CyclicDom.getIndices() {
  return whole.getIndices();
}

def CyclicDom.setIndices(x) {
  whole.setIndices(x);
  setup();
}

def CyclicDom.writeThis(x: Writer) {
  if verboseCyclicDistWriters {
    writeln(typeToString(this.type));
    writeln("------");
    for loc in dist.targetLocDom {
      x.writeln("[", loc, "=", dist.targetLocs(loc), "] owns ", locDoms(loc).myBlock);
    }
  } else {
    x.write(whole);
  }
}

def CyclicDom.numIndices return whole.numIndices;

def CyclicDom.these() {
  for i in whole do
    yield i;
}

def CyclicDom.these(param tag: iterator) where tag == iterator.leader {
  const wholeLow = whole.low,
        precomputedNumTasks = dist.tasksPerLocale;
  coforall locDom in locDoms do on locDom {
    const numTasks = precomputedNumTasks;

    var result: rank*range(eltType=idxType, stridable=true);
    var zeroedLocalPart = whole((...locDom.myBlock.getIndices())) - whole.low;
    for param i in 1..rank {
      var dim = zeroedLocalPart.dim(i);
      var wholestride = whole.dim(i).stride;
      if dim.high >= dim.low then
        result(i) = (dim.low / wholestride)..(dim.high / wholestride) by (dim.stride / wholestride);
      else
        result(i) = 1..0 by 1;
    }
    if numTasks == 1 {
      if debugCyclicDist then
        writeln(here.id, ": leader whole: ", whole,
                         " result: ", result,
                         " myblock: ", locDom.myBlock);
      yield result;
    } else {

      coforall taskid in 0..#numTasks {
        var splitRanges: rank*range(eltType=idxType, stridable=true) = result;
        const low = result(1).low, high = result(1).high;
        const (lo,hi) = _computeBlock(low, high - low + 1,
                                      low, high,
                                      numTasks, taskid);
        splitRanges(1) = result(1)(lo..hi);
        if debugCyclicDist then
          writeln(here.id, ": leader whole: ", whole,
                           " result: ", result,
                           " splitRanges: ", splitRanges);
        yield splitRanges;
      }
    }
  }
}

def CyclicDom.these(param tag: iterator, follower, param aligned: bool = false) where tag == iterator.follower {
  var t: rank*range(idxType, stridable=true);
  if debugCyclicDist then
    writeln(here.id, ": follower whole is: ", whole,
                     " follower is: ", follower);
  for param i in 1..rank {
    const wholestride = whole.dim(i).stride;
    t(i) = ((follower(i).low*wholestride)..(follower(i).high*wholestride) by (follower(i).stride*wholestride)) + whole.dim(i).low;
  }
  if debugCyclicDist then
    writeln(here.id, ": follower maps to: ", t);
  for i in [(...t)] do
    yield i;
}

def CyclicDom.supportsPrivatization() param return true;

def CyclicDom.getPrivatizeData() return 0;

def CyclicDom.privatize(privatizeData) {
  var distpid = dist.pid;
  var thisdist = dist;
  var privdist = __primitive("chpl_getPrivatizedClass", thisdist, distpid);
  var c = new CyclicDom(rank=rank, idxType=idxType, stridable=stridable, dist=privdist);
  c.locDoms = locDoms;
  c.whole = whole;
  return c;
}

def CyclicDom.getReprivatizeData() return 0;

def CyclicDom.reprivatize(other, reprivatizeData) {
  locDoms = other.locDoms;
  whole = other.whole;
}

def CyclicDom.dim(d: int) return whole.dim(d);

def CyclicDom.strideBy(str: int) {
  var alias = new CyclicDom(rank=rank, idxType=idxType, stridable=true, dist=dist);
  var t: rank*range(eltType=idxType, stridable=true);
  for param i in 1..rank {
    t(i) = this.dim(i) by str;
  }
  alias.setIndices(t);
  return alias;
}

def CyclicDom.strideBy(str: rank*int) {
  var alias = new CyclicDom(rank=rank, idxType=idxType, stridable=true, dist=dist);
  var t: rank*range(eltType=idxType, stridable=true);
  for i in 1..rank {
    t(i) = this.dim(i) by str(i);
  }
  alias.setIndices(t);
  return alias;
}

def CyclicDom.slice(param stridable: bool, ranges) {
  var alias = new CyclicDom(rank=rank, idxType=idxType, dist=dist, stridable=stridable||this.stridable);
  alias.setIndices(whole((...ranges)).getIndices());
  return alias;
}

def CyclicDom.localSlice(param stridable: bool, ranges) {
  return whole((...ranges));
}

class LocCyclicDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const wholeDom: CyclicDom(rank, idxType, stridable);
  var myBlock: domain(rank, idxType, true);
}

def _computeBlock(waylo, numelems, lo, wayhi, numblocks, blocknum) {
  def procToData(x, lo)
    return lo + (x:lo.type) + (x:real != x:int:real):lo.type;

  const blo =
    if blocknum == 0 then waylo
      else procToData((numelems:real * blocknum) / numblocks, lo);
  const bhi =
    if blocknum == numblocks - 1 then wayhi
      else procToData((numelems:real * (blocknum+1)) / numblocks, lo) - 1;

  return (blo, bhi);
}

class CyclicArr: BaseArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;
  var dom: CyclicDom(rank, idxType, stridable);

  var locArr: [dom.dist.targetLocDom] LocCyclicArr(eltType, rank, idxType, stridable);
  var myLocArr: LocCyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable);
  var pid: int = -1;
}

def CyclicArr.checkSlice(ranges) {
  for param i in 1..rank {
    if !dom.dim(i).boundsCheck(ranges(i)) {
      writeln(dom.dim(i), " ", ranges(i), " ", dom.dim(i).boundsCheck(ranges(i)));
      halt("array slice out of bounds in dimension ", i, ": ", ranges(i));
    }
  }
}

def CyclicArr.slice(d: CyclicDom) {
  var alias = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=d.stridable, dom=d, pid=pid);
  for i in dom.dist.targetLocDom {
    on dom.dist.targetLocs(i) {
      alias.locArr[i] = new LocCyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=d.stridable, locDom=d.locDoms[i]);
      alias.locArr[i].myElems => locArr[i].myElems[alias.locArr[i].locDom.myBlock];
      alias.myLocArr = alias.locArr[i];
    }
  }
  return alias;
}

def CyclicArr.localSlice(ranges) {
  var low: rank*idxType;
  for param i in 1..rank {
    low(i) = ranges(i).low;
  }

  var A => locArr(dom.dist.ind2locInd(low)).myElems((...ranges));
  return A;
}

def CyclicArr.getBaseDom() return dom;

def CyclicArr.setup() {
  coforall localeIdx in dom.dist.targetLocDom {
    on dom.dist.targetLocs(localeIdx) {
      locArr(localeIdx) = new LocCyclicArr(eltType, rank, idxType, stridable, dom.locDoms(localeIdx));
      if this.locale == here then
        myLocArr = locArr(localeIdx);
    }
  }
}

def CyclicArr.supportsPrivatization() param return true;

def CyclicArr.getPrivatizeData() return 0;

def CyclicArr.privatize(privatizeData) {
  var dompid = dom.pid;
  var thisdom = dom;
  var privdom = __primitive("chpl_getPrivatizedClass", thisdom, dompid);
  var c = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=privdom);
  c.locArr = locArr;
  for localeIdx in dom.dist.targetLocDom do
    if c.locArr(localeIdx).locale == here then
      c.myLocArr = c.locArr(localeIdx);
  return c;
}

def CyclicArr.this(i: idxType) var where rank == 1 {
  local {
    if myLocArr.locDom.myBlock.member(i) then
      return myLocArr.this(i);
  }
  return locArr(dom.dist.ind2locInd(i))(i);
}

def CyclicArr.this(i:rank*idxType) var {
  return locArr(dom.dist.ind2locInd(i))(i);
}

def CyclicArr.these() var {
  for i in dom do
    yield this(i);
}

def CyclicArr.these(param tag: iterator) where tag == iterator.leader {
  const wholeLow = dom.whole.low,
        precomputedNumTasks = dom.dist.tasksPerLocale;
  coforall locDom in dom.locDoms do on locDom {
    const numTasks = precomputedNumTasks;

    var result: rank*range(eltType=idxType, stridable=true);
    var zeroedLocalPart = dom.whole((...locDom.myBlock.getIndices())) - wholeLow;
    for param i in 1..rank {
      var dim = zeroedLocalPart.dim(i);
      var wholestride = dom.whole.dim(i).stride;
      if dim.high >= dim.low then
        result(i) = (dim.low / wholestride)..(dim.high / wholestride) by (dim.stride / wholestride);
      else
        result(i) = 1..0 by 1;
    }
    if numTasks == 1 {
      if debugCyclicDist then
        writeln(here.id, ": leader whole: ", dom.whole,
                         " result: ", result,
                         " myblock: ", locDom.myBlock);
      yield result;
    } else {

      coforall taskid in 0..#numTasks {
        var splitRanges: rank*range(eltType=idxType, stridable=true) = result;
        const low = result(1).low, high = result(1).high;
        const (lo,hi) = _computeBlock(low, high - low + 1,
                                      low, high,
                                      numTasks, taskid);
        splitRanges(1) = result(1)(lo..hi);
        if debugCyclicDist then
          writeln(here.id, ": leader whole: ", dom.whole,
                           " result: ", result,
                           " splitRanges: ", splitRanges);
        yield splitRanges;
      }
    }
  }
}

def CyclicArr.supportsAlignedFollower() param return true;

def CyclicArr.these(param tag: iterator, follower, param aligned: bool = false) var where tag == iterator.follower {
  var t: rank*range(eltType=idxType, stridable=true);
  for param i in 1..rank {
    const wholestride = dom.whole.dim(i).stride;
    t(i) = ((follower(i).low*wholestride)..(follower(i).high*wholestride) by (follower(i).stride*wholestride)) + dom.whole.dim(i).low;
  }
  const followThis = [(...t)];
  const arrSection = locArr(dom.dist.ind2locInd(followThis.low));
  if aligned {
    local {
      for i in followThis {
        yield arrSection.this(i);
      }
    }
  } else {
    def accessHelper(i) var {
      local {
        if myLocArr.locDom.myBlock.member(i) then
          return myLocArr.this(i);
      }
      return this(i);
    }

    for i in followThis {
      yield accessHelper(i);
    }
  }
}

def CyclicArr.writeThis(f: Writer) {
  if verboseCyclicDistWriters {
    writeln(typeToString(this.type));
    writeln("------");
  }
  for i in dom {
    writeln(this(i));
  }
}

class LocCyclicArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;

  const locDom: LocCyclicDom(rank, idxType, stridable);

  var myElems: [locDom.myBlock] eltType;

}

def LocCyclicArr.this(i:idxType) var {
  return myElems(i);
}

def LocCyclicArr.this(i:rank*idxType) var {
  return myElems((...i));
}

def LocCyclicArr.these() var {
  for elem in myElems {
    yield elem;
  }
}

//
// naive routine for dividing numLocales into rank factors
//
def _factor(param rank: int, value) {
  var factors: rank*int;
  for param i in 1..rank do
    factors(i) = 1;
  if value >= 1 {
    var iv = value;
    var factor = 1;
    while iv > 1 {
      for i in 2..iv {
        if iv % i == 0 {
          var j = 1;
          for i in 2..rank {
            if factors(i) < factors(j) then
              j = i;
          }
          factors(j) *= i;
          iv = iv / i;
          break;
        }
      }
    }
  }
  for i in 1..rank do
    for j in i+1..rank do
      if factors(i) < factors(j) then
        factors(i) <=> factors(j);
  return factors;
}

def _tupleOfZero(param size: int, type t) {
  var tmp: size * t;
  return tmp;
}

config param debugCyclicDist = false;
config param verboseCyclicDistWriters = false;

class Cyclic: BaseDist {
  param rank: int = 1;
  type idxType = int(64);

  const lowIdx: rank*idxType;
  const targetLocDom: domain(rank);
  const targetLocs: [targetLocDom] locale;

  const locDist: [targetLocDom] LocCyclic(rank, idxType);

  const tasksPerLocale = 1;
  var pid: int = -1;

  def Cyclic(param rank: int,
             type idxType = int(64),
             low: rank*idxType = _tupleOfZero(rank, idxType),
             targetLocales: [] locale = thisRealm.Locales,
             tasksPerLocale=0) {
    if rank == 1  {
      targetLocDom = [0..#targetLocales.numElements];
      targetLocs = targetLocales;
    } else if targetLocales.rank == 1 {
      const factors = _factor(rank, targetLocales.numElements);
      var ranges: rank*range;
      for param i in 1..rank {
        ranges(i) = 0..#factors(i);
      }
      targetLocDom = [(...ranges)];
      for (loc1, loc2) in (targetLocs, targetLocales) {
        loc1 = loc2;
      }
    } else {
      if targetLocales.rank != rank then
        compilerError("locales array rank must be one or match distribution rank");
      var ranges: rank*range;
      for param i in 1..rank do {
        var thisRange = targetLocales.domain.dim(i);
        ranges(i) = 0..#thisRange.length;
      }
      targetLocDom = [(...ranges)];
      targetLocs = reshape(targetLocales, targetLocDom);
    }

    for param i in 1..rank do
      lowIdx(i) = mod(low(i), targetLocDom.dim(i).length);

    if tasksPerLocale == 0 then
      this.tasksPerLocale = min reduce targetLocales.numCores;
    else
      this.tasksPerLocale = tasksPerLocale;
    coforall locid in targetLocDom {
      on targetLocs(locid) {
        locDist(locid) = new LocCyclic(rank, idxType, locid, this);
      }
    }
    if debugCyclicDist then
      for loc in locDist do writeln(loc);
  }

  def Cyclic(param rank, type idxType, other: Cyclic(rank, idxType)) {
    targetLocDom = other.targetLocDom;
    targetLocs = other.targetLocs;
    lowIdx = other.lowIdx;
    locDist = other.locDist;
    tasksPerLocale = other.tasksPerLocale;
  }

  def clone() return new Cyclic(rank=rank, idxType=idxType, other=this);
}

def Cyclic.getChunk(inds, locid) {
  var sliceBy: rank*range(eltType=idxType, stridable=true);
  var locidtup: rank*int;
  if rank == 1 then
    locidtup(1) = locid;
  else
    locidtup = locid;
  for param i in 1..rank {
    var distStride = targetLocDom.dim(i).length;
    var loclowidx = mod(lowIdx(i) + locidtup(i), distStride);
    var lowmod = mod(inds.dim(i).low, distStride);
    var offset = loclowidx - lowmod;
    if offset < 0 then
      sliceBy(i) = inds.dim(i).low + (distStride + offset)..inds.dim(i).high by distStride;
    else
      sliceBy(i) = inds.dim(i).low + offset..inds.dim(i).high by distStride;
  }
  return inds((...sliceBy));
  //
  // WANT:
  //var distWhole = locDist(locid).myChunk.getIndices();
  //return inds((...distWhole));
}

def Cyclic.supportsPrivatization() param return true;

def Cyclic.getPrivatizeData() return 0;

def Cyclic.privatize(privatizeData) {
  return new Cyclic(rank=rank, idxType=idxType, other=this);
}

def Cyclic.getReprivatizeData() return 0;

def Cyclic.reprivatize(other, reprivatizeData) {
  targetLocDom = other.targetLocDom;
  targetLocs = other.targetLocs;
  locDist = other.locDist;
  lowIdx = other.lowIdx;
  tasksPerLocale = other.tasksPerLocale;
}

def Cyclic.newArithmeticDom(param rank: int, type idxType, param stridable: bool) {
  if idxType != this.idxType then
    compilerError("Cyclic domain index type does not match distribution's");
  if rank != this.rank then
    compilerError("Cyclic domain rank does not match distribution's");
  var dom = new CyclicDom(rank=rank, idxType=idxType, dist = this, stridable=stridable);
  dom.setup();
  return dom;
}

def Cyclic.writeThis(x: Writer) {
  x.writeln(typeToString(this.type));
  x.writeln("------");
  for locid in targetLocDom do
    x.writeln(" [", locid, "=", targetLocs(locid), "] owns chunk: ", locDist(locid).myChunk);
}

def Cyclic.ind2locInd(i: idxType) {
  const numLocs:idxType = targetLocDom.numIndices:idxType;
  // this is wrong if i is less than lowIdx
  //return ((i - lowIdx(1)) % numLocs):int;
  // this works even if i is less than lowIdx
  return mod(mod(i, numLocs) - mod(lowIdx(1), numLocs), numLocs):int;
}

def Cyclic.ind2locInd(ind: rank*idxType) {
  var x: rank*int;
  for param i in 1..rank {
    var dimLen = targetLocDom.dim(i).length;
    //x(i) = ((ind(i) - lowIdx(i)) % dimLen):int;
    x(i) = mod(mod(ind(i), dimLen) - mod(lowIdx(i), dimLen), dimLen):int;
  }
  if rank == 1 then
    return x(1);
  else
    return x;
}

def Cyclic.ind2loc(i: idxType) where rank == 1 {
  return targetLocs(ind2locInd(i));
}

def Cyclic.ind2loc(i: rank*idxType) {
  return targetLocs(ind2locInd(i));
}

class LocCyclic {
  param rank: int;
  type idxType;

  const myChunk: domain(rank, idxType, true);

  def LocCyclic(param rank, type idxType, locid, dist: Cyclic(rank, idxType)) {
    var locidx: rank*int;
    var lowIdx = dist.lowIdx;

    if rank == 1 then
      locidx(1) = locid;
    else
      locidx = locid;

    var tuple: rank*range(idxType, stridable=true);

    for param i in 1..rank {
      const lower = min(idxType)..(lowIdx(i)+locidx(i)) by -dist.targetLocDom.dim(i).length;
      const upper = (lowIdx(i) + locidx(i))..max(idxType) by dist.targetLocDom.dim(i).length;
      const lo = lower.low, hi = upper.high;
      tuple(i) = lo..hi by dist.targetLocDom.dim(i).length;
    }
    myChunk = [(...tuple)];
  }
}

class CyclicDom : BaseArithmeticDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const dist: Cyclic(rank, idxType);

  var locDoms: [dist.targetLocDom] LocCyclicDom(rank, idxType, stridable);

  const whole: domain(rank, idxType, stridable);

  var pid: int = -1;
}

def CyclicDom.setup() {
  coforall localeIdx in dist.targetLocDom {
    on dist.targetLocs(localeIdx) {
      locDoms(localeIdx) = new LocCyclicDom(rank, idxType, stridable, this,
                                            dist.getChunk(whole, localeIdx));
    }
  }
}

def CyclicDom.buildArray(type eltType) {
  var arr = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=this);
  arr.setup();
  return arr;
}

def CyclicDom.getIndices() {
  return whole.getIndices();
}

def CyclicDom.setIndices(x) {
  whole.setIndices(x);
  setup();
}

def CyclicDom.writeThis(x: Writer) {
  if verboseCyclicDistWriters {
    writeln(typeToString(this.type));
    writeln("------");
    for loc in dist.targetLocDom {
      x.writeln("[", loc, "=", dist.targetLocs(loc), "] owns ", locDoms(loc).myBlock);
    }
  } else {
    x.write(whole);
  }
}

def CyclicDom.numIndices return whole.numIndices;

def CyclicDom.these() {
  for i in whole do
    yield i;
}

def CyclicDom.these(param tag: iterator) where tag == iterator.leader {
  const wholeLow = whole.low,
        precomputedNumTasks = dist.tasksPerLocale;
  coforall locDom in locDoms do on locDom {
    const numTasks = precomputedNumTasks;

    var result: rank*range(eltType=idxType, stridable=true);
    var zeroedLocalPart = whole((...locDom.myBlock.getIndices())) - whole.low;
    for param i in 1..rank {
      var dim = zeroedLocalPart.dim(i);
      var wholestride = whole.dim(i).stride;
      if dim.high >= dim.low then
        result(i) = (dim.low / wholestride)..(dim.high / wholestride) by (dim.stride / wholestride);
      else
        result(i) = 1..0 by 1;
    }
    if numTasks == 1 {
      if debugCyclicDist then
        writeln(here.id, ": leader whole: ", whole,
                         " result: ", result,
                         " myblock: ", locDom.myBlock);
      yield result;
    } else {
      var splitRanges: rank*range(eltType=idxType, stridable=true) = result;

      coforall taskid in 0..#numTasks {
        const low = result(1).low, high = result(1).high;
        const (lo,hi) = _computeBlock(low, high - low + 1,
                                      low, high,
                                      numTasks, taskid);
        splitRanges(1) = result(1)(lo..hi);
        if debugCyclicDist then
          writeln(here.id, ": leader whole: ", whole,
                           " result: ", result,
                           " splitRanges: ", splitRanges);
        yield splitRanges;
      }
    }
  }
}

def CyclicDom.these(param tag: iterator, follower, param aligned: bool = false) where tag == iterator.follower {
  var t: rank*range(idxType, stridable=true);
  if debugCyclicDist then
    writeln(here.id, ": follower whole is: ", whole,
                     " follower is: ", follower);
  for param i in 1..rank {
    const wholestride = whole.dim(i).stride;
    t(i) = ((follower(i).low*wholestride)..(follower(i).high*wholestride) by (follower(i).stride*wholestride)) + whole.dim(i).low;
  }
  if debugCyclicDist then
    writeln(here.id, ": follower maps to: ", t);
  for i in [(...t)] do
    yield i;
}

def CyclicDom.supportsPrivatization() param return true;

def CyclicDom.getPrivatizeData() return 0;

def CyclicDom.privatize(privatizeData) {
  var distpid = dist.pid;
  var thisdist = dist;
  var privdist = __primitive("chpl_getPrivatizedClass", thisdist, distpid);
  var c = new CyclicDom(rank=rank, idxType=idxType, stridable=stridable, dist=privdist);
  c.locDoms = locDoms;
  c.whole = whole;
  return c;
}

def CyclicDom.getReprivatizeData() return 0;

def CyclicDom.reprivatize(other, reprivatizeData) {
  locDoms = other.locDoms;
  whole = other.whole;
}

def CyclicDom.dim(d: int) return whole.dim(d);

def CyclicDom.strideBy(str: int) {
  var alias = new CyclicDom(rank=rank, idxType=idxType, stridable=true, dist=dist);
  var t: rank*range(eltType=idxType, stridable=true);
  for param i in 1..rank {
    t(i) = this.dim(i) by str;
  }
  alias.setIndices(t);
  return alias;
}

def CyclicDom.strideBy(str: rank*int) {
  var alias = new CyclicDom(rank=rank, idxType=idxType, stridable=true, dist=dist);
  var t: rank*range(eltType=idxType, stridable=true);
  for i in 1..rank {
    t(i) = this.dim(i) by str(i);
  }
  alias.setIndices(t);
  return alias;
}

def CyclicDom.slice(param stridable: bool, ranges) {
  var alias = new CyclicDom(rank=rank, idxType=idxType, dist=dist, stridable=stridable||this.stridable);
  alias.setIndices(whole((...ranges)).getIndices());
  return alias;
}

def CyclicDom.localSlice(param stridable: bool, ranges) {
  return whole((...ranges));
}

class LocCyclicDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const wholeDom: CyclicDom(rank, idxType, stridable);
  var myBlock: domain(rank, idxType, true);
}

def _computeBlock(waylo, numelems, lo, wayhi, numblocks, blocknum) {
  def procToData(x, lo)
    return lo + (x:lo.type) + (x:real != x:int:real):lo.type;

  const blo =
    if blocknum == 0 then waylo
      else procToData((numelems:real * blocknum) / numblocks, lo);
  const bhi =
    if blocknum == numblocks - 1 then wayhi
      else procToData((numelems:real * (blocknum+1)) / numblocks, lo) - 1;

  return (blo, bhi);
}

class CyclicArr: BaseArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;
  var dom: CyclicDom(rank, idxType, stridable);

  var locArr: [dom.dist.targetLocDom] LocCyclicArr(eltType, rank, idxType, stridable);
  var myLocArr: LocCyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable);
  var pid: int = -1;
}

def CyclicArr.checkSlice(ranges) {
  for param i in 1..rank {
    if !dom.dim(i).boundsCheck(ranges(i)) {
      writeln(dom.dim(i), " ", ranges(i), " ", dom.dim(i).boundsCheck(ranges(i)));
      halt("array slice out of bounds in dimension ", i, ": ", ranges(i));
    }
  }
}

def CyclicArr.slice(d: CyclicDom) {
  var alias = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=d.stridable, dom=d, pid=pid);
  for i in dom.dist.targetLocDom {
    on dom.dist.targetLocs(i) {
      alias.locArr[i] = new LocCyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=d.stridable, locDom=d.locDoms[i]);
      alias.locArr[i].myElems => locArr[i].myElems[alias.locArr[i].locDom.myBlock];
      alias.myLocArr = alias.locArr[i];
    }
  }
  return alias;
}

def CyclicArr.localSlice(ranges) {
  var low: rank*idxType;
  for param i in 1..rank {
    low(i) = ranges(i).low;
  }

  var A => locArr(dom.dist.ind2locInd(low)).myElems((...ranges));
  return A;
}

def CyclicArr.getBaseDom() return dom;

def CyclicArr.setup() {
  coforall localeIdx in dom.dist.targetLocDom {
    on dom.dist.targetLocs(localeIdx) {
      locArr(localeIdx) = new LocCyclicArr(eltType, rank, idxType, stridable, dom.locDoms(localeIdx));
      if this.locale == here then
        myLocArr = locArr(localeIdx);
    }
  }
}

def CyclicArr.supportsPrivatization() param return true;

def CyclicArr.getPrivatizeData() return 0;

def CyclicArr.privatize(privatizeData) {
  var dompid = dom.pid;
  var thisdom = dom;
  var privdom = __primitive("chpl_getPrivatizedClass", thisdom, dompid);
  var c = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=privdom);
  c.locArr = locArr;
  for localeIdx in dom.dist.targetLocDom do
    if c.locArr(localeIdx).locale == here then
      c.myLocArr = c.locArr(localeIdx);
  return c;
}

def CyclicArr.this(i: idxType) var where rank == 1 {
  local {
    if myLocArr.locDom.myBlock.member(i) then
      return myLocArr.this(i);
  }
  return locArr(dom.dist.ind2locInd(i))(i);
}

def CyclicArr.this(i:rank*idxType) var {
  return locArr(dom.dist.ind2locInd(i))(i);
}

def CyclicArr.these() var {
  for i in dom do
    yield this(i);
}

def CyclicArr.these(param tag: iterator) where tag == iterator.leader {
  const wholeLow = dom.whole.low,
        precomputedNumTasks = dom.dist.tasksPerLocale;
  coforall locDom in dom.locDoms do on locDom {
    const numTasks = precomputedNumTasks;

    var result: rank*range(eltType=idxType, stridable=true);
    var zeroedLocalPart = dom.whole((...locDom.myBlock.getIndices())) - wholeLow;
    for param i in 1..rank {
      var dim = zeroedLocalPart.dim(i);
      var wholestride = dom.whole.dim(i).stride;
      if dim.high >= dim.low then
        result(i) = (dim.low / wholestride)..(dim.high / wholestride) by (dim.stride / wholestride);
      else
        result(i) = 1..0 by 1;
    }
    if numTasks == 1 {
      if debugCyclicDist then
        writeln(here.id, ": leader whole: ", dom.whole,
                         " result: ", result,
                         " myblock: ", locDom.myBlock);
      yield result;
    } else {
      var splitRanges: rank*range(eltType=idxType, stridable=true) = result;

      coforall taskid in 0..#numTasks {
        const low = result(1).low, high = result(1).high;
        const (lo,hi) = _computeBlock(low, high - low + 1,
                                      low, high,
                                      numTasks, taskid);
        splitRanges(1) = result(1)(lo..hi);
        if debugCyclicDist then
          writeln(here.id, ": leader whole: ", dom.whole,
                           " result: ", result,
                           " splitRanges: ", splitRanges);
        yield splitRanges;
      }
    }
  }
}

def CyclicArr.supportsAlignedFollower() param return true;

def CyclicArr.these(param tag: iterator, follower, param aligned: bool = false) var where tag == iterator.follower {
  var t: rank*range(eltType=idxType, stridable=true);
  for param i in 1..rank {
    const wholestride = dom.whole.dim(i).stride;
    t(i) = ((follower(i).low*wholestride)..(follower(i).high*wholestride) by (follower(i).stride*wholestride)) + dom.whole.dim(i).low;
  }
  const followThis = [(...t)];
  const arrSection = locArr(dom.dist.ind2locInd(followThis.low));
  if aligned {
    local {
      for i in followThis {
        yield arrSection.this(i);
      }
    }
  } else {
    def accessHelper(i) var {
      local {
        if myLocArr.locDom.myBlock.member(i) then
          return myLocArr.this(i);
      }
      return this(i);
    }

    for i in followThis {
      yield accessHelper(i);
    }
  }
}

def CyclicArr.writeThis(f: Writer) {
  if verboseCyclicDistWriters {
    writeln(typeToString(this.type));
    writeln("------");
  }
  for i in dom {
    writeln(this(i));
  }
}

class LocCyclicArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;

  const locDom: LocCyclicDom(rank, idxType, stridable);

  var myElems: [locDom.myBlock] eltType;

}

def LocCyclicArr.this(i:idxType) var {
  return myElems(i);
}

def LocCyclicArr.this(i:rank*idxType) var {
  return myElems((...i));
}

def LocCyclicArr.these() var {
  for elem in myElems {
    yield elem;
  }
}