Add support for standard ops on tuple/scalar pairs

[reviewed by diten]

This patch adds overloads of standard operators on pairs of homogenous
tuples and scalars of matching element type by inserting them into the
internal ChapelTuple.chpl module.  The supported operators are: +, -,
*, /, %, **, &, |, ^, <<, and >>.  The implementation essentially
applies the scalar operator to each tuple element returning a tuple
result.  I shied away from equality-oriented operators due to concerns
that it such cases may represent programmer error rather than an
intention promote an equality test across the tuple components.  This
support could be reconsidered later or made available by a standard
module if desired but considered to be a dangerous default.

We have supported such overloads in the past and backed them out.
Browsing the old logs, the rationale for removing the support most
recently (r11766) was due to the implementation approach taken rather
than the lack of utility of the functions.  In particular, the
operators were supported using signatures as follows (excuse the old
syntax):

        pragma "inline" def +(a: _tuple, b) { ... }
        pragma "inline" def +(a, b: _tuple) { ... }

and the issue was that the completely generic non-tuple argument cast
too wide of a net, catching things that it wasn't intended to.  For
example, adding a tuple to an array of tuples fell into this overload
rather than promoting the +(tuple,tuple) overload.

The commit also rationalizes that users who want this can overload it
themselves which is true, but in practice, I find that these are very
natural operators to desire when doing tuple work (like storing
velocities and positions as tuples and shifting or scaling them, as in
n-body codes), and an annoyance to implement.

The main downside of supporting these operators is that it prevents
users from writing their own overloads with the same signatures, if
they desire.  My proposed solution to this would be to move these
overloaded operators, and those on tuple-tuple pairs, into an
auto-included standard module, and adding the capability we've
discussed in the past to override the auto-use for a particular
file/scope.

The overloads that I wanted to use to implement these operators
would have ideally been something like:

        inline proc +(a:?*?t, b: t)

That is, "accept a homogenous tuple of 't's of any size and a second
argument that coerces to type 't'.  However, the formal type query
syntax as implemented isn't rich enough to do this yet.  There's also
the question of how the reverse case +(scalar, tuple) would be written and
how symmetric it should be in terms of coercible types.

Instead, I've used the following signatures:

       inline proc +(x:_tuple, y: x(1).type) where isHomogeneousTuple(x) ...

and:

        inline proc +(x:?t, y: _tuple) where isHomogeneousTuple(y) &&
                                             t: (y(1).type)  ...

where the t: (y(1).type) is intended to express "t is coercible to
the type of the tuple elements" but doesn't quite seem to work
that way in the implementation -- it's acting more like equality
(TODO: why is this?)

One other open question is whether these operators should be extended
to heterogeneous tuples as well (as the tuple-tuple forms are).  I've
chosen not to here, primarily for simplicity and because it supports
the cases that I run into commonly.  I also think there's an argument
to be made that it's hard to know how to characterize what kind of
scalar argument can be combined with a heterogeneous tuple (which is
what the previous implementation was effectively trying to do and what
got it into trouble).  I can also imagine that we might want to
re-think whether the tuple-tuple ops should only be supported (at
least by default) for homogenous tuples down the road (for simplicity,
to avoid surprises), though I'm not championing that here.

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

// ChapelTuple.chpl
//
// tuple data implementation as a record
//
pragma "no use ChapelStandard"
module ChapelTuple {

  pragma "tuple" record _tuple {
    param size : int;
  }

  //
  // syntactic support for tuples
  //
  inline proc _build_tuple(x...) {
      return x;
  }

  pragma "allow ref"
  inline proc _build_tuple_always_allow_ref(x ...?size)
    return x;

  inline proc _build_tuple(type t ...?size) type
    return t;

  inline proc _build_tuple_always(x ...?size)
    return x;

  //
  // homogeneous tuple type syntax
  //
  proc *(param p: int, type t) type {
    var oneTuple: _build_tuple(t);
    proc _fill(param p: int, x: _tuple) {
      if x.size == p then
        return x;
      else if 2*x.size <= p then
        return _fill(p, ((...x), (...x)));
      else
        return _fill(p, ((...x), (..._fill(p-x.size, oneTuple))));
    }
    if p <= 0 then
      compilerError("tuple must have positive size");
    return _fill(p, oneTuple).type;
  }

  proc *(p: int, type t) type {
    compilerError("tuple size must be static");
  }

  // make it a tuple if it is not already
  inline proc chpl__tuplify(x) {
    if isTuple(x) then return x; else return (x,);
  }

  //
  // isTuple, isTupleType and isHomogeneousTuple param functions
  //
  proc isTuple(x: _tuple) param
    return true;

  proc isTuple(x) param
    return false;

  proc isHomogeneousTuple(x: _tuple) param
    return __primitive("is star tuple type", x);

  proc isTupleType(type t) param {
    const a: t;
    return isTuple(a);
  }

  //
  // tuple assignment
  //
  inline proc =(x: _tuple, y: _tuple) where x.size == y.size {
    for param i in 1..x.size do
      x(i) = y(i);
    return x;
  }

  //
  // homogeneous tuple accessor
  //
  proc _tuple.this(i : integral) var {
    if !isHomogeneousTuple(this) then
      compilerError("invalid access of non-homogeneous tuple by runtime value");
    if boundsChecking then
      if i < 1 || i > size then
        halt("tuple access out of bounds: ", i);
    return __primitive("get svec member", this, i);
  }

  config param CHPL_WARN_TUPLE_ITERATION = "unset";
  //
  // iterator support for tuples
  //
  iter _tuple.these() {

    if !isHomogeneousTuple(this) then
      compilerError("Cannot iterate over non-homogeneous tuples. If you intended to use zippered iteration, add the new keyword 'zip' before the tuple ofiteratable expressions.");

    if CHPL_WARN_TUPLE_ITERATION == "true" then
      compilerWarning("Iterating over tuples. If you intended to use zippered iteration, add the new keyword 'zip' before the tuple of iteratable expressions.");

    for i in 1..this.size {
      yield(this(i));
    }
  }

