Overloaded math on ranges for all combinations
of integer types between 32 and 64 bits.  This
is unfortunately done in a fairly brain-dead
way (lots of code cloning) due to reasons
captured in the rangeVsPromotion* and
arrayVsPromotion* futures.

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

//
// syntactic bridge code for bounded ranges
//
def _build_range(param bt: BoundedRangeType, low: int, high: int)
  return range(int, bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low: uint, high: uint)
  return range(uint, bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low: int(64), high: int(64))
  return range(int(64), bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low: uint(64), high: uint(64))
  return range(uint(64), bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low, high) {
  compilerError("range bounds are not integral");
}

//
// syntactic bridge code for unbounded ranges
//
def _build_range(param bt: BoundedRangeType, bound: int)
  return range(int, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint)
  return range(uint, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: int(64))
  return range(int(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint(64))
  return range(uint(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType)
  return range(int, bt, false);
def _build_range(param bt: BoundedRangeType, bound) {
  compilerError("range bound is not integral");
}

//
// range type implementation
//
//   paramterized by how it is bounded and by an integral element type
//
record range {
  type eltType = int;                            // element type
  param boundedType: BoundedRangeType = bounded; // bounded or not
  param stridable: bool = false;                 // range can be strided
  var _low : eltType = 1;                        // lower bound
  var _high : eltType = 0;                       // upper bound
  var _stride : int = 1;                         // integer stride of range
  var _promotionType : eltType;                  // enables promotion

  def low: eltType return _low;
  def high: eltType return _high;
  def stride: eltType return _stride; // should be :int ??

  def this(subrange: range(?eltType, ?boundedness, ?stridable)) {
    if (stride != 1 || _stride != 1) {
      halt("range slicing not supported for strided ranges yet");
    }
    if ((boundedType == bounded) && (_high < _low)) then
      return range(eltType, bounded, stridable:bool, _low:eltType, _high:eltType, _stride:eltType);
//    Why can't above return statement return this?  A function
//    resolution error results if return this is used, complaining
//    that there are multiple return types.
    if (boundedness == bounded) {
      if (subrange.low >= low && subrange.high <= high) {
        return range(eltType, bounded, stridable, subrange.low, subrange.high,
                     subrange.stride);
      } else {
        halt("range slice out of bounds: ", subrange);
      }
    } else if (boundedness == boundedLow) {
      return range(eltType, bounded, stridable, subrange.low, _high,
                   subrange.stride);
    } else if (boundedness == boundedHigh) {
      return range(eltType, bounded, stridable, _low, subrange.high,
                   subrange.stride);
    } else if (boundedness == boundedNone) {
      return range(eltType, bounded, stridable, _low, _high, subrange.stride);
    } else {
      compilerError("unexpected boundedness case in range.this()");
    }
  }

  def these() {
    if boundedType != bounded {
      if stridable {
        var i = if _stride > 0 then _low else _high;
        while true {
          yield i;
          i = i + _stride:eltType;
        }
      } else {
        var i = _low;
        while true {
          yield i;
          i = i + 1;
        }
      }
    } else {
      if stridable {
        var i = if _stride > 0 then _low else _high;
        while _low <= i && i <= _high {
          yield i;
          i = i + _stride:eltType;
        }
      } else {
        var i: eltType;
        for __primitive("c for loop", i, _low, _high, 1) {
          yield i;
        }
      }
    }
  }

  def length {
    if boundedType != bounded then
      compilerError("unable to determine length of unbounded range");
    return
      (if _stride > 0
        then (_high - _low + _stride:eltType) / _stride:eltType
        else (_low - _high + _stride:eltType) / _stride:eltType);
  }
}

def =(r1: range(stridable=?stridable), r2) {
  if !stridable 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;
}

def by(s : range, i : int) {
  if i == 0 then
    halt("illegal stride of 0");
  var as = range(s.eltType, s.boundedType, true, s._low, s._high, s._stride * i);
  if s._low > s._high then return as;

  if as._stride < 0 then
    as._low = as._low + (as._high - as._low) % (-as._stride):as.eltType;
  else
    as._high = as._high - (as._high - as._low) % (as._stride):as.eltType;
  if as.boundedType == boundedLow then
    if as._stride < 0 then
      halt("error: unbounded range has negative stride and lower bound");
  if as.boundedType == boundedHigh then
    if as._stride > 0 then
      halt("error: unbounded range has positive stride and upper bound");
  return as;
}

def _in(s : range, i : s.eltType) {
  if s.boundedType != bounded then
    compilerError("_in undefined on unbounded ranges");
  return i >= s._low && i <= s._high &&
    (i - s._low) % abs(s._stride):s.eltType == 0;
}

// really slow --- REWRITE
def _in(s1: range, s2: range) {
  if (s1.boundedType != bounded) | (s2.boundedType != bounded) then
    compilerError("_in undefined on unbounded ranges");
  for i in s2 do
    if !_in(s1, i) then
      return false;
  return true;
}

def range.writeThis(f: Writer) {
  if (boundedType == bounded) | (boundedType == boundedLow) then
    f.write(_low);
  f.write("..");
  if (boundedType == bounded) | (boundedType == boundedHigh) then
    f.write(_high);
  if _stride != 1 then
    f.write(" by ", _stride);
}

pragma "inline" def string.substring(s: range) {
  if s.boundedType != 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);
}

def range._translate(i : int) {
  if boundedType != bounded then
    compilerError("translate is not supported on unbounded ranges");
  return _low+i.._high+i by _stride;
}

def range._interior(i : int) {
  if boundedType != bounded then
    compilerError("interior is not supported on unbounded ranges");
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low.._low-1-i by _stride;
  } else if (i > 0) {
    x = _high+1-i.._high by _stride;
  }
  return x;
}

def range._exterior(i : int) {
  if boundedType != bounded then
    compilerError("exterior is not supported on unbounded ranges");
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low+i.._low-1 by _stride;
  } else if (i > 0) {
    x = _high+1.._high+i by _stride;
  }
  return x;
}

def range._expand(i : int) {
  if boundedType != bounded then
    compilerError("expand is not supported on unbounded ranges");
  return _low-i.._high+i by _stride;
}

