In dfft(), fixed race-y indexing into A[] by defining k1 in terms of the k (rather than using a loop-wide counter).

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

use BitOps;
use Random;
use Time;

// problem size configs
config const logN = 6;

// pseudo-random input configs
config const deterministic = false,
             seed = 314159265;

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

// boolean configs
config const printTiming = true;
config const printError = true;

def main() {
  // compute problem size
  const N = 1 << logN;

  // twiddle domain and arrays
  const TwiddleDom = [0..#N/4];
  var Twiddles: [TwiddleDom] complex;

  computeTwiddles(Twiddles);
  Twiddles = bitReverseShuffle(Twiddles);

  // problem domain and arrays
  const ProblemDom: domain(1) = [0..#N];
  var Z, z: [ProblemDom] complex;
  var realtemp, imagtemp: [ProblemDom] real;

  // generate pseudo-random input
  if deterministic then
    fillRandom(z, seed);
  else
    fillRandom(z);

  // conjugate input, storing result to work array
  Z = conjg(z);

  // TIMED SECTION
  var startTime = getCurrentTime();

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

  var execTime = getCurrentTime() - startTime;

  verifyResults(z, Z, execTime, Twiddles);
}

def computeTwiddles(W) {
  const n = W.numElements;
  const delta = 2.0 * atan(1.0) / n;

  W(0) = 1.0;
  W(n/2) = let cosDeltaN = cos(delta * n/2)
            in (cosDeltaN, cosDeltaN):complex;
  forall i in 1..n/2-1 {
    const x = cos(delta*i);
    const y = sin(delta*i);
    W(i)     = (x, y):complex;
    W(n - i) = (y, x):complex;
  }
}

// Check what the NSA supports for bit reversal
// rename this?
def bitReverseShuffle(W: [?WD]) {
  const n = WD.numIndices;
  const reverse = log2(n);
  var V: [WD] W.eltType;  // BLC: rename this field?
  /* BLC: could we do this as a permutation instead?
  var P: [i in WD] index(WD) = i;
  bitReverse(P);
  V(P) = W;
  */
  forall i in WD {  // BLC: could this be a uint domain?
    // BLC: what does this bitReverse function do with high-order bits?
    // BLC: could this be rewritten as one line?
    var ndx = bitReverse(i:uint(64), numBits=reverse);
    V(ndx:int) = W(i); // BLC: unfortunate cast
  }
  // BLC: W = V would be preferable
  return V;
}

// reverses numBits low-order bits of val
def bitReverse(val: ?valType, numBits = 64) {
  param mask: uint(64) = 0x0102040810204080;
  const valReverse64 = bitMatMultOr(mask, bitMatMultOr(val:uint(64), mask));
  const valReverse = bitRotLeft(valReverse64, numBits);
  return valReverse: valType;
}

def dfft(A: [?AD] complex, W) {

  var l = 1;
  var lasti:int;
  var m, m2: int;

  const n = AD.dim(1).length;

  for i in 2..logN by 2 {
    m = l << 2;
    m2 = 2*m;
    if (m2 > n) then break;
    forall k in 0..#n by m2 {
      var k1 = k/m2;
      var wk2 = W[k1];
      var wk1 = W[2*k1];
      var wk3 = (wk1.re - 2 * wk2.im * wk1.im,
                 2 * wk2.im * wk1.re - wk1.im):complex;
      for j in k..#l {
        butterfly(wk1, wk2, wk3, A[j..j+3*l by l]);
      }

      wk1 = W[2*k1+1];
      wk3 = (wk1.re - 2 * wk2.re * wk1.im,
             2 * wk2.re * wk1.re - wk1.im):complex;

      for j in k+m..#l {
        butterfly(wk1, (-wk2.im, wk2.re):complex, wk3, A[j..j+3*l by l]);
      }
    }
    l *= 4;
    lasti = i;
  }

  if ((l << 2) == n) {
    forall j in 0..#l {
      butterfly(1.0, 1.0, 1.0, A[j..j+3*l by l]);
    }
  } else {
    forall j in 0..#l {
      var a = A(j);
      var b = A(j+l);
      A(j) = a + b;
      A(j+l) = a - b;
    }
  }
}

def verifyResults(z, Z, execTime, Twiddles) {
  const N = Z.numElements;

  // BLC: This line wants /(complex,real) to be implemented directly:
  Z = conjg(Z) / N;

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

  // BLC: need to check this against written spec
  var maxerr = max reduce sqrt((z.re - Z.re)**2 + (z.im - Z.im)**2);

  maxerr = maxerr / logN / epsilon;

  write(if (maxerr < threshold) then "SUCCESS" else "FAILURE");
  if (printError) then writeln(", error = ", maxerr);
  writeln();
  writeln("N      = ", N);
  if (printTiming) {
    writeln(", error = ", maxerr);
    writeln("Time   = ", execTime);
    const gflop = 5.0 * N * logN / 1000000000.0;
    writeln("GFlops = ", gflop / execTime);
  }
}

def butterfly(wk1: complex, wk2: complex, wk3: complex,
              inout A:[1..4] complex) {
  var x0 = A[1] + A[2];
  var x1 = A[1] - A[2];
  var x2 = A[3] + A[4];
  var x3 = A[3] - A[4];

  A[1] = x0 + x2;
  x0 -= x2;
  A[3] = wk2 * x0;
  x0 = (x1.re - x3.im, x1.im + x3.re):complex;
  A[2] = wk1 * x0;
  x0 = (x1.re + x3.im, x1.im - x3.re):complex;
  A[4] = wk3 * x0;
}

use BitOps;
use Random;
use Time;

// problem size configs
config const logN = 6;

// pseudo-random input configs
config const deterministic = false,
             seed = 314159265;

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

// boolean configs
config const printTiming = true;
config const printError = true;

def main() {
  // compute problem size
  const N = 1 << logN;

  // twiddle domain and arrays
  const TwiddleDom = [0..#N/4];
  var Twiddles: [TwiddleDom] complex;

  computeTwiddles(Twiddles);
  Twiddles = bitReverseShuffle(Twiddles);

  // problem domain and arrays
  const ProblemDom: domain(1) = [0..#N];
  var Z, z: [ProblemDom] complex;
  var realtemp, imagtemp: [ProblemDom] real;

  // generate pseudo-random input
  if deterministic then
    fillRandom(z, seed);
  else
    fillRandom(z);

  // conjugate input, storing result to work array
  Z = conjg(z);

  // TIMED SECTION
  var startTime = getCurrentTime();

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

  var execTime = getCurrentTime() - startTime;

  verifyResults(z, Z, execTime, Twiddles);
}

def computeTwiddles(W) {
  const n = W.numElements;
  const delta = 2.0 * atan(1.0) / n;

  W(0) = 1.0;
  W(n/2) = let cosDeltaN = cos(delta * n/2)
            in (cosDeltaN, cosDeltaN):complex;
  forall i in 1..n/2-1 {
    const x = cos(delta*i);
    const y = sin(delta*i);
    W(i)     = (x, y):complex;
    W(n - i) = (y, x):complex;
  }
}

// Check what the NSA supports for bit reversal
// rename this?
def bitReverseShuffle(W: [?WD]) {
  const n = WD.numIndices;
  const reverse = log2(n);
  var V: [WD] W.eltType;  // BLC: rename this field?
  /* BLC: could we do this as a permutation instead?
  var P: [i in WD] index(WD) = i;
  bitReverse(P);
  V(P) = W;
  */
  forall i in WD {  // BLC: could this be a uint domain?
    // BLC: what does this bitReverse function do with high-order bits?
    // BLC: could this be rewritten as one line?
    var ndx = bitReverse(i:uint(64), numBits=reverse);
    V(ndx:int) = W(i); // BLC: unfortunate cast
  }
  // BLC: W = V would be preferable
  return V;
}

// reverses numBits low-order bits of val
def bitReverse(val: ?valType, numBits = 64) {
  param mask: uint(64) = 0x0102040810204080;
  const valReverse64 = bitMatMultOr(mask, bitMatMultOr(val:uint(64), mask));
  const valReverse = bitRotLeft(valReverse64, numBits);
  return valReverse: valType;
}

def dfft(A: [?AD] complex, W) {

  var l = 1;
  var lasti:int;
  var m, m2, k1: int;

  const n = AD.dim(1).length;

  for i in 2..logN by 2 {
    m = l << 2;
    m2 = 2*m;
    if (m2 > n) then break;
    k1 = 0;
    forall k in 0..#n by m2 {
      var wk2 = W[k1];
      var wk1 = W[2*k1];
      var wk3 = (wk1.re - 2 * wk2.im * wk1.im,
                 2 * wk2.im * wk1.re - wk1.im):complex;
      for j in k..#l {
        butterfly(wk1, wk2, wk3, A[j..j+3*l by l]);
      }

      wk1 = W[2*k1+1];
      wk3 = (wk1.re - 2 * wk2.re * wk1.im,
             2 * wk2.re * wk1.re - wk1.im):complex;

      for j in k+m..#l {
        butterfly(wk1, (-wk2.im, wk2.re):complex, wk3, A[j..j+3*l by l]);
      }

      k1 += 1;
    }
    l *= 4;
    lasti = i;
  }

  if ((l << 2) == n) {
    forall j in 0..#l {
      butterfly(1.0, 1.0, 1.0, A[j..j+3*l by l]);
    }
  } else {
    forall j in 0..#l {
      var a = A(j);
      var b = A(j+l);
      A(j) = a + b;
      A(j+l) = a - b;
    }
  }
}

