Updates to stream-fragmented.chpl

Updates to stream-fragmented.chpl

Based on conversations with David that stream-fragmented was
not getting competitive performance with stream, rewrote the
timings and GB/s calculations to more closely resemble MPI's
and inserted a local block.  This helps, but on my desktop,
we still seem to be a factor of ~3 off from the GB/s time
reported by stream.chpl.

Possible causes:

1) It looks like some COMM_ calls are not being removed from the main
   loop in spite of the local { } block.

2) Could be some factor related to the fact that we're using the
   DefaultArithmetic distribution rather than Block1D.

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

use Time, Types, Random;
use hpccMultilocale;

use HPCCProblemSize;

param numVectors = 3;
type elemType = real(64),
     indexType = int(64);

config const m = computeProblemSize(numVectors, elemType),
             alpha = 3.0;

config const numTrials = 10,
             epsilon = 0.0;

config const useRandomSeed = true,
             seed = if useRandomSeed then SeedGenerator.clockMS else 314159265;

config const printParams = true,
             printArrays = false,
             printStats = true;

def main() {
  printConfiguration();

  const ProblemSpace: domain(1, indexType) = [1..m];

  var localGBs: [LocaleSpace] real,
      allValidAnswer: [LocaleSpace] bool;

  coforall loc in Locales {
    on loc {
      const myProblemSpace: domain(1, indexType)
                          = BlockPartition(ProblemSpace, here.id, numLocales);

      var myA, myB, myC: [myProblemSpace] elemType;

      initVectors(myB, myC, ProblemSpace);

      const localTimings: [1..numTrials] real;

      for trial in 1..numTrials {
        const startTime = getCurrentTime();
        local {
	forall (a, b, c) in (myA, myB, myC) do
	  a = b + alpha * c;
        }
        localTimings(trial) = getCurrentTime() - startTime;
      }

      const myMinTime = min reduce localTimings;

      localGBs(here.id) = numVectors * numBytes(elemType) * (m / myMinTime) * 1e-9;
      allValidAnswer(here.id) = verifyResults(myA, myB, myC);
    }
  }

  const minGBs = min reduce localGBs,
        maxGBs = max reduce localGBs,
        avgGBs = + reduce localGBs / numLocales;

  const validAnswer = & reduce allValidAnswer;

  printResults(validAnswer, minGBs, avgGBs, maxGBs);
}

def printConfiguration() {
  if (printParams) {
    printProblemSize(elemType, numVectors, m);
    writeln("Number of trials = ", numTrials, "\n");
  }
}

def initVectors(B, C, ProblemSpace) {
  var randlist = new RandomStream(seed);

  randlist.skipToNth(B.domain.low);
  randlist.fillRandom(B);
  randlist.skipToNth(ProblemSpace.numIndices + C.domain.low);
  randlist.fillRandom(C);

  if (printArrays) {
    writelnFragArray("B is: ", B, "\n");
    writelnFragArray("C is: ", C, "\n");
  }
}

def verifyResults(A, B, C) {
  if (printArrays) then writelnFragArray("A is: ", A, "\n");

  const infNorm = max reduce [i in A.domain] abs(A(i) - (B(i) + alpha * C(i)));

  return (infNorm <= epsilon);
}

def printResults(successful, minGBs, avgGBs, maxGBs) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Fragmented, per-locale GBs:");
    writeln("  min = ", minGBs);
    writeln("  avg = ", avgGBs);
    writeln("  max = ", maxGBs);
  }
}

use Time, Types, Random;
use hpccMultilocale;

use HPCCProblemSize;

param numVectors = 3;
type elemType = real(64),
     indexType = int(64);

config const m = computeProblemSize(numVectors, elemType),
             alpha = 3.0;

config const numTrials = 10,
             epsilon = 0.0;

config const useRandomSeed = true,
             seed = if useRandomSeed then SeedGenerator.clockMS else 314159265;

