Example for Cyclic; Example for 'localSubdomains' on Block

//
// Distributions Primer
//
// This primer demonstrates uses of some of Chapel's standard
// distributions.  To use these distributions in a Chapel program,
// the respective module must be used:
//
use BlockDist, CyclicDist, BlockCycDist, ReplicatedDist;
use DimensionalDist2D, ReplicatedDim, BlockCycDim;

//
// For each distribution, we'll create a distributed domain and array
// and then initialize it just to give a brief flavor of how the
// distribution maps across locales.  Running this example on 6
// locales does a nice job of illustrating the distribution
// characteristics.
//
// All of these distributions support options to map to a different
// virtual locale grid than the one used by default (a
// multidimensional factoring of the built-in Locales array), as well
// as to control the amount of parallelism used in data parallel
// loops.  See the Standard Distributions chapter of the language spec
// for more details.
//

//
// Make the program size configurable from the command line.
//
config const n = 8;

//
// Declare a 2-dimensional domain Space that we will later use to
// initialize the distributed domains.
//
const Space = {1..n, 1..n};

//
// The Block distribution distributes a bounding box from
// n-dimensional space across the target locale array viewed as an
// n-dimensional virtual locale grid.  The bounding box is blocked
// into roughly equal portions across the locales.  Note that domains
// declared over a Block distribution can also store indices outside
// of the bounding box; the bounding box is merely used to compute
// the blocking of space.
//
// In this example, we declare a 2-dimensional Block-distributed
// domain BlockSpace and a Block-distributed array BA declared over
// the domain.
//
const BlockSpace = Space dmapped Block(boundingBox=Space);
var BA: [BlockSpace] int;

//
// To illustrate how the index set is distributed across locales,
// we'll use a forall loop to initialize each array element to the
// locale ID that stores that index/element/iteration.
//
forall ba in BA do
  ba = here.id;

//
// The 'hasSingleLocalSubdomain' method on arrays will return true if the
// index set for a locale can be represented by a single domain.
//
if !BA.hasSingleLocalSubdomain() then
  halt("For a Block distribution, the index set per locale should be \
      represented by a single domain");

//
// If the distribution's subdomains can be represented as single subdomain,
// we can use the 'localSubdomain' method to get the index set for the
// current locale.
//
// Below, we'll use the index set to confirm that the array elements have the
// correct locale id.
//

for L in Locales {
  on L {
    const indices = BA.localSubdomain();
    for i in indices {
      if BA[i] != L.id then
        halt("Error: incorrect locale id");
    }
  }
}

//
// Output the Block-distributed array to visually see how the elements
// are partitioned across the locales.
//
writeln("Block Array Index Map");
writeln(BA);
writeln();

//
// Most of Chapel's standard distributions support an optional
// targetLocales argument that permits you to pass in your own
// array of locales to be targeted.  In general, the targetLocales
// argument should match the rank of the distribution.  So for
// example, to map a Block to a [numLocales x 1] view of the
// locale set, one could do something like this:

//
// We start by creating our own array of the locale values.  Here
// we use the standard array reshape function for convenience,
// but more generally, this array could be accessed/assigned like any
// other.
//

