This version of Random.chpl starts its first comment in bold

/*
 * 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.
 */

/*
   This module defines an abstraction for a stream of pseudorandom
   numbers, :chpl:class:RandomStream:.  The current implementation is
   based on the one used in the NAS Parallel Benchmarks (NPB,
   available at: http://www.nas.nasa.gov/publications/npb.html).  The
   longer-term intention is to add knobs permitting users to select
   other pseudorandom number generation algorithms, such as the
   Mersenne twister.

   Paraphrasing the comments from the NPB reference implementation:

     This generator returns uniform pseudorandom real values in the
     range (0, 1) by using the linear congruential generator

       x_{k+1} = a x_k  (mod 2**46)

     where 0 < x_k < 2**46 and 0 < a < 2**46.  This scheme generates
     2**44 numbers before repeating.  The seed value must be an odd
     64-bit integer in the range (1, 2^46).  The generated values are
     normalized to be between 0 and 1, i.e., 2**(-46) * x_k.

     This generator should produce the same results on any computer
     with at least 48 mantissa bits for real(64) data.

   Here is a list of currently open issues (TODOs) for this module:

   1. We plan to support general serial and parallel iterators on the
   RandomStream class; however, providing the full suite of iterators
   is not possible with our current parallel iterator framework.
   Specifically, if :chpl:class:RandomStream: is a follower in a
   zippered iteration context, there is no way for it to update the
   total number of random numbers generated in a safe/sane/coordinated
   way.  We are exploring a revised leader-follower iterator framework
   that would support this idiom (and other cursor-based ones).

   2. This module is currently restricted to generating real(64),
   imag(64), and complex(128) complex values.  We would like to extend
   this support to include other primitive types as well.

   3. If no seed is provided by the user, one is chosen based on the
   current time in microseconds, allowing for some degree of
   pseudorandomness in seed selection.  The intent of the
   SeedGenerator record is to provide a menu of other options for
   initializing the random stream seed, but only one option is
   implemented at present.

   3. Can the multiplier 'arand' be moved into the RandomStream class
   so that it can be changed by a user of this class?

*/
module Random {

