removed redundant _array.eltType generic type field

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

//
// Support for domain types
//
pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, param rank: int, type idxType = int(32),
                       param stridable: bool = false) type
  return new _domain(rank, dist.newArithmeticDomain(rank, idxType, stridable));

pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where !__primitive("isEnumType", idxType) && idxType != opaque && idxType != _OpaqueIndex
  return new _domain(1, dist.newAssociativeDomain(idxType));

pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where __primitive("isEnumType", idxType)
  return new _domain(1, dist.newEnumeratedDomain(idxType));

pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where idxType == _OpaqueIndex
  return new _domain(1, dist.newOpaqueDomain(idxType));

// This function has no 'has runtime type' pragma since the idxType of
// opaque domains is _OpaqueIndex, not opaque.  This function is
// essentially a wrapper around the function that actually builds up
// the runtime type.
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where idxType == opaque
  return chpl_buildDomainRuntimeType(dist, _OpaqueIndex);

pragma "has runtime type"
def chpl_buildSparseDomainRuntimeType(dist, dom: _domain) type
  return new _domain(dom.rank, dist.newSparseDomain(dom.rank, dom._value.idxType, dom));

def chpl_convertValueToRuntimeType(dom: _domain) type
 where dom._value:BaseArithmeticDomain
  return chpl_buildDomainRuntimeType(dom._value.dist, dom._value.rank,
                            dom._value.idxType, dom._value.stridable);

def chpl_convertValueToRuntimeType(dom: _domain) type
 where dom._value:BaseSparseDomain
  return chpl_buildSparseDomainRuntimeType(dom._value.dist, dom._value.parentDom);

def chpl_convertValueToRuntimeType(dom: _domain) type
where !dom._value:BaseArithmeticDomain && !dom._value:BaseSparseDomain
  return chpl_buildDomainRuntimeType(dom._value.dist, dom._value.idxType);

//
// Support for array types
//
pragma "has runtime type"
def chpl_buildArrayRuntimeType(dom: _domain, type eltType) type
  return dom.buildArray(eltType);

def chpl_convertValueToRuntimeType(arr: _array) type
  return chpl_buildArrayRuntimeType(arr.domain, arr.eltType);

//
// Support for subdomain types
//
// Note the domain of a subdomain is not yet part of the runtime type
//
def chpl_buildSubDomainType(dom: _domain) type
  return chpl_convertValueToRuntimeType(dom);

//
// Support for domain expressions, e.g., [1..3, 1..3]
//
def chpl_buildDomainExpr(x: _domain)
  return x;

def chpl_buildDomainExpr(ranges: range(?) ...?rank) {
  for param i in 2..rank {
    if ranges(1).eltType != ranges(i).eltType then
      compilerError("domain has mixed dimensional type");
    if ranges(i).boundedType != BoundedRangeType.bounded then
      compilerError("domain has dimension of unbounded range");
  }
  var d: domain(rank, ranges(1).eltType, chpl_anyStridable(ranges));
  d.setIndices(ranges);
  return d;
}

//
// Support for index types
//
def chpl_buildIndexType(param rank: int, type idxType) type where rank == 1 {
  var x: idxType;
  return x;
}

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

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

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

def chpl_buildIndexType(type idxType) type where idxType == opaque
  return _OpaqueIndex;

//
// Domain wrapper record
//
pragma "domain"
pragma "has runtime type"
record _domain {
  param rank: int;
  var _value;
  var _promotionType: index(rank, _value.idxType);

  def ~_domain() {
    delete _value;
  }

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

  def this(ranges: range(?) ...rank)
    return new _domain(rank, _value.slice(chpl_anyStridable(ranges), ranges));

  def this(args ...rank) where _validRankChangeArgs(args, _value.idxType) {
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = chpl_anyStridable(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(rank, x);
  }

  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) return new _domain(rank, _value.exterior(i));

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

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

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

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

  def getIndices()
    return _value.getIndices();

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

  // associative array interface

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

//
// Array wrapper record
//
pragma "array"
pragma "has runtime type"
record _array {
  param rank: int;
  var _value;
  var _promotionType: _value.eltType;

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

  def ~_array() {
    delete _value;
  }

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

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

  pragma "inline"
  def this(i: _value.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(?) ...rank) var {
    if boundsChecking then
      _value.checkSlice(ranges);
    return new _array(rank, _value.slice(_dom((...ranges))._value));
  }

  pragma "valid var"
  def this(args ...rank) var where _validRankChangeArgs(args, _value.idxType) {
    if boundsChecking then
      _value.checkRankChange(args);
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = chpl_anyStridable(ranges);
    return new _array(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(rank, x);
  }

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

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

  // sparse array interface

  def IRV var {
    return _value.IRV;
  }

  // associative array interface

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

//
// Helper functions
//

// computes || reduction over stridable of ranges
def chpl_anyStridable(ranges, param d: int = 1) param {
  for param i in 1..ranges.size do
    if ranges(i).stridable then
      return true;
  return false;
}

// 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(?)) 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(?)) 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);
}

//
// Support for += and -= over domains
//
def chpl_isDomain(x: _domain) param return true;
def chpl_isDomain(x) param return false;

//
// Assignment of domains and arrays
//
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(?)) {
  d = [r];
  return d;
}

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

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

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

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

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

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

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

//
// Support for domain types
//
pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, param rank: int, type idxType = int(32),
                       param stridable: bool = false) type
  return new _domain(rank, dist.newArithmeticDomain(rank, idxType, stridable));

pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where !__primitive("isEnumType", idxType) && idxType != opaque && idxType != _OpaqueIndex
  return new _domain(1, dist.newAssociativeDomain(idxType));

pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where __primitive("isEnumType", idxType)
  return new _domain(1, dist.newEnumeratedDomain(idxType));

pragma "has runtime type"
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where idxType == _OpaqueIndex
  return new _domain(1, dist.newOpaqueDomain(idxType));

// This function has no 'has runtime type' pragma since the idxType of
// opaque domains is _OpaqueIndex, not opaque.  This function is
// essentially a wrapper around the function that actually builds up
// the runtime type.
def chpl_buildDomainRuntimeType(dist, type idxType) type
 where idxType == opaque
  return chpl_buildDomainRuntimeType(dist, _OpaqueIndex);

pragma "has runtime type"
def chpl_buildSparseDomainRuntimeType(dist, dom: _domain) type
  return new _domain(dom.rank, dist.newSparseDomain(dom.rank, dom._value.idxType, dom));

def chpl_convertValueToRuntimeType(dom: _domain) type
 where dom._value:BaseArithmeticDomain
  return chpl_buildDomainRuntimeType(dom._value.dist, dom._value.rank,
                            dom._value.idxType, dom._value.stridable);

def chpl_convertValueToRuntimeType(dom: _domain) type
 where dom._value:BaseSparseDomain
  return chpl_buildSparseDomainRuntimeType(dom._value.dist, dom._value.parentDom);

def chpl_convertValueToRuntimeType(dom: _domain) type
where !dom._value:BaseArithmeticDomain && !dom._value:BaseSparseDomain
  return chpl_buildDomainRuntimeType(dom._value.dist, dom._value.idxType);

