Fixed a couple of bugs in HPL in release/examples.

They were masked by the block-cyclic distribution.
Chapel is great!

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

//
// Use standard modules for vector and matrix Norms, Random numbers
// and Timing routines
//
use Norm, Random, Time;

//
// Use the user module for computing HPCC problem sizes
//
use HPCCProblemSize;

//
// Use the distributions we need for this computation
//
use BlockCycDist;

//
// The number of matrices and the element type of those matrices
// indexType can be int or int(64), elemType can be real or complex
//
const numMatrices = 1;
type indexType = int,
     elemType = real;

//
// Configuration constants indicating the problem size (n) and the
// block size (blkSize)
//
config const n = computeProblemSize(numMatrices, elemType, rank=2,
                                    memFraction=2, retType=indexType),
             blkSize = 8;

//
// Configuration constant used for verification thresholds
//
config const epsilon = 2.0e-15;

//
// Configuration constants to indicate whether or not to use a
// pseudo-random seed (based on the clock) or a fixed seed; and to
// specify the fixed seed explicitly
//
config const useRandomSeed = true,
             seed = if useRandomSeed then SeedGenerator.currentTime else 31415;

//
// Configuration constants to control what's printed -- benchmark
// parameters, input and output arrays, and/or statistics
//
config const printParams = true,
             printArrays = false,
             printStats = true;

//
// The program entry point
//
proc main() {
  printConfiguration();

  //
  // MatVectSpace is a 2D domain of type indexType that represents the
  // n x n matrix adjacent to the column vector b.  MatrixSpace is a
  // subdomain that is created by slicing into MatVectSpace,
  // inheriting all of its rows and its low column bound.  As our
  // standard distribution library is filled out, MatVectSpace will be
  // distributed using a BlockCyclic(blkSize) distribution.
  //
  const MatVectSpace: domain(2, indexType)
                      dmapped BlockCyclic(startIdx=(1,1), (blkSize,blkSize))
                    = [1..n, 1..n+1],
        MatrixSpace = MatVectSpace[.., ..n];

  var Ab : [MatVectSpace] elemType,  // the matrix A and vector b
      piv: [1..n] indexType;         // a vector of pivot values

  initAB(Ab);

  const startTime = getCurrentTime();     // capture the start time

  LUFactorize(n, Ab, piv);                 // compute the LU factorization

  var x = backwardSub(n, Ab);  // perform the back substitution

  const execTime = getCurrentTime() - startTime;  // store the elapsed time

  //
  // Validate the answer and print the results
  const validAnswer = verifyResults(Ab, MatrixSpace, x);
  printResults(validAnswer, execTime);
}

