Simplified hello, world example as requested.

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

config var message = "Hello, world!";

writeln(message);


This is a program I've been working on that tries to generate the CG
random matrix using iterators.

Made some edits to this since my last checkin to get it compiling and
executing.  Haven't yet done a comparison against the NAS CG
implementation to see if it's generating the same random indices and
values, but it is running and generating random indices and values.

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

use Random;

enum classVals {S, W, A, B, C, D, O};

config const probClass = S;

//const Class: domain(classVals) = [S..O];
const Class: domain(1) = [S..O];
const probSizes:   [Class] int = (/ 1400, 7000, 14000, 75000, 150000, 150000, 1400 /),
      nonZeroes:   [Class] int = (/ 7, 8, 11, 13, 15, 21, 7 /),
      shifts:      [Class] int = (/ 10, 12, 20, 60, 110, 500, 10 /),
      numIters:    [Class] int = (/ 15, 15, 15, 75, 75, 100, 15 /),
      verifyZetas: [Class] real = (/ 8.5971775078648, 10.362595087124,
                                    17.130235054029, 22.712745482631,
                                    28.973605592845, 52.5145321058, 0.0 /);

config const n = probSizes(probClass),
             rcond = 0.1,
             nonzer = nonZeroes(probClass),
             shift = shifts(probClass);

type elemType = real(64);

def main() {
  writeln("Chapel sparsity pattern is:");
  for res in makea() {
    writeln(res);
  }
}

iterator makea() {
  const nonzerMax = 16,
        naMax = 150000;
  var v: [1..nonzerMax] real,    // BLC: insert domains? or grow as necessary?
      iv: [1..nonzerMax] int,
      nzloc: [1..nonzerMax] int;
  var mark: [1..naMax] uint(8);   // BLC: bool?

  var size = 1.0;
  const ratio = rcond ** (1.0 / n);

  var randStr = RandomStream(314159265);

  for iouter in 1..n {
    var nzv = nonzer;

    sprnvc(n, nzv, v, iv, nzloc, mark, randStr);
    vecset(n, v, iv, nzv, iouter, 0.50);

    // BLC: replace with loop over iv or iv(1..nzv)?
    for ivelt in 1..nzv {
      const jcol = iv(ivelt),
            scale = size * v(ivelt);

      // BLC: replace with loop over iv or iv(1..nzv)?
      for ivelt1 in 1..nzv {
        const irow = iv(ivelt1);

        yield ((irow, jcol), v(ivelt1)*scale);
      }
    }
    size *= ratio;
  }

  for i in 1..n {
    yield ((i, i), rcond - shift);
  }
}

// BLC: change mark array to indefinite domain/array
def sprnvc(n, nz, v, iv, nzloc, mark, randStr) {
  const zeta = randStr.getFirst();

  const nn1 = log2(n);

  var nzv = 0,
      nzrow = 0;

  while (nzv < nz) {
    do {
      const vecelt = randStr.getNext(),
            i = (randStr.getNext() * nn1):int + 1;
      if (i <= n && mark(i) == 0) {
        mark(i) = 1;
        nzrow += 1;
        nzloc(nzrow) = i;
        nzv += 1;
        v(nzv) = vecelt;
        iv(nzv) = i;
      }
    } while (!(i <= n));
  }
  for i in 1..nzrow do
    mark(nzloc(i)) = 0;
}

def vecset(n, v, iv, inout nzv, i, val) {
  var set = false;
  for k in 1..nzv {
    if (iv(k) == i) {
      v(k) = val;
      set = true;
    }
  }
  if (!set) {
    nzv += 1;
    v(nzv) = val;
    iv(nzv) = i;
  }
}

enum classVals {S, W, A, B, C, D, O};

config const probClass = S;

const Class: domain(classVals) = [S..O];
const probSizes:   [Class] int = (/ 1400, 7000, 14000, 75000, 150000, 150000, 1400 /),
      nonZeroes:   [Class] int = (/ 7, 8, 11, 13, 15, 21, 7 /),
      shifts:      [Class] int = (/ 10, 12, 20, 60, 110, 500, 10 /),
      numIters:    [Class] int = (/ 15, 15, 15, 75, 75, 100, 15 /),
      verifyZetas: [Class] real = (/ 8.5971775078648, 10.362595087124,
                                    17.130235054029, 22.712745482631,
                                    28.973605592845, 52.5145321058, 0.0 /);

config const n = probSizes(probClass),
             rcond = 0.1,
             nonzer = nonZeroes(probClass);

const nonzer_max = 16,
      naMax = 150000;

type elemType = real(64);

def main() {
  for res in makea() {
    writeln("res is: ", res);
  }
}

iterator makea() {
  var v: [1..nonzer_max] real,
      iv: [1..nonzer_max] int,
      nzloc: [1..nonzer_max] int;
  var mark: [1..naMax] uint(8);

  var size = 1.0,
      ratio = rcond ** (1.0 / n),
      nnza = 0;

  for iouter in 1..n {
    const nzv = nonzer;

    sprnvc(n, nzv, v, iv, nzloc, mark);
    vecset(n, v, iv, nzv, iouter, 0.50);

    for ivelt in 1..nzv {
      const jcol = iv(ivelt),
            scale = size * v(ivelt);

      for ivelt1 in 1..nzv {
        const irow = iv(ivelt1);

        yield ((irow, jcol), v(ivelt1)*scale);
      }
    }
    size *= ratio;
  }

  for i in 1..n {
    yield ((i, i), rcond - shift);
  }
}

def sprnvc(n, nz, v, iv, nzloc, mark) {
  const tran = 314159265.0,
        amult = 1220703125.0,
        zeta = randlc(tran, amult);

  const nn1 = lg2(n);

  var nzv = 0,
      nzrow = 0;

  while (nzv < nz) {
    do {
      const vecelt = randlc(tran, amult),
            i = icnvrt(randlc(tran, amult), nn1) + 1;
      if (i <= n && mark(i - 1) == 0) {
        mark(i - 1) = 1;
        nzrow += 1;
        nzloc(nzrow = 1) = i;
        nzv += 1;
        v(nzv - 1) = vecelt;
        iv(nzv - 1) = i;
      }
    } while (!(i <= n));
  }
  for i in 1..nzrow do
    mark(nzloc(i-1) - 1) = 0;
}

def vecset(n, v, iv, nzv, i, val) {
  var set = false;
  for k in 1..nzv {
    if (iv(k - 1) == i) {
      v(k - 1) = val;
      set = true;
    }
  }
  if (!set) {
    nzv += 1;
    v(nzv - 1) = val;
    iv(nzv - 1) = i;
  }
}


add ability to disable AR

add ability to disable AR

use Logging, SearchIndex, SyntheticDataIndexer, Time;

config const peformAR = true;

