Protected calls to append in the _domain record with a lock

This seems to be enough to prevent error messages complaining about attempts to dereference nil.

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

def _build_domain_type(dist, param rank : int, type idxType = int,
                       param stridable = false) type
  return new _domain(rank, dist.buildDomain(rank, idxType, stridable));

def _build_domain_type(dist, type ind) type where !__primitive("isEnumType", ind) && !__primitive("isOpaqueType", ind)
  return new _domain(1, dist.buildDomain(ind));

def _build_domain_type(dist, type ind) type where __primitive("isEnumType", ind)
  return new _domain(1, dist.buildEnumDomain(ind));

def _build_domain_type(dist, type ind) type where __primitive("isOpaqueType", ind)
  return new _domain(1, dist.buildOpaqueDomain());

def _build_subdomain_type(dom) type
  return dom.buildSubdomain();

def _build_sparse_subdomain_type(dist, parentDom)
  return new _domain(parentDom.rank,
                     dist.buildSparseDomain(parentDom.rank,
                                            parentDom._value.idxType,
                                            parentDom._value));

def _build_opaque_domain_type(dist) type
  return new _domain(1, dist.buildOpaqueDomain());

def _build_array_type(dom, type eltType) type
  return dom.buildArray(eltType);

def _build_array(dom, type eltType)
  return dom.buildArray(eltType);

def _build_domain(x: _domain)
  return x;

def _build_domain(ranges: range(?eltType,BoundedRangeType.bounded,?stridable) ...?rank) {
  for param i in 2..rank do
    if eltType(1) != eltType(i) then
      compilerError("domain has mixed dimensional type");
  var d: domain(rank, eltType(1), _any_stridable(ranges));
  d.setIndices(ranges);
  return d;
}

def _wrapDomain(d) return new _domain(d.rank, d);

//
// computes || reduction over stridable of ranges
//
def _any_stridable(ranges, param d: int = 1) param {
  if ranges(d).stridable == true then
    return true;
  else if ranges.size > d then
    return _any_stridable(ranges, d+1);
  else
    return false;
}

def _build_open_interval_upper(x: _domain)
  return x.buildOpenIntervalUpper();

def _build_index_type(param rank: int, type idxType) type where rank == 1 {
  var x: idxType;
  return x;
}

def _build_index_type(param rank: int, type idxType) type where rank > 1 {
  var x: rank*idxType;
  return x;
}

def _build_index_type(param rank: int) type
  return _build_index_type(rank, int);

def _build_index_type(d: _domain) type
  return _build_index_type(d.rank, d._value.idxType);

def _build_index_type(type idxType) type where __primitive("isOpaqueType", idxType)
  return _OpaqueIndex;

//
// given a tuple args, returns true if the tuple contains only
// integers and ranges; that is, it is a valid argument list for rank
// change
//
def _validRankChangeArgs(args, type idxType) param {
  def _validRankChangeArg(type idxType, r: range(?e,?b,?s)) param return true;
  def _validRankChangeArg(type idxType, i: idxType) param return true;
  def _validRankChangeArg(type idxType, x) param return false;

  def help(param dim: int) param {
    if !_validRankChangeArg(idxType, args(dim)) then
      return false;
    else if dim < args.size then
      return help(dim+1);
    else
      return true;
  }

  return help(1);
}

def _getRankChangeRanges(args) {
  def isRange(r: range(?e,?b,?s)) param return 1;
  def isRange(r) param return 0;
  def _tupleize(x) {
    var y: 1*x.type;
    y(1) = x;
    return y;
  }
  def collectRanges(param dim: int) {
    if dim > args.size then
      compilerError("domain slice requires a range in at least one dimension");
    if isRange(args(dim)) then
      return collectRanges(dim+1, _tupleize(args(dim)));
    else
      return collectRanges(dim+1);
  }
  def collectRanges(param dim: int, x: _tuple) {
    if dim > args.size {
      return x;
    } else if dim < args.size {
      if isRange(args(dim)) then
        return collectRanges(dim+1, ((...x), args(dim)));
      else
        return collectRanges(dim+1, x);
    } else {
      if isRange(args(dim)) then
        return ((...x), args(dim));
      else
        return x;
    }
  }
  return collectRanges(1);
}

pragma "domain"
record _domain {
  param rank : int;
  var _value;
  var _promotionType: index(rank, _value.idxType);

  def initialize() {
    if _value == nil {
      _value = _value.buildEmptyDomain();
    }
    return this;
  }

  def these() {
    for i in _value.these() do
      yield i;
  }

  def this(ranges: range(?eltType,?boundedType,?stridable) ...rank)
    return new _domain(rank, _value.slice(_any_stridable(ranges), ranges));

  def this(args ...rank) where _validRankChangeArgs(args, _value.idxType) {
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = _any_stridable(ranges);
    return new _domain(rank, _value.rankChange(rank, stridable, args));
  }

  def dim(d : int)
    return _value.dim(d);

  def dimIter(param d, ind) {
    for i in _value.dimIter(d, ind) do yield i;
  }

  def buildArray(type eltType) {
    var x = _value.buildArray(eltType);
    if !_value._locked then {
      _value._arrs.append(x);
      _value._locked = false;  // writing to this sync var "unlocks" the lock!
    }
    return new _array(_value.idxType, eltType, rank, x);
  }

  // buildEmptyDomain is meant to return an uninitialized domain for
  // the given domain type. For example, it should return the same
  // sort of thing that var D: domain(2) dist(yourDist); would result
  // in if you didn't initialize it.  It should be a static method and
  // not refer to any fields that aren't types and params.
  def buildEmptyDomain() {
    var x = _value.buildEmptyDomain();
    return new _domain(rank, x);
  }

  def buildSubdomain() {
    var x = _value.buildSubdomain();
    return new _domain(rank, x);
  }

  def buildOpenIntervalUpper()
    return new _domain(rank, _value.buildOpenIntervalUpper());

  def clear() {
    _value.clear();
  }

  def create() {
    if _value.idxType != _OpaqueIndex then
      compilerError("domain.create() only applies to opaque domains");
    return _value.create();
  }

  def add(i) {
    _value.add(i);
  }

  def remove(i) {
    _value.remove(i);
  }

  def numIndices return _value.numIndices;
  def low return _value.low;
  def high return _value.high;

  def member(i) {
    return _value.member(i);
  }

  def order(i) {
    return _value.order(i);
  }

  def position(i) {
    return _value.position(i);
  }

  def expand(i: int ...rank)
    return expand(i);

  def expand(i: rank*int)
    return new _domain(rank, _value.expand(i));

  def expand(i: int) where rank > 1
    return new _domain(rank, _value.expand(i));

  def exterior(i: int ...rank)
    return exterior(i);

  def exterior(i: rank*int) {
    var x = _value.exterior(i);
    return new _domain(rank, x);
  }

  def interior(i: int ...rank)
    return interior(i);

  def interior(i: rank*int) {
    var x = _value.interior(i);
    return new _domain(rank, x);
  }

  def translate(i: int ...rank)
    return translate(i);

  def translate(i: rank*int) {
    var x = _value.translate(i);
    return new _domain(rank, x);
  }

  def subBlocks {
    for d in _value.subBlocks do
      yield d;
  }

  def setIndices(x) {
    _value.setIndices(x);
  }

  def getIndices()
    return _value.getIndices();

  // associative array interface

  def sorted() {
    for i in _value.sorted() {
      yield i;
    }
  }
}

