The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl -w
$|++;

use strict;
use Test::More tests => 785;

use lib "./t";
use _nrr_test_util;

use lib "./blib/lib";
use Number::Range::Regex qw ( range rangespec );
use Number::Range::Regex::Util qw (multi_union );

my ($sr1, $sr2, $r1, $r2, $r, $re);

$sr1 = Number::Range::Regex::TrivialRange->new(3, 9);
ok($sr1);
ok($sr1->to_string eq '3..9');
$r = $sr1->not();
ok($r->to_string eq '-inf..2,10..+inf');
$sr2 = range(10, 11);
ok($sr2);
ok($sr2->to_string eq '10..11');
$r = $sr2->not();
ok($r->to_string eq '-inf..9,12..+inf');
ok($sr1->touches($sr2));
ok(!$sr1->overlaps($sr2));
$r = $sr1->union($sr2);
ok($r);
ok($r->to_string eq '3..11');
ok(check_type($r, 'Simple'));
$re = $r->regex;
ok($re);
ok(test_range_regex(3, 11, $re));
$r = $r->not();
ok($r->to_string eq '-inf..2,12..+inf');
$r = $sr1->intersection($sr2);
ok($r);
ok($r->to_string eq '');
ok(check_type($r, 'Empty'));
$re = $r->regex;
ok($re);
ok( !/^$re$/ ) for (2..12);
ok( !$r->contains($_) ) for (2..12);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');
$r = $sr1->minus($sr2);
ok($r);
ok($r->to_string eq '3..9');
ok(check_type($r, 'Simple'));
$re = $r->regex;
ok($re);
ok(test_rangeobj_exhaustive($r));
$r = $sr2->minus($sr1);
ok($r);
ok($r->to_string eq '10..11');
ok(check_type($r, 'Simple'));
$re = $r->regex;
ok($re);
ok(test_rangeobj_exhaustive($r));
$r = $r->not();
ok($r->to_string eq '-inf..9,12..+inf');
$r = $sr2->xor($sr1);
ok($r);
ok($r->to_string eq '3..11');
ok(check_type($r, 'Simple'));
$re = $r->regex;
ok($re);
ok(test_rangeobj_exhaustive($r));
ok($sr1->union($sr2)->minus($sr1->intersection($sr2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)
$r = $r->not();
ok($r->to_string eq '-inf..2,12..+inf');

# sr1 = 3..9
$sr2 = Number::Range::Regex::TrivialRange->new(6, 8);
ok($sr2);
ok($sr2->to_string eq '6..8');
$r = $sr2->not();
ok($r->to_string eq '-inf..5,9..+inf');
ok($sr1->touches($sr2));
$r = $sr1->union($sr2);
ok($r);
ok($r->to_string eq '3..9');
ok(check_type($r, 'Simple,Trivial'));
$re = $r->regex;
ok($re);
ok(test_range_regex(3, 9, $re));
$r = $r->not();
ok($r->to_string eq '-inf..2,10..+inf');
$r = $sr1->intersection($sr2);
ok($r);
ok($r->to_string eq '6..8');
ok(check_type($r, 'Simple,Trivial'));
$re = $r->regex;
ok($re);
ok(test_range_regex(6, 8, $re));
$r = $r->not();
ok($r->to_string eq '-inf..5,9..+inf');
$r = $sr1->minus($sr2);
ok($r);
ok($r->to_string eq '3..5,9');
ok(check_type($r, 'Compound'));
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for (3..5,9);
ok( !/^$re$/ ) for (2,6..8,10);
ok( $r->contains($_) ) for (3..5,9);
ok( !$r->contains($_) ) for (2,6..8,10);
$r = $r->not();
ok($r->to_string eq '-inf..2,6..8,10..+inf');
$r = $sr2->minus($sr1);
ok($r);
ok($r->to_string eq '');
ok(check_type($r, 'Empty'));
$re = $r->regex;
ok($re);
ok( !/^$re$/ ) for (3..9);
ok( !$r->contains($_) ) for (3..9);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');
$r = $sr2->xor($sr1);
ok($r);
ok($r->to_string eq '3..5,9');
ok(check_type($r, 'Compound'));
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for (3..5,9);
ok( !/^$re$/ ) for (2,6..8,10);
ok( $r->contains($_) ) for (3..5,9);
ok( !$r->contains($_) ) for (2,6..8,10);
ok($sr1->union($sr2)->minus($sr1->intersection($sr2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)
$r = $r->not();
ok($r->to_string eq '-inf..2,6..8,10..+inf');

$sr1 = Number::Range::Regex::TrivialRange->new(100, 109);
ok($sr1);
ok($sr1->to_string eq '100..109');
$r = $sr1->not();
ok($r->to_string eq '-inf..99,110..+inf');
$sr2 = Number::Range::Regex::TrivialRange->new(120, 129);
ok($sr2);
ok($sr2->to_string eq '120..129');
$r = $sr2->not();
ok($r->to_string eq '-inf..119,130..+inf');
ok(!$sr1->touches($sr2));
ok(!$sr1->overlaps($sr2));
$r = $sr1->union($sr2);
ok($r);
ok(check_type($r, 'Compound'));
ok($r->to_string eq '100..109,120..129');
$re = $r->regex;
ok($re);
ok(test_range_regex(100, 109, $re));
ok(test_range_regex(120, 129, $re));
$r = $r->not();
ok($r->to_string eq '-inf..99,110..119,130..+inf');
$r = $sr1->intersection($sr2);
ok($r);
ok(check_type($r, 'Empty'));
ok($r->to_string eq '');
$re = $r->regex;
ok($re);
ok( !/^$re$/ ) for (99..130);
ok( !$r->contains($_) ) for (99..130);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');

# tr2 = 120..129
$sr1 = Number::Range::Regex::TrivialRange->new(110, 189);
ok($sr1);
ok($sr1->to_string eq '110..189');
ok($sr1->touches($sr2));
ok($sr1->overlaps($sr2));
$r = $sr1->not();
ok($r->to_string eq '-inf..109,190..+inf');
$r = $sr1->union($sr2);
ok($r);
ok(check_type($r, 'Simple,Trivial'));
ok($r->to_string eq '110..189');
$re = $r->regex;
ok($re);
ok(test_range_regex(110, 189, $re));
$r = $r->not();
ok($r->to_string eq '-inf..109,190..+inf');
$r = $sr1->intersection($sr2);
ok($r);
ok(check_type($r, 'Simple,Trivial'));
ok($r->to_string eq '120..129');
$re = $r->regex;
ok($re);
ok(110 !~ /^$re$/);
ok( !$r->contains(110) );
ok(test_range_regex(120, 129, $re));
ok(130 !~ /^$re$/);
ok( !$r->contains(130) );
$r = $r->not();
ok($r->to_string eq '-inf..119,130..+inf');

# tr2 = 120..129
$sr1 = Number::Range::Regex::TrivialRange->new(190, 199);
ok($sr1);
ok($sr1->to_string eq '190..199');
ok(!$sr1->touches($sr2));
ok(!$sr1->overlaps($sr2));
$r = $sr1->not();
ok($r->to_string eq '-inf..189,200..+inf');
$r = $sr1->union($sr2);
ok($r);
ok($r->to_string eq '120..129,190..199');
ok(check_type($r, 'Compound'));
$re = $r->regex;
ok(test_range_regex(120, 129, $re));
ok(135 !~ /^$re$/);
ok( !$r->contains(135) );
ok(test_range_regex(190, 199, $re));
$r = $r->not();
ok($r->to_string eq '-inf..119,130..189,200..+inf');
$r = $sr1->intersection($sr2);
ok($r);
ok(check_type($r, 'Empty'));
ok($r->to_string eq '');
$re = $r->regex;
ok($re);
ok( !/^$re$/ ) for (119..130,189..200);
ok( !$r->contains($_) ) for (119..130,189..200);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');

$r1 = range(100, 109);
ok($r1);
ok($r1->to_string eq '100..109');
$r = $r1->not();
ok($r->to_string eq '-inf..99,110..+inf');
$r2 = range(110, 189);
ok($r2);
ok($r2->to_string eq '110..189');
$r = $r2->not();
ok($r->to_string eq '-inf..109,190..+inf');
$r = $r1->union($r2);
ok($r);
ok($r->to_string eq '100..189');
ok(check_type($r, 'Simple'));
$re = $r->regex;
ok($re);
ok(test_range_regex(100, 189, $re));
$r = $r->not();
ok($r->to_string eq '-inf..99,190..+inf');
$r = $r1->intersection($r2);
ok($r);
ok($r->to_string eq '');
$re = $r->regex;
ok($re);
ok(check_type($r, 'Empty'));
ok( !/^$re$/ ) for (108..112);
ok( !$r->contains($_) ) for (108..112);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');

# r1 = 100..109
$r2 = range(109, 189);
ok($r2);
ok($r2->to_string eq '109..189');
$r = $r2->not();
ok($r->to_string eq '-inf..108,190..+inf');
$r = $r1->union($r2);
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '100..189');
$re = $r->regex;
ok($re);
ok(test_range_regex(100, 189, $re));
$r = $r->not();
ok($r->to_string eq '-inf..99,190..+inf');
$r = $r1->intersection($r2);
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '109');
$re = $r->regex;
ok($re);
ok(108 !~ /^$re$/);
ok(109 =~ /^$re$/);
ok(110 !~ /^$re$/);
$r = $r->not();
ok($r->to_string eq '-inf..108,110..+inf');

# r1 = 100..109
$r2 = range(111, 189);
ok($r2);
ok($r2->to_string eq '111..189');
$r = $r2->not();
ok($r->to_string eq '-inf..110,190..+inf');
$r = $r1->union($r2);
ok($r);
ok(check_type($r, 'Compound'));
ok($r->to_string eq '100..109,111..189');
$re = $r->regex;
# should be something like: "10[0-9]", "11[1-9]", "1[2-8]\d"
ok($re);
ok(test_range_regex(100, 109, $re));
ok(110 !~ /^$re$/);
ok(test_range_regex(111, 189, $re));
$r = $r->not();
ok($r->to_string eq '-inf..99,110,190..+inf');
$r = $r1->intersection($r2);
ok($r);
ok(check_type($r, 'Empty'));
ok($r->to_string eq '');
$re = $r->regex;
ok($re);
ok( !/^$re$/ ) for (108..112);
ok( !$r->contains($_) ) for (108..112);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');

$r1 = range(9725, 10033);
ok($r1);
ok($r1->to_string eq '9725..10033');
$r = $r1->not();
ok($r->to_string eq '-inf..9724,10034..+inf');
$r2 = range(10032, 10036);
ok($r2);
ok($r2->to_string eq '10032..10036');
$r = $r2->not();
ok($r->to_string eq '-inf..10031,10037..+inf');
$r = $r1->union($r2);
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '9725..10036');
$re = $r->regex;
ok($re);
ok(test_range_regex(9725, 10036, $re));
$r = $r->not();
ok($r->to_string eq '-inf..9724,10037..+inf');
$r = $r1->intersection($r2);
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '10032..10033');
$re = $r->regex;
ok($re);
ok(10031 !~ /^$re$/);
ok(10032 =~ /^$re$/);
ok(10033 =~ /^$re$/);
ok(10034 !~ /^$re$/);
$r = $r->not();
ok($r->to_string eq '-inf..10031,10034..+inf');