/**
  This is server-less mode of Chearch that should eventually have a repl
*/
proc main() {

  // simple class to escape the const intents of forall
  class Counter {
    var count: uint;
  }

  writeln("using ", Locales.size, " locales");
  writeln("initializing index");
  initPartitions();

  indexSyntheticDocuments();

  var t: Timer;

  writeln("---querying 2 on each locale");
  {
    for loc in Locales {
      on loc {
        var counts = new Counter();
        var t: Timer;
        local {
          var buffer = new InstructionBuffer(32);
          var writer = new InstructionWriter(buffer);
          buffer.clear();
          writer.write_push();
          writer.write_term(2);

          t.start();
          for result in localQuery(new Query(buffer)) {
            if (result.term != 2) {
              halt("term not 2 ", result);
            }
            counts.count += 1;
          }
          t.stop();

          delete buffer;
        }
        writeln("AL-", here.id, ",", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
      }
    }
  }

  var counts = new Counter();

  // this will hold the query instruction
  var buffer = new InstructionBuffer(32);

  // perform sample queries
  var writer = new InstructionWriter(buffer);

  if (peformAR) {
    writeln("---querying 2 on remote locales");
    for loc in Locales {
      if (loc.id == here.id) {
        continue;
      }

      var baselocaleKnownTermId = ((loc.id * 2048) + 1024 * 1024) : Term;
      var queryTimes: [1..1024] real;

      for termIdStep in queryTimes.domain {
        counts.count = 0;

        var localeKnownTermId = (baselocaleKnownTermId + termIdStep - 1): Term;

        buffer.clear();
        writer.write_push_term(localeKnownTermId);

        t.clear();
        t.start();
        for result in query(new Query(buffer)) {
          if (result.term != localeKnownTermId) {
            halt("term not ", localeKnownTermId, " got ", result);
          }
          counts.count += 1;
        }
        t.stop();
        queryTimes[termIdStep] = t.elapsed(TimeUnits.microseconds);
      }
      writeln("AR-", here.id, ",", Locales.size, ", ", queryTimes);
    }
  }

  writeln("---querying for 3");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(3);

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      if (result.term != 3) {
        halt("term not 3 ", result);
      }
      counts.count += 1;
    }
    t.stop();
    writeln("B,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  writeln("---querying for 3 AND 2");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(3);
    writer.write_push();
    writer.write_term(2);
    writer.write_and();

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      if ((result.term != 3) && (result.term != 2)) {
        halt("term not 3 or 2 ", result);
      }
      counts.count += 1;
    }
    t.stop();
    writeln("C,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  writeln("---querying for 3 OR 2");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(3);
    writer.write_push();
    writer.write_term(2);
    writer.write_or();

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      if ((result.term != 3) && (result.term != 2)) {
        halt("term not 3 or 2 ", result);
      }
      counts.count += 1;
    }
    t.stop();
    writeln("D,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  writeln("---querying for missing term");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(1024*1024 * 8);

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      writeln(result);
      counts.count += 1;
    }
    t.stop();
    if (counts.count > 0) {
      halt("counts > 0!");
    }
    writeln("E,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  delete buffer;
}

use Logging, SearchIndex, SyntheticDataIndexer, Time;

/**
  This is server-less mode of Chearch that should eventually have a repl
*/
proc main() {

  // simple class to escape the const intents of forall
  class Counter {
    var count: uint;
  }

  writeln("using ", Locales.size, " locales");
  writeln("initializing index");
  initPartitions();

  indexSyntheticDocuments();

  var t: Timer;

  writeln("---querying 2 on each locale");
  {
    for loc in Locales {
      on loc {
        var counts = new Counter();
        var t: Timer;
        local {
          var buffer = new InstructionBuffer(32);
          var writer = new InstructionWriter(buffer);
          buffer.clear();
          writer.write_push();
          writer.write_term(2);

          t.start();
          for result in localQuery(new Query(buffer)) {
            if (result.term != 2) {
              halt("term not 2 ", result);
            }
            counts.count += 1;
          }
          t.stop();

          delete buffer;
        }
        writeln("AL-", here.id, ",", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
      }
    }
  }

  var counts = new Counter();

  // this will hold the query instruction
  var buffer = new InstructionBuffer(32);

  // perform sample queries
  var writer = new InstructionWriter(buffer);

  writeln("---querying 2 on remote locales");
  {
    for loc in Locales {
      if (loc.id == here.id) {
        continue;
      }

      var baselocaleKnownTermId = ((loc.id * 2048) + 1024 * 1024) : Term;
      var queryTimes: [1..1024] real;

      for termIdStep in queryTimes.domain {
        counts.count = 0;

        var localeKnownTermId = (baselocaleKnownTermId + termIdStep - 1): Term;

        buffer.clear();
        writer.write_push_term(localeKnownTermId);

        t.clear();
        t.start();
        for result in query(new Query(buffer)) {
          if (result.term != localeKnownTermId) {
            halt("term not ", localeKnownTermId, " got ", result);
          }
          counts.count += 1;
        }
        t.stop();
        queryTimes[termIdStep] = t.elapsed(TimeUnits.microseconds);
      }
      writeln("AR-", here.id, ",", Locales.size, ", ", queryTimes);
    }
  }

  writeln("---querying for 3");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(3);

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      if (result.term != 3) {
        halt("term not 3 ", result);
      }
      counts.count += 1;
    }
    t.stop();
    writeln("B,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  writeln("---querying for 3 AND 2");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(3);
    writer.write_push();
    writer.write_term(2);
    writer.write_and();

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      if ((result.term != 3) && (result.term != 2)) {
        halt("term not 3 or 2 ", result);
      }
      counts.count += 1;
    }
    t.stop();
    writeln("C,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  writeln("---querying for 3 OR 2");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(3);
    writer.write_push();
    writer.write_term(2);
    writer.write_or();

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      if ((result.term != 3) && (result.term != 2)) {
        halt("term not 3 or 2 ", result);
      }
      counts.count += 1;
    }
    t.stop();
    writeln("D,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  writeln("---querying for missing term");
  {
    counts.count = 0;

    buffer.clear();
    writer.write_push();
    writer.write_term(1024*1024 * 8);

    t.clear();
    t.start();
    forall result in query(new Query(buffer)) {
      writeln(result);
      counts.count += 1;
    }
    t.stop();
    if (counts.count > 0) {
      halt("counts > 0!");
    }
    writeln("E,", Locales.size, ", ", t.elapsed(TimeUnits.microseconds), ",", counts.count);
  }

  delete buffer;
}


Increased problem size of future test case to prevent it from succeeding
on most platforms

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

config const n: int = 4096;

def foo(i: int) {
  if i < n {
    _debugWriteln(here.id, " pre ", i);
    sync begin foo(i+1);
    _debugWriteln(here.id, " post ", i);
  }
}

foo(1);

config const n: int = 1024;

def foo(i: int) {
  if i < n {
    _debugWriteln(here.id, " pre ", i);
    sync begin foo(i+1);
    _debugWriteln(here.id, " post ", i);
  }
}

foo(1);


Removed array and initialization code used for old bit matrix multiply.

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

//
// bit manipulation functions
//

// reimplementation note: one bitPop routine when numBits is a parameter
def bitPop(i: integral) where (i.type == int(64)) | (i.type == uint(64)) {
  const u1 = 10540996613548315209:uint(64);
  const u3 = 13176245766935394011:uint(64);
  const u7 = 9349719599003471367:uint(64);
  var ii = i: uint(64);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

def bitPop(i: integral) where (i.type == int(32)) | (i.type == uint(32)) {
  const u1 = 10540996613548315209:uint(32);
  const u3 = 13176245766935394011:uint(32);
  const u7 = 9349719599003471367:uint(32);
  var ii = i: uint(32);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

def bitPop(i: integral) where (i.type == int(16)) | (i.type == uint(16)) {
  const u1 = 10540996613548315209:uint(16);
  const u3 = 13176245766935394011:uint(16);
  const u7 = 9349719599003471367:uint(16);
  var ii = i: uint(16);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

def bitPop(i: integral) where (i.type == int(8)) | (i.type == uint(8)) {
  const u1 = 10540996613548315209:uint(8);
  const u3 = 13176245766935394011:uint(8);
  const u7 = 9349719599003471367:uint(8);
  var ii = i: uint(8);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

// do a bitwise matrix-multiplication of x and y treating
// them as 8x8 bit matrices and using | as the combinator
// function.  There should be a similar version using xor
// as the combinator, but I'm not sure where that would
// go to be perfectly truthful
def bitMatMultOr(x: uint(64), y: uint(64)): uint(64) {
  def transpose(x: uint(64)) {
   // Return the transpose of x, treating it as an 8x8 bit-matrix.
   return  x & 0x8040201008040201        |
          (x & 0x0080402010080402) <<  7 |
          (x & 0x0000804020100804) << 14 |
          (x & 0x0000008040201008) << 21 |
          (x & 0x0000000080402010) << 28 |
          (x & 0x0000000000804020) << 35 |
          (x & 0x0000000000008040) << 42 |
          (x & 0x0000000000000080) << 49 |
          (x >>  7) & 0x0080402010080402 |
          (x >> 14) & 0x0000804020100804 |
          (x >> 21) & 0x0000008040201008 |
          (x >> 28) & 0x0000000080402010 |
          (x >> 35) & 0x0000000000804020 |
          (x >> 42) & 0x0000000000008040 |
          (x >> 49) & 0x0000000000000080;
  }
  var yTranspose = transpose(y);
  var result:uint(64) = 0;
  for i in 0..7:uint(64) {
    var left = (x & (0xFF:uint(64) << (i*8))) >> (i*8);
    for param j in 0..7:uint(64) {
      if (0 != left & ((yTranspose & (0xFF:uint(64) << (j*8))) >> (j*8))) then
        result |= 1:uint(64) << (i*8 + j):uint(64);
    }
  }
  return result;
}

def bitRotLeft(x, shift) {
  var backshift = numBits(x) - shift;
  return (x << shift:int) | (x >> backshift:int);
}

def bitRotRight(x: uint(64), shift) {
  var backshift = numBits(x) - shift;
  return (x >> shift:int) | (x << backshift:int);
}

//
// bit manipulation functions
//

// reimplementation note: one bitPop routine when numBits is a parameter
def bitPop(i: integral) where (i.type == int(64)) | (i.type == uint(64)) {
  const u1 = 10540996613548315209:uint(64);
  const u3 = 13176245766935394011:uint(64);
  const u7 = 9349719599003471367:uint(64);
  var ii = i: uint(64);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

def bitPop(i: integral) where (i.type == int(32)) | (i.type == uint(32)) {
  const u1 = 10540996613548315209:uint(32);
  const u3 = 13176245766935394011:uint(32);
  const u7 = 9349719599003471367:uint(32);
  var ii = i: uint(32);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

def bitPop(i: integral) where (i.type == int(16)) | (i.type == uint(16)) {
  const u1 = 10540996613548315209:uint(16);
  const u3 = 13176245766935394011:uint(16);
  const u7 = 9349719599003471367:uint(16);
  var ii = i: uint(16);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

def bitPop(i: integral) where (i.type == int(8)) | (i.type == uint(8)) {
  const u1 = 10540996613548315209:uint(8);
  const u3 = 13176245766935394011:uint(8);
  const u7 = 9349719599003471367:uint(8);
  var ii = i: uint(8);
  var tmp = ii - ((ii >> 1) & u3) - ((ii >> 2) & u1);
  return ((((tmp + (tmp >> 3)) & u7) + ((tmp >> 6) & u7)) % 511):int;
}

param bitsPerBitMatDim = 8;
const bitMatDimDom = [1..bitsPerBitMatDim];
const bitMatDom = [1..bitsPerBitMatDim, 1..bitsPerBitMatDim];
var bitInd: [bitMatDom] uint(64);

// BLC: would like to rewrite this as an initializer on bitInd
// using [i in bitMatDom] syntax
for i in bitMatDimDom {
  for j in bitMatDimDom {
    bitInd(i,j) = 0x1:uint(64) << (((bitsPerBitMatDim-i)*bitsPerBitMatDim)
                                   + (bitsPerBitMatDim-j)):int;
    //    bitMatWrite(bitInd(i,j));
  }
}

def bitMatWrite(x: uint(64)) {
  var mask: uint(64) = 0x8000000000000000;
  for i in bitMatDimDom {
    for j in bitMatDimDom {
      if ((x & mask) != 0) {
        write("1");
      } else {
        write("0");
      }
      // BLC: should be: mask >>= 1;
      mask = mask >> 1;
    }
    writeln();
  }
  writeln();
}

// do a bitwise matrix-multiplication of x and y treating
// them as 8x8 bit matrices and using | as the combinator
// function.  There should be a similar version using xor
// as the combinator, but I'm not sure where that would
// go to be perfectly truthful
def bitMatMultOr(x: uint(64), y: uint(64)): uint(64) {
  def transpose(x: uint(64)) {
   // Return the transpose of x, treating it as an 8x8 bit-matrix.
   return  x & 0x8040201008040201        |
          (x & 0x0080402010080402) <<  7 |
          (x & 0x0000804020100804) << 14 |
          (x & 0x0000008040201008) << 21 |
          (x & 0x0000000080402010) << 28 |
          (x & 0x0000000000804020) << 35 |
          (x & 0x0000000000008040) << 42 |
          (x & 0x0000000000000080) << 49 |
          (x >>  7) & 0x0080402010080402 |
          (x >> 14) & 0x0000804020100804 |
          (x >> 21) & 0x0000008040201008 |
          (x >> 28) & 0x0000000080402010 |
          (x >> 35) & 0x0000000000804020 |
          (x >> 42) & 0x0000000000008040 |
          (x >> 49) & 0x0000000000000080;
  }
  var yTranspose = transpose(y);
  var result:uint(64) = 0;
  for i in 0..7:uint(64) {
    var left = (x & (0xFF:uint(64) << (i*8))) >> (i*8);
    for param j in 0..7:uint(64) {
      if (0 != left & ((yTranspose & (0xFF:uint(64) << (j*8))) >> (j*8))) then
        result |= 1:uint(64) << (i*8 + j):uint(64);
    }
  }
  return result;
}

def bitRotLeft(x, shift) {
  var backshift = numBits(x) - shift;
  return (x << shift:int) | (x >> backshift:int);
}

def bitRotRight(x: uint(64), shift) {
  var backshift = numBits(x) - shift;
  return (x >> shift:int) | (x << backshift:int);
}


Remove "no doc" pragma from private symbols in Random module

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

/*
   Support for pseudorandom number generation

   This module defines an abstraction for a stream of pseudorandom
   numbers, :class:`RandomStream`, supporting methods to get the next
   random number in the stream (:proc:`.getNext`), to fast-forward to
   a specific value in the stream (:proc:`.skipToNth` and
   :proc:`.getNth`), or to fill an array with random numbers in
   parallel (:proc:`~Random.RandomStream.fillRandom`).  The module
   also provides a standalone convenience function, :proc:`fillRandom`
   that can be used to fill an array with random numbers in parallel
   without manually creating a :class:`RandomStream` object.

   The current pseudorandom number generator (PRNG) implemented by
   this module uses the algorithm from the NAS Parallel Benchmarks
   (NPB, available at: http://www.nas.nasa.gov/publications/npb.html),
   which can be used to generate random values of type `real(64)`,
   `imag(64)`, and `complex(128)`.  The longer-term intention is to
   add knobs to select between a menu of additional PRNG algorithms
   (such as the Mersenne twister) and element types (such as integer
   types and other floating point widths).

   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 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. This module is currently restricted to generating `real(64)`,
   `imag(64)`, and `complex(128)` complex values using a single PRNG
   algorithm.  As noted above, we would like to extend this support to
   include other algorithms and primitive types over time.

   2. We plan to support general serial and parallel iterator methods
   on :class:`RandomStream`; however, providing the full suite of
   iterators is not possible with our current parallel iterator
   framework.  Specifically, if :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).  With
   Chapel's recent support for standalone parallel iterators, one
   could define a standalone parallel iterator for
   :class:`RandomStream`, but this effort has not yet been taken on.

   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
   :record:`SeedGenerators` is to provide a menu of other options
   for initializing the random stream seed, but only one option is
   implemented at present.

*/
module Random {

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

  /* Internal TODOs for developers:

     - should RandomStream be parameterized by the type to return?
       it seems odd that currently getNext() and getNth() return
       reals always...
  */

/*
   An instance of :record:`SeedGenerators` that provides a
   convenient means of generating seeds when the user does not wish to
   specify one manually.
*/
const SeedGenerator: SeedGenerators;

// CHPLDOC FIXME: Respect ordering of things in the file

/*
  Provides methods to help generate seeds when the user doesn't want
  to create one.  It currently only supports one such method, but the
  intention is to add more over time.

  .. note::
          Once Chapel supports static class methods,
          :const:`SeedGenerator` and :record:`SeedGenerators` should
          be combined into a single record type with static methods).

*/

record SeedGenerators {
  /*
    Generate a seed based on the current time in microseconds as
    reported by :proc:`Time.getCurrentTime`, ensuring that it
    meets the PRNG's requirements.
  */
  proc currentTime: int(64) {
    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);
  }
};

// CHPLDOC FEEDBACK: If easy, I'd suggest either deprecating the
// :arg <type> <name>: form or else switching the order to
// :arg <name> <type>: as it's an easy trap to simply type the same
// argument as in the function's signature.

// CHPLDOC FIXME: if the first line below is shifted one character to the
// left, it ends up being rendered like a method

// CHPLDOC FIXME: if the first line below is shifted one character to the
// right, it ends up causing a warning (promoted to error) in the .rst
// file.  It'd be preferable to have it declare the issue in terms of
// the .chpl line numbers.
//

/*
  Fill an array of `real(64)`, `imag(64)`, or `complex(128)` elements
  with pseudorandom values in parallel using a new
  :class:`RandomStream` created specifically for this call.  The
  first `arr.size` values from the stream will be assigned to the
  array's elements in row-major order for `real` and `imag` elements.
  For `complex` elements, consecutive pairs of random numbers are
  assigned to the real and imaginary components, respectively.  The
  parallelization strategy is determined by the array.

  :arg arr: The array to be filled, where T is `real(64)`, `imag(64)`, or `complex(128)`.
  :type arr: [] T

  :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`.
  :type seed: int(64)
*/

proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime)
  where (arr.eltType == real || arr.eltType == imag || arr.eltType == complex) {
  var randNums = new RandomStream(seed, eltType=arr.eltType, parSafe=false);
  randNums.fillRandom(arr);
  delete randNums;
}

pragma "no doc"
proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime) {
  compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
}

/*
  Models a stream of pseudorandom numbers.  See the module-level
  notes for :mod:`Random` for details on the PRNG used.
*/
class RandomStream {
  /*
    Specifies the type of value generated by the RandomStream.
    Currently, only `real(64)`, `imag(64)`, and `complex(128)` are
    supported.
  */
  type eltType = real(64);

  /*
    Indicates whether or not the RandomStream needs to be
    parallel-safe by default.  If multiple tasks interact with it in
    an uncoordinated fashion, this must be set to `true`.  If it will
    only be called from a single task, or if only one task will call
    into it at a time, setting to `false` will reduce overhead related
    to ensuring mutual exclusion.
  */
  param parSafe: bool = true;

  /*
    The seed value for the PRNG.  It must be an odd integer in the
    range (1, 2**46).
  */
  const seed: int(64);

  /*
    Constructs a new stream of random numbers using the specified seed
    and parallel safety.  Ensures that the seed value meets the PRNG's
    constraints.

    :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`..
    :type seed: int(64)

    :arg parSafe: The parallel safety setting.  Defaults to `true`.
    :type parSafe: bool

    :arg eltType: The element type to be generated.  Defaults to `real(64)`.
    :type eltType: type
  */
  proc RandomStream(seed: int(64) = SeedGenerator.currentTime,
                    param parSafe: bool = true,
                    type eltType = real(64)) {
    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");
    this.seed = seed;
    RandomStreamPrivate_cursor = seed;
    RandomStreamPrivate_count = 1;
  }

  pragma "no doc"
  proc RandomStreamPrivate_getNext_noLock() {
    if (eltType == complex) {
      RandomStreamPrivate_count += 2;
    } else {
      RandomStreamPrivate_count += 1;
    }
    return randlc(eltType, RandomStreamPrivate_cursor);
  }

  pragma "no doc"
  proc RandomStreamPrivate_skipToNth_noLock(in n: integral) {
    if eltType == complex then n = n*2 - 1;
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = randlc_skipto(seed, n);
  }

  /*
    Returns the next value in the random stream.

    :returns: The next value in the random stream as type :type:`eltType`.
   */
  proc getNext(): eltType {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const result = RandomStreamPrivate_getNext_noLock();
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  /*
    Advances/rewinds the stream to the `n`-th value in the sequence.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral
   */

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

  /*
    Advance/rewind the stream to the `n`-th value and return it
    (advancing the stream by one).  This is equivalent to
    :proc:`skipToNth()` followed by :proc:`getNext()`.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral

    :returns: The `n`-th value in the random stream as type :type:`eltType`.
  */

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

  /*
    Fill the argument array with pseudorandom values.  This method is
    identical to the standalone :proc:`fillRandom` procedure,
    except that it consumes random values from the
    :class:`RandomStream` object on which it's invoked rather
    than creating a new stream for the purpose of the call.

    :arg arr: The array to be filled
    :type arr: [] :type:`eltType`
  */
  proc fillRandom(arr: [] eltType) {
    forall (x, r) in zip(arr, iterate(arr.domain, arr.eltType)) do
      x = r;
  }

  pragma "no doc"
  proc fillRandom(arr: []) {
    compilerError("RandomStream(eltType=", typeToString(eltType), ") can only be used to fill arrays of ", typeToString(eltType));
  }

  pragma "no doc"
  proc iterate(D: domain, type resultType=real) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const start = RandomStreamPrivate_count;
    RandomStreamPrivate_count += D.numIndices.safeCast(int(64));
    RandomStreamPrivate_skipToNth_noLock(RandomStreamPrivate_count);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  pragma "no doc"
  proc writeThis(f: Writer) {
    f <~> "RandomStream(eltType=";
    f <~> typeToString(eltType);
    f <~> ", 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.
  //

  pragma "no doc"
  var RandomStreamPrivate_lock$: sync bool;
  pragma "no doc"
  var RandomStreamPrivate_cursor: real = seed;
  pragma "no doc"
  var RandomStreamPrivate_count: int(64) = 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
//
private const r23   = 0.5**23,
              t23   = 2.0**23,
              r46   = 0.5**46,
              t46   = 2.0**46,
              arand = 1220703125.0; // TODO: Is arand something that a
                                    // user might want to set on a
                                    // case-by-case basis?

//
// NPB-defined randlc routine
//
private proc randlc(inout x: real, a: real = arand) {
  var t1 = r23 * a;
  const a1 = floor(t1),
    a2 = a - 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;
}

// Wrapper that takes a result type (two calls for complex types)
private proc randlc(type resultType, inout x: real) {
  if resultType == complex then
    return (randlc(x), randlc(x)):complex;
  else
    if resultType == imag then
      //
      // BLC: I thought that casting real to imag did this automatically?
      //
      return _r2i(randlc(x));
    else
      return randlc(x);
 }

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

//
// iterate over outer ranges in tuple of ranges
//
private iter 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 outer(ranges, dim+1) do
        yield (i, (...j));
  } else {
    yield 0; // 1D case is a noop
  }
}

//
// RandomStream iterator implementation
//
pragma "no doc"
iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                     start: int(64)) {
  var cursor = randlc_skipto(seed, start);
  for i in D do
    yield randlc(resultType, cursor);
}

pragma "no doc"
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;
}

pragma "no doc"
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 outer(followThis) {
    var myStart = start;
    if ZD.rank > 1 then
      myStart += multiplier * ZD.indexOrder(((...outer), innerRange.low)).safeCast(int(64));
    else
      myStart += multiplier * ZD.indexOrder(innerRange.low).safeCast(int(64));
    if !innerRange.stridable {
      cursor = randlc_skipto(seed, myStart);
      for i in innerRange do
        yield randlc(resultType, cursor);
    } else {
      myStart -= innerRange.low.safeCast(int(64));
      for i in innerRange {
        cursor = randlc_skipto(seed, myStart + i.safeCast(int(64)) * multiplier);
        yield 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.
 */

/*
   Support for pseudorandom number generation

   This module defines an abstraction for a stream of pseudorandom
   numbers, :class:`RandomStream`, supporting methods to get the next
   random number in the stream (:proc:`.getNext`), to fast-forward to
   a specific value in the stream (:proc:`.skipToNth` and
   :proc:`.getNth`), or to fill an array with random numbers in
   parallel (:proc:`~Random.RandomStream.fillRandom`).  The module
   also provides a standalone convenience function, :proc:`fillRandom`
   that can be used to fill an array with random numbers in parallel
   without manually creating a :class:`RandomStream` object.

   The current pseudorandom number generator (PRNG) implemented by
   this module uses the algorithm from the NAS Parallel Benchmarks
   (NPB, available at: http://www.nas.nasa.gov/publications/npb.html),
   which can be used to generate random values of type `real(64)`,
   `imag(64)`, and `complex(128)`.  The longer-term intention is to
   add knobs to select between a menu of additional PRNG algorithms
   (such as the Mersenne twister) and element types (such as integer
   types and other floating point widths).

   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 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. This module is currently restricted to generating `real(64)`,
   `imag(64)`, and `complex(128)` complex values using a single PRNG
   algorithm.  As noted above, we would like to extend this support to
   include other algorithms and primitive types over time.

   2. We plan to support general serial and parallel iterator methods
   on :class:`RandomStream`; however, providing the full suite of
   iterators is not possible with our current parallel iterator
   framework.  Specifically, if :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).  With
   Chapel's recent support for standalone parallel iterators, one
   could define a standalone parallel iterator for
   :class:`RandomStream`, but this effort has not yet been taken on.

   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
   :record:`SeedGenerators` is to provide a menu of other options
   for initializing the random stream seed, but only one option is
   implemented at present.

*/
module Random {

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

  /* Internal TODOs for developers:

     - should RandomStream be parameterized by the type to return?
       it seems odd that currently getNext() and getNth() return
       reals always...
  */

/*
   An instance of :record:`SeedGenerators` that provides a
   convenient means of generating seeds when the user does not wish to
   specify one manually.
*/
const SeedGenerator: SeedGenerators;

// CHPLDOC FIXME: Respect ordering of things in the file

/*
  Provides methods to help generate seeds when the user doesn't want
  to create one.  It currently only supports one such method, but the
  intention is to add more over time.

  .. note::
          Once Chapel supports static class methods,
          :const:`SeedGenerator` and :record:`SeedGenerators` should
          be combined into a single record type with static methods).

*/

record SeedGenerators {
  /*
    Generate a seed based on the current time in microseconds as
    reported by :proc:`Time.getCurrentTime`, ensuring that it
    meets the PRNG's requirements.
  */
  proc currentTime: int(64) {
    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);
  }
};

// CHPLDOC FEEDBACK: If easy, I'd suggest either deprecating the
// :arg <type> <name>: form or else switching the order to
// :arg <name> <type>: as it's an easy trap to simply type the same
// argument as in the function's signature.

// CHPLDOC FIXME: if the first line below is shifted one character to the
// left, it ends up being rendered like a method

// CHPLDOC FIXME: if the first line below is shifted one character to the
// right, it ends up causing a warning (promoted to error) in the .rst
// file.  It'd be preferable to have it declare the issue in terms of
// the .chpl line numbers.
//

/*
  Fill an array of `real(64)`, `imag(64)`, or `complex(128)` elements
  with pseudorandom values in parallel using a new
  :class:`RandomStream` created specifically for this call.  The
  first `arr.size` values from the stream will be assigned to the
  array's elements in row-major order for `real` and `imag` elements.
  For `complex` elements, consecutive pairs of random numbers are
  assigned to the real and imaginary components, respectively.  The
  parallelization strategy is determined by the array.

  :arg arr: The array to be filled, where T is `real(64)`, `imag(64)`, or `complex(128)`.
  :type arr: [] T

  :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`.
  :type seed: int(64)
*/

proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime)
  where (arr.eltType == real || arr.eltType == imag || arr.eltType == complex) {
  var randNums = new RandomStream(seed, eltType=arr.eltType, parSafe=false);
  randNums.fillRandom(arr);
  delete randNums;
}

pragma "no doc"
proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime) {
  compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
}

/*
  Models a stream of pseudorandom numbers.  See the module-level
  notes for :mod:`Random` for details on the PRNG used.
*/
class RandomStream {
  /*
    Specifies the type of value generated by the RandomStream.
    Currently, only `real(64)`, `imag(64)`, and `complex(128)` are
    supported.
  */
  type eltType = real(64);

  /*
    Indicates whether or not the RandomStream needs to be
    parallel-safe by default.  If multiple tasks interact with it in
    an uncoordinated fashion, this must be set to `true`.  If it will
    only be called from a single task, or if only one task will call
    into it at a time, setting to `false` will reduce overhead related
    to ensuring mutual exclusion.
  */
  param parSafe: bool = true;

  /*
    The seed value for the PRNG.  It must be an odd integer in the
    range (1, 2**46).
  */
  const seed: int(64);

  /*
    Constructs a new stream of random numbers using the specified seed
    and parallel safety.  Ensures that the seed value meets the PRNG's
    constraints.

    :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`..
    :type seed: int(64)

    :arg parSafe: The parallel safety setting.  Defaults to `true`.
    :type parSafe: bool

    :arg eltType: The element type to be generated.  Defaults to `real(64)`.
    :type eltType: type
  */
  proc RandomStream(seed: int(64) = SeedGenerator.currentTime,
                    param parSafe: bool = true,
                    type eltType = real(64)) {
    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");
    this.seed = seed;
    RandomStreamPrivate_cursor = seed;
    RandomStreamPrivate_count = 1;
  }

  pragma "no doc"
  proc RandomStreamPrivate_getNext_noLock() {
    if (eltType == complex) {
      RandomStreamPrivate_count += 2;
    } else {
      RandomStreamPrivate_count += 1;
    }
    return randlc(eltType, RandomStreamPrivate_cursor);
  }

  pragma "no doc"
  proc RandomStreamPrivate_skipToNth_noLock(in n: integral) {
    if eltType == complex then n = n*2 - 1;
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = randlc_skipto(seed, n);
  }

  /*
    Returns the next value in the random stream.

    :returns: The next value in the random stream as type :type:`eltType`.
   */
  proc getNext(): eltType {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const result = RandomStreamPrivate_getNext_noLock();
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  /*
    Advances/rewinds the stream to the `n`-th value in the sequence.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral
   */

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

  /*
    Advance/rewind the stream to the `n`-th value and return it
    (advancing the stream by one).  This is equivalent to
    :proc:`skipToNth()` followed by :proc:`getNext()`.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral

    :returns: The `n`-th value in the random stream as type :type:`eltType`.
  */

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

  /*
    Fill the argument array with pseudorandom values.  This method is
    identical to the standalone :proc:`fillRandom` procedure,
    except that it consumes random values from the
    :class:`RandomStream` object on which it's invoked rather
    than creating a new stream for the purpose of the call.

    :arg arr: The array to be filled
    :type arr: [] :type:`eltType`
  */
  proc fillRandom(arr: [] eltType) {
    forall (x, r) in zip(arr, iterate(arr.domain, arr.eltType)) do
      x = r;
  }

  pragma "no doc"
  proc fillRandom(arr: []) {
    compilerError("RandomStream(eltType=", typeToString(eltType), ") can only be used to fill arrays of ", typeToString(eltType));
  }

  pragma "no doc"
  proc iterate(D: domain, type resultType=real) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const start = RandomStreamPrivate_count;
    RandomStreamPrivate_count += D.numIndices.safeCast(int(64));
    RandomStreamPrivate_skipToNth_noLock(RandomStreamPrivate_count);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  pragma "no doc"
  proc writeThis(f: Writer) {
    f <~> "RandomStream(eltType=";
    f <~> typeToString(eltType);
    f <~> ", 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.
  //

  pragma "no doc"
  var RandomStreamPrivate_lock$: sync bool;
  pragma "no doc"
  var RandomStreamPrivate_cursor: real = seed;
  pragma "no doc"
  var RandomStreamPrivate_count: int(64) = 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
//
pragma "no doc"
private const r23   = 0.5**23,
              t23   = 2.0**23,
              r46   = 0.5**46,
              t46   = 2.0**46,
              arand = 1220703125.0; // TODO: Is arand something that a
                                    // user might want to set on a
                                    // case-by-case basis?

//
// NPB-defined randlc routine
//
pragma "no doc"
private proc randlc(inout x: real, a: real = arand) {
  var t1 = r23 * a;
  const a1 = floor(t1),
    a2 = a - 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;
}

// Wrapper that takes a result type (two calls for complex types)
pragma "no doc"
private proc randlc(type resultType, inout x: real) {
  if resultType == complex then
    return (randlc(x), randlc(x)):complex;
  else
    if resultType == imag then
      //
      // BLC: I thought that casting real to imag did this automatically?
      //
      return _r2i(randlc(x));
    else
      return randlc(x);
 }

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

//
// iterate over outer ranges in tuple of ranges
//
pragma "no doc"
private iter 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 outer(ranges, dim+1) do
        yield (i, (...j));
  } else {
    yield 0; // 1D case is a noop
  }
}

//
// RandomStream iterator implementation
//
pragma "no doc"
iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                     start: int(64)) {
  var cursor = randlc_skipto(seed, start);
  for i in D do
    yield randlc(resultType, cursor);
}

pragma "no doc"
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;
}

pragma "no doc"
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 outer(followThis) {
    var myStart = start;
    if ZD.rank > 1 then
      myStart += multiplier * ZD.indexOrder(((...outer), innerRange.low)).safeCast(int(64));
    else
      myStart += multiplier * ZD.indexOrder(innerRange.low).safeCast(int(64));
    if !innerRange.stridable {
      cursor = randlc_skipto(seed, myStart);
      for i in innerRange do
        yield randlc(resultType, cursor);
    } else {
      myStart -= innerRange.low.safeCast(int(64));
      for i in innerRange {
        cursor = randlc_skipto(seed, myStart + i.safeCast(int(64)) * multiplier);
        yield randlc(resultType, cursor);
      }
    }
  }
}

} // close module Random


Remove TODO describing this precise change!  (thanks Thomas)

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

/*
   Support for pseudorandom number generation

   This module defines an abstraction for a stream of pseudorandom
   numbers, :class:`RandomStream`.  It also provides a
   convenience function, :proc:`fillRandom` that can be used to
   fill an array with random numbers in parallel.

   The current pseudorandom number generator (PRNG) implemented by
   this module uses the algorithm from the NAS Parallel Benchmarks
   (NPB, available at: http://www.nas.nasa.gov/publications/npb.html).
   The longer-term intention is to add knobs to select between a menu
   of PRNG 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 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. 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.

   2. 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 :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).

   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
   :record:`SeedGenerators` is to provide a menu of other options
   for initializing the random stream seed, but only one option is
   implemented at present.

   4. As noted above, we plan to add support for additional PRNG
   algorithms over time.

*/
module Random {

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

  /* Internal TODOs for developers:

     - should RandomStream be parameterized by the type to return?
       it seems odd that currently getNext() and getNth() return
       reals always...
  */

/*
   An instance of :record:`SeedGenerators` that provides a
   convenient means of generating seeds when the user does not wish to
   specify one manually.
*/
const SeedGenerator: SeedGenerators;

// CHPLDOC FIXME: Respect ordering of things in the file

/*
  Provides methods to help generate seeds when the user doesn't want
  to create one.  It currently only supports one such method, but the
  intention is to add more over time.

  (Note: once Chapel supports static class methods,
  :const:`SeedGenerator` and :record:`SeedGenerators` should
  be combined into a single record type with static methods).
*/

record SeedGenerators {
  /*
    Generate a seed based on the current time in microseconds as
    reported by :proc:`Time.getCurrentTime`, ensuring that it
    meets the PRNG's requirements.
  */
  proc currentTime: int(64) {
    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);
  }
};

// CHPLDOC FEEDBACK: If easy, I'd suggest either deprecating the
// :arg <type> <name>: form or else switching the order to
// :arg <name> <type>: as it's an easy trap to simply type the same
// argument as in the function's signature.

// CHPLDOC FIXME: if the first line below is shifted one character to the
// left, it ends up being rendered like a method

// CHPLDOC FIXME: if the first line below is shifted one character to the
// right, it ends up causing a warning (promoted to error) in the .rst
// file.  It'd be preferable to have it declare the issue in terms of
// the .chpl line numbers.
//

/*
  Fill an array of `real(64)`, `imag(64)`, or `complex(128)` elements
  with pseudorandom values in parallel using a new
  :class:`RandomStream` created specifically for this call.  The
  first `arr.size` values from the stream will be assigned to the
  array's elements in row-major order for `real` and `imag` elements.
  For `complex` elements, consecutive pairs of random numbers are
  assigned to the real and imaginary components, respectively.  The
  parallelization strategy is determined by the array.

  :arg arr: The array to be filled, where T is real(64), imag(64), or complex(128).
  :type arr: [] T

  :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`.
  :type seed: int(64)
*/

proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime)
  where (arr.eltType == real || arr.eltType == imag || arr.eltType == complex) {
  var randNums = new RandomStream(seed, parSafe=false);
  randNums.fillRandom(arr);
  delete randNums;
}

pragma "no doc"
proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime) {
  compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
}

/*
  Models a stream of pseudorandom numbers.  See the module-level
  notes for :mod:`Random` for details on the PRNG used.
*/
class RandomStream {
  /*
    Indicates whether or not the RandomStream needs to be
    parallel-safe by default.  If multiple tasks interact with it in
    an uncoordinated fashion, this must be set to `true`.  If it will
    only be called from a single task, or if only one task will call
    into it at a time, setting to `false` will reduce overhead related
    to ensuring mutual exclusion.
  */
  param parSafe: bool = true;
  /*
    The seed value for the PRNG.  It must be an odd integer in the
    range (1, 2**46).
  */
  const seed: int(64);

  /*
    Constructs a new stream of random numbers using the specified seed
    and parallel safety.  Ensures that the seed value meets the PRNG's
    constraints.

    :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`..
    :type seed: int(64)

    :arg parSafe: The parallel safety setting.  Defaults to `true`.
    :type parSafe: bool
  */
  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);
  }

  pragma "no doc"
  proc RandomStreamPrivate_getNext_noLock() {
    RandomStreamPrivate_count += 1;
    return RandomPrivate_randlc(RandomStreamPrivate_cursor);
  }

  /*
    Returns the next value in the random stream as a `real(64)`

    :returns: The next value in the random stream as a `real(64)`.
   */
  proc getNext(): real(64) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const result = RandomStreamPrivate_getNext_noLock();
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  pragma "no doc"
  proc RandomStreamPrivate_skipToNth_noLock(n: integral) {
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = RandomPrivate_randlc_skipto(seed, n);
  }

  /*
    Advances/rewinds the stream to the `n`-th value in the sequence.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral
   */

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

  /*
    Advance/rewind the stream to the `n`-th value and return it
    (advancing the strem by one).  This is equivalent to
    :proc:`skipToNth()` followed by :proc:`getNext()`.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral

    :returns: The `n`-th value in the random stream as a `real(64)`.
  */

  proc getNth(n: integral): real(64) {
    if (n <= 0) then
      halt("RandomStream.getNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_skipToNth_noLock(n);
    const result = RandomStreamPrivate_getNext_noLock();
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  /*
    Fill the argument array with pseudorandom values.  This method is
    identical to the standalone :proc:`fillRandom` procedure,
    except that it consumes random values from the
    :class:`RandomStream` object on which it's invoked rather
    than creating a new stream for the purpose of the call.

    :arg arr: The array to be filled, where T is real(64), imag(64), or complex(128).
    :type arr: [] T
  */

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

  pragma "no doc"
  proc iterate(D: domain, type resultType=real) {
    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);
    RandomStreamPrivate_skipToNth_noLock(RandomStreamPrivate_count);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  pragma "no doc"
  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.
  //

  pragma "no doc"
  var RandomStreamPrivate_lock$: sync bool;
  pragma "no doc"
  var RandomStreamPrivate_cursor: real;
  pragma "no doc"
  var RandomStreamPrivate_count: int(64);

  pragma "no doc"
  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
//
pragma "no doc"
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
//
pragma "no doc"
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)
pragma "no doc"
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
//
pragma "no doc"
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
//
pragma "no doc"
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
//
pragma "no doc"
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);
}

