/*
 *  Exec() lastIndex behavior is a bit peculiar, especially for non-global
 *  regexps (= regexps whose global flag is false).
 *
 *  For regexps with global=true, the behavior is quite straightforward:
 *  read lastIndex, and write it after matching regardless of success or
 *  failure of matching.
 *
 *  For regexps with global=false, it is a bit odd (E5 Section 15.10.6.2):
 *
 *    1. lastIndex is read from the object and coerced to integer (steps 4 and 5)
 *    2. the index is reset to zero if global=false (step 7)
 *    3. if the input is exhausted during matching, lastIndex is *written* to
 *       zero and a null is returned (step 9.a)
 *    4. if a match occurs, lastIndex is *not* written (step 11)
 */

var r, t;
var a, b;

/*
 *  Non-global regexp, normal use.
 */

/*===
0
0
0
0
0
===*/

try {
    r = /foo/;
    print(r.lastIndex);
    t = r.exec('bar');      /* no match -> reset to zero (but stays the same here, since it is already 0) */
    print(r.lastIndex);
    t = r.exec('foo');      /* match -> don't touch */
    print(r.lastIndex);
    t = r.exec('foofoo');   /* match -> don't touch */
    print(r.lastIndex);
    t = r.exec('foofoo');   /* match -> don't touch */
    print(r.lastIndex);
} catch (e) {
    print(e.name);
}

/*
 *  Non-global regexp, lastIndex should have no effect on matching
 *  and should only change if a match is not found.
 */

/*===
0
-1
foo
-1
0
9999
foo
9999
===*/

try {
    r = /foo/;
    print(r.lastIndex);
    r.lastIndex = -1;
    print(r.lastIndex);
    t = r.exec('foo');   /* match -> don't touch */
    print(t[0]);
    print(r.lastIndex);

    r = /foo/;
    print(r.lastIndex);
    r.lastIndex = 9999;
    print(r.lastIndex);
    t = r.exec('foo');   /* match -> don't touch */
    print(t[0]);
    print(r.lastIndex);
} catch (e) {
    print(e.name);
}


/*
 *  Non-global regexp, lastIndex is not written to if a match is found,
 *  even if lastIndex needs coercion.
 *
 *  Note: Smjs and Rhino seems to coerce lastIndex to number when
 *  it is written, so they fail this test.  lastIndex is just a
 *  normal writable property, see E5 Section 15.10.7.5.  It is
 *  coerced to integer *when used*.
 */

/*===
0 number
1 string
1 string
1 string
1 string
0 number
1 object
1 object
1 object
1 object
true
===*/

try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = "1";
    print(r.lastIndex, typeof r.lastIndex);
    r.exec('foo');
    print(r.lastIndex, typeof r.lastIndex);
    r.exec('foofoo');
    print(r.lastIndex, typeof r.lastIndex);
    r.exec('foofoo');
    print(r.lastIndex, typeof r.lastIndex); 

    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    a = new String("1");
    r.lastIndex = a;
    print(r.lastIndex, typeof r.lastIndex);
    r.exec('foo');
    print(r.lastIndex, typeof r.lastIndex);
    r.exec('foofoo');
    print(r.lastIndex, typeof r.lastIndex);
    r.exec('foofoo');
    print(r.lastIndex, typeof r.lastIndex); 
    print(r.lastIndex === a);   /* instance should still be the same */
} catch (e) {
    print(e.name);
}

/*
 *  Non-global regexp, more lastIndex behavior when match is found
 *  and when it is not.
 */

/*===
0 number
0 number
0 number
1 string
1 string
0 number
-1 number
-1 number
0 number
1000000000000 number
1000000000000 number
0 number
1 string
0 number
===*/

/* match: zero is not written to */
try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foobar');
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

/* match: string '1' is not written to, and is ignored */
try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = '1';
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foobar');
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

/* match: number -1 is not written to, and is ignored */
try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = -1;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foobar');
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

/* match: a large number is not written to, and is ignored */
try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = 1000000000000;  /* above 2^32 */
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foobar');
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

/* no match: previous value is overwritten with zero (step 9.a.i),
 * regardless of the global flag!
 *
 * Rhino and smjs fail this test.
 */
try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = '1';
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('bar');
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

/*
 *  Global regexp, normal use.
 */

/*===
0
foo
3
null
0
0
foo
3
Foo
6
FOO
9
null
0
===*/

try {
    r = /foo/g;
    print(r.lastIndex);
    t = r.exec('foo');
    print(t[0]);
    print(r.lastIndex);
    t = r.exec('foo');
    print(t);
    print(r.lastIndex);

    r = /foo/gi;
    print(r.lastIndex);
    t = r.exec('fooFooFOO');
    print(t[0]);
    print(r.lastIndex);
    t = r.exec('fooFooFOO');
    print(t[0]);
    print(r.lastIndex);
    t = r.exec('fooFooFOO');
    print(t[0]);
    print(r.lastIndex);
    t = r.exec('fooFooFOO');
    print(t);
    print(r.lastIndex);
} catch (e) {
    print(e.name);
}

/*
 *  Global regexp, change input string while lastIndex changes,
 *  just a sanity check.
 */

