Move Cyclic method declarations out of the class bodies and use the
def ClassName.MethodName() {} syntax. This makes it fit in better with the
Block and BlockCyclic distributions since they both are written this way.

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

//
// naive routine for dividing numLocales into rank factors
//
def _factor(param rank: int, value) {
  var factors: rank*int;
  for param i in 1..rank do
    factors(i) = 1;
  if value >= 1 {
    var iv = value;
    var factor = 1;
    while iv > 1 {
      for i in 2..iv {
        if iv % i == 0 {
          var j = 1;
          for i in 2..rank {
            if factors(i) < factors(j) then
              j = i;
          }
          factors(j) *= i;
          iv = iv / i;
          break;
        }
      }
    }
  }
  for i in 1..rank do
    for j in i+1..rank do
      if factors(i) < factors(j) then
        factors(i) <=> factors(j);
  return factors;
}

config param debugCyclicDist = false;
config param verboseCyclicDistWriters = false;

class Cyclic: BaseDist {
  param rank: int = 1;
  type idxType = int(64);

  const lowIdx: rank*idxType;
  const targetLocDom: domain(rank);
  const targetLocs: [targetLocDom] locale;

  const locDist: [targetLocDom] LocCyclic(rank, idxType);

  const tasksPerLocale = 1;
  var pid: int = -1;

  def Cyclic(param rank: int,
             type idxType = int(64),
             low: rank*idxType,
             targetLocales: [] locale = thisRealm.Locales,
             tasksPerLocale=0) {
    lowIdx = low;
    if rank == 1  {
      targetLocDom = [0..#targetLocales.numElements];
      targetLocs = targetLocales;
    } else if targetLocales.rank == 1 {
      const factors = _factor(rank, targetLocales.numElements);
      var ranges: rank*range;
      for param i in 1..rank {
        ranges(i) = 0..#factors(i);
      }
      targetLocDom = [(...ranges)];
      for (loc1, loc2) in (targetLocs, targetLocales) {
        loc1 = loc2;
      }
    } else {
      if targetLocales.rank != rank then
        compilerError("locales array rank must be one or match distribution rank");
      var ranges: rank*range;
      for param i in 1..rank do {
        var thisRange = targetLocales.domain.dim(i);
        ranges(i) = 0..#thisRange.length;
      }
      targetLocDom = [(...ranges)];
      targetLocs = reshape(targetLocales, targetLocDom);
    }

    if tasksPerLocale == 0 then
      this.tasksPerLocale = min reduce targetLocales.numCores;
    else
      this.tasksPerLocale = tasksPerLocale;
    coforall locid in targetLocDom {
      on targetLocs(locid) {
        locDist(locid) = new LocCyclic(rank, idxType, locid, this);
      }
    }
    if debugCyclicDist then
      for loc in locDist do writeln(loc);
  }

  def Cyclic(param rank, type idxType, other: Cyclic(rank, idxType)) {
    targetLocDom = other.targetLocDom;
    targetLocs = other.targetLocs;
    lowIdx = other.lowIdx;
    locDist = other.locDist;
    tasksPerLocale = other.tasksPerLocale;
  }

  def clone() return new Cyclic(rank=rank, idxType=idxType, other=this);
}

def Cyclic.getChunk(inds, locid) {
  return inds((...locDist(locid).myChunk.getIndices()));
}

def Cyclic.supportsPrivatization() param return true;

def Cyclic.privatize() {
  return new Cyclic(rank=rank, idxType=idxType, other=this);
}

def Cyclic.reprivatize(other) {
  targetLocDom = other.targetLocDom;
  targetLocs = other.targetLocs;
  locDist = other.locDist;
  lowIdx = other.lowIdx;
  tasksPerLocale = other.tasksPerLocale;
}

def Cyclic.newArithmeticDom(param rank: int, type idxType, param stridable: bool) {
  if idxType != this.idxType then
    compilerError("Cyclic domain index type does not match distribution's");
  if rank != this.rank then
    compilerError("Cyclic domain rank does not match distribution's");
  var dom = new CyclicDom(rank=rank, idxType=idxType, dist = this, stridable=stridable);
  dom.setup();
  return dom;
}

def Cyclic.writeThis(x: Writer) {
  x.writeln(typeToString(this.type));
  x.writeln("------");
  for locid in targetLocDom do
    x.writeln(" [", locid, "=", targetLocs(locid), "] owns chunk: ", locDist(locid).myChunk);
}

def Cyclic.ind2locInd(i: idxType) {
/*
  pragma "inline" def mod(a:integral, b:integral) {
    if (b <= 0) then
      halt("modulus divisor must be positive");
    var tmp = a % b:a.type;
    return if tmp < 0 then b:a.type + tmp else tmp;
  }
*/
  const numLocs:idxType = targetLocDom.numIndices:idxType;
  // this is wrong if i is less than lowIdx
  //return ((i - lowIdx(1)) % numLocs):int;
  // this works even if i is less than lowIdx
  return mod(mod(i, numLocs) - mod(lowIdx(1), numLocs), numLocs):int;
}

def Cyclic.ind2locInd(ind: rank*idxType) {
  var x: rank*int;
  for param i in 1..rank {
    var dimLen = targetLocDom.dim(i).length;
    //x(i) = ((ind(i) - lowIdx(i)) % dimLen):int;
    x(i) = mod(mod(ind(i), dimLen) - mod(lowIdx(i), dimLen), dimLen);
  }
  return x;
}

def Cyclic.ind2loc(i: idxType) where rank == 1 {
  return targetLocs(ind2locInd(i));
}

def Cyclic.ind2loc(i: rank*idxType) {
  return targetLocs(ind2locInd(i));
}

class LocCyclic {
  param rank: int;
  type idxType;

  const myChunk: domain(rank, idxType, true);

  def LocCyclic(param rank, type idxType, locid, dist: Cyclic(rank, idxType)) {
    var locidx: rank*int;
    var lowIdx = dist.lowIdx;

    if rank == 1 then
      locidx(1) = locid;
    else
      locidx = locid;

    var tuple: rank*range(idxType, stridable=true);

    for param i in 1..rank {
      const lower = min(idxType)..(lowIdx(i)+locidx(i)) by -dist.targetLocDom.dim(i).length;
      const upper = (lowIdx(i) + locidx(i))..max(idxType) by dist.targetLocDom.dim(i).length;
      const lo = lower.low, hi = upper.high;
      tuple(i) = lo..hi by dist.targetLocDom.dim(i).length;
    }
    myChunk = [(...tuple)];
  }
}

class CyclicDom : BaseArithmeticDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const dist: Cyclic(rank, idxType);

  var locDoms: [dist.targetLocDom] LocCyclicDom(rank, idxType, stridable);

  const whole: domain(rank, idxType, stridable);

  var pid: int = -1;
}

def CyclicDom.setup() {
  coforall localeIdx in dist.targetLocDom {
    on dist.targetLocs(localeIdx) {
      locDoms(localeIdx) = new LocCyclicDom(rank, idxType, stridable, this,
                                            dist.getChunk(whole, localeIdx));
    }
  }
}

def CyclicDom.buildArray(type eltType) {
  var arr = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=this);
  arr.setup();
  return arr;
}

def CyclicDom.setIndices(x) {
  whole.setIndices(x);
  setup();
}

def CyclicDom.writeThis(x: Writer) {
  if verboseCyclicDistWriters {
    writeln(typeToString(this.type));
    writeln("------");
    for loc in dist.targetLocDom {
      x.writeln("[", loc, "=", dist.targetLocs(loc), "] owns ", locDoms(loc).myBlock);
    }
  } else {
    x.writeln(whole);
  }
}

def CyclicDom.numIndices return whole.numIndices;

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

def CyclicDom.these(param tag: iterator) where tag == iterator.leader {
  const wholeLow = whole.low,
        precomputedNumTasks = dist.tasksPerLocale;
  coforall locDom in locDoms do on locDom {
    const locBlock = locDom.myBlock - wholeLow;
    const numTasks = precomputedNumTasks;
    if numTasks == 1 {
      yield locBlock.getIndices();
    } else {
      if rank == 1 {
        coforall taskid in 0..#numTasks {
          const low = locBlock.low, high = locBlock.high;
          const (lo,hi) = _computeBlock(low, high - low + 1,
                                       low, high,
                                       numTasks, taskid);
          yield locBlock[lo..hi].getIndices();
        }
      } else {
        var ranges: rank*range(stridable=true);
        for param i in 2..rank do
          ranges(i) = locBlock.dim(i);
        coforall taskid in 0..#numTasks {
          const low = locBlock.dim(1).low, high = locBlock.dim(1).high;
          const (lo,hi) = _computeBlock(low, high - low + 1,
                                        low, high,
                                        numTasks, taskid);
          ranges(1) = locBlock.dim(1)(lo..hi);
          yield locBlock[(...ranges)].getIndices();
        }
      }
    }
  }
}

def CyclicDom.these(param tag: iterator, follower, param aligned: bool = false) where tag == iterator.follower {
  var t: rank*range(idxType, stridable=true);
  for param i in 1..rank {
    t(i) = follower(i) + whole.dim(i).low;
  }
  for i in [(...t)] do
    yield i;
}

def CyclicDom.supportsPrivatization() param return true;

def CyclicDom.privatize() {
  var distpid = dist.pid;
  var thisdist = dist;
  var privdist = __primitive("chpl_getPrivatizedClass", thisdist, distpid);
  var c = new CyclicDom(rank=rank, idxType=idxType, stridable=stridable, dist=privdist);
  c.locDoms = locDoms;
  c.whole = whole;
  return c;
}

