To shut down the bleed of BuffersInternal's symbols into the namespace, remove the private module and move all of its contents into the main Buffers module, marking with private and "no doc" as appropriate to ensure the same documentation output.

/*
 * 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 buffers - regions of memory without a particular interpretation.

   This module provides :record:`bytes` and :record:`buffer` types which
   can be used to manage memory regions.

   A :record:`bytes` is a contiguous memory region - really a data structure
   containing a pointer, length, and also the information necessary to free the
   memory region when it is no longer used.

   A :record:`buffer` consists of a sequence views into :record:`bytes`
   objects. A :record:`bytes` object might be shared by several
   :record:`buffer` objects.

   These types should be safe to use in a multi-locale context. These types
   should free their memory after the last user of that memory goes out of
   scope. They are currently reference counted but that may not always be the
   case.

 */
module Buffers {
  use SysBasic;
  use Error;

  pragma "no doc"
  extern type qbytes_ptr_t;
  pragma "no doc"
  extern type qbuffer_ptr_t;
  pragma "no doc"
  extern type qbuffer_iter_t;
  private extern const QBYTES_PTR_NULL:qbytes_ptr_t;
  private extern const QBUFFER_PTR_NULL:qbuffer_ptr_t;

  private extern proc qbytes_retain(qb:qbytes_ptr_t);
  private extern proc qbytes_release(qb:qbytes_ptr_t);
  private extern proc qbytes_len(qb:qbytes_ptr_t):int(64);
  private extern proc qbytes_data(qb:qbytes_ptr_t):c_void_ptr;

  private extern proc qbytes_create_iobuf(out ret:qbytes_ptr_t):syserr;
  private extern proc qbytes_create_calloc(out ret:qbytes_ptr_t, len:int(64)):syserr;

  private extern proc qbuffer_iter_null():qbuffer_iter_t;

  private extern proc qbuffer_create(out buf:qbuffer_ptr_t):syserr;
  private extern proc qbuffer_retain(buf:qbuffer_ptr_t);
  private extern proc qbuffer_release(buf:qbuffer_ptr_t);

  private extern proc qbuffer_append(buf:qbuffer_ptr_t, bytes:qbytes_ptr_t, skip_bytes:int(64), len_bytes:int(64)):syserr;
  private extern proc qbuffer_append_buffer(buf:qbuffer_ptr_t, src:qbuffer_ptr_t, src_start:qbuffer_iter_t, src_end:qbuffer_iter_t):syserr;
  private extern proc qbuffer_prepend(buf:qbuffer_ptr_t, bytes:qbytes_ptr_t, skip_bytes:int(64), len_bytes:int(64)):syserr;
  private extern proc qbuffer_flatten(buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t, out bytes_out):syserr;
  private extern proc qbuffer_copyout(buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t, ref x, size):syserr;
  private extern proc qbuffer_copyin(buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t, ref x, size):syserr;

  private extern proc qbuffer_begin(buf:qbuffer_ptr_t):qbuffer_iter_t;
  private extern proc qbuffer_end(buf:qbuffer_ptr_t):qbuffer_iter_t;
  private extern proc qbuffer_iter_next_part(buf:qbuffer_ptr_t, ref it:qbuffer_iter_t);
  private extern proc qbuffer_iter_prev_part(buf:qbuffer_ptr_t, ref it:qbuffer_iter_t);
  private extern proc qbuffer_iter_advance(buf:qbuffer_ptr_t, ref it:qbuffer_iter_t, amt:int(64));

  private extern proc qbuffer_iter_get(it: qbuffer_iter_t, end:qbuffer_iter_t,
                                out bytes_out:qbytes_ptr_t,
                                out skip_out:int(64),
                                out len_out:int(64));
  private extern proc qbuffer_iter_num_bytes(start:qbuffer_iter_t, end:qbuffer_iter_t):int(64);

  private extern proc qbuffer_len(buf:qbuffer_ptr_t):int(64);

  private extern proc debug_print_qbuffer_iter(/*const*/ ref it:qbuffer_iter_t);

  private extern proc qbuffer_start_offset(buf:qbuffer_ptr_t):int(64);
  private extern proc qbuffer_end_offset(buf:qbuffer_ptr_t):int(64);
  private extern proc qbuffer_reposition(buf:qbuffer_ptr_t, new_offset_start:int(64));

  private extern proc bulk_get_bytes(src_locale:int, src_addr:qbytes_ptr_t):qbytes_ptr_t;

  private extern proc bulk_put_buffer(dst_locale:int, dst_addr:c_void_ptr, dst_len:int(64), buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t):syserr;

  // Now define the Chapel types using the originals..

  /* This type represents a contiguous sequence of bytes.
     This sequence of bytes is represented with a C pointer and length and is
     currently reference counted.  Note that this record contains private
     fields in addition to the home field.

   */
  pragma "ignore noinit"
  record bytes {
    /* The home locale storing the data */
    var home: locale;
    pragma "no doc"
    var _bytes_internal:qbytes_ptr_t = QBYTES_PTR_NULL;
  }

  // bytes methods.
  /* Construct an empty bytes object */
  proc bytes.bytes() {
    this._bytes_internal = QBYTES_PTR_NULL;
    this.home = here;
  }
  /*

     defaultOf appears to be unnecessary since the initializer
     in the class takes care of it.

  inline proc _defaultOf(type t): t where t == bytes {
    var ret:bytes = noinit;
    ret.home = here;
    ret._bytes_internal = QBYTES_PTR_NULL;
    return ret;
  }*/

  /* Construct a bytes object by allocating zero-filled memory.

     :arg len: the number of bytes to allocate
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
   */
  proc bytes.bytes(len:int(64), out error:syserr) {
    error = qbytes_create_calloc(this._bytes_internal, len);
    // The buffer is "retained" internally on creation, but only on success.
    this.home = here;
  }
  pragma "no doc"
  proc bytes.bytes(len:int(64)) {
    var error:syserr = ENOERR;
    error = qbytes_create_calloc(this._bytes_internal, len);
    if error then ioerror(error, "in bytes constructor");
    // The buffer is retained internally on construction, but only on success.
    this.home = here;
  }

  pragma "no doc"
  private proc create_iobuf(out error:syserr):bytes {
    var ret:bytes;
    error = qbytes_create_iobuf(ret._bytes_internal);
    // The buffer is "retained" internally on creation, but only on success.
    ret.home = here;
    return ret;
  }
  pragma "no doc"
  private proc create_iobuf():bytes {
    var err:syserr = ENOERR;
    var ret = create_iobuf(err);
    if err then ioerror(err, "in create_iobuf");
    return ret;
  }

  // TODO -- shouldn't have to write this this way!
  pragma "init copy fn"
  pragma "no doc"
  proc chpl__initCopy(x: bytes) {
    if x.home == here {
      qbytes_retain(x._bytes_internal);
      return x;
    } else {
      var ret:bytes;
      ret.home = here;
      // The initial ref count is 1, so no need to call qbytes_retain here.
      ret._bytes_internal = bulk_get_bytes(x.home.id, x._bytes_internal);
      return ret;
    }
  }

  pragma "no doc"
  proc =(ref ret:bytes, x:bytes) {
    // retain -- release
    if( x.home == here ) {
      on x.home {
        qbytes_retain(x._bytes_internal);
      }

      on ret.home {
        qbytes_release(ret._bytes_internal);
      }

      ret.home = x.home;
      ret._bytes_internal = x._bytes_internal;
    } else {
      on ret.home {
        qbytes_release(ret._bytes_internal);
      }
      ret.home = here;
      ret._bytes_internal = bulk_get_bytes(x.home.id, x._bytes_internal);
      // On return from bulk_get_bytes, the ref count in ret._bytes_internal
      // should be 1.
      // Note that the error case is not handled (bulk_get_bytes must succeed).
    }
  }

  pragma "no doc"
  proc bytes.~bytes() {
    on this.home {
      qbytes_release(this._bytes_internal);
      this._bytes_internal = QBYTES_PTR_NULL;
    }
  }

  /* :returns: the number of bytes stored in a :record:`bytes` object */
  proc bytes.len:int(64) {
    var ret:int(64);
    on home {
     ret = qbytes_len(this._bytes_internal);
    }
    return ret;
  }

  /* This type represents a particular location with a buffer.
     Use buffer methods like :proc:`buffer.start` and :proc:`buffer.advance` to
     create and manipulate :record:`buffer_iterator` s.  Note that this record
     contains private fields in addition to the home field.

   */
  pragma "ignore noinit"
  record buffer_iterator {
    /* The home locale storing the data */
    var home: locale;
    pragma "no doc"
    var _bufit_internal:qbuffer_iter_t = qbuffer_iter_null();
  }

  /* Create a :record:`buffer_iterator` that points nowhere */
  pragma "no doc"
  proc buffer_iterator.buffer_iterator() {
    this.home = here;
    this._bufit_internal = qbuffer_iter_null();
  }

  /* A region within a buffer (indicated by two :record:`buffer_iterator` s ) */
  pragma "ignore noinit"
  record buffer_range {
    var start:buffer_iterator;
    var end:buffer_iterator;
  }

  /* :returns: the number of bytes stored in a :record:`buffer_range` */
  proc buffer_range.len:int(64) {
    var ret:int(64);
    on this.start.home {
      ret = qbuffer_iter_num_bytes(this.start._bufit_internal,
                                   this.end._bufit_internal);
    }
    return ret;
  }

  // FUTURE -- we could create const buffers
  // with a boolean type param

  /* A buffer which can contain multiple memory regions
     (that is, multiple regions of :record:`bytes` objects).  Note that this
     record contains private fields in addition to the home field.

   */
  pragma "ignore noinit"
  record buffer {
    /* The home locale storing the data */
    var home:locale;
    pragma "no doc"
    var _buf_internal:qbuffer_ptr_t = QBUFFER_PTR_NULL;
  }