def verifyResults(z, Z, execTime, Twiddles) {
  const N = Z.numElements;

  // BLC: This line wants /(complex,real) to be implemented directly:
  Z = conjg(Z) / N;

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

  // BLC: need to check this against written spec
  var maxerr = max reduce sqrt((z.re - Z.re)**2 + (z.im - Z.im)**2);

  maxerr = maxerr / logN / epsilon;

  write(if (maxerr < threshold) then "SUCCESS" else "FAILURE");
  if (printError) then writeln(", error = ", maxerr);
  writeln();
  writeln("N      = ", N);
  if (printTiming) {
    writeln(", error = ", maxerr);
    writeln("Time   = ", execTime);
    const gflop = 5.0 * N * logN / 1000000000.0;
    writeln("GFlops = ", gflop / execTime);
  }
}

def butterfly(wk1: complex, wk2: complex, wk3: complex,
              inout A:[1..4] complex) {
  var x0 = A[1] + A[2];
  var x1 = A[1] - A[2];
  var x2 = A[3] + A[4];
  var x3 = A[3] - A[4];

  A[1] = x0 + x2;
  x0 -= x2;
  A[3] = wk2 * x0;
  x0 = (x1.re - x3.im, x1.im + x3.re):complex;
  A[2] = wk1 * x0;
  x0 = (x1.re + x3.im, x1.im - x3.re):complex;
  A[4] = wk3 * x0;
}


Converted format() to writef()

Converted format() to writef()

//
// This test is pulled from intern ii02 in the codes written in the
// Summer of 2011 to measure increase in productivity using Chapel
// versus C/MPI. It is a pared down version of the Locally Self-consistent
// Multiple Scattering (LSMS) application.
//

use BlockDist, Time;

config const debug = false;
config const perfTest = false;
config const reportFrequency = 1025;
config const displayLIZ = false;
config const serialForall = true;

config const span = 5;

const span_x = span,
	  span_y = span,
	  span_z = span,
	  spanTuple = (span, span, span);

config const atomSpacing = 5.000, //spacing in X, Y, and Z
			 lizRadius   = 7.110, //radius of LIZ sphere
			 nIterations = 1024;

const reductionFactor = 1000.0;

param nExtent = 13;
type  AtomMatrix = nExtent*real;