pragma "no doc"
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;
}

pragma "no doc"
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.
 */

/*
   Support for pseudorandom number generation

   This module defines an abstraction for a stream of pseudorandom
   numbers, :class:`RandomStream`.  It also provides a
   convenience function, :proc:`fillRandom` that can be used to
   fill an array with random numbers in parallel.

   The current pseudorandom number generator (PRNG) implemented by
   this module uses the algorithm from the NAS Parallel Benchmarks
   (NPB, available at: http://www.nas.nasa.gov/publications/npb.html).
   The longer-term intention is to add knobs to select between a menu
   of PRNG 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 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. 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.

   2. 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 :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).

   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
   :record:`SeedGenerators` is to provide a menu of other options
   for initializing the random stream seed, but only one option is
   implemented at present.

   4. As noted above, we plan to add support for additional PRNG
   algorithms over time.

*/
module Random {

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

  /* Internal TODOs for developers:

     - is the parSafe on each of the three main calls overkill?

     - should RandomStream be parameterized by the type to return?
       it seems odd that currently getNext() and getNth() return
       reals always...
  */

/*
   An instance of :record:`SeedGenerators` that provides a
   convenient means of generating seeds when the user does not wish to
   specify one manually.
*/
const SeedGenerator: SeedGenerators;

// CHPLDOC FIXME: Respect ordering of things in the file

/*
  Provides methods to help generate seeds when the user doesn't want
  to create one.  It currently only supports one such method, but the
  intention is to add more over time.

  (Note: once Chapel supports static class methods,
  :const:`SeedGenerator` and :record:`SeedGenerators` should
  be combined into a single record type with static methods).
*/

record SeedGenerators {
  /*
    Generate a seed based on the current time in microseconds as
    reported by :proc:`Time.getCurrentTime`, ensuring that it
    meets the PRNG's requirements.
  */
  proc currentTime: int(64) {
    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);
  }
};

