Remove lclIdxType from my block distribution experiments

As we discussed at the code review a few weeks back, my
implementation was not correct enough with its use of
locIdxType and glbIdxType (in particular, it was assumed
that values could be converted back and forth between
them willy-nilly), and that making it correct would
complicate the implementation more than it was worth at
this stage of the distribution studies.  For this
reason, replaced the global and local index types with
a single index type idxType.

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

// TODO: Have the global class leader/follower iterators defer to the
// local class leader/followers once we're within a locale?

// TODO: Make these into an official distribution?

config param debugBradsBlock1D = false;

// TODO: This would need to be moved somewhere more standard
enum IteratorType { leader, follower };

//
// The distribution class
//
class Block1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type idxType = int(64);

  //
  // The bounding box that defines the block distribution
  //
  const boundingBox: domain(1, idxType);

  //
  // a domain and array describing the set of target locales to which
  // the indices are mapped
  //
  const targetLocDom: domain(1);
  const targetLocs: [targetLocDom] locale;
  //
  // NOTE: originally I wrote this as:
  //
  //   const targetLocs = Locales;
  //   const targetLocDom = targetLocs.domain;
  //
  // but this had two interesting problems:
  //
  // (1) the capture of the targetLocs array only makes a copy of the
  //     array, not of its domain, so if someone were to resize the
  //     LocaleSpace domain outside of the class, this would inadvertantly
  //     get resized as well.  We really want to make a copy of both the
  //     domain and array (which I now do in the constructor).
  //
  // (2) Initializing targetLocs with Locales prevents us from assigning
  //     anything other than a numLocales-ary array of locales to it
  //     which is stricter than I wanted -- I only wanted to restrict
  //     us to a 1D array of locales.  In our current model, this would
  //     require us to have some sort of [domain(1)] locale declaration
  //     to make it work.  Or we could think about whether a model in
  //     which only the array's static type information is inferred
  //     for a field?

  //
  // an array of local distribution class descriptors -- set up in
  // initialize() below
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  // WANTED:
  //
  /*
  const locDist: [loc in targetLocDom] LocBlock1DDist(idxType)
                   = on targetLocs(loc) do
                       new LocBlock1DDist(idxType, targetLocDom.order(loc), this);
  */
  //
  // but this doesn't work yet because an array forall initializer
  // apparently can't refer to a local member domain.
  //
  const locDist: [targetLocDom] LocBlock1DDist(idxType);
  //
  // WORKAROUND: Initialize in the constructor instead
  //

  def Block1DDist(type idxType = int(64), bbox: domain(1, idxType),
                  targetLocales: [?targetLocalesDomain] locale) {
    boundingBox = bbox;
    targetLocDom = targetLocalesDomain;
    targetLocs = targetLocales;
    //
    // WANT TO DO:
    /*
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(idxType, locid, this);
    */
    //
    // BUT results in a _heapAlloc(type int(64)) call.
    // WORKAROUND:
    helpConstruct();
  }
  //
  // WORKAROUND CONTINUED:
  //
  def helpConstruct() {
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(idxType, locid, this);
  }
  //
  // END WORKAROUND
  //

  def writeThis(x:Writer) {
    x.writeln("BradsBlock1DPar");
    x.writeln("---------------");
    x.writeln("distributes: ", boundingBox);
    x.writeln("across locales: ", targetLocs);
    x.writeln("indexed via: ", targetLocDom);
    x.writeln("resulting in: ");
    for locid in targetLocDom do
      x.writeln("  [", locid, "] ", locDist(locid));
  }

  //
  // Create a new domain over this distribution with the given index
  // set (inds) and global index type (idxType, idxType)
  //
  // TODO: What would we do if domIdxType did not match idxType?
  //
  def newDomain(inds, type domIdxType = idxType) where domIdxType == idxType {
    return new Block1DDom(idxType, this, inds);
  }

  //
  // compute what chunk of inds is owned by a given locale -- assumes
  // it's being called on the locale in question
  //
  def getChunk(inds, locid) {
    // use domain slicing to get the intersection between what the
    // locale owns and the domain's index set

    // TODO: Should this be able to be written as myChunk[inds] ???
    return locDist(locid).myChunk(inds.dim(1));
  }

  //
  // Determine which locale owns a particular index
  //
  def ind2locInd(ind: idxType) {
    const ind0 = ind - boundingBox.low;
    const loc0 = (ind0 * targetLocs.numElements) / boundingBox.numIndices;
    const locInd = loc0: index(targetLocs.domain) + targetLocs.domain.low;
    return locInd;
  }

  def ind2loc(ind: idxType) {
    return targetLocs(ind2locInd(ind));
  }
}

//
// A per-locale local distribution class
//
class LocBlock1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type idxType;

  //
  // This stores the piece of the global bounding box owned by
  // the locale.  Note that my original guess that we'd want
  // to use lclIdxType here is wrong since we're talking about
  // the section of the global index space owned by the locale.
  //
  const myChunk: domain(1, idxType);
  const locid: int;
  const loc: locale;

  //
  // Compute what chunk of index(1) is owned by the current locale
  // Arguments:
  //
  def LocBlock1DDist(type idxType,
                     _locid: int, // the locale index from the target domain
                     dist: Block1DDist(idxType) // reference to glob dist
                     ) {
    locid = _locid;
    loc = dist.targetLocs(locid);
    const lo = dist.boundingBox.low;
    const hi = dist.boundingBox.high;
    const numelems = hi - lo + 1;
    const numlocs = dist.targetLocDom.numIndices;
    const locid0 = dist.targetLocDom.order(locid); // 0-based locale ID
    const blo = if (locid0 == 0) then min(idxType)
                else procToData((numelems: real * locid0) / numlocs, lo);
    const bhi = if (locid0 == numlocs - 1) then max(idxType)
                else procToData((numelems: real * (locid0+1)) / numlocs, lo) - 1;
    myChunk = [blo..bhi];
    if debugBradsBlock1D then
      writeln("locale ", locid, " owns ", myChunk);
  }

  //
  // a helper function for mapping processors to indices
  //
  def procToData(x, lo)
    return (lo + (x: lo.type) + (x:real != x:int:real));

  def writeThis(x:Writer) {
    x.write("locale ", loc.id, " owns chunk: ", myChunk);
  }
}

//
// The global domain class
//
class Block1DDom {
  //
  // The index types of the domain
  //
  type idxType;

  //
  // a pointer to the parent distribution
  //
  const dist: Block1DDist(idxType);

  //
  // a domain describing the complete domain
  //
  const whole: domain(1, idxType);

  //
  // an array of local domain class descriptors -- set up in
  // initialize() below
  //
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locDoms: [dist.targetLocDom] LocBlock1DDom(idxType);

  def initialize() {
    for locid in dist.targetLocDom do
      on dist.targetLocs(locid) do
        locDoms(locid) = new LocBlock1DDom(idxType, this,
                                           dist.getChunk(whole, locid));
    if debugBradsBlock1D then
      [loc in dist.targetLocDom] writeln(loc, " owns ", locDoms(loc));
  }

  //
  // the iterator for the domain -- currently sequential
  //
  def these() {
    for blk in locDoms do
      // TODO: Would want to do something like:
      // on blk do
      // But can't currently have yields in on clauses
        for ind in blk do
          yield ind;
  }

  //
  // this is the parallel iterator for the global domain, following
  // a variation on Steve and David's proposals -- I've split the
  // single iterator into two iterators, distinguished by where
  // clauses on their parameter values.  This permits each to only
  // take the parameters it cares about; permits the leader to be
  // defined in an inlineable way; and permits the follower for an
  // array iterator to be a var iterator
  //
  // I've named these methods newThese() for the time being
  // to avoid conflicting with the above which is currently targeted
  // by the compiler.  My current assumption is that we would want to
  // overload these() to serve this purpose in the final language
  // definition.
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    //
    // TODO: This currently only results in a single level of
    // per-locale parallelism -- no per-core parallelism; maybe
    // approach this by deferring to the local leader/follower
    // iterators at some point?
    //
    // TODO: Really want the parallelism across the target locales
    // to be expressed more independently of the distribution by
    // pushing it into the leader iterator.  Need to get an on clause
    // into here somehow.  That is:
    //
    //   coforall locDom in locDoms do
    //     on locDom do
    //       yield locDom.myBlock - whole.low;
    //
    // TODO: And really want to split the inter- and intra-locale
    // parallelism into two separate stages for communication
    // optimization and the like
    //
    // TODO: Abstract this subtraction of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    for locDom in locDoms do
      yield locDom.myBlock - whole.low;
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
    //
    // TODO: Abstract this addition of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    // TODO: Is there some clever way to invoke the leader/follower
    // iterator on the local blocks in here such that the per-core
    // parallelism is expressed at that level?  Seems like a nice
    // natural composition and might help with my fears about how
    // stencil communication will be done on a per-locale basis.
    for i in followThis {
      yield i + whole.low;
    }
  }

  //
  // the print method for the domain
  //
  def writeThis(x:Writer) {
    x.write(whole);
  }

  //
  // how to allocate a new array over this domain
  //
  def newArray(type elemType) {
    return new Block1DArr(idxType, elemType, this);
  }

  //
  // queries for the number of indices, low, and high bounds
  //
  def numIndices {
    return whole.numIndices;
  }

  def low {
    return whole.low;
  }

  def high {
    return whole.high;
  }
}

//
// the local domain class
//
class LocBlock1DDom {
  //
  // The index type of the domain
  //
  type idxType;

  //
  // a reference to the parent global domain class
  //
  const wholeDom: Block1DDom(idxType);

  //
  // a local domain describing the indices owned by this locale
  //
  var myBlock: domain(1, idxType);

  //
  // iterator over this locale's indices
  //
  def these() {
    // May want to do something like:
    // on this do
    // But can't currently have yields in on clauses
    for ind in myBlock do
      yield ind;
  }

  //
  // this is the parallel iterator for the local domain, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // how to write out this locale's indices
  //
  def writeThis(x:Writer) {
    x.write(myBlock);
  }

  //
  // queries for this locale's number of indices, low, and high bounds
  //
  def numIndices {
    return myBlock.numIndices;
  }

  def low {
    return myBlock.low;
  }

  def high {
    return myBlock.high;
  }
}

//
// the global array class
//
class Block1DArr {
  //
  // The index type of the domain
  //
  type idxType;

  //
  // the array's element type
  //
  type elemType;

  //
  // the global domain descriptor for this array
  //
  var dom: Block1DDom(idxType);

  //
  // an array of local array classes
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locArr: [dom.dist.targetLocDom] LocBlock1DArr(idxType, elemType);

  def initialize() {
    for locid in dom.dist.targetLocDom do
      on dom.dist.targetLocs(locid) do
        locArr(locid) = new LocBlock1DArr(idxType, elemType, dom.locDoms(locid));
  }

