Added another on-statement to deal with freeing of arrays.  This particular
on-statement makes sure that we are on the locale of an array before we remove
it from a domain's list of arrays.

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

//
// Abstract distribution class
//
class BaseDist {
  def newArithmeticDom(param rank: int, type idxType, param stridable: bool, param alias=false) {
    compilerError("arithmetic domains not supported by this distribution");
  }

  def newAssociativeDom(type idxType) {
    compilerError("associative domains not supported by this distribution");
  }

  def newEnumDom(type idxType) {
    compilerError("enumerated domains not supported by this distribution");
  }

  def newOpaqueDom(type idxType) {
    compilerError("opaque domains not supported by this distribution");
  }

  def newSparseDom(param rank: int, type idxType, dom: domain) {
    compilerError("opaque domains not supported by this distribution");
  }
}

//
// Abstract domain classes
//
class BaseDom {
  var _domCnt$: sync int = 0; // domain reference count and lock
  var _arrs: list(BaseArr);   // arrays declared over this domain

  def canCopyFromDevice param return false;
  def canCopyFromHost param return false;

  def destroyDom(arr: BaseArr = nil) {
    var cnt = _domCnt$ - 1;
    if cnt < 0 then
      halt("domain reference count is negative!");
    if arr then
      on arr do
        _arrs.remove(arr);
    _domCnt$ = cnt;
    return cnt;
  }

  def member(ind) : bool {
    halt("membership test not supported for this domain type");
    return false;
  }

  // used for associative domains/arrays
  def _backupArrays() {
    for arr in _arrs do
      arr._backupArray();
  }

  def _removeArrayBackups() {
    for arr in _arrs do
      arr._removeArrayBackup();
  }

  def _preserveArrayElement(oldslot, newslot) {
    for arr in _arrs do
      arr._preserveArrayElement(oldslot, newslot);
  }

  def supportsPrivatization() param return false;
}

class BaseArithmeticDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    compilerError("Illegal assignment to an arithmetic domain");
  }

  def add(x) {
    compilerError("Cannot add indices to an arithmetic domain");
  }
}

class BaseSparseDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    clear();
  }
}

class BaseAssociativeDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    clear();
  }
}

class BaseOpaqueDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    clear();
  }
}

class BaseEnumDom : BaseDom {
  def clear() {
    compilerError("Cannot clear an enumerated domain");
  }

  def clearForIteratableAssign() {
    compilerError("Illegal assignment to an enumerated domain");
  }
}

//
// Abstract array class
//
pragma "base array"
class BaseArr {
  var _arrCnt$: sync int = 0; // array reference count (and eventually lock)
  var _arrAlias: BaseArr;     // reference to base array if an alias

  def getBaseDom(): BaseDom {
    return nil;
  }

  def destroyArr(): int {
    var cnt = _arrCnt$ - 1;
    if cnt < 0 then
      halt("array reference count is negative!");
    _arrCnt$ = cnt;
    if cnt == 0 {
      var dom = getBaseDom();
      on dom {
        var cnt = dom.destroyDom(this);
        if cnt == 0 then
          delete dom;
      }
      if _arrAlias {
        on _arrAlias {
          var cnt = _arrAlias.destroyArr();
          if cnt == 0 then
            delete _arrAlias;
        }
      } else {
        destroyData();
      }
    }
    return cnt;
  }

  def destroyData() { }

  def reallocate(d: domain) {
    halt("reallocating not supported for this array type");
  }

  // This method is unsatisfactory -- see bradc's commit entries of
  // 01/02/08 around 14:30 for details
  def _purge( ind: int) {
    halt("purging not supported for this array type");
  }

  def _resize( length: int, old_map) {
    halt("resizing not supported for this array type");
  }

  def sparseShiftArray(shiftrange, initrange) {
    halt("sparseGrowDomain not supported for non-sparse arrays");
  }

  // methods for associative arrays
  def _backupArray() {
    halt("_backupArray() not supported for non-associative arrays");
  }

  def _removeArrayBackup() {
    halt("_removeArrayBackup() not supported for non-associative arrays");
  }

