Parallelized the leader iterator for the block-cyclic dimension descriptor for Dimensional,
but only in the limited case when there is exactly one index per block,
i.e. the stride of the domain dimension is the same as the dimension's block size.

Can be turned off optionally with --BlockCyclicDim_allowParLeader=false.

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

// One-dimensional block-cyclic distribution for use with Dimensional.

use d;

config const BlockCyclicDim_allowParLeader = true;

// the types to use for blockSzie and numLocales
type cycSizeT = uint(32);     // unsigned - for optimization
type cycSizeTuser = int(32);  // for versatility

class idist {
  // distribution parameters
  const numLocales: cycSizeTuser;
  const lowIdx: int(64); // ensure we can always subtract it
  const blockSize: cycSizeTuser;

  // for debugging
  const name:string;

  // tell the compiler these are positive
  proc blockSizePos   return blockSize: cycSizeT;
  proc numLocalesPos  return numLocales: cycSizeT;
  const cycleSizePos: cycSizeT = blockSizePos * numLocalesPos;
}

class idom {
  type idxType;
  type stoIndexT;
  param stridable: bool;

  // for debugging
  const name:string;

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

  // our range, normalized; its absolute stride
  var wholeR: rangeT;
  var wholeRstrideAbs: idxType;

  // a copy of idist constants
  const lowIdxAdj: idxType;
  const blockSizePos, numLocalesPos, cycleSizePos: cycSizeT;

  // amount of storage per locale per cycle - depends on wholeR.stride
  var storagePerCycle: cycSizeT = 0;
}

class ilocdom {
  type idxType;
  type stoIndexT;
  const locId: locIdT;
}

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

proc idist.dsiSupportsPrivatization1d() param return true;

proc idist.dsiGetPrivatizeData1d() {
  return (lowIdx, blockSize, numLocales, name);
}

proc idist.dsiPrivatize1d(privatizeData) {
  return new idist(lowIdx = privatizeData(1),
                   blockSize = privatizeData(2),
                   numLocales = privatizeData(3),
                   name = privatizeData(4));
}

proc idist.dsiUsesLocalLocID1d() param return false;

proc idom.dsiSupportsPrivatization1d() param return true;

proc idom.dsiGetPrivatizeData1d() {
  return tuple(wholeR, wholeRstrideAbs, storagePerCycle, lowIdxAdj, name);
}

proc idom.dsiPrivatize1d(privDist, privatizeData) {
  assert(privDist.locale == here); // sanity check
  return new idom(idxType   = this.idxType,
                  stoIndexT = this.stoIndexT,
                  stridable = this.stridable,
                  name            = privatizeData(5),
                  wholeR          = privatizeData(1),
                  wholeRstrideAbs = privatizeData(2),
                  storagePerCycle = privatizeData(3),
                  lowIdxAdj       = privatizeData(4),
                  // could include these in privatizeData
                  blockSizePos  = privDist.blockSizePos,
                  numLocalesPos = privDist.numLocalesPos,
                  cycleSizePos  = privDist.cycleSizePos);
}

proc idom.dsiGetReprivatizeData1d() {
  return tuple(wholeR, wholeRstrideAbs, storagePerCycle);
}

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

  this.wholeR          = reprivatizeData(1);
  this.wholeRstrideAbs = reprivatizeData(2);
  this.storagePerCycle = reprivatizeData(3);
}

proc idom.dsiUsesLocalLocID1d() param return false;

proc idom.dsiLocalDescUsesPrivatizedGlobalDesc1d() param return false;

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

// Check all restrictions/assumptions that must be satisfied by the user
// when constructing a 1-d BlockCyclic distribution.
inline proc idist.checkInvariants() {
  assert(blockSize > 0, "BlockCyclic1d-blockSize");
  assert(numLocales > 0, "BlockCyclic1d-numLocales");
}

proc idist.toString()
  return "BlockCyclicDim(" + numLocales:string + ", " +
         lowIdx:string + ", " + blockSize:string + ")";

// Assert that the value of 'src' is preserved when casting it to 'destT'.
inline proc _checkFitsWithin(src: integral, type destT)
  where _isIntegralType(destT)
{
  inline proc ensure(arg:bool) { assert(arg); }
  type maxuT = uint(64); // the largest unsigned type
  type srcT = src.type;
  proc numMantBits(type T) param
    return numBits(T) - if _isSignedType(T) then 1 else 0;

  if _isUnsignedType(destT) {
    ensure(isNonnegative(src));
    if numMantBits(srcT) > numMantBits(destT) then
      ensure(src:maxuT <= max(destT):maxuT);

  } else {
    // destT is signed
    if _isUnsignedType(srcT) && numMantBits(srcT) > numMantBits(destT) then
      ensure(src:maxuT <= max(destT):maxuT);
    if _isSignedType(srcT) && numBits(destT) < numBits(srcT) {
      ensure(src <= max(destT):srcT);
      ensure(src >= min(destT):srcT);
    }
  }
}

proc idist.dsiNewRectangularDom1d(type idxType, param stridable: bool,
                                  type stoIndexT)
{
  checkInvariants();
  _checkFitsWithin(this.lowIdx, idxType);
  const lowIdxDom = this.lowIdx: idxType;

  // Allow for idxType and/or stoIndexT to be unsigned, by replacing
  //  ind0 = ind -lowIdx  -->  ind0 = ind + lowIdxAdj
  //
  // where lowIdxAdj is
  //  -lowIdx whenever possible (more natural for debugging), otherwise
  //  -lowIdx shifted up by a multiple of cycleSize until it is >=0.
  //
  proc adjustLowIdx()  return cycleSizePos - mod(lowIdxDom, cycleSizePos);
  //
  const lowIdxAdj: idxType =
    if _isSignedType(idxType)
    then
      if _isSignedType(stoIndexT)
      then
        -lowIdxDom
      else
        if lowIdxDom <= 0 then -lowIdxDom else adjustLowIdx()
    else
      if lowIdxDom == 0 then lowIdxDom else adjustLowIdx()
    ;

    const negate = _isSignedType(idxType) && lowIdxDom <= 0;

  const result = new idom(idxType = idxType,
                  stoIndexT = stoIndexT,
                  stridable = stridable,
                  lowIdxAdj = lowIdxAdj,
                  blockSizePos  = this.blockSizePos,
                  numLocalesPos = this.numLocalesPos,
                  cycleSizePos  = this.cycleSizePos,
                  name = this.name);

  return result;
}

proc idom.dsiIsReplicated1d() param return false;

proc idom.dsiNewLocalDom1d(type stoIndexT, locId: locIdT) {
  const result = new ilocdom(idxType = this.idxType,
                             stoIndexT = stoIndexT,
                             locId = locId);
  return result;
}

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

proc ilocdom.dsiBuildLocalDom1d(newGlobDD, locId: locIdT) {
  assert(locId == this.locId);
  // There does not seem to be any optimizations from merging the two calls.
  const newLocDD = newGlobDD.dsiNewLocalDom1d(this.stoIndexT, locId);
  const newStoRng = newLocDD.dsiSetLocalIndices1d(newGlobDD, locId);
  return (newLocDD, newStoRng);
}

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

// #include f.chpl.formulas.txt

inline proc idom._dsiInd0(ind: idxType): idxType
  return ind + lowIdxAdj;

inline proc idom._dsiCycNo(ind: idxType)
  return divfloor(_dsiInd0(ind), cycleSizePos): idxType;

inline proc idom._dsiCycOff(ind: idxType)
  return mod(_dsiInd0(ind), cycleSizePos): cycSizeT;

// "formula" in the name emphasizes no sanity checking
inline proc idom._dsiLocNo_formula(ind: idxType): locIdT {
  // keep in sync with idist.dsiIndexToLocale1d()
  const ind0 = _dsiInd0(ind);
  return
    if isNonnegative(ind0) then ( (ind0/blockSizePos) % numLocalesPos ): locIdT
    else  mod(divfloor(ind0, blockSizePos), numLocalesPos): locIdT
    ;
}

inline proc idom._dsiLocOff(ind: idxType)
  return ( _dsiCycOff(ind) % blockSizePos ): stoIndexT;

// hoist some common code
inline proc idom._dsiStorageIdx2(cycNo, locOff)
  return cycNo * storagePerCycle + _divByStride(locOff);

// "formula" in the name implies no sanity checking
// in particular at the moment its type may not be stoIndexT
inline proc idom._dsiStorageIdx_formula(ind: idxType)
  return _dsiStorageIdx2(_dsiCycNo(ind), _dsiLocOff(ind));

inline proc idom._dsiStorageIdx(ind: idxType)
  return _dsiStorageIdx_formula(ind): stoIndexT;

// oblivious of 'wholeR'
inline proc idom._dsiIndicesOnCycLoc(cycNo: idxType, locNo: locIdT)
  : range(idxType)
{
  const startCycle = (cycNo * cycleSizePos): idxType - lowIdxAdj;
  const startLoc = startCycle:idxType + locNo:idxType * blockSizePos:idxType;
  return startLoc..#blockSizePos:idxType;
}

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

proc idist.dsiIndexToLocale1d(ind): locIdT {
  // keep in sync with idom._dsiLocNo_formula
  const ind0 = ind - lowIdx;
  const locNo =
    if ind0 >= 0 then ( (ind0 / blockSize) % numLocales ): locIdT
    else  mod(divfloor(ind0, blockSizePos), numLocalesPos): locIdT
    ;

  assert(0 <= locNo && locNo < numLocales);
  // todo: the following assert should not be needed - it can be proven
  assert(locNo == mod(ind0, cycleSizePos) / blockSize);

  return locNo;
}

// allow uint(64) indices, but assert that they fit in int(64)
inline proc idist.dsiIndexToLocale1d(ind: uint(64)): locIdT {
  type convT = int(64);
  assert(ind <= max(convT));
  return dsiIndexToLocale1d(ind:convT);
}

//var debugD1Shown = false;
//proc idom.debugIsD1() return name == "D1";

proc idom.dsiSetIndices1d(rangeArg: rangeT): void {
  // For now, require the user to provide unambiguous ranges only.
  // This requirement could potentially be avoided (as long as no arrays
  // are declared over the domain), but it simplifies/speeds up our code.
  //
  // todo: document this in the spec for this distribution.
  // see also an assert is dsiSetLocalIndices1d()
  assert(!rangeArg.isAmbiguous());

  const prevStoragePerCycle = storagePerCycle;

  // As of this writing, alignedLow/High are valid even for empty ranges
  if stridable {
    wholeR = rangeArg.alignedLow..rangeArg.alignedHigh by rangeArg.stride;
    wholeRstrideAbs = abs(rangeArg.stride): idxType;
    storagePerCycle = (1 + (blockSizePos - 1) / wholeRstrideAbs): cycSizeT;
  } else {
    assert(rangeArg.stride == 1); // ensures we can simplify things
    wholeR = rangeArg.alignedLow..rangeArg.alignedHigh;
    wholeRstrideAbs = 0; // be sure nobody ever reads this
    storagePerCycle = blockSizePos;
  }

//proc writei(i) {
//  write("(", _dsiCycNo(i), ",", _dsiLocNo_formula(i),
//        ",", _dsiCycOff(i) % blockSizePos, ")");
//}
//writeln();
//write  ("dsiSetIndices1d(", name, ", ", rangeArg, ") --> ");
//  write(wholeR, " | ");
//  writei(wholeR.low); write(" .. "); writei(wholeR.high);
//  writeln();
//if debugIsD1() then debugD1Shown = false;

  if prevStoragePerCycle != 0 && storagePerCycle != prevStoragePerCycle then
    stderr.writeln("warning: array resizing is not implemented upon change in dimension stride with 1-d BlockCyclic distribution");
}

inline proc idom._divByStride(locOff)  return
  if stridable then ( locOff / wholeRstrideAbs ): stoIndexT
  else              locOff: stoIndexT;