def _intersect(a: range, b: range) {
  if (a.boundedType != bounded) | (b.boundedType != bounded) then
    compilerError("insersection not defined on unbounded ranges");
  var g, x: int;
  (g, x) = _extended_euclid(a._stride, b._stride);
  var gg = g:a.eltType;
  var xx = x:a.eltType;
  var as = (a._stride):a.eltType;
  if abs(a._low - b._low) % gg != 0 then
    return 1..0:a.eltType by 1;
  var low = max(a._low, b._low);
  var high = min(a._high, b._high);
  var stride = a._stride * b._stride / g;
  var alignment = a._low + (b._low - a._low) * xx * as / gg;
  if alignment == 0 then
    alignment = stride:a.eltType;
  low = low + low % alignment;
  return low..high by stride;
}

// Extended-Euclid (Knuth Volume 2 --- Section 4.5.2)
// given two non-negative integers u and v
// returns (gcd(u, v), x) where x is set such that u*x + v*y = gcd(u, v)
def _extended_euclid(u: int, v: int) {
  var u1 = 1;
  var u2 = 0;
  var u3 = u;
  var v1 = 0;
  var v2 = 1;
  var v3 = v;
  while v3 != 0 {
    var q = u3 / v3;
    var t1 = u1 - v1 * q;
    var t2 = u2 - v2 * q;
    var t3 = u3 - v3 * q;
    u1 = v1;
    u2 = v2;
    u3 = v3;
    v1 = t1;
    v2 = t2;
    v3 = t3;
  }
  return (u3, u1);
}

def _build_open_interval_upper(r: range) {
  return range(r.eltType, r.boundedType, r.stridable, r._low, r._high-1);
}

def =(a: _domain, b: range) {
  const bdom = _build_domain(b);
  a = bdom;
  return a;
}

/* Would like to define these as follows:

def +(r: range, s: integral) {
  return range((r._low+s).type, r.boundedType, r.stridable, r._low+s, r._high+s, r._stride);
}

but because we don't distinguish "better" functions between promotion
and generics, it doesn't work -- see the arrayVsPromotion and
rangeVsPromotion futures for examples of this.

Alternatively, we might like to do:

def +(r: range, s: r.eltType) {
  return range((r._low+s).type, r.boundedType, r.stridable, r._low+s, r._high+s, r._stride);
}

but this doesn't allow an int(32) range to be added to an int(64)
integer, which seems useful/important/congruous to integer addition.
So, I'm cloning manually. */

def +(r: range, s: int) {
  return range((r._low+s).type, r.boundedType, r.stridable, r._low+s, r._high+s, r._stride);
}

def -(r: range, s: int) {
  return range((r._low-s).type, r.boundedType, r.stridable, r._low-s, r._high-s, r._stride);
}

def *(r: range, s: int) {
  return range((r._low*s).type, r.boundedType, stridable=true, r._low*s, r._high*s, r._stride*s);
}

def /(r: range, s: int) {
  return range((r._low/s).type, r.boundedType, stridable=true, r._low/s, r._high/s, r._stride/s);
}

def +(s:int, r: range) {
  return range((s+r._low).type, r.boundedType, r.stridable, s+r._low, s+r._high, r._stride);
}

def -(s:int, r: range) {
  return range((s-r._low).type, r.boundedType, r.stridable, s-r._low, s-r._high, r._stride);
}

def *(s:int, r: range) {
  return range((s*r._low).type, r.boundedType, stridable=true, s*r._low, s*r._high, s*r._stride);
}

def /(s:int, r: range) {
  return range((s/r._low).type, r.boundedType, stridable=true, s/r._low, s/r._high, s/r._stride);
}

def +(r: range, s: uint) {
  return range((r._low+s).type, r.boundedType, r.stridable, r._low+s, r._high+s, r._stride);
}

def -(r: range, s: uint) {
  return range((r._low-s).type, r.boundedType, r.stridable, r._low-s, r._high-s, r._stride);
}

def *(r: range, s: uint) {
  return range((r._low*s).type, r.boundedType, stridable=true, r._low*s, r._high*s, r._stride*s);
}

def /(r: range, s: uint) {
  return range((r._low/s).type, r.boundedType, stridable=true, r._low/s, r._high/s, r._stride/s);
}

def +(s:uint, r: range) {
  return range((s+r._low).type, r.boundedType, r.stridable, s+r._low, s+r._high, r._stride);
}

def -(s:uint, r: range) {
  return range((s-r._low).type, r.boundedType, r.stridable, s-r._low, s-r._high, r._stride);
}

def *(s:uint, r: range) {
  return range((s*r._low).type, r.boundedType, stridable=true, s*r._low, s*r._high, s*r._stride);
}

def /(s:uint, r: range) {
  return range((s/r._low).type, r.boundedType, stridable=true, s/r._low, s/r._high, s/r._stride);
}

def +(r: range, s: int(64)) {
  return range((r._low+s).type, r.boundedType, r.stridable, r._low+s, r._high+s, r._stride);
}

def -(r: range, s: int(64)) {
  return range((r._low-s).type, r.boundedType, r.stridable, r._low-s, r._high-s, r._stride);
}

def *(r: range, s: int(64)) {
  return range((r._low*s).type, r.boundedType, stridable=true, r._low*s, r._high*s, r._stride*s);
}

def /(r: range, s: int(64)) {
  return range((r._low/s).type, r.boundedType, stridable=true, r._low/s, r._high/s, r._stride/s);
}

def +(s:int(64), r: range) {
  return range((s+r._low).type, r.boundedType, r.stridable, s+r._low, s+r._high, r._stride);
}

def -(s:int(64), r: range) {
  return range((s-r._low).type, r.boundedType, r.stridable, s-r._low, s-r._high, r._stride);
}

def *(s:int(64), r: range) {
  return range((s*r._low).type, r.boundedType, stridable=true, s*r._low, s*r._high, s*r._stride);
}

def /(s:int(64), r: range) {
  return range((s/r._low).type, r.boundedType, stridable=true, s/r._low, s/r._high, s/r._stride);
}