  /* buffer methods. */

  /* Create an empty buffer.

     :arg error: (optional) capture an error that was encountered instead of
                  halting on error
   */
  proc buffer.buffer(out error:syserr) {
    this.home = here;
    error = qbuffer_create(this._buf_internal);
  }
  pragma "no doc"
  proc buffer.buffer() {
    var error:syserr = ENOERR;
    this.home = here;
    error = qbuffer_create(this._buf_internal);
    if error then ioerror(error, "in buffer constructor");
  }
  /*
     defaultOf appears to be unnecessary since the initializer
     in the class takes care of it.

  inline proc _defaultOf(type t): t where t == buffer {
    var ret:buffer = noinit;
    ret.home = here;
    ret._buf_internal = QBUFFER_PTR_NULL;
    return ret;
  }*/

  /* Flatten a buffer. Create a new :record:`bytes` object and copy
     the buffer into it. This function should work even if buffer is
     remote.

     :arg range: the region of the buffer to copy, for example buffer.all()
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
     :returns: a newly constructed bytes object on the current locale
   */
  proc buffer.flatten(range:buffer_range, out error:syserr) {
  //proc buffer.flatten(range:buffer_range, out error:syserr):bytes {
    var ret:bytes = new bytes();
    if this.home == here {
      error = qbuffer_flatten(this._buf_internal, range.start._bufit_internal, range.end._bufit_internal, ret._bytes_internal);
    } else {
      var dst_locale = here;
      var dst_len:int(64) = range.len;
      ret = new bytes(dst_len);
      var dst_addr = qbytes_data(ret._bytes_internal);
      on this.home {
        // Copy the buffer to the bytes...
        error = bulk_put_buffer(dst_locale.id, dst_addr, dst_len,
                                this._buf_internal,
                                range.start._bufit_internal,
                                range.end._bufit_internal);
      }
    }
    return ret;
  }

  pragma "no doc"
  proc buffer.flatten(range:buffer_range) {
  //proc buffer.flatten(range:buffer_range):bytes {
    var error:syserr = ENOERR;
    var ret:bytes = new bytes();
    ret = this.flatten(range,error);
    if error then ioerror(error, "in buffer.flatten");
    return ret;
  }

  // TODO -- shouldn't have to write this this way!
  pragma "init copy fn"
  pragma "no doc"
  proc chpl__initCopy(x: buffer) {
    if x.home == here {
      qbuffer_retain(x._buf_internal);
      return x;
    } else {
      var ret:buffer;
      var start_offset:int(64);
      var end_offset:int(64);

      on x.home {
        start_offset = qbuffer_start_offset(x._buf_internal);
        end_offset = qbuffer_end_offset(x._buf_internal);
      }

      var b = new bytes(end_offset - start_offset);
      var len = qbytes_len(b._bytes_internal);
      var ptr = qbytes_data(b._bytes_internal);

      // Now create a local buffer with the right data
      // in it starting at the right position.
      qbuffer_reposition(ret._buf_internal, start_offset);

      var there_uid = here.id;

      on x.home {
        var err:syserr = ENOERR;
        err = bulk_put_buffer(there_uid, ptr, len, x._buf_internal,
                              qbuffer_begin(x._buf_internal),
                              qbuffer_end(x._buf_internal));
        if err then ioerror(err, "in buffer init copy");
      }

      ret.append(b);
      return ret;
    }
  }

  pragma "no doc"
  proc =(ref ret:buffer, x:buffer) {
    ret.home = here;
    // retain -- release
    if( x.home == ret.home ) {
      on x.home {
        qbuffer_retain(x._buf_internal);
      }

      on ret.home {
        qbuffer_release(ret._buf_internal);
      }

      ret.home = x.home;
      ret._buf_internal = x._buf_internal;
    } else {
      on ret.home {
        qbuffer_release(ret._buf_internal);
      }

      var start_offset:int(64);
      var end_offset:int(64);

      on x.home {
        start_offset = qbuffer_start_offset(x._buf_internal);
        end_offset = qbuffer_end_offset(x._buf_internal);
      }

      var b = new bytes(end_offset - start_offset);
      var len = qbytes_len(b._bytes_internal);
      var ptr = qbytes_data(b._bytes_internal);

      // Now create a local buffer with the right data
      // in it starting at the right position.
      qbuffer_reposition(ret._buf_internal, start_offset);

      var there_uid = here.id;

      on x.home {
        var err:syserr = ENOERR;
        err = bulk_put_buffer(there_uid, ptr, len, x._buf_internal,
                              qbuffer_begin(x._buf_internal),
                              qbuffer_end(x._buf_internal));
        if err then ioerror(err, "in buffer assignment");
      }

      ret.append(b);
    }
  }

  pragma "no doc"
  proc buffer.~buffer() {
    on this.home {
      qbuffer_release(this._buf_internal);
      this._buf_internal = QBUFFER_PTR_NULL;
    }
  }

  /* :returns: the number of bytes stored in a :record:`buffer` object */
  proc buffer.len {
    var ret:int(64);
    on this.home {
      ret = qbuffer_len(this._buf_internal);
    }
    return ret;
  }

  /* Append a :record:`bytes` object to a :record:`buffer`.
     This function might store the passed bytes object by reference instead of
     copying it. The current implementation will always do so and will always
     increase the reference count of the bytes object.  The version of this
     function called without the error argument will halt if an error is
     encountered.

     :arg b: the :record:`bytes` object to append
     :arg skip_bytes: how many bytes at the front of b to skip
     :arg len_bytes: how many bytes to append to the buffer
     :arg error: (optional) capture an error that was encountered instead of
           halting on error
  */
  proc buffer.append(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len, out error:syserr) {
    on this.home {
      error = qbuffer_append(this._buf_internal, b._bytes_internal, skip_bytes, len_bytes);
    }
  }

  pragma "no doc"
  proc buffer.append(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len) {
    var err:syserr = ENOERR;
    this.append(b, skip_bytes, len_bytes, err);
    if err then ioerror(err, "in buffer.append");
  }

  /* Append a :record:`buffer` object to a :record:`buffer`.
     This function might store a pointers to the bytes objects contained in
     `buf` instead of copying them. If that happens, the current implementation
     will increase their reference counts.  The version of this function called
     without the error argument will halt if an error is encountered.

     :arg buf: the :record:`buffer` object to append
     :arg part: a :record:`buffer_range` indicating which section of the
                buffer to copy. Defaults to all of the buffer.
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
   */
  proc buffer.append(buf:buffer, part:buffer_range = buf.all(), out error:syserr) {
    on this.home {
      error = qbuffer_append_buffer(this._buf_internal, buf._buf_internal, part.start._bufit_internal, part.end._bufit_internal);
    }
  }
  pragma "no doc"
  proc buffer.append(buf:buffer, part:buffer_range = buf.all()) {
    var err:syserr = ENOERR;
    this.append(buf, part, err);
    if err then ioerror(err, "in buffer.append");
  }

  /* Prepend a :record:`bytes` object to a :record:`buffer`.
     This function might store the passed bytes object by reference instead of
     copying it. The current implementation will always do so and will always
     increase the reference count of the bytes object.  The version of this
     function called without the error argument will halt if an error is
     encountered.

     :arg b: the :record:`bytes` object to prepend
     :arg skip_bytes: how many bytes at the front of b to skip
     :arg len_bytes: how many bytes to append to the buffer
     :arg error: (optional) capture an error that was encountered instead of
           halting on error
  */
  proc buffer.prepend(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len, out error:syserr) {
    on this.home {
      qbuffer_prepend(this._buf_internal, b._bytes_internal, skip_bytes, len_bytes);
    }
  }
  pragma "no doc"
  proc buffer.prepend(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len) {
    var err:syserr = ENOERR;
    this.prepend(b, skip_bytes, len_bytes, err);
    if err then ioerror(err, "in buffer.prepend");
  }