  def _preserveArrayElement(oldslot, newslot) {
    halt("_preserveArrayElement() not supported for non-associative arrays");
  }

  def supportsAlignedFollower() param return false;

  def supportsPrivatization() param return false;
}

//
// Abstract distribution class
//
class BaseDist {
  def newArithmeticDom(param rank: int, type idxType, param stridable: bool, param alias=false) {
    compilerError("arithmetic domains not supported by this distribution");
  }

  def newAssociativeDom(type idxType) {
    compilerError("associative domains not supported by this distribution");
  }

  def newEnumDom(type idxType) {
    compilerError("enumerated domains not supported by this distribution");
  }

  def newOpaqueDom(type idxType) {
    compilerError("opaque domains not supported by this distribution");
  }

  def newSparseDom(param rank: int, type idxType, dom: domain) {
    compilerError("opaque domains not supported by this distribution");
  }
}

//
// Abstract domain classes
//
class BaseDom {
  var _domCnt$: sync int = 0; // domain reference count and lock
  var _arrs: list(BaseArr);   // arrays declared over this domain

  def canCopyFromDevice param return false;
  def canCopyFromHost param return false;

  def destroyDom(arr: BaseArr = nil) {
    var cnt = _domCnt$ - 1;
    if cnt < 0 then
      halt("domain reference count is negative!");
    if arr then
      _arrs.remove(arr);
    _domCnt$ = cnt;
    return cnt;
  }

  def member(ind) : bool {
    halt("membership test not supported for this domain type");
    return false;
  }

  // used for associative domains/arrays
  def _backupArrays() {
    for arr in _arrs do
      arr._backupArray();
  }

  def _removeArrayBackups() {
    for arr in _arrs do
      arr._removeArrayBackup();
  }

  def _preserveArrayElement(oldslot, newslot) {
    for arr in _arrs do
      arr._preserveArrayElement(oldslot, newslot);
  }

  def supportsPrivatization() param return false;
}

class BaseArithmeticDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    compilerError("Illegal assignment to an arithmetic domain");
  }

  def add(x) {
    compilerError("Cannot add indices to an arithmetic domain");
  }
}

class BaseSparseDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    clear();
  }
}

class BaseAssociativeDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    clear();
  }
}

class BaseOpaqueDom : BaseDom {
  def clear() {
    halt("clear not implemented for this distribution");
  }

  def clearForIteratableAssign() {
    clear();
  }
}

class BaseEnumDom : BaseDom {
  def clear() {
    compilerError("Cannot clear an enumerated domain");
  }

  def clearForIteratableAssign() {
    compilerError("Illegal assignment to an enumerated domain");
  }
}

//
// Abstract array class
//
pragma "base array"
class BaseArr {
  var _arrCnt$: sync int = 0; // array reference count (and eventually lock)
  var _arrAlias: BaseArr;     // reference to base array if an alias

  def getBaseDom(): BaseDom {
    return nil;
  }

  def destroyArr(): int {
    var cnt = _arrCnt$ - 1;
    if cnt < 0 then
      halt("array reference count is negative!");
    _arrCnt$ = cnt;
    if cnt == 0 {
      var dom = getBaseDom();
      on dom {
        var cnt = dom.destroyDom(this);
        if cnt == 0 then
          delete dom;
      }
      if _arrAlias {
        on _arrAlias {
          var cnt = _arrAlias.destroyArr();
          if cnt == 0 then
            delete _arrAlias;
        }
      } else {
        destroyData();
      }
    }
    return cnt;
  }

  def destroyData() { }

  def reallocate(d: domain) {
    halt("reallocating not supported for this array type");
  }

  // This method is unsatisfactory -- see bradc's commit entries of
  // 01/02/08 around 14:30 for details
  def _purge( ind: int) {
    halt("purging not supported for this array type");
  }

  def _resize( length: int, old_map) {
    halt("resizing not supported for this array type");
  }

  def sparseShiftArray(shiftrange, initrange) {
    halt("sparseGrowDomain not supported for non-sparse arrays");
  }

  // methods for associative arrays
  def _backupArray() {
    halt("_backupArray() not supported for non-associative arrays");
  }

  def _removeArrayBackup() {
    halt("_removeArrayBackup() not supported for non-associative arrays");
  }