def CyclicDom.reprivatize(other) {
  locDoms = other.locDoms;
  whole = other.whole;
}

def CyclicDom.dim(d: int) return whole.dim(d);

class LocCyclicDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const wholeDom: CyclicDom(rank, idxType, stridable);
  var myBlock: domain(rank, idxType, true);
}

def _computeBlock(waylo, numelems, lo, wayhi, numblocks, blocknum) {
  def procToData(x, lo)
    return lo + (x:lo.type) + (x:real != x:int:real):lo.type;

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

  return (blo, bhi);
}

class CyclicArr: BaseArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;
  var dom: CyclicDom(rank, idxType, stridable);

  var locArr: [dom.dist.targetLocDom] LocCyclicArr(eltType, rank, idxType, stridable);
  var myLocArr: LocCyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable);
  var pid: int = -1;
}

def CyclicArr.getBaseDom() return dom;

def CyclicArr.setup() {
  coforall localeIdx in dom.dist.targetLocDom {
    on dom.dist.targetLocs(localeIdx) {
      locArr(localeIdx) = new LocCyclicArr(eltType, rank, idxType, stridable, dom.locDoms(localeIdx));
      if this.locale == here then
        myLocArr = locArr(localeIdx);
    }
  }
}

def CyclicArr.supportsPrivatization() param return true;

def CyclicArr.privatize() {
  var dompid = dom.pid;
  var thisdom = dom;
  var privdom = __primitive("chpl_getPrivatizedClass", thisdom, dompid);
  var c = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=privdom);
  c.locArr = locArr;
  for localeIdx in dom.dist.targetLocDom do
    if c.locArr(localeIdx).locale == here then
      c.myLocArr = c.locArr(localeIdx);
  return c;
}

def CyclicArr.this(i: idxType) var where rank == 1 {
  local {
    if myLocArr.locDom.myBlock.member(i) then
      return myLocArr.this(i);
  }
  return locArr(dom.dist.ind2locInd(i))(i);
}

def CyclicArr.this(i:rank*idxType) var {
  return locArr(dom.dist.ind2locInd(i))(i);
}

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

def CyclicArr.these(param tag: iterator) where tag == iterator.leader {
  const wholeLow = dom.whole.low,
        precomputedNumTasks = dom.dist.tasksPerLocale;
  coforall locDom in dom.locDoms do on locDom {
    const locBlock = locDom.myBlock - wholeLow;
    const numTasks = precomputedNumTasks;
    if numTasks == 1 {
      yield locBlock.getIndices();
    } else {
      if rank == 1 {
        coforall taskid in 0..#numTasks {
          const low = locBlock.low, high = locBlock.high;
          const (lo,hi) = _computeBlock(low, high - low + 1,
                                       low, high,
                                       numTasks, taskid);
          yield locBlock[lo..hi].getIndices();
        }
      } else {
        var ranges: rank*range(stridable=true);
        for param i in 2..rank do
          ranges(i) = locBlock.dim(i);
        coforall taskid in 0..#numTasks {
          const low = locBlock.dim(1).low, high = locBlock.dim(1).high;
          const (lo,hi) = _computeBlock(low, high - low + 1,
                                        low, high,
                                        numTasks, taskid);
          ranges(1) = locBlock.dim(1)(lo..hi);
          yield locBlock[(...ranges)].getIndices();
        }
      }
    }
  }
}

def CyclicArr.supportsAlignedFollower() param return true;

def CyclicArr.these(param tag: iterator, follower, param aligned: bool = false) var where tag == iterator.follower {
  var t: rank*range(eltType=idxType, stridable=true);
  for param i in 1..rank {
    t(i) = follower(i) + dom.whole.dim(i).low;
  }
  const followThis = [(...t)];
  const arrSection = locArr(dom.dist.ind2locInd(followThis.low));
  if aligned {
    local {
      for i in followThis {
        yield arrSection.this(i);
      }
    }
  } else {
    def accessHelper(i) var {
      local {
        if myLocArr.locDom.myBlock.member(i) then
          return myLocArr.this(i);
      }
      return this(i);
    }

    for i in followThis {
      yield accessHelper(i);
    }
  }
}

def CyclicArr.writeThis(f: Writer) {
  if verboseCyclicDistWriters {
    writeln(typeToString(this.type));
    writeln("------");
  }
  for i in dom {
    writeln(this(i));
  }
}

class LocCyclicArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;

  const locDom: LocCyclicDom(rank, idxType, stridable);

  var myElems: [locDom.myBlock] eltType;

}

def LocCyclicArr.this(i:idxType) var {
  return myElems(i);
}

def LocCyclicArr.this(i:rank*idxType) var {
  return myElems((...i));
}

def LocCyclicArr.these() var {
  for elem in myElems {
    yield elem;
  }
}

//
// naive routine for dividing numLocales into rank factors
//
def _factor(param rank: int, value) {
  var factors: rank*int;
  for param i in 1..rank do
    factors(i) = 1;
  if value >= 1 {
    var iv = value;
    var factor = 1;
    while iv > 1 {
      for i in 2..iv {
        if iv % i == 0 {
          var j = 1;
          for i in 2..rank {
            if factors(i) < factors(j) then
              j = i;
          }
          factors(j) *= i;
          iv = iv / i;
          break;
        }
      }
    }
  }
  for i in 1..rank do
    for j in i+1..rank do
      if factors(i) < factors(j) then
        factors(i) <=> factors(j);
  return factors;
}

config param debugCyclicDist = false;
config param verboseCyclicDistWriters = false;

class Cyclic: BaseDist {
  param rank: int = 1;
  type idxType = int(64);

  const lowIdx: rank*idxType;
  const targetLocDom: domain(rank);
  const targetLocs: [targetLocDom] locale;

  const locDist: [targetLocDom] LocCyclic(rank, idxType);

  const tasksPerLocale = 1;
  var pid: int = -1;

  def Cyclic(param rank: int,
             type idxType = int(64),
             low: rank*idxType,
             targetLocales: [] locale = thisRealm.Locales,
             tasksPerLocale=0) {
    lowIdx = low;
    if rank == 1  {
      targetLocDom = [0..#targetLocales.numElements];
      targetLocs = targetLocales;
    } else if targetLocales.rank == 1 {
      const factors = _factor(rank, targetLocales.numElements);
      var ranges: rank*range;
      for param i in 1..rank {
        ranges(i) = 0..#factors(i);
      }
      targetLocDom = [(...ranges)];
      for (loc1, loc2) in (targetLocs, targetLocales) {
        loc1 = loc2;
      }
    } else {
      if targetLocales.rank != rank then
        compilerError("locales array rank must be one or match distribution rank");
      var ranges: rank*range;
      for param i in 1..rank do {
        var thisRange = targetLocales.domain.dim(i);
        ranges(i) = 0..#thisRange.length;
      }
      targetLocDom = [(...ranges)];
      targetLocs = reshape(targetLocales, targetLocDom);
    }

    if tasksPerLocale == 0 then
      this.tasksPerLocale = min reduce targetLocales.numCores;
    else
      this.tasksPerLocale = tasksPerLocale;
    coforall locid in targetLocDom {
      on targetLocs(locid) {
        locDist(locid) = new LocCyclic(rank, idxType, locid, this);
      }
    }
    if debugCyclicDist then
      for loc in locDist do writeln(loc);
  }

  def clone() return new Cyclic(rank=rank, idxType=idxType, other=this);

  def Cyclic(param rank, type idxType, other: Cyclic(rank, idxType)) {
    targetLocDom = other.targetLocDom;
    targetLocs = other.targetLocs;
    lowIdx = other.lowIdx;
    locDist = other.locDist;
    tasksPerLocale = other.tasksPerLocale;
  }

  def getChunk(inds, locid) {
    return inds((...locDist(locid).myChunk.getIndices()));
  }

  def supportsPrivatization() param return true;

  def privatize() {
    return new Cyclic(rank=rank, idxType=idxType, other=this);
  }

  def reprivatize(other) {
    targetLocDom = other.targetLocDom;
    targetLocs = other.targetLocs;
    locDist = other.locDist;
    lowIdx = other.lowIdx;
    tasksPerLocale = other.tasksPerLocale;
  }

  def newArithmeticDom(param rank: int, type idxType, param stridable: bool) {
    if idxType != this.idxType then
      compilerError("Cyclic domain index type does not match distribution's");
    if rank != this.rank then
      compilerError("Cyclic domain rank does not match distribution's");
    var dom = new CyclicDom(rank=rank, idxType=idxType, dist = this, stridable=stridable);
    dom.setup();
    return dom;
  }

  def writeThis(x: Writer) {
    x.writeln(typeToString(this.type));
    x.writeln("------");
    for locid in targetLocDom do
      x.writeln(" [", locid, "=", targetLocs(locid), "] owns chunk: ", locDist(locid).myChunk);
  }

  def ind2locInd(i: idxType) {
/*
    pragma "inline" def mod(a:integral, b:integral) {
      if (b <= 0) then
        halt("modulus divisor must be positive");
      var tmp = a % b:a.type;
      return if tmp < 0 then b:a.type + tmp else tmp;
    }
*/
    const numLocs:idxType = targetLocDom.numIndices:idxType;
    // this is wrong if i is less than lowIdx
    //return ((i - lowIdx(1)) % numLocs):int;
    // this works even if i is less than lowIdx
    return mod(mod(i, numLocs) - mod(lowIdx(1), numLocs), numLocs):int;
  }

  def ind2locInd(ind: rank*idxType) {
    var x: rank*int;
    for param i in 1..rank {
      var dimLen = targetLocDom.dim(i).length;
      //x(i) = ((ind(i) - lowIdx(i)) % dimLen):int;
      x(i) = mod(mod(ind(i), dimLen) - mod(lowIdx(i), dimLen), dimLen);
    }
    return x;
  }
  def ind2loc(i: idxType) where rank == 1 {
    return targetLocs(ind2locInd(i));
  }
  def ind2loc(i: rank*idxType) {
    return targetLocs(ind2locInd(i));
  }
}

class LocCyclic {
  param rank: int;
  type idxType;