def _isDomain(x: _domain) param return true;
def _isDomain(x) param return false;

def =(a: _domain, b: _domain) {
  for e in a._value._arrs do
    e.reallocate(b);
  a._value.setIndices(b._value.getIndices());
  return a;
}

def =(a: _domain, b: _tuple) {
  for ind in 1..b.size {
    a.add(b(ind));
  }
  return a;
}

def =(d: _domain, r: range(?e,?b,?s)) {
  d = _build_domain(r);
  return d;
}

def =(a: _domain, b) {  // b is iteratable
  a.clear();
  for ind in b {
    a.add(ind);
  }
  return a;
}

pragma "inline" def _pass(a: _domain)
  return a;

def _init(a: _domain)
  return a.buildEmptyDomain();

def _copy(a: _domain) {
  var b: a.buildEmptyDomain().type;
  b.setIndices(a.getIndices());
  return b;
}

def _domain.writeThis(f: Writer) {
  f.write(_value);
}

def by(a: _domain, b) {
  var x = a._value.strideBy(b);
  return new _domain(a.rank, x);
}

// this is a wrapper class for all arrays
pragma "array"
record _array {
  type idxType;
  type eltType;
  param rank : int;
  var _value;
  var _promotionType : eltType;

  def _dom
    return new _domain(rank, _value.dom);

  pragma "inline"
  def this(i: rank*idxType) var where rank > 1
    return _value(i);

  pragma "inline"
  def this(i: idxType ...rank) var where rank > 1
    return _value(i);

  pragma "inline"
  def this(i: idxType) var where rank == 1
    return _value(i);

  //
  // requires dense domain implementation that returns a tuple of
  // ranges via the getIndices() method; domain indexing is difficult
  // in the domain case because it has to be implemented on a
  // domain-by-domain basis; this is not terribly difficult in the
  // dense case because we can represent a domain by a tuple of
  // ranges, but in the sparse case, is there a general
  // representation?
  //
  pragma "valid var"
  def this(d: _domain) var where d.rank == rank
    return this((...d.getIndices()));

  pragma "valid var"
  def this(ranges: range(?_eltType,?boundedType,?stridable) ...rank) var {
    if boundsChecking then
      _value.checkSlice(ranges);
    return new _array(idxType, eltType, rank, _value.slice(_dom((...ranges))._value));
  }

  pragma "valid var"
  def this(args ...rank) var where _validRankChangeArgs(args, idxType) {
    if boundsChecking then
      _value.checkRankChange(args);
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = _any_stridable(ranges);
    return new _array(idxType, eltType, rank, _value.rankChange(rank, stridable, args));
  }

  def these() var {
    for i in _value.these() do
      yield i;
  }

  def numElements return _dom.numIndices; // assume dom name

  def reindex(d: _domain) where rank == 1 {
    var x = _value.reindex(d._value);
    return new _array(x.idxType, eltType, rank, x);
  }

  def reindex(d: _domain) where rank != 1 {
    var x = _value.reindex(d._value);
    return new _array(x.idxType, eltType, rank, x);
  }

  // sparse array interface

  def IRV var {
    return _value.IRV;
  }

  // associative array interface

  def sorted() {
    for i in _value.sorted() {
      yield i;
    }
  }
}

pragma "inline" def =(a: _array, b) {
  for (i,bb) in (a._dom,b) do
    a(i) = bb;
  return a;
}

def =(a: _array, b: _tuple) {
  a._value.tupleInit(b);
  return a;
}

def _desync(type t) where t: _syncvar || t: _singlevar {
  var x: t;
  return x.value;
}

def _desync(type t) where !(t: _syncvar|| t: _singlevar) {
  var x: t;
  return x;
}

def =(a: _array, b: _desync(a.eltType)) {
  for i in a._dom do
    a(i) = b;
  return a;
}

def _copy(a: _array) {
  var b : [a._dom] a.eltType;
  b = a;
  return b;
}

pragma "inline" def _pass(a: _array) {
  return a;
}

def _init(a: _array) {
  return a._dom.buildArray(a.eltType);
}

def _init(type t) where t: _array {
  return __primitive("build_array", t);
}

def _array.writeThis(f: Writer) {
  f.write(_value);
}

// this is the class that all array classes are derived from
pragma "BaseArray"
class BaseArray {
  def reallocate(d: _domain) {
    halt("reallocating not support 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");
  }
}

class BaseDomain {
  var _arrs: list(BaseArray),
      _locked: sync bool = false;

  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);
  }
}

class BaseArithmeticDomain : BaseDomain {
  def bbox(d:int) {
    halt("bbox not supported for this domain type");
    return 1..0;
  }
}

class BaseSparseArithmeticDomain : BaseArithmeticDomain {
}

//
// index for all opaque domains
//
class _OpaqueIndex { }

//
// Swap operators for arrays and domains
//
pragma "inline" def _chpl_swap(x: [], y: []) {
  for (i,j) in (x.domain, y.domain) do
    x(i) <=> y(j);
}

pragma "inline" def _chpl_swap(x: domain, y: domain) {
  const t = y;
  y = x;
  x = t;
}

//
// reshape function
//
def reshape(A: [], D: domain) {
  var B: [D] A.eltType;
  for (i,a) in (D,A) do
    B(i) = a;
  return B;
}

// This should go away once distributions are implemented
def distributed_warning(d) {
  if (numLocales > 1) {
    __primitive("chpl_warning", "'distributed' domains/arrays are not yet distributed across multiple locales");
  }
  return d;
}

//
// module support for iterators
//
def iteratorIndex(ic: _iteratorClass) {
  ic.advance();
  return ic.getValue();
}

pragma "expand tuples with values"
def iteratorIndex(t: _tuple) {
  pragma "expand tuples with values"
  def iteratorIndexHelp(t: _tuple, param dim: int) {
    if dim == t.size-1 then
      return _build_tuple_always_allow_ref(iteratorIndex(t(dim)), iteratorIndex(t(dim+1)));
    else
      return _build_tuple_always_allow_ref(iteratorIndex(t(dim)), (...iteratorIndexHelp(t, dim+1)));
  }

  return iteratorIndexHelp(t, 1);
}

def iteratorIndexType(x) type {
  return iteratorIndex(x).type;
}

def _iteratorClass.writeThis(f: Writer) {
  var first: bool = true;
  for e in this {
    if !first then
      f.write(" ");
    else
      first = false;
    f.write(e);
  }
}

def =(ic: _iteratorClass, xs) {
  for (e, x) in (ic, xs) do
    e = x;
  return ic;
}

def =(ic: _iteratorClass, x: iteratorIndexType(ic)) {
  for e in ic do
    e = x;
  return ic;
}

pragma "inline" def _pass(ic: _iteratorClass)
  return ic;

def _copy(ic: _iteratorClass) {
  return _ic_copy_help(_ic_copy_recursive(ic));

  def _ic_copy_recursive(ic) {
    for e in ic do
      yield _copy(e);
  }

  def _ic_copy_help(ic) {
    var i = 1, size = 4;
    var D = [1..size];

    // note that _getIterator is called in order to copy the iterator
    // class since for arrays we need to iterate once to get the
    // element type (at least for now); this also means that if this
    // iterator has side effects, we will see them; a better way to
    // handle this may be to get the static type (not initialize the
    // array) and use a primitive to set the array's element; that may
    // also handle skyline arrays
    var A: [D] iteratorIndexType(_getIterator(ic));

    for e in ic {
      if i > size {
        size = size * 2;
        D = [1..size];
      }
      A(i) = e;
      i = i + 1;
    }
    D = [1..i-1];
    return A;
  }
}