  /* :return: a :record:`buffer_iterator` to the start of a buffer */
  proc buffer.start():buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      ret._bufit_internal = qbuffer_begin(this._buf_internal);
    }
    return ret;
  }
  /* :return: a :record:`buffer_iterator` to the end of a buffer */
  proc buffer.end():buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      ret._bufit_internal = qbuffer_end(this._buf_internal);
    }
    return ret;
  }
  /* :return: a :record:`buffer_range` for the entirety of a buffer */
  proc buffer.all():buffer_range {
    return new buffer_range(this.start(), this.end());
  }

  /* Advance a :record:`buffer_iterator` to the next contigous
     memory region stored therein

     :arg it: the buffer iterator to advance
   */
  proc buffer.next_part(ref it:buffer_iterator) {
    on this.home {
      var x = it;
      qbuffer_iter_next_part(this._buf_internal, x._bufit_internal);
      it = x;
    }
  }

  /* Advance a :record:`buffer_iterator` to the previous contigous
     memory region stored therein

     :arg it: the buffer iterator to advance
   */
  proc buffer.prev_part(ref it:buffer_iterator) {
    on this.home {
      var x = it;
      qbuffer_iter_prev_part(this._buf_internal, x._bufit_internal);
      it = x;
    }
  }

  /* Advance a :record:`buffer_iterator` by a particular number
     of bytes.

     :arg it: the buffer iterator to advance
     :arg amount: the number of bytes to advance
   */
  proc buffer.advance(ref it:buffer_iterator, amount:int(64)) {
    on this.home {
      var x = it;
      qbuffer_iter_advance(this._buf_internal, x._bufit_internal, amount);
      it = x;
    }
  }

  pragma "no doc"
  proc buffer_iterator.debug_print()
  {
    on this.home {
      debug_print_qbuffer_iter(this._bufit_internal);
    }
  }

  /* methods to read/write basic types. */

  /* Read a basic type from a buffer. This method reads the value
     by copying from memory - so it reads a binary value in native endianness.

     :arg it: a :record:`buffer_iterator` where reading will start
     :arg value: a basic type (integral or floating point value)
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
     :returns: a buffer iterator storing the position immediately after
               the read value.
  */
  proc buffer.copyout(it:buffer_iterator, out value, out error:syserr):buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      var end:buffer_iterator = it;
      var tmp:value.type;
      var sz = numBytes(value.type);
      this.advance(end, sz);
      error = qbuffer_copyout(this._buf_internal, it._bufit_internal, end._bufit_internal, tmp, sz);
      value = tmp;
      ret = end;
    }
    return ret;
  }
  pragma "no doc"
  proc buffer.copyout(it:buffer_iterator, out value):buffer_iterator {
    var err:syserr = ENOERR;
    var ret = this.copyout(it, value, err);
    if err then ioerror(err, "in buffer.copyout");
    return ret;
  }

  /* Write a basic type to a buffer. This method writes the value
     by copying to memory - so it reads a binary value in native endianness.

     :arg it: a :record:`buffer_iterator` where reading will start
     :arg value: a basic type (integral or floating point value)
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
     :returns: a buffer iterator storing the position immediately after
               the written value.
  */
  proc buffer.copyin( it:buffer_iterator, value, out error:syserr):buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      //writeln("iterator on way in");
      //debug_print_qbuffer_iter(it._bufit_internal);
      var end = it;
      var tmp = value;
      var sz = numBytes(value.type);
      //writeln("iterator copied to end");
      //debug_print_qbuffer_iter(end._bufit_internal);

      //writeln("sz is ", sz);
      this.advance(end, sz);
      //debug_print_qbuffer_iter(end._bufit_internal);
      error = qbuffer_copyin(this._buf_internal, it._bufit_internal, end._bufit_internal, tmp, sz);
      ret = end;
    }
    return ret;
  }
  pragma "no doc"
  proc buffer.copyin( it:buffer_iterator, value):buffer_iterator {
    var err:syserr = ENOERR;
    var ret = this.copyin(it, value, err);
    if err then ioerror(err, "in buffer.copyin");
    return ret;
  }
}

/*
 * 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.
 */

pragma "no doc"
private module BufferInternals {
  use SysBasic;
  use Error;

  extern type qbytes_ptr_t;
  extern type qbuffer_ptr_t;
  extern type qbuffer_iter_t;
  extern const QBYTES_PTR_NULL:qbytes_ptr_t;
  extern const QBUFFER_PTR_NULL:qbuffer_ptr_t;

  extern proc qbytes_retain(qb:qbytes_ptr_t);
  extern proc qbytes_release(qb:qbytes_ptr_t);
  extern proc qbytes_len(qb:qbytes_ptr_t):int(64);
  extern proc qbytes_data(qb:qbytes_ptr_t):c_void_ptr;

  extern proc qbytes_create_iobuf(out ret:qbytes_ptr_t):syserr;
  extern proc qbytes_create_calloc(out ret:qbytes_ptr_t, len:int(64)):syserr;

  extern proc qbuffer_iter_null():qbuffer_iter_t;

  extern proc qbuffer_create(out buf:qbuffer_ptr_t):syserr;
  extern proc qbuffer_retain(buf:qbuffer_ptr_t);
  extern proc qbuffer_release(buf:qbuffer_ptr_t);

  extern proc qbuffer_append(buf:qbuffer_ptr_t, bytes:qbytes_ptr_t, skip_bytes:int(64), len_bytes:int(64)):syserr;
  extern proc qbuffer_append_buffer(buf:qbuffer_ptr_t, src:qbuffer_ptr_t, src_start:qbuffer_iter_t, src_end:qbuffer_iter_t):syserr;
  extern proc qbuffer_prepend(buf:qbuffer_ptr_t, bytes:qbytes_ptr_t, skip_bytes:int(64), len_bytes:int(64)):syserr;
  extern proc qbuffer_flatten(buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t, out bytes_out):syserr;
  extern proc qbuffer_copyout(buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t, ref x, size):syserr;
  extern proc qbuffer_copyin(buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t, ref x, size):syserr;

  extern proc qbuffer_begin(buf:qbuffer_ptr_t):qbuffer_iter_t;
  extern proc qbuffer_end(buf:qbuffer_ptr_t):qbuffer_iter_t;
  extern proc qbuffer_iter_next_part(buf:qbuffer_ptr_t, ref it:qbuffer_iter_t);
  extern proc qbuffer_iter_prev_part(buf:qbuffer_ptr_t, ref it:qbuffer_iter_t);
  extern proc qbuffer_iter_advance(buf:qbuffer_ptr_t, ref it:qbuffer_iter_t, amt:int(64));

  extern proc qbuffer_iter_get(it: qbuffer_iter_t, end:qbuffer_iter_t,
                                out bytes_out:qbytes_ptr_t,
                                out skip_out:int(64),
                                out len_out:int(64));
  extern proc qbuffer_iter_num_bytes(start:qbuffer_iter_t, end:qbuffer_iter_t):int(64);

  extern proc qbuffer_len(buf:qbuffer_ptr_t):int(64);

  extern proc debug_print_qbuffer_iter(/*const*/ ref it:qbuffer_iter_t);

  extern proc qbuffer_start_offset(buf:qbuffer_ptr_t):int(64);
  extern proc qbuffer_end_offset(buf:qbuffer_ptr_t):int(64);
  extern proc qbuffer_reposition(buf:qbuffer_ptr_t, new_offset_start:int(64));

  extern proc bulk_get_bytes(src_locale:int, src_addr:qbytes_ptr_t):qbytes_ptr_t;

  extern proc bulk_put_buffer(dst_locale:int, dst_addr:c_void_ptr, dst_len:int(64), buf:qbuffer_ptr_t, start:qbuffer_iter_t, end:qbuffer_iter_t):syserr;

}

/* Support for buffers - regions of memory without a particular interpretation.

   This module provides :record:`bytes` and :record:`buffer` types which
   can be used to manage memory regions.

   A :record:`bytes` is a contiguous memory region - really a data structure
   containing a pointer, length, and also the information necessary to free the
   memory region when it is no longer used.

   A :record:`buffer` consists of a sequence views into :record:`bytes`
   objects. A :record:`bytes` object might be shared by several
   :record:`buffer` objects.

   These types should be safe to use in a multi-locale context. These types
   should free their memory after the last user of that memory goes out of
   scope. They are currently reference counted but that may not always be the
   case.

 */
module Buffers {
  use BufferInternals; // TODO -- non-exporting use

  // Now define the Chapel types using the originals..

  /* This type represents a contiguous sequence of bytes.
     This sequence of bytes is represented with a C pointer and length and is
     currently reference counted.  Note that this record contains private
     fields in addition to the home field.

   */
  pragma "ignore noinit"
  record bytes {
    /* The home locale storing the data */
    var home: locale;
    pragma "no doc"
    var _bytes_internal:qbytes_ptr_t = QBYTES_PTR_NULL;
  }

  // bytes methods.
  /* Construct an empty bytes object */
  proc bytes.bytes() {
    this._bytes_internal = QBYTES_PTR_NULL;
    this.home = here;
  }
  /*

     defaultOf appears to be unnecessary since the initializer
     in the class takes care of it.

  inline proc _defaultOf(type t): t where t == bytes {
    var ret:bytes = noinit;
    ret.home = here;
    ret._bytes_internal = QBYTES_PTR_NULL;
    return ret;
  }*/

  /* Construct a bytes object by allocating zero-filled memory.

     :arg len: the number of bytes to allocate
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
   */
  proc bytes.bytes(len:int(64), out error:syserr) {
    error = qbytes_create_calloc(this._bytes_internal, len);
    // The buffer is "retained" internally on creation, but only on success.
    this.home = here;
  }
  pragma "no doc"
  proc bytes.bytes(len:int(64)) {
    var error:syserr = ENOERR;
    error = qbytes_create_calloc(this._bytes_internal, len);
    if error then ioerror(error, "in bytes constructor");
    // The buffer is retained internally on construction, but only on success.
    this.home = here;
  }

  pragma "no doc"
  private proc create_iobuf(out error:syserr):bytes {
    var ret:bytes;
    error = qbytes_create_iobuf(ret._bytes_internal);
    // The buffer is "retained" internally on creation, but only on success.
    ret.home = here;
    return ret;
  }
  pragma "no doc"
  private proc create_iobuf():bytes {
    var err:syserr = ENOERR;
    var ret = create_iobuf(err);
    if err then ioerror(err, "in create_iobuf");
    return ret;
  }

  // TODO -- shouldn't have to write this this way!
  pragma "init copy fn"
  pragma "no doc"
  proc chpl__initCopy(x: bytes) {
    if x.home == here {
      qbytes_retain(x._bytes_internal);
      return x;
    } else {
      var ret:bytes;
      ret.home = here;
      // The initial ref count is 1, so no need to call qbytes_retain here.
      ret._bytes_internal = bulk_get_bytes(x.home.id, x._bytes_internal);
      return ret;
    }
  }

  pragma "no doc"
  proc =(ref ret:bytes, x:bytes) {
    // retain -- release
    if( x.home == here ) {
      on x.home {
        qbytes_retain(x._bytes_internal);
      }

      on ret.home {
        qbytes_release(ret._bytes_internal);
      }

      ret.home = x.home;
      ret._bytes_internal = x._bytes_internal;
    } else {
      on ret.home {
        qbytes_release(ret._bytes_internal);
      }
      ret.home = here;
      ret._bytes_internal = bulk_get_bytes(x.home.id, x._bytes_internal);
      // On return from bulk_get_bytes, the ref count in ret._bytes_internal
      // should be 1.
      // Note that the error case is not handled (bulk_get_bytes must succeed).
    }
  }

