Add c_string concatenation (proc +) and remove unused param string version

[reviewed by David]

Somehow I missed adding c_string concatenation (proc +) in r23558.
Everything still worked thanks to generics and "upcasting" to Chapel
string.  In any case, this should remove some unnecessarily allocated
memory from conversion to string.

I also missed removing a param version of proc + on string.  It is no
longer used (or legal).

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

pragma "no use ChapelStandard"

// Chapel Strings
module String {
  use CString;

  pragma "default string value" extern var defaultStringValue: string = noinit;

  // String concatenation
  inline proc +(s: string, x: string)
    // FIX ME: leak c_string
    return toString(__primitive("string_concat", s.c_str(), x.c_str()));

  pragma "compiler generated"
  inline proc +(s: c_string, x: string)
    // FIX ME: leak c_string
    return toString(__primitive("string_concat", s, x.c_str()));

  pragma "compiler generated"
  inline proc +(s: string, x: c_string)
    // FIX ME: leak c_string
    return toString(__primitive("string_concat", s.c_str(), x));

  inline proc +(s: string, x: numeric)
    return s + x:string;

  inline proc +(x: numeric, s: string)
    return x:string + s;

  inline proc +(s: string, x: enumerated)
    return s + x:string;

  inline proc +(x: enumerated, s: string)
    return x:string + s;

  inline proc +(s: string, x: bool)
    return s + x:string;

  inline proc +(x: bool, s: string)
    return x:string + s;

  proc typeToString(type t) param {
    return __primitive("typeToString", t);
  }

  proc typeToString(x) param {
    compilerError("typeToString()'s argument must be a type, not a value");
  }

  // This implies that the *representation* of strings is shared.
  // If strings are reimplemented as classes or records, a less trivial
  // implementation for assignment will become necessary.
  inline proc =(ref a: string, b: string) { __primitive("move", a, b); }

  inline proc ==(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) == 0);
  inline proc !=(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) != 0);

  inline proc <=(a: string, b: string) return a.c_str()<=b.c_str();
  inline proc >=(a: string, b: string) return a.c_str()>=b.c_str();
  inline proc <(a: string, b: string) return a.c_str()<b.c_str();
  inline proc >(a: string, b: string) return a.c_str()>b.c_str();

  //
  // primitive string functions and methods
  //
  inline proc ascii(a: string) return ascii(a.c_str());
  inline proc string.length return this.c_str().length;
  inline proc string.size return this.length;
  inline proc string.substring(i: int)
    // FIX ME: leak c_string
    return toString(this.c_str().substring(i));
  inline proc _string_contains(a: string, b: string)
    return _string_contains(a.c_str(), b.c_str());

  /* Returns the index of the first occurrence of a substring within a string,
      or 0 if the substring is not in the string.
   */
  inline proc string.indexOf(substring:string):int
    return this.c_str().indexOf(substring.c_str());
  inline proc string.indexOf(substring:c_string):int
    return this.c_str().indexOf(substring);

  // cast to and from Chapel strings use c_string
  pragma "compiler generated"
  inline proc _cast(type t, x) where t==string && x.type != c_string
    // FIX ME: leak c_string
    return toString(_cast(c_string, x));

  pragma "compiler generated"
  inline proc _cast(type t, x: string) where t !=c_string
    return _cast(t, x.c_str());

  inline proc _cast(type t, x: c_string) where _isPrimitiveType(t) && t!=string
    return __primitive("cast", t, x);

  //
  // casts to complex
  //
  inline proc _cast(type t, x: c_string) where _isComplexType(t)
    return __primitive("cast", t, x);

  //
  // casts to imag
  //
  inline proc _cast(type t, x: c_string) where _isImagType(t)
    return __primitive("cast", t, x);

  //
  // casts from complex
  //
  inline proc _cast(type t, x: complex(?w)) where t == c_string {
    if isnan(x.re) || isnan(x.im) then
      return "nan";
    var re = (x.re):c_string;
    var im, op: c_string;
    if x.im < 0 {
      im = (-x.im):c_string;
      op = " - ";
    } else if im == "-0.0" {
      im = "0.0";
      op = " - ";
    } else {
      im = (x.im):c_string;
      op = " + ";
    }
    return (re + op + im + "i").c_str();
  }

  //
  // casts from imag
  //
  inline proc _cast(type t, x: imag(?w)) where t == c_string
    return __primitive("cast", t, x);

  pragma "compiler generated"
  pragma "init copy fn"
  inline proc chpl__initCopy(a) {
    // Currently, string representations are shared.
    // (See note on proc =(a:string, b:string) above.)
      return a;
  }

  proc chpldev_refToString(ref arg) {

    //
    // print out the address of class references as well
    //
    proc chpldev_classToString(x: object)
      return " (class = " + __primitive("ref to string", x) + ")";
    proc chpldev_classToString(x) return "";

    return __primitive("ref to string", arg) + chpldev_classToString(arg);
  }

}

// C strings
//extern type c_string; is a built-in primitive type
module CString {

  inline proc toString(cstr:c_string):string {
    return __primitive("string_from_c_string", cstr, 0, 0);
  }
  inline proc toString(cstr:c_string, len:int):string {
    return __primitive("string_from_c_string", cstr, 1, len);
  }