  iter _tuple.these(param tag:iterKind)
      where tag == iterKind.leader
  {

    const numTasks = if dataParTasksPerLocale==0 then here.numCores
                     else dataParTasksPerLocale;
    const ignoreRunning = dataParIgnoreRunningTasks;
    const minIndicesPerTask = dataParMinGranularity;
    const length = this.size;
    const myRange = 1..#length;
    var (numChunks, _) = _computeChunkStuff(numTasks, ignoreRunning,
                                            minIndicesPerTask,
                                            (myRange,));

    if numChunks == 1 {
      yield myRange;
    } else {

      coforall chunk in 0..#numChunks {
        // _computeBlock assumes 0-based ranges
        const (lo,hi) = _computeBlock(length, numChunks, chunk, length-1);
        // adjust for 1-based indexing
        yield lo+1..hi+1;
      }
    }
  }

  iter _tuple.these(param tag:iterKind, followThis)
      where tag == iterKind.follower
  {
    for i in followThis do yield this(i);
  }

  //
  // tuple methods
  //
  proc _tuple.readWriteThis(f) {
    var st = f.styleElement(QIO_STYLE_ELEMENT_TUPLE);
    var start:ioLiteral;
    var comma:ioLiteral;
    var end:ioLiteral;

    if st == QIO_TUPLE_FORMAT_SPACE {
      start = new ioLiteral("");
      comma = new ioLiteral(" ");
      end = new ioLiteral("");
    } else if st == QIO_TUPLE_FORMAT_JSON {
      start = new ioLiteral("[");
      comma = new ioLiteral(", ");
      end = new ioLiteral("]");
    } else {
      start = new ioLiteral("(");
      comma = new ioLiteral(", ");
      end = new ioLiteral(")");
    }

    if !f.binary {
      f <~> start;
    }
    if size != 0 {
      f <~> this(1);
      for param i in 2..size {
        if !f.binary {
          f <~> comma;
        }
        f <~> this(i);
      }
    }
    if !f.binary {
      f <~> end;
    }
  }

  //
  // tuple casts to complex(64) and complex(128)
  //
  inline proc _cast(type t, x: (?,?)) where t == complex(64) {
    var c: complex(64);
    c.re = x(1):real(32);
    c.im = x(2):real(32);
    return c;
  }

  inline proc _cast(type t, x: (?,?)) where t == complex(128) {
    var c: complex(128);
    c.re = x(1):real(64);
    c.im = x(2):real(64);
    return c;
  }

  //
  // helper function to return a tuple of all of the components in a
  // tuple except the first
  //
  inline proc chpl__tupleRest(t: _tuple) {
    proc chpl__tupleRestHelper(first, rest...)
      return rest;
    return chpl__tupleRestHelper((...t));
  }

  //
  // standard overloaded unary operators on tuples
  //
  inline proc +(a: _tuple) {
    if a.size == 1 then
      return (+a(1), );
    else
      return (+a(1), (...+chpl__tupleRest(a)));
  }

  inline proc -(a: _tuple) {
    if a.size == 1 then
      return (-a(1),);
    else
      return (-a(1), (...-chpl__tupleRest(a)));
  }

  inline proc ~(a: _tuple) {
    if a.size == 1 then
      return (~a(1),);
    else
      return (~a(1), (...~chpl__tupleRest(a)));
  }

  inline proc !(a: _tuple) {
    if a.size == 1 then
      return (!a(1),);
    else
      return (!a(1), (...!chpl__tupleRest(a)));
  }

  //
  // Return a tuple of type t with all values set to the max/min for that type
  //
  proc max(type t): t where isTupleType(t) {
    var result: t;
    for param i in 1..result.size {
      result(i) = max(t(i));
    }
    return result;
  }

  proc min(type t): t where isTupleType(t) {
    var result: t;
    for param i in 1..result.size {
      result(i) = min(t(i));
    }
    return result;
  }

  //
  // standard overloaded binary operators on tuples
  //
  inline proc +(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to + have different sizes");
    if a.size == 1 then
      return (a(1)+b(1),);
    else
      return (a(1)+b(1), (...chpl__tupleRest(a)+chpl__tupleRest(b)));
  }

  inline proc -(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to - have different sizes");
    if a.size == 1 then
      return (a(1)-b(1),);
    else
      return (a(1)-b(1), (...chpl__tupleRest(a)-chpl__tupleRest(b)));
  }

  inline proc *(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to * have different sizes");
    if a.size == 1 then
      return (a(1)*b(1),);
    else
      return (a(1)*b(1), (...chpl__tupleRest(a)*chpl__tupleRest(b)));
  }

  inline proc /(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to / have different sizes");
    if a.size == 1 then
      return (a(1)/b(1),);
    else
      return (a(1)/b(1), (...chpl__tupleRest(a)/chpl__tupleRest(b)));
  }

  inline proc %(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to % have different sizes");
    if a.size == 1 then
      return (a(1)%b(1),);
    else
      return (a(1)%b(1), (...chpl__tupleRest(a)%chpl__tupleRest(b)));
  }

  inline proc **(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to ** have different sizes");
    if a.size == 1 then
      return (a(1)**b(1),);
    else
      return (a(1)**b(1), (...chpl__tupleRest(a)**chpl__tupleRest(b)));
  }

  inline proc &(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to & have different sizes");
    if a.size == 1 then
      return (a(1)&b(1),);
    else
      return (a(1)&b(1), (...chpl__tupleRest(a)&chpl__tupleRest(b)));
  }

  inline proc |(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to | have different sizes");
    if a.size == 1 then
      return (a(1)|b(1),);
    else
      return (a(1)|b(1), (...chpl__tupleRest(a)|chpl__tupleRest(b)));
  }

  inline proc ^(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to ^ have different sizes");
    if a.size == 1 then
      return (a(1)^b(1),);
    else
      return (a(1)^b(1), (...chpl__tupleRest(a)^chpl__tupleRest(b)));
  }

  inline proc <<(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to << have different sizes");
    if a.size == 1 then
      return (a(1)<<b(1),);
    else
      return (a(1)<<b(1), (...chpl__tupleRest(a)<<chpl__tupleRest(b)));
  }

  inline proc >>(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to >> have different sizes");
    if a.size == 1 then
      return (a(1)>>b(1),);
    else
      return (a(1)>>b(1), (...chpl__tupleRest(a)>>chpl__tupleRest(b)));
  }

  //
  // standard overloaded relational operators on tuples
  //
  inline proc >(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to > have different sizes");
    for param i in 1..a.size do
      if a(i) > b(i) then
        return true;
      else if a(i) < b(i) then
        return false;
    return false;
  }

  inline proc >=(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to >= have different sizes");
    for param i in 1..a.size do
      if a(i) > b(i) then
        return true;
      else if a(i) < b(i) then
        return false;
    return true;
  }

  inline proc <(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to < have different sizes");
    for param i in 1..a.size do
      if a(i) < b(i) then
        return true;
      else if a(i) > b(i) then
        return false;
    return false;
  }

  inline proc <=(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to <= have different sizes");
    for param i in 1..a.size do
      if a(i) < b(i) then
        return true;
      else if a(i) > b(i) then
        return false;
    return true;
  }

  inline proc ==(a: _tuple, b: _tuple) {
    if a.size != b.size then
      return false;
    for param i in 1..a.size do
      if a(i) != b(i) then
        return false;
    return true;
  }

  inline proc !=(a: _tuple, b: _tuple) {
    if a.size != b.size then
      return true;
    for param i in 1..a.size do
      if a(i) != b(i) then
        return true;
    return false;
  }

  // standard overloaded binary operators on homog tuple / scalar pairs

  inline proc +(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)+y,);
    else
      return (x(1)+y, (...chpl__tupleRest(x)+y));
  }

  inline proc +(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x+y(1),);
    else
      return (x+y(1), (...x+chpl__tupleRest(y)));
  }

  inline proc -(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)-y,);
    else
      return (x(1)-y, (...chpl__tupleRest(x)-y));
  }

  inline proc -(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x-y(1),);
    else
      return (x-y(1), (...x-chpl__tupleRest(y)));
  }

  inline proc *(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)*y,);
    else
      return (x(1)*y, (...chpl__tupleRest(x)*y));
  }

  inline proc *(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       x: (y(1).type)  {
    if y.size == 1 then
      return (x*y(1),);
    else
      return (x*y(1), (...x*chpl__tupleRest(y)));
  }

  inline proc /(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)/y,);
    else
      return (x(1)/y, (...chpl__tupleRest(x)/y));
  }

  inline proc /(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x/y(1),);
    else
      return (x/y(1), (...x/chpl__tupleRest(y)));
  }

  inline proc %(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)%y,);
    else
      return (x(1)%y, (...chpl__tupleRest(x)%y));
  }

  inline proc %(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x%y(1),);
    else
      return (x%y(1), (...x%chpl__tupleRest(y)));
  }

  inline proc **(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)**y,);
    else
      return (x(1)**y, (...chpl__tupleRest(x)**y));
  }

  inline proc **(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x**y(1),);
    else
      return (x**y(1), (...x**chpl__tupleRest(y)));
  }

  inline proc &(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)&y,);
    else
      return (x(1)&y, (...chpl__tupleRest(x)&y));
  }

  inline proc &(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x&y(1),);
    else
      return (x&y(1), (...x&chpl__tupleRest(y)));
  }

  inline proc |(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)|y,);
    else
      return (x(1)|y, (...chpl__tupleRest(x)|y));
  }

  inline proc |(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x|y(1),);
    else
      return (x|y(1), (...x|chpl__tupleRest(y)));
  }

  inline proc ^(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)^y,);
    else
      return (x(1)^y, (...chpl__tupleRest(x)^y));
  }

  inline proc ^(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x^y(1),);
    else
      return (x^y(1), (...x^chpl__tupleRest(y)));
  }

  inline proc <<(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)<<y,);
    else
      return (x(1)<<y, (...chpl__tupleRest(x)<<y));
  }

  inline proc <<(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x<<y(1),);
    else
      return (x<<y(1), (...x<<chpl__tupleRest(y)));
  }

  inline proc >>(x: _tuple, y: x(1).type) where isHomogeneousTuple(x) {
    if x.size == 1 then
      return (x(1)>>y,);
    else
      return (x(1)>>y, (...chpl__tupleRest(x)>>y));
  }

  inline proc >>(x: ?t, y: _tuple) where isHomogeneousTuple(y) &&
                                       t: (y(1).type)  {
    if y.size == 1 then
      return (x>>y(1),);
    else
      return (x>>y(1), (...x>>chpl__tupleRest(y)));
  }

}

