Update isPrime() check to ensure modulo is using uints for both arguments.

/*
 * Math helper functions.
 */

// 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;
  }

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

// Yield successive permutations of the elements.
//
// Inspired by: https://docs.python.org/2/library/itertools.html#itertools.permutations
iter permutations(elements: [] string, length=-1): string {
  var n = elements.size;
  var r = length;

  // Default for r is length.
  if r == -1 then
    r = n;

  // If permutations are longer than number of elements, no permutations are
  // possible.
  if r > n then
    return;

  var indices: [{0..n-1}] int = [i in 0..n-1] i,
    cycles: [{0..n-1}] int = [c in n-r+1..n by -1] c;

  var perm = "";
  for i in indices[0.. # r] do
    perm += elements[i+1];
  yield perm;

  while true {
    var done = true;
    for i in 0..r-1 by -1 {
      cycles[i] -= 1;
      if cycles[i] == 0 {
        var rightInd = indices[i+1..],
          leftInd = indices[i..i];
        indices[i.. # rightInd.size] = rightInd;
        indices[(i + rightInd.size)..] = leftInd;
        cycles[i] = n - i;
      } else {
        indices[i] <=> indices[indices.size - cycles[i]];

        perm = "";
        for i in indices[0.. # r] do
          perm += elements[i+1];
        yield perm;

        done = false;
        break;
      }
    }

    // Did not yield a permutation, so we're done.
    if done then
      return;
  }
}

// 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;

  // If only one prime requested, stop here.
  if k == 1 then
    return;

  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;
  }
}

// Sieve of Eratosthenes up to max value.
// Generates prime numbers less than or equal to `m`.
iter primesUpTo(m: uint, param debug=false): uint {
  if m < 2 then
    halt("no primes less than 2");

  if m >= 2 then
    yield 2;

  if m == 2 then
    return;

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

  while q <= m {
    if ! D.member(q) {
      if debug then
        assert(isPrime(q));

      yield q;

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

// 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;
}

// Yield successive permutations of the elements.
//
// Inspired by: https://docs.python.org/2/library/itertools.html#itertools.permutations
iter permutations(elements: [] string, length=-1): string {
  var n = elements.size;
  var r = length;

  // Default for r is length.
  if r == -1 then
    r = n;

  // If permutations are longer than number of elements, no permutations are
  // possible.
  if r > n then
    return;

  var indices: [{0..n-1}] int = [i in 0..n-1] i,
    cycles: [{0..n-1}] int = [c in n-r+1..n by -1] c;

  var perm = "";
  for i in indices[0.. # r] do
    perm += elements[i+1];
  yield perm;

  while true {
    var done = true;
    for i in 0..r-1 by -1 {
      cycles[i] -= 1;
      if cycles[i] == 0 {
        var rightInd = indices[i+1..],
          leftInd = indices[i..i];
        indices[i.. # rightInd.size] = rightInd;
        indices[(i + rightInd.size)..] = leftInd;
        cycles[i] = n - i;
      } else {
        indices[i] <=> indices[indices.size - cycles[i]];

        perm = "";
        for i in indices[0.. # r] do
          perm += elements[i+1];
        yield perm;

        done = false;
        break;
      }
    }

    // Did not yield a permutation, so we're done.
    if done then
      return;
  }
}

// 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;

  // If only one prime requested, stop here.
  if k == 1 then
    return;

  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;
  }
}

// Sieve of Eratosthenes up to max value.
// Generates prime numbers less than or equal to `m`.
iter primesUpTo(m: uint, param debug=false): uint {
  if m < 2 then
    halt("no primes less than 2");

  if m >= 2 then
    yield 2;

  if m == 2 then
    return;

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

  while q <= m {
    if ! D.member(q) {
      if debug then
        assert(isPrime(q));

      yield q;

      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;
}


Implemented minloc and maxloc to explicitly require an index.  See new
test for example.

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

pragma "special free seqNode"
class _seqNode {
  type eltType;

  var _element : eltType;
  var _next : _seqNode of eltType;
}

pragma "seq"
record seq {
  type eltType;

  var _length : int;
  var _first : _seqNode of eltType;
  var _last : _seqNode of eltType;

  var _promotionType : eltType;

  def this(i : int) var {
    if i >= 0 {
      if i < 1 || i > length then
        halt("sequence index out of bounds: ", i);
      var tmp = _first;
      for j in 1..i-1 do
        tmp = tmp._next;
      return tmp._element;
    } else {
      if -i > length then
        halt("sequence index out of bounds: ", -i);
      var tmp = _first;
      for j in 1..length+i do
        tmp = tmp._next;
      return tmp._element;
    }
  }

  def this(i: int, is: int...?k) var {
    return this(i)(is);
  }

  def this(is: _tuple) var {
    return this((...is));
  }

  def getHeadCursor()
    return _first;

  def getNextCursor(c)
    return c._next;

  def getValue(c)
    return c._element;

  def isValidCursor?(c)
    return c != nil;

  def length : int
    return _length;

  def =length(x) {
    compilerError("illegal assignment to sequence's length");
  }

  def _append(e : eltType)
    return this._copy()._append_in_place(e);

  def _prepend(e : eltType)
    return this._copy()._prepend_in_place(e);

  def _concat(s : seq)
    return this._copy()._concat_in_place(s);

  def _append_in_place(e : eltType) {
    var tmp = e;
    var new = _seqNode(eltType, tmp);
    if _length > 0 {
      _last._next = new;
      _last = new;
    } else {
      _first = new;
      _last = new;
    }
    _length = _length + 1;
    return this;
  }

  def _yield(e: eltType) {
    this._append_in_place(e);
  }

  def _prepend_in_place(e : eltType) {
    var tmp = e;
    var new = _seqNode(eltType, tmp);
    if _length > 0 {
      new._next = _first;
      _first = new;
    } else {
      _first = new;
      _last = new;
    }
    _length = _length + 1;
    return this;
  }

  def _concat_in_place(s : seq) {
    if _length > 0 {
      _last._next = s._first;
      _last = s._last;
      _length = _length + s._length;
    } else {
      _first = s._first;
      _last = s._last;
      _length = s._length;
    }
    return this;
  }

  def _copy() {
    var new : seq of eltType;
    var tmp = _first;
    while tmp != nil {
      new._append_in_place(tmp._element);
      tmp = tmp._next;
    }
    return new;
  }

  def _delete() {
    _first = nil;
    _last = nil;
    _length = 0;
  }

  def _reverse() {
    var new : seq of eltType;
    var tmp = _first;
    while (tmp != nil) {
      new._prepend_in_place(tmp._element);
      tmp  = tmp._next;
    }
    return new;
  }
}

def =(s1: seq, s2: seq) {
  s1._delete();
  forall e in s2 do
    s1._append_in_place(e);
  return s1;
}

def #(s1:seq, s2) where s1.type == s2.type {
  return s1._concat(s2);
}

def #(s:seq, e) where e.type:s.eltType {
  return s._append(e);
}

def #(e, s:seq) where e.type:s.eltType {
  return s._prepend(e);
}

def reverse(s : seq, dim : int = 1) {
  if (dim != 1) {
    halt("reverse(:seq, dim=1) only implemented for dim 1");
  }
  return s._reverse();
}

def seq.writeThis(f: Writer) {
  f.write("(/");
  var tmp = _first;
  while tmp != nil {
    f.write(tmp._element);
    tmp = tmp._next;
    if tmp != nil {
      f.write(", ");
    }
  }
  f.write("/)");
}

def _reduce(r, s) { // reduce sequence s by reduction r
  for e in s do
    r.accumulate(e);
  return r.generate();
}

def _scan(r, s) {
  var s2: seq(r.eltType);
  for e in s {
    r.accumulate(e);
    s2._append_in_place(r.generate());
  }
  return s2;
}

def _to_seq( i) {
  // var s: seq (i.getValue(i.getNextCursor(i.getHeadCursor()))).type;
  var s: seq (i.getElemType());
  for x in i {
    s #= x;
  }
  return s;
}

def _sum_type(type eltType) {
  var x: eltType;
  return x + x;
}

class _sum {
  type eltType;
  var value : _sum_type(eltType).type;
  def accumulate(x) {
    value = value + x;
  }
  def generate() return value;
}

class _prod {
  type eltType;
  var value : eltType = _prod_id( eltType);

  def accumulate(x) {
    value = value * x;
  }
  def generate() return value;
}

class _max {
  type eltType;
  var value : eltType = min( eltType);

  def accumulate(x) {
    value = max(x, value);
  }
  def generate() return value;
}

class _min {
  type eltType;
  var value : eltType = max( eltType);

  def accumulate(x) {
    value = min(x, value);
  }
  def generate() return value;
}

class maxloc {
  type eltType;
  var value: eltType;
  var uninitialized = true;

  def accumulate(x) {
    if uninitialized || x(1) > value(1) then
      value = x;
    uninitialized = false;
  }
  def generate() return value;
}

class minloc {
  type eltType;
  var value: eltType;
  var uninitialized = true;

  def accumulate(x) {
    if uninitialized || x(1) < value(1) then
      value = x;
    uninitialized = false;
  }
  def generate() return value;
}

class _land {                 // logical and
  type eltType;
  var value : eltType = _land_id( eltType);

  def accumulate(x) {
    value = value && x;
  }
  def generate() return value;
}

class _lor {                 // logical or
  type eltType;
  var value : eltType = _lor_id( eltType);

  def accumulate(x) {
    value = value || x;
  }
  def generate() return value;
}

class _band {                 // bit-wise and
  type eltType;
  var value : eltType = _band_id( eltType);

  def accumulate(x) {
    value = value & x;
  }
  def generate() return value;
}

class _bor {                 // bit-wise or
  type eltType;
  var value : eltType = _bor_id( eltType);

  def accumulate(x) {
    value = value | x;
  }
  def generate() return value;
}

class _bxor {                // bit-wise xor
  type eltType;
  var value : eltType = _bxor_id( eltType);

  def accumulate(x) {
    value = value ^ x;
  }
  def generate() return value;
}

def _aseq._translate(i : int) {
  return _low+i.._high+i by _stride;
}

def _aseq._interior(i : int) {
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low.._low-1-i by _stride;
  } else if (i > 0) {
    x = _high+1-i.._high by _stride;
  }
  return x;
}

def _aseq._exterior(i : int) {
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low+i.._low-1 by _stride;
  } else if (i > 0) {
    x = _high+1.._high+i by _stride;
  }
  return x;
}

def _aseq._expand(i : int) {
  return _low-i.._high+i by _stride;
}

// Arithmetic sequence

def _build_aseq(low: int, high: int) return _aseq(int, low, high);
def _build_aseq(low: uint, high: uint) return _aseq(uint, low, high);
def _build_aseq(low: int(64), high: int(64)) return _aseq(int(64), low, high);
def _build_aseq(low: uint(64), high: uint(64)) return _aseq(uint(64), low, high);

def _build_aseq(low, high) {
  compilerError("arithmetic sequence bounds are not of integral type");
}

record _aseq {
  type eltType;
  var _low : eltType;
  var _high : eltType;
  var _stride : int = 1;

  var _promotionType : eltType;

  def low: eltType return _low;
  def high: eltType return _high;
  def stride: eltType return _stride;

  def initialize() {
    if _low > _high {
      _low = 1:eltType;
      _high = 0:eltType;
      _stride = 1;
    }
  }

  def getHeadCursor()
    if _stride > 0 then
      return _low;
    else
      return _high;

  def getNextCursor(c)
    return c + _stride:eltType;

  def getValue(c)
    return c;

  def isValidCursor?(c)
    return _low <= c && c <= _high;

  def length
    return
      (if _stride > 0
        then (_high - _low + _stride:eltType) / _stride:eltType
        else (_low - _high + _stride:eltType) / _stride:eltType);
}

def by(s : _aseq, i : int) {
  if i == 0 then
    halt("illegal stride of 0");
  if s._low == 1 && s._high == 0 then
    return _aseq(s.eltType, s._low, s._high, s._stride);
  var as = _aseq(s.eltType, s._low, s._high, s._stride * i);
  if as._stride < 0 then
    as._low = as._low + (as._high - as._low) % (-as._stride):as.eltType;
  else
    as._high = as._high - (as._high - as._low) % (as._stride):as.eltType;
  return as;
}

def _in(s : _aseq, i : s.eltType)
  return i >= s._low && i <= s._high &&
    (i - s._low) % abs(s._stride):s.eltType == 0;

// really slow --- REWRITE
def _in(s1: _aseq, s2: _aseq) {
  for i in s2 do
    if !_in(s1, i) then
      return false;
  return true;
}

def _aseq.writeThis(f: Writer) {
  f.write(_low, "..", _high);
  if (_stride != 1) then
    f.write(" by ", _stride);
}

pragma "inline" def string.substring(s: _aseq)
  if s._stride != 1 then
    return __primitive("string_strided_select", this, s._low, s._high, s._stride);
  else
    return __primitive("string_select", this, s._low, s._high);

// indefinite arithmetic sequence

def _build_iaseq(bound: int, param upper: int)
  return _iaseq(int, upper, bound);
def _build_iaseq(bound: uint, param upper: int)
  return _iaseq(uint, upper, bound);
def _build_iaseq(bound: int(64), param upper: int)
  return _iaseq(int(64), upper, bound);
def _build_iaseq(bound: uint(64), param upper: int)
  return _iaseq(uint(64), upper, bound);

def _build_iaseq(bound, upper) {
  compilerError("arithmetic sequence bound is not of integral type");
}

record _iaseq {
  type eltType;
  param _upper: int; // 0 bound is lower bound, 1 bound is upper bound
  var _bound: eltType;
  var _stride : int = 1;
  var _promotionType : eltType;

  def getHeadCursor() {
    if _upper == 1 && _stride > 0 then
      halt("error: indefinite arithmetic sequence has positive stride and upper bound");
    if _upper == 0 && _stride < 0 then
      halt("error: indefinite arithmetic sequence has negative stride and lower bound");
    return _bound;
  }

  def getNextCursor(c)
    return c + _stride:eltType;

  def getValue(c)
    return c;

  def isValidCursor?(c)
    return true;

  def length {
    halt("error: attempt to determine length of an indefinite arithmetic sequence");
    return 0:eltType;
  }
}

def by(s : _iaseq, i : int) {
  if i == 0 then
    halt("illegal stride of 0");
  return _iaseq(s.eltType, s._upper, s._bound, s._stride * i);
}

def _iaseq.writeThis(f: Writer) {
  if _upper then
    f.write("..", _bound);
  else
    f.write(_bound, "..");
  if (_stride != 1) then
    f.write(" by ", _stride);
}

pragma "special free seqNode"
class _seqNode {
  type eltType;

  var _element : eltType;
  var _next : _seqNode of eltType;
}

pragma "seq"
record seq {
  type eltType;

  var _length : int;
  var _first : _seqNode of eltType;
  var _last : _seqNode of eltType;

  var _promotionType : eltType;

  def this(i : int) var {
    if i >= 0 {
      if i < 1 || i > length then
        halt("sequence index out of bounds: ", i);
      var tmp = _first;
      for j in 1..i-1 do
        tmp = tmp._next;
      return tmp._element;
    } else {
      if -i > length then
        halt("sequence index out of bounds: ", -i);
      var tmp = _first;
      for j in 1..length+i do
        tmp = tmp._next;
      return tmp._element;
    }
  }

  def this(i: int, is: int...?k) var {
    return this(i)(is);
  }

  def this(is: _tuple) var {
    return this((...is));
  }

  def getHeadCursor()
    return _first;

  def getNextCursor(c)
    return c._next;

  def getValue(c)
    return c._element;

  def isValidCursor?(c)
    return c != nil;

  def length : int
    return _length;

  def =length(x) {
    compilerError("illegal assignment to sequence's length");
  }

  def _append(e : eltType)
    return this._copy()._append_in_place(e);

  def _prepend(e : eltType)
    return this._copy()._prepend_in_place(e);

  def _concat(s : seq)
    return this._copy()._concat_in_place(s);

  def _append_in_place(e : eltType) {
    var tmp = e;
    var new = _seqNode(eltType, tmp);
    if _length > 0 {
      _last._next = new;
      _last = new;
    } else {
      _first = new;
      _last = new;
    }
    _length = _length + 1;
    return this;
  }

  def _yield(e: eltType) {
    this._append_in_place(e);
  }

  def _prepend_in_place(e : eltType) {
    var tmp = e;
    var new = _seqNode(eltType, tmp);
    if _length > 0 {
      new._next = _first;
      _first = new;
    } else {
      _first = new;
      _last = new;
    }
    _length = _length + 1;
    return this;
  }

  def _concat_in_place(s : seq) {
    if _length > 0 {
      _last._next = s._first;
      _last = s._last;
      _length = _length + s._length;
    } else {
      _first = s._first;
      _last = s._last;
      _length = s._length;
    }
    return this;
  }

  def _copy() {
    var new : seq of eltType;
    var tmp = _first;
    while tmp != nil {
      new._append_in_place(tmp._element);
      tmp = tmp._next;
    }
    return new;
  }

  def _delete() {
    _first = nil;
    _last = nil;
    _length = 0;
  }

  def _reverse() {
    var new : seq of eltType;
    var tmp = _first;
    while (tmp != nil) {
      new._prepend_in_place(tmp._element);
      tmp  = tmp._next;
    }
    return new;
  }
}

def =(s1: seq, s2: seq) {
  s1._delete();
  forall e in s2 do
    s1._append_in_place(e);
  return s1;
}

def #(s1:seq, s2) where s1.type == s2.type {
  return s1._concat(s2);
}