  inline proc string.c_str():c_string {
    return __primitive("c_string_from_string", this);
  }

  inline proc ==(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) == 0;
  }

  inline proc ==(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) == 0;
  }

  inline proc !=(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) != 0;
  }

  inline proc !=(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) != 0;
  }

  inline proc <=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc <=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc >=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc >=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc <(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc <(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc >(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc >(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc =(ref a: c_string, b: c_string) {
    __primitive("=", a, b);
  }

  inline proc =(ref a: c_string, b: string) {
    __primitive("=", a, __primitive("c_string_from_string", b));
  }

  inline proc =(ref a: string, b: c_string) {
    __primitive("=", a, __primitive("string_from_c_string", b, 0, 0));
  }

  inline proc _cast(type t, x: c_string) where t == string {
    return __primitive("string_from_c_string", x, 0, 0);
  }

  inline proc _cast(type t, x: string) where t == c_string {
    return __primitive("c_string_from_string", x);
  }

  // Only support param c_string concatenation (for now)
  inline proc +(param a: c_string, param b: c_string) param
    return __primitive("string_concat", a, b);

  inline proc +(param s: c_string, param x: integral) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: integral, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: enumerated) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: enumerated, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: bool) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: bool, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(a: c_string, b: c_string)
    return __primitive("string_concat", a, b);

  proc ref c_string.writeThis(x: Writer) {
    x.write(this); // FIX ME? toString
  }

  //
  // primitive c_string functions and methods
  //
  inline proc ascii(param a: c_string) param return __primitive("ascii", a);
  inline proc ascii(a: c_string) return __primitive("ascii", a);
  inline proc c_string.length return __primitive("string_length", this);
  inline proc c_string.size return this.length;
  inline proc c_string.substring(i: int)
    return __primitive("string_index", this, i);
  inline proc _string_contains(a: string, b: string)
    return __primitive("string_contains", a, b);

  inline proc param c_string.length param
    return __primitive("string_length", this);
  inline proc _string_contains(param a: c_string, param b: c_string) param
    return __primitive("string_contains", a, b);

  /* Returns the index of the first occurrence of a substring within a string,
     or 0 if the substring is not in the string.
  */
  inline proc c_string.indexOf(substring:c_string):int
    return string_index_of(this, substring);
  extern proc string_index_of(haystack:c_string, needle:c_string):int;

}

pragma "no use ChapelStandard"

// Chapel Strings
module String {
  use CString;

  pragma "default string value" extern var defaultStringValue: string = noinit;

  // String concatenation
  inline proc +(s: string, x: string)
    // FIX ME: leak c_string
    return toString(__primitive("string_concat", s.c_str(), x.c_str()));

  pragma "compiler generated"
  inline proc +(s: c_string, x: string)
    // FIX ME: leak c_string
    return toString(__primitive("string_concat", s, x.c_str()));

  pragma "compiler generated"
  inline proc +(s: string, x: c_string)
    // FIX ME: leak c_string
    return toString(__primitive("string_concat", s.c_str(), x));

  inline proc +(s: string, x: numeric)
    return s + x:string;

  inline proc +(x: numeric, s: string)
    return x:string + s;

  inline proc +(s: string, x: enumerated)
    return s + x:string;

  inline proc +(x: enumerated, s: string)
    return x:string + s;

  inline proc +(s: string, x: bool)
    return s + x:string;

  inline proc +(x: bool, s: string)
    return x:string + s;

  proc typeToString(type t) param {
    return __primitive("typeToString", t);
  }

  proc typeToString(x) param {
    compilerError("typeToString()'s argument must be a type, not a value");
  }

  // This implies that the *representation* of strings is shared.
  // If strings are reimplemented as classes or records, a less trivial
  // implementation for assignment will become necessary.
  inline proc =(ref a: string, b: string) { __primitive("move", a, b); }

  inline proc ==(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) == 0);
  inline proc !=(a: string, b: string) return (__primitive("string_compare", a.c_str(), b.c_str()) != 0);

  inline proc <=(a: string, b: string) return a.c_str()<=b.c_str();
  inline proc >=(a: string, b: string) return a.c_str()>=b.c_str();
  inline proc <(a: string, b: string) return a.c_str()<b.c_str();
  inline proc >(a: string, b: string) return a.c_str()>b.c_str();
  inline proc +(param a: string, param b: string) param return __primitive("string_concat", a, b);

  //
  // primitive string functions and methods
  //
  inline proc ascii(a: string) return ascii(a.c_str());
  inline proc string.length return this.c_str().length;
  inline proc string.size return this.length;
  inline proc string.substring(i: int)
    // FIX ME: leak c_string
    return toString(this.c_str().substring(i));
  inline proc _string_contains(a: string, b: string)
    return _string_contains(a.c_str(), b.c_str());

  /* Returns the index of the first occurrence of a substring within a string,
      or 0 if the substring is not in the string.
   */
  inline proc string.indexOf(substring:string):int
    return this.c_str().indexOf(substring.c_str());
  inline proc string.indexOf(substring:c_string):int
    return this.c_str().indexOf(substring);

  // cast to and from Chapel strings use c_string
  pragma "compiler generated"
  inline proc _cast(type t, x) where t==string && x.type != c_string
    // FIX ME: leak c_string
    return toString(_cast(c_string, x));

  pragma "compiler generated"
  inline proc _cast(type t, x: string) where t !=c_string
    return _cast(t, x.c_str());

  inline proc _cast(type t, x: c_string) where _isPrimitiveType(t) && t!=string
    return __primitive("cast", t, x);

  //
  // casts to complex
  //
  inline proc _cast(type t, x: c_string) where _isComplexType(t)
    return __primitive("cast", t, x);

  //
  // casts to imag
  //
  inline proc _cast(type t, x: c_string) where _isImagType(t)
    return __primitive("cast", t, x);

  //
  // casts from complex
  //
  inline proc _cast(type t, x: complex(?w)) where t == c_string {
    if isnan(x.re) || isnan(x.im) then
      return "nan";
    var re = (x.re):c_string;
    var im, op: c_string;
    if x.im < 0 {
      im = (-x.im):c_string;
      op = " - ";
    } else if im == "-0.0" {
      im = "0.0";
      op = " - ";
    } else {
      im = (x.im):c_string;
      op = " + ";
    }
    return (re + op + im + "i").c_str();
  }

  //
  // casts from imag
  //
  inline proc _cast(type t, x: imag(?w)) where t == c_string
    return __primitive("cast", t, x);

  pragma "compiler generated"
  pragma "init copy fn"
  inline proc chpl__initCopy(a) {
    // Currently, string representations are shared.
    // (See note on proc =(a:string, b:string) above.)
      return a;
  }

  proc chpldev_refToString(ref arg) {

    //
    // print out the address of class references as well
    //
    proc chpldev_classToString(x: object)
      return " (class = " + __primitive("ref to string", x) + ")";
    proc chpldev_classToString(x) return "";

    return __primitive("ref to string", arg) + chpldev_classToString(arg);
  }

}