/*===
0
foo
3
FOO
6
null
0
===*/

try {
    r = /foo/gi;
    print(r.lastIndex);
 
    t = r.exec('foo');    /* match, leave at 3 */
    print(t[0]);
    print(r.lastIndex);

    t = r.exec('barFOO');
    print(t[0]);
    print(r.lastIndex);

    t = r.exec('foo');    /* starts at 6, out of bounds => null and 0 */
    print(t);
    print(r.lastIndex);
} catch (e) {
    print(e.name);
}

/*
 *  Coercion and update of a non-number lastIndex.  Out of bounds lastIndex
 *  causes exec() to return null, and reset lastIndex to zero.
 */

/*===
0
-1
null
0
999
null
0
object
2
Foo
3
6
number
===*/

try {
    r = /foo/gi;
    print(r.lastIndex);

    r.lastIndex = -1;
    print(r.lastIndex);
    t = r.exec('foo');
    print(t);
    print(r.lastIndex);

    r.lastIndex = 999;
    print(r.lastIndex);
    t = r.exec('foo');
    print(t);
    print(r.lastIndex);

    a = {};
    a.valueOf = function() { return 2; };
    r.lastIndex = a;
    print(typeof r.lastIndex);
    print(r.lastIndex.valueOf());
    t = r.exec('fooFoo');   /* start matching at 2, find match at 3 for 'Foo' */
    print(t[0]);
    print(t.index);
    print(r.lastIndex);
    print(typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

/*
 *  lastIndex is coerced with ToInteger() which -allows- values
 *  larger than 32 bits.  For instance, 0x100000000 must NOT be
 *  confused with 0x00000000 as would happen if lastIndex were
 *  coerced with ToUint32() for instance.
 */

/*===
0 number
null
0 number
0 number
null
0 number
===*/

try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = 4294967297.0;   /* 0x100000001 */
    t = r.exec('foofoofoo');   /* no match, lastIndex is reset to zero */
    print(t);
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

try {
    r = /foo/g;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = 4294967297.0;   /* 0x100000001 */
    t = r.exec('foofoofoo');   /* no match, lastIndex is reset to zero */
    print(t);
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

/*
 *  lastIndex can be NAN or +/- Infinity.  These have well defined
 *  behavior.  If the global flag is not set, they are ignored (but
 *  overwritten if there is no match).  If the global flag is set,
 *  NAN is coerced to +0.0 by ToInteger(), while +/- Infinity is used
 *  as is.  It will cause match to fail at step 9.a, and lastIndex to
 *  be overwritten with zero.
 *
 *  The sign of a zero is preserved by ToInteger().  It must be preserved
 *  for non-global regexps which -do match-.
 */

/*===
0 number
NaN number
foo
NaN number
null
0 number Infinity
0 number
NaN number
foo
3 number
null
0 number Infinity
===*/

try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = NaN;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');   /* match -> don't update */
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('bar');   /* no match -> overwrite with zero */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

try {
    r = /foo/g;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = NaN;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');   /* match -> update */
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('bar');   /* no match -> overwrite with zero */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

/*===
0 number
Infinity number
foo
Infinity number
null
0 number Infinity
0 number
Infinity number
null
0 number Infinity
===*/

try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = Infinity;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');   /* match -> don't update */
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('bar');   /* no match -> overwrite with zero */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

try {
    r = /foo/g;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = Infinity;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');   /* no match (since we start from after end of string -> overwrite */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

/*===
0 number
-Infinity number
foo
-Infinity number
null
0 number Infinity
0 number
-Infinity number
null
0 number Infinity
===*/

try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = -Infinity;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');   /* match -> don't update */
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('bar');   /* no match -> overwrite with zero */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

try {
    r = /foo/g;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = -Infinity;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');   /* no match (since we start from after end of string -> overwrite */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

/*===
0 number Infinity
0 number -Infinity
foo
0 number -Infinity
null
0 number Infinity
0 number Infinity
0 number -Infinity
foo
3 number
null
0 number Infinity
===*/

try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
    r.lastIndex = -0.0;
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
    t = r.exec('foo');   /* match -> don't update */
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
    t = r.exec('bar');   /* no match -> overwrite with zero (positive) */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

try {
    r = /foo/g;
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
    r.lastIndex = -0.0;
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);
    t = r.exec('foo');   /* match -> update */
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('bar');   /* no match -> overwrite with zero (positive) */
    print(t);
    print(r.lastIndex, typeof r.lastIndex, 1.0 / r.lastIndex);   /* check sign too */
} catch (e) {
    print(e.name);
}

/*
 *  The lastIndex value is floored.
 */

/*===
0 number
0.9 number
foo
0.9 number
0 number
0.9 number
foo
3 number
===*/

try {
    r = /foo/;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = 0.9;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}

try {
    r = /foo/g;
    print(r.lastIndex, typeof r.lastIndex);
    r.lastIndex = 0.9;
    print(r.lastIndex, typeof r.lastIndex);
    t = r.exec('foo');
    print(t[0]);
    print(r.lastIndex, typeof r.lastIndex);
} catch (e) {
    print(e.name);
}