config const printParams = true,
             printArrays = false,
             printStats = true;

def main() {
  printConfiguration();

  const ProblemSpace: domain(1, indexType) = [1..m];

  var minTimes, maxTimes, avgTimes: [LocaleSpace] real,
      allValidAnswer: [LocaleSpace] bool;

  coforall loc in Locales {
    on loc {
      const myProblemSpace: domain(1, indexType)
                          = BlockPartition(ProblemSpace, here.id, numLocales);

      var myA, myB, myC: [myProblemSpace] elemType;

      initVectors(myB, myC, ProblemSpace);

      const localTimings: [1..numTrials] real;

      for trial in 1..numTrials {
        const startTime = getCurrentTime();
	for (a, b, c) in (myA, myB, myC) do
	  a = b + alpha * c;
        localTimings(trial) = getCurrentTime() - startTime;
      }

      minTimes[here.id] = min reduce localTimings;
      maxTimes[here.id] = max reduce localTimings;
      avgTimes[here.id] = + reduce localTimings / numTrials;

      allValidAnswer(here.id) = verifyResults(myA, myB, myC);
    }
  }

  const minTime = min reduce minTimes,
        maxTime = max reduce maxTimes,
        avgTime = + reduce avgTimes / numLocales;

  const validAnswer = & reduce allValidAnswer;

  printResults(validAnswer, minTime, avgTime, maxTime);
}

def printConfiguration() {
  if (printParams) {
    printProblemSize(elemType, numVectors, m);
    writeln("Number of trials = ", numTrials, "\n");
  }
}

def initVectors(B, C, ProblemSpace) {
  var randlist = new RandomStream(seed);

  randlist.skipToNth(B.domain.low);
  randlist.fillRandom(B);
  randlist.skipToNth(ProblemSpace.numIndices + C.domain.low);
  randlist.fillRandom(C);

  if (printArrays) {
    writelnFragArray("B is: ", B, "\n");
    writelnFragArray("C is: ", C, "\n");
  }
}

def verifyResults(A, B, C) {
  if (printArrays) then writelnFragArray("A is: ", A, "\n");

  const infNorm = max reduce [i in A.domain] abs(A(i) - (B(i) + alpha * C(i)));

  return (infNorm <= epsilon);
}

def printResults(successful, minTime, avgTime, maxTime) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Execution time:");
    writeln("  min = ", minTime);
    writeln("  avg = ", avgTime);
    writeln("  max = ", maxTime);

    const GBPerSec = numVectors * numBytes(elemType) * (m / minTime) * 1e-9;
    writeln("Performance (GB/s) = ", GBPerSec);
  }
}


Checking in a commented out Random int function
that I wrote once but never got completely clean
due to range accuracy issues.  There was some
thought about adding a compilerWarning() call
that would be invoked if the integer size was
too big...

(note to self: see Dec 11, 2007 mail to Richard
Barrett for a bit more context).

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

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

use Time;

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

var SeedGenerator = new SeedGeneratorClass();

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  // These are meant to be private; internal to class; not set by user
  const r23   = 0.5**23,
        t23   = 2.0**23,
        r46   = 0.5**46,
        t46   = 2.0**46;
  var internalSeed:int(64);

  var cursorVal: real;

  def initialize() {
    internalSeed = seed;
    // ensure seed is odd
    if (internalSeed % 2 == 0) then internalSeed += 1;
    initCursorVal();
  }

  def initCursorVal() {
    cursorVal = internalSeed:real;
  }

  def randlc(inout x, a = 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;
  }

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

  // n is assumed to be 1..
  def getNth(in n : int) {
    n -= 1;
    var t = arand;
    initCursorVal();
    var retval = arand;
    while (n != 0) {
      const i = n / 2;
      if (2 * i != n) then
        randlc(cursorVal, t);
      retval = randlc(t, t);
      n = i;
    }
    return getNext();
  }

  def fillRandom(x: [] real) {
    for i in x.domain {
      x(i) = getNext();
    }
  }

  def fillRandom(x: [] complex) {
    for i in x.domain {
      x(i).re = getNext();
      x(i).im = getNext();
    }
  }

  /*  BLC: Would like to add something like this, but should
      really add a warning if the range is greater than max(int(64))
      or a real's precision something to deal with the possible
      accuracy issues...
  def fillRandom(x: [],
		 minval: x.eltType = min(x.eltType),
		 maxval: x.eltType = max(x.eltType))
               where _isIntegralType(x.eltType) {
    const numVals = maxval:real - minval:real + 1;
    for i in x.domain {
      x(i) = (minval + (getNext() * numVals)): x.eltType;
    }
  }
  */
}

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

  randNums.fillRandom(x);
}

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