def +(r: range, s: uint(64)) {
  return range((r._low+s).type, r.boundedType, r.stridable, r._low+s, r._high+s, r._stride);
}

def -(r: range, s: uint(64)) {
  return range((r._low-s).type, r.boundedType, r.stridable, r._low-s, r._high-s, r._stride);
}

def *(r: range, s: uint(64)) {
  return range((r._low*s).type, r.boundedType, stridable=true, r._low*s, r._high*s, r._stride*s);
}

def /(r: range, s: uint(64)) {
  return range((r._low/s).type, r.boundedType, stridable=true, r._low/s, r._high/s, r._stride/s);
}

def +(s:uint(64), r: range) {
  return range((s+r._low).type, r.boundedType, r.stridable, s+r._low, s+r._high, r._stride);
}

def -(s:uint(64), r: range) {
  return range((s-r._low).type, r.boundedType, r.stridable, s-r._low, s-r._high, r._stride);
}

def *(s:uint(64), r: range) {
  return range((s*r._low).type, r.boundedType, stridable=true, s*r._low, s*r._high, s*r._stride);
}

def /(s:uint(64), r: range) {
  return range((s/r._low).type, r.boundedType, stridable=true, s/r._low, s/r._high, s/r._stride);
}

//
// syntactic bridge code for bounded ranges
//
def _build_range(param bt: BoundedRangeType, low: int, high: int)
  return range(int, bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low: uint, high: uint)
  return range(uint, bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low: int(64), high: int(64))
  return range(int(64), bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low: uint(64), high: uint(64))
  return range(uint(64), bt, false, low, high);
def _build_range(param bt: BoundedRangeType, low, high) {
  compilerError("range bounds are not integral");
}

//
// syntactic bridge code for unbounded ranges
//
def _build_range(param bt: BoundedRangeType, bound: int)
  return range(int, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint)
  return range(uint, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: int(64))
  return range(int(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint(64))
  return range(uint(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType)
  return range(int, bt, false);
def _build_range(param bt: BoundedRangeType, bound) {
  compilerError("range bound is not integral");
}

//
// range type implementation
//
//   paramterized by how it is bounded and by an integral element type
//
record range {
  type eltType = int;                            // element type
  param boundedType: BoundedRangeType = bounded; // bounded or not
  param stridable: bool = false;                 // range can be strided
  var _low : eltType = 1;                        // lower bound
  var _high : eltType = 0;                       // upper bound
  var _stride : int = 1;                         // integer stride of range
  var _promotionType : eltType;                  // enables promotion

  def low: eltType return _low;
  def high: eltType return _high;
  def stride: eltType return _stride; // should be :int ??

  def this(subrange: range(?eltType, ?boundedness, ?stridable)) {
    if (stride != 1 || _stride != 1) {
      halt("range slicing not supported for strided ranges yet");
    }
    if ((boundedType == bounded) && (_high < _low)) then
      return range(eltType, bounded, stridable, _low:eltType, _high:eltType, _stride:eltType);
//    Why can't above return statement return this?  A function
//    resolution error results if return this is used, complaining
//    that there are multiple return types.
    if (boundedness == bounded) {
      if (subrange.low >= low && subrange.high <= high) {
        return range(eltType, bounded, stridable, subrange.low, subrange.high,
                     subrange.stride);
      } else {
        halt("range slice out of bounds: ", subrange);
      }
    } else if (boundedness == boundedLow) {
      return range(eltType, bounded, stridable, subrange.low, _high,
                   subrange.stride);
    } else if (boundedness == boundedHigh) {
      return range(eltType, bounded, stridable, _low, subrange.high,
                   subrange.stride);
    } else if (boundedness == boundedNone) {
      return range(eltType, bounded, stridable, _low, _high, subrange.stride);
    } else {
      compilerError("unexpected boundedness case in range.this()");
    }
  }

  def these() {
    if boundedType != bounded {
      if stridable {
        var i = if _stride > 0 then _low else _high;
        while true {
          yield i;
          i = i + _stride:eltType;
        }
      } else {
        var i = _low;
        while true {
          yield i;
          i = i + 1;
        }
      }
    } else {
      if stridable {
        var i = if _stride > 0 then _low else _high;
        while _low <= i && i <= _high {
          yield i;
          i = i + _stride:eltType;
        }
      } else {
        var i: eltType;
        for __primitive("c for loop", i, _low, _high, 1) {
          yield i;
        }
      }
    }
  }

  def length {
    if boundedType != bounded then
      compilerError("unable to determine length of unbounded range");
    return
      (if _stride > 0
        then (_high - _low + _stride:eltType) / _stride:eltType
        else (_low - _high + _stride:eltType) / _stride:eltType);
  }
}

def =(r1: range(stridable=?stridable), r2) {
  if !stridable 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;
}

def by(s : range, i : int) {
  if i == 0 then
    halt("illegal stride of 0");
  var as = range(s.eltType, s.boundedType, true, s._low, s._high, s._stride * i);
  if s._low > s._high then return as;

  if as._stride < 0 then
    as._low = as._low + (as._high - as._low) % (-as._stride):as.eltType;
  else
    as._high = as._high - (as._high - as._low) % (as._stride):as.eltType;
  if as.boundedType == boundedLow then
    if as._stride < 0 then
      halt("error: unbounded range has negative stride and lower bound");
  if as.boundedType == boundedHigh then
    if as._stride > 0 then
      halt("error: unbounded range has positive stride and upper bound");
  return as;
}

def _in(s : range, i : s.eltType) {
  if s.boundedType != bounded then
    compilerError("_in undefined on unbounded ranges");
  return i >= s._low && i <= s._high &&
    (i - s._low) % abs(s._stride):s.eltType == 0;
}

// really slow --- REWRITE
def _in(s1: range, s2: range) {
  if (s1.boundedType != bounded) | (s2.boundedType != bounded) then
    compilerError("_in undefined on unbounded ranges");
  for i in s2 do
    if !_in(s1, i) then
      return false;
  return true;
}

def range.writeThis(f: Writer) {
  if (boundedType == bounded) | (boundedType == boundedLow) then
    f.write(_low);
  f.write("..");
  if (boundedType == bounded) | (boundedType == boundedHigh) then
    f.write(_high);
  if _stride != 1 then
    f.write(" by ", _stride);
}

pragma "inline" def string.substring(s: range) {
  if s.boundedType != 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);
}

def range._translate(i : int) {
  if boundedType != bounded then
    compilerError("translate is not supported on unbounded ranges");
  return _low+i.._high+i by _stride;
}

def range._interior(i : int) {
  if boundedType != bounded then
    compilerError("interior is not supported on unbounded ranges");
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low.._low-1-i by _stride;
  } else if (i > 0) {
    x = _high+1-i.._high by _stride;
  }
  return x;
}