// C strings
//extern type c_string; is a built-in primitive type
module CString {

  inline proc toString(cstr:c_string):string {
    return __primitive("string_from_c_string", cstr, 0, 0);
  }
  inline proc toString(cstr:c_string, len:int):string {
    return __primitive("string_from_c_string", cstr, 1, len);
  }

  inline proc string.c_str():c_string {
    return __primitive("c_string_from_string", this);
  }

  inline proc ==(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) == 0;
  }

  inline proc ==(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) == 0;
  }

  inline proc ==(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) == 0;
  }

  inline proc !=(param s0: c_string, param s1: c_string) param {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: c_string, s1: c_string) {
    return __primitive("string_compare", s0, s1) != 0;
  }

  inline proc !=(s0: string, s1: c_string) {
    return __primitive("string_compare", s0.c_str(), s1) != 0;
  }

  inline proc !=(s0: c_string, s1: string) {
    return __primitive("string_compare", s0, s1.c_str()) != 0;
  }

  inline proc <=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc <=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) <= 0);
  }

  inline proc >=(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc >=(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) >= 0);
  }

  inline proc <(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc <(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) < 0);
  }

  inline proc >(a: c_string, b: c_string) {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc >(param a: c_string, param b: c_string) param {
    return (__primitive("string_compare", a, b) > 0);
  }

  inline proc =(ref a: c_string, b: c_string) {
    __primitive("=", a, b);
  }

  inline proc =(ref a: c_string, b: string) {
    __primitive("=", a, __primitive("c_string_from_string", b));
  }

  inline proc =(ref a: string, b: c_string) {
    __primitive("=", a, __primitive("string_from_c_string", b, 0, 0));
  }

  inline proc _cast(type t, x: c_string) where t == string {
    return __primitive("string_from_c_string", x, 0, 0);
  }

  inline proc _cast(type t, x: string) where t == c_string {
    return __primitive("c_string_from_string", x);
  }

  // Only support param c_string concatenation (for now)
  inline proc +(param a: c_string, param b: c_string) param
    return __primitive("string_concat", a, b);

  inline proc +(param s: c_string, param x: integral) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: integral, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: enumerated) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: enumerated, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  inline proc +(param s: c_string, param x: bool) param
    return __primitive("string_concat", s, x:c_string);

  inline proc +(param x: bool, param s: c_string) param
    return __primitive("string_concat", x:c_string, s);

  proc ref c_string.writeThis(x: Writer) {
    x.write(this); // FIX ME? toString
  }

  //
  // primitive c_string functions and methods
  //
  inline proc ascii(param a: c_string) param return __primitive("ascii", a);
  inline proc ascii(a: c_string) return __primitive("ascii", a);
  inline proc c_string.length return __primitive("string_length", this);
  inline proc c_string.size return this.length;
  inline proc c_string.substring(i: int)
    return __primitive("string_index", this, i);
  inline proc _string_contains(a: string, b: string)
    return __primitive("string_contains", a, b);

  inline proc param c_string.length param
    return __primitive("string_length", this);
  inline proc _string_contains(param a: c_string, param b: c_string) param
    return __primitive("string_contains", a, b);

  /* Returns the index of the first occurrence of a substring within a string,
     or 0 if the substring is not in the string.
  */
  inline proc c_string.indexOf(substring:c_string):int
    return string_index_of(this, substring);
  extern proc string_index_of(haystack:c_string, needle:c_string):int;

}