use Time;

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

var SeedGenerator = new SeedGeneratorClass();

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  // These are meant to be private; internal to class; not set by user
  const r23   = 0.5**23,
        t23   = 2.0**23,
        r46   = 0.5**46,
        t46   = 2.0**46;
  var internalSeed:int(64);

  var cursorVal: real;

  def initialize() {
    internalSeed = seed;
    // ensure seed is odd
    if (internalSeed % 2 == 0) then internalSeed += 1;
    initCursorVal();
  }

  def initCursorVal() {
    cursorVal = internalSeed:real;
  }

  def randlc(inout x, a = 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;
  }

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

  // n is assumed to be 1..
  def getNth(in n : int) {
    n -= 1;
    var t = arand;
    initCursorVal();
    var retval = arand;
    while (n != 0) {
      const i = n / 2;
      if (2 * i != n) then
        randlc(cursorVal, t);
      retval = randlc(t, t);
      n = i;
    }
    return getNext();
  }

  def fillRandom(x: [] real) {
    for i in x.domain {
      x(i) = getNext();
    }
  }

  def fillRandom(x: [] complex) {
    for i in x.domain {
      x(i).re = getNext();
      x(i).im = getNext();
    }
  }
}

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

  randNums.fillRandom(x);
}


spin wait to sync fix

spin wait to sync fix

Fixed this test which failed today.  I think this change captures the salient
features of this test.

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

var go: sync bool;

var a: sync int;

var c = 1;

def work() {
  var b = 2;

  begin {
    go;
    b = 2 * c;
    writeln("b is ", b);
    c = 2 * b;
    a = 2 * b;
    go = true;
  }
}

def jam_records() {
  cobegin {
    work();
    begin {
      a;
      work();
    }
  }
}

jam_records();
go = true;

var a = 1;
var go = false;

def work() {
  var b = 2;

  begin {
    while (!go);
    b = 2 * a;
    writeln( "b is ", b);
    a = 2 * b;
  }
}

def jam_records() {
  cobegin {
    work();
    begin {
      while (a == 1);
      work();
    }
  }
}

jam_records();
go = true;


Added minor comments and changed iteration domain of a 'for' loop.

/*
This test is inspired by an example from Michael Dietrich.
It bears similarity to n-body.

Since computeOne() is very simple, the test currently emphasizes
communication costs and array/iteration overhead.

A potential improvement is to change data movements so that each locale
receives only from its "left" neighbor and sends only to its "right" neighbor.
*/

use Time;
use BlockDist;

config const
  test1 = true,
  test2 = true,
  timing = false;

config const n = 1000;
const D_dr = {1..n};

const D_distrib = D_dr dmapped Block(D_dr);
var A, B: [D_distrib] real;

proc computeOne(b_i, b_j) {
  return b_i + b_j;
}

// a simple version of the algorithm
proc try1() {

  var tm: Timer;
  tm.start();

  // "forall": parallel, distributed over D_distrib
  // compute all A[i] in parallel
  forall i in D_distrib {

    // "for": sequential, local; over a privitized copy of D_distrib
    // accumulate into A[i] sequentially
    for j in D_distrib {

      // some computation
      A[i] += computeOne(B[i], B[j]);

    }
  }

  tm.stop();
  if timing then writeln("no caching: ", tm.elapsed(), " seconds");
}