  //
  // the global accessor for the array
  //
  def this(i: idxType) var {
    return locArr(dom.dist.ind2locInd(i))(i);
  }

  //
  // the iterator over the array's elements, currently sequential
  //
  def these() var {
    for loc in dom.dist.targetLocDom {
      // TODO: May want to do something like:
      // on this do
      // But can't currently have yields in on clauses
      for elem in locArr(loc) {
        yield elem;
      }
    }
  }

  //
  // this is the parallel iterator for the global array, see th
  // example for general notes on the approach
  //

  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    for blk in dom.newThese(IteratorType.leader) do
      yield blk;
  }

  def newThese(param iterator: IteratorType, followThis) var
        where iterator == IteratorType.follower {
    for i in followThis {
      yield this(i + dom.low);
    }
  }

  //
  // how to print out the whole array, sequentially
  //
  def writeThis(x: Writer) {
    var first = true;
    for loc in dom.dist.targetLocDom {
      // May want to do something like the following:
      //      on loc {
      // but it causes deadlock -- see writeThisUsingOn.chpl
        if (locArr(loc).numElements >= 1) {
          if (first) {
            first = false;
          } else {
            x.write(" ");
          }
          x.write(locArr(loc));
        }
        //    }
      stdout.flush();
    }
  }

  //
  // a query for the number of elements in the array
  //
  def numElements {
    return dom.numIndices;
  }
}

//
// the local array class
//
class LocBlock1DArr {
  //
  // The index type of the domain
  //
  type idxType;

  //
  // the element type
  //
  type elemType;

  //
  // a reference to the local domain class for this array and locale
  //
  const locDom: LocBlock1DDom(idxType);

  //
  // the block of local array data
  //
  var myElems: [locDom.myBlock] elemType;

  //
  // the accessor for the local array -- assumes the index is local
  //
  def this(i: idxType) var {
    return myElems(i);
  }

  //
  // iterator over the elements owned by this locale
  //
  def these() var {
    for elem in myElems {
      yield elem;
    }
  }

  //
  // this is the parallel iterator for the local array, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // prints out this locale's piece of the array
  //
  def writeThis(x: Writer) {
    // May want to do something like the following:
    //      on loc {
    // but it causes deadlock -- see writeThisUsingOn.chpl
    x.write(myElems);
  }

  //
  // query for the number of local array elements
  //
  def numElements {
    return myElems.numElements;
  }
}

// TODO: Have the global class leader/follower iterators defer to the
// local class leader/followers once we're within a locale?

// TODO: Make these into an official distribution?

// TODO: Would using nested classes allow me to avoid so much
// passing around of globIndexType and locIndexType?

config param debugBradsBlock1D = false;

// TODO: This would need to be moved somewhere more standard
enum IteratorType { leader, follower };

//
// The distribution class
//
class Block1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type glbIdxType = int(64);

  //
  // The bounding box that defines the block distribution
  //
  const boundingBox: domain(1, glbIdxType);

  //
  // a domain and array describing the set of target locales to which
  // the indices are mapped
  //
  const targetLocDom: domain(1);
  const targetLocs: [targetLocDom] locale;
  //
  // NOTE: originally I wrote this as:
  //
  //   const targetLocs = Locales;
  //   const targetLocDom = targetLocs.domain;
  //
  // but this had two interesting problems:
  //
  // (1) the capture of the targetLocs array only makes a copy of the
  //     array, not of its domain, so if someone were to resize the
  //     LocaleSpace domain outside of the class, this would inadvertantly
  //     get resized as well.  We really want to make a copy of both the
  //     domain and array (which I now do in the constructor).
  //
  // (2) Initializing targetLocs with Locales prevents us from assigning
  //     anything other than a numLocales-ary array of locales to it
  //     which is stricter than I wanted -- I only wanted to restrict
  //     us to a 1D array of locales.  In our current model, this would
  //     require us to have some sort of [domain(1)] locale declaration
  //     to make it work.  Or we could think about whether a model in
  //     which only the array's static type information is inferred
  //     for a field?

  //
  // an array of local distribution class descriptors -- set up in
  // initialize() below
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  // WANTED:
  //
  /*
  const locDist: [loc in targetLocDom] LocBlock1DDist(glbIdxType)
                   = on targetLocs(loc) do
                       new LocBlock1DDist(glbIdxType, targetLocDom.order(loc), this);
  */
  //
  // but this doesn't work yet because an array forall initializer
  // apparently can't refer to a local member domain.
  //
  const locDist: [targetLocDom] LocBlock1DDist(glbIdxType);
  //
  // WORKAROUND: Initialize in the constructor instead
  //

  def Block1DDist(type glbIdxType = int(64), bbox: domain(1, glbIdxType),
                  targetLocales: [?targetLocalesDomain] locale) {
    boundingBox = bbox;
    targetLocDom = targetLocalesDomain;
    targetLocs = targetLocales;
    //
    // WANT TO DO:
    /*
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(glbIdxType, locid, this);
    */
    //
    // BUT results in a _heapAlloc(type int(64)) call.
    // WORKAROUND:
    helpConstruct();
  }
  //
  // WORKAROUND CONTINUED:
  //
  def helpConstruct() {
    for locid in targetLocDom do
      on targetLocs(locid) do
        locDist(locid) = new LocBlock1DDist(glbIdxType, locid, this);
  }
  //
  // END WORKAROUND
  //

  def writeThis(x:Writer) {
    x.writeln("BradsBlock1DPar");
    x.writeln("---------------");
    x.writeln("distributes: ", boundingBox);
    x.writeln("across locales: ", targetLocs);
    x.writeln("indexed via: ", targetLocDom);
    x.writeln("resulting in: ");
    for locid in targetLocDom do
      x.writeln("  [", locid, "] ", locDist(locid));
  }

  //
  // Create a new domain over this distribution with the given index
  // set (inds) and the given global and local index type (idxType,
  // locIdxType)
  //
  def newDomain(inds, type idxType = glbIdxType, type locIdxType = idxType) {
    // Note that I'm fixing the global and local index types to be the
    // same, but making this a generic function would fix this
    return new Block1DDom(idxType, locIdxType, this, inds);
  }

  //
  // compute what chunk of inds is owned by a given locale -- assumes
  // it's being called on the locale in question
  //
  def getChunk(inds, locid) {
    // use domain slicing to get the intersection between what the
    // locale owns and the domain's index set

    // TODO: Should this be able to be written as myChunk[inds] ???
    return locDist(locid).myChunk(inds.dim(1));
  }

  //
  // Determine which locale owns a particular index
  //
  def ind2locInd(ind: glbIdxType) {
    const ind0 = ind - boundingBox.low;
    const loc0 = (ind0 * targetLocs.numElements) / boundingBox.numIndices;
    const locInd = loc0: index(targetLocs.domain) + targetLocs.domain.low;
    return locInd;
  }

  def ind2loc(ind: glbIdxType) {
    return targetLocs(ind2locInd(ind));
  }
}

//
// A per-locale local distribution class
//
class LocBlock1DDist {
  //
  // The distribution's index type and domain's global index type
  //
  type glbIdxType;

  //
  // This stores the piece of the global bounding box owned by
  // the locale.  Note that my original guess that we'd want
  // to use lclIdxType here is wrong since we're talking about
  // the section of the global index space owned by the locale.
  //
  const myChunk: domain(1, glbIdxType);
  const locid: int;
  const loc: locale;

  //
  // Compute what chunk of index(1) is owned by the current locale
  // Arguments:
  //
  def LocBlock1DDist(type glbIdxType,
                     _locid: int, // the locale index from the target domain
                     dist: Block1DDist(glbIdxType) // reference to glob dist
                     ) {
    locid = _locid;
    loc = dist.targetLocs(locid);
    const lo = dist.boundingBox.low;
    const hi = dist.boundingBox.high;
    const numelems = hi - lo + 1;
    const numlocs = dist.targetLocDom.numIndices;
    const locid0 = dist.targetLocDom.order(locid); // 0-based locale ID
    const blo = if (locid0 == 0) then min(glbIdxType)
                else procToData((numelems: real * locid0) / numlocs, lo);
    const bhi = if (locid0 == numlocs - 1) then max(glbIdxType)
                else procToData((numelems: real * (locid0+1)) / numlocs, lo) - 1;
    myChunk = [blo..bhi];
    if debugBradsBlock1D then
      writeln("locale ", locid, " owns ", myChunk);
  }

  //
  // a helper function for mapping processors to indices
  //
  def procToData(x, lo)
    return (lo + (x: lo.type) + (x:real != x:int:real));

  def writeThis(x:Writer) {
    x.write("locale ", loc.id, " owns chunk: ", myChunk);
  }
}

//
// The global domain class
//
class Block1DDom {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // a pointer to the parent distribution
  //
  const dist: Block1DDist(glbIdxType);

  //
  // a domain describing the complete domain
  //
  const whole: domain(1, glbIdxType);

  //
  // an array of local domain class descriptors -- set up in
  // initialize() below
  //
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locDoms: [dist.targetLocDom] LocBlock1DDom(glbIdxType, lclIdxType);

  def initialize() {
    for locid in dist.targetLocDom do
      on dist.targetLocs(locid) do
        locDoms(locid) = new LocBlock1DDom(glbIdxType, lclIdxType, this,
                                           dist.getChunk(whole, locid));
    if debugBradsBlock1D then
      [loc in dist.targetLocDom] writeln(loc, " owns ", locDoms(loc));
  }

  //
  // the iterator for the domain -- currently sequential
  //
  def these() {
    for blk in locDoms do
      // TODO: Would want to do something like:
      // on blk do
      // But can't currently have yields in on clauses
        for ind in blk do
          yield ind;
  }