//
// Support for array types
//
pragma "has runtime type"
def chpl_buildArrayRuntimeType(dom: _domain, type eltType) type
  return dom.buildArray(eltType);

def chpl_convertValueToRuntimeType(arr: _array) type
  return chpl_buildArrayRuntimeType(arr.domain, arr.eltType);

//
// Support for subdomain types
//
// Note the domain of a subdomain is not yet part of the runtime type
//
def chpl_buildSubDomainType(dom: _domain) type
  return chpl_convertValueToRuntimeType(dom);

//
// Support for domain expressions, e.g., [1..3, 1..3]
//
def chpl_buildDomainExpr(x: _domain)
  return x;

def chpl_buildDomainExpr(ranges: range(?) ...?rank) {
  for param i in 2..rank {
    if ranges(1).eltType != ranges(i).eltType then
      compilerError("domain has mixed dimensional type");
    if ranges(i).boundedType != BoundedRangeType.bounded then
      compilerError("domain has dimension of unbounded range");
  }
  var d: domain(rank, ranges(1).eltType, chpl_anyStridable(ranges));
  d.setIndices(ranges);
  return d;
}

//
// Support for index types
//
def chpl_buildIndexType(param rank: int, type idxType) type where rank == 1 {
  var x: idxType;
  return x;
}

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

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

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

def chpl_buildIndexType(type idxType) type where idxType == opaque
  return _OpaqueIndex;

//
// Domain wrapper record
//
pragma "domain"
pragma "has runtime type"
record _domain {
  param rank : int;
  var _value;
  var _promotionType: index(rank, _value.idxType);

  def ~_domain() {
    delete _value;
  }

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

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

  def this(args ...rank) where _validRankChangeArgs(args, _value.idxType) {
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = chpl_anyStridable(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(eltType, rank, x);
  }

  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) return new _domain(rank, _value.exterior(i));

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

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

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

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

  def getIndices()
    return _value.getIndices();

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

  // associative array interface

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

//
// Array wrapper record
//
pragma "array"
pragma "has runtime type"
record _array {
  type eltType;
  param rank : int;
  var _value;
  var _promotionType : eltType;

  def ~_array() {
    delete _value;
  }

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

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

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

  pragma "inline"
  def this(i: _value.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(eltType, rank, _value.slice(_dom((...ranges))._value));
  }

  pragma "valid var"
  def this(args ...rank) var where _validRankChangeArgs(args, _value.idxType) {
    if boundsChecking then
      _value.checkRankChange(args);
    var ranges = _getRankChangeRanges(args);
    param rank = ranges.size, stridable = chpl_anyStridable(ranges);
    return new _array(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(eltType, rank, x);
  }

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

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

  // sparse array interface

  def IRV var {
    return _value.IRV;
  }

  // associative array interface

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

//
// Helper functions
//

// computes || reduction over stridable of ranges
def chpl_anyStridable(ranges, param d: int = 1) param {
  for param i in 1..ranges.size do
    if ranges(i).stridable then
      return true;
  return false;
}

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

//
// Support for += and -= over domains
//
def chpl_isDomain(x: _domain) param return true;
def chpl_isDomain(x) param return false;

//
// Assignment of domains and arrays
//
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(?)) {
  d = [r];
  return d;
}

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

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

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

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

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

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

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


Fixed a bug in the non-parameter tuple access function in which not
all paths returned a value.  This resulted in C compilation warnings
when compiling with the -O option and the gasnet flags.

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

def _iteratorUnbounded(x) param
  return false;

def _iteratorUnbounded(r: range) param where r.boundedType != bounded
  return true;

pragma "inline" def _getIteratorTupleHelp(t: _tuple, param i: int) {
  if i == t.size {
    return (_getIterator(t(i)), 1); // ignore last == 1
  } else {
    return (_getIterator(t(i)), (..._getIteratorTupleHelp(t, i+1)));
  }
}

pragma "inline" def _getZipCursor1TupleHelp(t: _tuple, param i: int) {
  if i == t.size-1 {
    return (t(i).getZipCursor1(), 1); // ignore last == 1
  } else {
    return (t(i).getZipCursor1(), (..._getZipCursor1TupleHelp(t, i+1)));
  }
}

pragma "inline" def _getValueTupleHelp(ic: _tuple, c: _tuple, param i: int) {
  if i == ic.size-2 {
    return (ic(1).getValue(c(1)), ic(2).getValue(c(2)));
  } else {
    return (ic(1).getValue(c(1)), (..._getValueTupleHelp(ic, c, i+1)));
  }
}

pragma "scalar replace tuples" pragma "tuple" record _tuple {
  param size : int;

  pragma "inline" pragma "tuple get" def this(param i : int) var
    return 0;

  def this(i : int) var {
    for param j in 1..size do
      if i == j then
        return this(j);
    halt("tuple indexing out-of-bounds error");
    return this(1);
  }

  def these() {
    if size == 1 {
      for i in this(1) {
        var t: 1*i.type;
        t(1) = i;
        yield t;
      }
    } else {
      var ic = _getIteratorTupleHelp(this, 2);
      var c = _getZipCursor1TupleHelp(ic, 1);
      if c.size != ic.size then
        compilerError("internal size check failure; zipper failed");
      for i in this(1) {
        for param i in 1..ic.size-1 do
          c(i) = ic(i).getZipCursor2(c(i));
        // _iteratorUnbounded makes it so that we don't insert these
        // breaks if the first iterator can fully control the loop;
        // this is important so that the single loop iterator
        // optimization can fire; the break in the loop disables it
        if _iteratorUnbounded(this(1)) {
          for param i in 1..ic.size-1 do
            if !ic(i).isValidCursor(c(i)) then
              break;
        } else if boundsChecking {
          for param i in 1..ic.size-1 do
            if !_iteratorUnbounded(this(i+1)) then
              if !ic(i).isValidCursor(c(i)) then
                halt("zippered iterations have non-equal lengths");
        }
        if ic.size == 2 then
          yield (i, ic(1).getValue(c(1)));
        else
          yield (i, (..._getValueTupleHelp(ic, c, 1)));
        for param i in 1..ic.size-1 do
          c(i) = ic(i).getZipCursor3(c(i));
      }
      for param i in 1..ic.size-1 do
        c(i) = ic(i).getZipCursor4(c(i));
      if boundsChecking then
        for param i in 1..ic.size-1 do
          if !_iteratorUnbounded(this(i+1)) then
            if ic(i).isValidCursor(c(i)) then
              halt("zippered iterations have non-equal lengths");
    }
  }
}

pragma "inline" def =(x : _tuple, y) {
  for param i in 1..x.size do
    x(i) = y(i);
  return x;
}

def ==( a: _tuple, b: _tuple): bool {
  if (a.size != b.size) then
    return false;
  for param i in 1..a.size do
    if (a(i) != b(i)) then
      return false;
  return true;
}

def !=( a: _tuple, b: _tuple): bool {
  if (a.size != b.size) then
    return true;
  for param i in 1..a.size do
    if (a(i) != b(i)) then
      return true;
  return false;
}

def _tuple.writeThis(f: Writer) {
  f.write("(", this(1));
  for param i in 2..size do
    f.write(", ", this(i));
  f.write(")");
}

pragma "inline" def _cast(type t, x: _tuple) where (t == complex(64)) & (x.size == 2) {
  var c: complex(64);
  c.re = x(1):real(32);
  c.im = x(2):real(32);
  return c;
}

pragma "inline" def _cast(type t, x: _tuple) where (t == complex(128)) & (x.size == 2) {
  var c: complex(128);
  c.re = x(1):real(64);
  c.im = x(2):real(64);
  return c;
}

pragma "inline" def _cast(type t, x: _tuple) where (t == complex(256)) & (x.size == 2) {
  var c: complex(256);
  c.re = x(1):real(128);
  c.im = x(2):real(128);
  return c;
}

pragma "inline" def +(a: _tuple) {
  return a;
}

pragma "inline" def -(a: _tuple) {
  for param i in 1..a.size do
    a(i) = -a(i);
  return a;
}

pragma "inline" def +(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) + b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return (onetuple);
    } else {
      var rest = help(i+1);
      return (a(i) + b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def +(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a + b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a + b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def +(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) + b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) + b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def -(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) - b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) - b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def -(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a - b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a - b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def -(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) - b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) - b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def *(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) * b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) * b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def *(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a * b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a * b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def *(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) * b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) * b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def /(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) / b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) / b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def /(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a / b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a / b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def /(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) / b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) / b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def %(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) % b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) % b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def %(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a % b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a % b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def %(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) % b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) % b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def **(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ** b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ** b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def **(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a ** b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a ** b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def **(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ** b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ** b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def ~(a: _tuple) {
  for param i in 1..a.size do
    a(i) = ~a(i);
  return a;
}

pragma "inline" def &(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) & b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) & b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def &(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a & b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a & b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def &(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) & b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) & b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def |(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) | b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) | b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def |(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a | b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a | b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def |(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) | b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) | b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def ^(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ^ b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ^ b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def ^(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a ^ b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a ^ b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def ^(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ^ b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ^ b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def <<(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) << b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) << b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def <<(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a << b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a << b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def <<(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) << b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) << b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def >>(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) >> b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) >> b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def >>(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a >> b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a >> b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def >>(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) >> b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) >> b(i), (...rest));
    }
  }
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def !(a: _tuple) {
  var b: a.size * bool;
  for param i in 1..a.size do
    b(i) = !a(i);
  return b;
}