// overlap computation and communication
proc try2() {

  var tm: Timer;
  tm.start();

  // parallel over locales
  coforall loc_i in 0..#numLocales do on Locales[loc_i] {

      // local indices and slices of A, B
      const local_dom => B.localSubdomain();
      var local_A => A.localSlice(local_dom);
      var local_B => B.localSlice(local_dom);

      // local cache of a chunk of B used for computation
      // starting with the local portion
      var curr_dom = local_dom;
      var curr_array: [curr_dom] real;

      // local cache of the prefetched chunk of B
      var next_dom: curr_dom.type;
      var next_array: [next_dom] real;

      // process 'numLocales' chunks of B
      for loc_j in 0..#numLocales {

        const next_loc = (loc_i + loc_j + 1) % numLocales;
        const more_chunks = loc_j < numLocales-1;

        cobegin {

          // Task 1: prefetch into 'next_array'
          if more_chunks then on Locales[next_loc] {
            const local_subdom => B.localSubdomain();
            next_dom = local_subdom;
            next_array = B.localSlice(local_subdom);
          }

          // Task 2: compute with prefetched values in 'curr_array'
          // parallel within the locale
          forall (a_i, b_i) in zip(local_A, local_B) {
            for b_j in curr_array {
              a_i += computeOne(b_i, b_j);
            }
          }

        } // cobegin

        // move prefetched values from 'next_array' into 'curr_array'
        if more_chunks {
          curr_dom = next_dom;
          curr_array = next_array;
        }

      } // for loc_j

  } // coforall loc_i

  tm.stop();
  if timing then writeln("with caching: ", tm.elapsed(), " seconds");
}

proc main() {
  writeln("start");
  if test1 then try1();
  if test2 then try2();
  writeln("done");
}

/*
This test is inspired by an example from Michael Dietrich.
It bears similarity to n-body.

A potential improvement is to change data movements so that each locale
receives only from its "left" neighbor and sends only to its "right" neighbor.
*/

use Time;
use BlockDist;

config const
  test1 = true,
  test2 = true,
  timing = false;

config const n = 1000;
const D_dr = {1..n};

const D_distrib = D_dr dmapped Block(D_dr);
var A, B: [D_distrib] real;

proc computeOne(b_i, b_j) {
  return b_i + b_j;
}

// a simple version of the algorithm
proc try1() {

  var tm: Timer;
  tm.start();

  // "forall": parallel, distributed over D_distrib
  forall i in D_distrib {

    // "for": sequential, local
    for j in D_dr {

      // some computation
      A[i] += computeOne(B[i], B[j]);

    }
  }

  tm.stop();
  if timing then writeln("no caching: ", tm.elapsed(), " seconds");
}

// overlap computation and communication
proc try2() {

  var tm: Timer;
  tm.start();

  // parallel over locales
  coforall loc_i in 0..#numLocales do on Locales[loc_i] {

      // local indices and slices of A, B
      const local_dom => B.localSubdomain();
      var local_A => A.localSlice(local_dom);
      var local_B => B.localSlice(local_dom);

      // local cache of a chunk of B used for computation
      // starting with the local portion
      var curr_dom = local_dom;
      var curr_array: [curr_dom] real;

      // local cache of the prefetched chunk of B
      var next_dom: curr_dom.type;
      var next_array: [next_dom] real;

      // process 'numLocales' chunks of B
      for loc_j in 0..#numLocales {

        const next_loc = (loc_i + loc_j + 1) % numLocales;
        const more_chunks = loc_j < numLocales-1;

        cobegin {

          // Task 1: prefetch into 'next_array'
          if more_chunks then on Locales[next_loc] {
            const local_subdom => B.localSubdomain();
            next_dom = local_subdom;
            next_array = B.localSlice(local_subdom);
          }

          // Task 2: compute with prefetched values in 'curr_array'
          // parallel within the locale
          forall (a_i, b_i) in zip(local_A, local_B) {
            for b_j in curr_array {
              a_i += computeOne(b_i, b_j);
            }
          }

        } // cobegin

        // move prefetched values from 'next_array' into 'curr_array'
        if more_chunks {
          curr_dom = next_dom;
          curr_array = next_array;
        }

      } // for loc_j

  } // coforall loc_i

  tm.stop();
  if timing then writeln("with caching: ", tm.elapsed(), " seconds");
}