def range._exterior(i : int) {
  if boundedType != bounded then
    compilerError("exterior is not supported on unbounded ranges");
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low+i.._low-1 by _stride;
  } else if (i > 0) {
    x = _high+1.._high+i by _stride;
  }
  return x;
}

def range._expand(i : int) {
  if boundedType != bounded then
    compilerError("expand is not supported on unbounded ranges");
  return _low-i.._high+i by _stride;
}

def _intersect(a: range, b: range) {
  if (a.boundedType != bounded) | (b.boundedType != bounded) then
    compilerError("insersection not defined on unbounded ranges");
  var g, x: int;
  (g, x) = _extended_euclid(a._stride, b._stride);
  var gg = g:a.eltType;
  var xx = x:a.eltType;
  var as = (a._stride):a.eltType;
  if abs(a._low - b._low) % gg != 0 then
    return 1..0:a.eltType by 1;
  var low = max(a._low, b._low);
  var high = min(a._high, b._high);
  var stride = a._stride * b._stride / g;
  var alignment = a._low + (b._low - a._low) * xx * as / gg;
  if alignment == 0 then
    alignment = stride:a.eltType;
  low = low + low % alignment;
  return low..high by stride;
}

// Extended-Euclid (Knuth Volume 2 --- Section 4.5.2)
// given two non-negative integers u and v
// returns (gcd(u, v), x) where x is set such that u*x + v*y = gcd(u, v)
def _extended_euclid(u: int, v: int) {
  var u1 = 1;
  var u2 = 0;
  var u3 = u;
  var v1 = 0;
  var v2 = 1;
  var v3 = v;
  while v3 != 0 {
    var q = u3 / v3;
    var t1 = u1 - v1 * q;
    var t2 = u2 - v2 * q;
    var t3 = u3 - v3 * q;
    u1 = v1;
    u2 = v2;
    u3 = v3;
    v1 = t1;
    v2 = t2;
    v3 = t3;
  }
  return (u3, u1);
}

def _build_open_interval_upper(r: range) {
  return range(r.eltType, r.boundedType, r.stridable, r._low, r._high-1);
}

def =(a: _domain, b: range) {
  const bdom = _build_domain(b);
  a = bdom;
  return a;
}

def +(r: range, s:int) {
  return range((r._low+s).type, r.boundedType, r.stridable, r._low+s, r._high+s, r._stride);
}

def -(r: range, s:int) {
  return range((r._low-s).type, r.boundedType, r.stridable, r._low-s, r._high-s, r._stride);
}

def *(r: range, s:int) {
  return range((r._low*s).type, r.boundedType, stridable=true, r._low*s, r._high*s, r._stride*s);
}

def /(r: range, s:int) {
  return range((r._low/s).type, r.boundedType, stridable=true, r._low/s, r._high/s, r._stride/s);
}

def +(s:int, r: range) {
  return range((s+r._low).type, r.boundedType, r.stridable, s+r._low, s+r._high, r._stride);
}

def -(s:int, r: range) {
  return range((s-r._low).type, r.boundedType, r.stridable, s-r._low, s-r._high, r._stride);
}

def *(s:int, r: range) {
  return range((s*r._low).type, r.boundedType, stridable=true, s*r._low, s*r._high, s*r._stride);
}

def /(s:int, r: range) {
  return range((s/r._low).type, r.boundedType, stridable=true, s/r._low, s/r._high, s/r._stride);
}


Fix a bug in BradsBlock1DPar

Fix a bug in BradsBlock1DPar

In the constructor for the local distribution class, an index into the array
of locales was used directly where a zero based version was needed. This
caused problems for distributions declared over non-zero based arrays of
locales.

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

// TODO: Have the global class leader/follower iterators defer to the
// local class leader/followers once we're within a locale?

// TODO: Make these into an official distribution?

// TODO: Would using nested classes allow me to avoid so much
// passing around of globIndexType and locIndexType?

config param debugBradsBlock1D = false;

// TODO: This would need to be moved somewhere more standard
enum IteratorType { leader, follower };

