In the default arithmetic array and domain leader iterators, changed the
calculation of the number of elements so it multiplies together the number of
elements in each dimension rather than adding them.

It would be nice if we could use the domain numIndices method, but it doesn't
use uint(64)s unless that is the index type, so it is easier to overflow it.

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

config param debugDefaultDist = false;

class DefaultDist: BaseDist {
  def newArithmeticDom(param rank: int, type idxType, param stridable: bool)
    return new DefaultArithmeticDom(rank, idxType, stridable, this);

  def newAssociativeDom(type idxType)
    return new DefaultAssociativeDom(idxType, this);

  def newEnumDom(type idxType)
    return new DefaultEnumDom(idxType, this);

  def newOpaqueDom(type idxType)
    return new DefaultOpaqueDom(this);

  def newSparseDom(param rank: int, type idxType, dom: domain)
    return new DefaultSparseDom(rank, idxType, this, dom);

  def clone() return this;
}

//
// Note that the replicated copies are set up in ChapelLocale on the
// other locales.  This just sets it up on this locale.
//
pragma "private" var defaultDist = distributionValue(new DefaultDist());

class DefaultArithmeticDom: BaseArithmeticDom {
  param rank : int;
  type idxType;
  param stridable: bool;
  var dist: DefaultDist;
  var ranges : rank*range(idxType,BoundedRangeType.bounded,stridable);

  def linksDistribution() param return false;

  def DefaultArithmeticDom(param rank, type idxType, param stridable, dist) {
    this.dist = dist;
  }

  def clear() {
    var emptyRange: range(idxType, BoundedRangeType.bounded, stridable);
    for param i in 1..rank do
      ranges(i) = emptyRange;
  }

  def getIndices() return ranges;

  def setIndices(x) {
    if ranges.size != x.size then
      compilerError("rank mismatch in domain assignment");
    if ranges(1).eltType != x(1).eltType then
      compilerError("index type mismatch in domain assignment");
    ranges = x;
  }

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

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

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

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In domain leader code: this = ", this);
    var numCores = here.numCores;
    var runningTasks = here.runningTasks();
    if debugDefaultDist then
      writeln("    numCores=", numCores, ", runningTasks=", runningTasks);

    var numChunks: uint(64) =
    if (runningTasks >= numCores) then 1
    else if maxChunks == -1 then
      if maxThreads == 0 then (numCores-runningTasks+1):uint(64)
      else (min(numCores-runningTasks+1, maxThreads)):uint(64)
    else if maxThreads == 0 then (min(numCores-runningTasks+1, maxChunks)):uint(64)
      else (min(numCores-runningTasks+1, min(maxThreads, maxChunks))):uint(64);

    var numelems: uint(64) = 1;
    for param i in 1..rank do
      numelems *= ranges(i).length:uint(64);

    if debugDefaultDist then
      writeln("*** DI: rank=", rank, " numelems=", numelems, " numChunks=", numChunks, " ranges(1).length=", ranges(1).length);

    if ((numelems <= minElemsPerChunk*numChunks) ||
        ((ranges(1).length):uint(64) < numChunks) ||
        (numChunks == 1)) {
      if debugDefaultDist then
	writeln("*** minElemsPerChunk*numChunks = ",
		minElemsPerChunk*numChunks, ", using 1 chunk");
      if rank == 1 {
	yield tuple(0..ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:ranges(i).low.type..#(ranges(i).length);
      if debugDefaultDist then
	writeln("*** DI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeMyChunk(locBlock(1).length, locBlock(1).high,
                                        numChunks, chunk);
	tuple(1) = lo..hi;
	if debugDefaultDist then
	  writeln("*** DI: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    if debugDefaultDist then
      writeln("In domain follower code: Following ", follower);
    var block: ranges.type;
    if stridable {
      for param i in 1..rank do
        block(i) =
          if ranges(i).stride > 0 then
            ranges(i).low+follower(i).low*ranges(i).stride:ranges(i).eltType..ranges(i).low+follower(i).high*ranges(i).stride:ranges(i).eltType by ranges(i).stride
          else
            ranges(i).high+follower(i).high*ranges(i).stride:ranges(i).eltType..ranges(i).high+follower(i).low*ranges(i).stride:ranges(i).eltType by ranges(i).stride;
    } else {
      for  param i in 1..rank do
        block(i) = follower(i) + ranges(i).low;
    }
    if rank == 1 {
      for i in block do
        yield i;
    } else {
      for i in these_help(1, block) do
        yield i;
    }
  }

  def member(ind: idxType) where rank == 1 {
    if !ranges(1).member(ind) then
      return false;
    return true;
  }

  def member(ind: rank*idxType) {
    for param i in 1..rank do
      if !ranges(i).member(ind(i)) then
        return false;
    return true;
  }

  def order(ind: idxType) where rank == 1 {
    return ranges(1).order(ind);
  }

  def order(ind: rank*idxType) {
    var totOrder: idxType;
    var blk: idxType = 1;
    for param d in 1..rank by -1 {
      const orderD = ranges(d).order(ind(d));
      if (orderD == -1) then return orderD;
      totOrder += orderD * blk;
      blk *= ranges(d).length;
    }
    return totOrder;
  }

  def position(ind: idxType) where rank == 1 {
    var pos: 1*idxType;
    pos(1) = order(ind);
    return pos;
  }

  def position(ind: rank*idxType) {
    var pos: rank*idxType;
    for d in 1..rank {
      pos(d) = ranges(d).order(ind(d));
    }
    return pos;
  }

  def dims()
    return ranges;

  def dim(d : int)
    return ranges(d);

  def dim(param d : int)
    return ranges(d);

  def numIndices {
    var sum = 1:idxType;
    for param i in 1..rank do
      sum *= ranges(i).length;
    return sum;
    // WANT: return * reduce (this(1..rank).length);
  }

  def low {
    if rank == 1 {
      return ranges(1)._low;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._low;
      return result;
    }
  }

  def high {
    if rank == 1 {
      return ranges(1)._high;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._high;
      return result;
    }
  }

  def buildArray(type eltType) {
    return new DefaultArithmeticArr(eltType=eltType, rank=rank, idxType=idxType,
                                  /*stridable=stridable, reindexed=false, */
                                    stridable=stridable, reindexed=true,
                                    dom=this);
  }

  def slice(param stridable: bool, ranges) {
    var d = new DefaultArithmeticDom(rank, idxType,
                                     stridable || this.stridable, dist);
    for param i in 1..rank do
      d.ranges(i) = dim(i)(ranges(i));
    return d;
  }

  def rankChange(param rank: int, param stridable: bool, args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    var d = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    var i = 1;
    for param j in 1..args.size {
      if isRange(args(j)) {
        d.ranges(i) = dim(j)(args(j));
        i += 1;
      }
    }
    return d;
  }

  def translate(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = dim(i).translate(off(i));
    return x;
  }

  def chpl__unTranslate(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = dim(i).chpl__unTranslate(off(i));
    return x;
  }

  def interior(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do {
      if ((off(i) > 0) && (dim(i)._high+1-off(i) < dim(i)._low) ||
          (off(i) < 0) && (dim(i)._low-1-off(i) > dim(i)._high)) {
        halt("***Error: Argument to 'interior' function out of range in dimension ", i, "***");
      }
      x.ranges(i) = dim(i).interior(off(i));
    }
    return x;
  }

  def exterior(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = dim(i).exterior(off(i));
    return x;
  }

  def expand(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do {
      x.ranges(i) = ranges(i).expand(off(i));
      if (x.ranges(i)._low > x.ranges(i)._high) {
        halt("***Error: Degenerate dimension created in dimension ", i, "***");
      }
    }
    return x;
  }

  def expand(off: idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i).expand(off);
    return x;
  }

  def strideBy(str : rank*int) {
    var x = new DefaultArithmeticDom(rank, idxType, true, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i) by str(i);
    return x;
  }

  def strideBy(str : int) {
    var x = new DefaultArithmeticDom(rank, idxType, true, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i) by str;
    return x;
  }
}

class DefaultArithmeticArr: BaseArr {
  type eltType;
  param rank : int;
  type idxType;
  param stridable: bool;
  //param reindexed: bool = false; // may have blk(rank) != 1
  param reindexed: bool = true; // may have blk(rank) != 1

  var dom : DefaultArithmeticDom(rank=rank, idxType=idxType,
                                         stridable=stridable);
  var off: rank*idxType;
  var blk: rank*idxType;
  var str: rank*int;
  var origin: idxType;
  var factoredOffs: idxType;
  var size : idxType;
  var data : _ddata(eltType);
  var noinit: bool = false;

  def canCopyFromDevice param return true;

  def getBaseDom() return dom;

  def destroyData() {
    if dom.numIndices > 0 {
      pragma "no copy" pragma "no auto destroy" var dr = data;
      pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
      pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, 0);
      pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
      if (chpl__maybeAutoDestroyed(ev)) {
        for i in 0..dom.numIndices-1 {
          pragma "no copy" pragma "no auto destroy" var dr = data;
          pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
          pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, i);
          pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
          chpl__autoDestroy(ev);
        }
      }
    }
    delete data;
  }