Tiny fix to Random -- it prints out a message indicating
that fillRandom() isn't supported for 2D or higher arrays,
but doesn't give any indication where this message is
coming from, which makes it hard to determine who's printing
it out when encountering it.  Put the name fillRandom()
into the message to clarify.  (Should really, probably turn
it into a halt(), but didn't quite have the stomach to go
this far).

Maybe David would enjoy implementing fillRandom() for
higher dimensional-arrays as an exercise using Chapel
domains and arrays?

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

use Time;

class SeedGeneratorClass {
  def clockMS {
    return getCurrentTime(unit=microseconds):int(64);
  }
}

var SeedGenerator = SeedGeneratorClass();

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  def initialize() {
    if (seed % 2 == 1) then
      seed += 1;
  }

  var numGenerated = 0;

  def nextrandlc(x : real, a: real = arand) {
    const
      r23   = 0.5**23,
      t23   = 2.0**23,
      r46   = 0.5**46,
      t46   = 2.0**46;
    var t1 = r23 * a;
    var a1 = floor(t1);
    var a2 = a - t23 * a1;
    t1 = r23 * x;
    var x1 = floor(t1);
    var x2 = x - t23 * x1;
    t1 = a1 * x2 + a2 * x1;
    var t2 = floor(r23 * t1);
    var z  = t1 - t23 * t2;
    var t3 = t23 * z + a2 * x2;
    var t4 = floor(r46 * t3);
    var x3 = t3 - t46 * t4;
    return (x3, r46 * x3);
  }

  def initrandlc(in n : int) : real {
    var i : int, t : real, g : real;
    var x  = seed:real;
    t = arand;
    while n != 0 do {
      i = n / 2;
      if 2 * i != n then
        (x, g) = nextrandlc(x, t);
      (t, g) = nextrandlc(t, t);
      n = i;
    }
    return x;
  }

  def fillRandom(x:[?D] real) {

    if (D.rank > 1) {
      writeln ("fillRandom() not yet implemented for 2D or higher arrays.");
      [i in D] x(i) = -1.0;
    }
    else {
      var randlc_last_x: real = initrandlc(numGenerated+1);
      for i in D {
        (randlc_last_x,x(i)) = nextrandlc(randlc_last_x);
        numGenerated += 1;
      }
    }
  }

  def fillRandom(x:[?D] complex) {

    if (D.rank > 1) {
      writeln ("fillRandom() not yet implemented for 2D or higher arrays.");
      [i in D] x(i) = -1.0;
    }
    else {
      var randlc_last_x: real = initrandlc(numGenerated+1);
      for i in D {
        (randlc_last_x,x(i).re) = nextrandlc(randlc_last_x);
        (randlc_last_x,x(i).im) = nextrandlc(randlc_last_x);
        numGenerated += 2;
      }
    }
  }
}

def fillRandom(x:[], initseed: int(64) = SeedGenerator.clockMS) {
  var randNums = RandomStream(initseed);

  randNums.fillRandom(x);
}

use Time;

class SeedGeneratorClass {
  def clockMS {
    return getCurrentTime(unit=microseconds):int(64);
  }
}

var SeedGenerator = SeedGeneratorClass();

class RandomStream {
  const seed:int(64) = SeedGenerator.clockMS;
  const arand = 1220703125.0;

  def initialize() {
    if (seed % 2 == 1) then
      seed += 1;
  }

  var numGenerated = 0;

  def nextrandlc(x : real, a: real = arand) {
    const
      r23   = 0.5**23,
      t23   = 2.0**23,
      r46   = 0.5**46,
      t46   = 2.0**46;
    var t1 = r23 * a;
    var a1 = floor(t1);
    var a2 = a - t23 * a1;
    t1 = r23 * x;
    var x1 = floor(t1);
    var x2 = x - t23 * x1;
    t1 = a1 * x2 + a2 * x1;
    var t2 = floor(r23 * t1);
    var z  = t1 - t23 * t2;
    var t3 = t23 * z + a2 * x2;
    var t4 = floor(r46 * t3);
    var x3 = t3 - t46 * t4;
    return (x3, r46 * x3);
  }

  def initrandlc(in n : int) : real {
    var i : int, t : real, g : real;
    var x  = seed:real;
    t = arand;
    while n != 0 do {
      i = n / 2;
      if 2 * i != n then
        (x, g) = nextrandlc(x, t);
      (t, g) = nextrandlc(t, t);
      n = i;
    }
    return x;
  }

  def fillRandom(x:[?D] real) {

    if (D.rank > 1) {
      writeln ("Not yet implemented for 2D or higher arrays.");
      [i in D] x(i) = -1.0;
    }
    else {
      var randlc_last_x: real = initrandlc(numGenerated+1);
      for i in D {
        (randlc_last_x,x(i)) = nextrandlc(randlc_last_x);
        numGenerated += 1;
      }
    }
  }