  pragma "no doc"
  proc bytes.~bytes() {
    on this.home {
      qbytes_release(this._bytes_internal);
      this._bytes_internal = QBYTES_PTR_NULL;
    }
  }

  /* :returns: the number of bytes stored in a :record:`bytes` object */
  proc bytes.len:int(64) {
    var ret:int(64);
    on home {
     ret = qbytes_len(this._bytes_internal);
    }
    return ret;
  }

  /* This type represents a particular location with a buffer.
     Use buffer methods like :proc:`buffer.start` and :proc:`buffer.advance` to
     create and manipulate :record:`buffer_iterator` s.  Note that this record
     contains private fields in addition to the home field.

   */
  pragma "ignore noinit"
  record buffer_iterator {
    /* The home locale storing the data */
    var home: locale;
    pragma "no doc"
    var _bufit_internal:qbuffer_iter_t = qbuffer_iter_null();
  }

  /* Create a :record:`buffer_iterator` that points nowhere */
  pragma "no doc"
  proc buffer_iterator.buffer_iterator() {
    this.home = here;
    this._bufit_internal = qbuffer_iter_null();
  }

  /* A region within a buffer (indicated by two :record:`buffer_iterator` s ) */
  pragma "ignore noinit"
  record buffer_range {
    var start:buffer_iterator;
    var end:buffer_iterator;
  }

  /* :returns: the number of bytes stored in a :record:`buffer_range` */
  proc buffer_range.len:int(64) {
    var ret:int(64);
    on this.start.home {
      ret = qbuffer_iter_num_bytes(this.start._bufit_internal,
                                   this.end._bufit_internal);
    }
    return ret;
  }

  // FUTURE -- we could create const buffers
  // with a boolean type param

  /* A buffer which can contain multiple memory regions
     (that is, multiple regions of :record:`bytes` objects).  Note that this
     record contains private fields in addition to the home field.

   */
  pragma "ignore noinit"
  record buffer {
    /* The home locale storing the data */
    var home:locale;
    pragma "no doc"
    var _buf_internal:qbuffer_ptr_t = QBUFFER_PTR_NULL;
  }

  /* buffer methods. */

  /* Create an empty buffer.

     :arg error: (optional) capture an error that was encountered instead of
                  halting on error
   */
  proc buffer.buffer(out error:syserr) {
    this.home = here;
    error = qbuffer_create(this._buf_internal);
  }
  pragma "no doc"
  proc buffer.buffer() {
    var error:syserr = ENOERR;
    this.home = here;
    error = qbuffer_create(this._buf_internal);
    if error then ioerror(error, "in buffer constructor");
  }
  /*
     defaultOf appears to be unnecessary since the initializer
     in the class takes care of it.

  inline proc _defaultOf(type t): t where t == buffer {
    var ret:buffer = noinit;
    ret.home = here;
    ret._buf_internal = QBUFFER_PTR_NULL;
    return ret;
  }*/

  /* Flatten a buffer. Create a new :record:`bytes` object and copy
     the buffer into it. This function should work even if buffer is
     remote.

     :arg range: the region of the buffer to copy, for example buffer.all()
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
     :returns: a newly constructed bytes object on the current locale
   */
  proc buffer.flatten(range:buffer_range, out error:syserr) {
  //proc buffer.flatten(range:buffer_range, out error:syserr):bytes {
    var ret:bytes = new bytes();
    if this.home == here {
      error = qbuffer_flatten(this._buf_internal, range.start._bufit_internal, range.end._bufit_internal, ret._bytes_internal);
    } else {
      var dst_locale = here;
      var dst_len:int(64) = range.len;
      ret = new bytes(dst_len);
      var dst_addr = qbytes_data(ret._bytes_internal);
      on this.home {
        // Copy the buffer to the bytes...
        error = bulk_put_buffer(dst_locale.id, dst_addr, dst_len,
                                this._buf_internal,
                                range.start._bufit_internal,
                                range.end._bufit_internal);
      }
    }
    return ret;
  }

  pragma "no doc"
  proc buffer.flatten(range:buffer_range) {
  //proc buffer.flatten(range:buffer_range):bytes {
    var error:syserr = ENOERR;
    var ret:bytes = new bytes();
    ret = this.flatten(range,error);
    if error then ioerror(error, "in buffer.flatten");
    return ret;
  }

  // TODO -- shouldn't have to write this this way!
  pragma "init copy fn"
  pragma "no doc"
  proc chpl__initCopy(x: buffer) {
    if x.home == here {
      qbuffer_retain(x._buf_internal);
      return x;
    } else {
      var ret:buffer;
      var start_offset:int(64);
      var end_offset:int(64);

      on x.home {
        start_offset = qbuffer_start_offset(x._buf_internal);
        end_offset = qbuffer_end_offset(x._buf_internal);
      }

      var b = new bytes(end_offset - start_offset);
      var len = qbytes_len(b._bytes_internal);
      var ptr = qbytes_data(b._bytes_internal);

      // Now create a local buffer with the right data
      // in it starting at the right position.
      qbuffer_reposition(ret._buf_internal, start_offset);

      var there_uid = here.id;

      on x.home {
        var err:syserr = ENOERR;
        err = bulk_put_buffer(there_uid, ptr, len, x._buf_internal,
                              qbuffer_begin(x._buf_internal),
                              qbuffer_end(x._buf_internal));
        if err then ioerror(err, "in buffer init copy");
      }

      ret.append(b);
      return ret;
    }
  }

  pragma "no doc"
  proc =(ref ret:buffer, x:buffer) {
    ret.home = here;
    // retain -- release
    if( x.home == ret.home ) {
      on x.home {
        qbuffer_retain(x._buf_internal);
      }

      on ret.home {
        qbuffer_release(ret._buf_internal);
      }

      ret.home = x.home;
      ret._buf_internal = x._buf_internal;
    } else {
      on ret.home {
        qbuffer_release(ret._buf_internal);
      }

      var start_offset:int(64);
      var end_offset:int(64);

      on x.home {
        start_offset = qbuffer_start_offset(x._buf_internal);
        end_offset = qbuffer_end_offset(x._buf_internal);
      }

      var b = new bytes(end_offset - start_offset);
      var len = qbytes_len(b._bytes_internal);
      var ptr = qbytes_data(b._bytes_internal);

      // Now create a local buffer with the right data
      // in it starting at the right position.
      qbuffer_reposition(ret._buf_internal, start_offset);

      var there_uid = here.id;

      on x.home {
        var err:syserr = ENOERR;
        err = bulk_put_buffer(there_uid, ptr, len, x._buf_internal,
                              qbuffer_begin(x._buf_internal),
                              qbuffer_end(x._buf_internal));
        if err then ioerror(err, "in buffer assignment");
      }

      ret.append(b);
    }
  }

  pragma "no doc"
  proc buffer.~buffer() {
    on this.home {
      qbuffer_release(this._buf_internal);
      this._buf_internal = QBUFFER_PTR_NULL;
    }
  }

  /* :returns: the number of bytes stored in a :record:`buffer` object */
  proc buffer.len {
    var ret:int(64);
    on this.home {
      ret = qbuffer_len(this._buf_internal);
    }
    return ret;
  }

  /* Append a :record:`bytes` object to a :record:`buffer`.
     This function might store the passed bytes object by reference instead of
     copying it. The current implementation will always do so and will always
     increase the reference count of the bytes object.  The version of this
     function called without the error argument will halt if an error is
     encountered.

     :arg b: the :record:`bytes` object to append
     :arg skip_bytes: how many bytes at the front of b to skip
     :arg len_bytes: how many bytes to append to the buffer
     :arg error: (optional) capture an error that was encountered instead of
           halting on error
  */
  proc buffer.append(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len, out error:syserr) {
    on this.home {
      error = qbuffer_append(this._buf_internal, b._bytes_internal, skip_bytes, len_bytes);
    }
  }

  pragma "no doc"
  proc buffer.append(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len) {
    var err:syserr = ENOERR;
    this.append(b, skip_bytes, len_bytes, err);
    if err then ioerror(err, "in buffer.append");
  }

  /* Append a :record:`buffer` object to a :record:`buffer`.
     This function might store a pointers to the bytes objects contained in
     `buf` instead of copying them. If that happens, the current implementation
     will increase their reference counts.  The version of this function called
     without the error argument will halt if an error is encountered.

     :arg buf: the :record:`buffer` object to append
     :arg part: a :record:`buffer_range` indicating which section of the
                buffer to copy. Defaults to all of the buffer.
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
   */
  proc buffer.append(buf:buffer, part:buffer_range = buf.all(), out error:syserr) {
    on this.home {
      error = qbuffer_append_buffer(this._buf_internal, buf._buf_internal, part.start._bufit_internal, part.end._bufit_internal);
    }
  }
  pragma "no doc"
  proc buffer.append(buf:buffer, part:buffer_range = buf.all()) {
    var err:syserr = ENOERR;
    this.append(buf, part, err);
    if err then ioerror(err, "in buffer.append");
  }

  /* Prepend a :record:`bytes` object to a :record:`buffer`.
     This function might store the passed bytes object by reference instead of
     copying it. The current implementation will always do so and will always
     increase the reference count of the bytes object.  The version of this
     function called without the error argument will halt if an error is
     encountered.

     :arg b: the :record:`bytes` object to prepend
     :arg skip_bytes: how many bytes at the front of b to skip
     :arg len_bytes: how many bytes to append to the buffer
     :arg error: (optional) capture an error that was encountered instead of
           halting on error
  */
  proc buffer.prepend(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len, out error:syserr) {
    on this.home {
      qbuffer_prepend(this._buf_internal, b._bytes_internal, skip_bytes, len_bytes);
    }
  }
  pragma "no doc"
  proc buffer.prepend(b:bytes, skip_bytes:int(64) = 0, len_bytes:int(64) = b.len) {
    var err:syserr = ENOERR;
    this.prepend(b, skip_bytes, len_bytes, err);
    if err then ioerror(err, "in buffer.prepend");
  }