pragma "inline" pragma "iterator class copy"
def _getIterator(ic: _iteratorClass)
  return ic;

pragma "inline" pragma "ref" def _getIterator(x)
  return x.these();

pragma "inline" def _getIterator(x: _tuple) {
  pragma "inline" def _getIteratorHelp(x: _tuple, param dim: int) {
    if dim == x.size-1 then
      return (_getIterator(x(dim)), _getIterator(x(dim+1)));
    else
      return (_getIterator(x(dim)), (..._getIteratorHelp(x, dim+1)));
  }
  return _getIteratorHelp(x, 1);
}

def _getIterator(type t) {
  compilerError("cannot iterate over a type");
}

pragma "inline"
def _toLeader(iterator: _iteratorClass)
  return __primitive("to leader", iterator);

pragma "inline"
def _toLeader(x: _tuple)
  return _toLeader(x(1));

pragma "inline"
def _toFollower(iterator: _iteratorClass, leaderIndex)
  return __primitive("to follower", iterator, leaderIndex);

pragma "inline"
def _toFollower(x: _tuple, leaderIndex) {
  pragma "inline" def _toFollowerHelp(x: _tuple, leaderIndex, param dim: int) {
    if dim == x.size-1 then
      return (_toFollower(x(dim), leaderIndex), _toFollower(x(dim+1), leaderIndex));
    else
      return (_toFollower(x(dim), leaderIndex), (..._toFollowerHelp(x, leaderIndex, dim+1)));
  }
  return _toFollowerHelp(x, leaderIndex, 1);
}

def _build_domain_type(dist, param rank : int, type idxType = int,
                       param stridable = false) type
  return new _domain(rank, dist.buildDomain(rank, idxType, stridable));

def _build_domain_type(dist, type ind) type where !__primitive("isEnumType", ind) && !__primitive("isOpaqueType", ind)
  return new _domain(1, dist.buildDomain(ind));

def _build_domain_type(dist, type ind) type where __primitive("isEnumType", ind)
  return new _domain(1, dist.buildEnumDomain(ind));

def _build_domain_type(dist, type ind) type where __primitive("isOpaqueType", ind)
  return new _domain(1, dist.buildOpaqueDomain());

def _build_subdomain_type(dom) type
  return dom.buildSubdomain();

def _build_sparse_subdomain_type(dist, parentDom)
  return new _domain(parentDom.rank,
                     dist.buildSparseDomain(parentDom.rank,
                                            parentDom._value.idxType,
                                            parentDom._value));

def _build_opaque_domain_type(dist) type
  return new _domain(1, dist.buildOpaqueDomain());

def _build_array_type(dom, type eltType) type
  return dom.buildArray(eltType);

def _build_array(dom, type eltType)
  return dom.buildArray(eltType);

def _build_domain(x: _domain)
  return x;

def _build_domain(ranges: range(?eltType,BoundedRangeType.bounded,?stridable) ...?rank) {
  for param i in 2..rank do
    if eltType(1) != eltType(i) then
      compilerError("domain has mixed dimensional type");
  var d: domain(rank, eltType(1), _any_stridable(ranges));
  d.setIndices(ranges);
  return d;
}

def _wrapDomain(d) return new _domain(d.rank, d);

//
// computes || reduction over stridable of ranges
//
def _any_stridable(ranges, param d: int = 1) param {
  if ranges(d).stridable == true then
    return true;
  else if ranges.size > d then
    return _any_stridable(ranges, d+1);
  else
    return false;
}

def _build_open_interval_upper(x: _domain)
  return x.buildOpenIntervalUpper();

def _build_index_type(param rank: int, type idxType) type where rank == 1 {
  var x: idxType;
  return x;
}

def _build_index_type(param rank: int, type idxType) type where rank > 1 {
  var x: rank*idxType;
  return x;
}

def _build_index_type(param rank: int) type
  return _build_index_type(rank, int);

def _build_index_type(d: _domain) type
  return _build_index_type(d.rank, d._value.idxType);

def _build_index_type(type idxType) type where __primitive("isOpaqueType", idxType)
  return _OpaqueIndex;

//
// given a tuple args, returns true if the tuple contains only
// integers and ranges; that is, it is a valid argument list for rank
// change
//
def _validRankChangeArgs(args, type idxType) param {
  def _validRankChangeArg(type idxType, r: range(?e,?b,?s)) param return true;
  def _validRankChangeArg(type idxType, i: idxType) param return true;
  def _validRankChangeArg(type idxType, x) param return false;

  def help(param dim: int) param {
    if !_validRankChangeArg(idxType, args(dim)) then
      return false;
    else if dim < args.size then
      return help(dim+1);
    else
      return true;
  }

  return help(1);
}

def _getRankChangeRanges(args) {
  def isRange(r: range(?e,?b,?s)) param return 1;
  def isRange(r) param return 0;
  def _tupleize(x) {
    var y: 1*x.type;
    y(1) = x;
    return y;
  }
  def collectRanges(param dim: int) {
    if dim > args.size then
      compilerError("domain slice requires a range in at least one dimension");
    if isRange(args(dim)) then
      return collectRanges(dim+1, _tupleize(args(dim)));
    else
      return collectRanges(dim+1);
  }
  def collectRanges(param dim: int, x: _tuple) {
    if dim > args.size {
      return x;
    } else if dim < args.size {
      if isRange(args(dim)) then
        return collectRanges(dim+1, ((...x), args(dim)));
      else
        return collectRanges(dim+1, x);
    } else {
      if isRange(args(dim)) then
        return ((...x), args(dim));
      else
        return x;
    }
  }
  return collectRanges(1);
}

pragma "domain"
record _domain {
  param rank : int;
  var _value;
  var _promotionType: index(rank, _value.idxType);

  def initialize() {
    if _value == nil {
      _value = _value.buildEmptyDomain();
    }
    return this;
  }

  def these() {
    for i in _value.these() do
      yield i;
  }

  def this(ranges: range(?eltType,?boundedType,?stridable) ...rank)
    return new _domain(rank, _value.slice(_any_stridable(ranges), ranges));

  def this(args ...rank) where _validRankChangeArgs(args, _value.idxType) {
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = _any_stridable(ranges);
    return new _domain(rank, _value.rankChange(rank, stridable, args));
  }

  def dim(d : int)
    return _value.dim(d);

  def dimIter(param d, ind) {
    for i in _value.dimIter(d, ind) do yield i;
  }

  def buildArray(type eltType) {
    var x = _value.buildArray(eltType);
    _value._arrs.append(x);
    return new _array(_value.idxType, eltType, rank, x);
  }

  // buildEmptyDomain is meant to return an uninitialized domain for
  // the given domain type. For example, it should return the same
  // sort of thing that var D: domain(2) dist(yourDist); would result
  // in if you didn't initialize it.  It should be a static method and
  // not refer to any fields that aren't types and params.
  def buildEmptyDomain() {
    var x = _value.buildEmptyDomain();
    return new _domain(rank, x);
  }

  def buildSubdomain() {
    var x = _value.buildSubdomain();
    return new _domain(rank, x);
  }

  def buildOpenIntervalUpper()
    return new _domain(rank, _value.buildOpenIntervalUpper());