  //
  // this is the parallel iterator for the global domain, following
  // a variation on Steve and David's proposals -- I've split the
  // single iterator into two iterators, distinguished by where
  // clauses on their parameter values.  This permits each to only
  // take the parameters it cares about; permits the leader to be
  // defined in an inlineable way; and permits the follower for an
  // array iterator to be a var iterator
  //
  // I've named these methods newThese() for the time being
  // to avoid conflicting with the above which is currently targeted
  // by the compiler.  My current assumption is that we would want to
  // overload these() to serve this purpose in the final language
  // definition.
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    //
    // TODO: This currently only results in a single level of
    // per-locale parallelism -- no per-core parallelism; maybe
    // approach this by deferring to the local leader/follower
    // iterators at some point?
    //
    // TODO: Really want the parallelism across the target locales
    // to be expressed more independently of the distribution by
    // pushing it into the leader iterator.  Need to get an on clause
    // into here somehow.  That is:
    //
    //   coforall locDom in locDoms do
    //     on locDom do
    //       yield locDom.myBlock - whole.low;
    //
    // TODO: And really want to split the inter- and intra-locale
    // parallelism into two separate stages for communication
    // optimization and the like
    //
    // TODO: Abstract this subtraction of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    for locDom in locDoms do
      yield locDom.myBlock - whole.low;
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
    //
    // TODO: Abstract this addition of low into a function?
    // Note relationship between this operation and the
    // order/position functions -- any chance for creating similar
    // support? (esp. given how frequent this seems likely to be?)
    //
    // TODO: Is there some clever way to invoke the leader/follower
    // iterator on the local blocks in here such that the per-core
    // parallelism is expressed at that level?  Seems like a nice
    // natural composition and might help with my fears about how
    // stencil communication will be done on a per-locale basis.
    for i in followThis {
      yield i + whole.low;
    }
  }

  //
  // the print method for the domain
  //
  def writeThis(x:Writer) {
    x.write(whole);
  }

  //
  // how to allocate a new array over this domain
  //
  def newArray(type elemType) {
    return new Block1DArr(glbIdxType, lclIdxType, elemType, this);
  }

  //
  // queries for the number of indices, low, and high bounds
  //
  def numIndices {
    return whole.numIndices;
  }

  def low {
    return whole.low;
  }

  def high {
    return whole.high;
  }
}

//
// the local domain class
//
class LocBlock1DDom {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // a reference to the parent global domain class
  //
  const wholeDom: Block1DDom(glbIdxType, lclIdxType);

  //
  // a local domain describing the indices owned by this locale
  //
  var myBlock: domain(1, lclIdxType);

  //
  // iterator over this locale's indices
  //
  def these() {
    // May want to do something like:
    // on this do
    // But can't currently have yields in on clauses
    for ind in myBlock do
      yield ind;
  }

  //
  // this is the parallel iterator for the local domain, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // how to write out this locale's indices
  //
  def writeThis(x:Writer) {
    x.write(myBlock);
  }

  //
  // queries for this locale's number of indices, low, and high bounds
  //
  def numIndices {
    return myBlock.numIndices;
  }

  def low {
    return myBlock.low;
  }

  def high {
    return myBlock.high;
  }
}

//
// the global array class
//
class Block1DArr {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // the array's element type
  //
  type elemType;

  //
  // the global domain descriptor for this array
  //
  var dom: Block1DDom(glbIdxType, lclIdxType);

  //
  // an array of local array classes
  //
  // TODO: would like this to be const and initialize in-place,
  // removing the initialize method; would want to be able to use
  // an on-clause at the expression list to make this work.
  // Otherwise, would have to move the allocation into a function
  // just to get it at the statement level.
  //
  var locArr: [dom.dist.targetLocDom] LocBlock1DArr(glbIdxType, lclIdxType, elemType);

  def initialize() {
    for locid in dom.dist.targetLocDom do
      on dom.dist.targetLocs(locid) do
        locArr(locid) = new LocBlock1DArr(glbIdxType, lclIdxType, elemType, dom.locDoms(locid));
  }

  //
  // the global accessor for the array
  //
  def this(i: glbIdxType) var {
    return locArr(dom.dist.ind2locInd(i))(i);
  }

  //
  // the iterator over the array's elements, currently sequential
  //
  def these() var {
    for loc in dom.dist.targetLocDom {
      // TODO: May want to do something like:
      // on this do
      // But can't currently have yields in on clauses
      for elem in locArr(loc) {
        yield elem;
      }
    }
  }

  //
  // this is the parallel iterator for the global array, see th
  // example for general notes on the approach
  //

  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
    for blk in dom.newThese(IteratorType.leader) do
      yield blk;
  }

  def newThese(param iterator: IteratorType, followThis) var
        where iterator == IteratorType.follower {
    for i in followThis {
      yield this(i + dom.low);
    }
  }

  //
  // how to print out the whole array, sequentially
  //
  def writeThis(x: Writer) {
    var first = true;
    for loc in dom.dist.targetLocDom {
      // May want to do something like the following:
      //      on loc {
      // but it causes deadlock -- see writeThisUsingOn.chpl
        if (locArr(loc).numElements >= 1) {
          if (first) {
            first = false;
          } else {
            x.write(" ");
          }
          x.write(locArr(loc));
        }
        //    }
      stdout.flush();
    }
  }

  //
  // a query for the number of elements in the array
  //
  def numElements {
    return dom.numIndices;
  }
}

//
// the local array class
//
class LocBlock1DArr {
  //
  // The index types of the global and local domain portions
  //
  type glbIdxType;
  type lclIdxType;

  //
  // the element type
  //
  type elemType;

  //
  // a reference to the local domain class for this array and locale
  //
  const locDom: LocBlock1DDom(glbIdxType, lclIdxType);

  //
  // the block of local array data
  //
  var myElems: [locDom.myBlock] elemType;

  //
  // the accessor for the local array -- assumes the index is local
  //
  def this(i: lclIdxType) var {
    return myElems(i);
  }

  //
  // iterator over the elements owned by this locale
  //
  def these() var {
    for elem in myElems {
      yield elem;
    }
  }

  //
  // this is the parallel iterator for the local array, see global
  // domain parallel iterators for general notes on the approach
  //
  def newThese(param iterator: IteratorType)
        where iterator == IteratorType.leader {
  }

  def newThese(param iterator: IteratorType, followThis)
        where iterator == IteratorType.follower {
  }

  //
  // prints out this locale's piece of the array
  //
  def writeThis(x: Writer) {
    // May want to do something like the following:
    //      on loc {
    // but it causes deadlock -- see writeThisUsingOn.chpl
    x.write(myElems);
  }

  //
  // query for the number of local array elements
  //
  def numElements {
    return myElems.numElements;
  }
}


Fixed a bug with the atan2() math function such that
it now works in the compiler.

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

//
// Primitive math funs
//

pragma "inline" fun abs(x : int) return __primitive("abs", x);
pragma "inline" fun acos(x : float) return __primitive("acos", x);
pragma "inline" fun acosh(x : float) return __primitive("acosh", x);
pragma "inline" fun asin(x : float) return __primitive("asin", x);
pragma "inline" fun asinh(x : float) return __primitive("asinh", x);
pragma "inline" fun atan(x : float) return __primitive("atan", x);
pragma "inline" fun atan2(y : float, x : float) return __primitive("atan2", y, x);
pragma "inline" fun atanh(x : float) return __primitive("atanh", x);
pragma "inline" fun cbrt(x : float) return __primitive("cbrt", x);
pragma "inline" fun ceil(x : float) return __primitive("ceil", x);
pragma "inline" fun cos(x : float) return __primitive("cos", x);
pragma "inline" fun cosh(x : float) return __primitive("cosh", x);
pragma "inline" fun erf(x : float) return __primitive("erf", x);
pragma "inline" fun erfc(x : float) return __primitive("erfc", x);
pragma "inline" fun exp(x : float) return __primitive("exp", x);
pragma "inline" fun expm1(x : float) return __primitive("expm1", x);
pragma "inline" fun abs(x : float) return __primitive("fabs", x);
pragma "inline" fun floor(x : float) return __primitive("floor", x);
pragma "inline" fun lgamma(x : float) return __primitive("lgamma", x);
pragma "inline" fun log(x : float) return __primitive("log", x);
pragma "inline" fun log10(x : float) return __primitive("log10", x);
pragma "inline" fun log1p(x : float) return __primitive("log1p", x);
pragma "inline" fun rint(x: float) return __primitive("rint", x);
pragma "inline" fun sin(x: float) return __primitive("sin", x);
pragma "inline" fun sinh(x : float) return __primitive("sinh", x);
pragma "inline" fun sqrt(x : float) return __primitive("sqrt", x);
pragma "inline" fun tan(x: float) return __primitive("tan", x);
pragma "inline" fun tanh(x : float) return __primitive("tanh", x);

//
// Primitive math funs
//

pragma "inline" fun abs(x : int) return __primitive("abs", x);
pragma "inline" fun acos(x : float) return __primitive("acos", x);
pragma "inline" fun acosh(x : float) return __primitive("acosh", x);
pragma "inline" fun asin(x : float) return __primitive("asin", x);
pragma "inline" fun asinh(x : float) return __primitive("asinh", x);
pragma "inline" fun atan(x : float) return __primitive("atan", x);
pragma "inline" fun atanh(x : float) return __primitive("atanh", x);
pragma "inline" fun cbrt(x : float) return __primitive("cbrt", x);
pragma "inline" fun ceil(x : float) return __primitive("ceil", x);
pragma "inline" fun cos(x : float) return __primitive("cos", x);
pragma "inline" fun cosh(x : float) return __primitive("cosh", x);
pragma "inline" fun erf(x : float) return __primitive("erf", x);
pragma "inline" fun erfc(x : float) return __primitive("erfc", x);
pragma "inline" fun exp(x : float) return __primitive("exp", x);
pragma "inline" fun expm1(x : float) return __primitive("expm1", x);
pragma "inline" fun abs(x : float) return __primitive("fabs", x);
pragma "inline" fun floor(x : float) return __primitive("floor", x);
pragma "inline" fun lgamma(x : float) return __primitive("lgamma", x);
pragma "inline" fun log(x : float) return __primitive("log", x);
pragma "inline" fun log10(x : float) return __primitive("log10", x);
pragma "inline" fun log1p(x : float) return __primitive("log1p", x);
pragma "inline" fun rint(x: float) return __primitive("rint", x);
pragma "inline" fun sin(x: float) return __primitive("sin", x);
pragma "inline" fun sinh(x : float) return __primitive("sinh", x);
pragma "inline" fun sqrt(x : float) return __primitive("sqrt", x);
pragma "inline" fun tan(x: float) return __primitive("tan", x);
pragma "inline" fun tanh(x : float) return __primitive("tanh", x);

// Need to update these to use inline pragma

pragma "no codegen" pragma "rename atan2" fun atan2(y : float, x : float) return __primitive("atan2", x);


Set the number of core per numa domain appropriately and other small cleanup

[Reviewed by David]

This commit assumes the the number of cores per top level locale are
evenly distributed amongst the numa domains.  While in there, I
avoided set up of the childLocales if the number of sublocales was 0
and also changed numSubLocales to numSublocales.

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

// LocaleModel.chpl
//
// This provides a flat locale model architectural description.  The
// locales contain memory and a multi-core processor with homogeneous
// cores, and we ignore any affinity (NUMA effects) between the
// processor cores and the memory.  This architectural description is
// backward compatible with the architecture implicitly provided by
// releases 1.6 and preceding.
//
pragma "no use ChapelStandard"
module LocaleModel {

  // This should eventually be unified with
  // CHPL_LOCALE_MODEL_NUM_SUBLOCALES which is currently defined in the
  // runtime for any locale model that doesn't use sub locales
  param localeModelHasSubLocales = true;

  use ChapelLocale;
  use DefaultRectangular;
  use ChapelNumLocales;
  use Sys;