var MyLocaleView = {0..#numLocales, 1..1};
var MyLocales: [MyLocaleView] locale = reshape(Locales, MyLocaleView);

//
// Then we'll declare a distributed domain/array that targets
// this view of the locales:
//

const BlockSpace2 = Space dmapped Block(boundingBox=Space,
                                        targetLocales=MyLocales);
var BA2: [BlockSpace2] int;

//
// Then we'll do a similar computation as before to verify where
// everything ended up:
//
forall ba in BA2 do
  ba = here.id;

writeln("Block Array Index Map");
writeln(BA2);
writeln();

//
// We can use the 'targetLocales' method available on an array to get the
// locales array used as targets.
//
for (L, ML) in zip(BA2.targetLocales(), MyLocales) do
  if L != ML then
    halt("Error: BA2.targetLocales() should equal MyLocales");

//
// Next, we'll perform a similar computation for the Cyclic distribution.
// Cyclic distributions start at a designated n-dimensional index and
// distribute the n-dimensional space across an n-dimensional array
// of locales in a round-robin fashion (in each dimension).  As with
// the Block distribution, domains may be declared using the
// distribution who have lower indices that the starting index; that
// value should just be considered a parameterization of how the
// distribution is defined.
//
const CyclicSpace = Space dmapped Cyclic(startIdx=Space.low);
var CA: [CyclicSpace] int;

forall ca in CA do
  ca = here.id;

writeln("Cyclic Array Index Map");
writeln(CA);
writeln();

//
// The domain returned by 'localSubdomain' need not be a dense block, as is
// the case for the Cyclic Distribution.
//
on Locales[0] {
  const indices = CA.localSubdomain();
  for i in indices {
    if CA[i] != 0 then
      halt("Error: Cyclic array values on Locale 0 should be zero");
  }
}

//
// Next, we'll declare a Block-Cyclic distribution.  These
// distributions also deal out indices in a round-robin fashion,
// but rather than dealing out singleton indices, they deal out blocks
// of indices.  Thus, the BlockCyclic distribution is parameterized
// by a starting index (as with Cyclic) and a block size (per
// dimension) specifying how large the chunks to be dealt out are.
//
const BlkCycSpace = Space dmapped BlockCyclic(startIdx=Space.low,
                                              blocksize=(2, 3));
var BCA: [BlkCycSpace] int;

forall bca in BCA do
  bca = here.id;

writeln("Block-Cyclic Array Index Map");
writeln(BCA);
writeln();

//
// A locale's index set for a Block-Cyclic distribution cannot be represented
// by a single subdomain.
//
if BCA.hasSingleLocalSubdomain() then
  halt("A Block-Cyclic index set cannot be represented by a single subdomain");

//
// If the local index set cannot be represented by a single subdomain,
// we can use the 'localSubdomains' iterator to yield a number of domains
// that represent the whole index set.
//
// Let's write a function that will use 'localSubdomains' to verify the
// correctness of the array values.
//

proc verifyID(Data: []) {
  for L in Locales {
    on L {
      for indices in Data.localSubdomains() {
        for i in indices {
          if Data[i] != L.id then
            halt("Error: incorrect locale id");
        }
      }
    }
  }
}
verifyID(BCA);

//
// The 'localSubdomains' iterator is also available on distributions that
// can represent a locale's index set with a single domain. This allows us to
// write more general code that will work for all distributions.
//
// This means that we can call the 'verifyID' function on any array, like the
// 'BA' array from earlier.
//
verifyID(BA);

//
// The ReplicatedDist distribution is different: each of the
// original domain's indices - and the corresponding array elements -
// is replicated onto each locale. (Note: consistency among these
// array replicands is NOT maintained automatically.)
//
// This replication is observable in some cases but not others,
// as shown below. Note: this behavior may change in the future.
//
const ReplicatedSpace = Space dmapped ReplicatedDist();
var RA: [ReplicatedSpace] int;

// The replication is observable - this visits each replicand.
forall ra in RA do
  ra = here.id;

writeln("Replicated Array Index Map, ", RA.numElements, " elements total");
writeln(RA);
writeln();

//
// The replication is observable when the replicated array is
// on the left-hand side. If the right-hand side is not replicated,
// it is copied into each replicand.
// We illustrate this using a non-distributed array.
//
var A: [Space] int = [(i,j) in Space] i*100 + j;
RA = A;
writeln("Replicated Array after being array-assigned into");
writeln(RA);
writeln();

//
// Analogously, each replicand will be visited and
// other participated expressions will be computed on each locale
// (a) when the replicated array is assigned a scalar:
//       RA = 5;
// (b) when it appears first in a zippered forall loop:
//       forall (ra, a) in zip(RA, A) do ...;
// (c) when it appears in a for loop:
//       for ra in RA do ...;
//
// Zippering (RA,A) or (A,RA) in a 'for' loop will generate
// an error due to their different number of elements.

// Let RA store the Index Map again, for the examples below.
forall ra in RA do
  ra = here.id;

//
// Only the local replicand is accessed - replication is NOT observable
// and consistency is NOT maintained - when:
// (a) the replicated array is indexed - an individual element is read...
//
on Locales(0) do
  writeln("on ", here, ": ", RA(Space.low));
on Locales(LocaleSpace.high) do
  writeln("on ", here, ": ", RA(Space.low));
writeln();

// ...or an individual element is written;
on Locales(LocaleSpace.high) do
  RA(Space.low) = 7777;

writeln("Replicated Array after being indexed into");
writeln(RA);
writeln();

//
// (b) the replicated array is on the right-hand side of an assignment...
//
on Locales(LocaleSpace.high) do
  A = RA + 4;
writeln("Non-Replicated Array after assignment from Replicated Array + 4");
writeln(A);
writeln();

//
// (c) ...or, generally, the replicated array or domain participates
//     in a zippered forall loop, but not in the first position.
//     The loop could look like:
//
//       forall (a, (i,j), ra) in (A, ReplicatedSpace, RA) do ...;
//

//
// The DimensionalDist2D distribution lets us build a 2D distribution
// as a composition of specifiers for individual dimensions.
// Under such a "dimensional" distribution each dimension is handled
// independently of the other.
//
// The dimension specifiers are similar to the corresponding multi-dimensional
// distributions in constructor arguments and index-to-locale mapping rules.
// However, instead of an array of locales, a specifier constructor
// accepts just the number of locales that the indices in the corresponding
// dimension will be distributed across.
//
// The DimensionalDist2D constructor requires:
// * an [0..nl1-1, 0..nl2-1] array of locales, where
//   nl1 and nl2 are the number of locales in each dimension, and
// * two dimension specifiers, created for nl1 and nl2 locale counts, resp.
//
// Presently, the following dimension specifiers are available
// (shown here with their constructor arguments):
//
// * ReplicatedDim(numLocales)
// * BlockDim(numLocales, boundingBoxLow, boundingBoxHigh)
// * BlockCyclicDim(lowIdx, blockSize, numLocales)
//

//
// The following example creates a dimensional distribution that
// replicates over 2 locales (when available) in the first dimemsion
// and distributes using block-cyclic distribution in the second dimension.
// The example computes nl1 and nl2 and reshapes MyLocales correspondingly.
//

var (nl1, nl2) = if numLocales == 1 then (1, 1) else (2, numLocales/2);
MyLocaleView = {0..#nl1, 0..#nl2};
MyLocales = reshape(Locales[0..#nl1*nl2], MyLocaleView);

const DimReplicatedBlockcyclicSpace = Space
  dmapped DimensionalDist2D(MyLocales,
                            new ReplicatedDim(numLocales = nl1),
                            new BlockCyclicDim(numLocales = nl2,
                                               lowIdx = 1, blockSize = 2));

var DRBA: [DimReplicatedBlockcyclicSpace] int;

// The ReplicatedDim specifier always accesses the local replicand.
// (This differs from how the ReplicatedDist distribution works.)
//
// This example visits each replicand. The behavior is the same
// regardless of the second index into MyLocales below.

for locId1 in 0..#nl1 do on MyLocales[locId1, 0] {

  forall drba in DRBA do
    drba = here.id;

  writeln("Dimensional2D(Replicated,BlockCyclic) Array Index Map",
          " from ", here);

  // Technicality: 'writeln(DRBA)' would read DRBA always on Locale 0.
  // Since we want to see what DRBA contains on the current locale,
  // we use 'Helper' that is mapped using the default distribution.
  // 'Helper = DRBA' captures the view of DRBA on the current locale,
  // which we then print out.

  const Helper: [Space] int = DRBA;
  writeln(Helper);
  writeln();

}

//
// Distributions Primer
//
// This primer demonstrates uses of some of Chapel's standard
// distributions.  To use these distributions in a Chapel program,
// the respective module must be used:
//
use BlockDist, CyclicDist, BlockCycDist, ReplicatedDist;
use DimensionalDist2D, ReplicatedDim, BlockCycDim;

//
// For each distribution, we'll create a distributed domain and array
// and then initialize it just to give a brief flavor of how the
// distribution maps across locales.  Running this example on 6
// locales does a nice job of illustrating the distribution
// characteristics.
//
// All of these distributions support options to map to a different
// virtual locale grid than the one used by default (a
// multidimensional factoring of the built-in Locales array), as well
// as to control the amount of parallelism used in data parallel
// loops.  See the Standard Distributions chapter of the language spec
// for more details.
//

//
// Make the program size configurable from the command line.
//
config const n = 8;

//
// Declare a 2-dimensional domain Space that we will later use to
// initialize the distributed domains.
//
const Space = {1..n, 1..n};

//
// The Block distribution distributes a bounding box from
// n-dimensional space across the target locale array viewed as an
// n-dimensional virtual locale grid.  The bounding box is blocked
// into roughly equal portions across the locales.  Note that domains
// declared over a Block distribution can also store indices outside
// of the bounding box; the bounding box is merely used to compute
// the blocking of space.
//
// In this example, we declare a 2-dimensional Block-distributed
// domain BlockSpace and a Block-distributed array BA declared over
// the domain.
//
const BlockSpace = Space dmapped Block(boundingBox=Space);
var BA: [BlockSpace] int;

//
// To illustrate how the index set is distributed across locales,
// we'll use a forall loop to initialize each array element to the
// locale ID that stores that index/element/iteration.
//
forall ba in BA do
  ba = here.id;

//
// The 'hasSingleLocalSubdomain' method on arrays will return true if the
// index set for a locale can be represented by a single domain.
//
if !BA.hasSingleLocalSubdomain() then
  halt("For a Block distribution, the index set per locale should be \
      represented by a single domain");

//
// If the distribution's subdomains can be represented as single subdomain,
// we can use the 'localSubdomain' method to get the index set for the
// current locale.
//
// Below, we'll use the index set to confirm that the array elements have the
// correct locale id.
//

for L in Locales {
  on L {
    const indices = BA.localSubdomain();
    for i in indices {
      if BA[i] != L.id then
        halt("Error: incorrect locale id");
    }
  }
}

//
// Output the Block-distributed array to visually see how the elements
// are partitioned across the locales.
//
writeln("Block Array Index Map");
writeln(BA);
writeln();

//
// Most of Chapel's standard distributions support an optional
// targetLocales argument that permits you to pass in your own
// array of locales to be targeted.  In general, the targetLocales
// argument should match the rank of the distribution.  So for
// example, to map a Block to a [numLocales x 1] view of the
// locale set, one could do something like this:

//
// We start by creating our own array of the locale values.  Here
// we use the standard array reshape function for convenience,
// but more generally, this array could be accessed/assigned like any
// other.
//

var MyLocaleView = {0..#numLocales, 1..1};
var MyLocales: [MyLocaleView] locale = reshape(Locales, MyLocaleView);

//
// Then we'll declare a distributed domain/array that targets
// this view of the locales:
//

const BlockSpace2 = Space dmapped Block(boundingBox=Space,
                                        targetLocales=MyLocales);
var BA2: [BlockSpace2] int;

//
// Then we'll do a similar computation as before to verify where
// everything ended up:
//
forall ba in BA2 do
  ba = here.id;

writeln("Block Array Index Map");
writeln(BA2);
writeln();

//
// We can use the 'targetLocales' method available on an array to get the
// locales array used as targets.
//
for (L, ML) in zip(BA2.targetLocales(), MyLocales) do
  if L != ML then
    halt("Error: BA2.targetLocales() should equal MyLocales");

//
// Next, we'll perform a similar computation for the Cyclic distribution.
// Cyclic distributions start at a designated n-dimensional index and
// distribute the n-dimensional space across an n-dimensional array
// of locales in a round-robin fashion (in each dimension).  As with
// the Block distribution, domains may be declared using the
// distribution who have lower indices that the starting index; that
// value should just be considered a parameterization of how the
// distribution is defined.
//
const CyclicSpace = Space dmapped Cyclic(startIdx=Space.low);
var CA: [CyclicSpace] int;

forall ca in CA do
  ca = here.id;

writeln("Cyclic Array Index Map");
writeln(CA);
writeln();

//
// Next, we'll declare a Block-Cyclic distribution.  These
// distributions also deal out indices in a round-robin fashion,
// but rather than dealing out singleton indices, they deal out blocks
// of indices.  Thus, the BlockCyclic distribution is parameterized
// by a starting index (as with Cyclic) and a block size (per
// dimension) specifying how large the chunks to be dealt out are.
//
const BlkCycSpace = Space dmapped BlockCyclic(startIdx=Space.low,
                                              blocksize=(2, 3));
var BCA: [BlkCycSpace] int;

forall bca in BCA do
  bca = here.id;

writeln("Block-Cyclic Array Index Map");
writeln(BCA);
writeln();

//
// A locale's index set for a Block-Cyclic distribution cannot be represented
// by a single subdomain.
//
if BCA.hasSingleLocalSubdomain() then
  halt("A Block-Cyclic index set cannot be represented by a single subdomain");

//
// If the local index set cannot be represented by a single subdomain,
// we can use the 'localSubdomains' iterator to yield a number of domains
// that represent the whole index set.
//
for L in Locales {
  on L {
    for indices in BCA.localSubdomains() {
      for i in indices {
        if BCA[i] != L.id then
          halt("Error: incorrect locale id");
      }
    }
  }
}

//
// The ReplicatedDist distribution is different: each of the
// original domain's indices - and the corresponding array elements -
// is replicated onto each locale. (Note: consistency among these
// array replicands is NOT maintained automatically.)
//
// This replication is observable in some cases but not others,
// as shown below. Note: this behavior may change in the future.
//
const ReplicatedSpace = Space dmapped ReplicatedDist();
var RA: [ReplicatedSpace] int;

// The replication is observable - this visits each replicand.
forall ra in RA do
  ra = here.id;

writeln("Replicated Array Index Map, ", RA.numElements, " elements total");
writeln(RA);
writeln();

//
// The replication is observable when the replicated array is
// on the left-hand side. If the right-hand side is not replicated,
// it is copied into each replicand.
// We illustrate this using a non-distributed array.
//
var A: [Space] int = [(i,j) in Space] i*100 + j;
RA = A;
writeln("Replicated Array after being array-assigned into");
writeln(RA);
writeln();

//
// Analogously, each replicand will be visited and
// other participated expressions will be computed on each locale
// (a) when the replicated array is assigned a scalar:
//       RA = 5;
// (b) when it appears first in a zippered forall loop:
//       forall (ra, a) in zip(RA, A) do ...;
// (c) when it appears in a for loop:
//       for ra in RA do ...;
//
// Zippering (RA,A) or (A,RA) in a 'for' loop will generate
// an error due to their different number of elements.

// Let RA store the Index Map again, for the examples below.
forall ra in RA do
  ra = here.id;

//
// Only the local replicand is accessed - replication is NOT observable
// and consistency is NOT maintained - when:
// (a) the replicated array is indexed - an individual element is read...
//
on Locales(0) do
  writeln("on ", here, ": ", RA(Space.low));
on Locales(LocaleSpace.high) do
  writeln("on ", here, ": ", RA(Space.low));
writeln();

// ...or an individual element is written;
on Locales(LocaleSpace.high) do
  RA(Space.low) = 7777;

writeln("Replicated Array after being indexed into");
writeln(RA);
writeln();

//
// (b) the replicated array is on the right-hand side of an assignment...
//
on Locales(LocaleSpace.high) do
  A = RA + 4;
writeln("Non-Replicated Array after assignment from Replicated Array + 4");
writeln(A);
writeln();

//
// (c) ...or, generally, the replicated array or domain participates
//     in a zippered forall loop, but not in the first position.
//     The loop could look like:
//
//       forall (a, (i,j), ra) in (A, ReplicatedSpace, RA) do ...;
//

//
// The DimensionalDist2D distribution lets us build a 2D distribution
// as a composition of specifiers for individual dimensions.
// Under such a "dimensional" distribution each dimension is handled
// independently of the other.
//
// The dimension specifiers are similar to the corresponding multi-dimensional
// distributions in constructor arguments and index-to-locale mapping rules.
// However, instead of an array of locales, a specifier constructor
// accepts just the number of locales that the indices in the corresponding
// dimension will be distributed across.
//
// The DimensionalDist2D constructor requires:
// * an [0..nl1-1, 0..nl2-1] array of locales, where
//   nl1 and nl2 are the number of locales in each dimension, and
// * two dimension specifiers, created for nl1 and nl2 locale counts, resp.
//
// Presently, the following dimension specifiers are available
// (shown here with their constructor arguments):
//
// * ReplicatedDim(numLocales)
// * BlockDim(numLocales, boundingBoxLow, boundingBoxHigh)
// * BlockCyclicDim(lowIdx, blockSize, numLocales)
//

//
// The following example creates a dimensional distribution that
// replicates over 2 locales (when available) in the first dimemsion
// and distributes using block-cyclic distribution in the second dimension.
// The example computes nl1 and nl2 and reshapes MyLocales correspondingly.
//

var (nl1, nl2) = if numLocales == 1 then (1, 1) else (2, numLocales/2);
MyLocaleView = {0..#nl1, 0..#nl2};
MyLocales = reshape(Locales[0..#nl1*nl2], MyLocaleView);

const DimReplicatedBlockcyclicSpace = Space
  dmapped DimensionalDist2D(MyLocales,
                            new ReplicatedDim(numLocales = nl1),
                            new BlockCyclicDim(numLocales = nl2,
                                               lowIdx = 1, blockSize = 2));

var DRBA: [DimReplicatedBlockcyclicSpace] int;

// The ReplicatedDim specifier always accesses the local replicand.
// (This differs from how the ReplicatedDist distribution works.)
//
// This example visits each replicand. The behavior is the same
// regardless of the second index into MyLocales below.

for locId1 in 0..#nl1 do on MyLocales[locId1, 0] {

  forall drba in DRBA do
    drba = here.id;

  writeln("Dimensional2D(Replicated,BlockCyclic) Array Index Map",
          " from ", here);

  // Technicality: 'writeln(DRBA)' would read DRBA always on Locale 0.
  // Since we want to see what DRBA contains on the current locale,
  // we use 'Helper' that is mapped using the default distribution.
  // 'Helper = DRBA' captures the view of DRBA on the current locale,
  // which we then print out.

  const Helper: [Space] int = DRBA;
  writeln(Helper);
  writeln();

}


Implement speedier sieve of eratosthenes.

More than 2x improvement when finding 1M prime number (11+s down to ~5.7s).

/*
 * Math helper functions.
 */

use Containers;

// requires CHPL_GMP=gmp or system
use GMP;

// factorial(n) / (factorial(k) * factorial(n - k));
proc choose(n: int, k: int): BigInt {
  var result = factorial(n);
  result.div_q(Round.ZERO, result, factorial(k));
  result.div_q(Round.ZERO, result, factorial(n - k));
  return result;
}

// Yields proper divisors of n. Proper divisors of n are numbers less than n
// which divide evenly into n.
iter divisors(n) {
  var current = 1;

  while current <= n / 2 {
    yield current;

    do {
      current += 1;
    } while n % current != 0;
  }
}

// n!
proc factorial(n: int): BigInt {
  if n < 0 {
    halt("Error! non-positive factorial!?");
  } else if n == 0 {
    return new BigInt(1);
  } else {
    var result = factorial(n - 1);
    result.mul_ui(result, n: c_ulong);
    return result;
  }
}

// Print the Fibonacci number less than and equal to 4 million.
iter fibonacci(): BigInt {
  var current = new BigInt(1),
    next = new BigInt(1),
    tmp: BigInt;
  while true {
    yield current;

    current.add(current, next);
    current <=> next;
  }
}

// This is a fairly optimized version of calculating if a number is prime or
// not.
// See: http://stackoverflow.com/a/15285588
proc isPrime(number) {
  if number == 2 || number == 3 {
    return true;
  } else if number < 2 || number % 2 == 0 {
    return false;
  } else if number < 9 {
    return true;
  } else if number % 3 == 0 {
    return false;
  }

  var r = (number ** 0.5): uint,
    f: uint = 5;
  while (f <= r) {
    if number % f == 0 {
      return false;
    } else if number % (f + 2) == 0 {
      return false;
    }
    f += 6;
  }
  return true;
}

// Sieve of Eratosthenes
// Generates first k prime numbers.
//
// Inspired by: http://stackoverflow.com/a/568618
// And:         http://code.activestate.com/recipes/117119/#c4
iter primes(k: uint, param debug=false): uint {
  if k >= 1 then
    yield 2;

  var i: uint = 1,
    q: uint = 3;
  var D: domain(uint),
    A: [D] uint;

  while true {
    if ! D.member(q) {
      if debug then
        assert(isPrime(q));

      yield q;

      i += 1;
      if i == k then
        break;

      A[q * q] = 2 * q;
    } else {
      const p = A[q];
      var pq = p + q;
      while D.member(pq) do
        pq += p;
      A[pq] = p;
      D -= q;
    }
    q += 2;
  }
}

/* // Yield the first `k` prime numbers as BigInts. */
/* iter bigPrimes(k: int(64)): BigInt { */
/*   var i = 0, */
/*     q = new BigInt(2); */
/*   var D: domain(BigInt); */
/*   var A: [D] domain(BigInt); */

/*   while true { */
/*     if ! D.member(q) { */
/*       yield q; */
/*       i += 1; */
/*       var qSquared = new BigInt(); */
/*       qSquared.mul(q, q); */
/*       D += qSquared; */
/*       A[qSquared] += q; */
/*     } else { */
/*       for p in A[q] { */
/*         var pq = new BigInt(); */
/*         pq.add(p, q); */
/*         D += pq; */
/*         A[pq] = p; */
/*       } */
/*       D.remove(q); */
/*     } */
/*     if i == k { */
/*       break; */
/*     } */
/*     q += 1; */
/*   } */
/* } */

// Returns sum of individual digits of n. n is serialized as string, then each
// character is cast to an int, and added together.
proc sumDigits(n) {
  var nStr = n: string,
    result = 0;
  for i in 1..nStr.length {
    result += nStr.substring(i): int;
  }
  return result;
}

// Returns sum of all divisors of n.
proc sumDivisors(n) {
  var sum = 0;
  for i in divisors(n) {
    sum += i;
  }
  return sum;
}

/*
 * Math helper functions.
 */

use Containers;

// requires CHPL_GMP=gmp or system
use GMP;

// factorial(n) / (factorial(k) * factorial(n - k));
proc choose(n: int, k: int): BigInt {
  var result = factorial(n);
  result.div_q(Round.ZERO, result, factorial(k));
  result.div_q(Round.ZERO, result, factorial(n - k));
  return result;
}

// Yields proper divisors of n. Proper divisors of n are numbers less than n
// which divide evenly into n.
iter divisors(n) {
  var current = 1;

  while current <= n / 2 {
    yield current;

    do {
      current += 1;
    } while n % current != 0;
  }
}

// n!
proc factorial(n: int): BigInt {
  if n < 0 {
    halt("Error! non-positive factorial!?");
  } else if n == 0 {
    return new BigInt(1);
  } else {
    var result = factorial(n - 1);
    result.mul_ui(result, n: c_ulong);
    return result;
  }
}

// Print the Fibonacci number less than and equal to 4 million.
iter fibonacci(): BigInt {
  var current = new BigInt(1),
    next = new BigInt(1),
    tmp: BigInt;
  while true {
    yield current;

    current.add(current, next);
    current <=> next;
  }
}

// This is a fairly optimized version of calculating if a number is prime or
// not.
// See: http://stackoverflow.com/a/15285588
proc isPrime(number) {
  if number == 2 || number == 3 {
    return true;
  } else if number < 2 || number % 2 == 0 {
    return false;
  } else if number < 9 {
    return true;
  } else if number % 3 == 0 {
    return false;
  }

  var r = (number ** 0.5): uint,
    f: uint = 5;
  while (f <= r) {
    if number % f == 0 {
      return false;
    } else if number % (f + 2) == 0 {
      return false;
    }
    f += 6;
  }
  return true;
}

// Sieve of Eratosthenes
// Generates first k prime numbers.
//
// Inspired by: http://stackoverflow.com/a/568618
iter primes(k: uint, param debug=false): uint {
  var i: uint = 0,
    q: uint = 2;
  var D: domain(uint),
    A: [D] Vector(uint);

  while true {
    if ! D.member(q) {
      if debug then
        assert(isPrime(q));

      yield q;

      i += 1;
      if i == k then
        break;

      const qq = q * q;
      if ! D.member(qq) then
        A[qq] = new Vector(uint);

      A[qq].push(q);
    } else {
      for p in A[q] {

        const pq = p + q;
        if ! D.member(pq) then
          A[pq] = new Vector(uint);

        A[pq].push(p);
      }
      while ! A[q].empty do
        A[q].pop();
      delete A[q];
      D -= q;
    }
    q += 1;
  }

  // Clear out the vectors.
  for k in D {
    while ! A[k].empty do
      A[k].pop();
    delete A[k];
  }
}

/* // Yield the first `k` prime numbers as BigInts. */
/* iter bigPrimes(k: int(64)): BigInt { */
/*   var i = 0, */
/*     q = new BigInt(2); */
/*   var D: domain(BigInt); */
/*   var A: [D] domain(BigInt); */

/*   while true { */
/*     if ! D.member(q) { */
/*       yield q; */
/*       i += 1; */
/*       var qSquared = new BigInt(); */
/*       qSquared.mul(q, q); */
/*       D += qSquared; */
/*       A[qSquared] += q; */
/*     } else { */
/*       for p in A[q] { */
/*         var pq = new BigInt(); */
/*         pq.add(p, q); */
/*         D += pq; */
/*         A[pq] = p; */
/*       } */
/*       D.remove(q); */
/*     } */
/*     if i == k { */
/*       break; */
/*     } */
/*     q += 1; */
/*   } */
/* } */

// Returns sum of individual digits of n. n is serialized as string, then each
// character is cast to an int, and added together.
proc sumDigits(n) {
  var nStr = n: string,
    result = 0;
  for i in 1..nStr.length {
    result += nStr.substring(i): int;
  }
  return result;
}

// Returns sum of all divisors of n.
proc sumDivisors(n) {
  var sum = 0;
  for i in divisors(n) {
    sum += i;
  }
  return sum;
}


Changed config var declarations to be comma-separated list in
prod-cons example.

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

// Producer-consumer example. Producer thread slowly writes to sync variable
// s.  Child thread reads from s and outputs what was read.

use Time;

config var numIterations: int = 5,
           sleepTime: uint = 2;

var s: sync int;

begin {  // fork consumer computation
  for c in 1..numIterations do
    writeln("consumer got ", s);
}

// producer computation
for p in 1..numIterations {
  sleep(sleepTime);
  s = p;
}

// Producer-consumer example. Producer thread slowly writes to sync variable
// s.  Child thread reads from s and outputs what was read.

use Time;

config var numIterations: int = 5;
config var sleepTime: uint = 2;

var s: sync int;

begin {  // fork consumer computation
  for c in 1..numIterations do
    writeln("consumer got ", s);
}

// producer computation
for p in 1..numIterations {
  sleep(sleepTime);
  s = p;
}


Changed all unsigned literals (with the u suffix) to integer
literals when assigning to uints.

Now, the ran array is nonzero after HPCC_starts.

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

/*  This is the Chapel version of Random Access HPC benchmark.
 *
 *  For now, memory size is a compile time parameter.  That will
 *  be changed to a runtime parameter later.
 *
 *  Timing is not implemented yet.  So, the GUPs number is not
 *  computed.
 *
 *  This program performs the updates to Table and checks if they
 *  are correct.
 *
 *  This is a pretty straightforward translation from C.  More changes
 *  will be made to make it a true Chapel program.
 *
 *  last revised 9/18/2008 by marybeth
 */
param POLY:uint = 0x0000000000000007;
param PERIOD:int = 1317624576693539401;
param MEMSIZE:int = 1000;

var NUPDATE:int;

def HPCC_starts(n:int):uint {
  var i:int;
  var j:int;
  var n2:int = n;
  var temp:uint;
  var ran:uint;
  var D: domain(1) = [0..63];
  var m2: [D] uint;
  var result: uint;

  while (n2 < 0) do {
    n2 += PERIOD;
  }
  while (n2 > PERIOD) do {
    n2 -= PERIOD;
  }
  if (n2 == 0) {
    result = 0x1;
    return result;
  }

  temp = 0x1;
  for i in D {
    m2(i) = temp;
    if (temp:int) < 0 then
      temp = ((temp << 1) ^ POLY);
    else
      temp = ((temp << 1) ^ 0);
    if (temp:int) < 0 then
      temp = ((temp << 1) ^ POLY);
    else
      temp = ((temp << 1) ^ 0);
  }

  i = 62;
  while !((n2 >> i) & 1) do i -= 1;

  ran = 0x2;
  while (i > 0) do {
    temp = 0;
    for j in D do {
      if ((ran >> j:uint) & 1) then temp ^= m2(j);
    }
    ran = temp;
    i -= 1;
    if ((n2 >> i) & 1) then {
      if (ran:int) < 0 then
        ran = ((ran << 1) ^ POLY);
      else
        ran = ((ran << 1) ^ 0);
    }
  }
  return ran;
}

def RandomAccessUpdate(TableSize:uint, Table: [] uint) {
  var i:int;
  var D: domain(1) =  [0..127];
  var ran: [D] uint;
  var j:int;

  for i in Table.domain {
    Table(i) = i:uint;
  }
  for j in D {
    ran(j) = HPCC_starts (((NUPDATE/128) * j):int);
  }

  i = 0;
  do {
    for j in D {
      if (ran(j):int) < 0 then
        ran(j) = ((ran(j) << 1) ^ POLY);
      else
        ran(j) = ((ran(j) << 1) ^ 0);
      Table((ran(j) & (TableSize-1)):int) ^= ran(j);
    }
    i += 1;
  } while (i < NUPDATE/128);
}

def main() {

  var i:int;
  var temp:uint;
  var cputime:float;               /* CPU time to update table */
  var realtime:float;              /* Real time to update table */
  var GUPs: float;
  var failure:int;
  var totalMem:float = (MEMSIZE:float)/8.0;
  var TableSize:uint;
  var logTableSize:uint;
  const TableDomain: int;

  /* calculate local memory per node for the update table */
  totalMem = (MEMSIZE:float)/(8.0);

  /* calculate the size of update array (must be a power of 2) */
  totalMem *= 0.5;
  logTableSize = 0;
  TableSize = 1;
  while (totalMem >= 1.0) {
   totalMem *= 0.5;
   logTableSize += 1;
   TableSize = TableSize << 1;
  }

  TableDomain = (TableSize:int);
  var D:domain(1) = [0..TableDomain-1];
  var Table: [D] uint;

  NUPDATE = 4*(TableSize:int);

  writeln("Main table size = 2^",logTableSize," = ",TableSize," words");
  writeln("Number of updates = ",NUPDATE);

  RandomAccessUpdate( TableSize, Table );

  /* Verification of results (in serial or "safe" mode; optional) */
  i  = 0;
  temp = 0x1;
  do {
    if (temp:int) < 0 then
      temp = ((temp << 1) ^ POLY);
    else
      temp = ((temp << 1) ^ 0);
    Table((temp & (TableSize-1)):int) ^= temp;
    i += 1;
  } while (i < NUPDATE);

  temp = 0;
  for i in D {
    if (Table[i] != i) then temp += 1;
  }

  writeln("Found ", temp, " errors in ", TableSize, " locations");
  if (temp <= 0.01*TableSize) then
    writeln("(passed)");
  else
    writeln("(failed)");
}

/*  This is the Chapel version of Random Access HPC benchmark.
 *
 *  For now, memory size is a compile time parameter.  That will
 *  be changed to a runtime parameter later.
 *
 *  Timing is not implemented yet.  So, the GUPs number is not
 *  computed.
 *
 *  This program performs the updates to Table and checks if they
 *  are correct.
 *
 *  This is a pretty straightforward translation from C.  More changes
 *  will be made to make it a true Chapel program.
 *
 *  last revised 9/18/2008 by marybeth
 */
param POLY:uint = 0x0000000000000007u;
param PERIOD:int = 1317624576693539401;
param MEMSIZE:int = 1000;

var NUPDATE:int;

def HPCC_starts(n:int):uint {
  var i:int;
  var j:int;
  var n2:int = n;
  var temp:uint;
  var ran:uint;
  var D: domain(1) = [0..63];
  var m2: [D] uint;

  while (n2 < 0) do {
    n2 += PERIOD;
  }
  while (n2 > PERIOD) do {
    n2 -= PERIOD;
  }
  if (n2 == 0) then return 0x1u;

  temp = 1u;
  for i in D {
    m2(i) = temp;
    if (temp:int) < 0 then
      temp = ((temp << 1) ^ POLY);
    else
      temp = ((temp << 1) ^ 0);
    if (temp:int) < 0 then
      temp = ((temp << 1) ^ POLY);
    else
      temp = ((temp << 1) ^ 0);
  }

  i = 62;
  while !((n2 >> i) & 1) do i -= 1;

  ran = 0x2u;
  while (i > 0) do {
    temp = 0;
    for j in D do {
      if ((ran >> j:uint) & 1) then temp ^= m2(j);
    }
    ran = temp;
    i -= 1;
    if ((n2 >> i) & 1) then {
      if (ran:int) < 0 then
        ran = ((ran << 1) ^ POLY);
      else
        ran = ((ran << 1) ^ 0);
    }
  }
  return ran;
}

def RandomAccessUpdate(TableSize:uint, Table: [] uint) {
  var i:int;
  var D: domain(1) =  [0..127];
  var ran: [D] uint;
  var j:int;

  for i in Table.domain {
    Table(i) = i:uint;
  }
  for j in D {
    ran(j) = HPCC_starts (((NUPDATE/128) * j):int);
  }

  i = 0;
  do {
    for j in D {
      if (ran(j):int) < 0 then
        ran(j) = ((ran(j) << 1) ^ POLY);
      else
        ran(j) = ((ran(j) << 1) ^ 0);
      Table((ran(j) & (TableSize-1)):int) ^= ran(j);
    }
    i += 1;
  } while (i < NUPDATE/128);
}

def main() {

  var i:int;
  var temp:uint;
  var cputime:float;               /* CPU time to update table */
  var realtime:float;              /* Real time to update table */
  var GUPs: float;
  var failure:int;
  var totalMem:float = (MEMSIZE:float)/8.0;
  var TableSize:uint;
  var logTableSize:uint;
  const TableDomain: int;

  /* calculate local memory per node for the update table */
  totalMem = (MEMSIZE:float)/(8.0);

  /* calculate the size of update array (must be a power of 2) */
  totalMem *= 0.5;
  logTableSize = 0;
  TableSize = 1;
  while (totalMem >= 1.0) {
   totalMem *= 0.5;
   logTableSize += 1;
   TableSize = TableSize << 1;
  }

  TableDomain = (TableSize:int);
  var D:domain(1) = [0..TableDomain-1];
  var Table: [D] uint;

  NUPDATE = 4*(TableSize:int);

  writeln("Main table size = 2^",logTableSize," = ",TableSize," words");
  writeln("Number of updates = ",NUPDATE);

  RandomAccessUpdate( TableSize, Table );

  /* Verification of results (in serial or "safe" mode; optional) */
  i  = 0;
  temp = 0x1u;
  do {
    if (temp:int) < 0 then
      temp = ((temp << 1) ^ POLY);
    else
      temp = ((temp << 1) ^ 0);
    Table((temp & (TableSize-1)):int) ^= temp;
    i += 1;
  } while (i < NUPDATE);

  temp = 0;
  for i in D {
    if (Table[i] != i) then temp += 1;
  }

  writeln("Found ", temp, " errors in ", TableSize, " locations");
  if (temp <= 0.01*TableSize) then
    writeln("(passed)");
  else
    writeln("(failed)");
}


Added _cast(type t, x:c_string) where t==c_string.

_cast(c_string, x) is sometimes invoked when x: c_string,
e.g. from _command_line_cast().

Prior to this change, such a cast resulted in converting the actual
to a string then converting that back to c_string.
This change makes the actual go through directly, saving a few lines
in the generated code.

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

// Chapel Strings
module String {
  use CString;

  pragma "default string value" extern var defaultStringValue: string = noinit;

  // The following method is called by the compiler to determine the default
  // value of a given type.  For strings, this should return the value
  // defined above.
  // strings will be records, so param is not possible for them
  inline proc _defaultOf(type t) where t: string return defaultStringValue;

  // String concatenation
  inline proc +(s: string, x: string) {
    var cs = __primitive("string_concat", s.c_str(), x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: c_string, x: string) {
    var cs = __primitive("string_concat", s, x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: c_string) {
    var cs = __primitive("string_concat", s.c_str(), x);
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: numeric)
    return s + x:string;

  inline proc +(x: numeric, s: string)
    return x:string + s;

  inline proc +(s: string, x: enumerated)
    return s + x:string;

  inline proc +(x: enumerated, s: string)
    return x:string + s;

  inline proc +(s: string, x: bool)
    return s + x:string;

  inline proc +(x: bool, s: string)
    return x:string + s;

  proc typeToString(type t) param {
    return __primitive("typeToString", t);
  }

  proc typeToString(x) param {
    compilerError("typeToString()'s argument must be a type, not a value");
  }

  // This implies that the *representation* of strings is shared.
  // If strings are reimplemented as classes or records, a less trivial
  // implementation for assignment will become necessary.
  inline proc =(ref a: string, b: string) { __primitive("move", a, b); }

  inline proc ==(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) == 0);
  inline proc !=(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) != 0);

  inline proc <=(a: string, b: string) return a.c_str()<=b.c_str();
  inline proc >=(a: string, b: string) return a.c_str()>=b.c_str();
  inline proc <(a: string, b: string) return a.c_str()<b.c_str();
  inline proc >(a: string, b: string) return a.c_str()>b.c_str();

  //
  // primitive string functions and methods
  //
  inline proc ascii(a: string) return ascii(a.c_str());
  inline proc string.length return this.c_str().length;
  inline proc string.size return this.length;
  inline proc string.substring(i: int) {
    var cs = this.c_str().substring(i);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }
  inline proc string.substring(r: range(?)) {
    var cs = this.c_str().substring(r);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }

  inline proc _string_contains(a: string, b: string)
    return _string_contains(a.c_str(), b.c_str());

  /* args: any number of strings
     return: Returns true if this starts with one of the strings specified in args
   */
  inline proc string.startsWith(args ...?k):bool {
    for param i in 1..k {
      if (this.substring(0..args(i).length) == args(i))
        then return true;
    }
    return false;
  }

  /* Returns the index of the first occurrence of a substring within a string,
      or 0 if the substring is not in the string.
   */
  inline proc string.indexOf(substring:string):int
    return this.c_str().indexOf(substring.c_str());
  inline proc string.indexOf(substring:c_string):int
    return this.c_str().indexOf(substring);

  // cast to and from Chapel strings use c_string
  pragma "compiler generated"
  inline proc _cast(type t, x) where t == string {
    var cs = _cast(c_string_copy, x);
    // Note, this uses a non-allocating toString(), and steals cs (no need to free).
    const ret = toString(cs);
    return ret;
  }

  // The "compiler generated" flag is used, because this cast has to be weaker
  // than the c_string-to-enum cast generated in buildDefaultFunctions().
  // The circularity is caused by the built-in c_string-to-string coercion.
  // TODO: If string literals were all actually chpl_strings, then I think the
  // coercion (and this cruft) could be removed.
  pragma "compiler generated"
  inline proc _cast(type t, x: string) where isEnumType(t)
    return _cast(t, x.c_str());

  inline proc _cast(type t, x: string) where isBoolType(t) || isNumericType(t)
    return _cast(t, x.c_str());

  //
  // casts from c_string to bool types
  //
  inline proc _cast(type t, x:c_string) where isBoolType(t)
  {
    pragma "insert line file info"
    extern proc c_string_to_chpl_bool(x:c_string) : bool;
    return c_string_to_chpl_bool(x) : t;
  }

  //
  // casts from c_string to integer types
  //
  pragma "insert line file info"
  extern proc c_string_to_int8_t  (x:c_string) : int(8);
  pragma "insert line file info"
  extern proc c_string_to_int16_t (x:c_string) : int(16);
  pragma "insert line file info"
  extern proc c_string_to_int32_t (x:c_string) : int(32);
  pragma "insert line file info"
  extern proc c_string_to_int64_t (x:c_string) : int(64);
  pragma "insert line file info"
  extern proc c_string_to_uint8_t (x:c_string) : uint(8);
  pragma "insert line file info"
  extern proc c_string_to_uint16_t(x:c_string) : uint(16);
  pragma "insert line file info"
  extern proc c_string_to_uint32_t(x:c_string) : uint(32);
  pragma "insert line file info"
  extern proc c_string_to_uint64_t(x:c_string) : uint(64);

  inline proc _cast(type t, x:c_string) where t == int(8)
    return c_string_to_int8_t(x);
  inline proc _cast(type t, x:c_string) where t == int(16)
    return c_string_to_int16_t(x);
  inline proc _cast(type t, x:c_string) where t == int(32)
    return c_string_to_int32_t(x);
  inline proc _cast(type t, x:c_string) where t == int(64)
    return c_string_to_int64_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(8)
    return c_string_to_uint8_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(16)
    return c_string_to_uint16_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(32)
    return c_string_to_uint32_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(64)
    return c_string_to_uint64_t(x);

  //
  // casts from c_string to real/imag types
  //
  pragma "insert line file info"
  extern proc c_string_to_real32(x:c_string) : real(32);
  pragma "insert line file info"
  extern proc c_string_to_real64(x:c_string) : real(64);
  pragma "insert line file info"
  extern proc c_string_to_imag32(x:c_string) : imag(32);
  pragma "insert line file info"
  extern proc c_string_to_imag64(x:c_string) : imag(64);

  inline proc _cast(type t, x:c_string) where t == real(32)
    return c_string_to_real32(x);
  inline proc _cast(type t, x:c_string) where t == real(64)
    return c_string_to_real64(x);
  inline proc _cast(type t, x:c_string) where t == imag(32)
    return c_string_to_imag32(x);
  inline proc _cast(type t, x:c_string) where t == imag(64)
    return c_string_to_imag64(x);

  //
  // casts from c_string to complex types
  //
  pragma "insert line file info"
  extern proc c_string_to_complex64(x:c_string) : complex(64);
  pragma "insert line file info"
  extern proc c_string_to_complex128(x:c_string) : complex(128);

  inline proc _cast(type t, x:c_string) where t == complex(64)
    return c_string_to_complex64(x);
  inline proc _cast(type t, x:c_string) where t == complex(128)
    return c_string_to_complex128(x);

  //
  // casts from complex
  //
  inline proc _cast(type t, x: complex(?w)) where t == c_string_copy {
    if isnan(x.re) || isnan(x.im) then
      return __primitive("string_copy", "nan");
    var re = (x.re):c_string_copy;
    var im: c_string_copy;
    var op: c_string;
    if x.im < 0 {
      im = (-x.im):c_string_copy;
      op = " - ";
    } else if im == "-0.0" {
      im = "0.0":c_string_copy;
      op = " - ";
    } else {
      im = (x.im):c_string_copy;
      op = " + ";
    }
    // TODO: Add versions of the concatenation operator that consume their
    // c_string_copy arg or args.
    var ts0 = re + op;
    chpl_free_c_string_copy(re);
    var ts1 = ts0 + im;
    chpl_free_c_string_copy(ts0);
    chpl_free_c_string_copy(im);
    const ret = ts1 + "i";
    chpl_free_c_string_copy(ts1);
    return ret;
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:complex(?w)) where t == c_string
    return _cast(c_string_copy, x);

  pragma "init copy fn"
  inline proc chpl__initCopy(a)
    where a.type == c_string || a.type == c_string_copy {
    // Currently, string representations are shared.
    // (See note on proc =(a:string, b:string) above.)
      return a;
  }

  proc chpldev_refToString(ref arg) {

    //
    // print out the address of class references as well
    //
    proc chpldev_classToString(x: object)
      return " (class = " + __primitive("ref to string", x) + ")";
    proc chpldev_classToString(x) return "";

    return __primitive("ref to string", arg) + chpldev_classToString(arg);
  }

}

// C strings
// extern type c_string; is a built-in primitive type
//
// In terms of how they are used, c_strings are a "close to the metal"
// representation, being in essence the common NUL-terminated C string.
//
// C string copies
// extern type c_string_copy is also a built-in primitive type.
// It is the same as a c_string, but in its case represents "owned" data.
// Low-level routines that allocate string data off the heap and return a deep
// copy (including string_copy, string_concat, string_index and string_select)
// have the return type of c_string_copy to denote that ownership.
//
// The difference is ignored by the C compiler, but Chapel treats them as
// different types.  This difference allows us to have two versions of
// toString: one makes a copy of its c_string argument, the other simply
// pointer-copies its c_string_copy argument.  Both effectively return an
// "owned" C string, which is how the internal chpl_string type is currently
// interpreted.  (The new record-based string implementation has different
// rules, but still makes use of the distinction between unowned c_strings and
// owned c_string_copies.
module CString {

  // The following method is called by the compiler to determine the default
  // value of a given type.
  inline proc _defaultOf(type t) param where t: c_string return "":c_string;
  inline proc _defaultOf(type t) where t == c_string_copy return _nullString;

  inline proc toString(cstr:c_string):string {
    return __primitive("string_from_c_string", cstr, 0, 0);
  }
  inline proc toString(cstr:c_string, len:int):string {
    return __primitive("string_from_c_string", cstr, 1, len);
  }

  // These routines consume the c_string_copy argument.  In terms of MM, this
  // is the same as if the caller had called chpl_free_string_copy()
  extern proc string_from_c_string_copy(ref cstrc:c_string_copy,
                                        hasLen:bool, len:int) : string;
  inline proc toString(ref cstrc:c_string_copy) : string {
    return string_from_c_string_copy(cstrc, false, 0);
  }
  inline proc toString(ref cstrc:c_string_copy, len:int) : string {
    return string_from_c_string_copy(cstrc, true, len);
  }

  // WARNING: The bytes pointed to by the c_string return value are still owned
  // by the "this" operand.  The returned c_string should not be freed!
  inline proc string.c_str():c_string {
    return __primitive("c_string_from_string", this);
  }

  inline proc ==(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) == 0;
  }

  inline proc ==(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) == 0;
  }

  inline proc !=(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) != 0;
  }

  inline proc !=(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) != 0;
  }

  inline proc <=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc <=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc >=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc >=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc <(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc <(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc >(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc >(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc =(ref a: c_string, b: c_string) {
    __primitive("=", a, b);
  }

  inline proc =(ref a: c_string, b: string) {
    __primitive("=", a, b.c_str());
  }

  inline proc =(ref a: string, b: c_string) {
    __primitive("=", a, toString(b));
  }

  // Create a fresh copy of the RHS string, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string) {
    chpl_free_c_string_copy(a);
    var c = __primitive("string_copy", b);
    __primitive("=", a, c);
  }
  // Assume ownership of data brought by the RHS, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string_copy) {
    chpl_free_c_string_copy(a);
    __primitive("=", a, b);
  }

  // yes this is invoked sometimes
  inline proc _cast(type t, x: c_string) where t == c_string {
    return x;
  }

  inline proc _cast(type t, x: c_string) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, ref x: c_string_copy) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, x: string) where t == c_string {
    return x.c_str();
  }

  inline proc _cast(type t, x: string) where t == c_string_copy {
    return __primitive("string_copy", x);
  }

  // A c_string_copy can always be used as a c_string.
  inline proc _cast(type t, x: c_string_copy) where t == c_string {
    return __primitive("cast", t, x);
  }

  extern proc chpl_bool_to_c_string(x:bool) : c_string;
  inline proc _cast(type t, x: bool(?w)) where t == c_string {
    return chpl_bool_to_c_string(x:bool);
  }
  inline proc _cast(type t, x: bool(?w)) where t == c_string_copy {
    return __primitive("string_copy", chpl_bool_to_c_string(x:bool));
  }

  inline proc _cast(type t, x:enumerated) where t == c_string_copy {
    // Use the compiler-generated enum to c_string conversion.
    var cs = _cast(c_string, x);
    return __primitive("string_copy", cs);
  }

  inline proc _cast(type t, x:integral) where t == c_string_copy {
    extern proc integral_to_c_string_copy(x:int(64), size:uint(32), isSigned: bool) : c_string_copy ;
    return integral_to_c_string_copy(x:int(64), numBytes(x.type), isIntType(x.type));
  }

  extern proc real_to_c_string_copy(x:real(64), isImag: bool) : c_string_copy ;
  //
  // casts from real
  //
  inline proc _cast(type t, x:real(?w)) where t == c_string_copy {
    return real_to_c_string_copy(x:real(64), false);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:real(?w)) where t == c_string
    return _cast(c_string_copy, x);

  //
  // casts from imag
  //
  inline proc _cast(type t, x:imag(?w)) where t == c_string_copy {
    // The Chapel version of the imag --> real cast smashes it flat rather than
    // just stripping off the "i".  See ChapelBase:965.
    var r = __primitive("cast", real(64), x);
    return real_to_c_string_copy(r, true);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:imag(?w)) where t == c_string
    return _cast(c_string_copy, x);

  // Only support param c_string concatenation (for now)
  inline proc +(param a: c_string, param b: c_string) param
    return __primitive("string_concat", a, b);

  inline proc +(param s: c_string, param x: integral) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: integral, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: enumerated) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: enumerated, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: bool) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: bool, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(a: c_string, b: c_string)
    return __primitive("string_concat", a, b);

  proc c_string.writeThis(x: Writer) {
    x.write(this);
  }
  // The c_string_copy version is required, since apparently coercions are not
  // applied to "this".
  proc c_string_copy.writeThis(x: Writer) {
    x.write(this:c_string);
  }

  //
  // primitive c_string functions and methods
  //
  inline proc ascii(param a: c_string) param return __primitive("ascii", a);
  inline proc ascii(a: c_string) return __primitive("ascii", a);
  inline proc c_string.length return __primitive("string_length", this);
  inline proc c_string.size return this.length;
  inline proc c_string.substring(i: int)
    return __primitive("string_index", this, i);
  inline proc c_string.substring(r: range(?)) {
    var r2 = r[1..this.length];  // This may warn about ambiguously aligned ranges.
    var lo:int = r2.alignedLow, hi:int = r2.alignedHigh;
    return __primitive("string_select", this, lo, hi, r2.stride);
  }

  inline proc _string_contains(a: string, b: string)
    return __primitive("string_contains", a, b);

  inline proc param c_string.length param
    return __primitive("string_length", this);
  inline proc _string_contains(param a: c_string, param b: c_string) param
    return __primitive("string_contains", a, b);

  /* Returns the index of the first occurrence of a substring within a string,
     or 0 if the substring is not in the string.
  */
  inline proc c_string.indexOf(substring:c_string):int
    return string_index_of(this, substring);
  extern proc string_index_of(haystack:c_string, needle:c_string):int;

  // Use with care.  Not for the weak.
  inline proc chpl_free_c_string_copy(ref cs: c_string_copy) {
    pragma "insert line file info"
    extern proc chpl_rt_free_c_string(ref cs: c_string_copy);
    if (cs != _nullString) then chpl_rt_free_c_string(cs);
    // cs = _nullString;
  }

}

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

