Fixed a bug in the DefaultArithmeticDomain follower in which the stride was
ignored.  This fixes the following tests with parallel iterators enabled:

    test/performance/compiler/bradc/fft-timecomp
    test/release/examples/hpcc/fft
    test/studies/hpcc/FFT/fft-testPow4
    test/studies/hpcc/HPL/stonea/serial/hplExample4

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

use Schedules;
use List;

class DefaultDistribution: Distribution {
  def newArithmeticDomain(param rank: int, type idxType, param stridable: bool)
    return new DefaultArithmeticDomain(rank, idxType, stridable, this);

  def newAssociativeDomain(type idxType)
    return new DefaultAssociativeDomain(idxType, this);

  def newEnumeratedDomain(type idxType)
    return new DefaultEnumDomain(idxType, this);

  def newOpaqueDomain(type idxType)
    return new DefaultOpaqueDomain(this);

  def newSparseDomain(param rank: int, type idxType, dom: domain)
    return new DefaultSparseDomain(rank, idxType, this, dom);
}

var defaultDistribution = new DefaultDistribution();
var Block = new DefaultDistribution();

class DefaultArithmeticDomain: BaseArithmeticDomain {
  param rank : int;
  type idxType;
  param stridable: bool;
  var dist: DefaultDistribution;
  var ranges : rank*range(idxType,BoundedRangeType.bounded,stridable);

