This change adds the XMT pragma loop to ChapelRange leaders.

This change adds the XMT pragma loop to ChapelRange leaders. Tested on a
simple range loop forall i in 1..n (and confirmed it doesn't break codes
that shouldn't use it).

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

use DSIUtil;
config param debugChapelRange = false;

//
// range type
//
//   parameterized by an integral element type, by whether low and/or
//   high bounds exist, and by whether the stride is one or not
//
enum BoundedRangeType { bounded, boundedLow, boundedHigh, boundedNone };

pragma "range"
record range {
  type idxType = int;                            // element type
  param boundedType: BoundedRangeType = BoundedRangeType.bounded; // bounded or not
  param stridable: bool = false;                 // range can be strided
  var _low: idxType = 1;                         // lower bound
  var _high: idxType = 0;                        // upper bound
  var _stride: chpl__idxTypeToStrType(idxType) = 1;    // signed stride of range
  var _promotionType : idxType;                  // enables promotion

  pragma "inline" def low return _low;       // public getter for low bound
  pragma "inline" def high return _high;     // public getter for high bound
  pragma "inline" def stride return _stride; // public getter for stride
}

////////////////////////////////////////////////////////////////////////////////
// SYNTAX FUNCTIONS

//
// syntax function for bounded ranges
//
def _build_range(low: int, high: int)
  return new range(int, BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint, high: uint)
  return new range(uint, BoundedRangeType.bounded, false, low, high);
def _build_range(low: int(64), high: int(64))
  return new range(int(64), BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint(64), high: uint(64))
  return new range(uint(64), BoundedRangeType.bounded, false, low, high);

//
// syntax function for unbounded ranges
//
def _build_range(param bt: BoundedRangeType, bound: int)
  return new range(int, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint)
  return new range(uint, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: int(64))
  return new range(int(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint(64))
  return new range(uint(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType)
  return new range(int, bt, false);

//
// syntax function for by-expressions
//
def chpl__byHelp(r : range(?et, ?bt, ?sbl), str : chpl__idxTypeToStrType(et)) {
  if str == 0 then
    halt("range cannot be strided by zero");
  if r.boundedType == BoundedRangeType.boundedNone then
    halt("unbounded range cannot be strided");
  var result = new range(r.idxType, r.boundedType, true, r.low, r.high, r.stride*str);
  if r.low > r.high then
    return result;
  if result.stride < 0 then
    result._alignLow(result.high);
  else
    result._alignHigh(result.low);
  return result;
}

def chpl__legalIntCoerce(type t1, type t2) param {
  if (_isSignedType(t2)) {
    if (_isSignedType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (_isSignedType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

def by(r : range(?), str) {
  type idxType = r.idxType;
  type strType = chpl__idxTypeToStrType(idxType);
  type argType = str.type;
  if (argType == strType) {
    return chpl__byHelp(r, str);
  } else if (argType == idxType || chpl__legalIntCoerce(argType, strType)) {
    return chpl__byHelp(r, str:strType);
  } else {
    compilerError("type mismatch applying 'by' to range(",
                  typeToString(idxType), ") with ", typeToString(argType));
  }
}

//
// syntax functions for counted ranges
//
def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedLow {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.low + 1,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.low,
                   _high = r.low + (i-1)*abs(r.stride):resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedHigh {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high,
                   _high = r.high - 1,
                   _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high - (i-1)*abs(r.stride):resultType,
                   _high = r.high,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.bounded {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i > r.length then
    halt("bounded range is too small to access ", i, " elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.high,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = if r.stride < 0 then r.high + (i-1)*r.stride:resultType else r.low,
                   _high = if r.stride < 0 then r.high else r.low + (i-1)*r.stride:resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedNone {
  compilerError("count operator is not defined for unbounded ranges");
}

////////////////////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS

//
// return true if this range has a low bound
//
def range._hasLow() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedLow;

//
// return true if this range has a high bound
//
def range._hasHigh() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedHigh;

//
// Return the number in the range 0 <= result < b that is congruent to a (mod b)
//
def mod(a:integral, b:integral) {
  if (b <= 0) then
    halt("modulus divisor must be positive");
  var tmp = a % b:a.type;
  return if tmp < 0 then b:a.type + tmp else tmp;
}

//
// align low bound of this range to an alignment; snap up
//
def range._alignLow(alignment: idxType) {
  var s = abs(stride):idxType;
  // The following is equivalent to var d = abs(alignment - low) % s;
  // except that it avoids problems with an overflow in the subtraction.
  // It uses the fact that if a1 == b1 mod n and a2 == b2 mod n then
  // a1 - a2 == b1 - b2 mod n and the fact that abs(a-b) is a-b if a > b
  // and is b-a when b >= a.
  var d = if(alignment > low) then
    mod(mod(alignment,s):int - mod(low,s):int, s):idxType
  else
    mod(mod(low,s):int - mod(alignment, s):int, s):idxType;
  if d != 0 {
    if low < alignment then
      _low += d;
    else
      _low += s - d;
  }
}

//
// align high bound of this range to an alignment; snap down
//
def range._alignHigh(alignment: idxType) {
  var s = abs(stride):idxType;
  // See the note about this declaration in range._alignLow. It is like
  // var d = abs(alignment - high) % s; but avoids overflow problems.
  var d = if(alignment > high) then
    mod(mod(alignment,s):int - mod(high,s):int, s):idxType
  else
    mod(mod(high,s):int - mod(alignment,s):int,s):idxType;
  if d != 0 {
    if high > alignment then
      _high -= d;
    else
      _high -= s - d;
  }
}

//
// range assignment
//
def =(r1: range(stridable=?s1), r2: range(stridable=?s2)) {
  if r1.boundedType != r2.boundedType then
    compilerError("type mismatch in assignment of ranges with different boundedType parameters");
  if !s1 && s2 then
    if r2.stride != 1 then
      halt("non-stridable range assigned non-unit stride");
  r1._low = r2.low;
  r1._high = r2.high;
  r1._stride = r2.stride;
  return r1;
}

////////////////////////////////////////////////////////////////////////////////
// EXTERNAL FUNCTIONS

//
// return the intersection of this and other
//
def range.this(other: range(?idxType2, ?boundedType, ?stridable)) {
  //
  // determine boundedType of result
  //
  def computeBoundedType(r1, r2) param {
    param low = r1._hasLow() || r2._hasLow();
    param high = r1._hasHigh() || r2._hasHigh();
    if low && high then
      return BoundedRangeType.bounded;
    else if low then
      return BoundedRangeType.boundedLow;
    else if high then
      return BoundedRangeType.boundedHigh;
    else
      return BoundedRangeType.boundedNone;
  }

  //
  // returns (gcd(u, v), x) where x is set such that u*x + v*y =
  // gcd(u, v) assuming u and v are non-negative
  //
  // source: Knuth Volume 2 --- Section 4.5.2
  //
  def extendedEuclidHelp(u, v) {
    var zero: u.type = 0;
    var one: u.type = 1;
    var U = (one, zero, u);
    var V = (zero, one, v);
    while V(3) != 0 {
      (U, V) = let q = U(3)/V(3) in (V, U - V * (q, q, q));
    }
    return (U(3), U(1));
  }

  def extendedEuclid(u:int, v:int) {
    return extendedEuclidHelp(u,v);
  }

  def extendedEuclid(u:int(64), v:int(64)) {
    return extendedEuclidHelp(u,v);
  }

  var lo1 = if _hasLow() then this.low:idxType else other.low;
  var hi1 = if _hasHigh() then this.high:idxType else other.high;
  var st1 = abs(this.stride);
  var al1 = if this.stride < 0 then hi1 else lo1;

  var lo2 = if other._hasLow() then other.low else this.low:idxType;
  var hi2 = if other._hasHigh() then other.high else this.high:idxType;
  var st2 = abs(other.stride);
  var al2 = if this.stride < 0 then hi2 else lo2;

  var (g, x) = extendedEuclid(st1, st2);

  var result = new range(idxType,
                         computeBoundedType(this, other),
                         this.stridable | other.stridable,
                         max(lo1, lo2):idxType,
                         min(hi1, hi2):idxType,
                         (st1 * (st2 / g)):chpl__idxTypeToStrType(idxType));
  if lo1 > hi1 || lo2 > hi2 {
    // empty intersection
    if result.low < result.high then
      result._low <=> result._high;
    else if result.high == result.low then
      (result._low, result._high) = (1:idxType, 0:idxType);
    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;
    result._alignLow(al);
    result._alignHigh(al);
  } else if abs(lo1 - lo2) % g:idxType != 0 {
    // empty intersection, return degenerate result
    result._low <=> result._high;
  } else {
    // non-empty intersection

    if other.stride < 0 then
      result._stride = -result.stride;

    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;

    result._alignLow(al);
    result._alignHigh(al);
  }

  return result;
}

//
// default iterator optimized for unit stride
//
def range.these() {
  if boundedType != BoundedRangeType.bounded {
    if boundedType == BoundedRangeType.boundedNone then
      halt("iteration over a range with no bounds");
    if stridable {
      if boundedType == BoundedRangeType.boundedLow then
        if stride < 0 then
          halt("iteration over range with negative stride but no high bound");
      if boundedType == BoundedRangeType.boundedHigh then
        if stride > 0 then
          halt("iteration over range with positive stride but no low bound");
      var i = if stride > 0 then low else high;
      while true {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      if boundedType == BoundedRangeType.boundedHigh then
        halt("iteration over range with positive stride but no low bound");
      var i = low;
      while true {
        yield i;
        i = i + 1;
      }
    }
  } else {
    var i, last: idxType;
    if stridable {
      if stride > 0 {
        i = low;
        last = if i > high then low else high + stride:idxType;
      } else {
        i = high;
        last = if i < low then high else low + stride:idxType;
      }
      while i != last {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      i = low;
      last = if i > high then low else high + 1;
      while i != last {
        yield i;
        i = i + 1;
      }
    }
  }
}

def range.these(param tag: iterator) where tag == iterator.leader {
  // want "yield 0..length-1;"
  // but compilerError in length causes a problem because leaders are
  // resolved wherever an iterator is.
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if debugChapelRange then
    writeln("*** In range leader:"); // ", this);

  var v: idxType;
  if stride > 0 then
    v = (high - low) / stride:idxType + 1;
  else
    v = (low - high) / stride:idxType + 1;
  if v < 0 then
    v = 0;

  var numChunks = _computeNumChunks(v);
  if debugChapelRange then
    writeln("*** RI: length=", v, " numChunks=", numChunks);

  if debugChapelRange then
    writeln("*** RI: Using ", numChunks, " chunk(s)");
  if (CHPL_TARGET_PLATFORM != "xmt") {
    if numChunks == 1 {
      yield tuple(0..v-1);
    } else {
      coforall chunk in 0..numChunks-1 {
        const (lo,hi) = _computeBlock(v, numChunks, chunk, v-1);
        if debugChapelRange then
          writeln("*** RI: tuple = ", tuple(lo..hi));
        yield tuple(lo..hi);
      }
    }
  } else {

    var per_stream_i: uint(64) = 0;
    var total_streams_n: uint(64) = 0;

    __primitive_loop("xmt pragma forall i in n", per_stream_i,
                     total_streams_n) {
      const (lo,hi) = _computeBlock(v, total_streams_n, per_stream_i, v-1);
      yield tuple(lo..hi);
    }
  }
}

def range.these(param tag: iterator, follower) where tag == iterator.follower {
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if follower.size != 1 then
    halt("iteration over a range with multi-dimensional iterator");
  if debugChapelRange then
    writeln("In range follower code: Following ", follower);
  var followThis = follower(1);
  if stridable {
    var r = if stride > 0 then
        low+followThis.low*stride:idxType..low+followThis.high*stride:idxType by stride by followThis.stride
      else
        high+followThis.high*stride:idxType..high+followThis.low*stride:idxType by stride by followThis.stride;
    for i in r do
      yield i;
  } else {
    var r = low+followThis;
    for i in r do
      yield i;
  }
}

//
// returns the number of elements in this range
//
def range.length {
  if boundedType != BoundedRangeType.bounded then
    compilerError("unbounded range has infinite length");
  if low > high then
    return 0:idxType;
  const retVal = if stride > 0
               then (high - low) / stride:idxType + 1
               else (low - high) / stride:idxType + 1;
  return if (retVal < 0) then 0 else retVal;
}

//
// returns true if i is in this range
//
def range.member(i: idxType) {
  if stridable {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high && (high - i) % abs(stride):idxType == 0;
    } else {
      return true;
    }
  } else {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high;
    } else {
      return true;
    }
  }
}

def range.indexOrder(i: idxType) {
  if (!member(i)) then return (-1):idxType;
  return (i-low)/abs(stride);
}

//
// returns true if other is in bounds of this for all specified
// bounds; these functions are used to determine if an array slice is
// valid.  We break out the boundedNone case in order to permit
// unbounded ranges to slice ranges of various index types -- otherwise
// we get a compiler error in the boundsCheck function.
//
def range.boundsCheck(other: range(?e,?b,?s)) where b == BoundedRangeType.boundedNone {
  return true;
}

def range.boundsCheck(other: range(?e,?b,?s)) {
  var boundedOther: range(idxType,BoundedRangeType.bounded,s||this.stridable);
  if other._hasLow() then
    boundedOther._low = other.low;
  else
    boundedOther._low = low;
  if other._hasHigh() then
    boundedOther._high = other.high;
  else
    boundedOther._high = high;
  boundedOther._stride = other.stride;

  return (boundedOther.length == 0) || (this(boundedOther) == boundedOther);
}

def range.boundsCheck(other: idxType)
  return member(other);

//
// returns true if every i in other is in this range
//
def range.member(other: range(?))
  return this(other) == other;

//
// write implementation for ranges
//
def range.writeThis(f: Writer) {
  if _hasLow() then
    f.write(low);
  f.write("..");
  if _hasHigh() then
    f.write(high);
  if stride != 1 then
    f.write(" by ", stride);
}

//
// translate the indices in this range by i
//
def range.translate(i: idxType)
  return this + i;

//
// intended for internal use only:
//
def range.chpl__unTranslate(i: idxType) {
  return this - i;
}

//
// return an interior portion of this range
//
def range.interior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("interior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low, low-1-i, stride);
  else
    return new range(idxType, boundedType, stridable, high+1-i, high, stride);
}

//
// return an exterior portion of this range
//
def range.exterior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("exterior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low+i, low-1, stride);
  else
    return new range(idxType, boundedType, stridable, high+1, high+i, stride);
}

//
// returns an expanded range, or a contracted range if i < 0
//
def range.expand(i: idxType)
  return new range(idxType, boundedType, stridable, low-i, high+i, stride);

//
// range op scalar and scalar op range arithmetic
//
def +(r: range(?e,?b,?s), i: integral)
  return new range((r.low+i).type, b, s, r.low+i, r.high+i, r.stride);

def +(i:integral, r: range(?e,?b,?s))
  return new range((i+r.low).type, b, s, i+r.low, i+r.high, r.stride);

def -(r: range(?e,?b,?s), i: integral)
  return new range((r.low-i).type, b, s, r.low-i, r.high-i, r.stride);

//
// return a substring of a string with a range of indices
//
pragma "inline" def string.substring(s: range(?e,?b,?st)) {
  if s.boundedType != BoundedRangeType.bounded then
    compilerError("substring indexing undefined on unbounded ranges");
  if s.stride != 1 then
    return __primitive("string_strided_select", this, s.low, s.high, s.stride);
  else
    return __primitive("string_select", this, s.low, s.high);
}

use DSIUtil;
config param debugChapelRange = false;

//
// range type
//
//   parameterized by an integral element type, by whether low and/or
//   high bounds exist, and by whether the stride is one or not
//
enum BoundedRangeType { bounded, boundedLow, boundedHigh, boundedNone };

pragma "range"
record range {
  type idxType = int;                            // element type
  param boundedType: BoundedRangeType = BoundedRangeType.bounded; // bounded or not
  param stridable: bool = false;                 // range can be strided
  var _low: idxType = 1;                         // lower bound
  var _high: idxType = 0;                        // upper bound
  var _stride: chpl__idxTypeToStrType(idxType) = 1;    // signed stride of range
  var _promotionType : idxType;                  // enables promotion

  pragma "inline" def low return _low;       // public getter for low bound
  pragma "inline" def high return _high;     // public getter for high bound
  pragma "inline" def stride return _stride; // public getter for stride
}

////////////////////////////////////////////////////////////////////////////////
// SYNTAX FUNCTIONS

//
// syntax function for bounded ranges
//
def _build_range(low: int, high: int)
  return new range(int, BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint, high: uint)
  return new range(uint, BoundedRangeType.bounded, false, low, high);
def _build_range(low: int(64), high: int(64))
  return new range(int(64), BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint(64), high: uint(64))
  return new range(uint(64), BoundedRangeType.bounded, false, low, high);

//
// syntax function for unbounded ranges
//
def _build_range(param bt: BoundedRangeType, bound: int)
  return new range(int, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint)
  return new range(uint, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: int(64))
  return new range(int(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint(64))
  return new range(uint(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType)
  return new range(int, bt, false);

//
// syntax function for by-expressions
//
def chpl__byHelp(r : range(?et, ?bt, ?sbl), str : chpl__idxTypeToStrType(et)) {
  if str == 0 then
    halt("range cannot be strided by zero");
  if r.boundedType == BoundedRangeType.boundedNone then
    halt("unbounded range cannot be strided");
  var result = new range(r.idxType, r.boundedType, true, r.low, r.high, r.stride*str);
  if r.low > r.high then
    return result;
  if result.stride < 0 then
    result._alignLow(result.high);
  else
    result._alignHigh(result.low);
  return result;
}

def chpl__legalIntCoerce(type t1, type t2) param {
  if (_isSignedType(t2)) {
    if (_isSignedType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (_isSignedType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

def by(r : range(?), str) {
  type idxType = r.idxType;
  type strType = chpl__idxTypeToStrType(idxType);
  type argType = str.type;
  if (argType == strType) {
    return chpl__byHelp(r, str);
  } else if (argType == idxType || chpl__legalIntCoerce(argType, strType)) {
    return chpl__byHelp(r, str:strType);
  } else {
    compilerError("type mismatch applying 'by' to range(",
                  typeToString(idxType), ") with ", typeToString(argType));
  }
}

//
// syntax functions for counted ranges
//
def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedLow {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.low + 1,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.low,
                   _high = r.low + (i-1)*abs(r.stride):resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedHigh {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high,
                   _high = r.high - 1,
                   _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high - (i-1)*abs(r.stride):resultType,
                   _high = r.high,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.bounded {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i > r.length then
    halt("bounded range is too small to access ", i, " elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.high,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = if r.stride < 0 then r.high + (i-1)*r.stride:resultType else r.low,
                   _high = if r.stride < 0 then r.high else r.low + (i-1)*r.stride:resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedNone {
  compilerError("count operator is not defined for unbounded ranges");
}

////////////////////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS

//
// return true if this range has a low bound
//
def range._hasLow() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedLow;

//
// return true if this range has a high bound
//
def range._hasHigh() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedHigh;

//
// Return the number in the range 0 <= result < b that is congruent to a (mod b)
//
def mod(a:integral, b:integral) {
  if (b <= 0) then
    halt("modulus divisor must be positive");
  var tmp = a % b:a.type;
  return if tmp < 0 then b:a.type + tmp else tmp;
}

//
// align low bound of this range to an alignment; snap up
//
def range._alignLow(alignment: idxType) {
  var s = abs(stride):idxType;
  // The following is equivalent to var d = abs(alignment - low) % s;
  // except that it avoids problems with an overflow in the subtraction.
  // It uses the fact that if a1 == b1 mod n and a2 == b2 mod n then
  // a1 - a2 == b1 - b2 mod n and the fact that abs(a-b) is a-b if a > b
  // and is b-a when b >= a.
  var d = if(alignment > low) then
    mod(mod(alignment,s):int - mod(low,s):int, s):idxType
  else
    mod(mod(low,s):int - mod(alignment, s):int, s):idxType;
  if d != 0 {
    if low < alignment then
      _low += d;
    else
      _low += s - d;
  }
}

//
// align high bound of this range to an alignment; snap down
//
def range._alignHigh(alignment: idxType) {
  var s = abs(stride):idxType;
  // See the note about this declaration in range._alignLow. It is like
  // var d = abs(alignment - high) % s; but avoids overflow problems.
  var d = if(alignment > high) then
    mod(mod(alignment,s):int - mod(high,s):int, s):idxType
  else
    mod(mod(high,s):int - mod(alignment,s):int,s):idxType;
  if d != 0 {
    if high > alignment then
      _high -= d;
    else
      _high -= s - d;
  }
}

//
// range assignment
//
def =(r1: range(stridable=?s1), r2: range(stridable=?s2)) {
  if r1.boundedType != r2.boundedType then
    compilerError("type mismatch in assignment of ranges with different boundedType parameters");
  if !s1 && s2 then
    if r2.stride != 1 then
      halt("non-stridable range assigned non-unit stride");
  r1._low = r2.low;
  r1._high = r2.high;
  r1._stride = r2.stride;
  return r1;
}

////////////////////////////////////////////////////////////////////////////////
// EXTERNAL FUNCTIONS

//
// return the intersection of this and other
//
def range.this(other: range(?idxType2, ?boundedType, ?stridable)) {
  //
  // determine boundedType of result
  //
  def computeBoundedType(r1, r2) param {
    param low = r1._hasLow() || r2._hasLow();
    param high = r1._hasHigh() || r2._hasHigh();
    if low && high then
      return BoundedRangeType.bounded;
    else if low then
      return BoundedRangeType.boundedLow;
    else if high then
      return BoundedRangeType.boundedHigh;
    else
      return BoundedRangeType.boundedNone;
  }

  //
  // returns (gcd(u, v), x) where x is set such that u*x + v*y =
  // gcd(u, v) assuming u and v are non-negative
  //
  // source: Knuth Volume 2 --- Section 4.5.2
  //
  def extendedEuclidHelp(u, v) {
    var zero: u.type = 0;
    var one: u.type = 1;
    var U = (one, zero, u);
    var V = (zero, one, v);
    while V(3) != 0 {
      (U, V) = let q = U(3)/V(3) in (V, U - V * (q, q, q));
    }
    return (U(3), U(1));
  }

  def extendedEuclid(u:int, v:int) {
    return extendedEuclidHelp(u,v);
  }

  def extendedEuclid(u:int(64), v:int(64)) {
    return extendedEuclidHelp(u,v);
  }

  var lo1 = if _hasLow() then this.low:idxType else other.low;
  var hi1 = if _hasHigh() then this.high:idxType else other.high;
  var st1 = abs(this.stride);
  var al1 = if this.stride < 0 then hi1 else lo1;

  var lo2 = if other._hasLow() then other.low else this.low:idxType;
  var hi2 = if other._hasHigh() then other.high else this.high:idxType;
  var st2 = abs(other.stride);
  var al2 = if this.stride < 0 then hi2 else lo2;

  var (g, x) = extendedEuclid(st1, st2);

  var result = new range(idxType,
                         computeBoundedType(this, other),
                         this.stridable | other.stridable,
                         max(lo1, lo2):idxType,
                         min(hi1, hi2):idxType,
                         (st1 * (st2 / g)):chpl__idxTypeToStrType(idxType));
  if lo1 > hi1 || lo2 > hi2 {
    // empty intersection
    if result.low < result.high then
      result._low <=> result._high;
    else if result.high == result.low then
      (result._low, result._high) = (1:idxType, 0:idxType);
    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;
    result._alignLow(al);
    result._alignHigh(al);
  } else if abs(lo1 - lo2) % g:idxType != 0 {
    // empty intersection, return degenerate result
    result._low <=> result._high;
  } else {
    // non-empty intersection

    if other.stride < 0 then
      result._stride = -result.stride;

    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;

    result._alignLow(al);
    result._alignHigh(al);
  }

  return result;
}

//
// default iterator optimized for unit stride
//
def range.these() {
  if boundedType != BoundedRangeType.bounded {
    if boundedType == BoundedRangeType.boundedNone then
      halt("iteration over a range with no bounds");
    if stridable {
      if boundedType == BoundedRangeType.boundedLow then
        if stride < 0 then
          halt("iteration over range with negative stride but no high bound");
      if boundedType == BoundedRangeType.boundedHigh then
        if stride > 0 then
          halt("iteration over range with positive stride but no low bound");
      var i = if stride > 0 then low else high;
      while true {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      if boundedType == BoundedRangeType.boundedHigh then
        halt("iteration over range with positive stride but no low bound");
      var i = low;
      while true {
        yield i;
        i = i + 1;
      }
    }
  } else {
    var i, last: idxType;
    if stridable {
      if stride > 0 {
        i = low;
        last = if i > high then low else high + stride:idxType;
      } else {
        i = high;
        last = if i < low then high else low + stride:idxType;
      }
      while i != last {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      i = low;
      last = if i > high then low else high + 1;
      while i != last {
        yield i;
        i = i + 1;
      }
    }
  }
}

def range.these(param tag: iterator) where tag == iterator.leader {
  // want "yield 0..length-1;"
  // but compilerError in length causes a problem because leaders are
  // resolved wherever an iterator is.
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if debugChapelRange then
    writeln("*** In range leader:"); // ", this);

  var v: idxType;
  if stride > 0 then
    v = (high - low) / stride:idxType + 1;
  else
    v = (low - high) / stride:idxType + 1;
  if v < 0 then
    v = 0;

  var numChunks = _computeNumChunks(v);
  if debugChapelRange then
    writeln("*** RI: length=", v, " numChunks=", numChunks);

  if debugChapelRange then
    writeln("*** RI: Using ", numChunks, " chunk(s)");
  if numChunks == 1 {
    yield tuple(0..v-1);
  } else {
    coforall chunk in 0..numChunks-1 {
      const (lo,hi) = _computeBlock(v, numChunks, chunk, v-1);
      if debugChapelRange then
        writeln("*** RI: tuple = ", tuple(lo..hi));
      yield tuple(lo..hi);
    }
  }
}

def range.these(param tag: iterator, follower) where tag == iterator.follower {
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if follower.size != 1 then
    halt("iteration over a range with multi-dimensional iterator");
  if debugChapelRange then
    writeln("In range follower code: Following ", follower);
  var followThis = follower(1);
  if stridable {
    var r = if stride > 0 then
        low+followThis.low*stride:idxType..low+followThis.high*stride:idxType by stride by followThis.stride
      else
        high+followThis.high*stride:idxType..high+followThis.low*stride:idxType by stride by followThis.stride;
    for i in r do
      yield i;
  } else {
    var r = low+followThis;
    for i in r do
      yield i;
  }
}

//
// returns the number of elements in this range
//
def range.length {
  if boundedType != BoundedRangeType.bounded then
    compilerError("unbounded range has infinite length");
  if low > high then
    return 0:idxType;
  const retVal = if stride > 0
               then (high - low) / stride:idxType + 1
               else (low - high) / stride:idxType + 1;
  return if (retVal < 0) then 0 else retVal;
}

//
// returns true if i is in this range
//
def range.member(i: idxType) {
  if stridable {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high && (high - i) % abs(stride):idxType == 0;
    } else {
      return true;
    }
  } else {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high;
    } else {
      return true;
    }
  }
}

def range.indexOrder(i: idxType) {
  if (!member(i)) then return (-1):idxType;
  return (i-low)/abs(stride);
}

//
// returns true if other is in bounds of this for all specified
// bounds; these functions are used to determine if an array slice is
// valid.  We break out the boundedNone case in order to permit
// unbounded ranges to slice ranges of various index types -- otherwise
// we get a compiler error in the boundsCheck function.
//
def range.boundsCheck(other: range(?e,?b,?s)) where b == BoundedRangeType.boundedNone {
  return true;
}

def range.boundsCheck(other: range(?e,?b,?s)) {
  var boundedOther: range(idxType,BoundedRangeType.bounded,s||this.stridable);
  if other._hasLow() then
    boundedOther._low = other.low;
  else
    boundedOther._low = low;
  if other._hasHigh() then
    boundedOther._high = other.high;
  else
    boundedOther._high = high;
  boundedOther._stride = other.stride;

  return (boundedOther.length == 0) || (this(boundedOther) == boundedOther);
}

def range.boundsCheck(other: idxType)
  return member(other);

//
// returns true if every i in other is in this range
//
def range.member(other: range(?))
  return this(other) == other;

//
// write implementation for ranges
//
def range.writeThis(f: Writer) {
  if _hasLow() then
    f.write(low);
  f.write("..");
  if _hasHigh() then
    f.write(high);
  if stride != 1 then
    f.write(" by ", stride);
}

//
// translate the indices in this range by i
//
def range.translate(i: idxType)
  return this + i;

//
// intended for internal use only:
//
def range.chpl__unTranslate(i: idxType) {
  return this - i;
}

//
// return an interior portion of this range
//
def range.interior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("interior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low, low-1-i, stride);
  else
    return new range(idxType, boundedType, stridable, high+1-i, high, stride);
}

//
// return an exterior portion of this range
//
def range.exterior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("exterior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low+i, low-1, stride);
  else
    return new range(idxType, boundedType, stridable, high+1, high+i, stride);
}

//
// returns an expanded range, or a contracted range if i < 0
//
def range.expand(i: idxType)
  return new range(idxType, boundedType, stridable, low-i, high+i, stride);

//
// range op scalar and scalar op range arithmetic
//
def +(r: range(?e,?b,?s), i: integral)
  return new range((r.low+i).type, b, s, r.low+i, r.high+i, r.stride);

def +(i:integral, r: range(?e,?b,?s))
  return new range((i+r.low).type, b, s, i+r.low, i+r.high, r.stride);

def -(r: range(?e,?b,?s), i: integral)
  return new range((r.low-i).type, b, s, r.low-i, r.high-i, r.stride);

//
// return a substring of a string with a range of indices
//
pragma "inline" def string.substring(s: range(?e,?b,?st)) {
  if s.boundedType != BoundedRangeType.bounded then
    compilerError("substring indexing undefined on unbounded ranges");
  if s.stride != 1 then
    return __primitive("string_strided_select", this, s.low, s.high, s.stride);
  else
    return __primitive("string_select", this, s.low, s.high);
}


Update solution 8 to work with new-and-improved constness checking.

Previously, it was assigning values to const arrays after they were declared.


Fixed some issues in v2-stencil and updated the TODO list at the top of stencil.chpl.

git-svn-id: 979059e4684f5bf082c3598bc35355e1c0a26e59@20438 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

/*
Suggested TODO, in addition to implementing the actual computations:
- add a printout of grid/problem sizes etc. at start of program
- add verification that our cached slices survived after we have
  finished initializing everything.
  E.g. by dsiAccess-indexing into them and querying their domains.
- v1 messages can be now turned off (see util.chpl)
*/

use BlockDist;
use util;

config var chk = true;

/////////// grid of locales ///////////

// "x" -> first dimension
// from grid node (ix,iy): north (ix-1,iy); west (ix,iy-1)

config const gx=3, gy=3;
const gridDom = {1..gx, 1..gy};
const gridBig = {0..gx+1, 0..gy+1};

var gridLocales: [gridDom] locale;
setupGridLocales();
const gridDist = gridDom dmapped Block(gridDom, gridLocales);

/////////// the computation data and index sets ///////////

// the size of node-local data
config const ldx=2, ldy=3;
type elType = real;

// the size of all data
const allx = ldx * gx,
      ally = ldy * gy;

// for neighbor-cache pointers
var auxArr: [1..1] elType;
type cacheType = auxArr._value.type; // a class type, so it can be nil

///////////

// Node-local domains.
class LocalInfo {
  // our coordinates on the grid (perhaps not strictly needed)
  const mygx, mygy: int;
  // our coordinates in the data space
  const myhighx = mygx * ldx,
        mylowx  = myhighx - ldx + 1,
        myhighy = mygy * ldy,
        mylowy  = myhighy - ldy + 1;

  // frequently-used domains
  const domAlloc = {mylowx-1 .. myhighx+1, mylowy-1 .. myhighy+1};
  const domCompute = {mylowx..myhighx, mylowy..myhighy};
}

// A class for all the node-local domains.
class GlobalInfo {
  var infos: [gridDist] LocalInfo;
}

// constructor for GlobalInfo
proc GlobalInfo.GlobalInfo() {
  forall ((ix,iy), inf) in (gridDist, infos) {
    inf = new LocalInfo(mygx=ix, mygy=iy);
  }
}

// Here are all our local domains. WI <- "Working Indices".
const WI = new GlobalInfo();

assert(allx == WI.infos[gx,gy].myhighx, "a6 ");
assert(ally == WI.infos[gx,gy].myhighy, "a7");

///////////

// Node-local computation data.
class LocalData {
  const linfo: LocalInfo;

  // the locale share of the compute data, plus neighbor cache
  var ldata: [linfo.domAlloc] elType;

  // pointers to slices of neighbor data
  // from which we will fetch into our neighbor cache
  var cnorth, csouth, cwest, ceast: cacheType;
}

// A class for all node-local data.
class GlobalData {
  const name: string;
  var datas: [gridDist] LocalData;
}

// constructor for GlobalData
proc GlobalData.GlobalData(nameArg: string) {
  name=nameArg;
  forall (inf, dat, loc) in (WI.infos, datas, gridLocales) {
    dat = new LocalData(inf);
    // sanity checks
    assert(dat.locale == loc);
    assert(dat.linfo.locale == loc);
    assert(dat.linfo == inf);
  }

  /// get and store pointers to neighbor data slices ///

  serial(v1) do forall ((ix,iy), dat, inf) in (gridDist, datas, WI.infos) {
    msg1("neighbors for ", name, (ix, iy), "  domCompute=", inf.domCompute);
    dat.cnorth = storecache(-1,0, inf.mylowx-1,  inf.mylowy..inf.myhighy);
    dat.csouth = storecache(+1,0, inf.myhighx+1, inf.mylowy..inf.myhighy);
    dat.cwest = storecache(0,-1, inf.mylowx..inf.myhighx, inf.mylowy-1);
    dat.ceast = storecache(0,+1, inf.mylowx..inf.myhighx, inf.myhighy+1);

    proc storecache(dx, dy, slicex, slicey) {
      const ind = (ix+dx, iy+dy);
      if !gridDist.member(ind) {
	msg1("  ", ind, "  no neighbor");
	return nil;
      }
      const nbr = datas[ind]; // our neighbor
      var result: cacheType;
      on nbr {
	msg1("  ", ind, "  slice at [", slicex, ",", slicey, "]");
	var slice => nbr.ldata[slicex, slicey];
	result = slice._value;
	if !noRefCount then result._arrCnt.add(1);  // this is a bit low-level
      }
      return result;
    }  // storecache()

  }  // forall
}  // GlobalData constructor

// Our two global arrays, to switch between.
const WA = new GlobalData("WA"),
      WB = new GlobalData("WB");

// Reuse the name for an indexing operation.
// This does not access neighbor caches.
proc GlobalData.dsiAccess(ix,iy) var {
  if chk {
    if 1 <= ix && ix <= ldx * gx && 1 <= iy && iy <= ldy * gy
      then {/*OK*/}
      else halt("GlobalData.dsiAccess: index out of bounds ",
                (ix,ldx,gx), (iy,ldy,gy));
  }
  const gridx = (ix-1) / gx + 1, gridy = (iy-1) / gy + 1;
  return datas[gridx, gridy].ldata[ix,iy];
}

/////////// reference data ///////////

const refAlloc = {0..allx+1, 0..ally+1},
    refCompute = {1..allx, 1..ally},
  refAllocDist = refAlloc dmapped Block(refAlloc, gridLocales);

// Our reference data arrays, corresponding to WA, WB.
var RA, RB: [refAllocDist] elType;

/*
- make 'forall gridDist' distribute over locales
- ensure all component objects get created on the right locales as intended
- add a printout of sizes, grid, etc. at start of program
- add verification that our cached slices survived after we have
  finished initializing everything.
  E.g. by dsiAccess-indexing into them.
 - remove the debugging writeln() and re-enable asserts a1-a3
*/

use BlockDist;
use util;

config var chk = true;
config var init1 = true;

/////////// grid of locales ///////////

// "x" -> first dimension
// from grid node (ix,iy): north (ix-1,iy); west (ix,iy-1)

config const gx=3, gy=3;
const gridDom = {1..gx, 1..gy};
const gridBig = {0..gx+1, 0..gy+1};

var gridLocales: [gridDom] locale;
setupGridLocales();
const gridDist = gridDom dmapped Block(gridDom, gridLocales);

/////////// the computation data and index sets ///////////

// the size of node-local data
config const ldx=2, ldy=3;
type elType = real;

// for neighbor-cache pointers
var auxArr: [1..1] elType;
type cacheType = auxArr._value.type; // a class type, so it can be nil

///////////

// Node-local domains.
class LocalInfo {
  // our coordinates on the grid (perhaps not strictly needed)
  const mygx, mygy: int;
  // our coordinates in the data space
  const myhighx = mygx * ldx,
        mylowx  = myhighx - ldx + 1,
        myhighy = mygy * ldy,
        mylowy  = myhighy - ldy + 1;

  // frequently-used domains
  const domAlloc = {mylowx-1 .. myhighx+1, mylowy-1 .. myhighy+1};
  const domCompute = {mylowx..myhighx, mylowy..myhighy};
}

// A class for all the node-local domains.
class GlobalInfo {
  var infos: [gridDist] LocalInfo;
}

// constructor for GlobalInfo
proc GlobalInfo.GlobalInfo() {
  coforall ((ix,iy), inf) in (gridDist, infos) {
    inf = new LocalInfo(mygx=ix, mygy=iy);
    writeln("GlobalInfo constructor ", (ix,iy), "  on ", here.id,
	    "  inf.locale=", inf.locale.id);
  }
}

// Here are all our local domains. WI <- Working Indices.
const WI = new GlobalInfo();

///////////

// Node-local computation data.
class LocalData {
  const linfo: LocalInfo;

  // the locale share of the compute data, plus neighbor cache
  var ldata: [linfo.domAlloc] elType;

  // pointers to slices of neighbor data
  // from which we will fetch into our neighbor cache
  var cnorth, csouth, cwest, ceast: cacheType;
}

// A class for all node-local data.
class GlobalData {
  const name: string;
  var datas: [gridDist] LocalData;
}

// constructor for GlobalData
proc GlobalData.GlobalData(nameArg: string) {
  name=nameArg;
  coforall (_, inf, dat, loc) in (gridDist, WI.infos, datas, gridLocales) {
    dat = new LocalData(inf);
    writeln("GlobalData constructor for ", (inf.mygx, inf.mygy),
	    "  here=", here.id,
	    "  loc=", loc.id,
	    "  dat.locale=", dat.locale.id);
    // sanity checks
    if false {
    assert(dat.locale == loc, "a1");
    assert(dat.linfo.locale == loc, "a2");
    assert(dat.linfo == inf, "a3");
    }
  }
  exit(0); //vass
  /// get and store pointers to neighbor data slices ///

  serial(v1) do forall ((ix,iy), dat, inf) in (gridDist, datas, WI.infos) {
    msg1("neighbors for ", name, (ix, iy), "  domCompute ", inf.domCompute);
    dat.cnorth = storecache(-1,0, inf.mylowx-1,  inf.mylowy..inf.myhighy);
    dat.csouth = storecache(+1,0, inf.myhighx+1, inf.mylowy..inf.myhighy);
    dat.cwest = storecache(0,-1, inf.mylowx..inf.myhighx, inf.mylowy-1);
    dat.ceast = storecache(0,+1, inf.mylowx..inf.myhighx, inf.myhighy+1);

    proc storecache(dx, dy, slicex, slicey) {
      const ind = (ix+dx, iy+dy);
      if !gridDist.member(ind) {
	msg1("  ", ind, "  no neighbor");
	return nil;
      }
      const nbr = datas[ind]; // our neighbor
      var result: cacheType;
      on nbr {
	msg1("  ", ind, "  slice at [", slicex, ",", slicey, "]");
	var slice => nbr.ldata[slicex, slicey];
	result = slice._value;
	if !noRefCount then result._arrCnt.add(1);  // this is a bit low-level
      }
      return result;
    }  // storecache()

  }  // coforall
}  // GlobalData constructor

// Our two global arrays, to switch between.
const WA = new GlobalData("WA"),
      WB = new GlobalData("WB");

// Reuse the name for an indexing operation.
// This does not access neighbor caches.
proc GlobalData.dsiAccess(ix,iy) var {
  if chk then assert(1 <= ix && ix <= ldx * gx && 1 <= iy && iy <= ldy * gy,
		     "a5 ", (ix,ldx,gx), (iy,ldy,gy));
  const gridx = (ix-1) / gx + 1, gridy = (iy-1) / gy + 1;
  return datas[gridx, gridy].ldata[ix,iy];
}

/////////// reference data ///////////

const refx = ldx * gx,
      refy = ldy * gy;
assert(refx == WI.infos[gx,gy].myhighx, "a6 ");
assert(refy == WI.infos[gx,gy].myhighy, "a7");

const refAlloc = {0..refx+1, 0..refy+1},
    refCompute = {1..refx, 1..refy},
  refAllocDist = refAlloc dmapped Block(refAlloc, gridLocales);

// Our reference data arrays, corresponding to WA, WB.
var RA, RB: [refAllocDist] elType;


10 - summation of primes

10 - summation of primes


Resolve time-write.chpl regression (jira issue 25)

io/vass/time-write fails with --no-local performance testing. The stdout and
stderr streams are getting intermixed resulting in a verification failure.

This test writes a LOT of newlines to stdout and some info to stderr. The stuff
printed to stderr is part of the performance verification but some newlines
from stdout are getting intermixed so the verification fails. This isn't a real
issue but related to how slurm does stdout and stderr buffering. For srun
(interactive) line buffering occurs so we don't run into this, but with sbatch
only stdout is buffered so we run into this problem.

To solve this issue, this patch just adds some stdout and stderr flushes which
seems to have resolved the issue. I ran 20 trials and it seemed to go well
(normally the test fails within a trial or 2.) If we still get failures we can
update the verification to not look for the "starting trial x" stuff or we can
switch the test to put all output to stdout instead of stderr.

It's possible that we could run into this issue for any test that writes to
stderr and stdout and expects output in the right order, but I highly doubt we
will run into this with any other tests. This test rights some 30 million
newlines and heavily taxes the io buffering system, especially since the
hardware we're running on has limited io nodes.

/*
This is a test of Chapel's write() of a string vs. C's printf()
of the same string, which has no formatting in it.

The test exposes Chapel's overhead on top of the actual work
of displaying the string.

To make the timing meaningful, the test performs write()/printf()
multiple times. To reduce the output volume, we use a short string.
This has the (unintentional) effect of emphasizing the overhead.

Since we expect the output to be redirected, due to its high volume,
there may be various startup effects.
To mitigate them, we rotate the variants 'tries' times,
starting with the one we expect to be the fastest.
However, the initial measurements did not support this concern.

Test variations - in C:
* Use printf() or puts().
* For C, wrap printf() in a loop in C or in Chapel.
* Pass "\n" to printf() from Chapel as a Chapel 'string' or 'c_string'.
* For the previous one, make it a global constant or a local constant.
* We could also do the above variations for puts() - skip that for now,
  to reduce the amount of info to deal with, and perhaps puts() is
  less interesting.

Test variations - in Chapel:
* since stdout is blocking, we also try a variant using a workaround of
  creating a non-blocking version of it - see stdoutNB.

Michael's remarks on performance:

+ Chapel's write() or stdout.write()
actually call printf so that Chapel programs and C
programs can intermix their standard output. They
are also both line buffered which reduces performance.
  Chapel's stdout is using the C stdout FILE*,
and calling fwrite. That means that any data
sent to Chapel's stdout is buffered twice in
the application:
 - once in the QIO channel
 - once in the FILE* buffer
I would expect this to reduce performance.

+ We could revisit that design decision and make
write use file descriptor 1 if we no longer wanted
it to work this way. It would go faster but possibly
result in unexpected intermixings of stdout.

+ The performance lag of Chapel with openfd(1)
is worth investigating regardless of the above.

+ I also think that it's important to compare
locking=false with printf_unlocked.
[See "man unlocked_stdio" - basically it is the C version of printf
that does not lock the FILE* so it's only safe to use in a serial context.]
*/

use Time;

config const n = 3;
config const tries = 2;
config const fmt = "%7.3dr";
config const compareTimes = false;

// Each trial() function writes a newline 'n' times.

//////////////////////////////////////////////////////////////////

extern proc cf_trial(n: int);
extern proc cs_trial(n: int);

proc lstr_trial() {
  extern proc printf(format: c_string, arg...);
  for 1..n do
    printf("%s", "\n".c_str());
}

proc lcs_trial() {
  extern proc printf(format: c_string, arg...);
  const c_newline_local: c_string = "\n";
  for 1..n do
    printf("%s", c_newline_local);
}

const str_newline_global = "\n";
proc gstr_trial() {
  extern proc printf(format: c_string, arg...);
  for 1..n do
    printf("%s", str_newline_global.c_str());
}

const c_newline_global: c_string = "\n";
proc gcs_trial() {
  extern proc printf(format: c_string, arg...);
  for 1..n do
    printf("%s", c_newline_global);
}

// could do the same with puts() - would be too much, skip it for now

//////////////////////////////////////////////////////////////////

// write() a newline to stdout (non-blocking), 'n' times
const outfd = openfd(1);
const stdoutNB = outfd.writer(locking=false);
proc nb_trial() {
  for 1..n do
    stdoutNB.write("\n");
}

// write() a newline to stdout (blocking), 'n' times
proc sto_trial() {
  for 1..n do
    write("\n");
}

//////////////////////////////////////////////////////////////////

var tSave, tDummy, tcf, tcs, tlstr, tlcs, tgstr, tgcs, tnb, tsto: real;

inline proc addTime(ref t: real) {
  const tCurr = getCurrentTime();
  t += tCurr - tSave;
  tSave = tCurr;

}

const reportFormat = "%-24s " + fmt + "\n";
inline proc reportTime(title: string, time: real) {
  stderr.writef(reportFormat, title, time);
}

stderr.writef("n      %i\n", n);
stderr.writef("tries  %i\n", tries);

for t in 1..tries {
  stderr.writef("starting try %i\n", t);
  stderr.flush();

  addTime(tDummy);

  cf_trial(n);   addTime(tcf);
  cs_trial(n);   addTime(tcs);
  lstr_trial();  addTime(tlstr);
  lcs_trial();   addTime(tlcs);
  gstr_trial();  addTime(tgstr);
  gcs_trial();   addTime(tgcs);

  nb_trial();     addTime(tnb);
  sto_trial();   addTime(tsto);

  // flush any buffered newlines
  stdout.flush();
}

stderr.writef("done tries\n");
//stderr.writef("n      %i\n", n);
//stderr.writef("tries  %i\n", tries);

if compareTimes {
  reportTime("C printf", tcf);
  reportTime("C puts", tcs);
  reportTime("C/chpl string literal",  tlstr);
  reportTime("C/chpl c_string local",  tlcs);
  reportTime("C/chpl string global",   tgstr);
  reportTime("C/chpl c_string global", tgcs);
  reportTime("chpl non-blocking", tnb);
  reportTime("chpl stdout", tsto);
}

/*
This is a test of Chapel's write() of a string vs. C's printf()
of the same string, which has no formatting in it.

The test exposes Chapel's overhead on top of the actual work
of displaying the string.

To make the timing meaningful, the test performs write()/printf()
multiple times. To reduce the output volume, we use a short string.
This has the (unintentional) effect of emphasizing the overhead.

Since we expect the output to be redirected, due to its high volume,
there may be various startup effects.
To mitigate them, we rotate the variants 'tries' times,
starting with the one we expect to be the fastest.
However, the initial measurements did not support this concern.

Test variations - in C:
* Use printf() or puts().
* For C, wrap printf() in a loop in C or in Chapel.
* Pass "\n" to printf() from Chapel as a Chapel 'string' or 'c_string'.
* For the previous one, make it a global constant or a local constant.
* We could also do the above variations for puts() - skip that for now,
  to reduce the amount of info to deal with, and perhaps puts() is
  less interesting.

Test variations - in Chapel:
* since stdout is blocking, we also try a variant using a workaround of
  creating a non-blocking version of it - see stdoutNB.

Michael's remarks on performance:

+ Chapel's write() or stdout.write()
actually call printf so that Chapel programs and C
programs can intermix their standard output. They
are also both line buffered which reduces performance.
  Chapel's stdout is using the C stdout FILE*,
and calling fwrite. That means that any data
sent to Chapel's stdout is buffered twice in
the application:
 - once in the QIO channel
 - once in the FILE* buffer
I would expect this to reduce performance.

+ We could revisit that design decision and make
write use file descriptor 1 if we no longer wanted
it to work this way. It would go faster but possibly
result in unexpected intermixings of stdout.

+ The performance lag of Chapel with openfd(1)
is worth investigating regardless of the above.

+ I also think that it's important to compare
locking=false with printf_unlocked.
[See "man unlocked_stdio" - basically it is the C version of printf
that does not lock the FILE* so it's only safe to use in a serial context.]
*/

use Time;

config const n = 3;
config const tries = 2;
config const fmt = "%7.3dr";
config const compareTimes = false;

// Each trial() function writes a newline 'n' times.

//////////////////////////////////////////////////////////////////

extern proc cf_trial(n: int);
extern proc cs_trial(n: int);

proc lstr_trial() {
  extern proc printf(format: c_string, arg...);
  for 1..n do
    printf("%s", "\n".c_str());
}

proc lcs_trial() {
  extern proc printf(format: c_string, arg...);
  const c_newline_local: c_string = "\n";
  for 1..n do
    printf("%s", c_newline_local);
}

const str_newline_global = "\n";
proc gstr_trial() {
  extern proc printf(format: c_string, arg...);
  for 1..n do
    printf("%s", str_newline_global.c_str());
}

const c_newline_global: c_string = "\n";
proc gcs_trial() {
  extern proc printf(format: c_string, arg...);
  for 1..n do
    printf("%s", c_newline_global);
}

// could do the same with puts() - would be too much, skip it for now

//////////////////////////////////////////////////////////////////

// write() a newline to stdout (non-blocking), 'n' times
const outfd = openfd(1);
const stdoutNB = outfd.writer(locking=false);
proc nb_trial() {
  for 1..n do
    stdoutNB.write("\n");
}

// write() a newline to stdout (blocking), 'n' times
proc sto_trial() {
  for 1..n do
    write("\n");
}

//////////////////////////////////////////////////////////////////

var tSave, tDummy, tcf, tcs, tlstr, tlcs, tgstr, tgcs, tnb, tsto: real;

inline proc addTime(ref t: real) {
  const tCurr = getCurrentTime();
  t += tCurr - tSave;
  tSave = tCurr;

}

const reportFormat = "%-24s " + fmt + "\n";
inline proc reportTime(title: string, time: real) {
  stderr.writef(reportFormat, title, time);
}

stderr.writef("n      %i\n", n);
stderr.writef("tries  %i\n", tries);

for t in 1..tries {
  stderr.writef("starting try %i\n", t);
  addTime(tDummy);

  cf_trial(n);   addTime(tcf);
  cs_trial(n);   addTime(tcs);
  lstr_trial();  addTime(tlstr);
  lcs_trial();   addTime(tlcs);
  gstr_trial();  addTime(tgstr);
  gcs_trial();   addTime(tgcs);

  nb_trial();     addTime(tnb);
  sto_trial();   addTime(tsto);
}

stderr.writef("done tries\n");
//stderr.writef("n      %i\n", n);
//stderr.writef("tries  %i\n", tries);

if compareTimes {
  reportTime("C printf", tcf);
  reportTime("C puts", tcs);
  reportTime("C/chpl string literal",  tlstr);
  reportTime("C/chpl c_string local",  tlcs);
  reportTime("C/chpl string global",   tgstr);
  reportTime("C/chpl c_string global", tgcs);
  reportTime("chpl non-blocking", tnb);
  reportTime("chpl stdout", tsto);
}


Bugfix in out-of-place real to complex transform

use FFTW;

proc printcmp(x, y) {
	var err = max reduce abs(x-y);
	//writeln("----");
	//writeln(x);
	//writeln("**");
	//writeln(y);
	//writeln("----");
	writeln(err);
}

proc runtest(param ndim : int, fn : string) {
	/* Read in the data and set up the domains appropriately */
	var dims : ndim*int(32);
	var D : domain(ndim);
	// Define ranges here
	var rD,cD,reD,imD : domain(ndim,int,true);
	var A,B,goodA,goodB : [D] fftw_complex;
	{
		var f = open(fn,iomode.r).reader(kind=iokind.little);
		for ii in 1..ndim {
			f.read(dims(ii));
		}
		// Note compiler does not automatically fold select on params
		if (ndim == 1) {
			D = 0.. #dims(ndim);
		}
		if (ndim == 2) {
			D = {0.. #dims(1),0.. #dims(ndim)};
		}
		if (ndim == 3) {
			D = {0.. #dims(1),0.. #dims(2), 0.. #dims(ndim)};
		}
		for (r,c) in goodA {
			f.read(r);
			c = 0;
		}
		for (r,c) in goodB {
			f.read(r); f.read(c);
		}
		f.close();
		writeln("Data read...");
	}
	// Set up domains based on dimension size
	var first : ndim*int;
	if (ndim==1) {
		var ldim = dims(1)/2 + 1;
		// Domains for real FFT
		rD = 0.. #(2*ldim); // Padding to do in place transforms
		cD = 0.. #ldim;
		// Define domains to extract the real and imaginary parts for inplace transforms
		reD = rD[0..(2*ldim-1) by 2]; // Padding to do in place transforms
		imD = rD[1..(2*ldim-1) by 2]; // Padding to do in place transforms
		first=(0,);
	}
	if (ndim==2) {
		// Domains for real FFT
		var ldim = dims(2)/2+1;
		rD = {0.. #dims(1),0.. #(2*ldim)}; // Padding to do in place transforms
		cD = {0.. #dims(1),0.. #ldim};
		// Define domains to extract the real and imaginary parts for in place transforms
		reD = rD[..,0..(2*ldim-1) by 2]; // Padding to do in place transforms
		imD = rD[..,1..(2*ldim-1) by 2]; // Padding to do in place transforms
		first=(0,0);
	}
	if (ndim==3) {
		// Domains for real FFT
		var ldim = dims(3)/2+1;
		rD = {0.. #dims(1),0.. #dims(2),0.. #(2*ldim)}; // Padding to do in place transforms
		cD = {0.. #dims(1),0.. #dims(2),0.. #ldim};
		// Define domains to extract the real and imaginary parts for in place transforms
		reD = rD[..,..,0..(2*ldim-1) by 2]; // Padding to do in place transforms
		imD = rD[..,..,1..(2*ldim-1) by 2]; // Padding to do in place transforms
		first=(0,0,0);
	}

	// FFTW does not normalize inverse transform, set up norm
	var norm = * reduce dims;

	// FFT testing here
	var fwd = plan_dft(dims, A[first],B[first],FFTW_FORWARD,FFTW_ESTIMATE);
	var rev = plan_dft(dims, B[first],A[first],FFTW_BACKWARD,FFTW_ESTIMATE);
	// Test forward and reverse transform
	A = goodA;
	execute(fwd);
	printcmp(B,goodB);
	execute(rev);
	A /= norm;
	printcmp(A,goodA);
	destroy_plan(fwd);
	destroy_plan(rev);

	// Test in-place transforms
	fwd = plan_dft(dims,A[first],A[first],FFTW_FORWARD,FFTW_ESTIMATE);
	rev = plan_dft(dims,A[first],A[first],FFTW_BACKWARD,FFTW_ESTIMATE);
	A = goodA;
	// Test forward and reverse transform
	A = goodA;
	execute(fwd);
	printcmp(A,goodB);
	execute(rev);
	A /= norm; // FFTW does an unnormalized transform
	printcmp(A,goodA);
	destroy_plan(fwd);
	destroy_plan(rev);

	// Testing r2c and c2r
	var rA : [D] real(64); // No padding for an out-of place transform
	var cB : [cD] fftw_complex;
	fwd = plan_dft_r2c(dims,rA[first],cB[first],FFTW_ESTIMATE);
	rev = plan_dft_c2r(dims,cB[first],rA[first],FFTW_ESTIMATE);
	rA[D] = re(goodA);
	execute(fwd);
	printcmp(cB,goodB[cD]);
	execute(rev);
	rA /= norm;
	printcmp(rA[D],re(goodA));
	destroy_plan(fwd);
	destroy_plan(rev);
	// In place transform
	var rA2 : [rD] real(64);
	fwd = plan_dft_r2c(dims,rA2[first],rA2[first],FFTW_ESTIMATE);
	rev = plan_dft_c2r(dims,rA2[first],rA2[first],FFTW_ESTIMATE);
	rA2[D] = re(goodA);
	execute(fwd);
	printcmp(rA2[reD],re(goodB[cD]));
	printcmp(rA2[imD],im(goodB[cD]));
	execute(rev);
	rA2 /= norm;
	printcmp(rA2[D],re(goodA));
	destroy_plan(fwd);
	destroy_plan(rev);

}

writeln("1D");
runtest(1, "arr1d.dat");
writeln("2D");
runtest(2, "arr2d.dat");
writeln("3D");
runtest(3, "arr3d.dat");

use FFTW;

proc printcmp(x, y) {
	var err = max reduce abs(x-y);
	writeln(err);
}

proc runtest(param ndim : int, fn : string) {
	/* Read in the data and set up the domains appropriately */
	var dims : ndim*int(32);
	var D : domain(ndim);
	// Define ranges here
	var rD,cD,reD,imD : domain(ndim,int,true);
	var A,B,goodA,goodB : [D] fftw_complex;
	{
		var f = open(fn,iomode.r).reader(kind=iokind.little);
		for ii in 1..ndim {
			f.read(dims(ii));
		}
		// Note compiler does not automatically fold select on params
		if (ndim == 1) {
			D = 0.. #dims(ndim);
		}
		if (ndim == 2) {
			D = {0.. #dims(1),0.. #dims(ndim)};
			writeln(dims, D);
		}
		if (ndim == 3) {
			D = {0.. #dims(1),0.. #dims(2), 0.. #dims(ndim)};
		}
		for (r,c) in goodA {
			f.read(r);
			c = 0;
		}
		for (r,c) in goodB {
			f.read(r); f.read(c);
		}
		f.close();
		writeln("Data read...");
	}
	// Set up domains based on dimension size
	var first : ndim*int;
	if (ndim==1) {
		var ldim = dims(1)/2 + 1;
		// Domains for real FFT
		rD = 0.. #(2*ldim); // Padding to do in place transforms
		cD = 0.. #ldim;
		// Define domains to extract the real and imaginary parts
		reD = rD[0..(2*ldim-1) by 2]; // Padding to do in place transforms
		imD = rD[1..(2*ldim-1) by 2]; // Padding to do in place transforms
		first=(0,);
	}
	if (ndim==2) {
		// Domains for real FFT
		var ldim = (dims(2)/2+1);
		rD = {0.. #dims(1),0.. #(2*ldim)}; // Padding to do in place transforms
		cD = {0.. #dims(1),0.. #ldim};
		// Define domains to extract the real and imaginary parts
		reD = rD[..,0..(2*ldim-1) by 2]; // Padding to do in place transforms
		imD = rD[..,1..(2*ldim-1) by 2]; // Padding to do in place transforms
		first=(0,0);
	}
	writeln("Domains set....");

	// FFTW does not normalize inverse transform, set up norm
	var norm = * reduce dims;

	// FFT testing here
	var fwd = plan_dft(dims, A[first],B[first],FFTW_FORWARD,FFTW_ESTIMATE);
	var rev = plan_dft(dims, B[first],A[first],FFTW_BACKWARD,FFTW_ESTIMATE);
	// Test forward and reverse transform
	A = goodA;
	execute(fwd);
	printcmp(B,goodB);
	execute(rev);
	A /= norm;
	printcmp(A,goodA);
	destroy_plan(fwd);
	destroy_plan(rev);

	// Test in-place transforms
	fwd = plan_dft(dims,A[first],A[first],FFTW_FORWARD,FFTW_ESTIMATE);
	rev = plan_dft(dims,A[first],A[first],FFTW_BACKWARD,FFTW_ESTIMATE);
	A = goodA;
	// Test forward and reverse transform
	A = goodA;
	execute(fwd);
	printcmp(A,goodB);
	execute(rev);
	A /= norm; // FFTW does an unnormalized transform
	printcmp(A,goodA);
	destroy_plan(fwd);
	destroy_plan(rev);
	writeln("Complex-to-Complex tested...");

	// Testing r2c and c2r
	var rA : [rD] real(64);
	var cB : [cD] fftw_complex;
	fwd = plan_dft_r2c(dims,rA[first],cB[first],FFTW_ESTIMATE);
	rev = plan_dft_c2r(dims,cB[first],rA[first],FFTW_ESTIMATE);
	rA[D] = re(goodA);
	execute(fwd);
	printcmp(cB,goodB[cD]);
	execute(rev);
	rA /= norm;
	printcmp(rA[D],re(goodA));
	destroy_plan(fwd);
	destroy_plan(rev);
	// In place transform
	fwd = plan_dft_r2c(dims,rA[first],rA[first],FFTW_ESTIMATE);
	rev = plan_dft_c2r(dims,rA[first],rA[first],FFTW_ESTIMATE);
	rA[D] = re(goodA);
	execute(fwd);
	printcmp(rA[reD],re(goodB[cD]));
	printcmp(rA[imD],im(goodB[cD]));
	execute(rev);
	rA /= norm;
	printcmp(rA[D],re(goodA));
	destroy_plan(fwd);
	destroy_plan(rev);
	writeln("Real-to-Complex tested...");

}

runtest(1, "arr1d.dat");
runtest(2, "arr2d.dat");