  def fillRandom(x:[?D] complex) {

    if (D.rank > 1) {
      writeln ("Not yet implemented for 2D or higher arrays.");
      [i in D] x(i) = -1.0;
    }
    else {
      var randlc_last_x: real = initrandlc(numGenerated+1);
      for i in D {
        (randlc_last_x,x(i).re) = nextrandlc(randlc_last_x);
        (randlc_last_x,x(i).im) = nextrandlc(randlc_last_x);
        numGenerated += 2;
      }
    }
  }
}

def fillRandom(x:[], initseed: int(64) = SeedGenerator.clockMS) {
  var randNums = RandomStream(initseed);

  randNums.fillRandom(x);
}


Adjusted a comment on SSCA2 graph construction for correctness and clarity.

I am updating just our "main" version of SSCA2, for simplicity.

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

module SSCA2_RMAT_graph_generator

{
  // +=========================================================================+
  // |             RMAT approximate power law graph generator                  |
  // |                                 and                                     |
  // |                           SSCA #2 Kernel 1                              |
  // |                                                                         |
  // |  Generate approximate power law graph                                   |
  // |                                                                         |
  // |  This procedure accepts a generic graph representation which must       |
  // |  provide only the capabilities to:                                      |
  // |    1. add a vertex to a neighbor list for some other vertex             |
  // |    2. assign an integer weight to an entry in an associated weight list |
  // |                                                                         |
  // |  The RMAT procedure implicitly assumes that vertices are integers       |
  // |  in the range [0, 2^SCALE].  So vertices are integers in this           |
  // |  procedure and edges are pairs of integers.                             |
  // |                                                                         |
  // |  This implementation first generates the RMAT graph as a list of        |
  // |  triples, using an array of edges and an associated array of weights.   |
  // |  These are transformed into the Chapel graph representation using       |
  // |  native Chapel syntax for associative and sparse domains and arrays.    |
  // |  That transformation is Kernel 1 of the SSCA #2 benchmark.              |
  // |                                                                         |
  // |  The code uses two auxiliary procedures for clarity and to help the     |
  // |  compiler use more abstract, higher level, code.                        |
  // +=========================================================================+

  use SSCA2_compilation_config_params;

  record directed_vertex_pair {
    var start = 1: int;
    var end   = 1: int;
  }

  proc +(l: directed_vertex_pair, r: directed_vertex_pair)
      return new directed_vertex_pair (l.start + r.start, l.end + r.end);

  // ============================
  // Quadrant selection algorithm
  // ============================

  proc assign_quadrant ( u: real, a: real, b: real, c: real, d : real,
			bit : int ) : directed_vertex_pair
    {
      // ---------------------------------------------------------------------
      // The heart of the RMAT random graph generator is a procedure that
      // assigns a single bit of the edge starting and ending vertex by
      // assigning the edge with specified probability to one of the four
      // quadrants of a 2 x 2 grid.
      //
      // Chapel is able to promote this conditional-based scalar procedure to
      // array operations where it is not able to promote conditional code
      // directly.
      //
      // Determine randomly in which quadrant of the grid a point lies,
      // based on the following picture:
      //
      //     +---------------------------+
      //     | u < a         | u < a+b   |
      //     |-------------+-------------|
      //     | u < a + b + c | otherwise |
      //     +---------------------------+
      //
      // The probability of the edge falling in the upper left quadrant is  a,
      // in the upper right quadrant, b, the lower left quadrant  c
      // and the lower right quadrant  d, where the probabilities are
      // normalized to sum to one.
      // ---------------------------------------------------------------------

      var start_inc = 0;
      var end_inc   = 0;
      var edge : directed_vertex_pair;

      if u <= a then
	{}
      else if u <= a + b then
	{ end_inc = 1;}
      else if u <= a + b + c then
	{ start_inc = 1;}
      else
	{ start_inc = 1; end_inc = 1;};

      edge.start = bit * start_inc;
      edge.end   = bit * end_inc;
      return ( edge );
    }

  // ====================================
  // Main RMAT Graph Generation Procedure
  // ====================================~

  proc Gen_RMAT_graph ( a : real,
		       b : real,
		       c : real,
		       d : real,
		       SCALE :int,
		       N_VERTICES : int,
		       n_raw_edges : int,
		       MAX_EDGE_WEIGHT :int,
		       G )