//
// The distribution class
//
class Block1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type glbIdxType = int(64);

  //
  // The bounding box that defines the block distribution
  //
  const boundingBox: domain(1, glbIdxType);

  //
  // a domain and array describing the set of target locales to which
  // the indices are mapped
  //
  const targetLocDom: domain(1);
  const targetLocs: [targetLocDom] locale;
  //
  // NOTE: originally I wrote this as:
  //
  //   const targetLocs = Locales;
  //   const targetLocDom = targetLocs.domain;
  //
  // but this had two interesting problems:
  //
  // (1) the capture of the targetLocs array only makes a copy of the
  //     array, not of its domain, so if someone were to resize the
  //     LocaleSpace domain outside of the class, this would inadvertantly
  //     get resized as well.  We really want to make a copy of both the
  //     domain and array (which I now do in the constructor).
  //
  // (2) Initializing targetLocs with Locales prevents us from assigning
  //     anything other than a numLocales-ary array of locales to it
  //     which is stricter than I wanted -- I only wanted to restrict
  //     us to a 1D array of locales.  In our current model, this would
  //     require us to have some sort of [domain(1)] locale declaration
  //     to make it work.  Or we could think about whether a model in
  //     which only the array's static type information is inferred
  //     for a field?

  //
  // an array of local distribution class descriptors -- set up in
  // initialize() below
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  // WANTED:
  //
  /*
  const locDist: [loc in targetLocDom] LocBlock1DDist(glbIdxType)
                   = on targetLocs(loc) do
                       new LocBlock1DDist(glbIdxType, targetLocDom.order(loc), this);
  */
  //
  // but this doesn't work yet because an array forall initializer
  // apparently can't refer to a local member domain.
  //
  const locDist: [targetLocDom] LocBlock1DDist(glbIdxType);
  //
  // WORKAROUND: Initialize in the constructor instead
  //

  def Block1DDist(type glbIdxType = int(64), bbox: domain(1, glbIdxType),
                  targetLocales: [?targetLocalesDomain] locale) {
    boundingBox = bbox;
    targetLocDom = targetLocalesDomain;
    targetLocs = targetLocales;
    //
    // WANT TO DO:
    /*
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(glbIdxType, locid, this);
    */
    //
    // BUT results in a _heapAlloc(type int(64)) call.
    // WORKAROUND:
    helpConstruct();
  }
  //
  // WORKAROUND CONTINUED:
  //
  def helpConstruct() {
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(glbIdxType, locid, this);
  }
  //
  // END WORKAROUND
  //

  def writeThis(x:Writer) {
    x.writeln("BradsBlock1DPar");
    x.writeln("---------------");
    x.writeln("distributes: ", boundingBox);
    x.writeln("across locales: ", targetLocs);
    x.writeln("indexed via: ", targetLocDom);
    x.writeln("resulting in: ");
    for locid in targetLocDom do
      x.writeln("  [", locid, "] ", locDist(locid));
  }

  //
  // Create a new domain over this distribution with the given index
  // set (inds) and the given global and local index type (idxType,
  // locIdxType)
  //
  def newDomain(inds, type idxType = glbIdxType, type locIdxType = idxType) {
    // Note that I'm fixing the global and local index types to be the
    // same, but making this a generic function would fix this
    return new Block1DDom(idxType, locIdxType, this, inds);
  }

  //
  // compute what chunk of inds is owned by a given locale -- assumes
  // it's being called on the locale in question
  //
  def getChunk(inds, locid) {
    // use domain slicing to get the intersection between what the
    // locale owns and the domain's index set

    // TODO: Should this be able to be written as myChunk[inds] ???
    return locDist(locid).myChunk(inds.dim(1));
  }

  //
  // Determine which locale owns a particular index
  //
  def ind2locInd(ind: glbIdxType) {
    const ind0 = ind - boundingBox.low;
    const loc0 = (ind0 * targetLocs.numElements) / boundingBox.numIndices;
    const locInd = loc0: index(targetLocs.domain) + targetLocs.domain.low;
    return locInd;
  }

  def ind2loc(ind: glbIdxType) {
    return targetLocs(ind2locInd(ind));
  }
}

//
// A per-locale local distribution class
//
class LocBlock1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type glbIdxType;

  //
  // This stores the piece of the global bounding box owned by
  // the locale.  Note that my original guess that we'd want
  // to use lclIdxType here is wrong since we're talking about
  // the section of the global index space owned by the locale.
  //
  const myChunk: domain(1, glbIdxType);
  const locid: int;
  const loc: locale;

  //
  // Compute what chunk of index(1) is owned by the current locale
  // Arguments:
  //
  def LocBlock1DDist(type glbIdxType,
                     _locid: int, // the locale index from the target domain
                     dist: Block1DDist(glbIdxType) // reference to glob dist
                     ) {
    locid = _locid;
    loc = dist.targetLocs(locid);
    const lo = dist.boundingBox.low;
    const hi = dist.boundingBox.high;
    const numelems = hi - lo + 1;
    const numlocs = dist.targetLocDom.numIndices;
    const locid0 = dist.targetLocDom.order(locid); // 0-based locale ID
    const blo = if (locid0 == 0) then min(glbIdxType)
                else procToData((numelems: real * locid0) / numlocs, lo);
    const bhi = if (locid0 == numlocs - 1) then max(glbIdxType)
                else procToData((numelems: real * (locid0+1)) / numlocs, lo) - 1;
    myChunk = [blo..bhi];
    if debugBradsBlock1D then
      writeln("locale ", locid, " owns ", myChunk);
  }

  //
  // a helper function for mapping processors to indices
  //
  def procToData(x, lo)
    return (lo + (x: lo.type) + (x:real != x:int:real));

  def writeThis(x:Writer) {
    x.write("locale ", loc.id, " owns chunk: ", myChunk);
  }
}

//
// The global domain class
//
class Block1DDom {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // a pointer to the parent distribution
  //
  const dist: Block1DDist(glbIdxType);

  //
  // a domain describing the complete domain
  //
  const whole: domain(1, glbIdxType);

  //
  // an array of local domain class descriptors -- set up in
  // initialize() below
  //
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locDoms: [dist.targetLocDom] LocBlock1DDom(glbIdxType, lclIdxType);

  def initialize() {
    for locid in dist.targetLocDom do
      on dist.targetLocs(locid) do
        locDoms(locid) = new LocBlock1DDom(glbIdxType, lclIdxType, this,
                                           dist.getChunk(whole, locid));
    if debugBradsBlock1D then
      [loc in dist.targetLocDom] writeln(loc, " owns ", locDoms(loc));
  }

  //
  // the iterator for the domain -- currently sequential
  //
  def these() {
    for blk in locDoms do
      // TODO: Would want to do something like:
      // on blk do
      // But can't currently have yields in on clauses
        for ind in blk do
          yield ind;
  }