  def clear() {
    _value.clear();
  }

  def create() {
    if _value.idxType != _OpaqueIndex then
      compilerError("domain.create() only applies to opaque domains");
    return _value.create();
  }

  def add(i) {
    _value.add(i);
  }

  def remove(i) {
    _value.remove(i);
  }

  def numIndices return _value.numIndices;
  def low return _value.low;
  def high return _value.high;

  def member(i) {
    return _value.member(i);
  }

  def order(i) {
    return _value.order(i);
  }

  def position(i) {
    return _value.position(i);
  }

  def expand(i: int ...rank)
    return expand(i);

  def expand(i: rank*int)
    return new _domain(rank, _value.expand(i));

  def expand(i: int) where rank > 1
    return new _domain(rank, _value.expand(i));

  def exterior(i: int ...rank)
    return exterior(i);

  def exterior(i: rank*int) {
    var x = _value.exterior(i);
    return new _domain(rank, x);
  }

  def interior(i: int ...rank)
    return interior(i);

  def interior(i: rank*int) {
    var x = _value.interior(i);
    return new _domain(rank, x);
  }

  def translate(i: int ...rank)
    return translate(i);

  def translate(i: rank*int) {
    var x = _value.translate(i);
    return new _domain(rank, x);
  }

  def subBlocks {
    for d in _value.subBlocks do
      yield d;
  }

  def setIndices(x) {
    _value.setIndices(x);
  }

  def getIndices()
    return _value.getIndices();

  // associative array interface

  def sorted() {
    for i in _value.sorted() {
      yield i;
    }
  }
}

def _isDomain(x: _domain) param return true;
def _isDomain(x) param return false;

def =(a: _domain, b: _domain) {
  for e in a._value._arrs do
    e.reallocate(b);
  a._value.setIndices(b._value.getIndices());
  return a;
}

def =(a: _domain, b: _tuple) {
  for ind in 1..b.size {
    a.add(b(ind));
  }
  return a;
}

def =(d: _domain, r: range(?e,?b,?s)) {
  d = _build_domain(r);
  return d;
}

def =(a: _domain, b) {  // b is iteratable
  a.clear();
  for ind in b {
    a.add(ind);
  }
  return a;
}

pragma "inline" def _pass(a: _domain)
  return a;

def _init(a: _domain)
  return a.buildEmptyDomain();

def _copy(a: _domain) {
  var b: a.buildEmptyDomain().type;
  b.setIndices(a.getIndices());
  return b;
}

def _domain.writeThis(f: Writer) {
  f.write(_value);
}

def by(a: _domain, b) {
  var x = a._value.strideBy(b);
  return new _domain(a.rank, x);
}

// this is a wrapper class for all arrays
pragma "array"
record _array {
  type idxType;
  type eltType;
  param rank : int;
  var _value;
  var _promotionType : eltType;

  def _dom
    return new _domain(rank, _value.dom);

  pragma "inline"
  def this(i: rank*idxType) var where rank > 1
    return _value(i);

  pragma "inline"
  def this(i: idxType ...rank) var where rank > 1
    return _value(i);

  pragma "inline"
  def this(i: idxType) var where rank == 1
    return _value(i);

  //
  // requires dense domain implementation that returns a tuple of
  // ranges via the getIndices() method; domain indexing is difficult
  // in the domain case because it has to be implemented on a
  // domain-by-domain basis; this is not terribly difficult in the
  // dense case because we can represent a domain by a tuple of
  // ranges, but in the sparse case, is there a general
  // representation?
  //
  pragma "valid var"
  def this(d: _domain) var where d.rank == rank
    return this((...d.getIndices()));

  pragma "valid var"
  def this(ranges: range(?_eltType,?boundedType,?stridable) ...rank) var {
    if boundsChecking then
      _value.checkSlice(ranges);
    return new _array(idxType, eltType, rank, _value.slice(_dom((...ranges))._value));
  }

  pragma "valid var"
  def this(args ...rank) var where _validRankChangeArgs(args, idxType) {
    if boundsChecking then
      _value.checkRankChange(args);
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = _any_stridable(ranges);
    return new _array(idxType, eltType, rank, _value.rankChange(rank, stridable, args));
  }

  def these() var {
    for i in _value.these() do
      yield i;
  }

  def numElements return _dom.numIndices; // assume dom name

  def reindex(d: _domain) where rank == 1 {
    var x = _value.reindex(d._value);
    return new _array(x.idxType, eltType, rank, x);
  }

  def reindex(d: _domain) where rank != 1 {
    var x = _value.reindex(d._value);
    return new _array(x.idxType, eltType, rank, x);
  }

  // sparse array interface

  def IRV var {
    return _value.IRV;
  }

  // associative array interface

  def sorted() {
    for i in _value.sorted() {
      yield i;
    }
  }
}

pragma "inline" def =(a: _array, b) {
  for (i,bb) in (a._dom,b) do
    a(i) = bb;
  return a;
}

def =(a: _array, b: _tuple) {
  a._value.tupleInit(b);
  return a;
}

def _desync(type t) where t: _syncvar || t: _singlevar {
  var x: t;
  return x.value;
}

def _desync(type t) where !(t: _syncvar|| t: _singlevar) {
  var x: t;
  return x;
}

def =(a: _array, b: _desync(a.eltType)) {
  for i in a._dom do
    a(i) = b;
  return a;
}

def _copy(a: _array) {
  var b : [a._dom] a.eltType;
  b = a;
  return b;
}

pragma "inline" def _pass(a: _array) {
  return a;
}

def _init(a: _array) {
  return a._dom.buildArray(a.eltType);
}

def _init(type t) where t: _array {
  return __primitive("build_array", t);
}

def _array.writeThis(f: Writer) {
  f.write(_value);
}

// this is the class that all array classes are derived from
pragma "BaseArray"
class BaseArray {
  def reallocate(d: _domain) {
    halt("reallocating not support 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");
  }
}

class BaseDomain {
  var _arrs: list(BaseArray);

  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);
  }
}

class BaseArithmeticDomain : BaseDomain {
  def bbox(d:int) {
    halt("bbox not supported for this domain type");
    return 1..0;
  }
}

class BaseSparseArithmeticDomain : BaseArithmeticDomain {
}

//
// index for all opaque domains
//
class _OpaqueIndex { }

//
// Swap operators for arrays and domains
//
pragma "inline" def _chpl_swap(x: [], y: []) {
  for (i,j) in (x.domain, y.domain) do
    x(i) <=> y(j);
}

pragma "inline" def _chpl_swap(x: domain, y: domain) {
  const t = y;
  y = x;
  x = t;
}

//
// reshape function
//
def reshape(A: [], D: domain) {
  var B: [D] A.eltType;
  for (i,a) in (D,A) do
    B(i) = a;
  return B;
}

// This should go away once distributions are implemented
def distributed_warning(d) {
  if (numLocales > 1) {
    __primitive("chpl_warning", "'distributed' domains/arrays are not yet distributed across multiple locales");
  }
  return d;
}

//
// module support for iterators
//
def iteratorIndex(ic: _iteratorClass) {
  ic.advance();
  return ic.getValue();
}