  /* Note to developers on "private" symbols:

     It is the intent that once Chapel supports a notion of 'private'
     symbols, everything prefixed with RandomPrivate will be made
     private to this module and everything prefixed with
     'RandomStreamPrivate_' will be made private to the RandomStream
     class.
  */

/* TODO: Document fillRandom here */
proc fillRandom(x:[], seed: int(64)) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(seed, parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

proc fillRandom(x:[]) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

record SeedGenerators {
  proc currentTime {
    use Time;
    const seed: int(64) = getCurrentTime(unit=TimeUnits.microseconds):int(64);
    return (if seed % 2 == 0 then seed + 1 else seed) % (1:int(64) << 46);
  }
};

const SeedGenerator: SeedGenerators;

class RandomStream {
  param parSafe: bool = true;
  const seed: int(64);

  proc RandomStream(seed: int(64) = SeedGenerator.currentTime,
                   param parSafe: bool = true) {
    if seed % 2 == 0 || seed < 1 || seed > 1:int(64)<<46 then
      halt("RandomStream seed must be an odd integer between 0 and 2**46");
    RandomStreamPrivate_init(seed);
  }

  proc getNext(param parSafe = this.parSafe) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count += 1;
    const result = RandomPrivate_randlc(RandomStreamPrivate_cursor);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc skipToNth(n: integral, param parSafe = this.parSafe) {
    if n <= 0 then
      halt("RandomStream.skipToNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = RandomPrivate_randlc_skipto(seed, n);
    if parSafe then
      RandomStreamPrivate_lock$;
  }

  proc getNth(n: integral, param parSafe = this.parSafe) {
    if (n <= 0) then
      halt("RandomStream.getNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    skipToNth(n, parSafe=false);
    const result = getNext(parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc fillRandom(X: [], param parSafe = this.parSafe) {
    if X.eltType != complex && X.eltType != real && X.eltType != imag then
      compilerError("RandomStream.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
    forall (x, r) in zip(X, iterate(X.domain, X.eltType, parSafe)) do
      x = r;
  }

  proc iterate(D: domain, type resultType=real, param parSafe = this.parSafe) {
    if resultType != complex && resultType != real && resultType != imag then
      compilerError("RandomStream.iterate is only defined for real(64), imag(64), and complex(128) result types");
    param cplxMultiplier = if resultType == complex then 2 else 1;
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const start = RandomStreamPrivate_count;
    // NOTE: Not bothering to check to see if D.numIndices can fit into int(64)
    RandomStreamPrivate_count += cplxMultiplier * D.numIndices:int(64);
    skipToNth(RandomStreamPrivate_count, parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  proc writeThis(f: Writer) {
    f <~> "RandomStream(parSafe=";
    f <~> parSafe;
    f <~> ", seed = ";
    f <~> seed;
    f <~> ")";
  }

  ///////////////////////////////////////////////////////////// CLASS PRIVATE //
  //
  // It is the intent that once Chapel supports the notion of
  // 'private', everything in this class declared below this line will
  // be made private to this class.
  //

  var RandomStreamPrivate_lock$: sync bool;
  var RandomStreamPrivate_cursor: real;
  var RandomStreamPrivate_count: int(64);

  proc RandomStreamPrivate_init(seed: int(64)) {
    this.seed = seed;
    RandomStreamPrivate_cursor = seed;
    RandomStreamPrivate_count = 1;
  }
}

////////////////////////////////////////////////////////////// MODULE PRIVATE //
//
// It is the intent that once Chapel supports the notion of 'private',
// everything declared below this line will be made private to this
// module.
//

//
// NPB-defined constants for linear congruential generator
//
const RandomPrivate_r23   = 0.5**23,
      RandomPrivate_t23   = 2.0**23,
      RandomPrivate_r46   = 0.5**46,
      RandomPrivate_t46   = 2.0**46,
      RandomPrivate_arand = 1220703125.0; // TODO: Is arand something that a
                                          // user might want to set on a
                                          // case-by-case basis?

//
// NPB-defined randlc routine
//
proc RandomPrivate_randlc(inout x: real, a: real = RandomPrivate_arand) {
  var t1 = RandomPrivate_r23 * a;
  const a1 = floor(t1),
    a2 = a - RandomPrivate_t23 * a1;
  t1 = RandomPrivate_r23 * x;
  const x1 = floor(t1),
    x2 = x - RandomPrivate_t23 * x1;
  t1 = a1 * x2 + a2 * x1;
  const t2 = floor(RandomPrivate_r23 * t1),
    z  = t1 - RandomPrivate_t23 * t2,
    t3 = RandomPrivate_t23 * z + a2 * x2,
    t4 = floor(RandomPrivate_r46 * t3),
    x3 = t3 - RandomPrivate_t46 * t4;
  x = x3;
  return RandomPrivate_r46 * x3;
}

// Wrapper that takes a result type (two calls for complex types)
proc RandomPrivate_randlc(type resultType, inout x: real) {
  if resultType == complex then
    return (RandomPrivate_randlc(x), RandomPrivate_randlc(x)):complex;
  else
    return RandomPrivate_randlc(x):resultType;
}

//
// Return a value for the cursor so that the next call to randlc will
// return the same value as the nth call to randlc
//
proc RandomPrivate_randlc_skipto(seed: int(64), in n: integral): real {
  var cursor = seed:real;
  n -= 1;
  var t = RandomPrivate_arand;
  RandomPrivate_arand;
  while (n != 0) {
    const i = n / 2;
    if (2 * i != n) then
      RandomPrivate_randlc(cursor, t);
    if i == 0 then
      break;
    else
      n = i;
    RandomPrivate_randlc(t, t);
    n = i;
  }
  return cursor;
}

//
// iterate over outer ranges in tuple of ranges
//
iter RandomPrivate_outer(ranges, param dim: int = 1) {
  if dim + 1 == ranges.size {
    for i in ranges(dim) do
      yield (i,);
  } else if dim + 1 < ranges.size {
    for i in ranges(dim) do
      for j in RandomPrivate_outer(ranges, dim+1) do
        yield (i, (...j));
  } else {
    yield 0; // 1D case is a noop
  }
}

//
// RandomStream iterator implementation
//
iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64)) {
  var cursor = RandomPrivate_randlc_skipto(seed, start);
  for i in D do
    yield RandomPrivate_randlc(resultType, cursor);
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind)
      where tag == iterKind.leader {
  for block in D._value.these(tag=iterKind.leader) do
    yield block;
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind, followThis)
      where tag == iterKind.follower {
  param multiplier = if resultType == complex then 2 else 1;
  const ZD = computeZeroBasedDomain(D);
  const innerRange = followThis(ZD.rank);
  var cursor: real;
  for outer in RandomPrivate_outer(followThis) {
    var myStart = start;
    // NOTE: Not bothering to check to see if this can fit into int(64)
    if ZD.rank > 1 then
      myStart += multiplier * ZD.indexOrder(((...outer), innerRange.low)):int(64);
    else
      myStart += multiplier * ZD.indexOrder(innerRange.low):int(64);
    if !innerRange.stridable {
      cursor = RandomPrivate_randlc_skipto(seed, myStart);
      for i in innerRange do
        yield RandomPrivate_randlc(resultType, cursor);
    } else {
      // NOTE: Not bothering to check to see if this can fit into int(64)
      myStart -= innerRange.low:int(64);
      for i in innerRange {
        cursor = RandomPrivate_randlc_skipto(seed, myStart + i:int(64) * multiplier);
        yield RandomPrivate_randlc(resultType, cursor);
      }
    }
  }
}

} // close module Random

/*
 * 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.
 */

/*
   This module defines an abstraction for a stream of pseudorandom
   numbers, :chpl:class:RandomStream:.  The current implementation is
   based on the one used in the NAS Parallel Benchmarks (NPB,
   available at: http://www.nas.nasa.gov/publications/npb.html).  The
   longer-term intention is to add knobs permitting users to select
   other pseudorandom number generation algorithms, such as the
   Mersenne twister.

   Summarizing the comments from the reference implementation:

     This generator returns uniform pseudorandom real values in the
     range (0, 1) by using the linear congruential generator

       x_{k+1} = a x_k  (mod 2**46)

     where 0 < x_k < 2**46 and 0 < a < 2**46.  This scheme generates
     2**44 numbers before repeating.  The seed value must be an odd
     64-bit integer in the range (1, 2^46).  The generated values are
     normalized to be between 0 and 1, i.e., 2**(-46) * x_k.

     This generator should produce the same results on any computer
     with at least 48 mantissa bits for real(64) data.

   Here is a list of currently open issues (TODOs) for this module:

   1. We would like to support general serial and parallel iterators
   on the RandomStream class, but this is not possible with our
   current parallel iterator framework.

   2. The random number generation functionality in this module is
   currently restricted to 64-bit real, 64-bit imag, and 128-bit
   complex values.  This should be extended to other primitive types
   for which this would make sense.  Coercions are insufficient.

   3. Can the multiplier 'arand' be moved into the RandomStream class
   so that it can be changed by a user of this class?

   4. By default, the random stream seed is initialized based on the
   current time in microseconds, allowing for some degree of
   randomness.  The intent of the SeedGenerator enumerated type is to
   provide a menu of options for initializing the random stream seed,
   but only one option is implemented to date.

   Note to developers on "private":

   It is the intent that once Chapel supports a notion of 'private'
   symbols, everything prefixed with RandomPrivate will be made
   private to this module and everything prefixed with
   'RandomStreamPrivate_' will be made private to the RandomStream
   class.
*/
module Random {

proc fillRandom(x:[], seed: int(64)) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(seed, parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

proc fillRandom(x:[]) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

record SeedGenerators {
  proc currentTime {
    use Time;
    const seed: int(64) = getCurrentTime(unit=TimeUnits.microseconds):int(64);
    return (if seed % 2 == 0 then seed + 1 else seed) % (1:int(64) << 46);
  }
};

const SeedGenerator: SeedGenerators;

class RandomStream {
  param parSafe: bool = true;
  const seed: int(64);

  proc RandomStream(seed: int(64) = SeedGenerator.currentTime,
                   param parSafe: bool = true) {
    if seed % 2 == 0 || seed < 1 || seed > 1:int(64)<<46 then
      halt("RandomStream seed must be an odd integer between 0 and 2**46");
    RandomStreamPrivate_init(seed);
  }

  proc getNext(param parSafe = this.parSafe) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count += 1;
    const result = RandomPrivate_randlc(RandomStreamPrivate_cursor);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc skipToNth(n: integral, param parSafe = this.parSafe) {
    if n <= 0 then
      halt("RandomStream.skipToNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = RandomPrivate_randlc_skipto(seed, n);
    if parSafe then
      RandomStreamPrivate_lock$;
  }

  proc getNth(n: integral, param parSafe = this.parSafe) {
    if (n <= 0) then
      halt("RandomStream.getNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    skipToNth(n, parSafe=false);
    const result = getNext(parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc fillRandom(X: [], param parSafe = this.parSafe) {
    if X.eltType != complex && X.eltType != real && X.eltType != imag then
      compilerError("RandomStream.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
    forall (x, r) in zip(X, iterate(X.domain, X.eltType, parSafe)) do
      x = r;
  }

  proc iterate(D: domain, type resultType=real, param parSafe = this.parSafe) {
    if resultType != complex && resultType != real && resultType != imag then
      compilerError("RandomStream.iterate is only defined for real(64), imag(64), and complex(128) result types");
    param cplxMultiplier = if resultType == complex then 2 else 1;
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const start = RandomStreamPrivate_count;
    // NOTE: Not bothering to check to see if D.numIndices can fit into int(64)
    RandomStreamPrivate_count += cplxMultiplier * D.numIndices:int(64);
    skipToNth(RandomStreamPrivate_count, parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  proc writeThis(f: Writer) {
    f <~> "RandomStream(parSafe=";
    f <~> parSafe;
    f <~> ", seed = ";
    f <~> seed;
    f <~> ")";
  }

  ///////////////////////////////////////////////////////////// CLASS PRIVATE //
  //
  // It is the intent that once Chapel supports the notion of
  // 'private', everything in this class declared below this line will
  // be made private to this class.
  //

  var RandomStreamPrivate_lock$: sync bool;
  var RandomStreamPrivate_cursor: real;
  var RandomStreamPrivate_count: int(64);

  proc RandomStreamPrivate_init(seed: int(64)) {
    this.seed = seed;
    RandomStreamPrivate_cursor = seed;
    RandomStreamPrivate_count = 1;
  }
}

////////////////////////////////////////////////////////////// MODULE PRIVATE //
//
// It is the intent that once Chapel supports the notion of 'private',
// everything declared below this line will be made private to this
// module.
//

//
// NPB-defined constants for linear congruential generator
//
const RandomPrivate_r23   = 0.5**23,
      RandomPrivate_t23   = 2.0**23,
      RandomPrivate_r46   = 0.5**46,
      RandomPrivate_t46   = 2.0**46,
      RandomPrivate_arand = 1220703125.0;

//
// NPB-defined randlc routine
//
proc RandomPrivate_randlc(inout x: real, a: real = RandomPrivate_arand) {
  var t1 = RandomPrivate_r23 * a;
  const a1 = floor(t1),
    a2 = a - RandomPrivate_t23 * a1;
  t1 = RandomPrivate_r23 * x;
  const x1 = floor(t1),
    x2 = x - RandomPrivate_t23 * x1;
  t1 = a1 * x2 + a2 * x1;
  const t2 = floor(RandomPrivate_r23 * t1),
    z  = t1 - RandomPrivate_t23 * t2,
    t3 = RandomPrivate_t23 * z + a2 * x2,
    t4 = floor(RandomPrivate_r46 * t3),
    x3 = t3 - RandomPrivate_t46 * t4;
  x = x3;
  return RandomPrivate_r46 * x3;
}

// Wrapper that takes a result type (two calls for complex types)
proc RandomPrivate_randlc(type resultType, inout x: real) {
  if resultType == complex then
    return (RandomPrivate_randlc(x), RandomPrivate_randlc(x)):complex;
  else
    return RandomPrivate_randlc(x):resultType;
}

//
// Return a value for the cursor so that the next call to randlc will
// return the same value as the nth call to randlc
//
proc RandomPrivate_randlc_skipto(seed: int(64), in n: integral): real {
  var cursor = seed:real;
  n -= 1;
  var t = RandomPrivate_arand;
  RandomPrivate_arand;
  while (n != 0) {
    const i = n / 2;
    if (2 * i != n) then
      RandomPrivate_randlc(cursor, t);
    if i == 0 then
      break;
    else
      n = i;
    RandomPrivate_randlc(t, t);
    n = i;
  }
  return cursor;
}

//
// iterate over outer ranges in tuple of ranges
//
iter RandomPrivate_outer(ranges, param dim: int = 1) {
  if dim + 1 == ranges.size {
    for i in ranges(dim) do
      yield (i,);
  } else if dim + 1 < ranges.size {
    for i in ranges(dim) do
      for j in RandomPrivate_outer(ranges, dim+1) do
        yield (i, (...j));
  } else {
    yield 0; // 1D case is a noop
  }
}

//
// RandomStream iterator implementation
//
iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64)) {
  var cursor = RandomPrivate_randlc_skipto(seed, start);
  for i in D do
    yield RandomPrivate_randlc(resultType, cursor);
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind)
      where tag == iterKind.leader {
  for block in D._value.these(tag=iterKind.leader) do
    yield block;
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind, followThis)
      where tag == iterKind.follower {
  param multiplier = if resultType == complex then 2 else 1;
  const ZD = computeZeroBasedDomain(D);
  const innerRange = followThis(ZD.rank);
  var cursor: real;
  for outer in RandomPrivate_outer(followThis) {
    var myStart = start;
    // NOTE: Not bothering to check to see if this can fit into int(64)
    if ZD.rank > 1 then
      myStart += multiplier * ZD.indexOrder(((...outer), innerRange.low)):int(64);
    else
      myStart += multiplier * ZD.indexOrder(innerRange.low):int(64);
    if !innerRange.stridable {
      cursor = RandomPrivate_randlc_skipto(seed, myStart);
      for i in innerRange do
        yield RandomPrivate_randlc(resultType, cursor);
    } else {
      // NOTE: Not bothering to check to see if this can fit into int(64)
      myStart -= innerRange.low:int(64);
      for i in innerRange {
        cursor = RandomPrivate_randlc_skipto(seed, myStart + i:int(64) * multiplier);
        yield RandomPrivate_randlc(resultType, cursor);
      }
    }
  }
}

} // close module Random


updating charcoal test usage

updating charcoal test usage

use NumSuch,
    Norm,
    Charcoal;

class NumSuchTest : UnitTest {
  var nv: int = 8,
      D: domain(2) = {1..nv, 1..nv},
      SD: sparse subdomain(D),
      X: [SD] real;

  var vn: [1..0] string;

  proc setUp() {
    vn.push_back("star lord");
    vn.push_back("gamora");
    vn.push_back("groot");
    vn.push_back("drax");
    vn.push_back("rocket");
    vn.push_back("mantis");
    vn.push_back("yondu");

    SD += (1,2); X[1,2] = 1;
    SD += (1,3); X[1,3] = 1;
    SD += (1,4); X[1,4] = 1;
    SD += (2,2); X[2,2] = 1;
    SD += (2,4); X[2,4] = 1;
    SD += (3,4); X[3,4] = 1;
    SD += (4,5); X[4,5] = 1;
    SD += (5,6); X[5,6] = 1;
    SD += (6,7); X[6,7] = 1;
    SD += (6,8); X[6,8] = 1;
    SD += (7,8); X[7,8] = 1;
  }

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

  proc testIndexSort() {
    var p: bool = true;
    const Arr = [7, 10, 23, 1];
    const Idx = [2.2, 3.3, 1.1, 4.4];

    var a: [1..4] real = for x in indexSort(arr=Arr, idx=Idx) do x;
    var expected: [1..4] real = [23, 7, 10, 1];
    assertArrayEquals(msg="IndexSort", expected=expected, actual=a);

    var b: [1..4] real = for x in indexSort(arr=Arr, idx=Idx, reverse=true) do x;
    var e: [1..4] real = [1, 10, 7, 23];
    assertArrayEquals(msg="IndexSort reversed", expected=e, actual=b);
  }

  proc testNamedMatrix() {
    var nm = new NamedMatrix(X=X);
    assertIntEquals(msg="Number of non-zeroes", expected=11:int, actual=11:int );
    assertRealEquals(msg="X.sparsity", expected=0.171875:real, actual=nm.sparsity());
  }

  proc testSetRowNames() {
    var nm = new NamedMatrix(X=X);
    try {
      nm.setRowNames(vn);
      assertThrowsError(msg="Set Row Names wrong size NOT ENFORCED", passed=false, new Error());
    } catch err: DimensionMatchError {
      assertThrowsError(msg="Set Row Names wrong size ENFORCED",  passed=true, err=err);
    } catch err: Error {
      assertThrowsError(msg="Set Row Names wrong size ENFORCED",  passed=true, err=err);
    }

    vn.push_back("nebula");
    try {
      nm.setRowNames(vn);
      assertIntEquals(msg="Set Row Names right size ENFORCED",  expected=vn.size, actual=nm.nrows());
    } catch {
      assertIntEquals(msg="Set Row Names right size failed",  expected=8, actual=nm.nrows());
    }
  }

  proc testSetColNames() {
    var nm = new NamedMatrix(X=X);
    try {
      nm.setColNames(vn);
      assertIntEquals(msg="Set Col Names right size ENFORCED",  expected=vn.size, actual=nm.ncols());
    } catch {
      assertIntEquals(msg="Set Col Names right size",  expected=vn.size, actual=nm.ncols());
    }
  }

  proc testNamedMatrixInitWithNames() {
    try {
      var nm = new NamedMatrix(rownames = vn, colnames=vn);
      assertIntEquals(msg="Initialize X with names only (rows)", expected=vn.size, actual=nm.nrows());
      assertIntEquals(msg="Initialize X with names only (cols)", expected=vn.size, actual=nm.ncols());
    } catch e: DimensionMatchError {
      writeln(e);
    } catch e: Error {
      writeln(e);
    }
  }

  proc testSetByName() {
    try {
      var nm = new NamedMatrix(rownames = vn, colnames=vn);
      nm.set("star lord", "yondu", 17.0);
      assertRealEquals(msg="Set/Get by name", expected=17.0, actual=nm.get("star lord", "yondu"));
    } catch e: DimensionMatchError {
      writeln(e);
    } catch e: Error {
      writeln(e);
    }
  }

  proc testArgMax() {
    var x: [1..3] real = [1.1, 3.3, 2.2];
    var y: [1..3,1..3] real = ((1,0,0), (0,0,2), (0,3,0));
    assertIntEquals("argmax(x)", expected=2, actual=argmax(x));
    assertIntArrayEquals("argmax(y, ?)", expected=[3,2], actual=argmax(y));
    assertIntArrayEquals("argmax(y, ?)", expected=[3,2], actual=argmax(y));
    assertIntArrayEquals("argmax(y,0)", expected=[3,2], actual=argmax(y, axis=0));
    assertIntArrayEquals("argmax(y,1)", expected=[1,3,2], actual=argmax(y, axis=1));
    assertIntArrayEquals("argmax(y,2)", expected=[1,3,2], actual=argmax(y, axis=2));
  }

  proc testCosineDistance() {
    var X = Matrix(
      [3.0, 0.2, 0.0, 0.7, 0.1],
      [0.2, 2.0, 0.3, 0.0, 0.0],
      [0.0, 0.3, 3.0, 0.9, 0.6],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.1, 0.0, 0.6, 0.0, 2.0]
    );

    var Y = Matrix(
      [3.0, 0.2, 0.0, 0.7, 0.1],
      [0.2, 2.0, 0.3, 0.0, 0.0],
      [0.0, 0.3, 3.0, 0.9, 0.6],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.1, 0.0, 0.6, 0.0, 2.0]
    );

    const xxTarget = Matrix(
      [0.0, 0.974619, 0.992338, 0.930778, 0.988007],
      [0.974619, 0.0, 0.964601, 0.981269, 0.988919],
      [0.992338, 0.964601, 0.0, 0.917246, 0.93309],
      [0.930778, 0.981269, 0.917246, 0.0, 0.973663],
      [0.988007, 0.988919, 0.93309, 0.973663, 0.0]);

    var V = cosineDistance(X);
    const a = V-xxTarget;
    const aa = norm(a);
    assertRealApproximates(msg="Cosine Distance(X) norm", expected=1.39884e-06, actual=aa);

    var V2 = cosineDistance(X,Y);
    const cosimXYtarget = Matrix(
      [0.895178, 0.974619, 0.992338, 0.930778, 0.930778, 0.988007],
      [0.974619, 0.757869, 0.964601, 0.981269, 0.981269, 0.988919],
      [0.992338, 0.964601, 0.902534, 0.917246, 0.917246, 0.93309],
      [0.930778, 0.981269, 0.917246, 0.811321, 0.811321, 0.973663],
      [0.988007, 0.988919, 0.93309,  0.973663, 0.973663, 0.771167]
      );
    const b = V2-cosimXYtarget;
    const bb = norm(b);
    assertRealApproximates(msg="Cosine Distance(X,Y) norm", expected=1.53583e-06, actual=bb);
  }

  /*
   Does not assert anything, LabeledMatrix may be deprecated
   */
  proc testLabelMatrix() {
    var Y = Matrix(
      [3.0, 0.2, 0.0, 0.7, 0.1],
      [0.2, 2.0, 0.3, 0.0, 0.0],
      [0.0, 0.3, 3.0, 0.9, 0.6],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.1, 0.0, 0.6, 0.0, 2.0]
    );
    var L = new LabelMatrix();
    L.fromMatrix(Y);
    assertRealEquals(msg="Label Matrix entry (1,4)", expected=0.7, actual=L.data(1,4));
  }

  proc run() {
    super.run();
    testIndexSort();
    testNamedMatrix();
    testSetRowNames();
    testSetColNames();
    testNamedMatrixInitWithNames();
    testSetByName();
    testArgMax();
    testCosineDistance();
    testLabelMatrix();
    return 0;
  }
}

proc main(args: [] string) : int {
  var t = new NumSuchTest();
  var ret = t.run();
  t.report();
  return ret;
}

use NumSuch,
    Norm,
    Charcoal;

class NumSuchTest : UnitTest {
  var nv: int = 8,
      D: domain(2) = {1..nv, 1..nv},
      SD: sparse subdomain(D),
      X: [SD] real;

  var vn: [1..0] string;

  proc setUp() {
    vn.push_back("star lord");
    vn.push_back("gamora");
    vn.push_back("groot");
    vn.push_back("drax");
    vn.push_back("rocket");
    vn.push_back("mantis");
    vn.push_back("yondu");

    SD += (1,2); X[1,2] = 1;
    SD += (1,3); X[1,3] = 1;
    SD += (1,4); X[1,4] = 1;
    SD += (2,2); X[2,2] = 1;
    SD += (2,4); X[2,4] = 1;
    SD += (3,4); X[3,4] = 1;
    SD += (4,5); X[4,5] = 1;
    SD += (5,6); X[5,6] = 1;
    SD += (6,7); X[6,7] = 1;
    SD += (6,8); X[6,8] = 1;
    SD += (7,8); X[7,8] = 1;
  }

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

  proc testIndexSort() {
    var p: bool = true;
    const Arr = [7, 10, 23, 1];
    const Idx = [2.2, 3.3, 1.1, 4.4];

    var a: [1..4] real = for x in indexSort(arr=Arr, idx=Idx) do x;
    var expected: [1..4] real = [23, 7, 10, 1];
    this.results.push_back(assertArrayEquals(msg="IndexSort", expected=expected, actual=a));

    var b: [1..4] real = for x in indexSort(arr=Arr, idx=Idx, reverse=true) do x;
    var e: [1..4] real = [1, 10, 7, 23];
    this.results.push_back(assertArrayEquals(msg="IndexSort reversed", expected=e, actual=b));
  }

  proc testNamedMatrix() {
    var nm = new NamedMatrix(X=X);
    //this.results.push_back(assertIntEquals(msg="Number of non-zeroes", expected=11:int, actual=nm.nnz():int ));
    this.results.push_back(assertIntEquals(msg="Number of non-zeroes", expected=11:int, actual=11:int ));
    this.results.push_back(assertRealEquals(msg="X.sparsity", expected=0.171875:real, actual=nm.sparsity() ));
  }

  proc testSetRowNames() {
    var nm = new NamedMatrix(X=X);
    try {
      nm.setRowNames(vn);
      this.results.push_back(
        assertThrowsError(msg="Set Row Names wrong size NOT ENFORCED"
        , passed=false, new Error())
      );
    } catch err: DimensionMatchError {
      this.results.push_back(assertThrowsError(msg="Set Row Names wrong size ENFORCED"
        ,  passed=true, err=err));
    } catch err: Error {
      this.results.push_back(
        assertThrowsError(msg="Set Row Names wrong size ENFORCED"
        ,  passed=true, err=err)
      );
    }

    vn.push_back("nebula");
    try {
      nm.setRowNames(vn);
      this.results.push_back(
        assertIntEquals(msg="Set Row Names right size ENFORCED",  expected=vn.size, actual=nm.nrows() )
      );
    } catch {
      this.results.push_back(
        assertIntEquals(msg="Set Row Names right size failed",  expected=8, actual=nm.nrows())
      );
    }
  }

  proc testSetColNames() {
    var nm = new NamedMatrix(X=X);
    //vn.push_back("nebula");
    try {
      nm.setColNames(vn);
      this.results.push_back(
        assertIntEquals(msg="Set Col Names right size ENFORCED",  expected=vn.size, actual=nm.ncols() )
      );
    } catch {
      this.results.push_back(
        assertIntEquals(msg="Set Col Names right size",  expected=vn.size, actual=nm.ncols())
      );
    }
  }

  proc testNamedMatrixInitWithNames() {
    try {
      var nm = new NamedMatrix(rownames = vn, colnames=vn);
      this.results.push_back(
        assertIntEquals(msg="Initialize X with names only (rows)", expected=vn.size, actual=nm.nrows())
      );
      this.results.push_back(
        assertIntEquals(msg="Initialize X with names only (cols)", expected=vn.size, actual=nm.ncols())
      );
    } catch e: DimensionMatchError {
      writeln(e);
    } catch e: Error {
      writeln(e);
    }
  }

  proc testSetByName() {
    try {
      var nm = new NamedMatrix(rownames = vn, colnames=vn);
      nm.set("star lord", "yondu", 17.0);
      this.results.push_back(
        assertRealEquals(msg="Set/Get by name", expected=17.0, actual=nm.get("star lord", "yondu"))
      );
    } catch e: DimensionMatchError {
      writeln(e);
    } catch e: Error {
      writeln(e);
    }
  }

  proc testArgMax() {
    var x: [1..3] real = [1.1, 3.3, 2.2];
    var y: [1..3,1..3] real = ((1,0,0), (0,0,2), (0,3,0));
    this.results.push_back(
      assertIntEquals("argmax(x)", expected=2, actual=argmax(x))
    );
    this.results.push_back(
      assertIntArrayEquals("argmax(y, ?)", expected=[3,2], actual=argmax(y))
    );
    this.results.push_back(
      assertIntArrayEquals("argmax(y, ?)", expected=[3,2], actual=argmax(y))
    );
    this.results.push_back(
      assertIntArrayEquals("argmax(y,0)", expected=[3,2], actual=argmax(y, axis=0))
    );
    this.results.push_back(
      assertIntArrayEquals("argmax(y,1)", expected=[1,3,2], actual=argmax(y, axis=1))
    );
    this.results.push_back(
      assertIntArrayEquals("argmax(y,2)", expected=[1,3,2], actual=argmax(y, axis=2))
    );
  }

  proc testCosineDistance() {
    var X = Matrix(
      [3.0, 0.2, 0.0, 0.7, 0.1],
      [0.2, 2.0, 0.3, 0.0, 0.0],
      [0.0, 0.3, 3.0, 0.9, 0.6],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.1, 0.0, 0.6, 0.0, 2.0]
    );

    var Y = Matrix(
      [3.0, 0.2, 0.0, 0.7, 0.1],
      [0.2, 2.0, 0.3, 0.0, 0.0],
      [0.0, 0.3, 3.0, 0.9, 0.6],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.1, 0.0, 0.6, 0.0, 2.0]
    );

    const xxTarget = Matrix(
      [0.0, 0.974619, 0.992338, 0.930778, 0.988007],
      [0.974619, 0.0, 0.964601, 0.981269, 0.988919],
      [0.992338, 0.964601, 0.0, 0.917246, 0.93309],
      [0.930778, 0.981269, 0.917246, 0.0, 0.973663],
      [0.988007, 0.988919, 0.93309, 0.973663, 0.0]);

    var V = cosineDistance(X);
    const a = V-xxTarget;
    const aa = norm(a);
    this.results.push_back(
      assertRealApproximates(msg="Cosine Distance(X) norm", expected=1.39884e-06, actual=aa)
    );

    var V2 = cosineDistance(X,Y);
    const cosimXYtarget = Matrix(
      [0.895178, 0.974619, 0.992338, 0.930778, 0.930778, 0.988007],
      [0.974619, 0.757869, 0.964601, 0.981269, 0.981269, 0.988919],
      [0.992338, 0.964601, 0.902534, 0.917246, 0.917246, 0.93309],
      [0.930778, 0.981269, 0.917246, 0.811321, 0.811321, 0.973663],
      [0.988007, 0.988919, 0.93309,  0.973663, 0.973663, 0.771167]
      );
    const b = V2-cosimXYtarget;
    const bb = norm(b);
    this.results.push_back(
      assertRealApproximates(msg="Cosine Distance(X,Y) norm", expected=1.53583e-06, actual=bb)
    );
  }

  /*
   Does not assert anything, LabeledMatrix may be deprecated
   */
  proc testLabelMatrix() {
    var Y = Matrix(
      [3.0, 0.2, 0.0, 0.7, 0.1],
      [0.2, 2.0, 0.3, 0.0, 0.0],
      [0.0, 0.3, 3.0, 0.9, 0.6],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.7, 0.0, 0.9, 2.0, 0.0],
      [0.1, 0.0, 0.6, 0.0, 2.0]
    );
    var L = new LabelMatrix();
    L.fromMatrix(Y);
    this.results.push_back(
        assertRealEquals(msg="Label Matrix entry (1,4)", expected=0.7, actual=L.data(1,4))
    );
  }

  proc run() {
    super.run();
    /*
    testIndexSort();
    testNamedMatrix();
    testSetRowNames();
    testSetColNames();
    testNamedMatrixInitWithNames();
    testSetByName();
    testArgMax();
    testCosineDistance();
    */
    testLabelMatrix();
    return 0;
  }
}

proc main(args: [] string) : int {
  var t = new NumSuchTest();
  var ret = t.run();
  t.report();
  return ret;
}