Changed the %d string literal for reading in
an int from %lld to %d.  This code is still
really hacky and nonportable, though.  Need
to somehow abstract formatting strings to
be less architecture specific.

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

//
// Functions on _file primitive type, the C file pointer type
//

pragma "no codegen" var chpl_input_filename: string;
pragma "no codegen" var chpl_input_lineno: int;

pragma "inline" def _get_errno() return __primitive("get_errno");
pragma "inline" def _get_eof() return __primitive("get_eof");
pragma "inline" def _get_stdin() return __primitive("get_stdin");
pragma "inline" def _get_stdout() return __primitive("get_stdout");
pragma "inline" def _get_stderr() return __primitive("get_stderr");
pragma "inline" def _get_nullfile() return __primitive("get_nullfile");

pragma "inline" def _copy(x: _file) return x;
pragma "inline" def =(a: _file, b: _file) return b;
pragma "inline" def ==(a: _file, b: _file) return __primitive("==", a, b);
pragma "inline" def !=(a: _file, b: _file) return __primitive("!=", a, b);

pragma "inline" def _fopen(filename: string, mode: string)
  return __primitive("fopen", filename, mode);

pragma "inline" def _fclose(fp: _file)
  return __primitive("fclose", fp);

pragma "inline" def fprintf(fp: _file, fmt: string, val)
  return __primitive("fprintf", fp, fmt, val);

pragma "inline" def fscanf(fp: _file, fmt: string, inout val)
  return __primitive("fscanf", fp, fmt, val);

pragma "inline" def _string_fscanf(fp: _file)
  return __primitive("string_fscanf", fp);

pragma "inline" def _readLitChar(fp: _file, val: string, ignoreWhiteSpace: bool)
  return __primitive("readLit", fp, val, ignoreWhiteSpace);

pragma "rename _chpl_fflush"
def fflush(f: file) {
  fflush(f.fp);
}

pragma "rename _chpl_fflush_nil"
def fflush(f: _nilType) {
  halt("***Error: called fflush on nil***");
}

pragma "rename _chpl_assert_no_args"
def assert(test: bool) {
  if (!test) {
    _writeAssertFailed();
    if (!chpl_input_lineno) {
      fwriteln(stderr);
    }
    exit(0);
  }
}

pragma "rename _chpl_assert"
def assert(test: bool, args ...?numArgs) {
  if (!test) {
    _writeAssertFailed();
    if (!chpl_input_lineno) {
      fwrite(stderr, ": ");
    }
    for param i in 1..numArgs {
      fwrite(stderr, args(i));
    }
    fwriteln(stderr);
    exit(0);
  }
}

def _writeAssertFailed() {
  fflush(stdout);
  fwrite(stderr, "Assertion failed");
  if (chpl_input_lineno) {
    fwriteln(stderr, ": ", chpl_input_filename, ":", chpl_input_lineno);
  }
}

pragma "rename _chpl_halt_no_args"
def halt() {
  _writeHaltReached();
  if(!chpl_input_lineno) {
    fwriteln(stderr);
  }
  exit(0);
}

pragma "rename _chpl_halt"
def halt(args ...?numArgs) {
  _writeHaltReached();
  if (!chpl_input_lineno) {
    fwrite(stderr, ": ");
  }
  for param i in 1..numArgs {
    fwrite(stderr, args(i));
  }
  fwriteln(stderr);
  exit(0);
}

def _writeHaltReached() {
  fflush(stdout);
  fwrite(stderr, "Halt reached");
  if (chpl_input_lineno) {
    fwriteln(stderr, ": ", chpl_input_filename, ":", chpl_input_lineno);
  }
}

class file {
  var filename : string = "";
  var mode : string = "r";
  var path : string = ".";
  var fp : _file;
  var writeLock : sync uint(64);    // for serializing fwrite output

  def open {
    if (this == stdin || this == stdout || this == stderr) {
      halt("***Error: It is not necessary to open \"", filename, "\"***");
    }

    var fullFilename = path + "/" + filename;
    fp = _fopen(fullFilename, mode);

    if (fp == _get_nullfile()) {
      halt("***Error: Unable to open \"", fullFilename, "\": ",
           _get_errno(), "***");
    }
  }

  def _checkFileStateChangeLegality(state) {
    if (isOpen) {
      halt("Cannot change ", state, " of file ", path, "/", filename,
           " while it is open");
    }
  }

  def filename {
    return filename;
  }

  def =filename(newfilename) {
    _checkFileStateChangeLegality("filename");
    filename = newfilename;
  }

  def path {
    return path;
  }

  def =path(newpath) {
    _checkFileStateChangeLegality("path");
    path = newpath;
  }

  def mode {
    return mode;
  }

  def =mode(newmode: string) {
    _checkFileStateChangeLegality("mode");
    mode = newmode;
  }

  def isOpen: bool {
    var openStatus: bool = false;
    if (fp != _get_nullfile()) {
      openStatus = true;
    }
    return openStatus;
  }