// Chapel Strings
module String {
  use CString;

  pragma "default string value" extern var defaultStringValue: string = noinit;

  // The following method is called by the compiler to determine the default
  // value of a given type.  For strings, this should return the value
  // defined above.
  // strings will be records, so param is not possible for them
  inline proc _defaultOf(type t) where t: string return defaultStringValue;

  // String concatenation
  inline proc +(s: string, x: string) {
    var cs = __primitive("string_concat", s.c_str(), x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: c_string, x: string) {
    var cs = __primitive("string_concat", s, x.c_str());
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: c_string) {
    var cs = __primitive("string_concat", s.c_str(), x);
    const ret = toString(cs);
    // toString steals the c_string_copy, so no need to free it here.
    return ret;
  }

  inline proc +(s: string, x: numeric)
    return s + x:string;

  inline proc +(x: numeric, s: string)
    return x:string + s;

  inline proc +(s: string, x: enumerated)
    return s + x:string;

  inline proc +(x: enumerated, s: string)
    return x:string + s;

  inline proc +(s: string, x: bool)
    return s + x:string;

  inline proc +(x: bool, s: string)
    return x:string + s;

  proc typeToString(type t) param {
    return __primitive("typeToString", t);
  }

  proc typeToString(x) param {
    compilerError("typeToString()'s argument must be a type, not a value");
  }

  // This implies that the *representation* of strings is shared.
  // If strings are reimplemented as classes or records, a less trivial
  // implementation for assignment will become necessary.
  inline proc =(ref a: string, b: string) { __primitive("move", a, b); }

  inline proc ==(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) == 0);
  inline proc !=(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) != 0);

  inline proc <=(a: string, b: string) return a.c_str()<=b.c_str();
  inline proc >=(a: string, b: string) return a.c_str()>=b.c_str();
  inline proc <(a: string, b: string) return a.c_str()<b.c_str();
  inline proc >(a: string, b: string) return a.c_str()>b.c_str();

  //
  // primitive string functions and methods
  //
  inline proc ascii(a: string) return ascii(a.c_str());
  inline proc string.length return this.c_str().length;
  inline proc string.size return this.length;
  inline proc string.substring(i: int) {
    var cs = this.c_str().substring(i);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }
  inline proc string.substring(r: range(?)) {
    var cs = this.c_str().substring(r);
    const ret = toString(cs);
    // toString steals the returned c_string_copy, so no need to free it.
    return ret;
  }

  inline proc _string_contains(a: string, b: string)
    return _string_contains(a.c_str(), b.c_str());

  /* args: any number of strings
     return: Returns true if this starts with one of the strings specified in args
   */
  inline proc string.startsWith(args ...?k):bool {
    for param i in 1..k {
      if (this.substring(0..args(i).length) == args(i))
        then return true;
    }
    return false;
  }

  /* Returns the index of the first occurrence of a substring within a string,
      or 0 if the substring is not in the string.
   */
  inline proc string.indexOf(substring:string):int
    return this.c_str().indexOf(substring.c_str());
  inline proc string.indexOf(substring:c_string):int
    return this.c_str().indexOf(substring);

  // cast to and from Chapel strings use c_string
  pragma "compiler generated"
  inline proc _cast(type t, x) where t == string {
    var cs = _cast(c_string_copy, x);
    // Note, this uses a non-allocating toString(), and steals cs (no need to free).
    const ret = toString(cs);
    return ret;
  }

  // The "compiler generated" flag is used, because this cast has to be weaker
  // than the c_string-to-enum cast generated in buildDefaultFunctions().
  // The circularity is caused by the built-in c_string-to-string coercion.
  // TODO: If string literals were all actually chpl_strings, then I think the
  // coercion (and this cruft) could be removed.
  pragma "compiler generated"
  inline proc _cast(type t, x: string) where isEnumType(t)
    return _cast(t, x.c_str());

  inline proc _cast(type t, x: string) where isBoolType(t) || isNumericType(t)
    return _cast(t, x.c_str());

  //
  // casts from c_string to bool types
  //
  inline proc _cast(type t, x:c_string) where isBoolType(t)
  {
    pragma "insert line file info"
    extern proc c_string_to_chpl_bool(x:c_string) : bool;
    return c_string_to_chpl_bool(x) : t;
  }

  //
  // casts from c_string to integer types
  //
  pragma "insert line file info"
  extern proc c_string_to_int8_t  (x:c_string) : int(8);
  pragma "insert line file info"
  extern proc c_string_to_int16_t (x:c_string) : int(16);
  pragma "insert line file info"
  extern proc c_string_to_int32_t (x:c_string) : int(32);
  pragma "insert line file info"
  extern proc c_string_to_int64_t (x:c_string) : int(64);
  pragma "insert line file info"
  extern proc c_string_to_uint8_t (x:c_string) : uint(8);
  pragma "insert line file info"
  extern proc c_string_to_uint16_t(x:c_string) : uint(16);
  pragma "insert line file info"
  extern proc c_string_to_uint32_t(x:c_string) : uint(32);
  pragma "insert line file info"
  extern proc c_string_to_uint64_t(x:c_string) : uint(64);

  inline proc _cast(type t, x:c_string) where t == int(8)
    return c_string_to_int8_t(x);
  inline proc _cast(type t, x:c_string) where t == int(16)
    return c_string_to_int16_t(x);
  inline proc _cast(type t, x:c_string) where t == int(32)
    return c_string_to_int32_t(x);
  inline proc _cast(type t, x:c_string) where t == int(64)
    return c_string_to_int64_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(8)
    return c_string_to_uint8_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(16)
    return c_string_to_uint16_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(32)
    return c_string_to_uint32_t(x);
  inline proc _cast(type t, x:c_string) where t == uint(64)
    return c_string_to_uint64_t(x);

  //
  // casts from c_string to real/imag types
  //
  pragma "insert line file info"
  extern proc c_string_to_real32(x:c_string) : real(32);
  pragma "insert line file info"
  extern proc c_string_to_real64(x:c_string) : real(64);
  pragma "insert line file info"
  extern proc c_string_to_imag32(x:c_string) : imag(32);
  pragma "insert line file info"
  extern proc c_string_to_imag64(x:c_string) : imag(64);

  inline proc _cast(type t, x:c_string) where t == real(32)
    return c_string_to_real32(x);
  inline proc _cast(type t, x:c_string) where t == real(64)
    return c_string_to_real64(x);
  inline proc _cast(type t, x:c_string) where t == imag(32)
    return c_string_to_imag32(x);
  inline proc _cast(type t, x:c_string) where t == imag(64)
    return c_string_to_imag64(x);

  //
  // casts from c_string to complex types
  //
  pragma "insert line file info"
  extern proc c_string_to_complex64(x:c_string) : complex(64);
  pragma "insert line file info"
  extern proc c_string_to_complex128(x:c_string) : complex(128);

  inline proc _cast(type t, x:c_string) where t == complex(64)
    return c_string_to_complex64(x);
  inline proc _cast(type t, x:c_string) where t == complex(128)
    return c_string_to_complex128(x);

  //
  // casts from complex
  //
  inline proc _cast(type t, x: complex(?w)) where t == c_string_copy {
    if isnan(x.re) || isnan(x.im) then
      return __primitive("string_copy", "nan");
    var re = (x.re):c_string_copy;
    var im: c_string_copy;
    var op: c_string;
    if x.im < 0 {
      im = (-x.im):c_string_copy;
      op = " - ";
    } else if im == "-0.0" {
      im = "0.0":c_string_copy;
      op = " - ";
    } else {
      im = (x.im):c_string_copy;
      op = " + ";
    }
    // TODO: Add versions of the concatenation operator that consume their
    // c_string_copy arg or args.
    var ts0 = re + op;
    chpl_free_c_string_copy(re);
    var ts1 = ts0 + im;
    chpl_free_c_string_copy(ts0);
    chpl_free_c_string_copy(im);
    const ret = ts1 + "i";
    chpl_free_c_string_copy(ts1);
    return ret;
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:complex(?w)) where t == c_string
    return _cast(c_string_copy, x);

  pragma "init copy fn"
  inline proc chpl__initCopy(a)
    where a.type == c_string || a.type == c_string_copy {
    // Currently, string representations are shared.
    // (See note on proc =(a:string, b:string) above.)
      return a;
  }

  proc chpldev_refToString(ref arg) {

    //
    // print out the address of class references as well
    //
    proc chpldev_classToString(x: object)
      return " (class = " + __primitive("ref to string", x) + ")";
    proc chpldev_classToString(x) return "";

    return __primitive("ref to string", arg) + chpldev_classToString(arg);
  }

}