//
// blocked LU factorization with pivoting for matrix augmented with
// vector of RHS values.
//
proc LUFactorize(n: indexType, Ab: [?AbD] elemType,
                piv: [1..n] indexType) {

  // Initialize the pivot vector to represent the initially unpivoted matrix.
  piv = 1..n;

  /* The following diagram illustrates how we partition the matrix.
     Each iteration of the loop increments a variable blk by blkSize;
     point (blk, blk) is the upper-left location of the currently
     unfactored matrix (the dotted region represents the areas
     factored in prior iterations).  The unfactored matrix is
     partioned into four subdomains: tl, tr, bl, and br, and an
     additional domain (not shown), l, that is the union of tl and bl.

                (point blk, blk)
       +-------//------------------+
       |......//...................|
       |.....//....................|
       |....+-----+----------------|
       |....|     |                |
       |....| tl  |      tr        |
       |....|     |                |
       |....+-----+----------------|
       |....|     |                |
       |....|     |                |
       |....| bl  |      br        |
       |....|     |                |
       |....|     |                |
       +----+-----+----------------+
  */
  for blk in 1..n by blkSize {
    const tl = AbD[blk..#blkSize, blk..#blkSize],
          tr = AbD[blk..#blkSize, blk+blkSize..],
          bl = AbD[blk+blkSize.., blk..#blkSize],
          br = AbD[blk+blkSize.., blk+blkSize..],
          l  = AbD[blk.., blk..#blkSize];

    //
    // Now that we've sliced and diced Ab properly, do the blocked-LU
    // computation:
    //
    panelSolve(Ab, l, piv);
    updateBlockRow(Ab, tl, tr);

    //
    // update trailing submatrix (if any)
    //
    schurComplement(Ab, bl, tr, br);
  }
}

//
// Distributed matrix-multiply for HPL. The idea behind this algorithm is that
// some point the matrix will be partioned as shown in the following diagram:
//
//    [1]----+-----+-----+-----+
//     |     |bbbbb|bbbbb|bbbbb|  Solve for the dotted region by
//     |     |bbbbb|bbbbb|bbbbb|  multiplying the 'a' and 'b' region.
//     |     |bbbbb|bbbbb|bbbbb|  The 'a' region is a block column, the
//     +----[2]----+-----+-----+  'b' region is a block row.
//     |aaaaa|.....|.....|.....|
//     |aaaaa|.....|.....|.....|  The 'a' region was 'bl' in the calling
//     |aaaaa|.....|.....|.....|  function but called AD here.  Similarly,
//     +-----+-----+-----+-----+  'b' was 'tr' in the calling code, but BD
//     |aaaaa|.....|.....|.....|  here.
//     |aaaaa|.....|.....|.....|
//     |aaaaa|.....|.....|.....|
//     +-----+-----+-----+-----+
//
// Every locale with a block of data in the dotted region updates
// itself by multiplying the neighboring a-region block to its left
// with the neighboring b-region block above it and subtracting its
// current data from the result of this multiplication. To ensure that
// all locales have local copies of the data needed to perform this
// multiplication we copy the data A and B data into the replA and
// replB arrays, which will use a dimensional (block-cyclic,
// replicated-block) distribution (or vice-versa) to ensure that every
// locale only stores one copy of each block it requires for all of
// its rows/columns.
//
proc schurComplement(Ab: [?AbD] elemType, AD: domain, BD: domain, Rest: domain) {
  //
  // Copy data into replicated array so every processor has a local copy
  // of the data it will need to perform a local matrix-multiply.  These
  // replicated distributions aren't implemented yet, but imagine that
  // they look something like the following:
  //
  //var replAbD: domain(2)
  //            dmapped new Dimensional(BlkCyc(blkSize), Replicated)) = AbD[AD];
  //
  const replAD: domain(2, indexType) = AD,
        replBD: domain(2, indexType) = BD;

  const replA : [replAD] elemType = Ab[replAD],
        replB : [replBD] elemType = Ab[replBD];

  //  writeln("Rest = ", Rest);
  //  writeln("Rest by blkSize = ", Rest by (blkSize, blkSize));
  // do local matrix-multiply on a block-by-block basis
  forall (row,col) in Rest by (blkSize, blkSize) {
    //
    // At this point, the dgemms should all be local once we have
    // replication correct, so we'll want to assert that fact
    //
    //    local {
      const aBlkD = replAD[row..#blkSize, ..],
            bBlkD = replBD[.., col..#blkSize],
            cBlkD = AbD[row..#blkSize, col..#blkSize];

      dgemmNativeInds(replA[aBlkD], replB[bBlkD], Ab[cBlkD]);
      //    }
  }
}

//
// calculate C = C - A * B.
//
proc dgemmNativeInds(A: [] elemType,
                    B: [] elemType,
                    C: [] elemType) {
  for (iA, iC) in (A.domain.dim(1), C.domain.dim(1)) do
    for (jA, iB) in (A.domain.dim(2), B.domain.dim(1)) do
      for (jB, jC) in (B.domain.dim(2), C.domain.dim(2)) do
        C[iC,jC] -= A[iA, jA] * B[iB, jB];
}

//
// do unblocked-LU decomposition within the specified panel, update the
// pivot vector accordingly
//
proc panelSolve(Ab: [] elemType,
               panel: domain,
               piv: [] indexType) {

  for k in panel.dim(2) {             // iterate through the columns
    const col = panel[k.., k..k];

    // If there are no rows below the current column return
    if col.numIndices == 0 then return;

    // Find the pivot, the element with the largest absolute value.
    const ( , (pivotRow, )) = maxloc reduce(abs(Ab(col)), col);

    // Capture the pivot value explicitly (note that result of maxloc
    // is absolute value, so it can't be used directly).
    //
    const pivotVal = Ab[pivotRow, k];

    // Swap the current row with the pivot row and update the pivot vector
    // to reflect that
    Ab[k..k, ..] <=> Ab[pivotRow..pivotRow, ..];
    piv[k] <=> piv[pivotRow];

    if (pivotVal == 0) then
      halt("Matrix cannot be factorized");

    // divide all values below and in the same col as the pivot by
    // the pivot value
    Ab[k+1.., k..k] /= pivotVal;

    // update all other values below the pivot
    forall (i,j) in panel[k+1.., k+1..] do
      Ab[i,j] -= Ab[i,k] * Ab[k,j];
  }
}

//
// Update the block row (tr for top-right) portion of the matrix in a
// blocked LU decomposition.  Each step of the LU decomposition will
// solve a block (tl for top-left) portion of a matrix. This function
// solves the rows to the right of the block.
//
proc updateBlockRow(Ab: [] elemType,
                   tl: domain,
                   tr: domain) {

  for row in tr.dim(1) {
    const activeRow = tr[row..row, ..],
          prevRows = tr.dim(1).low..row-1;

    forall (i,j) in activeRow do
      for k in prevRows do
        Ab[i, j] -= Ab[i, k] * Ab[k,j];
  }
}

//
// compute the backwards substitution
//
proc backwardSub(n: indexType,
                 Ab: [] elemType) {
  const bd = Ab.domain.dim(1);
  var x: [bd] elemType;

  for i in bd by -1 do
    x[i] = (Ab[i,n+1] - (+ reduce [j in i+1..bd.high] (Ab[i,j] * x[j])))
            / Ab[i,i];

  return x;
}

//
// print out the problem size and block size if requested
//
proc printConfiguration() {
  if (printParams) {
    if (printStats) then printLocalesTasks();
    printProblemSize(elemType, numMatrices, n, rank=2);
    writeln("block size = ", blkSize, "\n");
  }
}

//
// construct an n by n+1 matrix filled with random values and scale
// it to be in the range -1.0..1.0
//
proc initAB(Ab: [] elemType) {
  fillRandom(Ab, seed);
  Ab = Ab * 2.0 - 1.0;
}

//
// calculate norms and residuals to verify the results
//
proc verifyResults(Ab, MatrixSpace, x) {
  initAB(Ab);

  const axmbNorm = norm(gaxpyMinus(Ab[.., 1..n], x, Ab[.., n+1..n+1]), normType.normInf);

  const a1norm   = norm(Ab[.., 1..n], normType.norm1),
        aInfNorm = norm(Ab[.., 1..n], normType.normInf),
        x1Norm   = norm(Ab[.., n+1..n+1], normType.norm1),
        xInfNorm = norm(Ab[.., n+1..n+1], normType.normInf);

  const resid1 = axmbNorm / (epsilon * a1norm * n),
        resid2 = axmbNorm / (epsilon * a1norm * x1Norm),
        resid3 = axmbNorm / (epsilon * aInfNorm * xInfNorm);

  if (printStats) {
    writeln("resid1: ", resid1);
    writeln("resid2: ", resid2);
    writeln("resid3: ", resid3);
  }

  return max(resid1, resid2, resid3) < 16.0;
}

//
// print success/failure, the execution time and the Gflop/s value
//
proc printResults(successful, execTime) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Execution time = ", execTime);
    const GflopPerSec = ((2.0/3.0) * n**3 + (3.0/2.0) * n**2) / execTime * 10e-9;
    writeln("Performance (Gflop/s) = ", GflopPerSec);
  }
}

//
// simple matrix-vector multiplication, solve equation A*x-y
//
proc gaxpyMinus(A: [],
                x: [?xD],
                y: [?yD]) {
  var res: [1..n] elemType;

  forall i in 1..n do
    res[i] = (+ reduce [j in xD] (A[i,j] * x[j])) - y[i,n+1];

  return res;
}

//
// Use standard modules for vector and matrix Norms, Random numbers
// and Timing routines
//
use Norm, Random, Time;

//
// Use the user module for computing HPCC problem sizes
//
use HPCCProblemSize;

//
// Use the distributions we need for this computation
//
use BlockCycDist;

//
// The number of matrices and the element type of those matrices
// indexType can be int or int(64), elemType can be real or complex
//
const numMatrices = 1;
type indexType = int,
     elemType = real;

//
// Configuration constants indicating the problem size (n) and the
// block size (blkSize)
//
config const n = computeProblemSize(numMatrices, elemType, rank=2,
                                    memFraction=2, retType=indexType),
             blkSize = 8;

//
// Configuration constant used for verification thresholds
//
config const epsilon = 2.0e-15;

//
// Configuration constants to indicate whether or not to use a
// pseudo-random seed (based on the clock) or a fixed seed; and to
// specify the fixed seed explicitly
//
config const useRandomSeed = true,
             seed = if useRandomSeed then SeedGenerator.currentTime else 31415;

//
// Configuration constants to control what's printed -- benchmark
// parameters, input and output arrays, and/or statistics
//
config const printParams = true,
             printArrays = false,
             printStats = true;

//
// The program entry point
//
proc main() {
  printConfiguration();

  //
  // MatVectSpace is a 2D domain of type indexType that represents the
  // n x n matrix adjacent to the column vector b.  MatrixSpace is a
  // subdomain that is created by slicing into MatVectSpace,
  // inheriting all of its rows and its low column bound.  As our
  // standard distribution library is filled out, MatVectSpace will be
  // distributed using a BlockCyclic(blkSize) distribution.
  //
  const MatVectSpace: domain(2, indexType)
                      dmapped BlockCyclic(startIdx=(1,1), (blkSize,blkSize))
                    = [1..n, 1..n+1],
        MatrixSpace = MatVectSpace[.., ..n];

  var Ab : [MatVectSpace] elemType,  // the matrix A and vector b
      piv: [1..n] indexType;         // a vector of pivot values

  initAB(Ab);

  const startTime = getCurrentTime();     // capture the start time

  LUFactorize(n, Ab, piv);                 // compute the LU factorization

  var x = backwardSub(n, Ab);  // perform the back substitution

  const execTime = getCurrentTime() - startTime;  // store the elapsed time

  //
  // Validate the answer and print the results
  const validAnswer = verifyResults(Ab, MatrixSpace, x);
  printResults(validAnswer, execTime);
}

//
// blocked LU factorization with pivoting for matrix augmented with
// vector of RHS values.
//
proc LUFactorize(n: indexType, Ab: [?AbD] elemType,
                piv: [1..n] indexType) {

  // Initialize the pivot vector to represent the initially unpivoted matrix.
  piv = 1..n;

  /* The following diagram illustrates how we partition the matrix.
     Each iteration of the loop increments a variable blk by blkSize;
     point (blk, blk) is the upper-left location of the currently
     unfactored matrix (the dotted region represents the areas
     factored in prior iterations).  The unfactored matrix is
     partioned into four subdomains: tl, tr, bl, and br, and an
     additional domain (not shown), l, that is the union of tl and bl.

                (point blk, blk)
       +-------//------------------+
       |......//...................|
       |.....//....................|
       |....+-----+----------------|
       |....|     |                |
       |....| tl  |      tr        |
       |....|     |                |
       |....+-----+----------------|
       |....|     |                |
       |....|     |                |
       |....| bl  |      br        |
       |....|     |                |
       |....|     |                |
       +----+-----+----------------+
  */
  for blk in 1..n by blkSize {
    const tl = AbD[blk..#blkSize, blk..#blkSize],
          tr = AbD[blk..#blkSize, blk+blkSize..],
          bl = AbD[blk+blkSize.., blk..#blkSize],
          br = AbD[blk+blkSize.., blk+blkSize..],
          l  = AbD[blk.., blk..#blkSize];

    //
    // Now that we've sliced and diced Ab properly, do the blocked-LU
    // computation:
    //
    panelSolve(Ab, l, piv);
    updateBlockRow(Ab, tl, tr);

    //
    // update trailing submatrix (if any)
    //
    schurComplement(Ab, bl, tr, br);
  }
}

//
// Distributed matrix-multiply for HPL. The idea behind this algorithm is that
// some point the matrix will be partioned as shown in the following diagram:
//
//    [1]----+-----+-----+-----+
//     |     |bbbbb|bbbbb|bbbbb|  Solve for the dotted region by
//     |     |bbbbb|bbbbb|bbbbb|  multiplying the 'a' and 'b' region.
//     |     |bbbbb|bbbbb|bbbbb|  The 'a' region is a block column, the
//     +----[2]----+-----+-----+  'b' region is a block row.
//     |aaaaa|.....|.....|.....|
//     |aaaaa|.....|.....|.....|  The 'a' region was 'bl' in the calling
//     |aaaaa|.....|.....|.....|  function but called AD here.  Similarly,
//     +-----+-----+-----+-----+  'b' was 'tr' in the calling code, but BD
//     |aaaaa|.....|.....|.....|  here.
//     |aaaaa|.....|.....|.....|
//     |aaaaa|.....|.....|.....|
//     +-----+-----+-----+-----+
//
// Every locale with a block of data in the dotted region updates
// itself by multiplying the neighboring a-region block to its left
// with the neighboring b-region block above it and subtracting its
// current data from the result of this multiplication. To ensure that
// all locales have local copies of the data needed to perform this
// multiplication we copy the data A and B data into the replA and
// replB arrays, which will use a dimensional (block-cyclic,
// replicated-block) distribution (or vice-versa) to ensure that every
// locale only stores one copy of each block it requires for all of
// its rows/columns.
//
proc schurComplement(Ab: [?AbD] elemType, AD: domain, BD: domain, Rest: domain) {
  //
  // Copy data into replicated array so every processor has a local copy
  // of the data it will need to perform a local matrix-multiply.  These
  // replicated distributions aren't implemented yet, but imagine that
  // they look something like the following:
  //
  //var replAbD: domain(2)
  //            dmapped new Dimensional(BlkCyc(blkSize), Replicated)) = AbD[AD];
  //
  const replAD: domain(2, indexType) = AD,
        replBD: domain(2, indexType) = BD;

  const replA : [replAD] elemType = Ab[replAD],
        replB : [replBD] elemType = Ab[replBD];

  //  writeln("Rest = ", Rest);
  //  writeln("Rest by blkSize = ", Rest by (blkSize, blkSize));
  // do local matrix-multiply on a block-by-block basis
  forall (row,col) in Rest by (blkSize, blkSize) {
    //
    // At this point, the dgemms should all be local once we have
    // replication correct, so we'll want to assert that fact
    //
    //    local {
      const aBlkD = replAD[row..#blkSize, ..],
            bBlkD = replBD[.., col..#blkSize],
            cBlkD = AbD[row..#blkSize, col..#blkSize];

      dgemmNativeInds(replA[aBlkD], replB[bBlkD], Ab[cBlkD]);
      //    }
  }
}

//
// calculate C = C - A * B.
//
proc dgemmNativeInds(A: [] elemType,
                    B: [] elemType,
                    C: [] elemType) {
  for (iA, iC) in (A.domain.dim(1), C.domain.dim(1)) do
    for (jA, iB) in (A.domain.dim(2), B.domain.dim(1)) do
      for (jB, jC) in (B.domain.dim(2), C.domain.dim(2)) do
        C[iC,jC] -= A[iA, jA] * B[iB, jB];
}

//
// do unblocked-LU decomposition within the specified panel, update the
// pivot vector accordingly
//
proc panelSolve(Ab: [] elemType,
               panel: domain,
               piv: [] indexType) {

  for k in panel.dim(2) {             // iterate through the columns
    var col = panel[k.., k..k];

    // If there are no rows below the current column return
    if col.numIndices == 0 then return;

    // Find the pivot, the element with the largest absolute value.
    const ( , (pivotRow, )) = maxloc reduce(abs(Ab(col)), col);

    // Capture the pivot value explicitly (note that result of maxloc
    // is absolute value, so it can't be used directly).
    //
    const pivotVal = Ab[pivotRow, k];

    // Swap the current row with the pivot row and update the pivot vector
    // to reflect that
    Ab[k..k, ..] <=> Ab[pivotRow..pivotRow, ..];
    piv[k] <=> piv[pivotRow];

    if (pivotVal == 0) then
      halt("Matrix cannot be factorized");

    // divide all values below and in the same col as the pivot by
    // the pivot value
    Ab[k+1.., k..k] /= pivotVal;

    // update all other values below the pivot
    forall (i,j) in panel[k+1.., k+1..] do
      Ab[i,j] -= Ab[i,k] * Ab[k,j];
  }
}

//
// Update the block row (tr for top-right) portion of the matrix in a
// blocked LU decomposition.  Each step of the LU decomposition will
// solve a block (tl for top-left) portion of a matrix. This function
// solves the rows to the right of the block.
//
proc updateBlockRow(Ab: [] elemType,
                   tl: domain,
                   tr: domain) {

  for row in tr.dim(1) {
    const activeRow = tr[row..row, ..],
          prevRows = tr.dim(1).low..row-1;

    forall (i,j) in activeRow do
      for k in prevRows do
        Ab[i, j] -= Ab[i, k] * Ab[k,j];
  }
}

//
// compute the backwards substitution
//
proc backwardSub(n: indexType,
                 Ab: [] elemType) {
  const bd = Ab.domain.dim(1);
  var x: [bd] elemType;

  for i in bd by -1 do
    x[i] = (Ab[n+1,i] - (+ reduce [j in i+1..bd.high] (Ab[i,j] * x[j])))
            / Ab[i,i];

  return x;
}

//
// print out the problem size and block size if requested
//
proc printConfiguration() {
  if (printParams) {
    if (printStats) then printLocalesTasks();
    printProblemSize(elemType, numMatrices, n, rank=2);
    writeln("block size = ", blkSize, "\n");
  }
}

//
// construct an n by n+1 matrix filled with random values and scale
// it to be in the range -1.0..1.0
//
proc initAB(Ab: [] elemType) {
  fillRandom(Ab, seed);
  Ab = Ab * 2.0 - 1.0;
}

//
// calculate norms and residuals to verify the results
//
proc verifyResults(Ab, MatrixSpace, x) {
  initAB(Ab);

  const axmbNorm = norm(gaxpyMinus(Ab[.., 1..n], x, Ab[.., n+1..n+1]), normType.normInf);

  const a1norm   = norm(Ab[.., 1..n], normType.norm1),
        aInfNorm = norm(Ab[.., 1..n], normType.normInf),
        x1Norm   = norm(Ab[.., n+1..n+1], normType.norm1),
        xInfNorm = norm(Ab[.., n+1..n+1], normType.normInf);

  const resid1 = axmbNorm / (epsilon * a1norm * n),
        resid2 = axmbNorm / (epsilon * a1norm * x1Norm),
        resid3 = axmbNorm / (epsilon * aInfNorm * xInfNorm);

  if (printStats) {
    writeln("resid1: ", resid1);
    writeln("resid2: ", resid2);
    writeln("resid3: ", resid3);
  }

  return max(resid1, resid2, resid3) < 16.0;
}

//
// print success/failure, the execution time and the Gflop/s value
//
proc printResults(successful, execTime) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Execution time = ", execTime);
    const GflopPerSec = ((2.0/3.0) * n**3 + (3.0/2.0) * n**2) / execTime * 10e-9;
    writeln("Performance (Gflop/s) = ", GflopPerSec);
  }
}

//
// simple matrix-vector multiplication, solve equation A*x-y
//
proc gaxpyMinus(A: [],
                x: [?xD],
                y: [?yD]) {
  var res: [1..n] elemType;

  forall i in 1..n do
    res[i] = (+ reduce [j in xD] (A[i,j] * x[j])) - y[n+1, i];

  return res;
}


Fix the path module

Fix the path 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.
 */

/* A file utilities library, specifically related to path operations

   The Path module focuses on manipulation of the path to a file or directory.
   Also provided are constant values representing common idioms that may vary
   across operating systems (though rarely in the modern era), such as general
   references to a parent directory or the current directory.

   .. note::

      This module is currently under development and will expand significantly
      in upcoming releases.  Stay tuned!

   Operations which occur on the files or directories referred to by these paths
   may be found in :mod:`FileSystem` (for operations *on* the file) or :mod:`IO`
   (for operations *within* the file).
*/
module Path {

use Error;

/* Represents generally the current directory */
const curDir = ".";
/* Represents generally the parent directory */
const parentDir = "..";
/* Denotes the separator between a directory and its child. */
const pathSep = "/";

pragma "no doc"
proc realPath(out error: syserr, name: string): string {
  extern proc chpl_fs_realpath(path: c_string, ref shortened: c_string_copy): syserr;

  var res: c_string_copy;
  error = chpl_fs_realpath(name.c_str(), res);
  var len = res.length;
  return new string(res:c_ptr(uint(8)), len, len+1, owned=true, needToCopy=false);
}

/* Given a path `name`, attempts to determine the canonical path referenced.
   This resolves and removes any :data:`curDir` and :data:`parentDir` uses
   present, as well as any symbolic links.  Returns the result

   Will halt with an error message if one is detected.

   :arg name: A path to resolve.  If the path does not refer to a valid file
              or directory, an error will occur.
   :type name: string

   :return: A canonical version of the argument.
   :rtype: string
*/
proc realPath(name: string) // : string
{
  var err: syserr = ENOERR;
  var ret = realPath(err, name);
  if err != ENOERR then ioerror(err, "in realPath of", name);
  return ret;
}

pragma "no doc"
proc file.realPath(out error: syserr)//: string
{
  extern proc chpl_fs_realpath_file(path: qio_file_ptr_t, ref shortened: c_string_copy): syserr;

  var res: c_string_copy;

  if (is_c_nil(_file_internal)) {
    // This file is referencing a null file.  We'll get a segfault if we
    // continue.
    error = EBADF;
    return "";
  }
  error = chpl_fs_realpath_file(_file_internal, res);
  var len = res.length;
  return new string(res:c_ptr(uint(8)), len, len+1, owned=true, needToCopy=false);
}

/* Determines the canonical path referenced by the :type:`~IO.file` record
   performing this operation.  This resolves and removes any :data:`curDir` and
   :data:`parentDir` uses present, as well as any symbolic links.  Returns the
   result

   Will halt with an error message if one is detected.

   :return: A canonical path to the file referenced by this :type:`~IO.file`
            record.  If the :type:`~IO.file` record is not valid, an error will
            occur
   :rtype: string
*/
proc file.realPath()// : string
{
  var err: syserr = ENOERR;
  var ret = realPath(err);
  if err != ENOERR then ioerror(err, "in file.realPath");
  return ret;
}

}

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

/* A file utilities library, specifically related to path operations

   The Path module focuses on manipulation of the path to a file or directory.
   Also provided are constant values representing common idioms that may vary
   across operating systems (though rarely in the modern era), such as general
   references to a parent directory or the current directory.

   .. note::

      This module is currently under development and will expand significantly
      in upcoming releases.  Stay tuned!

   Operations which occur on the files or directories referred to by these paths
   may be found in :mod:`FileSystem` (for operations *on* the file) or :mod:`IO`
   (for operations *within* the file).
*/
module Path {

use Error;

/* Represents generally the current directory */
const curDir = ".";
/* Represents generally the parent directory */
const parentDir = "..";
/* Denotes the separator between a directory and its child. */
const pathSep = "/";

pragma "no doc"
proc realPath(out error: syserr, name: string): string {
  extern proc chpl_fs_realpath(path: c_string, ref shortened: c_string_copy): syserr;

  var res: c_string_copy;
  error = chpl_fs_realpath(name.c_str(), res);
  return res:string;
}

/* Given a path `name`, attempts to determine the canonical path referenced.
   This resolves and removes any :data:`curDir` and :data:`parentDir` uses
   present, as well as any symbolic links.  Returns the result

   Will halt with an error message if one is detected.

   :arg name: A path to resolve.  If the path does not refer to a valid file
              or directory, an error will occur.
   :type name: string

   :return: A canonical version of the argument.
   :rtype: string
*/
proc realPath(name: string) // : string
{
  var err: syserr = ENOERR;
  var ret = realPath(err, name);
  if err != ENOERR then ioerror(err, "in realPath of", name);
  return ret;
}

pragma "no doc"
proc file.realPath(out error: syserr)//: string
{
  extern proc chpl_fs_realpath_file(path: qio_file_ptr_t, ref shortened: c_string_copy): syserr;

  var res: c_string_copy;

  if (is_c_nil(_file_internal)) {
    // This file is referencing a null file.  We'll get a segfault if we
    // continue.
    error = EBADF;
    return "";
  }
  error = chpl_fs_realpath_file(_file_internal, res);
  var len = res.length;
  return new string(res:c_ptr(uint(8)), len, len, owned=true, needToCopy=false);
}

/* Determines the canonical path referenced by the :type:`~IO.file` record
   performing this operation.  This resolves and removes any :data:`curDir` and
   :data:`parentDir` uses present, as well as any symbolic links.  Returns the
   result

   Will halt with an error message if one is detected.

   :return: A canonical path to the file referenced by this :type:`~IO.file`
            record.  If the :type:`~IO.file` record is not valid, an error will
            occur
   :rtype: string
*/
proc file.realPath()// : string
{
  var err: syserr = ENOERR;
  var ret = realPath(err);
  if err != ENOERR then ioerror(err, "in file.realPath");
  return ret;
}

}


Consolidate interfaces for mod and sgn using helper functions.

Put param versions of divceil/divfloor before their runtime counterparts.

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

/*
This module provides wrappers for <cmath> (math.h) numerical constants and
routines.  Its symbols are provided by default; an explicit 'use' statement
is not necessary.

The C Math library is part of the C Language Standard (ISO/IEC 9899), as
described in Section 7.12.  Please consult that standard for an
authoritative description of the expected properties of those constants and
routines.

In general, where the C math library provides a *double* and a *float* version
of a function, the float version has a suffix 'f'.  In the Chapel interface,
the suffix is dropped, and the type of the operand determines which version is
called -- according to the usual function overloading and resolution rules.
Normally, the result has the same precision
as the argument(s).  Please consult the C standard for specifics.

Rounding -- The rounding mode for floating-point addition (subtraction) is
implementation-defined.

Error Handling -- At present, Chapel does not provide control over error
handling in the Math module.  The default behavior is as if the macro
``math_errhandling`` is set to 0: Given erroneous input at run-time,
all math functions will return an implementation-defined value; no
exception will be generated.

*/
module Math {

  //////////////////////////////////////////////////////////////////////////
  // Helper constants and functions (not included in chpldocs).
  //
  pragma "no doc"
  extern proc chpl_macro_INFINITY():real(64);
  pragma "no doc"
  extern proc chpl_macro_NAN():real(64);

  pragma "no doc"
  extern proc chpl_macro_double_isinf(x: real(64)): c_int;
  pragma "no doc"
  extern proc chpl_macro_float_isinf(x: real(32)): c_int;
  pragma "no doc"
  extern proc chpl_macro_double_isfinite(x: real(64)): c_int;
  pragma "no doc"
  extern proc chpl_macro_float_isfinite(x: real(32)): c_int;
  pragma "no doc"
  extern proc chpl_macro_double_isnan(x: real(64)): c_int;
  pragma "no doc"
  extern proc chpl_macro_float_isnan(x: real(32)): c_int;

  pragma "no doc"
  proc _logBasePow2Help(in val, baseLog2) {
    var result = -1;
    while (val != 0) {
      val >>= baseLog2;
      result += 1;
    }
    return result;
  }

  pragma "no doc"
  proc logBasePow2(in val: int(?w), baseLog2) {
    if (val < 1) {
      halt("Can't take the log() of a non-positive integer");
    }
    return _logBasePow2Help(val, baseLog2);
  }

  pragma "no doc"
  proc logBasePow2(in val: uint(?w), baseLog2) {
    if (val < 1) {
      halt("Can't take the log() of a non-positive integer");
    }
    return _logBasePow2Help(val, baseLog2);
  }

  pragma "no doc"
  inline proc mod_help(m: integral, n: integral) {
    const temp = m % n;

    // eliminate some run-time tests if input(s) is(are) unsigned
    return
      if isNonnegative(n) then
      if isUintType(m.type)
        then temp
          else ( if temp >= 0 then temp else temp + n )
            else
              // n < 0
              ( if temp <= 0 then temp else temp + n );
  }

  pragma "no doc"
  inline proc mod_help(x: real(?w), y: real(w)): real(w) {
    // This codes up the standard definition, according to Wikipedia.
    // Is there a more efficient implementation for reals?
    return x - y*floor(x/y);
  }

  pragma "no doc"
  inline proc sgn_help(i : int(?w)): int(8)
    return ((i > 0) : int(8) - (i < 0) : int(8)) : int(8);

  pragma "no doc"
  inline proc sgn_help(i : uint(?w)): uint(8)
    return (i > 0) : uint(8);

  pragma "no doc"
  inline proc sgn_help(x : real(?w)): int(8)
    return ((x > 0.0) : int(8) - (x < 0.0) : int(8)) : int(8);
  //
  //////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////
  //
  // Public interface (included in chpldocs).
  //
  // (The entries below are alphabetized (case-insensitively) because chpldocs
  // presents them in declaration order.)
  //

  /* Returns the absolute value of the integer param argument `i`. */
  proc abs(param i : integral) param return if i < 0 then -i else i;

  /* Returns the magnitude (absolute value) of the argument `x`.

     :rtype: The type of `x`.
  */
  inline proc abs(x) where isNumericalType(x.type) {
    if isComplex(x) {
      return sqrt(x.re * x.re + x.im * -x.im);
    } else if isReal(x) {
      if numBits(x) == 64 {
        extern proc fabs(x: real(64)): real(64);
        return fabs(x);
      } else if numBits(x) == 32 {
        extern proc fabsf(x: real(32)): real(32);
        return fabsf(x);
      } else {
        compilerError("abs() -- Invalid real argument width: ", numBits(x));
      }
    } else if isImaginary(x) {
      if numBits(x) == 64 {
        extern proc fabs(x: real(64)): real(64);
        return fabs(_i2r(x));
      } else if numBits(x) == 32 {
        extern proc fabsf(x: real(32)): real(32);
        return fabsf(_i2r(x));
      } else {
        compilerError("abs() -- Invalid imaginary argument width ", numBits(x));
      }
    } else if isIntegral(x) {
      if (isSigned(x)) then
        return if i < 0 then -i else i;
      else
        return if x == 0 then 0 else i;
    } else{
      compilerError("abs() -- Invalid argument type: ", typeToString(x.type));
    }
  }

  /* Returns the arc cosine of the real argument `x`.

     It is an error if `x` is less than -1 or greater than 1.
  */
  inline proc acos(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc acos(x: real(64)): real(64);
      return acos(x);
    } else if w == 32 {
      extern proc acosf(x: real(32)): real(32);
      return acosf(x);
    } else {
      compilerError("acos() -- Invalid argument width ", w);
    }
  }

  /* Returns the inverse hyperbolic cosine of the argument `x`.

     It is an error if `x` is less than 1.
  */
  inline proc acosh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc acosh(x: real(64)): real(64);
      return acosh(x);
    } else if w == 32 {
      extern proc acoshf(x: real(32)): real(32);
      return acoshf(x);
    } else {
      compilerError("acosh() -- Invalid argument width ", w);
    }
  }

  /* Returns the arc sine of the argument `x`.

     It is an error if `x` is less than -1 or greater than 1.
  */
  inline proc asin(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc asin(x: real(64)): real(64);
      return asin(x);
    } else if w == 32 {
      extern proc asinf(x: real(32)): real(32);
      return asinf(x);
    } else {
      compilerError("asin() -- Invalid argument width ", w);
    }
  }

  /* Returns the inverse hyperbolic sine of the argument `x`. */
  inline proc asinh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc asinh(x: real(64)): real(64);
      return asinh(x);
    } else if w == 32 {
      extern proc asinyf(x: real(32)): real(32);
      return asinhf(x);
    } else {
      compilerError("asinh() -- Invalid argument width ", w);
    }
  }

  /* Returns the arc tangent of the argument `x`. */
  inline proc atan(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc atan(x: real(64)): real(64);
      return atan(x);
    } else if w == 32 {
      extern proc atanf(x: real(32)): real(32);
      return atanf(x);
    } else {
      compilerError("atan() -- Invalid argument width ", w);
    }
  }

  /* Returns the arc tangent of the ratio of the two arguments.

     This is equivalent to
     the arc tangent of `y` / `x` except that the signs of `y`
     and `x` are used to determine the quadrant of the result.
  */
  inline proc atan2(y: real(?w), x: real(w)) : real(w) {
    if w == 64 {
      extern proc atan2(y: real(64), x: real(64)): real(64);
      return atan2(y, x);
    } else if w == 32 {
      extern proc atan2f(y: real(32), x: real(32)): real(32);
      return atan2f(y, x);
    } else {
      compilerError("atan2() -- Invalid argument width ", w);
    }
  }

  /* Returns the inverse hyperbolic tangent of the argument `x`.

     It is an error if `x` is less than -1 or greater than 1.
  */
  inline proc atanh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc atanh(x: real(64)): real(64);
      return atanh(x);
    } else if w == 32 {
      extern proc atanhf(x: real(32)): real(32);
      return atanhf(x);
    } else {
      compilerError("atanh() -- Invalid argument width ", w);
    }
  }

  /* Returns the cube root of the argument `x`. */
  inline proc cbrt(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc cbrt(x: real(64)): real(64);
      return cbrt(x);
    } else if w == 32 {
      extern proc cbrtf(x: real(32)): real(32);
      return cbrtf(x);
    } else {
      compilerError("cbrt() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of the argument `x` rounded up to the nearest integer. */
  inline proc ceil(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc ceil(x: real(64)): real(64);
      return ceil(x);
    } else if w == 32 {
      extern proc ceilf(x: real(32)): real(32);
      return ceilf(x);
    } else {
      compilerError("ceil() -- Invalid argument width ", w);
    }
  }

  /* Returns the complex conjugate of the argument `x`. */
  inline proc conjg(x: complex(?w)) : complex(w) return (x.re, -x.im):complex(w);

  /* Returns the cosine of the argument `x`. */
  inline proc cos(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc cos(x: real(64)): real(64);
      return cos(x);
    } else if w == 32 {
      extern proc cosf(x: real(32)): real(32);
      return cosf(x);
    } else {
      compilerError("cos() -- Invalid argument width ", w);
    }
  }

  /* Returns the hyperbolic cosine of the argument `x`. */
  inline proc cosh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc cosh(x: real(64)): real(64);
      return cosh(x);
    } else if w == 32 {
      extern proc coshf(x: real(32)): real(32);
      return coshf(x);
    } else {
      compilerError("cosh() -- Invalid argument width ", w);
    }
  }

  /* Returns :proc:`ceil`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded up to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divceil(param m: integral, param n: integral) param return
    if isNonnegative(m) then
    if isNonnegative(n) then (m + n - 1) / n
      else                     m / n
        else
          if isNonnegative(n) then m / n
            else                     (m + n + 1) / n;

  /* Returns :proc:`ceil`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded up to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divceil(m: integral, n: integral) return
    if isNonnegative(m) then
    if isNonnegative(n) then (m + n - 1) / n
      else                     m / n
        else
          if isNonnegative(n) then m / n
            else                     (m + n + 1) / n;

  /* Returns :proc:`floor`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded down to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divfloor(param m: integral, param n: integral) param return
    if isNonnegative(m) then
    if isNonnegative(n) then m / n
      else                     (m - n - 1) / n
        else
          if isNonnegative(n) then (m - n + 1) / n
            else                     m / n;

  /* Returns :proc:`floor`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded down to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divfloor(m: integral, n: integral) return
    if isNonnegative(m) then
    if isNonnegative(n) then m / n
      else                     (m - n - 1) / n
        else
          if isNonnegative(n) then (m - n + 1) / n
            else                     m / n;

  /* Returns the error function of the argument `x`. */
  inline proc erf(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc erf(x: real(64)): real(64);
      return erf(x);
    } else if w == 32 {
      extern proc erff(x: real(32)): real(32);
      return erff(x);
    } else {
      compilerError("erf() -- Invalid argument width ", w);
    }
  }

  /* Returns the complementary error function of the argument.
     This is equivalent to 1.0 - :proc:`erf`\(`x`).
  */
  inline proc erfc(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc erfc(x: real(64)): real(64);
      return erfc(x);
    } else if w == 32 {
      extern proc erfcf(x: real(32)): real(32);
      return erfcf(x);
    } else {
      compilerError("erfc() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of the Napierien `e` raised to the power of the argument `x`. */
  inline proc exp(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc exp(x: real(64)): real(64);
      return exp(x);
    } else if w == 32 {
      extern proc expf(x: real(32)): real(32);
      return expf(x);
    } else {
      compilerError("exp() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of `2` raised to the power of the argument `x`. */
  inline proc exp2(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc exp2(x: real(64)): real(64);
      return exp2(x);
    } else if w == 32 {
      extern proc exp2f(x: real(32)): real(32);
      return exp2f(x);
    } else {
      compilerError("exp2() -- Invalid argument width ", w);
    }
  }

  /* Returns one less than the value of the Napierien `e` raised to the power
     of the argument `x`.
  */
  inline proc expm1(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc expm1(x: real(64)): real(64);
      return expm1(x);
    } else if w == 32 {
      extern proc expm1f(x: real(32)): real(32);
      return expm1f(x);
    } else {
      compilerError("expm1() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of the argument `x` rounded down to the nearest integer. */
  inline proc floor(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc floor(x: real(64)): real(64);
      return floor(x);
    } else if w == 32 {
      extern proc floorf(x: real(32)): real(32);
      return floorf(x);
    } else {
      compilerError("floor() -- Invalid argument width ", w);
    }
  }

  /* Returns a value for which :proc:`isinf` will return `true`. */
  inline proc INFINITY: real(64) return chpl_macro_INFINITY();

  /* Returns `true` if the argument `x` is a representation of a finite value;
     `false` otherwise.
  */
  inline proc isfinite(x: real(?w)) : real(w) {
    if w == 64 {
      return chpl_macro_double_isfinite(x);
    } else if w == 32 {
      return chpl_macro_float_isfinite(x);
    } else {
      compilerError("isfinite() -- Invalid argument width ", w);
    }
  }

  /* Returns `true` if the argument `x` is a representation of *infinity*;
     `false` otherwise. */
  inline proc isinf(x: real(?w)) : real(w) {
    if w == 64 {
      return chpl_macro_double_isinf(x);
    } else if w == 32 {
      return chpl_macro_float_isinf(x);
    } else {
      compilerError("isinf() -- Invalid argument width ", w);
    }
  }

  /* Returns `true` if the argument `x` does not represent a valid number;
     `false` otherwise. */
  inline proc isnan(x: real(?w)) : real(w) {
    if w == 64 {
      return chpl_macro_double_isnan(x);
    } else if w == 32 {
      return chpl_macro_float_isnan(x);
    } else {
      compilerError("isnan() -- Invalid argument width ", w);
    }
  }

  /* Returns the natural logarithm of the absolute value
     of the gamma function of the argument `x`.
  */
  inline proc lgamma(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc lgamma(x: real(64)): real(64);
      return lgamma(x);
    } else if w == 32 {
      extern proc lgammaf(x: real(32)): real(32);
      return lgammaf(x);
    } else {
      compilerError("lgamma() -- Invalid argument width ", w);
    }
  }

  /* Returns the natural logarithm of the argument `x`.

     It is an error if `x` is less than or equal to zero.
  */
  inline proc log(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc log(x: real(64)): real(64);
      return log(x);
    } else if w == 32 {
      extern proc logf(x: real(32)): real(32);
      return logf(x);
    } else {
      compilerError("log() -- Invalid argument width ", w);
    }
  }

  /* Returns the base 10 logarithm of the argument `x`.

     It is an error if `x` is less than or equal to zero.
  */
  inline proc log10(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc log10(x: real(64)): real(64);
      return log10(x);
    } else if w == 32 {
      extern proc log10f(x: real(32)): real(32);
      return log10f(x);
    } else {
      compilerError("log10() -- Invalid argument width ", w);
    }
  }

  /* Returns the natural logarithm of `x` + 1.

     It is an error if `x` is less than or equal to -1.
  */
  inline proc log1p(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc log1p(x: real(64)): real(64);
      return log1p(x);
    } else if w == 32 {
      extern proc log1pf(x: real(32)): real(32);
      return log1pf(x);
    } else {
      compilerError("log1p() -- Invalid argument width ", w);
    }
  }

  /* Returns the base 2 logarithm of the argument `x`.

     It is an error if `x` is less than or equal to zero.
  */
  inline proc log2(x) : x.type where isNumeric(x) {
    if isReal(x) {
      if w == 64 {
        extern proc log2(x: real(64)): real(64);
        return log2(x);
      } else if w == 32 {
        extern proc log2f(x: real(32)): real(32);
        return log2f(x);
      } else {
        compilerError("log2() -- Invalid real argument width ", w);
      }
    } else if isIntegral(x) {
      return logBasePow2(val, 1);
    } else {
      compilerError("log2() -- Invalid numeric argument type: ",
                    typeToString(x.type));
    }
  }

  /* Computes the mod operator on the two integer param arguments, defined as
     ``mod(x,y) = x - y * floor(x / y)``.
  */
  proc mod(param m: integral, param n: integral) param {
    param temp = m % n;

    // verbatim copy from the other 'mod', to simplify maintenance
    return
      if isNonnegative(n) then
      if isUintType(m.type)
        then temp
          else ( if temp >= 0 then temp else temp + n )
            else
              // n < 0
              ( if temp <= 0 then temp else temp + n );
  }

  /* Computes the mod operator on the two arguments, defined as
     ``mod(x,y) = x - y * floor(x / y)``.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  inline proc mod(m:numeric, n:numeric) {
    return mod_help(m, n);
  }

  /* Returns a value for which :proc:`isnan` will return `true`. */
  inline proc NAN : real(64) return chpl_macro_NAN();

  /* Returns the rounded integral value of the argument `x` determined by the
     current rounding direction.  :proc:`nearbyint` will not raise the "inexact"
     floating-point exception.
  */
  inline proc nearbyint(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc nearbyint(x: real(64)): real(64);
      return nearbyint(x);
    } else if w == 32 {
      extern proc nearbyintf(x: real(32)): real(32);
      return nearbyintf(x);
    } else {
      compilerError("nearbyint() -- Invalid argument width ", w);
    }
  }

  /* Returns the rounded integral value of the argument `x` determined by the
     current rounding direction.  :proc:`rint` may raise the "inexact" floating-point
     exception.
  */
  inline proc rint(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc rint(x: real(64)): real(64);
      return rint(x);
    } else if w == 32 {
      extern proc rintf(x: real(32)): real(32);
      return rintf(x);
    } else {
      compilerError("rint() -- Invalid argument width ", w);
    }
  }

  /* Returns the rounded integral value of the argument `x`. */
  inline proc round(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc round(x: real(64)): real(64);
      return round(x);
    } else if w == 32 {
      extern proc roundf(x: real(32)): real(32);
      return roundf(x);
    } else {
      compilerError("round() -- Invalid argument width ", w);
    }
  }

  /* Returns the signum function of the integer param argument `i`:
     1 if positive, -1 if negative, 0 if zero.
  */
  proc sgn(param i : integral) param
    return if i > 0 then 1 else if i == 0 then 0 else -1;

  /* Returns the signum function of the argument `x`:
     1 if positive, -1 if negative, 0 if zero.
  */
  inline proc sgn(x: numeric) return sgn_help(x);

  /* Returns the sine of the argument `x`. */
  inline proc sin(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc sin(x: real(64)): real(64);
      return sin(x);
    } else if w == 32 {
      extern proc sinf(x: real(32)): real(32);
      return sinf(x);
    } else {
      compilerError("sin() -- Invalid argument width ", w);
    }
  }

  /* Returns the hyperbolic sine of the argument `x`. */
  inline proc sinh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc sinh(x: real(64)): real(64);
      return sinh(x);
    } else if w == 32 {
      extern proc sinhf(x: real(32)): real(32);
      return sinhf(x);
    } else {
      compilerError("sinh() -- Invalid argument width ", w);
    }
  }

  /* Returns the square root of the argument `x`.

     It is an error if the `x` is less than zero.
  */
  inline proc sqrt(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc sqrt(x: real(64)): real(64);
      return sqrt(x);
    } else if w == 32 {
      extern proc sqrtf(x: real(32)): real(32);
      return sqrtf(x);
    } else {
      compilerError("sqrt() -- Invalid argument width ", w);
    }
  }

  /* Returns the tangent of the argument `x`. */
  inline proc tan(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc tan(x: real(64)): real(64);
      return tan(x);
    } else if w == 32 {
      extern proc tanf(x: real(32)): real(32);
      return tanf(x);
    } else {
      compilerError("tan() -- Invalid argument width ", w);
    }
  }

  /* Returns the hyperbolic tangent of the argument `x`. */
  inline proc tanh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc tanh(x: real(64)): real(64);
      return tanh(x);
    } else if w == 32 {
      extern proc tanhf(x: real(32)): real(32);
      return tanhf(x);
    } else {
      compilerError("tanh() -- Invalid argument width ", w);
    }
  }

  /* Returns the absolute value of the gamma function of the argument `x`. */
  inline proc tgamma(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc tgamma(x: real(64)): real(64);
      return tgamma(x);
    } else if w == 32 {
      extern proc tgammaf(x: real(32)): real(32);
      return tgammaf(x);
    } else {
      compilerError("tgamma() -- Invalid argument width ", w);
    }
  }

  /* Returns the nearest integral value to the argument `x` that is not larger
     than `x` in absolute value. */
  inline proc trunc(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc trunc(x: real(64)): real(64);
      return trunc(x);
    } else if w == 32 {
      extern proc truncf(x: real(32)): real(32);
      return truncf(x);
    } else {
      compilerError("trunc() -- Invalid argument width ", w);
    }
  }

} // end of module Math

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