  def makeAlias(B: DefaultArithmeticArr) {
    var A = B.reindex(dom);
    off = A.off;
    blk = A.blk;
    str = A.str;
    origin = A.origin;
    factoredOffs = A.factoredOffs;
    data = A.data;
    delete A;
  }

  def these() var {
    if rank == 1 {
      var first = getDataIndex(dom.low);
      var second = getDataIndex(dom.low+dom.ranges(1).stride:idxType);
      var step = (second-first):int;
      var last = first + (dom.numIndices-1) * step:idxType;
      for i in first..last by step do
        yield data(i);
    } else {
      for i in dom do
        yield this(i);
    }
  }

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In array leader code: [\n", this, "]");
    var numCores = here.numCores;
    var runningTasks = here.runningTasks();
    if debugDefaultDist then
      writeln("    numCores=", numCores, ", runningTasks=", runningTasks());
    var numChunks: uint(64) =
    if (runningTasks >= numCores) then 1
    else if maxChunks == -1 then
      if maxThreads == 0 then (numCores-runningTasks+1):uint(64)
      else (min(numCores-runningTasks+1, maxThreads)):uint(64)
    else if maxThreads == 0 then (min(numCores-runningTasks+1, maxChunks)):uint(64)
      else (min(numCores-runningTasks+1, min(maxThreads, maxChunks))):uint(64);

    var numelems: uint(64) = 1;
    for param i in 1..rank do
      numelems *= dom.ranges(i).length:uint(64);

    if debugDefaultDist then
      writeln("*** AI: rank=", rank, " numelems=", numelems, " numChunks=", numChunks, " dom.ranges(1).length=", dom.ranges(1).length);

