Add Vass's suggestions to the array vector ops primer

Vass reviewed and made a few suggestions, so implemented them here.

/*
 * Vector Operations on 1D Arrays Primer
 */

//
// 1D rectangular arrays support several list-like or vector-like capabilities.
// These are supported on arrays which have a 1:1 correspondence with their
// domains. This restriction is because many of these operations modify the
// array's domain, which would unexpectedly modify other arrays if they shared
// domains.
//
// As currently implemented, every operation that results in a domain size
// change results in an array reallocation and so is O(n).  This is expected
// to improve in a future release.
//

//
// Declare a 1D array and initialize it to the values 1..5. The anonymous
// domain used in this declaration has a 1:1 relationship with the array.
//
var A: [1..5] int = [i in 1..5] i;

writeln("A was initialized to: ", A);

//
// Add an element to the front of the array and one to the back of the array.
// This will modify the array's domain to become {0..6} and add elements
// 0 and 6 to the array.
//
A.push_front(0);
A.push_back(6);

writeln("After adding to the front and back A is: ", A);

//
// Look at the first and last array elements without modifying the array
//
writeln("The first and last elements in A are: ", (A.head(), A.tail()));

//
// Remove the two elements added earlier with push_front and push_back.
// The domain will become {1..5} again.
//
A.pop_front();
A.pop_back();

writeln("After popping the two elements pushed previously A is: ", A);

//
// Insert values 10, 11 and 12 at indices 4, 3, and 2.  The domain will
// grow by 3 to become {1..8} and the array elements above the inserted
// positions will be shifted up.
//
A.insert(4, 10);
A.insert(3, 11);
A.insert(2, 12);
writeln("After inserting some new values, A is: ", A);

//
// The method 'find' searches the array for the argument.  It returns a tuple
// containing a bool and an index. If the returned bool is true, the argument
// was found at the returned index.  If the bool is false, the value was not
// found and the index is unspecified.
//
var (found, idx) = A.find(10);
if found then
  writeln("Found 10 at index: ", idx);
else
  writeln("Didn't find 10");

(found, idx) = A.find(7);
if found then
  writeln("Found 7 at index: ", idx);
else
  writeln("Didn't find 7");

//
// A few other useful methods are available.
// To demo them, add a few more 5s to the array
//
A.push_front(5);
A.push_back(5);

//
// Count how many times an element is in the array
//
writeln("The value 5 is in A ", A.count(5), " times.");

//
// Reverse the elements in the array
//
writeln("Before calling reverse A is: ", A);
A.reverse();
writeln("After calling reverse A is: ", A);

//
// Array elements can be removed one at a time by specifying an
// index to remove.  The elements above the removed one will be
// shifted down.
//
A.remove(3);
writeln("After first remove A is: ", A);

//
// A range of indices can also be removed
//
A.remove(4..6);
writeln("After second remove A is: ", A);

//
// A starting index and a count also work
//
A.remove(2, 2);
writeln("After third remove A is: ", A);

//
// The array is still a normal 1D Chapel array and supports regular array
// operations such as parallel iteration:
//
forall i in A.domain {
  A[i] += 1;
}
writeln("After adding 1 to all elements A is: ", A);

//
// Or reductions:
//
writeln("The sum of elements in A is ", + reduce A);

//
// The clear method will empty the array completely.  If the domain's low
// bound was 'low' this sets the domain to {low..low-1}
//
A.clear();
writeln("After clearing, A is: ", A, " - with ", A.size, " elements");

/*
 * Vector Operations on 1D Arrays Primer
 */

//
// 1D rectangular arrays support several list-like or vector-like capabilities.
// These are supported on arrays which have a 1:1 correspondence with their
// domains. This restriction is because many of these operations modify the
// array's domain, which would unexpectedly modify other arrays if they shared
// domains.
//

//
// Declare a 1D array and initialize it to the values 1..5. The anonymous
// domain used in this declaration has a 1:1 relationship with the array.
//
var A: [1..5] int = [i in 1..5] i;

writeln("A was initialized to: ", A);

//
// Add an element to the front of the array and one to the back of the array.
// This will modify the array's domain to become {0..6} and add elements
// 0 and 6 to the array.
//
A.push_front(0);
A.push_back(6);

writeln("After adding to the front and back A is: ", A);

//
// Look at the first and last array elements without modifying the array
//
writeln("The first and last elements in A are: ", (A.head(), A.tail()));

//
// Remove the two elements added earlier with push_front and push_back.
// The domain will become {1..5} again.
//
A.pop_front();
A.pop_back();

writeln("After popping the two elements pushed previously A is: ", A);

//
// Insert values 10, 11 and 12 at indices 4, 3, and 2.  The domain will
// grow by 3 to become {1..8} and the array elements above the inserted
// positions will be shifted up.
//
A.insert(4, 10);
A.insert(3, 11);
A.insert(2, 12);
writeln("After inserting some new values, A is: ", A);