// _dsiStorageLow(), _dsiStorageHigh(): save a few mods and divisions
// at the cost of potentially allocating more storage
inline proc idom._dsiStorageLow(locId: locIdT): stoIndexT {
  const lowW = wholeR.low;

  // smallest cycNo(i) for i in wholeR
  var   lowCycNo  = _dsiCycNo(lowW): stoIndexT;
  const lowLocNo  = _dsiLocNo_formula(lowW);
  var   lowIdxAdj = 0: stoIndexT;

  // (Optional) tighten the storage if wholeR
  // does not fall on this locale in the lowest cycle.
  if lowLocNo > locId then
    lowCycNo += 1;
  else
    // (Optional) tighten the storage if wholeR
    // starts on this locale, but not at the beginning of it.
    if lowLocNo == locId then
      lowIdxAdj = _divByStride(_dsiLocOff(lowW));

  return lowCycNo * storagePerCycle:stoIndexT + lowIdxAdj;
}

inline proc idom._dsiStorageHigh(locId: locIdT): stoIndexT {
  const hiW = wholeR.high;

  // biggest cycNo(i) for i in wholeR
  var   hiCycNo  = _dsiCycNo(hiW): stoIndexT;
  const hiLocNo  = _dsiLocNo_formula(hiW);
  var   hiIdxAdj = (storagePerCycle - 1): stoIndexT;

  // (Optional) tighten the storage if wholeR
  // does not fall on this locale in the highest cycle.
  if hiLocNo < locId then
    hiCycNo -= 1;
  else
    // (Optional) tighten the storage if wholeR
    // ends on this locale, but not at the end of it.
    if hiLocNo == locId then
      hiIdxAdj = _divByStride(_dsiLocOff(hiW));

  return hiCycNo * storagePerCycle:stoIndexT + hiIdxAdj;
}

proc ilocdom.dsiSetLocalIndices1d(globDD, locId: locIdT): range(stoIndexT) {
  const stoLow = globDD._dsiStorageLow(locId);
  const stoHigh = globDD._dsiStorageHigh(locId);

//proc debugShowD1() {
// if debugD1Shown then return false; debugD1Shown = true; return true;
//}
//if globDD.debugIsD1() && !debugD1Shown then writeln();
//if !globDD.debugIsD1() || debugShowD1() {
//writeln("dsiSetLocalIndices1d ", globDD.name, "  l ", locId,
//        " -> ", stoLow, "..", stoHigh,
//        if stoLow <= stoHigh then "" else "  empty");
//}

  return stoLow:stoIndexT .. stoHigh:stoIndexT;
}

proc idom.dsiStorageUsesUserIndices() param return false;

proc idom.dsiAccess1d(ind: idxType): (locIdT, stoIndexT) {
  return (_dsiLocNo_formula(ind), _dsiStorageIdx(ind));
}

iter ilocdom.dsiMyDensifiedRangeForSingleTask1d(globDD) {
// todo: for the special case handled in dsiMyDensifiedRangeForTaskID1d,
// maybe handling it here will be beneficial, too?
  const locNo = this.locId;
  const wholeR = globDD.wholeR;
  const lowIdx = wholeR.low;
  const highIdx = wholeR.high;
  type retT = dsiMyDensifiedRangeType1d(globDD);
  param stridable = globDD.stridable;
  assert(stridable == wholeR.stridable); // sanity

  if lowIdx > highIdx then
    return;

  const lowCycNo = globDD._dsiCycNo(lowIdx);
  const highCycNo = globDD._dsiCycNo(highIdx);
  const up = wholeR.stride > 0;
  assert(lowIdx <= highIdx);

  proc mydensify(densifyee): dsiMyDensifiedRangeType1d(globDD) {
    const temp = densify(densifyee, wholeR);
    return temp:retT;
  }

  var curIndices = globDD._dsiIndicesOnCycLoc(
                            if up then lowCycNo else highCycNo, locNo);

  const firstRange = wholeR[curIndices];
  if firstRange.low <= firstRange.high then
    yield mydensify(firstRange);
  // else nothing to yield on this locale

  if lowCycNo == highCycNo then
    // we have covered all cycles
    return;

  proc advance() {
    curIndices = curIndices.translate(
      if !stridable || up then globDD.cycleSizePos else -globDD.cycleSizePos);
  }

  for cycNo in (lowCycNo + 1) .. (highCycNo - 1) {
    advance();
    const curRange =
      if stridable then curIndices by wholeR.stride align wholeR.alignment
      else              curIndices;
    yield mydensify(curRange);
  }

  advance();
  const lastRange = wholeR[curIndices];
  if lastRange.low <= lastRange.high then
    yield mydensify(lastRange);
}

// available in a special case only, for now
proc idom.dsiSingleTaskPerLocaleOnly1d()
  return !BlockCyclicDim_allowParLeader ||
         !((blockSizePos:wholeR.stride.type) == wholeR.stride);

// only works when idom.dsiSingleTaskPerLocaleOnly1d()
proc ilocdom.dsiMyDensifiedRangeForTaskID1d(globDD, taskid:int, numTasks:int)
{
  const wholeR = globDD.wholeR;
  const nLocs  = globDD.numLocalesPos :globDD.idxType;
  assert((globDD.blockSizePos:wholeR.stride.type) == wholeR.stride);
  assert(globDD.storagePerCycle == 1); // should follow from the previous

  // In this case, the densified range for *all* indices on this locale is:
  //   0..#wholeR.length by numLocales align AL
  // where
  //   (_dsiLocNo(wholeR.low) + AL) % numLocales == this.locId

  const firstLoc = globDD._dsiLocNo_formula(wholeR.low);
  const AL = this.locId + nLocs - firstLoc;

  // Here is the densified range for all indices on this locale.
  const hereDenseInds = 0..#wholeR.length by nLocs align AL;
  const hereNumInds   = hereDenseInds.length;
  const hereFirstInd  = hereDenseInds.first;

  // This is our piece of hereNumInds
  const (begNo,endNo) = _computeChunkStartEnd(hereNumInds, numTasks, taskid+1);

  // Pick the corresponding part of hereDenseInds
  const begIx = hereFirstInd + (begNo - 1) * nLocs;
  const endIx = hereFirstInd + (endNo - 1) * nLocs;
  assert(hereDenseInds.member(begIx));
  assert(hereDenseInds.member(endIx));

//writeln("MyDensifiedRangeForTaskID(", globDD.name, ") on ", locId,
//        "  taskid ", taskid, " of ", numTasks, "  ", begIx, "...", endIx,
//        "   fl=", firstLoc, " al=", AL,
//        "  fullR ", hereDenseInds, " myR ", begIx .. endIx by nLocs,
//        "");

  return begIx .. endIx by nLocs;
}

proc ilocdom.dsiMyDensifiedRangeType1d(globDD) type
  return range(idxType=globDD.idxType, stridable=globDD.stridable);

proc ilocdom.dsiLocalSliceStorageIndices1d(globDD, sliceRange)
  : range(stoIndexT, sliceRange.boundedType, false)
{
  if sliceRange.stridable {
    // to be done: figure out sliceRange's stride vs. globDD.wholeR.stride
    compilerError("localSlice is not implemented for the Dimensional distribution with a block-cyclic dimension descriptor when the slice is stridable");
  } else {
    const lowSid = if sliceRange.hasLowBound()
      then globDD._dsiStorageIdx(sliceRange.low)
      else 0: stoIndexT;
    const highSid = if sliceRange.hasHighBound()
      then globDD._dsiStorageIdx(sliceRange.high)
      else 0: stoIndexT;
    return new range(stoIndexT, sliceRange.boundedType, false, lowSid, highSid);
  }
}

iter idom.dsiSerialArrayIterator1d() {
  // dispatch here, for code clarity
  if stridable then
    for result in _dsiSerialArrayIterator1dStridable() do
      yield result;
  else
    for result in _dsiSerialArrayIterator1dUnitstride(wholeR) do
      yield result;
}

iter idom._dsiSerialArrayIterator1dUnitstride(rangeToIterateOver) {
  assert(!rangeToIterateOver.stridable);

  const firstIdx = rangeToIterateOver.low;
  const lastIdx = rangeToIterateOver.high;

  // This rarely fires, but if so it gets rid of lots of computations.
  // In the common case it adds just 1 branch to at least 2 branches.
  if firstIdx > lastIdx then return;

  // the current point, initialized to the starting point
  var cycNo = _dsiCycNo(firstIdx);
  var locNo = _dsiLocNo_formula(firstIdx);
  var locOff = _dsiLocOff(firstIdx);

  // the final point
  const lastCycNo = _dsiCycNo(lastIdx);
  const lastLocNo = _dsiLocNo_formula(lastIdx);
  const lastLocOff = _dsiLocOff(lastIdx);

  // shortcut
  proc spec(start, end)  return
    (locNo, _dsiStorageIdx2(cycNo, start): stoIndexT ..
             _dsiStorageIdx2(cycNo, end): stoIndexT);

  assert(cycNo <= lastCycNo);
  assert(locNo < numLocalesPos);
  while cycNo < lastCycNo {
    while locNo < numLocalesPos {
      yield spec(locOff, blockSizePos - 1);
      locNo += 1;
      locOff = 0;
    }
    cycNo += 1;
    locNo = 0;
  }

  assert(cycNo == lastCycNo);
  assert(locNo <= lastLocNo);
  while locNo < lastLocNo {
    yield spec(locOff, blockSizePos - 1);
    locNo += 1;
    locOff = 0;
  }

  assert(cycNo == lastCycNo);
  assert(locNo == lastLocNo);
  assert(locOff <= lastLocOff);
  yield spec(locOff, lastLocOff);
}

iter idom._dsiSerialArrayIterator1dStridable() {
  assert(stridable);
  compilerWarning("array iterator over stridable block-cyclic-dim arrays is presently not efficient", 4);

  // the simplest way out
  for ind in wholeR do
    yield (_dsiLocNo_formula(ind), _dsiStorageIdx(ind)..#(1:stoIndexT));
}

iter idom.dsiFollowerArrayIterator1d(undensRange): (locIdT, idxType) {
  if undensRange.stridable {
    // the simplest way out
    for ix in undensRange do
      yield dsiAccess1d(ix);

  } else {
    for (locNo, stoIxs) in _dsiSerialArrayIterator1dUnitstride(undensRange) do
      for stoIdx in stoIxs do
        yield (locNo, stoIdx);
  }
}

// One-dimensional block-cyclic distribution for use with Dimensional.

use d;

// the types to use for blockSzie and numLocales
type cycSizeT = uint(32);     // unsigned - for optimization
type cycSizeTuser = int(32);  // for versatility

class idist {
  // distribution parameters
  const numLocales: cycSizeTuser;
  const lowIdx: int(64); // ensure we can always subtract it
  const blockSize: cycSizeTuser;

  // for debugging
  const name:string;

  // tell the compiler these are positive
  proc blockSizePos   return blockSize: cycSizeT;
  proc numLocalesPos  return numLocales: cycSizeT;
  const cycleSizePos: cycSizeT = blockSizePos * numLocalesPos;
}

class idom {
  type idxType;
  type stoIndexT;
  param stridable: bool;

  // for debugging
  const name:string;

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

  // our range, normalized; its absolute stride
  var wholeR: rangeT;
  var wholeRstrideAbs: idxType;

  // a copy of idist constants
  const lowIdxAdj: idxType;
  const blockSizePos, numLocalesPos, cycleSizePos: cycSizeT;

  // amount of storage per locale per cycle - depends on wholeR.stride
  var storagePerCycle: cycSizeT = 0;
}

class ilocdom {
  type idxType;
  type stoIndexT;
  const locId: locIdT;
}

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

proc idist.dsiSupportsPrivatization1d() param return true;

proc idist.dsiGetPrivatizeData1d() {
  return (lowIdx, blockSize, numLocales, name);
}

proc idist.dsiPrivatize1d(privatizeData) {
  return new idist(lowIdx = privatizeData(1),
                   blockSize = privatizeData(2),
                   numLocales = privatizeData(3),
                   name = privatizeData(4));
}

proc idist.dsiUsesLocalLocID1d() param return false;

proc idom.dsiSupportsPrivatization1d() param return true;

proc idom.dsiGetPrivatizeData1d() {
  return tuple(wholeR, wholeRstrideAbs, storagePerCycle, lowIdxAdj, name);
}

proc idom.dsiPrivatize1d(privDist, privatizeData) {
  assert(privDist.locale == here); // sanity check
  return new idom(idxType   = this.idxType,
                  stoIndexT = this.stoIndexT,
                  stridable = this.stridable,
                  name            = privatizeData(5),
                  wholeR          = privatizeData(1),
                  wholeRstrideAbs = privatizeData(2),
                  storagePerCycle = privatizeData(3),
                  lowIdxAdj       = privatizeData(4),
                  // could include these in privatizeData
                  blockSizePos  = privDist.blockSizePos,
                  numLocalesPos = privDist.numLocalesPos,
                  cycleSizePos  = privDist.cycleSizePos);
}

proc idom.dsiGetReprivatizeData1d() {
  return tuple(wholeR, wholeRstrideAbs, storagePerCycle);
}

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

  this.wholeR          = reprivatizeData(1);
  this.wholeRstrideAbs = reprivatizeData(2);
  this.storagePerCycle = reprivatizeData(3);
}