  extern proc chpl_task_getRequestedSubloc(): int(32);

  config param debugLocaleModel = false;

  // We would really like a class-static storage class.(C++ nomenclature)
  var doneCreatingLocales: bool = false;

  // The chpl_localeID_t type is used internally.  It should not be exposed to
  // the user.  The runtime defines the actual type, as well as a functional
  // interface for assembling and disassembling chpl_localeID_t values.  This
  // module then provides the interface the compiler-emitted code uses to do
  // the same.

  type chpl_nodeID_t = int(32);
  type chpl_sublocID_t = int(32);

  extern record chpl_localeID_t {
    // We need to know that this is a record type in order to pass it to and
    // return it from runtime functions properly, but we don't need or want
    // to see its contents.
  };

  // We need an explicit copy constructor because the compiler cannot create
  // a correct one for a record type whose members are not known to it.
  pragma "init copy fn"
  extern chpl__initCopy_chpl_rt_localeID_t
  proc chpl__initCopy(initial: chpl_localeID_t): chpl_localeID_t;

  extern var chpl_nodeID: chpl_nodeID_t;

  // Runtime interface for manipulating global locale IDs.
  extern
    proc chpl_rt_buildLocaleID(node: chpl_nodeID_t,
                               subloc: chpl_sublocID_t): chpl_localeID_t;

  extern
    proc chpl_rt_nodeFromLocaleID(loc: chpl_localeID_t): chpl_nodeID_t;

  extern
    proc chpl_rt_sublocFromLocaleID(loc: chpl_localeID_t): chpl_sublocID_t;

  // Compiler (and module code) interface for manipulating global locale IDs..
  pragma "insert line file info"
  proc chpl_buildLocaleID(node: chpl_nodeID_t, subloc: chpl_sublocID_t)
    return chpl_rt_buildLocaleID(node, subloc);

  pragma "insert line file info"
  proc chpl_nodeFromLocaleID(loc: chpl_localeID_t)
    return chpl_rt_nodeFromLocaleID(loc);

  pragma "insert line file info"
  proc chpl_sublocFromLocaleID(loc: chpl_localeID_t)
    return chpl_rt_sublocFromLocaleID(loc);

  //
  // The NUMA sublocale model
  //
  class NumaDomain : AbstractLocaleModel {
    const sid: chpl_sublocID_t;
    const name: string;

    proc chpl_id() return (parent:LocaleModel)._node_id; // top-level node id
    proc chpl_localeid() {
      return chpl_buildLocaleID((parent:LocaleModel)._node_id:chpl_nodeID_t,
                                sid);
    }
    proc chpl_name() return name;

    proc NumaDomain() {
    }

    proc NumaDomain(_sid, _parent) {
      sid = _sid;
      name = "ND"+sid;
      parent = _parent;
    }

    proc readWriteThis(f) {
      parent.readWriteThis(f);
      f <~> '.'+name;
    }

    proc getChildCount(): int { return 0; }
    iter getChildIndices() : int {
      halt("No children to iterate over.");
      yield -1;
    }
    proc addChild(loc:locale) { halt("Cannot add children to this locale type."); }
    proc getChild(idx:int) : locale { return nil; }

    // This is commented out b/c it leads to an internal error during
    // the resolveIntents pass.  See
    // test/functions/iterators/sungeun/iterInClass.future
    //
    // iter getChildren() : locale {
    //  halt("No children to iterate over.");
    //  yield nil;
    // }
  }

  //
  // The node model
  //
  class LocaleModel : AbstractLocaleModel {
    const callStackSize: int;
    const _node_id : int;
    const local_name : string;

    const numSublocales: int;
    var childSpace: domain(1);
    var childLocales: [childSpace] NumaDomain;

    // This constructor must be invoked "on" the node
    // that it is intended to represent.  This trick is used
    // to establish the equivalence the "locale" field of the locale object
    // and the node ID portion of any wide pointer referring to it.
    proc LocaleModel() {
      if doneCreatingLocales {
        halt("Cannot create additional LocaleModel instances");
      }
      init();
    }

    proc LocaleModel(parent_loc : locale) {
      if doneCreatingLocales {
        halt("Cannot create additional LocaleModel instances");
      }
      parent = parent_loc;
      init();
    }

    proc chpl_id() return _node_id;     // top-level locale (node) number
    proc chpl_localeid() {
      extern const c_sublocid_any: chpl_sublocID_t;
      return chpl_buildLocaleID(_node_id:chpl_nodeID_t, c_sublocid_any);
    }
    proc chpl_name() return local_name;

    proc readWriteThis(f) {
      // Most classes will define it like this:
//      f <~> name;
      // but here it is defined thus for backward compatibility.
      f <~> new ioLiteral("LOCALE") <~> _node_id;
    }

    proc getChildSpace() return childSpace;

    proc getChildCount() return numSublocales;

    iter getChildIndices() : int {
      for idx in childSpace do
        yield idx;
    }

    proc getChild(idx:int) : locale {
      if boundsChecking then
        if (idx < 0) || (idx >= numSublocales) then
          halt("sublocale child index out of bounds (",idx,")");
      return childLocales[idx];
    }

    iter getChildren() : locale  {
      for loc in childLocales do
        yield loc;
    }

    proc getChildArray() {
      return childLocales;
    }

    //------------------------------------------------------------------------{
    //- Implementation (private)
    //-
    proc init() {
      _node_id = chpl_nodeID: int;

      // chpl_nodeName is defined in chplsys.c.
      // It supplies a node name obtained by running uname(3) on the
      // current node.  For this reason (as well), the constructor (or
      // at least this init method) must be run on the node it is
      // intended to describe.
      var comm, spawnfn : string;
      extern proc chpl_nodeName() : string;
      // sys_getenv returns zero on success.
      if sys_getenv("CHPL_COMM", comm) == 0 && comm == "gasnet" &&
        sys_getenv("GASNET_SPAWNFN", spawnfn) == 0 && spawnfn == "L"
      then local_name = chpl_nodeName() + "-" + _node_id : string;
      else local_name = chpl_nodeName();

      extern proc chpl_task_getCallStackSize(): int;
      callStackSize = chpl_task_getCallStackSize();

      extern proc chpl_numCoresOnThisLocale(): int;
      numCores = chpl_numCoresOnThisLocale();

      extern proc chpl_task_getNumSubLocales(): int(32);
      numSublocales = chpl_task_getNumSubLocales();

      if numSublocales >= 1 {
        childSpace = {0..#numSublocales};
        const numCoresPerNumaDomain = numCores/numSublocales;
        const origSubloc = chpl_task_getRequestedSubloc(); // this should be any
        for i in childSpace {
          // allocate the structure on the domain?
          // MUST CHANGE SUBLOC HERE (prolly by primitive)
          chpl_task_setSubLoc(i:chpl_sublocID_t);
          childLocales[i] = new NumaDomain(i:chpl_sublocID_t, this);
          childLocales[i].numCores = numCoresPerNumaDomain;
        }
        chpl_task_setSubLoc(origSubloc);
      }
    }
    //------------------------------------------------------------------------}

    inline proc subloc return c_sublocid_any;
 }

  //
  // An instance of this class is the default contents 'rootLocale'.
  //
  // In the current implementation a platform-specific architectural description
  // may overwrite this instance or any of its children to establish a more customized
  // representation of the system resources.
  //
  class RootLocale : AbstractRootLocale {

    // Would like to make myLocaleSpace distributed with one index per node.
    const myLocaleSpace: domain(1) = {0..numLocales-1};
    const myLocales: [myLocaleSpace] locale;

    proc RootLocale() {
      parent = nil;
      numCores = 0;
    }

    // The init() function must use initOnLocales() to iterate (in
    // parallel) over the locales to set up the LocaleModel object.
    // In addition, the initial 'here' must be set.
    proc init() {
      forall locIdx in initOnLocales() {
        chpl_task_setSubLoc(c_sublocid_any);
        const node = new LocaleModel(this);
        myLocales[locIdx] = node;
        numCores += node.numCores;
      }
    }

    // Has to be globally unique and not equal to a node ID.
    // We return numLocales for now, since we expect nodes to be
    // numbered less than this.
    // -1 is used in the abstract locale class to specify an invalid node ID.
    proc chpl_id() return numLocales;
    proc chpl_localeid() {
      extern const c_sublocid_any: chpl_sublocID_t;
      return chpl_buildLocaleID(numLocales:chpl_nodeID_t, c_sublocid_any);
    }
    proc chpl_name() return local_name();
    proc local_name() return "rootLocale";

    proc readWriteThis(f) {
      f <~> name;
    }

    proc getChildCount() return this.myLocaleSpace.numIndices;

    proc getChildSpace() return this.myLocaleSpace;

    iter getChildIndices() : int {
      for idx in this.myLocaleSpace do
        yield idx;
    }

    proc getChild(idx:int) return this.myLocales[idx];

    iter getChlidren() : locale  {
      for loc in this.myLocales do
        yield loc;
    }

    proc getDefaultLocaleSpace() return this.myLocaleSpace;
    proc getDefaultLocaleArray() return myLocales;

    proc localeIDtoLocale(id : chpl_localeID_t) {
      const dnode = chpl_nodeFromLocaleID(id);
      const dsubloc = chpl_sublocFromLocaleID(id);
      if (dsubloc == c_sublocid_curr) || (dsubloc == c_sublocid_any) then
        return (myLocales[dnode:int]):locale;
      else
        return (myLocales[dnode:int].getChild(dsubloc:int)):locale;
    }
  }

  //////////////////////////////////////////
  //
  // support for memory management
  //

  // Here be dragons: If the return type is specified, then normalize.cpp
  // inserts an initializer for the return value which calls its constructor,
  // which calls chpl_here_alloc ad infinitum.  But if the return type is
  // left off, it works!

  // The allocator pragma is used by scalar replacement.
  pragma "allocator"
  pragma "no sync demotion"
  proc chpl_here_alloc(x, md:int(16)) {
    pragma "insert line file info"
      extern proc chpl_mem_alloc(size:int, md:int(16)) : opaque;
    var nbytes = __primitive("sizeof", x);
    var mem = chpl_mem_alloc(nbytes, md + chpl_memhook_md_num());
    return __primitive("cast", x.type, mem);
  }

  pragma "allocator"
  pragma "no sync demotion"
  proc chpl_here_calloc(x, number:int, md:int(16)) {
    pragma "insert line file info"
      extern proc chpl_mem_calloc(number:int, size:int, md:int(16)) : opaque;
    var nbytes = __primitive("sizeof", x);
    var mem = chpl_mem_calloc(number, nbytes, md + chpl_memhook_md_num());
    return __primitive("cast", x.type, mem);
  }

