added namedmatrix prod and new init()

added namedmatrix prod and new init()

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() {
     this.initDone(); // NAMEDMATRIX DOESNT EXPLICITLY INHERIT ANYTHING SO IS THIS NECESSARY??
   }

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

   proc init(X, names: [] string) {
     this.init(X);
     try! this.setRowNames(names);
     try! this.setColNames(names);
   }

   proc init(X, rownames: [] string, colnames: [] string) {
     this.init(X);
     try! this.setRowNames(rownames);
     try! this.setColNames(colnames);
   }

   proc init(N: NamedMatrix) {
     this.init(N.X);
     this.rows = N.rows;
     this.cols = N.cols;
   }

   proc init(X, rows: BiMap, cols: BiMap) {
     this.init(X);
     this.rows = rows;
     this.cols = cols;
   }
}

/*
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.ndot(N: NamedMatrix) {
  var C: NamedMatrix = new NamedMatrix(X = dot(this.X,N.X), this.rows, N.cols);
  return C;
}

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));

  for 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, weights=false) {
  var q = "SELECT %s, %s FROM %s ORDER BY 1, 2;";
  var source_size_q = """
                      SELECT count(*) AS n FROM (SELECT distinct(s.source_cui) FROM r.cui_confabulation s) AS sources;
                      """;

  var target_size_q = """
                      SELECT count(*) AS n FROM (SELECT distinct(s.exhibited_cui) FROM r.cui_confabulation s) AS exhibited;
                      """;
  var cursor2 = con.cursor();
  var cursor3 = con.cursor();
  cursor2.query(source_size_q);
  cursor3.query(target_size_q);
  const row2 = cursor2.fetchone();
  const row3 = cursor3.fetchone();
  const source_size = row2['n']: int;
  const exhibit_size = row3['n']: int;

  var cursor = con.cursor();
  cursor.query(q,(fromField, toField, edgeTable));
  const size = cursor.rowcount(): int;
  var D: domain(2) = {1..source_size, 1..exhibit_size};
  var SD: sparse subdomain(D) dmapped CS();
  var X: [SD] real;
  var dom: domain(1) = {1..size};
  var indices: [dom] (int, int);
  var values: [dom] real;
  forall (row, i) in zip(cursor, dom) {
    indices[i] = (row[fromField]: int,row[toField]: int);
    values[i] = 1: 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() {
     this.initDone(); // NAMEDMATRIX DOESNT EXPLICITLY INHERIT ANYTHING SO IS THIS NECESSARY??
   }

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

   proc init(X, names: [] string) {
     this.init(X);
     try! this.setRowNames(names);
     try! this.setColNames(names);
   }

   proc init(X, rownames: [] string, colnames: [] string) {
     this.init(X);
     try! this.setRowNames(rownames);
     try! this.setColNames(colnames);
   }

   proc init(N: NamedMatrix) {
     this.init(N.X);
     this.rows = N.rows;
     this.cols = N.cols;
   }
}

/*
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));

  for 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, weights=false) {
  var q = "SELECT %s, %s FROM %s ORDER BY 1, 2;";
  var source_size_q = """
                      SELECT count(*) AS n FROM (SELECT distinct(s.source_cui) FROM r.cui_confabulation s) AS sources;
                      """;

  var target_size_q = """
                      SELECT count(*) AS n FROM (SELECT distinct(s.exhibited_cui) FROM r.cui_confabulation s) AS exhibited;
                      """;
  var cursor2 = con.cursor();
  var cursor3 = con.cursor();
  cursor2.query(source_size_q);
  cursor3.query(target_size_q);
  const row2 = cursor2.fetchone();
  const row3 = cursor3.fetchone();
  const source_size = row2['n']: int;
  const exhibit_size = row3['n']: int;

  var cursor = con.cursor();
  cursor.query(q,(fromField, toField, edgeTable));
  const size = cursor.rowcount(): int;
  var D: domain(2) = {1..source_size, 1..exhibit_size};
  var SD: sparse subdomain(D) dmapped CS();
  var X: [SD] real;
  var dom: domain(1) = {1..size};
  var indices: [dom] (int, int);
  var values: [dom] real;
  forall (row, i) in zip(cursor, dom) {
    indices[i] = (row[fromField]: int,row[toField]: int);
    values[i] = 1: 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;
}


Didn't like my phrasing; rework the first paragraph again.

/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  The :mod:`Memory` module provides procedures which report information
  about memory usage.  With one exception, to use these procedures you
  must enable memory tracking.  Do this by setting one or more of the
  config vars below, using appropriate ``--configVarName=value`` or
  ``-sconfigVarName=value`` command line options when you run the
  program.  If memory tracking is not enabled, calling any procedure
  described here, other than :proc:`locale.physicalMemory`, will cause
  the program to halt with an error message.

  ``memTrack``: :type:`bool`:
    Enable memory tracking.  This causes memory allocations and
    deallocations to be internally tracked, to support logging them on
    the fly and/or producing any of several kinds of memory-related
    reports, described below.

  ``memLeaksTable``: :type:`bool`:
    Enable memory tracking and produce detailed information about
    leaked memory by invoking :proc:`printMemTable` implicitly when
    when the program terminates normally.

  ``memLeaks``: :type:`bool`:
    Enable memory tracking and produce summary information about
    leaked memory by invoking :proc:`printMemLeaksTable` implicitly
    when the program terminates normally.

  ``memLeaksLog``: :type:`c_string`:
    Enable memory tracking and produce a report to the named file when
    the program terminates normally.
    The report contains the compilation command used to build the
    program, the execution command used to run it, the summary
    allocation statistics produced by :proc:`printMemStat`, and the
    summary information about leaked memory produced by
    :proc:`printMemLeaksTable`.

  ``memStats``: :type:`bool`:
    Enable memory tracking and produce summary memory statistics by
    invoking :proc:`printMemStat` implicitly at normal program
    termination.

  ``memMax``: :type:`uint`:
    If the value is greater than 0 (zero), enable memory tracking
    and use the value as a limit on the number of bytes of memory
    that can be allocated per top-level (network-connected) locale.
    If during execution the amount of allocated memory exceeds this
    limit on any locale, halt the program with a message saying so.

  The following two config variables do not enable memory tracking;
  they only modify how it is done.

  ``memThreshold``: :type:`uint`:
    If this is set to a value greater than 0 (zero), only allocation
    requests larger than this are tracked and/or reported.

  ``memLog``: :type:`c_string`:
    Memory reporting is written to this file.  By default it is the
    ``stdout`` associated with the process (not the Chapel channel
    with the same name).  Setting this config variable to a file path
    causes the reporting to be written to that named file instead.
 */