proc main() {
  writeln("start");
  if test1 then try1();
  if test2 then try2();
  writeln("done");
}


Fixed Random regressions

Fixed Random regressions

In addition to the changes I mentioned to Random last night, I also
did a little bit of code refactoring, and in doing so accidentally
introduced an int(32) where an int(64) was needed.  This check-in
fixes that change and should fix all of the stream regressions from
last night.

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

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

use Time;

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

var SeedGenerator = new SeedGeneratorClass();

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  // These are meant to be private; internal to class; not set by user
  const r23   = 0.5**23,
        t23   = 2.0**23,
        r46   = 0.5**46,
        t46   = 2.0**46;
  const internalSeed = computeInternalSeed();

  var sharedCursor = internalSeed:real;
  var sharedCount: int(64) = 1;

  def computeInternalSeed() {
    var val: int(64) = seed;
    // ensure seed is odd
    if (val % 2 == 0) then val += 1;
    return val;
  }

  def initCursorVal() {
    sharedCursor = internalSeed:real;
    sharedCount = 1;
  }

  def randlc(inout x, a = 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;
  }

  def getNext() {
    sharedCount += 1;
    return randlc(sharedCursor);
  }

  def skipToNth(in n: integral) {
    if (n <= 0) then
      halt("RandomStream.skipToNth() can only be called with positive values");
    initCursorVal();
    n -= 1;
    var t = arand;
    var retval = arand;
    while (n != 0) {
      const i = n / 2;
      if (2 * i != n) then
        randlc(sharedCursor, t);
      retval = randlc(t, t);
      n = i;
    }
    sharedCount = n;
  }

  // n is assumed to be 1..
  def getNth(n : integral) {
    if (n <= 0) then
      halt("RandomStream.getNth() can only be called with a positive value");
    skipToNth(n);
    return getNext();
  }

  def fillRandom(x: [] real) {
    for i in x.domain {
      x(i) = getNext();
    }
  }

  def fillRandom(x: [] complex) {
    for i in x.domain {
      x(i).re = getNext();
      x(i).im = getNext();
    }
  }

  def these() {
    halt("Need support for zipperable unbounded sequential iterators");
    yield getNext();
  }

  def these(leader) {
    halt("Someone's trying to call the leader");
    yield [1..10];
  }

  //
  // TODO: This will only work one time, and needs to start computing
  //       from the current value of sharedCount rather than assuming
  //       that the follower's indices are reasonable input for getNth
  //
  def these(follower) {
    // make a local copy of the 'this' random stream class
    var locStream = new RandomStream(seed);
    var val = locStream.getNth(follower.low);
    for i in follower {
      yield val;
      val = locStream.getNext();
    }
    //
    // Update the sharedCount of the original random stream class
    //
    // TODO: this needs to be locked to avoid a subtle race
    //       But really, we'd prefer some sort of setup/teardown for
    //       followers that gets the indices being iterated over.
    //       But since we're not really expecting this stream to
    //       be particularly useful a second time anyway, I didn't
    //       take this on right now.
    /*
    if (follower.high > sharedCount) {
      sharedCount = follower.high;
    }
    */
  }

  /*  BLC: Would like to add something like this, but should
      really add a warning if the range is greater than max(int(64))
      or a real's precision something to deal with the possible
      accuracy issues...
  def fillRandom(x: [],
		 minval: x.eltType = min(x.eltType),
		 maxval: x.eltType = max(x.eltType))
               where _isIntegralType(x.eltType) {
    const numVals = maxval:real - minval:real + 1;
    for i in x.domain {
      x(i) = (minval + (getNext() * numVals)): x.eltType;
    }
  }
  */
}

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

  randNums.fillRandom(x);
}

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