/*
This module provides wrappers for <cmath> (math.h) numerical constants and
routines.  Its symbols are provided by default; an explicit 'use' statement
is not necessary.

The C Math library is part of the C Language Standard (ISO/IEC 9899), as
described in Section 7.12.  Please consult that standard for an
authoritative description of the expected properties of those constants and
routines.

In general, where the C math library provides a *double* and a *float* version
of a function, the float version has a suffix 'f'.  In the Chapel interface,
the suffix is dropped, and the type of the operand determines which version is
called -- according to the usual function overloading and resolution rules.
Normally, the result has the same precision
as the argument(s).  Please consult the C standard for specifics.

Rounding -- The rounding mode for floating-point addition (subtraction) is
implementation-defined.

Error Handling -- At present, Chapel does not provide control over error
handling in the Math module.  The default behavior is as if the macro
``math_errhandling`` is set to 0: Given erroneous input at run-time,
all math functions will return an implementation-defined value; no
exception will be generated.

*/
module Math {

  //////////////////////////////////////////////////////////////////////////
  // Helper constants and functions (not included in chpldocs).
  //
  pragma "no doc"
  extern proc chpl_macro_INFINITY():real(64);
  pragma "no doc"
  extern proc chpl_macro_NAN():real(64);

  pragma "no doc"
  extern proc chpl_macro_double_isinf(x: real(64)): c_int;
  pragma "no doc"
  extern proc chpl_macro_float_isinf(x: real(32)): c_int;
  pragma "no doc"
  extern proc chpl_macro_double_isfinite(x: real(64)): c_int;
  pragma "no doc"
  extern proc chpl_macro_float_isfinite(x: real(32)): c_int;
  pragma "no doc"
  extern proc chpl_macro_double_isnan(x: real(64)): c_int;
  pragma "no doc"
  extern proc chpl_macro_float_isnan(x: real(32)): c_int;