pragma "expand tuples with values"
def iteratorIndex(t: _tuple) {
  pragma "expand tuples with values"
  def iteratorIndexHelp(t: _tuple, param dim: int) {
    if dim == t.size-1 then
      return _build_tuple_always_allow_ref(iteratorIndex(t(dim)), iteratorIndex(t(dim+1)));
    else
      return _build_tuple_always_allow_ref(iteratorIndex(t(dim)), (...iteratorIndexHelp(t, dim+1)));
  }

  return iteratorIndexHelp(t, 1);
}

def iteratorIndexType(x) type {
  return iteratorIndex(x).type;
}

def _iteratorClass.writeThis(f: Writer) {
  var first: bool = true;
  for e in this {
    if !first then
      f.write(" ");
    else
      first = false;
    f.write(e);
  }
}

def =(ic: _iteratorClass, xs) {
  for (e, x) in (ic, xs) do
    e = x;
  return ic;
}

def =(ic: _iteratorClass, x: iteratorIndexType(ic)) {
  for e in ic do
    e = x;
  return ic;
}

pragma "inline" def _pass(ic: _iteratorClass)
  return ic;

def _copy(ic: _iteratorClass) {
  return _ic_copy_help(_ic_copy_recursive(ic));

  def _ic_copy_recursive(ic) {
    for e in ic do
      yield _copy(e);
  }

  def _ic_copy_help(ic) {
    var i = 1, size = 4;
    var D = [1..size];

    // note that _getIterator is called in order to copy the iterator
    // class since for arrays we need to iterate once to get the
    // element type (at least for now); this also means that if this
    // iterator has side effects, we will see them; a better way to
    // handle this may be to get the static type (not initialize the
    // array) and use a primitive to set the array's element; that may
    // also handle skyline arrays
    var A: [D] iteratorIndexType(_getIterator(ic));

    for e in ic {
      if i > size {
        size = size * 2;
        D = [1..size];
      }
      A(i) = e;
      i = i + 1;
    }
    D = [1..i-1];
    return A;
  }
}

pragma "inline" pragma "iterator class copy"
def _getIterator(ic: _iteratorClass)
  return ic;

pragma "inline" pragma "ref" def _getIterator(x)
  return x.these();

pragma "inline" def _getIterator(x: _tuple) {
  pragma "inline" def _getIteratorHelp(x: _tuple, param dim: int) {
    if dim == x.size-1 then
      return (_getIterator(x(dim)), _getIterator(x(dim+1)));
    else
      return (_getIterator(x(dim)), (..._getIteratorHelp(x, dim+1)));
  }
  return _getIteratorHelp(x, 1);
}

def _getIterator(type t) {
  compilerError("cannot iterate over a type");
}

pragma "inline"
def _toLeader(iterator: _iteratorClass)
  return __primitive("to leader", iterator);

pragma "inline"
def _toLeader(x: _tuple)
  return _toLeader(x(1));

pragma "inline"
def _toFollower(iterator: _iteratorClass, leaderIndex)
  return __primitive("to follower", iterator, leaderIndex);

pragma "inline"
def _toFollower(x: _tuple, leaderIndex) {
  pragma "inline" def _toFollowerHelp(x: _tuple, leaderIndex, param dim: int) {
    if dim == x.size-1 then
      return (_toFollower(x(dim), leaderIndex), _toFollower(x(dim+1), leaderIndex));
    else
      return (_toFollower(x(dim), leaderIndex), (..._toFollowerHelp(x, leaderIndex, dim+1)));
  }
  return _toFollowerHelp(x, leaderIndex, 1);
}


Updated declarations in sparse example to implement
Steve's suggestion to make them more conversational
like the rest of the example.

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

/*
 * This test shows off some of Chapel's initial support for sparse
 * domains and arrays.  These features are still under development,
 * so your feedback on them is particularly valuable.
 */

//
// First, we declare a configuration variable, n, which defines the
// problem size for this example.  It's given a default value of 9,
// which can be over-ridden on the executable's command line using:
// --n=<value>
//
config var n = 9;

//
// Sparse domains in Chapel are defined in terms of a bounding domain.
// The role of this bounding domain is to define the range of legal
// indices for the sparse domain.  Here we declare a dense 2D
// arithmetic bounding domain of n x n indices which will serve as the
// index space for the sparse domain/array in our example.  As we will
// see, it will also be useful for operations that want to treat our
// sparse domain as though it was a dense n x n set of values.
//
const dnsDom = [1..n, 1..n];

//
// Here we declare our sparse domain.  The sparse keyword indicates
// that it will be used to only represent a subset of its bounding
// domain's indices, and that arrays declared using it will store a
// "zero" value (described further below) for all indices in the set
// dnsDom - spsDom.  Because we don't initialize the sparse domain, it
// is initially an empty set of indices.
//
var spsDom: sparse subdomain(dnsDom);

//
// Next, we use the sparse domain to declare a sparse array.  This
// uses Chapel's normal array declaration syntax.
//
var spsArr: [spsDom] real;

//
// I/O on sparse domains and arrays only prints out "non-zeroe" values;
// initially there are none, so these will both print in a degenerate manner
//
writeln("Initially, spsDom is: ", spsDom);
writeln("Initially, spsArr is: ", spsArr);
writeln();

//
// we can also do I/O more explicitly by iterating over the dense
// domain and indexing into the sparse array.  Note that it's legal to
// index into a sparse array in either its "zero" or "nonzero"
// positions; however it's only legal to assign to "nonzero"
// positions, since those are the only ones that are explicitly
// stored.
//
def writeSpsArr() {
  for (i,j) in dnsDom {
    write(spsArr(i,j), " ");
    if (j == n) then writeln();
  }
  writeln();
}

//
// Let's try that function we just wrote:
//
writeln("Printing spsArr with a dense representation:");
writeSpsArr();

//
// Chapel's sparse arrays store the element type's default value for
// their "zero" value by default -- so zeroes for numerical types,
// empty strings for strings, nil references for classes, etc.
// However, a different value can be stored at "zero" positions
// instead which is why we don't refer to it as the "zero" value and
// rather as the IRV or "Implicitly Replicated Value".  This value can
// be changed for a given array by assigning to that array's IRV
// field, allowing a more interesting value/string/class instance to
// be stored at all the "nonzero" values.
//
spsArr.IRV = 7.7;
writeln("Printing spsArr after changing its IRV:");
writeSpsArr();

//
// OK, now let's actually add some sparse indices to the DaSps domain
// and see what happens:
//
spsDom += (1,n);
spsDom += (n,n);
spsDom += (1,1);
spsDom += (n,1);

writeln("Printing spsArr after adding the corner indices:");
writeSpsArr();

//
// It appears as though nothing happened, but in fact it did.  The
// sparse domain spsDom was reallocated to store the four new (corner)
// indices; the sparse array was reallocated to allocate storage for
// the four new elements corresponding to those indices; and those
// elements were initialized to store the IRV, since that's the
// logical value that they were representing before the new sparse
// indices "filled in".  We can see this difference by going back to
// the default sparse I/O:
//
writeln("After adding corners, spsDom is:\n", spsDom);
writeln("After adding corners, spsArr is:\n", spsArr);
writeln();

//
// Or by assigning the array elements corresponding to the corners
// and using our dense printing function:
//
def computeVal(row, col) return row + col/10.0;

spsArr(1,1) = computeVal(1,1);
spsArr(1,n) = computeVal(1,n);
spsArr(n,1) = computeVal(n,1);
spsArr(n,n) = computeVal(n,n);

writeln("Printing spsArr after assigning the corner elements:");
writeSpsArr();

//
// Like other domains and arrays, sparse Chapel domains and arrays can
// be iterated over...
//