  pragma "allocator"
  pragma "no sync demotion"
  proc chpl_here_realloc(x, md:int(16)) {
    // TODO: The pointer should really be of type opaque, but we don't
    // handle object ==> opaque casts correctly.  (In codegen, opaque behaves
    // like an lvalue, but in the type system it isn't one.)
    pragma "insert line file info"
      extern proc chpl_mem_realloc(ptr:opaque, size:int, md:int(16)) : opaque;
    var nbytes = __primitive("sizeof", x);
    var mem = chpl_mem_realloc(__primitive("cast_to_void_star", x), nbytes,
                               md + chpl_memhook_md_num());
    return __primitive("cast", x.type, mem);
  }

  pragma "no sync demotion"
  proc chpl_here_free(x) {
    // TODO: The pointer should really be of type opaque, but we don't
    // handle object ==> opaque casts correctly.  (In codegen, opaque behaves
    // like an lvalue, but in the type system it isn't one.)
    pragma "insert line file info"
      extern proc chpl_mem_free(ptr:opaque): void;
    chpl_mem_free(__primitive("cast_to_void_star", x));
  }

  //////////////////////////////////////////
  //
  // support for "on" statements
  //

  //
  // runtime interface
  //
  extern proc chpl_comm_fork(loc_id: int, subloc_id: int,
                             fn: int, args: c_void_ptr, arg_size: int(32));
  extern proc chpl_comm_fork_fast(loc_id: int, subloc_id: int,
                                  fn: int, args: c_void_ptr, args_size: int(32));
  extern proc chpl_comm_fork_nb(loc_id: int, subloc_id: int,
                                fn: int, args: c_void_ptr, args_size: int(32));
  extern proc chpl_ftable_call(fn: int, args: c_void_ptr): void;
  extern proc chpl_task_setSubLoc(subLoc: int(32));
  extern const c_sublocid_any: chpl_sublocID_t;
  extern const c_sublocid_curr: chpl_sublocID_t;

  //
  // regular "on"
  //
  pragma "insert line file info"
  export
  proc chpl_executeOn(loc: chpl_localeID_t, // target locale
                      fn: int,              // on-body function idx
                      args: c_void_ptr,     // function args
                      args_size: int(32)    // args size
                     ) {
    const dnode =  chpl_nodeFromLocaleID(loc);
    const dsubloc =  chpl_sublocFromLocaleID(loc);
    if dnode != chpl_nodeID {
      chpl_comm_fork(dnode, dsubloc, fn, args, args_size);
    } else {
      // run directly on this node
      var origSubloc = chpl_task_getRequestedSubloc();
      if (dsubloc == origSubloc) {
        chpl_ftable_call(fn, args);
      } else {
        // move to a different sublocale
        chpl_task_setSubLoc(dsubloc);
        chpl_ftable_call(fn, args);
        chpl_task_setSubLoc(origSubloc);
      }
    }
  }

  //
  // fast "on" (doesn't do anything that could deadlock a comm layer,
  // in the Active Messages sense)
  //
  pragma "insert line file info"
  export
  proc chpl_executeOnFast(loc: chpl_localeID_t, // target locale
                          fn: int,              // on-body function idx
                          args: c_void_ptr,     // function args
                          args_size: int(32)    // args size
                         ) {
    const dnode =  chpl_nodeFromLocaleID(loc);
    const dsubloc =  chpl_sublocFromLocaleID(loc);
    if dnode != chpl_nodeID {
      chpl_comm_fork_fast(dnode, dsubloc, fn, args, args_size);
    } else {
      var origSubloc = chpl_task_getRequestedSubloc();
      if (dsubloc == origSubloc) {
        chpl_ftable_call(fn, args);
      } else {
        // move to a different sublocale
        chpl_task_setSubLoc(dsubloc);
        chpl_ftable_call(fn, args);
        chpl_task_setSubLoc(origSubloc);
      }
    }
  }

  // Unused for now due to bug in non-developer mode when using 'begin'
  inline proc chpl_executeOnNBAux(fn: int,         // on-body function idx
                                  args: c_void_ptr // function args
                                  ) {
    if __primitive("task_get_serial") then
      chpl_ftable_call(fn, args);
    else
      begin chpl_ftable_call(fn, args);
  }
  //
  // nonblocking "on" (doesn't wait for completion)
  //
  pragma "insert line file info"
  export
  proc chpl_executeOnNB(loc: chpl_localeID_t, // target locale
                        fn: int,              // on-body function idx
                        args: c_void_ptr,     // function args
                        args_size: int(32)    // args size
                       ) {
    //
    // If we're in serial mode, we should use blocking rather than
    // non-blocking "on" in order to serialize the forks.
    //
    const dnode =  chpl_nodeFromLocaleID(loc);
    const dsubloc =  chpl_sublocFromLocaleID(loc);
    if dnode != chpl_nodeID {
      if __primitive("task_get_serial") then
        chpl_comm_fork(dnode, dsubloc, fn, args, args_size);
      else
        chpl_comm_fork_nb(dnode, dsubloc, fn, args, args_size);
    } else {
      var origSubloc = chpl_task_getRequestedSubloc();
      // We'd like to call chpl_executeOnNBaux() here, but the begin
      //  statement seems to cause a problem
      if (dsubloc == origSubloc) {
        // run on this sublocale
        if false {
          chpl_executeOnNBAux(fn, args);
        } else {
          if __primitive("task_get_serial") then
            chpl_ftable_call(fn, args);
          else
            // begin chpl_ftable_call(fn, args);
            chpl_comm_fork_nb(dnode, dsubloc, fn, args, args_size);
        }
      } else {
        // move to a different sublocale
        chpl_task_setSubLoc(dsubloc);
        if false {
          chpl_executeOnNBAux(fn, args);
        } else {
          if __primitive("task_get_serial") then
            chpl_ftable_call(fn, args);
          else
            // begin chpl_ftable_call(fn, args);
            chpl_comm_fork_nb(dnode, dsubloc, fn, args, args_size);
        }
        chpl_task_setSubLoc(origSubloc);
      }
    }
  }

  //////////////////////////////////////////
  //
  // support for tasking statements: begin, cobegin, coforall
  //

  //
  // runtime interface
  //
  pragma "insert line file info"
  extern proc chpl_task_addToTaskList(fn: int, args: c_void_ptr, subloc_id: int,
                                      ref tlist: _task_list, tlist_node_id: int,
                                      is_begin: bool);
  extern proc chpl_task_processTaskList(tlist: _task_list);
  extern proc chpl_task_executeTasksInList(tlist: _task_list);
  extern proc chpl_task_freeTaskList(tlist: _task_list);

  //
  // add a task to a list of tasks being built for a begin statement
  //
  pragma "insert line file info"
  export
  proc chpl_taskListAddBegin(subloc_id: int,        // target sublocale
                             fn: int,               // task body function idx
                             args: c_void_ptr,      // function args
                             ref tlist: _task_list, // task list
                             tlist_node_id: int     // task list owner node
                            ) {
    chpl_task_addToTaskList(fn, args, subloc_id, tlist, tlist_node_id, true);
  }

  //
  // add a task to a list of tasks being built for a cobegin or coforall
  // statement
  //
  pragma "insert line file info"
  export
  proc chpl_taskListAddCoStmt(subloc_id: int,        // target sublocale
                              fn: int,               // task body function idx
                              args: c_void_ptr,      // function args
                              ref tlist: _task_list, // task list
                              tlist_node_id: int     // task list owner node
                             ) {
    chpl_task_addToTaskList(fn, args, subloc_id, tlist, tlist_node_id, false);
  }

  //
  // make sure all tasks in a list are known to the tasking layer
  //
  pragma "insert line file info"
  export
  proc chpl_taskListProcess(task_list: _task_list) {
    chpl_task_processTaskList(task_list);
  }

  //
  // make sure all tasks in a list have an opportunity to run
  //
  pragma "insert line file info"
  export
  proc chpl_taskListExecute(task_list: _task_list) {
    chpl_task_executeTasksInList(task_list);
  }

  //
  // do final cleanup for a task list
  //
  pragma "insert line file info"
  export
  proc chpl_taskListFree(task_list: _task_list) {
    chpl_task_freeTaskList(task_list);
  }
}

// LocaleModel.chpl
//
// This provides a flat locale model architectural description.  The
// locales contain memory and a multi-core processor with homogeneous
// cores, and we ignore any affinity (NUMA effects) between the
// processor cores and the memory.  This architectural description is
// backward compatible with the architecture implicitly provided by
// releases 1.6 and preceding.
//
pragma "no use ChapelStandard"
module LocaleModel {

  // This should eventually be unified with
  // CHPL_LOCALE_MODEL_NUM_SUBLOCALES which is currently defined in the
  // runtime for any locale model that doesn't use sub locales
  param localeModelHasSubLocales = true;

  use ChapelLocale;
  use DefaultRectangular;
  use ChapelNumLocales;
  use Sys;

  extern proc chpl_task_getRequestedSubloc(): int(32);

  config param debugLocaleModel = false;

  // We would really like a class-static storage class.(C++ nomenclature)
  var doneCreatingLocales: bool = false;

  // The chpl_localeID_t type is used internally.  It should not be exposed to
  // the user.  The runtime defines the actual type, as well as a functional
  // interface for assembling and disassembling chpl_localeID_t values.  This
  // module then provides the interface the compiler-emitted code uses to do
  // the same.

  type chpl_nodeID_t = int(32);
  type chpl_sublocID_t = int(32);

  extern record chpl_localeID_t {
    // We need to know that this is a record type in order to pass it to and
    // return it from runtime functions properly, but we don't need or want
    // to see its contents.
  };

  // We need an explicit copy constructor because the compiler cannot create
  // a correct one for a record type whose members are not known to it.
  pragma "init copy fn"
  extern chpl__initCopy_chpl_rt_localeID_t
  proc chpl__initCopy(initial: chpl_localeID_t): chpl_localeID_t;

  extern var chpl_nodeID: chpl_nodeID_t;

  // Runtime interface for manipulating global locale IDs.
  extern
    proc chpl_rt_buildLocaleID(node: chpl_nodeID_t,
                               subloc: chpl_sublocID_t): chpl_localeID_t;

  extern
    proc chpl_rt_nodeFromLocaleID(loc: chpl_localeID_t): chpl_nodeID_t;

  extern
    proc chpl_rt_sublocFromLocaleID(loc: chpl_localeID_t): chpl_sublocID_t;

  // Compiler (and module code) interface for manipulating global locale IDs..
  pragma "insert line file info"
  proc chpl_buildLocaleID(node: chpl_nodeID_t, subloc: chpl_sublocID_t)
    return chpl_rt_buildLocaleID(node, subloc);

  pragma "insert line file info"
  proc chpl_nodeFromLocaleID(loc: chpl_localeID_t)
    return chpl_rt_nodeFromLocaleID(loc);

  pragma "insert line file info"
  proc chpl_sublocFromLocaleID(loc: chpl_localeID_t)
    return chpl_rt_sublocFromLocaleID(loc);