  //
  // this is the parallel iterator for the global domain, following
  // a variation on Steve and David's proposals -- I've split the
  // single iterator into two iterators, distinguished by where
  // clauses on their parameter values.  This permits each to only
  // take the parameters it cares about; permits the leader to be
  // defined in an inlineable way; and permits the follower for an
  // array iterator to be a var iterator
  //
  // I've named these methods newThese() for the time being
  // to avoid conflicting with the above which is currently targeted
  // by the compiler.  My current assumption is that we would want to
  // overload these() to serve this purpose in the final language
  // definition.
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    //
    // TODO: This currently only results in a single level of
    // per-locale parallelism -- no per-core parallelism; maybe
    // approach this by deferring to the local leader/follower
    // iterators at some point?
    //
    // TODO: Really want the parallelism across the target locales
    // to be expressed more independently of the distribution by
    // pushing it into the leader iterator.  Need to get an on clause
    // into here somehow.  That is:
    //
    //   coforall locDom in locDoms do
    //     on locDom do
    //       yield locDom.myBlock - whole.low;
    //
    // TODO: And really want to split the inter- and intra-locale
    // parallelism into two separate stages for communication
    // optimization and the like
    //
    // TODO: Abstract this subtraction of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    for locDom in locDoms do
      yield locDom.myBlock - whole.low;
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
    //
    // TODO: Abstract this addition of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    // TODO: Is there some clever way to invoke the leader/follower
    // iterator on the local blocks in here such that the per-core
    // parallelism is expressed at that level?  Seems like a nice
    // natural composition and might help with my fears about how
    // stencil communication will be done on a per-locale basis.
    for i in followThis {
      yield i + whole.low;
    }
  }

  //
  // the print method for the domain
  //
  def writeThis(x:Writer) {
    x.write(whole);
  }

  //
  // how to allocate a new array over this domain
  //
  def newArray(type elemType) {
    return new Block1DArr(glbIdxType, lclIdxType, elemType, this);
  }

  //
  // queries for the number of indices, low, and high bounds
  //
  def numIndices {
    return whole.numIndices;
  }

  def low {
    return whole.low;
  }

  def high {
    return whole.high;
  }
}

//
// the local domain class
//
class LocBlock1DDom {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // a reference to the parent global domain class
  //
  const wholeDom: Block1DDom(glbIdxType, lclIdxType);

  //
  // a local domain describing the indices owned by this locale
  //
  var myBlock: domain(1, lclIdxType);

  //
  // iterator over this locale's indices
  //
  def these() {
    // May want to do something like:
    // on this do
    // But can't currently have yields in on clauses
    for ind in myBlock do
      yield ind;
  }

  //
  // this is the parallel iterator for the local domain, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // how to write out this locale's indices
  //
  def writeThis(x:Writer) {
    x.write(myBlock);
  }

  //
  // queries for this locale's number of indices, low, and high bounds
  //
  def numIndices {
    return myBlock.numIndices;
  }

  def low {
    return myBlock.low;
  }

  def high {
    return myBlock.high;
  }
}

//
// the global array class
//
class Block1DArr {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // the array's element type
  //
  type elemType;

  //
  // the global domain descriptor for this array
  //
  var dom: Block1DDom(glbIdxType, lclIdxType);

  //
  // an array of local array classes
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locArr: [dom.dist.targetLocDom] LocBlock1DArr(glbIdxType, lclIdxType, elemType);

  def initialize() {
    for locid in dom.dist.targetLocDom do
      on dom.dist.targetLocs(locid) do
        locArr(locid) = new LocBlock1DArr(glbIdxType, lclIdxType, elemType, dom.locDoms(locid));
  }

  //
  // the global accessor for the array
  //
  def this(i: glbIdxType) var {
    return locArr(dom.dist.ind2locInd(i))(i);
  }

  //
  // the iterator over the array's elements, currently sequential
  //
  def these() var {
    for loc in dom.dist.targetLocDom {
      // TODO: May want to do something like:
      // on this do
      // But can't currently have yields in on clauses
      for elem in locArr(loc) {
        yield elem;
      }
    }
  }

  //
  // this is the parallel iterator for the global array, see th
  // example for general notes on the approach
  //

  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    for blk in dom.newThese(IteratorType.leader) do
      yield blk;
  }

  def newThese(param iterator: IteratorType, followThis) var
        where iterator == IteratorType.follower {
    for i in followThis {
      yield this(i + dom.low);
    }
  }

  //
  // how to print out the whole array, sequentially
  //
  def writeThis(x: Writer) {
    var first = true;
    for loc in dom.dist.targetLocDom {
      // May want to do something like the following:
      //      on loc {
      // but it causes deadlock -- see writeThisUsingOn.chpl
        if (locArr(loc).numElements >= 1) {
          if (first) {
            first = false;
          } else {
            x.write(" ");
          }
          x.write(locArr(loc));
        }
        //    }
      stdout.flush();
    }
  }

  //
  // a query for the number of elements in the array
  //
  def numElements {
    return dom.numIndices;
  }
}

//
// the local array class
//
class LocBlock1DArr {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // the element type
  //
  type elemType;

  //
  // a reference to the local domain class for this array and locale
  //
  const locDom: LocBlock1DDom(glbIdxType, lclIdxType);

  //
  // the block of local array data
  //
  var myElems: [locDom.myBlock] elemType;

  //
  // the accessor for the local array -- assumes the index is local
  //
  def this(i: lclIdxType) var {
    return myElems(i);
  }

  //
  // iterator over the elements owned by this locale
  //
  def these() var {
    for elem in myElems {
      yield elem;
    }
  }

  //
  // this is the parallel iterator for the local array, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // prints out this locale's piece of the array
  //
  def writeThis(x: Writer) {
    // May want to do something like the following:
    //      on loc {
    // but it causes deadlock -- see writeThisUsingOn.chpl
    x.write(myElems);
  }

  //
  // query for the number of local array elements
  //
  def numElements {
    return myElems.numElements;
  }
}

// TODO: Have the global class leader/follower iterators defer to the
// local class leader/followers once we're within a locale?

// TODO: Make these into an official distribution?

// TODO: Would using nested classes allow me to avoid so much
// passing around of globIndexType and locIndexType?

config param debugBradsBlock1D = false;

// TODO: This would need to be moved somewhere more standard
enum IteratorType { leader, follower };