def #(s:seq, e) where e.type:s.eltType {
  return s._append(e);
}

def #(e, s:seq) where e.type:s.eltType {
  return s._prepend(e);
}

def reverse(s : seq, dim : int = 1) {
  if (dim != 1) {
    halt("reverse(:seq, dim=1) only implemented for dim 1");
  }
  return s._reverse();
}

def seq.writeThis(f: Writer) {
  f.write("(/");
  var tmp = _first;
  while tmp != nil {
    f.write(tmp._element);
    tmp = tmp._next;
    if tmp != nil {
      f.write(", ");
    }
  }
  f.write("/)");
}

def _reduce(r, s) { // reduce sequence s by reduction r
  for e in s do
    r.accumulate(e);
  return r.generate();
}

def _scan(r, s) {
  var s2: seq(r.eltType);
  for e in s {
    r.accumulate(e);
    s2._append_in_place(r.generate());
  }
  return s2;
}

def _to_seq( i) {
  // var s: seq (i.getValue(i.getNextCursor(i.getHeadCursor()))).type;
  var s: seq (i.getElemType());
  for x in i {
    s #= x;
  }
  return s;
}

def _sum_type(type eltType) {
  var x: eltType;
  return x + x;
}

class _sum {
  type eltType;
  var value : _sum_type(eltType).type;
  def accumulate(x) {
    value = value + x;
  }
  def generate() return value;
}