// CHPLDOC FEEDBACK: If easy, I'd suggest either deprecating the
// :arg <type> <name>: form or else switching the order to
// :arg <name> <type>: as it's an easy trap to simply type the same
// argument as in the function's signature.

// CHPLDOC FIXME: if the first line below is shifted one character to the
// left, it ends up being rendered like a method

// CHPLDOC FIXME: if the first line below is shifted one character to the
// right, it ends up causing a warning (promoted to error) in the .rst
// file.  It'd be preferable to have it declare the issue in terms of
// the .chpl line numbers.
//

/*
  Fill an array of `real(64)`, `imag(64)`, or `complex(128)` elements
  with pseudorandom values in parallel using a new
  :class:`RandomStream` created specifically for this call.  The
  first `arr.size` values from the stream will be assigned to the
  array's elements in row-major order for `real` and `imag` elements.
  For `complex` elements, consecutive pairs of random numbers are
  assigned to the real and imaginary components, respectively.  The
  parallelization strategy is determined by the array.

  :arg arr: The array to be filled, where T is real(64), imag(64), or complex(128).
  :type arr: [] T

  :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`.
  :type seed: int(64)
*/

proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime)
  where (arr.eltType == real || arr.eltType == imag || arr.eltType == complex) {
  var randNums = new RandomStream(seed, parSafe=false);
  randNums.fillRandom(arr);
  delete randNums;
}