  def close {
    if (this == stdin || this == stdout || this == stderr) {
      halt("***Error: You may not close \"", filename, "\"***");
    }
    if (fp == _get_nullfile()) {
      var fullFilename = path + "/" + filename;
      halt("***Error: Trying to close \"", fullFilename,
           "\" which isn't open***");
    }
    var returnVal: int = _fclose(fp);
    if (returnVal < 0) {
      var fullFilename = path + "/" + filename;
      halt("***Error: The close of \"", fullFilename, "\" failed: ",
           _get_errno(), "***");
    }
    fp = _get_nullfile();
  }
}

const stdin  = file("stdin", "r", "/dev", _get_stdin());
const stdout = file("stdout", "w", "/dev", _get_stdout());
const stderr = file("stderr", "w", "/dev", _get_stderr());

def _fopenError(f: file, isRead: bool) {
  var fullFilename:string = f.path + "/" + f.filename;
  if (isRead) {
    halt("***Error: You must open \"", fullFilename,
         "\" before trying to read from it***");
  } else {
    halt("***Error: You must open \"", fullFilename,
         "\" before trying to write to it***");
  }
}

def _fmodeError(f: file, isRead: bool) {
  var fullFilename: string = f.path + "/" + f.filename;
  var modestring = if (isRead) then "reading" else "writing";
  halt("***Error: ", fullFilename, " not open for ", modestring, "***");
}

def _fprintfError() {
  halt("***Error: Write failed: ", _get_errno(), "***");
}

def _fscanfError() {
  halt("***Error: Read failed: ", _get_errno(), "***");
}