class _prod {
  type eltType;
  var value : eltType = _prod_id( eltType);

  def accumulate(x) {
    value = value * x;
  }
  def generate() return value;
}

class _max {
  type eltType;
  var value : eltType = min( eltType);

  def accumulate(x) {
    value = max(x, value);
  }
  def generate() return value;
}

class _min {
  type eltType;
  var value : eltType = max( eltType);

  def accumulate(x) {
    value = min(x, value);
  }
  def generate() return value;
}

class _land {                 // logical and
  type eltType;
  var value : eltType = _land_id( eltType);

  def accumulate(x) {
    value = value && x;
  }
  def generate() return value;
}

class _lor {                 // logical or
  type eltType;
  var value : eltType = _lor_id( eltType);

  def accumulate(x) {
    value = value || x;
  }
  def generate() return value;
}

class _band {                 // bit-wise and
  type eltType;
  var value : eltType = _band_id( eltType);

  def accumulate(x) {
    value = value & x;
  }
  def generate() return value;
}

class _bor {                 // bit-wise or
  type eltType;
  var value : eltType = _bor_id( eltType);

  def accumulate(x) {
    value = value | x;
  }
  def generate() return value;
}

class _bxor {                // bit-wise xor
  type eltType;
  var value : eltType = _bxor_id( eltType);