//
// The method 'find' searches the array for the argument.  It returns a tuple
// containing a bool and an index. If the returned bool is true, the argument
// was found at the returned index.  If the bool is false, the value was not
// found and the index is unspecified.
//
var (found, idx) = A.find(10);
if found then
  writeln("Found 10 at index: ", idx);
else
  writeln("Didn't find 10");

(found, idx) = A.find(7);
if found then
  writeln("Found 7 at index: ", idx);
else
  writeln("Didn't find 7");

//
// Add a few more 5s to the array
//
A.push_front(5);
A.push_back(5);

//
// Count how many times an element is in the array
//
writeln("The value 5 is in A ", A.count(5), " times.");

//
// Reverse the elements in the array
//
writeln("Before calling reverse A is: ", A);
A.reverse();
writeln("After calling reverse A is: ", A);

//
// Array elements can be removed one at a time by specifying an
// index to remove.  The elements above the removed one will be
// shifted down.
//
A.remove(3);
writeln("After first remove A is: ", A);

//
// A range of indices can also be removed
//
A.remove(4..6);
writeln("After second remove A is: ", A);

//
// A starting index and a count also work
//
A.remove(2, 2);
writeln("After third remove A is: ", A);

//
// The array is still a normal 1D Chapel array and supports regular array
// operations such as parallel iteration:
//
forall i in A.domain {
  A[i] += 1;
}
writeln("After adding 1 to all elements A is: ", A);

//
// Or reductions:
//
writeln("The sum of elements in A is ", + reduce A);

//
// The clear method will empty the array completely.  If the domain's low
// bound was 'low' this sets the domain to {low..low-1}
//
A.clear();
writeln("After clearing, A is: ", A);


Final mod to top-level description/disclaimer for filerator functions

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

use Sort;

//
// This module defines several iterators that are designed to help
// support reasoning about directory and file contents.  At present,
// all are serial and single-locale, though the ultimate intention
// is to also support parallel and multi-locale options.
//
// These interfaces should be considered experimental and subject to
// change for the 1.10 release.  For that reason, send in feedback if
// you would like to see changes or additional arguments/features.
// And, as always, send in bug reports if you think we got something
// wrong.
//

/* iter listdir(path: string, dotfiles=false, dirs=true, files=true,
                listlinks=true): string

    listdir() lists the contents of a directory, similar to 'ls'
      * path: the directory whose contents should be listed
      * dotfiles: should files/directories that start with '.' be listed?
      * dirs: should dirs be listed?
      * files: should files be listed?
      * listlinks: should symbolic links be listed?

   By default this routine lists all files and directories in the
   current directory, including symbolic links, as long as they don't
   start with '.'
*/

iter listdir(path: string, dotfiles=false, dirs=true, files=true,
             listlinks=true): string {
  extern type DIRptr;
  extern type direntptr;
  extern proc opendir(name: c_string): DIRptr;
  extern proc readdir(dirp: DIRptr): direntptr;
  extern proc closedir(dirp: DIRptr): c_int;
  extern proc chpl_rt_isDir(pathname: c_string, followLinks: bool): c_int;

  proc direntptr.d_name(): c_string {
    extern proc chpl_rt_direntptr_getname(d: direntptr): c_string;

    return chpl_rt_direntptr_getname(this);
  }

  var dir: DIRptr;
  var ent: direntptr;
  dir = opendir(path:c_string);
  if (!is_c_nil(dir)) {
    ent = readdir(dir);
    while (!is_c_nil(ent)) {
      const filename = ent.d_name();
      if (dotfiles || filename.substring(1) != '.') {
        if (filename != "." && filename != "..") {
          const fullpath = path + "/" + filename;
          //
          // chpl_rt_isDir() returns 'true' if 'fullpath' is a
          // directory.  If we are listing symbolic links
          // (listlinks==true), it will also return symbolic links to
          // directories.
          //
          if (chpl_rt_isDir(fullpath:c_string, listlinks)) {
            if (dirs) then
              yield filename;
          } else {
            if (files) then
              yield filename;
          }
        }
      }
      ent = readdir(dir);
    }
    closedir(dir);
  } else {
    extern proc perror(s: c_string);
    perror("error in listdir(): ");
  }
}

/* iter walkdirs(path: string=".", topdown=true, depth=max(int),
                 dotfiles=false, followlinks=false, sort=false): string

   walkdirs() recursively walks a directory structure, yielding
   directory names.  The strings that are generated will be rooted
   from 'path'.
     * path: the directory to start from
     * topdown: indicates whether to yield the directories using a
       preorder (vs. postorder) traversal
     * depth: indicates the maximal depth of recursion to use
     * dotfiles: indicates whether to enter directories with dotfile names
     * followlinks: indicates whether to follow symbolic links or not
     * sort: indicates whether to consider subdirectories in sorted
       order or not

   by default, walkdirs() will start in the current directory, process
   directories in preorder; recursively traverse subdirectories; and
   neither follow dotfile directories nor symbolic links.  It will not
   sort the directories by default.
*/

