Oops, forgot to update fft -- I switched argument orders in computeProblemSize

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

use BitOps, Random, Time;

use HPCCProblemSize;

param radix = 4;

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

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

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

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

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

def main() {
  printConfiguration();

  const TwiddleDom: domain(1, int(64)) distributed Block = [0:int(64)..#m/4];
  var Twiddles: [TwiddleDom] elemType;

  const ProblemDom: domain(1, int(64)) distributed Block = [0:int(64)..#m];
  var Z, z: [ProblemDom] elemType;

  initVectors(Twiddles, z);

  const startTime = getCurrentTime();

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

  const execTime = getCurrentTime() - startTime;

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

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

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

  fillRandom(z, seed);

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

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

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

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

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

def dfft(A: [?ADom], W) {
  const numElements = A.numElements;

  for (str, span) in genDFTPhases(numElements, radix) {
    forall (bankStart, twidIndex) in (ADom by 2*span, 0..) {
      var wk2 = W(twidIndex),
          wk1 = W(2*twidIndex),
          wk3 = (wk1.re - 2 * wk2.im * wk1.im,
                 2 * wk2.im * wk1.re - wk1.im):elemType;

      forall lo in bankStart..#str do
        butterfly(wk1, wk2, wk3, A[lo.. by str #radix]);

      wk1 = W(2*twidIndex+1);
      wk3 = (wk1.re - 2 * wk2.re * wk1.im,
             2 * wk2.re * wk1.re - wk1.im):elemType;
      wk2 *= 1.0i;

      forall lo in bankStart+span..#str do
        butterfly(wk1, wk2, wk3, A[lo.. by str #radix]);
    }
  }

  const str = radix**log4(numElements-1);
  if (str*radix == numElements) then
    forall lo in 0..#str do
      butterfly(1.0, 1.0, 1.0, A[lo.. by str #radix]);
  else
    forall lo in 0..#str {
      const a = A(lo),
            b = A(lo+str);
      A(lo)     = a + b;
      A(lo+str) = a - b;
    }
}

def genDFTPhases(numElements, radix) {
  var stride = 1;
  for 1..log4(numElements-1) {
    const span = stride * radix;
    yield (stride, span);
    stride = span;
  }
}

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

  X(0) = x0 + x2;
  x0 -= x2;
  X(2) = wk2 * x0;
  x0 = x1 + x3rot;
  X(1) = wk1 * x0;
  x0 = x1 - x3rot;
  X(3) = wk3 * x0;

  A = X;
}

def log4(x) return logBasePow2(x, 2);

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

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

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

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

  return (maxerr < threshold);
}

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

use BitOps, Random, Time;

use HPCCProblemSize;

param radix = 4;

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

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

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

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

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

def main() {
  printConfiguration();

  const TwiddleDom: domain(1, int(64)) distributed Block = [0:int(64)..#m/4];
  var Twiddles: [TwiddleDom] elemType;

  const ProblemDom: domain(1, int(64)) distributed Block = [0:int(64)..#m];
  var Z, z: [ProblemDom] elemType;

  initVectors(Twiddles, z);

  const startTime = getCurrentTime();

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

  const execTime = getCurrentTime() - startTime;

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

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

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

  fillRandom(z, seed);

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

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

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

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

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

def dfft(A: [?ADom], W) {
  const numElements = A.numElements;

  for (str, span) in genDFTPhases(numElements, radix) {
    forall (bankStart, twidIndex) in (ADom by 2*span, 0..) {
      var wk2 = W(twidIndex),
          wk1 = W(2*twidIndex),
          wk3 = (wk1.re - 2 * wk2.im * wk1.im,
                 2 * wk2.im * wk1.re - wk1.im):elemType;

      forall lo in bankStart..#str do
        butterfly(wk1, wk2, wk3, A[lo.. by str #radix]);

      wk1 = W(2*twidIndex+1);
      wk3 = (wk1.re - 2 * wk2.re * wk1.im,
             2 * wk2.re * wk1.re - wk1.im):elemType;
      wk2 *= 1.0i;

      forall lo in bankStart+span..#str do
        butterfly(wk1, wk2, wk3, A[lo.. by str #radix]);
    }
  }

  const str = radix**log4(numElements-1);
  if (str*radix == numElements) then
    forall lo in 0..#str do
      butterfly(1.0, 1.0, 1.0, A[lo.. by str #radix]);
  else
    forall lo in 0..#str {
      const a = A(lo),
            b = A(lo+str);
      A(lo)     = a + b;
      A(lo+str) = a - b;
    }
}

def genDFTPhases(numElements, radix) {
  var stride = 1;
  for 1..log4(numElements-1) {
    const span = stride * radix;
    yield (stride, span);
    stride = span;
  }
}

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

  X(0) = x0 + x2;
  x0 -= x2;
  X(2) = wk2 * x0;
  x0 = x1 + x3rot;
  X(1) = wk1 * x0;
  x0 = x1 - x3rot;
  X(3) = wk3 * x0;

  A = X;
}

def log4(x) return logBasePow2(x, 2);

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

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

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

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

  return (maxerr < threshold);
}

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


Apply private to symbols in the Error standard module

There are a couple of helper extern functions which should be private to the
module.  When chpldoc recognizes private, these will no longer need the "no
doc" pragma.

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

   This module helps to handle errors. In particular, it enables routines
   to return a syserr - encoding an error state - and then contains routines
   that can be provided a syserr in order to print out a useful error message.

   This module defines the type syserr, which can encode an error code or an
   error message. This type can be returned from routines generating an error.

   The IO module uses these routines in a way that supports error inspection
   and also rapid prototyping. Most routines in the IO module have two forms.
   In one form, an error (of type syserr) is returned in an out error argument.
   In the second form, no error is returned, and instead the task will halt
   with a fatal error if an error is encountered.

 */
module Error {

use SysBasic;

// here's what we need from Sys
pragma "no doc"
private extern proc sys_strerror_syserr_str(error:syserr, out err_in_strerror:err_t):c_string;

/* This function takes in a string and returns it in double-quotes,
   with internal double-quotes escaped with backslash.
   */
pragma "no doc"
private proc quote_string(s:string, len:ssize_t) {
  extern const QIO_STRING_FORMAT_CHPL: uint(8);
  extern proc qio_quote_string(s:uint(8), e:uint(8), f:uint(8),
                               ptr: c_string, len:ssize_t,
                               ref ret:c_string, ti: c_void_ptr): syserr;
  extern proc qio_strdup(s: c_string): c_string;

  var ret: c_string;
  // 34 is ASCII double quote
  var err: syserr = qio_quote_string(34:uint(8), 34:uint(8),
                                    QIO_STRING_FORMAT_CHPL,
                                    s.c_str(), len, ret, c_nil);

  // This doesn't handle the case where ret==NULL as did the previous
  // version in QIO, but I'm not sure how that was used.

  if err then return qio_strdup("<error>");
  return ret;
}

/* Halt with a useful message if there was an error. Do nothing if the error
   argument does not indicate an error occured. The error message printed
   when halting will describe the error passed and msg will be appended to it.

   :arg error: the error object
   :arg msg: extra information to print after the error description
 */
proc ioerror(error:syserr, msg:string)
{
  if( error ) {
    var errstr:c_string;
    var strerror_err:err_t = ENOERR;
    errstr = sys_strerror_syserr_str(error, strerror_err);
    __primitive("chpl_error", errstr + " " + msg.c_str());
  }
}

/* Halt with a useful message if there was an error. Do nothing if the error
   argument does not indicate an error occured. The error message printed
   when halting will describe the error passed and msg will be appended to it,
   along with the path related to the error (for example, the path to a file
   which could not be opened).

   :arg error: the error object
   :arg msg: extra information to print after the error description
   :arg path: a path to print out that is related to the error
 */
proc ioerror(error:syserr, msg:string, path:string)
{
  if( error ) {
    var errstr:c_string;
    var quotedpath:c_string;
    var strerror_err:err_t = ENOERR;
    errstr = sys_strerror_syserr_str(error, strerror_err);
    quotedpath = quote_string(path, path.length:ssize_t);
    __primitive("chpl_error", errstr + " " + msg.c_str() + " with path " + quotedpath);
  }
}

/* Halt with a useful message if there was an error. Do nothing if the error
   argument does not indicate an error occured. The error message printed
   when halting will describe the error passed and msg will be appended to it,
   along with the path and file offset related to the error. For example, this
   routine might indicate a file format error at a particular location.

   :arg error: the error object
   :arg msg: extra information to print after the error description
   :arg path: a path to print out that is related to the error
   :arg offset: an offset to print out that is related to the error
 */
proc ioerror(error:syserr, msg:string, path:string, offset:int(64))
{
  if( error ) {
    var errstr:c_string;
    var quotedpath:c_string;
    var strerror_err:err_t = ENOERR;
    errstr = sys_strerror_syserr_str(error, strerror_err);
    quotedpath = quote_string(path, path.length:ssize_t);
    // TODO: Because the output of concatenation (+) is an allocated string,
    // this routine leaks like a sieve.
    __primitive("chpl_error", errstr + " " + msg.c_str() + " with path " + quotedpath + " offset " + offset:c_string_copy);
  }
}

/* Halt with a useful message. Instead of an error argument, this routine takes
   in an error string to report.
   The error message printed when halting will describe the error passed and
   msg will be appended to it, along with the path and file offset related to
   the error. For example, this routine might indicate a file format error at a
   particular location.

   This routine .

   :arg errstr: the error string
   :arg msg: extra information to print after the error description
   :arg path: a path to print out that is related to the error
   :arg offset: an offset to print out that is related to the error
 */
proc ioerror(errstr:string, msg:string, path:string, offset:int(64))
{
  var quotedpath:c_string;
  quotedpath = quote_string(path, path.length:ssize_t);
  // TODO: Because the output of concatenation (+) is an allocated string,
  // this routine leaks like a sieve.
  __primitive("chpl_error", errstr + " " + msg.c_str() + " with path " + quotedpath + " offset " + offset:c_string_copy);
}

/* Convert a syserr error code to a human-readable string describing that
   error.

   :arg errstr: the error string
   :returns: a string describing the error
 */
proc errorToString(error:syserr):string
{
  var errstr:c_string = "unknown"; // Why initialize this?
  var strerror_err:err_t = ENOERR;
  errstr = sys_strerror_syserr_str(error, strerror_err);
  return errstr;
}

}

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

   This module helps to handle errors. In particular, it enables routines
   to return a syserr - encoding an error state - and then contains routines
   that can be provided a syserr in order to print out a useful error message.

   This module defines the type syserr, which can encode an error code or an
   error message. This type can be returned from routines generating an error.

   The IO module uses these routines in a way that supports error inspection
   and also rapid prototyping. Most routines in the IO module have two forms.
   In one form, an error (of type syserr) is returned in an out error argument.
   In the second form, no error is returned, and instead the task will halt
   with a fatal error if an error is encountered.

 */
module Error {

use SysBasic;

// here's what we need from Sys
pragma "no doc"
extern proc sys_strerror_syserr_str(error:syserr, out err_in_strerror:err_t):c_string;

/* This function takes in a string and returns it in double-quotes,
   with internal double-quotes escaped with backslash.
   */
pragma "no doc"
proc quote_string(s:string, len:ssize_t) {
  extern const QIO_STRING_FORMAT_CHPL: uint(8);
  extern proc qio_quote_string(s:uint(8), e:uint(8), f:uint(8),
                               ptr: c_string, len:ssize_t,
                               ref ret:c_string, ti: c_void_ptr): syserr;
  extern proc qio_strdup(s: c_string): c_string;

  var ret: c_string;
  // 34 is ASCII double quote
  var err: syserr = qio_quote_string(34:uint(8), 34:uint(8),
                                    QIO_STRING_FORMAT_CHPL,
                                    s.c_str(), len, ret, c_nil);

  // This doesn't handle the case where ret==NULL as did the previous
  // version in QIO, but I'm not sure how that was used.

  if err then return qio_strdup("<error>");
  return ret;
}

/* Halt with a useful message if there was an error. Do nothing if the error
   argument does not indicate an error occured. The error message printed
   when halting will describe the error passed and msg will be appended to it.

   :arg error: the error object
   :arg msg: extra information to print after the error description
 */
proc ioerror(error:syserr, msg:string)
{
  if( error ) {
    var errstr:c_string;
    var strerror_err:err_t = ENOERR;
    errstr = sys_strerror_syserr_str(error, strerror_err);
    __primitive("chpl_error", errstr + " " + msg.c_str());
  }
}

/* Halt with a useful message if there was an error. Do nothing if the error
   argument does not indicate an error occured. The error message printed
   when halting will describe the error passed and msg will be appended to it,
   along with the path related to the error (for example, the path to a file
   which could not be opened).

   :arg error: the error object
   :arg msg: extra information to print after the error description
   :arg path: a path to print out that is related to the error
 */
proc ioerror(error:syserr, msg:string, path:string)
{
  if( error ) {
    var errstr:c_string;
    var quotedpath:c_string;
    var strerror_err:err_t = ENOERR;
    errstr = sys_strerror_syserr_str(error, strerror_err);
    quotedpath = quote_string(path, path.length:ssize_t);
    __primitive("chpl_error", errstr + " " + msg.c_str() + " with path " + quotedpath);
  }
}

/* Halt with a useful message if there was an error. Do nothing if the error
   argument does not indicate an error occured. The error message printed
   when halting will describe the error passed and msg will be appended to it,
   along with the path and file offset related to the error. For example, this
   routine might indicate a file format error at a particular location.

   :arg error: the error object
   :arg msg: extra information to print after the error description
   :arg path: a path to print out that is related to the error
   :arg offset: an offset to print out that is related to the error
 */
proc ioerror(error:syserr, msg:string, path:string, offset:int(64))
{
  if( error ) {
    var errstr:c_string;
    var quotedpath:c_string;
    var strerror_err:err_t = ENOERR;
    errstr = sys_strerror_syserr_str(error, strerror_err);
    quotedpath = quote_string(path, path.length:ssize_t);
    // TODO: Because the output of concatenation (+) is an allocated string,
    // this routine leaks like a sieve.
    __primitive("chpl_error", errstr + " " + msg.c_str() + " with path " + quotedpath + " offset " + offset:c_string_copy);
  }
}

/* Halt with a useful message. Instead of an error argument, this routine takes
   in an error string to report.
   The error message printed when halting will describe the error passed and
   msg will be appended to it, along with the path and file offset related to
   the error. For example, this routine might indicate a file format error at a
   particular location.

   This routine .

   :arg errstr: the error string
   :arg msg: extra information to print after the error description
   :arg path: a path to print out that is related to the error
   :arg offset: an offset to print out that is related to the error
 */
proc ioerror(errstr:string, msg:string, path:string, offset:int(64))
{
  var quotedpath:c_string;
  quotedpath = quote_string(path, path.length:ssize_t);
  // TODO: Because the output of concatenation (+) is an allocated string,
  // this routine leaks like a sieve.
  __primitive("chpl_error", errstr + " " + msg.c_str() + " with path " + quotedpath + " offset " + offset:c_string_copy);
}

/* Convert a syserr error code to a human-readable string describing that
   error.

   :arg errstr: the error string
   :returns: a string describing the error
 */
proc errorToString(error:syserr):string
{
  var errstr:c_string = "unknown"; // Why initialize this?
  var strerror_err:err_t = ENOERR;
  errstr = sys_strerror_syserr_str(error, strerror_err);
  return errstr;
}

}


Remove "no doc" pragma from private symbols in Types module.

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

/*
Functions related to predefined types.

This is an automatic module, so these functions are available
to all Chapel programs.
*/
module Types {

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isType(type t) param return true;
/* Returns `true` if the argument is a type. */
pragma "no instantiation limit"
proc isType(e) param return false;

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(type t)  param return false;
pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(param p) param return true;
/* Returns `true` if the argument is a param. */
pragma "no instantiation limit"
proc isParam(e)       param return false;

// TODO eliminate this; beware of isPrimitive()
pragma "no doc"
pragma "no instantiation limit"
proc _isPrimitiveType(type t) param return
  isBoolType(t)  ||
  isIntegralType(t) ||
  isRealType(t)     ||
//To allow imag, need to define casts from primitive types into imag.
//isImagType(t)     ||
  (t == c_string);

/*
Returns `true` if the type `t` is a primitive type,
as defined by the language specification.
*/
pragma "no instantiation limit"
proc isPrimitiveType(type t) param return
  isVoidType(t) || isBoolType(t) || isIntType(t) || isUintType(t) ||
  isRealType(t) || isImagType(t) || isComplexType(t) || isStringType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`.
*/
pragma "no instantiation limit"
proc isNumericType(type t) param return
  isIntegralType(t) || isFloatType(t) || isComplexType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`.
*/
pragma "no instantiation limit"
proc isIntegralType(type t) param return
  isIntType(t) || isUintType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`real`, `imag`.
*/
pragma "no instantiation limit"
proc isFloatType(type t) param return
  isRealType(t) || isImagType(t);

// We document the following functions as one group, for user convenience.
pragma "no instantiation limit"
proc isVoidType(type t) param return t == void;

pragma "no instantiation limit"
proc isBoolType(type t) param return
  (t == bool) || (t == bool(8)) || (t == bool(16)) || (t == bool(32)) || (t == bool(64));

pragma "no instantiation limit"
proc isIntType(type t) param return
  (t == int(8)) || (t == int(16)) || (t == int(32)) || (t == int(64));

pragma "no instantiation limit"
proc isUintType(type t) param return
  (t == uint(8)) || (t == uint(16)) || (t == uint(32)) || (t == uint(64));

pragma "no instantiation limit"
proc isEnumType(type t) param {
  proc isEnumHelp(type t: enumerated) param return true;
  proc isEnumHelp(type t) param return false;
  return isEnumHelp(t);
}

pragma "no instantiation limit"
proc isComplexType(type t) param return
  (t == complex(64)) || (t == complex(128));

pragma "no instantiation limit"
proc isRealType(type t) param return
  (t == real(32)) || (t == real(64));

pragma "no instantiation limit"
proc isImagType(type t) param return
  (t == imag(32)) || (t == imag(64));

// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type.
The argument must be a type.
*/
pragma "no instantiation limit"
proc isStringType(type t) param return t == string;

// Returns the unsigned equivalent of the input type.
pragma "no doc"
proc chpl__unsignedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return uint(numBits(t));
}

// Returns the signed equivalent of the input type.
pragma "no doc"
proc chpl__signedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return int(numBits(t));
}

pragma "no doc"
proc chpl__maxIntTypeSameSign(type t) type {
  if ! isIntegralType(t) then
    compilerError("type t is non-integral: ", typeToString(t));

  if (isIntType(t)) then
    return int(64);
  else
    return uint(64);
}

//
// isXxxValue() - trivial implementations
// no isVoid() or isVoidValue() - it might not work
// Let these be for internal consumption for now,
// due to lack of consensus for the name.
//

pragma "no doc"
proc isBoolValue(e)      param  return isBoolType(e.type);
pragma "no doc"
proc isIntValue(e)       param  return isIntType(e.type);
pragma "no doc"
proc isUintValue(e)      param  return isUintType(e.type);
pragma "no doc"
proc isRealValue(e)      param  return isRealType(e.type);
pragma "no doc"
proc isImagValue(e)      param  return isImagType(e.type);
pragma "no doc"
proc isComplexValue(e)   param  return isComplexType(e.type);
pragma "no doc"
proc isStringValue(e)    param  return isStringType(e.type);
pragma "no doc"
proc isIntegralValue(e)  param  return isIntegralType(e.type);
pragma "no doc"
proc isFloatValue(e)     param  return isFloatType(e.type);
pragma "no doc"
proc isNumericValue(e)   param  return isNumericType(e.type);
pragma "no doc"
proc isPrimitiveValue(e) param  return isPrimitiveType(e.type);
pragma "no doc"
proc isEnumValue(e)      param  return isEnumType(e.type);
//Defined elsewhere:
// isTupleValue
// isHomogeneousTupleValue
pragma "no doc"
proc isClassValue(e)     param  return isClassType(e.type);
pragma "no doc"
proc isRecordValue(e)    param  return isRecordType(e.type);
pragma "no doc"
proc isUnionValue(e)     param  return isUnionType(e.type);
//Defined elsewhere:
// isRangeValue
// isDmapValue
// isDomainValue
// isArrayValue
// isSyncValue
// isSingleValue
pragma "no doc"
proc isAtomicValue(e)    param  return isAtomicType(e.type);

//
// ixXxx() - the argument can be either a type or a value
//

// Set 1 - types.
// In chpldoc, isBool(e) et al. will apply to types as well.
pragma "no doc"
proc isBool(type t)      param  return isBoolType(t);
pragma "no doc"
proc isInt(type t)       param  return isIntType(t);
pragma "no doc"
proc isUint(type t)      param  return isUintType(t);
pragma "no doc"
proc isReal(type t)      param  return isRealType(t);
pragma "no doc"
proc isImag(type t)      param  return isImagType(t);
pragma "no doc"
proc isComplex(type t)   param  return isComplexType(t);
pragma "no doc"
proc isString(type t)    param  return isStringType(t);
pragma "no doc"
proc isIntegral(type t)  param  return isIntegralType(t);
pragma "no doc"
proc isFloat(type t)     param  return isFloatType(t);
pragma "no doc"
proc isNumeric(type t)   param  return isNumericType(t);
pragma "no doc"
proc isPrimitive(type t) param  return isPrimitiveType(t);
pragma "no doc"
proc isEnum(type t)      param  return isEnumType(t);
pragma "no doc"
proc isTuple(type t)     param  return isTupleType(t);
pragma "no doc"
proc isHomogeneousTuple(type t)  param  return isHomogeneousTupleType(t);
pragma "no doc"
proc isClass(type t)     param  return isClassType(t);
pragma "no doc"
proc isRecord(type t)    param  return isRecordType(t);
pragma "no doc"
proc isUnion(type t)     param  return isUnionType(t);
pragma "no doc"
proc isRange(type t)     param  return isRangeType(t);
pragma "no doc"
proc isDmap(type t)      param  return isDmapType(t);
pragma "no doc"
proc isDomain(type t)    param  return isDomainType(t);
pragma "no doc"
proc isArray(type t)     param  return isArrayType(t);
pragma "no doc"
proc isSync(type t)      param  return isSyncType(t);
pragma "no doc"
proc isSingle(type t)    param  return isSingleType(t);
pragma "no doc"
proc isAtomic(type t)    param  return isAtomicType(t);

// Set 2 - values.
/*
Returns `true` if the argument is a primitive type,
as defined by the language specification, or a value of a primitive type.
*/
proc isPrimitive(e) param  return isPrimitiveValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`, or a value of such a type.
*/
proc isNumeric(e)   param  return isNumericValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, or a value of such a type.
*/
proc isIntegral(e)  param  return isIntegralValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`real`, `imag`, or a value of such a type.
*/
proc isFloat(e)     param  return isFloatValue(e);
// We document the following functions as one group, for user convenience.
proc isBool(e)      param  return isBoolValue(e);
proc isInt(e)       param  return isIntValue(e);
proc isUint(e)      param  return isUintValue(e);
proc isReal(e)      param  return isRealValue(e);
proc isImag(e)      param  return isImagValue(e);
proc isComplex(e)   param  return isComplexValue(e);
proc isString(e)    param  return isStringValue(e);
proc isEnum(e)      param  return isEnumValue(e);
proc isTuple(e)     param  return isTupleValue(e);
proc isHomogeneousTuple(e: _tuple)  param  return isHomogeneousTupleValue(e);
proc isClass(e)     param  return isClassValue(e);
proc isRecord(e)    param  return isRecordValue(e);
proc isUnion(e)     param  return isUnionValue(e);
proc isRange(e)     param  return isRangeValue(e);
proc isDmap(e)      param  return isDmapValue(e);
proc isDomain(e)    param  return isDomainValue(e);
proc isArray(e)     param  return isArrayValue(e);
pragma "no doc"
proc isSync(e: sync)     param  return true; // workaround: not isSyncValue
proc isSync(e)           param  return false;
pragma "no doc"
proc isSingle(e: single) param  return true; // workaround: not isSingleValue
proc isSingle(e)         param  return false;
// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type or a value of such a type.
*/
proc isAtomic(e)    param  return isAtomicValue(e);

// for internal use until we have a better name
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)         param  return false;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: sync)   param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: single) param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)  param where isAtomicType(e.type)  return true;

// Is 'sub' a subtype (or equal to) 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`. */
proc isSubtype(type sub, type super) param where   sub: super  return true;
pragma "no doc"
proc isSubtype(type sub, type super) param where !(sub: super) return false;

// Is 'sub' a proper subtype of 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`
   and is not `super`. */
proc isProperSubtype(type sub, type super) param
  where isSubtype(sub, super) && sub != super
  return true;
pragma "no doc"
proc isProperSubtype(type sub, type super) param
  return false;

// Returns true if it is legal to coerce t1 to t2, false otherwise.
pragma "no doc"
proc chpl__legalIntCoerce(type t1, type t2) param
{
  if (isIntType(t2)) {
    if (isIntType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (isIntType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

// Returns the type with which both s and t are compatible
// That is, both s and t can be coerced to the returned type.
private proc chpl__commonType(type s, type t) type
{
  if ! isIntegralType(s) then
    compilerError("Type ", typeToString(s) , " is non-integral: ");
  if ! isIntegralType(t) then
    compilerError("Type ", typeToString(t) , " is non-integral: ");

  if numBits(s) > numBits(t) then return s;
  if numBits(s) < numBits(t) then return t;

  if isIntType(s) && ! isIntType(t) ||
     isIntType(t) && ! isIntType(s) then
    compilerError("Types ", typeToString(s) , " and ", typeToString(t), " are incompatible.");

  return s;
}

//
// numBits(type) -- returns the number of bits in a type
//
// joint documentation, for user convenience
/*
Returns the number of bits used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBits(type t) param where t == bool {
  compilerError("default-width 'bool' does not have a well-defined size");
}
pragma "no doc"
proc numBits(type t) param where t == bool(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == bool(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == bool(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == bool(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == int(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == int(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == int(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == int(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == uint(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == uint(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == uint(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == uint(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == real(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == real(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == imag(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == imag(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(128) return 128;

//
// numBytes(type) -- returns the number of bytes in a type
//

pragma "no doc" // do we want to document this one?
param bitsPerByte = 8;

/*
Returns the number of bytes used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBytes(type t) param return numBits(t)/8;

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc min(type t) param  where isBool(t)      return false: t;

pragma "no doc"
proc min(type t) param  where t == int(8)    return 0x80: t;
pragma "no doc"
proc min(type t) param  where t == int(16)   return 0x8000: t;
pragma "no doc"
proc min(type t) param  where t == int(32)   return 0x80000000: t;
pragma "no doc"
proc min(type t) param  where t == int(64)   return 0x8000000000000000: t;

pragma "no doc"
proc min(type t) param  where isUint(t)      return 0: t;

pragma "no doc"
proc min(type t) where isFloatType(t)        return __primitive( "_min", t);

pragma "no doc"
proc min(type t) where isComplexType(t) {
  var x: t;
  x.re = min(x.re.type);
  x.im = min(x.im.type);
  return x;
}

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is a `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc max(type t) param  where isBool(t)      return true: t;

pragma "no doc"
proc max(type t) param  where t == int(8)    return 0x7f: t;
pragma "no doc"
proc max(type t) param  where t == int(16)   return 0x7fff: t;
pragma "no doc"
proc max(type t) param  where t == int(32)   return 0x7fffffff: t;
pragma "no doc"
proc max(type t) param  where t == int(64)   return 0x7fffffffffffffff: t;

pragma "no doc"
proc max(type t) param  where t == uint(8)   return 0xff: t;
pragma "no doc"
proc max(type t) param  where t == uint(16)  return 0xffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(32)  return 0xffffffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(64)  return 0xffffffffffffffff: t;

pragma "no doc"
proc max(type t) where isFloatType(t)        return __primitive( "_max", t);

pragma "no doc"
proc max(type t) where isComplexType(t) {
  var x: t;
  x.re = max(x.re.type);
  x.im = max(x.im.type);
  return x;
}

pragma "no doc"
iter chpl_enumerate(type t: enumerated) {
  const enumTuple = chpl_enum_enumerate(t);
  for i in 1..enumTuple.size do
    yield enumTuple(i);
}

// TODO add chpl_ to these functions' names - they are not intended for user.
private proc enum_minbits(type t: enumerated) param {
  return __primitive( "enum min bits", t);
}
private proc enum_issigned(type t: enumerated) param {
  return __primitive( "enum is signed", t);
}
// TODO - maybe this function can be useful for the user, for C interop?
// If so, give it a different name.
pragma "no doc"
proc enum_mintype(type t: enumerated) type {
  param minbits = enum_minbits(t);
  param signed = enum_issigned(t);
  if signed {
    return int(minbits);
  } else {
    return uint(minbits);
  }
}

pragma "no doc" // documented with the other numBits() above
proc numBits(type t: enumerated) param {
  return numBits(enum_mintype(t));
}

/*
Returns `this`, cast to the type `T`.
Generates a run-time error if `this` cannot be represented by `T`,
for example ``(-1).safeCast(uint)`` or ``256.safeCast(uint(8))``.

This method performs the minimum number of runtime checks.
For example, when casting from `uint(8)` to `uint(64)`,
no checks at all will be done.
*/
inline proc integral.safeCast(type T) : T where isUintType(T) {
  if castChecking {
    if isIntType(this.type) {
      // int(?) -> uint(?)
      if this < 0 then // runtime check
        halt("casting "+typeToString(this.type)+" less than 0 to "+typeToString(T));
    }

    if max(this.type):uint > max(T):uint {
      // [u]int(?) -> uint(?)
      if (this:uint > max(T):uint) then // runtime check
        halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
             typeToString(T)+" to "+typeToString(T));
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
inline proc integral.safeCast(type T) : T where isIntType(T) {
  if castChecking {
    if max(this.type):uint > max(T):uint {
      // this isUintType check lets us avoid a runtime check for this < 0
      if isUintType(this.type) {
        // uint(?) -> int(?)
        if this:uint > max(T):uint then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      } else {
        // int(?) -> int(?)
        // max(T) <= max(int), so cast to int is safe
        if this:int > max(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
    if isIntType(this.type) {
      if min(this.type):int < min(T):int {
        // int(?) -> int(?)
        if this:int < min(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value less than the minimum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
proc integral.safeCast(type T) {
  compilerError("safeCast is only supported between integral types");
}

//
// identity functions (for reductions)
//
pragma "no doc"
inline proc _prod_id(type t) return 1:t;
pragma "no doc"
inline proc _land_id(type t) return true;
pragma "no doc"
inline proc _lor_id(type t) return false;
pragma "no doc"
inline proc _lxor_id(type t) return false;
pragma "no doc"
inline proc _band_id(type t) {
  // MPF - this doesn't really make sense for floating-point types,
  // but old code had it casting MAX_UINT to the same type...
  // I just moved it from the code generator since we don't need
  // a primitive to generate constants.
  if t == bool then return true;
  if t == int(8) then return -1:t;
  if t == int(16) then return -1:t;
  if t == int(32) then return -1:t;
  if t == int(64) then return -1:t;
  if t == real(32) then return max(uint(32)):t;
  if t == real(64) then return max(uint(64)):t;
  if t == imag(32) then return max(uint(32)):t;
  if t == imag(64) then return max(uint(64)):t;
  if t == complex(64) then return (max(uint(32)):real(32), max(uint(32)):real(32)):t;
  if t == complex(128) then return (max(uint(64)):real(64), max(uint(64)):real(64)):t;
  return max(t);
}
pragma "no doc"
inline proc _bor_id(type t) return 0:t;
pragma "no doc"
inline proc _bxor_id(type t) return 0:t;

} // module Types

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

/*
Functions related to predefined types.

This is an automatic module, so these functions are available
to all Chapel programs.
*/
module Types {

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isType(type t) param return true;
/* Returns `true` if the argument is a type. */
pragma "no instantiation limit"
proc isType(e) param return false;

pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(type t)  param return false;
pragma "no doc" // joint documentation with the next one
pragma "no instantiation limit"
proc isParam(param p) param return true;
/* Returns `true` if the argument is a param. */
pragma "no instantiation limit"
proc isParam(e)       param return false;

// TODO eliminate this; beware of isPrimitive()
pragma "no doc"
pragma "no instantiation limit"
proc _isPrimitiveType(type t) param return
  isBoolType(t)  ||
  isIntegralType(t) ||
  isRealType(t)     ||
//To allow imag, need to define casts from primitive types into imag.
//isImagType(t)     ||
  (t == c_string);

/*
Returns `true` if the type `t` is a primitive type,
as defined by the language specification.
*/
pragma "no instantiation limit"
proc isPrimitiveType(type t) param return
  isVoidType(t) || isBoolType(t) || isIntType(t) || isUintType(t) ||
  isRealType(t) || isImagType(t) || isComplexType(t) || isStringType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`.
*/
pragma "no instantiation limit"
proc isNumericType(type t) param return
  isIntegralType(t) || isFloatType(t) || isComplexType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`int`, `uint`.
*/
pragma "no instantiation limit"
proc isIntegralType(type t) param return
  isIntType(t) || isUintType(t);

/*
Returns `true` if the type `t` is one the following types, of any width:
`real`, `imag`.
*/
pragma "no instantiation limit"
proc isFloatType(type t) param return
  isRealType(t) || isImagType(t);

// We document the following functions as one group, for user convenience.
pragma "no instantiation limit"
proc isVoidType(type t) param return t == void;

pragma "no instantiation limit"
proc isBoolType(type t) param return
  (t == bool) || (t == bool(8)) || (t == bool(16)) || (t == bool(32)) || (t == bool(64));

pragma "no instantiation limit"
proc isIntType(type t) param return
  (t == int(8)) || (t == int(16)) || (t == int(32)) || (t == int(64));

pragma "no instantiation limit"
proc isUintType(type t) param return
  (t == uint(8)) || (t == uint(16)) || (t == uint(32)) || (t == uint(64));

pragma "no instantiation limit"
proc isEnumType(type t) param {
  proc isEnumHelp(type t: enumerated) param return true;
  proc isEnumHelp(type t) param return false;
  return isEnumHelp(t);
}

pragma "no instantiation limit"
proc isComplexType(type t) param return
  (t == complex(64)) || (t == complex(128));

pragma "no instantiation limit"
proc isRealType(type t) param return
  (t == real(32)) || (t == real(64));

pragma "no instantiation limit"
proc isImagType(type t) param return
  (t == imag(32)) || (t == imag(64));

// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type.
The argument must be a type.
*/
pragma "no instantiation limit"
proc isStringType(type t) param return t == string;

// Returns the unsigned equivalent of the input type.
pragma "no doc"
proc chpl__unsignedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return uint(numBits(t));
}

// Returns the signed equivalent of the input type.
pragma "no doc"
proc chpl__signedType(type t) type
{
  if ! isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return int(numBits(t));
}

pragma "no doc"
proc chpl__maxIntTypeSameSign(type t) type {
  if ! isIntegralType(t) then
    compilerError("type t is non-integral: ", typeToString(t));

  if (isIntType(t)) then
    return int(64);
  else
    return uint(64);
}

//
// isXxxValue() - trivial implementations
// no isVoid() or isVoidValue() - it might not work
// Let these be for internal consumption for now,
// due to lack of consensus for the name.
//

pragma "no doc"
proc isBoolValue(e)      param  return isBoolType(e.type);
pragma "no doc"
proc isIntValue(e)       param  return isIntType(e.type);
pragma "no doc"
proc isUintValue(e)      param  return isUintType(e.type);
pragma "no doc"
proc isRealValue(e)      param  return isRealType(e.type);
pragma "no doc"
proc isImagValue(e)      param  return isImagType(e.type);
pragma "no doc"
proc isComplexValue(e)   param  return isComplexType(e.type);
pragma "no doc"
proc isStringValue(e)    param  return isStringType(e.type);
pragma "no doc"
proc isIntegralValue(e)  param  return isIntegralType(e.type);
pragma "no doc"
proc isFloatValue(e)     param  return isFloatType(e.type);
pragma "no doc"
proc isNumericValue(e)   param  return isNumericType(e.type);
pragma "no doc"
proc isPrimitiveValue(e) param  return isPrimitiveType(e.type);
pragma "no doc"
proc isEnumValue(e)      param  return isEnumType(e.type);
//Defined elsewhere:
// isTupleValue
// isHomogeneousTupleValue
pragma "no doc"
proc isClassValue(e)     param  return isClassType(e.type);
pragma "no doc"
proc isRecordValue(e)    param  return isRecordType(e.type);
pragma "no doc"
proc isUnionValue(e)     param  return isUnionType(e.type);
//Defined elsewhere:
// isRangeValue
// isDmapValue
// isDomainValue
// isArrayValue
// isSyncValue
// isSingleValue
pragma "no doc"
proc isAtomicValue(e)    param  return isAtomicType(e.type);

//
// ixXxx() - the argument can be either a type or a value
//

// Set 1 - types.
// In chpldoc, isBool(e) et al. will apply to types as well.
pragma "no doc"
proc isBool(type t)      param  return isBoolType(t);
pragma "no doc"
proc isInt(type t)       param  return isIntType(t);
pragma "no doc"
proc isUint(type t)      param  return isUintType(t);
pragma "no doc"
proc isReal(type t)      param  return isRealType(t);
pragma "no doc"
proc isImag(type t)      param  return isImagType(t);
pragma "no doc"
proc isComplex(type t)   param  return isComplexType(t);
pragma "no doc"
proc isString(type t)    param  return isStringType(t);
pragma "no doc"
proc isIntegral(type t)  param  return isIntegralType(t);
pragma "no doc"
proc isFloat(type t)     param  return isFloatType(t);
pragma "no doc"
proc isNumeric(type t)   param  return isNumericType(t);
pragma "no doc"
proc isPrimitive(type t) param  return isPrimitiveType(t);
pragma "no doc"
proc isEnum(type t)      param  return isEnumType(t);
pragma "no doc"
proc isTuple(type t)     param  return isTupleType(t);
pragma "no doc"
proc isHomogeneousTuple(type t)  param  return isHomogeneousTupleType(t);
pragma "no doc"
proc isClass(type t)     param  return isClassType(t);
pragma "no doc"
proc isRecord(type t)    param  return isRecordType(t);
pragma "no doc"
proc isUnion(type t)     param  return isUnionType(t);
pragma "no doc"
proc isRange(type t)     param  return isRangeType(t);
pragma "no doc"
proc isDmap(type t)      param  return isDmapType(t);
pragma "no doc"
proc isDomain(type t)    param  return isDomainType(t);
pragma "no doc"
proc isArray(type t)     param  return isArrayType(t);
pragma "no doc"
proc isSync(type t)      param  return isSyncType(t);
pragma "no doc"
proc isSingle(type t)    param  return isSingleType(t);
pragma "no doc"
proc isAtomic(type t)    param  return isAtomicType(t);

// Set 2 - values.
/*
Returns `true` if the argument is a primitive type,
as defined by the language specification, or a value of a primitive type.
*/
proc isPrimitive(e) param  return isPrimitiveValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, `real`, `imag`, `complex`, or a value of such a type.
*/
proc isNumeric(e)   param  return isNumericValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`int`, `uint`, or a value of such a type.
*/
proc isIntegral(e)  param  return isIntegralValue(e);
/*
Returns `true` if the argument is one the following types, of any width:
`real`, `imag`, or a value of such a type.
*/
proc isFloat(e)     param  return isFloatValue(e);
// We document the following functions as one group, for user convenience.
proc isBool(e)      param  return isBoolValue(e);
proc isInt(e)       param  return isIntValue(e);
proc isUint(e)      param  return isUintValue(e);
proc isReal(e)      param  return isRealValue(e);
proc isImag(e)      param  return isImagValue(e);
proc isComplex(e)   param  return isComplexValue(e);
proc isString(e)    param  return isStringValue(e);
proc isEnum(e)      param  return isEnumValue(e);
proc isTuple(e)     param  return isTupleValue(e);
proc isHomogeneousTuple(e: _tuple)  param  return isHomogeneousTupleValue(e);
proc isClass(e)     param  return isClassValue(e);
proc isRecord(e)    param  return isRecordValue(e);
proc isUnion(e)     param  return isUnionValue(e);
proc isRange(e)     param  return isRangeValue(e);
proc isDmap(e)      param  return isDmapValue(e);
proc isDomain(e)    param  return isDomainValue(e);
proc isArray(e)     param  return isArrayValue(e);
pragma "no doc"
proc isSync(e: sync)     param  return true; // workaround: not isSyncValue
proc isSync(e)           param  return false;
pragma "no doc"
proc isSingle(e: single) param  return true; // workaround: not isSingleValue
proc isSingle(e)         param  return false;
// Here is a single doc comment for the above.
/*
Each of the above functions returns `true` if its argument is
a corresponding type or a value of such a type.
*/
proc isAtomic(e)    param  return isAtomicValue(e);

// for internal use until we have a better name
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)         param  return false;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: sync)   param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e: single) param  return true;
pragma "no doc"
proc chpl_isSyncSingleAtomic(e)  param where isAtomicType(e.type)  return true;

// Is 'sub' a subtype (or equal to) 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`. */
proc isSubtype(type sub, type super) param where   sub: super  return true;
pragma "no doc"
proc isSubtype(type sub, type super) param where !(sub: super) return false;

// Is 'sub' a proper subtype of 'super'?
/* Returns `true` if the type `sub` is a subtype of the type `super`
   and is not `super`. */
proc isProperSubtype(type sub, type super) param
  where isSubtype(sub, super) && sub != super
  return true;
pragma "no doc"
proc isProperSubtype(type sub, type super) param
  return false;

// Returns true if it is legal to coerce t1 to t2, false otherwise.
pragma "no doc"
proc chpl__legalIntCoerce(type t1, type t2) param
{
  if (isIntType(t2)) {
    if (isIntType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (isIntType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

// Returns the type with which both s and t are compatible
// That is, both s and t can be coerced to the returned type.
pragma "no doc"
private proc chpl__commonType(type s, type t) type
{
  if ! isIntegralType(s) then
    compilerError("Type ", typeToString(s) , " is non-integral: ");
  if ! isIntegralType(t) then
    compilerError("Type ", typeToString(t) , " is non-integral: ");

  if numBits(s) > numBits(t) then return s;
  if numBits(s) < numBits(t) then return t;

  if isIntType(s) && ! isIntType(t) ||
     isIntType(t) && ! isIntType(s) then
    compilerError("Types ", typeToString(s) , " and ", typeToString(t), " are incompatible.");

  return s;
}

//
// numBits(type) -- returns the number of bits in a type
//
// joint documentation, for user convenience
/*
Returns the number of bits used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBits(type t) param where t == bool {
  compilerError("default-width 'bool' does not have a well-defined size");
}
pragma "no doc"
proc numBits(type t) param where t == bool(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == bool(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == bool(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == bool(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == int(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == int(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == int(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == int(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == uint(8) return 8;
pragma "no doc"
proc numBits(type t) param where t == uint(16) return 16;
pragma "no doc"
proc numBits(type t) param where t == uint(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == uint(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == real(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == real(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == imag(32) return 32;
pragma "no doc"
proc numBits(type t) param where t == imag(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(64) return 64;
pragma "no doc"
proc numBits(type t) param where t == complex(128) return 128;

//
// numBytes(type) -- returns the number of bytes in a type
//

pragma "no doc" // do we want to document this one?
param bitsPerByte = 8;

/*
Returns the number of bytes used to store the values of type `t`.
This is available for all numeric types, fixed-width `bool` types,
and ``enum`` types.
It is not available for default-width `bool`.
*/
proc numBytes(type t) param return numBits(t)/8;

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc min(type t) param  where isBool(t)      return false: t;

pragma "no doc"
proc min(type t) param  where t == int(8)    return 0x80: t;
pragma "no doc"
proc min(type t) param  where t == int(16)   return 0x8000: t;
pragma "no doc"
proc min(type t) param  where t == int(32)   return 0x80000000: t;
pragma "no doc"
proc min(type t) param  where t == int(64)   return 0x8000000000000000: t;

pragma "no doc"
proc min(type t) param  where isUint(t)      return 0: t;

pragma "no doc"
proc min(type t) where isFloatType(t)        return __primitive( "_min", t);

pragma "no doc"
proc min(type t) where isComplexType(t) {
  var x: t;
  x.re = min(x.re.type);
  x.im = min(x.im.type);
  return x;
}

// joint documentation, for user convenience
/*
Returns the minimum value the type `t` can store.
`t` can be one of the following types, of any width:
`bool`, `int`, `uint`, `real`, `imag`, `complex`.
When `t` is a `bool` type, it returns `false`.
When `t` is a `real`, `imag`, or `complex` type,
it is a non-``param`` function.
*/
proc max(type t) param  where isBool(t)      return true: t;

pragma "no doc"
proc max(type t) param  where t == int(8)    return 0x7f: t;
pragma "no doc"
proc max(type t) param  where t == int(16)   return 0x7fff: t;
pragma "no doc"
proc max(type t) param  where t == int(32)   return 0x7fffffff: t;
pragma "no doc"
proc max(type t) param  where t == int(64)   return 0x7fffffffffffffff: t;

pragma "no doc"
proc max(type t) param  where t == uint(8)   return 0xff: t;
pragma "no doc"
proc max(type t) param  where t == uint(16)  return 0xffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(32)  return 0xffffffff: t;
pragma "no doc"
proc max(type t) param  where t == uint(64)  return 0xffffffffffffffff: t;

pragma "no doc"
proc max(type t) where isFloatType(t)        return __primitive( "_max", t);

pragma "no doc"
proc max(type t) where isComplexType(t) {
  var x: t;
  x.re = max(x.re.type);
  x.im = max(x.im.type);
  return x;
}

pragma "no doc"
iter chpl_enumerate(type t: enumerated) {
  const enumTuple = chpl_enum_enumerate(t);
  for i in 1..enumTuple.size do
    yield enumTuple(i);
}

// TODO add chpl_ to these functions' names - they are not intended for user.
pragma "no doc"
private proc enum_minbits(type t: enumerated) param {
  return __primitive( "enum min bits", t);
}
pragma "no doc"
private proc enum_issigned(type t: enumerated) param {
  return __primitive( "enum is signed", t);
}
// TODO - maybe this function can be useful for the user, for C interop?
// If so, give it a different name.
pragma "no doc"
proc enum_mintype(type t: enumerated) type {
  param minbits = enum_minbits(t);
  param signed = enum_issigned(t);
  if signed {
    return int(minbits);
  } else {
    return uint(minbits);
  }
}

pragma "no doc" // documented with the other numBits() above
proc numBits(type t: enumerated) param {
  return numBits(enum_mintype(t));
}

/*
Returns `this`, cast to the type `T`.
Generates a run-time error if `this` cannot be represented by `T`,
for example ``(-1).safeCast(uint)`` or ``256.safeCast(uint(8))``.

This method performs the minimum number of runtime checks.
For example, when casting from `uint(8)` to `uint(64)`,
no checks at all will be done.
*/
inline proc integral.safeCast(type T) : T where isUintType(T) {
  if castChecking {
    if isIntType(this.type) {
      // int(?) -> uint(?)
      if this < 0 then // runtime check
        halt("casting "+typeToString(this.type)+" less than 0 to "+typeToString(T));
    }

    if max(this.type):uint > max(T):uint {
      // [u]int(?) -> uint(?)
      if (this:uint > max(T):uint) then // runtime check
        halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
             typeToString(T)+" to "+typeToString(T));
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
inline proc integral.safeCast(type T) : T where isIntType(T) {
  if castChecking {
    if max(this.type):uint > max(T):uint {
      // this isUintType check lets us avoid a runtime check for this < 0
      if isUintType(this.type) {
        // uint(?) -> int(?)
        if this:uint > max(T):uint then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      } else {
        // int(?) -> int(?)
        // max(T) <= max(int), so cast to int is safe
        if this:int > max(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value greater than the maximum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
    if isIntType(this.type) {
      if min(this.type):int < min(T):int {
        // int(?) -> int(?)
        if this:int < min(T):int then // runtime check
          halt("casting "+typeToString(this.type)+" with a value less than the minimum of "+
               typeToString(T)+" to "+typeToString(T));
      }
    }
  }
  return this:T;
}

pragma "no doc" // documented with the other safeCast above
proc integral.safeCast(type T) {
  compilerError("safeCast is only supported between integral types");
}

//
// identity functions (for reductions)
//
pragma "no doc"
inline proc _prod_id(type t) return 1:t;
pragma "no doc"
inline proc _land_id(type t) return true;
pragma "no doc"
inline proc _lor_id(type t) return false;
pragma "no doc"
inline proc _lxor_id(type t) return false;
pragma "no doc"
inline proc _band_id(type t) {
  // MPF - this doesn't really make sense for floating-point types,
  // but old code had it casting MAX_UINT to the same type...
  // I just moved it from the code generator since we don't need
  // a primitive to generate constants.
  if t == bool then return true;
  if t == int(8) then return -1:t;
  if t == int(16) then return -1:t;
  if t == int(32) then return -1:t;
  if t == int(64) then return -1:t;
  if t == real(32) then return max(uint(32)):t;
  if t == real(64) then return max(uint(64)):t;
  if t == imag(32) then return max(uint(32)):t;
  if t == imag(64) then return max(uint(64)):t;
  if t == complex(64) then return (max(uint(32)):real(32), max(uint(32)):real(32)):t;
  if t == complex(128) then return (max(uint(64)):real(64), max(uint(64)):real(64)):t;
  return max(t);
}
pragma "no doc"
inline proc _bor_id(type t) return 0:t;
pragma "no doc"
inline proc _bxor_id(type t) return 0:t;

} // module Types


Adding another cast for cases when ssize_t != int64_t

This is similar to the casts inserted in r19807, and is simply in a case
that wasn't caught by other tests in examples/ (and which I apparently
failed to search for adequately).

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

use SysBasic;

// here's what we need from Sys
extern proc sys_strerror_str(error:syserr, inout err_in_strerror:syserr):string;
// here's what we need from QIO
extern proc qio_quote_string_chpl(ptr:string, len:ssize_t):string;

proc ioerror(error:syserr, msg:string)
{
  var errstr:string;
  var strerror_err:syserr = ENOERR;
  errstr = sys_strerror_str(error, strerror_err);
  __primitive("chpl_error", errstr + " " + msg);
}

proc ioerror(error:syserr, msg:string, path:string)
{
  if( error ) {
    var errstr:string;
    var quotedpath:string;
    var strerror_err:syserr = ENOERR;
    errstr = sys_strerror_str(error, strerror_err);
    quotedpath = qio_quote_string_chpl(path, path.length:ssize_t);
    __primitive("chpl_error", errstr + " " + msg + " with path " + quotedpath);
  }
}

proc ioerror(error:syserr, msg:string, path:string, offset:int(64))
{
  var errstr:string;
  var quotedpath:string;
  var strerror_err:syserr = ENOERR;
  errstr = sys_strerror_str(error, strerror_err);
  quotedpath = qio_quote_string_chpl(path, path.length:ssize_t);
  __primitive("chpl_error", errstr + " " + msg + " with path " + quotedpath + " offset " + offset:string);
}

proc ioerror(errstr:string, msg:string, path:string, offset:int(64))
{
  var quotedpath:string;
  quotedpath = qio_quote_string_chpl(path, path.length:ssize_t);
  __primitive("chpl_error", errstr + " " + msg + " with path " + quotedpath + " offset " + offset:string);
}

proc errorToString(error:syserr):string
{
  var errstr:string = "unknown";
  var strerror_err:syserr = ENOERR;
  errstr = sys_strerror_str(error, strerror_err);
  return errstr;
}

use SysBasic;

// here's what we need from Sys
extern proc sys_strerror_str(error:syserr, inout err_in_strerror:syserr):string;
// here's what we need from QIO
extern proc qio_quote_string_chpl(ptr:string, len:ssize_t):string;

proc ioerror(error:syserr, msg:string)
{
  var errstr:string;
  var strerror_err:syserr = ENOERR;
  errstr = sys_strerror_str(error, strerror_err);
  __primitive("chpl_error", errstr + " " + msg);
}

proc ioerror(error:syserr, msg:string, path:string)
{
  if( error ) {
    var errstr:string;
    var quotedpath:string;
    var strerror_err:syserr = ENOERR;
    errstr = sys_strerror_str(error, strerror_err);
    quotedpath = qio_quote_string_chpl(path, path.length:ssize_t);
    __primitive("chpl_error", errstr + " " + msg + " with path " + quotedpath);
  }
}

proc ioerror(error:syserr, msg:string, path:string, offset:int(64))
{
  var errstr:string;
  var quotedpath:string;
  var strerror_err:syserr = ENOERR;
  errstr = sys_strerror_str(error, strerror_err);
  quotedpath = qio_quote_string_chpl(path, path.length:ssize_t);
  __primitive("chpl_error", errstr + " " + msg + " with path " + quotedpath + " offset " + offset:string);
}

proc ioerror(errstr:string, msg:string, path:string, offset:int(64))
{
  var quotedpath:string;
  quotedpath = qio_quote_string_chpl(path, path.length);
  __primitive("chpl_error", errstr + " " + msg + " with path " + quotedpath + " offset " + offset:string);
}

proc errorToString(error:syserr):string
{
  var errstr:string = "unknown";
  var strerror_err:syserr = ENOERR;
  errstr = sys_strerror_str(error, strerror_err);
  return errstr;
}


Apparently I hadn't saved since moving the commented out _defaultOf for distributions.  Commit that change so there aren't two copies in the modules.

// Types.chpl
//
// Standard type routines.
//

//
// type predicates
//
proc chpl__isType(type t) param return true;
proc chpl__isType(e) param return false;

pragma "no instantiation limit"
proc _isPrimitiveType(type t) param return
  _isBooleanType(t)  ||
  _isIntegralType(t) ||
  _isRealType(t)     ||
//To allow imag, need to define casts from primitive types into imag.
//_isImagType(t)     ||
  (t == c_string);

pragma "no instantiation limit"
proc _isSimpleScalarType(type t) param return
  _isBooleanType(t) | _isIntegralType(t) | _isFloatType(t);

pragma "no instantiation limit"
proc _isBooleanType(type t) param return
  (t == bool) | (t == bool(8)) | (t == bool(16)) | (t == bool(32)) | (t == bool(64));

pragma "no instantiation limit"
proc _isIntegralType(type t) param return
  _isSignedType(t) || _isUnsignedType(t);

pragma "no instantiation limit"
proc _isSignedType(type t) param return
  (t == int(8)) || (t == int(16)) || (t == int(32)) || (t == int(64));

pragma "no instantiation limit"
proc _isUnsignedType(type t) param return
  (t == uint(8)) || (t == uint(16)) || (t == uint(32)) || (t == uint(64));

pragma "no instantiation limit"
proc _isEnumeratedType(type t) param {
  proc isEnum(type t: enumerated) param return true;
  proc isEnum(type t) param return false;
  return isEnum(t);
}

pragma "no instantiation limit"
proc _isComplexType(type t) param return
  (t == complex(64)) | (t == complex(128));

pragma "no instantiation limit"
proc _isFloatType(type t) param return
  (t == real(32)) | (t == real(64)) |
  (t == imag(32)) | (t == imag(64));

pragma "no instantiation limit"
proc _isRealType(type t) param return
  (t == real(32)) | (t == real(64));

pragma "no instantiation limit"
proc _isImagType(type t) param return
  (t == imag(32)) | (t == imag(64));

// Returns the unsigned equivalent of the input type.
proc chpl__unsignedType(type t) type
{
  if ! _isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return uint(numBits(t));
}

// Returns the signed equivalent of the input type.
proc chpl__signedType(type t) type
{
  if ! _isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return int(numBits(t));
}

proc chpl__maxIntTypeSameSign(type t) type {
  if ! _isIntegralType(t) then
    compilerError("type t is non-integral: ", typeToString(t));

  if (_isSignedType(t)) then
    return int(64);
  else
    return uint(64);
}

//
// These procedures indicate whether or not a type t is a specific type
//
proc isRangeType(type t) param where   t: range  return true;
proc isRangeType(type t) param where !(t: range) return false;

proc isDmapType(type t) param where   t: _distribution  return true;
proc isDmapType(type t) param where !(t: _distribution) return false;

proc isDomainType(type t) param where   t: _domain  return true;
proc isDomainType(type t) param where !(t: _domain) return false;

proc isArrayType(type t) param where   t: _array  return true;
proc isArrayType(type t) param where !(t: _array) return false;

// Is 'sub' a subtype (or equal to) 'super'?
proc isSubtype(type sub, type super) param where   sub: super  return true;
proc isSubtype(type sub, type super) param where !(sub: super) return false;

// Is 'sub' a proper subtype of 'super'?
proc isProperSubtype(type sub, type super) param
  where isSubtype(sub, super) && sub != super
  return true;
proc isProperSubtype(type sub, type super) param
  return false;

// What follows are the type _defaultOf methods, used to initialize types
// Booleans
inline proc _defaultOf(type t) param where (_isBooleanType(t)) return false:t;

// ints, reals, imags, complexes
inline proc _defaultOf(type t) param where (_isIntegralType(t)) return 0:t;
// TODO: In order to make _defaultOf param for reals and imags we had to split
// the cases into their default size and a non-param case.  It is hoped that
// in the future, floating point numbers may be castable whilst param.  In that
// world, we can again shrink these calls into the size-ignorant case.
inline proc _defaultOf(type t) param where t == real(64) return 0.0:t;
inline proc _defaultOf(type t) where (_isRealType(t) && t != real(64)) return 0.0:t;
inline proc _defaultOf(type t) param where t == imag(64) return 0.0i:t;
inline proc _defaultOf(type t) where (_isImagType(t) && t != imag(64)) return 0.0i:t;
// Also, complexes cannot yet be parametized
inline proc _defaultOf(type t): t where (_isComplexType(t)) {
  var ret:t = noinit;
  param floatwidth = numBits(t)/2;
  ret.re = 0.0:real(floatwidth);
  ret.im = 0.0:real(floatwidth);
  return ret;
}

// Enums
inline proc _defaultOf(type t) param where (_isEnumeratedType(t)) {
  return chpl_enum_first(t);
}

// Classes
inline proc _defaultOf(type t) where (isClassType(t)) return nil:t;

// Various types whose default value is known
inline proc _defaultOf(type t) param where t: void return _void;
inline proc _defaultOf(type t) where t: opaque return _nullOpaque;
inline proc _defaultOf(type t) where t: chpl_taskID_t return chpl_nullTaskID;
inline proc _defaultOf(type t) where t: _sync_aux_t return _nullSyncVarAuxFields;
inline proc _defaultOf(type t) where t: _single_aux_t return _nullSingleVarAuxFields;
inline proc _defaultOf(type t) where t: _task_list return _nullTaskList;

// When I finish removing PRIM_INIT before initialization to a known value, then
// this method should work.  Until then, my stopgap will be an external function
// in the runtime.
//inline proc _defaultOf(type t) where t: memory_order return memory_order_seq_cst;
extern proc _defaultOfMemoryOrder(): memory_order;

pragma "no instantiation limit"
pragma "compiler generated"
inline proc _defaultOf(type t) {
  select t {
    when memory_order {
      return _defaultOfMemoryOrder();
    }
    otherwise {
      return nil:t;
    }
  }
}

// Returns true if it is legal to coerce t1 to t2, false otherwise.
proc chpl__legalIntCoerce(type t1, type t2) param
{
  if (_isSignedType(t2)) {
    if (_isSignedType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (_isSignedType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

// Returns the type with which both s and t are compatible
// That is, both s and t can be coerced to the returned type.
proc chpl__commonType(type s, type t) type
{
  if ! _isIntegralType(s) then
    compilerError("Type ", typeToString(s) , " is non-integral: ");
  if ! _isIntegralType(t) then
    compilerError("Type ", typeToString(t) , " is non-integral: ");

  if numBits(s) > numBits(t) then return s;
  if numBits(s) < numBits(t) then return t;

  if _isSignedType(s) && ! _isSignedType(t) ||
     _isSignedType(t) && ! _isSignedType(s) then
    compilerError("Types ", typeToString(s) , " and ", typeToString(t), " are incompatible.");

  return s;
}

//
// numBits(type) -- returns the number of bits in a type
//

proc numBits(type t) param where t == bool {
  compilerError("default-width 'bool' does not have a well-defined size");
}
proc numBits(type t) param where t == bool(8) return 8;
proc numBits(type t) param where t == bool(16) return 16;
proc numBits(type t) param where t == bool(32) return 32;
proc numBits(type t) param where t == bool(64) return 64;
proc numBits(type t) param where t == int(8) return 8;
proc numBits(type t) param where t == int(16) return 16;
proc numBits(type t) param where t == int(32) return 32;
proc numBits(type t) param where t == int(64) return 64;
proc numBits(type t) param where t == uint(8) return 8;
proc numBits(type t) param where t == uint(16) return 16;
proc numBits(type t) param where t == uint(32) return 32;
proc numBits(type t) param where t == uint(64) return 64;
proc numBits(type t) param where t == real(32) return 32;
proc numBits(type t) param where t == real(64) return 64;
proc numBits(type t) param where t == imag(32) return 32;
proc numBits(type t) param where t == imag(64) return 64;
proc numBits(type t) param where t == complex(64) return 64;
proc numBits(type t) param where t == complex(128) return 128;

//
// numBytes(type) -- returns the number of bytes in a type
//

param bitsPerByte = 8;

proc numBytes(type t) param return numBits(t)/8;

//
// min(type) -- returns the minimum value a type can store
//

proc min(type t) where t == bool
  return false;

proc min(type t) where _isIntegralType(t) || _isFloatType(t)
  return __primitive( "_min", t);

proc min(type t) where _isComplexType(t) {
  var x: t;
  x.re = min(x.re.type);
  x.im = min(x.im.type);
  return x;
}

//
// max(type) -- returns the maximum value a type can store
//

proc max(type t) where t == bool
  return true;

proc max(type t) where _isIntegralType(t) || _isFloatType(t)
  return __primitive( "_max", t);

proc max(type t) where _isComplexType(t) {
  var x: t;
  x.re = max(x.re.type);
  x.im = max(x.im.type);
  return x;
}

iter chpl_enumerate(type t: enumerated) {
  const enumTuple = chpl_enum_enumerate(t);
  for i in 1..enumTuple.size do
    yield enumTuple(i);
}

proc enum_minbits(type t: enumerated) param {
  return __primitive( "enum min bits", t);
}
proc enum_issigned(type t: enumerated) param {
  return __primitive( "enum is signed", t);
}
proc enum_mintype(type t: enumerated) type {
  param minbits = enum_minbits(t);
  param signed = enum_issigned(t);
  if signed {
    return int(minbits);
  } else {
    return uint(minbits);
  }
}

proc numBits(type t: enumerated) param {
  return numBits(enum_mintype(t));
}

//
// identity functions (for reductions)
//
inline proc _prod_id(type t) return 1:t;
inline proc _land_id(type t) return true;
inline proc _lor_id(type t) return false;
inline proc _lxor_id(type t) return false;
inline proc _band_id(type t) {
  // MPF - this doesn't really make sense for floating-point types,
  // but old code had it casting MAX_UINT to the same type...
  // I just moved it from the code generator since we don't need
  // a primitive to generate constants.
  if t == bool then return true;
  if t == int(8) then return -1:t;
  if t == int(16) then return -1:t;
  if t == int(32) then return -1:t;
  if t == int(64) then return -1:t;
  if t == real(32) then return max(uint(32)):t;
  if t == real(64) then return max(uint(64)):t;
  if t == imag(32) then return max(uint(32)):t;
  if t == imag(64) then return max(uint(64)):t;
  if t == complex(64) then return (max(uint(32)):real(32), max(uint(32)):real(32)):t;
  if t == complex(128) then return (max(uint(64)):real(64), max(uint(64)):real(64)):t;
  return max(t);
}
inline proc _bor_id(type t) return 0:t;
inline proc _bxor_id(type t) return 0:t;

// Types.chpl
//
// Standard type routines.
//

//
// type predicates
//
proc chpl__isType(type t) param return true;
proc chpl__isType(e) param return false;

pragma "no instantiation limit"
proc _isPrimitiveType(type t) param return
  _isBooleanType(t)  ||
  _isIntegralType(t) ||
  _isRealType(t)     ||
//To allow imag, need to define casts from primitive types into imag.
//_isImagType(t)     ||
  (t == c_string);

pragma "no instantiation limit"
proc _isSimpleScalarType(type t) param return
  _isBooleanType(t) | _isIntegralType(t) | _isFloatType(t);

pragma "no instantiation limit"
proc _isBooleanType(type t) param return
  (t == bool) | (t == bool(8)) | (t == bool(16)) | (t == bool(32)) | (t == bool(64));

pragma "no instantiation limit"
proc _isIntegralType(type t) param return
  _isSignedType(t) || _isUnsignedType(t);

pragma "no instantiation limit"
proc _isSignedType(type t) param return
  (t == int(8)) || (t == int(16)) || (t == int(32)) || (t == int(64));

pragma "no instantiation limit"
proc _isUnsignedType(type t) param return
  (t == uint(8)) || (t == uint(16)) || (t == uint(32)) || (t == uint(64));

pragma "no instantiation limit"
proc _isEnumeratedType(type t) param {
  proc isEnum(type t: enumerated) param return true;
  proc isEnum(type t) param return false;
  return isEnum(t);
}

pragma "no instantiation limit"
proc _isComplexType(type t) param return
  (t == complex(64)) | (t == complex(128));

pragma "no instantiation limit"
proc _isFloatType(type t) param return
  (t == real(32)) | (t == real(64)) |
  (t == imag(32)) | (t == imag(64));

pragma "no instantiation limit"
proc _isRealType(type t) param return
  (t == real(32)) | (t == real(64));

pragma "no instantiation limit"
proc _isImagType(type t) param return
  (t == imag(32)) | (t == imag(64));

// Returns the unsigned equivalent of the input type.
proc chpl__unsignedType(type t) type
{
  if ! _isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return uint(numBits(t));
}

// Returns the signed equivalent of the input type.
proc chpl__signedType(type t) type
{
  if ! _isIntegralType(t) then
    compilerError("range idxType is non-integral: ", typeToString(t));

  return int(numBits(t));
}

proc chpl__maxIntTypeSameSign(type t) type {
  if ! _isIntegralType(t) then
    compilerError("type t is non-integral: ", typeToString(t));

  if (_isSignedType(t)) then
    return int(64);
  else
    return uint(64);
}

//
// These procedures indicate whether or not a type t is a specific type
//
proc isRangeType(type t) param where   t: range  return true;
proc isRangeType(type t) param where !(t: range) return false;

proc isDmapType(type t) param where   t: _distribution  return true;
proc isDmapType(type t) param where !(t: _distribution) return false;

proc isDomainType(type t) param where   t: _domain  return true;
proc isDomainType(type t) param where !(t: _domain) return false;

proc isArrayType(type t) param where   t: _array  return true;
proc isArrayType(type t) param where !(t: _array) return false;

// Is 'sub' a subtype (or equal to) 'super'?
proc isSubtype(type sub, type super) param where   sub: super  return true;
proc isSubtype(type sub, type super) param where !(sub: super) return false;

// Is 'sub' a proper subtype of 'super'?
proc isProperSubtype(type sub, type super) param
  where isSubtype(sub, super) && sub != super
  return true;
proc isProperSubtype(type sub, type super) param
  return false;

// What follows are the type _defaultOf methods, used to initialize types
// Booleans
inline proc _defaultOf(type t) param where (_isBooleanType(t)) return false:t;

// ints, reals, imags, complexes
inline proc _defaultOf(type t) param where (_isIntegralType(t)) return 0:t;
// TODO: In order to make _defaultOf param for reals and imags we had to split
// the cases into their default size and a non-param case.  It is hoped that
// in the future, floating point numbers may be castable whilst param.  In that
// world, we can again shrink these calls into the size-ignorant case.
inline proc _defaultOf(type t) param where t == real(64) return 0.0:t;
inline proc _defaultOf(type t) where (_isRealType(t) && t != real(64)) return 0.0:t;
inline proc _defaultOf(type t) param where t == imag(64) return 0.0i:t;
inline proc _defaultOf(type t) where (_isImagType(t) && t != imag(64)) return 0.0i:t;
// Also, complexes cannot yet be parametized
inline proc _defaultOf(type t): t where (_isComplexType(t)) {
  var ret:t = noinit;
  param floatwidth = numBits(t)/2;
  ret.re = 0.0:real(floatwidth);
  ret.im = 0.0:real(floatwidth);
  return ret;
}

// Enums
inline proc _defaultOf(type t) param where (_isEnumeratedType(t)) {
  return chpl_enum_first(t);
}

// Classes
inline proc _defaultOf(type t) where (isClassType(t)) return nil:t;

// Various types whose default value is known
inline proc _defaultOf(type t) param where t: void return _void;
inline proc _defaultOf(type t) where t: opaque return _nullOpaque;
inline proc _defaultOf(type t) where t: chpl_taskID_t return chpl_nullTaskID;
inline proc _defaultOf(type t) where t: _sync_aux_t return _nullSyncVarAuxFields;
inline proc _defaultOf(type t) where t: _single_aux_t return _nullSingleVarAuxFields;
inline proc _defaultOf(type t) where t: _task_list return _nullTaskList;

/* Need new <alias>() for this to function
proc _defaultOf(type t) where t:_distribution {
  var ret: t = noinit;
  type valType = __primitive("query type field", t, "_valueType");
  var typeInstance = new <valType>();
  ret = chpl__buildDistValue(typeInstance);
  return ret;
}
*/

// When I finish removing PRIM_INIT before initialization to a known value, then
// this method should work.  Until then, my stopgap will be an external function
// in the runtime.
//inline proc _defaultOf(type t) where t: memory_order return memory_order_seq_cst;
extern proc _defaultOfMemoryOrder(): memory_order;

pragma "no instantiation limit"
pragma "compiler generated"
inline proc _defaultOf(type t) {
  select t {
    when memory_order {
      return _defaultOfMemoryOrder();
    }
    otherwise {
      return nil:t;
    }
  }
}

// Returns true if it is legal to coerce t1 to t2, false otherwise.
proc chpl__legalIntCoerce(type t1, type t2) param
{
  if (_isSignedType(t2)) {
    if (_isSignedType(t1)) {
      return (numBits(t1) <= numBits(t2));
    } else {
      return (numBits(t1) < numBits(t2));
    }
  } else {
    if (_isSignedType(t1)) {
      return false;
    } else {
      return (numBits(t1) <= numBits(t2));
    }
  }
}

// Returns the type with which both s and t are compatible
// That is, both s and t can be coerced to the returned type.
proc chpl__commonType(type s, type t) type
{
  if ! _isIntegralType(s) then
    compilerError("Type ", typeToString(s) , " is non-integral: ");
  if ! _isIntegralType(t) then
    compilerError("Type ", typeToString(t) , " is non-integral: ");

  if numBits(s) > numBits(t) then return s;
  if numBits(s) < numBits(t) then return t;

  if _isSignedType(s) && ! _isSignedType(t) ||
     _isSignedType(t) && ! _isSignedType(s) then
    compilerError("Types ", typeToString(s) , " and ", typeToString(t), " are incompatible.");

  return s;
}

//
// numBits(type) -- returns the number of bits in a type
//

proc numBits(type t) param where t == bool {
  compilerError("default-width 'bool' does not have a well-defined size");
}
proc numBits(type t) param where t == bool(8) return 8;
proc numBits(type t) param where t == bool(16) return 16;
proc numBits(type t) param where t == bool(32) return 32;
proc numBits(type t) param where t == bool(64) return 64;
proc numBits(type t) param where t == int(8) return 8;
proc numBits(type t) param where t == int(16) return 16;
proc numBits(type t) param where t == int(32) return 32;
proc numBits(type t) param where t == int(64) return 64;
proc numBits(type t) param where t == uint(8) return 8;
proc numBits(type t) param where t == uint(16) return 16;
proc numBits(type t) param where t == uint(32) return 32;
proc numBits(type t) param where t == uint(64) return 64;
proc numBits(type t) param where t == real(32) return 32;
proc numBits(type t) param where t == real(64) return 64;
proc numBits(type t) param where t == imag(32) return 32;
proc numBits(type t) param where t == imag(64) return 64;
proc numBits(type t) param where t == complex(64) return 64;
proc numBits(type t) param where t == complex(128) return 128;

//
// numBytes(type) -- returns the number of bytes in a type
//

param bitsPerByte = 8;

proc numBytes(type t) param return numBits(t)/8;

//
// min(type) -- returns the minimum value a type can store
//

proc min(type t) where t == bool
  return false;

proc min(type t) where _isIntegralType(t) || _isFloatType(t)
  return __primitive( "_min", t);

proc min(type t) where _isComplexType(t) {
  var x: t;
  x.re = min(x.re.type);
  x.im = min(x.im.type);
  return x;
}

//
// max(type) -- returns the maximum value a type can store
//

proc max(type t) where t == bool
  return true;

proc max(type t) where _isIntegralType(t) || _isFloatType(t)
  return __primitive( "_max", t);

proc max(type t) where _isComplexType(t) {
  var x: t;
  x.re = max(x.re.type);
  x.im = max(x.im.type);
  return x;
}

iter chpl_enumerate(type t: enumerated) {
  const enumTuple = chpl_enum_enumerate(t);
  for i in 1..enumTuple.size do
    yield enumTuple(i);
}

proc enum_minbits(type t: enumerated) param {
  return __primitive( "enum min bits", t);
}
proc enum_issigned(type t: enumerated) param {
  return __primitive( "enum is signed", t);
}
proc enum_mintype(type t: enumerated) type {
  param minbits = enum_minbits(t);
  param signed = enum_issigned(t);
  if signed {
    return int(minbits);
  } else {
    return uint(minbits);
  }
}

proc numBits(type t: enumerated) param {
  return numBits(enum_mintype(t));
}

//
// identity functions (for reductions)
//
inline proc _prod_id(type t) return 1:t;
inline proc _land_id(type t) return true;
inline proc _lor_id(type t) return false;
inline proc _lxor_id(type t) return false;
inline proc _band_id(type t) {
  // MPF - this doesn't really make sense for floating-point types,
  // but old code had it casting MAX_UINT to the same type...
  // I just moved it from the code generator since we don't need
  // a primitive to generate constants.
  if t == bool then return true;
  if t == int(8) then return -1:t;
  if t == int(16) then return -1:t;
  if t == int(32) then return -1:t;
  if t == int(64) then return -1:t;
  if t == real(32) then return max(uint(32)):t;
  if t == real(64) then return max(uint(64)):t;
  if t == imag(32) then return max(uint(32)):t;
  if t == imag(64) then return max(uint(64)):t;
  if t == complex(64) then return (max(uint(32)):real(32), max(uint(32)):real(32)):t;
  if t == complex(128) then return (max(uint(64)):real(64), max(uint(64)):real(64)):t;
  return max(t);
}
inline proc _bor_id(type t) return 0:t;
inline proc _bxor_id(type t) return 0:t;


Get tests actually running for simple cases, like hello.chpl.

use Exec;
use FileSystem;
use IO;

config const testDir = "/Users/tvandoren/src/chapel/test/release/examples/programs";

config const testverbose = false;
config const printEnv = testverbose;
config const noRecurse = false;

proc main() {
  var testRunner = new TestRunner(testDir, !noRecurse);
  testRunner.runTests();
  delete testRunner;
}

class TestRunner {
  const testDir, chplHome: string,
    recurse: bool;

  proc TestRunner(testDir, recurse) {
    this.testDir = testDir;
    this.recurse = recurse;
    this.chplHome = this.getChplHome();
  }

  proc runTests() {
    setEnv("CHPL_HOME", this.chplHome);
    this.printEnvironment();

    // TODO: forall test in this.findTests() {
    for test in this.findTests() {
      this.runTest(test);
    }
  }

  proc getChplHome() {
    const output = run("chpl --print-chpl-home");
    var chplHome: string;

    for i in 1..output.length {
      if output.substring(i) == "\t" {
        chplHome = output.substring(1..i-1);
        break;
      }
    }

    if testverbose then
      writeln("CHPL_HOME: ", chplHome);

    return chplHome;
  }

  proc printEnvironment() {
    if printEnv {
      const chplEnv = run("$CHPL_HOME/util/printchplenv");
      writeln("### Chapel Environment ###");
      write(chplEnv);
      writeln("##########################");
      writeln();
    }
  }

  proc runTest(test) {
    writeln("testing: ", test);

    var t = new Test(test);
    t.runTest();
    delete t;
  }

  iter findTests() {
    for filename in findfiles(this.testDir, recursive=this.recurse) {
      if this.isChapelFile(filename) {
        yield filename;
      }
    }
  }

  iter findTests(param tag: iterKind)
    where tag == iterKind.standalone
  {
    forall filename in findfiles(this.testDir, recursive=this.recurse) {
      if this.isChapelFile(filename) {
        yield filename;
      }
    }
  }

  proc isChapelFile(filename) {
    return getTestBasename(filename) + ".chpl" == filename;
  }

  proc writeThis(w: Writer) {
    w <~> "TestRunner(";
    w <~> "testDir=";
    w <~> testDir;
    w <~> ")";
  }
}

class Test {
  const test, testDir, testBasename, testName: string;
  var diff: string,
    success: bool;

  proc Test(test) {
    this.test = test;
    (testDir, testBasename) = splitFilename(test);
    testName = getTestBasename(testBasename);

    this.validateTestFiles();
  }

  proc validateTestFiles() {
    // TODO: implement this!
  }

  proc runTest() {
    const testOutput = this.compile() + this.execute();
    if this.checkOutput(testOutput) {
      writeln("SUCCESS: " + this.test);
    } else {
      writeln("ERROR: matching output: " + this.test);
    }
    return this.result;
  }

  proc result {
    // return pass/fail as bool, then diff
    return (this.success, this.diff);
  }

  proc fullTestName {
    return this.testDir + "/" + this.testName;
  }

  proc goodFile {
    return this.fullTestName + ".good";
  }

  proc expectedOutput {
    var f = open(this.goodFile, iomode.r),
      r = f.reader();
    var o: string;
    r.readstring(o);
    r.close();
    f.close();
    return o;
  }

  proc checkOutput(testOutput) {
    const expectedOut = this.expectedOutput;
    this.success = testOutput == expectedOut;
    if !this.success {
      this.diff = this.generateDiff(expectedOut, testOutput);
    }
    return this.success;
  }

  proc actualOutputFile {
    return this.fullTestName + ".out.tmp";
  }

  proc generateDiff(expectedOutput, actualOutput) {
    var f = open(this.actualOutputFile, iomode.cw),
      w = f.writer();
    w.write(actualOutput);
    w.close();
    f.close();

    const cmd = "diff " + this.goodFile + " " + this.actualOutputFile;
    if testverbose then
      writeln("Running diff command: " + cmd);
    return run(cmd);
  }

  proc compile() {
    const cmd = "chpl -o " + this.fullTestName + " " + this.test;
    if testverbose then
      writeln("Running compiler: " + cmd);
    return run(cmd);
  }

  proc execute() {
    const cmd = this.fullTestName;
    if testverbose then
      writeln("Running binary: " + cmd);
    const output = run(cmd);

    if testverbose then
      writeln("Deleting " + this.fullTestName);
    remove(this.fullTestName);

    return output;
  }

  proc writeThis(w: Writer) {
    w <~> "Test(";
    w <~> "test=";
    w <~> test;
    w <~> ", testDir=";
    w <~> testDir;
    w <~> ", testBasename=";
    w <~> testBasename;
    w <~> ", testName=";
    w <~> testName;
    w <~> ")";
  }
}

proc getTestBasename(filename) {
  return filename.substring(..filename.length-5);
}

/* Return tuple that is dirname and basename. */
proc splitFilename(filename) {
  for i in 1..filename.length by -1 {
    if filename.substring(i) == "/" {
      return (filename.substring(1..i-1), filename.substring(i+1..));
    }
  }
  return ("", filename);
}

use Exec;
use FileSystem;
use IO;

config const testDir = "/Users/tvandoren/src/chapel/test/release/examples/programs";

config const testverbose = false;
config const printEnv = testverbose;

proc main() {
  var testRunner = new TestRunner(testDir);
  testRunner.runTests();
  delete testRunner;
}

class TestRunner {
  const testDir, chplHome: string;

  proc TestRunner(testDir) {
    this.testDir = testDir;
    this.chplHome = this.getChplHome();
  }

  proc runTests() {
    setEnv("CHPL_HOME", this.chplHome);
    this.printEnvironment();

    // forall test in this.findTests() {
    for test in this.findTests() {
      this.runTest(test);
    }
  }

  proc getChplHome() {
    const output = run("chpl --print-chpl-home");
    var chplHome: string;

    for i in 1..output.length {
      if output.substring(i) == "\t" {
        chplHome = output.substring(1..i-1);
        break;
      }
    }

    if testverbose then
      writeln("CHPL_HOME: ", chplHome);

    return chplHome;
  }

  proc printEnvironment() {
    if printEnv {
      const chplEnv = run("$CHPL_HOME/util/printchplenv");
      writeln("### Chapel Environment ###");
      write(chplEnv);
      writeln("##########################");
      writeln();
    }
  }

  proc runTest(test) {
    writeln("testing: ", test);

    var t = new Test(test);
    t.runTest();
    delete t;
  }

  iter findTests() {
    for filename in findfiles(this.testDir, recursive=true) {
      if this.isChapelFile(filename) {
        yield filename;
      }
    }
  }

  /* iter findTests(param tag: iterKind) */
  /*   where tag == iterKind.standalone */
  /* { */
  /*   forall filename in findfiles(this.testDir, recursive=true) { */
  /*     if this.isChapelFile(filename) { */
  /*       yield filename; */
  /*     } */
  /*   } */
  /* } */

  proc isChapelFile(filename) {
    return getTestBasename(filename) + ".chpl" == filename;
  }

  proc writeThis(w: Writer) {
    w <~> "TestRunner(";
    w <~> "testDir=";
    w <~> testDir;
    w <~> ")";
  }
}

class Test {
  const test, testDir, testBasename, testName: string;

  proc Test(test) {
    this.test = test;
    (testDir, testBasename) = splitFilename(test);
    testName = getTestBasename(testBasename);

    this.validateTestFiles();
  }

  proc validateTestFiles() {
    // TODO: implement this!
  }

  proc runTest() {
    const testOutput = this.compile() + this.execute();
    if this.checkOutput(testOutput) {
      writeln("SUCCESS: " + this.test);
    } else {
      writeln("ERROR: matching output: " + this.test);
    }
  }

  proc fullTestName {
    return this.testDir + "/" + this.testName;
  }

  proc goodFile {
    return this.fullTestName + ".good";
  }

  proc expectedOutput {
    var f = open(this.goodFile, iomode.r),
      r = f.reader();
    var o: string;
    r.readstring(o);
    r.close();
    f.close();
    return o;
  }

  proc checkOutput(testOutput) {
    return testOutput == this.expectedOutput;
  }

  proc compile() {
    const cmd = "chpl -o " + this.fullTestName + " " + this.test;
    if testverbose then
      writeln("Running compiler: " + cmd);
    return run(cmd);
  }

  proc execute() {
    const cmd = this.fullTestName;
    if testverbose then
      writeln("Running binary: " + cmd);
    const output = run(cmd);

    if testverbose then
      writeln("Deleting " + this.fullTestName);
    remove(this.fullTestName);

    return output;
  }

  proc writeThis(w: Writer) {
    w <~> "Test(";
    w <~> "test=";
    w <~> test;
    w <~> ", testDir=";
    w <~> testDir;
    w <~> ", testBasename=";
    w <~> testBasename;
    w <~> ", testName=";
    w <~> testName;
    w <~> ")";
  }
}

proc getTestBasename(filename) {
  return filename.substring(..filename.length-5);
}

/* Return tuple that is dirname and basename. */
proc splitFilename(filename) {
  for i in 1..filename.length by -1 {
    if filename.substring(i) == "/" {
      return (filename.substring(1..i-1), filename.substring(i+1..));
    }
  }
  return ("", filename);
}