  def accumulate(x) {
    value = value ^ x;
  }
  def generate() return value;
}

def _aseq._translate(i : int) {
  return _low+i.._high+i by _stride;
}

def _aseq._interior(i : int) {
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low.._low-1-i by _stride;
  } else if (i > 0) {
    x = _high+1-i.._high by _stride;
  }
  return x;
}

def _aseq._exterior(i : int) {
  var x = _low.._high by _stride;
  if (i < 0) {
    x = _low+i.._low-1 by _stride;
  } else if (i > 0) {
    x = _high+1.._high+i by _stride;
  }
  return x;
}

def _aseq._expand(i : int) {
  return _low-i.._high+i by _stride;
}

// Arithmetic sequence

def _build_aseq(low: int, high: int) return _aseq(int, low, high);
def _build_aseq(low: uint, high: uint) return _aseq(uint, low, high);
def _build_aseq(low: int(64), high: int(64)) return _aseq(int(64), low, high);
def _build_aseq(low: uint(64), high: uint(64)) return _aseq(uint(64), low, high);

def _build_aseq(low, high) {
  compilerError("arithmetic sequence bounds are not of integral type");
}

record _aseq {
  type eltType;
  var _low : eltType;
  var _high : eltType;
  var _stride : int = 1;

  var _promotionType : eltType;

  def low: eltType return _low;
  def high: eltType return _high;
  def stride: eltType return _stride;

  def initialize() {
    if _low > _high {
      _low = 1:eltType;
      _high = 0:eltType;
      _stride = 1;
    }
  }

  def getHeadCursor()
    if _stride > 0 then
      return _low;
    else
      return _high;

  def getNextCursor(c)
    return c + _stride:eltType;

  def getValue(c)
    return c;

  def isValidCursor?(c)
    return _low <= c && c <= _high;

  def length
    return
      (if _stride > 0
        then (_high - _low + _stride:eltType) / _stride:eltType
        else (_low - _high + _stride:eltType) / _stride:eltType);
}

def by(s : _aseq, i : int) {
  if i == 0 then
    halt("illegal stride of 0");
  if s._low == 1 && s._high == 0 then
    return _aseq(s.eltType, s._low, s._high, s._stride);
  var as = _aseq(s.eltType, s._low, s._high, s._stride * i);
  if as._stride < 0 then
    as._low = as._low + (as._high - as._low) % (-as._stride):as.eltType;
  else
    as._high = as._high - (as._high - as._low) % (as._stride):as.eltType;
  return as;
}

def _in(s : _aseq, i : s.eltType)
  return i >= s._low && i <= s._high &&
    (i - s._low) % abs(s._stride):s.eltType == 0;

// really slow --- REWRITE
def _in(s1: _aseq, s2: _aseq) {
  for i in s2 do
    if !_in(s1, i) then
      return false;
  return true;
}

def _aseq.writeThis(f: Writer) {
  f.write(_low, "..", _high);
  if (_stride != 1) then
    f.write(" by ", _stride);
}

pragma "inline" def string.substring(s: _aseq)
  if s._stride != 1 then
    return __primitive("string_strided_select", this, s._low, s._high, s._stride);
  else
    return __primitive("string_select", this, s._low, s._high);

// indefinite arithmetic sequence

def _build_iaseq(bound: int, param upper: int)
  return _iaseq(int, upper, bound);
def _build_iaseq(bound: uint, param upper: int)
  return _iaseq(uint, upper, bound);
def _build_iaseq(bound: int(64), param upper: int)
  return _iaseq(int(64), upper, bound);
def _build_iaseq(bound: uint(64), param upper: int)
  return _iaseq(uint(64), upper, bound);

def _build_iaseq(bound, upper) {
  compilerError("arithmetic sequence bound is not of integral type");
}

record _iaseq {
  type eltType;
  param _upper: int; // 0 bound is lower bound, 1 bound is upper bound
  var _bound: eltType;
  var _stride : int = 1;
  var _promotionType : eltType;

  def getHeadCursor() {
    if _upper == 1 && _stride > 0 then
      halt("error: indefinite arithmetic sequence has positive stride and upper bound");
    if _upper == 0 && _stride < 0 then
      halt("error: indefinite arithmetic sequence has negative stride and lower bound");
    return _bound;
  }

  def getNextCursor(c)
    return c + _stride:eltType;

  def getValue(c)
    return c;

  def isValidCursor?(c)
    return true;

  def length {
    halt("error: attempt to determine length of an indefinite arithmetic sequence");
    return 0:eltType;
  }
}

def by(s : _iaseq, i : int) {
  if i == 0 then
    halt("illegal stride of 0");
  return _iaseq(s.eltType, s._upper, s._bound, s._stride * i);
}

def _iaseq.writeThis(f: Writer) {
  if _upper then
    f.write("..", _bound);
  else
    f.write(_bound, "..");
  if (_stride != 1) then
    f.write(" by ", _stride);
}


Implemented reduction

Implemented reduction

use Collection.Queue;

class DistributedQueueSlotNode {
  type eltType;
  var elt : eltType;
  var next : DistributedQueueSlotNode(eltType);
}

/*
  For each slot we maintain a two-locked queue.
*/
class DistributedQueueSlot {
  type eltType;

  // Locks are atomic to allow us to remotely contest for the lock.
  var headLock$ : sync bool;
  var tailLock$ : sync bool;

  // Number of elements in this slot. If the count is negative, that means a 'dequeue'
  // has skipped over this, and as 'enqueue' we should as well to catch up.
  var size : atomic int;

  var head : DistributedQueueSlotNode(eltType);
  var tail : DistributedQueueSlotNode(eltType);

  proc DistributedQueueSlot(type eltType) {
    head = new DistributedQueueSlotNode(eltType);
    tail = head;
  }