// C strings
// extern type c_string; is a built-in primitive type
//
// In terms of how they are used, c_strings are a "close to the metal"
// representation, being in essence the common NUL-terminated C string.
//
// C string copies
// extern type c_string_copy is also a built-in primitive type.
// It is the same as a c_string, but in its case represents "owned" data.
// Low-level routines that allocate string data off the heap and return a deep
// copy (including string_copy, string_concat, string_index and string_select)
// have the return type of c_string_copy to denote that ownership.
//
// The difference is ignored by the C compiler, but Chapel treats them as
// different types.  This difference allows us to have two versions of
// toString: one makes a copy of its c_string argument, the other simply
// pointer-copies its c_string_copy argument.  Both effectively return an
// "owned" C string, which is how the internal chpl_string type is currently
// interpreted.  (The new record-based string implementation has different
// rules, but still makes use of the distinction between unowned c_strings and
// owned c_string_copies.
module CString {

  // The following method is called by the compiler to determine the default
  // value of a given type.
  inline proc _defaultOf(type t) param where t: c_string return "":c_string;
  inline proc _defaultOf(type t) where t == c_string_copy return _nullString;

  inline proc toString(cstr:c_string):string {
    return __primitive("string_from_c_string", cstr, 0, 0);
  }
  inline proc toString(cstr:c_string, len:int):string {
    return __primitive("string_from_c_string", cstr, 1, len);
  }