  /* :return: a :record:`buffer_iterator` to the start of a buffer */
  proc buffer.start():buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      ret._bufit_internal = qbuffer_begin(this._buf_internal);
    }
    return ret;
  }
  /* :return: a :record:`buffer_iterator` to the end of a buffer */
  proc buffer.end():buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      ret._bufit_internal = qbuffer_end(this._buf_internal);
    }
    return ret;
  }
  /* :return: a :record:`buffer_range` for the entirety of a buffer */
  proc buffer.all():buffer_range {
    return new buffer_range(this.start(), this.end());
  }

  /* Advance a :record:`buffer_iterator` to the next contigous
     memory region stored therein

     :arg it: the buffer iterator to advance
   */
  proc buffer.next_part(ref it:buffer_iterator) {
    on this.home {
      var x = it;
      qbuffer_iter_next_part(this._buf_internal, x._bufit_internal);
      it = x;
    }
  }

  /* Advance a :record:`buffer_iterator` to the previous contigous
     memory region stored therein

     :arg it: the buffer iterator to advance
   */
  proc buffer.prev_part(ref it:buffer_iterator) {
    on this.home {
      var x = it;
      qbuffer_iter_prev_part(this._buf_internal, x._bufit_internal);
      it = x;
    }
  }

  /* Advance a :record:`buffer_iterator` by a particular number
     of bytes.

     :arg it: the buffer iterator to advance
     :arg amount: the number of bytes to advance
   */
  proc buffer.advance(ref it:buffer_iterator, amount:int(64)) {
    on this.home {
      var x = it;
      qbuffer_iter_advance(this._buf_internal, x._bufit_internal, amount);
      it = x;
    }
  }

  pragma "no doc"
  proc buffer_iterator.debug_print()
  {
    on this.home {
      debug_print_qbuffer_iter(this._bufit_internal);
    }
  }

  /* methods to read/write basic types. */

  /* Read a basic type from a buffer. This method reads the value
     by copying from memory - so it reads a binary value in native endianness.

     :arg it: a :record:`buffer_iterator` where reading will start
     :arg value: a basic type (integral or floating point value)
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
     :returns: a buffer iterator storing the position immediately after
               the read value.
  */
  proc buffer.copyout(it:buffer_iterator, out value, out error:syserr):buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      var end:buffer_iterator = it;
      var tmp:value.type;
      var sz = numBytes(value.type);
      this.advance(end, sz);
      error = qbuffer_copyout(this._buf_internal, it._bufit_internal, end._bufit_internal, tmp, sz);
      value = tmp;
      ret = end;
    }
    return ret;
  }
  pragma "no doc"
  proc buffer.copyout(it:buffer_iterator, out value):buffer_iterator {
    var err:syserr = ENOERR;
    var ret = this.copyout(it, value, err);
    if err then ioerror(err, "in buffer.copyout");
    return ret;
  }

  /* Write a basic type to a buffer. This method writes the value
     by copying to memory - so it reads a binary value in native endianness.

     :arg it: a :record:`buffer_iterator` where reading will start
     :arg value: a basic type (integral or floating point value)
     :arg error: (optional) capture an error that was encountered instead of
                 halting on error
     :returns: a buffer iterator storing the position immediately after
               the written value.
  */
  proc buffer.copyin( it:buffer_iterator, value, out error:syserr):buffer_iterator {
    var ret:buffer_iterator;
    ret.home = this.home;
    on this.home {
      //writeln("iterator on way in");
      //debug_print_qbuffer_iter(it._bufit_internal);
      var end = it;
      var tmp = value;
      var sz = numBytes(value.type);
      //writeln("iterator copied to end");
      //debug_print_qbuffer_iter(end._bufit_internal);

      //writeln("sz is ", sz);
      this.advance(end, sz);
      //debug_print_qbuffer_iter(end._bufit_internal);
      error = qbuffer_copyin(this._buf_internal, it._bufit_internal, end._bufit_internal, tmp, sz);
      ret = end;
    }
    return ret;
  }
  pragma "no doc"
  proc buffer.copyin( it:buffer_iterator, value):buffer_iterator {
    var err:syserr = ENOERR;
    var ret = this.copyin(it, value, err);
    if err then ioerror(err, "in buffer.copyin");
    return ret;
  }
}


Did a few housecleaning steps to MG, but nothing
serious.

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

// This is a port of the NPB MG benchmark (version 3.0) written in
// Chapel by Brad Chamberlain.  As with any port, a number of
// structural and stylistic choices were made; in some cases, the
// original identifiers were preserved, either for clarity, or because
// the author couldn't come up with better names for them.  In other
// cases, more verbose names were used in hopes of improving
// readability.

// In general, types are elided from declarations unless they are
// believed to be ambiguous when reading the source, or greatly
// improve the code's readability.  This is obviously a judgement
// call.

// It should be noted that as of this writing (Nov 2005), our
// prototype Chapel implementation is not yet far enough along to
// handle this code.  This therefore represents the target that we are
// striving for rather than something that works today.

// an enumerated type indicating the standard NPB problem classes:
//   S: small
//   W: workstation
//   A-D: increasingly large "production" runs
//   O: other

enum classVals {S, W, A, B, C, D};

// arrays of values used to characterize each problem class:
//   probsize: the problem size per dimension of the finest grid
//   iterations: the number of iterations to run
//   checksum: the expected result, if the default characteristics are used

const Class: domain(classVals);
const probSizes:  [Class] int = (32, 64, 256, 256, 512, 1024),
      iterations: [Class] int = (4, 40, 4, 20, 20, 50),
      checksums:  [Class] real  = (0.0000530770700573,
                                   0.00000000000000000250391406439,
                                   0.000002433365309,
                                   0.00000180056440132,
                                   0.000000570674826298,
                                   0.000000000158327506043);

// this is the problem class that will be used for the run.  Since
// it's a configuration constant, it may be set on the executable's
// command-line.  The default is to run class S.

config const probClass: classVals = S;

// the problem size.  By default, the problem size will be the same in
// each dimension (n), as specified by the probClass variable.  This
// default may be overridden for all dimensions by specifying a new
// value for n on the executable's command-line or by overriding any
// of the dimensional problem sizes individually (nx, ny, nz).

config const n = probSize(probClass);
config const nx = n,
             ny = n,
             nz = n;

// the number of levels in the multigrid hierarchy.  This is the log_2
// of the smallest dimension.  The user may specify a different number
// of levels on the executable's command-line.

config const numLevels = log2(min(nx,ny,nz)),

// the number of iterations to run.  Again, determined by the problem
// class by default, but overridable.

             numIters = iterations(probClass),

// the checksum for the benchmark, again overridable.

             verifyValue = checksum(probClass);

// A flag specifying whether or not a "warmup" round should be run, a
// single iteration should be run before the timing is started to warm
// up the cache (as in the NPB version).

config const warmup = true;

// a small, 4-element array type that we'll use to store the 4
// coefficient values that define the weight values used in each of
// the 27-point stencils.

type coeff = [0..3] real;

// the domains that define the multigrid data structure
//   Levels: a 1D arithmetic domain that defines the levels of the
//     hierarchy
//   Base: the base 3D arithmetic domain that defines the problem
//     size; this domain is distributed by blocking the three
//     dimensions across the entire locale set using the standard
//     "Block" distribution.

//   Hier: an array of 3D arithmetic domains, one for each level of
//     the hierarchy; each domain is a subdomain of "Base", and each is
//     strided by twice as much as the previous.  A negative stride is
//     used to start counting from the top of each dimension.  Thus, for
//     a 256^3 Base domain, the last domain would contain the single
//     index (256, 256, 256) rather than (1, 1, 1).  This is more
//     cosmetic than anything, and done to match the NAS version.
//   Stencil: a 3x3x3 domain used to describe the 27-point stencils
//     used in this computation

const Levels: domain(1) = [1..numLevels];
const Base: domain(3) distributed(Block(3)) = [1..nx, 1..ny, 1..nz];
const Hier: [lvl in Levels] domain(Base) = Base by -2**(lvl-1);
const Stencil: domain(3) = [-1..1, -1..1, -1..1];

// ENTRY POINT:

def main() {
  // two timer variables that are used to time the initialization and
  // the benchmark time, respectively
  var initTimer, benchTimer: timer;

  // Here's the timed initialization portion (indentation for emphasis)
  initTimer.start();

    // These are our main arrays; V is the input array; U and R are
    // the hierarchical arrays used in the computation
    var V: [Base] real;
    var U, R: [lvl in Levels] [Hier(lvl)] real;

    // Initialize everything
    initializeMG();
  initTimer.stop();

  // Here's the actual benchmark run; rnm2 is our checksum value
  benchTimer.start();
    var rnm2 = computeMG(V, U, R);
  benchTimer.stop();

  // print out the results of the run and timings
  printResults(rnm2, initTimer.read(), benchTimer.read());
}

// TOP-LEVEL FUNCTIONS (called by main())

// initializeMG() gets things set up

def initializeMG(V, U, R) {
  // print some standard heading information
  writeln(" NAS Parallel Benchmarks 3.0 (Chapel version) - MG Benchmark");
  writeln(" Size: ", nx, "x", ny, "x", nz);
  writeln(" Iterations: ", numIters);
  writeln();

  // warm up the cache if the user has requested it
  if (warmup) {
    initArrays(V, U, R);
    runOneIteration(V, U, R);
  }

  // initialize the arrays
  initArrays(V, U, R);
}

// computeMG() runs the computation, returning the computed checksum