pragma "inline" def >(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) > b then
      return true;
    else if a(i) < b then
      return false;
  return false;
}

pragma "inline" def >(a, b: _tuple) {
  for param i in 1..b.size do
    if a > b(i) then
      return true;
    else if a < b(i) then
      return false;
  return false;
}

pragma "inline" def >(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) > b(i) then
      return true;
    else if a(i) < b(i) then
      return false;
  return false;
}

pragma "inline" def >=(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) > b then
      return true;
    else if a(i) < b then
      return false;
  return true;
}

pragma "inline" def >=(a, b: _tuple) {
  for param i in 1..b.size do
    if a > b(i) then
      return true;
    else if a < b(i) then
      return false;
  return true;
}

pragma "inline" def >=(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) > b(i) then
      return true;
    else if a(i) < b(i) then
      return false;
  return true;
}

pragma "inline" def <(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) < b then
      return true;
    else if a(i) > b then
      return false;
  return false;
}

pragma "inline" def <(a, b: _tuple) {
  for param i in 1..b.size do
    if a < b(i) then
      return true;
    else if a > b(i) then
      return false;
  return false;
}

pragma "inline" def <(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) < b(i) then
      return true;
    else if a(i) > b(i) then
      return false;
  return false;
}

pragma "inline" def <=(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) < b(i) then
      return true;
    else if a(i) > b(i) then
      return false;
  return true;
}

pragma "inline" def <=(a, b: _tuple) {
  for param i in 1..b.size do
    if a < b(i) then
      return true;
    else if a > b(i) then
      return false;
  return true;
}

pragma "inline" def <=(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) < b(i) then
      return true;
    else if a(i) > b(i) then
      return false;
  return true;
}

pragma "scalar replace tuples"
record _square_tuple {
  param size: int;
  var tuple;

  def these_help(param dim: int) {
    if dim == size - 1 {
      for i in tuple(dim) do
        for j in tuple(size) do
          yield (i, j);
    } else {
      for i in tuple(dim) do
        for j in these_help(dim+1) do
          yield (i, (...j));
    }
  }

  def these() {
    if size == 1 {
      for i in tuple(1) do
        yield i;
    } else {
      for i in these_help(1) do
        yield i;
    }
  }
}

def _build_domain(x ...?size) where size > 1
  return _square_tuple(size, x);

pragma "inline" def _build_tuple(x ...?size) {
  if size == 1 then
    return x(1);
  else
    return x;
}

def _iteratorUnbounded(x) param
  return false;

def _iteratorUnbounded(r: range) param where r.boundedType != bounded
  return true;

pragma "inline" def _getIteratorTupleHelp(t: _tuple, param i: int) {
  if i == t.size {
    return (_getIterator(t(i)), 1); // ignore last == 1
  } else {
    return (_getIterator(t(i)), (..._getIteratorTupleHelp(t, i+1)));
  }
}

pragma "inline" def _getZipCursor1TupleHelp(t: _tuple, param i: int) {
  if i == t.size-1 {
    return (t(i).getZipCursor1(), 1); // ignore last == 1
  } else {
    return (t(i).getZipCursor1(), (..._getZipCursor1TupleHelp(t, i+1)));
  }
}

pragma "inline" def _getValueTupleHelp(ic: _tuple, c: _tuple, param i: int) {
  if i == ic.size-2 {
    return (ic(1).getValue(c(1)), ic(2).getValue(c(2)));
  } else {
    return (ic(1).getValue(c(1)), (..._getValueTupleHelp(ic, c, i+1)));
  }
}

pragma "scalar replace tuples" pragma "tuple" record _tuple {
  param size : int;

  pragma "inline" pragma "tuple get" def this(param i : int) var
    return 0;

  def this(i : int) var {
    for param j in 1..size do
      if i == j then
        return this(j);
    halt("tuple indexing out-of-bounds error");
  }

  def these() {
    if size == 1 {
      for i in this(1) {
        var t: 1*i.type;
        t(1) = i;
        yield t;
      }
    } else {
      var ic = _getIteratorTupleHelp(this, 2);
      var c = _getZipCursor1TupleHelp(ic, 1);
      if c.size != ic.size then
        compilerError("internal size check failure; zipper failed");
      for i in this(1) {
        for param i in 1..ic.size-1 do
          c(i) = ic(i).getZipCursor2(c(i));
        // _iteratorUnbounded makes it so that we don't insert these
        // breaks if the first iterator can fully control the loop;
        // this is important so that the single loop iterator
        // optimization can fire; the break in the loop disables it
        if _iteratorUnbounded(this(1)) {
          for param i in 1..ic.size-1 do
            if !ic(i).isValidCursor(c(i)) then
              break;
        } else if boundsChecking {
          for param i in 1..ic.size-1 do
            if !_iteratorUnbounded(this(i+1)) then
              if !ic(i).isValidCursor(c(i)) then
                halt("zippered iterations have non-equal lengths");
        }
        if ic.size == 2 then
          yield (i, ic(1).getValue(c(1)));
        else
          yield (i, (..._getValueTupleHelp(ic, c, 1)));
        for param i in 1..ic.size-1 do
          c(i) = ic(i).getZipCursor3(c(i));
      }
      for param i in 1..ic.size-1 do
        c(i) = ic(i).getZipCursor4(c(i));
      if boundsChecking then
        for param i in 1..ic.size-1 do
          if !_iteratorUnbounded(this(i+1)) then
            if ic(i).isValidCursor(c(i)) then
              halt("zippered iterations have non-equal lengths");
    }
  }
}

