/***********************************************************************
 * Chapel implementation of "99 bottles of beer"
 *
 * by Brad Chamberlain and Steve Deitz
 * 07/13/2006 in Knoxville airport while waiting for flight home from
 *            HPLS workshop
 * compiles and runs with chpl compiler version 1.12.0
 * for more information, contact: chapel_info@cray.com
 * 
 *
 * Notes: 
 * o as in all good parallel computations, boundary conditions
 *   constitute the vast bulk of complexity in this code (invite Brad to
 *   tell you about his zany boundary condition simplification scheme)
 * o uses type inference for variables, arguments
 * o relies on integer->string coercions
 * o uses named argument passing (for documentation purposes only)
 ***********************************************************************/
 
// allow executable command-line specification of number of bottles 
// (e.g., ./a.out -snumBottles=999999)
config const numBottles = 99;
const numVerses = numBottles+1;
 
// a domain to describe the space of lyrics
var LyricsSpace: domain(1) = {1..numVerses};
 
// array of lyrics
var Lyrics: [LyricsSpace] string;
 
// parallel computation of lyrics array
[verse in LyricsSpace] Lyrics(verse) = computeLyric(verse);
 
// as in any good parallel language, I/O to stdout is serialized.
// (Note that I/O to a file could be parallelized using a parallel
// prefix computation on the verse strings' lengths with file seeking)
writeln(Lyrics);
 
 
// HELPER FUNCTIONS:
 
proc computeLyric(verseNum) {
  var bottleNum = numBottles - (verseNum - 1);
  var nextBottle = (bottleNum + numVerses - 1)%numVerses;
  return "\n" // disguise space used to separate elements in array I/O
    + describeBottles(bottleNum, startOfVerse=true) + " on the wall, "
    + describeBottles(bottleNum) + ".\n"
    + computeAction(bottleNum)
    + describeBottles(nextBottle) + " on the wall.\n";
}
 
 
proc describeBottles(bottleNum, startOfVerse:bool = false) {
  // NOTE: bool should not be necessary here (^^^^); working around bug
  var bottleDescription = if (bottleNum) then bottleNum:string 
    else (if startOfVerse then "N" 
            else "n") 
           + "o more";
  return bottleDescription 
    + " bottle" + (if (bottleNum == 1) then "" else "s") 
    + " of beer";
}
 
 
proc computeAction(bottleNum) {
  return if (bottleNum == 0) then "Go to the store and buy some more, "
    else "Take one down and pass it around, ";
}


// Modules...
module M1 {
  var x = 10;

  var y = 13.0;
}

module M2 {
  use M1 except y;
  use M1 only y;
  proc main() {
    writeln("M2 -> M1 -> x " + x);
  }
}


// Classes, records, unions...
const PI: real = 3.14159;

record Point {
  var x, y: real;
}
var p: Point;
writeln("Distance from origin: " + sqrt(p.x ** 2 + p.y ** 2)); 
p = new Point(1.0, 2.0);
writeln("Distance from origin: " + sqrt(p.x ** 2 + p.y ** 2)); 

class Circle {
  var p: Point;
  var r: real;
}
var c = new Circle(r=2.0);
proc Circle.area()
  return PI * r ** 2;
writeln("Area of circle: " + c.area());

class Oval: Circle {
  var r2: real;
}
proc Oval.area()
  return PI * r * r2;

delete c;
c = nil;
c = new Oval(r=1.0, r2=2.0);
writeln("Area of oval: " + c.area());

// This is a valid decimal integer:
var x = 0000000000012;

union U {
  var i: int;
  var r: real;
}

// chapel ranges are awesome.
var r1 = 1..10,            // 1 2 3 4 5 6 7 8 9 10
  r2 = 10..1,              // no values in this range
  r3 = 1..10 by -1,        // 10 9 8 7 6 5 4 3 2 1
  r4 = 1..10 by 2,         // 1 3 5 7 9
  r5 = 1..10 by 2 align 0, // 2 4 6 8 10
  r6 = 1..10 by 2 align 2, // 2 4 6 8 10
  r7 = 1..10 # 3,          // 1 2 3
  r8 = 1..10 # -2,         // 9 10
  r9 = 1..100 # 10 by 2,   // 1 3 5 7 9
  ra = 1..100 by 2 # 10,   // 1 3 5 7 9 11 13 15 17 19
  rb = 1.. # 100 by 10;    // 1 11 21 31 41 51 61 71 81 91

// create a variable with default initialization
var myVarWithoutInit: real = noinit;
myVarWithoutInit = 1.0;

// Chapel has <~> operator for read and write I/O operations.
class IntPair {
  var x: int;
  var y: int;
  proc readWriteThis(f) {
    f <~> x <~> new ioLiteral(",") <~> y <~> new ioNewline();
  }
}
var ip = new IntPair(17,2);
write(ip);

var targetDom = {1..10},
  target: [targetDom] int;
coforall i in targetDom with (ref target) {
  target[i] = i ** 3;
}

var wideOpen = 0o777,
  mememe = 0o600,
  clique_y = 0O660,
  zeroOct = 0o0,
  minPosOct = 0O1;

private module M3 {
  private proc foo() {

  }

  private iter bar() {
    for i in 1..10 {
      yield i;
    }
  }

  private var x: int;

}
prototype module X {

  proc f() throws {
    throw new Error();
  }

  proc g() {
    try {
      f();
      try! f();
    } catch e {
      writeln("Caught ", e);
    }
  }

  proc int.add() { }

  g();

  override proc test() throws {
    var a = new borrowed IntPair();
    var b = new owned IntPair();
    var c = new shared IntPair();
    throw new unmanaged Error();
  }
}