# r1 = 9725..10033
$r2 = range(10033, 10036);
ok($r2);
ok($r2->to_string eq '10033..10036');
$r = $r2->not();
ok($r->to_string eq '-inf..10032,10037..+inf');
$r = $r1->union($r2);
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '9725..10036');
$re = $r->regex;
ok($re);
ok(test_range_regex(9725, 10036, $re));
$r = $r->not();
ok($r->to_string eq '-inf..9724,10037..+inf');
$r = $r1->intersection($r2);
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '10033');
$re = $r->regex;
ok($re);
ok(10032 !~ /^$re$/);
ok(10033 =~ /^$re$/);
ok(10034 !~ /^$re$/);
$r = $r->not();
ok($r->to_string eq '-inf..10032,10034..+inf');

# r1 = 9725..10033
$r2 = range(10034, 10036);
ok($r2);
ok($r2->to_string eq '10034..10036');
$r = $r2->not();
ok($r->to_string eq '-inf..10033,10037..+inf');
$r = $r1->union($r2);
ok($r);
ok($r->to_string eq '9725..10036');
ok(check_type($r, 'Simple'));
$re = $r->regex;
ok($re);
ok(test_range_regex(9725, 10036, $re));
$r = $r->not();
ok($r->to_string eq '-inf..9724,10037..+inf');
$r = $r1->intersection($r2);
ok($r);
ok(check_type($r, 'Empty'));
ok($r->to_string eq '');
$re = $r->regex;
ok($re);
ok(10032 !~ /^$re$/);
ok(10033 !~ /^$re$/);
ok(10034 !~ /^$re$/);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');

