scope {
    /* Some sanity checks... */
    assert 0xffff === 0xf_f__f___f;
    assert 0b101 === 5;
    assert 0b101 === 0b_1__0___1;
    assert 0x13 === 0b_0001_0011;
    assert 123 === 1_2__3;
    assert 501 === 0o7_6__5;
}

scope {
  //print(0.INT_MIN, 0.INT_MAX);
  assert 0.INT_MIN < 0;
  assert 0.INT_MAX > 0;
  
  assert 0.INT_MIN-1 === 0.INT_MAX /* This (signed underflow/overflow) "should" be is platform-dependent: */;
  assert 0.INT_MAX+1 === 0.INT_MIN /* This (signed underflow/overflow) "should" be is platform-dependent: */;

  var i = 0;
  assert 'integer' === typename i;
  assert 0 === i.compare(0);
  assert i.compare(1) < 0;
  assert i.compare(-1) > 0;
  assert 0.0 === i.toDouble();
  assert 0 !== i.toDouble();
  assert "0" === i.toJSONString();
  assert "0" === i.toString();
  assert "1" === 1.toString();
  i = 42;
  assert '*' === i.toChar();
}

scope {
  var d = 0.0;
  assert 'double' === typename d;
  assert 0 === d.compare(0);
  assert d.compare(1) < 0;
  assert d.compare(-1) > 0;
  assert 0 === d.toInt();
  assert 0.0 !== d.toInt();
  assert "0.0" === d.toJSONString();
  assert "0.0" === d.toString();
  assert "1.0" === 1.0.toString();
  assert 1 === 0.5.ceil();
  assert 0 === (-0.5).ceil(); // parens needed: dot has higher precedence than unary -.
  //assert 0 === 0.ceil(); // INTEGER 0 doesn't have ceil()
  assert 0 === 0.0.ceil();
  assert -1 === (-1.0).ceil();
  assert -1 === (-1.01).ceil();
  assert 0 === (-0.999).ceil();
  assert -2 === (-2.2).ceil();
  assert 3 === 2.2.ceil();
  assert 2 === 2.0.ceil();

  assert 1 === 1.5.floor();
  assert 1 === 1.0.floor();
  assert 0 === 0.0.floor();
  assert 0 === 0.5.floor();
  assert -1 === (-0.5).floor();
  assert -2 === (-1.5).floor();
}

scope {
  1.0.prototype.twice = proc(){ return this * 2 };
  assert 6.2 === 3.1.twice();
  assert 8.0 === (2.0*2).twice();
  //unset 0.0.prototype.twice; // unset doesn't like this, so...
  var d = 0.0;
  unset d.prototype.twice, d;
}

scope { // number.parseInt/Double/Number()
    const pi = 0.parseInt, pf = 0.parseDouble, pn = 0.parseNumber;
    assert 1 === pi(1);
    assert 1 === pi('1');
    assert undefined === pn('1_');
    assert undefined === pi('1_');
    assert 1 === pi('1.0');
    assert -1 === pi('-1');
    assert -1 === pi('-1.2');
    assert undefined === pi('1-1');
    assert 1.0 === pf(1);
    assert 1.0 === pf('1');
    assert 1.0 === pf(true);
    assert 0.0 === pf(false);
    assert 0.0 === pf('-0');
    assert 1 === pn(1);
    assert 1 === pn('1');
    assert 1.0 === pn(1.0);
    assert 1.0 === pn('1.0');
    assert 0 === pn('-0');
    assert 0.0 === pn('  +   0.0');
    assert 56 === pn('0o70');
    assert 56 === pn('0o7_0');
    assert 56 === pn('0o_7__0');
    assert undefined === pn('0o1_')/*trailing non-digit*/;
    assert undefined === pn('0o18')/*trailing non-[octal-]digit*/;
    assert -1 === pn('-0o1');
    assert -1 === pn('-0x0001');
    assert undefined === pn('-0x0001.')/*trailing non-digit*/;
    assert undefined === pn(pn);
    assert pf('1') === pn('1.0');

    assert 'double' === typename pn("1.3") /* parseNumber() keeps the numeric type */;
    assert 'integer' === typename pn("1") /* parseNumber() keeps the numeric type */;
    assert 'integer' === typename pi("1.3") /* parseInt() reduces to an integer */;
}

scope { // number.nthPrime()
    assert 2 === 0.nthPrime(1);
    assert 7919 === 0.nthPrime(1000);
    assert 'CWAL_RC_RANGE' === catch {0.nthPrime(0)}.codeString();
    assert 'CWAL_RC_RANGE' === catch {0.nthPrime(1001)}.codeString();
    assert 'CWAL_RC_MISUSE' === catch {0.nthPrime()}.codeString();
}

scope { // toString()
    assert '1' === 1.toString();
    assert '1.0' === 1.0.toString();
    assert '1' === 1.0.toString('d');
    assert '000c' === 12.toString('04x');
    assert '000C' === 12.toString('04X');
    assert '0e' === 0b11_10.toString('02x');
    assert 'FBF' === 0x_f_B_f.toString('X');
    assert '765' === 0o7_6_5.toString('o');
    assert '765' === 0b111_110_101.toString('o');
}

scope {
    assert 0x_f_0 === 2_4__0;
    /* Interesting: we can't catch these errors because they trigger
       in the tokenizer while slurping the {...} blocks.  That means
       that catch cannot really know that it "could" safely convert
       these fatal syntax errors to non-fatal exceptions. */
    //assert 0 === catch {1_}.message.indexOf('Malformed');
    //assert catch {1_2.3}.message.indexOf('not legal') > 0;
    /* So... to test these we'll wrap them in strings and eval them in
       the 2nd-pass phase, as 2nd-pass eval knows that it can convert
       fatal syntax errors to non-fatal exceptions. Note that using
       eval=>{...} to capture them as strings cannot work here for the
       same timing reason. */
    assert 0 === catch -> {'1_'}.message.indexOf('Malformed numeric literal');
    assert 0 === catch -> {'1.2_'}.message.indexOf('Malformed numeric literal');
    assert 0 === catch -> {'0b1_'}.message.indexOf('Malformed binary');
    assert 0 === catch -> {'0o1_'}.message.indexOf('Malformed octal');
    assert 0 === catch -> {'0x1_'}.message.indexOf('Malformed hex');
    assert catch -> {'1_2.3'}.message.indexOf('not legal in floating-point') > 0;
}

if(0.INT_MAX > 0xFFFF_FFFF /* 64 bit */) {
    const largeI = 0xffff_ffff_ffff,
          largeD = largeI.toDouble();
    assert 0.parseNumber(largeD.toString()) === largeD;
    assert 0.parseNumber(largeI.toString()) === largeI;

    const largeD2 = 10.356 + 0xffffffffffff,
          d2Str = largeD2.toString();
    /* was "2.814749767107e14" prior to 20181127, but now
       it's "281474976710665.4" (or thereabouts). */
    assert d2Str.indexOf('e')<0 /* no scientific notation */;
    assert 0.parseNumber(d2Str) === largeD2;
}