  const myChunk: domain(rank, idxType, true);

  def LocCyclic(param rank, type idxType, locid, dist: Cyclic(rank, idxType)) {
    var locidx: rank*int;
    var lowIdx = dist.lowIdx;

    if rank == 1 then
      locidx(1) = locid;
    else
      locidx = locid;

    var tuple: rank*range(idxType, stridable=true);

    for param i in 1..rank {
      const lower = min(idxType)..(lowIdx(i)+locidx(i)) by -dist.targetLocDom.dim(i).length;
      const upper = (lowIdx(i) + locidx(i))..max(idxType) by dist.targetLocDom.dim(i).length;
      const lo = lower.low, hi = upper.high;
      tuple(i) = lo..hi by dist.targetLocDom.dim(i).length;
    }
    myChunk = [(...tuple)];
  }
}

class CyclicDom : BaseArithmeticDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const dist: Cyclic(rank, idxType);

  var locDoms: [dist.targetLocDom] LocCyclicDom(rank, idxType, stridable);

  const whole: domain(rank, idxType, stridable);

  var pid: int = -1;

  def setup() {
    coforall localeIdx in dist.targetLocDom {
      on dist.targetLocs(localeIdx) {
        locDoms(localeIdx) = new LocCyclicDom(rank, idxType, stridable, this,
                                              dist.getChunk(whole, localeIdx));
      }
    }
  }

  def buildArray(type eltType) {
    var arr = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=this);
    arr.setup();
    return arr;
  }

  def setIndices(x) {
    whole.setIndices(x);
    setup();
  }

  def writeThis(x: Writer) {
    if verboseCyclicDistWriters {
      writeln(typeToString(this.type));
      writeln("------");
      for loc in dist.targetLocDom {
        x.writeln("[", loc, "=", dist.targetLocs(loc), "] owns ", locDoms(loc).myBlock);
      }
    } else {
      x.writeln(whole);
    }
  }
  def numIndices return whole.numIndices;

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

  def these(param tag: iterator) where tag == iterator.leader {
    const wholeLow = whole.low,
          precomputedNumTasks = dist.tasksPerLocale;
    coforall locDom in locDoms do on locDom {
      const locBlock = locDom.myBlock - wholeLow;
      const numTasks = precomputedNumTasks;
      if numTasks == 1 {
        yield locBlock.getIndices();
      } else {
        if rank == 1 {
          coforall taskid in 0..#numTasks {
            const low = locBlock.low, high = locBlock.high;
            const (lo,hi) = _computeBlock(low, high - low + 1,
                                         low, high,
                                         numTasks, taskid);
            yield locBlock[lo..hi].getIndices();
          }
        } else {
          var ranges: rank*range(stridable=true);
          for param i in 2..rank do
            ranges(i) = locBlock.dim(i);
          coforall taskid in 0..#numTasks {
            const low = locBlock.dim(1).low, high = locBlock.dim(1).high;
            const (lo,hi) = _computeBlock(low, high - low + 1,
                                          low, high,
                                          numTasks, taskid);
            ranges(1) = locBlock.dim(1)(lo..hi);
            yield locBlock[(...ranges)].getIndices();
          }
        }
      }
    }
  }

  def these(param tag: iterator, follower, param aligned: bool = false) where tag == iterator.follower {
    var t: rank*range(idxType, stridable=true);
    for param i in 1..rank {
      t(i) = follower(i) + whole.dim(i).low;
    }
    for i in [(...t)] do
      yield i;
  }

  def supportsPrivatization() param return true;

  def privatize() {
    var distpid = dist.pid;
    var thisdist = dist;
    var privdist = __primitive("chpl_getPrivatizedClass", thisdist, distpid);
    var c = new CyclicDom(rank=rank, idxType=idxType, stridable=stridable, dist=privdist);
    c.locDoms = locDoms;
    c.whole = whole;
    return c;
  }

  def reprivatize(other) {
    locDoms = other.locDoms;
    whole = other.whole;
  }

  def dim(d: int) return whole.dim(d);
}

class LocCyclicDom {
  param rank: int;
  type idxType;
  param stridable: bool;

  const wholeDom: CyclicDom(rank, idxType, stridable);
  var myBlock: domain(rank, idxType, true);
}

def _computeBlock(waylo, numelems, lo, wayhi, numblocks, blocknum) {
  def procToData(x, lo)
    return lo + (x:lo.type) + (x:real != x:int:real):lo.type;

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

  return (blo, bhi);
}

class CyclicArr: BaseArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;
  var dom: CyclicDom(rank, idxType, stridable);

  var locArr: [dom.dist.targetLocDom] LocCyclicArr(eltType, rank, idxType, stridable);
  var myLocArr: LocCyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable);
  var pid: int = -1;

  def getBaseDom() return dom;

  def setup() {
    coforall localeIdx in dom.dist.targetLocDom {
      on dom.dist.targetLocs(localeIdx) {
        locArr(localeIdx) = new LocCyclicArr(eltType, rank, idxType, stridable, dom.locDoms(localeIdx));
        if this.locale == here then
          myLocArr = locArr(localeIdx);
      }
    }
  }

  def supportsPrivatization() param return true;

  def privatize() {
    var dompid = dom.pid;
    var thisdom = dom;
    var privdom = __primitive("chpl_getPrivatizedClass", thisdom, dompid);
    var c = new CyclicArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=privdom);
    c.locArr = locArr;
    for localeIdx in dom.dist.targetLocDom do
      if c.locArr(localeIdx).locale == here then
        c.myLocArr = c.locArr(localeIdx);
    return c;
  }

  def this(i: idxType) var where rank == 1 {
    local {
      if myLocArr.locDom.myBlock.member(i) then
        return myLocArr.this(i);
    }
    return locArr(dom.dist.ind2locInd(i))(i);
  }

  def this(i:rank*idxType) var {
    return locArr(dom.dist.ind2locInd(i))(i);
  }

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

  def these(param tag: iterator) where tag == iterator.leader {
    const wholeLow = dom.whole.low,
          precomputedNumTasks = dom.dist.tasksPerLocale;
    coforall locDom in dom.locDoms do on locDom {
      const locBlock = locDom.myBlock - wholeLow;
      const numTasks = precomputedNumTasks;
      if numTasks == 1 {
        yield locBlock.getIndices();
      } else {
        if rank == 1 {
          coforall taskid in 0..#numTasks {
            const low = locBlock.low, high = locBlock.high;
            const (lo,hi) = _computeBlock(low, high - low + 1,
                                         low, high,
                                         numTasks, taskid);
            yield locBlock[lo..hi].getIndices();
          }
        } else {
          var ranges: rank*range(stridable=true);
          for param i in 2..rank do
            ranges(i) = locBlock.dim(i);
          coforall taskid in 0..#numTasks {
            const low = locBlock.dim(1).low, high = locBlock.dim(1).high;
            const (lo,hi) = _computeBlock(low, high - low + 1,
                                          low, high,
                                          numTasks, taskid);
            ranges(1) = locBlock.dim(1)(lo..hi);
            yield locBlock[(...ranges)].getIndices();
          }
        }
      }
    }
  }

  def supportsAlignedFollower() param return true;

  def these(param tag: iterator, follower, param aligned: bool = false) var where tag == iterator.follower {
    var t: rank*range(eltType=idxType, stridable=true);
    for param i in 1..rank {
      t(i) = follower(i) + dom.whole.dim(i).low;
    }
    const followThis = [(...t)];
    const arrSection = locArr(dom.dist.ind2locInd(followThis.low));
    if aligned {
      local {
        for i in followThis {
          yield arrSection.this(i);
        }
      }
    } else {
      def accessHelper(i) var {
        local {
          if myLocArr.locDom.myBlock.member(i) then
            return myLocArr.this(i);
        }
        return this(i);
      }

      for i in followThis {
        yield accessHelper(i);
      }
    }
  }

  def writeThis(f: Writer) {
    if verboseCyclicDistWriters {
      writeln(typeToString(this.type));
      writeln("------");
    }
    for i in dom {
      writeln(this(i));
    }
  }
}

class LocCyclicArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;

  const locDom: LocCyclicDom(rank, idxType, stridable);

  var myElems: [locDom.myBlock] eltType;

  def this(i:idxType) var {
    return myElems(i);
  }

  def this(i:rank*idxType) var {
    return myElems((...i));
  }

  def these() var {
    for elem in myElems {
      yield elem;
    }
  }
}


More test cleanup

More test cleanup

use FFTW;

proc printcmp(x, y) {
	var err = max reduce abs(x-y);
	writeln(err);
}