iter walkdirs(path: string=".", topdown=true, depth=max(int), dotfiles=false,
              followlinks=false, sort=false): string {

  if (topdown) then
    yield path;

  if (depth) {
    var subdirs = listdir(path, dotfiles=dotfiles, files=false, listlinks=followlinks);
    if (sort) then
      QuickSort(subdirs);
    for subdir in subdirs {
      const fullpath = path + "/" + subdir;
      for subdir in walkdirs(fullpath, topdown, depth-1, dotfiles,
                             followlinks, sort) do
        yield subdir;
    }
  }

  if (!topdown) then
    yield path;
}

/* iter wordexp(pattern="*")

   wordexp() gives a glob-like capability, implemented with C's wordexp()
   (which is, itself a routine that provides a glob-like capability :)
     * pattern: the glob pattern to match against

   By default, it will list all files/directories in the current directory
*/

iter wordexp(pattern="*") {
  extern type wordexp_t;
  extern proc wordexp(pattern:c_string, ref ret_glob: wordexp_t,
                      flags_c_int): c_int;
  extern proc chpl_wordexp_num(x:wordexp_t): size_t;
  extern proc chpl_wordexp_index(x:wordexp_t, idx:size_t): c_string;
  extern proc wordfree(ref glb:wordexp_t);

  var glb : wordexp_t;

  const err = wordexp(pattern:c_string, glb, 0);

  for i in 0..chpl_wordexp_num(glb)-1 {
    yield chpl_wordexp_index(glb, i): string;
  }

  wordfree(glb);
}

/* iter glob(pattern="*")

   glob() gives glob() capabilities and is implemented using C's glob()
     * pattern: the glob pattern to match against

   By default, it will list all files/directories in the current directory
*/

iter glob(pattern="*") {
  extern type glob_t;
  extern proc glob(pattern:c_string, flags: c_int, errfunc:c_void_ptr,
                   ref ret_glob:glob_t):c_int;
  extern proc chpl_glob_num(x:glob_t): size_t;
  extern proc chpl_glob_index(x:glob_t, idx:size_t): c_string;
  extern proc globfree(ref glb:glob_t);

  var glb : glob_t;

  const err = glob(pattern:c_string, 0, c_nil, glb);

  for i in 0..chpl_glob_num(glb)-1 do
    yield chpl_glob_index(glb, i): string;

  globfree(glb);
}

/* iter findfiles(startdir = ".", recursive=false, dotfiles=false)

   findfiles() is a simple find-like utility implemented using the
   above routines
     * startdir: where to start when looking for files
     * recursive: tells whether or not to descend recurisvely
     * dotfiles: tells whether or not to yield dotfiles
*/

iter findfiles(startdir = ".", recursive=false, dotfiles=false) {
  if (recursive) then
    for subdir in walkdirs(startdir, dotfiles=dotfiles) do
      for file in listdir(subdir, dotfiles=dotfiles, dirs=false, files=true, listlinks=true) do
        yield subdir+"/"+file;
  else
    for file in listdir(startdir, dotfiles=dotfiles, dirs=false, files=true, listlinks=false) do
      yield startdir+"/"+file;
}

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

use Sort;

//
// This module defines several iterators that are designed to help
// support reasoning about directory and file contents.  At present,
// all are serial.
//

/* iter listdir(path: string, dotfiles=false, dirs=true, files=true,
                listlinks=true): string

    listdir() lists the contents of a directory, similar to 'ls'
      * path: the directory whose contents should be listed
      * dotfiles: should files/directories that start with '.' be listed?
      * dirs: should dirs be listed?
      * files: should files be listed?
      * listlinks: should symbolic links be listed?

   By default this routine lists all files and directories in the
   current directory, including symbolic links, as long as they don't
   start with '.'
*/

