Change the error for using the count operator with an unbounded range from a
runtime halt to a compilerError.

This avoids a less clear error about using a function that doesn't return a
value.

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@17297 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;

  const numTasks = if dataParTasksPerLocale==0 then here.numCores
                   else dataParTasksPerLocale;
  const ignoreRunning = dataParIgnoreRunningTasks;
  const minIndicesPerTask = dataParMinGranularity;

  var numChunks = _computeNumChunks(numTasks, ignoreRunning,
                                    minIndicesPerTask, 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);
}

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 {
  halt("cannot use # operator on an unbounded range");
}

////////////////////////////////////////////////////////////////////////////////
// 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;

  const numTasks = if dataParTasksPerLocale==0 then here.numCores
                   else dataParTasksPerLocale;
  const ignoreRunning = dataParIgnoreRunningTasks;
  const minIndicesPerTask = dataParMinGranularity;

  var numChunks = _computeNumChunks(numTasks, ignoreRunning,
                                    minIndicesPerTask, 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);
}


fixed the indexing bug found by Brad

fixed the indexing bug found by Brad

/******************************************************************************
* This is a benchmark written to emulate the behavior of a typical
* computational fluid dynamics benchmark.
* There are several versions of this benchmark - the original written with
* the use of CHOMBO, other versions are written in pure c. This version is
* written in Chapel in order to explore the use of Chapel iterators and
* performance.
*******************************************************************************/
module CFD_mini {

 use Time;

 const dx = 0.5;
 const factor1 = 1.0/12.0;
 const factor2 = 2.0;

 config var nCell = 128;
 config var nBox = 32;
 config var nGhost = 2;
 config var nComp = 5;
 config var test = 0;

 var totalWidth = nCell+2*nGhost;
 var cellData: domain(3) = {0..nCell-1,0..nCell-1,0..nCell-1};
 var withGhosts: domain(3) = cellData.expand(nGhost);
 var boxDomain: domain(1) = {0..nBox-1};
 var compDomain: domain(1) = {0..nComp-1};
 var xFluxDomain: domain(3) = {0..nCell-1,0..nCell-1,0..nCell};
 var yFluxDomain: domain(3) = {0..nCell-1,0..nCell,0..nCell-1};
 var zFluxDomain: domain(3) = {0..nCell,0..nCell-1,0..nCell-1};
 var FluxDomain: domain(3) = {0..nCell,0..nCell,0..nCell};
 var loopTime: Timer;