  def _preserveArrayElement(oldslot, newslot) {
    halt("_preserveArrayElement() not supported for non-associative arrays");
  }

  def supportsAlignedFollower() param return false;

  def supportsPrivatization() param return false;
}


Fixed type-o from 17152.

Fixed type-o from 17152.

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

  if dataParTasksPerLocale<0 then halt("dataParTasksPerLocale must be >= 0");
  if dataParMinGranularity<0 then halt("dataParMinGranularity must be >= 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;

  if dataParTasksPerLocale<0 then halt("dataParTasksPerLocale must be >= 0");
  if dataParMinGranularity<0 then halt("dataParMinGranularity must be >= 0");
  const numTasks = if dataParTasksPerLocale==0 then here.numCores
                   else dataParTasksPerLocale;
  const ignoreRunning = dataParIgnoreRunningTasks;
  const minIndicesPerTask = dataParMinGranularity;

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


Apply private to an external procedure in the Memory standard module

There's a couple of other procedures in here with "no doc" attached, but they
print out a "deprecated" error, so I think it is better to let them do that
than to print out the private error.

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

/*
  The :mod:`Memory` module provides procedures which report information
  about memory usage.  With one exception, to use these procedures you
  must enable memory tracking.  Do this by setting one or more of the
  config vars below, using appropriate ``--configVarName=value`` or
  ``-sconfigVarName=value`` command line options when you run the
  program.  If memory tracking is not enabled, calling any procedure
  described here, other than :proc:`locale.physicalMemory`, will cause
  the program to halt with an error message.

  ``memTrack``: :type:`bool`:
    Enable memory tracking.  This causes memory allocations and
    deallocations to be internally tracked, to support logging them on
    the fly and/or producing any of several kinds of memory-related
    reports, described below.

  ``memLeaks``: :type:`bool`:
    Enable memory tracking and produce detailed information about
    leaked memory by invoking :proc:`printMemAllocs` implicitly when
    the program terminates normally.

  ``memLeaksByType``: :type:`bool`:
    Enable memory tracking and produce summary information about
    leaked memory by invoking :proc:`printMemAllocsByType` implicitly
    when the program terminates normally.

  ``memLeaksLog``: :type:`c_string`:
    Enable memory tracking and append a report to the named file when
    the program terminates normally.
    The report contains the compilation command used to build the
    program, the execution command used to run it, the summary
    allocation statistics produced by :proc:`printMemAllocStats`, and the
    summary information about leaked memory produced by
    :proc:`printMemAllocsByType`.

  ``memStats``: :type:`bool`:
    Enable memory tracking and produce summary memory statistics by
    invoking :proc:`printMemAllocStats` implicitly at normal program
    termination.

  ``memMax``: :type:`uint`:
    If the value is greater than 0 (zero), enable memory tracking
    and use the value as a limit on the number of bytes of memory
    that can be allocated per top-level (network-connected) locale.
    If during execution the amount of allocated memory exceeds this
    limit on any locale, halt the program with a message saying so.

  The following two config variables do not enable memory tracking;
  they only modify how it is done.

  ``memThreshold``: :type:`uint`:
    If this is set to a value greater than 0 (zero), only allocation
    requests larger than this are tracked and/or reported.

  ``memLog``: :type:`c_string`:
    Memory reporting is written to this file.  By default it is the
    ``stdout`` associated with the process (not the Chapel channel
    with the same name).  Setting this config variable to a file path
    causes the reporting to be written to that named file instead.
    In multilocale executions each top-level locale produces output
    to its own file, with a dot ('.') and the locale ID appended to
    this path.
 */
module Memory {

pragma "no doc"
pragma "insert line file info" private extern proc chpl_memoryUsed(): uint(64);

/*
  The amount of memory returned by :proc:`locale.physicalMemory` can
  be expressed either as individual bytes or as chunks of 2**10,
  2**20, or 2**30 bytes.
 */
enum MemUnits {Bytes, KB, MB, GB};

/*
  How much physical memory is present on this locale?

  Note that this is a physical quantity and does not take into account
  things like virtual memory support which might allow for allocating
  a larger amount, or system or user limits which might prevent
  allocating so much.

  *Note:* Unlike the other procedures in the :mod:`Memory` module, this
  one does not require memory tracking to be enabled.

  :arg unit: Units in which the returned value is to be expressed.
  :type unit: MemUnits
  :arg retType: Type of the returned value.  Defaults to `int(64)`.
  :type retType: type
  :returns: Size of physical memory on the locale where the call is made.
  :rtype: :type:`retType`
 */
proc locale.physicalMemory(unit: MemUnits=MemUnits.Bytes, type retType=int(64)) {
  extern proc chpl_bytesPerLocale(): uint(64);

  var bytesInLocale: uint(64);

  on this do bytesInLocale = chpl_bytesPerLocale();

  var retVal: retType;
  select (unit) {
    when MemUnits.Bytes do retVal = bytesInLocale:retType;
    when MemUnits.KB do retVal = (bytesInLocale:retType / 1024):retType;
    when MemUnits.MB do retVal = (bytesInLocale:retType / (1024**2)):retType;
    when MemUnits.GB do retVal = (bytesInLocale:retType / (1024**3)):retType;
  }

  return retVal;
}

/*
  How much memory is this program currently using on this locale?

  This is the amount of memory known to be currently allocated on the
  calling top-level (network-connected) locale by the program, through
  Chapel mechanisms.  It does not include memory allocated directly
  from the system, outside of Chapel mechanisms, such as allocations
  made by code written in other languages and linked into the program.

  :returns: Amount of allocated memory, in bytes.
  :rtype: `uint(64)`
 */
proc memoryUsed() {
  return chpl_memoryUsed();
}

/*
  Print detailed information about allocated memory to ``memLog``.
  The report contains a section for each top-level locale, containing
  a table of entries for the allocations made on that locale.  Each
  entry shows the source file and line at which the allocation was
  requested, the address and size (bytes) of the allocated space, and
  a description of the type of information the requesting code said it
  was going to store there.

  :arg thresh: Do not print entries whose size is less than this.
    Defaults to 0.
  :type thresh: int
*/
proc printMemAllocs(thresh=0) {
  pragma "insert line file info"
  extern proc chpl_printMemAllocs(thresh);

  chpl_printMemAllocs(thresh);
}

pragma "no doc"
proc printMemTable(thresh=0) {
  warning('printMemTable() is deprecated ' +
          'and will be removed in Chapel 1.13; ' +
          'please use printMemAllocs() instead.');
  printMemAllocs(thresh);
}

/*
  Print summary information about allocated memory to ``memLog``.  The
  report contains a section for each top-level locale, containing a
  table of entries, one for each different allocation type for which
  at least one allocation exists on that locale.  The entries show the
  type (that is, the string the code requesting the allocation used to
  describe what it would store there) and the total number of
  allocations and bytes allocated for that type.
*/
proc printMemAllocsByType() {
  pragma "insert line file info"
  extern proc chpl_printMemAllocsByType();

  chpl_printMemAllocsByType();
}

pragma "no doc"
proc printMemLeaksTable() {
  warning('printMemLeaksTable() is deprecated ' +
          'and will be removed in Chapel 1.13.' +
          'Please use printMemAllocsByType() instead.');
  printMemAllocsByType();
}

/*
  Print summary memory statistics to ``memLog``.  The report contains
  a section for each top-level locale showing the number of bytes of
  memory currently allocated, the maximum number allocated at any
  point during execution (the high-water mark), and the sum of the
  sizes of all allocation and deallocation requests.
*/
proc printMemAllocStats() {
  pragma "insert line file info"
  extern proc chpl_printMemAllocStats();

  chpl_printMemAllocStats();
}

pragma "no doc"
proc printMemStat() {
  warning('printMemStat() is deprecated ' +
          'and will be removed in Chapel 1.13.' +
          'please use printMemAllocStats() instead.');
  printMemAllocStats();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on any locale.  Continue reporting until :proc:`stopVerboseMem`
  is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMem() {
  extern proc chpl_startVerboseMem();
  chpl_startVerboseMem();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on any locale.
 */
proc stopVerboseMem() {
  extern proc chpl_stopVerboseMem();
  chpl_stopVerboseMem();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on this locale.  Continue reporting until
  :proc:`stopVerboseMemHere` is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMemHere() {
  extern proc chpl_startVerboseMemHere();
  chpl_startVerboseMemHere();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on this locale.
 */
proc stopVerboseMemHere() {
  extern proc chpl_stopVerboseMemHere();
  chpl_stopVerboseMemHere();
}

}

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

/*
  The :mod:`Memory` module provides procedures which report information
  about memory usage.  With one exception, to use these procedures you
  must enable memory tracking.  Do this by setting one or more of the
  config vars below, using appropriate ``--configVarName=value`` or
  ``-sconfigVarName=value`` command line options when you run the
  program.  If memory tracking is not enabled, calling any procedure
  described here, other than :proc:`locale.physicalMemory`, will cause
  the program to halt with an error message.

  ``memTrack``: :type:`bool`:
    Enable memory tracking.  This causes memory allocations and
    deallocations to be internally tracked, to support logging them on
    the fly and/or producing any of several kinds of memory-related
    reports, described below.

  ``memLeaks``: :type:`bool`:
    Enable memory tracking and produce detailed information about
    leaked memory by invoking :proc:`printMemAllocs` implicitly when
    the program terminates normally.

  ``memLeaksByType``: :type:`bool`:
    Enable memory tracking and produce summary information about
    leaked memory by invoking :proc:`printMemAllocsByType` implicitly
    when the program terminates normally.

  ``memLeaksLog``: :type:`c_string`:
    Enable memory tracking and append a report to the named file when
    the program terminates normally.
    The report contains the compilation command used to build the
    program, the execution command used to run it, the summary
    allocation statistics produced by :proc:`printMemAllocStats`, and the
    summary information about leaked memory produced by
    :proc:`printMemAllocsByType`.

  ``memStats``: :type:`bool`:
    Enable memory tracking and produce summary memory statistics by
    invoking :proc:`printMemAllocStats` implicitly at normal program
    termination.

  ``memMax``: :type:`uint`:
    If the value is greater than 0 (zero), enable memory tracking
    and use the value as a limit on the number of bytes of memory
    that can be allocated per top-level (network-connected) locale.
    If during execution the amount of allocated memory exceeds this
    limit on any locale, halt the program with a message saying so.

  The following two config variables do not enable memory tracking;
  they only modify how it is done.

  ``memThreshold``: :type:`uint`:
    If this is set to a value greater than 0 (zero), only allocation
    requests larger than this are tracked and/or reported.

  ``memLog``: :type:`c_string`:
    Memory reporting is written to this file.  By default it is the
    ``stdout`` associated with the process (not the Chapel channel
    with the same name).  Setting this config variable to a file path
    causes the reporting to be written to that named file instead.
    In multilocale executions each top-level locale produces output
    to its own file, with a dot ('.') and the locale ID appended to
    this path.
 */
module Memory {

pragma "no doc"
pragma "insert line file info" extern proc chpl_memoryUsed(): uint(64);

/*
  The amount of memory returned by :proc:`locale.physicalMemory` can
  be expressed either as individual bytes or as chunks of 2**10,
  2**20, or 2**30 bytes.
 */
enum MemUnits {Bytes, KB, MB, GB};

/*
  How much physical memory is present on this locale?

  Note that this is a physical quantity and does not take into account
  things like virtual memory support which might allow for allocating
  a larger amount, or system or user limits which might prevent
  allocating so much.

  *Note:* Unlike the other procedures in the :mod:`Memory` module, this
  one does not require memory tracking to be enabled.

  :arg unit: Units in which the returned value is to be expressed.
  :type unit: MemUnits
  :arg retType: Type of the returned value.  Defaults to `int(64)`.
  :type retType: type
  :returns: Size of physical memory on the locale where the call is made.
  :rtype: :type:`retType`
 */
proc locale.physicalMemory(unit: MemUnits=MemUnits.Bytes, type retType=int(64)) {
  extern proc chpl_bytesPerLocale(): uint(64);

  var bytesInLocale: uint(64);

  on this do bytesInLocale = chpl_bytesPerLocale();

  var retVal: retType;
  select (unit) {
    when MemUnits.Bytes do retVal = bytesInLocale:retType;
    when MemUnits.KB do retVal = (bytesInLocale:retType / 1024):retType;
    when MemUnits.MB do retVal = (bytesInLocale:retType / (1024**2)):retType;
    when MemUnits.GB do retVal = (bytesInLocale:retType / (1024**3)):retType;
  }

  return retVal;
}

/*
  How much memory is this program currently using on this locale?

  This is the amount of memory known to be currently allocated on the
  calling top-level (network-connected) locale by the program, through
  Chapel mechanisms.  It does not include memory allocated directly
  from the system, outside of Chapel mechanisms, such as allocations
  made by code written in other languages and linked into the program.

  :returns: Amount of allocated memory, in bytes.
  :rtype: `uint(64)`
 */
proc memoryUsed() {
  return chpl_memoryUsed();
}

/*
  Print detailed information about allocated memory to ``memLog``.
  The report contains a section for each top-level locale, containing
  a table of entries for the allocations made on that locale.  Each
  entry shows the source file and line at which the allocation was
  requested, the address and size (bytes) of the allocated space, and
  a description of the type of information the requesting code said it
  was going to store there.

  :arg thresh: Do not print entries whose size is less than this.
    Defaults to 0.
  :type thresh: int
*/
proc printMemAllocs(thresh=0) {
  pragma "insert line file info"
  extern proc chpl_printMemAllocs(thresh);

  chpl_printMemAllocs(thresh);
}

pragma "no doc"
proc printMemTable(thresh=0) {
  warning('printMemTable() is deprecated ' +
          'and will be removed in Chapel 1.13; ' +
          'please use printMemAllocs() instead.');
  printMemAllocs(thresh);
}

/*
  Print summary information about allocated memory to ``memLog``.  The
  report contains a section for each top-level locale, containing a
  table of entries, one for each different allocation type for which
  at least one allocation exists on that locale.  The entries show the
  type (that is, the string the code requesting the allocation used to
  describe what it would store there) and the total number of
  allocations and bytes allocated for that type.
*/
proc printMemAllocsByType() {
  pragma "insert line file info"
  extern proc chpl_printMemAllocsByType();

  chpl_printMemAllocsByType();
}

pragma "no doc"
proc printMemLeaksTable() {
  warning('printMemLeaksTable() is deprecated ' +
          'and will be removed in Chapel 1.13.' +
          'Please use printMemAllocsByType() instead.');
  printMemAllocsByType();
}

/*
  Print summary memory statistics to ``memLog``.  The report contains
  a section for each top-level locale showing the number of bytes of
  memory currently allocated, the maximum number allocated at any
  point during execution (the high-water mark), and the sum of the
  sizes of all allocation and deallocation requests.
*/
proc printMemAllocStats() {
  pragma "insert line file info"
  extern proc chpl_printMemAllocStats();

  chpl_printMemAllocStats();
}

pragma "no doc"
proc printMemStat() {
  warning('printMemStat() is deprecated ' +
          'and will be removed in Chapel 1.13.' +
          'please use printMemAllocStats() instead.');
  printMemAllocStats();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on any locale.  Continue reporting until :proc:`stopVerboseMem`
  is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMem() {
  extern proc chpl_startVerboseMem();
  chpl_startVerboseMem();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on any locale.
 */
proc stopVerboseMem() {
  extern proc chpl_stopVerboseMem();
  chpl_stopVerboseMem();
}

/*
  Start on-the-fly reporting of memory allocations and deallocations
  done on this locale.  Continue reporting until
  :proc:`stopVerboseMemHere` is called.

  The reporting output consists of a single line describing each
  memory allocation or deallocation, written to ``memLog``.
 */
proc startVerboseMemHere() {
  extern proc chpl_startVerboseMemHere();
  chpl_startVerboseMemHere();
}

/*
  Stop on-the-fly reporting of memory allocations and deallocations
  done on this locale.
 */
proc stopVerboseMemHere() {
  extern proc chpl_stopVerboseMemHere();
  chpl_stopVerboseMemHere();
}

}