pragma "rename _chpl_fread_int"
def fread(f: file = stdin, inout val: int) {
  if (f.isOpen) {
    var returnVal: int = fscanf(f.fp, "%d", val);
    if (returnVal == _get_eof()) {
      halt("***Error: Read failed: EOF***");
    }
    if (returnVal < 0) {
      _fscanfError();
    } else if (returnVal == 0) {
      halt("***Error: No int was read***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

def _fwrite_lock( f: file) {
  var me: uint(64) = __primitive( "thread_id");
  if (isFull( f.writeLock)) {
    if (readXX( f.writeLock) == me) {
      return false;
    }
  }
  f.writeLock = me;
  return true;
}

def _fwrite_unlock( f: file) {
  writeFE( f.writeLock, 0);  // writeXE works also as it should be full before
}

pragma "rename _chpl_fread_uint"
def fread(f: file = stdin, inout val: uint) {
  if (f.isOpen) {
    var returnVal: int = fscanf(f.fp, "%llu", val);
    if (returnVal == _get_eof()) {
      halt("***Error: Read failed: EOF***");
    }
    if (returnVal < 0) {
      _fscanfError();
    } else if (returnVal == 0) {
      halt("***Error: No int was read***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fread_real"
def fread(f: file = stdin, inout val: real) {
  if (f.isOpen) {
    var returnVal: int = fscanf(f.fp, "%lg", val);
    if (returnVal == _get_eof()) {
      halt("***Error: Read failed: EOF***");
    }
    if (returnVal < 0) {
      _fscanfError();
    } else if (returnVal == 0) {
      halt("***Error: No real was read***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fread_complex"
def fread(f: file = stdin, inout val: complex) {
  var realPart: real;
  var imagPart: real;
  var imagI: string;
  var matchingCharWasRead: int;
  var isNeg: bool;

  fread(f, realPart);
  matchingCharWasRead = _readLitChar(f.fp, "+", true);
  if (matchingCharWasRead != 1) {
    matchingCharWasRead = _readLitChar(f.fp, "-", true);
    if (matchingCharWasRead != 1) {
      halt("***Error: Incorrect format for complex numbers***");
    }
    isNeg = true;
  }

  fread(f, imagPart);
  matchingCharWasRead = _readLitChar(f.fp, "i", false);
  if (matchingCharWasRead != 1) {
    halt("***Error: Incorrect format for complex numbers***");
  }

  val.re = realPart;
  if (isNeg) {
    val.im = -imagPart;
  } else {
    val.im = imagPart;
  }
}

pragma "rename _chpl_fread_string"
def fread(f: file = stdin, inout val: string) {
  if (f.isOpen) {
    val = _string_fscanf(f.fp);
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fread_bool"
def fread(f: file = stdin, inout val: bool) {
  if (f.isOpen) {
    var valString : string = _string_fscanf(f.fp);
    if (valString == "true") {
      val = true;
    } else if (valString == "false") {
      val = false;
    } else {
      halt("***Error: Not of bool type***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fwrite_string"
def fwrite(f: file, val: string) {
  var need_release: bool = _fwrite_lock( f);

  if (!f.isOpen) {
    _fopenError(f, isRead = false);
  }
  if (f.mode != "w") {
    _fmodeError(f, isRead = false);
  }
  var returnVal = fprintf(f.fp, "%s", val);
  if (returnVal < 0) {
    _fprintfError();
  }

  if (need_release) { _fwrite_unlock( f); }
}

pragma "rename _chpl_fwrite_nil"
def fwrite(f: file, x : _nilType) : void {
  var need_release: bool = _fwrite_lock( f);

  if (f.isOpen) {
    var returnVal: int = fprintf(f.fp, "%s", "nil");
    if (returnVal < 0) {
      _fprintfError();
    } else if (returnVal == 0) {
      halt("*** Error: No value was written***");
    }
  } else {
    _fopenError(f, isRead = false);
  }
  if (need_release) { _fwrite_unlock( f); }
}

def fwrite(f: file, args ...?numArgs) {
  var need_release: bool = _fwrite_lock( f);
  for param i in 1..numArgs {
    fwrite(f, args(i));
  }
  if (need_release) { _fwrite_unlock( f); }
}

def fwriteln(f: file, args ...?numArgs) {
  var need_release: bool = _fwrite_lock( f);
  fwrite(f, (...args));
  fwriteln(f);
  if (need_release) { _fwrite_unlock( f); }
}

def fwriteln(f: file) {
  var need_release: bool = _fwrite_lock( f);
  fwrite(f, "\n");
  if (need_release) { _fwrite_unlock( f); }
}

def write(args ...?numArgs) {
  fwrite(stdout, (...args));
}

def writeln(args ...?numArgs) {
  fwriteln(stdout, (...args));
}

def writeln() {
  fwriteln(stdout);
}

//
// Functions on _file primitive type, the C file pointer type
//

pragma "no codegen" var chpl_input_filename: string;
pragma "no codegen" var chpl_input_lineno: int;

pragma "inline" def _get_errno() return __primitive("get_errno");
pragma "inline" def _get_eof() return __primitive("get_eof");
pragma "inline" def _get_stdin() return __primitive("get_stdin");
pragma "inline" def _get_stdout() return __primitive("get_stdout");
pragma "inline" def _get_stderr() return __primitive("get_stderr");
pragma "inline" def _get_nullfile() return __primitive("get_nullfile");

pragma "inline" def _copy(x: _file) return x;
pragma "inline" def =(a: _file, b: _file) return b;
pragma "inline" def ==(a: _file, b: _file) return __primitive("==", a, b);
pragma "inline" def !=(a: _file, b: _file) return __primitive("!=", a, b);

pragma "inline" def _fopen(filename: string, mode: string)
  return __primitive("fopen", filename, mode);

pragma "inline" def _fclose(fp: _file)
  return __primitive("fclose", fp);

pragma "inline" def fprintf(fp: _file, fmt: string, val)
  return __primitive("fprintf", fp, fmt, val);

pragma "inline" def fscanf(fp: _file, fmt: string, inout val)
  return __primitive("fscanf", fp, fmt, val);

pragma "inline" def _string_fscanf(fp: _file)
  return __primitive("string_fscanf", fp);

pragma "inline" def _readLitChar(fp: _file, val: string, ignoreWhiteSpace: bool)
  return __primitive("readLit", fp, val, ignoreWhiteSpace);

pragma "rename _chpl_fflush"
def fflush(f: file) {
  fflush(f.fp);
}

pragma "rename _chpl_fflush_nil"
def fflush(f: _nilType) {
  halt("***Error: called fflush on nil***");
}

pragma "rename _chpl_assert_no_args"
def assert(test: bool) {
  if (!test) {
    _writeAssertFailed();
    if (!chpl_input_lineno) {
      fwriteln(stderr);
    }
    exit(0);
  }
}

pragma "rename _chpl_assert"
def assert(test: bool, args ...?numArgs) {
  if (!test) {
    _writeAssertFailed();
    if (!chpl_input_lineno) {
      fwrite(stderr, ": ");
    }
    for param i in 1..numArgs {
      fwrite(stderr, args(i));
    }
    fwriteln(stderr);
    exit(0);
  }
}

def _writeAssertFailed() {
  fflush(stdout);
  fwrite(stderr, "Assertion failed");
  if (chpl_input_lineno) {
    fwriteln(stderr, ": ", chpl_input_filename, ":", chpl_input_lineno);
  }
}

pragma "rename _chpl_halt_no_args"
def halt() {
  _writeHaltReached();
  if(!chpl_input_lineno) {
    fwriteln(stderr);
  }
  exit(0);
}

pragma "rename _chpl_halt"
def halt(args ...?numArgs) {
  _writeHaltReached();
  if (!chpl_input_lineno) {
    fwrite(stderr, ": ");
  }
  for param i in 1..numArgs {
    fwrite(stderr, args(i));
  }
  fwriteln(stderr);
  exit(0);
}

def _writeHaltReached() {
  fflush(stdout);
  fwrite(stderr, "Halt reached");
  if (chpl_input_lineno) {
    fwriteln(stderr, ": ", chpl_input_filename, ":", chpl_input_lineno);
  }
}

class file {
  var filename : string = "";
  var mode : string = "r";
  var path : string = ".";
  var fp : _file;
  var writeLock : sync uint(64);    // for serializing fwrite output

  def open {
    if (this == stdin || this == stdout || this == stderr) {
      halt("***Error: It is not necessary to open \"", filename, "\"***");
    }

    var fullFilename = path + "/" + filename;
    fp = _fopen(fullFilename, mode);

    if (fp == _get_nullfile()) {
      halt("***Error: Unable to open \"", fullFilename, "\": ",
           _get_errno(), "***");
    }
  }

  def _checkFileStateChangeLegality(state) {
    if (isOpen) {
      halt("Cannot change ", state, " of file ", path, "/", filename,
           " while it is open");
    }
  }

  def filename {
    return filename;
  }

  def =filename(newfilename) {
    _checkFileStateChangeLegality("filename");
    filename = newfilename;
  }

  def path {
    return path;
  }

  def =path(newpath) {
    _checkFileStateChangeLegality("path");
    path = newpath;
  }

  def mode {
    return mode;
  }

  def =mode(newmode: string) {
    _checkFileStateChangeLegality("mode");
    mode = newmode;
  }

  def isOpen: bool {
    var openStatus: bool = false;
    if (fp != _get_nullfile()) {
      openStatus = true;
    }
    return openStatus;
  }

  def close {
    if (this == stdin || this == stdout || this == stderr) {
      halt("***Error: You may not close \"", filename, "\"***");
    }
    if (fp == _get_nullfile()) {
      var fullFilename = path + "/" + filename;
      halt("***Error: Trying to close \"", fullFilename,
           "\" which isn't open***");
    }
    var returnVal: int = _fclose(fp);
    if (returnVal < 0) {
      var fullFilename = path + "/" + filename;
      halt("***Error: The close of \"", fullFilename, "\" failed: ",
           _get_errno(), "***");
    }
    fp = _get_nullfile();
  }
}

const stdin  = file("stdin", "r", "/dev", _get_stdin());
const stdout = file("stdout", "w", "/dev", _get_stdout());
const stderr = file("stderr", "w", "/dev", _get_stderr());

def _fopenError(f: file, isRead: bool) {
  var fullFilename:string = f.path + "/" + f.filename;
  if (isRead) {
    halt("***Error: You must open \"", fullFilename,
         "\" before trying to read from it***");
  } else {
    halt("***Error: You must open \"", fullFilename,
         "\" before trying to write to it***");
  }
}

def _fmodeError(f: file, isRead: bool) {
  var fullFilename: string = f.path + "/" + f.filename;
  var modestring = if (isRead) then "reading" else "writing";
  halt("***Error: ", fullFilename, " not open for ", modestring, "***");
}

def _fprintfError() {
  halt("***Error: Write failed: ", _get_errno(), "***");
}

def _fscanfError() {
  halt("***Error: Read failed: ", _get_errno(), "***");
}

pragma "rename _chpl_fread_int"
def fread(f: file = stdin, inout val: int) {
  if (f.isOpen) {
    var returnVal: int = fscanf(f.fp, "%lld", val);
    if (returnVal == _get_eof()) {
      halt("***Error: Read failed: EOF***");
    }
    if (returnVal < 0) {
      _fscanfError();
    } else if (returnVal == 0) {
      halt("***Error: No int was read***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

def _fwrite_lock( f: file) {
  var me: uint(64) = __primitive( "thread_id");
  if (isFull( f.writeLock)) {
    if (readXX( f.writeLock) == me) {
      return false;
    }
  }
  f.writeLock = me;
  return true;
}

def _fwrite_unlock( f: file) {
  writeFE( f.writeLock, 0);  // writeXE works also as it should be full before
}

pragma "rename _chpl_fread_uint"
def fread(f: file = stdin, inout val: uint) {
  if (f.isOpen) {
    var returnVal: int = fscanf(f.fp, "%llu", val);
    if (returnVal == _get_eof()) {
      halt("***Error: Read failed: EOF***");
    }
    if (returnVal < 0) {
      _fscanfError();
    } else if (returnVal == 0) {
      halt("***Error: No int was read***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fread_real"
def fread(f: file = stdin, inout val: real) {
  if (f.isOpen) {
    var returnVal: int = fscanf(f.fp, "%lg", val);
    if (returnVal == _get_eof()) {
      halt("***Error: Read failed: EOF***");
    }
    if (returnVal < 0) {
      _fscanfError();
    } else if (returnVal == 0) {
      halt("***Error: No real was read***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fread_complex"
def fread(f: file = stdin, inout val: complex) {
  var realPart: real;
  var imagPart: real;
  var imagI: string;
  var matchingCharWasRead: int;
  var isNeg: bool;

  fread(f, realPart);
  matchingCharWasRead = _readLitChar(f.fp, "+", true);
  if (matchingCharWasRead != 1) {
    matchingCharWasRead = _readLitChar(f.fp, "-", true);
    if (matchingCharWasRead != 1) {
      halt("***Error: Incorrect format for complex numbers***");
    }
    isNeg = true;
  }

  fread(f, imagPart);
  matchingCharWasRead = _readLitChar(f.fp, "i", false);
  if (matchingCharWasRead != 1) {
    halt("***Error: Incorrect format for complex numbers***");
  }

  val.re = realPart;
  if (isNeg) {
    val.im = -imagPart;
  } else {
    val.im = imagPart;
  }
}

pragma "rename _chpl_fread_string"
def fread(f: file = stdin, inout val: string) {
  if (f.isOpen) {
    val = _string_fscanf(f.fp);
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fread_bool"
def fread(f: file = stdin, inout val: bool) {
  if (f.isOpen) {
    var valString : string = _string_fscanf(f.fp);
    if (valString == "true") {
      val = true;
    } else if (valString == "false") {
      val = false;
    } else {
      halt("***Error: Not of bool type***");
    }
  } else {
    _fopenError(f, isRead = true);
  }
}

pragma "rename _chpl_fwrite_string"
def fwrite(f: file, val: string) {
  var need_release: bool = _fwrite_lock( f);

  if (!f.isOpen) {
    _fopenError(f, isRead = false);
  }
  if (f.mode != "w") {
    _fmodeError(f, isRead = false);
  }
  var returnVal = fprintf(f.fp, "%s", val);
  if (returnVal < 0) {
    _fprintfError();
  }

  if (need_release) { _fwrite_unlock( f); }
}

pragma "rename _chpl_fwrite_nil"
def fwrite(f: file, x : _nilType) : void {
  var need_release: bool = _fwrite_lock( f);

  if (f.isOpen) {
    var returnVal: int = fprintf(f.fp, "%s", "nil");
    if (returnVal < 0) {
      _fprintfError();
    } else if (returnVal == 0) {
      halt("*** Error: No value was written***");
    }
  } else {
    _fopenError(f, isRead = false);
  }
  if (need_release) { _fwrite_unlock( f); }
}

def fwrite(f: file, args ...?numArgs) {
  var need_release: bool = _fwrite_lock( f);
  for param i in 1..numArgs {
    fwrite(f, args(i));
  }
  if (need_release) { _fwrite_unlock( f); }
}

def fwriteln(f: file, args ...?numArgs) {
  var need_release: bool = _fwrite_lock( f);
  fwrite(f, (...args));
  fwriteln(f);
  if (need_release) { _fwrite_unlock( f); }
}

def fwriteln(f: file) {
  var need_release: bool = _fwrite_lock( f);
  fwrite(f, "\n");
  if (need_release) { _fwrite_unlock( f); }
}

def write(args ...?numArgs) {
  fwrite(stdout, (...args));
}

def writeln(args ...?numArgs) {
  fwriteln(stdout, (...args));
}

def writeln() {
  fwriteln(stdout);
}


More chameneos cleanup

More chameneos cleanup

* renamed is_same to avoid underscore

* switched to the same nested select statement to determine color
  as Lydia did in release (happily, it seems to improve performance
  slightly?)

* removed dead TODOs

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

/* The Computer Language Benchmarks Game
   http://benchmarksgame.alioth.debian.org/

   contributed by Hannah Hemmaplardh
   modified by Lydia Duncan and Brad Chamberlain
*/

//
// the following params indicate the number of bits to use when storing
// IDs in a meeting place's 'state' and the corresponding bitmask
// to use when extracting chameneos IDs, respectively.
//
config param NUM_CHAMENEOS_ID_BITS = 8;  // # of state bits to use for IDs
param CHAMENEOS_ID_MASK = (0x1 << NUM_CHAMENEOS_ID_BITS) - 1;

config const n = 100,                // number of meetings to take place
             numChameneos1 = 3,      // size of population 1
             numChameneos2 = 10;     // size of population 2

if (numChameneos1 < 2 || numChameneos2 < 2) then
  halt("There must be at least 2 chameneos");

if (n < 0) then
  halt("the number of meetings must be non-negative");

//
// the colors the chameneos can take on
//
param numColors = 3;
enum Color {blue=0, red, yellow};
//
// TODO: Would be cool to support a param .size query for enums to
// avoid the whole C "declare one extra enum for the number" hack and
// then we could do param numColors = Color.size; (or, if not size,
// some other query function).
//

proc main() {
  printColorEquations();

  const population1 = populate(numChameneos1),
        population2 = populate(numChameneos2);

  run(population1);
  run(population2);
}

//
// populate() creates an array of chameneos of the given size.  If the
// size is 10, it uses the predefined array of colors; otherwise, it
// deals out colors round-robin.
//
//
// TODO: Currently, the compiler prints out an error if we remove the
// return type from here; seems it would be nicer if it just put in
// the array temp?  (unless we think we want to return unevaluated
// iterators, which we may?  But even then, maybe in the meantime, we
// should realize it?)
//
proc populate(size): [1..size] Chameneos {
  //
  // TODO: Really want support for 'use Color' for cases like this
  //
  const colorsFor10 = [Color.blue, Color.red, Color.yellow, Color.red,
                       Color.yellow, Color.blue, Color.red, Color.yellow,
                       Color.red, Color.blue];

  return [i in 1..size] new Chameneos(i, if (size == 10) then colorsFor10[i]
                                                         else ((i-1)%3):Color);
}

//
// run() takes a population of chameneos and a MeetingPlace, has the
// chameneos print their initial colors, then allows the them to meet
// in parallel.
//
proc run(population) {
  //
  // create a meeting place
  //
  const meetingPlace = new MeetingPlace(n);

  //
  // print the colors of the initial population
  //
  for i in population do
    write(" ", i.color);
  writeln();

  //
  // fire off a task per chameneos, and have them try to meet
  //
  coforall i in population do
    i.tryToMeet(meetingPlace, population);

  printInfo(population);
}

//
// This class represents a place for chameneos to meet
//
class MeetingPlace {
  //
  // the 'state' variable packs a chameneos ID in the bottom
  // NUM_CHAMENEOS_ID_BITS bits and the number of meetings in the
  // upper ones, permitting atomic operations to be performed on both
  // values simultaneously.
  //
  var state: atomic int;

  //
  // constructor initializes the number of meetings to take place
  //
  proc MeetingPlace(numMeetings) {
    state.write(numMeetings << NUM_CHAMENEOS_ID_BITS);
  }
}

//
// a class representing a chameneos
//
class Chameneos {
  const id: int;                       // its unique ID
  var color: Color;                    // its color
  var meetings,                        // the number of meetings it's had
      meetingsWithSelf: int;           // the number of meetings with itself
  var meetingCompleted: atomic bool;   // indicates that a meeting is over

  //
  // tryToMeet() tells a chameneos to go to a given MeetingPlace,
  // and try to get involved in meetings with the rest of the
  // population.  If it is first, it can try to wait for another
  // chameneos to arrive; if it is second, it can try to meet with the
  // first chameneos.  The word 'try' is used because other chameneos
  // may be trying to do the same thing simultaneously and beat it to
  // the action.
  //
  proc tryToMeet(meetingPlace, population) {
    //
    // keep trying to meet, as long as there are meetings remaining
    //
    do {
      //
      // grab a copy of the current state
      //
      const currentState = meetingPlace.state.read(memory_order_acquire);

      //
      // if the current state is non-empty, meetings remain
      //
      if (currentState) {
        //
        // extract the number of remaining meetings and the other
        // chameneos's index (if one is present) from the state
        //
        const meetingsLeft = currentState >> NUM_CHAMENEOS_ID_BITS,
              peer_idx = currentState & CHAMENEOS_ID_MASK;

        var newState: int;  // we'll store the new state here

        if (peer_idx) {
          //
          // if another chameneos was waiting in the meeting place, we
          // will hope to meet with it; if we can, the resulting state
          // would have one fewer meetings and no waiting chameneos.
          //
          newState = (meetingsLeft - 1) << NUM_CHAMENEOS_ID_BITS;
        } else {
          //
          // if no other chameneos was waiting, we will hope to enter
          // the meeting place; if we can, the resulting state would
          // have the same number of meetings and our ID stored in the
          // low bits.
          //
          newState = currentState | id;
        }

        //
        // use a CAS to see if the state remains the same, and to swap
        // in the new state we've computed if it is.
        //
        if (meetingPlace.state.compareExchangeStrong(currentState, newState,
                                                     memory_order_acq_rel)) {
          //
          // We get to meet!
          //
          if (peer_idx) {
            //
            // If we were the second chameneos in, we run the meeting
            //
            runMeeting(population, peer_idx);
          } else {
            //
            // If we were the first chameneos in, we wait for the
            // other to complete the meeting
            //
            meetingCompleted.waitFor(true);
            //
            // and then reset our meetingCompleted flag for next time
            //
            meetingCompleted.write(false, memory_order_release);
          }
        }
      }
      //
      // continue looping as long as meetings remain
      //
    } while (currentState);
  }

  //
  // runMeeting() computes the meeting itself between two chameneos,
  // by the second to enter the one that is 'this'
  //
  proc runMeeting(population, peer_idx) {
    //
    // If we meet with ourself, something's wrong, so check for that
    //
    const metSelf = (id == peer_idx);

    //
    // get the peer that we're meeting with and compute the new color
    // that we'll share as a function of our current colors
    //
    const peer = population[peer_idx];
    const newColor = getNewColor(color, peer.color);

    //
    // Update our peer's state: store the new color, update its
    // meetings, and then tell it that the meeting's over
    //
    //
    // TODO: abstract the following two assignments into a method to
    // avoid repetition?  Or is that more trouble than it's worth?
    //
    peer.color = newColor;
    peer.meetings += 1;
    peer.meetingsWithSelf += metSelf;
    peer.meetingCompleted.write(true, memory_order_release);

    //
    // Then update ourself similarly
    //
    color = newColor;
    meetings += 1;
    meetingsWithSelf += metSelf;
  }
}

//
// getNewColor() returns the complement of two colors; if the two colors
// are the same value, the color stays the same; otherwise, it becomes the
// third color.
//
inline proc getNewColor(myColor, otherColor) {
  select myColor {
    when Color.blue {
      select otherColor {
        when Color.blue do return Color.blue;
        when Color.red  do return Color.yellow;
        otherwise          return Color.red;
      }
    }
    when Color.red {
      select otherColor {
        when Color.blue do return Color.yellow;
        when Color.red  do return Color.red;
        otherwise          return Color.blue;
      }
    }
    otherwise {
      select otherColor {
        when Color.blue do return Color.red;
        when Color.red  do return Color.blue;
        otherwise          return Color.yellow;
      }
    }
  }
}

//
// printColorEquations() prints the result of getNewColor() for all
// pairs of colors
//
proc printColorEquations() {
  const colors = [Color.blue, Color.red, Color.yellow];
  for color1 in colors {
    for color2 in colors {
      writeln(color1, " + ", color2, " -> ", getNewColor(color1, color2));
    }
  }
  writeln();
}

//
// printInfo() prints out information about the population using
// a combination of numbers and English text
//
proc printInfo(population) {
  //
  // for each chameneos, print the number of meetings and spell out
  // the number of self-meetings for each chameneos
  //
  for c in population {
    write(c.meetings);
    spellInt(c.meetingsWithSelf);
  }

  //
  // compute the total number of meetings and spell it out
  //
  const totalMeetings = + reduce population.meetings;
  spellInt(totalMeetings);
  writeln();
}

//
// the base-10 digits
//
// TODO: Would be nice to be able to make this local?
//
enum Digit {zero=0, one, two, three, four, five, six, seven, eight, nine};

//
// spellInt() takes out an integer value and prints its digits out
// in English
//
proc spellInt(n) {
  //
  // Cast n to a string so that it's easier (?!) to pick out its characters
  //
  var s = n:string;
  //
  // TODO: would be nice to replace this with a character iterator?
  //
  for i in 1..s.length do
    write(" ", (s.substring(i):int):Digit);
  writeln();
}

/* The Computer Language Benchmarks Game
   http://benchmarksgame.alioth.debian.org/

   contributed by Hannah Hemmaplardh
   modified by Lydia Duncan and Brad Chamberlain
*/

//
// the following params indicate the number of bits to use when storing
// IDs in a meeting place's 'state' and the corresponding bitmask
// to use when extracting chameneos IDs, respectively.
//
config param NUM_CHAMENEOS_ID_BITS = 8;  // # of state bits to use for IDs
param CHAMENEOS_ID_MASK = (0x1 << NUM_CHAMENEOS_ID_BITS) - 1;

config const n = 100,                // number of meetings to take place
             numChameneos1 = 3,      // size of population 1
             numChameneos2 = 10;     // size of population 2

if (numChameneos1 < 2 || numChameneos2 < 2) then
  halt("There must be at least 2 chameneos");

if (n < 0) then
  halt("the number of meetings must be non-negative");

//
// the colors the chameneos can take on
//
param numColors = 3;
enum Color {blue=0, red, yellow};
//
// TODO: Would be cool to support a param .size query for enums to
// avoid the whole C "declare one extra enum for the number" hack and
// then we could do param numColors = Color.size; (or, if not size,
// some other query function).
//

proc main() {
  printColorEquations();

  const population1 = populate(numChameneos1),
        population2 = populate(numChameneos2);

  run(population1);
  run(population2);
}

//
// populate() creates an array of chameneos of the given size.  If the
// size is 10, it uses the predefined array of colors; otherwise, it
// deals out colors round-robin.
//
//
// TODO: Currently, the compiler prints out an error if we remove the
// return type from here; seems it would be nicer if it just put in
// the array temp?  (unless we think we want to return unevaluated
// iterators, which we may?  But even then, maybe in the meantime, we
// should realize it?)
//
proc populate(size): [1..size] Chameneos {
  //
  // TODO: Really want support for 'use Color' for cases like this
  //
  const colorsFor10 = [Color.blue, Color.red, Color.yellow, Color.red,
                       Color.yellow, Color.blue, Color.red, Color.yellow,
                       Color.red, Color.blue];

  return [i in 1..size] new Chameneos(i, if (size == 10) then colorsFor10[i]
                                                         else ((i-1)%3):Color);
}

//
// run() takes a population of chameneos and a MeetingPlace, has the
// chameneos print their initial colors, then allows the them to meet
// in parallel.
//
proc run(population) {
  //
  // create a meeting place
  //
  const meetingPlace = new MeetingPlace(n);

  //
  // print the colors of the initial population
  //
  for i in population do
    write(" ", i.color);
  writeln();

  //
  // fire off a task per chameneos, and have them try to meet
  //
  coforall i in population do
    i.tryToMeet(meetingPlace, population);

  printInfo(population);
}

//
// This class represents a place for chameneos to meet
//
class MeetingPlace {
  //
  // the 'state' variable packs a chameneos ID in the bottom
  // NUM_CHAMENEOS_ID_BITS bits and the number of meetings in the
  // upper ones, permitting atomic operations to be performed on both
  // values simultaneously.
  //
  var state: atomic int;

  //
  // constructor initializes the number of meetings to take place
  //
  proc MeetingPlace(numMeetings) {
    state.write(numMeetings << NUM_CHAMENEOS_ID_BITS);
  }
}

//
// a class representing a chameneos
//
class Chameneos {
  const id: int;                       // its unique ID
  var color: Color;                    // its color
  var meetings,                        // the number of meetings it's had
      meetingsWithSelf: int;           // the number of meetings with itself
                                       // TODO: Is this really needed?
  var meetingCompleted: atomic bool;   // indicates that a meeting is over

  //
  // tryToMeet() tells a chameneos to go to a given MeetingPlace,
  // and try to get involved in meetings with the rest of the
  // population.  If it is first, it can try to wait for another
  // chameneos to arrive; if it is second, it can try to meet with the
  // first chameneos.  The word 'try' is used because other chameneos
  // may be trying to do the same thing simultaneously and beat it to
  // the action.
  //
  proc tryToMeet(meetingPlace, population) {
    //
    // keep trying to meet, as long as there are meetings remaining
    //
    do {
      //
      // grab a copy of the current state
      //
      const currentState = meetingPlace.state.read(memory_order_acquire);

      //
      // if the current state is non-empty, meetings remain
      //
      if (currentState) {
        //
        // extract the number of remaining meetings and the other
        // chameneos's index (if one is present) from the state
        //
        const meetingsLeft = currentState >> NUM_CHAMENEOS_ID_BITS,
              peer_idx = currentState & CHAMENEOS_ID_MASK;

        var newState: int;  // we'll store the new state here

        if (peer_idx) {
          //
          // if another chameneos was waiting in the meeting place, we
          // will hope to meet with it; if we can, the resulting state
          // would have one fewer meetings and no waiting chameneos.
          //
          newState = (meetingsLeft - 1) << NUM_CHAMENEOS_ID_BITS;
        } else {
          //
          // if no other chameneos was waiting, we will hope to enter
          // the meeting place; if we can, the resulting state would
          // have the same number of meetings and our ID stored in the
          // low bits.
          //
          newState = currentState | id;
        }

        //
        // use a CAS to see if the state remains the same, and to swap
        // in the new state we've computed if it is.
        //
        if (meetingPlace.state.compareExchangeStrong(currentState, newState,
                                                     memory_order_acq_rel)) {
          //
          // We get to meet!
          //
          if (peer_idx) {
            //
            // If we were the second chameneos in, we run the meeting
            //
            runMeeting(population, peer_idx);
          } else {
            //
            // If we were the first chameneos in, we wait for the
            // other to complete the meeting
            //
            meetingCompleted.waitFor(true);
            //
            // and then reset our meetingCompleted flag for next time
            //
            meetingCompleted.write(false, memory_order_release);
          }
        }
      }
      //
      // continue looping as long as meetings remain
      //
    } while (currentState);
  }

  //
  // runMeeting() computes the meeting itself between two chameneos,
  // by the second to enter the one that is 'this'
  //
  proc runMeeting(population, peer_idx) {
    //
    // If we meet with ourself, something's wrong, so let's check for
    // that.
    //
    // TODO: Note we can remove this if (a) the halt is removed and (b)
    // we are over-counting meetings with ourself
    //
    const is_same = (id == peer_idx);

    //
    // get the peer that we're meeting with and compute the new color
    // that we'll share as a function of our current colors
    //
    const peer = population[peer_idx];
    const newColor = getNewColor(color, peer.color);

    //
    // Update our peer's state: store the new color, update its
    // meetings, and then tell it that the meeting's over
    //
    //
    // TODO: abstract the following two assignments into a method to
    // avoid repetition?  Or is that more trouble than it's worth?
    //
    peer.color = newColor;
    peer.meetings += 1;
    peer.meetingsWithSelf += is_same;   // TODO: we bump the count twice?
    peer.meetingCompleted.write(true, memory_order_release);

    //
    // Then update ourself similarly
    //
    color = newColor;
    meetings += 1;
    meetingsWithSelf += is_same;
  }
}

//
// getNewColor() returns the complement of two colors; if the two colors
// are the same value, the color stays the same; otherwise, it becomes the
// third color.
//
inline proc getNewColor(myColor, otherColor) {
  if (myColor == otherColor) then
    return myColor;

  return (numColors - myColor - otherColor) : Color;
}

//
// printColorEquations() prints the result of getNewColor() for all
// pairs of colors
//
proc printColorEquations() {
  const colors = [Color.blue, Color.red, Color.yellow];
  for color1 in colors {
    for color2 in colors {
      writeln(color1, " + ", color2, " -> ", getNewColor(color1, color2));
    }
  }
  writeln();
}

//
// printInfo() prints out information about the population using
// a combination of numbers and English text
//
proc printInfo(population) {
  //
  // for each chameneos, print the number of meetings and spell out
  // the number of self-meetings for each chameneos
  //
  for c in population {
    write(c.meetings);
    spellInt(c.meetingsWithSelf);
  }

  //
  // compute the total number of meetings and spell it out
  //
  const totalMeetings = + reduce population.meetings;
  spellInt(totalMeetings);
  writeln();
}

//
// the base-10 digits
//
// TODO: Would be nice to be able to make this local?
//
enum Digit {zero=0, one, two, three, four, five, six, seven, eight, nine};

//
// spellInt() takes out an integer value and prints its digits out
// in English
//
proc spellInt(n) {
  //
  // Cast n to a string so that it's easier (?!) to pick out its characters
  //
  var s = n:string;
  //
  // TODO: would be nice to replace this with a character iterator?
  //
  for i in 1..s.length do
    write(" ", (s.substring(i):int):Digit);
  writeln();
}