// ChapelTuple.chpl
//
// tuple data implementation as a record
//
pragma "no use ChapelStandard"
module ChapelTuple {

  pragma "tuple" record _tuple {
    param size : int;
  }

  //
  // syntactic support for tuples
  //
  inline proc _build_tuple(x...) {
      return x;
  }

  pragma "allow ref"
  inline proc _build_tuple_always_allow_ref(x ...?size)
    return x;

  inline proc _build_tuple(type t ...?size) type
    return t;

  inline proc _build_tuple_always(x ...?size)
    return x;

  //
  // homogeneous tuple type syntax
  //
  proc *(param p: int, type t) type {
    var oneTuple: _build_tuple(t);
    proc _fill(param p: int, x: _tuple) {
      if x.size == p then
        return x;
      else if 2*x.size <= p then
        return _fill(p, ((...x), (...x)));
      else
        return _fill(p, ((...x), (..._fill(p-x.size, oneTuple))));
    }
    if p <= 0 then
      compilerError("tuple must have positive size");
    return _fill(p, oneTuple).type;
  }

  proc *(p: int, type t) type {
    compilerError("tuple size must be static");
  }

  // make it a tuple if it is not already
  inline proc chpl__tuplify(x) {
    if isTuple(x) then return x; else return (x,);
  }