/* Read in the data and set up the domains appropriately */
param ndim : int = 1;
var dims : ndim*int(32);
var D : domain(ndim);
// Define ranges here
var rD,cD,reD,imD : domain(ndim,int,true);
var A,B,goodA,goodB : [D] fftw_complex;
{
	var f = open("arr1d.dat",iomode.r).reader(kind=iokind.little);
	for ii in 1..ndim {
		f.read(dims(ii));
	}
	D = 0.. #dims(1);
	for (r,c) in goodA {
		f.read(r);
		c = 0;
	}
	for (r,c) in goodB {
		f.read(r); f.read(c);
	}
	f.close();
}
// Set up domains based on dimension size
if (ndim==1) {
	// Domains for real FFT
	rD = 0.. #(2*(dims(1)/2 + 1)); // Padding to do in place transforms
	cD = 0.. #(dims(1)/2 + 1);
	// Define domains to extract the real and imaginary parts
	reD = rD[0..(2*(dims(1)/2)+1) by 2]; // Padding to do in place transforms
	imD = rD[1..(2*(dims(1)/2) + 1) by 2]; // Padding to do in place transforms
}

// FFTW does not normalize inverse transform, set up norm
var norm = * reduce dims;

// FFT testing here
var fwd = plan_dft_1d(dims, A[0],B[0],FFTW_FORWARD,FFTW_ESTIMATE);
var rev = plan_dft_1d(dims, B[0],A[0],FFTW_BACKWARD,FFTW_ESTIMATE);
// Test forward and reverse transform
A = goodA;
execute(fwd);
printcmp(B,goodB);
execute(rev);
A /= norm;
printcmp(A,goodA);
destroy_plan(fwd);
destroy_plan(rev);

// Test in-place transforms
fwd = plan_dft_1d(dims,A[0],A[0],FFTW_FORWARD,FFTW_ESTIMATE);
rev = plan_dft_1d(dims,A[0],A[0],FFTW_BACKWARD,FFTW_ESTIMATE);
A = goodA;
// Test forward and reverse transform
A = goodA;
execute(fwd);
printcmp(A,goodB);
execute(rev);
A /= norm; // FFTW does an unnormalized transform
printcmp(A,goodA);
destroy_plan(fwd);
destroy_plan(rev);

// Testing r2c and c2r
var rA : [rD] real(64);
var cB : [cD] fftw_complex;
fwd = plan_dft_r2c_1d(dims,rA[0],cB[0],FFTW_ESTIMATE);
rev = plan_dft_c2r_1d(dims,cB[0],rA[0],FFTW_ESTIMATE);
rA[D] = re(goodA);
execute(fwd);
printcmp(cB,goodB[cD]);
execute(rev);
rA /= norm;
printcmp(rA[D],re(goodA));
destroy_plan(fwd);
destroy_plan(rev);
// In place transform
fwd = plan_dft_r2c_1d(dims,rA[0],rA[0],FFTW_ESTIMATE);
rev = plan_dft_c2r_1d(dims,rA[0],rA[0],FFTW_ESTIMATE);
rA[D] = re(goodA);
execute(fwd);
printcmp(rA[reD],re(goodB[cD]));
printcmp(rA[imD],im(goodB[cD]));
execute(rev);
rA /= norm;
printcmp(rA[D],re(goodA));
destroy_plan(fwd);
destroy_plan(rev);

use FFTW;

var err : real;

/* Read in the data and set up the domains appropriately */
param ndim : int = 1;
var dims : ndim*int(32);
var D : domain(ndim);
// Define ranges here
var rD,cD,reD,imD : domain(ndim,int,true);
var A,B,goodA,goodB : [D] fftw_complex;
{
	var f = open("arr1d.dat",iomode.r).reader(kind=iokind.little);
	for ii in 1..ndim {
		f.read(dims(ii));
	}
	D = 0.. #dims(1);
	for (r,c) in goodA {
		f.read(r);
		c = 0;
	}
	for (r,c) in goodB {
		f.read(r); f.read(c);
	}
	f.close();
}
// Set up domains based on dimension size
if (ndim==1) {
	// Domains for real FFT
	rD = 0.. #(2*(dims(1)/2 + 1)); // Padding to do in place transforms
	cD = 0.. #(dims(1)/2 + 1);
	// Define domains to extract the real and imaginary parts
	reD = rD[0..(2*(dims(1)/2)+1) by 2]; // Padding to do in place transforms
	imD = rD[1..(2*(dims(1)/2) + 1) by 2]; // Padding to do in place transforms
}

// FFTW does not normalize inverse transform, set up norm
var norm = * reduce dims;

// FFT testing here
var fwd = plan_dft_1d(dims, A[0],B[0],FFTW_FORWARD,FFTW_ESTIMATE);
var rev = plan_dft_1d(dims, B[0],A[0],FFTW_BACKWARD,FFTW_ESTIMATE);
// Test forward and reverse transform
A = goodA;
execute(fwd);
err = max reduce abs(B-goodB);
writeln(err);
execute(rev);
A /= norm;
err = max reduce abs(A-goodA);
writeln(err);
destroy_plan(fwd);
destroy_plan(rev);

// Test in-place transforms
fwd = plan_dft_1d(dims,A[0],A[0],FFTW_FORWARD,FFTW_ESTIMATE);
rev = plan_dft_1d(dims,A[0],A[0],FFTW_BACKWARD,FFTW_ESTIMATE);
A = goodA;
// Test forward and reverse transform
A = goodA;
execute(fwd);
err = max reduce abs(A-goodB);
writeln(err);
execute(rev);
A /= * reduce dims; // FFTW does an unnormalized transform
err = max reduce abs(A-goodA);
writeln(err);
destroy_plan(fwd);
destroy_plan(rev);

// Testing r2c and c2r
var rA : [rD] real(64);
var cB : [cD] fftw_complex;
fwd = plan_dft_r2c_1d(dims,rA[0],cB[0],FFTW_ESTIMATE);
rev = plan_dft_c2r_1d(dims,cB[0],rA[0],FFTW_ESTIMATE);
rA[D] = re(goodA);
execute(fwd);
err = max reduce abs(cB - goodB[cD]);
writeln(err);
execute(rev);
rA /= norm;
err = max reduce abs(rA[D] - re(goodA));
writeln(err);
destroy_plan(fwd);
destroy_plan(rev);
// In place transform
fwd = plan_dft_r2c_1d(dims,rA[0],rA[0],FFTW_ESTIMATE);
rev = plan_dft_c2r_1d(dims,rA[0],rA[0],FFTW_ESTIMATE);
rA[D] = re(goodA);
execute(fwd);
err = (max reduce abs(rA[reD] - re(goodB[cD]))) + (max reduce abs(rA[imD] - im(goodB[cD])));
writeln(err);
execute(rev);
rA /= norm;
err = max reduce abs(rA[D] - re(goodA));
writeln(err);
destroy_plan(fwd);
destroy_plan(rev);


Backing out Lee's commit due to the code freeze.

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

use DSIUtil;
config param debugChapelRange = false;

//
// range type
//
//   parameterized by an integral element type, by whether low and/or
//   high bounds exist, and by whether the stride is one or not
//
enum BoundedRangeType { bounded, boundedLow, boundedHigh, boundedNone };

pragma "range"
record range {
  type idxType = int;                            // element type
  param boundedType: BoundedRangeType = BoundedRangeType.bounded; // bounded or not
  param stridable: bool = false;                 // range can be strided
  var _low: idxType = 1;                         // lower bound
  var _high: idxType = 0;                        // upper bound
  var _stride: chpl__idxTypeToStrType(idxType) = 1;    // signed stride of range
  var _promotionType : idxType;                  // enables promotion

  pragma "inline" def low return _low;       // public getter for low bound
  pragma "inline" def high return _high;     // public getter for high bound
  pragma "inline" def stride return _stride; // public getter for stride
}

////////////////////////////////////////////////////////////////////////////////
// SYNTAX FUNCTIONS

//
// syntax function for bounded ranges
//
def _build_range(low: int, high: int)
  return new range(int, BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint, high: uint)
  return new range(uint, BoundedRangeType.bounded, false, low, high);
def _build_range(low: int(64), high: int(64))
  return new range(int(64), BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint(64), high: uint(64))
  return new range(uint(64), BoundedRangeType.bounded, false, low, high);