  //
  // The NUMA sublocale model
  //
  class NumaDomain : AbstractLocaleModel {
    const sid: chpl_sublocID_t;
    const name: string;

    proc chpl_id() return (parent:LocaleModel)._node_id; // top-level node id
    proc chpl_localeid() {
      return chpl_buildLocaleID((parent:LocaleModel)._node_id:chpl_nodeID_t,
                                sid);
    }
    proc chpl_name() return name;

    proc NumaDomain() {
    }

    proc NumaDomain(_sid, _parent) {
      sid = _sid;
      name = "ND"+sid;
      parent = _parent;
    }

    proc readWriteThis(f) {
      parent.readWriteThis(f);
      f <~> '.'+name;
    }

    proc getChildCount(): int { return 0; }
    iter getChildIndices() : int {
      halt("No children to iterate over.");
      yield -1;
    }
    proc addChild(loc:locale) { halt("Cannot add children to this locale type."); }
    proc getChild(idx:int) : locale { return nil; }

    // This is commented out b/c it leads to an internal error during
    // the resolveIntents pass.  See
    // test/functions/iterators/sungeun/iterInClass.future
    //
    // iter getChildren() : locale {
    //  halt("No children to iterate over.");
    //  yield nil;
    // }
  }

  //
  // The node model
  //
  class LocaleModel : AbstractLocaleModel {
    const callStackSize: int;
    const _node_id : int;
    const local_name : string;

    const numSubLocales: int;
    var childSpace: domain(1);
    var childLocales: [childSpace] NumaDomain;

    // This constructor must be invoked "on" the node
    // that it is intended to represent.  This trick is used
    // to establish the equivalence the "locale" field of the locale object
    // and the node ID portion of any wide pointer referring to it.
    proc LocaleModel() {
      if doneCreatingLocales {
        halt("Cannot create additional LocaleModel instances");
      }
      init();
    }

    proc LocaleModel(parent_loc : locale) {
      if doneCreatingLocales {
        halt("Cannot create additional LocaleModel instances");
      }
      parent = parent_loc;
      init();
    }

    proc chpl_id() return _node_id;     // top-level locale (node) number
    proc chpl_localeid() {
      extern const c_sublocid_any: chpl_sublocID_t;
      return chpl_buildLocaleID(_node_id:chpl_nodeID_t, c_sublocid_any);
    }
    proc chpl_name() return local_name;

    proc readWriteThis(f) {
      // Most classes will define it like this:
//      f <~> name;
      // but here it is defined thus for backward compatibility.
      f <~> new ioLiteral("LOCALE") <~> _node_id;
    }

    proc getChildSpace() return childSpace;

    proc getChildCount() return numSubLocales;

    iter getChildIndices() : int {
      for idx in childSpace do
        yield idx;
    }

    proc getChild(idx:int) : locale {
      if boundsChecking then
        if (idx < 0) || (idx >= numSubLocales) then
          halt("sublocale child index out of bounds (",idx,")");
      return childLocales[idx];
    }

    iter getChildren() : locale  {
      for loc in childLocales do
        yield loc;
    }

    proc getChildArray() {
      return childLocales;
    }

    //------------------------------------------------------------------------{
    //- Implementation (private)
    //-
    proc init() {
      _node_id = chpl_nodeID: int;

      // chpl_nodeName is defined in chplsys.c.
      // It supplies a node name obtained by running uname(3) on the
      // current node.  For this reason (as well), the constructor (or
      // at least this init method) must be run on the node it is
      // intended to describe.
      var comm, spawnfn : string;
      extern proc chpl_nodeName() : string;
      // sys_getenv returns zero on success.
      if sys_getenv("CHPL_COMM", comm) == 0 && comm == "gasnet" &&
        sys_getenv("GASNET_SPAWNFN", spawnfn) == 0 && spawnfn == "L"
      then local_name = chpl_nodeName() + "-" + _node_id : string;
      else local_name = chpl_nodeName();

      extern proc chpl_task_getCallStackSize(): int;
      callStackSize = chpl_task_getCallStackSize();

      extern proc chpl_numCoresOnThisLocale(): int;
      numCores = chpl_numCoresOnThisLocale();

      extern proc chpl_task_getNumSubLocales(): int(32);
      numSubLocales = chpl_task_getNumSubLocales();

      childSpace = {0..#numSubLocales};
      const origSubloc = chpl_task_getRequestedSubloc(); // this should be any
      for i in childSpace {
        // allocate the structure on the domain?
        // MUST CHANGE SUBLOC HERE (prolly by primitive)
        chpl_task_setSubLoc(i:chpl_sublocID_t);
        childLocales[i] = new NumaDomain(i:chpl_sublocID_t, this);
        childLocales[i].numCores = 1; // for now
      }
      chpl_task_setSubLoc(origSubloc);
    }
    //------------------------------------------------------------------------}

    inline proc subloc return c_sublocid_any;
 }

  //
  // An instance of this class is the default contents 'rootLocale'.
  //
  // In the current implementation a platform-specific architectural description
  // may overwrite this instance or any of its children to establish a more customized
  // representation of the system resources.
  //
  class RootLocale : AbstractRootLocale {

    // Would like to make myLocaleSpace distributed with one index per node.
    const myLocaleSpace: domain(1) = {0..numLocales-1};
    const myLocales: [myLocaleSpace] locale;

    proc RootLocale() {
      parent = nil;
      numCores = 0;
    }

    // The init() function must use initOnLocales() to iterate (in
    // parallel) over the locales to set up the LocaleModel object.
    // In addition, the initial 'here' must be set.
    proc init() {
      forall locIdx in initOnLocales() {
        chpl_task_setSubLoc(c_sublocid_any);
        const node = new LocaleModel(this);
        myLocales[locIdx] = node;
        numCores += node.numCores;
      }
    }

    // Has to be globally unique and not equal to a node ID.
    // We return numLocales for now, since we expect nodes to be
    // numbered less than this.
    // -1 is used in the abstract locale class to specify an invalid node ID.
    proc chpl_id() return numLocales;
    proc chpl_localeid() {
      extern const c_sublocid_any: chpl_sublocID_t;
      return chpl_buildLocaleID(numLocales:chpl_nodeID_t, c_sublocid_any);
    }
    proc chpl_name() return local_name();
    proc local_name() return "rootLocale";

    proc readWriteThis(f) {
      f <~> name;
    }

    proc getChildCount() return this.myLocaleSpace.numIndices;

    proc getChildSpace() return this.myLocaleSpace;

    iter getChildIndices() : int {
      for idx in this.myLocaleSpace do
        yield idx;
    }

    proc getChild(idx:int) return this.myLocales[idx];

    iter getChlidren() : locale  {
      for loc in this.myLocales do
        yield loc;
    }

    proc getDefaultLocaleSpace() return this.myLocaleSpace;
    proc getDefaultLocaleArray() return myLocales;

    proc localeIDtoLocale(id : chpl_localeID_t) {
      const dnode = chpl_nodeFromLocaleID(id);
      const dsubloc = chpl_sublocFromLocaleID(id);
      if (dsubloc == c_sublocid_curr) || (dsubloc == c_sublocid_any) then
        return (myLocales[dnode:int]):locale;
      else
        return (myLocales[dnode:int].getChild(dsubloc:int)):locale;
    }
  }

  //////////////////////////////////////////
  //
  // support for memory management
  //

  // Here be dragons: If the return type is specified, then normalize.cpp
  // inserts an initializer for the return value which calls its constructor,
  // which calls chpl_here_alloc ad infinitum.  But if the return type is
  // left off, it works!

  // The allocator pragma is used by scalar replacement.
  pragma "allocator"
  pragma "no sync demotion"
  proc chpl_here_alloc(x, md:int(16)) {
    pragma "insert line file info"
      extern proc chpl_mem_alloc(size:int, md:int(16)) : opaque;
    var nbytes = __primitive("sizeof", x);
    var mem = chpl_mem_alloc(nbytes, md + chpl_memhook_md_num());
    return __primitive("cast", x.type, mem);
  }

  pragma "allocator"
  pragma "no sync demotion"
  proc chpl_here_calloc(x, number:int, md:int(16)) {
    pragma "insert line file info"
      extern proc chpl_mem_calloc(number:int, size:int, md:int(16)) : opaque;
    var nbytes = __primitive("sizeof", x);
    var mem = chpl_mem_calloc(number, nbytes, md + chpl_memhook_md_num());
    return __primitive("cast", x.type, mem);
  }

  pragma "allocator"
  pragma "no sync demotion"
  proc chpl_here_realloc(x, md:int(16)) {
    // TODO: The pointer should really be of type opaque, but we don't
    // handle object ==> opaque casts correctly.  (In codegen, opaque behaves
    // like an lvalue, but in the type system it isn't one.)
    pragma "insert line file info"
      extern proc chpl_mem_realloc(ptr:opaque, size:int, md:int(16)) : opaque;
    var nbytes = __primitive("sizeof", x);
    var mem = chpl_mem_realloc(__primitive("cast_to_void_star", x), nbytes,
                               md + chpl_memhook_md_num());
    return __primitive("cast", x.type, mem);
  }

  pragma "no sync demotion"
  proc chpl_here_free(x) {
    // TODO: The pointer should really be of type opaque, but we don't
    // handle object ==> opaque casts correctly.  (In codegen, opaque behaves
    // like an lvalue, but in the type system it isn't one.)
    pragma "insert line file info"
      extern proc chpl_mem_free(ptr:opaque): void;
    chpl_mem_free(__primitive("cast_to_void_star", x));
  }

  //////////////////////////////////////////
  //
  // support for "on" statements
  //

  //
  // runtime interface
  //
  extern proc chpl_comm_fork(loc_id: int, subloc_id: int,
                             fn: int, args: c_void_ptr, arg_size: int(32));
  extern proc chpl_comm_fork_fast(loc_id: int, subloc_id: int,
                                  fn: int, args: c_void_ptr, args_size: int(32));
  extern proc chpl_comm_fork_nb(loc_id: int, subloc_id: int,
                                fn: int, args: c_void_ptr, args_size: int(32));
  extern proc chpl_ftable_call(fn: int, args: c_void_ptr): void;
  extern proc chpl_task_setSubLoc(subLoc: int(32));
  extern const c_sublocid_any: chpl_sublocID_t;
  extern const c_sublocid_curr: chpl_sublocID_t;