iter listdir(path: string, dotfiles=false, dirs=true, files=true,
             listlinks=true): string {
  extern type DIRptr;
  extern type direntptr;
  extern proc opendir(name: c_string): DIRptr;
  extern proc readdir(dirp: DIRptr): direntptr;
  extern proc closedir(dirp: DIRptr): c_int;
  extern proc chpl_rt_isDir(pathname: c_string, followLinks: bool): c_int;

  proc direntptr.d_name(): c_string {
    extern proc chpl_rt_direntptr_getname(d: direntptr): c_string;

    return chpl_rt_direntptr_getname(this);
  }

  var dir: DIRptr;
  var ent: direntptr;
  dir = opendir(path:c_string);
  if (!is_c_nil(dir)) {
    ent = readdir(dir);
    while (!is_c_nil(ent)) {
      const filename = ent.d_name();
      if (dotfiles || filename.substring(1) != '.') {
        if (filename != "." && filename != "..") {
          const fullpath = path + "/" + filename;
          //
          // chpl_rt_isDir() returns 'true' if 'fullpath' is a
          // directory.  If we are listing symbolic links
          // (listlinks==true), it will also return symbolic links to
          // directories.
          //
          if (chpl_rt_isDir(fullpath:c_string, listlinks)) {
            if (dirs) then
              yield filename;
          } else {
            if (files) then
              yield filename;
          }
        }
      }
      ent = readdir(dir);
    }
    closedir(dir);
  } else {
    extern proc perror(s: c_string);
    perror("error in listdir(): ");
  }
}

/* iter walkdirs(path: string=".", topdown=true, depth=max(int),
                 dotfiles=false, followlinks=false, sort=false): string

   walkdirs() recursively walks a directory structure, yielding
   directory names.  The strings that are generated will be rooted
   from 'path'.
     * path: the directory to start from
     * topdown: indicates whether to yield the directories using a
       preorder (vs. postorder) traversal
     * depth: indicates the maximal depth of recursion to use
     * dotfiles: indicates whether to enter directories with dotfile names
     * followlinks: indicates whether to follow symbolic links or not
     * sort: indicates whether to consider subdirectories in sorted
       order or not

   by default, walkdirs() will start in the current directory, process
   directories in preorder; recursively traverse subdirectories; and
   neither follow dotfile directories nor symbolic links.  It will not
   sort the directories by default.
*/

iter walkdirs(path: string=".", topdown=true, depth=max(int), dotfiles=false,
              followlinks=false, sort=false): string {

  if (topdown) then
    yield path;

  if (depth) {
    var subdirs = listdir(path, dotfiles=dotfiles, files=false, listlinks=followlinks);
    if (sort) then
      QuickSort(subdirs);
    for subdir in subdirs {
      const fullpath = path + "/" + subdir;
      for subdir in walkdirs(fullpath, topdown, depth-1, dotfiles,
                             followlinks, sort) do
        yield subdir;
    }
  }

  if (!topdown) then
    yield path;
}

/* iter wordexp(pattern="*")

   wordexp() gives a glob-like capability, implemented with C's wordexp()
   (which is, itself a routine that provides a glob-like capability :)
     * pattern: the glob pattern to match against

   By default, it will list all files/directories in the current directory
*/

iter wordexp(pattern="*") {
  extern type wordexp_t;
  extern proc wordexp(pattern:c_string, ref ret_glob: wordexp_t,
                      flags_c_int): c_int;
  extern proc chpl_wordexp_num(x:wordexp_t): size_t;
  extern proc chpl_wordexp_index(x:wordexp_t, idx:size_t): c_string;
  extern proc wordfree(ref glb:wordexp_t);

  var glb : wordexp_t;

  const err = wordexp(pattern:c_string, glb, 0);

  for i in 0..chpl_wordexp_num(glb)-1 {
    yield chpl_wordexp_index(glb, i): string;
  }

  wordfree(glb);
}

/* iter glob(pattern="*")

   glob() gives glob() capabilities and is implemented using C's glob()
     * pattern: the glob pattern to match against

   By default, it will list all files/directories in the current directory
*/

iter glob(pattern="*") {
  extern type glob_t;
  extern proc glob(pattern:c_string, flags: c_int, errfunc:c_void_ptr,
                   ref ret_glob:glob_t):c_int;
  extern proc chpl_glob_num(x:glob_t): size_t;
  extern proc chpl_glob_index(x:glob_t, idx:size_t): c_string;
  extern proc globfree(ref glb:glob_t);

  var glb : glob_t;

  const err = glob(pattern:c_string, 0, c_nil, glb);

  for i in 0..chpl_glob_num(glb)-1 do
    yield chpl_glob_index(glb, i): string;

  globfree(glb);
}

/* iter findfiles(startdir = ".", recursive=false, dotfiles=false)

   findfiles() is a simple find-like utility implemented using the
   above routines
     * startdir: where to start when looking for files
     * recursive: tells whether or not to descend recurisvely
     * dotfiles: tells whether or not to yield dotfiles
*/

iter findfiles(startdir = ".", recursive=false, dotfiles=false) {
  if (recursive) then
    for subdir in walkdirs(startdir, dotfiles=dotfiles) do
      for file in listdir(subdir, dotfiles=dotfiles, dirs=false, files=true, listlinks=true) do
        yield subdir+"/"+file;
  else
    for file in listdir(startdir, dotfiles=dotfiles, dirs=false, files=true, listlinks=false) do
      yield startdir+"/"+file;
}