  pragma "no doc"
  proc _logBasePow2Help(in val, baseLog2) {
    var result = -1;
    while (val != 0) {
      val >>= baseLog2;
      result += 1;
    }
    return result;
  }

  pragma "no doc"
  proc logBasePow2(in val: int(?w), baseLog2) {
    if (val < 1) {
      halt("Can't take the log() of a non-positive integer");
    }
    return _logBasePow2Help(val, baseLog2);
  }

  pragma "no doc"
  proc logBasePow2(in val: uint(?w), baseLog2) {
    if (val < 1) {
      halt("Can't take the log() of a non-positive integer");
    }
    return _logBasePow2Help(val, baseLog2);
  }
  //
  //////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////
  //
  // Public interface (included in chpldocs).
  //
  // (The entries below are alphabetized (case-insensitively) because chpldocs
  // presents them in declaration order.)
  //

  /* Returns the absolute value of the integer param argument `i`. */
  proc abs(param i : integral) param return if i < 0 then -i else i;

  /* Returns the magnitude (absolute value) of the argument `x`.

     :rtype: The type of `x`.
  */
  inline proc abs(x) where isNumericalType(x.type) {
    if isComplex(x) {
      return sqrt(x.re * x.re + x.im * -x.im);
    } else if isReal(x) {
      if numBits(x) == 64 {
        extern proc fabs(x: real(64)): real(64);
        return fabs(x);
      } else if numBits(x) == 32 {
        extern proc fabsf(x: real(32)): real(32);
        return fabsf(x);
      } else {
        compilerError("abs() -- Invalid real argument width: ", numBits(x));
      }
    } else if isImaginary(x) {
      if numBits(x) == 64 {
        extern proc fabs(x: real(64)): real(64);
        return fabs(_i2r(x));
      } else if numBits(x) == 32 {
        extern proc fabsf(x: real(32)): real(32);
        return fabsf(_i2r(x));
      } else {
        compilerError("abs() -- Invalid imaginary argument width ", numBits(x));
      }
    } else if isIntegral(x) {
      if (isSigned(x)) then
        return if i < 0 then -i else i;
      else
        return if x == 0 then 0 else i;
    } else{
      compilerError("abs() -- Invalid argument type: ", typeToString(x.type));
    }
  }