def computeMG(V, U, R) {
  resid(R(1), V, U(1));
  norm2u3(R(1));
  for it in 1..numIters {
    runOneIteration(V, U, R);
  }
  var (rnm2, _) = norm2u3(R(1));

  return rnm2;
}

// printResults() performs the checksum verification and prints out
// some general information about the run

def printResults(rnm2, inittime, runtime) {
  var verified = false;

  if (verifyValue != 0.0) {
    const epsilon = 0.00000001,
          delta = rnm2 - verifyValue;

    if (abs(delta) <= epsilon) {
      writeln(" VERIFICATION SUCCESSFUL ");
      writeln(" L2 Norm is ", rnm2);
      writeln(" Error is ", delta);
      verified = true;
    } else {
      writeln(" VERIFICATION FAILED");
      writeln(" L2 norm is ", rnm2);
      writeln(" The correct L2 Norm is ", verifyValue);
    }
  } else {
    writeln(" Problem size unknown");
    writeln(" NO VERIFICATION PERFORMED");
  }
  writeln(" MG Benchmark Completed.");
  writeln(" Size = ", nx, "x", ny, "x", nz);
  writeln(" Iterations = ", numIters);
  writeln(" Time in seconds = ", runtime);
  write("Verification = ");
  if (verified) {
    writeln("   SUCCESSFUL");
  } else {
    writeln(" UNSUCCESSFUL");
  }
  writeln(" Version = 3.0");
}

// running one iteration means running a round of mg3P followed by
// one residual

def runOneIteration(V, U, R) {
  mg3P(V, U, R);
  resid(R(1), V, U(1));
}

// mg3P() contains all the work needed to run a single multigrid cycle

def mg3P(V, U, R) {
  // project up the hierarchy
  for lvl in 2..numLevels {
    rprj3(R(lvl), R(lvl-1));
  }

  // compute at the top of the hierarchy
  U(numLevels) = 0.0;
  psinv(U(numLevels), R(numLevels));

  // interpolate down the hierarchy
  for lvl in 2..numLevels-1 by -1 {
    U(lvl) = 0.0;
    interp(U(lvl), U(lvl+1));
    resid(R(lvl), R(lvl), U(lvl));
    psinv(U(lvl), R(lvl));
  }

  // compute at the bottom of the hierarchy
  interp(U(1), U(2));
  resid(R(1), V, U(1));
  psinv(U(1), R(1));
}

// STENCIL ROUTINES: These are the guts of the multigrid computation;
// each applies a 27-point (3x3x3) stencil to an array to compute a
// modification to another array. resid() and psinv() conserve the
// problem size; rprj3() projects a finer array to a coarser one; and
// interp() does the opposite.  All four of these are reasonably
// similar, with some small differences.  I'll comment the first one
// well, and the others where they differ

// resid() applies a 27-point stencil to U, subtracts the result from
// V, and assigns to R.

def resid(R, V, U) {
  // these are the weight values for the stencil
  const a: coeff = (-8.0/3.0, 0.0, 1.0/6.0, 1.0/12.0);

  // this sets up the 3x3x3 array of weights; the idea is that the
  // stencil element should get the 0th coefficient value; those
  // positions that are one coordinate off should get the 1st; two
  // coordinates off should get the 2nd and so on.  I wrote this by
  // doing some bool tests, and adding the results together,
  // relying on the implicit conversion of bools to ints.  My
  // hope is that by declaring these to be const, the compiler will do
  // stencil optimizations on them as defined in Deitz's SC2001 paper.

  const a3d: [(i,j,k) in Stencil] real = a((i!=0) + (j!=0) + (k!=0));

  // grab the U array's domain and stride information, for
  // convenience.  The stride is returned as a triple of ints

  const UD = U.Domain,
        Ustr = U.stride;

  // The square brackets here are a forall expressions, used to
  // iterate over a domain, naming the index if desired.  So this
  // iterates over all of the indices in U's domain (calling them
  // ijk), performing for each index a sum reduction over the indices
  // in the 27-point Stencil domain.  The elements that are reduced
  // are the product of the corresponding stencil weight (a3d(off))
  // and the U array element corresponding to the original position
  // (U(ijk)), offset by the stencil index multiplied by the offset
  // elementwise (Ustr*off).

  // This array of values is subtracted from the V array and assigned
  // to the R array.

  R = V - [ijk in UD] sum reduce [off in Stencil]
                                   (a3d(off) * U(ijk + Ustr*off));
}

// psinv() is almost identical to psinv().  It computes the stencil on
// the R array, accumulating the result into the U array.  The main
// difference is that its weight array varies depending on the problem
// size for some reason (maybe there was a bug in the early version of
// the benchmark that has been preserved for backwards compatability?)

def psinv(U, R) {
  const c: coeff = initCValues();
  const c3d: [(i,j,k) in Stencil] real = c((i!=0) + (j!=0) + (k!=0));

  const RD = R.Domain,
        Rstr = R.stride;

  U += [ijk in RD] sum reduce [off in Stencil] (c3d(off) * R(ijk + Rstr*off));
}

// rprj3() is very similar except that the R array is finer than the S
// array, so the main loop iterates over the domain of S rather than
// R.

def rprj3(S, R) {
  const w: coeff = (0.5, 0.25, 0.125, 0.0625);
  const w3d: [(i,j,k) in Stencil] real = w((i!=0) + (j!=0) + (k!=0));

  const SD = S.Domain,
        Rstr = R.stride;

  S = [ijk in SD] sum reduce [off in Stencil] (w3d(off) * R(ijk + Rstr*off));
}

// the interp() routines is slightly more complicated because for each
// point being interpolated, there are one of eight possible
// topologies for the points being sampled (ranging from being
// directly aligned, in which case the point is simply used, to being
// completely unaligned in which case the 8 neighboring points are
// averaged).

// I represented this here by creating a domain IDom that represents
// the 8 topologies (numbered -1..0 for each dimension, since we've
// aligned the coarser domains with the higher indices); IStn is then
// an array of stencil domains, like Stencil above, except that each
// offset range is either -1..0 or 0..0, depending on the topology.
// The "w" array stores the weights for each of the stencils, which is
// simply 1.0 dividedby the number of elements in the stencil (1 - 8).

// the computation proceeds very similarly to the previous stencils,
// except that we perform the eight topologies in parallel.  We also
// iterate over the coarser (S) array's domain, and apply offsets to
// the finer (R) array offsets in order to get the different
// topologies.

def interp(R, S) {
  const IDom: domain(3) = [-1..0, -1..0, -1..0];
  const IStn: [(i,j,k) in IDom] domain(3) = [i..0, j..0, k..0];
  const w: [ijk in IDom] real = 1.0 / IStn.numIndices();

  const SD = S.Domain(),
        Rstr = R.stride,
        Sstr = S.stride;

  forall ioff in IDom {
    [ijk in SD] R(ijk + Rstr*ioff)
                = w(ioff) * sum reduce [off in IStn(ioff)] S(ijk + Sstr*off);
  }
}

// norm2u3() computes approximate norms from an array using sum and
// max reductions.  It returns a 2-tuple of values (only one of which
// is used in this benchmark // the other is dropped on the floor at
// the callsite).

def norm2u3(R) {
  const rnm2 = sqrt((sum reduce R**2)/(nx*ny*nz)),
        rnmu = max reduce abs(R);

  return (rnm2, rnmu);
}

// initCValues() sets the c values for the psinv() stencil

def initCValues(probClass) {
  if (probClass == A || probClass == S || probClass == W) {
    return (-3.0/8.0,  1.0/32.0, -1.0/64.0, 0.0);
  } else {
    return (-3.0/17.0, 1.0/33.0, -1.0/61.0, 0.0);
  }
}

// SETUP ROUTINES:

// initArrays() initializes the arrays used in the computation.

def initArrays(V, U, R) {
  // conservatively, one might want to do "V=0.0; U=0.0; R=0.0; zran3(V);",
  // but the following is minimal:
  zran3(V);
  U(1) = 0.0;

  // this is the most speculative part of this implementation; I've
  // proposed support in Chapel for supporting boundary conditions by
  // associating funs with arrays that specify what should be
  // done when an array reference is out-of-bounds.  Some basic,
  // provided funs might be to declare an error, to return the
  // zero for the array's element type, to wrap the reference around
  // to the opposite side of the array, or to reflect the reference
  // back into the array.  Because all of the 27-point stencils in
  // this benchmark need to be applied using a 3D toroidal wrap, I
  // specify this here using a method on the array.

  // If such a mechanism was not provided, the user would have to
  // explicitly declare extra storage for the boundaries and copy the
  // boundary cells around, which is both tedious, and less likely to
  // result in the systolic communication pattern that the stencils
  // require.  I argue that a mechanism like the following is not that
  // much more onerous than doing the local per-processor boundaries
  // that the compiler and runtime already have to manage, and that it
  // therefore imposes similar requirements and challenges.  I can
  // expound more on this in person than is worth embedding here.

  V.setBoundaryCondition(wrap);
  forall lvl in Levels {
    U(lvl).setBoundaryCondition(wrap);
    R(lvl).setBoundaryCondition(wrap);
  }
}

// zran3() computes a random array of values, finds the 10 largest and
// smallest values, and sets these values to 1.0 and -1.0 respectively
// in our input array, setting the rest to 0.0.  It's for this reason
// that one mightwant to store the input array as a sparse array,
// though this implementation didn't take that approach (a sparse
// version could be written on request // if we did our job right,
// just the declaration of V would change)