proc idom.dsiUsesLocalLocID1d() param return false;

proc idom.dsiLocalDescUsesPrivatizedGlobalDesc1d() param return false;

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

// Check all restrictions/assumptions that must be satisfied by the user
// when constructing a 1-d BlockCyclic distribution.
inline proc idist.checkInvariants() {
  assert(blockSize > 0, "BlockCyclic1d-blockSize");
  assert(numLocales > 0, "BlockCyclic1d-numLocales");
}

proc idist.toString()
  return "BlockCyclicDim(" + numLocales:string + ", " +
         lowIdx:string + ", " + blockSize:string + ")";

// Assert that the value of 'src' is preserved when casting it to 'destT'.
inline proc _checkFitsWithin(src: integral, type destT)
  where _isIntegralType(destT)
{
  inline proc ensure(arg:bool) { assert(arg); }
  type maxuT = uint(64); // the largest unsigned type
  type srcT = src.type;
  proc numMantBits(type T) param
    return numBits(T) - if _isSignedType(T) then 1 else 0;

  if _isUnsignedType(destT) {
    ensure(isNonnegative(src));
    if numMantBits(srcT) > numMantBits(destT) then
      ensure(src:maxuT <= max(destT):maxuT);

  } else {
    // destT is signed
    if _isUnsignedType(srcT) && numMantBits(srcT) > numMantBits(destT) then
      ensure(src:maxuT <= max(destT):maxuT);
    if _isSignedType(srcT) && numBits(destT) < numBits(srcT) {
      ensure(src <= max(destT):srcT);
      ensure(src >= min(destT):srcT);
    }
  }
}

proc idist.dsiNewRectangularDom1d(type idxType, param stridable: bool,
                                  type stoIndexT)
{
  checkInvariants();
  _checkFitsWithin(this.lowIdx, idxType);
  const lowIdxDom = this.lowIdx: idxType;

  // Allow for idxType and/or stoIndexT to be unsigned, by replacing
  //  ind0 = ind -lowIdx  -->  ind0 = ind + lowIdxAdj
  //
  // where lowIdxAdj is
  //  -lowIdx whenever possible (more natural for debugging), otherwise
  //  -lowIdx shifted up by a multiple of cycleSize until it is >=0.
  //
  proc adjustLowIdx()  return cycleSizePos - mod(lowIdxDom, cycleSizePos);
  //
  const lowIdxAdj: idxType =
    if _isSignedType(idxType)
    then
      if _isSignedType(stoIndexT)
      then
        -lowIdxDom
      else
        if lowIdxDom <= 0 then -lowIdxDom else adjustLowIdx()
    else
      if lowIdxDom == 0 then lowIdxDom else adjustLowIdx()
    ;

    const negate = _isSignedType(idxType) && lowIdxDom <= 0;

  const result = new idom(idxType = idxType,
                  stoIndexT = stoIndexT,
                  stridable = stridable,
                  lowIdxAdj = lowIdxAdj,
                  blockSizePos  = this.blockSizePos,
                  numLocalesPos = this.numLocalesPos,
                  cycleSizePos  = this.cycleSizePos,
                  name = this.name);

  return result;
}

proc idom.dsiIsReplicated1d() param return false;

proc idom.dsiNewLocalDom1d(type stoIndexT, locId: locIdT) {
  const result = new ilocdom(idxType = this.idxType,
                             stoIndexT = stoIndexT,
                             locId = locId);
  return result;
}

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

proc ilocdom.dsiBuildLocalDom1d(newGlobDD, locId: locIdT) {
  assert(locId == this.locId);
  // There does not seem to be any optimizations from merging the two calls.
  const newLocDD = newGlobDD.dsiNewLocalDom1d(this.stoIndexT, locId);
  const newStoRng = newLocDD.dsiSetLocalIndices1d(newGlobDD, locId);
  return (newLocDD, newStoRng);
}

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

// #include f.chpl.formulas.txt

inline proc idom._dsiInd0(ind: idxType): idxType
  return ind + lowIdxAdj;

inline proc idom._dsiCycNo(ind: idxType)
  return divfloor(_dsiInd0(ind), cycleSizePos): idxType;

inline proc idom._dsiCycOff(ind: idxType)
  return mod(_dsiInd0(ind), cycleSizePos): cycSizeT;

// "formula" in the name emphasizes no sanity checking
inline proc idom._dsiLocNo_formula(ind: idxType): locIdT {
  // keep in sync with idist.dsiIndexToLocale1d()
  const ind0 = _dsiInd0(ind);
  return
    if isNonnegative(ind0) then ( (ind0/blockSizePos) % numLocalesPos ): locIdT
    else  mod(divfloor(ind0, blockSizePos), numLocalesPos): locIdT
    ;
}

inline proc idom._dsiLocOff(ind: idxType)
  return ( _dsiCycOff(ind) % blockSizePos ): stoIndexT;

// hoist some common code
inline proc idom._dsiStorageIdx2(cycNo, locOff)
  return cycNo * storagePerCycle + _divByStride(locOff);

// "formula" in the name implies no sanity checking
// in particular at the moment its type may not be stoIndexT
inline proc idom._dsiStorageIdx_formula(ind: idxType)
  return _dsiStorageIdx2(_dsiCycNo(ind), _dsiLocOff(ind));

inline proc idom._dsiStorageIdx(ind: idxType)
  return _dsiStorageIdx_formula(ind): stoIndexT;

// oblivious of 'wholeR'
inline proc idom._dsiIndicesOnCycLoc(cycNo: idxType, locNo: locIdT)
  : range(idxType)
{
  const startCycle = (cycNo * cycleSizePos): idxType - lowIdxAdj;
  const startLoc = startCycle:idxType + locNo:idxType * blockSizePos:idxType;
  return startLoc..#blockSizePos:idxType;
}

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

proc idist.dsiIndexToLocale1d(ind): locIdT {
  // keep in sync with idom._dsiLocNo_formula
  const ind0 = ind - lowIdx;
  const locNo =
    if ind0 >= 0 then ( (ind0 / blockSize) % numLocales ): locIdT
    else  mod(divfloor(ind0, blockSizePos), numLocalesPos): locIdT
    ;

  assert(0 <= locNo && locNo < numLocales);
  // todo: the following assert should not be needed - it can be proven
  assert(locNo == mod(ind0, cycleSizePos) / blockSize);

  return locNo;
}

// allow uint(64) indices, but assert that they fit in int(64)
inline proc idist.dsiIndexToLocale1d(ind: uint(64)): locIdT {
  type convT = int(64);
  assert(ind <= max(convT));
  return dsiIndexToLocale1d(ind:convT);
}

//var debugD1Shown = false;
//proc idom.debugIsD1() return name == "D1";

proc idom.dsiSetIndices1d(rangeArg: rangeT): void {
  // For now, require the user to provide unambiguous ranges only.
  // This requirement could potentially be avoided (as long as no arrays
  // are declared over the domain), but it simplifies/speeds up our code.
  //
  // todo: document this in the spec for this distribution.
  // see also an assert is dsiSetLocalIndices1d()
  assert(!rangeArg.isAmbiguous());

  const prevStoragePerCycle = storagePerCycle;

  // As of this writing, alignedLow/High are valid even for empty ranges
  if stridable {
    wholeR = rangeArg.alignedLow..rangeArg.alignedHigh by rangeArg.stride;
    wholeRstrideAbs = abs(rangeArg.stride): idxType;
    storagePerCycle = (1 + (blockSizePos - 1) / wholeRstrideAbs): cycSizeT;
  } else {
    assert(rangeArg.stride == 1); // ensures we can simplify things
    wholeR = rangeArg.alignedLow..rangeArg.alignedHigh;
    wholeRstrideAbs = 0; // be sure nobody ever reads this
    storagePerCycle = blockSizePos;
  }

//proc writei(i) {
//  write("(", _dsiCycNo(i), ",", _dsiLocNo_formula(i),
//        ",", _dsiCycOff(i) % blockSizePos, ")");
//}
//writeln();
//write  ("dsiSetIndices1d(", name, ", ", rangeArg, ") --> ");
//  write(wholeR, " | ");
//  writei(wholeR.low); write(" .. "); writei(wholeR.high);
//  writeln();
//if debugIsD1() then debugD1Shown = false;

  if prevStoragePerCycle != 0 && storagePerCycle != prevStoragePerCycle then
    stderr.writeln("warning: array resizing is not implemented upon change in dimension stride with 1-d BlockCyclic distribution");
}

inline proc idom._divByStride(locOff)  return
  if stridable then ( locOff / wholeRstrideAbs ): stoIndexT
  else              locOff: stoIndexT;

// _dsiStorageLow(), _dsiStorageHigh(): save a few mods and divisions
// at the cost of potentially allocating more storage
inline proc idom._dsiStorageLow(locId: locIdT): stoIndexT {
  const lowW = wholeR.low;

  // smallest cycNo(i) for i in wholeR
  var   lowCycNo  = _dsiCycNo(lowW): stoIndexT;
  const lowLocNo  = _dsiLocNo_formula(lowW);
  var   lowIdxAdj = 0: stoIndexT;

  // (Optional) tighten the storage if wholeR
  // does not fall on this locale in the lowest cycle.
  if lowLocNo > locId then
    lowCycNo += 1;
  else
    // (Optional) tighten the storage if wholeR
    // starts on this locale, but not at the beginning of it.
    if lowLocNo == locId then
      lowIdxAdj = _divByStride(_dsiLocOff(lowW));

  return lowCycNo * storagePerCycle:stoIndexT + lowIdxAdj;
}

inline proc idom._dsiStorageHigh(locId: locIdT): stoIndexT {
  const hiW = wholeR.high;

  // biggest cycNo(i) for i in wholeR
  var   hiCycNo  = _dsiCycNo(hiW): stoIndexT;
  const hiLocNo  = _dsiLocNo_formula(hiW);
  var   hiIdxAdj = (storagePerCycle - 1): stoIndexT;

  // (Optional) tighten the storage if wholeR
  // does not fall on this locale in the highest cycle.
  if hiLocNo < locId then
    hiCycNo -= 1;
  else
    // (Optional) tighten the storage if wholeR
    // ends on this locale, but not at the end of it.
    if hiLocNo == locId then
      hiIdxAdj = _divByStride(_dsiLocOff(hiW));

  return hiCycNo * storagePerCycle:stoIndexT + hiIdxAdj;
}

