Made a trivial change to log this correction to a comment in my
previous checkin:

Instead of writing

  var d: domain(1,stridable=true) = [1..n by 2];

the way this works is

  var d: domain(1) distributed(SingleLocaleDistribution(stridable=true))
       = [1..n by 2];

This is because stridable is not part of all arithmetic domains but
rather is part of this particular distribution.  Perhaps it should be
part of all arithmetic domains though.

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

def _build_domain_type(dist, param rank : int,
                       type dimensional_index_type = int) {
  var x = dist.buildDomain(rank, dimensional_index_type);
  return _domain(x.type, x.getValue(x.getHeadCursor()).type, dimensional_index_type, rank, x);
}

def _build_domain_type(dist, type ind) where !__primitive("isEnumType", ind) {
  var x = dist.buildDomain(ind);
  return _domain(x.type, ind, ind, 1, x);
}

def _build_domain_type(dist, type ind) where __primitive("isEnumType", ind) {
  var x = dist.buildEnumDomain(ind);
  return _domain(x.type, ind, ind, 1, x);
}

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

// sjd: note that sparse domain is not pointing to bounding domain anymore
//      as of BLC change of 3/23/07
def _build_sparse_subdomain_type(dist, parentDom) {
  var x = dist.buildSparseDomain(parentDom.rank, parentDom._dim_index_type, parentDom._value.stridable);
  return _domain(x.type, x.getValue(x.getHeadCursor()).type, parentDom._dim_index_type, parentDom.rank, x);
}

record _ArrayTypeInfo {
  type eltType;
  var dom: _domain;
}

def _init(a: _ArrayTypeInfo)
  return a.dom.buildArray(a.eltType);

def _build_array_type(dom, type eltType)
  return _ArrayTypeInfo(eltType, dom);

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

def _build_domain(x) // where x:_domain // see test/arrays/bradc/paulslice.chpl
  return x;                             // is * on arithmetic sequences?

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

def _build_domain(ranges: range(?eltType,0,?stridable) ...?rank) {
  type t = ranges(1).eltType;
  param domain_stridable = _any_stridable(ranges);
  var d: domain(rank, t)
           distributed (SingleLocaleDistribution(stridable=domain_stridable));
  d.setIndices(ranges);
  return d;
}

def _build_exclusive_upper_domain(x: _domain)
  return x.buildExclusiveUpperDomain();

def _build_index_type(param i: int) where i > 1 {
  var x : i*int;
  return x;
}

def _build_index_type(param i: int) where i == 1 {
  var x : int;
  return x;
}

def _build_index_type(dom) {
  var x: dom._index_type;
  return x;
}

pragma "domain"
record _domain {
  type _domain_type;
  type _index_type;
  type _dim_index_type;
  param rank : int;
  var _value : _domain_type;
  var _promotionType : _index_type;

  def initialize() {
    if _value == nil {
      var x = buildEmptyDomain();
      _value = x._value;
    }
  }

  def getHeadCursor()
    return _value.getHeadCursor();

  def getNextCursor(c)
    return _value.getNextCursor(c);

  def getValue(c)
    return _value.getValue(c);

  def isValidCursor?(c)
    return _value.isValidCursor?(c);

  def this(dim : int)
    return _value(dim);

  def buildArray(type eltType) {
    var x = _value.buildArray(eltType);
    _value._arrs #= x;
    return _array(x.type, _index_type, _dim_index_type, eltType, rank, x);
  }

  def buildEmptyDomain() {
    var x = _value.buildEmptyDomain();
    return _domain(x.type, _index_type, _dim_index_type, rank, x);
  }

  def buildSparseDomain() {
    var x = _value.buildSparseDomain();
    return _domain(x.type, _index_type, _dim_index_type, rank, x);
  }

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

  def buildExclusiveUpperDomain()
    return _domain(_domain_type, _index_type, _dim_index_type, rank, _value.buildExclusiveUpperDomain());

  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 expand(i: int ...rank)
    return expand(i);

  def expand(i: rank*int) {
    var x = _value.expand(i);
    return _domain(x.type, _index_type, _dim_index_type, rank, x);
  }

  def expand(i: int) where rank > 1 {
    var x = _value.expand(i);
    return _domain(x.type, _index_type, _dim_index_type, rank, x);
  }

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

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

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

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

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

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

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

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

  def getIndices()
    return _value.getIndices();
}

def =(a: _domain, b: _domain) {
//  if a == nil then return b; // stopgap: why? --sjd
  for e in a._value._arrs do
    e.reallocate(b);
  a._value.setIndices(b._value.getIndices());
  return a;
}

def _pass(a: _domain)
  return a;

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

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

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

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

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

  def _dom var {
    var x : _domain(_value.dom.type, _index_type, _dim_index_type, rank);
    x._value = _value.dom;
    return x;
  }

  def this(d: _domain) {
    var x = _value.slice(d._value);
    return _array(x.type, _index_type, _dim_index_type, eltType, rank, x);
  }

  def this(i: range(_dim_index_type,0,?stridable) ...rank) {
    var d = [(...i)];
    return this(d);
  }

  def =this(d: _domain, val) {
    var y = _array(_array_type, _index_type, _dim_index_type, eltType, rank);
    y._value = _value.slice(d._value);
    y = val;
  }

  def =this(d: _domain, val: eltType) {
    var y = _array(_array_type, _index_type, _dim_index_type, eltType, rank);
    y._value = _value.slice(d._value);
    y = val;
  }

  def =this(i: range(_dim_index_type,0,?stridable) ...rank, val) where rank > 0 {
    var d = [(...i)];
    var y = _array(_array_type, _index_type, _dim_index_type, eltType, rank);
    y._value = _value.slice(d._value);
    y = val;
  }

  def =this(i: range(_dim_index_type,0,?stridable) ...rank, val: eltType) where rank > 0 {
    var d = [(...i)];
    var y = _array(_array_type, _index_type, _dim_index_type, eltType, rank);
    y._value = _value.slice(d._value);
    y = val;
  }

  def this(i: _index_type)
    return _value(i);

  def this(i: _dim_index_type ...rank) where rank > 1
    return _value(i);

  def =this(i: _index_type, val: eltType) {
    _value(i) = val;
  }

  def =this(i: _dim_index_type ...rank, val: eltType) where rank > 1 {
    _value(i) = val;
  }

  def getHeadCursor()
    return _value.getHeadCursor();

  def getNextCursor(c)
    return _value.getNextCursor(c);

  def getValue(c)
    return _value.getValue(c);

  def isValidCursor?(c)
    return _value.isValidCursor?(c);

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

  def view(d: _domain) {
    var x = _value.view(d._value);
    return _array(x.type, _index_type, _dim_index_type, eltType, rank, x);
  }
}

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

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

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

def _pass(a: _array) {
  return a;
}

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

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

// this is the class that all array classes are derived from
pragma "BaseArray"
class BaseArray {
  def reallocate(d: _domain) {
    halt("reallocating not support for this array type");
  }

  def purge( ind: int) {
    halt("purging not supported for this array type");
  }

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

class BaseDomain {
  var _arrs: seq of BaseArray;
}


OOps, should've tested that merge before committing

// Wrapper for FFTW version 3.

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

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

  As in general FFTW, the flow is to:

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

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

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

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

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