  //
  // isTuple, isTupleType and isHomogeneousTuple param functions
  //
  proc isTuple(x: _tuple) param
    return true;

  proc isTuple(x) param
    return false;

  proc isHomogeneousTuple(x: _tuple) param
    return __primitive("is star tuple type", x);

  proc isTupleType(type t) param {
    const a: t;
    return isTuple(a);
  }

  //
  // tuple assignment
  //
  inline proc =(x: _tuple, y: _tuple) where x.size == y.size {
    for param i in 1..x.size do
      x(i) = y(i);
    return x;
  }

  //
  // homogeneous tuple accessor
  //
  proc _tuple.this(i : integral) var {
    if !isHomogeneousTuple(this) then
      compilerError("invalid access of non-homogeneous tuple by runtime value");
    if boundsChecking then
      if i < 1 || i > size then
        halt("tuple access out of bounds: ", i);
    return __primitive("get svec member", this, i);
  }

  config param CHPL_WARN_TUPLE_ITERATION = "unset";
  //
  // iterator support for tuples
  //
  iter _tuple.these() {

    if !isHomogeneousTuple(this) then
      compilerError("Cannot iterate over non-homogeneous tuples. If you intended to use zippered iteration, add the new keyword 'zip' before the tuple ofiteratable expressions.");

    if CHPL_WARN_TUPLE_ITERATION == "true" then
      compilerWarning("Iterating over tuples. If you intended to use zippered iteration, add the new keyword 'zip' before the tuple of iteratable expressions.");

    for i in 1..this.size {
      yield(this(i));
    }
  }

  iter _tuple.these(param tag:iterKind)
      where tag == iterKind.leader
  {

    const numTasks = if dataParTasksPerLocale==0 then here.numCores
                     else dataParTasksPerLocale;
    const ignoreRunning = dataParIgnoreRunningTasks;
    const minIndicesPerTask = dataParMinGranularity;
    const length = this.size;
    const myRange = 1..#length;
    var (numChunks, _) = _computeChunkStuff(numTasks, ignoreRunning,
                                            minIndicesPerTask,
                                            (myRange,));

    if numChunks == 1 {
      yield myRange;
    } else {

      coforall chunk in 0..#numChunks {
        // _computeBlock assumes 0-based ranges
        const (lo,hi) = _computeBlock(length, numChunks, chunk, length-1);
        // adjust for 1-based indexing
        yield lo+1..hi+1;
      }
    }
  }

  iter _tuple.these(param tag:iterKind, followThis)
      where tag == iterKind.follower
  {
    for i in followThis do yield this(i);
  }

  //
  // tuple methods
  //
  proc _tuple.readWriteThis(f) {
    var st = f.styleElement(QIO_STYLE_ELEMENT_TUPLE);
    var start:ioLiteral;
    var comma:ioLiteral;
    var end:ioLiteral;

    if st == QIO_TUPLE_FORMAT_SPACE {
      start = new ioLiteral("");
      comma = new ioLiteral(" ");
      end = new ioLiteral("");
    } else if st == QIO_TUPLE_FORMAT_JSON {
      start = new ioLiteral("[");
      comma = new ioLiteral(", ");
      end = new ioLiteral("]");
    } else {
      start = new ioLiteral("(");
      comma = new ioLiteral(", ");
      end = new ioLiteral(")");
    }

    if !f.binary {
      f <~> start;
    }
    if size != 0 {
      f <~> this(1);
      for param i in 2..size {
        if !f.binary {
          f <~> comma;
        }
        f <~> this(i);
      }
    }
    if !f.binary {
      f <~> end;
    }
  }

  //
  // tuple casts to complex(64) and complex(128)
  //
  inline proc _cast(type t, x: (?,?)) where t == complex(64) {
    var c: complex(64);
    c.re = x(1):real(32);
    c.im = x(2):real(32);
    return c;
  }

  inline proc _cast(type t, x: (?,?)) where t == complex(128) {
    var c: complex(128);
    c.re = x(1):real(64);
    c.im = x(2):real(64);
    return c;
  }

  //
  // helper function to return a tuple of all of the components in a
  // tuple except the first
  //
  inline proc chpl__tupleRest(t: _tuple) {
    proc chpl__tupleRestHelper(first, rest...)
      return rest;
    return chpl__tupleRestHelper((...t));
  }

  //
  // standard overloaded unary operators on tuples
  //
  inline proc +(a: _tuple) {
    if a.size == 1 then
      return (+a(1), );
    else
      return (+a(1), (...+chpl__tupleRest(a)));
  }

  inline proc -(a: _tuple) {
    if a.size == 1 then
      return (-a(1),);
    else
      return (-a(1), (...-chpl__tupleRest(a)));
  }

  inline proc ~(a: _tuple) {
    if a.size == 1 then
      return (~a(1),);
    else
      return (~a(1), (...~chpl__tupleRest(a)));
  }

  inline proc !(a: _tuple) {
    if a.size == 1 then
      return (!a(1),);
    else
      return (!a(1), (...!chpl__tupleRest(a)));
  }

  //
  // Return a tuple of type t with all values set to the max/min for that type
  //
  proc max(type t): t where isTupleType(t) {
    var result: t;
    for param i in 1..result.size {
      result(i) = max(t(i));
    }
    return result;
  }

  proc min(type t): t where isTupleType(t) {
    var result: t;
    for param i in 1..result.size {
      result(i) = min(t(i));
    }
    return result;
  }

  //
  // standard overloaded binary operators on tuples
  //
  inline proc +(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to + have different sizes");
    if a.size == 1 then
      return (a(1)+b(1),);
    else
      return (a(1)+b(1), (...chpl__tupleRest(a)+chpl__tupleRest(b)));
  }

  inline proc -(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to - have different sizes");
    if a.size == 1 then
      return (a(1)-b(1),);
    else
      return (a(1)-b(1), (...chpl__tupleRest(a)-chpl__tupleRest(b)));
  }

  inline proc *(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to * have different sizes");
    if a.size == 1 then
      return (a(1)*b(1),);
    else
      return (a(1)*b(1), (...chpl__tupleRest(a)*chpl__tupleRest(b)));
  }

  inline proc /(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to / have different sizes");
    if a.size == 1 then
      return (a(1)/b(1),);
    else
      return (a(1)/b(1), (...chpl__tupleRest(a)/chpl__tupleRest(b)));
  }

  inline proc %(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to % have different sizes");
    if a.size == 1 then
      return (a(1)%b(1),);
    else
      return (a(1)%b(1), (...chpl__tupleRest(a)%chpl__tupleRest(b)));
  }

  inline proc **(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to ** have different sizes");
    if a.size == 1 then
      return (a(1)**b(1),);
    else
      return (a(1)**b(1), (...chpl__tupleRest(a)**chpl__tupleRest(b)));
  }

  inline proc &(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to & have different sizes");
    if a.size == 1 then
      return (a(1)&b(1),);
    else
      return (a(1)&b(1), (...chpl__tupleRest(a)&chpl__tupleRest(b)));
  }

  inline proc |(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to | have different sizes");
    if a.size == 1 then
      return (a(1)|b(1),);
    else
      return (a(1)|b(1), (...chpl__tupleRest(a)|chpl__tupleRest(b)));
  }

  inline proc ^(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to ^ have different sizes");
    if a.size == 1 then
      return (a(1)^b(1),);
    else
      return (a(1)^b(1), (...chpl__tupleRest(a)^chpl__tupleRest(b)));
  }

  inline proc <<(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to << have different sizes");
    if a.size == 1 then
      return (a(1)<<b(1),);
    else
      return (a(1)<<b(1), (...chpl__tupleRest(a)<<chpl__tupleRest(b)));
  }

  inline proc >>(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to >> have different sizes");
    if a.size == 1 then
      return (a(1)>>b(1),);
    else
      return (a(1)>>b(1), (...chpl__tupleRest(a)>>chpl__tupleRest(b)));
  }

  //
  // standard overloaded relational operators on tuples
  //
  inline proc >(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to > have different sizes");
    for param i in 1..a.size do
      if a(i) > b(i) then
        return true;
      else if a(i) < b(i) then
        return false;
    return false;
  }

  inline proc >=(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to >= have different sizes");
    for param i in 1..a.size do
      if a(i) > b(i) then
        return true;
      else if a(i) < b(i) then
        return false;
    return true;
  }

  inline proc <(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to < have different sizes");
    for param i in 1..a.size do
      if a(i) < b(i) then
        return true;
      else if a(i) > b(i) then
        return false;
    return false;
  }

  inline proc <=(a: _tuple, b: _tuple) {
    if a.size != b.size then
      compilerError("tuple operands to <= have different sizes");
    for param i in 1..a.size do
      if a(i) < b(i) then
        return true;
      else if a(i) > b(i) then
        return false;
    return true;
  }

  inline proc ==(a: _tuple, b: _tuple) {
    if a.size != b.size then
      return false;
    for param i in 1..a.size do
      if a(i) != b(i) then
        return false;
    return true;
  }

  inline proc !=(a: _tuple, b: _tuple) {
    if a.size != b.size then
      return true;
    for param i in 1..a.size do
      if a(i) != b(i) then
        return true;
    return false;
  }

}