writeln("Iterating over spsDom and indexing into spsArr:");
for ij in spsDom do
  writeln("spsArr(", ij, ") = ", spsArr(ij));
writeln();

writeln("Iterating over spsArr:");
for a in spsArr do
  writeln(a, " ");
writeln();

//
// ...reductions can be taken...
//
var sparseSum = + reduce spsArr;
var denseSum = + reduce [ij in dnsDom] spsArr(ij);

writeln("the sum of the sparse elements is: ", sparseSum);
writeln("the sum of the dense elements is: ", denseSum);
writeln();

//
// ...and slices will be allowed to be taken (sparse slices of dense
// arrays, dense slices of sparse arrays, sparse slices of sparse
// arrays, etc.), but those aren't implemented yet... :(
//

//
// OK, let's clear things out and start again, this time defining a
// sparse domain whose diagonal elements are represented.
//
spsDom.clear();     // empty the sparse index set
spsArr.IRV = 0.0;   // reset the IRV

for i in 1..n do
  spsDom += (i,i);

[(i,j) in spsDom] spsArr(i,j) = computeVal(i,j);

writeln("Printing spsArr after resetting and adding the diagonal:");
writeSpsArr();

//
// Here are some other ways to enumerate sparse indices.  You can
// assign a sparse domain a tuple of indices:
//

spsDom.clear();
spsDom = ((1,1), (n/2, n/2), (n,n));

[(i,j) in spsDom] spsArr(i,j) = computeVal(i,j);

writeln("Printing spsArr after resetting and assigning a tuple of indices:");
writeSpsArr();

//
// You can also define an iterator and have it generate the sparse
// indices:
//

iterator antiDiag(n) {
  for i in 1..n do
    yield (i, n-i+1);
}

spsDom = antiDiag(n);

[(i,j) in spsDom] spsArr(i,j) = computeVal(i,j);

writeln("Printing spsArr after resetting and assigning the antiDiag iterator:");
writeSpsArr();

//
// We'll close with a brief note on performance: Chapel's default
// single-locale sparse format represents the domain using a dense
// sorted vector of explicitly represented indices and the array using
// a dense vector of elements.  This format is general, meaning that
// we can support 1D, 2D, 3D, ..., nD sparse domains and arrays in
// addition to the 2D case shown here.  However, it is also very
// general which is why we use it.  As Chapel continues to develop, it
// will support additional sparse formats that make various tradeoffs
// in efficiency and representation as part of its standard
// distribution library.  In addition, advanced users will be able to
// define their own sparse domain/array representations using the
// user-defined distribution capability.
//
// Regardless of the sparse format used, operations over a sparse
// domain's indices or sparse array's elements should typically be
// proportional to the number of nonzeroes (nnz) rather than the size
// of the dense bounding box.  Operations like inserting new indices
// or testing for membership will tend to vary depending on the
// representation.
//
// For example, in the default representation, adding indices in
// reverse sorted order will require O(nnz**2) time due to all of the
// insertions required.  For this reason, users are encouraged to add
// indices in sorted order when performance matters.
//
// Other general rules of thumb when working with sparse domains and
// arrays is to make the domains constant (const) whenever possible,
// setting their index set in their initializer using a forall
// expression or iterator invocation; and to assign sparse domain
// indices before declaring arrays over those sparse domains when
// possible (to avoid reallocating the arrays more than is necessary.
// While the choice of a sparse representations may make the impact of
// these decisions more or less crucial, they are good general rules
// of thumb.
//
// As a final performance-related note, there are several important
// compiler optimizations that remain unimplemented for sparse domains
// and arrays.  Today's implementation is meant to expose users to
// Chapel's sparse concepts, but additional performance will be added
// over time.
//
// Anyone reading this far who is interested in exploring more with
// sparse domains and arrays in Chapel is encouraged to contact us at
// chapel_info@cray.com.  We currently have a Compressed Sparse Row
// (CSR) sparse domain/array implementation in the works and a
// virtually complete implementation of the NAS CG benchmark that
// makes use of it, and would be happy to share those examples with
// users of the release.

/*
 * This test shows off some of Chapel's initial support for sparse
 * domains and arrays.  These features are still under development,
 * so your feedback on them is particularly valuable.
 */

config var n = 9;                       // Problem size;

const dnsDom = [1..n, 1..n];            // Dense bounding domain

var spsDom: sparse subdomain(dnsDom);   // 2D sparse subdomain of D
var spsArr: [spsDom] real;              // 2D sparse array

//
// I/O on sparse domains and arrays only prints out "non-zeroe" values;
// initially there are none, so these will both print in a degenerate manner
//
writeln("Initially, spsDom is: ", spsDom);
writeln("Initially, spsArr is: ", spsArr);
writeln();

//
// we can also do I/O more explicitly by iterating over the dense
// domain and indexing into the sparse array.  Note that it's legal to
// index into a sparse array in either its "zero" or "nonzero"
// positions; however it's only legal to assign to "nonzero"
// positions, since those are the only ones that are explicitly
// stored.
//
def writeSpsArr() {
  for (i,j) in dnsDom {
    write(spsArr(i,j), " ");
    if (j == n) then writeln();
  }
  writeln();
}

//
// Let's try that function we just wrote:
//
writeln("Printing spsArr with a dense representation:");
writeSpsArr();

//
// Chapel's sparse arrays store the element type's default value for
// their "zero" value by default -- so zeroes for numerical types,
// empty strings for strings, nil references for classes, etc.
// However, a different value can be stored at "zero" positions
// instead which is why we don't refer to it as the "zero" value and
// rather as the IRV or "Implicitly Replicated Value".  This value can
// be changed for a given array by assigning to that array's IRV
// field, allowing a more interesting value/string/class instance to
// be stored at all the "nonzero" values.
//
spsArr.IRV = 7.7;
writeln("Printing spsArr after changing its IRV:");
writeSpsArr();

//
// OK, now let's actually add some sparse indices to the DaSps domain
// and see what happens:
//
spsDom += (1,n);
spsDom += (n,n);
spsDom += (1,1);
spsDom += (n,1);

writeln("Printing spsArr after adding the corner indices:");
writeSpsArr();

//
// It appears as though nothing happened, but in fact it did.  The
// sparse domain spsDom was reallocated to store the four new (corner)
// indices; the sparse array was reallocated to allocate storage for
// the four new elements corresponding to those indices; and those
// elements were initialized to store the IRV, since that's the
// logical value that they were representing before the new sparse
// indices "filled in".  We can see this difference by going back to
// the default sparse I/O:
//
writeln("After adding corners, spsDom is:\n", spsDom);
writeln("After adding corners, spsArr is:\n", spsArr);
writeln();

//
// Or by assigning the array elements corresponding to the corners
// and using our dense printing function:
//
def computeVal(row, col) return row + col/10.0;

spsArr(1,1) = computeVal(1,1);
spsArr(1,n) = computeVal(1,n);
spsArr(n,1) = computeVal(n,1);
spsArr(n,n) = computeVal(n,n);

writeln("Printing spsArr after assigning the corner elements:");
writeSpsArr();

//
// Like other domains and arrays, sparse Chapel domains and arrays can
// be iterated over...
//

writeln("Iterating over spsDom and indexing into spsArr:");
for ij in spsDom do
  writeln("spsArr(", ij, ") = ", spsArr(ij));
writeln();