proc ilocdom.dsiSetLocalIndices1d(globDD, locId: locIdT): range(stoIndexT) {
  const stoLow = globDD._dsiStorageLow(locId);
  const stoHigh = globDD._dsiStorageHigh(locId);

//proc debugShowD1() {
// if debugD1Shown then return false; debugD1Shown = true; return true;
//}
//if globDD.debugIsD1() && !debugD1Shown then writeln();
//if !globDD.debugIsD1() || debugShowD1() {
//writeln("dsiSetLocalIndices1d ", globDD.name, "  l ", locId,
//        " -> ", stoLow, "..", stoHigh,
//        if stoLow <= stoHigh then "" else "  empty");
//}

  return stoLow:stoIndexT .. stoHigh:stoIndexT;
}

proc idom.dsiStorageUsesUserIndices() param return false;

proc idom.dsiAccess1d(ind: idxType): (locIdT, stoIndexT) {
  return (_dsiLocNo_formula(ind), _dsiStorageIdx(ind));
}

iter ilocdom.dsiMyDensifiedRangeForSingleTask1d(globDD) {
  const locNo = this.locId;
  const wholeR = globDD.wholeR;
  const lowIdx = wholeR.low;
  const highIdx = wholeR.high;
  type retT = dsiMyDensifiedRangeType1d(globDD);
  param stridable = globDD.stridable;
  assert(stridable == wholeR.stridable); // sanity

  if lowIdx > highIdx then
    return;

  const lowCycNo = globDD._dsiCycNo(lowIdx);
  const highCycNo = globDD._dsiCycNo(highIdx);
  const up = wholeR.stride > 0;
  assert(lowIdx <= highIdx);

  proc mydensify(densifyee): dsiMyDensifiedRangeType1d(globDD) {
    const temp = densify(densifyee, wholeR);
    return temp:retT;
  }

  var curIndices = globDD._dsiIndicesOnCycLoc(
                            if up then lowCycNo else highCycNo, locNo);

  const firstRange = wholeR[curIndices];
  if firstRange.low <= firstRange.high then
    yield mydensify(firstRange);
  // else nothing to yield on this locale

  if lowCycNo == highCycNo then
    // we have covered all cycles
    return;

  proc advance() {
    curIndices = curIndices.translate(
      if !stridable || up then globDD.cycleSizePos else -globDD.cycleSizePos);
  }

  for cycNo in (lowCycNo + 1) .. (highCycNo - 1) {
    advance();
    const curRange =
      if stridable then curIndices by wholeR.stride align wholeR.alignment
      else              curIndices;
    yield mydensify(curRange);
  }

  advance();
  const lastRange = wholeR[curIndices];
  if lastRange.low <= lastRange.high then
    yield mydensify(lastRange);
}

proc idom.dsiSingleTaskPerLocaleOnly1d() param return true;

// not supported at the moment:
//proc ilocdom.dsiMyDensifiedRangeForTaskID1d(globDD, taskid:int, numTasks:int)

proc ilocdom.dsiMyDensifiedRangeType1d(globDD) type
  return range(idxType=globDD.idxType, stridable=globDD.stridable);

proc ilocdom.dsiLocalSliceStorageIndices1d(globDD, sliceRange)
  : range(stoIndexT, sliceRange.boundedType, false)
{
  if sliceRange.stridable {
    // to be done: figure out sliceRange's stride vs. globDD.wholeR.stride
    compilerError("localSlice is not implemented for the Dimensional distribution with a block-cyclic dimension descriptor when the slice is stridable");
  } else {
    const lowSid = if sliceRange.hasLowBound()
      then globDD._dsiStorageIdx(sliceRange.low)
      else 0: stoIndexT;
    const highSid = if sliceRange.hasHighBound()
      then globDD._dsiStorageIdx(sliceRange.high)
      else 0: stoIndexT;
    return new range(stoIndexT, sliceRange.boundedType, false, lowSid, highSid);
  }
}

iter idom.dsiSerialArrayIterator1d() {
  // dispatch here, for code clarity
  if stridable then
    for result in _dsiSerialArrayIterator1dStridable() do
      yield result;
  else
    for result in _dsiSerialArrayIterator1dUnitstride(wholeR) do
      yield result;
}

iter idom._dsiSerialArrayIterator1dUnitstride(rangeToIterateOver) {
  assert(!rangeToIterateOver.stridable);

  const firstIdx = rangeToIterateOver.low;
  const lastIdx = rangeToIterateOver.high;

  // This rarely fires, but if so it gets rid of lots of computations.
  // In the common case it adds just 1 branch to at least 2 branches.
  if firstIdx > lastIdx then return;

  // the current point, initialized to the starting point
  var cycNo = _dsiCycNo(firstIdx);
  var locNo = _dsiLocNo_formula(firstIdx);
  var locOff = _dsiLocOff(firstIdx);

  // the final point
  const lastCycNo = _dsiCycNo(lastIdx);
  const lastLocNo = _dsiLocNo_formula(lastIdx);
  const lastLocOff = _dsiLocOff(lastIdx);

  // shortcut
  proc spec(start, end)  return
    (locNo, _dsiStorageIdx2(cycNo, start): stoIndexT ..
             _dsiStorageIdx2(cycNo, end): stoIndexT);

  assert(cycNo <= lastCycNo);
  assert(locNo < numLocalesPos);
  while cycNo < lastCycNo {
    while locNo < numLocalesPos {
      yield spec(locOff, blockSizePos - 1);
      locNo += 1;
      locOff = 0;
    }
    cycNo += 1;
    locNo = 0;
  }

  assert(cycNo == lastCycNo);
  assert(locNo <= lastLocNo);
  while locNo < lastLocNo {
    yield spec(locOff, blockSizePos - 1);
    locNo += 1;
    locOff = 0;
  }

  assert(cycNo == lastCycNo);
  assert(locNo == lastLocNo);
  assert(locOff <= lastLocOff);
  yield spec(locOff, lastLocOff);
}

iter idom._dsiSerialArrayIterator1dStridable() {
  assert(stridable);
  compilerWarning("array iterator over stridable block-cyclic-dim arrays is presently not efficient", 4);

  // the simplest way out
  for ind in wholeR do
    yield (_dsiLocNo_formula(ind), _dsiStorageIdx(ind)..#(1:stoIndexT));
}

iter idom.dsiFollowerArrayIterator1d(undensRange): (locIdT, idxType) {
  if undensRange.stridable {
    // the simplest way out
    for ix in undensRange do
      yield dsiAccess1d(ix);

  } else {
    for (locNo, stoIxs) in _dsiSerialArrayIterator1dUnitstride(undensRange) do
      for stoIdx in stoIxs do
        yield (locNo, stoIdx);
  }
}


code is a mess, but all things multiply.  Python allows for a lot of magic

module MLP {
  // https://gist.github.com/ben-albrecht/e2765ddd49007744a09dd016535038f9

  use LinearAlgebra;
  use Random;
  //config const epoch = 5000;
  config const epoch = 5,
               lr = 0.1;

  /*
   Attempts to implement the Multi-Layer Perceptron described here
   https://www.analyticsvidhya.com/blog/2017/05/neural-network-from-scratch-in-python-and-r/
   I'm going to do it very literally the first time
   */
  proc main() {
    writeln("Hola Mundo!");
    var X = Matrix(
       [1.0,0.0,1.0,0.0],
       [1.0,0.0,1.0,1.0],
       [0.0,1.0,0.0,1.0]);
    var y = Vector([1.0,1.0,0.0]);

    // Variable Initialization
    const inputLayerNeurons = X.shape[1],
          hiddenlayer_neurons = 3,
          output_neurons = 1;

    /* Create Ranges for the objects */
    const inputRange = 0..X.shape[1],
          hiddenRange = 0..#3,
          outputRange = 0..#1;

    var ones: [hiddenRange] real = 1.0;
    /* Weight and bias matrices
       Note that inputRange counts the number of inputs, not the dimension
       of the input.  We'll have to clarify that.
     */
    var wh: [{inputRange, hiddenRange}] real,
        //bh: [{0..0, hiddenRange}] real,
        wout: [{hiddenRange, outputRange}] real;
        //bout: [{0..0, outputRange}] real;

    var bh: [hiddenRange] real;
    var bout: [outputRange] real;

    fillRandom(wh);
    fillRandom(bh);
    fillRandom(wout);
    fillRandom(bout);

    var bhm: [hiddenRange, hiddenRange] real;
    for i in hiddenRange{
      bhm[i,..] = bh;
    }

    for i in 1..#epoch {

      /* Forward propagation */
      //writeln("X shape: ", X.shape);
      //writeln("wh shape: ", wh.shape);
      //writeln("bh shape: ", bh.shape);
      //writeln("bhm shape: ", bhm.shape);
      //writeln("bhm:\n ", bhm);
      var hiddenLayerInput1 = dot(X,wh);
      //writeln(hidden_layer_input1.shape);
      //writeln(bh.shape);
      //writeln(bhm.shape);
      /* Here, Python pulls a bullshit move
         It adds b to each ROW of dot(X,wh), which is bullshit any way you slice it.
       */
      //var hidden_layer_input = matPlus(hidden_layer_input1, bs);
      var hiddenLayerInput = matPlus(hiddenLayerInput1, bhm);
      var hiddenLayerActivations: [hiddenLayerInput.domain] real;
      hiddenLayerActivations = sigmoid(hiddenLayerInput);
      //writeln(hiddenLayerInput);
      writeln("hiddenLayerActivations ", hiddenLayerActivations);
      writeln("hiddenLayerActivations shape ", hiddenLayerActivations.shape);

      var outputLayerInput1 = dot(hiddenLayerActivations, wout);
      //writeln("outputLayerInput1 shape ", outputLayerInput1.shape);
      //writeln("wout shape ", wout.shape);
      var outputLayerInput = matPlus(outputLayerInput1, wout);
      //writeln("outputLayerInput shape ", outputLayerInput.shape);
      var output: [outputLayerInput.domain] real;
      output = sigmoid(outputLayerInput);
      //writeln("output:\n", output);

      /* Back Propagation */
      var E = y - output;
      //writeln("E ", E);
      var slopeOutputLayer: [output.domain] real;
      slopeOutputLayer = derivatives_sigmoid(output);
      writeln("slopeOutputLayer:\n ", slopeOutputLayer);
      var slopeHiddenLayer: [hiddenLayerActivations.domain] real;
      slopeHiddenLayer = derivatives_sigmoid(hiddenLayerActivations);
      /*
        This freaks me out, this does element-wise multiplication.
       */
      var dOutput: [output.domain] real;
      dOutput = E * slopeOutputLayer;
      /*
      writeln("wout ", wout);
      writeln("wout shape ", wout.shape);
      */
      writeln("dOutput ", dOutput);
      writeln("dOutput shape ", dOutput.shape);
      var errorAtHiddenLayer = dot(dOutput, wout.T);
      /*
      writeln("errorAtHiddenLayer ", errorAtHiddenLayer);
      writeln("errorAtHiddenLayer shape ", errorAtHiddenLayer.shape);
      writeln("slopeHiddenLayer shape ", slopeHiddenLayer.shape);
       */
      var dHiddenLayer = dot(errorAtHiddenLayer,slopeHiddenLayer);
      writeln("dHiddenLayer ", dHiddenLayer);
      writeln("dHiddenLayer shape ", dHiddenLayer.shape);

      wout += dot(hiddenLayerActivations.T, dOutput) * lr;
      writeln("ones ", ones);
      writeln("ones shape ", ones.shape);
      bout += dot(dOutput.T, ones) * lr;
      wh += dot(X.T, dHiddenLayer) * lr;
      bh += dot(dHiddenLayer, ones.T) * lr;

    }

  }

  proc sigmoid(x: real) {
    return (1/(1 + exp(-x)));
  }

  proc derivatives_sigmoid(x) {
    return x * (1-x);
  }
}

module MLP {
  // https://gist.github.com/ben-albrecht/e2765ddd49007744a09dd016535038f9

  use LinearAlgebra;
  use Random;
  //config const epoch = 5000;
  config const epoch = 5,
               lr = 0.1;