  proc add(elt : eltType) {
    on this {
      var node = new DistributedQueueSlotNode(eltType, elt=elt);
      local {
        tailLock$ = true;
        tail.next = node;
        tail = node;
        tailLock$;
      }
    }
  }

  // Note, this should be *only* called after we know for a fact that there is an element
  // waiting for us...
  // TODO: Check performance using an 'on' statement...
  proc remove() : eltType {
    var elt : eltType;
    on this {
      var _elt : eltType;
      local {
        headLock$ = true;
        var node = head;
        var newHead = head.next;

        if newHead == nil {
          var barrier : atomic uint;
          while newHead == nil {
            chpl_task_yield();
            barrier.fetchAdd(1);
            newHead = head.next;
          }
        }

        head = newHead;
        _elt = newHead.elt;
        headLock$;
        delete node;
      }
      elt = _elt;
    }
    return elt;
  }

  proc ~DistributedQueueSlot() {
    on this {
      local {
        var curr = head;

        while curr != nil {
          var tmp = curr.next;
          delete curr;
          curr = tmp;
        }
      }
    }
  }
}

/*
  An unbounded queue that is distributed across nodes.
*/
class DistributedQueue : Queue {
  var targetLocDom : domain(1) = LocaleSpace;
  var targetLocales : [targetLocDom] locale = Locales;

  // Privatization id
  var pid : int;

  // Keeps track of which slot we are on...
  var globalHead : atomic uint;
  var globalTail : atomic uint;

  // To 'freeze' the queue, we must ensure that current mutating operations finish
  // first. However, at the same time we want to reduce communication by keeping
  // a task counter for each node that can be checked at next to no cost.
  var concurrentTasks : atomic uint;
  var frozenState : atomic bool;