pragma "inline" def =(x : _tuple, y) {
  for param i in 1..x.size do
    x(i) = y(i);
  return x;
}

def ==( a: _tuple, b: _tuple): bool {
  if (a.size != b.size) then
    return false;
  for param i in 1..a.size do
    if (a(i) != b(i)) then
      return false;
  return true;
}

def !=( a: _tuple, b: _tuple): bool {
  if (a.size != b.size) then
    return true;
  for param i in 1..a.size do
    if (a(i) != b(i)) then
      return true;
  return false;
}

def _tuple.writeThis(f: Writer) {
  f.write("(", this(1));
  for param i in 2..size do
    f.write(", ", this(i));
  f.write(")");
}

pragma "inline" def _cast(type t, x: _tuple) where (t == complex(64)) & (x.size == 2) {
  var c: complex(64);
  c.re = x(1):real(32);
  c.im = x(2):real(32);
  return c;
}

pragma "inline" def _cast(type t, x: _tuple) where (t == complex(128)) & (x.size == 2) {
  var c: complex(128);
  c.re = x(1):real(64);
  c.im = x(2):real(64);
  return c;
}

pragma "inline" def _cast(type t, x: _tuple) where (t == complex(256)) & (x.size == 2) {
  var c: complex(256);
  c.re = x(1):real(128);
  c.im = x(2):real(128);
  return c;
}

pragma "inline" def +(a: _tuple) {
  return a;
}

pragma "inline" def -(a: _tuple) {
  for param i in 1..a.size do
    a(i) = -a(i);
  return a;
}

pragma "inline" def +(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) + b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return (onetuple);
    } else {
      var rest = help(i+1);
      return (a(i) + b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def +(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a + b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a + b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def +(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) + b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) + b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def -(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) - b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) - b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def -(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a - b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a - b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def -(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) - b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) - b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def *(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) * b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) * b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def *(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a * b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a * b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def *(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) * b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) * b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def /(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) / b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) / b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def /(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a / b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a / b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def /(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) / b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) / b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def %(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) % b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) % b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def %(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a % b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a % b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def %(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) % b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) % b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def **(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ** b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ** b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def **(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a ** b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a ** b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def **(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ** b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ** b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def ~(a: _tuple) {
  for param i in 1..a.size do
    a(i) = ~a(i);
  return a;
}

pragma "inline" def &(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) & b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) & b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def &(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a & b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a & b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def &(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) & b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) & b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def |(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) | b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) | b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def |(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a | b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a | b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def |(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) | b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) | b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def ^(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ^ b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ^ b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def ^(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a ^ b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a ^ b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def ^(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) ^ b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) ^ b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def <<(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) << b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) << b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def <<(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a << b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a << b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def <<(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) << b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) << b(i), (...rest));
    }
  }

  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def >>(a: _tuple, b) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) >> b;
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) >> b, (...rest));
    }
  }
  return help(1);
}

pragma "inline" def >>(a, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == b.size {
      var result = a >> b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a >> b(i), (...rest));
    }
  }
  return help(1);
}

pragma "inline" def >>(a: _tuple, b: _tuple) {
  pragma "inline" def help(param i: int) {
    if i == a.size {
      var result = a(i) >> b(i);
      var onetuple: 1 * result.type;
      onetuple(1) = result;
      return onetuple;
    } else {
      var rest = help(i+1);
      return (a(i) >> b(i), (...rest));
    }
  }
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  return help(1);
}

pragma "inline" def !(a: _tuple) {
  var b: a.size * bool;
  for param i in 1..a.size do
    b(i) = !a(i);
  return b;
}

pragma "inline" def >(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) > b then
      return true;
    else if a(i) < b then
      return false;
  return false;
}

pragma "inline" def >(a, b: _tuple) {
  for param i in 1..b.size do
    if a > b(i) then
      return true;
    else if a < b(i) then
      return false;
  return false;
}

pragma "inline" def >(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) > b(i) then
      return true;
    else if a(i) < b(i) then
      return false;
  return false;
}

pragma "inline" def >=(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) > b then
      return true;
    else if a(i) < b then
      return false;
  return true;
}

pragma "inline" def >=(a, b: _tuple) {
  for param i in 1..b.size do
    if a > b(i) then
      return true;
    else if a < b(i) then
      return false;
  return true;
}

pragma "inline" def >=(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) > b(i) then
      return true;
    else if a(i) < b(i) then
      return false;
  return true;
}

pragma "inline" def <(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) < b then
      return true;
    else if a(i) > b then
      return false;
  return false;
}

pragma "inline" def <(a, b: _tuple) {
  for param i in 1..b.size do
    if a < b(i) then
      return true;
    else if a > b(i) then
      return false;
  return false;
}

pragma "inline" def <(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) < b(i) then
      return true;
    else if a(i) > b(i) then
      return false;
  return false;
}

pragma "inline" def <=(a: _tuple, b) {
  for param i in 1..a.size do
    if a(i) < b(i) then
      return true;
    else if a(i) > b(i) then
      return false;
  return true;
}

pragma "inline" def <=(a, b: _tuple) {
  for param i in 1..b.size do
    if a < b(i) then
      return true;
    else if a > b(i) then
      return false;
  return true;
}

pragma "inline" def <=(a: _tuple, b: _tuple) {
  if (a.size != b.size) then
    compilerError("Tuples must have the same size");

  for param i in 1..a.size do
    if a(i) < b(i) then
      return true;
    else if a(i) > b(i) then
      return false;
  return true;
}

pragma "scalar replace tuples"
record _square_tuple {
  param size: int;
  var tuple;

  def these_help(param dim: int) {
    if dim == size - 1 {
      for i in tuple(dim) do
        for j in tuple(size) do
          yield (i, j);
    } else {
      for i in tuple(dim) do
        for j in these_help(dim+1) do
          yield (i, (...j));
    }
  }

  def these() {
    if size == 1 {
      for i in tuple(1) do
        yield i;
    } else {
      for i in these_help(1) do
        yield i;
    }
  }
}

def _build_domain(x ...?size) where size > 1
  return _square_tuple(size, x);

pragma "inline" def _build_tuple(x ...?size) {
  if size == 1 then
    return x(1);
  else
    return x;
}


fixing racy code

fixing racy code

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

module MDForce {

  use initMD;
  use Time;

  // define the interface for a force object
  class Force {
    var cutforcesq, eng_vdwl : real;
    var virial, mass : real; // virial is average kinetic energy over time, see 'Virial theorem'

    var wipetime, maintime: real;

    proc Force();
    proc compute(store : bool) : void {}
  }

  // 'Embedded atom model'
  class ForceEAM : Force  {
    var numDensity, numPotentials : int;