  //
  // regular "on"
  //
  pragma "insert line file info"
  export
  proc chpl_executeOn(loc: chpl_localeID_t, // target locale
                      fn: int,              // on-body function idx
                      args: c_void_ptr,     // function args
                      args_size: int(32)    // args size
                     ) {
    const dnode =  chpl_nodeFromLocaleID(loc);
    const dsubloc =  chpl_sublocFromLocaleID(loc);
    if dnode != chpl_nodeID {
      chpl_comm_fork(dnode, dsubloc, fn, args, args_size);
    } else {
      // run directly on this node
      var origSubloc = chpl_task_getRequestedSubloc();
      if (dsubloc == origSubloc) {
        chpl_ftable_call(fn, args);
      } else {
        // move to a different sublocale
        chpl_task_setSubLoc(dsubloc);
        chpl_ftable_call(fn, args);
        chpl_task_setSubLoc(origSubloc);
      }
    }
  }

  //
  // fast "on" (doesn't do anything that could deadlock a comm layer,
  // in the Active Messages sense)
  //
  pragma "insert line file info"
  export
  proc chpl_executeOnFast(loc: chpl_localeID_t, // target locale
                          fn: int,              // on-body function idx
                          args: c_void_ptr,     // function args
                          args_size: int(32)    // args size
                         ) {
    const dnode =  chpl_nodeFromLocaleID(loc);
    const dsubloc =  chpl_sublocFromLocaleID(loc);
    if dnode != chpl_nodeID {
      chpl_comm_fork_fast(dnode, dsubloc, fn, args, args_size);
    } else {
      var origSubloc = chpl_task_getRequestedSubloc();
      if (dsubloc == origSubloc) {
        chpl_ftable_call(fn, args);
      } else {
        // move to a different sublocale
        chpl_task_setSubLoc(dsubloc);
        chpl_ftable_call(fn, args);
        chpl_task_setSubLoc(origSubloc);
      }
    }
  }

  // Unused for now due to bug in non-developer mode when using 'begin'
  inline proc chpl_executeOnNBAux(fn: int,         // on-body function idx
                                  args: c_void_ptr // function args
                                  ) {
    if __primitive("task_get_serial") then
      chpl_ftable_call(fn, args);
    else
      begin chpl_ftable_call(fn, args);
  }
  //
  // nonblocking "on" (doesn't wait for completion)
  //
  pragma "insert line file info"
  export
  proc chpl_executeOnNB(loc: chpl_localeID_t, // target locale
                        fn: int,              // on-body function idx
                        args: c_void_ptr,     // function args
                        args_size: int(32)    // args size
                       ) {
    //
    // If we're in serial mode, we should use blocking rather than
    // non-blocking "on" in order to serialize the forks.
    //
    const dnode =  chpl_nodeFromLocaleID(loc);
    const dsubloc =  chpl_sublocFromLocaleID(loc);
    if dnode != chpl_nodeID {
      if __primitive("task_get_serial") then
        chpl_comm_fork(dnode, dsubloc, fn, args, args_size);
      else
        chpl_comm_fork_nb(dnode, dsubloc, fn, args, args_size);
    } else {
      var origSubloc = chpl_task_getRequestedSubloc();
      // We'd like to call chpl_executeOnNBaux() here, but the begin
      //  statement seems to cause a problem
      if (dsubloc == origSubloc) {
        // run on this sublocale
        if false {
          chpl_executeOnNBAux(fn, args);
        } else {
          if __primitive("task_get_serial") then
            chpl_ftable_call(fn, args);
          else
            // begin chpl_ftable_call(fn, args);
            chpl_comm_fork_nb(dnode, dsubloc, fn, args, args_size);
        }
      } else {
        // move to a different sublocale
        chpl_task_setSubLoc(dsubloc);
        if false {
          chpl_executeOnNBAux(fn, args);
        } else {
          if __primitive("task_get_serial") then
            chpl_ftable_call(fn, args);
          else
            // begin chpl_ftable_call(fn, args);
            chpl_comm_fork_nb(dnode, dsubloc, fn, args, args_size);
        }
        chpl_task_setSubLoc(origSubloc);
      }
    }
  }

  //////////////////////////////////////////
  //
  // support for tasking statements: begin, cobegin, coforall
  //

  //
  // runtime interface
  //
  pragma "insert line file info"
  extern proc chpl_task_addToTaskList(fn: int, args: c_void_ptr, subloc_id: int,
                                      ref tlist: _task_list, tlist_node_id: int,
                                      is_begin: bool);
  extern proc chpl_task_processTaskList(tlist: _task_list);
  extern proc chpl_task_executeTasksInList(tlist: _task_list);
  extern proc chpl_task_freeTaskList(tlist: _task_list);

  //
  // add a task to a list of tasks being built for a begin statement
  //
  pragma "insert line file info"
  export
  proc chpl_taskListAddBegin(subloc_id: int,        // target sublocale
                             fn: int,               // task body function idx
                             args: c_void_ptr,      // function args
                             ref tlist: _task_list, // task list
                             tlist_node_id: int     // task list owner node
                            ) {
    chpl_task_addToTaskList(fn, args, subloc_id, tlist, tlist_node_id, true);
  }

  //
  // add a task to a list of tasks being built for a cobegin or coforall
  // statement
  //
  pragma "insert line file info"
  export
  proc chpl_taskListAddCoStmt(subloc_id: int,        // target sublocale
                              fn: int,               // task body function idx
                              args: c_void_ptr,      // function args
                              ref tlist: _task_list, // task list
                              tlist_node_id: int     // task list owner node
                             ) {
    chpl_task_addToTaskList(fn, args, subloc_id, tlist, tlist_node_id, false);
  }

  //
  // make sure all tasks in a list are known to the tasking layer
  //
  pragma "insert line file info"
  export
  proc chpl_taskListProcess(task_list: _task_list) {
    chpl_task_processTaskList(task_list);
  }

  //
  // make sure all tasks in a list have an opportunity to run
  //
  pragma "insert line file info"
  export
  proc chpl_taskListExecute(task_list: _task_list) {
    chpl_task_executeTasksInList(task_list);
  }