    { use Random;

      const vertex_range = 1..N_VERTICES,
	    edge_range   = 1..n_raw_edges,
	    rand_range   = 1..n_raw_edges + 1;

      // Random Numbers return in the range [0.0, 1.0)

      var Rand_Gen = if REPRODUCIBLE_PROBLEMS then
	               new RandomStream (seed = 0556707007)
		     else
		       new RandomStream ();

      var   Noisy_a     : [edge_range] real,
	    Noisy_b     : [edge_range] real,
            Noisy_c     : [edge_range] real,
            Noisy_d     : [edge_range] real,
            norm        : [edge_range] real,
	    Unif_Random : [edge_range] real,
	    Edges       = [edge_range] new directed_vertex_pair ();

       // ---------------------------------------------------------------
      // The RMAT algorithm is based on recursively sub-dividing a grid.
      // In this case, the grid is square of order 2^SCALE by 2^SCALE.
      // So exactly "SCALE" steps of recursion will be required and the
      // recursion can be implemented directly by iteration.
      // ---------------------------------------------------------------

      // -----------------------------------------------------------------
      // Note on random number generators -- the RMAT generator creates
      // 5*SCALE vectors of length 2**SCALE.  The dependence on powers
      // of two provides an opportunity to expose statistical correlations
      // in the pseudo-random numbers.  This certainly occurs with the
      // current Chapel Random module.  The "skips" in the random number
      // sequence dramatically change the results.  Without them, the
      // Chapel RMAT matrices are inconsistent with what is seen in
      // other implementations.
      // -----------------------------------------------------------------

      writeln ("Random graph generated by stride of 1 in one random stream",
	       " with skips" );

      var bit = 1 << SCALE;
      var   skip : real;

      for depth in 1..SCALE do {
	  bit >>= 1;

	  // randomize the coefficients, tweaking them by numbers in [-.05, .05)

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_a = a * (0.95 + 0.1 * Unif_Random);

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_b = b * (0.95 + 0.1 * Unif_Random);

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_c = c * (0.95 + 0.1 * Unif_Random);

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_d = d * (0.95 + 0.1 * Unif_Random);

	  norm     = 1.0 / (Noisy_a + Noisy_b + Noisy_c + Noisy_d);
	  Noisy_a *= norm;
	  Noisy_b *= norm;
	  Noisy_c *= norm;
	  Noisy_d *= norm;

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);

	  Edges += assign_quadrant ( Unif_Random, Noisy_a, Noisy_b,
				     Noisy_c, Noisy_d, bit );

	};

      // ---------------------------------------------------------------------
      // Assign weights to edges randomly, then randomly relabel the vertices
      // to avoid locality from the obvious imbalance that will arise when
      // one of the coefficients is clearly larger than the others
      // ---------------------------------------------------------------------

      var permutation : [vertex_range] int = vertex_range;
      var Edge_Weight : [edge_range] int;

      Rand_Gen.fillRandom ( Unif_Random  );
      Edge_Weight = floor (1 + Unif_Random * MAX_EDGE_WEIGHT) : int;

      Rand_Gen.fillRandom ( Unif_Random (vertex_range) );

      for v in vertex_range do
	{ var new_id : int;
	  new_id = floor (1 + Unif_Random (v) * N_VERTICES) : int;
	  permutation (v) <=> permutation (new_id);
	};

      var node_count : [vertex_range] int = 0;

      //      for e in edge_range do {
      //	Edges(e).start = permutation (Edges(e).start);
      //	Edges(e).end   = permutation (Edges(e).end  );
      //      }
      Edges.start = permutation (Edges.start);
      Edges.end   = permutation (Edges.end  );

      if DEBUG_GRAPH_GENERATOR || DEBUG_WEIGHT_GENERATOR then {
	writeln ();
	for e in edge_range do
	  writeln ("edge (", e, ") : (", Edges(e), ", ",
		   Edge_Weight (e), ")" );
      }

      // --------------------------
      // Graph consistency checking
      // --------------------------

      assert ( Edges.start > 0,
	       "edge start vertices out of low end of range");

      assert ( Edges.end > 0,
	       "edge end vertices out of low end of range");

      assert ( Edges.start <= 2**SCALE,
	       "edge start vertices out of high end of range");

      assert ( Edges.end <= 2**SCALE,
	       "edge end vertices out of high end of range");

      assert ( Edge_Weight > 0,
	       "edge weightd out of low end of range");

      assert ( Edge_Weight <= MAX_EDGE_WEIGHT,
	       "edge weightd out of high end of range");

      writeln (); writeln ("Vertex Set in G:", G.vertices);

      // ----------------------------------------------------------
      // Kernel 1: assemble graph from list of triples.
      // Include only non-self incident edges.
      // In case of (start, end) duplicates, the first triple wins.
      // Alternatively, could skip the member(v) check (so the last triple
      // wins) or also use += instead of = (to sum duplicates' weights).
      // ----------------------------------------------------------

      var collisions = 0, self_edges = 0;
      for e in edge_range do {
	var u = Edges (e).start;
	var v = Edges (e).end  ;

	if ( v != u ) then {
	  if G.Neighbors (u).member(v) then {
	    collisions += 1;
	  }
	  else {
	    G.Neighbors (u).add (v);
	    G.Row(u).Weight (v) = Edge_Weight (e);
	  }
	}
	else
	  self_edges += 1;
      }

      writeln ( "# of raw edges generated  ", n_raw_edges );
      writeln ( "# of duplicate edges      ", collisions );
      writeln ( "# of self edges           ", self_edges );
      writeln ( "# of edges in final graph ",
		+ reduce [v in G.vertices] G.n_Neighbors (v) );

      if DEBUG_GRAPH_GENERATOR || DEBUG_WEIGHT_GENERATOR then {
	writeln ();
	writeln ("tuples denote (edge, weight)");
	writeln ();
	for u in G.vertices do {
	  write ( "row ", u, ": [", G.n_Neighbors (u), "] " );
	  for (v,w) in (G .Neighbors (u), G.edge_weight (u) ) do
	    write ( (v, w) );
	  writeln ();
	}
      }

      var max_edges = max reduce [v in vertex_range] G.n_Neighbors (v);

      var edge_count : [0..max_edges] int = 0;