pragma "no doc"
proc fillRandom(arr: [], seed: int(64) = SeedGenerator.currentTime) {
  compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
}

/*
  Models a stream of pseudorandom numbers.  See the module-level
  notes for :mod:`Random` for details on the PRNG used.
*/
class RandomStream {
  /*
    Indicates whether or not the RandomStream needs to be
    parallel-safe by default.  If multiple tasks interact with it in
    an uncoordinated fashion, this must be set to `true`.  If it will
    only be called from a single task, or if only one task will call
    into it at a time, setting to `false` will reduce overhead related
    to ensuring mutual exclusion.
  */
  param parSafe: bool = true;
  /*
    The seed value for the PRNG.  It must be an odd integer in the
    range (1, 2**46).
  */
  const seed: int(64);

  /*
    Constructs a new stream of random numbers using the specified seed
    and parallel safety.  Ensures that the seed value meets the PRNG's
    constraints.

    :arg seed: The seed to use for the PRNG.  Defaults to :proc:`SeedGenerator.currentTime <SeedGenerators.currentTime>`..
    :type seed: int(64)

    :arg parSafe: The parallel safety setting.  Defaults to `true`.
    :type parSafe: bool
  */
  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);
  }

  pragma "no doc"
  proc RandomStreamPrivate_getNext_noLock() {
    RandomStreamPrivate_count += 1;
    return RandomPrivate_randlc(RandomStreamPrivate_cursor);
  }

  /*
    Returns the next value in the random stream as a `real(64)`

    :returns: The next value in the random stream as a `real(64)`.
   */
  proc getNext(): real(64) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const result = RandomStreamPrivate_getNext_noLock();
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  pragma "no doc"
  proc RandomStreamPrivate_skipToNth_noLock(n: integral) {
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = RandomPrivate_randlc_skipto(seed, n);
  }

  /*
    Advances/rewinds the stream to the `n`-th value in the sequence.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral
   */

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

  /*
    Advance/rewind the stream to the `n`-th value and return it
    (advancing the strem by one).  This is equivalent to
    :proc:`skipToNth()` followed by :proc:`getNext()`.

    :arg n: The position in the stream to skip to.  Must be non-negative.
    :type n: integral

    :returns: The `n`-th value in the random stream as a `real(64)`.
  */

  proc getNth(n: integral): real(64) {
    if (n <= 0) then
      halt("RandomStream.getNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_skipToNth_noLock(n);
    const result = RandomStreamPrivate_getNext_noLock();
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  /*
    Fill the argument array with pseudorandom values.  This method is
    identical to the standalone :proc:`fillRandom` procedure,
    except that it consumes random values from the
    :class:`RandomStream` object on which it's invoked rather
    than creating a new stream for the purpose of the call.

    :arg arr: The array to be filled, where T is real(64), imag(64), or complex(128).
    :type arr: [] T
  */

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

  pragma "no doc"
  proc iterate(D: domain, type resultType=real) {
    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);
    RandomStreamPrivate_skipToNth_noLock(RandomStreamPrivate_count);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  pragma "no doc"
  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.
  //

  pragma "no doc"
  var RandomStreamPrivate_lock$: sync bool;
  pragma "no doc"
  var RandomStreamPrivate_cursor: real;
  pragma "no doc"
  var RandomStreamPrivate_count: int(64);

  pragma "no doc"
  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
//
pragma "no doc"
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
//
pragma "no doc"
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)
pragma "no doc"
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
//
pragma "no doc"
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
//
pragma "no doc"
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
//
pragma "no doc"
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);
}

pragma "no doc"
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;
}

pragma "no doc"
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