  /*
   Attempts to implement the Multi-Layer Perceptron described here
   https://www.analyticsvidhya.com/blog/2017/05/neural-network-from-scratch-in-python-and-r/
   I'm going to do it very literally the first time
   */
  proc main() {
    writeln("Hola Mundo!");
    var X = Matrix(
       [1.0,0.0,1.0,0.0],
       [1.0,0.0,1.0,1.0],
       [0.0,1.0,0.0,1.0]);
    var y = Vector([1,1,0]);

    // Variable Initialization
    const inputLayerNeurons = X.shape[1],
          hiddenlayer_neurons = 3,
          output_neurons = 1;

    /* Create Ranges for the objects */
    const inputRange = 0..X.shape[1],
          hiddenRange = 0..#3,
          outputRange = 0..#1;

    /* Weight and bias matrices
       Note that inputRange counts the number of inputs, not the dimension
       of the input.  We'll have to clarify that.
     */
    var wh: [{inputRange, hiddenRange}] real,
        //bh: [{0..0, hiddenRange}] real,
        wout: [{hiddenRange, outputRange}] real,
        bout: [{0..0, outputRange}] real;

    var bh: [hiddenRange] real;

    fillRandom(wh);
    fillRandom(bh);
    fillRandom(wout);
    fillRandom(bout);

    var bhm: [hiddenRange, hiddenRange] real;
    for i in hiddenRange{
      bhm[i,..] = bh;
    }

    for i in 1..#epoch {

      /* Forward propagation */
      //writeln("X shape: ", X.shape);
      //writeln("wh shape: ", wh.shape);
      //writeln("bh shape: ", bh.shape);
      //writeln("bhm shape: ", bhm.shape);
      //writeln("bhm:\n ", bhm);
      var hidden_layer_input1 = dot(X,wh);
      //writeln(hidden_layer_input1.shape);
      //writeln(bh.shape);
      //writeln(bhm.shape);
      /* Here, Python pulls a bullshit move
         It adds b to each ROW of dot(X,wh), which is bullshit any way you slice it.
       */
      //var hidden_layer_input = matPlus(hidden_layer_input1, bs);
      var hidden_layer_input = matPlus(hidden_layer_input1, bhm);
    }

  }

  proc sigmoid(x: real) {
    return (1/(1 + exp(-x)));
  }

  proc derivatives_sigmoid(x) {
    return x * (1-x);
  }
}


Changed my _data class in the prelude to use the new =this function.

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

class pragma "codegen data" _data { -- a c array, basically
  type t;
  pragma "rename _data_this" function this(i : integer) : t {
    return __primitive("coerce", t, i);
  }
  pragma "rename _data_set" function =this(i : integer, val : t) {
    return __primitive("array_set", this, i, val);
  }
}

-- I/O

pragma "rename _chpl_write_boolean" function write(x : boolean) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_integer" function write(x : integer) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_float" function write(x : float) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_complex" function write(x : complex) : void {
                __primitive("write", x);
}
pragma "rename _chpl_write_string" function write(x : string) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_nil" function write(x : _nilType) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_linefeed" function writeln() : void {
         __primitive("writeln");
}
pragma "rename _chpl_read_boolean" function read(inout x : boolean) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_integer" function read(inout x : integer) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_float" function read(inout x : float) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_complex" function read(inout x : complex) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_string" function read(inout x : string) : void {
         __primitive("read", x);
}

pragma "rename _chpl_tostring_boolean"
function _chpl_tostring(x : boolean, format : string) : string {
         return __primitive("coerce", string, x);
}

pragma "rename _chpl_tostring_integer"
function _chpl_tostring(x : integer, format : string) : string {
         return __primitive("coerce", string, x);
}

pragma "rename _chpl_tostring_float"
function _chpl_tostring(x : float, format : string) : string {
         return __primitive("coerce", string, x);
}

pragma "rename _chpl_tostring_complex"
function _chpl_tostring(x : complex, format : string) : string {
         return __primitive("coerce", string, x);
}

-- intrinsic type values

const false: boolean = 0;
const true: boolean = 1;

-- math

function sqrt(x: float): float {}
function abs(x: ?t): float {}  -- BLC: should be ": t"

-- boundary classes/functions

function wrap() {}

-- reductions

class reduction {
}

class sum {
}

class max {
}

class min {
}

class maxloc {
}

class minloc {
}

-- timers

class timer {
  function start();
  function stop();
  function read(): float;
}

-- memory tests (These tests will be moved to a module, once we have modules.)

function _chpl_memtest_printMemTable();
function _chpl_memtest_printMemStat();
function _chpl_memtest_resetMemStat();
function _chpl_memtest_allocAndFree();
function _chpl_memtest_freedMalloc();
function _chpl_memtest_freedWithoutMalloc();
function _chpl_memtest_reallocWithoutMalloc();
function _chpl_memtest_reallocZeroSize();
function _chpl_memtest_mallocOutOfMemory();
function _chpl_memtest_reallocOutOfMemory();

function _chpl_complex_read_hack(inout x) {
  __primitive("read", x);
}

function _chpl_complex_tostring_hack(x, format : string) : string {
  return __primitive("coerce", string, x);
}

class pragma "codegen data" _data { -- a c array, basically
  type t;
  pragma "rename _data_this" function this(i : integer) : t {
    return __primitive("coerce", t, i);
  }
  pragma "rename _data_set" function set(i : integer, val : t) {
    return __primitive("array_set", this, i, val);
  }
}

-- I/O

pragma "rename _chpl_write_boolean" function write(x : boolean) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_integer" function write(x : integer) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_float" function write(x : float) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_complex" function write(x : complex) : void {
                __primitive("write", x);
}
pragma "rename _chpl_write_string" function write(x : string) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_nil" function write(x : _nilType) : void {
         __primitive("write", x);
}
pragma "rename _chpl_write_linefeed" function writeln() : void {
         __primitive("writeln");
}
pragma "rename _chpl_read_boolean" function read(inout x : boolean) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_integer" function read(inout x : integer) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_float" function read(inout x : float) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_complex" function read(inout x : complex) : void {
         __primitive("read", x);
}
pragma "rename _chpl_read_string" function read(inout x : string) : void {
         __primitive("read", x);
}

pragma "rename _chpl_tostring_boolean"
function _chpl_tostring(x : boolean, format : string) : string {
         return __primitive("coerce", string, x);
}

pragma "rename _chpl_tostring_integer"
function _chpl_tostring(x : integer, format : string) : string {
         return __primitive("coerce", string, x);
}

pragma "rename _chpl_tostring_float"
function _chpl_tostring(x : float, format : string) : string {
         return __primitive("coerce", string, x);
}

pragma "rename _chpl_tostring_complex"
function _chpl_tostring(x : complex, format : string) : string {
         return __primitive("coerce", string, x);
}

-- intrinsic type values

const false: boolean = 0;
const true: boolean = 1;

-- math

function sqrt(x: float): float {}
function abs(x: ?t): float {}  -- BLC: should be ": t"

-- boundary classes/functions

function wrap() {}

-- reductions

class reduction {
}

class sum {
}

class max {
}

class min {
}

class maxloc {
}

class minloc {
}

-- timers

class timer {
  function start();
  function stop();
  function read(): float;
}

-- memory tests (These tests will be moved to a module, once we have modules.)

function _chpl_memtest_printMemTable();
function _chpl_memtest_printMemStat();
function _chpl_memtest_resetMemStat();
function _chpl_memtest_allocAndFree();
function _chpl_memtest_freedMalloc();
function _chpl_memtest_freedWithoutMalloc();
function _chpl_memtest_reallocWithoutMalloc();
function _chpl_memtest_reallocZeroSize();
function _chpl_memtest_mallocOutOfMemory();
function _chpl_memtest_reallocOutOfMemory();

function _chpl_complex_read_hack(inout x) {
  __primitive("read", x);
}

function _chpl_complex_tostring_hack(x, format : string) : string {
  return __primitive("coerce", string, x);
}


Changed the serial multi-dimensional path in the complex fillRandom method
to use the complex version of the chpl__these iterator. It was using the
iterator that yields reals, so only the real parts of the complex numbers were
getting assigned.

This fixes the test_2D_fillRandom_complex regression.

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

// This random number generator was written from the NPB
// random number generator.
//
// It's the linear congruential generator with
//
// m = 2^46 (the modulus)
// a = 1220703125.0 (the multiplier)
// c = 0 (the increment)
//
// It requires an odd integer for its seed.

use Time;

config param debugParRandom: bool = false;

record SeedGeneratorRecord {
  def clockMS {
    return getCurrentTime(unit=TimeUnits.microseconds):int(64);
  }
}

var SeedGenerator: SeedGeneratorRecord;

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  // These are meant to be private; internal to class; not set by user
  const r23   = 0.5**23,
        t23   = 2.0**23,
        r46   = 0.5**46,
        t46   = 2.0**46;
  const internalSeed = computeInternalSeed();

  var lock$: sync bool = false;
  var sharedCursor = internalSeed:real;
  var sharedCount: int(64) = 1;

  def computeInternalSeed() {
    var val: int(64) = seed;
    // ensure seed is odd
    if (val % 2 == 0) then val += 1;
    return val;
  }

  def initCursorVal() {
    sharedCursor = internalSeed:real;
    sharedCount = 1;
  }

  def randlc(inout x, a = arand) {
    var t1 = r23 * a;
    const a1 = floor(t1),
          a2 = a - t23 * a1;
    t1 = r23 * x;
    const x1 = floor(t1),
          x2 = x - t23 * x1;
    t1 = a1 * x2 + a2 * x1;
    const t2 = floor(r23 * t1),
          z  = t1 - t23 * t2,
          t3 = t23 * z + a2 * x2,
          t4 = floor(r46 * t3),
          x3 = t3 - t46 * t4;
    x = x3;

    return r46 * x3;
  }

  def getNext() {
    sharedCount += 1;
    return randlc(sharedCursor);
  }

  def skipToNth(in n: integral) {
    if (n <= 0) then
      halt("RandomStream.skipToNth() can only be called with positive values");
    initCursorVal();
    sharedCount = n;
    n -= 1;
    var t = arand;
    var retval = arand;
    while (n != 0) {
      const i = n / 2;
      if (2 * i != n) then
        randlc(sharedCursor, t);
      retval = randlc(t, t);
      n = i;
    }
  }

  // n is assumed to be 1..
  def getNth(n : integral) {
    if (n <= 0) then
      halt("RandomStream.getNth() can only be called with a positive value, received: ", n);
    skipToNth(n);
    return getNext();
  }

  def fillRandom(X: [] real(32)) {
    lock$;
    const startCount: int(64) = sharedCount;
    sharedCount = startCount + X.numElements;
    lock$ = false;
    if (X.rank == 1) {
      forall (x, r) in (X, chpl__these(startCount, X.numElements)) {
        x = r : real(32);
      }
    } else {
      for (x,r) in (X, chpl__these(startCount, X.numElements)) {
        x = r : real(32);
      }
    }
  }

  def fillRandom(X: [] real) {
    lock$;
    const startCount: int(64) = sharedCount;
    sharedCount = startCount + X.numElements;
    lock$ = false;
    if (X.rank == 1) {
      forall (x, r) in (X, chpl__these(startCount, X.numElements)) {
        x = r;
      }
    } else {
      for (x,r) in (X, chpl__these(startCount, X.numElements)) {
        x = r;
      }
    }
  }

  def fillRandom(X: [] complex) {
    lock$;
    const startCount: int(64) = sharedCount;
    sharedCount = startCount + 2*X.numElements;
    lock$ = false;
    if X.rank == 1 {
      forall (x, r) in (X, chpl__cplx_these(startCount, X.numElements)) do
        x = r;
    } else {
      for (x, r) in (X, chpl__cplx_these(startCount, X.numElements)) do
        x = r;
    }
  }

  //
  // The following are internal-only iterators used by the fillRandom
  // routine.  We would like to support general leaders/followers on
  // RandomStreams, but the update to the shared cursor (sharedCount)
  // is not possible to do in a safe way with our current iterator
  // interface.  Future work should help with this.
  //
  def chpl__these(startAt: int(64), numElements) {
    skipToNth(startAt);
    for i in 1..numElements {
      const val = getNext();
      yield val;
    }
  }

  def chpl__these(param tag: iterator, startAt: int(64), numElements)
      where tag == iterator.leader {
    var D: domain(1, int(64)) = [1..numElements:int(64)];
    halt("Someone's trying to call the leader");
    yield D;
  }

  def chpl__these(param tag: iterator, follower, startAt: int(64), numElements)
      where tag == iterator.follower {
    if follower.size != 1 then
      halt("RandomStream's parallel iterator cannot use multi-dimensional iterators");
    // make a local copy of the 'this' random stream class
    var locStream = new RandomStream(seed);
    locStream.skipToNth(follower(1).low + startAt);
    for i in follower {
      if (debugParRandom) then
        writeln("Doing iteration ", format("#####", i+1), " on locale ", here.id);
      const val = locStream.getNext();
      yield val;
    }
    delete locStream;
  }

  def chpl__cplx_these(startAt: int(64), numElements) {
    skipToNth(startAt);
    for i in 1..numElements {
      const val = (getNext(), getNext()):complex;
      yield val;
    }
  }

  def chpl__cplx_these(param tag: iterator, startAt: int(64), numElements)
      where tag == iterator.leader {
    var D: domain(1, int(64)) = [1..numElements:int(64)];
    halt("Someone's trying to call the leader");
    yield D;
  }

  def chpl__cplx_these(param tag: iterator, follower,
                       startAt: int(64), numElements)
      where tag == iterator.follower {
    if follower.size != 1 then
      halt("RandomStream's parallel iterator cannot use multi-dimensional iterators");
    // make a local copy of the 'this' random stream class
    var locStream = new RandomStream(seed);
    locStream.skipToNth((2*follower(1).low + startAt));
    for i in follower {
      if (debugParRandom) then
        writeln("Doing iteration ", format("#####", i+1), " on locale ", here.id);
      const val = (locStream.getNext(), locStream.getNext()):complex;
      yield val;
    }
    delete locStream;
  }

  /*  BLC: Would like to add something like this, but should
      really add a warning if the range is greater than max(int(64))
      or a real's precision something to deal with the possible
      accuracy issues...
  def fillRandom(x: [],
		 minval: x.eltType = min(x.eltType),
		 maxval: x.eltType = max(x.eltType))
               where _isIntegralType(x.eltType) {
    const numVals = maxval:real - minval:real + 1;
    for i in x.domain {
      x(i) = (minval + (getNext() * numVals)): x.eltType;
    }
  }
  */
}