  5) Call cleanup().

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

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

module FFTW {

  use SysCTypes;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  // Since the calls to FFTW are the same, pull the extern declarations out.
  // TODO : This should be cleaned up further and made consistent across the file

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

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

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

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

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

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

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

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

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

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

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

  // Using plans
  /*
    Executes the given plan.

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

  /*
    Destroys the given plan.

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

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

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

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

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

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

    extern proc fftw_execute(const plan : fftw_plan);

    extern proc fftw_destroy_plan(plan : fftw_plan);

    extern proc fftw_cleanup();

    extern proc fftw_init_threads() : c_int;

    extern proc fftw_cleanup_threads();

    extern proc fftw_plan_with_nthreads(n : c_int);
  }
}

// Wrapper for FFTW version 3.

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

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

  As in general FFTW, the flow is to:

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

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

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

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

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

  5) Call cleanup().

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

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

module FFTW {

  use SysCTypes;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  // Since the calls to FFTW are the same, pull the extern declarations out.
  // TODO : This should be cleaned up further and made consistent across the file

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

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

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

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

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

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

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

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

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

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

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

  // Using plans
  /*
    Executes the given plan.

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

  /*
    Destroys the given plan.

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

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

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

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

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

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

    extern proc fftw_execute(const plan : fftw_plan);

    extern proc fftw_destroy_plan(plan : fftw_plan);

    extern proc fftw_cleanup();

    extern proc fftw_init_threads() : c_int;

    extern proc fftw_cleanup_threads();

    extern proc fftw_plan_with_nthreads(n : c_int);
  }
}


Tweaks to the replicated dimension descriptor for Dimensional.

* numLocales is now const.

* The same descriptor object can now be shared between different
DimensionalDist objects. I added a check to verify that the locale
cache the descriptor keeps is the same for all uses of a given
descriptor object. If not, it halts.

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

// One-dimensional distributions for use with Dimensional.

use d;

/////////////////////////////////////////////////////////////////////////////
// 1-d distribution - replicated

class vdist {
  // REQ over how many locales
  // todo: can the Dimensional do without this one?
  const numLocales: int;

  // Locale ID in our dimension of the locale this instance is on.
  // Because of these, it may be incorrect to share the same vdist
  // object between multiple DimensionalDist objects.
  var localLocID = invalidLocID;
  var localLocIDlegit = false;
}

class vdom {
  // REQ the parameters of our dimension of the domain being created
  type idxType;
  param stridable: bool;

  // convenience
  proc rangeT type  return range(idxType, BoundedRangeType.bounded, stridable);
//todo-remove?  proc domainT type return domain(1, idxType, stridable);

  // our range
  var wholeR: rangeT;

  // locale ID in our dimension of the locale this instance is on
  var localLocID = invalidLocID;
  var localLocIDlegit = false;
}

class vlocdom {
  type stoIndexT;
  param stridable;
  // our copy of wholeR
  var locWholeR: range(stoIndexT, stridable=stridable);
}

/////////// privatization - start

// REQ does this class support privatization?
proc vdist.dsiSupportsPrivatization1d() param return true;

// REQ if privatization is supported - same purpose as dsiGetPrivatizeData()
proc vdist.dsiGetPrivatizeData1d() {
  return tuple(numLocales);
}

// REQ if privatization is supported - same purpose as dsiPrivatize()
proc vdist.dsiPrivatize1d(privatizeData) {
  return new vdist(numLocales = privatizeData(1));
}

// REQ does this class need -- and provide -- the localLocID?
// dsiStoreLocalLocID1d() will be invoked on privatized copies
// only when dsiSupportsPrivatization1d is true (obviously).
proc vdist.dsiUsesLocalLocID1d() param return true;

// REQ if dsiUsesLocalLocID1d: store the localLocID
// If 'legit' is false, this is a privatized copy on a locale
// that's not among our Dimensional distribution's target locales.
proc vdist.dsiStoreLocalLocID1d(localLocID: locIdT, legit: bool) {
  // This will get invoked multiple times if this vdist object
  // is reused in another DimensionalDist object.
  // In which case the cache better be the same for both uses.
  if this.localLocID == invalidLocID {
    // This is the intended use.
    this.localLocID = localLocID;
    this.localLocIDlegit = legit;
  } else {
    if this.localLocID == localLocID &&
       this.localLocIDlegit == localLocIDlegit {
      // alright, let it be for now
    } else {
      halt("Inconsistent locale cache in a ReplicatedDim descriptor object. One cause can be a reuse of such an object for different DimensionalDist objects whose target locales differ and/or this object is reused in a different dimension.");
    }
  }
}

// REQ if dsiUsesLocalLocID1d: retrieve the localLocID and its legitimacy
proc vdist.dsiGetLocalLocID1d(): (locIdT, bool) {
  assert(this.localLocID != invalidLocID);
  return (this.localLocID, this.localLocIDlegit);
}

// REQ does this class support privatization?
proc vdom.dsiSupportsPrivatization1d() param return true;

// REQ if privatization is supported - same purpose as dsiGetPrivatizeData()
proc vdom.dsiGetPrivatizeData1d() {
  return tuple(wholeR);
}

// REQ if privatization is supported - same purpose as dsiPrivatize()
// 'privDist' is the corresponding 1-d distribution descriptor,
// privatized (if it supports privatization).
proc vdom.dsiPrivatize1d(privDist, privatizeData) {
  assert(privDist.locale == here); // sanity check
  return new vdom(idxType   = this.idxType,
                  stridable = this.stridable,
                  wholeR    = privatizeData(1));
}

// REQ if privatization is supported - same purpose as dsiGetReprivatizeData()
proc vdom.dsiGetReprivatizeData1d() {
  return tuple(wholeR);
}

// REQ if privatization is supported - same purpose as dsiReprivatize()
proc vdom.dsiReprivatize1d(other, reprivatizeData) {
  if other.idxType   != this.idxType ||
     other.stridable != this.stridable then
    compilerError("inconsistent types in privatization");

  this.wholeR = reprivatizeData(1);
}

// REQ does this class need the localLocID?
// (See also vdist.dsiUsesLocalLocID1d.)
proc vdom.dsiUsesLocalLocID1d() param return true;

// REQ if dsiUsesLocalLocID1d: store the localLocID
// This is invoked when a DimensionalDom is created, including
// from DimensionalDom.dsiPrivatize(), but not DimensionalDom.dsiReprivatize()
proc vdom.dsiStoreLocalLocID1d((localLocID, legit): (locIdT, bool)) {
  // no big deal, but currently we intend to update this just once
  assert(this.localLocID == invalidLocID);
  this.localLocID = localLocID;
  this.localLocIDlegit = legit;
}

// REQ if dsiUsesLocalLocID1d: retrieve the localLocID
proc vdom.dsiGetLocalLocID1d(): (locIdT, bool) {
  assert(this.localLocID != invalidLocID);
  return (this.localLocID, this.localLocIDlegit);
}

// REQ does a local domain descriptor use a pointer
// to the privatized global domain descriptor?
// Consulted only when dsiSupportsPrivatization1d is true.
proc vdom.dsiLocalDescUsesPrivatizedGlobalDesc1d() param return false;

// REQ if dsiLocalDescUsesPrivatizedGlobalDesc1d: store the pointer to
// the privatized copy of the global descriptor that is on vlocdom's locale.
// Note: this is invoked only while privatizing the 1-d global descriptor.
// In particular, this is not invoked when the local descriptors are
// initially created.
// todo: we already pass globDD to all methods on vlocdom, so probably
// we do not need this
//
proc vlocdom.dsiStoreLocalDescToPrivatizedGlobalDesc1d(privGlobDesc) {
  compilerError("should not need dsiStoreLocalDescToPrivatizedGlobalDesc1d");
  // e.g.: this.privGlobDesc = privGlobDesc;
}

/////////// privatization - end

// REQ create a 1-d global domain descriptor for dsiNewRectangularDom()
// where our dimension is a range(idxType, bounded, stridable)
// stoIndexT is the same as in vdom.dsiNewLocalDom1d.
proc vdist.dsiNewRectangularDom1d(type idxType, param stridable: bool,
                                  type stoIndexT)
{
  // ignore stoIndexT - all we need is for other places to work out
  return new vdom(idxType, stridable);
}

// A nicety: produce a string showing the parameters.
// This might get renamed in the future.
proc vdist.toString()
  return "ReplicatedDim(" + numLocales:string + ")";

// REQ is this a replicated distribution?
proc vdom.dsiIsReplicated1d() param return true;

// REQ produce a local domain descriptor for our dimension
// on a locale for the given locale index locId, for dsiNewRectangularDom().
// Note: may be invoked multiple times/locales with the same locId.
// - one for each point in the other dimension(s).
// stoIndexT must be the index type of the range returned by
// dsiSetLocalIndices1d().
proc vdom.dsiNewLocalDom1d(type stoIndexT, locId: locIdT) {
  return new vlocdom(stoIndexT, wholeR.stridable);
}

// REQ given our dimension of the array index, on which locale is it located?
proc vdist.dsiIndexToLocale1d(indexx): locIdT {
  assert(localLocIDlegit);
  return localLocID;
}

// REQ update our data structures as needed upon dsiSetIndices()
proc vdom.dsiSetIndices1d(rangeArg: rangeT): void {
  wholeR = rangeArg;
}

// REQ update our local data structures as needed upon dsiSetIndices();
// return a range of "storage indices" to be stored locally.
//
// Each "storage index" corresponds to some "user index" (i.e.
// an index used to index into the array mapped with the Dimensional
// distribution) that is allocated on the receiver's locale.
//
// The returned range indicates *how many* of user indices are stored locally.
// It does NOT indicate *which* user indices those are. I.e. this range
// may contain exactly the user indices that are stored locally - or not
// (in which case dsiAccess1d() et al. will need to map each user index
// to an index in the range this returns).
//
// 'globDD' is the global domain descriptor (a 'vdom', in this case).
// The return type must be range(stoIndexT, stridable= true or false)
// where 'stoIndexT' is the arg of dsiNewLocalDom1d() that created 'this'.
//
proc vlocdom.dsiSetLocalIndices1d(globDD, locId: locIdT) {
  locWholeR = globDD.wholeR;
  return locWholeR;
}

// REQ-2 - like dsiBuildRectangularDom: create a new global domain descriptor
// for our dimension when the corresponding domain is created as a modification
// of an existing domain, preserving the domain map.
//
// 'rangeArg' is what dsiSetIndices1d would be invoked on.
// 'DD' is the distribution descriptor for 'this' (a 'vdist', in this case).
// 'idxType' and 'stoIndexT' of the result are the same as for 'this'.
//
// Just like in the multi-dimensional case, we show here how to write
// dsiBuildRectangularDom1d from existing methods.
//
proc vdom.dsiBuildRectangularDom1d(DD,
                                   param stridable:bool,
                                   rangeArg: range(idxType,
                                                   BoundedRangeType.bounded,
                                                   stridable))
{
  type dummy_stoIndexT = int; // use 'this.stoIndexT' when needed
  const result = DD.dsiNewRectangularDom1d(this.idxType, stridable,
                                           dummy_stoIndexT);
  result.dsiSetIndices1d(rangeArg);
  return result;
}

// REQ-2 - the local-descriptor companion to dsiBuildRectangularDom1d.
//
// Just like
//   dsiBuildRectangularDom1d = dsiNewRectangularDom1d + dsiSetIndices1d,
// we show here
//   dsiBuildLocalDom1d       = dsiNewLocalDom1d       + dsiSetLocalIndices1d.
//
// 'idxType' and 'stoIndexT' of the result are the same as for 'this'.
// 'newGlobDD' is the global domain descriptor (a 'vdom', in this case)
// whose local descriptor is to be created.
//
// Returns (new local 1-d domain descriptor, new storage range),
// which could be the results of dsiNewLocalDom1d() and
// dsiSetLocalIndices1d(), resp.
//
proc vlocdom.dsiBuildLocalDom1d(newGlobDD, locId: locIdT) {
  type  old_stoIndexT = this.stoIndexT;

  const newLocDD = newGlobDD.dsiNewLocalDom1d(old_stoIndexT, locId);
  const newStoRng = newLocDD.dsiSetLocalIndices1d(newGlobDD, locId);

  return (newLocDD, newStoRng);
}

// REQ indicate whether "storage indices" returned by dsiSetLocalIndices1d()
// correspond to "user indices". (In which case dsiAccess1d() returns
// (as a part of a tuple) its argument without modification.)
// As of this writing, this is used for assertions only.
proc vdom.dsiStorageUsesUserIndices() param return true;

// REQ given one dimension of the array index, return
// (locale id, storage index on that locale) for that index.
// The storage index must have the type DimensionalDom.stoIndexT.
proc vdom.dsiAccess1d(indexx: idxType): (locIdT, idxType) {
  // todo: provide access from non-"legit" locales;
  // maybe to locId=0?
  assert(localLocIDlegit);
  return (localLocID, indexx);
}

// REQ yield the densified range to be used in the leader iterator
// on this descriptor's locale, when there is only a single task.
iter vlocdom.dsiMyDensifiedRangeForSingleTask1d(globDD)
  : dsiMyDensifiedRangeType1d(globDD)
{
  yield 0:globDD.idxType..#locWholeR.length;
}

// REQ whether this distribution can handle only a single task per locale.
proc vdom.dsiSingleTaskPerLocaleOnly1d() param return false;

// REQ unless dsiSingleTaskPerLocaleOnly1d() is true.
// Yield the densified range to be used in the leader iterator
// on this descriptor's locale for task 'taskid' of 0..#numTasks.
// Note: should be densified w.r.t. the entire global range in this dimension.
proc vlocdom.dsiMyDensifiedRangeForTaskID1d(globDD, taskid:int, numTasks:int)
  : dsiMyDensifiedRangeType1d(globDD)
{
  type IT = globDD.idxType;
  const (startIx, endIx) = _computeChunkStartEnd(locWholeR.length,
                                                 numTasks:IT, taskid:IT+1);
  return (startIx:IT - 1) .. (endIx:IT - 1);
}

// REQ the range type returned/yielded by dsiMyDensifiedRangeForSingleTask1d()
// and (if applicable) by dsiMyDensifiedRangeForTaskID1d().
proc vlocdom.dsiMyDensifiedRangeType1d(globDD) type
  return range(globDD.idxType);

// REQ for iterating over array elements.
// Yield tuples (locId, iterable) s.t. the following results in
// traversing all array elements in order:
//   for (l,itr) in dsiSerialArrayIterator1d() do
//    for ix in itr do
//      yield (local storage on locale l)[ix]
iter vdom.dsiSerialArrayIterator1d() {
  assert(localLocIDlegit);
  yield (localLocID, wholeR);
}

// REQ for array follower iterator.
//
// Yield pairs (locId, storageOffset) that traverse storage for
// the array elements corresponding to the indices given by undensRange,
// preserving the order in which those indices occur.
//
// This is similar to dsiSerialArrayIterator1d(), except
// * only the indicated elements must be traversed, in the indicated order;
// * one storage offset is yielded each time.
//
// The latter is chosen to simplify dsiFollowerArrayIterator1d implementations.
// It also eliminates one loop nest per dimension in DimensionalArr follower.
//
iter vdom.dsiFollowerArrayIterator1d(undensRange): (locIdT, idxType) {
  assert(localLocIDlegit);
  for i in undensRange do
    yield (localLocID, i);
}

/////////////////////////////////////////////////////////////////////////////
// 1-d distribution - block

class sdist {
  // the type of bbStart, bbLength
  // (also (ab)used as the idxType of the domains created over this dist.)
  // (todo - straighten that out)
  type idxType;

  const numLocales: int;

  // the .low and .length of BlockDist's 'boundingBox' for our dimension
  const bbStart: idxType;
  const bbLength: idxType;

  proc boundingBox return bbStart .. (bbStart + bbLength - 1);
}

class sdom {
  type idxType;
  param stridable: bool;

  // convenience
  proc rangeT type  return range(idxType, BoundedRangeType.bounded, stridable);

  // our range
  var wholeR: rangeT;

  // privatized distribution descriptor
  const pdist;
}

class slocdom {
  var myRange;
}

/////////// privatization - start

proc sdist.dsiSupportsPrivatization1d() param return true;

proc sdist.dsiGetPrivatizeData1d() {
  return (numLocales, bbStart, bbLength);
}

proc sdist.dsiPrivatize1d(privatizeData) {
  return new sdist(privatizeData, this.idxType);
}

// constructor for privatization
proc sdist.sdist(privatizeData, type idxType) {
  numLocales = privatizeData(1);
  bbStart = privatizeData(2);
  bbLength = privatizeData(3);
}

proc sdist.dsiUsesLocalLocID1d() param return false;

proc sdom.dsiSupportsPrivatization1d() param return true;

proc sdom.dsiGetPrivatizeData1d() {
  return tuple(wholeR);
}

proc sdom.dsiPrivatize1d(privDist, privatizeData) {
  assert(privDist.locale == here); // sanity check
  return new sdom(idxType   = this.idxType,
                  stridable = this.stridable,
                  wholeR    = privatizeData(1),
                  pdist     = privDist);
}

proc sdom.dsiGetReprivatizeData1d() {
  return tuple(wholeR);
}

proc sdom.dsiReprivatize1d(other, reprivatizeData) {
  if other.idxType   != this.idxType ||
     other.stridable != this.stridable then
    compilerError("inconsistent types in privatization");

  this.wholeR = reprivatizeData(1);
}

proc sdom.dsiUsesLocalLocID1d() param return false;

proc sdom.dsiLocalDescUsesPrivatizedGlobalDesc1d() param return false;

/////////// privatization - end

// Constructor. idxType is inferred from the 'bbLow' argument
// (alternative: default to 'int' instead).
proc sdist.sdist(numLocales, boundingBoxLow, boundingBoxHigh, type idxType = boundingBoxLow.type) {
  if !(boundingBoxLow <= boundingBoxHigh) then halt("'sdist' distribution must have a non-empty bounding box between boundingBoxLow and boundingBoxHigh; got ", boundingBoxLow, " .. ", boundingBoxHigh);
  assert(numLocales > 0); // so we can cast it to any int type
  this.numLocales = numLocales;
  this.bbStart = boundingBoxLow;
  this.bbLength = (boundingBoxHigh - boundingBoxLow + 1):idxType;
  assert(this.bbLength > 0);
}

proc sdist.toString()
  return "BlockDim(" + numLocales:string + ", " + boundingBox:string + ")";

proc sdist.dsiNewRectangularDom1d(type idxType, param stridable: bool,
                                  type stoIndexT)
{
  // ignore stoIndexT - all we need is for other places to work out
  if idxType != this.idxType then
    compilerError("The index type ", typeToString(idxType),
                  " does not match the index type ",typeToString(this.idxType),
                  " of the 'sdist' 1-d distribution");
  return new sdom(idxType = idxType, stridable = stridable, pdist = this);
}

proc sdom.dsiIsReplicated1d() param return false;

proc sdom.dsiNewLocalDom1d(type stoIndexT, locId: locIdT) {
  var defaultVal: range(stoIndexT, stridable=this.stridable);
  return new slocdom(myRange = defaultVal);
}

proc sdist.dsiIndexToLocale1d(indexx): locIdT {
  if indexx <= bbStart then
    return 0;

  // (numLocales-1) is the answer when
  //   indexx >= bbStart + (numLocales-1) * (bbLength+1) / numLocales
  // whose r.h.s. could be stored in 'sdist', trading memory for computation.
  // We simplify the condition to 'indexx >= bbStart + bbLength'.
  // (Note: (bbStart + bbLength - 1) will not always map to (numLocales - 1)).
  // If the condition doesn't hold, the formula gives the answer.
  // (Compared to the formula in Block.targetLocsIdx, we run comparisons
  // *before* multiplying and dividing.)
  //
  if indexx >= bbStart + bbLength then
    return numLocales - 1;

  // need to think what to do if numLocales is not int
  type adjT =
    if numLocales.type == int && idxType == uint(64)
    then uint(64)
    else numLocales.type;

  const result = (indexx - bbStart) * numLocales:adjT / bbLength;
  assert(0 <= result && result < numLocales:adjT);
  return result:locIdT;
}

proc sdom.dsiSetIndices1d(rangeArg: rangeT): void {
  wholeR = rangeArg;
}

// Helper: compute the chunk to be stored on locID
proc sdom._dsiComputeMyRange(locId): rangeT {
  const dom = this;
  const dist = dom.pdist;
  // see LocBlock.LocBlock()
  const (blo, bhi) = _computeBlock(dist.bbLength, dist.numLocales,
           locId, max(dom.idxType), min(dom.idxType), dist.bbStart);
  const myChunk = blo..bhi;
  // see Block.getChunk()
  const chunk = dom.wholeR(myChunk);
  return chunk;
}

proc slocdom.dsiSetLocalIndices1d(globDD, locId: locIdT) {
  myRange = globDD._dsiComputeMyRange(locId);
  return myRange;
}

/////////////////////////////////

proc sdom.dsiBuildRectangularDom1d(DD,
                                   param stridable:bool,
                                   rangeArg: range(idxType,
                                                   BoundedRangeType.bounded,
                                                   stridable))
{
  // There does not seem to be any optimizations from merging the two calls.
  type dummy_stoIndexT = int;
  const result = DD.dsiNewRectangularDom1d(this.idxType, stridable,
                                           dummy_stoIndexT);
  result.dsiSetIndices1d(rangeArg);
  return result;
}

proc slocdom.dsiBuildLocalDom1d(newGlobDD, locId: locIdT) {
  type  old_stoIndexT = this.myRange.idxType; // essentially 'this.stoIndexT'

  const newLocDD = newGlobDD.dsiNewLocalDom1d(old_stoIndexT, locId);
  const newStoRng = newLocDD.dsiSetLocalIndices1d(newGlobDD, locId);

  return (newLocDD, newStoRng);
}

/////////////////////////////////

proc sdom.dsiStorageUsesUserIndices() param return true;

proc sdom.dsiAccess1d(indexx: idxType): (locIdT, idxType) {
  return (pdist.dsiIndexToLocale1d(indexx), indexx);
}

iter slocdom.dsiMyDensifiedRangeForSingleTask1d(globDD) {
  const locRange = densify(myRange, globDD.wholeR, userErrors=false);
  yield locRange: range(globDD.idxType);
}

proc sdom.dsiSingleTaskPerLocaleOnly1d() param return false;

proc slocdom.dsiMyDensifiedRangeForTaskID1d(globDD, taskid:int, numTasks:int) {
  const locRange = densify(myRange, globDD.wholeR, userErrors=false);
  // Copied straight from BlockDom leader - replace locBlock(parDim)->locRange.
  const (lo, hi) = _computeBlock(locRange.length, numTasks, taskid,
                                 locRange.high, locRange.low, locRange.low);

  // If this can occasionally be an empty range, add a check to Dimensional
  // to not yield anything in such a case.
  return lo..hi;
}

proc slocdom.dsiMyDensifiedRangeType1d(globDD) type
  return range(globDD.idxType);

iter sdom.dsiSerialArrayIterator1d() {
  // The Block distribution assigns indices to locales contiguously and
  // so that (i1<i2) => (locId1<=locId2). This is defined by the domain map.
  // As we traverse the range (i.e. this.wholeR), locIds will increase
  // if the stride is positive, and decrease otherwise.
  // On a single locale, slocdom.myRange (aka this._dsiComputeMyRange())
  // already happens to reflect the desired direction.

  for locId in (0..#pdist.numLocales) by sgn(wholeR.stride) {
    // We do not go to slocdom for myRange because (a) recomputing it
    // is probably cheaper than going to a remote locale, and
    // (b) we do not store slocdom objects in sdom.
    const localRange = _dsiComputeMyRange(locId);
//writeln("sdom.dsiSerialArrayIterator1d  ", locId, ", ", localRange);
    yield (locId, localRange);
  }
}

iter sdom.dsiFollowerArrayIterator1d(undensRange): (locIdT, idxType) {
//writeln("sdom.dsiFollowerArrayIterator1d  undensRange ", undensRange);

  const heuristic_weight_rangepoint = 2; // measure of work in (a) below
  const heuristic_weight_locale = 3;     // measure of work in (b)

  if heuristic_weight_locale * undensRange.stride >
      heuristic_weight_rangepoint * pdist.bbLength / pdist.numLocales
  {
//writeln("sdom.dsiFollowerArrayIterator1d case A");
    // (a) no benefit to factor out computations for a particular locale
    for ix in undensRange do
      yield dsiAccess1d(ix);

  } else {
    // (b) per-locale computation is amortized over enough undensRange indices
    const lowLocId = pdist.dsiIndexToLocale1d(undensRange.alignedLow);
    const highLocId = pdist.dsiIndexToLocale1d(undensRange.alignedHigh);

    // check for a supposedly more common case of following our own leader
    if lowLocId == highLocId {
//writeln("sdom.dsiFollowerArrayIterator1d case B1  lowLocId ", lowLocId);
      for i in undensRange do
        yield (lowLocId, i);

    } else {
      assert(lowLocId < highLocId);
      for locId in lowLocId .. highLocId by sgn(undensRange.stride) {
        // We do not go to slocdom for myRange - same rationale as above.
        const thisLocaleRange = _dsiComputeMyRange(locId);
        const localRangeToFollow = undensRange(thisLocaleRange);
//writeln("sdom.dsiFollowerArrayIterator1d case B2  locId ", locId, "  localRangeToFollow ", localRangeToFollow);
        for i in localRangeToFollow do
          yield (locId, i);
      }
    }
  }
}

// One-dimensional distributions for use with Dimensional.

use d;

/////////////////////////////////////////////////////////////////////////////
// 1-d distribution - replicated

class vdist {
  // REQ over how many locales
  // todo: can the Dimensional do without this one?
  var numLocales: int;

  // locale ID in our dimension of the locale this instance is on
  var localLocID = invalidLocID;
  var localLocIDlegit = false;
}

class vdom {
  // REQ the parameters of our dimension of the domain being created
  type idxType;
  param stridable: bool;

  // convenience
  proc rangeT type  return range(idxType, BoundedRangeType.bounded, stridable);
//todo-remove?  proc domainT type return domain(1, idxType, stridable);

  // our range
  var wholeR: rangeT;

  // locale ID in our dimension of the locale this instance is on
  var localLocID = invalidLocID;
  var localLocIDlegit = false;
}

class vlocdom {
  type stoIndexT;
  param stridable;
  // our copy of wholeR
  var locWholeR: range(stoIndexT, stridable=stridable);
}

/////////// privatization - start

// REQ does this class support privatization?
proc vdist.dsiSupportsPrivatization1d() param return true;

// REQ if privatization is supported - same purpose as dsiGetPrivatizeData()
proc vdist.dsiGetPrivatizeData1d() {
  return tuple(numLocales);
}

// REQ if privatization is supported - same purpose as dsiPrivatize()
proc vdist.dsiPrivatize1d(privatizeData) {
  return new vdist(numLocales = privatizeData(1));
}

// REQ does this class need -- and provide -- the localLocID?
// dsiStoreLocalLocID1d() will be invoked on privatized copies
// only when dsiSupportsPrivatization1d is true (obviously).
proc vdist.dsiUsesLocalLocID1d() param return true;

// REQ if dsiUsesLocalLocID1d: store the localLocID
// If 'legit' is false, this is a privatized copy on a locale
// that's not among our Dimensional distribution's target locales.
proc vdist.dsiStoreLocalLocID1d(localLocID: locIdT, legit: bool) {
  // no big deal, but currently we intend to update this just once
  assert(this.localLocID == invalidLocID);
  this.localLocID = localLocID;
  this.localLocIDlegit = legit;
}

// REQ if dsiUsesLocalLocID1d: retrieve the localLocID and its legitimacy
proc vdist.dsiGetLocalLocID1d(): (locIdT, bool) {
  assert(this.localLocID != invalidLocID);
  return (this.localLocID, this.localLocIDlegit);
}

// REQ does this class support privatization?
proc vdom.dsiSupportsPrivatization1d() param return true;

// REQ if privatization is supported - same purpose as dsiGetPrivatizeData()
proc vdom.dsiGetPrivatizeData1d() {
  return tuple(wholeR);
}

// REQ if privatization is supported - same purpose as dsiPrivatize()
// 'privDist' is the corresponding 1-d distribution descriptor,
// privatized (if it supports privatization).
proc vdom.dsiPrivatize1d(privDist, privatizeData) {
  assert(privDist.locale == here); // sanity check
  return new vdom(idxType   = this.idxType,
                  stridable = this.stridable,
                  wholeR    = privatizeData(1));
}

// REQ if privatization is supported - same purpose as dsiGetReprivatizeData()
proc vdom.dsiGetReprivatizeData1d() {
  return tuple(wholeR);
}

// REQ if privatization is supported - same purpose as dsiReprivatize()
proc vdom.dsiReprivatize1d(other, reprivatizeData) {
  if other.idxType   != this.idxType ||
     other.stridable != this.stridable then
    compilerError("inconsistent types in privatization");

  this.wholeR = reprivatizeData(1);
}

// REQ does this class need the localLocID?
// (See also vdist.dsiUsesLocalLocID1d.)
proc vdom.dsiUsesLocalLocID1d() param return true;

// REQ if dsiUsesLocalLocID1d: store the localLocID
// This is invoked when a DimensionalDom is created, including
// from DimensionalDom.dsiPrivatize(), but not DimensionalDom.dsiReprivatize()
proc vdom.dsiStoreLocalLocID1d((localLocID, legit): (locIdT, bool)) {
  // no big deal, but currently we intend to update this just once
  assert(this.localLocID == invalidLocID);
  this.localLocID = localLocID;
  this.localLocIDlegit = legit;
}

// REQ if dsiUsesLocalLocID1d: retrieve the localLocID
proc vdom.dsiGetLocalLocID1d(): (locIdT, bool) {
  assert(this.localLocID != invalidLocID);
  return (this.localLocID, this.localLocIDlegit);
}

// REQ does a local domain descriptor use a pointer
// to the privatized global domain descriptor?
// Consulted only when dsiSupportsPrivatization1d is true.
proc vdom.dsiLocalDescUsesPrivatizedGlobalDesc1d() param return false;

// REQ if dsiLocalDescUsesPrivatizedGlobalDesc1d: store the pointer to
// the privatized copy of the global descriptor that is on vlocdom's locale.
// Note: this is invoked only while privatizing the 1-d global descriptor.
// In particular, this is not invoked when the local descriptors are
// initially created.
// todo: we already pass globDD to all methods on vlocdom, so probably
// we do not need this
//
proc vlocdom.dsiStoreLocalDescToPrivatizedGlobalDesc1d(privGlobDesc) {
  compilerError("should not need dsiStoreLocalDescToPrivatizedGlobalDesc1d");
  // e.g.: this.privGlobDesc = privGlobDesc;
}

/////////// privatization - end

// REQ create a 1-d global domain descriptor for dsiNewRectangularDom()
// where our dimension is a range(idxType, bounded, stridable)
// stoIndexT is the same as in vdom.dsiNewLocalDom1d.
proc vdist.dsiNewRectangularDom1d(type idxType, param stridable: bool,
                                  type stoIndexT)
{
  // ignore stoIndexT - all we need is for other places to work out
  return new vdom(idxType, stridable);
}

// A nicety: produce a string showing the parameters.
// This might get renamed in the future.
proc vdist.toString()
  return "ReplicatedDim(" + numLocales:string + ")";

// REQ is this a replicated distribution?
proc vdom.dsiIsReplicated1d() param return true;

// REQ produce a local domain descriptor for our dimension
// on a locale for the given locale index locId, for dsiNewRectangularDom().
// Note: may be invoked multiple times/locales with the same locId.
// - one for each point in the other dimension(s).
// stoIndexT must be the index type of the range returned by
// dsiSetLocalIndices1d().
proc vdom.dsiNewLocalDom1d(type stoIndexT, locId: locIdT) {
  return new vlocdom(stoIndexT, wholeR.stridable);
}

// REQ given our dimension of the array index, on which locale is it located?
proc vdist.dsiIndexToLocale1d(indexx): locIdT {
  assert(localLocIDlegit);
  return localLocID;
}

// REQ update our data structures as needed upon dsiSetIndices()
proc vdom.dsiSetIndices1d(rangeArg: rangeT): void {
  wholeR = rangeArg;
}

// REQ update our local data structures as needed upon dsiSetIndices();
// return a range of "storage indices" to be stored locally.
//
// Each "storage index" corresponds to some "user index" (i.e.
// an index used to index into the array mapped with the Dimensional
// distribution) that is allocated on the receiver's locale.
//
// The returned range indicates *how many* of user indices are stored locally.
// It does NOT indicate *which* user indices those are. I.e. this range
// may contain exactly the user indices that are stored locally - or not
// (in which case dsiAccess1d() et al. will need to map each user index
// to an index in the range this returns).
//
// 'globDD' is the global domain descriptor (a 'vdom', in this case).
// The return type must be range(stoIndexT, stridable= true or false)
// where 'stoIndexT' is the arg of dsiNewLocalDom1d() that created 'this'.
//
proc vlocdom.dsiSetLocalIndices1d(globDD, locId: locIdT) {
  locWholeR = globDD.wholeR;
  return locWholeR;
}

// REQ-2 - like dsiBuildRectangularDom: create a new global domain descriptor
// for our dimension when the corresponding domain is created as a modification
// of an existing domain, preserving the domain map.
//
// 'rangeArg' is what dsiSetIndices1d would be invoked on.
// 'DD' is the distribution descriptor for 'this' (a 'vdist', in this case).
// 'idxType' and 'stoIndexT' of the result are the same as for 'this'.
//
// Just like in the multi-dimensional case, we show here how to write
// dsiBuildRectangularDom1d from existing methods.
//
proc vdom.dsiBuildRectangularDom1d(DD,
                                   param stridable:bool,
                                   rangeArg: range(idxType,
                                                   BoundedRangeType.bounded,
                                                   stridable))
{
  type dummy_stoIndexT = int; // use 'this.stoIndexT' when needed
  const result = DD.dsiNewRectangularDom1d(this.idxType, stridable,
                                           dummy_stoIndexT);
  result.dsiSetIndices1d(rangeArg);
  return result;
}

// REQ-2 - the local-descriptor companion to dsiBuildRectangularDom1d.
//
// Just like
//   dsiBuildRectangularDom1d = dsiNewRectangularDom1d + dsiSetIndices1d,
// we show here
//   dsiBuildLocalDom1d       = dsiNewLocalDom1d       + dsiSetLocalIndices1d.
//
// 'idxType' and 'stoIndexT' of the result are the same as for 'this'.
// 'newGlobDD' is the global domain descriptor (a 'vdom', in this case)
// whose local descriptor is to be created.
//
// Returns (new local 1-d domain descriptor, new storage range),
// which could be the results of dsiNewLocalDom1d() and
// dsiSetLocalIndices1d(), resp.
//
proc vlocdom.dsiBuildLocalDom1d(newGlobDD, locId: locIdT) {
  type  old_stoIndexT = this.stoIndexT;

  const newLocDD = newGlobDD.dsiNewLocalDom1d(old_stoIndexT, locId);
  const newStoRng = newLocDD.dsiSetLocalIndices1d(newGlobDD, locId);

  return (newLocDD, newStoRng);
}

// REQ indicate whether "storage indices" returned by dsiSetLocalIndices1d()
// correspond to "user indices". (In which case dsiAccess1d() returns
// (as a part of a tuple) its argument without modification.)
// As of this writing, this is used for assertions only.
proc vdom.dsiStorageUsesUserIndices() param return true;

// REQ given one dimension of the array index, return
// (locale id, storage index on that locale) for that index.
// The storage index must have the type DimensionalDom.stoIndexT.
proc vdom.dsiAccess1d(indexx: idxType): (locIdT, idxType) {
  // todo: provide access from non-"legit" locales;
  // maybe to locId=0?
  assert(localLocIDlegit);
  return (localLocID, indexx);
}

// REQ yield the densified range to be used in the leader iterator
// on this descriptor's locale, when there is only a single task.
iter vlocdom.dsiMyDensifiedRangeForSingleTask1d(globDD)
  : dsiMyDensifiedRangeType1d(globDD)
{
  yield 0:globDD.idxType..#locWholeR.length;
}

// REQ whether this distribution can handle only a single task per locale.
proc vdom.dsiSingleTaskPerLocaleOnly1d() param return false;

// REQ unless dsiSingleTaskPerLocaleOnly1d() is true.
// Yield the densified range to be used in the leader iterator
// on this descriptor's locale for task 'taskid' of 0..#numTasks.
// Note: should be densified w.r.t. the entire global range in this dimension.
proc vlocdom.dsiMyDensifiedRangeForTaskID1d(globDD, taskid:int, numTasks:int)
  : dsiMyDensifiedRangeType1d(globDD)
{
  type IT = globDD.idxType;
  const (startIx, endIx) = _computeChunkStartEnd(locWholeR.length,
                                                 numTasks:IT, taskid:IT+1);
  return (startIx:IT - 1) .. (endIx:IT - 1);
}

// REQ the range type returned/yielded by dsiMyDensifiedRangeForSingleTask1d()
// and (if applicable) by dsiMyDensifiedRangeForTaskID1d().
proc vlocdom.dsiMyDensifiedRangeType1d(globDD) type
  return range(globDD.idxType);

// REQ for iterating over array elements.
// Yield tuples (locId, iterable) s.t. the following results in
// traversing all array elements in order:
//   for (l,itr) in dsiSerialArrayIterator1d() do
//    for ix in itr do
//      yield (local storage on locale l)[ix]
iter vdom.dsiSerialArrayIterator1d() {
  assert(localLocIDlegit);
  yield (localLocID, wholeR);
}

// REQ for array follower iterator.
//
// Yield pairs (locId, storageOffset) that traverse storage for
// the array elements corresponding to the indices given by undensRange,
// preserving the order in which those indices occur.
//
// This is similar to dsiSerialArrayIterator1d(), except
// * only the indicated elements must be traversed, in the indicated order;
// * one storage offset is yielded each time.
//
// The latter is chosen to simplify dsiFollowerArrayIterator1d implementations.
// It also eliminates one loop nest per dimension in DimensionalArr follower.
//
iter vdom.dsiFollowerArrayIterator1d(undensRange): (locIdT, idxType) {
  assert(localLocIDlegit);
  for i in undensRange do
    yield (localLocID, i);
}

/////////////////////////////////////////////////////////////////////////////
// 1-d distribution - block

class sdist {
  // the type of bbStart, bbLength
  // (also (ab)used as the idxType of the domains created over this dist.)
  // (todo - straighten that out)
  type idxType;

  const numLocales: int;

  // the .low and .length of BlockDist's 'boundingBox' for our dimension
  const bbStart: idxType;
  const bbLength: idxType;

  proc boundingBox return bbStart .. (bbStart + bbLength - 1);
}

class sdom {
  type idxType;
  param stridable: bool;

  // convenience
  proc rangeT type  return range(idxType, BoundedRangeType.bounded, stridable);

  // our range
  var wholeR: rangeT;

  // privatized distribution descriptor
  const pdist;
}

class slocdom {
  var myRange;
}

/////////// privatization - start

proc sdist.dsiSupportsPrivatization1d() param return true;

proc sdist.dsiGetPrivatizeData1d() {
  return (numLocales, bbStart, bbLength);
}

proc sdist.dsiPrivatize1d(privatizeData) {
  return new sdist(privatizeData, this.idxType);
}

// constructor for privatization
proc sdist.sdist(privatizeData, type idxType) {
  numLocales = privatizeData(1);
  bbStart = privatizeData(2);
  bbLength = privatizeData(3);
}

proc sdist.dsiUsesLocalLocID1d() param return false;

proc sdom.dsiSupportsPrivatization1d() param return true;

proc sdom.dsiGetPrivatizeData1d() {
  return tuple(wholeR);
}

proc sdom.dsiPrivatize1d(privDist, privatizeData) {
  assert(privDist.locale == here); // sanity check
  return new sdom(idxType   = this.idxType,
                  stridable = this.stridable,
                  wholeR    = privatizeData(1),
                  pdist     = privDist);
}

proc sdom.dsiGetReprivatizeData1d() {
  return tuple(wholeR);
}

proc sdom.dsiReprivatize1d(other, reprivatizeData) {
  if other.idxType   != this.idxType ||
     other.stridable != this.stridable then
    compilerError("inconsistent types in privatization");

  this.wholeR = reprivatizeData(1);
}

proc sdom.dsiUsesLocalLocID1d() param return false;

proc sdom.dsiLocalDescUsesPrivatizedGlobalDesc1d() param return false;

/////////// privatization - end

// Constructor. idxType is inferred from the 'bbLow' argument
// (alternative: default to 'int' instead).
proc sdist.sdist(numLocales, boundingBoxLow, boundingBoxHigh, type idxType = boundingBoxLow.type) {
  if !(boundingBoxLow <= boundingBoxHigh) then halt("'sdist' distribution must have a non-empty bounding box between boundingBoxLow and boundingBoxHigh; got ", boundingBoxLow, " .. ", boundingBoxHigh);
  assert(numLocales > 0); // so we can cast it to any int type
  this.numLocales = numLocales;
  this.bbStart = boundingBoxLow;
  this.bbLength = (boundingBoxHigh - boundingBoxLow + 1):idxType;
  assert(this.bbLength > 0);
}

proc sdist.toString()
  return "BlockDim(" + numLocales:string + ", " + boundingBox:string + ")";

proc sdist.dsiNewRectangularDom1d(type idxType, param stridable: bool,
                                  type stoIndexT)
{
  // ignore stoIndexT - all we need is for other places to work out
  if idxType != this.idxType then
    compilerError("The index type ", typeToString(idxType),
                  " does not match the index type ",typeToString(this.idxType),
                  " of the 'sdist' 1-d distribution");
  return new sdom(idxType = idxType, stridable = stridable, pdist = this);
}

proc sdom.dsiIsReplicated1d() param return false;

proc sdom.dsiNewLocalDom1d(type stoIndexT, locId: locIdT) {
  var defaultVal: range(stoIndexT, stridable=this.stridable);
  return new slocdom(myRange = defaultVal);
}

proc sdist.dsiIndexToLocale1d(indexx): locIdT {
  if indexx <= bbStart then
    return 0;

  // (numLocales-1) is the answer when
  //   indexx >= bbStart + (numLocales-1) * (bbLength+1) / numLocales
  // whose r.h.s. could be stored in 'sdist', trading memory for computation.
  // We simplify the condition to 'indexx >= bbStart + bbLength'.
  // (Note: (bbStart + bbLength - 1) will not always map to (numLocales - 1)).
  // If the condition doesn't hold, the formula gives the answer.
  // (Compared to the formula in Block.targetLocsIdx, we run comparisons
  // *before* multiplying and dividing.)
  //
  if indexx >= bbStart + bbLength then
    return numLocales - 1;

  // need to think what to do if numLocales is not int
  type adjT =
    if numLocales.type == int && idxType == uint(64)
    then uint(64)
    else numLocales.type;

  const result = (indexx - bbStart) * numLocales:adjT / bbLength;
  assert(0 <= result && result < numLocales:adjT);
  return result:locIdT;
}

proc sdom.dsiSetIndices1d(rangeArg: rangeT): void {
  wholeR = rangeArg;
}

// Helper: compute the chunk to be stored on locID
proc sdom._dsiComputeMyRange(locId): rangeT {
  const dom = this;
  const dist = dom.pdist;
  // see LocBlock.LocBlock()
  const (blo, bhi) = _computeBlock(dist.bbLength, dist.numLocales,
           locId, max(dom.idxType), min(dom.idxType), dist.bbStart);
  const myChunk = blo..bhi;
  // see Block.getChunk()
  const chunk = dom.wholeR(myChunk);
  return chunk;
}

proc slocdom.dsiSetLocalIndices1d(globDD, locId: locIdT) {
  myRange = globDD._dsiComputeMyRange(locId);
  return myRange;
}

/////////////////////////////////

proc sdom.dsiBuildRectangularDom1d(DD,
                                   param stridable:bool,
                                   rangeArg: range(idxType,
                                                   BoundedRangeType.bounded,
                                                   stridable))
{
  // There does not seem to be any optimizations from merging the two calls.
  type dummy_stoIndexT = int;
  const result = DD.dsiNewRectangularDom1d(this.idxType, stridable,
                                           dummy_stoIndexT);
  result.dsiSetIndices1d(rangeArg);
  return result;
}

proc slocdom.dsiBuildLocalDom1d(newGlobDD, locId: locIdT) {
  type  old_stoIndexT = this.myRange.idxType; // essentially 'this.stoIndexT'

  const newLocDD = newGlobDD.dsiNewLocalDom1d(old_stoIndexT, locId);
  const newStoRng = newLocDD.dsiSetLocalIndices1d(newGlobDD, locId);

  return (newLocDD, newStoRng);
}

/////////////////////////////////

proc sdom.dsiStorageUsesUserIndices() param return true;

proc sdom.dsiAccess1d(indexx: idxType): (locIdT, idxType) {
  return (pdist.dsiIndexToLocale1d(indexx), indexx);
}

iter slocdom.dsiMyDensifiedRangeForSingleTask1d(globDD) {
  const locRange = densify(myRange, globDD.wholeR, userErrors=false);
  yield locRange: range(globDD.idxType);
}

proc sdom.dsiSingleTaskPerLocaleOnly1d() param return false;

proc slocdom.dsiMyDensifiedRangeForTaskID1d(globDD, taskid:int, numTasks:int) {
  const locRange = densify(myRange, globDD.wholeR, userErrors=false);
  // Copied straight from BlockDom leader - replace locBlock(parDim)->locRange.
  const (lo, hi) = _computeBlock(locRange.length, numTasks, taskid,
                                 locRange.high, locRange.low, locRange.low);

  // If this can occasionally be an empty range, add a check to Dimensional
  // to not yield anything in such a case.
  return lo..hi;
}

proc slocdom.dsiMyDensifiedRangeType1d(globDD) type
  return range(globDD.idxType);

iter sdom.dsiSerialArrayIterator1d() {
  // The Block distribution assigns indices to locales contiguously and
  // so that (i1<i2) => (locId1<=locId2). This is defined by the domain map.
  // As we traverse the range (i.e. this.wholeR), locIds will increase
  // if the stride is positive, and decrease otherwise.
  // On a single locale, slocdom.myRange (aka this._dsiComputeMyRange())
  // already happens to reflect the desired direction.

  for locId in (0..#pdist.numLocales) by sgn(wholeR.stride) {
    // We do not go to slocdom for myRange because (a) recomputing it
    // is probably cheaper than going to a remote locale, and
    // (b) we do not store slocdom objects in sdom.
    const localRange = _dsiComputeMyRange(locId);
//writeln("sdom.dsiSerialArrayIterator1d  ", locId, ", ", localRange);
    yield (locId, localRange);
  }
}

iter sdom.dsiFollowerArrayIterator1d(undensRange): (locIdT, idxType) {
//writeln("sdom.dsiFollowerArrayIterator1d  undensRange ", undensRange);

  const heuristic_weight_rangepoint = 2; // measure of work in (a) below
  const heuristic_weight_locale = 3;     // measure of work in (b)

  if heuristic_weight_locale * undensRange.stride >
      heuristic_weight_rangepoint * pdist.bbLength / pdist.numLocales
  {
//writeln("sdom.dsiFollowerArrayIterator1d case A");
    // (a) no benefit to factor out computations for a particular locale
    for ix in undensRange do
      yield dsiAccess1d(ix);

  } else {
    // (b) per-locale computation is amortized over enough undensRange indices
    const lowLocId = pdist.dsiIndexToLocale1d(undensRange.alignedLow);
    const highLocId = pdist.dsiIndexToLocale1d(undensRange.alignedHigh);

    // check for a supposedly more common case of following our own leader
    if lowLocId == highLocId {
//writeln("sdom.dsiFollowerArrayIterator1d case B1  lowLocId ", lowLocId);
      for i in undensRange do
        yield (lowLocId, i);

    } else {
      assert(lowLocId < highLocId);
      for locId in lowLocId .. highLocId by sgn(undensRange.stride) {
        // We do not go to slocdom for myRange - same rationale as above.
        const thisLocaleRange = _dsiComputeMyRange(locId);
        const localRangeToFollow = undensRange(thisLocaleRange);
//writeln("sdom.dsiFollowerArrayIterator1d case B2  locId ", locId, "  localRangeToFollow ", localRangeToFollow);
        for i in localRangeToFollow do
          yield (locId, i);
      }
    }
  }
}


Removing race condition from test via the introduction of a sync var

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

class mysumreduce {
  type t;
  var state: sync t;

  def init() {
    state = 0;
  }

  def combine(x: t) {
    state += x;
  }

  def finalize(): t {
    return state;
  }
}

config var n: int = 10;

var D: domain(1) = [1..n];

var A: [D] int;

forall i in D {
  A(i) = i;
}

var myreduce = new mysumreduce(t = int);
myreduce.init();
myreduce.combine(A);
var result = myreduce.finalize();

writeln("result is: ", result);

class mysumreduce {
  type t;
  var state: t;

  def init() {
    state = 0;
  }

  def combine(x: t) {
    state += x;
  }

  def finalize(): t {
    return state;
  }
}

config var n: int = 10;

var D: domain(1) = [1..n];

var A: [D] int;

forall i in D {
  A(i) = i;
}

var myreduce = new mysumreduce(t = int);
myreduce.init();
myreduce.combine(A);
var result = myreduce.finalize();

writeln("result is: ", result);