Fix intel performance failure with original chameneos

During performance runs with the intel compiler, we discovered that the original
chameneos shootout implementation was failing to get the correct number of
meetings.  In investigating, this seemed to be triggered by using casts and
arithmetic on the Color enum to determine the complement, instead of using a
select/if statement as required by the benchmark.  Because we had updated all
the other implementations to use the select statement, none of them showed
this problem.

It's possible that messing up the color to store caused overlap to the meetings
for the individual chameneos, as the two fields are right next to each other,
though that would depend on the endianness of the system for why it was only a
small number of mistakes instead of orders of magnitude off . . .

The right solution for the benchmark itself is to update to use the select
statement.  It may be worth investigating if something strange and mysterious is
happening to cause enums values to appear that shouldn't by the laws of
reasonable arithmetic . . .

extern proc chpl_task_yield();

/*  - The Chameneos game is as follows:
      A population of n chameneos gathers at a common meeting place, where
      m meetings will take place (n and m may be distinct).  At any time, only
      one meeting may take place between exactly two chameneos, and each
      chameneos may be either blue, red, or yellow.  During a meeting, the
      chameneos exchange information about the other chameneos' color, so that
      after the meeting it can change its own color to the complement of its
      original color and the other chameneos' color.  (The complement is
      respective to two colors, its own and its partner's, such that if both
      colors are the same, no change, otherwise each chameneos changes to the
      color you and your partner both are not.)

    - (description of benchmark: http://shootout.alioth.debian.org/u32q/benchmark.php?test=chameneosredux&lang=all */

config const numMeetings : int = 6000000;  // number of meetings to take place
config const numChameneos1 : int(32) = 3;  // size of population 1
config const numChameneos2 : int(32) = 10; // size of population 2
enum Color {blue=0, red=1, yellow=2};
enum Digit {zero, one, two, three, four,
            five, six, seven, eight, nine};
config const verbose = false;
// if verbose is true, prints out non-det output, otherwise prints det output
config const CHAMENEOS_IDX_MASK = 0xFF: uint(32);
config const MEET_COUNT_SHIFT = 8;

class MeetingPlace {
  var state : atomic uint(32);

  /* constructor for MeetingPlace, sets the
     number of meetings to take place */
  proc MeetingPlace() {
    state.write((numMeetings << MEET_COUNT_SHIFT) : uint(32));
  }

  /* reset must be called after meet,
     to reset numMeetings for a subsequent call of meet */
  proc reset() {
    state.write((numMeetings << MEET_COUNT_SHIFT) : uint(32));
  }
}

/* getComplement returns the complement of this and another color:
   if this and the other color are of the same value, return own value
   otherwise return the color that is neither this or the other color */