 proc main() {

   writeln("nCell=",nCell," nBox=",nBox);

   var old_data:[0..nBox-1,0..nComp-1,-2..nCell+1,-2..nCell+1,-2..nCell+1] real;
   var new_data:[0..nBox-1,0..nComp-1,-2..nCell+1,-2..nCell+1,-2..nCell+1] real;

   // initialize the data in the arrays
   // Each cell value in both the old and new data is initialized to be
   // the manhattan distance from the origin
   for b in boxDomain {
     for c in  compDomain {
       for (z,y,x) in withGhosts{
         old_data[b,c,z,y,x] = c + dx*(z+y+x);
         new_data[b,c,z,y,x] = old_data[b,c,z,y,x];
      }
    }
  }

/*******************************************************************************
 * Process Boxes:
 *  Processing the boxes means that we will be reading the data from
 *  old_boxes and writing them to new_boxes
 *  The following are the equations for this calculation
 *
 *  There are 5 components: p, e, u, v, w (density, energy, velocity (3D))
 *  Each of these components is represented as a 3D array (initialized
 *  above).
 * p_{t+1}=factor2*g(p_t)*g(u_t)+factor2*g'(p_t)*g'(v_t)+factor2*g"(p_t)*g"(w_t)
 * e_{t+1}=factor2*g(e_t)*g(u_t)+factor2*g'(e_t)*g'(v_t)+factor2*g"(e_t)*g"(w_t)
 * u_{t+1}=factor2*g(u_t)*g(u_t)+factor2*g'(u_t)*g'(v_t)+factor2*g"(u_t)*g"(w_t)
 * v_{t+1}=factor2*g(v_t)*g(u_t)+factor2*g'(v_t)*g'(v_t)+factor2*g"(v_t)*g"(w_t)
 * w_{t+1}=factor2*g(w_t)*g(u_t)+factor2*g'(w_t)*g'(v_t)+factor2*g"(w_t)*g"(w_t)
 *
 *
 *  g(data) is a stencil operation that looks like the following:
 *  g(data[z][y][x]) = factor1*
 *        (data[z][y][x-2]+7*(data[z][y][x-1]+data[z][y][x])+data[z][y][x+1])
 *  similarly for g' and g"
 *  g'(data[z][y][x]) = factor1*
 *        (data[z][y-2][x]+7*(data[z][y-1][x]+data[z][y][x])+data[z][y+1][x])
 *  g"(data[z][y][x]) = factor1*
 *        (data[z-2][y][x]+7*(data[z-1][y][x]+data[z][y][x])+data[z+1][y][x])
 *
 *
 *  Step 1 is to calculate all of the g() values
 *  Step 2 multiplies the values together for the first column in the
 *         equations above
 *  Step 3 Return to Step 1 for g' and then for g"
 ****************************************************************************/
   loopTime.start();
   forall b in boxDomain {
     var FluxCache: [0..nComp-1,0..nCell,0..nCell,0..nCell] real;

     // x-direction
     // compute the flux on the faces in each direction
     for z in 0..nCell-1{
     for y in 0..nCell-1{
     for x in 0..nCell{
       FluxCache[0,z,y,x] = factor1 *  (old_data[b,0,z,y,x-2] +
                                       7*(old_data[b,0,z,y,x-1] +
                                         old_data[b,0,z,y,x]) +
                                         old_data[b,0,z,y,x+1]);
       FluxCache[1,z,y,x] = factor1 *  (old_data[b,1,z,y,x-2] +
                                       7*(old_data[b,1,z,y,x-1] +
                                         old_data[b,1,z,y,x]) +
                                         old_data[b,1,z,y,x+1]);
       FluxCache[2,z,y,x] = factor1 *  (old_data[b,2,z,y,x-2] +
                                       7*(old_data[b,2,z,y,x-1] +
                                         old_data[b,2,z,y,x]) +
                                         old_data[b,2,z,y,x+1]);
       FluxCache[3,z,y,x] = factor1 *  (old_data[b,3,z,y,x-2] +
                                       7*(old_data[b,3,z,y,x-1] +
                                         old_data[b,3,z,y,x]) +
                                         old_data[b,3,z,y,x+1]);
       FluxCache[4,z,y,x] = factor1 *  (old_data[b,4,z,y,x-2] +
                                       7*(old_data[b,4,z,y,x-1] +
                                         old_data[b,4,z,y,x]) +
                                         old_data[b,4,z,y,x+1]);
       FluxCache[0,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[1,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[3,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[4,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[2,z,y,x] *= factor2*FluxCache[2,z,y,x];
     }}}

     // accumulate the difference into the new data cells
    for z in 0..nCell-1{
    for y in 0..nCell-1{
    for x in 0..nCell-1{
      for c in compDomain{
        new_data[b,c,z,y,x] += FluxCache[c,z,y,x+1] - FluxCache[c,z,y,x];
      }
    }}}

     // compute the flux on the faces in each direction
     for z in 0..nCell-1{
     for y in 0..nCell{
     for x in 0..nCell-1{
       FluxCache[0,z,y,x] = factor1*  (old_data[b,0,z,y-2,x] +
                                         7*(old_data[b,0,z,y-1,x] +
                                           old_data[b,0,z,y,x]) +
                                         old_data[b,0,z,y+1,x]);
       FluxCache[1,z,y,x] = factor1*  (old_data[b,1,z,y-2,x] +
                                         7*(old_data[b,1,z,y-1,x] +
                                           old_data[b,1,z,y,x]) +
                                         old_data[b,1,z,y+1,x]);
       FluxCache[2,z,y,x] = factor1*  (old_data[b,2,z,y-2,x] +
                                         7*(old_data[b,2,z,y-1,x] +
                                           old_data[b,2,z,y,x]) +
                                         old_data[b,2,z,y+1,x]);
       FluxCache[3,z,y,x] = factor1*  (old_data[b,3,z,y-2,x] +
                                         7*(old_data[b,3,z,y-1,x] +
                                           old_data[b,3,z,y,x]) +
                                         old_data[b,3,z,y+1,x]);
       FluxCache[4,z,y,x] = factor1*  (old_data[b,4,z,y-2,x] +
                                         7*(old_data[b,4,z,y-1,x] +
                                           old_data[b,4,z,y,x]) +
                                         old_data[b,4,z,y+1,x]);
       FluxCache[0,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[1,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[2,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[4,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[3,z,y,x] *= factor2*FluxCache[3,z,y,x];
     }}}

     // accumulate the difference into the new data cells
     for z in 0..nCell-1{
     for y in 0..nCell-1{
     for x in 0..nCell-1{
      for c in compDomain{
        new_data[b,c,z,y,x] += FluxCache[c,z,y+1,x] - FluxCache[c,z,y,x];
      }
     }}}

    // z-direction
     for z in 0..nCell{
     for y in 0..nCell-1{
     for x in 0..nCell-1{
      FluxCache[0,z,y,x] = factor1*  (old_data[b,0,z-2,y,x] +
                                    7*(old_data[b,0,z-1,y,x] +
                                       old_data[b,0,z,y,x]) +
                                       old_data[b,0,z+1,y,x]);
      FluxCache[1,z,y,x] = factor1*  (old_data[b,1,z-2,y,x] +
                                    7*(old_data[b,1,z-1,y,x] +
                                       old_data[b,1,z,y,x]) +
                                       old_data[b,1,z+1,y,x]);
      FluxCache[2,z,y,x] = factor1*  (old_data[b,2,z-2,y,x] +
                                    7*(old_data[b,2,z-1,y,x] +
                                       old_data[b,2,z,y,x]) +
                                       old_data[b,2,z+1,y,x]);
      FluxCache[3,z,y,x] = factor1*  (old_data[b,3,z-2,y,x] +
                                    7*(old_data[b,3,z-1,y,x] +
                                       old_data[b,3,z,y,x]) +
                                       old_data[b,3,z+1,y,x]);
      FluxCache[4,z,y,x] = factor1*  (old_data[b,4,z-2,y,x] +
                                    7*(old_data[b,4,z-1,y,x] +
                                       old_data[b,4,z,y,x]) +
                                       old_data[b,4,z+1,y,x]);
       FluxCache[0,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[1,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[2,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[3,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[4,z,y,x] *= factor2*FluxCache[4,z,y,x];
    }}}

     // accumulate the difference into the new data cells
    for z in 0..nCell-1{
    for y in 0..nCell-1{
    for x in 0..nCell-1{
      for c in compDomain{
        new_data[b,c,z,y,x] += FluxCache[c,z+1,y,x] - FluxCache[c,z,y,x];
      }
    }}}
  }
  loopTime.stop();

  if(test){
    writeln("Resulting Data");
    for (z,y,x) in cellData{
      if(x%4==0){
        writeln();
      }
      write(new_data[0,0,z,y,x],",");
    }
    writeln();
  }else{
    writeln("LoopTime: ",loopTime.elapsed());
  }

 }
}

/******************************************************************************
* This is a benchmark written to emulate the behavior of a typical
* computational fluid dynamics benchmark.
* There are several versions of this benchmark - the original written with
* the use of CHOMBO, other versions are written in pure c. This version is
* written in Chapel in order to explore the use of Chapel iterators and
* performance.
*******************************************************************************/
module CFD_mini {

 use Time;

 const dx = 0.5;
 const factor1 = 1.0/12.0;
 const factor2 = 2.0;

 config var nCell = 128;
 config var nBox = 32;
 config var nGhost = 2;
 config var nComp = 5;
 config var test = 0;

 var totalWidth = nCell+2*nGhost;
 var cellData: domain(3) = {0..nCell-1,0..nCell-1,0..nCell-1};
 var withGhosts: domain(3) = cellData.expand(nGhost);
 var boxDomain: domain(1) = {0..nBox-1};
 var compDomain: domain(1) = {0..nComp-1};
 var xFluxDomain: domain(3) = {0..nCell-1,0..nCell-1,0..nCell};
 var yFluxDomain: domain(3) = {0..nCell-1,0..nCell,0..nCell-1};
 var zFluxDomain: domain(3) = {0..nCell,0..nCell-1,0..nCell-1};
 var FluxDomain: domain(3) = {0..nCell,0..nCell,0..nCell};
 var loopTime: Timer;

 proc main() {

   writeln("nCell=",nCell," nBox=",nBox);

   var old_data:[0..nBox-1,0..nComp-1,-2..nCell+1,-2..nCell+1,-2..nCell+1] real;
   var new_data:[0..nBox-1,0..nComp-1,-2..nCell+1,-2..nCell+1,-2..nCell+1] real;

   // initialize the data in the arrays
   // Each cell value in both the old and new data is initialized to be
   // the manhattan distance from the origin
   for b in boxDomain {
     for c in  compDomain {
       for (z,y,x) in withGhosts{
         old_data[b,c,z,y,x] = c + dx*(z+y+x);
         new_data[b,c,z,y,x] = old_data[b,c,z,y,x];
      }
    }
  }

/*******************************************************************************
 * Process Boxes:
 *  Processing the boxes means that we will be reading the data from
 *  old_boxes and writing them to new_boxes
 *  The following are the equations for this calculation
 *
 *  There are 5 components: p, e, u, v, w (density, energy, velocity (3D))
 *  Each of these components is represented as a 3D array (initialized
 *  above).
 * p_{t+1}=factor2*g(p_t)*g(u_t)+factor2*g'(p_t)*g'(v_t)+factor2*g"(p_t)*g"(w_t)
 * e_{t+1}=factor2*g(e_t)*g(u_t)+factor2*g'(e_t)*g'(v_t)+factor2*g"(e_t)*g"(w_t)
 * u_{t+1}=factor2*g(u_t)*g(u_t)+factor2*g'(u_t)*g'(v_t)+factor2*g"(u_t)*g"(w_t)
 * v_{t+1}=factor2*g(v_t)*g(u_t)+factor2*g'(v_t)*g'(v_t)+factor2*g"(v_t)*g"(w_t)
 * w_{t+1}=factor2*g(w_t)*g(u_t)+factor2*g'(w_t)*g'(v_t)+factor2*g"(w_t)*g"(w_t)
 *
 *
 *  g(data) is a stencil operation that looks like the following:
 *  g(data[z][y][x]) = factor1*
 *        (data[z][y][x-2]+7*(data[z][y][x-1]+data[z][y][x])+data[z][y][x+1])
 *  similarly for g' and g"
 *  g'(data[z][y][x]) = factor1*
 *        (data[z][y-2][x]+7*(data[z][y-1][x]+data[z][y][x])+data[z][y+1][x])
 *  g"(data[z][y][x]) = factor1*
 *        (data[z-2][y][x]+7*(data[z-1][y][x]+data[z][y][x])+data[z+1][y][x])
 *
 *
 *  Step 1 is to calculate all of the g() values
 *  Step 2 multiplies the values together for the first column in the
 *         equations above
 *  Step 3 Return to Step 1 for g' and then for g"
 ****************************************************************************/
   loopTime.start();
   forall b in boxDomain {
     var FluxCache: [0..nComp-1,0..nCell,0..nCell,0..nCell] real;

     // x-direction
     // compute the flux on the faces in each direction
     for z in 0..nCell-1{
     for y in 0..nCell-1{
     for x in 0..nCell{
       FluxCache[0,z,y,x] = factor1 *  (old_data[b,0,z,y,x-2] +
                                       7*(old_data[b,0,z,y,x-1] +
                                         old_data[b,0,z,y,x]) +
                                         old_data[b,0,z,y,x+1]);
       FluxCache[1,z,y,x] = factor1 *  (old_data[b,1,z,y,x-2] +
                                       7*(old_data[b,1,z,y,x-1] +
                                         old_data[b,1,z,y,x]) +
                                         old_data[b,1,z,y,x+1]);
       FluxCache[2,z,y,x] = factor1 *  (old_data[b,2,z,y,x-2] +
                                       7*(old_data[b,2,z,y,x-1] +
                                         old_data[b,2,z,y,x]) +
                                         old_data[b,2,z,y,x+1]);
       FluxCache[3,z,y,x] = factor1 *  (old_data[b,3,z,y,x-2] +
                                       7*(old_data[b,3,z,y,x-1] +
                                         old_data[b,3,z,y,x]) +
                                         old_data[b,3,z,y,x+1]);
       FluxCache[4,z,y,x] = factor1 *  (old_data[b,4,z,y,x-2] +
                                       7*(old_data[b,4,z,y,x-1] +
                                         old_data[b,4,z,y,x]) +
                                         old_data[b,4,z,y,x+1]);
       FluxCache[0,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[1,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[3,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[4,z,y,x] *= factor2*FluxCache[2,z,y,x];
       FluxCache[2,z,y,x] *= factor2*FluxCache[2,z,y,x];
     }}}

     // accumulate the difference into the new data cells
    for z in 0..nCell-1{
    for y in 0..nCell-1{
    for x in 0..nCell-1{
      for c in compDomain{
        new_data[b,c,z,y,x] += FluxCache[c,z,y,x+1] - FluxCache[c,z,y,x];
      }
    }}}

     // compute the flux on the faces in each direction
     for z in 0..nCell-1{
     for y in 0..nCell{
     for x in 0..nCell-1{
       FluxCache[0,z,y,x] = factor1*  (old_data[b,0,z,y-2,x] +
                                         7*(old_data[b,0,z,y-1,x] +
                                           old_data[b,0,z,y,x]) +
                                         old_data[b,0,z,y+1,x]);
       FluxCache[1,z,y,x] = factor1*  (old_data[b,0,z,y-2,x] +
                                         7*(old_data[b,1,z,y-1,x] +
                                           old_data[b,1,z,y,x]) +
                                         old_data[b,1,z,y+1,x]);
       FluxCache[2,z,y,x] = factor1*  (old_data[b,2,z,y-2,x] +
                                         7*(old_data[b,2,z,y-1,x] +
                                           old_data[b,2,z,y,x]) +
                                         old_data[b,2,z,y+1,x]);
       FluxCache[3,z,y,x] = factor1*  (old_data[b,3,z,y-2,x] +
                                         7*(old_data[b,3,z,y-1,x] +
                                           old_data[b,3,z,y,x]) +
                                         old_data[b,3,z,y+1,x]);
       FluxCache[4,z,y,x] = factor1*  (old_data[b,4,z,y-2,x] +
                                         7*(old_data[b,4,z,y-1,x] +
                                           old_data[b,4,z,y,x]) +
                                         old_data[b,4,z,y+1,x]);
       FluxCache[0,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[1,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[2,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[4,z,y,x] *= factor2*FluxCache[3,z,y,x];
       FluxCache[3,z,y,x] *= factor2*FluxCache[3,z,y,x];
     }}}

     // accumulate the difference into the new data cells
     for z in 0..nCell-1{
     for y in 0..nCell-1{
     for x in 0..nCell-1{
      for c in compDomain{
        new_data[b,c,z,y,x] += FluxCache[c,z,y+1,x] - FluxCache[c,z,y,x];
      }
     }}}

    // z-direction
     for z in 0..nCell{
     for y in 0..nCell-1{
     for x in 0..nCell-1{
      FluxCache[0,z,y,x] = factor1*  (old_data[b,0,z-2,y,x] +
                                    7*(old_data[b,0,z-1,y,x] +
                                       old_data[b,0,z,y,x]) +
                                       old_data[b,0,z+1,y,x]);
      FluxCache[1,z,y,x] = factor1*  (old_data[b,1,z-2,y,x] +
                                    7*(old_data[b,1,z-1,y,x] +
                                       old_data[b,1,z,y,x]) +
                                       old_data[b,1,z+1,y,x]);
      FluxCache[2,z,y,x] = factor1*  (old_data[b,2,z-2,y,x] +
                                    7*(old_data[b,2,z-1,y,x] +
                                       old_data[b,2,z,y,x]) +
                                       old_data[b,2,z+1,y,x]);
      FluxCache[3,z,y,x] = factor1*  (old_data[b,3,z-2,y,x] +
                                    7*(old_data[b,3,z-1,y,x] +
                                       old_data[b,3,z,y,x]) +
                                       old_data[b,3,z+1,y,x]);
      FluxCache[4,z,y,x] = factor1*  (old_data[b,4,z-2,y,x] +
                                    7*(old_data[b,4,z-1,y,x] +
                                       old_data[b,4,z,y,x]) +
                                       old_data[b,4,z+1,y,x]);
       FluxCache[0,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[1,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[2,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[3,z,y,x] *= factor2*FluxCache[4,z,y,x];
       FluxCache[4,z,y,x] *= factor2*FluxCache[4,z,y,x];
    }}}

     // accumulate the difference into the new data cells
    for z in 0..nCell-1{
    for y in 0..nCell-1{
    for x in 0..nCell-1{
      for c in compDomain{
        new_data[b,c,z,y,x] += FluxCache[c,z+1,y,x] - FluxCache[c,z,y,x];
      }
    }}}
  }
  loopTime.stop();

  if(test){
    writeln("Resulting Data");
    for (z,y,x) in cellData{
      if(x%4==0){
        writeln();
      }
      write(new_data[0,0,z,y,x],",");
    }
    writeln();
  }else{
    writeln("LoopTime: ",loopTime.elapsed());
  }

 }
}


Added a few comments in norm example.

Added a few comments in norm example.

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

module Norm{
  // Module for computing matrix and vector norms:
  // 1-norm, 2-norm and infinity norm for vectors
  // 1-norm, infinity norm and Frobenius norm for matrices

  enum normType {norm1, norm2, normInf, normFrob};

  def norm(x: [], p: normType) where x.rank == 1 {
  // vector norms
    select (p) {
      when norm1 do return + reduce abs(x);
      when norm2 do return sqrt(+ reduce (abs(x)*abs(x)));
      when normInf do return max reduce abs(x);
      when normFrob do halt("Frobenius norm not defined for 1D arrays");
      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x: [?D], p: normType) where x.rank == 2 {
  // matrix norms
    select (p) {
      when norm1 do
        return max reduce [j in D(2)] (+ reduce abs(x[D(1), j]));

      when norm2 do
        halt("Haven't implemented 2-norm for 2D arrays yet");

      when normInf do
        return max reduce [i in D(1)] (+ reduce abs(x[i, D(2)]));

      when normFrob do return sqrt(+ reduce (abs(x)*abs(x)));

      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x: [], p: normType) where x.rank > 2 {
  // norms not implemented for > 2D arrays
    compilerError("Norms not implemented for array ranks > 2D");
  }

  def norm(x: []) {
  //  default norms
    select (x.rank) {
      when 1 do return norm(x, norm2);
      when 2 do return norm(x, normFrob);
      otherwise compilerError("Norms not implemented for array ranks > 2D");
    }
  }
}

module TestNorm {
  use Norm;

  def testNorm(arr: []) {
    var testType = if (arr.rank == 1) then "vector" else "matrix";
    writeln("Test of ", testType, " norms.  Array = ");
    writeln(arr);
    writeln("1-norm = ", norm(arr, norm1));
    if (arr.rank == 1) then
      writeln("2-norm = " , norm(arr, norm2));
    writeln("infinity norm = ", norm(arr, normInf));
    if (arr.rank == 2) then
      writeln("frobenius norm = ", norm(arr, normFrob));
    writeln("default norm = ", norm(arr));
    writeln();
  }

  def main() {
    const D1 = [1..4];
    var a:[D1] real;
    a = 2.0;
    testNorm(a);

    const D2 = [1..2,1..2];
    var b:[D2] real;
    b = 2.0;
    testNorm(b);

    const D3 = [1..3,1..2];
    var c:[D3] real;
    c = 2.0;
    testNorm(c);

    var d:[D2] int;
    d = 1;
    testNorm(d);

    var e:[D2] complex;
    e = 1.0 + 1.0i;
    testNorm(e);
  }
}

module Norm{
  enum normType {norm1, norm2, normInf, normFrob};

  def norm(x: [], p: normType) where x.rank == 1 {
    select (p) {
      when norm1 do return + reduce abs(x);
      when norm2 do return sqrt(+ reduce (abs(x)*abs(x)));
      when normInf do return max reduce abs(x);
      when normFrob do halt("Frobenius norm not defined for 1D arrays");
      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x: [?D], p: normType) where x.rank == 2 {
    select (p) {
      when norm1 do
        return max reduce [j in D(2)] (+ reduce abs(x[D(1), j]));

      when norm2 do
        halt("Haven't implemented 2-norm for 2D arrays yet");

      when normInf do
        return max reduce [i in D(1)] (+ reduce abs(x[i, D(2)]));

      when normFrob do return sqrt(+ reduce (abs(x)*abs(x)));

      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x: [], p: normType) where x.rank > 2 {
    compilerError("Norms not implemented for array ranks > 2D");
  }

  def norm(x: []) {
    select (x.rank) {
      when 1 do return norm(x, norm2);
      when 2 do return norm(x, normFrob);
      otherwise compilerError("Norms not implemented for array ranks > 2D");
    }
  }
}

module TestNorm {
  use Norm;

  def testNorm(arr: []) {
    var testType = if (arr.rank == 1) then "vector" else "matrix";
    writeln("Test of ", testType, " norms.  Array = ");
    writeln(arr);
    writeln("1-norm = ", norm(arr, norm1));
    if (arr.rank == 1) then
      writeln("2-norm = " , norm(arr, norm2));
    writeln("infinity norm = ", norm(arr, normInf));
    if (arr.rank == 2) then
      writeln("frobenius norm = ", norm(arr, normFrob));
    writeln("default norm = ", norm(arr));
    writeln();
  }

  def main() {
    const D1 = [1..4];
    var a:[D1] real;
    a = 2.0;
    testNorm(a);

    const D2 = [1..2,1..2];
    var b:[D2] real;
    b = 2.0;
    testNorm(b);

    const D3 = [1..3,1..2];
    var c:[D3] real;
    c = 2.0;
    testNorm(c);

    var d:[D2] int;
    d = 1;
    testNorm(d);

    var e:[D2] complex;
    e = 1.0 + 1.0i;
    testNorm(e);

    var f: [1..2, 1..3, 1..4] real = 0.0;
    //    norm(f, norm1);
    //    norm(f);
  }
}


Converted format() to writef()

Converted format() to writef()

// NAS FT - Initial port to Chapel based on ZPL

use BitOps, Time;

config const
  verbose         = true,
  timers_enabled  = false,
  timers_disabled = false;

const
  arand = 1220703125.0,
  seed  = 314159265.0,
  r23   = 0.5**23,
  t23   = 2.0**23,
  r46   = 0.5**46,
  t46   = 2.0**46;

proc nextrandlc(x : real, a : real) {
  var t1 = r23 * a;
  var a1 = floor(t1);
  var a2 = a - t23 * a1;
  t1 = r23 * x;
  var x1 = floor(t1);
  var x2 = x - t23 * x1;
  t1 = a1 * x2 + a2 * x1;
  var t2 = floor(r23 * t1);
  var z  = t1 - t23 * t2;
  var t3 = t23 * z + a2 * x2;
  var t4 = floor(r46 * t3);
  var x3 = t3 - t46 * t4;
  return (x3, r46 * x3);
}

proc initrandlc(seed, a : real, in n : int) : real {
  var i : int, t : real, g : real;
  var x : real = seed;
  t = a;
  while n != 0 do {
    i = n / 2;
    if 2 * i != n then
      (x, g) = nextrandlc(x, t);
    (t, g) = nextrandlc(t, t);
    n = i;
  }
  return x;
}

var randlc_last_n : int = -2,
    randlc_last_x : real;
proc randlc(n : int) : real {
  var result : real;
  if n != randlc_last_n + 1 then
    randlc_last_x = initrandlc(seed, arand, n);
  randlc_last_n = n;
  (randlc_last_x, result) = nextrandlc(randlc_last_x, arand);
  return result;
}

enum classes {S = 1, W, A, B, C, D};

const Class: domain(classes);

/* Values associate with the problem class type in order specified
   by classes */
const class_nx: [Class] int = (64, 32, 128, 256, 512, 1024);
var class_ny: [Class] int = (64, 128, 256, 256, 512, 1024);
var class_nz: [Class] int  = (64, 128, 256, 512, 512, 2048);
var class_niter: [Class] int = (6, 6, 6, 20, 20, 25);

/* More readable format
   ('S',   64,   64,   64,  6),
   ('W',   32,  128,  128,  6),
   ('A',  128,  256,  256,  6),
   ('B',  256,  256,  512, 20),
   ('C',  512,  512,  512, 20),
   ('D', 1024, 1024, 2048, 25)
*/

config const problem_class = classes.S;

config var
  nx : int = class_nx[problem_class],
  ny : int = class_ny[problem_class],
  nz : int = class_nz[problem_class],
  niter : int = class_niter[problem_class];

const
  alpha = 1.0e-6,
  pi = 3.141592653589793238,
  epsilon = 1.0e-12;

var
  DXYZ : domain(3) = {0..nx-1, 0..ny-1, 0..nz-1},
  U0 : [DXYZ] complex,
  U1 : [DXYZ] complex,
  U2 : [DXYZ] complex,
  Twiddle : [DXYZ] real;

writeln(" NAS Parallel Benchmarks 2.4  -- FT Benchmark");
writef(" Size       : %15i\n", nx*ny*nz);
writef(" Iterations : %15i\n", niter);

proc compute_initial_conditions(X1) {
  for (i,j,k) in DXYZ { // serial to ensure proper repeatable results
    X1(i,j,k).re = randlc(((i*ny+j)*nz+k)*2);
    X1(i,j,k).im = randlc(((i*ny+j)*nz+k)*2+1);
  }
}

proc compute_index_map(Twiddle) {
  const ap = -4.0 * alpha * pi * pi;
  forall (i,j,k) in DXYZ do
    Twiddle(i,j,k) = exp(ap*(((i+nx/2) % nx - nx/2)**2 +
                             ((j+ny/2) % ny - ny/2)**2 +
                             ((k+nz/2) % nz - nz/2)**2));
}

var fftblock : int = 16, fftblockpad : int = 18;

var u : [0..nz-1] complex;

proc fft_init() {
  var t : real, ti : real;
  var m = popcount((nz-1):uint);
  var ku = 2;
  var ln = 1;
  u(0) = m+0i;
  for j in 1..m {
    for i in 0..ln-1 {
      u(i+ku-1).re = cos(i*pi/ln);
      u(i+ku-1).im = sin(i*pi/ln);
    }
    ku += ln;
    ln *= 2;
  }
}

proc fftz2(dir, l, m, n, ny, ny1 : int, u, x, y) {
  var lk = 1<<(l-1), li = 1<<(m-l), lj = 2*lk;
  for i in 0..li-1 {
    var i11 = i * lk, i12 = i11 + n/2, i21 = i * lj, i22 = i21 + lk;
    var u1 = if dir == 1 then u(li+i) else conjg(u(li+i));
    for k in 0..lk-1 {
      for j in 0..ny-1 {
        var x11 = x(i11+k, j);
        var x21 = x(i12+k, j);
        y(i21+k, j) = x11+x21;
        y(i22+k, j) = u1*(x11-x21);
      }
    }
  }
}

proc cfftz(dir, m, n, ny, ny1 : int, x, y) {
  for l in 1..m by 2 {
    fftz2(dir, l, m, n, ny, ny1, u, x, y);
    if l != m then
      fftz2(dir, l+1, m, n, ny, ny1, u, y, x);
    else
      forall ij in {0..n-1, 0..ny-1} do
        x(ij) = y(ij);
  }
}

proc cffts1(dir, n, X1, X2, ny, ny1, x, y) {
  for j in DXYZ.dim(2) {
    for kk in DXYZ.dim(3) by ny {
      [(i1,_,i3) in {0..n-1,j..j,kk..kk+ny-1}] x(i1,i3-kk) = X1(i1,j,i3);
      cfftz(dir, popcount((n-1):uint), n, ny, ny1, x, y);
      [(i1,_,i3) in {0..n-1,j..j,kk..kk+ny-1}] X2(i1,j,i3) = x(i1,i3-kk);
    }
  }
}

proc cffts2(dir, n, X1, X2, ny, ny1, x, y) {
  for i in DXYZ.dim(1) {
    for kk in DXYZ.dim(3) by ny {
      [(_,i2,i3) in {i..i,0..n-1,kk..kk+ny-1}] x(i2,i3-kk) = X1(i,i2,i3);
      cfftz(dir, popcount((n-1):uint), n, ny, ny1, x, y);
      [(_,i2,i3) in {i..i,0..n-1,kk..kk+ny-1}] X2(i,i2,i3) = x(i2,i3-kk);
    }
  }
}

proc cffts3(dir, n, X1, X2, ny, ny1, x, y) {
  for i in DXYZ.dim(1) {
    for jj in DXYZ.dim(2) by ny {
      [(_,i2,i3) in {i..i,jj..jj+ny-1,0..n-1}] x(i3,i2-jj) = X1(i,i2,i3);
      cfftz(dir, popcount((n-1):uint), n, ny, ny1, x, y);
      [(_,i2,i3) in {i..i,jj..jj+ny-1,0..n-1}] X2(i,i2,i3) = x(i3,i2-jj);
    }
  }
}

proc fft(dir : int, X1, X2) {
  var x : [0..nz-1, 0..fftblockpad-1] complex;
  var y : [0..nz-1, 0..fftblockpad-1] complex;
  if dir == 1 {
    cffts3(dir, nz, X1, X1, fftblock, fftblockpad, x, y);
    cffts2(dir, ny, X1, X1, fftblock, fftblockpad, x, y);
    cffts1(dir, nx, X1, X2, fftblock, fftblockpad, x, y);
  } else {
    cffts1(dir, nx, X1, X1, fftblock, fftblockpad, x, y);
    cffts2(dir, ny, X1, X1, fftblock, fftblockpad, x, y);
    cffts3(dir, nz, X1, X2, fftblock, fftblockpad, x, y);
  }
}

proc evolve(X1, X2, Twiddle) {
  forall ijk in DXYZ {
    X1(ijk) *= (Twiddle(ijk) + 0i);
    X2(ijk) = X1(ijk);
  }
}

var sums : [1..niter] complex; // verification checksums

const
  vdata_s : [1..6] complex =
     ( 5.546087004964e+02+4.845363331978e+02i,
       5.546385409189e+02+4.865304269511e+02i,
       5.546148406171e+02+4.883910722336e+02i,
       5.545423607415e+02+4.901273169046e+02i,
       5.544255039624e+02+4.917475857993e+02i,
       5.542683411902e+02+4.932597244941e+02i ),

  vdata_w : [1..6] complex =
     ( 5.673612178944e+02+5.293246849175e+02i,
       5.631436885271e+02+5.282149986629e+02i,
       5.594024089970e+02+5.270996558037e+02i,
       5.560698047020e+02+5.260027904925e+02i,
       5.530898991250e+02+5.249400845633e+02i,
       5.504159734538e+02+5.239212247086e+02i ),

  vdata_a : [1..6] complex =
     ( 5.046735008193e+02+5.114047905510e+02i,
       5.059412319734e+02+5.098809666433e+02i,
       5.069376896287e+02+5.098144042213e+02i,
       5.077892868474e+02+5.101336130759e+02i,
       5.085233095391e+02+5.104914655194e+02i,
       5.091487099959e+02+5.107917842803e+02i ),

  vdata_b : [1..20] complex =
     ( 5.177643571579e+02+5.077803458597e+02i,
       5.154521291263e+02+5.088249431599e+02i,
       5.146409228649e+02+5.096208912659e+02i,
       5.142378756213e+02+5.101023387619e+02i,
       5.139626667737e+02+5.103976610617e+02i,
       5.137423460082e+02+5.105948019802e+02i,
       5.135547056878e+02+5.107404165783e+02i,
       5.133910925466e+02+5.108576573661e+02i,
       5.132470705390e+02+5.109577278523e+02i,
       5.131197729984e+02+5.110460304483e+02i,
       5.130070319283e+02+5.111252433800e+02i,
       5.129070537032e+02+5.111968077718e+02i,
       5.128182883502e+02+5.112616233064e+02i,
       5.127393733383e+02+5.113203605551e+02i,
       5.126691062020e+02+5.113735928093e+02i,
       5.126064276004e+02+5.114218460548e+02i,
       5.125504076570e+02+5.114656139760e+02i,
       5.125002331720e+02+5.115053595966e+02i,
       5.124551951846e+02+5.115415130407e+02i,
       5.124146770029e+02+5.115744692211e+02i ),

  vdata_c : [1..20] complex =
     ( 5.195078707457e+02+5.149019699238e+02i,
       5.155422171134e+02+5.127578201997e+02i,
       5.144678022222e+02+5.122251847514e+02i,
       5.140150594328e+02+5.121090289018e+02i,
       5.137550426810e+02+5.121143685824e+02i,
       5.135811056728e+02+5.121496764568e+02i,
       5.134569343165e+02+5.121870921893e+02i,
       5.133651975661e+02+5.122193250322e+02i,
       5.132955192805e+02+5.122454735794e+02i,
       5.132410471738e+02+5.122663649603e+02i,
       5.131971141679e+02+5.122830879827e+02i,
       5.131605205716e+02+5.122965869718e+02i,
       5.131290734194e+02+5.123075927445e+02i,
       5.131012720314e+02+5.123166486553e+02i,
       5.130760908195e+02+5.123241541685e+02i,
       5.130528295923e+02+5.123304037599e+02i,
       5.130310107773e+02+5.123356167976e+02i,
       5.130103090133e+02+5.123399592211e+02i,
       5.129905029333e+02+5.123435588985e+02i,
       5.129714421109e+02+5.123465164008e+02i ),

  vdata_d : [1..25] complex =
     ( 5.122230065252e+02+5.118534037109e+02i,
       5.120463975765e+02+5.117061181082e+02i,
       5.119865766760e+02+5.117096364601e+02i,
       5.119518799488e+02+5.117373863950e+02i,
       5.119269088223e+02+5.117680347632e+02i,
       5.119082416858e+02+5.117967875532e+02i,
       5.118943814638e+02+5.118225281841e+02i,
       5.118842385057e+02+5.118451629348e+02i,
       5.118769435632e+02+5.118649119387e+02i,
       5.118718203448e+02+5.118820803844e+02i,
       5.118683569061e+02+5.118969781011e+02i,
       5.118661708593e+02+5.119098918835e+02i,
       5.118649768950e+02+5.119210777066e+02i,
       5.118645605626e+02+5.119307604484e+02i,
       5.118647586618e+02+5.119391362671e+02i,
       5.118654451572e+02+5.119463757241e+02i,
       5.118665212451e+02+5.119526269238e+02i,
       5.118679083821e+02+5.119580184108e+02i,
       5.118695433664e+02+5.119626617538e+02i,
       5.118713748264e+02+5.119666538138e+02i,
       5.118733606701e+02+5.119700787219e+02i,
       5.118754661974e+02+5.119730095953e+02i,
       5.118776626738e+02+5.119755100241e+02i,
       5.118799262314e+02+5.119776353561e+02i,
       5.118822370068e+02+5.119794338060e+02i );

proc verify() {
  var rerr : real, ierr : real;
  for iterNo in 1..niter {
    select problem_class {
      when classes.S {
        rerr = (sums(iterNo).re - vdata_s(iterNo).re) / vdata_s(iterNo).re;
        ierr = (sums(iterNo).im - vdata_s(iterNo).im) / vdata_s(iterNo).im;
      }
      when classes.W {
        rerr = (sums(iterNo).re - vdata_w(iterNo).re) / vdata_w(iterNo).re;
        ierr = (sums(iterNo).im - vdata_w(iterNo).im) / vdata_w(iterNo).im;
      }
      when classes.A {
        rerr = (sums(iterNo).re - vdata_a(iterNo).re) / vdata_a(iterNo).re;
        ierr = (sums(iterNo).im - vdata_a(iterNo).im) / vdata_a(iterNo).im;
      }
      when classes.B {
        rerr = (sums(iterNo).re - vdata_b(iterNo).re) / vdata_b(iterNo).re;
        ierr = (sums(iterNo).im - vdata_b(iterNo).im) / vdata_b(iterNo).im;
      }
      when classes.C {
        rerr = (sums(iterNo).re - vdata_c(iterNo).re) / vdata_c(iterNo).re;
        ierr = (sums(iterNo).im - vdata_c(iterNo).im) / vdata_c(iterNo).im;
      }
      when classes.D {
        rerr = (sums(iterNo).re - vdata_d(iterNo).re) / vdata_d(iterNo).re;
        ierr = (sums(iterNo).im - vdata_d(iterNo).im) / vdata_d(iterNo).im;
      }
    }
    if !(abs(rerr) <= epsilon && abs(ierr) <= epsilon) then
      return false;
  }
  return true;
}

proc checksum(i, X1) {
  var chk : complex;
  serial {
    // 'with' => no race - in 'serial'
    [j in 1..1024 with (ref chk)] chk += X1((5*j) % nx, (3*j) % ny, j % nz);
  }
  chk = chk / ((nx * ny * nz) + 0i);
  sums(i) = chk;
  if verbose then
    writeln("T = ", i, "    Checksum = ", sums(i).re, " ", sums(i).im);
}

//
// Run problem once to ensure all data is touched
//
compute_index_map(Twiddle);
compute_initial_conditions(U1);
fft_init();
fft(1, U1, U0);

//
// Restart benchmark
//
var totalTime: Timer;
totalTime.start();

compute_index_map(Twiddle);
compute_initial_conditions(U1);
fft_init();
fft(1, U1, U0);

for iterNo in 1..niter {
  evolve(U0, U1, Twiddle);
  fft(-1, U1, U2);
  checksum(iterNo, U2);
}

writeln();
var verified = verify();
if verified {
  writeln("Result verification successful.");
} else {
  writeln("Result verification failed.");
}

totalTime.stop(); // Do I want to stop here?
var tm = totalTime.elapsed();

writeln();
writeln(" FT Benchmark Completed.");
writeln(" Class           =                        ", problem_class);
writef(" Size            = %24i\n", nx*ny*nz);
writef(" Iterations      = %24i\n", niter);
if timers_enabled then
  writef(" Time in seconds = %24r\n", tm);
writeln(" Operation type  =           floating point");
writeln(" Verification    = ", if verified then
						   "              SUCCESSFUL" else
						   "                  FAILED");

// NAS FT - Initial port to Chapel based on ZPL

use BitOps, Time;

config const
  verbose         = true,
  timers_enabled  = false,
  timers_disabled = false;

const
  arand = 1220703125.0,
  seed  = 314159265.0,
  r23   = 0.5**23,
  t23   = 2.0**23,
  r46   = 0.5**46,
  t46   = 2.0**46;

proc nextrandlc(x : real, a : real) {
  var t1 = r23 * a;
  var a1 = floor(t1);
  var a2 = a - t23 * a1;
  t1 = r23 * x;
  var x1 = floor(t1);
  var x2 = x - t23 * x1;
  t1 = a1 * x2 + a2 * x1;
  var t2 = floor(r23 * t1);
  var z  = t1 - t23 * t2;
  var t3 = t23 * z + a2 * x2;
  var t4 = floor(r46 * t3);
  var x3 = t3 - t46 * t4;
  return (x3, r46 * x3);
}

proc initrandlc(seed, a : real, in n : int) : real {
  var i : int, t : real, g : real;
  var x : real = seed;
  t = a;
  while n != 0 do {
    i = n / 2;
    if 2 * i != n then
      (x, g) = nextrandlc(x, t);
    (t, g) = nextrandlc(t, t);
    n = i;
  }
  return x;
}

var randlc_last_n : int = -2,
    randlc_last_x : real;
proc randlc(n : int) : real {
  var result : real;
  if n != randlc_last_n + 1 then
    randlc_last_x = initrandlc(seed, arand, n);
  randlc_last_n = n;
  (randlc_last_x, result) = nextrandlc(randlc_last_x, arand);
  return result;
}

enum classes {S = 1, W, A, B, C, D};

const Class: domain(classes);

/* Values associate with the problem class type in order specified
   by classes */
const class_nx: [Class] int = (64, 32, 128, 256, 512, 1024);
var class_ny: [Class] int = (64, 128, 256, 256, 512, 1024);
var class_nz: [Class] int  = (64, 128, 256, 512, 512, 2048);
var class_niter: [Class] int = (6, 6, 6, 20, 20, 25);

/* More readable format
   ('S',   64,   64,   64,  6),
   ('W',   32,  128,  128,  6),
   ('A',  128,  256,  256,  6),
   ('B',  256,  256,  512, 20),
   ('C',  512,  512,  512, 20),
   ('D', 1024, 1024, 2048, 25)
*/

config const problem_class = classes.S;

config var
  nx : int = class_nx[problem_class],
  ny : int = class_ny[problem_class],
  nz : int = class_nz[problem_class],
  niter : int = class_niter[problem_class];

const
  alpha = 1.0e-6,
  pi = 3.141592653589793238,
  epsilon = 1.0e-12;

var
  DXYZ : domain(3) = {0..nx-1, 0..ny-1, 0..nz-1},
  U0 : [DXYZ] complex,
  U1 : [DXYZ] complex,
  U2 : [DXYZ] complex,
  Twiddle : [DXYZ] real;

writeln(" NAS Parallel Benchmarks 2.4  -- FT Benchmark");
writeln(" Size       : ", format("%15ld", nx*ny*nz));
writeln(" Iterations : ", format("%15d", niter));

proc compute_initial_conditions(X1) {
  for (i,j,k) in DXYZ { // serial to ensure proper repeatable results
    X1(i,j,k).re = randlc(((i*ny+j)*nz+k)*2);
    X1(i,j,k).im = randlc(((i*ny+j)*nz+k)*2+1);
  }
}

proc compute_index_map(Twiddle) {
  const ap = -4.0 * alpha * pi * pi;
  forall (i,j,k) in DXYZ do
    Twiddle(i,j,k) = exp(ap*(((i+nx/2) % nx - nx/2)**2 +
                             ((j+ny/2) % ny - ny/2)**2 +
                             ((k+nz/2) % nz - nz/2)**2));
}

var fftblock : int = 16, fftblockpad : int = 18;

var u : [0..nz-1] complex;

proc fft_init() {
  var t : real, ti : real;
  var m = popcount((nz-1):uint);
  var ku = 2;
  var ln = 1;
  u(0) = m+0i;
  for j in 1..m {
    for i in 0..ln-1 {
      u(i+ku-1).re = cos(i*pi/ln);
      u(i+ku-1).im = sin(i*pi/ln);
    }
    ku += ln;
    ln *= 2;
  }
}

proc fftz2(dir, l, m, n, ny, ny1 : int, u, x, y) {
  var lk = 1<<(l-1), li = 1<<(m-l), lj = 2*lk;
  for i in 0..li-1 {
    var i11 = i * lk, i12 = i11 + n/2, i21 = i * lj, i22 = i21 + lk;
    var u1 = if dir == 1 then u(li+i) else conjg(u(li+i));
    for k in 0..lk-1 {
      for j in 0..ny-1 {
        var x11 = x(i11+k, j);
        var x21 = x(i12+k, j);
        y(i21+k, j) = x11+x21;
        y(i22+k, j) = u1*(x11-x21);
      }
    }
  }
}

proc cfftz(dir, m, n, ny, ny1 : int, x, y) {
  for l in 1..m by 2 {
    fftz2(dir, l, m, n, ny, ny1, u, x, y);
    if l != m then
      fftz2(dir, l+1, m, n, ny, ny1, u, y, x);
    else
      forall ij in {0..n-1, 0..ny-1} do
        x(ij) = y(ij);
  }
}

proc cffts1(dir, n, X1, X2, ny, ny1, x, y) {
  for j in DXYZ.dim(2) {
    for kk in DXYZ.dim(3) by ny {
      [(i1,_,i3) in {0..n-1,j..j,kk..kk+ny-1}] x(i1,i3-kk) = X1(i1,j,i3);
      cfftz(dir, popcount((n-1):uint), n, ny, ny1, x, y);
      [(i1,_,i3) in {0..n-1,j..j,kk..kk+ny-1}] X2(i1,j,i3) = x(i1,i3-kk);
    }
  }
}

proc cffts2(dir, n, X1, X2, ny, ny1, x, y) {
  for i in DXYZ.dim(1) {
    for kk in DXYZ.dim(3) by ny {
      [(_,i2,i3) in {i..i,0..n-1,kk..kk+ny-1}] x(i2,i3-kk) = X1(i,i2,i3);
      cfftz(dir, popcount((n-1):uint), n, ny, ny1, x, y);
      [(_,i2,i3) in {i..i,0..n-1,kk..kk+ny-1}] X2(i,i2,i3) = x(i2,i3-kk);
    }
  }
}

proc cffts3(dir, n, X1, X2, ny, ny1, x, y) {
  for i in DXYZ.dim(1) {
    for jj in DXYZ.dim(2) by ny {
      [(_,i2,i3) in {i..i,jj..jj+ny-1,0..n-1}] x(i3,i2-jj) = X1(i,i2,i3);
      cfftz(dir, popcount((n-1):uint), n, ny, ny1, x, y);
      [(_,i2,i3) in {i..i,jj..jj+ny-1,0..n-1}] X2(i,i2,i3) = x(i3,i2-jj);
    }
  }
}

proc fft(dir : int, X1, X2) {
  var x : [0..nz-1, 0..fftblockpad-1] complex;
  var y : [0..nz-1, 0..fftblockpad-1] complex;
  if dir == 1 {
    cffts3(dir, nz, X1, X1, fftblock, fftblockpad, x, y);
    cffts2(dir, ny, X1, X1, fftblock, fftblockpad, x, y);
    cffts1(dir, nx, X1, X2, fftblock, fftblockpad, x, y);
  } else {
    cffts1(dir, nx, X1, X1, fftblock, fftblockpad, x, y);
    cffts2(dir, ny, X1, X1, fftblock, fftblockpad, x, y);
    cffts3(dir, nz, X1, X2, fftblock, fftblockpad, x, y);
  }
}

proc evolve(X1, X2, Twiddle) {
  forall ijk in DXYZ {
    X1(ijk) *= (Twiddle(ijk) + 0i);
    X2(ijk) = X1(ijk);
  }
}

var sums : [1..niter] complex; // verification checksums

const
  vdata_s : [1..6] complex =
     ( 5.546087004964e+02+4.845363331978e+02i,
       5.546385409189e+02+4.865304269511e+02i,
       5.546148406171e+02+4.883910722336e+02i,
       5.545423607415e+02+4.901273169046e+02i,
       5.544255039624e+02+4.917475857993e+02i,
       5.542683411902e+02+4.932597244941e+02i ),

  vdata_w : [1..6] complex =
     ( 5.673612178944e+02+5.293246849175e+02i,
       5.631436885271e+02+5.282149986629e+02i,
       5.594024089970e+02+5.270996558037e+02i,
       5.560698047020e+02+5.260027904925e+02i,
       5.530898991250e+02+5.249400845633e+02i,
       5.504159734538e+02+5.239212247086e+02i ),

  vdata_a : [1..6] complex =
     ( 5.046735008193e+02+5.114047905510e+02i,
       5.059412319734e+02+5.098809666433e+02i,
       5.069376896287e+02+5.098144042213e+02i,
       5.077892868474e+02+5.101336130759e+02i,
       5.085233095391e+02+5.104914655194e+02i,
       5.091487099959e+02+5.107917842803e+02i ),

  vdata_b : [1..20] complex =
     ( 5.177643571579e+02+5.077803458597e+02i,
       5.154521291263e+02+5.088249431599e+02i,
       5.146409228649e+02+5.096208912659e+02i,
       5.142378756213e+02+5.101023387619e+02i,
       5.139626667737e+02+5.103976610617e+02i,
       5.137423460082e+02+5.105948019802e+02i,
       5.135547056878e+02+5.107404165783e+02i,
       5.133910925466e+02+5.108576573661e+02i,
       5.132470705390e+02+5.109577278523e+02i,
       5.131197729984e+02+5.110460304483e+02i,
       5.130070319283e+02+5.111252433800e+02i,
       5.129070537032e+02+5.111968077718e+02i,
       5.128182883502e+02+5.112616233064e+02i,
       5.127393733383e+02+5.113203605551e+02i,
       5.126691062020e+02+5.113735928093e+02i,
       5.126064276004e+02+5.114218460548e+02i,
       5.125504076570e+02+5.114656139760e+02i,
       5.125002331720e+02+5.115053595966e+02i,
       5.124551951846e+02+5.115415130407e+02i,
       5.124146770029e+02+5.115744692211e+02i ),

  vdata_c : [1..20] complex =
     ( 5.195078707457e+02+5.149019699238e+02i,
       5.155422171134e+02+5.127578201997e+02i,
       5.144678022222e+02+5.122251847514e+02i,
       5.140150594328e+02+5.121090289018e+02i,
       5.137550426810e+02+5.121143685824e+02i,
       5.135811056728e+02+5.121496764568e+02i,
       5.134569343165e+02+5.121870921893e+02i,
       5.133651975661e+02+5.122193250322e+02i,
       5.132955192805e+02+5.122454735794e+02i,
       5.132410471738e+02+5.122663649603e+02i,
       5.131971141679e+02+5.122830879827e+02i,
       5.131605205716e+02+5.122965869718e+02i,
       5.131290734194e+02+5.123075927445e+02i,
       5.131012720314e+02+5.123166486553e+02i,
       5.130760908195e+02+5.123241541685e+02i,
       5.130528295923e+02+5.123304037599e+02i,
       5.130310107773e+02+5.123356167976e+02i,
       5.130103090133e+02+5.123399592211e+02i,
       5.129905029333e+02+5.123435588985e+02i,
       5.129714421109e+02+5.123465164008e+02i ),

  vdata_d : [1..25] complex =
     ( 5.122230065252e+02+5.118534037109e+02i,
       5.120463975765e+02+5.117061181082e+02i,
       5.119865766760e+02+5.117096364601e+02i,
       5.119518799488e+02+5.117373863950e+02i,
       5.119269088223e+02+5.117680347632e+02i,
       5.119082416858e+02+5.117967875532e+02i,
       5.118943814638e+02+5.118225281841e+02i,
       5.118842385057e+02+5.118451629348e+02i,
       5.118769435632e+02+5.118649119387e+02i,
       5.118718203448e+02+5.118820803844e+02i,
       5.118683569061e+02+5.118969781011e+02i,
       5.118661708593e+02+5.119098918835e+02i,
       5.118649768950e+02+5.119210777066e+02i,
       5.118645605626e+02+5.119307604484e+02i,
       5.118647586618e+02+5.119391362671e+02i,
       5.118654451572e+02+5.119463757241e+02i,
       5.118665212451e+02+5.119526269238e+02i,
       5.118679083821e+02+5.119580184108e+02i,
       5.118695433664e+02+5.119626617538e+02i,
       5.118713748264e+02+5.119666538138e+02i,
       5.118733606701e+02+5.119700787219e+02i,
       5.118754661974e+02+5.119730095953e+02i,
       5.118776626738e+02+5.119755100241e+02i,
       5.118799262314e+02+5.119776353561e+02i,
       5.118822370068e+02+5.119794338060e+02i );

proc verify() {
  var rerr : real, ierr : real;
  for iterNo in 1..niter {
    select problem_class {
      when classes.S {
        rerr = (sums(iterNo).re - vdata_s(iterNo).re) / vdata_s(iterNo).re;
        ierr = (sums(iterNo).im - vdata_s(iterNo).im) / vdata_s(iterNo).im;
      }
      when classes.W {
        rerr = (sums(iterNo).re - vdata_w(iterNo).re) / vdata_w(iterNo).re;
        ierr = (sums(iterNo).im - vdata_w(iterNo).im) / vdata_w(iterNo).im;
      }
      when classes.A {
        rerr = (sums(iterNo).re - vdata_a(iterNo).re) / vdata_a(iterNo).re;
        ierr = (sums(iterNo).im - vdata_a(iterNo).im) / vdata_a(iterNo).im;
      }
      when classes.B {
        rerr = (sums(iterNo).re - vdata_b(iterNo).re) / vdata_b(iterNo).re;
        ierr = (sums(iterNo).im - vdata_b(iterNo).im) / vdata_b(iterNo).im;
      }
      when classes.C {
        rerr = (sums(iterNo).re - vdata_c(iterNo).re) / vdata_c(iterNo).re;
        ierr = (sums(iterNo).im - vdata_c(iterNo).im) / vdata_c(iterNo).im;
      }
      when classes.D {
        rerr = (sums(iterNo).re - vdata_d(iterNo).re) / vdata_d(iterNo).re;
        ierr = (sums(iterNo).im - vdata_d(iterNo).im) / vdata_d(iterNo).im;
      }
    }
    if !(abs(rerr) <= epsilon && abs(ierr) <= epsilon) then
      return false;
  }
  return true;
}

proc checksum(i, X1) {
  var chk : complex;
  serial {
    // 'with' => no race - in 'serial'
    [j in 1..1024 with (ref chk)] chk += X1((5*j) % nx, (3*j) % ny, j % nz);
  }
  chk = chk / ((nx * ny * nz) + 0i);
  sums(i) = chk;
  if verbose then
    writeln("T = ", i, "    Checksum = ", sums(i).re, " ", sums(i).im);
}

//
// Run problem once to ensure all data is touched
//
compute_index_map(Twiddle);
compute_initial_conditions(U1);
fft_init();
fft(1, U1, U0);

//
// Restart benchmark
//
var totalTime: Timer;
totalTime.start();

compute_index_map(Twiddle);
compute_initial_conditions(U1);
fft_init();
fft(1, U1, U0);

for iterNo in 1..niter {
  evolve(U0, U1, Twiddle);
  fft(-1, U1, U2);
  checksum(iterNo, U2);
}

writeln();
var verified = verify();
if verified {
  writeln("Result verification successful.");
} else {
  writeln("Result verification failed.");
}

totalTime.stop(); // Do I want to stop here?
var tm = totalTime.elapsed();

writeln();
writeln(" FT Benchmark Completed.");
writeln(" Class           =                        ", problem_class);
writeln(" Size            = ", format("%24ld", nx*ny*nz));
writeln(" Iterations      = ", format("%24d", niter));
if timers_enabled then {
  writeln(" Time in seconds = ", format("%24g", tm));
 }
writeln(" Operation type  =           floating point");
writeln(" Verification    = ", if verified then
						   "              SUCCESSFUL" else
						   "                  FAILED");