    // potentials as array data
    var rSpace : domain(1);
    var frhoSpace : domain(1);
    var frho : [frhoSpace] real;
    var rhor, z2r : [rSpace] real;

    // spline form for computation
    var deltaPotential, rdr, deltaDensity, rdrho : real;
    var splineSpace : domain(1);
    var frSplineSpace : domain(1);
    var rzSplineSpace : domain(1);
    var rhor_spline, z2r_spline : [rzSplineSpace] real;
    var frho_spline : [frSplineSpace] real;

    class FPChunk {
      var Fluff : domain(3);
      var Fp : [Fluff] [perBinSpace] real;
    }

    var FP : [LocaleGridDom] FPChunk;

    class Funcfl {
      var eamFile : string;
      var numDensity, numPotentials : int;
      var deltaDensity, deltaPotential, cut, mass : real;
      var zrSpace : domain(1);
      var frhoSpace : domain(1);
      var frho : [frhoSpace] real;
      var rhor, zr : [zrSpace] real;
    }

    var funcfl : Funcfl;

    proc ForceEAM(cf : real) {
      // use the fluff domain already calculated for communication
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          FP[ijk] = new FPChunk(Grid[ijk].Fluff);
        }
      }
      cutforcesq = cf*cf;
      coeff("Cu_u6.eam");
      init_style();
    }

    proc coeff(fname : string) {
      funcfl = new Funcfl();
      funcfl.eamFile = fname;
      var fchan = open(fname, iomode.r);
      var rd = fchan.reader();

      rd.readln(); // skip header info
      rd.read(int); // skip atomic number

      mass = rd.readln(real);

      (funcfl.numDensity,
       funcfl.deltaDensity,
       funcfl.numPotentials,
       funcfl.deltaPotential,
       funcfl.cut) = rd.readln(int,real,int,real,real);

      funcfl.frhoSpace = {1..(funcfl.numDensity+1)};
      funcfl.zrSpace = {1..(funcfl.numPotentials+1)};
      grab(rd, funcfl.frho); // verified correctness
      grab(rd, funcfl.zr);
      grab(rd, funcfl.rhor);
      cutforcesq = funcfl.cut * funcfl.cut;
    }

    proc init_style() {
      file2array();
      array2spline();
    }

    proc file2array() {
      var i, j, k, m, n : int;
      var ntypes = 1;
      var sixth : real = 1.0 / 6.0;

      var active = 0;
      var rmax, rhomax : real;
      deltaPotential = 0;
      deltaDensity = 0;
      rmax = 0;
      rhomax = 0;

      deltaPotential = max(deltaPotential, funcfl.deltaPotential);
      deltaDensity = max(deltaDensity, funcfl.deltaDensity);
      rmax = max(rmax, (funcfl.numPotentials - 1) * funcfl.deltaPotential);
      rhomax = max(rhomax, (funcfl.numDensity - 1) * funcfl.deltaDensity);

      numPotentials = (rmax / deltaPotential + .5) : int;
      numDensity = (rhomax / deltaDensity + .5) : int;

      frhoSpace = {1..(numDensity+1)};

      var r, p, cof1, cof2, cof3, cof4 : real;

      for (f,m) in zip(frho[2..], 2.. ) {
        m -= 1; // get us to 0-based C arrays.
        r = (m - 1) * deltaDensity;
        p = r / funcfl.deltaDensity + 1.0;
        k = p : int;
        k = min(k, funcfl.numDensity-2);
        k = max(k, 2);
        p -= k;
        p = min(p, 2.0);
        cof1 = -sixth * p * (p-1.0) * (p-2.0);
        cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
        cof3 = -.5 * p * (p+1.0) * (p - 2.0);
        cof4 = sixth * p * (p * p - 1.0);
        k += 1; // get us to 1-based chapel arrays.
        f = cof1 * funcfl.frho[k-1] + cof2 * funcfl.frho[k] + cof3 * funcfl.frho[k + 1] + cof4 * funcfl.frho[k+2];
      } // verified correct

      rSpace = {1..(numPotentials+1)};
      for (f,m) in zip(rhor[2..], 2..) {
        m -= 1;
        r = (m - 1) * deltaPotential;
        p = r / funcfl.deltaPotential + 1.0;
        k = p : int;
        k = min(k, funcfl.numPotentials - 2);
        k = max(k, 2);
        p -= k;
        p = min(p, 2.0);
        cof1 = -sixth * p * (p-1.0) * (p-2.0);
        cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
        cof3 = -.5 * p * (p+1.0) * (p - 2.0);
        cof4 = sixth * p * (p * p - 1.0);
        k += 1;
        f = cof1 * funcfl.rhor[k-1] + cof2 * funcfl.rhor[k] + cof3 * funcfl.rhor[k+1] + cof4 * funcfl.rhor[k+2];
      }

      for (f,m) in zip(z2r[2..], 2..) {
        m -= 1;
        r = (m - 1) * deltaPotential;
        p = r / funcfl.deltaPotential + 1.0;
        k = p : int;
        k = min(k, funcfl.numPotentials - 2);
        k = max(k, 2);
        p -= k;
        p = min(p,2.0);
        cof1 = -sixth * p * (p - 1.0) * (p - 2.0);
        cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
        cof3 = -.5 * p * (p+1.0) * (p - 2.0);
        cof4 = sixth * p * (p * p - 1.0);
        k += 1;
        var zri : real = cof1 * funcfl.zr[k-1] + cof2 * funcfl.zr[k] + cof3 * funcfl.zr[k+1] + cof4 * funcfl.zr[k+2];
        f = 27.2 * 0.529 * zri * zri;
      }
    }

    proc array2spline() {
      rdr = 1.0 / deltaPotential;
      rdrho = 1.0 / deltaDensity;

      frSplineSpace = {0..((numDensity+1) * 7)-1};
      rzSplineSpace = {0..((numPotentials+1) * 7)-1};

      interp(numDensity, deltaDensity, frho, frho_spline);
      interp(numPotentials, deltaPotential, rhor, rhor_spline);
      interp(numPotentials, deltaPotential, z2r, z2r_spline);
    }

    // copied straight from c++, not entirely sure what it does
    proc interp(n : int, delta : real, arr : [] real, spline : [] real) {
      for (f,m) in zip(arr[2..],1..) {
        spline[m*7 + 6] = f;
      }

      spline[1*7 + 5] = spline[2*7+6] - spline[1*7+6];
      spline[2 * 7 + 5] = 0.5 * (spline[3 * 7 + 6] - spline[1 * 7 + 6]);
      spline[(n - 1) * 7 + 5] = 0.5 * (spline[n * 7 + 6] - spline[(n - 2) * 7 + 6]);
      spline[n * 7 + 5] = spline[n * 7 + 6] - spline[(n - 1) * 7 + 6];

      for m in 3..n-2 {
        spline[m * 7 + 5] = ((spline[(m - 2) * 7 + 6] - spline[(m + 2) * 7 + 6]) +
          8.0 * (spline[(m + 1) * 7 + 6] - spline[(m - 1) * 7 + 6])) / 12.0;
      }

      for m in 1..n-1 {
         spline[m * 7 + 4] = 3.0 * (spline[(m + 1) * 7 + 6] - spline[m * 7 + 6]) -
           2.0 * spline[m * 7 + 5] - spline[(m + 1) * 7 + 5];
         spline[m * 7 + 3] = spline[m * 7 + 5] + spline[(m + 1) * 7 + 5] -
           2.0 * (spline[(m + 1) * 7 + 6] - spline[m * 7 + 6]);
      }

      spline[n*7 + 4] = 0.0;
      spline[n*7 + 3] = 0.0;

      for m in 1..n {
        spline[m * 7 + 2] = spline[m * 7 + 5] / delta;
        spline[m * 7 + 1] = 2.0 * spline[m * 7 + 4] / delta;
        spline[m * 7 + 0] = 3.0 * spline[m * 7 + 3] / delta;
      }
    }

    proc grab(rd,list : [] real) {
      for a in list[2..list.domain.high] {
        a = rd.read(real);
      }
      list[1] = list[2];
    }

    proc compute(store : bool) {
      if debug then writeln("entering EAM compute...");
      var evdwl, vir : atomic real;
      virial = 0.0;
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];
          forall (b,c) in zip(Me.Bins,Me.Count[Me.Real]) {
            for a in b[1..c] {
              a.f = (0.0,0.0,0.0);
            }
          }
        }
      }

      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] do {
          const Me = Grid[ijk];

          for (b,p,c,r) in zip(Me.Bins,Me.Pos[Me.Real], Me.Count[Me.Real], Me.Real) {
            const exist = 1..c;
            for (a,x,i) in zip(b[exist],p[exist], exist) {
              var rhoi : atomic real;
              forall (n,j) in a.neighs[1..a.ncount] {
                const del = x - Me.Pos[n][j];
                const rsq = dot(del, del);

                if rsq < cutforcesq {
                  var p = sqrt(rsq) * rdr + 1.0;
                  var m : int = p : int;
                  if m >= numPotentials - 1 then m = numPotentials - 1;
                  p -= m;
                  if p >= 1.0 then p = 1.0;
                  var val = ((rhor_spline[m*7 + 3] * p + rhor_spline[m * 7 + 4]) * p + rhor_spline[m*7+5]) * p + rhor_spline[m*7+6];
                  rhoi.add(val);
                }
              }

              var p = 1.0 * rhoi.read() * rdrho + 1.0;
              var m = p : int;
              m = max(1, min(m, numDensity - 1));
              p -= m;
              p = min(p, 1.0);
              FP[ijk].Fp[r][i] = (frho_spline[m*7+0] * p + frho_spline[m*7 + 1]) * p + frho_spline[m*7 + 2];

              if store {
                evdwl.add(((frho_spline[m*7+3] * p + frho_spline[m*7+4]) * p + frho_spline[m*7+5]) * p + frho_spline[m*7+6]);
              }
            }
          }
        }
      }

      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];
          for (Src, Dest, neigh) in zip(Me.SrcSlice, Me.DestSlice, Me.Neighs) {
            FP[ijk].Fp[Dest] = FP[neigh].Fp[Src];
          }
        }
      }

      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];

          for (b,p,c,rbin) in zip(Me.Bins, Me.Pos[Me.Real], Me.Count[Me.Real], Me.Real) {
            const exist = 1..c;
            for (a,x,i) in zip(b[exist],p[exist],exist) {
              var fx, fy, fz : real;
              for (n,j) in a.neighs[1..a.ncount] {
                const del = x - Me.Pos[n][j];
                const rsq = dot(del, del);

                if rsq < cutforcesq {
                  const r = sqrt(rsq);
                  var p = r * rdr + 1.0;
                  var m : int = p : int;
                  if m >= numPotentials - 1 then m = numPotentials - 1;
                  p -= m;
                  if p >= 1.0 then p = 1.0;

                  var rhoip = (rhor_spline[m * 7 + 0] * p + rhor_spline[m*7+1]) * p + rhor_spline[m*7 + 2];
                  var z2p = (z2r_spline[m * 7 + 0] * p + z2r_spline[m * 7 + 1]) * p + z2r_spline[m * 7 + 2];
                  var z2 = ((z2r_spline[m * 7 + 3] * p + z2r_spline[m * 7 + 4]) * p + z2r_spline[m * 7 + 5]) * p + z2r_spline[m*7+6];

                  var recip = 1.0/r;
                  var phi = z2 * recip;
                  var phip = z2p * recip - phi * recip;
                  var psip = FP[ijk].Fp[rbin][i] * rhoip + FP[ijk].Fp[n][j] * rhoip + phip;
                  var fpair = -psip * recip;

                  const res = del * fpair;
                  (fx, fy, fz) = (fx, fy, fz) + res;

                  fpair *= .5;

                  if store {
                    vir.add(rsq * fpair);
                    evdwl.add(.5 * phi);
                  }
                }
              }
              a.f = (fx, fy, fz);
            }
          }
        }
      }
      eng_vdwl += 2.0 * evdwl.read();
      virial = vir.read();
    }
  }

  // Lennard-Jones potential
  class ForceLJ : Force {
    proc ForceLJ(cf : real) {
      cutforcesq = cf * cf;
    }

    proc compute(store : bool) : void {
      eng_vdwl = 0;
      virial = 0;
      var fTimer : Timer;

      // wipe old forces of real AND ghost atoms
      fTimer.start();
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];
          forall (b,c) in zip(Me.Bins,Me.Count[Me.Real]) {
            for a in b[1..c] {
              a.f = (0.0,0.0,0.0);
            }
          }
        }
      }

      // track wipetime for testing purposes
      wipetime += fTimer.elapsed();
      fTimer.stop();
      fTimer.clear();

      // for each atom, compute force between itself and its neighbors
      // can make outer loop parallel if we can make force modifications atomic
      fTimer.start();
      var engs, virs : [LocaleGridDom] real;
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          // bring stuff onto this locale
          const Me = Grid[ijk];

          // by using an atomic variable, we can make the outer loop parallel
          var eng, vir : atomic real;

          forall (b,p,c) in zip(Me.Bins, Me.Pos[Me.Real], Me.Count[Me.Real], Me.Real) {
            for (a, x) in zip(b[1..c],p[1..c]) {
              for(n,i) in a.neighs[1..a.ncount] {
                const del = x - Me.Pos[n][i];
                const rsq = dot(del,del);

                if rsq < cutforcesq {
                  const sr2: real = 1.0 / rsq;
                  const sr6 : real = sr2 * sr2 * sr2;
                  const force : real = 48.0 * sr6 * (sr6 - .5) * sr2;
                  a.f += del * force;

                  if store {
                    eng.add(4.0 * sr6 * (sr6 - 1.0));
                    vir.add(.5 * rsq * force);
                  }
                }
              }
            }
          }

          // cut down on communication
          engs[ijk] = eng.read();
          virs[ijk] = vir.read();
        }
      }

      // update values thermo depends on
      eng_vdwl = + reduce engs;
      virial = + reduce virs;

      // higher resolution performance timings - look at the actual computation
      maintime += fTimer.elapsed();
    }
  }
}