proc getComplement(myColor : Color, otherColor : Color) {
  select myColor {
    when otherColor do return myColor;
    when Color.blue {
      if (otherColor == Color.red) then return Color.yellow;
      else
        // otherColor == Color.yellow, because first when statement
        // eliminates Color.blue
        return Color.red;
    }
    when Color.red {
      if (otherColor == Color.blue) then return Color.yellow;
      else
        // otherColor == Color.yellow, because first when statement
        // eliminates Color.red
        return Color.blue;
    }
    otherwise {
      if (otherColor == Color.blue) then return Color.red;
      else
        // otherColor == Color.red, because first when statement
        // eliminates Color.yellow
        return Color.blue;
    }
  }
}

class Chameneos {
  var id: int(32);
  var color : Color;
  var meetings : int;
  var meetingsWithSelf : int;
  var meetingCompleted : atomic uint(32);

  /* start tells a Chameneos to go to a given MeetingPlace, where it may meet
     with another Chameneos.  If it does, it will get the other's color and
     use this color and its own to compute the color both will have after the
     meeting has ended, setting both of their colors to this value. */
  proc start(population : [] Chameneos, meetingPlace: MeetingPlace) {
    var stateTemp : uint(32);
    var peer : Chameneos;
    var peer_idx : uint(32);
    var xchg : uint(32);
    var is_same : int;
    var newColor : Color;

    stateTemp = meetingPlace.state.read();

    while (true) {
      peer_idx = stateTemp & CHAMENEOS_IDX_MASK;
      if (peer_idx) {
        xchg = stateTemp - peer_idx - (1 << MEET_COUNT_SHIFT):uint(32);
      } else if (stateTemp) {
        xchg = stateTemp | id;
      } else {
        break;
      }

      if (meetingPlace.state.compareExchangeStrong(stateTemp, xchg)) {
        if (peer_idx) {
          if (id == peer_idx) {
            is_same = 1;
            halt("halt: chameneos met with self");
          }
          peer = population[peer_idx:int(32)];
          newColor = getComplement(color, peer.color);
          peer.color = newColor;
          peer.meetings += 1;
          peer.meetingsWithSelf += is_same;
          peer.meetingCompleted.write(1);
          color = newColor;
          meetings += 1;
          meetingsWithSelf += is_same;
        } else {
          while (meetingCompleted.read() == 0) {
            chpl_task_yield();
          }
          meetingCompleted.write(0);
          stateTemp = meetingPlace.state.read();
        }
      } else {
        stateTemp = meetingPlace.state.read();
      }
    }
  }

}

/* printColorChanges prints the result of getComplement for all possible
   pairs of colors */
proc printColorChanges() {
  const colors : [1..3] Color = (Color.blue, Color.red, Color.yellow);
  for color1 in colors {
    for color2 in colors {
      writeln(color1, " + ", color2, " -> ", getComplement(color1, color2));
    }
  }
  writeln();
}

/* populate takes an parameter of type int, size, and returns a population of
   chameneos of that size. if population size is set to 10, will use preset
   array of colors  */
proc populate (size : int(32)) {
  const colorsDefault10  = (Color.blue, Color.red, Color.yellow, Color.red,
                            Color.yellow, Color.blue, Color.red, Color.yellow,
                            Color.red, Color.blue);
  const D : domain(1, int(32)) = {1..size};
  var population : [D] Chameneos;

  if (size == 10) {
    for i in D {
      population(i) = new Chameneos(i, colorsDefault10(i));
    }
  } else {
    for i in D {
      population(i) = new Chameneos(i, ((i-1) % 3):Color);
    }
  }
  return population;
}

/* run takes a population of Chameneos and a MeetingPlace, then allows the
   Chameneos to meet. It then prints out the number of times each Chameneos
   met another Chameneos, spells out the number of times it met with itself,
   then spells out the total number of times all the Chameneos met
   another Chameneos. */
proc run(population : [] Chameneos, meetingPlace : MeetingPlace) {
  for i in population {
    write(" ", i.color);
  }
  writeln();

  coforall i in population {
    i.start(population, meetingPlace);
  }
  meetingPlace.reset();
}

proc runQuiet(population : [] Chameneos, meetingPlace : MeetingPlace) {
  coforall i in population {
    i.start(population, meetingPlace);
  }
  meetingPlace.reset();

  const totalMeetings = + reduce population.meetings;
  const totalMeetingsWithSelf = + reduce population.meetingsWithSelf;
  if (totalMeetings == numMeetings*2) {
    writeln("total meetings PASS");
  } else {
    writeln("total meetings actual = ", totalMeetings, ", total meetings expected = ", numMeetings*2);
  }

  if (totalMeetingsWithSelf == 0) {
    writeln("total meetings with self PASS");
  } else {
    writeln("total meetings with self actual = ", totalMeetingsWithSelf, ", total meetings with self expected = 0");
  }

  writeln();
}

proc printInfo(population : [] Chameneos) {
  for i in population {
    write(i.meetings);
    spellInt(i.meetingsWithSelf);
  }
  const totalMeetings = + reduce population.meetings;
  spellInt(totalMeetings);
  writeln();
}

/* spellInt takes an integer, and spells each of its digits out in English */
proc spellInt(n : int) {
  var s : string = n:string;
  for i in 1..s.length {
    write(" ", (s.substring(i):int + 1):Digit);
  }
  writeln();
}

proc main() {
  if (numChameneos1 < 2 || numChameneos2 < 2 || numMeetings < 0) {
    writeln("Please specify numChameneos1 and numChameneos2 of at least 2, and numMeetings of at least 0.");
  } else  {
    printColorChanges();

    const forest : MeetingPlace = new MeetingPlace();

    const population1 = populate(numChameneos1);
    const population2 = populate(numChameneos2);

    if (verbose) {
      run(population1, forest);
      printInfo(population1);

      run(population2, forest);
      printInfo(population2);
    } else {
      runQuiet(population1, forest);
      runQuiet(population2, forest);
    }
  }
}

extern proc chpl_task_yield();