  /* Returns the arc cosine of the real argument `x`.

     It is an error if `x` is less than -1 or greater than 1.
  */
  inline proc acos(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc acos(x: real(64)): real(64);
      return acos(x);
    } else if w == 32 {
      extern proc acosf(x: real(32)): real(32);
      return acosf(x);
    } else {
      compilerError("acos() -- Invalid argument width ", w);
    }
  }

  /* Returns the inverse hyperbolic cosine of the argument `x`.

     It is an error if `x` is less than 1.
  */
  inline proc acosh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc acosh(x: real(64)): real(64);
      return acosh(x);
    } else if w == 32 {
      extern proc acoshf(x: real(32)): real(32);
      return acoshf(x);
    } else {
      compilerError("acosh() -- Invalid argument width ", w);
    }
  }

  /* Returns the arc sine of the argument `x`.

     It is an error if `x` is less than -1 or greater than 1.
  */
  inline proc asin(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc asin(x: real(64)): real(64);
      return asin(x);
    } else if w == 32 {
      extern proc asinf(x: real(32)): real(32);
      return asinf(x);
    } else {
      compilerError("asin() -- Invalid argument width ", w);
    }
  }

  /* Returns the inverse hyperbolic sine of the argument `x`. */
  inline proc asinh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc asinh(x: real(64)): real(64);
      return asinh(x);
    } else if w == 32 {
      extern proc asinyf(x: real(32)): real(32);
      return asinhf(x);
    } else {
      compilerError("asinh() -- Invalid argument width ", w);
    }
  }

  /* Returns the arc tangent of the argument `x`. */
  inline proc atan(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc atan(x: real(64)): real(64);
      return atan(x);
    } else if w == 32 {
      extern proc atanf(x: real(32)): real(32);
      return atanf(x);
    } else {
      compilerError("atan() -- Invalid argument width ", w);
    }
  }

  /* Returns the arc tangent of the ratio of the two arguments.

     This is equivalent to
     the arc tangent of `y` / `x` except that the signs of `y`
     and `x` are used to determine the quadrant of the result.
  */
  inline proc atan2(y: real(?w), x: real(w)) : real(w) {
    if w == 64 {
      extern proc atan2(y: real(64), x: real(64)): real(64);
      return atan2(y, x);
    } else if w == 32 {
      extern proc atan2f(y: real(32), x: real(32)): real(32);
      return atan2f(y, x);
    } else {
      compilerError("atan2() -- Invalid argument width ", w);
    }
  }

  /* Returns the inverse hyperbolic tangent of the argument `x`.

     It is an error if `x` is less than -1 or greater than 1.
  */
  inline proc atanh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc atanh(x: real(64)): real(64);
      return atanh(x);
    } else if w == 32 {
      extern proc atanhf(x: real(32)): real(32);
      return atanhf(x);
    } else {
      compilerError("atanh() -- Invalid argument width ", w);
    }
  }

  /* Returns the cube root of the argument `x`. */
  inline proc cbrt(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc cbrt(x: real(64)): real(64);
      return cbrt(x);
    } else if w == 32 {
      extern proc cbrtf(x: real(32)): real(32);
      return cbrtf(x);
    } else {
      compilerError("cbrt() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of the argument `x` rounded up to the nearest integer. */
  inline proc ceil(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc ceil(x: real(64)): real(64);
      return ceil(x);
    } else if w == 32 {
      extern proc ceilf(x: real(32)): real(32);
      return ceilf(x);
    } else {
      compilerError("ceil() -- Invalid argument width ", w);
    }
  }

  /* Returns the complex conjugate of the argument `x`. */
  inline proc conjg(x: complex(?w)) : complex(w) return (x.re, -x.im):complex(w);

  /* Returns the cosine of the argument `x`. */
  inline proc cos(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc cos(x: real(64)): real(64);
      return cos(x);
    } else if w == 32 {
      extern proc cosf(x: real(32)): real(32);
      return cosf(x);
    } else {
      compilerError("cos() -- Invalid argument width ", w);
    }
  }

  /* Returns the hyperbolic cosine of the argument `x`. */
  inline proc cosh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc cosh(x: real(64)): real(64);
      return cosh(x);
    } else if w == 32 {
      extern proc coshf(x: real(32)): real(32);
      return coshf(x);
    } else {
      compilerError("cosh() -- Invalid argument width ", w);
    }
  }

  /* Returns :proc:`ceil`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded up to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divceil(m: integral, n: integral) return
    if isNonnegative(m) then
    if isNonnegative(n) then (m + n - 1) / n
      else                     m / n
        else
          if isNonnegative(n) then m / n
            else                     (m + n + 1) / n;

  /* Returns :proc:`ceil`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded up to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divceil(param m: integral, param n: integral) param return
    if isNonnegative(m) then
    if isNonnegative(n) then (m + n - 1) / n
      else                     m / n
        else
          if isNonnegative(n) then m / n
            else                     (m + n + 1) / n;

  /* Returns :proc:`floor`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded down to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divfloor(m: integral, n: integral) return
    if isNonnegative(m) then
    if isNonnegative(n) then m / n
      else                     (m - n - 1) / n
        else
          if isNonnegative(n) then (m - n + 1) / n
            else                     m / n;

  /* Returns :proc:`floor`\(`m`/`n`),
     i.e., the fraction `m`/`n` rounded down to the nearest integer.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc divfloor(param m: integral, param n: integral) param return
    if isNonnegative(m) then
    if isNonnegative(n) then m / n
      else                     (m - n - 1) / n
        else
          if isNonnegative(n) then (m - n + 1) / n
            else                     m / n;

  /* Returns the error function of the argument `x`. */
  inline proc erf(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc erf(x: real(64)): real(64);
      return erf(x);
    } else if w == 32 {
      extern proc erff(x: real(32)): real(32);
      return erff(x);
    } else {
      compilerError("erf() -- Invalid argument width ", w);
    }
  }

  /* Returns the complementary error function of the argument.
     This is equivalent to 1.0 - :proc:`erf`\(`x`).
  */
  inline proc erfc(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc erfc(x: real(64)): real(64);
      return erfc(x);
    } else if w == 32 {
      extern proc erfcf(x: real(32)): real(32);
      return erfcf(x);
    } else {
      compilerError("erfc() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of the Napierien `e` raised to the power of the argument `x`. */
  inline proc exp(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc exp(x: real(64)): real(64);
      return exp(x);
    } else if w == 32 {
      extern proc expf(x: real(32)): real(32);
      return expf(x);
    } else {
      compilerError("exp() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of `2` raised to the power of the argument `x`. */
  inline proc exp2(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc exp2(x: real(64)): real(64);
      return exp2(x);
    } else if w == 32 {
      extern proc exp2f(x: real(32)): real(32);
      return exp2f(x);
    } else {
      compilerError("exp2() -- Invalid argument width ", w);
    }
  }

  /* Returns one less than the value of the Napierien `e` raised to the power
     of the argument `x`.
  */
  inline proc expm1(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc expm1(x: real(64)): real(64);
      return expm1(x);
    } else if w == 32 {
      extern proc expm1f(x: real(32)): real(32);
      return expm1f(x);
    } else {
      compilerError("expm1() -- Invalid argument width ", w);
    }
  }

  /* Returns the value of the argument `x` rounded down to the nearest integer. */
  inline proc floor(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc floor(x: real(64)): real(64);
      return floor(x);
    } else if w == 32 {
      extern proc floorf(x: real(32)): real(32);
      return floorf(x);
    } else {
      compilerError("floor() -- Invalid argument width ", w);
    }
  }

  /* Returns a value for which :proc:`isinf` will return `true`. */
  inline proc INFINITY: real(64) return chpl_macro_INFINITY();

  /* Returns `true` if the argument `x` is a representation of a finite value;
     `false` otherwise.
  */
  inline proc isfinite(x: real(?w)) : real(w) {
    if w == 64 {
      return chpl_macro_double_isfinite(x);
    } else if w == 32 {
      return chpl_macro_float_isfinite(x);
    } else {
      compilerError("isfinite() -- Invalid argument width ", w);
    }
  }

  /* Returns `true` if the argument `x` is a representation of *infinity*;
     `false` otherwise. */
  inline proc isinf(x: real(?w)) : real(w) {
    if w == 64 {
      return chpl_macro_double_isinf(x);
    } else if w == 32 {
      return chpl_macro_float_isinf(x);
    } else {
      compilerError("isinf() -- Invalid argument width ", w);
    }
  }

  /* Returns `true` if the argument `x` does not represent a valid number;
     `false` otherwise. */
  inline proc isnan(x: real(?w)) : real(w) {
    if w == 64 {
      return chpl_macro_double_isnan(x);
    } else if w == 32 {
      return chpl_macro_float_isnan(x);
    } else {
      compilerError("isnan() -- Invalid argument width ", w);
    }
  }

  /* Returns the natural logarithm of the absolute value
     of the gamma function of the argument `x`.
  */
  inline proc lgamma(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc lgamma(x: real(64)): real(64);
      return lgamma(x);
    } else if w == 32 {
      extern proc lgammaf(x: real(32)): real(32);
      return lgammaf(x);
    } else {
      compilerError("lgamma() -- Invalid argument width ", w);
    }
  }

  /* Returns the natural logarithm of the argument `x`.

     It is an error if `x` is less than or equal to zero.
  */
  inline proc log(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc log(x: real(64)): real(64);
      return log(x);
    } else if w == 32 {
      extern proc logf(x: real(32)): real(32);
      return logf(x);
    } else {
      compilerError("log() -- Invalid argument width ", w);
    }
  }

  /* Returns the base 10 logarithm of the argument `x`.

     It is an error if `x` is less than or equal to zero.
  */
  inline proc log10(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc log10(x: real(64)): real(64);
      return log10(x);
    } else if w == 32 {
      extern proc log10f(x: real(32)): real(32);
      return log10f(x);
    } else {
      compilerError("log10() -- Invalid argument width ", w);
    }
  }

  /* Returns the natural logarithm of `x` + 1.

     It is an error if `x` is less than or equal to -1.
  */
  inline proc log1p(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc log1p(x: real(64)): real(64);
      return log1p(x);
    } else if w == 32 {
      extern proc log1pf(x: real(32)): real(32);
      return log1pf(x);
    } else {
      compilerError("log1p() -- Invalid argument width ", w);
    }
  }

  /* Returns the base 2 logarithm of the argument `x`.

     It is an error if `x` is less than or equal to zero.
  */
  inline proc log2(x) : x.type where isNumeric(x) {
    if isReal(x) {
      if w == 64 {
        extern proc log2(x: real(64)): real(64);
        return log2(x);
      } else if w == 32 {
        extern proc log2f(x: real(32)): real(32);
        return log2f(x);
      } else {
        compilerError("log2() -- Invalid real argument width ", w);
      }
    } else if isIntegral(x) {
      return logBasePow2(val, 1);
    } else {
      compilerError("log2() -- Invalid numeric argument type: ",
                    typeToString(x.type));
    }
  }

  /* Computes the mod operator on the two integer param arguments, defined as
     ``mod(x,y) = x - y * floor(x / y)``.
  */
  proc mod(param m: integral, param n: integral) param {
    param temp = m % n;

    // verbatim copy from the other 'mod', to simplify maintenance
    return
      if isNonnegative(n) then
      if isUintType(m.type)
        then temp
          else ( if temp >= 0 then temp else temp + n )
            else
              // n < 0
              ( if temp <= 0 then temp else temp + n );
  }

  /* Computes the mod operator on the two arguments, defined as
     ``mod(x,y) = x - y * floor(x / y)``.

     If the arguments are of unsigned type, then
     fewer condititionals will be evaluated at run time.
  */
  proc mod(m: integral, n: integral) {
    const temp = m % n;

    // eliminate some run-time tests if input(s) is(are) unsigned
    return
      if isNonnegative(n) then
      if isUintType(m.type)
        then temp
          else ( if temp >= 0 then temp else temp + n )
            else
              // n < 0
              ( if temp <= 0 then temp else temp + n );
  }

  /* Computes the mod operator on the two numbers, defined as
     ``mod(x,y) = x - y * floor(x / y)``.
  */
  proc mod(x: real(?w), y: real(w)): real(w) {
    // This codes up the standard definition, according to Wikipedia.
    // Is there a more efficient implementation for reals?
    return x - y*floor(x/y);
  }

  /* Returns a value for which :proc:`isnan` will return `true`. */
  inline proc NAN : real(64) return chpl_macro_NAN();

  /* Returns the rounded integral value of the argument `x` determined by the
     current rounding direction.  :proc:`nearbyint` will not raise the "inexact"
     floating-point exception.
  */
  inline proc nearbyint(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc nearbyint(x: real(64)): real(64);
      return nearbyint(x);
    } else if w == 32 {
      extern proc nearbyintf(x: real(32)): real(32);
      return nearbyintf(x);
    } else {
      compilerError("nearbyint() -- Invalid argument width ", w);
    }
  }

  /* Returns the rounded integral value of the argument `x` determined by the
     current rounding direction.  :proc:`rint` may raise the "inexact" floating-point
     exception.
  */
  inline proc rint(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc rint(x: real(64)): real(64);
      return rint(x);
    } else if w == 32 {
      extern proc rintf(x: real(32)): real(32);
      return rintf(x);
    } else {
      compilerError("rint() -- Invalid argument width ", w);
    }
  }

  /* Returns the rounded integral value of the argument `x`. */
  inline proc round(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc round(x: real(64)): real(64);
      return round(x);
    } else if w == 32 {
      extern proc roundf(x: real(32)): real(32);
      return roundf(x);
    } else {
      compilerError("round() -- Invalid argument width ", w);
    }
  }

  /* Returns the signum function of the integer param argument `i`:
     1 if positive, -1 if negative, 0 if zero.
  */
  proc sgn(param i : integral) param
    return if i > 0 then 1 else if i == 0 then 0 else -1;

  /* Returns the signum function of the integer argument `i`:
     1 if positive, -1 if negative, 0 if zero.
  */
  inline proc sgn(i : int(?w)): int(8)
    return ((i > 0) : int(8) - (i < 0) : int(8)) : int(8);

  /* Returns the signum function of the unsigned integer argument `i`:
     1 if positive, -1 if negative, 0 if zero.
  */
  inline proc sgn(i : uint(?w)): uint(8)
    return (i > 0) : uint(8);

  /* Returns the signum function of the real argument `x`:
     1 if positive, -1 if negative, 0 if zero.
  */
  inline proc sgn(x : real(?w)): int(8)
    return ((x > 0.0) : int(8) - (x < 0.0) : int(8)) : int(8);

  /* Returns the sine of the argument `x`. */
  inline proc sin(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc sin(x: real(64)): real(64);
      return sin(x);
    } else if w == 32 {
      extern proc sinf(x: real(32)): real(32);
      return sinf(x);
    } else {
      compilerError("sin() -- Invalid argument width ", w);
    }
  }

  /* Returns the hyperbolic sine of the argument `x`. */
  inline proc sinh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc sinh(x: real(64)): real(64);
      return sinh(x);
    } else if w == 32 {
      extern proc sinhf(x: real(32)): real(32);
      return sinhf(x);
    } else {
      compilerError("sinh() -- Invalid argument width ", w);
    }
  }

  /* Returns the square root of the argument `x`.

     It is an error if the `x` is less than zero.
  */
  inline proc sqrt(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc sqrt(x: real(64)): real(64);
      return sqrt(x);
    } else if w == 32 {
      extern proc sqrtf(x: real(32)): real(32);
      return sqrtf(x);
    } else {
      compilerError("sqrt() -- Invalid argument width ", w);
    }
  }

  /* Returns the tangent of the argument `x`. */
  inline proc tan(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc tan(x: real(64)): real(64);
      return tan(x);
    } else if w == 32 {
      extern proc tanf(x: real(32)): real(32);
      return tanf(x);
    } else {
      compilerError("tan() -- Invalid argument width ", w);
    }
  }

  /* Returns the hyperbolic tangent of the argument `x`. */
  inline proc tanh(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc tanh(x: real(64)): real(64);
      return tanh(x);
    } else if w == 32 {
      extern proc tanhf(x: real(32)): real(32);
      return tanhf(x);
    } else {
      compilerError("tanh() -- Invalid argument width ", w);
    }
  }

  /* Returns the absolute value of the gamma function of the argument `x`. */
  inline proc tgamma(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc tgamma(x: real(64)): real(64);
      return tgamma(x);
    } else if w == 32 {
      extern proc tgammaf(x: real(32)): real(32);
      return tgammaf(x);
    } else {
      compilerError("tgamma() -- Invalid argument width ", w);
    }
  }

  /* Returns the nearest integral value to the argument `x` that is not larger
     than `x` in absolute value. */
  inline proc trunc(x: real(?w)) : real(w) {
    if w == 64 {
      extern proc trunc(x: real(64)): real(64);
      return trunc(x);
    } else if w == 32 {
      extern proc truncf(x: real(32)): real(32);
      return truncf(x);
    } else {
      compilerError("trunc() -- Invalid argument width ", w);
    }
  }

} // end of module Math