Adding copyright info.

Adding copyright info.

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

// Uncommented when this becomes the default string implementation
// pragma "no use ChapelStandard"

/*
 * NOTES:
 *
 * - An empty string is represented by the default baseType value
 *   which is always considered to be a string literal.  It will never
 *   be freed or reference counted.  Another option would be to use a
 *   special NULL extern symbol.  Unfortunately, since _defaultOf()
 *   currently does not work for types with "ignore noinit", we'd
 *   still have to handle the default baseType value, so it would not
 *   be worth it to use NULL.  If the situation changes, it might be
 *   worth reconsidering (also taking into consideration the runtime
 *   functions that back this).
 *
 * - Not all operations support interaction with the baseType, e.g.,
 *   relational operations.  I chose to implement the ones I thought
 *   were commonly used like concatenation and assignment.  When this
 *   becomes the default, the compiler should automatically coerce the
 *   rest.
 *
 * - It is assumed the the baseType is a local-only type, so we never
 *   make a remote copy of one passed in by the user, though remote
 *   copies are made of internal baseType variables.
 *
 * - The base field is reference counted in the following manner.
 *   Every string has a ref count object and an alias ref count
 *   object.  If base is aliased, the the alias ref count object
 *   points to the ref count object of the base being aliased.
 *   Otherwise, the alias ref count object is equal to the ref count
 *   object.  There are set of ref counting support routines that
 *   update the "right" ref count, but can also bypassed if the alias
 *   check has already been performed.  I'm sure it's possible to
 *   implement this using a single ref count object, but this was
 *   easier to debug.
 *
 * TODOS:
 *
 * - When this becomes the default string implementation, the compiler
 *   may need to know of the string implementation as it does for
 *   array, etc.  Consider adding a pragma on the record.
 *
 * - Consider storing max size in the string record to enable more
 *   control of buffer reuse.
 *
 * - Consider changing primitives like string_copy and string_concat
 *   to take optional (or maybe required) length arguments to avoid
 *   extra calls to strlen().
 *
 */

module BaseStringType {
  use CString;
  // This type must support the c_string interface
  type baseType = c_string;
  param baseTypeString = "c_string":c_string;
  inline proc free_baseType(s) { chpl_free_c_string(s); }

  pragma "insert line file info"
  extern proc stringMove(dest: c_string, src: c_string, len: int): c_string;
  pragma "insert line file info"
  extern proc remoteStringCopy(src_loc: int, src_addr: c_string, len: int): c_string;

  config const debugStrings = false;
}

// Chapel Strings
module NewString {
  use BaseStringType;

  class string_refcnt {
    var val: atomic_refcnt;
    inline proc inc(cnt=1) { val.inc(); }
    inline proc dec(cnt=1) return val.dec();
    inline proc read(cnt=1) return val._cnt.read();
  }

  // pragma "string record"
  pragma "ignore noinit"
  pragma "no default functions"
  record string_rec {
    var base: baseType;
    var len: int;
    var home: locale; // this does not change through assignment
    var refCnt: string_refcnt;
    var aliasRefCnt: string_refcnt;

    proc string_rec() {
      if debugStrings then writeln("in string()");
      this.home = here;
      this.refCnt = new string_refcnt();
      this.aliasRefCnt = this.refCnt;
      if debugStrings then writeln("leaving string()");
    }

    proc ref ~string_rec() {
      if debugStrings then writeln("in ~string()");
      if this.isEmptyString() {
        delete this.refCnt;
      } else {
        const refs =  decRefCnt();
        if refs < 0 then halt("string reference count is negative!");
        if refs == 0 then {
          if debugStrings then writeln("  freeing base: ", this.base);
          on this.home {
            free_baseType(this.base);
            freeRefCnt();
          }
        } else {
          if debugStrings then writeln("  not freeing base: ", this.base);
          if isAlias then delete this.refCnt;
        }
      }
      if debugStrings then writeln("leaving ~string()");
    }

    // This is assumed to be called from this.home
    proc ref reinitString(s: baseType, slen:int =-1) {
      assert(s.locale.id == here.id);
      assert(this.home.id == here.id);
      if debugStrings then writeln("in string.reinitString()");
      if this.isEmptyString() {
        if slen==0 || s==_defaultOf(baseType) then return; // nothing to do
        else this.incRefCntNoAlias(); // update my own ref count
      }
      const new_len = if slen == -1 then s.length else slen;
      // If the this.base is longer than s, then reuse the buffer
      if new_len != 0 {
        if new_len < this.len {
          // reuse the buffer
          this.base = stringMove(this.base, s, new_len);
        } else {
          // free the old buffer
          if this.len != 0 then
            on home do free_baseType(this.base);
          // allocate a new buffer
          this.base = stringMove(_defaultOf(baseType), s, new_len);
        }
      } else {
        // free the old buffer
        if this.len != 0 then free_baseType(this.base);
        this.base = _defaultOf(baseType); // empty string are always literals
      }
      this.len = new_len;
      if debugStrings then writeln("leaving string.reinitString()");
    }

    inline proc c_str() {
      if debugStrings then writeln("in .c_str()");
      if this.home.id != here.id then
        halt("Cannot call .c_str() on a remote string");
      if debugStrings then writeln("leaving .c_str()");
      return this.base;
    }

    inline proc length return len;
    inline proc size return len;

    // Returns a string containing the character at the given index of
    // the string, or an empty string if the index is out of bounds.
    inline proc substring(i: int) {
      var ret: string_rec;
      if !this.isEmptyString() && i>0 {
        const sremote = this.home.id != here.id;
        const sbase = if sremote
                      then remoteStringCopy(this.home.id, this.base, this.len)
                      else this.base;
        const cs = sbase.substring(i);
        if cs != _defaultOf(baseType) {
          ret.base = cs;
          ret.len = 1;
          ret.incRefCntNoAlias();
        }
        if sremote then free_baseType(sbase);
      }
      return ret;
    }

    // Returns a string containing the string sliced by the given
    // range, or an empty string if the range does not overlap.
    inline proc substring(r: range) {
      var ret: string_rec;
      if !this.isEmptyString() && r.size>0 {
        const sremote = this.home.id != here.id;
        const sbase = if sremote
                      then remoteStringCopy(this.home.id, this.base, this.len)
                      else this.base;
        const cs = sbase.substring(r);
        if cs != _defaultOf(baseType) {
          ret.base = cs;
          ret.len = cs.length;
          ret.incRefCntNoAlias();
        }
        if sremote then free_baseType(sbase);
      }
      return ret;
    }

    // Returns the index of the first occurrence of a substring within a
    // string or 0 if the substring is not in the string.
    inline proc find(s: string_rec):int {
      var ret: int;
      if this.isEmptyString() then return 0;
      if s.isEmptyString() then return 0;
      // Assume s.base is shorter than this.base, so go to the home locale
      on this.home {
        const sremote = s.home.id != here.id;
        const sbase = if sremote
                      then remoteStringCopy(s.home.id, s.base, s.len)
                      else s.base;
        ret = this.base.indexOf(sbase);
        if sremote then free_baseType(sbase);
      }
      return ret;
    }

    inline proc find(s: baseType):int {
      if s.locale.id != here.id then
        halt("Cannot search a string with a remote "+baseTypeString);
      if this.isEmptyString() then return 0;
      if s==_defaultOf(baseType) then return 0;
      // We don't support remote c_string, so for this version, copy this.base
      const sremote = this.home.id != here.id;
      const sbase = if sremote
                    then remoteStringCopy(this.home.id, this.base, this.len)
                    else this.base;
      const ret = sbase.indexOf(s);
      if sremote then free_baseType(sbase);
      return ret;
    }

    // ref count functions
    inline proc isAlias return this.refCnt != this.aliasRefCnt;

    inline proc incRefCnt() {
      if !this.isAlias then this.refCnt.inc();
                       else this.aliasRefCnt.inc();
    }

    inline proc incRefCntNoAlias() {
      this.refCnt.inc();
    }

    inline proc decRefCnt() {
      if !this.isAlias then return this.refCnt.dec();
                       else return this.aliasRefCnt.dec();
    }

    inline proc decRefCntNoAlias() {
      return this.refCnt.dec();
    }

    inline proc freeRefCnt() {
      delete this.refCnt;
      if this.isAlias then delete this.aliasRefCnt;
    }

    inline proc isEmptyString() {
      if this.isAlias then return false;
      else return this.len==0; // this should be enough of a check
    }

    proc writeThis(f: Writer) {
      if !this.isEmptyString() {
        if (this.home.id != here.id) {
          const tcs = remoteStringCopy(this.home.id, this.base, this.len);
          f.write(tcs);
          free_baseType(tcs);
        } else {
          f.write(this.base);
        }
      }
    }
  }

  pragma "donor fn"
  pragma "auto copy fn"
    // We'd like this to be by ref, but doing so leads to an internal
    // compiler error.  See
    // $CHPL_HOME/test/types/records/sungeun/recordWithRefCopyFns.future
    /*inline*/ proc chpl__autoCopy(/*ref*/ s: string_rec) {
    if debugStrings then writeln("in autoCopy()");
    pragma "no auto destroy"
    var ret: string_rec;
    if !s.isEmptyString() {
      ret.home = s.home;
      ret.base = s.base;
      ret.len = s.len;
      if !s.isAlias then ret.aliasRefCnt = s.refCnt;
                    else ret.aliasRefCnt = s.aliasRefCnt;
      ret.incRefCnt();
    }
    if debugStrings then writeln("leaving autoCopy()");
    return ret;
  }

  /*
   * NOTES: Just a word on memory allocation in the generated code.
   * Any function that returns a string copies the returned value
   * including the c_string via a call to chpl__initCopy().  The
   * copied value is returned, and the original is destroyed on the
   * way out.  I'd really like to figure out a way to pass back the
   * original string without copying it.  I think I might be able to
   * do it by putting some sort of flag in the string record that is
   * used by initCopy().
   */
  pragma "init copy fn"
  inline proc chpl__initCopy(ref s: string_rec) {
    if debugStrings then writeln("in initCopy()");
    var ret: string_rec;
    if !s.isEmptyString() {
      const slen = s.len; // cache the remote copy of len
      if s.home.id == here.id {
        if debugStrings then writeln("  local initCopy");
        ret.base = stringMove(_defaultOf(baseType), s.base, slen);
      } else {
        if debugStrings then writeln("  remote initCopy: ", s.home.id);
        ret.base = remoteStringCopy(s.home.id, s.base, slen);
      }
      ret.len = slen;
      ret.incRefCntNoAlias();
    }
    if debugStrings then writeln("leaving initCopy()");
    return ret;
  }