    if ((numelems <= minElemsPerChunk*numChunks) ||
        ((dom.ranges(1).length):uint(64) < numChunks) ||
        (numChunks == 1)) {
      if debugDefaultDist then
	writeln("*** minElemsPerChunk*numChunks = ",
		minElemsPerChunk*numChunks, ", using 1 chunk");
      if rank == 1 {
	yield tuple(0..dom.ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..dom.ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:dom.ranges(i).low.type..#(dom.ranges(i).length);
      if debugDefaultDist then
	writeln("*** AI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeMyChunk(locBlock(1).length, locBlock(1).high,
                                        numChunks, chunk);
	tuple(1) = lo..hi;
	if debugDefaultDist then
	  writeln("*** AI: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) var where tag == iterator.follower {
    if debugDefaultDist then
      writeln("*** In array follower code: [\n", this, "]");
    for i in dom.these(tag=iterator.follower, follower) do
      yield this(i);
  }

  def computeFactoredOffs() {
    factoredOffs = 0:idxType;
    for i in 1..rank do {
      factoredOffs = factoredOffs + blk(i) * off(i);
    }
  }

  def initialize() {
    if noinit == true then return;
    for param dim in 1..rank {
      off(dim) = dom.dim(dim)._low;
      str(dim) = dom.dim(dim)._stride;
    }
    blk(rank) = 1:idxType;
    for param dim in 1..rank-1 by -1 do
      blk(dim) = blk(dim+1) * dom.dim(dim+1).length;
    computeFactoredOffs();
    size = blk(1) * dom.dim(1).length;
    data = new _ddata(eltType);
    data.init(size);
  }

  pragma "inline"
  def getDataIndex(ind: idxType ...1) where rank == 1
    return getDataIndex(ind);

  pragma "inline"
  def getDataIndex(ind: rank* idxType) {
    var sum = origin;
    if stridable {
      for param i in 1..rank do
        sum += (ind(i) - off(i)) * blk(i) / str(i):idxType;
    } else {
      if reindexed {
        for param i in 1..rank do
          sum += ind(i) * blk(i);
      } else {
        for param i in 1..rank-1 do
          sum += ind(i) * blk(i);
        sum += ind(rank);
      }
      sum -= factoredOffs;
    }
    return sum;
  }

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

  pragma "inline"
  def this(ind : rank*idxType) var {
    if boundsChecking then
      if !dom.member(ind) then
        halt("array index out of bounds: ", ind);
    return data(getDataIndex(ind));
  }

  def reindex(d: DefaultArithmeticDom) {
    if rank != d.rank then
      compilerError("illegal implicit rank change");
    for param i in 1..rank do
      if d.dim(i).length != dom.dim(i).length then
        halt("extent in dimension ", i, " does not match actual");
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=d.rank,
                                         idxType=d.idxType,
                                         stridable=d.stridable,
                                         reindexed=true, dom=d, noinit=true);
    alias.data = data;
    alias.size = size: d.idxType;
    for param i in 1..rank {
      alias.off(i) = d.dim(i)._low;
      alias.blk(i) = (blk(i) * dom.dim(i)._stride / str(i)) : d.idxType;
      alias.str(i) = d.dim(i)._stride;
    }
    alias.origin = origin:d.idxType;
    alias.computeFactoredOffs();
    return alias;
  }

  def checkSlice(ranges) {
    for param i in 1..rank do
      if !dom.dim(i).boundsCheck(ranges(i)) then
        halt("array slice out of bounds in dimension ", i, ": ", ranges(i));
  }

  def slice(d: DefaultArithmeticDom) {
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                         idxType=idxType,
                                         stridable=d.stridable,
                                         reindexed=reindexed,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.size = size;
    alias.blk = blk;
    alias.str = str;
    alias.origin = origin;
    for param i in 1..rank {
      alias.off(i) = d.dim(i)._low;
      alias.origin += blk(i) * (d.dim(i)._low - off(i)) / str(i);
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def checkRankChange(args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    for param i in 1..args.size do
      if isRange(args(i)) then
        if !dom.dim(i).boundsCheck(args(i)) then
          halt("array slice out of bounds in dimension ", i, ": ", args(i));
  }

  def rankChange(d, param newRank: int, param newStridable: bool, args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    var alias = new DefaultArithmeticArr(eltType=eltType, rank=newRank,
                                         idxType=idxType,
                                         stridable=newStridable, reindexed=true,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.size = size;
    var i = 1;
    alias.origin = origin;
    for param j in 1..args.size {
      if isRange(args(j)) {
        alias.off(i) = d.dim(i)._low;
        alias.origin += blk(j) * (d.dim(i)._low - off(j)) / str(j);
        alias.blk(i) = blk(j);
        alias.str(i) = str(j);
        i += 1;
      } else {
        alias.origin += blk(j) * (args(j) - off(j)) / str(j);
      }
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def reallocate(d: domain) {
    if (d._value.type == dom.type) {
      var copy = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                          idxType=idxType,
                                          stridable=d._value.stridable,
                                          reindexed=reindexed, dom=d._value);
      for i in d((...dom.ranges)) do
        copy(i) = this(i);
      off = copy.off;
      blk = copy.blk;
      str = copy.str;
      origin = copy.origin;
      factoredOffs = copy.factoredOffs;
      size = copy.size;
      destroyData();
      data = copy.data;
      delete copy;
    } else {
      halt("illegal reallocation");
    }
  }

  def tupleInit(b: _tuple) {
    def _tupleInitHelp(j, param rank: int, b: _tuple) {
      if rank == 1 {
        for param i in 1..b.size {
          j(this.rank-rank+1) = dom.dim(this.rank-rank+1).low + i - 1;
          this(j) = b(i);
        }
      } else {
        for param i in 1..b.size {
          j(this.rank-rank+1) = dom.dim(this.rank-rank+1).low + i - 1;
          _tupleInitHelp(j, rank-1, b(i));
        }
      }
    }

    if rank == 1 {
      for param i in 1..b.size do
        this(this.dom.dim(1).low + i - 1) = b(i);
    } else {
      var j: rank*int;
      _tupleInitHelp(j, rank, b);
    }
  }
}

def DefaultArithmeticDom.writeThis(f: Writer) {
  f.write("[", dim(1));
  for i in 2..rank do
    f.write(", ", dim(i));
  f.write("]");
}

def DefaultArithmeticArr.writeThis(f: Writer) {
  if dom.numIndices == 0 then return;
  var i : rank*idxType;
  for dim in 1..rank do
    i(dim) = dom.dim(dim)._low;
  label next while true {
    f.write(this(i));
    if i(rank) <= (dom.dim(rank)._high - dom.dim(rank)._stride:idxType) {
      f.write(" ");
      i(rank) += dom.dim(rank)._stride:idxType;
    } else {
      for dim in 1..rank-1 by -1 {
        if i(dim) <= (dom.dim(dim)._high - dom.dim(dim)._stride:idxType) {
          i(dim) += dom.dim(dim)._stride:idxType;
          for dim2 in dim+1..rank {
            f.writeln();
            i(dim2) = dom.dim(dim2)._low;
          }
          continue next;
        }
      }
      break;
    }
  }
}

//
// helper function for blocking index ranges
// - based on _computeBlock() but specialized for cases
//   where the low bound is always zero
//
def _computeMyChunk(numelems, wayhi, numblocks, blocknum) {
  if debugDefaultDist then
    writeln("in _computeMyChunk: numelems=",  numelems, " wayhi=", wayhi,
	    " numblocks=", numblocks, " blocknum=", blocknum);
  /*
  if numblocks == 1 then
    return (0:wayhi.type, numelems:wayhi.type-1);
  */

  if numelems == 0 then
    return (1:wayhi.type, 0:wayhi.type);

  def procToData(x)
    return x:wayhi.type + (x:real != x:int:real):wayhi.type;

  const blo =
    if blocknum == 0 then 0
      else procToData((numelems:real * blocknum) / numblocks);
  const bhi =
    if blocknum == numblocks - 1 then wayhi
      else procToData((numelems:real * (blocknum+1)) / numblocks) - 1;

  return (blo, bhi);
}

config param debugDefaultDist = false;

class DefaultDist: BaseDist {
  def newArithmeticDom(param rank: int, type idxType, param stridable: bool)
    return new DefaultArithmeticDom(rank, idxType, stridable, this);

  def newAssociativeDom(type idxType)
    return new DefaultAssociativeDom(idxType, this);

  def newEnumDom(type idxType)
    return new DefaultEnumDom(idxType, this);

  def newOpaqueDom(type idxType)
    return new DefaultOpaqueDom(this);

  def newSparseDom(param rank: int, type idxType, dom: domain)
    return new DefaultSparseDom(rank, idxType, this, dom);

  def clone() return this;
}

//
// Note that the replicated copies are set up in ChapelLocale on the
// other locales.  This just sets it up on this locale.
//
pragma "private" var defaultDist = distributionValue(new DefaultDist());

class DefaultArithmeticDom: BaseArithmeticDom {
  param rank : int;
  type idxType;
  param stridable: bool;
  var dist: DefaultDist;
  var ranges : rank*range(idxType,BoundedRangeType.bounded,stridable);

  def linksDistribution() param return false;

  def DefaultArithmeticDom(param rank, type idxType, param stridable, dist) {
    this.dist = dist;
  }

  def clear() {
    var emptyRange: range(idxType, BoundedRangeType.bounded, stridable);
    for param i in 1..rank do
      ranges(i) = emptyRange;
  }

  def getIndices() return ranges;

  def setIndices(x) {
    if ranges.size != x.size then
      compilerError("rank mismatch in domain assignment");
    if ranges(1).eltType != x(1).eltType then
      compilerError("index type mismatch in domain assignment");
    ranges = x;
  }

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

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

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

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In domain leader code: this = ", this);
    var numCores = here.numCores;
    var runningTasks = here.runningTasks();
    if debugDefaultDist then
      writeln("    numCores=", numCores, ", runningTasks=", runningTasks);

    var numChunks: uint(64) =
    if (runningTasks >= numCores) then 1
    else if maxChunks == -1 then
      if maxThreads == 0 then (numCores-runningTasks+1):uint(64)
      else (min(numCores-runningTasks+1, maxThreads)):uint(64)
    else if maxThreads == 0 then (min(numCores-runningTasks+1, maxChunks)):uint(64)
      else (min(numCores-runningTasks+1, min(maxThreads, maxChunks))):uint(64);

    var numelems: uint(64) = 0;
    for param i in 1..rank do
      numelems += (ranges(i).length):uint(64);
    if debugDefaultDist then
      writeln("*** DI: rank=", rank, " numelems=", numelems, " numChunks=", numChunks, " ranges(1).length=", ranges(1).length);

    if ((numelems <= minElemsPerChunk*numChunks) ||
        ((ranges(1).length):uint(64) < numChunks) ||
        (numChunks == 1)) {
      if debugDefaultDist then
	writeln("*** minElemsPerChunk*numChunks = ",
		minElemsPerChunk*numChunks, ", using 1 chunk");
      if rank == 1 {
	yield tuple(0..ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:ranges(i).low.type..#(ranges(i).length);
      if debugDefaultDist then
	writeln("*** DI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeMyChunk(locBlock(1).length, locBlock(1).high,
                                        numChunks, chunk);
	tuple(1) = lo..hi;
	if debugDefaultDist then
	  writeln("*** DI: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    if debugDefaultDist then
      writeln("In domain follower code: Following ", follower);
    var block: ranges.type;
    if stridable {
      for param i in 1..rank do
        block(i) =
          if ranges(i).stride > 0 then
            ranges(i).low+follower(i).low*ranges(i).stride:ranges(i).eltType..ranges(i).low+follower(i).high*ranges(i).stride:ranges(i).eltType by ranges(i).stride
          else
            ranges(i).high+follower(i).high*ranges(i).stride:ranges(i).eltType..ranges(i).high+follower(i).low*ranges(i).stride:ranges(i).eltType by ranges(i).stride;
    } else {
      for  param i in 1..rank do
        block(i) = follower(i) + ranges(i).low;
    }
    if rank == 1 {
      for i in block do
        yield i;
    } else {
      for i in these_help(1, block) do
        yield i;
    }
  }

  def member(ind: idxType) where rank == 1 {
    if !ranges(1).member(ind) then
      return false;
    return true;
  }

  def member(ind: rank*idxType) {
    for param i in 1..rank do
      if !ranges(i).member(ind(i)) then
        return false;
    return true;
  }

  def order(ind: idxType) where rank == 1 {
    return ranges(1).order(ind);
  }

  def order(ind: rank*idxType) {
    var totOrder: idxType;
    var blk: idxType = 1;
    for param d in 1..rank by -1 {
      const orderD = ranges(d).order(ind(d));
      if (orderD == -1) then return orderD;
      totOrder += orderD * blk;
      blk *= ranges(d).length;
    }
    return totOrder;
  }

  def position(ind: idxType) where rank == 1 {
    var pos: 1*idxType;
    pos(1) = order(ind);
    return pos;
  }

  def position(ind: rank*idxType) {
    var pos: rank*idxType;
    for d in 1..rank {
      pos(d) = ranges(d).order(ind(d));
    }
    return pos;
  }

  def dims()
    return ranges;

  def dim(d : int)
    return ranges(d);

  def dim(param d : int)
    return ranges(d);

  def numIndices {
    var sum = 1:idxType;
    for param i in 1..rank do
      sum *= ranges(i).length;
    return sum;
    // WANT: return * reduce (this(1..rank).length);
  }

  def low {
    if rank == 1 {
      return ranges(1)._low;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._low;
      return result;
    }
  }

  def high {
    if rank == 1 {
      return ranges(1)._high;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._high;
      return result;
    }
  }

  def buildArray(type eltType) {
    return new DefaultArithmeticArr(eltType=eltType, rank=rank, idxType=idxType,
                                  /*stridable=stridable, reindexed=false, */
                                    stridable=stridable, reindexed=true,
                                    dom=this);
  }

  def slice(param stridable: bool, ranges) {
    var d = new DefaultArithmeticDom(rank, idxType,
                                     stridable || this.stridable, dist);
    for param i in 1..rank do
      d.ranges(i) = dim(i)(ranges(i));
    return d;
  }

  def rankChange(param rank: int, param stridable: bool, args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    var d = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    var i = 1;
    for param j in 1..args.size {
      if isRange(args(j)) {
        d.ranges(i) = dim(j)(args(j));
        i += 1;
      }
    }
    return d;
  }

  def translate(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = dim(i).translate(off(i));
    return x;
  }

  def chpl__unTranslate(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = dim(i).chpl__unTranslate(off(i));
    return x;
  }

  def interior(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do {
      if ((off(i) > 0) && (dim(i)._high+1-off(i) < dim(i)._low) ||
          (off(i) < 0) && (dim(i)._low-1-off(i) > dim(i)._high)) {
        halt("***Error: Argument to 'interior' function out of range in dimension ", i, "***");
      }
      x.ranges(i) = dim(i).interior(off(i));
    }
    return x;
  }

  def exterior(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = dim(i).exterior(off(i));
    return x;
  }

  def expand(off: rank*idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do {
      x.ranges(i) = ranges(i).expand(off(i));
      if (x.ranges(i)._low > x.ranges(i)._high) {
        halt("***Error: Degenerate dimension created in dimension ", i, "***");
      }
    }
    return x;
  }

  def expand(off: idxType) {
    var x = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i).expand(off);
    return x;
  }

  def strideBy(str : rank*int) {
    var x = new DefaultArithmeticDom(rank, idxType, true, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i) by str(i);
    return x;
  }

  def strideBy(str : int) {
    var x = new DefaultArithmeticDom(rank, idxType, true, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i) by str;
    return x;
  }
}

class DefaultArithmeticArr: BaseArr {
  type eltType;
  param rank : int;
  type idxType;
  param stridable: bool;
  //param reindexed: bool = false; // may have blk(rank) != 1
  param reindexed: bool = true; // may have blk(rank) != 1

  var dom : DefaultArithmeticDom(rank=rank, idxType=idxType,
                                         stridable=stridable);
  var off: rank*idxType;
  var blk: rank*idxType;
  var str: rank*int;
  var origin: idxType;
  var factoredOffs: idxType;
  var size : idxType;
  var data : _ddata(eltType);
  var noinit: bool = false;

  def canCopyFromDevice param return true;

  def getBaseDom() return dom;

  def destroyData() {
    if dom.numIndices > 0 {
      pragma "no copy" pragma "no auto destroy" var dr = data;
      pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
      pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, 0);
      pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
      if (chpl__maybeAutoDestroyed(ev)) {
        for i in 0..dom.numIndices-1 {
          pragma "no copy" pragma "no auto destroy" var dr = data;
          pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
          pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, i);
          pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
          chpl__autoDestroy(ev);
        }
      }
    }
    delete data;
  }

  def makeAlias(B: DefaultArithmeticArr) {
    var A = B.reindex(dom);
    off = A.off;
    blk = A.blk;
    str = A.str;
    origin = A.origin;
    factoredOffs = A.factoredOffs;
    data = A.data;
    delete A;
  }

  def these() var {
    if rank == 1 {
      var first = getDataIndex(dom.low);
      var second = getDataIndex(dom.low+dom.ranges(1).stride:idxType);
      var step = (second-first):int;
      var last = first + (dom.numIndices-1) * step:idxType;
      for i in first..last by step do
        yield data(i);
    } else {
      for i in dom do
        yield this(i);
    }
  }

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In array leader code: [\n", this, "]");
    var numCores = here.numCores;
    var runningTasks = here.runningTasks();
    if debugDefaultDist then
      writeln("    numCores=", numCores, ", runningTasks=", runningTasks());
    var numChunks: uint(64) =
    if (runningTasks >= numCores) then 1
    else if maxChunks == -1 then
      if maxThreads == 0 then (numCores-runningTasks+1):uint(64)
      else (min(numCores-runningTasks+1, maxThreads)):uint(64)
    else if maxThreads == 0 then (min(numCores-runningTasks+1, maxChunks)):uint(64)
      else (min(numCores-runningTasks+1, min(maxThreads, maxChunks))):uint(64);

    var numelems: uint(64) = 0;
    for param i in 1..rank do
      numelems += (dom.ranges(i).length):uint(64);
    if debugDefaultDist then
      writeln("*** AI: rank=", rank, " numelems=", numelems, " numChunks=", numChunks, " dom.ranges(1).length=", dom.ranges(1).length);

    if ((numelems <= minElemsPerChunk*numChunks) ||
        ((dom.ranges(1).length):uint(64) < numChunks) ||
        (numChunks == 1)) {
      if debugDefaultDist then
	writeln("*** minElemsPerChunk*numChunks = ",
		minElemsPerChunk*numChunks, ", using 1 chunk");
      if rank == 1 {
	yield tuple(0..dom.ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..dom.ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:dom.ranges(i).low.type..#(dom.ranges(i).length);
      if debugDefaultDist then
	writeln("*** AI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeMyChunk(locBlock(1).length, locBlock(1).high,
                                        numChunks, chunk);
	tuple(1) = lo..hi;
	if debugDefaultDist then
	  writeln("*** AI: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) var where tag == iterator.follower {
    if debugDefaultDist then
      writeln("*** In array follower code: [\n", this, "]");
    for i in dom.these(tag=iterator.follower, follower) do
      yield this(i);
  }

  def computeFactoredOffs() {
    factoredOffs = 0:idxType;
    for i in 1..rank do {
      factoredOffs = factoredOffs + blk(i) * off(i);
    }
  }

  def initialize() {
    if noinit == true then return;
    for param dim in 1..rank {
      off(dim) = dom.dim(dim)._low;
      str(dim) = dom.dim(dim)._stride;
    }
    blk(rank) = 1:idxType;
    for param dim in 1..rank-1 by -1 do
      blk(dim) = blk(dim+1) * dom.dim(dim+1).length;
    computeFactoredOffs();
    size = blk(1) * dom.dim(1).length;
    data = new _ddata(eltType);
    data.init(size);
  }

  pragma "inline"
  def getDataIndex(ind: idxType ...1) where rank == 1
    return getDataIndex(ind);

  pragma "inline"
  def getDataIndex(ind: rank* idxType) {
    var sum = origin;
    if stridable {
      for param i in 1..rank do
        sum += (ind(i) - off(i)) * blk(i) / str(i):idxType;
    } else {
      if reindexed {
        for param i in 1..rank do
          sum += ind(i) * blk(i);
      } else {
        for param i in 1..rank-1 do
          sum += ind(i) * blk(i);
        sum += ind(rank);
      }
      sum -= factoredOffs;
    }
    return sum;
  }

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

  pragma "inline"
  def this(ind : rank*idxType) var {
    if boundsChecking then
      if !dom.member(ind) then
        halt("array index out of bounds: ", ind);
    return data(getDataIndex(ind));
  }

  def reindex(d: DefaultArithmeticDom) {
    if rank != d.rank then
      compilerError("illegal implicit rank change");
    for param i in 1..rank do
      if d.dim(i).length != dom.dim(i).length then
        halt("extent in dimension ", i, " does not match actual");
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=d.rank,
                                         idxType=d.idxType,
                                         stridable=d.stridable,
                                         reindexed=true, dom=d, noinit=true);
    alias.data = data;
    alias.size = size: d.idxType;
    for param i in 1..rank {
      alias.off(i) = d.dim(i)._low;
      alias.blk(i) = (blk(i) * dom.dim(i)._stride / str(i)) : d.idxType;
      alias.str(i) = d.dim(i)._stride;
    }
    alias.origin = origin:d.idxType;
    alias.computeFactoredOffs();
    return alias;
  }

  def checkSlice(ranges) {
    for param i in 1..rank do
      if !dom.dim(i).boundsCheck(ranges(i)) then
        halt("array slice out of bounds in dimension ", i, ": ", ranges(i));
  }

  def slice(d: DefaultArithmeticDom) {
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                         idxType=idxType,
                                         stridable=d.stridable,
                                         reindexed=reindexed,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.size = size;
    alias.blk = blk;
    alias.str = str;
    alias.origin = origin;
    for param i in 1..rank {
      alias.off(i) = d.dim(i)._low;
      alias.origin += blk(i) * (d.dim(i)._low - off(i)) / str(i);
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def checkRankChange(args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    for param i in 1..args.size do
      if isRange(args(i)) then
        if !dom.dim(i).boundsCheck(args(i)) then
          halt("array slice out of bounds in dimension ", i, ": ", args(i));
  }

  def rankChange(d, param newRank: int, param newStridable: bool, args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    var alias = new DefaultArithmeticArr(eltType=eltType, rank=newRank,
                                         idxType=idxType,
                                         stridable=newStridable, reindexed=true,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.size = size;
    var i = 1;
    alias.origin = origin;
    for param j in 1..args.size {
      if isRange(args(j)) {
        alias.off(i) = d.dim(i)._low;
        alias.origin += blk(j) * (d.dim(i)._low - off(j)) / str(j);
        alias.blk(i) = blk(j);
        alias.str(i) = str(j);
        i += 1;
      } else {
        alias.origin += blk(j) * (args(j) - off(j)) / str(j);
      }
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def reallocate(d: domain) {
    if (d._value.type == dom.type) {
      var copy = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                          idxType=idxType,
                                          stridable=d._value.stridable,
                                          reindexed=reindexed, dom=d._value);
      for i in d((...dom.ranges)) do
        copy(i) = this(i);
      off = copy.off;
      blk = copy.blk;
      str = copy.str;
      origin = copy.origin;
      factoredOffs = copy.factoredOffs;
      size = copy.size;
      destroyData();
      data = copy.data;
      delete copy;
    } else {
      halt("illegal reallocation");
    }
  }

  def tupleInit(b: _tuple) {
    def _tupleInitHelp(j, param rank: int, b: _tuple) {
      if rank == 1 {
        for param i in 1..b.size {
          j(this.rank-rank+1) = dom.dim(this.rank-rank+1).low + i - 1;
          this(j) = b(i);
        }
      } else {
        for param i in 1..b.size {
          j(this.rank-rank+1) = dom.dim(this.rank-rank+1).low + i - 1;
          _tupleInitHelp(j, rank-1, b(i));
        }
      }
    }

    if rank == 1 {
      for param i in 1..b.size do
        this(this.dom.dim(1).low + i - 1) = b(i);
    } else {
      var j: rank*int;
      _tupleInitHelp(j, rank, b);
    }
  }
}

def DefaultArithmeticDom.writeThis(f: Writer) {
  f.write("[", dim(1));
  for i in 2..rank do
    f.write(", ", dim(i));
  f.write("]");
}

def DefaultArithmeticArr.writeThis(f: Writer) {
  if dom.numIndices == 0 then return;
  var i : rank*idxType;
  for dim in 1..rank do
    i(dim) = dom.dim(dim)._low;
  label next while true {
    f.write(this(i));
    if i(rank) <= (dom.dim(rank)._high - dom.dim(rank)._stride:idxType) {
      f.write(" ");
      i(rank) += dom.dim(rank)._stride:idxType;
    } else {
      for dim in 1..rank-1 by -1 {
        if i(dim) <= (dom.dim(dim)._high - dom.dim(dim)._stride:idxType) {
          i(dim) += dom.dim(dim)._stride:idxType;
          for dim2 in dim+1..rank {
            f.writeln();
            i(dim2) = dom.dim(dim2)._low;
          }
          continue next;
        }
      }
      break;
    }
  }
}

//
// helper function for blocking index ranges
// - based on _computeBlock() but specialized for cases
//   where the low bound is always zero
//
def _computeMyChunk(numelems, wayhi, numblocks, blocknum) {
  if debugDefaultDist then
    writeln("in _computeMyChunk: numelems=",  numelems, " wayhi=", wayhi,
	    " numblocks=", numblocks, " blocknum=", blocknum);
  /*
  if numblocks == 1 then
    return (0:wayhi.type, numelems:wayhi.type-1);
  */

  if numelems == 0 then
    return (1:wayhi.type, 0:wayhi.type);

  def procToData(x)
    return x:wayhi.type + (x:real != x:int:real):wayhi.type;

  const blo =
    if blocknum == 0 then 0
      else procToData((numelems:real * blocknum) / numblocks);
  const bhi =
    if blocknum == numblocks - 1 then wayhi
      else procToData((numelems:real * (blocknum+1)) / numblocks) - 1;

  return (blo, bhi);
}


A bit more work on the FFT code.  Converted things from floats
to complexes which cleaned some of the top-level code up nicely.
Still have lots of meat to flesh out, though (no pun intended).

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

config var logN: int = 4;

// PLACEHOLDER for external stuff
class timer {
  fun start();
  fun stop();
  fun gettime() { return 1.0; }
}
// END PLACEHOLDER

fun main() {
  const N = 1 << logN;
  const gflop = 5.0 * N * logN / 1000000000.0;

  const EPS = 2.0 ** -51.0;
  const THRESHOLD = 16.0;

// BLC: const domains not supported (constdomain.chpl)
//  const D: domain(1) = [1..N];
// rewritten as var:
  var D: domain(1) = [1..N];
  var DW: domain(1) = [1..N/4];

  var A: [D] complex;
  var B: [D] complex;
  var W: [DW] complex;

  prand_array(A);

  B = A;                   // save A for verification step

  twiddles(W);
  W = bit_reverse(W);

  A.im = -A.im;            // conjugate data

  A = bit_reverse(A);

  dfft(A, W);

  A.re =  A.re / N;        // conjugate and scale data
  A.im = -A.im / N;

  var fftTimer = timer();
  fftTimer.start();

  A = bit_reverse(A);
  dfft(A, W);

  fftTimer.stop();
  var time = fftTimer.gettime();

  var maxerr = max reduce sqrt((B.re - A.re)**2 + (B.im - A.im)**2);

  maxerr = maxerr / logN / EPS;

  var status = if (maxerr < THRESHOLD) then "SUCCESS" else "FAILURE";
  writeln(status, ", error = ", maxerr);
  writeln("\n");

  writeln("N      = ", N);
  writeln("Time   = ", time);
  writeln("GFlops = ", gflop / time);
}

fun bit_reverse(W) {
  return W;
/*
//  const WD = W.domain;
  const n = WD.extent();
//  const mask: uint = 0x0102040810204080;
  const shift = (log(n) / log(2)): integer;

  for i in 1..n {
    const ndx = MTA_BIT_MAT_OR(mask, MTA_BIT_MAT_OR(i, mask));
    ndx = MTA_ROTATE_LEFT(ndx, shift);
    v(2*ndx) = w(2*i);
    v(2*ndx + 1) = w(2*i + 1);
  }

  return V;
*/
}

fun dfft(n, logn, a, w) {
/*
  cf1st(n, a, w);

  var l = 8;
  for i in 4..logn/2 by 2 {
    l *= 4;
    cftmd1(n, l, a, w);
  }
//  for i =
*/
}

fun prand_array(X) {
}

//fun twiddles(W: [?DW] float) {
fun twiddles(W) {
/*
//  const DW = W.domain();
  const n = DW.extent() / 2;
  const delta = atan(1.0) / n;

  W(0) = 1.0;
  W(1) = 0.0;
  W(n) = cos(delta * n);
  W(n+1) = W(n);
  for i in 2..n-1 by 2 {
    const x = cos(delta * i);
    const y = sin(delta * i);
    W(i)           = x;
    W(i+1)         = y;
    W(2*n - i)     = y;
    W(2*n - i + 1) = x;
  }
  writeln(W);
*/
}

/*

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mta_task.h>
#include <machine/runtime.h>

double timer()
{ return ((double) mta_get_clock(0) / mta_clock_freq()); }

#pragma mta inline
void btrfly(j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d)
  int j;
  double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, *a, *b, *c, *d;
{ double x0r = a[j    ] + b[j    ];
  double x0i = a[j + 1] + b[j + 1];
  double x1r = a[j    ] - b[j    ];
  double x1i = a[j + 1] - b[j + 1];
  double x2r = c[j    ] + d[j    ];
  double x2i = c[j + 1] + d[j + 1];
  double x3r = c[j    ] - d[j    ];
  double x3i = c[j + 1] - d[j + 1];

  a[j    ] = x0r + x2r;
  a[j + 1] = x0i + x2i;
  x0r -= x2r;
  x0i -= x2i;
  c[j    ] = wk2r * x0r - wk2i * x0i;
  c[j + 1] = wk2r * x0i + wk2i * x0r;
  x0r = x1r - x3i;
  x0i = x1i + x3r;
  b[j    ] = wk1r * x0r - wk1i * x0i;
  b[j + 1] = wk1r * x0i + wk1i * x0r;
  x0r = x1r + x3i;
  x0i = x1i - x3r;
  d[j    ] = wk3r * x0r - wk3i * x0i;
  d[j + 1] = wk3r * x0i + wk3i * x0r;
}

double * bit_reverse(int n, double *w) {
  unsigned int i, mask, shift;
  double *v = new double[2 * n];

  mask  = 0x0102040810204080;
  shift = (int) (log(n) / log(2));

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (i = 0; i < n; i++) {
      int ndx = MTA_BIT_MAT_OR(mask, MTA_BIT_MAT_OR(i, mask));
      ndx = MTA_ROTATE_LEFT(ndx, shift);
      v[2 * ndx]     = w[2 * i];
      v[2 * ndx + 1] = w[2 * i + 1];
  }

  free(w);
  return(v);
}

void twiddles(int n, double *w)
{ int i;
  double delta = atan(1.0) / n;

  w[0]     = 1;
  w[1]     = 0;
  w[n]     = cos(delta * n);
  w[n + 1] = w[n];

#pragma mta assert no dependence
  for (i = 2; i < n; i += 2) {
      double x = cos(delta * i);
      double y = sin(delta * i);
      w[i]             = x;
      w[i + 1]         = y;
      w[2 * n - i]     = y;
      w[2 * n - i + 1] = x;
} }

void cft1st(int n, double *a, double *w)
{ int j, k1;

  double *v   = w + 1;
  double *b   = a + 2;
  double *c   = a + 4;
  double *d   = a + 6;
  double wk1r = w[2];

  double x0r = a[0] + a[2];
  double x0i = a[1] + a[3];
  double x1r = a[0] - a[2];
  double x1i = a[1] - a[3];
  double x2r = a[4] + a[6];
  double x2i = a[5] + a[7];
  double x3r = a[4] - a[6];
  double x3i = a[5] - a[7];

  a[0] = x0r + x2r;
  a[1] = x0i + x2i;
  a[4] = x0r - x2r;
  a[5] = x0i - x2i;
  a[2] = x1r - x3i;
  a[3] = x1i + x3r;
  a[6] = x1r + x3i;
  a[7] = x1i - x3r;

  x0r  = a[8]  + a[10];
  x0i  = a[9]  + a[11];
  x1r  = a[8]  - a[10];
  x1i  = a[9]  - a[11];
  x2r  = a[12] + a[14];
  x2i  = a[13] + a[15];
  x3r  = a[12] - a[14];
  x3i  = a[13] - a[15];
  a[8] = x0r + x2r;
  a[9] = x0i + x2i;
  a[12] = x2i - x0i;
  a[13] = x0r - x2r;
  x0r   = x1r - x3i;
  x0i   = x1i + x3r;
  a[10] = wk1r * (x0r - x0i);
  a[11] = wk1r * (x0r + x0i);
  x0r   = x3i + x1r;
  x0i   = x3r - x1i;
  a[14] = wk1r * (x0i - x0r);
  a[15] = wk1r * (x0i + x0r);

#pragma mta use 100 streams
#pragma mta no scalar expansion
#pragma mta assert no dependence
  for (j = 16, k1 = 2; j < n; j += 16, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

      btrfly (j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);

      wk1r = w[k1 + k1 + 2];
      wk1i = v[k1 + k1 + 2];
      wk3r = wk1r - 2 * wk2r * wk1i;
      wk3i = 2 * wk2r * wk1r - wk1i;

      btrfly (j + 8, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);
} }

void cftmd0(int n, int l, double *a, double *w)
{ int j, m = l << 2;

  double wk1r = w[2];

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = 0; j < l; j += 2)
      btrfly(j, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, a, b, c, d);

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = m; j < l + m; j += 2)
      btrfly(j, wk1r, wk1r, 0.0, 1.0, - wk1r, wk1r, a, b, c, d);
}

void cftmd1(int n, int l, double *a, double *w)
{ int j, k, k1;

  int m  = l << 2;
  int m2 = 2 * m;

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

  cftmd0(n, l, a, w);

#pragma mta use 100 streams
#pragma mta no scalar expansion
#pragma mta assert no dependence
  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

      for (j = k; j < l + k; j += 2)
          btrfly (j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);

      wk1r = w[k1 + k1 + 2];
      wk1i = v[k1 + k1 + 2];
      wk3r = wk1r - 2 * wk2r * wk1i;
      wk3i = 2 * wk2r * wk1r - wk1i;

      for (j = k + m; j < l + k + m; j += 2)
          btrfly (j, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);
} }

void cftmd21(int n, int l, double *a, double *w)
{ int j, k, k1;
  int m  = l << 2;
  int m2 = 2 * m;
  int m3 = 3 * m;

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = k; j < k + l; j += 2)
      btrfly (j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);

      wk1r = w[k1 + k1 + 2];
      wk1i = v[k1 + k1 + 2];
      wk3r = wk1r - 2 * wk2r * wk1i;
      wk3i = 2 * wk2r * wk1r - wk1i;

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = k + m; j < k + m + l; j += 2)
      btrfly (j, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);

} }

void cftmd2(int n, int l, double *a, double *w)
{ int j, k, k1;

  int m  = l << 2;
  int m2 = 2 * m;

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

  cftmd0(n, l, a, w);

  if (m2 >= n) return;
  if (m2 >= n / 8) {cftmd21(n, l, a, w); return;}

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = 0; j < l; j += 2)  {
#pragma mta assert no dependence
  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

      btrfly (j + k, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);
  }

#pragma mta assert no dependence
  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1 + 2];
      double wk1i = v[k1 + k1 + 2];
      double wk3r = wk1r - 2 * wk2r * wk1i;
      double wk3i = 2 * wk2r * wk1r - wk1i;

      btrfly (j + k + m, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);

} } }

void dfft(int n, int logn, double *a, double *w)
{ int i, l, j;
  double *v, *b, *c, *d;

  cft1st(n, a, w);

  i = 4; l = 8;

  for ( ; i <= logn / 2; i += 2, l *= 4) cftmd1(n, l, a, w);
  for ( ; i <= logn - 1; i += 2, l *= 4) cftmd2(n, l, a, w);

  v = w + 1;
  b = a + l;
  c = a + l + l;
  d = a + l + l + l;

  if ((l << 2) == n) {

#pragma mta use 100 streams
#pragma mta assert no dependence
     for (j = 0; j < l; j += 2)
         btrfly(j, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, a, b, c, d);

  } else {

#pragma mta use 100 streams
#pragma mta assert no dependence
     for (j = 0; j < l; j += 2) {
         double x0r = a[j    ];
         double x0i = a[j + 1];
         double x1r = b[j    ];
         double x1i = b[j + 1];
         a[j    ]   = x0r + x1r;
         a[j + 1]   = x0i + x1i;
         b[j    ]   = x0r - x1r;
         b[j + 1]   = x0i - x1i;
} }  }

*/

config var logN: int = 4;

// PLACEHOLDER for external stuff
class timer {
  fun start();
  fun stop();
  fun gettime() { return 1.0; }
}
// END PLACEHOLDER

fun main() {
  const N = 1 << logN;
  const N2 = 2 * N;
  const gflop = 5.0 * N * logN / 1000000000.0;

  const EPS = 2.0 ** -51.0;
  const THRESHOLD = 16.0;

// BLC: const domains not supported (constdomain.chpl)
//  const DN2: domain(1) = [1..N2];
// rewritten as var:
  var DN2: domain(1) = [1..N2];
// BLC: subdomains not supported (subdomain.chpl)
//  const DOdd: subdomain(DN2) = DN2 by 2;
// BLC: const domains not supported (constdomain.chpl)
//  const DOdd: domain(1) = DN2 by 2;
  var DOdd: domain(1) = DN2 by 2;
//  var DOdd: domain(1) = [1..N2 by 2];
  var DNhalf: domain(1) = [0..N/2 - 1];

  var A: [DN2] float;
  var B: [DN2] float;
  var W: [DNhalf] float;

  prand_array(A);

  B = A;  // save A for verification step

  twiddles(W);
  W = bit_reverse(W);

  forall i in DOdd do
    A(i) = -A(i);

  A = bit_reverse(A);

  forall i in DOdd do

  dfft(A, W);

  forall i in DOdd {
    A(i) = A(i) / N;
    A(i+1) = -A(i+1) / N;
  }

  var fftTimer = timer();
  fftTimer.start();

  A = bit_reverse(A);
  dfft(A, W);

  fftTimer.stop();
  var time = fftTimer.gettime();

// BLC: reductions not working yet...
  var maxerr = max reduce [i in DOdd] sqrt((B(i) - A(i))**2 +
                                           (B(i+1) - A(i+1))**2);

  maxerr = maxerr / logN / EPS;

  var status = if (maxerr < THRESHOLD) then "SUCCESS" else "FAILURE";
  writeln(status, ", error = ", maxerr);
  writeln("\n");

  writeln("N      = ", N);
  writeln("Time   = ", time);
  writeln("GFlops = ", gflop / time);
}

fun bit_reverse(W) {
//  const WD = W.domain;
  const n = WD.extent();
//  const mask: uint = 0x0102040810204080;
  const shift = (log(n) / log(2)): integer;

  for i in 1..n {
    const ndx = MTA_BIT_MAT_OR(mask, MTA_BIT_MAT_OR(i, mask));
    ndx = MTA_ROTATE_LEFT(ndx, shift);
    v(2*ndx) = w(2*i);
    v(2*ndx + 1) = w(2*i + 1);
  }

  return V;
}

fun dfft(n, logn, a, w) {
  cf1st(n, a, w);

  var l = 8;
  for i in 4..logn/2 by 2 {
    l *= 4;
    cftmd1(n, l, a, w);
  }
//  for i =
}

fun prand_array(X) {
}

//fun twiddles(W: [?DW] float) {
fun twiddles(W) {
//  const DW = W.domain();
  const n = DW.extent() / 2;
  const delta = atan(1.0) / n;

  W(0) = 1.0;
  W(1) = 0.0;
  W(n) = cos(delta * n);
  W(n+1) = W(n);
  for i in 2..n-1 by 2 {
    const x = cos(delta * i);
    const y = sin(delta * i);
    W(i)           = x;
    W(i+1)         = y;
    W(2*n - i)     = y;
    W(2*n - i + 1) = x;
  }
  writeln(W);
}

/*

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mta_task.h>
#include <machine/runtime.h>

double timer()
{ return ((double) mta_get_clock(0) / mta_clock_freq()); }

#pragma mta inline
void btrfly(j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d)
  int j;
  double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, *a, *b, *c, *d;
{ double x0r = a[j    ] + b[j    ];
  double x0i = a[j + 1] + b[j + 1];
  double x1r = a[j    ] - b[j    ];
  double x1i = a[j + 1] - b[j + 1];
  double x2r = c[j    ] + d[j    ];
  double x2i = c[j + 1] + d[j + 1];
  double x3r = c[j    ] - d[j    ];
  double x3i = c[j + 1] - d[j + 1];

  a[j    ] = x0r + x2r;
  a[j + 1] = x0i + x2i;
  x0r -= x2r;
  x0i -= x2i;
  c[j    ] = wk2r * x0r - wk2i * x0i;
  c[j + 1] = wk2r * x0i + wk2i * x0r;
  x0r = x1r - x3i;
  x0i = x1i + x3r;
  b[j    ] = wk1r * x0r - wk1i * x0i;
  b[j + 1] = wk1r * x0i + wk1i * x0r;
  x0r = x1r + x3i;
  x0i = x1i - x3r;
  d[j    ] = wk3r * x0r - wk3i * x0i;
  d[j + 1] = wk3r * x0i + wk3i * x0r;
}

double * bit_reverse(int n, double *w) {
  unsigned int i, mask, shift;
  double *v = new double[2 * n];

  mask  = 0x0102040810204080;
  shift = (int) (log(n) / log(2));

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (i = 0; i < n; i++) {
      int ndx = MTA_BIT_MAT_OR(mask, MTA_BIT_MAT_OR(i, mask));
      ndx = MTA_ROTATE_LEFT(ndx, shift);
      v[2 * ndx]     = w[2 * i];
      v[2 * ndx + 1] = w[2 * i + 1];
  }

  free(w);
  return(v);
}

void twiddles(int n, double *w)
{ int i;
  double delta = atan(1.0) / n;

  w[0]     = 1;
  w[1]     = 0;
  w[n]     = cos(delta * n);
  w[n + 1] = w[n];

#pragma mta assert no dependence
  for (i = 2; i < n; i += 2) {
      double x = cos(delta * i);
      double y = sin(delta * i);
      w[i]             = x;
      w[i + 1]         = y;
      w[2 * n - i]     = y;
      w[2 * n - i + 1] = x;
} }

void cft1st(int n, double *a, double *w)
{ int j, k1;

  double *v   = w + 1;
  double *b   = a + 2;
  double *c   = a + 4;
  double *d   = a + 6;
  double wk1r = w[2];

  double x0r = a[0] + a[2];
  double x0i = a[1] + a[3];
  double x1r = a[0] - a[2];
  double x1i = a[1] - a[3];
  double x2r = a[4] + a[6];
  double x2i = a[5] + a[7];
  double x3r = a[4] - a[6];
  double x3i = a[5] - a[7];

  a[0] = x0r + x2r;
  a[1] = x0i + x2i;
  a[4] = x0r - x2r;
  a[5] = x0i - x2i;
  a[2] = x1r - x3i;
  a[3] = x1i + x3r;
  a[6] = x1r + x3i;
  a[7] = x1i - x3r;

  x0r  = a[8]  + a[10];
  x0i  = a[9]  + a[11];
  x1r  = a[8]  - a[10];
  x1i  = a[9]  - a[11];
  x2r  = a[12] + a[14];
  x2i  = a[13] + a[15];
  x3r  = a[12] - a[14];
  x3i  = a[13] - a[15];
  a[8] = x0r + x2r;
  a[9] = x0i + x2i;
  a[12] = x2i - x0i;
  a[13] = x0r - x2r;
  x0r   = x1r - x3i;
  x0i   = x1i + x3r;
  a[10] = wk1r * (x0r - x0i);
  a[11] = wk1r * (x0r + x0i);
  x0r   = x3i + x1r;
  x0i   = x3r - x1i;
  a[14] = wk1r * (x0i - x0r);
  a[15] = wk1r * (x0i + x0r);

#pragma mta use 100 streams
#pragma mta no scalar expansion
#pragma mta assert no dependence
  for (j = 16, k1 = 2; j < n; j += 16, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

      btrfly (j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);

      wk1r = w[k1 + k1 + 2];
      wk1i = v[k1 + k1 + 2];
      wk3r = wk1r - 2 * wk2r * wk1i;
      wk3i = 2 * wk2r * wk1r - wk1i;

      btrfly (j + 8, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);
} }

void cftmd0(int n, int l, double *a, double *w)
{ int j, m = l << 2;

  double wk1r = w[2];

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = 0; j < l; j += 2)
      btrfly(j, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, a, b, c, d);

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = m; j < l + m; j += 2)
      btrfly(j, wk1r, wk1r, 0.0, 1.0, - wk1r, wk1r, a, b, c, d);
}

void cftmd1(int n, int l, double *a, double *w)
{ int j, k, k1;

  int m  = l << 2;
  int m2 = 2 * m;

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

  cftmd0(n, l, a, w);

#pragma mta use 100 streams
#pragma mta no scalar expansion
#pragma mta assert no dependence
  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

      for (j = k; j < l + k; j += 2)
          btrfly (j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);

      wk1r = w[k1 + k1 + 2];
      wk1i = v[k1 + k1 + 2];
      wk3r = wk1r - 2 * wk2r * wk1i;
      wk3i = 2 * wk2r * wk1r - wk1i;

      for (j = k + m; j < l + k + m; j += 2)
          btrfly (j, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);
} }

void cftmd21(int n, int l, double *a, double *w)
{ int j, k, k1;
  int m  = l << 2;
  int m2 = 2 * m;
  int m3 = 3 * m;

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = k; j < k + l; j += 2)
      btrfly (j, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);

      wk1r = w[k1 + k1 + 2];
      wk1i = v[k1 + k1 + 2];
      wk3r = wk1r - 2 * wk2r * wk1i;
      wk3i = 2 * wk2r * wk1r - wk1i;

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = k + m; j < k + m + l; j += 2)
      btrfly (j, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);

} }

void cftmd2(int n, int l, double *a, double *w)
{ int j, k, k1;

  int m  = l << 2;
  int m2 = 2 * m;

  double *v = w + 1;
  double *b = a + l;
  double *c = a + l + l;
  double *d = a + l + l + l;

  cftmd0(n, l, a, w);

  if (m2 >= n) return;
  if (m2 >= n / 8) {cftmd21(n, l, a, w); return;}

#pragma mta use 100 streams
#pragma mta assert no dependence
  for (j = 0; j < l; j += 2)  {
#pragma mta assert no dependence
  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1];
      double wk1i = v[k1 + k1];
      double wk3r = wk1r - 2 * wk2i * wk1i;
      double wk3i = 2 * wk2i * wk1r - wk1i;

      btrfly (j + k, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, a, b, c, d);
  }

#pragma mta assert no dependence
  for (k = m2, k1 = 2; k < n; k += m2, k1 += 2) {
      double wk2r = w[k1];
      double wk2i = v[k1];
      double wk1r = w[k1 + k1 + 2];
      double wk1i = v[k1 + k1 + 2];
      double wk3r = wk1r - 2 * wk2r * wk1i;
      double wk3i = 2 * wk2r * wk1r - wk1i;

      btrfly (j + k + m, wk1r, wk1i, - wk2i, wk2r, wk3r, wk3i, a, b, c, d);

} } }

void dfft(int n, int logn, double *a, double *w)
{ int i, l, j;
  double *v, *b, *c, *d;

  cft1st(n, a, w);

  i = 4; l = 8;

  for ( ; i <= logn / 2; i += 2, l *= 4) cftmd1(n, l, a, w);
  for ( ; i <= logn - 1; i += 2, l *= 4) cftmd2(n, l, a, w);

  v = w + 1;
  b = a + l;
  c = a + l + l;
  d = a + l + l + l;

  if ((l << 2) == n) {

#pragma mta use 100 streams
#pragma mta assert no dependence
     for (j = 0; j < l; j += 2)
         btrfly(j, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, a, b, c, d);

  } else {

#pragma mta use 100 streams
#pragma mta assert no dependence
     for (j = 0; j < l; j += 2) {
         double x0r = a[j    ];
         double x0i = a[j + 1];
         double x1r = b[j    ];
         double x1i = b[j + 1];
         a[j    ]   = x0r + x1r;
         a[j + 1]   = x0i + x1i;
         b[j    ]   = x0r - x1r;
         b[j + 1]   = x0i - x1i;
} }  }

*/


Removed extra () no longer necessary in this test.

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

class adomain : value {
  param rank : integer;

  var low : _data(integer) = _data(integer, 2);
  var high : _data(integer) = _data(integer, 2);
  var stride : _data(integer) = _data(integer, 2);
  var alignment : _data(integer) = _data(integer, 2);

  function set(dim : integer, x : _aseq) {
    low(dim) = x._low;
    high(dim) = x._high;
    stride(dim) = x._stride;
    alignment(dim) = x._low;
  }

  iterator _forall(dim : integer) : integer {
    var alow : integer = low(dim);
    var ahigh : integer = high(dim);
    var astride : integer = stride(dim);
    forall i in alow..ahigh by astride do
      yield i;
  }

  iterator _forall() : (integer, integer) {
    for i in _forall(0) {
      for j in _forall(1) {
        yield (i, j);
      }
    }
  }
}

function write(x : adomain) {
  write("[");
  var first : boolean = true;
  var hi : integer = x.rank-1;
  for i in 0..hi {
    if not first then
      write(", ");
    write(x.low(i));
    write("..");
    write(x.high(i));
    var str = x.stride(i);
    if (str != 1) then
      write(" by ", str);
    first = false;
  }
  write("]");
}

var d : adomain(2);
d.set(0, 1..5);
d.set(1, 2..8 by 2);
writeln(d);

writeln(d._forall(0));
writeln(d._forall(1));
writeln(d._forall());

class adomain : value {
  param rank : integer;

  var low : _data(integer) = _data(integer, 2);
  var high : _data(integer) = _data(integer, 2);
  var stride : _data(integer) = _data(integer, 2);
  var alignment : _data(integer) = _data(integer, 2);

  function set(dim : integer, x : _aseq) {
    low(dim) = x._low;
    high(dim) = x._high;
    stride(dim) = x._stride;
    alignment(dim) = x._low;
  }

  iterator _forall(dim : integer) : integer {
    var alow : integer = low(dim);
    var ahigh : integer = high(dim);
    var astride : integer = stride(dim);
    forall i in alow..ahigh by astride do
      yield i;
  }

  iterator _forall() : (integer, integer) {
    for i in _forall(0) {
      for j in _forall(1) {
        yield (i, j);
      }
    }
  }
}

function write(x : adomain) {
  write("[");
  var first : boolean = true;
  var hi : integer = x.rank-1;
  for i in 0..hi {
    if not first then
      write(", ");
    write(x.low()(i));
    write("..");
    write(x.high()(i));
    var str = x.stride()(i);
    if (str != 1) then
      write(" by ", str);
    first = false;
  }
  write("]");
}

var d : adomain(2);
d.set(0, 1..5);
d.set(1, 2..8 by 2);
writeln(d);

writeln(d._forall(0));
writeln(d._forall(1));
writeln(d._forall());