Added : [] type specification for all array formal arguments in the norm example.

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

module Norm{
  enum normType {norm1, norm2, normInf, normFrob};

  def norm(x: [], p: normType) where x.rank == 1 {
    select (p) {
      when norm1 do return + reduce abs(x);
      when norm2 do return sqrt(+ reduce (abs(x)*abs(x)));
      when normInf do return max reduce abs(x);
      when normFrob do halt("From-norm not defined for 1D arrays");
      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x: [?D], p: normType) where x.rank == 2 {
    select (p) {
      when norm1 do
        return max reduce [j in D(2)] (+ reduce abs(x[D(1), j]));

      when norm2 do
        halt("Haven't implemented 2-norm for 2D arrays yet");

      when normInf do
        return max reduce [i in D(1)] (+ reduce abs(x[i, D(2)]));

      when normFrob do return sqrt(+ reduce (abs(x)*abs(x)));

      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x: [], p: normType) where x.rank > 2 {
    compilerError("Norms not implemented for array ranks > 2D");
  }

  def norm(x: []) {
    select (x.rank) {
      when 1 do return norm(x, norm2);
      when 2 do return norm(x, normFrob);
      otherwise compilerError("Norms not implemented for array ranks > 2D");
    }
  }
}

module TestNorm {
  use Norm;

  def testNorm(arr: []) {
    var testType = if (arr.rank == 1) then "vector" else "matrix";
    writeln("Test of ", testType, " norms.  Array = ");
    writeln(arr);
    writeln("1-norm = ", norm(arr, norm1));
    if (arr.rank == 1) then
      writeln("2-norm = " , norm(arr, norm2));
    writeln("infinity norm = ", norm(arr, normInf));
    if (arr.rank == 2) then
      writeln("frobenius norm = ", norm(arr, normFrob));
    writeln("default norm = ", norm(arr));
    writeln();
  }

  def main() {
    const D1 = [1..4];
    var a:[D1] real;
    a = 2.0;
    testNorm(a);

    const D2 = [1..2,1..2];
    var b:[D2] real;
    b = 2.0;
    testNorm(b);

    const D3 = [1..3,1..2];
    var c:[D3] real;
    c = 2.0;
    testNorm(c);

    var d:[D2] int;
    d = 1;
    testNorm(d);

    var e:[D2] complex;
    e = 1.0 + 1.0i;
    testNorm(e);

    var f: [1..2, 1..3, 1..4] real = 0.0;
    //    norm(f, norm1);
    //    norm(f);
  }
}

module Norm{
  enum normType {norm1, norm2, normInf, normFrob};

  def norm(x, p: normType) where x.rank == 1 {
    select (p) {
      when norm1 do return + reduce abs(x);
      when norm2 do return sqrt(+ reduce (abs(x)*abs(x)));
      when normInf do return max reduce abs(x);
      when normFrob do halt("From-norm not defined for 1D arrays");
      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x:[?D], p: normType) where x.rank == 2 {
    select (p) {
      when norm1 do
        return max reduce [j in D(2)] (+ reduce abs(x[D(1), j]));

      when norm2 do
        halt("Haven't implemented 2-norm for 2D arrays yet");

      when normInf do
        return max reduce [i in D(1)] (+ reduce abs(x[i, D(2)]));

      when normFrob do return sqrt(+ reduce (abs(x)*abs(x)));

      otherwise halt("Unexpected norm type");
    }
  }

  def norm(x, p: normType) where x.rank > 2 {
    compilerError("Norms not implemented for array ranks > 2D");
  }

  def norm(x) {
    select (x.rank) {
      when 1 do return norm(x, norm2);
      when 2 do return norm(x, normFrob);
      otherwise compilerError("Norms not implemented for array ranks > 2D");
    }
  }
}

module TestNorm {
  use Norm;

  def testNorm(arr) {
    var testType = if (arr.rank == 1) then "vector" else "matrix";
    writeln("Test of ", testType, " norms.  Array = ");
    writeln(arr);
    writeln("1-norm = ", norm(arr, norm1));
    if (arr.rank == 1) then
      writeln("2-norm = " , norm(arr, norm2));
    writeln("infinity norm = ", norm(arr, normInf));
    if (arr.rank == 2) then
      writeln("frobenius norm = ", norm(arr, normFrob));
    writeln("default norm = ", norm(arr));
    writeln();
  }

  def main() {
    const D1 = [1..4];
    var a:[D1] real;
    a = 2.0;
    testNorm(a);

    const D2 = [1..2,1..2];
    var b:[D2] real;
    b = 2.0;
    testNorm(b);

    const D3 = [1..3,1..2];
    var c:[D3] real;
    c = 2.0;
    testNorm(c);

    var d:[D2] int;
    d = 1;
    testNorm(d);

    var e:[D2] complex;
    e = 1.0 + 1.0i;
    testNorm(e);

    var f: [1..2, 1..3, 1..4] real = 0.0;
    //    norm(f, norm1);
    //    norm(f);
  }
}


Implemented concurrent iterators

Implemented concurrent iterators

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

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

  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();

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


Adding a missing word to one of the comments.

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

//
// Atomics primer
//
// This primer illustrates Chapel's atomic variables.  For more information
// on Chapel's atomics, see $CHPL_HOME/docs/technotes/README.atomics
//
config const n = 31;
const R = 1..n;

// The 'atomic' keyword is a type qualifiers that can be applied to
// the following Chapel primitive types to declare atomic variables:
//
// - bool
// - int (all supported sizes)
// - uint (all supported sizes)
// - real (all supported sizes)
//
// Atomic variables support a limited number of operations that are
// currently implemented as methods.
//

var x: atomic int;

// Atomic variables can only be used or assigned via the read() and
// write() methods, respectively.
//

x.write(n);
if x.read() != n then
  halt("Error: x (", x.read(), ") != n (", n, ")");

// All atomic types support atomic exchange(),
// compareExchangeStrong(), and compareExchangeWeak(),
// compareExchange() (same as compareExchangeStrong()).
//
// The exchange() method atomically swaps the old value with the given
// argument and returns the old value.  The compareExchange(),
// compareExchangeStrong() and compareExchangeWeak() methods only
// perform the swap if the current value is equal to the first argument,
// returning true if the exchange was performed  See README.atomics for
// the difference between the weak and strong variety.
//
// In the following example, n parallel tasks are created via a
// coforall statement.  Each task tries to set the current value of x
// id-1, but only one will succeed.
//
var numFound: atomic int;
coforall id in R {
  numFound.add(x.compareExchangeWeak(n, id-1));
}
if numFound.read() != 1 then
  halt("Error: numFound != 1 (", numFound.read(), ")");
if x.read() == n then
  halt("Error: x == n");

// As a convenience, atomic bools also support testAndSet() and clear().
// The testAndSet() method atomically reads the value of the variable, sets
// it to true, then returns the original value.  The clear() method
// atomically sets the value to false.
//
// In the following example, we create n tasks and each task calls
// testAndSet() on the atomic variable named flag and saves the
// result in a unique element of the result array.
//
var flag: atomic bool;
var result: [R] bool;
coforall r in result do
  r = flag.testAndSet();

// When the coforall is complete, only one of the above testAndSet()
// calls should have returned false.
//
var found = false;
for r in result {
  if !r then
    if found then
      halt("Error: found multiple times!");
    else
      found = true;
}
if !found then
  halt("Error: not found!");
flag.clear();

// The integral and real atomic types also support the following atomic
// fetch and non-fetching operations:
//
// - fetchAdd() and add()
// - fetchSub() and sub()
// - fetchOr() and or() (bit-wise) (integral types only)
// - fetchAnd() and and() (bit-wise) (integral types only)
//
// Each of the above atomically reads the variable, stores the result
// of the operation (+, -, |, or &) using the value and the method
// argument, then, for the fetchOps functions, returns the original
// value.
//
// In the following example, we create n tasks to atomically increment
// the atomic variable a with the square of the task's given id.
//
var a: atomic int;
coforall id in R do a.add(id*id);

// The sum of this finite series should be n(n+1)*(2n+1)/6
//
var expected = n*(n+1)*(2*n+1)/6;
if a.read() != expected then
  halt("Error: a=", a, " (should be ", expected, ")");

// In the following example, we create n tasks to atomically increment
// the atomic variable a with the square of the task's given id.  The
// sum of this finite series is n(n+1)*(2n+1)/6. If the returned value
// is the expected value less id*id (last task to arrive), check the
// results.
//
a.write(0);
const sumOfSq = n*(n+1)*(2*n+1)/6;
coforall id in R {
  const mySq = id*id;
  const last = a.fetchAdd(mySq);
  if sumOfSq-mySq == last {
    //
    const t = a.read();
    if t != n*(n+1)*(2*n+1)/6 then
      halt("Error: a=", t, " (should be ", sumOfSq, ") id=", id);
  }
}

//
// Atomics primer
//
// This primer illustrates Chapel's atomic variables.  For more information
// on Chapel's atomics, see $CHPL_HOME/docs/technotes/README.atomics
//
config const n = 31;
const R = 1..n;

// The 'atomic' keyword is a type qualifiers that can be applied to
// the following Chapel primitive types to declare atomic variables:
//
// - bool
// - int (all supported sizes)
// - uint (all supported sizes)
// - real (all supported sizes)
//
// Atomic variables support a limited number of operations that are
// currently implemented as methods.
//

var x: atomic int;

// Atomic variables can only be used or assigned via the read() and
// write() methods, respectively.
//

x.write(n);
if x.read() != n then
  halt("Error: x (", x.read(), ") != n (", n, ")");

// All atomic types support atomic exchange(),
// compareExchangeStrong(), and compareExchangeWeak(),
// compareExchange() (same as compareExchangeStrong()).
//
// The exchange() method atomically swaps the old value with the given
// argument and returns the old value.  The compareExchange(),
// compareExchangeStrong() and compareExchangeWeak() methods only
// perform the swap if the current value is equal to the first argument,
// returning true if the exchange was performed  See README.atomics for
// the difference between the weak and strong variety.
//
// In the following example, n parallel tasks are created via a
// coforall statement.  Each task tries to set the current value of x
// id-1, but only will succeed.
//
var numFound: atomic int;
coforall id in R {
  numFound.add(x.compareExchangeWeak(n, id-1));
}
if numFound.read() != 1 then
  halt("Error: numFound != 1 (", numFound.read(), ")");
if x.read() == n then
  halt("Error: x == n");

// As a convenience, atomic bools also support testAndSet() and clear().
// The testAndSet() method atomically reads the value of the variable, sets
// it to true, then returns the original value.  The clear() method
// atomically sets the value to false.
//
// In the following example, we create n tasks and each task calls
// testAndSet() on the atomic variable named flag and saves the
// result in a unique element of the result array.
//
var flag: atomic bool;
var result: [R] bool;
coforall r in result do
  r = flag.testAndSet();

// When the coforall is complete, only one of the above testAndSet()
// calls should have returned false.
//
var found = false;
for r in result {
  if !r then
    if found then
      halt("Error: found multiple times!");
    else
      found = true;
}
if !found then
  halt("Error: not found!");
flag.clear();

// The integral and real atomic types also support the following atomic
// fetch and non-fetching operations:
//
// - fetchAdd() and add()
// - fetchSub() and sub()
// - fetchOr() and or() (bit-wise) (integral types only)
// - fetchAnd() and and() (bit-wise) (integral types only)
//
// Each of the above atomically reads the variable, stores the result
// of the operation (+, -, |, or &) using the value and the method
// argument, then, for the fetchOps functions, returns the original
// value.
//
// In the following example, we create n tasks to atomically increment
// the atomic variable a with the square of the task's given id.
//
var a: atomic int;
coforall id in R do a.add(id*id);

// The sum of this finite series should be n(n+1)*(2n+1)/6
//
var expected = n*(n+1)*(2*n+1)/6;
if a.read() != expected then
  halt("Error: a=", a, " (should be ", expected, ")");

// In the following example, we create n tasks to atomically increment
// the atomic variable a with the square of the task's given id.  The
// sum of this finite series is n(n+1)*(2n+1)/6. If the returned value
// is the expected value less id*id (last task to arrive), check the
// results.
//
a.write(0);
const sumOfSq = n*(n+1)*(2*n+1)/6;
coforall id in R {
  const mySq = id*id;
  const last = a.fetchAdd(mySq);
  if sumOfSq-mySq == last {
    //
    const t = a.read();
    if t != n*(n+1)*(2*n+1)/6 then
      halt("Error: a=", t, " (should be ", sumOfSq, ") id=", id);
  }
}