# r1 = 9725..10033
$r2 = range(10035, 10036);
ok($r2);
ok($r2->to_string eq '10035..10036');
$r = $r2->not();
ok($r->to_string eq '-inf..10034,10037..+inf');
$r = $r1->union($r2);
ok($r);
ok(check_type($r, 'Compound'));
ok($r->to_string eq '9725..10033,10035..10036');
$re = $r->regex;
ok($re);
ok(10033 =~ /^$re$/);
ok(10034 !~ /^$re$/);
ok(10035 =~ /^$re$/);
$r = $r->not();
ok($r->to_string eq '-inf..9724,10034,10037..+inf');
$r = $r1->intersection($r2);
ok($r);
ok(check_type($r, 'Empty'));
ok($r->to_string eq '');
$re = $r->regex;
ok($re);
ok(10032 !~ /^$re$/);
ok(10033 !~ /^$re$/);
ok(10034 !~ /^$re$/);
$r = $r->not();
ok($r->to_string eq '-inf..+inf');

$sr1 = range( 1, 7 );
$sr2 = range( 2, 11 );
$r = $sr1->minus( $sr2 );
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '1');
ok($r->not->to_string eq '-inf..0,2..+inf');
$re = $r->regex;
ok($re);
ok(0 !~ /^$re$/);
ok(1 =~ /^$re$/);
ok(2 !~ /^$re$/);
$r = $sr2->minus( $sr1 );
ok($r);
ok(check_type($r, 'Simple'));
ok($r->to_string eq '8..11');
ok($r->not->to_string eq '-inf..7,12..+inf');
$re = $r->regex;
ok($re);
ok(test_range_regex(8, 11, $re));
$r = $sr1->xor( $sr2 );
ok(check_type($r, 'Compound'));
ok($r->to_string eq '1,8..11');
ok($r->not->to_string eq '-inf..0,2..7,12..+inf');
$re = $r->regex;
ok($re);
ok(0 !~ /^$re$/);
ok(1 =~ /^$re$/);
ok(2 !~ /^$re$/);
ok(test_range_regex(8, 11, $re));
ok($sr1->union($sr2)->minus($sr1->intersection($sr2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)
$r = $sr2->xor( $sr1 );
ok(check_type($r, 'Compound'));
ok($r->to_string eq '1,8..11');
ok($r->not->to_string eq '-inf..0,2..7,12..+inf');
$re = $r->regex;
ok($re);
ok(0 !~ /^$re$/);
ok(1 =~ /^$re$/);
ok(2 !~ /^$re$/);
ok(test_range_regex(8, 11, $re));
ok($sr1->union($sr2)->minus($sr1->intersection($sr2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)

# sr1 = 1..7
$sr2 = range( 2, 6 );
$r = $sr1->minus( $sr2 );
ok($r);
ok(check_type($r, 'Compound'));
ok($r->to_string eq '1,7');
ok($r->not->to_string eq '-inf..0,2..6,8..+inf');
$re = $r->regex;
ok($re);
ok(0 !~ /^$re$/);
ok(1 =~ /^$re$/);
ok(2 !~ /^$re$/);
ok(6 !~ /^$re$/);
ok(7 =~ /^$re$/);
ok(8 !~ /^$re$/);
$r = $sr2->minus( $sr1 );
ok($r);
ok(check_type($r, 'Empty'));
ok($r->to_string eq '');
ok($r->not->to_string eq '-inf..+inf');
$re = $r->regex;
ok($re);
ok( !/^$re$/ ) for (0..8);
ok( !$r->contains($_) ) for (0..8);
$r = $sr1->xor( $sr2 );
ok(check_type($r, 'Compound'));
ok($r->to_string eq '1,7');
ok($r->not->to_string eq '-inf..0,2..6,8..+inf');
$re = $r->regex;
ok($re);
ok(0 !~ /^$re$/);
ok(1 =~ /^$re$/);
ok(2 !~ /^$re$/);
ok(6 !~ /^$re$/);
ok(7 =~ /^$re$/);
ok(8 !~ /^$re$/);
ok($sr1->union($sr2)->minus($sr1->intersection($sr2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)
$r = $sr1->xor( $sr2 );
ok(check_type($r, 'Compound'));
ok($r->to_string eq '1,7');
ok($r->not->to_string eq '-inf..0,2..6,8..+inf');
$re = $r->regex;
ok($re);
ok(0 !~ /^$re$/);
ok(1 =~ /^$re$/);
ok(2 !~ /^$re$/);
ok(6 !~ /^$re$/);
ok(7 =~ /^$re$/);
ok(8 !~ /^$re$/);
ok($sr1->union($sr2)->minus($sr1->intersection($sr2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)

# tests of compound ranges
my $mul2 = rangespec('0,2,4,6,8');
my $mul3 = rangespec('0,3,6,9');

$r = $mul2->union($mul3);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 0,2,3,4,6,8,9 );
ok( !/^$re$/ ) for ( 1,5,7 );
ok( $r->contains($_) ) for ( 0,2,3,4,6,8,9 );
ok( !$r->contains($_) ) for ( 1,5,7 );
ok($r->not->to_string eq '-inf..-1,1,5,7,10..+inf');

$r = $mul2->intersection($mul3);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 0,6 );
ok( !/^$re$/ ) for ( 1..5,7..9 );
ok( $r->contains($_) ) for ( 0,6 );
ok( !$r->contains($_) ) for ( 1..5,7..9 );
ok($r->not->to_string eq '-inf..-1,1..5,7..+inf');

$r = $mul2->minus($mul3);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 2,4,8 );
ok( !/^$re$/ ) for ( 0,1,3,5..7,9 );
ok( $r->contains($_) ) for ( 2,4,8 );
ok( !$r->contains($_) ) for ( 0,1,3,5..7,9 );
ok($r->not->to_string eq '-inf..1,3,5..7,9..+inf');

$r = $mul3->minus($mul2);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 3,9 );
ok( !/^$re$/ ) for ( 0..2,4..8 );
ok( $r->contains($_) ) for ( 3,9 );
ok( !$r->contains($_) ) for ( 0..2,4..8 );
ok($r->not->to_string eq '-inf..2,4..8,10..+inf');

$r = $mul3->xor($mul2);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 2,3,4,8,9 );
ok( !/^$re$/ ) for ( 0,1,5..7 );
ok( $r->contains($_) ) for ( 2,3,4,8,9 );
ok( !$r->contains($_) ) for ( 0,1,5..7 );
ok($r->not->to_string eq '-inf..1,5..7,10..+inf');
ok($mul3->union($mul2)->minus($mul3->intersection($mul2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)

ok(rangespec('1..7')->xor( rangespec('1..11') )->to_string() eq '8..11');
ok(rangespec('1..11')->xor( rangespec('1..11') )->to_string() eq '');
ok(rangespec('1..7')->xor( rangespec('1..6') )->to_string() eq '7');
ok(rangespec('1..7')->xor( rangespec('3..11') )->to_string() eq '1..2,8..11');
ok(rangespec('1..11')->xor( rangespec('3..11') )->to_string() eq '1..2');
ok(rangespec('1..7')->xor( rangespec('3..6') )->to_string() eq '1..2,7');

# tests of promotion of simplerange to compoundrange
my $near5 = rangespec('4..6');
$r = $near5->union($mul2);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 0,2,4,5,6,8 );
ok( !/^$re$/ ) for ( 1,3,7,9 );
ok( $r->contains($_) ) for ( 0,2,4,5,6,8 );
ok( !$r->contains($_) ) for ( 1,3,7,9 );
ok($r->not->to_string eq '-inf..-1,1,3,7,9..+inf');
$r = $near5->intersection($mul2);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 4,6 );
ok( !/^$re$/ ) for ( 0..3,5,7..9 );
ok( $r->contains($_) ) for ( 4,6 );
ok( !$r->contains($_) ) for ( 0..3,5,7..9 );
ok($r->not->to_string eq '-inf..3,5,7..+inf');
$r = $near5->minus($mul2);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 5 );
ok( !/^$re$/ ) for ( 0..4,6..9 );
ok( $r->contains($_) ) for ( 5 );
ok( !$r->contains($_) ) for ( 0..4,6..9 );
ok($r->not->to_string eq '-inf..4,6..+inf');
$r = $near5->xor($mul2);
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 0,2,5,8 );
ok( !/^$re$/ ) for ( 1,3,4,6,7,9 );
ok( $r->contains($_) ) for ( 0,2,5,8 );
ok( !$r->contains($_) ) for ( 1,3,4,6,7,9 );
ok($r->not->to_string eq '-inf..-1,1,3..4,6..7,9..+inf');
ok($near5->union($mul2)->minus($near5->intersection($mul2))->to_string eq $r->to_string); #a xor b = (a u b) - (a int b)

$r = rangespec( '-1..2,4..5,8..9,14' )->invert();
ok( $r->to_string eq '-inf..-2,3,6..7,10..13,15..+inf' );
$r = rangespec( '2..5,10..+inf' )->invert();
ok( $r->to_string eq '-inf..1,6..9' );
$r = rangespec( '-10..-5,-2..+inf' )->invert();
ok( $r->to_string eq '-inf..-11,-4..-3' );
$r = rangespec( '-inf..-10,-5..-2' )->invert();
ok( $r->to_string eq '-9..-6,-1..+inf' );
$r = rangespec( '-inf..2,5..10' )->invert();
ok( $r->to_string eq '3..4,11..+inf' );
$r = rangespec( '-1..2,4..5,8..9,14' )->xor( rangespec('-inf..+inf', {allow_wildcard => 1} ) );
ok( $r->to_string eq '-inf..-2,3,6..7,10..13,15..+inf' );
$r = rangespec( '-1..2,4..5,8..9,14' )->xor( rangespec('-inf..6') );
ok( $r->to_string eq '-inf..-2,3,6,8..9,14');
$r = rangespec( '-21..-18,-16..-15,-12..-11,-6' )->xor( rangespec('-inf..-9') );
ok( $r->to_string eq '-inf..-22,-17,-14..-13,-10..-9,-6');
$r = rangespec( '-1..2,4..5,8..9,14' )->xor( rangespec('6..+inf') );
ok( $r->to_string eq '-1..2,4..7,10..13,15..+inf' );
$r = rangespec( '-21..-18,-16..-15,-12..-11,-6' )->xor( rangespec('-14..+inf') );
ok( $r->to_string eq '-21..-18,-16..-13,-10..-7,-5..+inf' );

$r = rangespec( '-1..2,4..5,8..9,14' );
$r = $r->xor( rangespec( '-22..-15,-11,-8..4,8..10' ) );
ok( $r->to_string eq '-22..-15,-11,-8..-2,3,5,10,14' );
$r = rangespec( '-17..-16,4,6..9,10' );
$r = $r->xor( rangespec( '-15,-11..-3,10..14,17' ) );
ok( $r->to_string eq '-17..-15,-11..-3,4,6..9,11..14,17' );

# test implicit range collapsing
$r = rangespec( '1..2,3,4..5,6,7..8' );
ok($r);
$re = $r->regex;
ok($re);
ok( /^$re$/ ) for ( 1..8 );
ok( !/^$re$/ ) for ( 0,9 );
ok( $r->to_string eq '1..8' );

# test explicit range collapsing
my @ranges = (range( 1, 2 ), range(3, 3));
@ranges = Number::Range::Regex::CompoundRange::_collapse_ranges( @ranges );
ok(@ranges == 1);