module MDForce {

  use initMD;
  use Time;

  // define the interface for a force object
  class Force {
    var cutforcesq, eng_vdwl : real;
    var virial, mass : real; // virial is average kinetic energy over time, see 'Virial theorem'

    var wipetime, maintime: real;

    proc Force();
    proc compute(store : bool) : void {}
  }

  // 'Embedded atom model'
  class ForceEAM : Force  {
    var numDensity, numPotentials : int;

    // potentials as array data
    var rSpace : domain(1);
    var frhoSpace : domain(1);
    var frho : [frhoSpace] real;
    var rhor, z2r : [rSpace] real;

    // spline form for computation
    var deltaPotential, rdr, deltaDensity, rdrho : real;
    var splineSpace : domain(1);
    var frSplineSpace : domain(1);
    var rzSplineSpace : domain(1);
    var rhor_spline, z2r_spline : [rzSplineSpace] real;
    var frho_spline : [frSplineSpace] real;

    class FPChunk {
      var Fluff : domain(3);
      var Fp : [Fluff] [perBinSpace] real;
    }

    var FP : [LocaleGridDom] FPChunk;

    class Funcfl {
      var eamFile : string;
      var numDensity, numPotentials : int;
      var deltaDensity, deltaPotential, cut, mass : real;
      var zrSpace : domain(1);
      var frhoSpace : domain(1);
      var frho : [frhoSpace] real;
      var rhor, zr : [zrSpace] real;
    }