const GridDom  = {0..#span_x, 0..#span_y, 0..#span_z};
const GridDist = GridDom dmapped Block(GridDom);

const LocalesDist = {0..#numLocales} dmapped Block({0..#numLocales});

class Cache {
	var space: sparse subdomain(GridDom);
	var atoms: [space] AtomMatrix;
}

proc main() {
	var t: Timer;

	writeln("[[ LSMS ]]");
	writeln("Problem size = [", span_x, ", ", span_y, ", ", span_z, "]");

	//parameters for each atom
	var atoms: [GridDist] AtomMatrix;

	//list of atoms in LIZ for each atom
	var lizDoms: [GridDist] sparse subdomain(GridDom);
	//list of atoms accessed by local atoms, for each locale
	var caches: [LocalesDist] Cache;
	forall cache in caches {
		cache = new Cache();
	}

	//compute LIZ and caches
        serial serialForall do // TODO: enable 'forall' in parallel
	forall (a, liz) in zip(GridDist, lizDoms) {
		for ac in GridDom do if a != ac {
			local {
				var dist = atomSpacing * sqrt( + reduce ([d in 1..3] circularDistance(a[d],ac[d],spanTuple[d])**2));
				if dist <= lizRadius {
					liz += ac;
					caches[here.id].space += ac;
				}
			}
		}
	}
	if displayLIZ then writeln("LIZ counts: ", [liz in lizDoms] liz.numIndices);

	//initialize atom values
	forall (i, atom) in zip(GridDist, atoms) {
		local for param e in 1..nExtent do atom[e] = GridDist.indexOrder(i);
	}

	t.start();

	for itr in 0..#nIterations {
		if itr % reportFrequency == 0 && reportFrequency <= nIterations {
			writef("step ####\n", itr);
		}
		//load data into caches
		forall cache in caches {
			forall i in cache.space {
				cache.atoms[i] = atoms[i]; //local and remote loads
			}
		}
		//add up neighbors' contributions
		forall (a, liz) in zip(GridDist, lizDoms) {
			local {
				var total: AtomMatrix;
				for ac in liz {
					/*TODO this does a binary search to find the atom in the cache
					  try to find a way to loop through cache? or store the location */
					total += caches[here.id].atoms[ac];
				}
				for param e in 1..nExtent {
					atoms[a][e] += total[e] / reductionFactor;
				}
			}
		}
		if itr == 0 || itr == nIterations-1 {
			checkExpected(itr, atoms);
		}
	}

	t.stop();

	writeln("Success!");
	if perfTest then writeln("Chapel time = ", t.elapsed(), " s");
}

proc checkExpected(itr: int, atoms: [?AtomDom] AtomMatrix) {

	proc check(atom: AtomMatrix, expected): bool {
		const tolerance = max(expected * 1e-9, 0.1);

		var pass = true;
		for param e in 1..nExtent {
			if abs(atom[e] - expected) > tolerance then pass = false;
		}
		if debug then writef( "%11.9r ~=~ %11.9r\n",
                                      atom[1], expected);

		return pass;
	}

	if span != 5 {
		writeln("span != 5, expected value check disabled...");
		return;
	}
	if itr > 0 && nIterations != 1024 {
	writeln("nIterations != 1024, expected value check disabled...");
		return;
	}

	const first = AtomDom.low,
		  last  = AtomDom.high;

	var pass: bool;
	if itr == 0 {
		pass = check(atoms[first], 0.775)
			&& check(atoms[last],  125.5);
	} else {
		pass = check(atoms[first], 5320018409.9)
			&& check(atoms[last],  5324998555.3);
	}
	if !pass {
		writeln("Computed values do not match expected values at step ", itr);
		exit(1);
	}
}

proc circularDistance(a, b, size: int) {
	if a < b {
		return min(a+size-b, b-a);
	} else {
		return min(a-b, b-a+size);
	}
}

proc compactWriteArray(arr: [?D], fmtStr = "#") where D.rank == 3 {
	for j in D.dim[2] {
		for i in D.dim[1] {
                  for a in arr[i,j,..] do writef(fmtStr, a);
                  if i < D.dim[1].high then write("   ");
		}
		writeln();
	}
}

//
// This test is pulled from intern ii02 in the codes written in the
// Summer of 2011 to measure increase in productivity using Chapel
// versus C/MPI. It is a pared down version of the Locally Self-consistent
// Multiple Scattering (LSMS) application.
//

use BlockDist, Time;

config const debug = false;
config const perfTest = false;
config const reportFrequency = 1025;
config const displayLIZ = false;
config const serialForall = true;

config const span = 5;

const span_x = span,
	  span_y = span,
	  span_z = span,
	  spanTuple = (span, span, span);

config const atomSpacing = 5.000, //spacing in X, Y, and Z
			 lizRadius   = 7.110, //radius of LIZ sphere
			 nIterations = 1024;

const reductionFactor = 1000.0;

param nExtent = 13;
type  AtomMatrix = nExtent*real;

const GridDom  = {0..#span_x, 0..#span_y, 0..#span_z};
const GridDist = GridDom dmapped Block(GridDom);

const LocalesDist = {0..#numLocales} dmapped Block({0..#numLocales});

class Cache {
	var space: sparse subdomain(GridDom);
	var atoms: [space] AtomMatrix;
}

proc main() {
	var t: Timer;

	writeln("[[ LSMS ]]");
	writeln("Problem size = [", span_x, ", ", span_y, ", ", span_z, "]");

	//parameters for each atom
	var atoms: [GridDist] AtomMatrix;

	//list of atoms in LIZ for each atom
	var lizDoms: [GridDist] sparse subdomain(GridDom);
	//list of atoms accessed by local atoms, for each locale
	var caches: [LocalesDist] Cache;
	forall cache in caches {
		cache = new Cache();
	}

	//compute LIZ and caches
        serial serialForall do // TODO: enable 'forall' in parallel
	forall (a, liz) in zip(GridDist, lizDoms) {
		for ac in GridDom do if a != ac {
			local {
				var dist = atomSpacing * sqrt( + reduce ([d in 1..3] circularDistance(a[d],ac[d],spanTuple[d])**2));
				if dist <= lizRadius {
					liz += ac;
					caches[here.id].space += ac;
				}
			}
		}
	}
	if displayLIZ then writeln("LIZ counts: ", [liz in lizDoms] liz.numIndices);

	//initialize atom values
	forall (i, atom) in zip(GridDist, atoms) {
		local for param e in 1..nExtent do atom[e] = GridDist.indexOrder(i);
	}

	t.start();

	for itr in 0..#nIterations {
		if itr % reportFrequency == 0 && reportFrequency <= nIterations {
			writeln("step ", format("####", itr));
		}
		//load data into caches
		forall cache in caches {
			forall i in cache.space {
				cache.atoms[i] = atoms[i]; //local and remote loads
			}
		}
		//add up neighbors' contributions
		forall (a, liz) in zip(GridDist, lizDoms) {
			local {
				var total: AtomMatrix;
				for ac in liz {
					/*TODO this does a binary search to find the atom in the cache
					  try to find a way to loop through cache? or store the location */
					total += caches[here.id].atoms[ac];
				}
				for param e in 1..nExtent {
					atoms[a][e] += total[e] / reductionFactor;
				}
			}
		}
		if itr == 0 || itr == nIterations-1 {
			checkExpected(itr, atoms);
		}
	}

	t.stop();

	writeln("Success!");
	if perfTest then writeln("Chapel time = ", t.elapsed(), " s");
}

proc checkExpected(itr: int, atoms: [?AtomDom] AtomMatrix) {

	proc check(atom: AtomMatrix, expected): bool {
		const tolerance = max(expected * 1e-9, 0.1);

		var pass = true;
		for param e in 1..nExtent {
			if abs(atom[e] - expected) > tolerance then pass = false;
		}
		if debug then writeln( format("%11.9g",atom[1]), " ~=~ ",
							   format("%11.9g",expected));
		return pass;
	}

	if span != 5 {
		writeln("span != 5, expected value check disabled...");
		return;
	}
	if itr > 0 && nIterations != 1024 {
	writeln("nIterations != 1024, expected value check disabled...");
		return;
	}

	const first = AtomDom.low,
		  last  = AtomDom.high;

	var pass: bool;
	if itr == 0 {
		pass = check(atoms[first], 0.775)
			&& check(atoms[last],  125.5);
	} else {
		pass = check(atoms[first], 5320018409.9)
			&& check(atoms[last],  5324998555.3);
	}
	if !pass {
		writeln("Computed values do not match expected values at step ", itr);
		exit(1);
	}
}

proc circularDistance(a, b, size: int) {
	if a < b {
		return min(a+size-b, b-a);
	} else {
		return min(a-b, b-a+size);
	}
}

proc compactWriteArray(arr: [?D], fmtStr = "#") where D.rank == 3 {
	for j in D.dim[2] {
		for i in D.dim[1] {
			write([a in arr[i,j,..]] format(fmtStr, a));
			if i < D.dim[1].high then write("   ");
		}
		writeln();
	}
}


Make a wording change in a comment in the iterators primer.

My reviewer preferred a small wording change in a comment in this example.
That works for me.

/*
 * Iterators Primer
 *
 * This primer contains several examples of iterators:
 *   an iterator to generate the Fibonacci numbers,
 *   an iterator defined by multiple loops
 *   and a recursive iterator over a tree.
 *
 * It also contains examples of the two kinds of parallel iteration:
 *  data-parallel (forall), and
 *  task-parallel (coforall).
 */

//
// fibonacci - generates the first n Fibonacci numbers
//
// The state of the iterator is stored in the tuple (current, next).
// Each time the iterator runs, it returns the current Fibonacci number,
// and then updates the state to the next one.
iter fibonacci(n: int) {
  var (current, next) = (0, 1);
  for 1..n {
    yield current;
      // The first time this iterator is run, it proceeds this far
      // and then yields (returns) the first value of current (== 0),
      // and its state is preserved.
      // The next time it is called, execution resumes here.
      // It continues until another yield is reached.
      // If the iterator completes or encounters a return statement,
      // execution of the enclosing loop terminates immediately.
    (current, next) = (next, current + next);
  }
}

//
// This example uses zipper iteration to iterate over the unbounded range 1..
// and the fibonacci iterator with n set to ten.
// Zipper iteration means that each iterator is advanced to the next yield
// and the two values they return are returned as a tuple.
//
writeln("Fibonacci Numbers");
for (i, j) in zip(1.., fibonacci(10)) {
  write("The ", i);
  select i {
    when 1 do write("st");
    when 2 do write("nd");
    when 3 do write("rd");
    otherwise write("th");
  }
  writeln(" Fibonacci number is ", j);
}
writeln();

//
// multiloop - generate the outer (Cartesian) product of indices in two ranges
// and yield them as tuples.
//
iter multiloop(n: int) {
  for i in 1..n do
    for j in 1..n do
      yield (i, j);
}

//
// Use writeln to output the values that the iterator generates.
//
// This is an example of promotion.
// Promotion means that a procedure containing an iterator call is repeated
// for each value the iterator returns.
//
// In this case, writeln() is called with each value returned by the multiloop()
// iterator.
//
writeln("Multiloop Tuples");
writeln(multiloop(3));
writeln(); // line break

//
// define a tree class and initialize an instance to
//
//      a
//     / \
//    b   c
//       / \
//      d   e
//
class Tree {
  var data: string;
  var left, right: Tree;
}

var tree = new Tree("a", new Tree("b"), new Tree("c", new Tree("d"),
                                                 new Tree("e")));

//
// postorder - iterate over the Tree in postorder using recursion
//
// Each yield statement returns a node,
// or equivalently the subtree rooted at that node.
//
iter postorder(tree: Tree): Tree {
  if tree != nil {
    // Call the iterator recursively on the left subtree and then expand
    // the result.
    for child in postorder(tree.left) do
      yield child;

    // Call the iterator recursively on the right subtree and then expand
    // the result.
    for child in postorder(tree.right) do
      yield child;

    // Finally, yield the node itself.
    yield tree;
  }
}

//
// This visits the nodes of the tree in postorder and prints them out.
// It uses the "first" flag to avoid printing a leading space.
//
proc Tree.writeThis(x: Writer)
{
  var first = true;
  for node in postorder(tree) {
    if first then first = false;
      else x.write(" ");
    x.write(node.data);
  }
}

//
// Output the data in the tree using the postorder iterator.
//
writeln("Tree Data");
writeln(tree);
writeln();

//
// Iterators get more interesting in a parallel context.
// When invoked in a forall loop (or semantically equivalent context),
// iterators that create parallel tasks and assign work to
// those tasks are required.  Such iterators are a fairly big topic
// and are described in detail in the parIters.chpl example in
// this directory.

//
// The coforall loop uses the serial version iterator
// to spawn a separate task for each of the values it yields.
// If you use coforall, you are asserting that the manipulations
// done with each yielded value can be done in parallel
// (i.e. in no particular order).
//
// All of the spawned tasks must complete before execution continues
// at the end of the coforall statement body.
//

// This just does something else noticeable to the tree data --
// prefixing each string with "node_".
proc decorate(s:string) return "node_" + s;

//
// This decorates each node in the tree in parallel, using a coforall.
// Then it writes out the resulting tree data using a postorder traversal.
//
writeln("Task parallel iteration");
coforall node in postorder(tree) do
  node.data = decorate(node.data);
writeln(tree);
writeln();

/*
 * Iterators Primer
 *
 * This primer contains several examples of iterators:
 *   an iterator to generate the Fibonacci numbers,
 *   an iterator defined by multiple loops
 *   and a recursive iterator over a tree.
 *
 * It also contains examples of the two kinds of parallel iteration:
 *  data-parallel (forall), and
 *  task-parallel (coforall).
 */

//
// fibonacci - generates the first n Fibonacci numbers
//
// The state of the iterator is stored in the tuple (current, next).
// Each time the iterator runs, it returns the current Fibonacci number,
// and then updates the state to the next one.
iter fibonacci(n: int) {
  var (current, next) = (0, 1);
  for 1..n {
    yield current;
      // The first time this iterator is run, it proceeds this far
      // and then yields (returns) the first value of current (== 0),
      // and its state is preserved.
      // The next time it is called, execution resumes here.
      // It continues until another yield is reached.
      // If the iterator completes or encounters a return statement,
      // execution of the enclosing loop terminates immediately.
    (current, next) = (next, current + next);
  }
}

//
// This example uses zipper iteration to iterate over the unbounded range 1..
// and the fibonacci iterator with n set to ten.
// Zipper iteration means that each iterator is advanced to the next yield
// and the two values they return are returned as a tuple.
//
writeln("Fibonacci Numbers");
for (i, j) in zip(1.., fibonacci(10)) {
  write("The ", i);
  select i {
    when 1 do write("st");
    when 2 do write("nd");
    when 3 do write("rd");
    otherwise write("th");
  }
  writeln(" Fibonacci number is ", j);
}
writeln();

//
// multiloop - generate the outer (Cartesian) product of indices in two ranges
// and yield them as tuples.
//
iter multiloop(n: int) {
  for i in 1..n do
    for j in 1..n do
      yield (i, j);
}

//
// Use writeln to output the values that the iterator generates.
//
// This is an example of promotion.
// Promotion means that a procedure containing an iterator call is repeated
// for each value the iterator returns.
//
// In this case, writeln() is called with each value returned by the multiloop()
// iterator.
//
writeln("Multiloop Tuples");
writeln(multiloop(3));
writeln(); // line break

//
// define a tree class and initialize an instance to
//
//      a
//     / \
//    b   c
//       / \
//      d   e
//
class Tree {
  var data: string;
  var left, right: Tree;
}

var tree = new Tree("a", new Tree("b"), new Tree("c", new Tree("d"),
                                                 new Tree("e")));

//
// postorder - iterate over the Tree in postorder using recursion
//
// Each yield statement returns a node,
// or equivalently the subtree rooted at that node.
//
iter postorder(tree: Tree): Tree {
  if tree != nil {
    // Call the iterator recursively on the left subtree and then expand
    // the result.
    for child in postorder(tree.left) do
      yield child;

    // Call the iterator recursively on the right subtree and then expand
    // the result.
    for child in postorder(tree.right) do
      yield child;

    // Finally, yield the node itself.
    yield tree;
  }
}

//
// This visits the nodes of the tree in postorder and prints them out.
// It uses the "first" flag to avoid printing a leading space.
//
proc Tree.writeThis(x: Writer)
{
  var first = true;
  for node in postorder(tree) {
    if first then first = false;
      else x.write(" ");
    x.write(node.data);
  }
}

//
// Output the data in the tree using the postorder iterator.
//
writeln("Tree Data");
writeln(tree);
writeln();

//
// Iterators get more interesting in a parallel context.
// When invoked in a forall loop (or semantically equivalent context),
// iterators that create parallel tasks and assign work to
// those tasks are required.  These are known as leader/follower
// or standalone paralllel iterators in Chapel, and they are a fairly
// big topic of their own.  See the parIters.chpl test in
// this directory for an extended example of defining and using
// leader/follower and standalone parallel iterators.

//
// The coforall loop uses the serial version iterator
// to spawn a separate task for each of the values it yields.
// If you use coforall, you are asserting that the manipulations
// done with each yielded value can be done in parallel
// (i.e. in no particular order).
//
// All of the spawned tasks must complete before execution continues
// at the end of the coforall statement body.
//

// This just does something else noticeable to the tree data --
// prefixing each string with "node_".
proc decorate(s:string) return "node_" + s;

//
// This decorates each node in the tree in parallel, using a coforall.
// Then it writes out the resulting tree data using a postorder traversal.
//
writeln("Task parallel iteration");
coforall node in postorder(tree) do
  node.data = decorate(node.data);
writeln(tree);
writeln();


Add Lydia adlibs to GitIntro test.

Add Lydia adlibs to GitIntro test.

/** Git Intro!
 *
 * The Chapel team's intro to git version control. The core team members should
 * update their name tuple in the array below.
 */

// To play Chapel adlibs, fill out your tuple with a programming language, a
// feeling, and a verb.
//
// name
//         programming language
//                 feeling
//                         verb
const TheTeam = [
  ("Ben", "", "", ""),
  ("Brad", "xfig", "liberated", "begat"),
  ("David", "", "", ""),
  ("Greg", "", "", ""),
  ("Kyle", "C", "well", "like"),
  ("Lydia", "Java", "pleasantly intrigued", "poke"),
  ("Michael", "", "", ""),
  ("Elliot", "", "", ""),
  ("Simon", "", "", ""),
  ("Sung", "", "", ""),
  ("Thomas", "Python", "ecstatic", "<3"),
  ("Tim", "C", "free", "sorta kinda like"),
  ("TMac", "", "", ""),
  ("Tom", "", "", ""),
  ("Vass", "", "", "")
];

// Prologue
writeln("Chapel Adlibs!");
writeln("--------------");
writeln();

// Print the adlibs!
for (name, lang, feeling, verb) in TheTeam {
  writef("My name is %s. I used to use %s for all my problems.\nNow, I use Chapel and feel %s! I %s Chapel.\n",
         name, lang, feeling, verb);
  writeln();
}

/** Git Intro!
 *
 * The Chapel team's intro to git version control. The core team members should
 * update their name tuple in the array below.
 */

// To play Chapel adlibs, fill out your tuple with a programming language, a
// feeling, and a verb.
//
// name
//         programming language
//                 feeling
//                         verb
const TheTeam = [
  ("Ben", "", "", ""),
  ("Brad", "xfig", "liberated", "begat"),
  ("David", "", "", ""),
  ("Greg", "", "", ""),
  ("Kyle", "C", "well", "like"),
  ("Lydia", "", "", ""),
  ("Michael", "", "", ""),
  ("Elliot", "", "", ""),
  ("Simon", "", "", ""),
  ("Sung", "", "", ""),
  ("Thomas", "Python", "ecstatic", "<3"),
  ("Tim", "C", "free", "sorta kinda like"),
  ("TMac", "", "", ""),
  ("Tom", "", "", ""),
  ("Vass", "", "", "")
];

// Prologue
writeln("Chapel Adlibs!");
writeln("--------------");
writeln();

// Print the adlibs!
for (name, lang, feeling, verb) in TheTeam {
  writef("My name is %s. I used to use %s for all my problems.\nNow, I use Chapel and feel %s! I %s Chapel.\n",
         name, lang, feeling, verb);
  writeln();
}


Accidentally left a primer change lying around.  Added a reference to an
additional primer, altered some word choice, and changed a domain comment to
read {range} instead of [range]

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

/*
 * Arrays Primer
 *
 * This primer is a tutorial on Chapel rectangular arrays.
 *
 */

//
// Arrays in Chapel are specified using a square-bracketed expression
// that specifies the array's index set, followed by the array's
// element type.  Rectangular arrays are those whose indices are
// integers or tuples of integers, supporting standard
// multidimensional, rectilinear index sets.
//

config const n = 5;

//
// Declare an array of five 64-bit real values:
//
var A: [1..n] real;

//
// Like other variable types in Chapel, arrays are initialized so that
// each element stores its default value.  So our array of real values
// above will default to an array of the value 0.0.
//

writeln("Initially, A is: ", A);

//
// Arrays can also be declared using the the array literal syntax.
// Array literals are specified by enclosing a comma separated list of
// expressions in square brackets.  The domain of the array will be
// 1-based in each dimension, and the type of the array's element
// is the type of the first element listed.
//
var A2 = [-1.1, -2.2, -3.3, -4.4, -5.5];

writeln("Initially, A2 is: ", A2);

//
// Arrays can be accessed using scalar index values of the appropriate
// type, using either parenthesis or square brackets:
//

A[1] = 1.1;
A(2) = 2.2;

writeln("After assigning two elements, A is: ", A);

//
// Arrays can also be accessed using ranges to refer to subsets of
// array elements, or sub-arrays, using a technique called slicing:
//

A[2..4] = 3.3;

writeln("After assigning its interior values, A is: ", A);
writeln();

//
// Just like with array indexing, either square brackets or parenthesis
// can be used for array slicing:
//

writeln("A(2..4) is: ", A(2..4), "\n");

//
// Note: further information on slicing can be found in "slices.chpl"
//

//
// Arrays can be multidimensional as well:
//

var B: [1..n, 1..n] real;

forall (i,j) in {1..n, 1..n} do
  B(i,j) = i + j/10.0;

writeln("Initially, B is:\n", B, "\n");

//
// An array's elements can be iterated over in a for or forall loop,
// which causes the index variable to store references to an array's
// elements:
//

forall b in B do
  b += 1;

writeln("After incrementing B's elements, B is:\n", B, "\n");

//
// An array's index set is referred to as a domain -- a first-class
// language concept that stores the set of indices used to access the
// array.  The arrays above are declared with the anonymous domains
// {1..n} and {1..n, 1..n}.  An array's domain can be accessed using
// the .domain method:
//

forall (i,j) in B.domain do
  B(i,j) -= 1;

writeln("After decrementing B's elements, B is:\n", B, "\n");

//
// Domains can also be queried when arrays are passed to functions
// in order to refer to the domain via a new identifier within the
// context of that function:
//

printArr(B);

proc printArr(X: [?D] real) {
  writeln("within printArr, D is: ", D, "\n");
  forall (i,j) in D do
    X(i,j) = -X(i,j);
  writeln("after negating X within printArr, X is:\n", X, "\n");
}

//
// Arrays are passed to functions by reference by default, so the
// modifications to X in function printArr are reflected on B as
// well:
//

writeln("After calling printArr, B is:\n", B, "\n");

//
// Domains can also be declared and named.  This has the advantages
// of allowing them to be reused for multiple arrays, for associating
// names with different index spaces, and for amortizing the overhead
// of storing distributed arrays across multiple similar array
// variables.
//
// The following domain declaration defines a 2D arithemetic domain
// called ProbSpace which is the same size and shape as B was above.
//

var ProbSpace: domain(2) = {1..n, 1..n};

//
// We then use that domain to declare some arrays...
//

var C, D, E: [ProbSpace] bool;

// ...and to iterate over their index spaces...

for (i,j) in ProbSpace do
  C(i,j) = (i+j) % 3 == 0;

writeln("After initializing C, its value is:\n", C, "\n");

//
// When indexing over a multidimensional domain, the indices
// can be captured into a single tuple variable rather than
// destructuring the tuple into its integer components.
// Similarly, multidimensional array accesses can be expressed using
// tuple indices rather than multiple integer arguments.  In the
// following example, the index variable ij stores a 2-tuple of
// integers (2*int in Chapel).  This is a really inefficient way
// to assign the diagonal values "true" -- note the use of tuple
// indexing to tease the individual components out of the 2-tuple.
//

for ij in ProbSpace do
  D(ij) = ij(1) == ij(2);

writeln("After initializing D, its value is:\n", D, "\n");

//
// Array expressions of similar size and shape support whole-array
// assignment.
//

E = C;

writeln("After assigning C to E, E's value is:\n", E, "\n");

//
// Whole array assignment also allows scalar values to be promoted
// and assigned to every element of an array
//

B = 0.0;

writeln("After being reset, B is:\n", B, "\n");

//
// An array need not be indexed using the domain used to declare it,
// though doing so presents the compiler with opportunities to
// optimize bounds checks away.  In the following loop, there is
// no known relation between B and ProbSpace, so bounds checks are
// harder to prove away (requires symbolic analysis of the definitions
// of the two domains and the invariance of their bounds)
//

for (i,j) in ProbSpace do
  B(i,j) = i + j/10.0;

writeln("B has been re-initialized to:\n", B, "\n");

//
// Whole-array assignment can also be used for arrays or sub-arrays
// whose index spaces differ:
//

var F, G: [ProbSpace] real;

F[2..n-1, 2..n-1] = B[1..n-2, 3..n];

writeln("After assigning a slice of B to F, F's value is:\n", F, "\n");

//
// Arrays can also be sliced using unbounded ranges in which either
// the low and/or high bounds are omitted.  In this case, they will be
// inherited from the array's bounds.
//

G[2.., ..] = B[..n-1, ..];

writeln("After assigning a slice of B to G, G's value is:\n", G, "\n");

//
// Array slicing also supports rank-change semantics when sliced using
// a scalar value rather than a range.  In the following assignment,
// recall that A was our initial 1-dimensional array.
//

A = B[n/2, ..];

writeln("After being assigned a slice of B, A is:\n", A, "\n");

//
// Domains can also be sliced.  However, rather than having indexing
// semantics, domain slicing results in intersection of the domain's
// index set and the specified slice.
//
// Domain slicing, like array indexing and slicing, can be written
// with either square brackets or parenthesis.
//

writeln("ProbSpace[1..n-2, 3..] is: ", ProbSpace[1..n-2, 3..], "\n");

// Ranges also support slicing in this way, though we don't
// demonstrate that here.

//
// Domain variables and expressions can also be used to specify an
// array slice rather than using lists of ranges.  For example:
//

const ProbSpaceSlice = ProbSpace[0..n+1, 3..];

writeln("B[ProbSpaceSlice] is:\n", B[ProbSpaceSlice], "\n");

//
// Forall loops over domains and arrays can be written using the
// syntax "[<ind> in <Dom>] ..." which is shorthand for "forall ind
// in Dom do ..."
//

const offset = (1,1); // a 2-tuple offset

[ij in ProbSpace[2..n-1, 2..n-1]] F(ij) = B(ij + offset);

writeln("After assigning F a shifted slice of B, it is:\n", F, "\n");

[b in B] b = -b;

writeln("After negating B, it is:\n", B, "\n");

//
// Note that this shorthand resembles the array type definition in
// a variable declaration.  Due to this similarity, and for
// convenience, we allow the indices in an array type specifier to be
// named in order to support in-place initialization of the array.
// For example:
//

const Z: [ProbSpace] complex = [(i,j) in ProbSpace] i + j*1.0i;

writeln("Z is initialized to be:\n", Z, "\n");

//
// This is simply shorthand for:
//   const Z: [ProbSpace] complex = [i,j in ProbSpace] i + j*1.0i;
// or more generally:
//   [const|var] <ident>: [<ind> in <dom>] <elemtype> = <expr>;
// is equivalent to:
//   [const|var] <ident>: [<dom>] <elemtype> = [<ind> in <dom>] <expr>;
//

//
// Another advantage to declaring named domain variables is that their
// index sets can be reassigned.  This results in a logical
// re-allocation of the array variable in question, preserving array
// values for indices that existed in both the old and new domain
// values:
//

var VarDom = {1..n};

var VarArr: [VarDom] real = [i in VarDom] i;

writeln("Initially, VarArr = ", VarArr, "\n");

//
// Now, if we reassign VarDom, VarArr will be reallocated with the
// old values preserved and the new values initialized to the element
// type's default value.
//

VarDom = {1..2*n};

writeln("After doubling VarDom, VarArr = ", VarArr, "\n");

//
// As mentioned before, this reallocation preserves values according
// to index, so if we extend the lower bound of the domain, the
// non-zero values will still logically be associated with indices
// 1..n:
//

VarDom = {-n+1..2*n};

writeln("After lowering VarDom's lower bound, VarArr = ", VarArr, "\n");

//
// If the domain shrinks, values will be thrown away
//

VarDom = {2..n-1};

writeln("After shrinking VarDom, VarArr = ", VarArr, "\n");

//
// One trick to reallocate an array without preserving any values is
// to assign its domain a degenerate domain and then assign it the
// new value:
//

VarDom = {1..0};

writeln("VarArr should now be empty: ", VarArr, "\n");

VarDom = {1..n};

writeln("VarArr should now be reset: ", VarArr, "\n");

//
// Note that querying an array's domain via the .domain method or
// the function argument query syntax does not result in a domain
// expression that can be reassigned.  In particular, we cannot do:
//
//   VarArr.domain = {1..2*n};
//
// nor:
//
//   proc foo(X: [?D]) {  D = {1..2*n};  }
//
// Only a domain variable or a domain function argument can be
// reassigned to reallocate arrays.  This is to avoid confusion
// since assigning one domain variable can cause a number of
// arrays to be reallocated.  It also implies that arrays declared
// using an anonymous domain cannot be reallocated.  So for our
// original array declarations A and B, we have no way of reallocating
// them.  Arrays with constant domains provide the compiler with
// optimization benefits, so this supports a common case efficiently.
//
// A.domain = {1..2*n};
//

//
// As some of our examples have shown, arrays in Chapel can have
// arbitrary element types -- numeric values, classes, or records.
// Arrays can also support array elements, and initial support for
// this is implemented in our compiler.  For example:
//

var Y: [ProbSpace] [1..3] real;

forall (i,j) in ProbSpace do
  for k in 1..3 do
    Y(i,j)(k) = i*10 + j + k/10.0;

writeln("Y is:\n", Y);

//
// Our current implementation does not yet support arrays of arrays
// where the inner array size is a function of the outer -- they must
// all be of uniform size.  In particular, it is our intention to
// support things like:
//
//   const Triangle: [1..row] real = [row in 1..n] [col in 1..row] row + col/10.0;
//
// and:
//
//   var HierArr: [lvl in 1..n] [1..2**lvl, 1..2**lvl] real;
//
// If such capabilities would be of use to you, please let us know and
// we will adjust their priority accordingly.
//

/*
 * Arrays Primer
 *
 * This primer is a tutorial on Chapel rectangular arrays.
 *
 */

//
// Arrays in Chapel are specified using a square-bracketed expression
// that specifies the array's index set, followed by the array's
// element type.  Rectangular arrays are those whose indices are
// integers or tuples of integers, supporting standard
// multidimensional, rectilinear index sets.
//

config const n = 5;

//
// Declare an array of five 64-bit real values:
//
var A: [1..n] real;

//
// Like other variable types in Chapel, arrays are initialized so that
// each element stores its default value.  So our array of real values
// above will default to an array of the value 0.0.
//

writeln("Initially, A is: ", A);

//
// Arrays can also be declared using the the array literal syntax.
// Array literals are specified by enclosing a comma separated list of
// expressions in square brackets.  The domain of the array will be
// 1-based in each dimension, and the type of the array's element
// is the type of the first element listed.
//
var A2 = [-1.1, -2.2, -3.3, -4.4, -5.5];

writeln("Initially, A2 is: ", A2);

//
// Arrays can be accessed using scalar index values of the appropriate
// type, using either parenthesis or square brackets:
//

A[1] = 1.1;
A(2) = 2.2;

writeln("After assigning two elements, A is: ", A);

//
// Arrays can also be accessed using ranges to refer to subsets of
// array elements, or sub-arrays, using a technique called slicing:
//

A[2..4] = 3.3;

writeln("After assigning its interior values, A is: ", A);
writeln();

//
// Just like with array indexing, either square brackets or parenthesis
// can be used for array slicing:
//

writeln("A(2..4) is: ", A(2..4), "\n");

//
// Arrays can be multidimensional as well:
//

var B: [1..n, 1..n] real;

forall (i,j) in {1..n, 1..n} do
  B(i,j) = i + j/10.0;

writeln("Initially, B is:\n", B, "\n");

//
// An array's elements can be iterated over in a for or forall loop,
// which causes the index variable to store references to an array's
// elements:
//

forall b in B do
  b += 1;

writeln("After incrementing B's elements, B is:\n", B, "\n");

//
// An array's index set is referred to as a domain -- a first-class
// language concept that stores the set of indices used to access the
// array.  The arrays above are declared with the anonymous domains
// [1..n] and [1..n, 1..n].  An array's domain can be accessed using
// the .domain method:
//

forall (i,j) in B.domain do
  B(i,j) -= 1;

writeln("After decrementing B's elements, B is:\n", B, "\n");

//
// Domains can also be queried when arrays are passed to functions
// in order to refer to the domain via a new identifier within the
// context of that function:
//

printArr(B);

proc printArr(X: [?D] real) {
  writeln("within printArr, D is: ", D, "\n");
  forall (i,j) in D do
    X(i,j) = -X(i,j);
  writeln("after negating X within printArr, X is:\n", X, "\n");
}

//
// Arrays are passed to functions by reference by default, so the
// modifications to X in function printArr are reflected on B as
// well:
//

writeln("After calling printArr, B is:\n", B, "\n");

//
// Domains can also be declared and named.  This has the advantages
// of allowing them to be reused for multiple arrays, for associating
// names with different index spaces, and for amortizing the overhead
// of storing distributed arrays across multiple similar array
// variables.
//
// The following domain declaration defines a 2D arithemetic domain
// called ProbSpace that's the same size and shape as B was above.
//

var ProbSpace: domain(2) = {1..n, 1..n};

//
// We then use that domain to declare some arrays...
//

var C, D, E: [ProbSpace] bool;

// ...and to iterate over their index spaces...

for (i,j) in ProbSpace do
  C(i,j) = (i+j) % 3 == 0;

writeln("After initializing C, its value is:\n", C, "\n");

//
// When indexing over a multidimensional domain, the indices
// can be captured into a single tuple variable rather than
// destructuring the tuple into its integer components.
// Similarly, multidimensional array accesses can be expressed using
// tuple indices rather than multiple integer arguments.  In the
// following example, the index variable ij stores a 2-tuple of
// integers (2*int in Chapel).  This is a really inefficient way
// to assign the diagonal values "true" -- note the use of tuple
// indexing to tease the individual components out of the 2-tuple.
//

for ij in ProbSpace do
  D(ij) = ij(1) == ij(2);

writeln("After initializing D, its value is:\n", D, "\n");

//
// Array expressions of similar size and shape support whole-array
// assignment.
//

E = C;

writeln("After assigning C to E, E's value is:\n", E, "\n");

//
// Whole array assignment also allows scalar values to be promoted
// and assigned to every element of an array
//

B = 0.0;

writeln("After being reset, B is:\n", B, "\n");

//
// An array need not be indexed using the domain used to declare it,
// though doing so presents the compiler with opportunities to
// optimize bounds checks away.  In the following loop, there is
// no known relation between B and ProbSpace, so bounds checks are
// harder to prove away (requires symbolic analysis of the definitions
// of the two domains and the invariance of their bounds)
//

for (i,j) in ProbSpace do
  B(i,j) = i + j/10.0;

writeln("B has been re-initialized to:\n", B, "\n");

//
// Whole-array assignment can also be used for arrays or sub-arrays
// whose index spaces differ:
//

var F, G: [ProbSpace] real;

F[2..n-1, 2..n-1] = B[1..n-2, 3..n];

writeln("After assigning a slice of B to F, F's value is:\n", F, "\n");

//
// Arrays can also be sliced using unbounded ranges in which either
// the low and/or high bounds are omitted.  In this case, they will be
// inherited from the array's bounds.
//

G[2.., ..] = B[..n-1, ..];

writeln("After assigning a slice of B to G, G's value is:\n", G, "\n");

//
// Array slicing also supports rank-change semantics when sliced using
// a scalar value rather than a range.  In the following assignment,
// recall that A was our initial 1-dimensional array.
//

A = B[n/2, ..];

writeln("After being assigned a slice of B, A is:\n", A, "\n");

//
// Domains can also be sliced.  However, rather than having indexing
// semantics, domain slicing results in intersection of the domain's
// index set and the specified slice.
//
// Domain slicing, like array indexing and slicing, can be written
// with either square brackets or parenthesis.
//

writeln("ProbSpace[1..n-2, 3..] is: ", ProbSpace[1..n-2, 3..], "\n");

// Ranges also support slicing in this way, though we don't
// demonstrate that here.

//
// Domain variables and expressions can also be used to specify an
// array slice rather than using lists of ranges.  For example:
//

const ProbSpaceSlice = ProbSpace[0..n+1, 3..];

writeln("B[ProbSpaceSlice] is:\n", B[ProbSpaceSlice], "\n");

//
// Forall loops over domains and arrays can be written using the
// syntax "[<ind> in <Dom>] ..." which is shorthand for "forall ind
// in Dom do ..."
//

const offset = (1,1); // a 2-tuple offset

[ij in ProbSpace[2..n-1, 2..n-1]] F(ij) = B(ij + offset);

writeln("After assigning F a shifted slice of B, it is:\n", F, "\n");

[b in B] b = -b;

writeln("After negating B, it is:\n", B, "\n");

//
// Note that this shorthand resembles the array type definition in
// a variable declaration.  Due to this similarity, and for
// convenience, we allow the indices in an array type specifier to be
// named in order to support in-place initialization of the array.
// For example:
//

const Z: [ProbSpace] complex = [(i,j) in ProbSpace] i + j*1.0i;

writeln("Z is initialized to be:\n", Z, "\n");

//
// This is simply shorthand for:
//   const Z: [ProbSpace] complex = [i,j in ProbSpace] i + j*1.0i;
// or more generally:
//   [const|var] <ident>: [<ind> in <dom>] <elemtype> = <expr>;
// is equivalent to:
//   [const|var] <ident>: [<dom>] <elemtype> = [<ind> in <dom>] <expr>;
//

//
// Another advantage to declaring named domain variables is that their
// index sets can be reassigned.  This results in a logical
// re-allocation of the array variable in question, preserving array
// values for indices that existed in both the old and new domain
// values:
//

var VarDom = {1..n};

var VarArr: [VarDom] real = [i in VarDom] i;

writeln("Initially, VarArr = ", VarArr, "\n");

//
// Now, if we reassign VarDom, VarArr will be reallocated with the
// old values preserved and the new values initialized to the element
// type's default value.
//

VarDom = {1..2*n};

writeln("After doubling VarDom, VarArr = ", VarArr, "\n");

//
// As mentioned before, this reallocation preserves values according
// to index, so if we extend the lower bound of the domain, the
// non-zero values will still logically be associated with indices
// 1..n:
//

VarDom = {-n+1..2*n};

writeln("After lowering VarDom's lower bound, VarArr = ", VarArr, "\n");

//
// If the domain shrinks, values will be thrown away
//

VarDom = {2..n-1};

writeln("After shrinking VarDom, VarArr = ", VarArr, "\n");

//
// One trick to reallocate an array without preserving any values is
// to assign its domain a degenerate domain and then assign it the
// new value:
//

VarDom = {1..0};

writeln("VarArr should now be empty: ", VarArr, "\n");

VarDom = {1..n};

writeln("VarArr should now be reset: ", VarArr, "\n");

//
// Note that querying an array's domain via the .domain method or
// the function argument query syntax does not result in a domain
// expression that can be reassigned.  In particular, we cannot do:
//
//   VarArr.domain = {1..2*n};
//
// nor:
//
//   proc foo(X: [?D]) {  D = {1..2*n};  }
//
// Only a domain variable or a domain function argument can be
// reassigned to reallocate arrays.  This is to avoid confusion
// since assigning one domain variable can cause a number of
// arrays to be reallocated.  It also implies that arrays declared
// using an anonymous domain cannot be reallocated.  So for our
// original array declarations A and B, we have no way of reallocating
// them.  Arrays with constant domains provide the compiler with
// optimization benefits, so this supports a common case efficiently.
//
// A.domain = {1..2*n};
//

//
// As some of our examples have shown, arrays in Chapel can have
// arbitrary element types -- numeric values, classes, or records.
// Arrays can also support array elements, and initial support for
// this is implemented in our compiler.  For example:
//

var Y: [ProbSpace] [1..3] real;

forall (i,j) in ProbSpace do
  for k in 1..3 do
    Y(i,j)(k) = i*10 + j + k/10.0;

writeln("Y is:\n", Y);

//
// Our current implementation does not yet support arrays of arrays
// where the inner array size is a function of the outer -- they must
// all be of uniform size.  In particular, it is our intention to
// support things like:
//
//   const Triangle: [1..row] real = [row in 1..n] [col in 1..row] row + col/10.0;
//
// and:
//
//   var HierArr: [lvl in 1..n] [1..2**lvl, 1..2**lvl] real;
//
// If such capabilities would be of use to you, please let us know and
// we will adjust their priority accordingly.
//


Updated Private distribution to accept tuple index for dsiAccess method.  This
should fix the regression for

  multilocale/deitz/needMultiLocales/dist/test_private_space.

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

//
// Private Distribution, Domain, and Array
//  Defines PrivateSpace, an instance of PrivateDom
//
class Private: BaseDist {
  def dsiNewArithmeticDom(param rank: int, type idxType, param stridable: bool) {
    return new PrivateDom(rank=rank, idxType=idxType, stridable=stridable);
  }

  def writeThis(x: Writer) {
    x.writeln("Private Distribution");
  }
}

class PrivateDom: BaseArithmeticDom {
  param rank: int;
  type idxType;
  param stridable: bool;
  var pid: int = -1;
  var dist: Private;

  def getDist() return dist;
  def these() { for i in 0..numLocales-1 do yield i; }

  def these(param tag: iterator) where tag == iterator.leader {
    coforall loc in Locales do on loc {
      var t: 1*range(idxType);
      t(1) = here.id..here.id;
      yield t;
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    for i in follower(1) do
      yield i;
  }

  def dsiSerialWrite(x: Writer) { x.write("Private Domain"); }

  def dsiBuildArray(type eltType)
    return new PrivateArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=this);

  def dsiNumIndices return numLocales;
  def dsiLow return 0;
  def dsiHigh return numLocales-1;
  def dsiSetIndices(x: domain) { compilerError("cannot reassign private domain"); }
  def dsiGetIndices() { return [0..numLocales-1]; }

  def dsiRequiresPrivatization() param return true;
  def linksDistribution() param return false;

  def dsiGetPrivatizeData() return 0;

  def dsiPrivatize(privatizeData)
    return new PrivateDom(rank=rank, idxType=idxType, stridable=stridable, dist=dist);

  def dsiGetReprivatizeData() return 0;

  def dsiReprivatize(other, reprivatizeData) { }

  def dsiMember(i) return 0 <= i && i <= numLocales-1;
}

class PrivateArr: BaseArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;
  var dom: PrivateDom(rank, idxType, stridable);
  var data: eltType;
  var pid: int = -1;
}

def PrivateArr.dsiGetBaseDom() return dom;

def PrivateArr.dsiRequiresPrivatization() param return true;

def PrivateArr.dsiGetPrivatizeData() return 0;

def PrivateArr.dsiPrivatize(privatizeData) {
  var dompid = dom.pid;
  var thisdom = dom;
  var privdom = __primitive("chpl_getPrivatizedClass", thisdom, dompid);
  return new PrivateArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=privdom);
}