def zran3(V) {
  const numCharges = 10;
  var pos, neg: [1..numCharges] index(Base);

  // compute the random array of values
  fillRandom(V, 314159265);

  // the following does a number of maxloc/minloc reductions, each of
  // which returns the location of the largest/smallest element as an
  // index of the array's domain.  This is an easy, though slow way to
  // perform this computation.  A better way would be to write a
  // user-defined reduction using Chapel's features for that, but this
  // requires more effort and it isn't being timed anyway.

  for i in 1..numCharges {
    pos(i) = maxloc reduce V;
    neg(i) = minloc reduce V;
    pos(i) = 0.5;  // (remove from consideration on next iteration)
    neg(i) = 0.5;
  }

  // zero out the array and assign the initial 1.0 and -1.0 values
  V = 0.0;
  for i in 1..numCharges {
    V(pos(i)) =  1.0;
    V(neg(i)) = -1.0;
  }
}

// This is a port of the NPB MG benchmark (version 3.0) written in
// Chapel by Brad Chamberlain.  As with any port, a number of
// structural and stylistic choices were made; in some cases, the
// original identifiers were preserved, either for clarity, or because
// the author couldn't come up with better names for them.  In other
// cases, more verbose names were used in hopes of improving
// readability.

// In general, types are elided from declarations unless they are
// believed to be ambiguous when reading the source, or greatly
// improve the code's readability.  This is obviously a judgement
// call.

// It should be noted that as of this writing (Nov 2005), our
// prototype Chapel implementation is not yet far enough along to
// handle this code.  This therefore represents the target that we are
// striving for rather than something that works today.

// an enumerated type indicating the standard NPB problem classes:
//   S: small
//   W: workstation
//   A-D: increasingly large "production" runs
//   O: other

enum classVals {S, W, A, B, C, D, O};

// arrays of values used to characterize each problem class:
//   probsize: the problem size per dimension of the finest grid
//   iterations: the number of iterations to run
//   checksum: the expected result, if the default characteristics are used

const probSize: [S..O] int = (32, 64, 256, 256, 512, 1024, 256),
      iterations: [S..O] int = (4, 40, 4, 20, 20, 50, 4),
      checksum: [S..O] real  = (0.0000530770700573,
                                  0.00000000000000000250391406439,
                                  0.000002433365309,
                                  0.00000180056440132,
                                  0.000000570674826298,
                                  0.000000000158327506043,
                                  0.0);

// this is the problem class that will be used for the run.  Since
// it's a configuration constant, it may be set on the executable's
// command-line.  The default is to run class S.

config const Class: classVals = S;

// the problem size.  By default, the problem size will be the same in
// each dimension (n), as specified by the Class variable.  This
// default may be overridden for all dimensions by specifying a new
// value for n on the executable's command-line or by overriding any
// of the dimensional problem sizes individually (nx, ny, nz).

config const n = probSize(Class);
config const nx = n,
             ny = n,
             nz = n;

// the number of levels in the multigrid hierarchy.  This is the log_2
// of the smallest dimension.  The user may specify a different number
// of levels on the executable's command-line.

config const numLevels = log2(min(nx,ny,nz)),

// the number of iterations to run.  Again, determined by the problem
// class by default, but overridable.

             numIters = iterations(Class),

// the checksum for the benchmark, again overridable.

             verifyValue = checksum(Class);

// A flag specifying whether or not a "warmup" round should be run, a
// single iteration should be run before the timing is started to warm
// up the cache (as in the NPB version).

config const warmup = true;

// a small, 4-element array type that we'll use to store the 4
// coefficient values that define the weight values used in each of
// the 27-point stencils.

type coeff = [0..3] real;

// the domains that define the multigrid data structure
//   Levels: a 1D arithmetic domain that defines the levels of the
//     hierarchy
//   Base: the base 3D arithmetic domain that defines the problem
//     size; this domain is distributed by blocking the three
//     dimensions across the entire locale set using the standard
//     "Block" distribution.

//   Hier: an array of 3D arithmetic domains, one for each level of
//     the hierarchy; each domain is a subdomain of "Base", and each is
//     strided by twice as much as the previous.  A negative stride is
//     used to start counting from the top of each dimension.  Thus, for
//     a 256^3 Base domain, the last domain would contain the single
//     index (256, 256, 256) rather than (1, 1, 1).  This is more
//     cosmetic than anything, and done to match the NAS version.
//   Stencil: a 3x3x3 domain used to describe the 27-point stencils
//     used in this computation

const Levels: domain(1) = [1..numLevels];
const Base: domain(3) distributed(Block(3)) = [1..nx, 1..ny, 1..nz];
const Hier: [lvl in Levels] domain(Base) = Base by -2**(lvl-1);
const Stencil: domain(3) = [-1..1, -1..1, -1..1];

// ENTRY POINT:

def main() {
  // two timer variables that are used to time the initialization and
  // the benchmark time, respectively
  var initTimer, benchTimer: timer;

  // Here's the timed initialization portion (indentation for emphasis)
  initTimer.start();

    // These are our main arrays; V is the input array; U and R are
    // the hierarchical arrays used in the computation
    var V: [Base] real;
    var U, R: [lvl in Levels] [Hier(lvl)] real;

    // Initialize everything
    initializeMG();
  initTimer.stop();

  // Here's the actual benchmark run; rnm2 is our checksum value
  benchTimer.start();
    var rnm2 = computeMG(V, U, R);
  benchTimer.stop();

  // print out the results of the run and timings
  printResults(rnm2, initTimer.read(), benchTimer.read());
}

// TOP-LEVEL FUNCTIONS (called by main())

// initializeMG() gets things set up

def initializeMG(V, U, R) {
  // print some standard heading information
  writeln(" NAS Parallel Benchmarks 3.0 (Chapel version) - MG Benchmark");
  writeln(" Size: ", nx, "x", ny, "x", nz);
  writeln(" Iterations: ", numIters);
  writeln();

  // warm up the cache if the user has requested it
  if (warmup) {
    initArrays(V, U, R);
    runOneIteration(V, U, R);
  }

  // initialize the arrays
  initArrays(V, U, R);
}

// computeMG() runs the computation, returning the computed checksum

def computeMG(V, U, R) {
  resid(R(1), V, U(1));
  norm2u3(R(1));
  for it in (1..numIters) {
    runOneIteration(V, U, R);
  }
  var (rnm2, _) = norm2u3(R(1));

  return rnm2;
}

// printResults() performs the checksum verification and prints out
// some general information about the run

def printResults(rnm2, inittime, runtime) {
  var verified = false;

  if (verifyValue != 0.0) {
    const epsilon = 0.00000001,
          delta = rnm2 - verifyValue;

    if (abs(delta) <= epsilon) {
      writeln(" VERIFICATION SUCCESSFUL ");
      writeln(" L2 Norm is ", rnm2);
      writeln(" Error is ", delta);
      verified = true;
    } else {
      writeln(" VERIFICATION FAILED");
      writeln(" L2 norm is ", rnm2);
      writeln(" The correct L2 Norm is ", verifyValue);
    }
  } else {
    writeln(" Problem size unknown");
    writeln(" NO VERIFICATION PERFORMED");
  }
  writeln(" MG Benchmark Completed.");
  writeln(" Size = ", nx, "x", ny, "x", nz);
  writeln(" Iterations = ", numIters);
  writeln(" Time in seconds = ", runtime);
  write("Verification = ");
  if (verified) {
    writeln("   SUCCESSFUL");
  } else {
    writeln(" UNSUCCESSFUL");
  }
  writeln(" Version = 3.0");
}

// running one iteration means running a round of mg3P followed by
// one residual

def runOneIteration(V, U, R) {
  mg3P(V, U, R);
  resid(R(1), V, U(1));
}

// mg3P() contains all the work needed to run a single multigrid cycle

def mg3P(V, U, R) {
  // project up the hierarchy
  for lvl in (2..numLevels) {
    rprj3(R(lvl), R(lvl-1));
  }

  // compute at the top of the hierarchy
  U(numLevels) = 0.0;
  psinv(U(numLevels), R(numLevels));

  // interpolate down the hierarchy
  for lvl in (2..numLevels-1) by -1 {
    U(lvl) = 0.0;
    interp(U(lvl), U(lvl+1));
    resid(R(lvl), R(lvl), U(lvl));
    psinv(U(lvl), R(lvl));
  }

  // compute at the bottom of the hierarchy
  interp(U(1), U(2));
  resid(R(1), V, U(1));
  psinv(U(1), R(1));
}

// STENCIL ROUTINES: These are the guts of the multigrid computation;
// each applies a 27-point (3x3x3) stencil to an array to compute a
// modification to another array. resid() and psinv() conserve the
// problem size; rprj3() projects a finer array to a coarser one; and
// interp() does the opposite.  All four of these are reasonably
// similar, with some small differences.  I'll comment the first one
// well, and the others where they differ

// resid() applies a 27-point stencil to U, subtracts the result from
// V, and assigns to R.

def resid(R, V, U) {
  // these are the weight values for the stencil
  const a: coeff = (-8.0/3.0, 0.0, 1.0/6.0, 1.0/12.0);

  // this sets up the 3x3x3 array of weights; the idea is that the
  // stencil element should get the 0th coefficient value; those
  // positions that are one coordinate off should get the 1st; two
  // coordinates off should get the 2nd and so on.  I wrote this by
  // doing some bool tests, and adding the results together,
  // relying on the implicit conversion of bools to ints.  My
  // hope is that by declaring these to be const, the compiler will do
  // stencil optimizations on them as defined in Deitz's SC2001 paper.

  const a3d: [(i,j,k) in Stencil] real = a((i!=0) + (j!=0) + (k!=0));

  // grab the U array's domain and stride information, for
  // convenience.  The stride is returned as a triple of ints

  const UD = U.Domain,
        Ustr = U.stride;

  // The square brackets here are a forall expressions, used to
  // iterate over a domain, naming the index if desired.  So this
  // iterates over all of the indices in U's domain (calling them
  // ijk), performing for each index a sum reduction over the indices
  // in the 27-point Stencil domain.  The elements that are reduced
  // are the product of the corresponding stencil weight (a3d(off))
  // and the U array element corresponding to the original position
  // (U(ijk)), offset by the stencil index multiplied by the offset
  // elementwise (Ustr*off).

  // This array of values is subtracted from the V array and assigned
  // to the R array.

  R = V - [ijk in UD] sum reduce [off in Stencil]
                                   (a3d(off) * U(ijk + Ustr*off));
}