    var funcfl : Funcfl;

    proc ForceEAM(cf : real) {
      // use the fluff domain already calculated for communication
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          FP[ijk] = new FPChunk(Grid[ijk].Fluff);
        }
      }
      cutforcesq = cf*cf;
      coeff("Cu_u6.eam");
      init_style();
    }

    proc coeff(fname : string) {
      funcfl = new Funcfl();
      funcfl.eamFile = fname;
      var fchan = open(fname, iomode.r);
      var rd = fchan.reader();

      rd.readln(); // skip header info
      rd.read(int); // skip atomic number

      mass = rd.readln(real);

      (funcfl.numDensity,
       funcfl.deltaDensity,
       funcfl.numPotentials,
       funcfl.deltaPotential,
       funcfl.cut) = rd.readln(int,real,int,real,real);

      funcfl.frhoSpace = {1..(funcfl.numDensity+1)};
      funcfl.zrSpace = {1..(funcfl.numPotentials+1)};
      grab(rd, funcfl.frho); // verified correctness
      grab(rd, funcfl.zr);
      grab(rd, funcfl.rhor);
      cutforcesq = funcfl.cut * funcfl.cut;
    }

    proc init_style() {
      file2array();
      array2spline();
    }

    proc file2array() {
      var i, j, k, m, n : int;
      var ntypes = 1;
      var sixth : real = 1.0 / 6.0;

      var active = 0;
      var rmax, rhomax : real;
      deltaPotential = 0;
      deltaDensity = 0;
      rmax = 0;
      rhomax = 0;

      deltaPotential = max(deltaPotential, funcfl.deltaPotential);
      deltaDensity = max(deltaDensity, funcfl.deltaDensity);
      rmax = max(rmax, (funcfl.numPotentials - 1) * funcfl.deltaPotential);
      rhomax = max(rhomax, (funcfl.numDensity - 1) * funcfl.deltaDensity);

      numPotentials = (rmax / deltaPotential + .5) : int;
      numDensity = (rhomax / deltaDensity + .5) : int;

      frhoSpace = {1..(numDensity+1)};

      var r, p, cof1, cof2, cof3, cof4 : real;

      for (f,m) in zip(frho[2..], 2.. ) {
        m -= 1; // get us to 0-based C arrays.
        r = (m - 1) * deltaDensity;
        p = r / funcfl.deltaDensity + 1.0;
        k = p : int;
        k = min(k, funcfl.numDensity-2);
        k = max(k, 2);
        p -= k;
        p = min(p, 2.0);
        cof1 = -sixth * p * (p-1.0) * (p-2.0);
        cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
        cof3 = -.5 * p * (p+1.0) * (p - 2.0);
        cof4 = sixth * p * (p * p - 1.0);
        k += 1; // get us to 1-based chapel arrays.
        f = cof1 * funcfl.frho[k-1] + cof2 * funcfl.frho[k] + cof3 * funcfl.frho[k + 1] + cof4 * funcfl.frho[k+2];
      } // verified correct

      rSpace = {1..(numPotentials+1)};
      for (f,m) in zip(rhor[2..], 2..) {
        m -= 1;
        r = (m - 1) * deltaPotential;
        p = r / funcfl.deltaPotential + 1.0;
        k = p : int;
        k = min(k, funcfl.numPotentials - 2);
        k = max(k, 2);
        p -= k;
        p = min(p, 2.0);
        cof1 = -sixth * p * (p-1.0) * (p-2.0);
        cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
        cof3 = -.5 * p * (p+1.0) * (p - 2.0);
        cof4 = sixth * p * (p * p - 1.0);
        k += 1;
        f = cof1 * funcfl.rhor[k-1] + cof2 * funcfl.rhor[k] + cof3 * funcfl.rhor[k+1] + cof4 * funcfl.rhor[k+2];
      }

      for (f,m) in zip(z2r[2..], 2..) {
        m -= 1;
        r = (m - 1) * deltaPotential;
        p = r / funcfl.deltaPotential + 1.0;
        k = p : int;
        k = min(k, funcfl.numPotentials - 2);
        k = max(k, 2);
        p -= k;
        p = min(p,2.0);
        cof1 = -sixth * p * (p - 1.0) * (p - 2.0);
        cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
        cof3 = -.5 * p * (p+1.0) * (p - 2.0);
        cof4 = sixth * p * (p * p - 1.0);
        k += 1;
        var zri : real = cof1 * funcfl.zr[k-1] + cof2 * funcfl.zr[k] + cof3 * funcfl.zr[k+1] + cof4 * funcfl.zr[k+2];
        f = 27.2 * 0.529 * zri * zri;
      }
    }

    proc array2spline() {
      rdr = 1.0 / deltaPotential;
      rdrho = 1.0 / deltaDensity;

      frSplineSpace = {0..((numDensity+1) * 7)-1};
      rzSplineSpace = {0..((numPotentials+1) * 7)-1};

      interp(numDensity, deltaDensity, frho, frho_spline);
      interp(numPotentials, deltaPotential, rhor, rhor_spline);
      interp(numPotentials, deltaPotential, z2r, z2r_spline);
    }

    // copied straight from c++, not entirely sure what it does
    proc interp(n : int, delta : real, arr : [] real, spline : [] real) {
      for (f,m) in zip(arr[2..],1..) {
        spline[m*7 + 6] = f;
      }

      spline[1*7 + 5] = spline[2*7+6] - spline[1*7+6];
      spline[2 * 7 + 5] = 0.5 * (spline[3 * 7 + 6] - spline[1 * 7 + 6]);
      spline[(n - 1) * 7 + 5] = 0.5 * (spline[n * 7 + 6] - spline[(n - 2) * 7 + 6]);
      spline[n * 7 + 5] = spline[n * 7 + 6] - spline[(n - 1) * 7 + 6];

      for m in 3..n-2 {
        spline[m * 7 + 5] = ((spline[(m - 2) * 7 + 6] - spline[(m + 2) * 7 + 6]) +
          8.0 * (spline[(m + 1) * 7 + 6] - spline[(m - 1) * 7 + 6])) / 12.0;
      }

      for m in 1..n-1 {
         spline[m * 7 + 4] = 3.0 * (spline[(m + 1) * 7 + 6] - spline[m * 7 + 6]) -
           2.0 * spline[m * 7 + 5] - spline[(m + 1) * 7 + 5];
         spline[m * 7 + 3] = spline[m * 7 + 5] + spline[(m + 1) * 7 + 5] -
           2.0 * (spline[(m + 1) * 7 + 6] - spline[m * 7 + 6]);
      }

      spline[n*7 + 4] = 0.0;
      spline[n*7 + 3] = 0.0;

      for m in 1..n {
        spline[m * 7 + 2] = spline[m * 7 + 5] / delta;
        spline[m * 7 + 1] = 2.0 * spline[m * 7 + 4] / delta;
        spline[m * 7 + 0] = 3.0 * spline[m * 7 + 3] / delta;
      }
    }

    proc grab(rd,list : [] real) {
      for a in list[2..list.domain.high] {
        a = rd.read(real);
      }
      list[1] = list[2];
    }

    proc compute(store : bool) {
      if debug then writeln("entering EAM compute...");
      var evdwl : real;
      virial = 0.0;
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];
          forall (b,c) in zip(Me.Bins,Me.Count[Me.Real]) {
            for a in b[1..c] {
              a.f = (0.0,0.0,0.0);
            }
          }
        }
      }

      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] do {
          const Me = Grid[ijk];

          for (b,p,c,r) in zip(Me.Bins,Me.Pos[Me.Real], Me.Count[Me.Real], Me.Real) {
            const exist = 1..c;
            for (a,x,i) in zip(b[exist],p[exist], exist) {
              var rhoi : atomic real;
              forall (n,j) in a.neighs[1..a.ncount] {
                const del = x - Me.Pos[n][j];
                const rsq = dot(del, del);

                if rsq < cutforcesq {
                  var p = sqrt(rsq) * rdr + 1.0;
                  var m : int = p : int;
                  if m >= numPotentials - 1 then m = numPotentials - 1;
                  p -= m;
                  if p >= 1.0 then p = 1.0;
                  var val = ((rhor_spline[m*7 + 3] * p + rhor_spline[m * 7 + 4]) * p + rhor_spline[m*7+5]) * p + rhor_spline[m*7+6];
                  rhoi.add(val);
                }
              }

              var p = 1.0 * rhoi.read() * rdrho + 1.0;
              var m = p : int;
              m = max(1, min(m, numDensity - 1));
              p -= m;
              p = min(p, 1.0);
              FP[ijk].Fp[r][i] = (frho_spline[m*7+0] * p + frho_spline[m*7 + 1]) * p + frho_spline[m*7 + 2];

              if store {
                evdwl += ((frho_spline[m*7+3] * p + frho_spline[m*7+4]) * p + frho_spline[m*7+5]) * p + frho_spline[m*7+6];
              }
            }
          }
        }
      }

      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];
          for (Src, Dest, neigh) in zip(Me.SrcSlice, Me.DestSlice, Me.Neighs) {
            FP[ijk].Fp[Dest] = FP[neigh].Fp[Src];
          }
        }
      }

      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];

          for (b,p,c,rbin) in zip(Me.Bins, Me.Pos[Me.Real], Me.Count[Me.Real], Me.Real) {
            const exist = 1..c;
            for (a,x,i) in zip(b[exist],p[exist],exist) {
              var fx, fy, fz : real;
              for (n,j) in a.neighs[1..a.ncount] {
                const del = x - Me.Pos[n][j];
                const rsq = dot(del, del);

                if rsq < cutforcesq {
                  const r = sqrt(rsq);
                  var p = r * rdr + 1.0;
                  var m : int = p : int;
                  if m >= numPotentials - 1 then m = numPotentials - 1;
                  p -= m;
                  if p >= 1.0 then p = 1.0;

                  var rhoip = (rhor_spline[m * 7 + 0] * p + rhor_spline[m*7+1]) * p + rhor_spline[m*7 + 2];
                  var z2p = (z2r_spline[m * 7 + 0] * p + z2r_spline[m * 7 + 1]) * p + z2r_spline[m * 7 + 2];
                  var z2 = ((z2r_spline[m * 7 + 3] * p + z2r_spline[m * 7 + 4]) * p + z2r_spline[m * 7 + 5]) * p + z2r_spline[m*7+6];

                  var recip = 1.0/r;
                  var phi = z2 * recip;
                  var phip = z2p * recip - phi * recip;
                  var psip = FP[ijk].Fp[rbin][i] * rhoip + FP[ijk].Fp[n][j] * rhoip + phip;
                  var fpair = -psip * recip;

                  const res = del * fpair;
                  (fx, fy, fz) = (fx, fy, fz) + res;

                  fpair *= .5;

                  if store {
                    virial += rsq * fpair;
                    evdwl += .5 * phi;
                  }
                }
              }
              a.f = (fx, fy, fz);
            }
          }
        }
      }
      eng_vdwl += 2.0 * evdwl;
    }
  }

  // Lennard-Jones potential
  class ForceLJ : Force {
    proc ForceLJ(cf : real) {
      cutforcesq = cf * cf;
    }

    proc compute(store : bool) : void {
      eng_vdwl = 0;
      virial = 0;
      var fTimer : Timer;

      // wipe old forces of real AND ghost atoms
      fTimer.start();
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          const Me = Grid[ijk];
          forall (b,c) in zip(Me.Bins,Me.Count[Me.Real]) {
            for a in b[1..c] {
              a.f = (0.0,0.0,0.0);
            }
          }
        }
      }

      // track wipetime for testing purposes
      wipetime += fTimer.elapsed();
      fTimer.stop();
      fTimer.clear();

      // for each atom, compute force between itself and its neighbors
      // can make outer loop parallel if we can make force modifications atomic
      fTimer.start();
      var engs, virs : [LocaleGridDom] real;
      coforall ijk in LocaleGridDom {
        on LocaleGrid[ijk] {
          // bring stuff onto this locale
          const Me = Grid[ijk];

          // by using an atomic variable, we can make the outer loop parallel
          var eng, vir : atomic real;

          forall (b,p,c) in zip(Me.Bins, Me.Pos[Me.Real], Me.Count[Me.Real], Me.Real) {
            for (a, x) in zip(b[1..c],p[1..c]) {
              for(n,i) in a.neighs[1..a.ncount] {
                const del = x - Me.Pos[n][i];
                const rsq = dot(del,del);

                if rsq < cutforcesq {
                  const sr2: real = 1.0 / rsq;
                  const sr6 : real = sr2 * sr2 * sr2;
                  const force : real = 48.0 * sr6 * (sr6 - .5) * sr2;
                  a.f += del * force;

                  if store {
                    eng.add(4.0 * sr6 * (sr6 - 1.0));
                    vir.add(.5 * rsq * force);
                  }
                }
              }
            }
          }

          // cut down on communication
          engs[ijk] = eng.read();
          virs[ijk] = vir.read();
        }
      }

      // update values thermo depends on
      eng_vdwl = + reduce engs;
      virial = + reduce virs;

      // higher resolution performance timings - look at the actual computation
      maintime += fTimer.elapsed();
    }
  }
}