  // We maintain an array of slots, wherein each slot is a pointer into a node's
  // address space. To maximize parallelism, we maintain numLocales * maxTaskPar
  // to reduce the amount of contention.
  var nSlots = here.maxTaskPar * targetLocales.size;
  var slotSpace = {0..#nSlots};
  var slots : [slotSpace] DistributedQueueSlot(eltType);

  proc DistributedQueue(type eltType, targetLocDom=LocaleSpace, targetLocales=Locales) {
    nSlots = here.maxTaskPar * targetLocales.size;
    slotSpace = {0..#nSlots};

    // Initialize each slot. We use a round-robin algorithm.
    var idx : atomic int;
    for 0 .. #here.maxTaskPar {
      for loc in targetLocales do on loc {
        var i = idx.fetchAdd(1);
        slots[i] = new DistributedQueueSlot(eltType);
      }
    }

    pid = _newPrivatizedClass(this);
  }

  proc DistributedQueue(other, privData, type eltType = other.eltType) {
    nSlots = other.nSlots;
    slotSpace = other.slotSpace;
    slots = privData;
  }

  proc dsiPrivatize(privData) {
      return new DistributedQueue(this, privData);
  }

  proc dsiGetPrivatizeData() {
    return slots;
  }

  inline proc getPrivatizedThis {
    return chpl_getPrivatizedCopy(this.type, pid);
  }

  proc add(elt : eltType) : bool {
    var localThis = getPrivatizedThis;
    ref _globalTail = globalTail;

    local {
      localThis.concurrentTasks.add(1);

      // Check if the queue is now 'immutable'.
      if localThis.frozenState.read() == true {
        localThis.concurrentTasks.sub(1);
        return false;
      }
    }

    var _nSlots : int;
    local { _nSlots = localThis.nSlots; }

    // Find a slot we can add to; skip over any with a negative size
    while true {
      var tail = (_globalTail.fetchAdd(1) % _nSlots : uint) : int;
      var slot : DistributedQueueSlot(eltType);
      local { slot = localThis.slots[tail]; }
      var sz = slot.size.fetchAdd(1);

      if sz >= 0 {
        slot.add(elt);
        local { localThis.concurrentTasks.sub(1); }
        return true;
      }
    }

    halt("Somehow broke out of add loop...");
  }

  proc remove() : (bool, eltType) {
    var localThis = getPrivatizedThis;

    local {
      localThis.concurrentTasks.add(1);

      // Check if the queue is now 'immutable'.
      if localThis.frozenState.read() == true {
        localThis.concurrentTasks.sub(1);
        return (false, _defaultOf(eltType));
      }
    }

    // Find a slot we can take from; if the slot is empty, we bail as it is empty.
    var head = (globalHead.fetchAdd(1) % localThis.nSlots : uint) : int;
    var slot : DistributedQueueSlot(eltType);
    local { slot = localThis.slots[head]; }
    var sz = slot.size.fetchSub(1);

    // Nothing for us...
    if sz < 1 {
      local { localThis.concurrentTasks.sub(1); }
      return (false, _defaultOf(eltType));
    }

    // There is something for us
    var elt = slot.remove();
    local { localThis.concurrentTasks.sub(1); }
    return (true, elt);
  }

  proc isFrozen() : bool {
    return getPrivatizedThis.frozenState.read();
  }

  proc freeze() {
    coforall loc in Locales do on loc {
      var localThis = getPrivatizedThis;
      localThis.frozenState.write(true);
      localThis.concurrentTasks.waitFor(0);
    }
  }

  proc unfreeze() {
    coforall loc in Locales do on loc {
      var localThis = getPrivatizedThis;
      localThis.frozenState.write(false);
    }
  }

  /*
    Iterate in FIFO order.

    TODO: Once the issue of serial iteration leaking state is fixed, a simple
    one-way channel implementation may prove very useful here to get an item
    in a round-robin manner.
  */
  iter these() : eltType {
    if !isFrozen() {
      halt("Iteration only supported while frozen...");
    }

    var localThis = getPrivatizedThis;

    // Fill our slots to visit in FIFO order.
    var head = globalHead.read();
    var tail = globalTail.read();

    // Check if empty...
    if head >= tail {
      return;
    }

    var nElems = tail - head;
    var nSearchNodes = min(nElems, nSlots) : int;
    var nodes : [{0..#nSearchNodes}] DistributedQueueSlotNode(eltType);
    var startIdx = (head % nSlots : uint) : int;
    for offset in 0 .. #nSearchNodes {
      nodes[offset] = slots[(startIdx + offset) % nSlots].head.next;
    }

    var iterations = nElems;
    var nodeIdx = 0;
    while iterations > 0 {
      yield nodes[nodeIdx].elt;
      nodes[nodeIdx] = nodes[nodeIdx].next;
      nodeIdx = (nodeIdx + 1) % nSlots;
      iterations = iterations - 1;
    }
  }

  iter these(param tag : iterKind) where tag == iterKind.leader {
    coforall slot in getPrivatizedThis.slots do on slot do yield slot;
  }

  iter these(param tag : iterKind, followThis) where tag == iterKind.follower {
    var node = followThis.head.next;
    while node != nil {
      yield node.elt;
      node = node.next;
    }
  }

  proc ~DistributedQueue() {
    var localThis = getPrivatizedThis;
    for slot in localThis.slots do delete slot;
  }
}

proc main() {
  var dq = new DistributedQueue(int);

  for i in 1 .. 100 {
    dq.add(i);
  }
  dq.freeze();

  writeln(+ reduce dq);
}

use Collection.Queue;

class DistributedQueueSlotNode {
  type eltType;
  var elt : eltType;
  var next : DistributedQueueSlotNode(eltType);
}

/*
  For each slot we maintain a two-locked queue.
*/
class DistributedQueueSlot {
  type eltType;

  // Locks are atomic to allow us to remotely contest for the lock.
  var headLock$ : sync bool;
  var tailLock$ : sync bool;

  // Number of elements in this slot. If the count is negative, that means a 'dequeue'
  // has skipped over this, and as 'enqueue' we should as well to catch up.
  var size : atomic int;

  var head : DistributedQueueSlotNode(eltType);
  var tail : DistributedQueueSlotNode(eltType);

  proc DistributedQueueSlot(type eltType) {
    head = new DistributedQueueSlotNode(eltType);
    tail = head;
  }

  proc add(elt : eltType) {
    on this {
      var node = new DistributedQueueSlotNode(eltType, elt=elt);
      local {
        tailLock$ = true;
        tail.next = node;
        tail = node;
        tailLock$;
      }
    }
  }

  // Note, this should be *only* called after we know for a fact that there is an element
  // waiting for us...
  // TODO: Check performance using an 'on' statement...
  proc remove() : eltType {
    var elt : eltType;
    on this {
      var _elt : eltType;
      local {
        headLock$ = true;
        var node = head;
        var newHead = head.next;

        if newHead == nil {
          var barrier : atomic uint;
          while newHead == nil {
            chpl_task_yield();
            barrier.fetchAdd(1);
            newHead = head.next;
          }
        }

        head = newHead;
        _elt = newHead.elt;
        headLock$;
        delete node;
      }
      elt = _elt;
    }
    return elt;
  }

  proc ~DistributedQueueSlot() {
    on this {
      local {
        var curr = head;

        while curr != nil {
          var tmp = curr.next;
          delete curr;
          curr = tmp;
        }
      }
    }
  }
}

/*
  An unbounded queue that is distributed across nodes.
*/
class DistributedQueue : Queue {
  var targetLocDom : domain(1) = LocaleSpace;
  var targetLocales : [targetLocDom] locale = Locales;

  // Privatization id
  var pid : int;

  // Keeps track of which slot we are on...
  var globalHead : atomic uint;
  var globalTail : atomic uint;

  // To 'freeze' the queue, we must ensure that current mutating operations finish
  // first. However, at the same time we want to reduce communication by keeping
  // a task counter for each node that can be checked at next to no cost.
  var concurrentTasks : atomic uint;
  var frozenState : atomic bool;

  // We maintain an array of slots, wherein each slot is a pointer into a node's
  // address space. To maximize parallelism, we maintain numLocales * maxTaskPar
  // to reduce the amount of contention.
  var nSlots = here.maxTaskPar * targetLocales.size;
  var slotSpace = {0..#nSlots};
  var slots : [slotSpace] DistributedQueueSlot(eltType);

  proc DistributedQueue(type eltType, targetLocDom=LocaleSpace, targetLocales=Locales) {
    nSlots = here.maxTaskPar * targetLocales.size;
    slotSpace = {0..#nSlots};

    // Initialize each slot. We use a round-robin algorithm.
    var idx : atomic int;
    for 0 .. #here.maxTaskPar {
      for loc in targetLocales do on loc {
        var i = idx.fetchAdd(1);
        slots[i] = new DistributedQueueSlot(eltType);
      }
    }

    pid = _newPrivatizedClass(this);
  }

  proc DistributedQueue(other, privData, type eltType = other.eltType) {
    nSlots = other.nSlots;
    slotSpace = other.slotSpace;
    slots = privData;
  }

  proc dsiPrivatize(privData) {
      return new DistributedQueue(this, privData);
  }

  proc dsiGetPrivatizeData() {
    return slots;
  }

  inline proc getPrivatizedThis {
    return chpl_getPrivatizedCopy(this.type, pid);
  }

  proc add(elt : eltType) : bool {
    var localThis = getPrivatizedThis;
    ref _globalTail = globalTail;

    local {
      localThis.concurrentTasks.add(1);

      // Check if the queue is now 'immutable'.
      if localThis.frozenState.read() == true {
        localThis.concurrentTasks.sub(1);
        return false;
      }
    }

    var _nSlots : int;
    local { _nSlots = localThis.nSlots; }

    // Find a slot we can add to; skip over any with a negative size
    while true {
      var tail = (_globalTail.fetchAdd(1) % _nSlots : uint) : int;
      var slot : DistributedQueueSlot(eltType);
      local { slot = localThis.slots[tail]; }
      var sz = slot.size.fetchAdd(1);

      if sz >= 0 {
        slot.add(elt);
        local { localThis.concurrentTasks.sub(1); }
        return true;
      }
    }

    halt("Somehow broke out of add loop...");
  }

  proc remove() : (bool, eltType) {
    var localThis = getPrivatizedThis;

    local {
      localThis.concurrentTasks.add(1);

      // Check if the queue is now 'immutable'.
      if localThis.frozenState.read() == true {
        localThis.concurrentTasks.sub(1);
        return (false, _defaultOf(eltType));
      }
    }

    // Find a slot we can take from; if the slot is empty, we bail as it is empty.
    var head = (globalHead.fetchAdd(1) % localThis.nSlots : uint) : int;
    var slot : DistributedQueueSlot(eltType);
    local { slot = localThis.slots[head]; }
    var sz = slot.size.fetchSub(1);

    // Nothing for us...
    if sz < 1 {
      local { localThis.concurrentTasks.sub(1); }
      return (false, _defaultOf(eltType));
    }

    // There is something for us
    var elt = slot.remove();
    local { localThis.concurrentTasks.sub(1); }
    return (true, elt);
  }

  proc isFrozen() : bool {
    return getPrivatizedThis.frozenState.read();
  }

  proc freeze() {
    coforall loc in Locales do on loc {
      var localThis = getPrivatizedThis;
      localThis.frozenState.write(true);
      localThis.concurrentTasks.waitFor(0);
    }
  }

  proc unfreeze() {
    coforall loc in Locales do on loc {
      var localThis = getPrivatizedThis;
      localThis.frozenState.write(false);
    }
  }

  /*
    Iterate in FIFO order.

    TODO: Once the issue of serial iteration leaking state is fixed, a simple
    one-way channel implementation may prove very useful here to get an item
    in a round-robin manner.
  */
  iter these() : eltType {
    if !isFrozen() {
      halt("Iteration only supported while frozen...");
    }

    var localThis = getPrivatizedThis;

    // Fill our slots to visit in FIFO order.
    var head = globalHead.read();
    var tail = globalTail.read();

    // Check if empty...
    if head >= tail {
      return;
    }

    var nElems = tail - head;
    var nSearchNodes = min(nElems, nSlots) : int;
    var nodes : [{0..#nSearchNodes}] DistributedQueueSlotNode(eltType);
    var startIdx = (head % nSlots : uint) : int;
    for offset in 0 .. #nSearchNodes {
      nodes[offset] = slots[(startIdx + offset) % nSlots].head.next;
    }

    var iterations = nElems;
    var nodeIdx = 0;
    while iterations > 0 {
      yield nodes[nodeIdx].elt;
      nodes[nodeIdx] = nodes[nodeIdx].next;
      nodeIdx = (nodeIdx + 1) % nSlots;
      iterations = iterations - 1;
    }
  }

  iter these(param tag : iterKind) where tag == iterKind.leader {
    coforall slot in getPrivatizedThis.slots do on slot do yield slot;
  }

  iter these(param tag : iterKind, followThis) where tag == iterKind.follower {
    var node = followThis.head.next;
    while node != nil {
      yield node.elt;
      node = node.next;
    }
  }

  proc ~DistributedQueue() {
    var localThis = getPrivatizedThis;
    for slot in localThis.slots do delete slot;
  }
}

proc main() {
  var dq = new DistributedQueue(int);

  for i in 1 .. 100 {
    dq.add(i);
  }
  dq.freeze();

  forall elem in dq {
    writeln(elem);
  }
}


Final (small) edit to this, I hope.

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

use BitOps;
use Random;
use Time;

use HPCCProblemSize;

param radix = 4;

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

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

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

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

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

def main() {
  printConfiguration();

  const TwiddleDom = [0..m/4);
  var Twiddles: [TwiddleDom] elemType;

  const ProblemDom = [0..m);
  var Z, z: [ProblemDom] elemType;

  initVectors(Twiddles, z);

  const startTime = getCurrentTime();

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

  const execTime = getCurrentTime() - startTime;

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

def dfft(Z, Twiddles) {
  cft1st(Z, Twiddles);

  var span = radix;

  for i in [radix..n) by 2 {
    if (i <= n/2) then
      cftmd1(span, Z, Twiddles);
    else
      cftmd2(span, Z, Twiddles);
    span *= radix;
  }

  if (radix*span == Z.numElements) then
    forall j in [0..span) do
      butterfly(1.0, 1.0, 1.0, Z[j..j+3*span by span]);
  else
    forall j in [0..span) {
      const a = Z(j),
            b = Z(j+span);
      Z(j)      = a + b;
      Z(j+span) = a - b;
    }
}

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

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

  fillRandom(z, seed);

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

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

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

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

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

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

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

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

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

  return (maxerr < threshold);
}

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

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

  A(1) = x0 + x2;
  x0 -= x2;
  A(3) = wk2 * x0;
  x0 = x1 + x3rot;
  A(2) = wk1 * x0;
  x0 = x1 - x3rot;
  A(4) = wk3 * x0;
}

def cft1st(A, W) {
  var x0 = A(0) + A(1),
      x1 = A(0) - A(1),
      x2 = A(2) + A(3),
      x3rot = (A(2) - A(3))*1.0i;
  const wk1r = W(1).re;

  A(0) = x0 + x2;
  A(2) = x0 - x2;
  A(1) = x1 + x3rot;
  A(3) = x1 - x3rot;

  x0 = A(4) + A(5);
  x1 = A(4) - A(5);
  x2 = A(6) + A(7);
  const x3 = A(6) - A(7);
  A(4) = x0 + x2;
  A(6) = (x2.im - x0.im, x0.re - x2.re):complex;
  x0 = x1 + x3*1.0i;
  A(5) = wk1r * (x0.re - x0.im, x0.re + x0.im):complex;
  x0 = (x3.im + x1.re, x3.re - x1.im):complex;
  A(7) = wk1r * (x0.im - x0.re, x0.im + x0.re):complex;

  forall (j,k1) in ([8..A.numElements) by 8, 1..) {
    var wk2 = W(k1),
        wk1 = W(2*k1),
        wk3 = (wk1.re - 2* wk2.im * wk1.im,
               2 * wk2.im * wk1.re - wk1.im):complex;

    butterfly(wk1, wk2, wk3, A[j..j+3]);

    wk1 = W(2*k1+1);
    wk3 = (wk1.re - 2*wk2.re * wk1.im,
           2*wk2.re * wk1.re - wk1.im):complex;
    wk2 = wk2*1.0i;
    butterfly(wk1, wk2, wk3, A[j+4..j+7]);
  }
}

def cftmd0(span, A, W) {
  const wk1r = W(1).re,
        m = radix*span;

  forall j in [0..span) do
    butterfly(1.0, 1.0, 1.0, A[j..j+3*span by span]);

  forall j in [m..m+span) do
    butterfly((wk1r, wk1r):complex, 1.0i, (-wk1r, wk1r):complex,
              A[j..j+3*span by span]);
}

def cftmd1(span, A, W) {
  const m = radix*span,
        m2 = 2*m;

  cftmd0(span, A, W);
  forall (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = W(k1),
        wk1 = W(2*k1),
        wk3 = interpIm(wk1, wk2);
    for j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = W(2*k1+1);
    wk3 = interpRe(wk1, wk2);

    for j in [k+m..k+m+span) do
      butterfly(wk1, wk2*1.0i, wk3, A[j..j+3*span by span]);
  }
}

def cftmd2(span, A, W) {
  const m = radix*span,
        m2 = 2*m,
        numElems = A.numElements;

  cftmd0(span, A, W);
  if (m2 >= numElems) return;
  if (m2 >= numElems / 8) {
    cftmd21(span, A, W);
    return;
  }

  forall j in [0..span) {
    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = W(k1),
            wk1 = W(k1 + k1),
            wk3 = interpIm(wk1, wk2);
      butterfly(wk1, wk2, wk3, A[j+k..j+k+3*span by span]);
    }

    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = W(k1),
            wk1 = W(2*k1 + 1),
            wk3 = interpRe(wk1, wk2);
      wk2 = wk2*1.0i;

      butterfly(wk1, wk2, wk3, A[j+k+m..j+k+m+3*span by span]);
    }
  }
}

def cftmd21(span, A, W) {
  const m = radix*span,
        m2 = 2*m;

  for (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = W(k1),
        wk1 = W(2*k1),
        wk3 = interpIm(wk1, wk2);

    forall j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = W(2*k1 + 1);
    wk3 = interpRe(wk1, wk2);
    wk2 = wk2*1.0i;

    forall j in [k+m..k+m+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);
  }
}

def interpIm(a, b)
  return (a.re - 2*b.im*a.im, 2*b.im*a.re - a.im):complex;

def interpRe(a, b)
  return (a.re - 2*b.re*a.im, 2*b.re*a.re - a.im):complex;

use BitOps;
use Random;
use Time;

use HPCCProblemSize;

param radix = 4;

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

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

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

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

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

def main() {
  printConfiguration();

  const TwiddleDom = [0..m/4);
  var Twiddles: [TwiddleDom] elemType;

  const ProblemDom = [0..m);
  var Z, z: [ProblemDom] elemType;

  initVectors(Twiddles, z);

  const startTime = getCurrentTime();

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

  const execTime = getCurrentTime() - startTime;

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

def dfft(Z, Twiddles) {
  cft1st(Z, Twiddles);

  var span = radix;

  for i in [radix..n) by 2 {
    if (i <= n/2) then
      cftmd1(span, Z, Twiddles);
    else
      cftmd2(span, Z, Twiddles);
    span *= radix;
  }

  if (radix*span == Z.numElements) then
    forall j in [0..span) do
      butterfly(1.0, 1.0, 1.0, Z[j..j+3*span by span]);
  else
    forall j in [0..span) {
      const a = Z(j),
            b = Z(j+span);
      Z(j)      = a + b;
      Z(j+span) = a - b;
    }
}

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

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

  fillRandom(z, seed);

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

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

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

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

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

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

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

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

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

  return (maxerr < threshold);
}

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

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