use Time;

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

var SeedGenerator = new SeedGeneratorClass();

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  // These are meant to be private; internal to class; not set by user
  const r23   = 0.5**23,
        t23   = 2.0**23,
        r46   = 0.5**46,
        t46   = 2.0**46;
  const internalSeed = computeInternalSeed();

  var sharedCursor = internalSeed:real;
  var sharedCount = 1;

  def computeInternalSeed() {
    var val: int(64) = seed;
    // ensure seed is odd
    if (val % 2 == 0) then val += 1;
    return val;
  }

  def initCursorVal() {
    sharedCursor = internalSeed:real;
    sharedCount = 1;
  }

  def randlc(inout x, a = 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;
  }

  def getNext() {
    sharedCount += 1;
    return randlc(sharedCursor);
  }

  def skipToNth(in n: integral) {
    if (n <= 0) then
      halt("RandomStream.skipToNth() can only be called with positive values");
    initCursorVal();
    n -= 1;
    var t = arand;
    var retval = arand;
    while (n != 0) {
      const i = n / 2;
      if (2 * i != n) then
        randlc(sharedCursor, t);
      retval = randlc(t, t);
      n = i;
    }
    sharedCount = n;
  }

  // n is assumed to be 1..
  def getNth(n : integral) {
    if (n <= 0) then
      halt("RandomStream.getNth() can only be called with a positive value");
    skipToNth(n);
    return getNext();
  }

  def fillRandom(x: [] real) {
    for i in x.domain {
      x(i) = getNext();
    }
  }

  def fillRandom(x: [] complex) {
    for i in x.domain {
      x(i).re = getNext();
      x(i).im = getNext();
    }
  }

  def these() {
    halt("Need support for zipperable unbounded sequential iterators");
    yield getNext();
  }

  def these(leader) {
    halt("Someone's trying to call the leader");
    yield [1..10];
  }

  //
  // TODO: This will only work one time, and needs to start computing
  //       from the current value of sharedCount rather than assuming
  //       that the follower's indices are reasonable input for getNth
  //
  def these(follower) {
    // make a local copy of the 'this' random stream class
    var locStream = new RandomStream(seed);
    var val = locStream.getNth(follower.low);
    for i in follower {
      yield val;
      val = locStream.getNext();
    }
    //
    // Update the sharedCount of the original random stream class
    //
    // TODO: this needs to be locked to avoid a subtle race
    //       But really, we'd prefer some sort of setup/teardown for
    //       followers that gets the indices being iterated over.
    //       But since we're not really expecting this stream to
    //       be particularly useful a second time anyway, I didn't
    //       take this on right now.
    /*
    if (follower.high > sharedCount) {
      sharedCount = follower.high;
    }
    */
  }

  /*  BLC: Would like to add something like this, but should
      really add a warning if the range is greater than max(int(64))
      or a real's precision something to deal with the possible
      accuracy issues...
  def fillRandom(x: [],
		 minval: x.eltType = min(x.eltType),
		 maxval: x.eltType = max(x.eltType))
               where _isIntegralType(x.eltType) {
    const numVals = maxval:real - minval:real + 1;
    for i in x.domain {
      x(i) = (minval + (getNext() * numVals)): x.eltType;
    }
  }
  */
}

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

  randNums.fillRandom(x);
}


Mark argument names as syntax in comment.

Trivial comment cleanup.

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

/*
   The `Search` module is designed to support standard search routines. The
   current interface is minimal and should be expected to grow and evolve
   over time.
 */