def fillRandom(x:[], initseed: int(64) = SeedGenerator.clockMS) {
  var randNums = new RandomStream(initseed);

  randNums.fillRandom(x);

  delete randNums;
}

// This random number generator was written from the NPB
// random number generator.
//
// It's the linear congruential generator with
//
// m = 2^46 (the modulus)
// a = 1220703125.0 (the multiplier)
// c = 0 (the increment)
//
// It requires an odd integer for its seed.

use Time;

config param debugParRandom: bool = false;

record SeedGeneratorRecord {
  def clockMS {
    return getCurrentTime(unit=TimeUnits.microseconds):int(64);
  }
}

var SeedGenerator: SeedGeneratorRecord;

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  // These are meant to be private; internal to class; not set by user
  const r23   = 0.5**23,
        t23   = 2.0**23,
        r46   = 0.5**46,
        t46   = 2.0**46;
  const internalSeed = computeInternalSeed();

  var lock$: sync bool = false;
  var sharedCursor = internalSeed:real;
  var sharedCount: int(64) = 1;

  def computeInternalSeed() {
    var val: int(64) = seed;
    // ensure seed is odd
    if (val % 2 == 0) then val += 1;
    return val;
  }

  def initCursorVal() {
    sharedCursor = internalSeed:real;
    sharedCount = 1;
  }

  def randlc(inout x, a = arand) {
    var t1 = r23 * a;
    const a1 = floor(t1),
          a2 = a - t23 * a1;
    t1 = r23 * x;
    const x1 = floor(t1),
          x2 = x - t23 * x1;
    t1 = a1 * x2 + a2 * x1;
    const t2 = floor(r23 * t1),
          z  = t1 - t23 * t2,
          t3 = t23 * z + a2 * x2,
          t4 = floor(r46 * t3),
          x3 = t3 - t46 * t4;
    x = x3;

    return r46 * x3;
  }

  def getNext() {
    sharedCount += 1;
    return randlc(sharedCursor);
  }

  def skipToNth(in n: integral) {
    if (n <= 0) then
      halt("RandomStream.skipToNth() can only be called with positive values");
    initCursorVal();
    sharedCount = n;
    n -= 1;
    var t = arand;
    var retval = arand;
    while (n != 0) {
      const i = n / 2;
      if (2 * i != n) then
        randlc(sharedCursor, t);
      retval = randlc(t, t);
      n = i;
    }
  }

  // n is assumed to be 1..
  def getNth(n : integral) {
    if (n <= 0) then
      halt("RandomStream.getNth() can only be called with a positive value, received: ", n);
    skipToNth(n);
    return getNext();
  }

  def fillRandom(X: [] real(32)) {
    lock$;
    const startCount: int(64) = sharedCount;
    sharedCount = startCount + X.numElements;
    lock$ = false;
    if (X.rank == 1) {
      forall (x, r) in (X, chpl__these(startCount, X.numElements)) {
        x = r : real(32);
      }
    } else {
      for (x,r) in (X, chpl__these(startCount, X.numElements)) {
        x = r : real(32);
      }
    }
  }

  def fillRandom(X: [] real) {
    lock$;
    const startCount: int(64) = sharedCount;
    sharedCount = startCount + X.numElements;
    lock$ = false;
    if (X.rank == 1) {
      forall (x, r) in (X, chpl__these(startCount, X.numElements)) {
        x = r;
      }
    } else {
      for (x,r) in (X, chpl__these(startCount, X.numElements)) {
        x = r;
      }
    }
  }

  def fillRandom(X: [] complex) {
    lock$;
    const startCount: int(64) = sharedCount;
    sharedCount = startCount + 2*X.numElements;
    lock$ = false;
    if X.rank == 1 {
      forall (x, r) in (X, chpl__cplx_these(startCount, X.numElements)) do
        x = r;
    } else {
      for (x, r) in (X, chpl__these(startCount, X.numElements)) do
        x = r;
    }
  }

  //
  // The following are internal-only iterators used by the fillRandom
  // routine.  We would like to support general leaders/followers on
  // RandomStreams, but the update to the shared cursor (sharedCount)
  // is not possible to do in a safe way with our current iterator
  // interface.  Future work should help with this.
  //
  def chpl__these(startAt: int(64), numElements) {
    skipToNth(startAt);
    for i in 1..numElements {
      const val = getNext();
      yield val;
    }
  }

  def chpl__these(param tag: iterator, startAt: int(64), numElements)
      where tag == iterator.leader {
    var D: domain(1, int(64)) = [1..numElements:int(64)];
    halt("Someone's trying to call the leader");
    yield D;
  }

  def chpl__these(param tag: iterator, follower, startAt: int(64), numElements)
      where tag == iterator.follower {
    if follower.size != 1 then
      halt("RandomStream's parallel iterator cannot use multi-dimensional iterators");
    // make a local copy of the 'this' random stream class
    var locStream = new RandomStream(seed);
    locStream.skipToNth(follower(1).low + startAt);
    for i in follower {
      if (debugParRandom) then
        writeln("Doing iteration ", format("#####", i+1), " on locale ", here.id);
      const val = locStream.getNext();
      yield val;
    }
    delete locStream;
  }

  def chpl__cplx_these(startAt: int(64), numElements) {
    skipToNth(startAt);
    for i in 1..numElements {
      const val = (getNext(), getNext()):complex;
      yield val;
    }
  }

  def chpl__cplx_these(param tag: iterator, startAt: int(64), numElements)
      where tag == iterator.leader {
    var D: domain(1, int(64)) = [1..numElements:int(64)];
    halt("Someone's trying to call the leader");
    yield D;
  }

  def chpl__cplx_these(param tag: iterator, follower,
                       startAt: int(64), numElements)
      where tag == iterator.follower {
    if follower.size != 1 then
      halt("RandomStream's parallel iterator cannot use multi-dimensional iterators");
    // make a local copy of the 'this' random stream class
    var locStream = new RandomStream(seed);
    locStream.skipToNth((2*follower(1).low + startAt));
    for i in follower {
      if (debugParRandom) then
        writeln("Doing iteration ", format("#####", i+1), " on locale ", here.id);
      const val = (locStream.getNext(), locStream.getNext()):complex;
      yield val;
    }
    delete locStream;
  }

  /*  BLC: Would like to add something like this, but should
      really add a warning if the range is greater than max(int(64))
      or a real's precision something to deal with the possible
      accuracy issues...
  def fillRandom(x: [],
		 minval: x.eltType = min(x.eltType),
		 maxval: x.eltType = max(x.eltType))
               where _isIntegralType(x.eltType) {
    const numVals = maxval:real - minval:real + 1;
    for i in x.domain {
      x(i) = (minval + (getNext() * numVals)): x.eltType;
    }
  }
  */
}

def fillRandom(x:[], initseed: int(64) = SeedGenerator.clockMS) {
  var randNums = new RandomStream(initseed);

  randNums.fillRandom(x);

  delete randNums;
}


Remove fake complex conversions.

Remove fake complex conversions.

Complex coercions wrappers work fine.  The problem is that
the front end defines two different "complex" types: one
is a class defined in _chpl_complex and the other is
a BUILTIN_TYPE defined in type.cpp.

Either one of these will work, both create problems.  Steve: do you
want to pick one to remove?

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

pragma "rename _chpl_complex" record complex {
  var re : float;
  var im : float;
}

pragma "rename _chpl_write_complex"
function write(x : complex) {
  write(x.re, " + ", x.im, "i");
}

pragma "no codegen"
pragma "rename _chpl_read_complex"
function read(inout x : complex) {
  _chpl_complex_read_hack(x);
}

pragma "no codegen"
pragma "rename _chpl_tostring_complex"
function _chpl_tostring(x : complex, format : string) : string {
  return _chpl_complex_tostring_hack(x, format);
}

pragma "omit for noanalysis" function +(x : complex, y : complex)
  return complex(x.re + y.re, x.im + y.im);

pragma "omit for noanalysis" function -(x : complex, y : complex)
  return complex(x.re - y.re, x.im - y.im);

pragma "omit for noanalysis" function *(x : complex, y : complex)
  return complex(x.re*y.re - x.im*y.im, x.im*y.re + x.re*y.im);

pragma "omit for noanalysis" function /(x : complex, y : complex)
  return let d = y.re*y.re + y.im*y.im in
    complex((x.re*y.re + x.im*y.im)/d, (x.im*y.re - x.re*y.im)/d);

-- Get rid of below when coercion wrapper is working

pragma "omit for noanalysis" function +(x : complex, y : float)
  return complex(x.re + y, x.im);

pragma "omit for noanalysis" function +(x : float, y : complex)
  return complex(x + y.re, y.im);

pragma "omit for noanalysis" function -(x : complex, y : float)
  return complex(x.re - y, x.im);

pragma "omit for noanalysis" function -(x : float, y : complex)
  return complex(x - y.re, -y.im);

pragma "omit for noanalysis" function *(x : complex, y : float)
  return complex(x.re*y, x.im*y);

pragma "omit for noanalysis" function *(x : float, y : complex)
  return complex(x*y.re, x*y.im);

pragma "omit for noanalysis" function /(x : complex, y : float)
  return complex(x.re/y, x.im/y);