def PrivateArr.dsiAccess(i: idxType) var {
  if _local then
    return data;
  else if i == here.id then
    return data;
  else {
    if boundsChecking then
      if i < 0 || i >= numLocales then
        halt("array index out of bounds: ", i);
    var arrpid = this.pid;
    var privarr = this;
    on Locales(i) {
      privarr = __primitive("chpl_getPrivatizedClass", privarr, arrpid);
    }
    return privarr.data;
  }
}

def PrivateArr.dsiAccess(i: 1*idxType) var
  return dsiAccess(i(1));

def PrivateArr.these() var {
  for i in dom do
    yield dsiAccess(i);
}

def PrivateArr.these(param tag: iterator) where tag == iterator.leader {
  coforall loc in Locales do on loc {
    var t: 1*range(idxType);
    t(1) = here.id..here.id;
    yield t;
  }
}

def PrivateArr.these(param tag: iterator, follower) var where tag == iterator.follower {
  for i in follower(1) do
    yield dsiAccess(i);
}

def PrivateArr.dsiSerialWrite(x: Writer) {
  var first: bool = true;
  for i in dom {
    if first then first = !first; else write(" ");
    write(dsiAccess(i));
  }
}

const PrivateSpace: domain(1) distributed new dist(new Private());

//
// Private Distribution, Domain, and Array
//  Defines PrivateSpace, an instance of PrivateDom
//
class Private: BaseDist {
  def dsiNewArithmeticDom(param rank: int, type idxType, param stridable: bool) {
    return new PrivateDom(rank=rank, idxType=idxType, stridable=stridable);
  }