/*
   Searches through the pre-sorted array `Data` looking for the value `val` using
   a sequential linear search.  Returns a tuple indicating (1) whether or not
   the value was found and (2) the location of the value if it was found, or
   the location where the value should have been if it was not found.

   :arg Data: The sorted array to search
   :arg val: The value to find in the array

   :returns: A tuple indicating (1) if the value was found and (2) the location of the value if it was found or the location where the value should have been if it was not found.

 */
proc LinearSearch(Data:[?Dom], val) {
  for i in Dom {
    if (Data(i) == val) {
      return (true, i);
    } else if (Data(i) > val) {
      return (false, i);
    }
  }
  return (false,Dom.high+1);
}

// would really like to drop the lo/hi arguments here, but right now
// that causes too big of a memory leak
/*
   Searches through the pre-sorted array `Data` looking for the value `val`
   using a sequential binary search.  If provided, only the indices `lo`
   through `hi` will be considered, otherwise the whole array will be
   searched. Returns a tuple indicating (1) whether or not the value was
   found and (2) the location of the value if it was found, or the location
   where the value should have been if it was not found.

   :arg Data: The sorted array to search
   :arg val: The value to find in the array
   :arg lo: The lowest index to consider while searching
   :type lo: `integral`
   :arg hi: The highest index to consider while searching
   :type hi: `integral`

   :returns: A tuple indicating (1) if the value was found and (2) the location of the value if it was found or the location where the value should have been if it was not found.

 */
proc BinarySearch(Data:[?Dom], val, in lo = Dom.low, in hi = Dom.high) {
  while (lo <= hi) {
    const mid = (hi - lo)/2 + lo;
    if (Data(mid) == val) {
      return (true, mid);
    } else if (val > Data(mid)) {
      lo = mid+1;
    } else {
      hi = mid-1;
    }
  }
  return (false, lo);
}

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

/*
   The `Search` module is designed to support standard search routines. The
   current interface is minimal and should be expected to grow and evolve
   over time.
 */

/*
   Searches through the pre-sorted array Data looking for the value val using
   a sequential linear search.  Returns a tuple indicating (1) whether or not
   the value was found and (2) the location of the value if it was found, or
   the location where the value should have been if it was not found.

   :arg Data: The sorted array to search
   :arg val: The value to find in the array

   :returns: A tuple indicating (1) if the value was found and (2) the location of the value if it was found or the location where the value should have been if it was not found.

 */
proc LinearSearch(Data:[?Dom], val) {
  for i in Dom {
    if (Data(i) == val) {
      return (true, i);
    } else if (Data(i) > val) {
      return (false, i);
    }
  }
  return (false,Dom.high+1);
}

// would really like to drop the lo/hi arguments here, but right now
// that causes too big of a memory leak
/*
   Searches through the pre-sorted array `Data` looking for the value `val`
   using a sequential binary search.  If provided, only the indices `lo`
   through `hi` will be considered, otherwise the whole array will be
   searched. Returns a tuple indicating (1) whether or not the value was
   found and (2) the location of the value if it was found, or the location
   where the value should have been if it was not found.

   :arg Data: The sorted array to search
   :arg val: The value to find in the array
   :arg lo: The lowest index to consider while searching
   :type lo: `integral`
   :arg hi: The highest index to consider while searching
   :type hi: `integral`

   :returns: A tuple indicating (1) if the value was found and (2) the location of the value if it was found or the location where the value should have been if it was not found.

 */
proc BinarySearch(Data:[?Dom], val, in lo = Dom.low, in hi = Dom.high) {
  while (lo <= hi) {
    const mid = (hi - lo)/2 + lo;
    if (Data(mid) == val) {
      return (true, mid);
    } else if (val > Data(mid)) {
      lo = mid+1;
    } else {
      hi = mid-1;
    }
  }
  return (false, lo);
}