//
// The distribution class
//
class Block1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type glbIdxType = int(64);

  //
  // The bounding box that defines the block distribution
  //
  const boundingBox: domain(1, glbIdxType);

  //
  // a domain and array describing the set of target locales to which
  // the indices are mapped
  //
  const targetLocDom: domain(1);
  const targetLocs: [targetLocDom] locale;
  //
  // NOTE: originally I wrote this as:
  //
  //   const targetLocs = Locales;
  //   const targetLocDom = targetLocs.domain;
  //
  // but this had two interesting problems:
  //
  // (1) the capture of the targetLocs array only makes a copy of the
  //     array, not of its domain, so if someone were to resize the
  //     LocaleSpace domain outside of the class, this would inadvertantly
  //     get resized as well.  We really want to make a copy of both the
  //     domain and array (which I now do in the constructor).
  //
  // (2) Initializing targetLocs with Locales prevents us from assigning
  //     anything other than a numLocales-ary array of locales to it
  //     which is stricter than I wanted -- I only wanted to restrict
  //     us to a 1D array of locales.  In our current model, this would
  //     require us to have some sort of [domain(1)] locale declaration
  //     to make it work.  Or we could think about whether a model in
  //     which only the array's static type information is inferred
  //     for a field?

  //
  // an array of local distribution class descriptors -- set up in
  // initialize() below
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  // WANTED:
  //
  /*
  const locDist: [loc in targetLocDom] LocBlock1DDist(glbIdxType)
                   = on targetLocs(loc) do
                       new LocBlock1DDist(glbIdxType, targetLocDom.order(loc), this);
  */
  //
  // but this doesn't work yet because an array forall initializer
  // apparently can't refer to a local member domain.
  //
  const locDist: [targetLocDom] LocBlock1DDist(glbIdxType);
  //
  // WORKAROUND: Initialize in the constructor instead
  //

  def Block1DDist(type glbIdxType = int(64), bbox: domain(1, glbIdxType),
                  targetLocales: [?targetLocalesDomain] locale) {
    boundingBox = bbox;
    targetLocDom = targetLocalesDomain;
    targetLocs = targetLocales;
    //
    // WANT TO DO:
    /*
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(glbIdxType, locid, this);
    */
    //
    // BUT results in a _heapAlloc(type int(64)) call.
    // WORKAROUND:
    helpConstruct();
  }
  //
  // WORKAROUND CONTINUED:
  //
  def helpConstruct() {
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(glbIdxType, locid, this);
  }
  //
  // END WORKAROUND
  //

  def writeThis(x:Writer) {
    x.writeln("BradsBlock1DPar");
    x.writeln("---------------");
    x.writeln("distributes: ", boundingBox);
    x.writeln("across locales: ", targetLocs);
    x.writeln("indexed via: ", targetLocDom);
    x.writeln("resulting in: ");
    for locid in targetLocDom do
      x.writeln("  [", locid, "] ", locDist(locid));
  }

  //
  // Create a new domain over this distribution with the given index
  // set (inds) and the given global and local index type (idxType,
  // locIdxType)
  //
  def newDomain(inds, type idxType = glbIdxType, type locIdxType = idxType) {
    // Note that I'm fixing the global and local index types to be the
    // same, but making this a generic function would fix this
    return new Block1DDom(idxType, locIdxType, this, inds);
  }

  //
  // compute what chunk of inds is owned by a given locale -- assumes
  // it's being called on the locale in question
  //
  def getChunk(inds, locid) {
    // use domain slicing to get the intersection between what the
    // locale owns and the domain's index set

    // TODO: Should this be able to be written as myChunk[inds] ???
    return locDist(locid).myChunk(inds.dim(1));
  }

  //
  // Determine which locale owns a particular index
  //
  def ind2locInd(ind: glbIdxType) {
    const ind0 = ind - boundingBox.low;
    const loc0 = (ind0 * targetLocs.numElements) / boundingBox.numIndices;
    const locInd = loc0: index(targetLocs.domain) + targetLocs.domain.low;
    return locInd;
  }

  def ind2loc(ind: glbIdxType) {
    return targetLocs(ind2locInd(ind));
  }
}

//
// A per-locale local distribution class
//
class LocBlock1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type glbIdxType;

  //
  // This stores the piece of the global bounding box owned by
  // the locale.  Note that my original guess that we'd want
  // to use lclIdxType here is wrong since we're talking about
  // the section of the global index space owned by the locale.
  //
  const myChunk: domain(1, glbIdxType);
  const locid: int;
  const loc: locale;

  //
  // Compute what chunk of index(1) is owned by the current locale
  // Arguments:
  //
  def LocBlock1DDist(type glbIdxType,
                     _locid: int, // the locale index from the target domain
                     dist: Block1DDist(glbIdxType) // reference to glob dist
                     ) {
    locid = _locid;
    loc = dist.targetLocs(locid);
    const lo = dist.boundingBox.low;
    const hi = dist.boundingBox.high;
    const numelems = hi - lo + 1;
    const numlocs = dist.targetLocDom.numIndices;
    const locid0 = dist.targetLocDom.order(locid); // 0-based locale ID
    const blo = if (locid == 0) then min(glbIdxType)
                else procToData((numelems: real * locid) / numlocs, lo);
    const bhi = if (locid == numlocs - 1) then max(glbIdxType)
                else procToData((numelems: real * (locid+1)) / numlocs, lo) - 1;
    myChunk = [blo..bhi];
    if debugBradsBlock1D then
      writeln("locale ", locid, " owns ", myChunk);
  }

  //
  // a helper function for mapping processors to indices
  //
  def procToData(x, lo)
    return (lo + (x: lo.type) + (x:real != x:int:real));

  def writeThis(x:Writer) {
    x.write("locale ", loc.id, " owns chunk: ", myChunk);
  }
}

//
// The global domain class
//
class Block1DDom {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // a pointer to the parent distribution
  //
  const dist: Block1DDist(glbIdxType);

  //
  // a domain describing the complete domain
  //
  const whole: domain(1, glbIdxType);

  //
  // an array of local domain class descriptors -- set up in
  // initialize() below
  //
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locDoms: [dist.targetLocDom] LocBlock1DDom(glbIdxType, lclIdxType);

  def initialize() {
    for locid in dist.targetLocDom do
      on dist.targetLocs(locid) do
        locDoms(locid) = new LocBlock1DDom(glbIdxType, lclIdxType, this,
                                           dist.getChunk(whole, locid));
    if debugBradsBlock1D then
      [loc in dist.targetLocDom] writeln(loc, " owns ", locDoms(loc));
  }