  def writeThis(x: Writer) {
    x.writeln("Private Distribution");
  }
}

class PrivateDom: BaseArithmeticDom {
  param rank: int;
  type idxType;
  param stridable: bool;
  var pid: int = -1;
  var dist: Private;

  def getDist() return dist;
  def these() { for i in 0..numLocales-1 do yield i; }

  def these(param tag: iterator) where tag == iterator.leader {
    coforall loc in Locales do on loc {
      var t: 1*range(idxType);
      t(1) = here.id..here.id;
      yield t;
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    for i in follower(1) do
      yield i;
  }

  def dsiSerialWrite(x: Writer) { x.write("Private Domain"); }

  def dsiBuildArray(type eltType)
    return new PrivateArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=this);

  def dsiNumIndices return numLocales;
  def dsiLow return 0;
  def dsiHigh return numLocales-1;
  def dsiSetIndices(x: domain) { compilerError("cannot reassign private domain"); }
  def dsiGetIndices() { return [0..numLocales-1]; }

  def dsiRequiresPrivatization() param return true;
  def linksDistribution() param return false;

  def dsiGetPrivatizeData() return 0;

  def dsiPrivatize(privatizeData)
    return new PrivateDom(rank=rank, idxType=idxType, stridable=stridable, dist=dist);