      for v in G.vertices do
	edge_count (G.n_Neighbors (v)) += 1;

      writeln ("histograph of node distributions by number of outgoing edges");
      writeln ( "# edges  # nodes");
      for count in 0..max_edges do
	writeln ( count, "  ", edge_count (count) );

      writeln ();

    }
}

module SSCA2_RMAT_graph_generator

{
  // +=========================================================================+
  // |             RMAT approximate power law graph generator                  |
  // |                                 and                                     |
  // |                           SSCA #2 Kernel 1                              |
  // |                                                                         |
  // |  Generate approximate power law graph                                   |
  // |                                                                         |
  // |  This procedure accepts a generic graph representation which must       |
  // |  provide only the capabilities to:                                      |
  // |    1. add a vertex to a neighbor list for some other vertex             |
  // |    2. assign an integer weight to an entry in an associated weight list |
  // |                                                                         |
  // |  The RMAT procedure implicitly assumes that vertices are integers       |
  // |  in the range [0, 2^SCALE].  So vertices are integers in this           |
  // |  procedure and edges are pairs of integers.                             |
  // |                                                                         |
  // |  This implementation first generates the RMAT graph as a list of        |
  // |  triples, using an array of edges and an associated array of weights.   |
  // |  These are transformed into the Chapel graph representation using       |
  // |  native Chapel syntax for associative and sparse domains and arrays.    |
  // |  That transformation is Kernel 1 of the SSCA #2 benchmark.              |
  // |                                                                         |
  // |  The code uses two auxiliary procedures for clarity and to help the     |
  // |  compiler use more abstract, higher level, code.                        |
  // +=========================================================================+

  use SSCA2_compilation_config_params;

  record directed_vertex_pair {
    var start = 1: int;
    var end   = 1: int;
  }

  proc +(l: directed_vertex_pair, r: directed_vertex_pair)
      return new directed_vertex_pair (l.start + r.start, l.end + r.end);

  // ============================
  // Quadrant selection algorithm
  // ============================

  proc assign_quadrant ( u: real, a: real, b: real, c: real, d : real,
			bit : int ) : directed_vertex_pair
    {
      // ---------------------------------------------------------------------
      // The heart of the RMAT random graph generator is a procedure that
      // assigns a single bit of the edge starting and ending vertex by
      // assigning the edge with specified probability to one of the four
      // quadrants of a 2 x 2 grid.
      //
      // Chapel is able to promote this conditional-based scalar procedure to
      // array operations where it is not able to promote conditional code
      // directly.
      //
      // Determine randomly in which quadrant of the grid a point lies,
      // based on the following picture:
      //
      //     +---------------------------+
      //     | u < a         | u < a+b   |
      //     |-------------+-------------|
      //     | u < a + b + c | otherwise |
      //     +---------------------------+
      //
      // The probability of the edge falling in the upper left quadrant is  a,
      // in the upper right quadrant, b, the lower left quadrant  c
      // and the lower right quadrant  d, where the probabilities are
      // normalized to sum to one.
      // ---------------------------------------------------------------------

      var start_inc = 0;
      var end_inc   = 0;
      var edge : directed_vertex_pair;

      if u <= a then
	{}
      else if u <= a + b then
	{ end_inc = 1;}
      else if u <= a + b + c then
	{ start_inc = 1;}
      else
	{ start_inc = 1; end_inc = 1;};

      edge.start = bit * start_inc;
      edge.end   = bit * end_inc;
      return ( edge );
    }

  // ====================================
  // Main RMAT Graph Generation Procedure
  // ====================================~

  proc Gen_RMAT_graph ( a : real,
		       b : real,
		       c : real,
		       d : real,
		       SCALE :int,
		       N_VERTICES : int,
		       n_raw_edges : int,
		       MAX_EDGE_WEIGHT :int,
		       G )

    { use Random;

      const vertex_range = 1..N_VERTICES,
	    edge_range   = 1..n_raw_edges,
	    rand_range   = 1..n_raw_edges + 1;

      // Random Numbers return in the range [0.0, 1.0)

      var Rand_Gen = if REPRODUCIBLE_PROBLEMS then
	               new RandomStream (seed = 0556707007)
		     else
		       new RandomStream ();

      var   Noisy_a     : [edge_range] real,
	    Noisy_b     : [edge_range] real,
            Noisy_c     : [edge_range] real,
            Noisy_d     : [edge_range] real,
            norm        : [edge_range] real,
	    Unif_Random : [edge_range] real,
	    Edges       = [edge_range] new directed_vertex_pair ();

       // ---------------------------------------------------------------
      // The RMAT algorithm is based on recursively sub-dividing a grid.
      // In this case, the grid is square of order 2^SCALE by 2^SCALE.
      // So exactly "SCALE" steps of recursion will be required and the
      // recursion can be implemented directly by iteration.
      // ---------------------------------------------------------------

      // -----------------------------------------------------------------
      // Note on random number generators -- the RMAT generator creates
      // 5*SCALE vectors of length 2**SCALE.  The dependence on powers
      // of two provides an opportunity to expose statistical correlations
      // in the pseudo-random numbers.  This certainly occurs with the
      // current Chapel Random module.  The "skips" in the random number
      // sequence dramatically change the results.  Without them, the
      // Chapel RMAT matrices are inconsistent with what is seen in
      // other implementations.
      // -----------------------------------------------------------------

      writeln ("Random graph generated by stride of 1 in one random stream",
	       " with skips" );

      var bit = 1 << SCALE;
      var   skip : real;

      for depth in 1..SCALE do {
	  bit >>= 1;

	  // randomize the coefficients, tweaking them by numbers in [-.05, .05)

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_a = a * (0.95 + 0.1 * Unif_Random);

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_b = b * (0.95 + 0.1 * Unif_Random);

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_c = c * (0.95 + 0.1 * Unif_Random);

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);
	  Noisy_d = d * (0.95 + 0.1 * Unif_Random);

	  norm     = 1.0 / (Noisy_a + Noisy_b + Noisy_c + Noisy_d);
	  Noisy_a *= norm;
	  Noisy_b *= norm;
	  Noisy_c *= norm;
	  Noisy_d *= norm;

	  skip = Rand_Gen.getNext ();
	  Rand_Gen.fillRandom (Unif_Random);

	  Edges += assign_quadrant ( Unif_Random, Noisy_a, Noisy_b,
				     Noisy_c, Noisy_d, bit );

	};