  //
  // the iterator for the domain -- currently sequential
  //
  def these() {
    for blk in locDoms do
      // TODO: Would want to do something like:
      // on blk do
      // But can't currently have yields in on clauses
        for ind in blk do
          yield ind;
  }

  //
  // this is the parallel iterator for the global domain, following
  // a variation on Steve and David's proposals -- I've split the
  // single iterator into two iterators, distinguished by where
  // clauses on their parameter values.  This permits each to only
  // take the parameters it cares about; permits the leader to be
  // defined in an inlineable way; and permits the follower for an
  // array iterator to be a var iterator
  //
  // I've named these methods newThese() for the time being
  // to avoid conflicting with the above which is currently targeted
  // by the compiler.  My current assumption is that we would want to
  // overload these() to serve this purpose in the final language
  // definition.
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    //
    // TODO: This currently only results in a single level of
    // per-locale parallelism -- no per-core parallelism; maybe
    // approach this by deferring to the local leader/follower
    // iterators at some point?
    //
    // TODO: Really want the parallelism across the target locales
    // to be expressed more independently of the distribution by
    // pushing it into the leader iterator.  Need to get an on clause
    // into here somehow.  That is:
    //
    //   coforall locDom in locDoms do
    //     on locDom do
    //       yield locDom.myBlock - whole.low;
    //
    // TODO: And really want to split the inter- and intra-locale
    // parallelism into two separate stages for communication
    // optimization and the like
    //
    // TODO: Abstract this subtraction of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    for locDom in locDoms do
      yield locDom.myBlock - whole.low;
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
    //
    // TODO: Abstract this addition of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    // TODO: Is there some clever way to invoke the leader/follower
    // iterator on the local blocks in here such that the per-core
    // parallelism is expressed at that level?  Seems like a nice
    // natural composition and might help with my fears about how
    // stencil communication will be done on a per-locale basis.
    for i in followThis {
      yield i + whole.low;
    }
  }

  //
  // the print method for the domain
  //
  def writeThis(x:Writer) {
    x.write(whole);
  }

  //
  // how to allocate a new array over this domain
  //
  def newArray(type elemType) {
    return new Block1DArr(glbIdxType, lclIdxType, elemType, this);
  }

  //
  // queries for the number of indices, low, and high bounds
  //
  def numIndices {
    return whole.numIndices;
  }

  def low {
    return whole.low;
  }

  def high {
    return whole.high;
  }
}

//
// the local domain class
//
class LocBlock1DDom {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // a reference to the parent global domain class
  //
  const wholeDom: Block1DDom(glbIdxType, lclIdxType);

  //
  // a local domain describing the indices owned by this locale
  //
  var myBlock: domain(1, lclIdxType);

  //
  // iterator over this locale's indices
  //
  def these() {
    // May want to do something like:
    // on this do
    // But can't currently have yields in on clauses
    for ind in myBlock do
      yield ind;
  }

  //
  // this is the parallel iterator for the local domain, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // how to write out this locale's indices
  //
  def writeThis(x:Writer) {
    x.write(myBlock);
  }

  //
  // queries for this locale's number of indices, low, and high bounds
  //
  def numIndices {
    return myBlock.numIndices;
  }

  def low {
    return myBlock.low;
  }

  def high {
    return myBlock.high;
  }
}

//
// the global array class
//
class Block1DArr {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // the array's element type
  //
  type elemType;

  //
  // the global domain descriptor for this array
  //
  var dom: Block1DDom(glbIdxType, lclIdxType);

  //
  // an array of local array classes
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locArr: [dom.dist.targetLocDom] LocBlock1DArr(glbIdxType, lclIdxType, elemType);

  def initialize() {
    for locid in dom.dist.targetLocDom do
      on dom.dist.targetLocs(locid) do
        locArr(locid) = new LocBlock1DArr(glbIdxType, lclIdxType, elemType, dom.locDoms(locid));
  }

  //
  // the global accessor for the array
  //
  def this(i: glbIdxType) var {
    return locArr(dom.dist.ind2locInd(i))(i);
  }

  //
  // the iterator over the array's elements, currently sequential
  //
  def these() var {
    for loc in dom.dist.targetLocDom {
      // TODO: May want to do something like:
      // on this do
      // But can't currently have yields in on clauses
      for elem in locArr(loc) {
        yield elem;
      }
    }
  }

  //
  // this is the parallel iterator for the global array, see th
  // example for general notes on the approach
  //

  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    for blk in dom.newThese(IteratorType.leader) do
      yield blk;
  }

  def newThese(param iterator: IteratorType, followThis) var
        where iterator == IteratorType.follower {
    for i in followThis {
      yield this(i + dom.low);
    }
  }

  //
  // how to print out the whole array, sequentially
  //
  def writeThis(x: Writer) {
    var first = true;
    for loc in dom.dist.targetLocDom {
      // May want to do something like the following:
      //      on loc {
      // but it causes deadlock -- see writeThisUsingOn.chpl
        if (locArr(loc).numElements >= 1) {
          if (first) {
            first = false;
          } else {
            x.write(" ");
          }
          x.write(locArr(loc));
        }
        //    }
      stdout.flush();
    }
  }

  //
  // a query for the number of elements in the array
  //
  def numElements {
    return dom.numIndices;
  }
}

//
// the local array class
//
class LocBlock1DArr {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // the element type
  //
  type elemType;

  //
  // a reference to the local domain class for this array and locale
  //
  const locDom: LocBlock1DDom(glbIdxType, lclIdxType);

  //
  // the block of local array data
  //
  var myElems: [locDom.myBlock] elemType;

  //
  // the accessor for the local array -- assumes the index is local
  //
  def this(i: lclIdxType) var {
    return myElems(i);
  }

  //
  // iterator over the elements owned by this locale
  //
  def these() var {
    for elem in myElems {
      yield elem;
    }
  }

  //
  // this is the parallel iterator for the local array, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // prints out this locale's piece of the array
  //
  def writeThis(x: Writer) {
    // May want to do something like the following:
    //      on loc {
    // but it causes deadlock -- see writeThisUsingOn.chpl
    x.write(myElems);
  }

  //
  // query for the number of local array elements
  //
  def numElements {
    return myElems.numElements;
  }
}