  A(1) = x0 + x2;
  x0 -= x2;
  A(3) = wk2 * x0;
  x0 = x1 + x3rot;
  A(2) = wk1 * x0;
  x0 = x1 - x3rot;
  A(4) = wk3 * x0;
}

def cft1st(A, W) {
  var x0 = A(0) + A(1),
      x1 = A(0) - A(1),
      x2 = A(2) + A(3),
      x3rot = (A(2) - A(3))*1.0i;
  const wk1r = W(1).re;

  A(0) = x0 + x2;
  A(2) = x0 - x2;
  A(1) = x1 + x3rot;
  A(3) = x1 - x3rot;

  x0 = A(4) + A(5);
  x1 = A(4) - A(5);
  x2 = A(6) + A(7);
  const x3 = A(6) - A(7);  // BLC: try to eliminate this
  x3rot = (A(6) - A(7))*1.0i;
  A(4) = x0 + x2;
  A(6) = (x2.im - x0.im, x0.re - x2.re):complex;
  x0 = x1 + x3rot;
  A(5) = wk1r * (x0.re - x0.im, x0.re + x0.im):complex;
  x0 = (x3.im + x1.re, x3.re - x1.im):complex;
  A(7) = wk1r * (x0.im - x0.re, x0.im + x0.re):complex;

  forall (j,k1) in ([8..A.numElements) by 8, 1..) {
    var wk2 = W(k1),
        wk1 = W(2*k1),
        wk3 = (wk1.re - 2* wk2.im * wk1.im,
               2 * wk2.im * wk1.re - wk1.im):complex;

    butterfly(wk1, wk2, wk3, A[j..j+3]);

    wk1 = W(2*k1+1);
    wk3 = (wk1.re - 2*wk2.re * wk1.im,
           2*wk2.re * wk1.re - wk1.im):complex;
    wk2 = wk2*1.0i;
    butterfly(wk1, wk2, wk3, A[j+4..j+7]);
  }
}