      // ---------------------------------------------------------------------
      // Assign weights to edges randomly, then randomly relabel the vertices
      // to avoid locality from the obvious imbalance that will arise when
      // one of the coefficients is clearly larger than the others
      // ---------------------------------------------------------------------

      var permutation : [vertex_range] int = vertex_range;
      var Edge_Weight : [edge_range] int;

      Rand_Gen.fillRandom ( Unif_Random  );
      Edge_Weight = floor (1 + Unif_Random * MAX_EDGE_WEIGHT) : int;

      Rand_Gen.fillRandom ( Unif_Random (vertex_range) );

      for v in vertex_range do
	{ var new_id : int;
	  new_id = floor (1 + Unif_Random (v) * N_VERTICES) : int;
	  permutation (v) <=> permutation (new_id);
	};

      var node_count : [vertex_range] int = 0;

      //      for e in edge_range do {
      //	Edges(e).start = permutation (Edges(e).start);
      //	Edges(e).end   = permutation (Edges(e).end  );
      //      }
      Edges.start = permutation (Edges.start);
      Edges.end   = permutation (Edges.end  );

      if DEBUG_GRAPH_GENERATOR || DEBUG_WEIGHT_GENERATOR then {
	writeln ();
	for e in edge_range do
	  writeln ("edge (", e, ") : (", Edges(e), ", ",
		   Edge_Weight (e), ")" );
      }

      // --------------------------
      // Graph consistency checking
      // --------------------------

      assert ( Edges.start > 0,
	       "edge start vertices out of low end of range");

      assert ( Edges.end > 0,
	       "edge end vertices out of low end of range");

      assert ( Edges.start <= 2**SCALE,
	       "edge start vertices out of high end of range");

      assert ( Edges.end <= 2**SCALE,
	       "edge end vertices out of high end of range");

      assert ( Edge_Weight > 0,
	       "edge weightd out of low end of range");

      assert ( Edge_Weight <= MAX_EDGE_WEIGHT,
	       "edge weightd out of high end of range");

      writeln (); writeln ("Vertex Set in G:", G.vertices);

      // ----------------------------------------------------------
      // Kernel 1: assemble graph from list of triples.  Include
      // only non-self incident edges.  in case of duplicates, last
      // in wins (+= instead of = works to take sum of weights)
      // ----------------------------------------------------------

      var collisions = 0, self_edges = 0;
      for e in edge_range do {
	var u = Edges (e).start;
	var v = Edges (e).end  ;

	if ( v != u ) then {
	  if G.Neighbors (u).member(v) then {
	    collisions += 1;
	  }
	  else {
	    G.Neighbors (u).add (v);
	    G.Row(u).Weight (v) = Edge_Weight (e);
	  }
	}
	else
	  self_edges += 1;
      }

      writeln ( "# of raw edges generated  ", n_raw_edges );
      writeln ( "# of duplicate edges      ", collisions );
      writeln ( "# of self edges           ", self_edges );
      writeln ( "# of edges in final graph ",
		+ reduce [v in G.vertices] G.n_Neighbors (v) );

      if DEBUG_GRAPH_GENERATOR || DEBUG_WEIGHT_GENERATOR then {
	writeln ();
	writeln ("tuples denote (edge, weight)");
	writeln ();
	for u in G.vertices do {
	  write ( "row ", u, ": [", G.n_Neighbors (u), "] " );
	  for (v,w) in (G .Neighbors (u), G.edge_weight (u) ) do
	    write ( (v, w) );
	  writeln ();
	}
      }

      var max_edges = max reduce [v in vertex_range] G.n_Neighbors (v);

      var edge_count : [0..max_edges] int = 0;

      for v in G.vertices do
	edge_count (G.n_Neighbors (v)) += 1;

      writeln ("histograph of node distributions by number of outgoing edges");
      writeln ( "# edges  # nodes");
      for count in 0..max_edges do
	writeln ( count, "  ", edge_count (count) );

      writeln ();

    }
}