  //
  // Assignment functions
  //
  proc =(ref lhs: string_rec, rhs: string_rec) {
    if debugStrings then writeln("in proc =()");
    inline proc helpMe(ref lhs: string_rec, rhs: string_rec) {
      if rhs.home.id == here.id {
        if debugStrings then writeln("  rhs local");
        lhs.reinitString(rhs.base, rhs.len);
      } else {
        if debugStrings then writeln("  rhs remote: ", rhs.home.id);
        const len = rhs.len; // cache the remote copy of len
        // TODO: reuse the base from remoteStringCopy()
        const rs = if len!=0 then remoteStringCopy(rhs.home.id, rhs.base, len)
                   else _defaultOf(baseType);
        lhs.reinitString(rs, len);
        if len!=0 then free_baseType(rs);
      }
    }
    if lhs.home.id == here.id then {
      if debugStrings then writeln("  lhs local");
      helpMe(lhs, rhs);
    }
    else {
      if debugStrings then writeln("  lhs remote: ", lhs.home.id);
      on lhs do helpMe(lhs, rhs);
    }
    if debugStrings then writeln("leaving proc =()");
  }

  proc =(ref lhs: string_rec, rhs_c: baseType) {
    if debugStrings then writeln("in proc =() "+baseTypeString);
    const hereId = here.id;
    // Make this some sort of local check once we have local types/vars
    if (rhs_c.locale.id != hereId) || (lhs.home.id != hereId) then
      halt("Cannot assign a remote "+baseTypeString+" to a string.");
    const len = rhs_c.length;
    lhs.reinitString(rhs_c, len);
    if debugStrings then writeln("leaving proc =() "+baseTypeString);
  }

  //
  // Concatenation
  //
  proc +(s0: string_rec, s1: string_rec) {
    if debugStrings then writeln("in proc +()");
    if s0.isEmptyString() then return s1;
    if s1.isEmptyString() then return s0;
    var ret: string_rec;
    const s0len = s0.len;
    const s1len = s1.len;
    ret.len = s0len + s1len;
    const s0remote = s0.home.id != here.id;
    const s0base = if s0remote
                   then remoteStringCopy(s0.home.id, s0.base, s0len)
                   else s0.base;
    const s1remote = s1.home.id != here.id;
    const s1base = if s1remote
                   then remoteStringCopy(s1.home.id, s1.base, s1len)
                   else s1.base;
    ret.base = s0base+s1base;
    ret.incRefCntNoAlias();
    if s0remote then free_baseType(s0base);
    if s1remote then free_baseType(s1base);
    if debugStrings then writeln("leaving proc +()");
    return ret;
  }

  proc +(s: string_rec, cs: baseType) {
    if cs.locale.id != here.id then
      halt("Cannot concatenate a remote "+baseTypeString+".");
    if debugStrings then writeln("in proc +() string+"+baseTypeString);
    if cs == _defaultOf(baseType) then return s;
    if s.isEmptyString() then return cs:string_rec;
    var ret: string_rec;
    const slen = s.len;
    ret.len = slen + cs.length;
    const sremote = s.home.id != here.id;
    const sbase = if sremote
                  then remoteStringCopy(s.home.id, s.base, slen)
                  else s.base;
    ret.base = sbase+cs;
    ret.incRefCntNoAlias();
    if sremote then free_baseType(sbase);
    if debugStrings then writeln("leaving proc +() string+"+baseTypeString);
    return ret;
  }

  proc +(cs: baseType, s: string_rec) {
    if cs.locale.id != here.id then
      halt("Cannot concatenate a remote "+baseTypeString+".");
    if debugStrings then writeln("in proc +() "+baseTypeString+"+string");
    if cs == _defaultOf(baseType) then return s;
    if s.isEmptyString() then return cs:string_rec;
    var ret: string_rec;
    const slen = s.len;
    ret.len = cs.length + slen;
    const sremote = s.home.id != here.id;
    const sbase = if sremote
                  then remoteStringCopy(s.home.id, s.base, slen)
                  else s.base;
    ret.base = cs+sbase;
    ret.incRefCntNoAlias();
    if sremote then free_baseType(sbase);
    if debugStrings then writeln("leaving proc +() "+baseTypeString+"+string");
    return ret;
  }

  //
  // Concatenation with other types is done by casting to the baseType
  //
  inline proc concatHelp(s: string_rec, x:?t) where t != string_rec {
    const cs = x:baseType;
    const ret = s + cs;
    if !_isBooleanType(x.type) && !_isEnumeratedType(x.type) then
      free_baseType(cs);
    return ret;
  }

  inline proc concatHelp(x:?t, s: string_rec) where t != string_rec  {
    const cs = x:baseType;
    const ret = cs + s;
    if !_isBooleanType(x.type) && !_isEnumeratedType(x.type) then
      free_baseType(cs);
    return ret;
  }

  inline proc +(s: string_rec, x: numeric) return concatHelp(s, x);
  inline proc +(x: numeric, s: string_rec) return concatHelp(x, s);
  inline proc +(s: string_rec, x: enumerated) return concatHelp(s, x);
  inline proc +(x: enumerated, s: string_rec) return concatHelp(x, s);
  inline proc +(s: string_rec, x: bool) return concatHelp(s, x);
  inline proc +(x: bool, s: string_rec) return concatHelp(x, s);

  //
  // Relational operators
  //
  // Poor man's substitute for first class function support
  enum relType {eq=1, neq, lt, lte, gt, gte};
  inline proc relationalHelp(a: string_rec, b: string_rec,
                             param op: relType) {
    inline proc doOp(a: baseType, b: baseType, param op: relType) {
      select op {
        when relType.eq do return a==b;
        when relType.neq do return a!=b;
        when relType.lt do return a<b;
        when relType.lte do return a<=b;
        when relType.gt do return a>b;
        when relType.gte do return a>=b;
        otherwise compilerError("op not supported (", op:baseType, ")");
      }
    }
    var ret: bool;
    if a.home.id == b.home.id {
      on a.home do ret = doOp(a.base, b.base, op);
    } else {
      // Copy the a if it is remote and not the empty string
      var abase: baseType;
      const alen = a.len;
      const aIsEmpty = a.isEmptyString();
      const aremote = (a.home.id!=here.id) && !aIsEmpty;
      if aremote then abase = remoteStringCopy(a.home.id, a.base, alen);
      else if !aIsEmpty then abase = a.base;
      else abase = _defaultOf(baseType);
      // Ditto for b
      var bbase: baseType;
      const blen = b.len;
      const bIsEmpty = b.isEmptyString();
      const bremote = (b.home.id!=here.id) && !bIsEmpty;
      if bremote then bbase = remoteStringCopy(b.home.id, b.base, blen);
      else if !bIsEmpty then bbase = b.base;
      else bbase = _defaultOf(baseType);

      ret = doOp(abase, bbase, op);

      if aremote then free_baseType(abase);
      if bremote then free_baseType(bbase);
    }
    return ret;
  }

  inline proc ==(a: string_rec, b: string_rec)
    return relationalHelp(a, b, relType.eq);
  inline proc !=(a: string_rec, b: string_rec)
    return relationalHelp(a, b, relType.neq);
  inline proc <(a: string_rec, b: string_rec)
    return relationalHelp(a, b, relType.lt);
  inline proc <=(a: string_rec, b: string_rec)
    return relationalHelp(a, b, relType.lte);
  inline proc >(a: string_rec, b: string_rec)
    return relationalHelp(a, b, relType.gt);
  inline proc >=(a: string_rec, b: string_rec)
    return relationalHelp(a, b, relType.gte);

  //
  // Primitive string functions and methods
  //
  inline proc ascii(s: string_rec) {
    var ret: int(32); // return type of ascii primitive
    on s.home do ret = ascii(s.base);
    return ret;
  }

  //
  // Casts
  //

  // Cast from baseType to string
  inline proc _cast(type t, cs: baseType) where t==string_rec {
    if debugStrings then writeln("in _cast() "+baseTypeString+"-string");
    if cs.locale.id != here.id then
      halt("Cannot cast a remote "+baseTypeString+" to string.");
    var ret: string_rec;
    ret.len = cs.length;
    if ret.len != 0 then ret.base = __primitive("string_copy", cs);
    ret.incRefCntNoAlias();
    if debugStrings then writeln("leaving _cast() "+baseTypeString+"-string");
    return ret;
  }

  // Other casts to strings use the baseType
  inline proc _cast(type t, x) where t==string_rec && x.type != baseType {
    if debugStrings then writeln("in _cast() ", typeToString(t), "-string");
    const cs = x:baseType;
    var ret: string_rec;
    ret.len = cs.length;
    assert(ret.len != 0);
    if _isBooleanType(x.type) || _isEnumeratedType(x.type) then
      // These are returned as string literals so copy the string
      ret.base = stringMove(_defaultOf(baseType), cs, ret.len);
    else
      ret.base = cs;
    ret.incRefCntNoAlias();
    if debugStrings then writeln("leaving _cast() ", typeToString(t),
                                 "-string");
    return ret;
  }

  // Cast from string to the baseType
  // This currently does not allocate a new baseType string
  // It is exactly the same as string.c_str()
  inline proc _cast(type t, s: string_rec) where t==baseType {
    if debugStrings then writeln("in _cast() string-c_"+baseTypeString);
    if s.home.id != here.id then
      halt("Cannot cast a remote string to "+baseTypeString);
    if debugStrings then writeln("leaving _cast() string-"+baseTypeString);
    return s.base;
  }

  // Other casts from strings use the baseType
  inline proc _cast(type t, s: string_rec) where t!=baseType {
    if debugStrings then writeln("in _cast() string-", typeToString(t));
    const sremote = s.home.id != here.id;
    const sbase = if sremote
                  then remoteStringCopy(s.home.id, s.base, s.len)
                  else s.base;
    // Note that any error checking on the string is done in the runtime
    const ret = sbase:t;
    if sremote then free_baseType(sbase);
    if debugStrings then writeln("leaving _cast() string-", typeToString(t));
    return ret;
  }

  // Functions that are no longer supported:
  // - toString()
  // - _string_contains()

}


Snapshot of converting Random.chpl over to chpldocs

I ran into a bug due to accidentally using a trailing underscore,
but strangely the error message points to line 53 of the rst:

   It is the intent that once Chapel supports a notion of 'private'

rather than the one where the error is presumably occurring (a few
lines later):

   'RandomStreamPrivate_' will be made private to the RandomStream

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