module Memory {

pragma "no doc"
pragma "insert line file info" extern proc chpl_memoryUsed(): uint(64);

/*
  The amount of memory returned by :proc:`locale.physicalMemory` can
  be expressed either as individual bytes or as chunks of 2**10,
  2**20, or 2**30 bytes.
 */
enum MemUnits {Bytes, KB, MB, GB};

/*
  How much physical memory is present on this locale?

  Note that this is a physical quantity and does not take into account
  things like virtual memory support which might allow for allocating
  a larger amount, or system or user limits which might prevent
  allocating so much.

  *Note:* Unlike the other procedures in the :mod:`Memory` module, this
  one does not require memory tracking to be enabled.

  :arg unit: Units in which the returned value is to be expressed.
  :type unit: MemUnits
  :arg retType: Type of the returned value.  Defaults to `int(64)`.
  :type retType: type
  :returns: Size of physical memory on the locale where the call is made.
  :rtype: :type:`retType`
 */
proc locale.physicalMemory(unit: MemUnits=MemUnits.Bytes, type retType=int(64)) {
  extern proc chpl_bytesPerLocale(): uint(64);

  var bytesInLocale: uint(64);

  on this do bytesInLocale = chpl_bytesPerLocale();

  var retVal: retType;
  select (unit) {
    when MemUnits.Bytes do retVal = bytesInLocale:retType;
    when MemUnits.KB do retVal = (bytesInLocale:retType / 1024):retType;
    when MemUnits.MB do retVal = (bytesInLocale:retType / (1024**2)):retType;
    when MemUnits.GB do retVal = (bytesInLocale:retType / (1024**3)):retType;
  }

  return retVal;
}

/*
  How much memory is this program currently using on this locale?

  This is the amount of memory known to be currently allocated on the
  calling top-level (network-connected) locale by the program, through
  Chapel mechanisms.  It does not include memory allocated directly
  from the system, outside of Chapel mechanisms, such as allocations
  made by code written in other languages and linked into the program.

  :returns: Amount of allocated memory, in bytes.
  :rtype: `uint(64)`
 */
proc memoryUsed() {
  return chpl_memoryUsed();
}

/*
  Print detailed information about allocated memory to ``memLog``.
  The report contains a section for each top-level locale, containing
  a table of entries for the allocations made on that locale.  Each
  entry shows the source file and line at which the allocation was
  requested, the address and size (bytes) of the allocated space, and
  a description of the type of information the requesting code said it
  was going to store there.

  :arg thresh: Do not print entries whose size is less than this.
    Defaults to 0.
  :type thresh: int
*/
proc printMemTable(thresh=0) {
  pragma "insert line file info"
  extern proc chpl_printMemTable(thresh);

  chpl_printMemTable(thresh);
}

/*
  Print summary information about allocated memory to ``memLog``.  The
  report contains a section for each top-level locale, containing a
  table of entries, one for each different allocation type for which
  at least one allocation exists on that locale.  The entries show the
  type (that is, the string the code requesting the allocation used to
  describe what it would store there) and the total number of
  allocations and bytes allocated for that type.
*/
proc printMemLeaksTable() {
  extern proc chpl_printLeakedMemTable();
  chpl_printLeakedMemTable();
}

/*
  Print summary memory statistics to ``memLog``.  The report contains
  a section for each top-level locale showing the number of bytes of
  memory currently allocated, the maximum number allocated at any
  point during execution (the high-water mark), and the sum of the
  sizes of all allocation and deallocation requests.
*/
proc printMemStat() {
  pragma "insert line file info"
  extern proc chpl_printMemStat();

  chpl_printMemStat();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on any locale.  Continue reporting until :proc:`stopVerboseMem`
  is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMem() {
  extern proc chpl_startVerboseMem();
  chpl_startVerboseMem();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on any locale.
 */
proc stopVerboseMem() {
  extern proc chpl_stopVerboseMem();
  chpl_stopVerboseMem();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on this locale.  Continue reporting until
  :proc:`stopVerboseMemHere` is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMemHere() {
  extern proc chpl_startVerboseMemHere();
  chpl_startVerboseMemHere();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on this locale.
 */
proc stopVerboseMemHere() {
  extern proc chpl_stopVerboseMemHere();
  chpl_stopVerboseMemHere();
}

}

/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  The :mod:`Memory` module provides procedures which report information
  regarding memory usage.  Except for :proc:`locale.physicalMemory` as
  noted below, all of the procedures in :mod:`Memory` require memory
  tracking to be enabled.  This can be done by setting at least one of
  the following configuration variables at execution time, using either
  the ``--configVarName=value`` or ``-sconfigVarName=value`` command
  line options when a program is run.  If this is not done, they will
  halt the program with an error message when called.

  ``memTrack``: :type:`bool`:
    Enable memory tracking.  This causes memory allocations and
    deallocations to be internally tracked, to support logging them on
    the fly and/or producing any of several kinds of memory-related
    reports, described below.

  ``memLeaksTable``: :type:`bool`:
    Enable memory tracking and produce detailed information about
    leaked memory by invoking :proc:`printMemTable` implicitly when
    when the program terminates normally.

  ``memLeaks``: :type:`bool`:
    Enable memory tracking and produce summary information about
    leaked memory by invoking :proc:`printMemLeaksTable` implicitly
    when the program terminates normally.

  ``memLeaksLog``: :type:`c_string`:
    Enable memory tracking and produce a report to the named file when
    the program terminates normally.
    The report contains the compilation command used to build the
    program, the execution command used to run it, the summary
    allocation statistics produced by :proc:`printMemStat`, and the
    summary information about leaked memory produced by
    :proc:`printMemLeaksTable`.

  ``memStats``: :type:`bool`:
    Enable memory tracking and produce summary memory statistics by
    invoking :proc:`printMemStat` implicitly at normal program
    termination.

  ``memMax``: :type:`uint`:
    If the value is greater than 0 (zero), enable memory tracking
    and use the value as a limit on the number of bytes of memory
    that can be allocated per top-level (network-connected) locale.
    If during execution the amount of allocated memory exceeds this
    limit on any locale, halt the program with a message saying so.

  The following two config variables do not enable memory tracking;
  they only modify how it is done.

  ``memThreshold``: :type:`uint`:
    If this is set to a value greater than 0 (zero), only allocation
    requests larger than this are tracked and/or reported.

  ``memLog``: :type:`c_string`:
    Memory reporting is written to this file.  By default it is the
    ``stdout`` associated with the process (not the Chapel channel
    with the same name).  Setting this config variable to a file path
    causes the reporting to be written to that named file instead.
 */
module Memory {

pragma "no doc"
pragma "insert line file info" extern proc chpl_memoryUsed(): uint(64);

/*
  The amount of memory returned by :proc:`locale.physicalMemory` can
  be expressed either as individual bytes or as chunks of 2**10,
  2**20, or 2**30 bytes.
 */
enum MemUnits {Bytes, KB, MB, GB};

/*
  How much physical memory is present on this locale?

  Note that this is a physical quantity and does not take into account
  things like virtual memory support which might allow for allocating
  a larger amount, or system or user limits which might prevent
  allocating so much.

  *Note:* Unlike the other procedures in the :mod:`Memory` module, this
  one does not require memory tracking to be enabled.

  :arg unit: Units in which the returned value is to be expressed.
  :type unit: MemUnits
  :arg retType: Type of the returned value.  Defaults to `int(64)`.
  :type retType: type
  :returns: Size of physical memory on the locale where the call is made.
  :rtype: :type:`retType`
 */
proc locale.physicalMemory(unit: MemUnits=MemUnits.Bytes, type retType=int(64)) {
  extern proc chpl_bytesPerLocale(): uint(64);

  var bytesInLocale: uint(64);

  on this do bytesInLocale = chpl_bytesPerLocale();

  var retVal: retType;
  select (unit) {
    when MemUnits.Bytes do retVal = bytesInLocale:retType;
    when MemUnits.KB do retVal = (bytesInLocale:retType / 1024):retType;
    when MemUnits.MB do retVal = (bytesInLocale:retType / (1024**2)):retType;
    when MemUnits.GB do retVal = (bytesInLocale:retType / (1024**3)):retType;
  }

  return retVal;
}

/*
  How much memory is this program currently using on this locale?

  This is the amount of memory known to be currently allocated on the
  calling top-level (network-connected) locale by the program, through
  Chapel mechanisms.  It does not include memory allocated directly
  from the system, outside of Chapel mechanisms, such as allocations
  made by code written in other languages and linked into the program.

  :returns: Amount of allocated memory, in bytes.
  :rtype: `uint(64)`
 */
proc memoryUsed() {
  return chpl_memoryUsed();
}

/*
  Print detailed information about allocated memory to ``memLog``.
  The report contains a section for each top-level locale, containing
  a table of entries for the allocations made on that locale.  Each
  entry shows the source file and line at which the allocation was
  requested, the address and size (bytes) of the allocated space, and
  a description of the type of information the requesting code said it
  was going to store there.

  :arg thresh: Do not print entries whose size is less than this.
    Defaults to 0.
  :type thresh: int
*/
proc printMemTable(thresh=0) {
  pragma "insert line file info"
  extern proc chpl_printMemTable(thresh);

  chpl_printMemTable(thresh);
}

/*
  Print summary information about allocated memory to ``memLog``.  The
  report contains a section for each top-level locale, containing a
  table of entries, one for each different allocation type for which
  at least one allocation exists on that locale.  The entries show the
  type (that is, the string the code requesting the allocation used to
  describe what it would store there) and the total number of
  allocations and bytes allocated for that type.
*/
proc printMemLeaksTable() {
  extern proc chpl_printLeakedMemTable();
  chpl_printLeakedMemTable();
}

/*
  Print summary memory statistics to ``memLog``.  The report contains
  a section for each top-level locale showing the number of bytes of
  memory currently allocated, the maximum number allocated at any
  point during execution (the high-water mark), and the sum of the
  sizes of all allocation and deallocation requests.
*/
proc printMemStat() {
  pragma "insert line file info"
  extern proc chpl_printMemStat();

  chpl_printMemStat();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on any locale.  Continue reporting until :proc:`stopVerboseMem`
  is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMem() {
  extern proc chpl_startVerboseMem();
  chpl_startVerboseMem();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on any locale.
 */
proc stopVerboseMem() {
  extern proc chpl_stopVerboseMem();
  chpl_stopVerboseMem();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on this locale.  Continue reporting until
  :proc:`stopVerboseMemHere` is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMemHere() {
  extern proc chpl_startVerboseMemHere();
  chpl_startVerboseMemHere();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on this locale.
 */
proc stopVerboseMemHere() {
  extern proc chpl_stopVerboseMemHere();
  chpl_stopVerboseMemHere();
}

}


Added comments to the Random module to better document
the type of generator that is implemented.

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

// This random number generator was written from the NPB
// random number generator.
//
// It's the linear congruential generator with
//
// m = 2^46 (the modulus)
// a = 1220703125.0 (the multiplier)
// c = 0 (the increment)
//
// It requires an odd integer for its seed.

use Time;

class SeedGeneratorClass {
  def clockMS {
    return getCurrentTime(unit=microseconds):int(64);
  }
}

var SeedGenerator = SeedGeneratorClass();

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;
  const r23   = 0.5**23,
        t23   = 2.0**23,
        r46   = 0.5**46,
        t46   = 2.0**46;

  var cursorVal: real;

  def initialize() {
    cursorVal = seed:real;
    getNext();
  }

  def randlc(inout x) {
    var t1 = r23 * arand;
    const a1 = floor(t1),
          a2 = arand - t23 * a1;
    t1 = r23 * x;
    const x1 = floor(t1),
          x2 = x - t23 * x1;
    t1 = a1 * x2 + a2 * x1;
    const t2 = floor(r23 * t1),
          z  = t1 - t23 * t2,
          t3 = t23 * z + a2 * x2,
          t4 = floor(r46 * t3),
          x3 = t3 - t46 * t4;
    x = x3;

    return r46 * x3;
  }

  // NOTE: this routine is current untested.
  // reworked things a bit to simplify the
  // interface, take advantage of class
  // ability to retain state, but haven't
  // tried this routine yet
  def getNth(in n : int) {
    var x  = seed:real,
        t = arand;
    while (n != 0) {
      const i = n / 2;
      if (2 * i != n) then
        nextrandlc(x);
      nextrandlc(t);
      n = i;
    }
    return x;
  }

  def nextrandlc() {
    return randlc(cursorVal);
  }

  def getNext() {
    return nextrandlc();
  }

  def fillRandom(x:[?D] real) {
    for i in D {
      x(i) = nextrandlc();
    }
  }

  def fillRandom(x:[?D] complex) {
    for i in D {
      x(i).re = nextrandlc();
      x(i).im = nextrandlc();
    }
  }
}

def fillRandom(x:[], initseed: int(64) = SeedGenerator.clockMS) {
  var randNums = RandomStream(initseed);

  randNums.fillRandom(x);
}


quick cleanup

quick cleanup

/*
 A simple Graph object.  This code was originally in the Chapel source, which can be found here:
 https://github.com/chapel-lang/chapel/blob/master/test/studies/labelprop/Graph.chpl

 */

 module Graph {

     //
     // VertexData: stores the neighbor list of a vertex.
     //
     /* private */
     record VertexData {
       type nodeIdType;
       param weighted; //hardcoded for now.
       type edgeWeightType;

       param initialFirstAvail;
       param initialLastAvail;

       //
       // We will represent the neighbor list as an array of nleType.
       // nle = Neighbor List Element.
       //

       //type nleType = (nodeIdType, edgeWeightType);
       type nleType = (nodeIdType,);
       var ndom = {initialFirstAvail..initialLastAvail};
       var neighborList: [ndom] nleType;
       var last = 0;

       param nid = 1;
       param weight = 2;

       proc numNeighbors()  return ndom.numIndices;
 /*
       var firstAvailNeighbor$: [vertex_domain] sync int = initialFirstAvail;

       // Both the vertex and firstAvail must be passed by reference.
       // TODO: possibly compute how many neighbors the vertex has, first.
       // Then allocate that big of a neighbor list right away.
       // That way there will be no need for a sync, just an atomic.
       G.Row[u].addEdgeOnVertex(u, v, w, firstAvailNeighbor$[u]);
 */
       // firstAvail$ must be passed by reference
       proc addEdgeOnVertex(to:nodeIdType, weight:edgeWeightType, firstAvail$: sync int) {
         on this do {
           // todo: the compiler should make these values local automatically!
           const /*u = from,*/ v = to, w = weight;
             // Lock and unlock should be within 'local', but currently
             // need to pull them out due to implementation.
             // lock the vertex
             const edgePos = firstAvail$;

           local {
             const prevNdomLen = ndom.high;
             if edgePos > prevNdomLen {
               // grow our arrays, by 2x
               // statistics: growCount += 1;
               ndom = {1..prevNdomLen * 2};
               // bounds checking below will ensure (edgePos <= ndom.high)
             }
             // store the edge
             if weighted {
               neighborList[edgePos] = (v, w);
             } else {
               neighborList[edgePos] = (v,);
             }
           }

             // release the lock
             firstAvail$ = edgePos + 1;
         } // on
       }

       // not parallel-safe
       proc tidyNeighbors(firstAvail$: sync int) {
         local {
           // no synchronization here
           var edgeCount = firstAvail$.readXX() - 1;
           RemoveDuplicates(1, edgeCount);
           // TODO: ideally if we don't save much memory, do not resize
           if edgeCount != ndom.numIndices {
             // statistics: shrinkCount += 1;
             ndom = 1..edgeCount;
           }
           // writeln("stats ", growCount, " ", shrinkCount, ".");
         }
       }
     } // record VertexData

     use BlockDist;

     // ------------------------------------------------------------------------
     // The data structures below are chosen to implement an irregular (sparse)
     // graph using rectangular domains and arrays.
     // Each node in the graph has a list of neighbors and a corresponding list
     // of (integer) weights for the implicit edges.
     // ------------------------------------------------------------------------

     /* store a graph
      */
     class Graph {
       type nodeIdType = int(64);
       param weighted = false;
       type edgeWeightType = int(64);

       const vertices; // generic type - domain of vertices

       param initialFirstAvail = 1;
       param initialLastAvail = 1;

       var   Row      : [vertices] VertexData(nodeIdType, weighted,
                                      edgeWeightType,
                                      initialFirstAvail, initialLastAvail);
       var num_edges = -1;

       /* iterate over all neighbor (ID, weight) pairs
          (actually returns an iterable rather than being
           an iterator).
        */
       proc NeighborPairs( v : index (vertices) ) {
         return Row (v).neighborList;
       }

       /* iterate over all neighbor IDs
        */
       iter Neighbors( v : index (vertices) ) {
         for nlElm in Row(v).neighborList do
           yield nlElm(1); // todo -- use nid
       }

       /* iterate over all neighbor IDs
        */
       iter Neighbors( v : index (vertices), param tag: iterKind)
       where tag == iterKind.leader {
         for block in Row(v).neighborList._value.these(tag) do
           yield block;
       }

       /* iterate over all neighbor IDs
        */
       iter Neighbors( v : index (vertices), param tag: iterKind, followThis)
       where tag == iterKind.follower {
         for nlElm in Row(v).neighborList._value.these(tag, followThis) do
           yield nElm(1);
       }

       /* iterate over all neighbor weights
        */
       iter edge_weight( v : index (vertices) ) {
         for nlElm in Row(v).neighborList do
           yield nlElm(2); // todo -- use VertexData.weight
       }

       /* iterate over all neighbor weights
        */
       iter edge_weight( v : index (vertices), param tag: iterKind)
       where tag == iterKind.leader {
         for block in Row(v).neighborList._value.these(tag) do
           yield block;
       }

       /* iterate over all neighbor weights
        */
       iter edge_weight( v : index (vertices), param tag: iterKind, followThis)
       where tag == iterKind.follower {
         for nlElm in Row(v).neighborList._value.these(tag, followThis) do
           yield nlElm(2); // todo -- use VertexData.weight
       }

       /* return the number of neighbors
        */
       proc   n_Neighbors (v : index (vertices) )
       {return Row (v).numNeighbors();}

     } // class Associative_Graph

     /* how to use Graph: e.g.
     const vertex_domain =
       if DISTRIBUTION_TYPE == "BLOCK" then
         {1..N_VERTICES} dmapped Block ( {1..N_VERTICES} )
       else
     {1..N_VERTICES} ;

     writeln("allocating Associative_Graph");
     var G = new Associative_Graph (vertex_domain);
     */

     /* Helps to construct a graph from row, column, value
        format.
     */
     proc buildUndirectedGraph(triples, param weighted:bool, vertices) where
       isRecordType(triples.eltType)
     {

       // sync version, one-pass, but leaves 0s in graph
       /*
       var r: triples.eltType;
       var G = new Graph(nodeIdType = r.to.type,
                         edgeWeightType = r.weight.type,
                         vertices = vertices);
       var firstAvailNeighbor$: [vertices] sync int = G.initialFirstAvail;
       forall trip in triples {
         var u = trip.from;
         var v = trip.to;
         var w = trip.weight;
         // Both the vertex and firstAvail must be passed by reference.
         // TODO: possibly compute how many neighbors the vertex has, first.
         // Then allocate that big of a neighbor list right away.
         // That way there will be no need for a sync, just an atomic.
         G.Row[u].addEdgeOnVertex(v, w, firstAvailNeighbor$[u]);
         G.Row[v].addEdgeOnVertex(u, w, firstAvailNeighbor$[v]);
       }*/

       // atomic version, tidier
       var r: triples.eltType;
       var G = new Graph(nodeIdType = r.to.type,
                         edgeWeightType = r.weight.type,
                         vertices = vertices,
                         initialLastAvail=0);
       var next$: [vertices] atomic int;

       forall x in next$ {
         next$.write(G.initialFirstAvail);
       }

       // Pass 1: count.
       forall trip in triples {
         var u = trip.from;
         var v = trip.to;
         var w = trip.weight;
         // edge from u to v will be represented in both u and v's edge
         // lists
         next$[u].add(1, memory_order_relaxed);
         next$[v].add(1, memory_order_relaxed);
       }
       // resize the edge lists
       forall v in vertices {
         var min = G.initialFirstAvail;
         var max = next$[v].read(memory_order_relaxed) - 1;
         G.Row[v].ndom = {min..max};
       }
       // reset all of the counters.
       forall x in next$ {
         next$.write(G.initialFirstAvail, memory_order_relaxed);
       }
       // Pass 2: populate.
       forall trip in triples {
         var u = trip.from;
         var v = trip.to;
         var w = trip.weight;
         // edge from u to v will be represented in both u and v's edge
         // lists
         var uslot = next$[u].fetchAdd(1, memory_order_relaxed);
         var vslot = next$[v].fetchAdd(1, memory_order_relaxed);
         G.Row[u].neighborList[uslot] = (v,);
         G.Row[v].neighborList[vslot] = (u,);
       }

       return G;
     }

     /*
      Take a general sparse matrix and turn it into a Graph. Mixed w/ this SO
        https://stackoverflow.com/questions/45846989/how-to-iterate-non-zeroes-in-a-sparse-matrix-in-chapel/46248469#46248469
      */
     proc buildFromSparseMatrix(A: [], param weighted:bool, param directed:bool) {
       const n = max reduce A.shape;
       writeln("A domain ", A.shape);
       writeln("n verts ", n);

       const vertices: domain(1) = {1..n};
       var G = new Graph(nodeIdType = int.type,
                         edgeWeightType = A.eltType,
                         vertices = vertices,
                         initialLastAvail=0);
       var next$: [vertices] atomic int;

       // Set number of edges per node, copying logic above but with a matrix
       if !directed {
         forall v in vertices {
           next$[v].write(G.initialFirstAvail);
         }
         // Increase the domain size at the right nodes
         for (u,v) in A.domain {
           var w = A[u,v];
           // edge from u to v will be represented in both u and v's edge
           // lists
           next$[u].add(1, memory_order_relaxed);
           next$[v].add(1, memory_order_relaxed);
         }
         // resize the edge lists
         forall v in vertices {
           var min = G.initialFirstAvail;
           var max = next$[v].read(memory_order_relaxed) - 1;
           G.Row[v].ndom = {min..max};
         }
         // reset all of the counters.
         forall x in next$ {
           next$.write(G.initialFirstAvail, memory_order_relaxed);
         }
         // Pass 2: populate.
         //forall trip in triples {
         forall (u,v) in A.domain {
           var w = A[u,v];
           // edge from u to v will be represented in both u and v's edge
           // lists
           var uslot = next$[u].fetchAdd(1, memory_order_relaxed);
           var vslot = next$[v].fetchAdd(1, memory_order_relaxed);
           G.Row[u].neighborList[uslot] = (v,);
           G.Row[v].neighborList[vslot] = (u,);
         }
       }

       // In this case just run along the rows, since no symmetry
       if directed {
         forall v in vertices {
           next$[v].write(G.initialFirstAvail);
           // There should be a way to bulk add, but I don't want to mess with it just yet.
           var t = 0;
           for c in A.domain.dimIter(2,v) do {
             t = t+1;
             next$[v].add(1, memory_order_relaxed);
             G.Row[v].ndom = {G.initialFirstAvail..t};
             G.Row[v].neighborList[t] = (c,);
           }
           //writeln(" row ", v, " nnzs: ", t);
         }
       }

       return G;
     }
 }

/*
 A simple Graph object.  This code was originally in the Chapel source, which can be found here:
 https://github.com/chapel-lang/chapel/blob/master/test/studies/labelprop/Graph.chpl

 */

 module Graph {

     //
     // VertexData: stores the neighbor list of a vertex.
     //
     /* private */
     record VertexData {
       type nodeIdType;
       param weighted; //hardcoded for now.
       type edgeWeightType;

       param initialFirstAvail;
       param initialLastAvail;

       //
       // We will represent the neighbor list as an array of nleType.
       // nle = Neighbor List Element.
       //

       //type nleType = (nodeIdType, edgeWeightType);
       type nleType = (nodeIdType,);
       var ndom = {initialFirstAvail..initialLastAvail};
       var neighborList: [ndom] nleType;
       var last = 0;

       param nid = 1;
       param weight = 2;

       proc numNeighbors()  return ndom.numIndices;
 /*
       var firstAvailNeighbor$: [vertex_domain] sync int = initialFirstAvail;

       // Both the vertex and firstAvail must be passed by reference.
       // TODO: possibly compute how many neighbors the vertex has, first.
       // Then allocate that big of a neighbor list right away.
       // That way there will be no need for a sync, just an atomic.
       G.Row[u].addEdgeOnVertex(u, v, w, firstAvailNeighbor$[u]);
 */
       // firstAvail$ must be passed by reference
       proc addEdgeOnVertex(to:nodeIdType, weight:edgeWeightType, firstAvail$: sync int) {
         on this do {
           // todo: the compiler should make these values local automatically!
           const /*u = from,*/ v = to, w = weight;
             // Lock and unlock should be within 'local', but currently
             // need to pull them out due to implementation.
             // lock the vertex
             const edgePos = firstAvail$;

           local {
             const prevNdomLen = ndom.high;
             if edgePos > prevNdomLen {
               // grow our arrays, by 2x
               // statistics: growCount += 1;
               ndom = {1..prevNdomLen * 2};
               // bounds checking below will ensure (edgePos <= ndom.high)
             }
             // store the edge
             if weighted {
               neighborList[edgePos] = (v, w);
             } else {
               neighborList[edgePos] = (v,);
             }
           }

             // release the lock
             firstAvail$ = edgePos + 1;
         } // on
       }

       // not parallel-safe
       proc tidyNeighbors(firstAvail$: sync int) {
         local {
           // no synchronization here
           var edgeCount = firstAvail$.readXX() - 1;
           RemoveDuplicates(1, edgeCount);
           // TODO: ideally if we don't save much memory, do not resize
           if edgeCount != ndom.numIndices {
             // statistics: shrinkCount += 1;
             ndom = 1..edgeCount;
           }
           // writeln("stats ", growCount, " ", shrinkCount, ".");
         }
       }
     } // record VertexData

     use BlockDist;

     // ------------------------------------------------------------------------
     // The data structures below are chosen to implement an irregular (sparse)
     // graph using rectangular domains and arrays.
     // Each node in the graph has a list of neighbors and a corresponding list
     // of (integer) weights for the implicit edges.
     // ------------------------------------------------------------------------

     /* store a graph
      */
     class Graph {
       type nodeIdType = int(64);
       param weighted = false;
       type edgeWeightType = int(64);

       const vertices; // generic type - domain of vertices

       param initialFirstAvail = 1;
       param initialLastAvail = 1;

       var   Row      : [vertices] VertexData(nodeIdType, weighted,
                                      edgeWeightType,
                                      initialFirstAvail, initialLastAvail);
       var num_edges = -1;

       /* iterate over all neighbor (ID, weight) pairs
          (actually returns an iterable rather than being
           an iterator).
        */
       proc NeighborPairs( v : index (vertices) ) {
         return Row (v).neighborList;
       }

       /* iterate over all neighbor IDs
        */
       iter Neighbors( v : index (vertices) ) {
         for nlElm in Row(v).neighborList do
           yield nlElm(1); // todo -- use nid
       }

       /* iterate over all neighbor IDs
        */
       iter Neighbors( v : index (vertices), param tag: iterKind)
       where tag == iterKind.leader {
         for block in Row(v).neighborList._value.these(tag) do
           yield block;
       }

       /* iterate over all neighbor IDs
        */
       iter Neighbors( v : index (vertices), param tag: iterKind, followThis)
       where tag == iterKind.follower {
         for nlElm in Row(v).neighborList._value.these(tag, followThis) do
           yield nElm(1);
       }

       /* iterate over all neighbor weights
        */
       iter edge_weight( v : index (vertices) ) {
         for nlElm in Row(v).neighborList do
           yield nlElm(2); // todo -- use VertexData.weight
       }

       /* iterate over all neighbor weights
        */
       iter edge_weight( v : index (vertices), param tag: iterKind)
       where tag == iterKind.leader {
         for block in Row(v).neighborList._value.these(tag) do
           yield block;
       }

       /* iterate over all neighbor weights
        */
       iter edge_weight( v : index (vertices), param tag: iterKind, followThis)
       where tag == iterKind.follower {
         for nlElm in Row(v).neighborList._value.these(tag, followThis) do
           yield nlElm(2); // todo -- use VertexData.weight
       }

       /* return the number of neighbors
        */
       proc   n_Neighbors (v : index (vertices) )
       {return Row (v).numNeighbors();}

     } // class Associative_Graph

     /* how to use Graph: e.g.
     const vertex_domain =
       if DISTRIBUTION_TYPE == "BLOCK" then
         {1..N_VERTICES} dmapped Block ( {1..N_VERTICES} )
       else
     {1..N_VERTICES} ;

     writeln("allocating Associative_Graph");
     var G = new Associative_Graph (vertex_domain);
     */

     /* Helps to construct a graph from row, column, value
        format.
     */
     proc buildUndirectedGraph(triples, param weighted:bool, vertices) where
       isRecordType(triples.eltType)
     {

       // sync version, one-pass, but leaves 0s in graph
       /*
       var r: triples.eltType;
       var G = new Graph(nodeIdType = r.to.type,
                         edgeWeightType = r.weight.type,
                         vertices = vertices);
       var firstAvailNeighbor$: [vertices] sync int = G.initialFirstAvail;
       forall trip in triples {
         var u = trip.from;
         var v = trip.to;
         var w = trip.weight;
         // Both the vertex and firstAvail must be passed by reference.
         // TODO: possibly compute how many neighbors the vertex has, first.
         // Then allocate that big of a neighbor list right away.
         // That way there will be no need for a sync, just an atomic.
         G.Row[u].addEdgeOnVertex(v, w, firstAvailNeighbor$[u]);
         G.Row[v].addEdgeOnVertex(u, w, firstAvailNeighbor$[v]);
       }*/

       // atomic version, tidier
       var r: triples.eltType;
       var G = new Graph(nodeIdType = r.to.type,
                         edgeWeightType = r.weight.type,
                         vertices = vertices,
                         initialLastAvail=0);
       var next$: [vertices] atomic int;

       forall x in next$ {
         next$.write(G.initialFirstAvail);
       }

       // Pass 1: count.
       forall trip in triples {
         var u = trip.from;
         var v = trip.to;
         var w = trip.weight;
         // edge from u to v will be represented in both u and v's edge
         // lists
         next$[u].add(1, memory_order_relaxed);
         next$[v].add(1, memory_order_relaxed);
       }
       // resize the edge lists
       forall v in vertices {
         var min = G.initialFirstAvail;
         var max = next$[v].read(memory_order_relaxed) - 1;
         G.Row[v].ndom = {min..max};
       }
       // reset all of the counters.
       forall x in next$ {
         next$.write(G.initialFirstAvail, memory_order_relaxed);
       }
       // Pass 2: populate.
       forall trip in triples {
         var u = trip.from;
         var v = trip.to;
         var w = trip.weight;
         // edge from u to v will be represented in both u and v's edge
         // lists
         var uslot = next$[u].fetchAdd(1, memory_order_relaxed);
         var vslot = next$[v].fetchAdd(1, memory_order_relaxed);
         G.Row[u].neighborList[uslot] = (v,);
         G.Row[v].neighborList[vslot] = (u,);
       }

       return G;
     }

     /*
      Take a general sparse matrix and turn it into a Graph. Mixed w/ this SO
        https://stackoverflow.com/questions/45846989/how-to-iterate-non-zeroes-in-a-sparse-matrix-in-chapel/46248469#46248469
      */
     proc buildFromSparseMatrix(A: [], param weighted:bool, param directed:bool) {
       const n = max reduce A.shape;
       writeln("A domain ", A.shape);
       writeln("n verts ", n);

       const vertices: domain(1) = {1..n};
       var G = new Graph(nodeIdType = int.type,
                         edgeWeightType = A.eltType,
                         vertices = vertices,
                         initialLastAvail=0);
       var next$: [vertices] atomic int;

       // Set number of edges per node, copying logic above but with a matrix
       if !directed {
         forall v in vertices {
           next$[v].write(G.initialFirstAvail);
         }
         // Increase the domain size at the right nodes
         for (u,v) in A.domain {
           var w = A[u,v];
           // edge from u to v will be represented in both u and v's edge
           // lists
           next$[u].add(1, memory_order_relaxed);
           next$[v].add(1, memory_order_relaxed);
         }
         // resize the edge lists
         forall v in vertices {
           var min = G.initialFirstAvail;
           var max = next$[v].read(memory_order_relaxed) - 1;
           G.Row[v].ndom = {min..max};
         }
         // reset all of the counters.
         forall x in next$ {
           next$.write(G.initialFirstAvail, memory_order_relaxed);
         }
         // Pass 2: populate.
         //forall trip in triples {
         forall (u,v) in A.domain {
           //var u = trip.from;
           //var v = trip.to;
           var w = A[u,v];
           // edge from u to v will be represented in both u and v's edge
           // lists
           var uslot = next$[u].fetchAdd(1, memory_order_relaxed);
           var vslot = next$[v].fetchAdd(1, memory_order_relaxed);
           G.Row[u].neighborList[uslot] = (v,);
           G.Row[v].neighborList[vslot] = (u,);
         }
       }

       // In this case just run along the rows, since no symmetry
       if directed {
         forall v in vertices {
           next$[v].write(G.initialFirstAvail);
           // There should be a way to bulk add, but I don't want to mess with it just yet.
           var t = 0;
           for c in A.domain.dimIter(2,v) do {
             t = t+1;
             next$[v].add(1, memory_order_relaxed);
             G.Row[v].ndom = {G.initialFirstAvail..t};
             G.Row[v].neighborList[t] = (c,);
           }
           //writeln(" row ", v, " nnzs: ", t);
         }
       }

       return G;
     }
 }