  def dsiGetReprivatizeData() return 0;

  def dsiReprivatize(other, reprivatizeData) { }

  def dsiMember(i) return 0 <= i && i <= numLocales-1;
}

class PrivateArr: BaseArr {
  type eltType;
  param rank: int;
  type idxType;
  param stridable: bool;
  var dom: PrivateDom(rank, idxType, stridable);
  var data: eltType;
  var pid: int = -1;
}

def PrivateArr.dsiGetBaseDom() return dom;

def PrivateArr.dsiRequiresPrivatization() param return true;

def PrivateArr.dsiGetPrivatizeData() return 0;

def PrivateArr.dsiPrivatize(privatizeData) {
  var dompid = dom.pid;
  var thisdom = dom;
  var privdom = __primitive("chpl_getPrivatizedClass", thisdom, dompid);
  return new PrivateArr(eltType=eltType, rank=rank, idxType=idxType, stridable=stridable, dom=privdom);
}

def PrivateArr.dsiAccess(i: idxType) var {
  if _local then
    return data;
  else if i == here.id then
    return data;
  else {
    if boundsChecking then
      if i < 0 || i >= numLocales then
        halt("array index out of bounds: ", i);
    var arrpid = this.pid;
    var privarr = this;
    on Locales(i) {
      privarr = __primitive("chpl_getPrivatizedClass", privarr, arrpid);
    }
    return privarr.data;
  }
}

def PrivateArr.these() var {
  for i in dom do
    yield dsiAccess(i);
}

def PrivateArr.these(param tag: iterator) where tag == iterator.leader {
  coforall loc in Locales do on loc {
    var t: 1*range(idxType);
    t(1) = here.id..here.id;
    yield t;
  }
}

def PrivateArr.these(param tag: iterator, follower) var where tag == iterator.follower {
  for i in follower(1) do
    yield dsiAccess(i);
}

def PrivateArr.dsiSerialWrite(x: Writer) {
  var first: bool = true;
  for i in dom {
    if first then first = !first; else write(" ");
    write(dsiAccess(i));
  }
}

const PrivateSpace: domain(1) distributed new dist(new Private());