def cftmd0(span, A, W) {
  const wk1r = W(1).re,
        m = radix*span;

  forall j in [0..span) do
    butterfly(1.0, 1.0, 1.0, A[j..j+3*span by span]);

  forall j in [m..m+span) do
    butterfly((wk1r, wk1r):complex, 1.0i, (-wk1r, wk1r):complex,
              A[j..j+3*span by span]);
}

def cftmd1(span, A, W) {
  const m = radix*span,
        m2 = 2*m;

  cftmd0(span, A, W);
  forall (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = W(k1),
        wk1 = W(2*k1),
        wk3 = interp1(wk1, wk2);
    for j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = W(2*k1+1);
    wk3 = interp2(wk1, wk2);

    for j in [k+m..k+m+span) do
      butterfly(wk1, wk2*1.0i, wk3, A[j..j+3*span by span]);
  }
}

def cftmd2(span, A, W) {
  const m = radix*span,
        m2 = 2*m,
        numElems = A.numElements;

  cftmd0(span, A, W);
  if (m2 >= numElems) return;
  if (m2 >= numElems / 8) {
    cftmd21(span, A, W);
    return;
  }

  forall j in [0..span) {
    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = W(k1),
            wk1 = W(k1 + k1),
            wk3 = interp1(wk1, wk2);
      butterfly(wk1, wk2, wk3, A[j+k..j+k+3*span by span]);
    }

    forall (k,k1) in ([m2..numElems) by m2, 1..) {
      const wk2 = W(k1),
            wk1 = W(2*k1 + 1),
            wk3 = interp2(wk1, wk2);
      wk2 = wk2*1.0i;

      butterfly(wk1, wk2, wk3, A[j+k+m..j+k+m+3*span by span]);
    }
  }
}

def cftmd21(span, A, W) {
  const m = radix*span,
        m2 = 2*m;

  for (k,k1) in ([m2..A.numElements) by m2, 1..) {
    var wk2 = W(k1),
        wk1 = W(2*k1),
        wk3 = interp1(wk1, wk2);

    forall j in [k..k+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);

    wk1 = W(2*k1 + 1);
    wk3 = interp2(wk1, wk2);
    wk2 = wk2*1.0i;

    forall j in [k+m..k+m+span) do
      butterfly(wk1, wk2, wk3, A[j..j+3*span by span]);
  }
}

def interp1(a, b)
  return (a.re - 2*b.im*a.im, 2*b.im*a.re - a.im):complex;

def interp2(a, b)
  return (a.re - 2*b.re*a.im, 2*b.re*a.re - a.im):complex;