/*
   This module defines an abstraction for a stream of pseudorandom
   numbers, :chpl:class:RandomStream:.  The current implementation is
   based on the one used in the NAS Parallel Benchmarks (NPB,
   available at: http://www.nas.nasa.gov/publications/npb.html).  The
   longer-term intention is to add knobs permitting users to select
   other pseudorandom number generation algorithms, such as the
   Mersenne twister.

   Summarizing the comments from the reference implementation:

     This generator returns uniform pseudorandom real values in the
     range (0, 1) by using the linear congruential generator

       x_{k+1} = a x_k  (mod 2**46)

     where 0 < x_k < 2**46 and 0 < a < 2**46.  This scheme generates
     2**44 numbers before repeating.  The seed value must be an odd
     64-bit integer in the range (1, 2^46).  The generated values are
     normalized to be between 0 and 1, i.e., 2**(-46) * x_k.

     This generator should produce the same results on any computer
     with at least 48 mantissa bits for real(64) data.

   Here is a list of currently open issues (TODOs) for this module:

   1. We would like to support general serial and parallel iterators
   on the RandomStream class, but this is not possible with our
   current parallel iterator framework.

   2. The random number generation functionality in this module is
   currently restricted to 64-bit real, 64-bit imag, and 128-bit
   complex values.  This should be extended to other primitive types
   for which this would make sense.  Coercions are insufficient.

   3. Can the multiplier 'arand' be moved into the RandomStream class
   so that it can be changed by a user of this class?

   4. By default, the random stream seed is initialized based on the
   current time in microseconds, allowing for some degree of
   randomness.  The intent of the SeedGenerator enumerated type is to
   provide a menu of options for initializing the random stream seed,
   but only one option is implemented to date.

   Note to developers on "private":

   It is the intent that once Chapel supports a notion of 'private'
   symbols, everything prefixed with RandomPrivate will be made
   private to this module and everything prefixed with
   'RandomStreamPrivate_' will be made private to the RandomStream
   class.
*/
module Random {

proc fillRandom(x:[], seed: int(64)) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(seed, parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

proc fillRandom(x:[]) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

record SeedGenerators {
  proc currentTime {
    use Time;
    const seed: int(64) = getCurrentTime(unit=TimeUnits.microseconds):int(64);
    return (if seed % 2 == 0 then seed + 1 else seed) % (1:int(64) << 46);
  }
};

const SeedGenerator: SeedGenerators;

class RandomStream {
  param parSafe: bool = true;
  const seed: int(64);

  proc RandomStream(seed: int(64) = SeedGenerator.currentTime,
                   param parSafe: bool = true) {
    if seed % 2 == 0 || seed < 1 || seed > 1:int(64)<<46 then
      halt("RandomStream seed must be an odd integer between 0 and 2**46");
    RandomStreamPrivate_init(seed);
  }

  proc getNext(param parSafe = this.parSafe) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count += 1;
    const result = RandomPrivate_randlc(RandomStreamPrivate_cursor);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc skipToNth(n: integral, param parSafe = this.parSafe) {
    if n <= 0 then
      halt("RandomStream.skipToNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = RandomPrivate_randlc_skipto(seed, n);
    if parSafe then
      RandomStreamPrivate_lock$;
  }

  proc getNth(n: integral, param parSafe = this.parSafe) {
    if (n <= 0) then
      halt("RandomStream.getNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    skipToNth(n, parSafe=false);
    const result = getNext(parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc fillRandom(X: [], param parSafe = this.parSafe) {
    if X.eltType != complex && X.eltType != real && X.eltType != imag then
      compilerError("RandomStream.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
    forall (x, r) in zip(X, iterate(X.domain, X.eltType, parSafe)) do
      x = r;
  }

  proc iterate(D: domain, type resultType=real, param parSafe = this.parSafe) {
    if resultType != complex && resultType != real && resultType != imag then
      compilerError("RandomStream.iterate is only defined for real(64), imag(64), and complex(128) result types");
    param cplxMultiplier = if resultType == complex then 2 else 1;
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const start = RandomStreamPrivate_count;
    // NOTE: Not bothering to check to see if D.numIndices can fit into int(64)
    RandomStreamPrivate_count += cplxMultiplier * D.numIndices:int(64);
    skipToNth(RandomStreamPrivate_count, parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  proc writeThis(f: Writer) {
    f <~> "RandomStream(parSafe=";
    f <~> parSafe;
    f <~> ", seed = ";
    f <~> seed;
    f <~> ")";
  }

  ///////////////////////////////////////////////////////////// CLASS PRIVATE //
  //
  // It is the intent that once Chapel supports the notion of
  // 'private', everything in this class declared below this line will
  // be made private to this class.
  //

  var RandomStreamPrivate_lock$: sync bool;
  var RandomStreamPrivate_cursor: real;
  var RandomStreamPrivate_count: int(64);

  proc RandomStreamPrivate_init(seed: int(64)) {
    this.seed = seed;
    RandomStreamPrivate_cursor = seed;
    RandomStreamPrivate_count = 1;
  }
}

////////////////////////////////////////////////////////////// MODULE PRIVATE //
//
// It is the intent that once Chapel supports the notion of 'private',
// everything declared below this line will be made private to this
// module.
//

//
// NPB-defined constants for linear congruential generator
//
const RandomPrivate_r23   = 0.5**23,
      RandomPrivate_t23   = 2.0**23,
      RandomPrivate_r46   = 0.5**46,
      RandomPrivate_t46   = 2.0**46,
      RandomPrivate_arand = 1220703125.0;

//
// NPB-defined randlc routine
//
proc RandomPrivate_randlc(inout x: real, a: real = RandomPrivate_arand) {
  var t1 = RandomPrivate_r23 * a;
  const a1 = floor(t1),
    a2 = a - RandomPrivate_t23 * a1;
  t1 = RandomPrivate_r23 * x;
  const x1 = floor(t1),
    x2 = x - RandomPrivate_t23 * x1;
  t1 = a1 * x2 + a2 * x1;
  const t2 = floor(RandomPrivate_r23 * t1),
    z  = t1 - RandomPrivate_t23 * t2,
    t3 = RandomPrivate_t23 * z + a2 * x2,
    t4 = floor(RandomPrivate_r46 * t3),
    x3 = t3 - RandomPrivate_t46 * t4;
  x = x3;
  return RandomPrivate_r46 * x3;
}

// Wrapper that takes a result type (two calls for complex types)
proc RandomPrivate_randlc(type resultType, inout x: real) {
  if resultType == complex then
    return (RandomPrivate_randlc(x), RandomPrivate_randlc(x)):complex;
  else
    return RandomPrivate_randlc(x):resultType;
}

//
// Return a value for the cursor so that the next call to randlc will
// return the same value as the nth call to randlc
//
proc RandomPrivate_randlc_skipto(seed: int(64), in n: integral): real {
  var cursor = seed:real;
  n -= 1;
  var t = RandomPrivate_arand;
  RandomPrivate_arand;
  while (n != 0) {
    const i = n / 2;
    if (2 * i != n) then
      RandomPrivate_randlc(cursor, t);
    if i == 0 then
      break;
    else
      n = i;
    RandomPrivate_randlc(t, t);
    n = i;
  }
  return cursor;
}

//
// iterate over outer ranges in tuple of ranges
//
iter RandomPrivate_outer(ranges, param dim: int = 1) {
  if dim + 1 == ranges.size {
    for i in ranges(dim) do
      yield (i,);
  } else if dim + 1 < ranges.size {
    for i in ranges(dim) do
      for j in RandomPrivate_outer(ranges, dim+1) do
        yield (i, (...j));
  } else {
    yield 0; // 1D case is a noop
  }
}

//
// RandomStream iterator implementation
//
iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64)) {
  var cursor = RandomPrivate_randlc_skipto(seed, start);
  for i in D do
    yield RandomPrivate_randlc(resultType, cursor);
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind)
      where tag == iterKind.leader {
  for block in D._value.these(tag=iterKind.leader) do
    yield block;
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind, followThis)
      where tag == iterKind.follower {
  param multiplier = if resultType == complex then 2 else 1;
  const ZD = computeZeroBasedDomain(D);
  const innerRange = followThis(ZD.rank);
  var cursor: real;
  for outer in RandomPrivate_outer(followThis) {
    var myStart = start;
    // NOTE: Not bothering to check to see if this can fit into int(64)
    if ZD.rank > 1 then
      myStart += multiplier * ZD.indexOrder(((...outer), innerRange.low)):int(64);
    else
      myStart += multiplier * ZD.indexOrder(innerRange.low):int(64);
    if !innerRange.stridable {
      cursor = RandomPrivate_randlc_skipto(seed, myStart);
      for i in innerRange do
        yield RandomPrivate_randlc(resultType, cursor);
    } else {
      // NOTE: Not bothering to check to see if this can fit into int(64)
      myStart -= innerRange.low:int(64);
      for i in innerRange {
        cursor = RandomPrivate_randlc_skipto(seed, myStart + i:int(64) * multiplier);
        yield RandomPrivate_randlc(resultType, cursor);
      }
    }
  }
}

} // close module Random

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

//
// Random Module
//
// This standard module contains a random number generator based on
// the one used in the NPB benchmarks.  Tailoring the NPB comments to
// this code, we can say the following:
//
//   This generator returns uniform pseudorandom real values in the
//   range (0, 1) by using the linear congruential generator
//
//     x_{k+1} = a x_k  (mod 2**46)
//
//   where 0 < x_k < 2**46 and 0 < a < 2**46.  This scheme generates
//   2**44 numbers before repeating.  The seed value must be an odd
//   64-bit integer in the range (1, 2^46).  The generated values are
//   normalized to be between 0 and 1, i.e., 2**(-46) * x_k.
//
//   This generator should produce the same results on any computer
//   with at least 48 mantissa bits for real(64) data.
//
// Open Issues
//
// 1. We would like to support general serial and parallel iterators
// on the RandomStream class, but this is not possible with our
// current parallel iterator framework.
//
// 2. The random number generation functionality in this module is
// currently restricted to 64-bit real, 64-bit imag, and 128-bit
// complex values.  This should be extended to other primitive types
// for which this would make sense.  Coercions are insufficient.
//
// 3. Can the multiplier 'arand' be moved into the RandomStream class
// so that it can be changed by a user of this class.
//
// 4. By default, the random stream seed is initialized based on the
// current time in microseconds, allowing for some degree of
// randomness.  The intent of the SeedGenerator enumerated type is to
// provide a menu of options for initializing the random stream seed,
// but only one option is implemented to date.
//
// Note on Private
//
// It is the intent that once Chapel supports the notion of 'private',
// everything prefixed with RandomPrivate_ will be made private to
// this module and everything prefixed with RandomStreamPrivate_ will
// be made private to the RandomStream class.
//

proc fillRandom(x:[], seed: int(64)) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(seed, parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

proc fillRandom(x:[]) {
  if x.eltType != complex && x.eltType != real && x.eltType != imag then
    compilerError("Random.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
  var randNums = new RandomStream(parSafe=false);
  randNums.fillRandom(x);
  delete randNums;
}

record SeedGenerators {
  proc currentTime {
    use Time;
    const seed: int(64) = getCurrentTime(unit=TimeUnits.microseconds):int(64);
    return (if seed % 2 == 0 then seed + 1 else seed) % (1:int(64) << 46);
  }
};

const SeedGenerator: SeedGenerators;

class RandomStream {
  param parSafe: bool = true;
  const seed: int(64);

  proc RandomStream(seed: int(64) = SeedGenerator.currentTime,
                   param parSafe: bool = true) {
    if seed % 2 == 0 || seed < 1 || seed > 1:int(64)<<46 then
      halt("RandomStream seed must be an odd integer between 0 and 2**46");
    RandomStreamPrivate_init(seed);
  }

  proc getNext(param parSafe = this.parSafe) {
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count += 1;
    const result = RandomPrivate_randlc(RandomStreamPrivate_cursor);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc skipToNth(n: integral, param parSafe = this.parSafe) {
    if n <= 0 then
      halt("RandomStream.skipToNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    RandomStreamPrivate_count = n;
    RandomStreamPrivate_cursor = RandomPrivate_randlc_skipto(seed, n);
    if parSafe then
      RandomStreamPrivate_lock$;
  }

  proc getNth(n: integral, param parSafe = this.parSafe) {
    if (n <= 0) then
      halt("RandomStream.getNth(n) called with non-positive 'n' value", n);
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    skipToNth(n, parSafe=false);
    const result = getNext(parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return result;
  }

  proc fillRandom(X: [], param parSafe = this.parSafe) {
    if X.eltType != complex && X.eltType != real && X.eltType != imag then
      compilerError("RandomStream.fillRandom is only defined for real(64), imag(64), and complex(128) arrays");
    forall (x, r) in zip(X, iterate(X.domain, X.eltType, parSafe)) do
      x = r;
  }

  proc iterate(D: domain, type resultType=real, param parSafe = this.parSafe) {
    if resultType != complex && resultType != real && resultType != imag then
      compilerError("RandomStream.iterate is only defined for real(64), imag(64), and complex(128) result types");
    param cplxMultiplier = if resultType == complex then 2 else 1;
    if parSafe then
      RandomStreamPrivate_lock$ = true;
    const start = RandomStreamPrivate_count;
    // NOTE: Not bothering to check to see if D.numIndices can fit into int(64)
    RandomStreamPrivate_count += cplxMultiplier * D.numIndices:int(64);
    skipToNth(RandomStreamPrivate_count, parSafe=false);
    if parSafe then
      RandomStreamPrivate_lock$;
    return RandomPrivate_iterate(resultType, D, seed, start);
  }

  proc writeThis(f: Writer) {
    f <~> "RandomStream(parSafe=";
    f <~> parSafe;
    f <~> ", seed = ";
    f <~> seed;
    f <~> ")";
  }

  ///////////////////////////////////////////////////////////// CLASS PRIVATE //
  //
  // It is the intent that once Chapel supports the notion of
  // 'private', everything in this class declared below this line will
  // be made private to this class.
  //

  var RandomStreamPrivate_lock$: sync bool;
  var RandomStreamPrivate_cursor: real;
  var RandomStreamPrivate_count: int(64);

  proc RandomStreamPrivate_init(seed: int(64)) {
    this.seed = seed;
    RandomStreamPrivate_cursor = seed;
    RandomStreamPrivate_count = 1;
  }
}

////////////////////////////////////////////////////////////// MODULE PRIVATE //
//
// It is the intent that once Chapel supports the notion of 'private',
// everything declared below this line will be made private to this
// module.
//

//
// NPB-defined constants for linear congruential generator
//
const RandomPrivate_r23   = 0.5**23,
      RandomPrivate_t23   = 2.0**23,
      RandomPrivate_r46   = 0.5**46,
      RandomPrivate_t46   = 2.0**46,
      RandomPrivate_arand = 1220703125.0;

//
// NPB-defined randlc routine
//
proc RandomPrivate_randlc(inout x: real, a: real = RandomPrivate_arand) {
  var t1 = RandomPrivate_r23 * a;
  const a1 = floor(t1),
    a2 = a - RandomPrivate_t23 * a1;
  t1 = RandomPrivate_r23 * x;
  const x1 = floor(t1),
    x2 = x - RandomPrivate_t23 * x1;
  t1 = a1 * x2 + a2 * x1;
  const t2 = floor(RandomPrivate_r23 * t1),
    z  = t1 - RandomPrivate_t23 * t2,
    t3 = RandomPrivate_t23 * z + a2 * x2,
    t4 = floor(RandomPrivate_r46 * t3),
    x3 = t3 - RandomPrivate_t46 * t4;
  x = x3;
  return RandomPrivate_r46 * x3;
}

// Wrapper that takes a result type (two calls for complex types)
proc RandomPrivate_randlc(type resultType, inout x: real) {
  if resultType == complex then
    return (RandomPrivate_randlc(x), RandomPrivate_randlc(x)):complex;
  else
    return RandomPrivate_randlc(x):resultType;
}

//
// Return a value for the cursor so that the next call to randlc will
// return the same value as the nth call to randlc
//
proc RandomPrivate_randlc_skipto(seed: int(64), in n: integral): real {
  var cursor = seed:real;
  n -= 1;
  var t = RandomPrivate_arand;
  RandomPrivate_arand;
  while (n != 0) {
    const i = n / 2;
    if (2 * i != n) then
      RandomPrivate_randlc(cursor, t);
    if i == 0 then
      break;
    else
      n = i;
    RandomPrivate_randlc(t, t);
    n = i;
  }
  return cursor;
}

//
// iterate over outer ranges in tuple of ranges
//
iter RandomPrivate_outer(ranges, param dim: int = 1) {
  if dim + 1 == ranges.size {
    for i in ranges(dim) do
      yield (i,);
  } else if dim + 1 < ranges.size {
    for i in ranges(dim) do
      for j in RandomPrivate_outer(ranges, dim+1) do
        yield (i, (...j));
  } else {
    yield 0; // 1D case is a noop
  }
}

//
// RandomStream iterator implementation
//
iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64)) {
  var cursor = RandomPrivate_randlc_skipto(seed, start);
  for i in D do
    yield RandomPrivate_randlc(resultType, cursor);
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind)
      where tag == iterKind.leader {
  for block in D._value.these(tag=iterKind.leader) do
    yield block;
}

iter RandomPrivate_iterate(type resultType, D: domain, seed: int(64),
                          start: int(64), param tag: iterKind, followThis)
      where tag == iterKind.follower {
  param multiplier = if resultType == complex then 2 else 1;
  const ZD = computeZeroBasedDomain(D);
  const innerRange = followThis(ZD.rank);
  var cursor: real;
  for outer in RandomPrivate_outer(followThis) {
    var myStart = start;
    // NOTE: Not bothering to check to see if this can fit into int(64)
    if ZD.rank > 1 then
      myStart += multiplier * ZD.indexOrder(((...outer), innerRange.low)):int(64);
    else
      myStart += multiplier * ZD.indexOrder(innerRange.low):int(64);
    if !innerRange.stridable {
      cursor = RandomPrivate_randlc_skipto(seed, myStart);
      for i in innerRange do
        yield RandomPrivate_randlc(resultType, cursor);
    } else {
      // NOTE: Not bothering to check to see if this can fit into int(64)
      myStart -= innerRange.low:int(64);
      for i in innerRange {
        cursor = RandomPrivate_randlc_skipto(seed, myStart + i:int(64) * multiplier);
        yield RandomPrivate_randlc(resultType, cursor);
      }
    }
  }
}


Add chpldoc comments to two functions in the Search module

These comments are mostly copied from the spec.

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

/*
   The `Search` module is designed to support standard search routines. The
   current interface is minimal and should be expected to grow and evolve
   over time.
 */

/*
   Searches through the pre-sorted array Data looking for the value val using
   a sequential linear search.  Returns a tuple indicating (1) whether or not
   the value was found and (2) the location of the value if it was found, or
   the location where the value should have been if it was not found.

   :arg Data: The sorted array to search
   :arg val: The value to find in the array
   :returns: A tuple indicating (1) if the value was found and (2) the location of the value if it was found or the location where the value should have been if it was not found.

 */

proc LinearSearch(Data:[?Dom], val) {
  for i in Dom {
    if (Data(i) == val) {
      return (true, i);
    } else if (Data(i) > val) {
      return (false, i);
    }
  }
  return (false,Dom.high+1);
}

// would really like to drop the lo/hi arguments here, but right now
// that causes too big of a memory leak
/*
   Searches through the pre-sorted array `Data` looking for the value `val`
   using a sequential binary search.  If provided, only the indices `lo`
   through `hi` will be considered, otherwise the whole array will be
   searched. Returns a tuple indicating (1) whether or not the value was
   found and (2) the location of the value if it was found, or the location
   where the value should have been if it was not found.

   :arg Data: The sorted array to search
   :arg val: The value to find in the array
   :arg lo: The lowest index to consider while searching
   :type:lo: integral
   :arg hi: The highest index to consider while searching
   :type hi: integral
   :returns: A tuple indicating (1) if the value was found and (2) the location of the value if it was found or the location where the value should have been if it was not found.

 */
proc BinarySearch(Data:[?Dom], val, in lo = Dom.low, in hi = Dom.high) {
  while (lo <= hi) {
    const mid = (hi - lo)/2 + lo;
    if (Data(mid) == val) {
      return (true, mid);
    } else if (val > Data(mid)) {
      lo = mid+1;
    } else {
      hi = mid-1;
    }
  }
  return (false, lo);
}

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

proc LinearSearch(Data:[?Dom], val) {
  for i in Dom {
    if (Data(i) == val) {
      return (true, i);
    } else if (Data(i) > val) {
      return (false, i);
    }
  }
  return (false,Dom.high+1);
}

// would really like to drop the lo/hi arguments here, but right now
// that causes too big of a memory leak
proc BinarySearch(Data:[?Dom], val, in lo = Dom.low, in hi = Dom.high) {
  while (lo <= hi) {
    const mid = (hi - lo)/2 + lo;
    if (Data(mid) == val) {
      return (true, mid);
    } else if (val > Data(mid)) {
      lo = mid+1;
    } else {
      hi = mid-1;
    }
  }
  return (false, lo);
}


Removed meta-comments from slices.chpl since this is an example intended for users

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

/*  This example program demonstrates the use of array slicing and
    reindexing.

    In this program, diagonal blocks (or slices) of A are initialized
    according to the row indices of the diagonal block.
       A(i,j) = i, for all (i,j) in diagonal blocks
    If the block has been reindexed, then each diagonal block is
    initialized with the same values.  If the block has not been
    reindexed, then the block has the same indices as the full array,
    so each diagonal block is initialized with different values.

    (Incomplete diagonal blocks are not initialized.  If blk does not
    divide into n evenly, the last diagonal block of A will be
    uninitialized.)

    Three different cases are given, to show different ways of slicing
    arrays, with and without reindexing.  Array aliases are declared
    for the diagonal blocks in two of the three cases.  One of these
    array aliases uses reindexing of the domain of the array alias.
    The initialization of the blocks is handled in either initBlock
    or initBlock2.  The function initBlock does not reindex the domain
    of the input array while initBlock2 does.
*/

// Configuration variables for n and blk where the array A is nxn and
// blk is the size of the diagonal subblock.
config const n = 10,
             blk = 2;

def main() {

  // Variable declarations for the range vec, and for the domain D
  // and array A.  Vec is used to define the ranges of each dimension
  // of D and it is later used in the iterator for the for loop statements
  // which call the init function for each diagonal block of A.
  const vec = 1..n;
  var D = [vec,vec];
  var A:[D] int;

  // D0 is a domain which is used to reindex the diagonal blocks of A.
  var D0 = [1..blk, 1..blk];

  // Each of the following three for loop statements uses the
  // iterator blockIter to yield a range subvec that is then
  // used to define (subvec x subvec) blocks along the diagonal
  // of A.

  //  Case 1:  No reindexing of the diagonal blocks.
  //  The array slice is sent directly to initBlock.
  writeln("Initializing the diagonal blocks of A.");
  writeln("No reindexing of diagonal blocks.");
  for subvec in blockIter(vec,blk) {
    initBlock(A[subvec,subvec]);
  }

  writeln(A);
  writeln();

  // A is reset to zero between each case.  (This step is not
  // necessary, but done to show that each case starts with a zero array.)
  A = 0;

  // Case 2:  An array alias with reindexing is used to point to each
  // diagonal block, and that array alias is sent to initBlock.
  writeln("Initializing the diagonal blocks of A.");
  writeln("Reindexing of each diagonal block in alias declaration. ");
  for subvec in blockIter(vec,blk) {
    var Ablock: [D0] => A[subvec,subvec];
    initBlock(Ablock);
  }

  writeln(A);
  writeln();

  A = 0;

  // Case 3:  An array alias without reindexing is used to point
  // to each diagonal block.  This alias is then sent to initBlock2
  // where the domain of the array argument is reindexed.
  writeln("Initializing the diagonal blocks of A.");
  writeln("Reindexing of array argument in init function definition. ");
  for subvec in blockIter(vec,blk) {
    var Ablock => A[subvec,subvec];
    initBlock2(Ablock);
  }

  writeln(A);
  writeln();
}

// The iterator blockIter yields a range that defines a subset
// of vec, which is always of length blk.
def blockIter(vec:range,blk) {
  for i in vec by blk {
    if (i + blk-1 <= vec.high) then
      yield i..i+blk-1;
  }
}

// This function sets each element of A to be the value of its
// row index.
def initBlock(A) {
  for (i,j) in A.domain {
    A(i,j) = i;
  }
}

// This function reindexes the domain of the array argument to
// be of [1..blk,1..blk].  It expects arrays to be blk x blk.
// This function sets each element of A to be the value of its
// row index.
def initBlock2(A: [1..blk,1..blk]) {
  for (i,j) in A.domain {
    A(i,j) = i;
  }
}

/*  This example program demonstrates the use of array slicing and
    reindexing.

    In this program, diagonal blocks (or slices) of A are initialized
    according to the row indices of the diagonal block.
       A(i,j) = i, for all (i,j) in diagonal blocks
    If the block has been reindexed, then each diagonal block is
    initialized with the same values.  If the block has not been
    reindexed, then the block has the same indices as the full array,
    so each diagonal block is initialized with different values.

    (Incomplete diagonal blocks are not initialized.  If blk does not
    divide into n evenly, the last diagonal block of A will be
    uninitialized.)

    Three different cases are given, to show different ways of slicing
    arrays, with and without reindexing.  Array aliases are declared
    for the diagonal blocks in two of the three cases.  One of these
    array aliases uses reindexing of the domain of the array alias.
    The initialization of the blocks is handled in either initBlock
    or initBlock2.  The function initBlock does not reindex the domain
    of the input array while initBlock2 does.
*/

// Configuration variables for n and blk where the array A is nxn and
// blk is the size of the diagonal subblock.
config const n = 10,
             blk = 2;

def main() {

  // Variable declarations for the range vec, and for the domain D
  // and array A.  Vec is used to define the ranges of each dimension
  // of D and it is later used in the iterator for the for loop statements
  // which call the init function for each diagonal block of A.
  const vec = 1..n;
  var D = [vec,vec];
  var A:[D] int;

  // D0 is a domain which is used to reindex the diagonal blocks of A.
  var D0 = [1..blk, 1..blk];

  // Each of the following three for loop statements uses the
  // iterator blockIter to yield a range subvec that is then
  // used to define (subvec x subvec) blocks along the diagonal
  // of A.

  //  Case 1:  No reindexing of the diagonal blocks.
  //  The array slice is sent directly to initBlock.
  writeln("Initializing the diagonal blocks of A.");
  writeln("No reindexing of diagonal blocks.");
  for subvec in blockIter(vec,blk) { // sjd: changed forall to for
    initBlock(A[subvec,subvec]);
  }

  writeln(A);
  writeln();

  // A is reset to zero between each case.  (This step is not
  // necessary, but done to show that each case starts with a zero array.)
  A = 0;

  // Case 2:  An array alias with reindexing is used to point to each
  // diagonal block, and that array alias is sent to initBlock.
  writeln("Initializing the diagonal blocks of A.");
  writeln("Reindexing of each diagonal block in alias declaration. ");
  for subvec in blockIter(vec,blk) { // sjd: changed forall to for
    var Ablock: [D0] => A[subvec,subvec];
    initBlock(Ablock);
  }

  writeln(A);
  writeln();

  A = 0;

  // Case 3:  An array alias without reindexing is used to point
  // to each diagonal block.  This alias is then sent to initBlock2
  // where the domain of the array argument is reindexed.
  writeln("Initializing the diagonal blocks of A.");
  writeln("Reindexing of array argument in init function definition. ");
  for subvec in blockIter(vec,blk) { // sjd: changed forall to for
    var Ablock => A[subvec,subvec];
    initBlock2(Ablock);
  }

  writeln(A);
  writeln();
}

// The iterator blockIter yields a range that defines a subset
// of vec, which is always of length blk.
def blockIter(vec:range,blk) {
  for i in vec by blk {
    if (i + blk-1 <= vec.high) then
      yield i..i+blk-1;
  }
}

// This function sets each element of A to be the value of its
// row index.
def initBlock(A) {
  for (i,j) in A.domain {
    A(i,j) = i;
  }
}

// This function reindexes the domain of the array argument to
// be of [1..blk,1..blk].  It expects arrays to be blk x blk.
// This function sets each element of A to be the value of its
// row index.
def initBlock2(A: [1..blk,1..blk]) {
  for (i,j) in A.domain {
    A(i,j) = i;
  }
}


Fix string literal formatting

Fix string literal formatting

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

/* Functions for producing program help and usage.

   Chapel programs can declare main to take arguments like this:

   .. code-block:: chapel

     proc main(args: [] string) {
       for a in args {
         // process arguments
         writeln("Got argument ", a);
       }
     }

   Programs that use this feature might need to expand upon the usage message
   that explains which config variables are available. To do so, this module
   includes the :proc:`printUsage` function.
 */
module Help {

  /*
     Print out a usage message for config variables. This function does not
     exit. A typical use of this function would be to call it when ``"--help"``
     or ``"-h"`` are encountered when doing custom argument processing. In
     addition to calling this function, programs with custom argument
     processing should probably also print out a description of the arguments
     that they take.  Once both help messages have been output, the program
     would normally exit early - for example, by returning from main.

     A typical example might be this program, that accumulates a list of
     filenames in addition to handling config variable arguments:

     .. code-block:: chapel

       use Help;

       config const x = 5;

       proc main(args: [] string) {
         var filenames: [1..0] string;

         var programName = args[0];

         for a in args[1..] {
           if a == "-h" || a == "--help" {
             writeln("Usage: ", programName, " <options> filename [filenames]");
             printUsage();
             exit(1); // returning 1 from main is also an option
           } else {
             filenames.push_back(a);
           }
         }

         writef("Got filenames = %ht\n", filenames);
         writef("Got configuration variable x = %ht\n", x);
       }
   */
  proc printUsage() {
    extern proc printHelpTable();
    extern proc printConfigVarTable();

    printHelpTable();
    printConfigVarTable();
  }
}

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

/* Functions for producing program help and usage.

   Chapel programs can declare main to take arguments like this:

   .. code-block:: chapel

     proc main(args: [] string) {
       for a in args {
         // process arguments
         writeln("Got argument ", a);
       }
     }

   Programs that use this feature might need to expand upon the usage message
   that explains which config variables are available. To do so, this module
   includes the :proc:`printUsage` function.
 */
module Help {

  /* Print out a usage message for config variables. This function does not
     exit. A typical use of this function would be to call it when "--help" or
     "-h" are encountered when doing custom argument processing. In addition
     to calling this function, programs with custom argument processing should
     probably also print out a description of the arguments that they take.
     Once both help messages have been output, the program would normally
     exit early - for example, by returning from main.

     A typical example might be this program, that accumulates a list of
     filenames in addition to handling config variable arguments:

     .. code-block:: chapel

       use Help;

       config const x = 5;

       proc main(args: [] string) {
         var filenames: [1..0] string;

         var programName = args[0];

         for a in args[1..] {
           if a == "-h" || a == "--help" {
             writeln("Usage: ", programName, " <options> filename [filenames]");
             printUsage();
             exit(1); // returning 1 from main is also an option
           } else {
             filenames.push_back(a);
           }
         }

         writef("Got filenames = %ht\n", filenames);
         writef("Got configuration variable x = %ht\n", x);
       }
   */
  proc printUsage() {
    extern proc printHelpTable();
    extern proc printConfigVarTable();

    printHelpTable();
    printConfigVarTable();
  }
}


Adding a 64-bit case to an existing test.
Currently it crashes the same way as the 32-bit case.

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

proc test1(x: uint(?xx), y: uint(?yy)) {
  return xx;
}
writeln(test1(1:uint(32), 2:uint(32)));
writeln(test1(1:uint(64), 2:uint(64)));

proc test2(x: uint(?xx), y: uint(?yy)) {
  return yy;
}
writeln(test2(1:uint(32), 2:uint(32)));
writeln(test2(1:uint(64), 2:uint(64)));

proc test1(x: uint(?xx), y: uint(?yy)) {
  return xx;
}
writeln(test1(1:uint(32), 2:uint(32)));

proc test2(x: uint(?xx), y: uint(?yy)) {
  return yy;
}
writeln(test2(1:uint(32), 2:uint(32)));


Remove "no doc" pragma from now private symbols in HDFS module

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

/*

Support for Hadoop Distributed Filesystem

This module implements support for the
`Hadoop <http://hadoop.apache.org/>`_
`Distributed Filesystem <http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html>`_ (HDFS).

Dependencies
------------

The HDFS functionality in Chapel is dependent upon both Hadoop and Java being
installed.  Your ``HADOOP_INSTALL``, ``JAVA_INSTALL`` and ``CLASSPATH``
environment variables must be set as described below in
:ref:`setting-up-hadoop`.  Without this it will not compile with HDFS, even if
the flags are set. As well, the HDFS functionality is also dependent upon the
``CHPL_AUXIO_INCLUDE`` and ``CHPL_AUXIO_LIBS`` environment variables being set
properly. For more information on how to set these properly, see
doc/technotes/README.auxIO in a Chapel release.

Enabling HDFS Support
---------------------

Once you have ensured that Hadoop and Java are installed and have the
five variables above, defined, set the environment variable
CHPL_AUX_FILESYS to 'hdfs' to enable HDFS support:

.. code-block:: sh

  export CHPL_AUX_FILESYS=hdfs

Then, rebuild Chapel by executing 'make' from $CHPL_HOME.

.. code-block:: sh

  make

.. note::

  If HDFS support is not enabled (which is the default), all
  features described below will compile successfully but will result in
  an error at runtime such as: "No HDFS Support".

Using HDFS Support in Chapel
----------------------------

There are three ways provided to open HDFS files within Chapel.

Using an HDFS filesystem with open(url="hdfs://...")
****************************************************

.. code-block:: chapel

  // Open a file on HDFS connecting to the default HDFS instance
  var f = open(mode=iomode.r, url="hdfs://host:port/path");

  // Open up a reader and read from the file
  var reader = f.reader();

  // ...

  reader.close();

  f.close();

Explicitly Using Replicated HDFS Connections and Files
******************************************************

.. code-block:: chapel

  use HDFS;

  // Connect to HDFS via the default username (or whichever you want)
  //
  var hdfs = hdfsChapelConnect("default", 0);

  //
  // Create a file per locale
  //
  var gfl  = hdfs.hdfsOpen("/user/johnDoe/isThisAfile.txt", iomode.r);

  ...
  //
  // On any given locale, you can get the local file for the locale that
  // the task is currently running on via:
  //
  var fl = gfl.getLocal();

  // This file can be used as with a traditional file in Chapel, by
  // creating reader channels on it.

  // When you are done and want to close the files and disconnect from
  // HDFS, use:

  gfl.hdfsClose();
  hdfs.hdfsChapelDisconnect();

Explicitly Using Local HDFS Connections and Files
*************************************************

The HDFS module file also supports non-replicated values across
locales. So if you only wanted to connect to HDFS and open a file on
locale 1 you could do:

.. code-block:: chapel

  on Locales[1] {
    var hdfs = hdfs_chapel_connect("default", 0);
    var fl = hdfs.hdfs_chapel_open("/user/johnDoe/myFile.txt", iomode.cw);
    ...
    var read = fl.reader();
    ...
    fl.close();
    hdfs.hdfs_chapel_disconnect();
  }

The only stipulations are that you cannot open a file in both read and
write mode at the same time. (i.e iomode.r and iomode.cw are the only
modes that are supported, due to HDFS limitations).

.. _setting-up-hadoop:

Setting up Hadoop
-----------------

If you have a working installation of Hadoop already, you can skip
this section, other than to set up your CLASSPATH environment
variable.  This section is written so that people without sudo
permission can install and use HDFS.  If you do have sudo permissions,
you can usually install all of these via a package manager.

The general outline for these instructions is:

  1. Install and point to the jdk to provide code Chapel needs to
     compile against libhdfs (:ref:`Step 1 <setup-hadoop-1>`)
  2. Install Hadoop (:ref:`Step 2 <setup-hadoop-2>`)
  3. Set up Hadoop on (a) the local host or (b) a cluster of hosts
     (:ref:`Step 3 <setup-hadoop-3>`)
  4. Start up HDFS (:ref:`Step 4 <setup-hadoop-4>`)
  5. Stop HDFS when you're done (:ref:`Step 5 <setup-hadoop-5>`)
  6. Set up Chapel to run in distributed mode (:ref:`Step 6 <setup-hadoop-6>`)

First reflect your directory structure and version numbers (etc) in the
:ref:`sample .bashrc <setup-hadoop-bashrc>` and put it in your .bashrc (or
.bash_profile -- your choice) and source whichever one you put it into.

.. _setup-hadoop-1:

1. Make sure you have a SERVER edition of the jdk installed and
   point JAVA_INSTALL to it (see the same .bashrc below)

.. _setup-hadoop-2:

2. Install Hadoop

   * Download the latest version of Hadoop and unpack it

   * Now in the unpacked directory, open conf/hadoop-env.sh and edit:

     * set ``JAVA_INSTALL`` to be the part before ``bin/``... when you do:

        .. code-block:: sh

          which java

     * set ``HADOOP_CLASSPATH=$HADOOP_HOME/""*:$HADOOP_HOME/lib/""*:``

   * Now in conf/hdfs-site.xml put the replication number that you
     want for the field ``dfs.replication`` (this will set the
     replication of blocks of the files in HDFS)

   * Now set up passwordless ssh, if you haven't yet:

     .. code-block:: sh

       ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
       cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

.. _setup-hadoop-3:

3. Set up Hadoop

   a. For the local host - See the
      `Hadoop website <http://hadoop.apache.org/docs/stable/single_node_setup.html>`_
      for good documentation on how to do this.

   b. For a cluster of hosts. If you want to run Hadoop over a cluster, there
      are good tutorials online. Although it is usually as easy as making
      edits to the following files in ``$HADOOP_HOME/conf``:

      * adding the name of the nodes to ``slaves``
      * putting what you want to be the namenode in ``masters``
      * putting the master node in ``core-site.xml`` and ``mapred-site.xml``
      * running:

        .. code-block:: sh

         hadoop-daemon.sh start datanode
         hadoop-daemon.sh start tasktracker

      After this go to your datanode site and you should see a new
      datanode.

      A good online tutorial for this as well can be found here:
      http://hadoop.apache.org/docs/stable/cluster_setup.html

.. _setup-hadoop-4:

4. Start HDFS

   * Now all we need to do is format the namenode and start things up:

     .. code-block:: sh

       hadoop namenode -format
       start-all.sh  # (This will start hdfs and the tasktracker/jobtracker)

   * In general, hadoop has the same type of commands as bash,
     usually in the form:

     .. code-block:: sh

         hadoop dfs -<command> <regular args to that command>

   * At this point, you can compile and run Chapel programs using HDFS

   * You can check the status of Hadoop via http, for example on a local
     host (e.g., for :ref:`3a above <setup-hadoop-3>`), using:

       *  http://localhost:50070/
       *  http://localhost:50030/

     For cluster mode (:ref:`3b <setup-hadoop-3>`), you'll use the name of the
     master host in the URL and its port (see the web for details).

.. _setup-hadoop-5:

5. Shut things down:

   .. code-block:: sh

     stop-all.sh   # (This will stop hdfs and mapreduce)

.. _setup-hadoop-6:

6. Set up Chapel to run in distributed mode:

   * You'll need to set up your Chapel environment to target multiple
     locales in the standard way (see README.multilocale and the
     "Settings to run Chapel on multiple nodes" section of the
     .bashrc below).

   * After this you should be able to run Chapel code with HDFS over
     a cluster of computers the same way as you normally would.

.. _setup-hadoop-bashrc:

Here is a sample .bashrc for using Hadoop within Chapel:

.. code-block:: sh

  #
  # For Hadoop
  #
  export HADOOP_INSTALL=<Place where you have Hadoop installed>
  export HADOOP_HOME=$HADOOP_INSTALL
  export HADOOP_VERSION=<Your Hadoop version number>
  #
  # Note that the following environment variables might contain more paths than
  # those listed below if you have more than one IO system enabled. These are all
  # that you will need in order to use HDFS (only)
  #
  export CHPL_AUXIO_INCLUDE="-I$JAVA_INSTALL/include -I$JAVA_INSTALL/include/linux  -I$HADOOP_INSTALL/src/c++/libhdfs"
  export CHPL_AUXIO_LIBS="-L$JAVA_INSTALL/jre/lib/amd64/server -L$HADOOP_INSTALL/c++/Linux-amd64-64/lib"

  #
  # So we can run things such as start-all.sh etc. from anywhere and
  # don't need to be in $HADOOP_INSTALL
  #
  export PATH=$PATH:$HADOOP_INSTALL/bin

  #
  # Point to the JDK installation
  #
  export JAVA_INSTALL=<Place where you have the jdk installed>

  #
  # Add Hadoop directories to the Java class path
  #
  export CLASSPATH=$CLASSPATH:$HADOOP_HOME/""*:$HADOOP_HOME/lib/""*:$HADOOP_HOME/conf/""*:$(hadoop classpath):

  #
  # So we don't have to "install" these things
  #
  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HADOOP_HOME/c++/Linux-amd64-64/lib:$HADOOP_HOME/src/c++/libhdfs:$JAVA_INSTALL/jre/lib/amd64/server:$JAVA_INSTALL:$HADOOP_HOME/lib:$JAVA_INSTALL/jre/lib/amd64:$CLASSPATH

  #
  # Settings to run Chapel on multiple nodes
  #
  export GASNET_SPAWNFN=S
  export SSH_SERVERS=<the names of the computers in your cluster>
  export SSH_CMD=ssh
  export SSH_OPTIONS=-x
  export GASNET_ROUTE_OUTPUT=0

HDFS Support Types and Functions
--------------------------------

 */
module HDFS {

use IO, SysBasic, Error, UtilReplicatedVar;

pragma "no doc"
extern type qio_locale_map_ptr_t;     // array of locale to byte range mappings
pragma "no doc"
extern type char_ptr_ptr;             // char**

private extern const QIO_LOCALE_MAP_PTR_T_NULL: qio_locale_map_ptr_t;
private extern const hdfs_function_struct:qio_file_functions_t;
private extern const hdfs_function_struct_ptr:qio_file_functions_ptr_t;

pragma "no doc"
extern record hdfs_block_byte_map_t {
  var locale_id: int;
  var start_byte: int(64);
  var len: int(64);
}

// Connect to HDFS
private extern proc hdfs_connect(out fs: c_void_ptr, path: c_string, port: int): syserr;

// Disconnect from HDFS
private extern proc hdfs_disconnect(fs: c_void_ptr): syserr;

// Allocate an array for our locale mappings
private extern proc hdfs_alloc_array(n: int): char_ptr_ptr;

// Create a mapping locale_name -> locale_id (need this due to hdfs and since
// we cant pass strings inside extern records when multilocale)
private extern proc hdfs_create_locale_mapping(ref arr: char_ptr_ptr, num: int, loc_name: c_string);

// Return arr[i]
private extern proc hdfs_index_array(locs: qio_locale_map_ptr_t, i: int): hdfs_block_byte_map_t;

// Get block owners.
// Returns an array of hdfs_block_byte_map_t's
private extern proc hdfs_get_owners(f: qio_file_ptr_t, out locales: qio_locale_map_ptr_t, out num_blocks: c_int, arr: char_ptr_ptr, loc_nums:int): syserr;

// ********* For multilocale ************
/* Holds a file per locale */
record hdfsChapelFile {
  pragma "no doc"
  var files: [rcDomain] file;
}

/* Holds a connection to HDFS per locale */
record hdfsChapelFileSystem {
  pragma "no doc"
  var home: locale;
  pragma "no doc"
  var _internal_file: [rcDomain] c_void_ptr; // contains hdfsFS
}

// --------- Connecting/disconnecting ---------

/* Make a connection to HDFS for a single locale */
proc hdfsChapelConnect(out error: syserr, path: c_string, port: int): c_void_ptr{
  var ret: c_void_ptr;
  error = hdfs_connect(ret, path, port);
  return ret;
}

/* Connect to HDFS and create a filesystem ptr per locale */
proc hdfsChapelConnect(path: string, port: int): hdfsChapelFileSystem {
  var ret: hdfsChapelFileSystem;
  forall loc in Locales {
    on loc {
      var err: syserr;
      // TODO XXX: HACK: this accessing of locale.id copies the string onto this locale...
      // If you get rid of this IT WILL BREAK. (at least as strings currently stand)
      var hack = path.locale.id;
      const tmpstr = path;
      rcLocal(ret._internal_file) = hdfsChapelConnect(err, tmpstr.c_str(), port);
      if err then ioerror(err, "Unable to connect to HDFS", tmpstr);
    }
  }
  return ret;
}

/* Diconnect from the configured HDFS filesystem on each locale */
proc hdfsChapelFileSystem.hdfsChapelDisconnect() {
  forall loc in Locales {
    on loc {
      var err: syserr;
      err = hdfs_disconnect(rcLocal(this._internal_file));
      if err then ioerror(err, "Unable to disconnect from HDFS");
    }
  }
}

// ----- Opening/Closing ---------

/* Open a file on each locale */
proc hdfsChapelFileSystem.hdfsOpen(path:string, mode:iomode, hints:iohints=IOHINT_NONE, style:iostyle =
    defaultIOStyle()):hdfsChapelFile {
  var err:syserr = ENOERR;
  var ret: hdfsChapelFile;
  forall loc in Locales {
    on loc {
      rcLocal(ret.files) = open(err, path, mode, hints, style, rcLocal(this._internal_file));
      if err then ioerror(err, "in foreign open", path);
    }
  }
  return ret;
}

pragma "no doc"
proc hdfsChapelFile.hdfsClose(out err: syserr) {
  err = qio_file_close(rcLocal(this.files)._file_internal);
}

/* Close a file opened with :proc:`hdfsChapelFileSystem.hdfsOpen` */
proc hdfsChapelFile.hdfsClose() {
  forall loc in Locales {
    on loc {
      var err: syserr = ENOERR;
      this.hdfsClose(err);
      if err then ioerror(err, "Unable to close HDFS file");
    }
  }
}

// ------------- General Utils ----------
/* Returns the file for the locale that we are on when we query */
pragma "no doc"
proc hdfsChapelFile.getLocal(): file {
  return rcLocal(this.files);
}

/* Convenience funtion. Does the same as file.reader except that we dont have
   to get our local file first */
pragma "no doc"
proc hdfsChapelFile.hdfsReader(param kind=iokind.dynamic, param locking=true, start:int(64) = 0, end:int(64) = max(int(64)), hints:iohints = IOHINT_NONE) {
  return rcLocal(this.files).reader(kind, locking, start, end, hints);
}

// ------------- End mulitlocale ---------------

pragma "no doc"
record hdfsChapelFile_local {
  var home: locale = here;
  var _internal_:qio_locale_map_ptr_t = QIO_LOCALE_MAP_PTR_T_NULL;
}

pragma "no doc"
record hdfsChapelFileSystem_local {
  var home: locale;
  var _internal_: c_void_ptr;
}

pragma "no doc"
// this is used in hdfsChapelFileSystem_local.hdfs_chapel_open
proc open(out error:syserr, path:string, mode:iomode, hints:iohints=IOHINT_NONE,
    style:iostyle = defaultIOStyle(), fs:c_void_ptr):file {
  var local_style = style;
  var ret:file;
  ret.home = here;
  error = qio_file_open_access_usr(ret._file_internal, path, _modestring(mode),
      hints, local_style, fs, hdfs_function_struct_ptr);
  return ret;
}

/* Open a file on an HDFS filesystem for a single locale */
proc hdfsChapelFileSystem_local.hdfs_chapel_open(path:string, mode:iomode, hints:iohints=IOHINT_NONE, style:iostyle = defaultIOStyle()):file {
  var err:syserr = ENOERR;
  var ret = open(err, path, mode, hints, style, this._internal_);
  if err then ioerror(err, "in foreign open", path);
  return ret;
}

pragma "no doc"
proc hdfsChapelFileSystem_local.hdfs_chapel_disconnect() {
  on this.home {
    var err: syserr;
    err = hdfs_disconnect(this._internal_);
    if err then ioerror(err, "Unable to disconnect from HDFS");
  }
}

/* Connect to an HDFS filesystem on a single locale */
proc hdfs_chapel_connect(path:string, port: int): hdfsChapelFileSystem_local{

  var err: syserr;
  var ret = hdfs_chapel_connect(err, path, port);
  if err then ioerror(err, "Unable to connect to HDFS", path);
  return ret;
}

pragma "no doc"
proc hdfs_chapel_connect(out error:syserr, path:string, port: int): hdfsChapelFileSystem_local{
  var ret:hdfsChapelFileSystem_local;
  ret.home = here;
  error = hdfs_connect(ret._internal_, path.c_str(), port);
  return ret;
}

pragma "no doc"
proc getHosts(f: file) {
  var ret: hdfsChapelFile_local;
  var ret_num: c_int;
  var arr: char_ptr_ptr = hdfs_alloc_array(numLocales);
  for loc in Locales {
    hdfs_create_locale_mapping(arr, loc.id, loc.name.c_str());
  }
  var err = hdfs_get_owners(f._file_internal, ret._internal_, ret_num, arr, numLocales);
  if err then ioerror(err, "Unable to get owners for HDFS file");
  return (ret, ret_num);
}

pragma "no doc"
proc getLocaleBytes(g: hdfsChapelFile_local, i: int) {
  var ret = hdfs_index_array(g._internal_, i);
  return ret;
}

} /* end of module */

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

/*

Support for Hadoop Distributed Filesystem

This module implements support for the
`Hadoop <http://hadoop.apache.org/>`_
`Distributed Filesystem <http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html>`_ (HDFS).

Dependencies
------------

The HDFS functionality in Chapel is dependent upon both Hadoop and Java being
installed.  Your ``HADOOP_INSTALL``, ``JAVA_INSTALL`` and ``CLASSPATH``
environment variables must be set as described below in
:ref:`setting-up-hadoop`.  Without this it will not compile with HDFS, even if
the flags are set. As well, the HDFS functionality is also dependent upon the
``CHPL_AUXIO_INCLUDE`` and ``CHPL_AUXIO_LIBS`` environment variables being set
properly. For more information on how to set these properly, see
doc/technotes/README.auxIO in a Chapel release.

Enabling HDFS Support
---------------------

Once you have ensured that Hadoop and Java are installed and have the
five variables above, defined, set the environment variable
CHPL_AUX_FILESYS to 'hdfs' to enable HDFS support:

.. code-block:: sh

  export CHPL_AUX_FILESYS=hdfs

Then, rebuild Chapel by executing 'make' from $CHPL_HOME.

.. code-block:: sh

  make

.. note::

  If HDFS support is not enabled (which is the default), all
  features described below will compile successfully but will result in
  an error at runtime such as: "No HDFS Support".

Using HDFS Support in Chapel
----------------------------

There are three ways provided to open HDFS files within Chapel.

Using an HDFS filesystem with open(url="hdfs://...")
****************************************************

.. code-block:: chapel

  // Open a file on HDFS connecting to the default HDFS instance
  var f = open(mode=iomode.r, url="hdfs://host:port/path");

  // Open up a reader and read from the file
  var reader = f.reader();

  // ...

  reader.close();

  f.close();

Explicitly Using Replicated HDFS Connections and Files
******************************************************

.. code-block:: chapel

  use HDFS;

  // Connect to HDFS via the default username (or whichever you want)
  //
  var hdfs = hdfsChapelConnect("default", 0);

  //
  // Create a file per locale
  //
  var gfl  = hdfs.hdfsOpen("/user/johnDoe/isThisAfile.txt", iomode.r);

  ...
  //
  // On any given locale, you can get the local file for the locale that
  // the task is currently running on via:
  //
  var fl = gfl.getLocal();

  // This file can be used as with a traditional file in Chapel, by
  // creating reader channels on it.

  // When you are done and want to close the files and disconnect from
  // HDFS, use:

  gfl.hdfsClose();
  hdfs.hdfsChapelDisconnect();

Explicitly Using Local HDFS Connections and Files
*************************************************

The HDFS module file also supports non-replicated values across
locales. So if you only wanted to connect to HDFS and open a file on
locale 1 you could do:

.. code-block:: chapel

  on Locales[1] {
    var hdfs = hdfs_chapel_connect("default", 0);
    var fl = hdfs.hdfs_chapel_open("/user/johnDoe/myFile.txt", iomode.cw);
    ...
    var read = fl.reader();
    ...
    fl.close();
    hdfs.hdfs_chapel_disconnect();
  }

The only stipulations are that you cannot open a file in both read and
write mode at the same time. (i.e iomode.r and iomode.cw are the only
modes that are supported, due to HDFS limitations).

.. _setting-up-hadoop:

Setting up Hadoop
-----------------

If you have a working installation of Hadoop already, you can skip
this section, other than to set up your CLASSPATH environment
variable.  This section is written so that people without sudo
permission can install and use HDFS.  If you do have sudo permissions,
you can usually install all of these via a package manager.

The general outline for these instructions is:

  1. Install and point to the jdk to provide code Chapel needs to
     compile against libhdfs (:ref:`Step 1 <setup-hadoop-1>`)
  2. Install Hadoop (:ref:`Step 2 <setup-hadoop-2>`)
  3. Set up Hadoop on (a) the local host or (b) a cluster of hosts
     (:ref:`Step 3 <setup-hadoop-3>`)
  4. Start up HDFS (:ref:`Step 4 <setup-hadoop-4>`)
  5. Stop HDFS when you're done (:ref:`Step 5 <setup-hadoop-5>`)
  6. Set up Chapel to run in distributed mode (:ref:`Step 6 <setup-hadoop-6>`)

First reflect your directory structure and version numbers (etc) in the
:ref:`sample .bashrc <setup-hadoop-bashrc>` and put it in your .bashrc (or
.bash_profile -- your choice) and source whichever one you put it into.

.. _setup-hadoop-1:

1. Make sure you have a SERVER edition of the jdk installed and
   point JAVA_INSTALL to it (see the same .bashrc below)

.. _setup-hadoop-2:

2. Install Hadoop

   * Download the latest version of Hadoop and unpack it

   * Now in the unpacked directory, open conf/hadoop-env.sh and edit:

     * set ``JAVA_INSTALL`` to be the part before ``bin/``... when you do:

        .. code-block:: sh

          which java

     * set ``HADOOP_CLASSPATH=$HADOOP_HOME/""*:$HADOOP_HOME/lib/""*:``

   * Now in conf/hdfs-site.xml put the replication number that you
     want for the field ``dfs.replication`` (this will set the
     replication of blocks of the files in HDFS)

   * Now set up passwordless ssh, if you haven't yet:

     .. code-block:: sh

       ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
       cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

.. _setup-hadoop-3:

3. Set up Hadoop

   a. For the local host - See the
      `Hadoop website <http://hadoop.apache.org/docs/stable/single_node_setup.html>`_
      for good documentation on how to do this.

   b. For a cluster of hosts. If you want to run Hadoop over a cluster, there
      are good tutorials online. Although it is usually as easy as making
      edits to the following files in ``$HADOOP_HOME/conf``:

      * adding the name of the nodes to ``slaves``
      * putting what you want to be the namenode in ``masters``
      * putting the master node in ``core-site.xml`` and ``mapred-site.xml``
      * running:

        .. code-block:: sh

         hadoop-daemon.sh start datanode
         hadoop-daemon.sh start tasktracker

      After this go to your datanode site and you should see a new
      datanode.

      A good online tutorial for this as well can be found here:
      http://hadoop.apache.org/docs/stable/cluster_setup.html

.. _setup-hadoop-4:

4. Start HDFS

   * Now all we need to do is format the namenode and start things up:

     .. code-block:: sh

       hadoop namenode -format
       start-all.sh  # (This will start hdfs and the tasktracker/jobtracker)

   * In general, hadoop has the same type of commands as bash,
     usually in the form:

     .. code-block:: sh

         hadoop dfs -<command> <regular args to that command>

   * At this point, you can compile and run Chapel programs using HDFS

   * You can check the status of Hadoop via http, for example on a local
     host (e.g., for :ref:`3a above <setup-hadoop-3>`), using:

       *  http://localhost:50070/
       *  http://localhost:50030/

     For cluster mode (:ref:`3b <setup-hadoop-3>`), you'll use the name of the
     master host in the URL and its port (see the web for details).

.. _setup-hadoop-5:

5. Shut things down:

   .. code-block:: sh

     stop-all.sh   # (This will stop hdfs and mapreduce)

.. _setup-hadoop-6:

6. Set up Chapel to run in distributed mode:

   * You'll need to set up your Chapel environment to target multiple
     locales in the standard way (see README.multilocale and the
     "Settings to run Chapel on multiple nodes" section of the
     .bashrc below).

   * After this you should be able to run Chapel code with HDFS over
     a cluster of computers the same way as you normally would.

.. _setup-hadoop-bashrc:

Here is a sample .bashrc for using Hadoop within Chapel:

.. code-block:: sh

  #
  # For Hadoop
  #
  export HADOOP_INSTALL=<Place where you have Hadoop installed>
  export HADOOP_HOME=$HADOOP_INSTALL
  export HADOOP_VERSION=<Your Hadoop version number>
  #
  # Note that the following environment variables might contain more paths than
  # those listed below if you have more than one IO system enabled. These are all
  # that you will need in order to use HDFS (only)
  #
  export CHPL_AUXIO_INCLUDE="-I$JAVA_INSTALL/include -I$JAVA_INSTALL/include/linux  -I$HADOOP_INSTALL/src/c++/libhdfs"
  export CHPL_AUXIO_LIBS="-L$JAVA_INSTALL/jre/lib/amd64/server -L$HADOOP_INSTALL/c++/Linux-amd64-64/lib"

  #
  # So we can run things such as start-all.sh etc. from anywhere and
  # don't need to be in $HADOOP_INSTALL
  #
  export PATH=$PATH:$HADOOP_INSTALL/bin

  #
  # Point to the JDK installation
  #
  export JAVA_INSTALL=<Place where you have the jdk installed>

  #
  # Add Hadoop directories to the Java class path
  #
  export CLASSPATH=$CLASSPATH:$HADOOP_HOME/""*:$HADOOP_HOME/lib/""*:$HADOOP_HOME/conf/""*:$(hadoop classpath):

  #
  # So we don't have to "install" these things
  #
  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HADOOP_HOME/c++/Linux-amd64-64/lib:$HADOOP_HOME/src/c++/libhdfs:$JAVA_INSTALL/jre/lib/amd64/server:$JAVA_INSTALL:$HADOOP_HOME/lib:$JAVA_INSTALL/jre/lib/amd64:$CLASSPATH

  #
  # Settings to run Chapel on multiple nodes
  #
  export GASNET_SPAWNFN=S
  export SSH_SERVERS=<the names of the computers in your cluster>
  export SSH_CMD=ssh
  export SSH_OPTIONS=-x
  export GASNET_ROUTE_OUTPUT=0

HDFS Support Types and Functions
--------------------------------

 */
module HDFS {

use IO, SysBasic, Error, UtilReplicatedVar;

pragma "no doc"
extern type qio_locale_map_ptr_t;     // array of locale to byte range mappings
pragma "no doc"
extern type char_ptr_ptr;             // char**

pragma "no doc"
private extern const QIO_LOCALE_MAP_PTR_T_NULL: qio_locale_map_ptr_t;
pragma "no doc"
private extern const hdfs_function_struct:qio_file_functions_t;
pragma "no doc"
private extern const hdfs_function_struct_ptr:qio_file_functions_ptr_t;

pragma "no doc"
extern record hdfs_block_byte_map_t {
  var locale_id: int;
  var start_byte: int(64);
  var len: int(64);
}

// Connect to HDFS
pragma "no doc"
private extern proc hdfs_connect(out fs: c_void_ptr, path: c_string, port: int): syserr;

// Disconnect from HDFS
pragma "no doc"
private extern proc hdfs_disconnect(fs: c_void_ptr): syserr;

// Allocate an array for our locale mappings
pragma "no doc"
private extern proc hdfs_alloc_array(n: int): char_ptr_ptr;

// Create a mapping locale_name -> locale_id (need this due to hdfs and since
// we cant pass strings inside extern records when multilocale)
pragma "no doc"
private extern proc hdfs_create_locale_mapping(ref arr: char_ptr_ptr, num: int, loc_name: c_string);

// Return arr[i]
pragma "no doc"
private extern proc hdfs_index_array(locs: qio_locale_map_ptr_t, i: int): hdfs_block_byte_map_t;

// Get block owners.
// Returns an array of hdfs_block_byte_map_t's
pragma "no doc"
private extern proc hdfs_get_owners(f: qio_file_ptr_t, out locales: qio_locale_map_ptr_t, out num_blocks: c_int, arr: char_ptr_ptr, loc_nums:int): syserr;

// ********* For multilocale ************
/* Holds a file per locale */
record hdfsChapelFile {
  pragma "no doc"
  var files: [rcDomain] file;
}

/* Holds a connection to HDFS per locale */
record hdfsChapelFileSystem {
  pragma "no doc"
  var home: locale;
  pragma "no doc"
  var _internal_file: [rcDomain] c_void_ptr; // contains hdfsFS
}

// --------- Connecting/disconnecting ---------

/* Make a connection to HDFS for a single locale */
proc hdfsChapelConnect(out error: syserr, path: c_string, port: int): c_void_ptr{
  var ret: c_void_ptr;
  error = hdfs_connect(ret, path, port);
  return ret;
}

/* Connect to HDFS and create a filesystem ptr per locale */
proc hdfsChapelConnect(path: string, port: int): hdfsChapelFileSystem {
  var ret: hdfsChapelFileSystem;
  forall loc in Locales {
    on loc {
      var err: syserr;
      // TODO XXX: HACK: this accessing of locale.id copies the string onto this locale...
      // If you get rid of this IT WILL BREAK. (at least as strings currently stand)
      var hack = path.locale.id;
      const tmpstr = path;
      rcLocal(ret._internal_file) = hdfsChapelConnect(err, tmpstr.c_str(), port);
      if err then ioerror(err, "Unable to connect to HDFS", tmpstr);
    }
  }
  return ret;
}

/* Diconnect from the configured HDFS filesystem on each locale */
proc hdfsChapelFileSystem.hdfsChapelDisconnect() {
  forall loc in Locales {
    on loc {
      var err: syserr;
      err = hdfs_disconnect(rcLocal(this._internal_file));
      if err then ioerror(err, "Unable to disconnect from HDFS");
    }
  }
}

// ----- Opening/Closing ---------

/* Open a file on each locale */
proc hdfsChapelFileSystem.hdfsOpen(path:string, mode:iomode, hints:iohints=IOHINT_NONE, style:iostyle =
    defaultIOStyle()):hdfsChapelFile {
  var err:syserr = ENOERR;
  var ret: hdfsChapelFile;
  forall loc in Locales {
    on loc {
      rcLocal(ret.files) = open(err, path, mode, hints, style, rcLocal(this._internal_file));
      if err then ioerror(err, "in foreign open", path);
    }
  }
  return ret;
}

pragma "no doc"
proc hdfsChapelFile.hdfsClose(out err: syserr) {
  err = qio_file_close(rcLocal(this.files)._file_internal);
}

/* Close a file opened with :proc:`hdfsChapelFileSystem.hdfsOpen` */
proc hdfsChapelFile.hdfsClose() {
  forall loc in Locales {
    on loc {
      var err: syserr = ENOERR;
      this.hdfsClose(err);
      if err then ioerror(err, "Unable to close HDFS file");
    }
  }
}

// ------------- General Utils ----------
/* Returns the file for the locale that we are on when we query */
pragma "no doc"
proc hdfsChapelFile.getLocal(): file {
  return rcLocal(this.files);
}

/* Convenience funtion. Does the same as file.reader except that we dont have
   to get our local file first */
pragma "no doc"
proc hdfsChapelFile.hdfsReader(param kind=iokind.dynamic, param locking=true, start:int(64) = 0, end:int(64) = max(int(64)), hints:iohints = IOHINT_NONE) {
  return rcLocal(this.files).reader(kind, locking, start, end, hints);
}

// ------------- End mulitlocale ---------------

pragma "no doc"
record hdfsChapelFile_local {
  var home: locale = here;
  var _internal_:qio_locale_map_ptr_t = QIO_LOCALE_MAP_PTR_T_NULL;
}

pragma "no doc"
record hdfsChapelFileSystem_local {
  var home: locale;
  var _internal_: c_void_ptr;
}

pragma "no doc"
// this is used in hdfsChapelFileSystem_local.hdfs_chapel_open
proc open(out error:syserr, path:string, mode:iomode, hints:iohints=IOHINT_NONE,
    style:iostyle = defaultIOStyle(), fs:c_void_ptr):file {
  var local_style = style;
  var ret:file;
  ret.home = here;
  error = qio_file_open_access_usr(ret._file_internal, path, _modestring(mode),
      hints, local_style, fs, hdfs_function_struct_ptr);
  return ret;
}

/* Open a file on an HDFS filesystem for a single locale */
proc hdfsChapelFileSystem_local.hdfs_chapel_open(path:string, mode:iomode, hints:iohints=IOHINT_NONE, style:iostyle = defaultIOStyle()):file {
  var err:syserr = ENOERR;
  var ret = open(err, path, mode, hints, style, this._internal_);
  if err then ioerror(err, "in foreign open", path);
  return ret;
}

pragma "no doc"
proc hdfsChapelFileSystem_local.hdfs_chapel_disconnect() {
  on this.home {
    var err: syserr;
    err = hdfs_disconnect(this._internal_);
    if err then ioerror(err, "Unable to disconnect from HDFS");
  }
}

/* Connect to an HDFS filesystem on a single locale */
proc hdfs_chapel_connect(path:string, port: int): hdfsChapelFileSystem_local{

  var err: syserr;
  var ret = hdfs_chapel_connect(err, path, port);
  if err then ioerror(err, "Unable to connect to HDFS", path);
  return ret;
}

pragma "no doc"
proc hdfs_chapel_connect(out error:syserr, path:string, port: int): hdfsChapelFileSystem_local{
  var ret:hdfsChapelFileSystem_local;
  ret.home = here;
  error = hdfs_connect(ret._internal_, path.c_str(), port);
  return ret;
}

pragma "no doc"
proc getHosts(f: file) {
  var ret: hdfsChapelFile_local;
  var ret_num: c_int;
  var arr: char_ptr_ptr = hdfs_alloc_array(numLocales);
  for loc in Locales {
    hdfs_create_locale_mapping(arr, loc.id, loc.name.c_str());
  }
  var err = hdfs_get_owners(f._file_internal, ret._internal_, ret_num, arr, numLocales);
  if err then ioerror(err, "Unable to get owners for HDFS file");
  return (ret, ret_num);
}

pragma "no doc"
proc getLocaleBytes(g: hdfsChapelFile_local, i: int) {
  var ret = hdfs_index_array(g._internal_, i);
  return ret;
}

} /* end of module */