writeln("Iterating over spsArr:");
for a in spsArr do
  writeln(a, " ");
writeln();

//
// ...reductions can be taken...
//
var sparseSum = + reduce spsArr;
var denseSum = + reduce [ij in dnsDom] spsArr(ij);

writeln("the sum of the sparse elements is: ", sparseSum);
writeln("the sum of the dense elements is: ", denseSum);
writeln();

//
// ...and slices will be allowed to be taken (sparse slices of dense
// arrays, dense slices of sparse arrays, sparse slices of sparse
// arrays, etc.), but those aren't implemented yet... :(
//

//
// OK, let's clear things out and start again, this time defining a
// sparse domain whose diagonal elements are represented.
//
spsDom.clear();     // empty the sparse index set
spsArr.IRV = 0.0;   // reset the IRV

for i in 1..n do
  spsDom += (i,i);

[(i,j) in spsDom] spsArr(i,j) = computeVal(i,j);

writeln("Printing spsArr after resetting and adding the diagonal:");
writeSpsArr();

//
// Here are some other ways to enumerate sparse indices.  You can
// assign a sparse domain a tuple of indices:
//

spsDom.clear();
spsDom = ((1,1), (n/2, n/2), (n,n));

[(i,j) in spsDom] spsArr(i,j) = computeVal(i,j);

writeln("Printing spsArr after resetting and assigning a tuple of indices:");
writeSpsArr();

//
// You can also define an iterator and have it generate the sparse
// indices:
//

iterator antiDiag(n) {
  for i in 1..n do
    yield (i, n-i+1);
}

spsDom = antiDiag(n);

[(i,j) in spsDom] spsArr(i,j) = computeVal(i,j);

writeln("Printing spsArr after resetting and assigning the antiDiag iterator:");
writeSpsArr();

//
// We'll close with a brief note on performance: Chapel's default
// single-locale sparse format represents the domain using a dense
// sorted vector of explicitly represented indices and the array using
// a dense vector of elements.  This format is general, meaning that
// we can support 1D, 2D, 3D, ..., nD sparse domains and arrays in
// addition to the 2D case shown here.  However, it is also very
// general which is why we use it.  As Chapel continues to develop, it
// will support additional sparse formats that make various tradeoffs
// in efficiency and representation as part of its standard
// distribution library.  In addition, advanced users will be able to
// define their own sparse domain/array representations using the
// user-defined distribution capability.
//
// Regardless of the sparse format used, operations over a sparse
// domain's indices or sparse array's elements should typically be
// proportional to the number of nonzeroes (nnz) rather than the size
// of the dense bounding box.  Operations like inserting new indices
// or testing for membership will tend to vary depending on the
// representation.
//
// For example, in the default representation, adding indices in
// reverse sorted order will require O(nnz**2) time due to all of the
// insertions required.  For this reason, users are encouraged to add
// indices in sorted order when performance matters.
//
// Other general rules of thumb when working with sparse domains and
// arrays is to make the domains constant (const) whenever possible,
// setting their index set in their initializer using a forall
// expression or iterator invocation; and to assign sparse domain
// indices before declaring arrays over those sparse domains when
// possible (to avoid reallocating the arrays more than is necessary.
// While the choice of a sparse representations may make the impact of
// these decisions more or less crucial, they are good general rules
// of thumb.
//
// As a final performance-related note, there are several important
// compiler optimizations that remain unimplemented for sparse domains
// and arrays.  Today's implementation is meant to expose users to
// Chapel's sparse concepts, but additional performance will be added
// over time.
//
// Anyone reading this far who is interested in exploring more with
// sparse domains and arrays in Chapel is encouraged to contact us at
// chapel_info@cray.com.  We currently have a Compressed Sparse Row
// (CSR) sparse domain/array implementation in the works and a
// virtually complete implementation of the NAS CG benchmark that
// makes use of it, and would be happy to share those examples with
// users of the release.


Minor unit test change

Minor unit test change

use Collection;

// Tests that all items added are returned.
proc counterTest(c : Collection(int)) {
  const nElems = 100;
  const expected = (nElems * (nElems + 1)) / 2;

  // Fill collection concurrently...
  c.unfreeze();
  forall i in 1 .. nElems {
    c.add(i);
  }
  c.freeze();

  // Test to see if it contains middle element
  assert(c.contains((nElems / 2) : int));

  // Iterate over the collection.
  var actual = 0;
  for elem in c {
    actual = actual + elem;
  }
  assert(actual == expected);

  // Iterate concurrently over the collection.
  var concurrentActual : atomic int;
  forall elem in c {
    concurrentActual.add(elem);
  }
  assert(concurrentActual.read() == expected);

  // Empty collection
  c.unfreeze();
  concurrentActual.write(0);
  coforall loc in Locales do on loc {
    var perLocaleActual : atomic int;
    coforall tid in 0..#here.maxTaskPar {
      var (hasElem, elt) = (true, 0);
      var perTaskActual : int;
      while hasElem {
        perTaskActual = perTaskActual + elt;
        (hasElem, elt) = c.remove();
      }
      perLocaleActual.add(perTaskActual);
    }
    concurrentActual.add(perLocaleActual.read());
  }

  assert(c.size() == 0 && c.isEmpty());
}

use Collection;

// Tests that all items added are returned.
proc counterTest(c : Collection(int)) {
  const nElems = 100;
  const expected = (nElems * (nElems + 1)) / 2;

  // Fill collection concurrently...
  c.unfreeze();
  forall i in 1 .. nElems {
    c.add(i);
  }
  c.freeze();

  // Iterate over the collection.
  var actual = 0;
  for elem in c {
    actual = actual + elem;
  }
  assert(actual == expected);

  // Iterate concurrently over the collection.
  var concurrentActual : atomic int;
  forall elem in c {
    concurrentActual.add(elem);
  }
  assert(concurrentActual.read() == expected);

  // Empty collection
  c.unfreeze();
  concurrentActual.write(0);
  coforall loc in Locales do on loc {
    coforall tid in 0..#here.maxTaskPar {
      var (hasElem, elt) = (true, 0);
      while hasElem {
        concurrentActual.add(elt);
        (hasElem, elt) = c.remove();
      }
    }
  }

  assert(c.size() == 0 && c.isEmpty());
}


Drat, I forgot the copyright file

Drat, I forgot the copyright file

/*
 * 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.
 */

// Wrapper for FFTW version 3.

/*
  Support for key FFTW version 3 routines in Chapel trappings

  This module defines a Chapel wrapper around key routines from FFTW
  version 3.  Over time, the intention is to expand this support to
  additional routines, prioritizing based on user feedback.

  As in general FFTW, the flow is to:

  0) Call init_threads() and plan_with_nthreads() if using the
     multithreaded FFTW interface.

  1) Create plan(s) using the plan_dft*() routines.

  2) Execute the plan(s) using the execute() routine.

  3) Destroy the plan(s) using the destroy_plan() routine.

  4) If using the multithreaded FFTW interface, call
     cleanup_threads().

  5) Call cleanup().

  At present, clients of the FFTW module must specify fftw3.h and -I,
  -L, -l flags sufficient to specify the required libraries and find
  the files on the 'chpl' command line.  We are currently
  investigating techniques to streamline this process.
*/

// TODOs for Brad (in priority order):
//
// - Can we get a form of use/requires in that will simplify the
//   command-line?
//
// - How do we feel about FFTW_ALLCAPS names given that the routines
//   themselves don't have fftw_ prefixes, that they're defined as
//   c_int's, and that they're all-caps?
//
// - Related question about fftw_ types taking type c_int, though
//   maybe we don't care that much since symbolic values are taken
//   in?
//