  def DefaultArithmeticDomain(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 rank == 1 {
      yield 0..ranges(1).length-1;
    } else {
      var block = ranges;
      for param i in 1..rank do
        block(i) = 0..block(i).length-1;
      yield block;
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    if rank == 1 {
      if stridable {
        var block =
          ranges(1).low+follower.low*ranges(1).stride:ranges(1).eltType..ranges(1).low+follower.high*ranges(1).stride:ranges(1).eltType by ranges(1).stride;
        for i in block do
          yield i;
      } else {
        var block = follower + ranges(1).low;
        for i in block do
          yield i;
      }
    } else {
      if stridable {
        for param i in 1..rank do
          follower(i) =
            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 {
        for param i in 1..rank do
          follower(i) = follower(i) + ranges(i).low;
      }
      for i in these_help(1, follower) 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 dim(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 DefaultArithmeticArray(eltType, rank, idxType, stridable,
                                           dom=this);
  }

  def slice(param stridable: bool, ranges) {
    var d = new DefaultArithmeticDomain(rank, idxType, 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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i).expand(off);
    return x;
  }

  // this should eventually...
  //  ...be in the distribtion's implementation of the blocked domain
  // ...yield a subBlock of the domain per thread per locale
  def subBlocks {
    if (rank != 1) then halt("subBlocks not yet implemented for >1D domains");
    for block in BlockSchedule(this, Locales(0).numCores) {
      yield block;
    }
  }

  def strideBy(str : rank*int) {
    var x = new DefaultArithmeticDomain(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 DefaultArithmeticDomain(rank, idxType, true, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i) by str;
    return x;
  }
}

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

  var dom : DefaultArithmeticDomain(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 ~DefaultArithmeticArray() { delete data; }

  def these() var {
    for i in dom do
      yield this(i);
  }

  def these(param tag: iterator) where tag == iterator.leader {
    for block in dom.these(tag=iterator.leader) do
      yield block;
  }

  def these(param tag: iterator, follower) var where tag == iterator.follower {
    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, size:int); // ahh!!! can't cast to int here
    data.init();
  }

  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);
    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 data(sum:int); // !!ahh
  }

  def reindex(d: DefaultArithmeticDomain) {
    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 DefaultArithmeticArray(eltType, d.rank, d.idxType,
                                                d.stridable, true, d,
                                                noinit=true);
    //    was:  (eltType, rank, idxType, d.stridable, true, 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: DefaultArithmeticDomain) {
    var alias = new DefaultArithmeticArray(eltType, rank, idxType,
                                                d.stridable, reindexed,
                                                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(param newRank: int, param newStridable: bool, args) {
    var d = dom.rankChange(newRank, newStridable, args);

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

    var alias = new DefaultArithmeticArray(eltType, newRank, idxType,
                                                newStridable, true, 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 DefaultArithmeticArray(eltType, rank, idxType,
                                                 d._value.stridable, reindexed,
                                                 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;
      data = copy.data;
    } 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 DefaultArithmeticDomain.writeThis(f: Writer) {
  f.write("[", dim(1));
  for i in 2..rank do
    f.write(", ", dim(i));
  f.write("]");
}

def DefaultArithmeticArray.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;
    }
  }
}

use Schedules;
use List;

class DefaultDistribution: Distribution {
  def newArithmeticDomain(param rank: int, type idxType, param stridable: bool)
    return new DefaultArithmeticDomain(rank, idxType, stridable, this);

  def newAssociativeDomain(type idxType)
    return new DefaultAssociativeDomain(idxType, this);

  def newEnumeratedDomain(type idxType)
    return new DefaultEnumDomain(idxType, this);

  def newOpaqueDomain(type idxType)
    return new DefaultOpaqueDomain(this);

  def newSparseDomain(param rank: int, type idxType, dom: domain)
    return new DefaultSparseDomain(rank, idxType, this, dom);
}

var defaultDistribution = new DefaultDistribution();
var Block = new DefaultDistribution();

class DefaultArithmeticDomain: BaseArithmeticDomain {
  param rank : int;
  type idxType;
  param stridable: bool;
  var dist: DefaultDistribution;
  var ranges : rank*range(idxType,BoundedRangeType.bounded,stridable);

  def DefaultArithmeticDomain(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 rank == 1 {
      yield 0..ranges(1).length-1;
    } else {
      var block = ranges;
      for param i in 1..rank do
        block(i) = 0..block(i).length-1;
      yield block;
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    if rank == 1 {
      if stridable {
        var block =
          ranges(1).low+follower.low*ranges(1).stride:ranges(1).eltType..ranges(1).low+follower.high*ranges(1).stride:ranges(1).eltType;
        for i in block do
          yield i;
      } else {
        var block = follower + ranges(1).low;
        for i in block do
          yield i;
      }
    } else {
      if stridable {
        for param i in 1..rank do
          follower(i) =
            ranges(i).low+follower(i).low*ranges(i).stride:ranges(i).eltType..ranges(i).low+follower(i).high*ranges(i).stride:ranges(i).eltType;
      } else {
        for param i in 1..rank do
          follower(i) = follower(i) + ranges(i).low;
      }
      for i in these_help(1, follower) 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 dim(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 DefaultArithmeticArray(eltType, rank, idxType, stridable,
                                           dom=this);
  }

  def slice(param stridable: bool, ranges) {
    var d = new DefaultArithmeticDomain(rank, idxType, 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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(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 DefaultArithmeticDomain(rank, idxType, stridable, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i).expand(off);
    return x;
  }

  // this should eventually...
  //  ...be in the distribtion's implementation of the blocked domain
  // ...yield a subBlock of the domain per thread per locale
  def subBlocks {
    if (rank != 1) then halt("subBlocks not yet implemented for >1D domains");
    for block in BlockSchedule(this, Locales(0).numCores) {
      yield block;
    }
  }

  def strideBy(str : rank*int) {
    var x = new DefaultArithmeticDomain(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 DefaultArithmeticDomain(rank, idxType, true, dist);
    for i in 1..rank do
      x.ranges(i) = ranges(i) by str;
    return x;
  }
}

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

  var dom : DefaultArithmeticDomain(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 ~DefaultArithmeticArray() { delete data; }

  def these() var {
    for i in dom do
      yield this(i);
  }

  def these(param tag: iterator) where tag == iterator.leader {
    for block in dom.these(tag=iterator.leader) do
      yield block;
  }

  def these(param tag: iterator, follower) var where tag == iterator.follower {
    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, size:int); // ahh!!! can't cast to int here
    data.init();
  }

  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);
    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 data(sum:int); // !!ahh
  }

  def reindex(d: DefaultArithmeticDomain) {
    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 DefaultArithmeticArray(eltType, d.rank, d.idxType,
                                                d.stridable, true, d,
                                                noinit=true);
    //    was:  (eltType, rank, idxType, d.stridable, true, 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: DefaultArithmeticDomain) {
    var alias = new DefaultArithmeticArray(eltType, rank, idxType,
                                                d.stridable, reindexed,
                                                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(param newRank: int, param newStridable: bool, args) {
    var d = dom.rankChange(newRank, newStridable, args);

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

    var alias = new DefaultArithmeticArray(eltType, newRank, idxType,
                                                newStridable, true, 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 DefaultArithmeticArray(eltType, rank, idxType,
                                                 d._value.stridable, reindexed,
                                                 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;
      data = copy.data;
    } 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 DefaultArithmeticDomain.writeThis(f: Writer) {
  f.write("[", dim(1));
  for i in 2..rank do
    f.write(", ", dim(i));
  f.write("]");
}

def DefaultArithmeticArray.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;
    }
  }
}


Serialized a whole array assignment before a section of code that is checked
for memory leaks.  This is important to avoid ending up with a case where we
may free the task pool descriptor associated with the parallel execution of the
whole array assignment during the section of code that is checked.  Better
memory leak checks will check for memory leaks at the end of the program's
execution after cleanup quiesces.

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

// find out how much memory is leaked when a slice
// is passed in to another function

use Memory;

const N = 5;
var A: [1..N] int;
serial true do A = 7;
var sum = -3;

def foo (X) {
  var sum = 0;
  for i in X.domain do sum += X[i]*i;
  return sum;
}

def bar () {
sum += foo(A[A.domain.low+1..A.domain.high-1]);
}

var m1 = memoryUsed();
bar();
var m2 = memoryUsed();

writeln("Amount of leaked memory after calling bar(): ", m2:int - m1:int);
writeln("sum is ", sum);
writeln(A);

// find out how much memory is leaked when a slice
// is passed in to another function

use Memory;

const N = 5;
var A: [1..N] int = 7;
var sum = -3;

def foo (X) {
  var sum = 0;
  for i in X.domain do sum += X[i]*i;
  return sum;
}

def bar () {
sum += foo(A[A.domain.low+1..A.domain.high-1]);
}

var m1 = memoryUsed();
bar();
var m2 = memoryUsed();

writeln("Amount of leaked memory after calling bar(): ", m2:int - m1:int);
writeln("sum is ", sum);
writeln(A);


Very basic Assert chpldoc-ing

Very basic Assert chpldoc-ing

Forgot to add the 'module Assert' declaration earlier since this
module wasn't on our master list from the chpldoc deep-dive.  :(
And it feels late to add it now.  So for now, adding the documentation
without the module-level docs (which are there, simply not triggered).

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

/*
  Provide simple assertion capabilities.

  Note that in the current implementation, these asserts never become
  no-ops.  That is, using them will always incur execution-time
  checks.
*/

//
// TODO: Uncomment the following (didn't make it in in time for code
// freeze or a few nights of testing before doc freeze for 1.11)
//
// module Assert {

/*
  Assert that a boolean condition is true.  If it is false, prints
  'assert failed' and halts the program.

  :arg test: the boolean condition
  :type test: `bool`
*/
proc assert(test: bool) {
  if !test then
    __primitive("chpl_error", "assert failed");
}

/*
  Assert that a boolean condition is true.  If it is false, prints
  'assert failed - ' followed by all subsequent arguments, as though
  printed using :proc:`~IO.write()`.

  :arg test: the boolean condition
  :type test: `bool`

  :arg args: other arguments to print
*/
proc assert(test: bool, args ...?numArgs) {
  if !test {
    var tmpstring: c_string;
    tmpstring.write((...args));
    __primitive("chpl_error", "assert failed - " + tmpstring);
  }
}

//
// See TOOO above
//
// }
//

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

proc assert(test: bool) {
  if !test then
    __primitive("chpl_error", "assert failed");
}

proc assert(test: bool, args ...?numArgs) {
  if !test {
    var tmpstring: c_string;
    tmpstring.write((...args));
    __primitive("chpl_error", "assert failed - " + tmpstring);
  }
}


Fixed typo in comment (the word "returning" was repeated twice)

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

/* This file contains a block-LU solver function named blockLU() along
 * with its supporting functions and iterators.  It also contains a
 * main function that tests the blockLU function.
 *
 * The test matrix is read in from blockLU-Mat.dat by default, but a
 * different input file can be specified on the command line using
 * --inputfile=<filename>.  The default block size is 2, but this can
 * be overridden on the command line using --block=<blocksize>.
 *
 * This blockLU function is written for a single locale.  It uses an
 * iterator to define blocks of indices that make expressing the block
 * algorithm easier and less error-prone.  Array aliases are defined
 * with these block indices at each iteration, so that the submatrices
 * involved in the block factorization and in the subsequent updates
 * are easily referenced and modified.
 */

// configuration variables:
//   inputfile: a string representing the file containing matrix data
//   block:     the block size to use for the block-LU factorization

config const inputfile = "blockLU-Mat.dat";
config var block = 2;

// blockLU: Computes block-LU with pivoting.  The arguments are as follows:
//   A: the input array to be factored.  It must be 2D, square, and
//        its dimensions must be defined using identical ranges
//
//   blk: the blocking factor, which must be positive and less than or
//        equal to A's size.
//
//   piv: a vector used to store and output the permutations performed
//        by pivoting operations

def blockLU(A: [?D], blk, piv: [D.dim(1)]) where (D.rank == 2) {

  // Test that the domain of A is square with the same index set for
  // each dimension.
  if (D.dim(1) != D.dim(2)) then
    halt("blockLU requires square matrix with same dimensions");

  // Test that 0 < blk <= n, where n = length of one dimension of A.
  if (blk <= 0) || (blk > D.dim(1).length) then
    halt(blk," is an invalid block size passed to blockLU");

  [i in D.dim(1)] piv(i) = i;    // initialize the pivot vector

  // Main loop of block LU uses an iterator to compute three sets of
  // index ranges -- those that are unfactored, divided into those
  // currently being factored and those that remain.

  for (UnfactoredInds, CurrentBlockInds, TrailingBlockInds)
        in generateBlockLURanges(D.dim(1), blk) {

    // Create array aliases for various submatrices of current block
    // iteration as follows:
    //
    //     |Factored    |   |Factored   |   |Factored   |
    //     |  _ _ _ _ _ |   |    _ _ _ _|   |    _ _ _ _|
    // A = | |Unfactored| = |   |A1 | A2| = |   |A11 A12|
    //     | |          |   |   |   |   |   |   |A21 A22|
    //
    // where A1 = |A11|
    //            |A21| is the current rectangular block that
    //                  is factored in this iteration
    //
    //       A2 = |A12|
    //            |A22| is updated after the LU factorization
    //                  of A1

    var A1  => A[UnfactoredInds, CurrentBlockInds],
        A2  => A[UnfactoredInds, TrailingBlockInds],
        A11 => A[CurrentBlockInds, CurrentBlockInds],
        A21 => A[TrailingBlockInds, CurrentBlockInds],
        A12 => A[CurrentBlockInds, TrailingBlockInds],
        A22 => A[TrailingBlockInds, TrailingBlockInds];

    // First compute the LU factorization of A1...
    //
    // We loop over the rows of A11, but we eliminate the
    // the full rectangular A1 block.

    for k in CurrentBlockInds {

      // Find pivot for kth column:
      //   identify largest magnitude element in kth column
      //   (pivot), from the diagonal element downward
      //   through all rows of A1
      const pivotRow = computePivotRow(A1[k.., k..k]),
            pivot = A1[pivotRow, k];

      // If kth row is not row of pivot, swap rows:
      if (pivotRow != k) {

        // store row permutation in piv array
        piv(k) <=> piv(pivotRow);

        // swap values in rows k and pivotRow of the full
        // matrix A
        A[k..k, ..] <=> A[pivotRow..pivotRow, ..];
      }

      // If pivot is zero, halt.  Matrix is singular and
      // cannot be factored.
      if (pivot == 0) then halt("zero pivot encountered");

      // Compute the k-th elimination step:
      //   store multipliers...
      A1[k+1.., k..k] /= pivot;

      //   ..and subtract scaled kth row from remaining
      //   unfactored rows of A1

      forall (i,j) in [UnfactoredInds(k+1..), CurrentBlockInds(k+1..)] do
        A1(i,j) -= A1(i,k) * A1(k,j);
    }

    // ... and now update A2.
    // First update A12.

    forall j in TrailingBlockInds do
      for k in CurrentBlockInds do
        forall i in CurrentBlockInds(k+1..) do
          A12(i,j) -= A1(i,k) * A12(k,j);

    // And then update A22 -= A21*A12.
    //
    // MMIterator is used to generate the indices for this
    // matrix-matrix multiplication step.  MMIterator can
    // be optimized (e.g., for the loop nesting order and loop
    // unrolling) separately from this blockLU routine.

    for (i,j,k1,k2) in MMIterator(A21.domain, A12.domain) do
      A22(i,j) -= A21(i,k1) * A12(k2,j);
  }
}

// The generateBlockLURanges iterator returns a 3-tuple of
// the ranges to be used in each iteration of block LU:
//   (UnfactoredInds, CurrentBlockInds, TrailingBlockInds).
// By defining these ranges, the blockLU code is cleaner. It
// eliminates the need to test for the last iteration where a full-sized
// block may not be possible and only the factorization step (and not
// the update step) is required.
//
// The range UnfactoredInds is equivalent to the range
// CurrentBlockInds concatenated with the range TrailingBlockInds.
//
// The range CurrentBlockInds has the length of blksize, except
// for the end case when the last block may not be of full size.
// This iterator takes care of this end case so that testing for
//  it is not necessary in the blockLU code itself.
//
// The range TrailingBlockInds will be an empty range for the
// the last block iteration.  When it is empty, the loops in
// the blockLU code will not execute, so testing for this case is
// not necessary.

def generateBlockLURanges(D:range, blksize) {
  const end = D.high;

  for i in D by blksize {
    const hi = min(i + blksize-1, end);
    yield (i..end, i..hi, hi+1..end);
  }
}

// The MMIterator generates indices for a matrix multiplication loop.
// This iterator can be tuned for loop nesting order or loop unrolling,
// isolating these optimizations from the blockLU code (or any other
// code in a complete linear algebra library that uses matrix-matrix
// multiplication).

def MMIterator(D1, D2) {
  for j in D2.dim(2) do
    for (k1, k2) in (D1.dim(2), D2.dim(1)) do
      for i in D1.dim(1) do
        yield (i,j,k1,k2);
}

// This function computes the pivot row for an LU pivoting operation
// by using Chapel's maxloc reduction on the absolute values in the
// matrix, dropping the maximum value on the floor using the
// underscore operator, and returning the row of the resulting index.

def computePivotRow(A:[?D]) {
   const (_, ind) = maxloc reduce (abs(A), D);
   return ind(1);
}

// The main function here provides a test harness for calling the
// blockLU routine

def main() {

  // Open the input file, read in the array size, ensure it's square
  var infile = file(inputfile, path='./', mode='r');
  infile.open();
  const m = infile.read(int),
        n = infile.read(int);
  if (m != n) then halt("Input matrix must be square");

  // Ensure the block size is appropriate.
  // It cannot be less than 1.  Reset to 1 if this happens.
  // It cannot exceed the size of n.  Reset to n if this happens.
  block = max(1, block);
  block = min(block, n);

  // declare a range A1D, 2D domain AD, and array (matrix) A
  const A1D = 1..n,
        AD = [A1D, A1D];
  var A: [AD] real;

  // Initialize A by reading values in from infile.
  for ij in AD do
    infile.read(A(ij));

  // close the input file
  infile.close();

  // declare the pivot permutation vector
  var piv: [A1D] int;

  // print out the initial matrix
  writeln("Unfactored Matrix:\n", A, "\n");

  // perform the blocking
  blockLU(A, block, piv);

  // print out the results
  writeln("Factored Matrix:\n", A, "\n");
  writeln("Pivot Vector:\n", piv);
}

/* This file contains a block-LU solver function named blockLU() along
 * with its supporting functions and iterators.  It also contains a
 * main function that tests the blockLU function.
 *
 * The test matrix is read in from blockLU-Mat.dat by default, but a
 * different input file can be specified on the command line using
 * --inputfile=<filename>.  The default block size is 2, but this can
 * be overridden on the command line using --block=<blocksize>.
 *
 * This blockLU function is written for a single locale.  It uses an
 * iterator to define blocks of indices that make expressing the block
 * algorithm easier and less error-prone.  Array aliases are defined
 * with these block indices at each iteration, so that the submatrices
 * involved in the block factorization and in the subsequent updates
 * are easily referenced and modified.
 */

// configuration variables:
//   inputfile: a string representing the file containing matrix data
//   block:     the block size to use for the block-LU factorization

config const inputfile = "blockLU-Mat.dat";
config var block = 2;

// blockLU: Computes block-LU with pivoting.  The arguments are as follows:
//   A: the input array to be factored.  It must be 2D, square, and
//        its dimensions must be defined using identical ranges
//
//   blk: the blocking factor, which must be positive and less than or
//        equal to A's size.
//
//   piv: a vector used to store and output the permutations performed
//        by pivoting operations

def blockLU(A: [?D], blk, piv: [D.dim(1)]) where (D.rank == 2) {

  // Test that the domain of A is square with the same index set for
  // each dimension.
  if (D.dim(1) != D.dim(2)) then
    halt("blockLU requires square matrix with same dimensions");

  // Test that 0 < blk <= n, where n = length of one dimension of A.
  if (blk <= 0) || (blk > D.dim(1).length) then
    halt(blk," is an invalid block size passed to blockLU");

  [i in D.dim(1)] piv(i) = i;    // initialize the pivot vector

  // Main loop of block LU uses an iterator to compute three sets of
  // index ranges -- those that are unfactored, divided into those
  // currently being factored and those that remain.

  for (UnfactoredInds, CurrentBlockInds, TrailingBlockInds)
        in generateBlockLURanges(D.dim(1), blk) {

    // Create array aliases for various submatrices of current block
    // iteration as follows:
    //
    //     |Factored    |   |Factored   |   |Factored   |
    //     |  _ _ _ _ _ |   |    _ _ _ _|   |    _ _ _ _|
    // A = | |Unfactored| = |   |A1 | A2| = |   |A11 A12|
    //     | |          |   |   |   |   |   |   |A21 A22|
    //
    // where A1 = |A11|
    //            |A21| is the current rectangular block that
    //                  is factored in this iteration
    //
    //       A2 = |A12|
    //            |A22| is updated after the LU factorization
    //                  of A1

    var A1  => A[UnfactoredInds, CurrentBlockInds],
        A2  => A[UnfactoredInds, TrailingBlockInds],
        A11 => A[CurrentBlockInds, CurrentBlockInds],
        A21 => A[TrailingBlockInds, CurrentBlockInds],
        A12 => A[CurrentBlockInds, TrailingBlockInds],
        A22 => A[TrailingBlockInds, TrailingBlockInds];

    // First compute the LU factorization of A1...
    //
    // We loop over the rows of A11, but we eliminate the
    // the full rectangular A1 block.

    for k in CurrentBlockInds {

      // Find pivot for kth column:
      //   identify largest magnitude element in kth column
      //   (pivot), from the diagonal element downward
      //   through all rows of A1
      const pivotRow = computePivotRow(A1[k.., k..k]),
            pivot = A1[pivotRow, k];

      // If kth row is not row of pivot, swap rows:
      if (pivotRow != k) {

        // store row permutation in piv array
        piv(k) <=> piv(pivotRow);

        // swap values in rows k and pivotRow of the full
        // matrix A
        A[k..k, ..] <=> A[pivotRow..pivotRow, ..];
      }

      // If pivot is zero, halt.  Matrix is singular and
      // cannot be factored.
      if (pivot == 0) then halt("zero pivot encountered");

      // Compute the k-th elimination step:
      //   store multipliers...
      A1[k+1.., k..k] /= pivot;

      //   ..and subtract scaled kth row from remaining
      //   unfactored rows of A1

      forall (i,j) in [UnfactoredInds(k+1..), CurrentBlockInds(k+1..)] do
        A1(i,j) -= A1(i,k) * A1(k,j);
    }

    // ... and now update A2.
    // First update A12.

    forall j in TrailingBlockInds do
      for k in CurrentBlockInds do
        forall i in CurrentBlockInds(k+1..) do
          A12(i,j) -= A1(i,k) * A12(k,j);

    // And then update A22 -= A21*A12.
    //
    // MMIterator is used to generate the indices for this
    // matrix-matrix multiplication step.  MMIterator can
    // be optimized (e.g., for the loop nesting order and loop
    // unrolling) separately from this blockLU routine.

    for (i,j,k1,k2) in MMIterator(A21.domain, A12.domain) do
      A22(i,j) -= A21(i,k1) * A12(k2,j);
  }
}

// The generateBlockLURanges iterator returns a 3-tuple of
// the ranges to be used in each iteration of block LU:
//   (UnfactoredInds, CurrentBlockInds, TrailingBlockInds).
// By defining these ranges, the blockLU code is cleaner. It
// eliminates the need to test for the last iteration where a full-sized
// block may not be possible and only the factorization step (and not
// the update step) is required.
//
// The range UnfactoredInds is equivalent to the range
// CurrentBlockInds concatenated with the range TrailingBlockInds.
//
// The range CurrentBlockInds has the length of blksize, except
// for the end case when the last block may not be of full size.
// This iterator takes care of this end case so that testing for
//  it is not necessary in the blockLU code itself.
//
// The range TrailingBlockInds will be an empty range for the
// the last block iteration.  When it is empty, the loops in
// the blockLU code will not execute, so testing for this case is
// not necessary.

def generateBlockLURanges(D:range, blksize) {
  const end = D.high;

  for i in D by blksize {
    const hi = min(i + blksize-1, end);
    yield (i..end, i..hi, hi+1..end);
  }
}

// The MMIterator generates indices for a matrix multiplication loop.
// This iterator can be tuned for loop nesting order or loop unrolling,
// isolating these optimizations from the blockLU code (or any other
// code in a complete linear algebra library that uses matrix-matrix
// multiplication).

def MMIterator(D1, D2) {
  for j in D2.dim(2) do
    for (k1, k2) in (D1.dim(2), D2.dim(1)) do
      for i in D1.dim(1) do
        yield (i,j,k1,k2);
}

// This function computes the pivot row for an LU pivoting operation
// by using Chapel's maxloc reduction on the absolute values in the
// matrix, dropping the maximum value on the floor using the
// underscore operator, and returning returning the row of the
// resulting index.

def computePivotRow(A:[?D]) {
   const (_, ind) = maxloc reduce (abs(A), D);
   return ind(1);
}

// The main function here provides a test harness for calling the
// blockLU routine

def main() {

  // Open the input file, read in the array size, ensure it's square
  var infile = file(inputfile, path='./', mode='r');
  infile.open();
  const m = infile.read(int),
        n = infile.read(int);
  if (m != n) then halt("Input matrix must be square");

  // Ensure the block size is appropriate.
  // It cannot be less than 1.  Reset to 1 if this happens.
  // It cannot exceed the size of n.  Reset to n if this happens.
  block = max(1, block);
  block = min(block, n);

  // declare a range A1D, 2D domain AD, and array (matrix) A
  const A1D = 1..n,
        AD = [A1D, A1D];
  var A: [AD] real;

  // Initialize A by reading values in from infile.
  for ij in AD do
    infile.read(A(ij));

  // close the input file
  infile.close();

  // declare the pivot permutation vector
  var piv: [A1D] int;

  // print out the initial matrix
  writeln("Unfactored Matrix:\n", A, "\n");

  // perform the blocking
  blockLU(A, block, piv);

  // print out the results
  writeln("Factored Matrix:\n", A, "\n");
  writeln("Pivot Vector:\n", piv);
}