pragma "omit for noanalysis" function /(x : float, y : complex)
  return let d = y.re*y.re + y.im*y.im in
    complex(x*y.re/d, -x*y.im/d);


Using newest readline

Using newest readline

extern proc memcpy(a:[], b, len);

var barrier : atomic int;

proc string.toBytes() var {
   var b : [1..this.length] uint(8);
   memcpy(b, this, this.length);
   return b;
}

const pairs = "ATCGGCTAUAMKRYWWSSYRKMVBHDDHBVNN\n\n".toBytes();
var table : [1..128] uint(8);

proc main() {
  var info : string;
  var temp : string;
  var inFile = openfd(0); // stdin
  const fileLen = inFile.length();
  var data : [1..fileLen] uint(8);
  var r = inFile.reader(kind=ionative, locking=false);

  var numRead = 1;
  const lineSize = 61;
  var idx = 1;
  while numRead <= fileLen && r.readline(data, numRead, idx) do { idx += numRead; }

  forall i in 1..pairs.size by 2 {
    table[pairs[i]] = pairs[i+1];      // uppercase
    if pairs[i] != ascii("\n") then
      table[pairs[i] + 32] = pairs[i+1]; // lowercase
  }

  var end = data.size - 1;

  while true {
    var from = end;
    while data[from] != ascii(">") do from -= 1;

    begin process(data, from, end);

    end = from - 2;
    if end <= 0 then break;
  }

  barrier.waitFor(3);

  var f = openfd(1);
  var binout = f.writer(iokind.native, locking=false);
  binout.write(data);
}

proc process(ref data : [],in from : int, end : int) {
  while data[from] != 0xa do from += 1;
  from += 1;

  const len = end - from;
  const off = 60 - (len % 61);

  if off > 0 {
    var m = from + 60 - off;
    while m < end {
      for i in 0..off-1 by -1 do data[m+1+i] = data[m+i];
      data[m+1] = 0xa;
      m += 61;
    }
  }

  const middle = (end-from)/2;
  for i in 0 .. middle do {
    const c = table[data[from+i]];
    data[from+i] = table[data[end-i]];
    data[end-i] = c;
  }

  barrier.add(1);
}

extern proc memcpy(a:[], b, len);

var barrier : atomic int;

proc string.toBytes() var {
   var b : [1..this.length] uint(8);
   memcpy(b, this, this.length);
   return b;
}

const pairs = "ATCGGCTAUAMKRYWWSSYRKMVBHDDHBVNN\n\n".toBytes();
var table : [1..128] uint(8);

proc main() {
  var info : string;
  var temp : string;
  var inFile = openfd(0); // stdin
  const fileLen = inFile.length();
  var data : [1..fileLen] uint(8);
  var r = inFile.reader(kind=ionative, locking=false);

  var numRead = 1;
  const lineSize = 61;
  while numRead <= fileLen && r.readline(data[numRead..fileLen], numRead) do {}

  forall i in 1..pairs.size by 2 {
    table[pairs[i]] = pairs[i+1];      // uppercase
    if pairs[i] != ascii("\n") then
      table[pairs[i] + 32] = pairs[i+1]; // lowercase
  }

  var end = data.size - 1;

  while true {
    var from = end;
    while data[from] != ascii(">") do from -= 1;

    begin process(data, from, end);

    end = from - 2;
    if end <= 0 then break;
  }

  barrier.waitFor(3);

  var f = openfd(1);
  var binout = f.writer(iokind.native, locking=false);
  binout.write(data);
}

proc process(ref data : [],in from : int, end : int) {
  while data[from] != 0xa do from += 1;
  from += 1;

  const len = end - from;
  const off = 60 - (len % 61);

  if off > 0 {
    var m = from + 60 - off;
    while m < end {
      for i in 0..off-1 by -1 do data[m+1+i] = data[m+i];
      data[m+1] = 0xa;
      m += 61;
    }
  }

  const middle = (end-from)/2;
  for i in 0 .. middle do {
    const c = table[data[from+i]];
    data[from+i] = table[data[end-i]];
    data[end-i] = c;
  }

  barrier.add(1);
}


Fixed thinko in test, changing for to forall.

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

def bar(i: int) return [1..3];

var A = bar(1..3);

def foo() {
  for i in 1..3 do
    for j in A(i) do
      yield j;
}

def foo(param tag: iterator) where tag == iterator.leader {
  for i in 1..3 do
    yield A(i);
}

def foo(param tag: iterator, follower) where tag == iterator.follower {
  for j in follower do
    yield j;
}

for i in foo() do
  writeln(i);
writeln();
forall i in foo() do
  writeln(i);

def bar(i: int) return [1..3];

var A = bar(1..3);

def foo() {
  for i in 1..3 do
    for j in A(i) do
      yield j;
}

def foo(param tag: iterator) where tag == iterator.leader {
  for i in 1..3 do
    yield A(i);
}

def foo(param tag: iterator, follower) where tag == iterator.follower {
  for j in follower do
    yield j;
}

for i in foo() do
  writeln(i);
writeln();
for i in foo() do
  writeln(i);


removing main() method

removing main() method

/*
 View NumSuch `on Github <https://github.com/buddha314/numsuch>`_

 It would be fun to do some math! But not yet I guess

 Let's try out a code block:

 .. code-block:: chapel

    var d: domain(1) = {1..71};

 */
module NumSuch{
  use LinearAlgebra,
      Core,
      GraphUtils,
      PeelPropagation,
      GraphEntropy,
      Stats;
}

/*
 View NumSuch `on Github <https://github.com/buddha314/numsuch>`_

 It would be fun to do some math! But not yet I guess

 Let's try out a code block:

 .. code-block:: chapel

    var d: domain(1) = {1..71};

 */
module NumSuch{
  use LinearAlgebra,
      Core,
      GraphUtils,
      PeelPropagation,
      GraphEntropy,
      Stats;

  proc main() {
    writeln("don't expect much...");
  }
}


Snapshot.

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

use BitOps;
use Random;
use Time;

use HPCCProblemSize;

param radix = 4;

param numVectors = 2;
type elemType = complex(128);

config const n = computeProblemSize(elemType, numVectors, returnLog2 = true);
const m = 2**n;

config const epsilon = 2.0 ** -51.0,
             threshold = 16.0;

config const useRandomSeed = true,
             seed = if useRandomSeed then SeedGenerator.clockMS else 314159265;

config const printParams = true,
             printArrays = false,
             printStats = true;

def main() {
  printConfiguration();

  const TwiddleDom = [0..m/4);
  var Twiddles: [TwiddleDom] elemType;

  const ProblemDom = [0..m);
  var Z, z: [ProblemDom] elemType;

  initVectors(Twiddles, z);

  const startTime = getCurrentTime();

  Z = conjg(z);
  bitReverseShuffle(Z);
  dfft(Z, Twiddles);

  const execTime = getCurrentTime() - startTime;

  const validAnswer = verifyResults(z, Z, Twiddles);
  printResults(validAnswer, execTime);
}

def dfft(Z, Twiddles) {
  cft1st(Z, Twiddles);

  var span = radix;

  for i in [radix..n) by 2 {
    if (i <= n/2) then
      cftmd1(span, Z, Twiddles);
    else
      cftmd2(span, Z, Twiddles);
    span *= radix;
  }

  if (radix*span == Z.numElements) then
    forall j in [0..span) do
      butterfly(1.0, 1.0, 1.0, Z[j..j+3*span by span]);
  else
    forall j in [0..span) {
      const a = Z(j),
            b = Z(j+span);
      Z(j)      = a + b;
      Z(j+span) = a - b;
    }
}

def printConfiguration() {
  if (printParams) then printProblemSize(elemType, numVectors, m);
}

def initVectors(Twiddles, z) {
  computeTwiddles(Twiddles);
  bitReverseShuffle(Twiddles);

  fillRandom(z, seed);

  if (printArrays) {
    writeln("After initialization, Twiddles is: ", Twiddles, "\n");
    writeln("z is: ", z, "\n");
  }
}

def computeTwiddles(Twiddles) {
  const numTwdls = Twiddles.numElements,
        delta = 2.0 * atan(1.0) / numTwdls;

  Twiddles(0) = 1.0;
  Twiddles(numTwdls/2) = let x = cos(delta * numTwdls/2)
                          in (x, x):complex;
  forall i in [1..numTwdls/2) {
    const x = cos(delta*i),
          y = sin(delta*i);
    Twiddles(i)            = (x, y):complex;
    Twiddles(numTwdls - i) = (y, x):complex;
  }
}

def bitReverseShuffle(Vect: [?Dom]) {
  const numBits = log2(Vect.numElements),
        Perm: [i in Dom] index(Dom) = bitReverse(i, revBits = numBits),
        Temp = Vect(Perm);
  Vect = Temp;
}

def bitReverse(val: ?valType, revBits = 64) {
  param mask = 0x0102040810204080;
  const valReverse64 = bitMatMultOr(mask, bitMatMultOr(val:uint(64), mask)),
        valReverse = bitRotLeft(valReverse64, revBits);
  return valReverse: valType;
}

def verifyResults(z, Z, Twiddles) {
  if (printArrays) then writeln("After FFT, Z is: ", Z, "\n");

  Z = conjg(Z) / m;
  bitReverseShuffle(Z);
  dfft(Z, Twiddles);

  if (printArrays) then writeln("After inverse FFT, Z is: ", Z, "\n");

  var maxerr = max reduce sqrt((z.re - Z.re)**2 + (z.im - Z.im)**2);
  maxerr /= (epsilon * n);
  if (printStats) then writeln("error = ", maxerr);

  return (maxerr < threshold);
}

def printResults(successful, execTime) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Execution time = ", execTime);
    writeln("Performance (Gflop/s) = ", 5.0 * (m * n) / execTime / 1.0e-9);
  }
}

def butterfly(wk1, wk2, wk3, inout A:[1..radix]) {
  var x0 = A(1) + A(2),
      x1 = A(1) - A(2),
      x2 = A(3) + A(4),
      x3rot = (A(3) - A(4))*1.0i;

  A(1) = x0 + x2;
  x0 -= x2;
  A(3) = wk2 * x0;
  x0 = x1 + x3rot;
  A(2) = wk1 * x0;
  x0 = x1 - x3rot;
  A(4) = wk3 * x0;
}

def cft1st(A, Twiddles) {
  var x0 = A(0) + A(1),
      x1 = A(0) - A(1),
      x2 = A(2) + A(3),
      x3rot = (A(2) - A(3))*1.0i;
  const wk1r = Twiddles(1).re;

  A(0) = x0 + x2;
  A(2) = x0 - x2;
  A(1) = x1 + x3rot;
  A(3) = x1 - x3rot;

  x0 = A(4) + A(5);
  x1 = A(4) - A(5);
  x2 = A(6) + A(7);
  const x3 = A(6) - A(7);  // BLC: try to eliminate this
  x3rot = (A(6) - A(7))*1.0i;
  A(4) = x0 + x2;
  A(6) = (x2.im - x0.im, x0.re - x2.re):complex;
  x0 = x1 + x3rot;
  A(5) = wk1r * (x0.re - x0.im, x0.re + x0.im):complex;
  x0 = (x3.im + x1.re, x3.re - x1.im):complex;
  A(7) = wk1r * (x0.im - x0.re, x0.im + x0.re):complex;

  forall (j,k1) in ([8..A.numElements) by 8, 1..) {
    var wk2 = Twiddles(k1),
        wk1 = Twiddles(2*k1),
        wk3 = (wk1.re - 2* wk2.im * wk1.im,
               2 * wk2.im * wk1.re - wk1.im):complex;

    butterfly(wk1, wk2, wk3, A[j..j+3]);

    wk1 = Twiddles(2*k1+1);
    wk3 = (wk1.re - 2*wk2.re * wk1.im,
           2*wk2.re * wk1.re - wk1.im):complex;
    wk2 = wk2*1.0i;
    butterfly(wk1, wk2, wk3, A[j+4..j+7]);
  }
}