//
// syntax function for unbounded ranges
//
def _build_range(param bt: BoundedRangeType, bound: int)
  return new range(int, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint)
  return new range(uint, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: int(64))
  return new range(int(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint(64))
  return new range(uint(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType)
  return new range(int, bt, false);

//
// syntax function for by-expressions
//
def chpl__byHelp(r : range(?et, ?bt, ?sbl), str : chpl__idxTypeToStrType(et)) {
  if str == 0 then
    halt("range cannot be strided by zero");
  if r.boundedType == BoundedRangeType.boundedNone then
    halt("unbounded range cannot be strided");
  var result = new range(r.idxType, r.boundedType, true, r.low, r.high, r.stride*str);
  if r.low > r.high then
    return result;
  if result.stride < 0 then
    result._alignLow(result.high);
  else
    result._alignHigh(result.low);
  return result;
}

def chpl__legalIntCoerce(type t1, type t2) param {
  if (_isSignedType(t2)) {
    if (_isSignedType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (_isSignedType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

def by(r : range(?), str) {
  type idxType = r.idxType;
  type strType = chpl__idxTypeToStrType(idxType);
  type argType = str.type;
  if (argType == strType) {
    return chpl__byHelp(r, str);
  } else if (argType == idxType || chpl__legalIntCoerce(argType, strType)) {
    return chpl__byHelp(r, str:strType);
  } else {
    compilerError("type mismatch applying 'by' to range(",
                  typeToString(idxType), ") with ", typeToString(argType));
  }
}

//
// syntax functions for counted ranges
//
def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedLow {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.low + 1,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.low,
                   _high = r.low + (i-1)*abs(r.stride):resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedHigh {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high,
                   _high = r.high - 1,
                   _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high - (i-1)*abs(r.stride):resultType,
                   _high = r.high,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.bounded {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i > r.length then
    halt("bounded range is too small to access ", i, " elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.high,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = if r.stride < 0 then r.high + (i-1)*r.stride:resultType else r.low,
                   _high = if r.stride < 0 then r.high else r.low + (i-1)*r.stride:resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedNone {
  compilerError("count operator is not defined for unbounded ranges");
}

////////////////////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS

//
// return true if this range has a low bound
//
def range._hasLow() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedLow;

//
// return true if this range has a high bound
//
def range._hasHigh() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedHigh;

//
// Return the number in the range 0 <= result < b that is congruent to a (mod b)
//
def mod(a:integral, b:integral) {
  if (b <= 0) then
    halt("modulus divisor must be positive");
  var tmp = a % b:a.type;
  return if tmp < 0 then b:a.type + tmp else tmp;
}

//
// align low bound of this range to an alignment; snap up
//
def range._alignLow(alignment: idxType) {
  var s = abs(stride):idxType;
  // The following is equivalent to var d = abs(alignment - low) % s;
  // except that it avoids problems with an overflow in the subtraction.
  // It uses the fact that if a1 == b1 mod n and a2 == b2 mod n then
  // a1 - a2 == b1 - b2 mod n and the fact that abs(a-b) is a-b if a > b
  // and is b-a when b >= a.
  var d = if(alignment > low) then
    mod(mod(alignment,s):int - mod(low,s):int, s):idxType
  else
    mod(mod(low,s):int - mod(alignment, s):int, s):idxType;
  if d != 0 {
    if low < alignment then
      _low += d;
    else
      _low += s - d;
  }
}

//
// align high bound of this range to an alignment; snap down
//
def range._alignHigh(alignment: idxType) {
  var s = abs(stride):idxType;
  // See the note about this declaration in range._alignLow. It is like
  // var d = abs(alignment - high) % s; but avoids overflow problems.
  var d = if(alignment > high) then
    mod(mod(alignment,s):int - mod(high,s):int, s):idxType
  else
    mod(mod(high,s):int - mod(alignment,s):int,s):idxType;
  if d != 0 {
    if high > alignment then
      _high -= d;
    else
      _high -= s - d;
  }
}

//
// range assignment
//
def =(r1: range(stridable=?s1), r2: range(stridable=?s2)) {
  if r1.boundedType != r2.boundedType then
    compilerError("type mismatch in assignment of ranges with different boundedType parameters");
  if !s1 && s2 then
    if r2.stride != 1 then
      halt("non-stridable range assigned non-unit stride");
  r1._low = r2.low;
  r1._high = r2.high;
  r1._stride = r2.stride;
  return r1;
}

////////////////////////////////////////////////////////////////////////////////
// EXTERNAL FUNCTIONS

//
// return the intersection of this and other
//
def range.this(other: range(?idxType2, ?boundedType, ?stridable)) {
  //
  // determine boundedType of result
  //
  def computeBoundedType(r1, r2) param {
    param low = r1._hasLow() || r2._hasLow();
    param high = r1._hasHigh() || r2._hasHigh();
    if low && high then
      return BoundedRangeType.bounded;
    else if low then
      return BoundedRangeType.boundedLow;
    else if high then
      return BoundedRangeType.boundedHigh;
    else
      return BoundedRangeType.boundedNone;
  }

  //
  // returns (gcd(u, v), x) where x is set such that u*x + v*y =
  // gcd(u, v) assuming u and v are non-negative
  //
  // source: Knuth Volume 2 --- Section 4.5.2
  //
  def extendedEuclidHelp(u, v) {
    var zero: u.type = 0;
    var one: u.type = 1;
    var U = (one, zero, u);
    var V = (zero, one, v);
    while V(3) != 0 {
      (U, V) = let q = U(3)/V(3) in (V, U - V * (q, q, q));
    }
    return (U(3), U(1));
  }

  def extendedEuclid(u:int, v:int) {
    return extendedEuclidHelp(u,v);
  }

  def extendedEuclid(u:int(64), v:int(64)) {
    return extendedEuclidHelp(u,v);
  }

  var lo1 = if _hasLow() then this.low:idxType else other.low;
  var hi1 = if _hasHigh() then this.high:idxType else other.high;
  var st1 = abs(this.stride);
  var al1 = if this.stride < 0 then hi1 else lo1;

  var lo2 = if other._hasLow() then other.low else this.low:idxType;
  var hi2 = if other._hasHigh() then other.high else this.high:idxType;
  var st2 = abs(other.stride);
  var al2 = if this.stride < 0 then hi2 else lo2;

  var (g, x) = extendedEuclid(st1, st2);

  var result = new range(idxType,
                         computeBoundedType(this, other),
                         this.stridable | other.stridable,
                         max(lo1, lo2):idxType,
                         min(hi1, hi2):idxType,
                         (st1 * (st2 / g)):chpl__idxTypeToStrType(idxType));
  if lo1 > hi1 || lo2 > hi2 {
    // empty intersection
    if result.low < result.high then
      result._low <=> result._high;
    else if result.high == result.low then
      (result._low, result._high) = (1:idxType, 0:idxType);
    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;
    result._alignLow(al);
    result._alignHigh(al);
  } else if abs(lo1 - lo2) % g:idxType != 0 {
    // empty intersection, return degenerate result
    result._low <=> result._high;
  } else {
    // non-empty intersection

    if other.stride < 0 then
      result._stride = -result.stride;

    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;

    result._alignLow(al);
    result._alignHigh(al);
  }

  return result;
}

//
// default iterator optimized for unit stride
//
def range.these() {
  if boundedType != BoundedRangeType.bounded {
    if boundedType == BoundedRangeType.boundedNone then
      halt("iteration over a range with no bounds");
    if stridable {
      if boundedType == BoundedRangeType.boundedLow then
        if stride < 0 then
          halt("iteration over range with negative stride but no high bound");
      if boundedType == BoundedRangeType.boundedHigh then
        if stride > 0 then
          halt("iteration over range with positive stride but no low bound");
      var i = if stride > 0 then low else high;
      while true {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      if boundedType == BoundedRangeType.boundedHigh then
        halt("iteration over range with positive stride but no low bound");
      var i = low;
      while true {
        yield i;
        i = i + 1;
      }
    }
  } else {
    var i, last: idxType;
    if stridable {
      if stride > 0 {
        i = low;
        last = if i > high then low else high + stride:idxType;
      } else {
        i = high;
        last = if i < low then high else low + stride:idxType;
      }
      while i != last {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      i = low;
      last = if i > high then low else high + 1;
      while i != last {
        yield i;
        i = i + 1;
      }
    }
  }
}

def range.these(param tag: iterator) where tag == iterator.leader {
  // want "yield 0..length-1;"
  // but compilerError in length causes a problem because leaders are
  // resolved wherever an iterator is.
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if debugChapelRange then
    writeln("*** In range leader:"); // ", this);

  var v: idxType;
  if stride > 0 then
    v = (high - low) / stride:idxType + 1;
  else
    v = (low - high) / stride:idxType + 1;
  if v < 0 then
    v = 0;

  var numChunks = _computeNumChunks(v);
  if debugChapelRange then
    writeln("*** RI: length=", v, " numChunks=", numChunks);

  if debugChapelRange then
    writeln("*** RI: Using ", numChunks, " chunk(s)");
  if numChunks == 1 {
    yield tuple(0..v-1);
  } else {
    coforall chunk in 0..numChunks-1 {
      const (lo,hi) = _computeBlock(v, numChunks, chunk, v-1);
      if debugChapelRange then
        writeln("*** RI: tuple = ", tuple(lo..hi));
      yield tuple(lo..hi);
    }
  }
}

def range.these(param tag: iterator, follower) where tag == iterator.follower {
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if follower.size != 1 then
    halt("iteration over a range with multi-dimensional iterator");
  if debugChapelRange then
    writeln("In range follower code: Following ", follower);
  var followThis = follower(1);
  if stridable {
    var r = if stride > 0 then
        low+followThis.low*stride:idxType..low+followThis.high*stride:idxType by stride by followThis.stride
      else
        high+followThis.high*stride:idxType..high+followThis.low*stride:idxType by stride by followThis.stride;
    for i in r do
      yield i;
  } else {
    var r = low+followThis;
    for i in r do
      yield i;
  }
}

//
// returns the number of elements in this range
//
def range.length {
  if boundedType != BoundedRangeType.bounded then
    compilerError("unbounded range has infinite length");
  if low > high then
    return 0:idxType;
  const retVal = if stride > 0
               then (high - low) / stride:idxType + 1
               else (low - high) / stride:idxType + 1;
  return if (retVal < 0) then 0 else retVal;
}

//
// returns true if i is in this range
//
def range.member(i: idxType) {
  if stridable {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high && (high - i) % abs(stride):idxType == 0;
    } else {
      return true;
    }
  } else {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high;
    } else {
      return true;
    }
  }
}

def range.indexOrder(i: idxType) {
  if (!member(i)) then return (-1):idxType;
  return (i-low)/abs(stride);
}

//
// returns true if other is in bounds of this for all specified
// bounds; these functions are used to determine if an array slice is
// valid.  We break out the boundedNone case in order to permit
// unbounded ranges to slice ranges of various index types -- otherwise
// we get a compiler error in the boundsCheck function.
//
def range.boundsCheck(other: range(?e,?b,?s)) where b == BoundedRangeType.boundedNone {
  return true;
}

def range.boundsCheck(other: range(?e,?b,?s)) {
  var boundedOther: range(idxType,BoundedRangeType.bounded,s||this.stridable);
  if other._hasLow() then
    boundedOther._low = other.low;
  else
    boundedOther._low = low;
  if other._hasHigh() then
    boundedOther._high = other.high;
  else
    boundedOther._high = high;
  boundedOther._stride = other.stride;

  return (boundedOther.length == 0) || (this(boundedOther) == boundedOther);
}

def range.boundsCheck(other: idxType)
  return member(other);

//
// returns true if every i in other is in this range
//
def range.member(other: range(?))
  return this(other) == other;

//
// write implementation for ranges
//
def range.writeThis(f: Writer) {
  if _hasLow() then
    f.write(low);
  f.write("..");
  if _hasHigh() then
    f.write(high);
  if stride != 1 then
    f.write(" by ", stride);
}

//
// translate the indices in this range by i
//
def range.translate(i: idxType)
  return this + i;

//
// intended for internal use only:
//
def range.chpl__unTranslate(i: idxType) {
  return this - i;
}

//
// return an interior portion of this range
//
def range.interior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("interior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low, low-1-i, stride);
  else
    return new range(idxType, boundedType, stridable, high+1-i, high, stride);
}

//
// return an exterior portion of this range
//
def range.exterior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("exterior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low+i, low-1, stride);
  else
    return new range(idxType, boundedType, stridable, high+1, high+i, stride);
}

//
// returns an expanded range, or a contracted range if i < 0
//
def range.expand(i: idxType)
  return new range(idxType, boundedType, stridable, low-i, high+i, stride);

//
// range op scalar and scalar op range arithmetic
//
def +(r: range(?e,?b,?s), i: integral)
  return new range((r.low+i).type, b, s, r.low+i, r.high+i, r.stride);

def +(i:integral, r: range(?e,?b,?s))
  return new range((i+r.low).type, b, s, i+r.low, i+r.high, r.stride);

def -(r: range(?e,?b,?s), i: integral)
  return new range((r.low-i).type, b, s, r.low-i, r.high-i, r.stride);

//
// return a substring of a string with a range of indices
//
pragma "inline" def string.substring(s: range(?e,?b,?st)) {
  if s.boundedType != BoundedRangeType.bounded then
    compilerError("substring indexing undefined on unbounded ranges");
  if s.stride != 1 then
    return __primitive("string_strided_select", this, s.low, s.high, s.stride);
  else
    return __primitive("string_select", this, s.low, s.high);
}

use DSIUtil;
config param debugChapelRange = false;

//
// range type
//
//   parameterized by an integral element type, by whether low and/or
//   high bounds exist, and by whether the stride is one or not
//
enum BoundedRangeType { bounded, boundedLow, boundedHigh, boundedNone };

pragma "range"
record range {
  type idxType = int;                            // element type
  param boundedType: BoundedRangeType = BoundedRangeType.bounded; // bounded or not
  param stridable: bool = false;                 // range can be strided
  var _low: idxType = 1;                         // lower bound
  var _high: idxType = 0;                        // upper bound
  var _stride: chpl__idxTypeToStrType(idxType) = 1;    // signed stride of range
  var _promotionType : idxType;                  // enables promotion

  pragma "inline" def low return _low;       // public getter for low bound
  pragma "inline" def high return _high;     // public getter for high bound
  pragma "inline" def stride return _stride; // public getter for stride
}

////////////////////////////////////////////////////////////////////////////////
// SYNTAX FUNCTIONS

//
// syntax function for bounded ranges
//
def _build_range(low: int, high: int)
  return new range(int, BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint, high: uint)
  return new range(uint, BoundedRangeType.bounded, false, low, high);
def _build_range(low: int(64), high: int(64))
  return new range(int(64), BoundedRangeType.bounded, false, low, high);
def _build_range(low: uint(64), high: uint(64))
  return new range(uint(64), BoundedRangeType.bounded, false, low, high);

//
// syntax function for unbounded ranges
//
def _build_range(param bt: BoundedRangeType, bound: int)
  return new range(int, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint)
  return new range(uint, bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: int(64))
  return new range(int(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType, bound: uint(64))
  return new range(uint(64), bt, false, bound, bound);
def _build_range(param bt: BoundedRangeType)
  return new range(int, bt, false);

//
// syntax function for by-expressions
//
def chpl__byHelp(r : range(?et, ?bt, ?sbl), str : chpl__idxTypeToStrType(et)) {
  if str == 0 then
    halt("range cannot be strided by zero");
  if r.boundedType == BoundedRangeType.boundedNone then
    halt("unbounded range cannot be strided");
  var result = new range(r.idxType, r.boundedType, true, r.low, r.high, r.stride*str);
  if r.low > r.high then
    return result;
  if result.stride < 0 then
    result._alignLow(result.high);
  else
    result._alignHigh(result.low);
  return result;
}

def chpl__legalIntCoerce(type t1, type t2) param {
  if (_isSignedType(t2)) {
    if (_isSignedType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (_isSignedType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

def by(r : range(?), str) {
  type idxType = r.idxType;
  type strType = chpl__idxTypeToStrType(idxType);
  type argType = str.type;
  if (argType == strType) {
    return chpl__byHelp(r, str);
  } else if (argType == idxType || chpl__legalIntCoerce(argType, strType)) {
    return chpl__byHelp(r, str:strType);
  } else {
    compilerError("type mismatch applying 'by' to range(",
                  typeToString(idxType), ") with ", typeToString(argType));
  }
}

//
// syntax functions for counted ranges
//
def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedLow {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.low + 1,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.low,
                   _high = r.low + (i-1)*abs(r.stride):resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedHigh {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i == 0 then
    return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high,
                   _high = r.high - 1,
                   _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = r.high - (i-1)*abs(r.stride):resultType,
                   _high = r.high,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.bounded {
  type resultType = (r.low + i).type;
  if i < 0 then
    halt("range cannot have a negative number of elements");
  if i > r.length then
    halt("bounded range is too small to access ", i, " elements");
  if i == 0 then
    return new range(idxType = resultType,
                     boundedType = BoundedRangeType.bounded,
                     stridable = r.stridable,
                     _low = r.high,
                     _high = r.low,
                     _stride = r.stride);

  return new range(idxType = resultType,
                   boundedType = BoundedRangeType.bounded,
                   stridable = r.stridable,
                   _low = if r.stride < 0 then r.high + (i-1)*r.stride:resultType else r.low,
                   _high = if r.stride < 0 then r.high else r.low + (i-1)*r.stride:resultType,
                   _stride = r.stride);
}

def #(r:range(?), i:integral)
  where r.boundedType == BoundedRangeType.boundedNone {
  compilerError("count operator is not defined for unbounded ranges");
}

////////////////////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS

//
// return true if this range has a low bound
//
def range._hasLow() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedLow;

//
// return true if this range has a high bound
//
def range._hasHigh() param
  return boundedType == BoundedRangeType.bounded || boundedType == BoundedRangeType.boundedHigh;

//
// Return the number in the range 0 <= result < b that is congruent to a (mod b)
//
def mod(a:integral, b:integral) {
  if (b <= 0) then
    halt("modulus divisor must be positive");
  var tmp = a % b:a.type;
  return if tmp < 0 then b:a.type + tmp else tmp;
}

//
// align low bound of this range to an alignment; snap up
//
def range._alignLow(alignment: idxType) {
  var s = abs(stride):idxType;
  // The following is equivalent to var d = abs(alignment - low) % s;
  // except that it avoids problems with an overflow in the subtraction.
  // It uses the fact that if a1 == b1 mod n and a2 == b2 mod n then
  // a1 - a2 == b1 - b2 mod n and the fact that abs(a-b) is a-b if a > b
  // and is b-a when b >= a.
  var d = if(alignment > low) then
    mod(mod(alignment,s):int - mod(low,s):int, s):idxType
  else
    mod(mod(low,s):int - mod(alignment, s):int, s):idxType;
  if d != 0 {
    if low < alignment then
      _low += d;
    else
      _low += s - d;
  }
}

//
// align high bound of this range to an alignment; snap down
//
def range._alignHigh(alignment: idxType) {
  var s = abs(stride):idxType;
  // See the note about this declaration in range._alignLow. It is like
  // var d = abs(alignment - high) % s; but avoids overflow problems.
  var d = if(alignment > high) then
    mod(mod(alignment,s):int - mod(high,s):int, s):idxType
  else
    mod(mod(high,s):int - mod(alignment,s):int,s):idxType;
  if d != 0 {
    if high > alignment then
      _high -= d;
    else
      _high -= s - d;
  }
}

//
// range assignment
//
def =(r1: range(stridable=?s1), r2: range(stridable=?s2)) {
  if r1.boundedType != r2.boundedType then
    compilerError("type mismatch in assignment of ranges with different boundedType parameters");
  if !s1 && s2 then
    if r2.stride != 1 then
      halt("non-stridable range assigned non-unit stride");
  r1._low = r2.low;
  r1._high = r2.high;
  r1._stride = r2.stride;
  return r1;
}

////////////////////////////////////////////////////////////////////////////////
// EXTERNAL FUNCTIONS

//
// return the intersection of this and other
//
def range.this(other: range(?idxType2, ?boundedType, ?stridable)) {
  //
  // determine boundedType of result
  //
  def computeBoundedType(r1, r2) param {
    param low = r1._hasLow() || r2._hasLow();
    param high = r1._hasHigh() || r2._hasHigh();
    if low && high then
      return BoundedRangeType.bounded;
    else if low then
      return BoundedRangeType.boundedLow;
    else if high then
      return BoundedRangeType.boundedHigh;
    else
      return BoundedRangeType.boundedNone;
  }

  //
  // returns (gcd(u, v), x) where x is set such that u*x + v*y =
  // gcd(u, v) assuming u and v are non-negative
  //
  // source: Knuth Volume 2 --- Section 4.5.2
  //
  def extendedEuclidHelp(u, v) {
    var zero: u.type = 0;
    var one: u.type = 1;
    var U = (one, zero, u);
    var V = (zero, one, v);
    while V(3) != 0 {
      (U, V) = let q = U(3)/V(3) in (V, U - V * (q, q, q));
    }
    return (U(3), U(1));
  }

  def extendedEuclid(u:int, v:int) {
    return extendedEuclidHelp(u,v);
  }

  def extendedEuclid(u:int(64), v:int(64)) {
    return extendedEuclidHelp(u,v);
  }

  var lo1 = if _hasLow() then this.low:idxType else other.low;
  var hi1 = if _hasHigh() then this.high:idxType else other.high;
  var st1 = abs(this.stride);
  var al1 = if this.stride < 0 then hi1 else lo1;

  var lo2 = if other._hasLow() then other.low else this.low:idxType;
  var hi2 = if other._hasHigh() then other.high else this.high:idxType;
  var st2 = abs(other.stride);
  var al2 = if this.stride < 0 then hi2 else lo2;

  var (g, x) = extendedEuclid(st1, st2);

  var result = new range(idxType,
                         computeBoundedType(this, other),
                         this.stridable | other.stridable,
                         max(lo1, lo2):idxType,
                         min(hi1, hi2):idxType,
                         (st1 * (st2 / g)):chpl__idxTypeToStrType(idxType));
  if lo1 > hi1 || lo2 > hi2 {
    // empty intersection
    if result.low < result.high then
      result._low <=> result._high;
    else if result.high == result.low then
      (result._low, result._high) = (1:idxType, 0:idxType);
    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;
    result._alignLow(al);
    result._alignHigh(al);
  } else if abs(lo1 - lo2) % g:idxType != 0 {
    // empty intersection, return degenerate result
    result._low <=> result._high;
  } else {
    // non-empty intersection

    if other.stride < 0 then
      result._stride = -result.stride;

    var al = al1 + (al2:idxType - al1) * x:idxType * st1:idxType / g:idxType;

    result._alignLow(al);
    result._alignHigh(al);
  }

  return result;
}

//
// default iterator optimized for unit stride
//
def range.these() {
  if boundedType != BoundedRangeType.bounded {
    if boundedType == BoundedRangeType.boundedNone then
      halt("iteration over a range with no bounds");
    if stridable {
      if boundedType == BoundedRangeType.boundedLow then
        if stride < 0 then
          halt("iteration over range with negative stride but no high bound");
      if boundedType == BoundedRangeType.boundedHigh then
        if stride > 0 then
          halt("iteration over range with positive stride but no low bound");
      var i = if stride > 0 then low else high;
      while true {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      if boundedType == BoundedRangeType.boundedHigh then
        halt("iteration over range with positive stride but no low bound");
      var i = low;
      while true {
        yield i;
        i = i + 1;
      }
    }
  } else {
    var i, last: idxType;
    if stridable {
      if stride > 0 {
        i = low;
        last = if i > high then low else high + stride:idxType;
      } else {
        i = high;
        last = if i < low then high else low + stride:idxType;
      }
      while i != last {
        yield i;
        i = i + stride:idxType;
      }
    } else {
      i = low;
      last = if i > high then low else high + 1;
      while i != last {
        yield i;
        i = i + 1;
      }
    }
  }
}

def range.these(param tag: iterator) where tag == iterator.leader {
  // want "yield 0..length-1;"
  // but compilerError in length causes a problem because leaders are
  // resolved wherever an iterator is.
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if debugChapelRange then
    writeln("*** In range leader:"); // ", this);

  var v: idxType;
  if stride > 0 then
    v = (high - low) / stride:idxType + 1;
  else
    v = (low - high) / stride:idxType + 1;
  if v < 0 then
    v = 0;

  var numChunks = _computeNumChunks(v);
  if debugChapelRange then
    writeln("*** RI: length=", v, " numChunks=", numChunks);

  if debugChapelRange then
    writeln("*** RI: Using ", numChunks, " chunk(s)");
  if (CHPL_TARGET_PLATFORM != "xmt") {
    if numChunks == 1 {
      yield tuple(0..v-1);
    } else {
      coforall chunk in 0..numChunks-1 {
        const (lo,hi) = _computeBlock(v, numChunks, chunk, v-1);
        if debugChapelRange then
          writeln("*** RI: tuple = ", tuple(lo..hi));
        yield tuple(lo..hi);
      }
    }
  } else {

    var per_stream_i: uint(64) = 0;
    var total_streams_n: uint(64) = 0;

    __primitive_loop("xmt pragma forall i in n", per_stream_i,
                     total_streams_n) {
      const (lo,hi) = _computeBlock(v, total_streams_n, per_stream_i, v-1);
      yield tuple(lo..hi);
    }
  }
}

def range.these(param tag: iterator, follower) where tag == iterator.follower {
  if boundedType == BoundedRangeType.boundedNone then
    halt("iteration over a range with no bounds");
  if follower.size != 1 then
    halt("iteration over a range with multi-dimensional iterator");
  if debugChapelRange then
    writeln("In range follower code: Following ", follower);
  var followThis = follower(1);
  if stridable {
    var r = if stride > 0 then
        low+followThis.low*stride:idxType..low+followThis.high*stride:idxType by stride by followThis.stride
      else
        high+followThis.high*stride:idxType..high+followThis.low*stride:idxType by stride by followThis.stride;
    for i in r do
      yield i;
  } else {
    var r = low+followThis;
    for i in r do
      yield i;
  }
}

//
// returns the number of elements in this range
//
def range.length {
  if boundedType != BoundedRangeType.bounded then
    compilerError("unbounded range has infinite length");
  if low > high then
    return 0:idxType;
  const retVal = if stride > 0
               then (high - low) / stride:idxType + 1
               else (low - high) / stride:idxType + 1;
  return if (retVal < 0) then 0 else retVal;
}

//
// returns true if i is in this range
//
def range.member(i: idxType) {
  if stridable {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low && (i - low) % abs(stride):idxType == 0;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high && (high - i) % abs(stride):idxType == 0;
    } else {
      return true;
    }
  } else {
    if boundedType == BoundedRangeType.bounded {
      return i >= low && i <= high;
    } else if boundedType == BoundedRangeType.boundedLow {
      return i >= low;
    } else if boundedType == BoundedRangeType.boundedHigh {
      return i <= high;
    } else {
      return true;
    }
  }
}

def range.indexOrder(i: idxType) {
  if (!member(i)) then return (-1):idxType;
  return (i-low)/abs(stride);
}

//
// returns true if other is in bounds of this for all specified
// bounds; these functions are used to determine if an array slice is
// valid.  We break out the boundedNone case in order to permit
// unbounded ranges to slice ranges of various index types -- otherwise
// we get a compiler error in the boundsCheck function.
//
def range.boundsCheck(other: range(?e,?b,?s)) where b == BoundedRangeType.boundedNone {
  return true;
}

def range.boundsCheck(other: range(?e,?b,?s)) {
  var boundedOther: range(idxType,BoundedRangeType.bounded,s||this.stridable);
  if other._hasLow() then
    boundedOther._low = other.low;
  else
    boundedOther._low = low;
  if other._hasHigh() then
    boundedOther._high = other.high;
  else
    boundedOther._high = high;
  boundedOther._stride = other.stride;

  return (boundedOther.length == 0) || (this(boundedOther) == boundedOther);
}

def range.boundsCheck(other: idxType)
  return member(other);

//
// returns true if every i in other is in this range
//
def range.member(other: range(?))
  return this(other) == other;

//
// write implementation for ranges
//
def range.writeThis(f: Writer) {
  if _hasLow() then
    f.write(low);
  f.write("..");
  if _hasHigh() then
    f.write(high);
  if stride != 1 then
    f.write(" by ", stride);
}

//
// translate the indices in this range by i
//
def range.translate(i: idxType)
  return this + i;

//
// intended for internal use only:
//
def range.chpl__unTranslate(i: idxType) {
  return this - i;
}

//
// return an interior portion of this range
//
def range.interior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("interior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low, low-1-i, stride);
  else
    return new range(idxType, boundedType, stridable, high+1-i, high, stride);
}

//
// return an exterior portion of this range
//
def range.exterior(i: idxType) {
  if boundedType != BoundedRangeType.bounded then
    compilerError("exterior is not supported on unbounded ranges");
  if i == 0 then
    return this;
  else if i < 0 then
    return new range(idxType, boundedType, stridable, low+i, low-1, stride);
  else
    return new range(idxType, boundedType, stridable, high+1, high+i, stride);
}

//
// returns an expanded range, or a contracted range if i < 0
//
def range.expand(i: idxType)
  return new range(idxType, boundedType, stridable, low-i, high+i, stride);

//
// range op scalar and scalar op range arithmetic
//
def +(r: range(?e,?b,?s), i: integral)
  return new range((r.low+i).type, b, s, r.low+i, r.high+i, r.stride);

def +(i:integral, r: range(?e,?b,?s))
  return new range((i+r.low).type, b, s, i+r.low, i+r.high, r.stride);

def -(r: range(?e,?b,?s), i: integral)
  return new range((r.low-i).type, b, s, r.low-i, r.high-i, r.stride);

//
// return a substring of a string with a range of indices
//
pragma "inline" def string.substring(s: range(?e,?b,?st)) {
  if s.boundedType != BoundedRangeType.bounded then
    compilerError("substring indexing undefined on unbounded ranges");
  if s.stride != 1 then
    return __primitive("string_strided_select", this, s.low, s.high, s.stride);
  else
    return __primitive("string_select", this, s.low, s.high);
}


Made _build_domain(arg) function (which implements [arg] via the
parser) require arg to be a domain.  This function is overloaded for
the other cases (e.g. ranges) but this case was generic for the domain
case.  With increased robustness elsewhere, I made this change and
removed the comment asking why this could not be done.

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

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

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

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

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

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

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

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

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

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

def _build_domain(x: _domain)
  return x;

def _build_domain(ranges: range(?eltType,bounded,?stridable) ...?rank) {
  var d: domain(rank, eltType(1), _any_stridable(ranges));
  d.setIndices(ranges);
  return d;
}

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

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

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

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

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

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

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

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

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

  def this(d: _domain)
    return _domain(rank, _value.slice(d._value));

  def this(ranges: range(?eltType,?boundedType,?stridable) ...rank) {
    param stridable = _any_stridable(ranges);
    var newRanges: rank*range(_value.idxType, bounded, stridable);
    for param j in 1..rank {
      newRanges(j) = _value.dim(j)(ranges(j));
    }
    var x = _value.dist.buildDomain(rank, _value.idxType, stridable);
    x.setIndices(newRanges);
    return this(_domain(rank, x));
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  pragma "valid lvalue"
  def _dom
    return _domain(rank, _value.dom);

  pragma "valid lvalue"
  def this(d: _domain) {
    _value.checkSlice(d._value);
    var x = _value.slice(_dom(d)._value);
    return _array(_dim_index_type, eltType, rank, x);
  }

  pragma "valid lvalue"
  def this(rs: range(?_eltType,?boundedType,?stridable) ...rank) {
    _value.checkSlice(rs);
    var x = _value.slice(_dom((...rs))._value);
    return _array(_dim_index_type, eltType, rank, x);
  }

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

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

  def this(i: _dim_index_type) var where rank == 1
    return _value(i);

  def validRankChangeArguments(t) param {
    def validRankChangeArgument(r: range(?eltType,?boundedType,?stridable)) param
      return true;

    def validRankChangeArgument(i: _dim_index_type) param
      return true;

    def validRankChangeArgument(x) param
      return false;

    def help(param dim: int) param {
      if !validRankChangeArgument(t(dim)) then
        return false;
      else if dim < t.size then
        return help(dim+1);
      else
        return true;
    }
    return help(1);
  }

  pragma "valid lvalue"
  def this(irs ...rank) where validRankChangeArguments(irs) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;
    def _tupleize(x) {
      var y: 1*x.type;
      y(1) = x;
      return y;
    }
    def collectRanges(param dim: int) {
      if isRange(irs(dim))
        return collectRanges(dim+1, _tupleize(irs(dim)));
      else
        return collectRanges(dim+1);
    }
    def collectRanges(param dim: int, x: _tuple) {
      if dim > irs.size {
        return x;
      } else if dim < irs.size {
        if isRange(irs(dim)) then
          return collectRanges(dim+1, ((...x), irs(dim)));
        else
          return collectRanges(dim+1, x);
      } else {
        if isRange(irs(dim)) then
          return ((...x), irs(dim));
        else
          return x;
      }
    }
    var rs = collectRanges(1);
    var x = _value.rankChange(rs.size, _any_stridable(rs), irs, rs);
    return _array(_dim_index_type, eltType, rs.size, x);
  }

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

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

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

  def reindex(d: _domain) where rank != 1 {
    var x = _value.reindex(d._value);
    type xDimIndexType = x.idxType;       // BLC: hacks to get around
    type xIndexType = rank*xDimIndexType;  // inflexibility of tuple syntax
    return _array(x.idxType, eltType, rank, x);
  }

  def IRV var {
    return _value.IRV;
  }
}

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

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

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

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

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

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

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

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

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

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

  def sparseShiftArray(shiftrange, initrange) {
    halt("sparseGrowDomain not supported for non-sparse arrays");
  }
}

class BaseDomain {
  var _arrs: list of BaseArray;

  def member(ind) : bool {
    halt("membership test not supported for this domain type");
    return false;
  }
}

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

class BaseSparseArithmeticDomain : BaseArithmeticDomain {
}

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

def _copy(ic: _iteratorClass) {
  var capacity = 4, size = 0;
  var data = _ddata(__primitive("get_ic_type", ic), capacity);
  data.init();

  for e in ic {
    if size == capacity {
      capacity = capacity * 2;
      var tmp = _ddata(__primitive("get_ic_type", ic), capacity);
      tmp.init();
      for i in 0..size-1 do
        tmp(i) = data(i);
      data = tmp;
    }
    data(size) = e;
    size = size + 1;
  }

  var A: [1..size] __primitive("get_ic_type", ic);
  for i in 0..size-1 do
    A(i+1) = data(i);
  return A;
}

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

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

/*  This might be preferable for domains, but it doesn't work because
    domains apparently can't be modified once sent into a function!?

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

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

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

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

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

def _build_sparse_subdomain_type(dist, parentDom) {
  var x = dist.buildSparseDomain(parentDom.rank, parentDom._value.idxType, parentDom._value);
  return _domain(parentDom.rank, x);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  def this(d: _domain)
    return _domain(rank, _value.slice(d._value));

  def this(ranges: range(?eltType,?boundedType,?stridable) ...rank) {
    param stridable = _any_stridable(ranges);
    var newRanges: rank*range(_value.idxType, bounded, stridable);
    for param j in 1..rank {
      newRanges(j) = _value.dim(j)(ranges(j));
    }
    var x = _value.dist.buildDomain(rank, _value.idxType, stridable);
    x.setIndices(newRanges);
    return this(_domain(rank, x));
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  pragma "valid lvalue"
  def _dom
    return _domain(rank, _value.dom);

  pragma "valid lvalue"
  def this(d: _domain) {
    _value.checkSlice(d._value);
    var x = _value.slice(_dom(d)._value);
    return _array(_dim_index_type, eltType, rank, x);
  }

  pragma "valid lvalue"
  def this(rs: range(?_eltType,?boundedType,?stridable) ...rank) {
    _value.checkSlice(rs);
    var x = _value.slice(_dom((...rs))._value);
    return _array(_dim_index_type, eltType, rank, x);
  }

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

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

  def this(i: _dim_index_type) var where rank == 1
    return _value(i);

  def validRankChangeArguments(t) param {
    def validRankChangeArgument(r: range(?eltType,?boundedType,?stridable)) param
      return true;

    def validRankChangeArgument(i: _dim_index_type) param
      return true;

    def validRankChangeArgument(x) param
      return false;

    def help(param dim: int) param {
      if !validRankChangeArgument(t(dim)) then
        return false;
      else if dim < t.size then
        return help(dim+1);
      else
        return true;
    }
    return help(1);
  }

  pragma "valid lvalue"
  def this(irs ...rank) where validRankChangeArguments(irs) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;
    def _tupleize(x) {
      var y: 1*x.type;
      y(1) = x;
      return y;
    }
    def collectRanges(param dim: int) {
      if isRange(irs(dim))
        return collectRanges(dim+1, _tupleize(irs(dim)));
      else
        return collectRanges(dim+1);
    }
    def collectRanges(param dim: int, x: _tuple) {
      if dim > irs.size {
        return x;
      } else if dim < irs.size {
        if isRange(irs(dim)) then
          return collectRanges(dim+1, ((...x), irs(dim)));
        else
          return collectRanges(dim+1, x);
      } else {
        if isRange(irs(dim)) then
          return ((...x), irs(dim));
        else
          return x;
      }
    }
    var rs = collectRanges(1);
    var x = _value.rankChange(rs.size, _any_stridable(rs), irs, rs);
    return _array(_dim_index_type, eltType, rs.size, x);
  }

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

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

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

  def reindex(d: _domain) where rank != 1 {
    var x = _value.reindex(d._value);
    type xDimIndexType = x.idxType;       // BLC: hacks to get around
    type xIndexType = rank*xDimIndexType;  // inflexibility of tuple syntax
    return _array(x.idxType, eltType, rank, x);
  }

  def IRV var {
    return _value.IRV;
  }
}

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

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

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

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

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

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

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

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

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

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

  def sparseShiftArray(shiftrange, initrange) {
    halt("sparseGrowDomain not supported for non-sparse arrays");
  }
}

class BaseDomain {
  var _arrs: list of BaseArray;

  def member(ind) : bool {
    halt("membership test not supported for this domain type");
    return false;
  }
}

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

class BaseSparseArithmeticDomain : BaseArithmeticDomain {
}

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

def _copy(ic: _iteratorClass) {
  var capacity = 4, size = 0;
  var data = _ddata(__primitive("get_ic_type", ic), capacity);
  data.init();

  for e in ic {
    if size == capacity {
      capacity = capacity * 2;
      var tmp = _ddata(__primitive("get_ic_type", ic), capacity);
      tmp.init();
      for i in 0..size-1 do
        tmp(i) = data(i);
      data = tmp;
    }
    data(size) = e;
    size = size + 1;
  }

  var A: [1..size] __primitive("get_ic_type", ic);
  for i in 0..size-1 do
    A(i+1) = data(i);
  return A;
}

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

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

/*  This might be preferable for domains, but it doesn't work because
    domains apparently can't be modified once sent into a function!?

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