module FFTW {

  use SysCTypes;

  // Define the various planner flags
  // See Sec. 4.3.2 of FFTW manual "Planner Flags"

  //
  // TODO: Is it weird that we use FFTW_ names here, but not in
  // the routines?  Should we wrap the external flags so that
  // they can be ints or enums and avoid the all-caps C scheme
  // or is the familiarity better?
  //

  extern const FFTW_FORWARD : c_int;
  /* These two constants are symbolic aliases for making the sign of
     the exponent in the formula that defines the Fourier transform
     -1 or 1, respectively. */
  extern const FFTW_BACKWARD : c_int;

  extern const FFTW_ESTIMATE : c_uint;
  extern const FFTW_MEASURE : c_uint;
  extern const FFTW_PATIENT : c_uint;
  extern const FFTW_EXHAUSTIVE : c_uint;
  /*
     All of the above are planning-rigor flags.  See `Section 4.3.2
     <http://www.fftw.org/doc/Planner-Flags.html>`_ of the FFTW manual
     for details.
  */
  extern const FFTW_WISDOM_ONLY : c_uint;
  extern const FFTW_DESTROY_INPUT : c_uint;
  extern const FFTW_PRESERVE_INPUT : c_uint;
  /*
     All of the above are algorithm-restriction flags.  See `Section
     4.3.2 <http://www.fftw.org/doc/Planner-Flags.html>`_ of the FFTW
     manual for details.
  */
  extern const FFTW_UNALIGNED : c_uint;

  /*
    'fftw_plan' is an opaque type used by FFTW for storing and passing
    plans between routines.
  */
  extern type fftw_plan; // opaque type

  // Multi-threaded setup routines below
  /*
    Initialize the threads used for multi-threaded FFTW plans.

    :returns: Zero if there was an error, non-zero otherwise.
  */
  proc init_threads() : c_int {
    return C_FFTW.fftw_init_threads();
  }

  /*
    Registers the number of threads to use for multi-threaded FFTW
    plans.

    :arg nthreads: The number of threads to use.
    :type nthreads: int
   */
  proc plan_with_nthreads(nthreads: int) {
    C_FFTW.fftw_plan_with_nthreads(nthreads.safeCast(c_int));
  }

  // Planner functions
  // Complex : 4.3.1
  // NOTE : We pass in arrays using ref

  // TODO: Can we have the plan_dft() routine below take in native
  // Chapel types without changing the external C constants from
  // type c_int?  Probably given that Chapel's int will be >=
  // C's int for the forseeable future.  See also the TODO above
  // about whether we'd want to represent those constants using
  // more native Chapel types anyway.

  /*
    Creates a plan for a complex->complex DFT.

    :arg input: The input array, which can be of any rank
    :type input: [] `complex(128)`

    :arg output: The output array, with rank matching the input array's
    :type output: [] `complex(128)`

    :arg sign: The sign of the exponent in the DFT formula.
    :type sign: c_int

    :arg flags: planning-rigor and/or algorithm-restriction flags
    :type sign: c_int

    :returns: The fftw_plan representing the plan
  */
  proc plan_dft(input: [] complex(128), output: [] complex(128),
                 sign: c_int, flags: c_uint) : fftw_plan
  {
    param rank = input.rank;

    var dims: rank*c_int;
    for param i in 1..rank do
      dims(i) = input.domain.dim(i).size.safeCast(c_int);

    return C_FFTW.fftw_plan_dft(rank.safeCast(c_int), dims, input,
                                     output, sign, flags);
  }

  // Real-to-complex and complex-to-real planning routines There are
  // two cases that we treat independently here : in-place and
  // out-of-place transforms

  // Out-of-place routines
  proc plan_dft_r2c(input : [] real(64), output : [] complex(128), flags :c_uint) : fftw_plan
  {
    param rank = input.rank: c_int;

    var dims: rank*c_int;
    for param i in 1..rank do
      dims(i) = input.domain.dim(i).size: c_int;

    return C_FFTW.fftw_plan_dft_r2c(rank, dims, input, output, flags);
  }

  proc plan_dft_c2r(input : [] complex(128), output : [] real(64),  flags :c_uint) : fftw_plan
  {
    param rank = output.rank: c_int; // The dimensions are that of the real array

    var dims: rank*c_int;
    for param i in 1..rank do
      dims(i) = output.domain.dim(i).size: c_int;

    return C_FFTW.fftw_plan_dft_c2r(rank, dims, input, output, flags);
  }

  // In-place routines, note that these take in the true leading dimension
  // TODO : We should put in checks to see that the sizes are consistent
  // TODO : We should check on types...
  proc plan_dft_r2c(realDom : domain, input : [] ?t, flags : c_uint) : fftw_plan
  {
    param rank = realDom.rank: c_int;

    var dims: rank*c_int;
    for param i in 1..rank do
      dims(i) = realDom.dim(i).size: c_int;

    return C_FFTW.fftw_plan_dft_r2c(rank, dims, input, input, flags);
  }
  proc plan_dft_c2r(realDom : domain, input: [] ?t, flags : c_uint) : fftw_plan
  {
    param rank = realDom.rank: c_int;

    var dims: rank*c_int;
    for param i in 1..rank do
      dims(i) = realDom.dim(i).size: c_int;

    return C_FFTW.fftw_plan_dft_c2r(rank, dims, input, input, flags);
  }

  // Using plans
  /*
    Executes the given plan.

    :arg plan: The plan to execute, as computed by a plan*() routine.
    :type plan: fftw_plan
  */
  proc execute(const plan: fftw_plan) {
   C_FFTW.fftw_execute(plan);
  }

  /*
    Destroys the given plan.

    :arg plan: The plan to destroy
    :type plan: fftw_plan
  */
  proc destroy_plan(plan: fftw_plan) {
    C_FFTW.fftw_destroy_plan(plan);
  }

  /*
    Cleans up the threads if multi-threaded FFTW's were enabled using
    :proc:`init_threads()`.
  */
  proc cleanup_threads() {
    C_FFTW.fftw_cleanup_threads();
  }

  /*
    Cleans up FFTW overall.
  */
  proc cleanup() {
    C_FFTW.fftw_cleanup();
  }

  pragma "no doc"
  module C_FFTW {
    extern proc fftw_plan_dft(rank: c_int,
                              n,  // BLC: having trouble being specific
                              in1: [] complex(128),
                              out1: [] complex(128),
                              sign : c_int, c_flags : c_uint) : fftw_plan;

    extern proc fftw_plan_dft_r2c(rank: c_int,
                                  n,  // BLC: having trouble being specific
                                  in1: [],
                                  out1: [],
                                  c_flags : c_uint) : fftw_plan;

    extern proc fftw_plan_dft_c2r(rank: c_int,
                                  n,  // BLC: having trouble being specific
                                  in1: [],
                                  out1: [],
                                  c_flags : c_uint) : fftw_plan;

    extern proc fftw_execute(const plan : fftw_plan);

    extern proc fftw_destroy_plan(plan : fftw_plan);

    extern proc fftw_cleanup();

    extern proc fftw_init_threads() : c_int;

    extern proc fftw_cleanup_threads();

    extern proc fftw_plan_with_nthreads(n : c_int);
  }
}