def cftmd0(span, A, Twiddles) {
  const wk1r = Twiddles(1).re,
        m = radix*span;

  forall j in [0..span) do
    butterfly(1.0, 1.0, 1.0, A[j..j+3*span by span]);

  forall j in [m..m+span) do
    butterfly((wk1r, wk1r):complex, 1.0i, (-wk1r, wk1r):complex,
              A[j..j+3*span by span]);
}

def cftmd1(span, A, Twiddles) {
  const m = radix*span,
        m2 = 2*m;

  cftmd0(span, A, Twiddles);
  forall (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = Twiddles(k1),
        wk1 = Twiddles(2*k1),
        wk3 = interp1(wk1, wk2);
    for j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = Twiddles(2*k1+1);
    wk3 = interp2(wk1, wk2);

    for j in [k+m..k+m+span) do
      butterfly(wk1, wk2*1.0i, wk3, A[j..j+3*span by span]);
  }
}

def cftmd2(span, A, Twiddles) {
  const m = radix*span,
        m2 = 2*m,
        numElems = A.numElements;

  cftmd0(span, A, Twiddles);
  if (m2 >= numElems) return;
  if (m2 >= numElems / 8) {
    cftmd21(span, A, Twiddles);
    return;
  }

  forall j in [0..span) {
    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = Twiddles(k1),
            wk1 = Twiddles(k1 + k1),
            wk3 = interp1(wk1, wk2);
      butterfly(wk1, wk2, wk3, A[j+k..j+k+3*span by span]);
    }

    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = Twiddles(k1),
            wk1 = Twiddles(2*k1 + 1),
            wk3 = interp2(wk1, wk2);
      wk2 = wk2*1.0i;

      butterfly(wk1, wk2, wk3, A[j+k+m..j+k+m+3*span by span]);
    }
  }
}

def cftmd21(span, A, Twiddles) {
  const m = radix*span,
        m2 = 2*m;

  for (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = Twiddles(k1),
        wk1 = Twiddles(2*k1),
        wk3 = interp1(wk1, wk2);

    forall j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = Twiddles(2*k1 + 1);
    wk3 = interp2(wk1, wk2);
    wk2 = wk2*1.0i;

    forall j in [k+m..k+m+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);
  }
}

def interp1(a, b)
  return (a.re - 2*b.im*a.im, 2*b.im*a.re - a.im):complex;

def interp2(a, b)
  return (a.re - 2*b.re*a.im, 2*b.re*a.re - a.im):complex;

// TODO: clean up complex arithmetic
use BitOps;
use Random;
use Time;

use HPCCProblemSize;

param radix = 4;

param numVectors = 2;
type elemType = complex(128);

config const n = computeProblemSize(elemType, numVectors, returnLog2 = true);
const m = 2**n;

config const epsilon = 2.0 ** -51.0,
             threshold = 16.0;

config const useRandomSeed = true,
             seed = if useRandomSeed then SeedGenerator.clockMS else 314159265;

config const printParams = true,
             printArrays = false,
             printStats = true;

def main() {
  printConfiguration();

  const TwiddleDom = [0..m/4);
  var Twiddles: [TwiddleDom] elemType;

  const ProblemDom = [0..m);
  var Z, z: [ProblemDom] elemType;

  initVectors(Twiddles, z);

  const startTime = getCurrentTime();

  Z = conjg(z);
  bitReverseShuffle(Z);
  dfft(Z, Twiddles);

  const execTime = getCurrentTime() - startTime;

  const validAnswer = verifyResults(z, Z, Twiddles);
  printResults(validAnswer, execTime);
}

def dfft(Z, Twiddles) {
  cft1st(Z, Twiddles);

  var span = radix;

  for i in [radix..n) by 2 {
    if (i <= n/2) then
      cftmd1(span, Z, Twiddles);
    else
      cftmd2(span, Z, Twiddles);
    span *= radix;
  }

  if (radix*span == Z.numElements) then
    forall j in [0..span) do
      butterfly(1.0, 1.0, 1.0, Z[j..j+3*span by span]);
  else
    forall j in [0..span) {
      const a = Z(j),
            b = Z(j+span);
      Z(j)      = a + b;
      Z(j+span) = a - b;
    }
}

def printConfiguration() {
  if (printParams) then printProblemSize(elemType, numVectors, m);
}

def initVectors(Twiddles, z) {
  computeTwiddles(Twiddles);
  bitReverseShuffle(Twiddles);

  fillRandom(z, seed);

  if (printArrays) {
    writeln("After initialization, Twiddles is: ", Twiddles, "\n");
    writeln("z is: ", z, "\n");
  }
}

def computeTwiddles(Twiddles) {
  const numTwdls = Twiddles.numElements,
        delta = 2.0 * atan(1.0) / numTwdls;

  Twiddles(0) = 1.0;
  Twiddles(numTwdls/2) = let x = cos(delta * numTwdls/2)
                          in (x, x):complex;
  forall i in [1..numTwdls/2) {
    const x = cos(delta*i),
          y = sin(delta*i);
    Twiddles(i)            = (x, y):complex;
    Twiddles(numTwdls - i) = (y, x):complex;
  }
}

def bitReverseShuffle(Vect: [?Dom]) {
  const numBits = log2(Vect.numElements),
        Perm: [i in Dom] index(Dom) = bitReverse(i, revBits = numBits),
        Temp = Vect(Perm);
  Vect = Temp;
}

def bitReverse(val: ?valType, revBits = 64) {
  param mask = 0x0102040810204080;
  const valReverse64 = bitMatMultOr(mask, bitMatMultOr(val:uint(64), mask)),
        valReverse = bitRotLeft(valReverse64, revBits);
  return valReverse: valType;
}

def verifyResults(z, Z, Twiddles) {
  if (printArrays) then writeln("After FFT, Z is: ", Z, "\n");

  Z = conjg(Z) / m;
  bitReverseShuffle(Z);
  dfft(Z, Twiddles);

  if (printArrays) then writeln("After inverse FFT, Z is: ", Z, "\n");

  var maxerr = max reduce sqrt((z.re - Z.re)**2 + (z.im - Z.im)**2);
  maxerr /= (epsilon * n);
  if (printStats) then writeln("error = ", maxerr);

  return (maxerr < threshold);
}

def printResults(successful, execTime) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Execution time = ", execTime);
    writeln("Performance (Gflop/s) = ", 5.0 * (m * n) / execTime / 1.0e-9);
  }
}

def butterfly(wk1, wk2, wk3, inout A:[1..radix]) {
  var x0 = A(1) + A(2),
      x1 = A(1) - A(2),
      x2 = A(3) + A(4),
      x3rot = (A(3) - A(4))*1.0i;

  A(1) = x0 + x2;
  x0 -= x2;
  A(3) = wk2 * x0;
  x0 = x1 + x3rot;
  A(2) = wk1 * x0;
  x0 = x1 - x3rot;
  A(4) = wk3 * x0;
}

def cft1st(A, Twiddles) {
  var x0 = A(0) + A(1),
      x1 = A(0) - A(1),
      x2 = A(2) + A(3),
      x3rot = (A(2) - A(3))*1.0i;
  const wk1r = Twiddles(1).re;

  A(0) = x0 + x2;
  A(2) = x0 - x2;
  A(1) = x1 + x3rot;
  A(3) = x1 - x3rot;

  x0 = A(4) + A(5);
  x1 = A(4) - A(5);
  x2 = A(6) + A(7);
  const x3 = A(6) - A(7);  // BLC: try to eliminate this
  x3rot = (A(6) - A(7))*1.0i;
  A(4) = x0 + x2;
  A(6) = (x2.im - x0.im, x0.re - x2.re):complex;
  x0 = x1 + x3rot;
  A(5) = wk1r * (x0.re - x0.im, x0.re + x0.im):complex;
  x0 = (x3.im + x1.re, x3.re - x1.im):complex;
  A(7) = wk1r * (x0.im - x0.re, x0.im + x0.re):complex;

  forall (j,k1) in ([8..A.numElements) by 8, 1..) {
    var wk2 = Twiddles(k1),
        wk1 = Twiddles(2*k1),
        wk3 = (wk1.re - 2* wk2.im * wk1.im,
               2 * wk2.im * wk1.re - wk1.im):complex;

    butterfly(wk1, wk2, wk3, A[j..j+3]);

    wk1 = Twiddles(2*k1+1);
    wk3 = (wk1.re - 2*wk2.re * wk1.im,
           2*wk2.re * wk1.re - wk1.im):complex;
    wk2 = wk2*1.0i;
    butterfly(wk1, wk2, wk3, A[j+4..j+7]);
  }
}

def cftmd0(span, A, Twiddles) {
  const wk1r = Twiddles(1).re,
        m = radix*span;

  forall j in [0..span) do
    butterfly(1.0, 1.0, 1.0, A[j..j+3*span by span]);

  forall j in [m..m+span) do
    butterfly((wk1r, wk1r):complex, 1.0i, (-wk1r, wk1r):complex,
              A[j..j+3*span by span]);
}

def cftmd1(span, A, Twiddles) {
  const m = radix*span,
        m2 = 2*m;

  cftmd0(span, A, Twiddles);
  forall (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = Twiddles(k1),
        wk1 = Twiddles(2*k1),
        wk3 = interp1(wk1, wk2);
    //(wk1.re - 2 * wk2.im * wk1.im,
    // 2 * wk2.im * wk1.re - wk1.im):complex;
    for j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = Twiddles(2*k1+1);
    wk3 = interp2(wk1, wk2);
    //(wk1.re - 2 * wk2.re * wk1.im,
    // 2 * wk2.re * wk1.re - wk1.im):complex;

    for j in [k+m..k+m+span) do
      butterfly(wk1, wk2*1.0i, wk3, A[j..j+3*span by span]);
  }
}

def cftmd2(span, A, Twiddles) {
  const m = radix*span,
        m2 = 2*m,
        numElems = A.numElements;

  cftmd0(span, A, Twiddles);
  if (m2 >= numElems) return;
  if (m2 >= numElems / 8) {
    cftmd21(span, A, Twiddles);
    return;
  }

  forall j in [0..span) {
    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = Twiddles(k1),
            wk1 = Twiddles(k1 + k1),
            wk3 = interp1(wk1, wk2);
      //            wk3 = (wk1.re - 2*wk2.im * wk1.im,
      //                   2 * wk2.im * wk1.re - wk1.im):complex;
      butterfly(wk1, wk2, wk3, A[j+k..j+k+3*span by span]);
    }

    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = Twiddles(k1),
            wk1 = Twiddles(2*k1 + 1),
            wk3 = interp2(wk1, wk2);
      //(wk1.re - 2*wk2.re * wk1.im,
      //                   2*wk2.re * wk1.re - wk1.im):complex;
      wk2 = wk2*1.0i;

      butterfly(wk1, wk2, wk3, A[j+k+m..j+k+m+3*span by span]);
    }
  }
}

def cftmd21(span, A, Twiddles) {
  const m = radix*span,
        m2 = 2*m;

  for (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = Twiddles(k1),
        wk1 = Twiddles(2*k1),
        wk3 = interp1(wk1, wk2);
    //        wk3 = (wk1.re - 2*wk2.im * wk1.im,
    //               2*wk2.im * wk1.re - wk1.im):complex;

    forall j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = Twiddles(2*k1 + 1);
    wk3 = interp2(wk1, wk2);
    //    wk3 = (wk1.re - 2*wk2.re * wk1.im,
    //           2*wk2.re * wk1.re - wk1.im):complex;
    wk2 = wk2*1.0i;

    forall j in [k+m..k+m+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);
  }
}

def interp1(a, b)
  return (a.re - 2*b.im*a.im,
          2*b.im*a.re - a.im): complex;

def interp2(a, b)
  return (a.re - 2*b.re*a.im,
          2*b.re*a.re - a.im): complex;