  //
  // do final cleanup for a task list
  //
  pragma "insert line file info"
  export
  proc chpl_taskListFree(task_list: _task_list) {
    chpl_task_freeTaskList(task_list);
  }
}


Revert "Revert "temporarily break hello.chpl test to verify travis prints start_test log""

This reverts commit 1371b6c899d3058ae61a5b6a0c3e937881f9f5a9.

writeln("Hello, world!!!");    // print 'Hello, world!' to the console

writeln("Hello, world!");    // print 'Hello, world!' to the console


Fixed future to use "var s = Ftree()" to initialize a field rather than "var s: Ftree()" since it captures the same issue and will continue to parse even if we disallow the latter.

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

class FTree {
  var  indices: domain(int);
  var  nodes  : [indices] real;
}

class Function1d {
  var s = FTree();
}

def main() {
  var f: Function1d;
  //var ff = Function1d();
}

class FTree {
  var  indices: domain(int);
  var  nodes  : [indices] real;
}

class Function1d {
  var s: FTree();
}

def main() {
  var f: Function1d;
  //var ff = Function1d();
}


Bug fix in ssca2 algorithm.

Bug fix in ssca2 algorithm.

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

-- heuristic value taken from the Matlab implementation to set default
-- values if the user doesn't override any of them
config const scale = 9;

-- input values to describe the graph.  Override these on the
-- executable's command-line using the -s flag
config const totVertices: integer = 2**scale;
config const maxCliqueSize : integer = 10;
config const probUnidirectional: float = 0.3;
config const maxParallelEdges: integer = 3;
config const probIntercliqueEdges: float = 0.6;
config const percentIntegerWeights: float = 0.6;
config const maxIntWeight: integer = 8**scale;
config const maxStrLen: integer = ceil(scale/6.0);

config const cliqueGeneratorsPerLocale: integer = 1;
config const numCliqueGenerators: integer = numLocales * cliqueGeneratorsPerLocale;

var Vertices: domain(1) = 1..totVertices;
type vertex: index(Vertices);

--var EdgeSpace: domain(Vertices*Vertices);
--var EdgeSpace: domain(2) = [1..totVertices, 1..totVertices];
var EdgeSpace: domain(2) = (1..totVertices, 1..totVertices);
--var AdjSpace: sparse domain(EdgeSpace) = nil;
var AdjSpace: domain(EdgeSpace) = EdgeSpace;
var edge: index(Edges);

record multiedge {
  var intWeights: seq(integer);
  var strWeights: seq(string);
};

var Edges: [AdjSpace] multiedge;

union weightType {
  var intWeight: integer;
  var stringWeight: string;
}

record edgeTuple {
  var source: vertex;
  var sink: vertex;
  var weight: weightType;
}

-- random number stuff: currently unimplemented, and doing so in
-- parallel will require some care

-- return a random float in [0.0, 1.0)
function randomFloat() {
  halt("Need to implement randomFloat()");
  return 0.0;
}

-- flip a coin, returning heads (true) with probability headsProb
function coinFlip(headsProb: float = 0.5) {
  return (randomFloat() < headsProb);
}

-- return a random integer from 1 to maxval, inclusive
function randomInt(maxint: integer) {
  halt("Need to implement randomInt()");
  return 0;
}

-- return a random string of length strlen
function randomString(strlen: integer) {
  halt("Need to implement randomString()");
  return "<empty>";
}

function generateDirectedEdges(v1, v2) {
  var edges: seq(edgeTuple);
  var numParallelEdges: integer = randomInt(maxParallelEdges);
  forall parEdge in 1..numParallelEdges { -- TODO: could drop "parEdge"
    var weight: weightType;
    if (coinFlip(percentIntegerWeights)) {
      weight.intWeight = randomInt(maxIntWeight);
    } else {
      weight.stringWeight = randomString(maxStrLen);
    }
    edges #= edgeTuple(source = v1, sink = v2, weight = weight);
  }
  return edges;
}

function generateEdges(v1, v2) {
  var edges: seq(edgeTuple);
  var forward = true,   -- generate forward edges?
      backward = true;  -- generate backward edges?

  if (coinFlip(probUnidirectional)) {
    if (coinFlip()) {
      backward = false;
    } else {
      forward = false;
    }
  }
  if (forward) {
    edges #= generateDirectedEdges(v1, v2);
  }
  if (backward) {
    edges #= generateDirectedEdges(v2, v1);
  }
}

function generateEdgeList() {
  var edgeTuples: seq(edgeTuple);
  var numVerticesTaken = 0;
  var clique: [Vertices] integer;
  var numCliques = 0;

  forall g in 1..numCliqueGenerators { -- TODO: could drop "g"
    while (numVerticesTaken != totVertices) {
      var cliqueSize: integer;
      var vertexOffset: integer;
      var myCliqueNum: integer;
      atomic {
        vertexOffset = numVerticesTaken;
        var numVerticesRemaining = totVertices - numVerticesTaken;
        cliqueSize = min(randomInt(maxCliqueSize), numVerticesRemaining);
        numVerticesTaken += cliqueSize;
        numCliques += 1;
        myCliqueNum = numCliques;
      }
      -- generate intraclique edges
      forall v1 in 1..cliqueSize {
        clique(v1) = myCliqueNum;
        forall v2 in v1+1..cliqueSize {
          edgeTuples #= generateEdges(v1 + vertexOffset, v2 + vertexOffset);
        }
      }
    }
  }

  -- generate interclique edges
  -- the following uses the basic exponential distribution from
  -- the pseudocode in the written spec rather than the
  -- distribution of the executable specification
  forall v1 in Vertices {
    var d = 2;
    var p = probIntercliqueEdges;
    while (d < totVertices/2) {
      var v2: vertex = (v1+d) mod totVertices;
      var r = randomFloat();
      if (clique(v1) != clique(v2) and r <= p) {
        edgeTuples #= generateEdges(v1, v2);
      }

      d *= 2;
      p /= 2;
    }
  }

  -- TODO: at this point, I think we're required to randomly shuffle
  -- the vertex numbers and edge tuples

  return edgeTuples;
}

function kernel1(edgeTuples: seq(edgeTuple)) {
  forall t in edgeTuples {
    var newEdge: edge = (t.source, t.sink);
    AdjSpace += newEdge;
    typeselect t.weight {
      when i:integer do
        Edges(newEdge).intWeights #= i;
      when s:string do
        Edges(newEdge).strWeights #= s;
    }
  }
}

function kernel2(soughtString: string) {
  var maxIntWeight = sum reduce Edges.intWeights;
  var maxEdges: sparse domain(EdgeSpace) = nil;

  forall e in Edges {
    cobegin {
      forall i in e.intWeights {
        if (i == maxIntWeight) {
          maxEdges += e;
        }
      }
      forall s in e.strWeights {
        if (s == soughtString) {
          maxEdges += e;
        }
      }
    }
  }

  return maxEdges;
}

function kernel3(startEdges, subGraphEdgeLength: integer) {
  forall startEdge in startEdges {
    results #= findSubgraph(startEdge);
  }

  function findSubgraph(startEdge) {
    var frontier: sparse domain(Vertices);
    frontier += sv1;
    frontier += sv2;
    var subgraph = SubGraph(V = frontier, E = startEdge);
    for dist in 1..startEdges {
      var newEdges: seq(edge);
      forall v1 in frontier {
        forall v2 in AdjDom(v1, *) {
          newEdges #= (v1, v2);
        }
      }
--      subgraph.E += newEdges;
      forall e in newEdges {
        subgraph.E += e;
      }
--      frontier += [(v1, v2) in newEdges] v2;
      forall (v1, v2) in newEdges {
        frontier += v2;
      }
--      frontier -= subgraph.V;
      forall v in subgraph.V {
        frontier -= v;
      }
      subgraph.V += frontier;
      forall v in frontier {
        subgraph.V += v;
      }
      return subgraph;
    }
  }
}

function kernel4(maxClusterSize: integer, k4alpha) {
  halt("kernel4 not implemented yet");
}

function writeConfigs() {
  writeln("scale = ", scale);
  writeln("totVertices = ", totVertices);
  writeln("maxCliqueSize = ", maxCliqueSize);
  writeln("probUnidirectional = ", probUnidirectional);
  writeln("maxParallelEdges = ", maxParallelEdges);
  writeln("probIntercliqueEdges = ", probIntercliqueEdges);
  writeln("percentIntegerWeights = ", percentIntegerWeights);
  writeln("maxIntWeight = ", maxIntWeight);
  writeln("maxStrLen = ", maxStrLen);
}

function main() {
  writeConfigs();
  generateEdgeList();
}

-- heuristic value taken from the Matlab implementation to set default
-- values if the user doesn't override any of them
config const scale = 9;

-- input values to describe the graph.  Override these on the
-- executable's command-line using the -s flag
config const totVertices: integer = 2**scale;
config const maxCliqueSize : integer = 10;
config const probUnidirectional: float = 0.3;
config const maxParallelEdges: integer = 3;
config const probIntercliqueEdges: float = 0.6;
config const percentIntegerWeights: float = 0.6;
config const maxIntWeight: integer = 8**scale;
config const maxStrLen: integer = ceil(scale/6.0);

config const cliqueGeneratorsPerLocale: integer = 1;
config const numCliqueGenerators: integer = numLocales * cliqueGeneratorsPerLocale;

var Vertices: domain(1) = 1..totVertices;
type vertex: index(Vertices);

--var EdgeSpace: domain(Vertices*Vertices);
--var EdgeSpace: domain(2) = [1..totVertices, 1..totVertices];
var EdgeSpace: domain(2) = (1..totVertices, 1..totVertices);
--var AdjSpace: sparse domain(EdgeSpace) = nil;
var AdjSpace: domain(EdgeSpace) = EdgeSpace;
var edge: index(Edges);

record multiedge {
  var intWeights: seq(integer);
  var strWeights: seq(string);
};

var Edges: [AdjSpace] multiedge;

union weightType {
  var intWeight: integer;
  var stringWeight: string;
}

record edgeTuple {
  var source: vertex;
  var sink: vertex;
  var weight: weightType;
}

-- random number stuff: currently unimplemented, and doing so in
-- parallel will require some care

-- return a random float in [0.0, 1.0)
function randomFloat() {
  halt("Need to implement randomFloat()");
  return 0.0;
}

-- flip a coin, returning heads (true) with probability headsProb
function coinFlip(headsProb: float = 0.5) {
  return (randomFloat() < headsProb);
}

-- return a random integer from 1 to maxval, inclusive
function randomInt(maxint: integer) {
  halt("Need to implement randomInt()");
  return 0;
}

-- return a random string of length strlen
function randomString(strlen: integer) {
  halt("Need to implement randomString()");
  return "<empty>";
}

function generateDirectedEdges(v1, v2) {
  var edges: seq(edgeTuple);
  var numParallelEdges: integer = randomInt(maxParallelEdges);
  forall parEdge in 1..numParallelEdges { -- TODO: could drop "parEdge"
    var weight: weightType;
    if (coinFlip(percentIntegerWeights)) {
      weight.intWeight = randomInt(maxIntWeight);
    } else {
      weight.stringWeight = randomString(maxStrLen);
    }
    edges #= edgeTuple(source = v1, sink = v2, weight = weight);
  }
  return edges;
}

function generateEdges(v1, v2) {
  var edges: seq(edgeTuple);
  var forward = true,   -- generate forward edges?
      backward = true;  -- generate backward edges?

  if (coinFlip(probUnidirectional)) {
    if (coinFlip()) {
      backward = false;
    } else {
      forward = false;
    }
  }
  if (forward) {
    edges #= generateDirectedEdges(v1, v2);
  }
  if (backward) {
    edges #= generateDirectedEdges(v2, v1);
  }
}

function generateEdgeList() {
  var edgeTuples: seq(edgeTuple);
  var numVerticesTaken = 0;
  var clique: [Vertices] integer;
  var numCliques = 0;

  forall g in 1..numCliqueGenerators { -- TODO: could drop "g"
    while (numVerticesTaken != totVertices) {
      var cliqueSize: integer;
      var vertexOffset: integer;
      var myCliqueNum: integer;
      atomic {
        vertexOffset = numVerticesTaken;
        var numVerticesRemaining = totVertices - numVerticesTaken;
        cliqueSize = min(randomInt(maxCliqueSize), numVerticesRemaining);
        numVerticesTaken += cliqueSize;
        numCliques += 1;
        myCliqueNum = numCliques;
      }
      -- generate intraclique edges
      forall v1 in 1..cliqueSize {
        clique(v1) = myCliqueNum;
        forall v2 in v1+1..cliqueSize {
          edgeTuples #= generateEdges(v1 + vertexOffset, v2 + vertexOffset);
        }
      }
    }
  }

  -- generate interclique edges
  -- the following uses the basic exponential distribution from
  -- the pseudocode in the written spec rather than the
  -- distribution of the executable specification
  forall v1 in Vertices {
    var d = 2;
    var p = probIntercliqueEdges;
    while (d < totVertices/2) {
      var v2: vertex = (v1+d) mod totVertices;
      var r = randomFloat();
      if (clique(v1) != clique(v2) and r <= p) {
        edgeTuples #= generateEdges(v1, v2);
      }

      d *= 2;
      p /= 2;
    }
  }

  -- TODO: at this point, I think we're required to randomly shuffle
  -- the vertex numbers and edge tuples

  return edgeTuples;
}

function kernel1(edgeTuples: seq(edgeTuple)) {
  forall t in edgeTuples {
    var newEdge: edge = (t.source, t.sink);
    AdjSpace += newEdge;
    typeselect t.weight {
      when i:integer do
        Edges(newEdge).intWeights #= i;
      when s:string do
        Edges(newEdge).strWeights #= s;
    }
  }
}

function kernel2(soughtString: string) {
  var maxIntWeight = sum reduce Edges.intWeights;
  var maxEdges: sparse domain(EdgeSpace) = nil;

  forall e in Edges {
    cobegin {
      forall i in e.intWeights {
        if (i == maxIntWeight) {
          maxEdges += e;
        }
      }
      forall s in e.strWeights {
        if (s == soughtString) {
          maxEdges += e;
        }
      }
    }
  }

  return maxEdges;
}

function kernel3(startEdges, subGraphEdgeLength: integer) {
  forall startEdge in startEdges {
    results #= findSubgraph(startEdge);
  }

  function findSubgraph(startEdge) {
    var frontier: sparse domain(Vertices);
    frontier += sv1;
    frontier += sv2;
    var subgraph = SubGraph(V = frontier, E = startEdge);
    for dist in 1..startEdges {
      var newEdges: seq(edge);
      forall v1 in frontier {
        forall v2 in AdjDom(v1, *) {
          newEdges #= (v1, v2);
        }
      }
--      subgraph.E += newEdges;
      forall e in newEdges {
        subgraph.E += newEdges;
      }
--      frontier += [(v1, v2) in newEdges] v2;
      forall (v1, v2) in newEdges {
        frontier += v2;
      }
--      frontier -= subgraph.V;
      forall v in subgraph.V {
        frontier -= v;
      }
      subgraph.V += frontier;
      forall v in frontier {
        subgraph.V += v;
      }
      return subgraph;
    }
  }
}

function kernel4(maxClusterSize: integer, k4alpha) {
  halt("kernel4 not implemented yet");
}

function writeConfigs() {
  writeln("scale = ", scale);
  writeln("totVertices = ", totVertices);
  writeln("maxCliqueSize = ", maxCliqueSize);
  writeln("probUnidirectional = ", probUnidirectional);
  writeln("maxParallelEdges = ", maxParallelEdges);
  writeln("probIntercliqueEdges = ", probIntercliqueEdges);
  writeln("percentIntegerWeights = ", percentIntegerWeights);
  writeln("maxIntWeight = ", maxIntWeight);
  writeln("maxStrLen = ", maxStrLen);
}

function main() {
  writeConfigs();
  generateEdgeList();
}