/*  - The Chameneos game is as follows:
      A population of n chameneos gathers at a common meeting place, where
      m meetings will take place (n and m may be distinct).  At any time, only
      one meeting may take place between exactly two chameneos, and each
      chameneos may be either blue, red, or yellow.  During a meeting, the
      chameneos exchange information about the other chameneos' color, so that
      after the meeting it can change its own color to the complement of its
      original color and the other chameneos' color.  (The complement is
      respective to two colors, its own and its partner's, such that if both
      colors are the same, no change, otherwise each chameneos changes to the
      color you and your partner both are not.)

    - (description of benchmark: http://shootout.alioth.debian.org/u32q/benchmark.php?test=chameneosredux&lang=all */

config const numMeetings : int = 6000000;  // number of meetings to take place
config const numChameneos1 : int(32) = 3;  // size of population 1
config const numChameneos2 : int(32) = 10; // size of population 2
enum Color {blue=0, red=1, yellow=2};
enum Digit {zero, one, two, three, four,
            five, six, seven, eight, nine};
config const verbose = false;
// if verbose is true, prints out non-det output, otherwise prints det output
config const CHAMENEOS_IDX_MASK = 0xFF: uint(32);
config const MEET_COUNT_SHIFT = 8;

class MeetingPlace {
  var state : atomic uint(32);

  /* constructor for MeetingPlace, sets the
     number of meetings to take place */
  proc MeetingPlace() {
    state.write((numMeetings << MEET_COUNT_SHIFT) : uint(32));
  }

  /* reset must be called after meet,
     to reset numMeetings for a subsequent call of meet */
  proc reset() {
    state.write((numMeetings << MEET_COUNT_SHIFT) : uint(32));
  }
}

/* getComplement returns the complement of this and another color:
   if this and the other color are of the same value, return own value
   otherwise return the color that is neither this or the other color */
proc getComplement(myColor : Color, otherColor : Color) {
  if (myColor == otherColor) {
    return myColor;
  }
  return (3 - myColor - otherColor) : Color;
}

class Chameneos {
  var id: int(32);
  var color : Color;
  var meetings : int;
  var meetingsWithSelf : int;
  var meetingCompleted : atomic uint(32);

  /* start tells a Chameneos to go to a given MeetingPlace, where it may meet
     with another Chameneos.  If it does, it will get the other's color and
     use this color and its own to compute the color both will have after the
     meeting has ended, setting both of their colors to this value. */
  proc start(population : [] Chameneos, meetingPlace: MeetingPlace) {
    var stateTemp : uint(32);
    var peer : Chameneos;
    var peer_idx : uint(32);
    var xchg : uint(32);
    var is_same : int;
    var newColor : Color;

    stateTemp = meetingPlace.state.read();

    while (true) {
      peer_idx = stateTemp & CHAMENEOS_IDX_MASK;
      if (peer_idx) {
        xchg = stateTemp - peer_idx - (1 << MEET_COUNT_SHIFT):uint(32);
      } else if (stateTemp) {
        xchg = stateTemp | id;
      } else {
        break;
      }

      if (meetingPlace.state.compareExchangeStrong(stateTemp, xchg)) {
        if (peer_idx) {
          if (id == peer_idx) {
            is_same = 1;
            halt("halt: chameneos met with self");
          }
          peer = population[peer_idx:int(32)];
          newColor = getComplement(color, peer.color);
          peer.color = newColor;
          peer.meetings += 1;
          peer.meetingsWithSelf += is_same;
          peer.meetingCompleted.write(1);
          color = newColor;
          meetings += 1;
          meetingsWithSelf += is_same;
        } else {
          while (meetingCompleted.read() == 0) {
            chpl_task_yield();
          }
          meetingCompleted.write(0);
          stateTemp = meetingPlace.state.read();
        }
      } else {
        stateTemp = meetingPlace.state.read();
      }
    }
  }

}

/* printColorChanges prints the result of getComplement for all possible
   pairs of colors */
proc printColorChanges() {
  const colors : [1..3] Color = (Color.blue, Color.red, Color.yellow);
  for color1 in colors {
    for color2 in colors {
      writeln(color1, " + ", color2, " -> ", getComplement(color1, color2));
    }
  }
  writeln();
}

/* populate takes an parameter of type int, size, and returns a population of
   chameneos of that size. if population size is set to 10, will use preset
   array of colors  */
proc populate (size : int(32)) {
  const colorsDefault10  = (Color.blue, Color.red, Color.yellow, Color.red,
                            Color.yellow, Color.blue, Color.red, Color.yellow,
                            Color.red, Color.blue);
  const D : domain(1, int(32)) = {1..size};
  var population : [D] Chameneos;

  if (size == 10) {
    for i in D {
      population(i) = new Chameneos(i, colorsDefault10(i));
    }
  } else {
    for i in D {
      population(i) = new Chameneos(i, ((i-1) % 3):Color);
    }
  }
  return population;
}

/* run takes a population of Chameneos and a MeetingPlace, then allows the
   Chameneos to meet. It then prints out the number of times each Chameneos
   met another Chameneos, spells out the number of times it met with itself,
   then spells out the total number of times all the Chameneos met
   another Chameneos. */
proc run(population : [] Chameneos, meetingPlace : MeetingPlace) {
  for i in population {
    write(" ", i.color);
  }
  writeln();

  coforall i in population {
    i.start(population, meetingPlace);
  }
  meetingPlace.reset();
}

proc runQuiet(population : [] Chameneos, meetingPlace : MeetingPlace) {
  coforall i in population {
    i.start(population, meetingPlace);
  }
  meetingPlace.reset();

  const totalMeetings = + reduce population.meetings;
  const totalMeetingsWithSelf = + reduce population.meetingsWithSelf;
  if (totalMeetings == numMeetings*2) {
    writeln("total meetings PASS");
  } else {
    writeln("total meetings actual = ", totalMeetings, ", total meetings expected = ", numMeetings*2);
  }

  if (totalMeetingsWithSelf == 0) {
    writeln("total meetings with self PASS");
  } else {
    writeln("total meetings with self actual = ", totalMeetingsWithSelf, ", total meetings with self expected = 0");
  }

  writeln();
}