// psinv() is almost identical to psinv().  It computes the stencil on
// the R array, accumulating the result into the U array.  The main
// difference is that its weight array varies depending on the problem
// size for some reason (maybe there was a bug in the early version of
// the benchmark that has been preserved for backwards compatability?)

def psinv(U, R) {
  const c: coeff = initCValues();
  const c3d: [(i,j,k) in Stencil] real = c((i!=0) + (j!=0) + (k!=0));

  const RD = R.Domain,
        Rstr = R.stride;

  U += [ijk in RD] sum reduce [off in Stencil] (c3d(off) * R(ijk + Rstr*off));
}

// rprj3() is very similar except that the R array is finer than the S
// array, so the main loop iterates over the domain of S rather than
// R.

def rprj3(S, R) {
  const w: coeff = (0.5, 0.25, 0.125, 0.0625);
  const w3d: [(i,j,k) in Stencil] real = w((i!=0) + (j!=0) + (k!=0));

  const SD = S.Domain,
        Rstr = R.stride;

  S = [ijk in SD] sum reduce [off in Stencil] (w3d(off) * R(ijk + Rstr*off));
}

// the interp() routines is slightly more complicated because for each
// point being interpolated, there are one of eight possible
// topologies for the points being sampled (ranging from being
// directly aligned, in which case the point is simply used, to being
// completely unaligned in which case the 8 neighboring points are
// averaged).

// I represented this here by creating a domain IDom that represents
// the 8 topologies (numbered -1..0 for each dimension, since we've
// aligned the coarser domains with the higher indices); IStn is then
// an array of stencil domains, like Stencil above, except that each
// offset range is either -1..0 or 0..0, depending on the topology.
// The "w" array stores the weights for each of the stencils, which is
// simply 1.0 dividedby the number of elements in the stencil (1 - 8).

// the computation proceeds very similarly to the previous stencils,
// except that we perform the eight topologies in parallel.  We also
// iterate over the coarser (S) array's domain, and apply offsets to
// the finer (R) array offsets in order to get the different
// topologies.

def interp(R, S) {
  const IDom: domain(3) = [-1..0, -1..0, -1..0];
  const IStn: [(i,j,k) in IDom] domain(3) = [i..0, j..0, k..0];
  const w: [ijk in IDom] real = 1.0 / IStn.numIndices();

  const SD = S.Domain(),
        Rstr = R.stride,
        Sstr = S.stride;

  forall ioff in IDom {
    [ijk in SD] R(ijk + Rstr*ioff)
                = w(ioff) * sum reduce [off in IStn(ioff)] S(ijk + Sstr*off);
  }
}

// norm2u3() computes approximate norms from an array using sum and
// max reductions.  It returns a 2-tuple of values (only one of which
// is used in this benchmark // the other is dropped on the floor at
// the callsite).

def norm2u3(R) {
  const rnm2 = sqrt((sum reduce R**2)/(nx*ny*nz)),
        rnmu = max reduce abs(R);

  return (rnm2, rnmu);
}

// initCValues() sets the c values for the psinv() stencil

def initCValues(Class) {
  if (Class == A || Class == S || Class == W) {
    return (-3.0/8.0,  1.0/32.0, -1.0/64.0, 0.0);
  } else {
    return (-3.0/17.0, 1.0/33.0, -1.0/61.0, 0.0);
  }
}

// SETUP ROUTINES:

// initArrays() initializes the arrays used in the computation.

def initArrays(V, U, R) {
  // conservatively, one might want to do "V=0.0; U=0.0; R=0.0; zran3(V);",
  // but the following is minimal:
  zran3(V);
  U(1) = 0.0;

  // this is the most speculative part of this implementation; I've
  // proposed support in Chapel for supporting boundary conditions by
  // associating funs with arrays that specify what should be
  // done when an array reference is out-of-bounds.  Some basic,
  // provided funs might be to declare an error, to return the
  // zero for the array's element type, to wrap the reference around
  // to the opposite side of the array, or to reflect the reference
  // back into the array.  Because all of the 27-point stencils in
  // this benchmark need to be applied using a 3D toroidal wrap, I
  // specify this here using a method on the array.

  // If such a mechanism was not provided, the user would have to
  // explicitly declare extra storage for the boundaries and copy the
  // boundary cells around, which is both tedious, and less likely to
  // result in the systolic communication pattern that the stencils
  // require.  I argue that a mechanism like the following is not that
  // much more onerous than doing the local per-processor boundaries
  // that the compiler and runtime already have to manage, and that it
  // therefore imposes similar requirements and challenges.  I can
  // expound more on this in person than is worth embedding here.

  V.setBoundaryCondition(wrap);
  forall lvl in Levels {
    U(lvl).setBoundaryCondition(wrap);
    R(lvl).setBoundaryCondition(wrap);
  }
}

// zran3() computes a random array of values, finds the 10 largest and
// smallest values, and sets these values to 1.0 and -1.0 respectively
// in our input array, setting the rest to 0.0.  It's for this reason
// that one mightwant to store the input array as a sparse array,
// though this implementation didn't take that approach (a sparse
// version could be written on request // if we did our job right,
// just the declaration of V would change)

def zran3(V) {
  const numCharges = 10;
  var pos, neg: [1..numCharges] index(Base);

  // compute the random array of values
  V = [i,j,k in Base] longRandlc((i-1) + (j-1)*nx + (k-1)*nx*ny);

  // the following does a number of maxloc/minloc reductions, each of
  // which returns the location of the largest/smallest element as an
  // index of the array's domain.  This is an easy, though slow way to
  // perform this computation.  A better way would be to write a
  // user-defined reduction using Chapel's features for that, but this
  // required lots more effort and it isn't being timed anyway.

  for i in (1..numCharges) {
    pos(i) = maxloc reduce V;
    neg(i) = minloc reduce V;
    pos(i) = 0.5;  // (remove from consideration on next iteration)
    neg(i) = 0.5;
  }

  // zero out the array and assign the initial 1.0 and -1.0 values
  V = 0.0;
  for i in (1..numCharges) {
    V(pos(i)) =  1.0;
    V(neg(i)) = -1.0;
  }
}

// "RANDOM" NUMBER STUFF: This is basically the same as what the NPB
// supplies in randdp.f; I don't claim to understand them in any
// depth.

def longRandlc(n) {
  const s = 314159265.0,
        arand = 5.0**13;

  var kk = n,
      t1 = s,
      t2 = arand,
      t3: real;

  while (kk != 0) {
    var ik = kk / 2;
    if (2*ik != kk) {
      t3 = randlc(t1, t2);
    }
    if (kk > 0) {
      t3 = randlc(t2, t2);
    }
    kk = ik;
  }

  return randlc(t1, arand);
}

def randlc(x, a) {
  const r23 = 0.5**23,
        t23 = 2.0**23,
        r46 = 0.5**46,
        t46 = 2.0**46;

  var t1, t2, t3, t4, a1, a2, x1, x2, y: real;

  t1 = r23 * x;
  a1 = t1:int;
  a2 = a - t23 * a1;

  t1 = r23 * x;
  x1 = t1:int;
  x2 = x - t23 * x1;
  t1 = a1 * x2 + a2 * x1;
  t2 = (r23 * t1):int;
  y = t1 - t23 * t2;
  t3 = t23 * y + a2 * x2;
  t3 = (r46 * t3):int;
  x = t3 - t46 * t4;

  return r46 * x;
}


wow, had to fix a crappy python bug and it made me sad...

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

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

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

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

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

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

    var bh: [hiddenRange] real;

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

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

    for i in 1..#epoch {

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

  }

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

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

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

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

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

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

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

    // Weight and bias matrices
    var wh: [{inputRange, hiddenRange}] real,
        bh: [{0..0, hiddenRange}] real,
        wout: [{hiddenRange, outputRange}] real,
        bout: [{0..0, outputRange}] real;

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

    for i in 1..#epoch {

      /* Forward propagation */
      var hidden_layer_input1 = dot(X,wh);
      writeln(hidden_layer_input1);
    }

  }

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

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


Fix capacityParSafe test to complete more reliably. [reviewed by diten]

This test fails occasionally in the nightly tests.  It sometimes runs out of
resources (memory or available threads), which is not the main point of the
test.  The main point is to create contention on the capacity element in an
associative domain descriptor by trying to update it under parallel execution.

The change involves replacing a 1000-wide coforall containing one update per
task with a 100-wide coforall containing 100 updates per task.  This presumably
increases the chances for contention by a factor of 10 without exceeding the
practical limit of a few hundred parallel tasks.

At the same time, the requested capacity was reduced to (i+j), to keep
the total amount of allocated memory within reasonable bounds.

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

// capacityParSafe.chpl
//
// Test that calls to set capacity work correctly in parallel.
//

// Safety under parallel execution must be requested explicitly (!).
var D : domain(string, parSafe=true);

// Try to get requests to interfere with each other by launching 100
// tasks and then having them all try to update the requested capacity
// 100 times.
coforall i in 1..100 {
  for j in 1..100 do
    D.requestCapacity(i+j);
}

writeln("SUCCESS");

// capacityParSafe.chpl
//
// Test that calls to set capacity work correctly in parallel.
//

// Safety under parallel execution must be requested explicitly (!).
var D : domain(string, parSafe=true);

coforall i in 1..1000 {
    D.requestCapacity(i);
}

writeln("SUCCESS");