  // These routines consume the c_string_copy argument.  In terms of MM, this
  // is the same as if the caller had called chpl_free_string_copy()
  extern proc string_from_c_string_copy(ref cstrc:c_string_copy,
                                        hasLen:bool, len:int) : string;
  inline proc toString(ref cstrc:c_string_copy) : string {
    return string_from_c_string_copy(cstrc, false, 0);
  }
  inline proc toString(ref cstrc:c_string_copy, len:int) : string {
    return string_from_c_string_copy(cstrc, true, len);
  }

  // WARNING: The bytes pointed to by the c_string return value are still owned
  // by the "this" operand.  The returned c_string should not be freed!
  inline proc string.c_str():c_string {
    return __primitive("c_string_from_string", this);
  }

  inline proc ==(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) == 0;
  }

  inline proc ==(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) == 0;
  }

  inline proc !=(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) != 0;
  }

  inline proc !=(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) != 0;
  }

  inline proc <=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc <=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc >=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc >=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc <(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc <(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc >(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc >(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc =(ref a: c_string, b: c_string) {
    __primitive("=", a, b);
  }

  inline proc =(ref a: c_string, b: string) {
    __primitive("=", a, b.c_str());
  }

  inline proc =(ref a: string, b: c_string) {
    __primitive("=", a, toString(b));
  }

  // Create a fresh copy of the RHS string, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string) {
    chpl_free_c_string_copy(a);
    var c = __primitive("string_copy", b);
    __primitive("=", a, c);
  }
  // Assume ownership of data brought by the RHS, first releasing the LHS.
  inline proc =(ref a: c_string_copy, b: c_string_copy) {
    chpl_free_c_string_copy(a);
    __primitive("=", a, b);
  }

  inline proc _cast(type t, x: c_string) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, ref x: c_string_copy) where t == string {
    return toString(x);
  }

  inline proc _cast(type t, x: string) where t == c_string {
    return x.c_str();
  }

  inline proc _cast(type t, x: string) where t == c_string_copy {
    return __primitive("string_copy", x);
  }

  // A c_string_copy can always be used as a c_string.
  inline proc _cast(type t, x: c_string_copy) where t == c_string {
    return __primitive("cast", t, x);
  }

  extern proc chpl_bool_to_c_string(x:bool) : c_string;
  inline proc _cast(type t, x: bool(?w)) where t == c_string {
    return chpl_bool_to_c_string(x:bool);
  }
  inline proc _cast(type t, x: bool(?w)) where t == c_string_copy {
    return __primitive("string_copy", chpl_bool_to_c_string(x:bool));
  }

  inline proc _cast(type t, x:enumerated) where t == c_string_copy {
    // Use the compiler-generated enum to c_string conversion.
    var cs = _cast(c_string, x);
    return __primitive("string_copy", cs);
  }

  inline proc _cast(type t, x:integral) where t == c_string_copy {
    extern proc integral_to_c_string_copy(x:int(64), size:uint(32), isSigned: bool) : c_string_copy ;
    return integral_to_c_string_copy(x:int(64), numBytes(x.type), isIntType(x.type));
  }

  extern proc real_to_c_string_copy(x:real(64), isImag: bool) : c_string_copy ;
  //
  // casts from real
  //
  inline proc _cast(type t, x:real(?w)) where t == c_string_copy {
    return real_to_c_string_copy(x:real(64), false);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:real(?w)) where t == c_string
    return _cast(c_string_copy, x);

  //
  // casts from imag
  //
  inline proc _cast(type t, x:imag(?w)) where t == c_string_copy {
    // The Chapel version of the imag --> real cast smashes it flat rather than
    // just stripping off the "i".  See ChapelBase:965.
    var r = __primitive("cast", real(64), x);
    return real_to_c_string_copy(r, true);
  }
  // TODO: This is only in place to support test code in types/string/sungeun.
  // Nor user nor module code should use this cast, because it strips ownership
  // from the c_string_copy returned by the above cast without first arranging
  // for its disposal.
  inline proc _cast(type t, x:imag(?w)) where t == c_string
    return _cast(c_string_copy, x);

  // Only support param c_string concatenation (for now)
  inline proc +(param a: c_string, param b: c_string) param
    return __primitive("string_concat", a, b);

  inline proc +(param s: c_string, param x: integral) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: integral, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: enumerated) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: enumerated, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: bool) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: bool, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(a: c_string, b: c_string)
    return __primitive("string_concat", a, b);

  proc c_string.writeThis(x: Writer) {
    x.write(this);
  }
  // The c_string_copy version is required, since apparently coercions are not
  // applied to "this".
  proc c_string_copy.writeThis(x: Writer) {
    x.write(this:c_string);
  }

  //
  // primitive c_string functions and methods
  //
  inline proc ascii(param a: c_string) param return __primitive("ascii", a);
  inline proc ascii(a: c_string) return __primitive("ascii", a);
  inline proc c_string.length return __primitive("string_length", this);
  inline proc c_string.size return this.length;
  inline proc c_string.substring(i: int)
    return __primitive("string_index", this, i);
  inline proc c_string.substring(r: range(?)) {
    var r2 = r[1..this.length];  // This may warn about ambiguously aligned ranges.
    var lo:int = r2.alignedLow, hi:int = r2.alignedHigh;
    return __primitive("string_select", this, lo, hi, r2.stride);
  }

  inline proc _string_contains(a: string, b: string)
    return __primitive("string_contains", a, b);

  inline proc param c_string.length param
    return __primitive("string_length", this);
  inline proc _string_contains(param a: c_string, param b: c_string) param
    return __primitive("string_contains", a, b);

  /* Returns the index of the first occurrence of a substring within a string,
     or 0 if the substring is not in the string.
  */
  inline proc c_string.indexOf(substring:c_string):int
    return string_index_of(this, substring);
  extern proc string_index_of(haystack:c_string, needle:c_string):int;

  // Use with care.  Not for the weak.
  inline proc chpl_free_c_string_copy(ref cs: c_string_copy) {
    pragma "insert line file info"
    extern proc chpl_rt_free_c_string(ref cs: c_string_copy);
    if (cs != _nullString) then chpl_rt_free_c_string(cs);
    // cs = _nullString;
  }

}