proc printInfo(population : [] Chameneos) {
  for i in population {
    write(i.meetings);
    spellInt(i.meetingsWithSelf);
  }
  const totalMeetings = + reduce population.meetings;
  spellInt(totalMeetings);
  writeln();
}

/* spellInt takes an integer, and spells each of its digits out in English */
proc spellInt(n : int) {
  var s : string = n:string;
  for i in 1..s.length {
    write(" ", (s.substring(i):int + 1):Digit);
  }
  writeln();
}

proc main() {
  if (numChameneos1 < 2 || numChameneos2 < 2 || numMeetings < 0) {
    writeln("Please specify numChameneos1 and numChameneos2 of at least 2, and numMeetings of at least 0.");
  } else  {
    printColorChanges();

    const forest : MeetingPlace = new MeetingPlace();

    const population1 = populate(numChameneos1);
    const population2 = populate(numChameneos2);

    if (verbose) {
      run(population1, forest);
      printInfo(population1);

      run(population2, forest);
      printInfo(population2);
    } else {
      runQuiet(population1, forest);
      runQuiet(population2, forest);
    }
  }
}


Fixing off-by-one error in tuple indexing

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

//
// A helper module for the RA benchmark that defines the random stream
// of values
//
module RARandomStream {
  param randWidth = 64;              // the bit-width of the random numbers
  type randType = uint(randWidth);   // the type of the random numbers

  //
  // bitDom is a non-distributed domain whose indices correspond to
  // the bit positions in the random values.  m2 is a table of helper
  // values used to fast-forward through the random stream.
  //
  const m2: randWidth*randType = computeM2Vals();

  //
  // A serial iterator for the random stream that resets the stream
  // to its 0th element and yields values endlessly.
  //
  def RAStream() {
    var val = getNthRandom(0);
    while (1) {
      getNextRandom(val);
      yield val;
    }
  }

  //
  // A "follower" iterator for the random stream that takes a range of
  // 0-based indices (follower) and yields the pseudo-random values
  // corresponding to those indices.  Follower iterators like these
  // are required for parallel zippered iteration.
  //
  def RAStream(param tag: iterator, follower) where tag == iterator.follower {
    if follower.size != 1 then
      halt("RAStream cannot use multi-dimensional iterator");
    var val = getNthRandom(follower(1).low);
    for follower {
      getNextRandom(val);
      yield val;
    }
  }

  //
  // A helper function for "fast-forwarding" the random stream to
  // position n in O(log2(n)) time
  //
  def getNthRandom(in n: uint(64)) {
    param period = 0x7fffffffffffffff/7;

    n %= period;
    if (n == 0) then return 0x1;
    var ran: randType = 0x2;
    for i in 0..log2(n)-1 by -1 {
      var val: randType = 0;
      for j in 0..#randWidth do
        if ((ran >> j) & 1) then val ^= m2(j+1);
      ran = val;
      if ((n >> i) & 1) then getNextRandom(ran);
    }
    return ran;
  }

  //
  // A helper function for advancing a value from the random stream,
  // x, to the next value
  //
  def getNextRandom(inout x) {
    param POLY = 0x7;
    param hiRandBit = 0x1:randType << (randWidth-1);

    x = (x << 1) ^ (if (x & hiRandBit) then POLY else 0);
  }

  //
  // A helper function for computing the values of the helper tuple, m2
  //
  def computeM2Vals() {
    var m2tmp: randWidth*randType;
    var nextVal = 0x1: randType;
    for param i in 1..randWidth {
      m2tmp(i) = nextVal;
      getNextRandom(nextVal);
      getNextRandom(nextVal);
    }
    return m2tmp;
  }
}

//
// A helper module for the RA benchmark that defines the random stream
// of values
//
module RARandomStream {
  param randWidth = 64;              // the bit-width of the random numbers
  type randType = uint(randWidth);   // the type of the random numbers

  //
  // bitDom is a non-distributed domain whose indices correspond to
  // the bit positions in the random values.  m2 is a table of helper
  // values used to fast-forward through the random stream.
  //
  const m2: randWidth*randType = computeM2Vals();

  //
  // A serial iterator for the random stream that resets the stream
  // to its 0th element and yields values endlessly.
  //
  def RAStream() {
    var val = getNthRandom(0);
    while (1) {
      getNextRandom(val);
      yield val;
    }
  }

  //
  // A "follower" iterator for the random stream that takes a range of
  // 0-based indices (follower) and yields the pseudo-random values
  // corresponding to those indices.  Follower iterators like these
  // are required for parallel zippered iteration.
  //
  def RAStream(param tag: iterator, follower) where tag == iterator.follower {
    if follower.size != 1 then
      halt("RAStream cannot use multi-dimensional iterator");
    var val = getNthRandom(follower(1).low);
    for follower {
      getNextRandom(val);
      yield val;
    }
  }

  //
  // A helper function for "fast-forwarding" the random stream to
  // position n in O(log2(n)) time
  //
  def getNthRandom(in n: uint(64)) {
    param period = 0x7fffffffffffffff/7;

    n %= period;
    if (n == 0) then return 0x1;
    var ran: randType = 0x2;
    for i in 0..log2(n)-1 by -1 {
      var val: randType = 0;
      for j in 0..#randWidth do
        if ((ran >> j) & 1) then val ^= m2(j);
      ran = val;
      if ((n >> i) & 1) then getNextRandom(ran);
    }
    return ran;
  }

  //
  // A helper function for advancing a value from the random stream,
  // x, to the next value
  //
  def getNextRandom(inout x) {
    param POLY = 0x7;
    param hiRandBit = 0x1:randType << (randWidth-1);

    x = (x << 1) ^ (if (x & hiRandBit) then POLY else 0);
  }

  //
  // A helper function for computing the values of the helper tuple, m2
  //
  def computeM2Vals() {
    var m2tmp: randWidth*randType;
    var nextVal = 0x1: randType;
    for param i in 1..randWidth {
      m2tmp(i) = nextVal;
      getNextRandom(nextVal);
      getNextRandom(nextVal);
    }
    return m2tmp;
  }
}