use utf8;






package BNum;

use overload (
	'""'	=> \&_str,
	'<=>'	=> \&_cmp,
);

sub new {
	my $self = shift;
	bless [@_], ref $self || $self
}

sub flip {
	my @a = @{+shift};
	$a[2] = !$a[2];
	bless \@a
}

my $brackets = qw/ [ ( ) ] /;
sub _str {
	my $v = sprintf "%.2f", $_[0][0];
	$_[0][2]
		? $v . ($_[0][1] == 1 ? "]" : ")")
		: ($_[0][1] == 1 ? "(" : "[" ) . $v;
}

sub _cmp {
	my ($a, $b, $swap) = @_;

	
	if ($swap) { return -_ncmp($a, $b) }
	if (!ref $b || !$b->isa(__PACKAGE__)) { return _ncmp($a, $b) }

	$a->[0] <=> $b->[0] || $a->[1] <=> $b->[1]
}

sub _ncmp {
	
	my ($a, $b) = @_;
	$a->[0] <=> $b || $a->[1] <=> 0
}

package RealSet;
use Carp;
use overload (
	'""'	=> \&_str,
	'|'	=> \&_or,
	'&'	=> \&_and,
	'~'	=> \&_neg,
	'-'	=> \&_diff,
	'bool'	=> \&not_empty, 
);

my %pm = qw/ [ -1 ( 1 ) -1 ] 1 /;
sub range {
	my ($cls, $a, $b, $spec) = @_;
	$spec =~ /^( \[ | \( )( \) | \] )$/x	or croak "bad spec $spec";

	$a = BNum->new($a, $pm{$1}, 0);
	$b = BNum->new($b, $pm{$2}, 1);
	normalize($a < $b ? [$a, $b] : [])
}

sub normalize {
	my @a = @{+shift};
	
	
	
	for (my $i = $
		splice @a, $i - 1, 2
			if $a[$i] <= $a[$i - 1]
	}
	bless \@a
}

sub not_empty { scalar @{ normalize shift } }

sub _str {
	my (@a, @s) = @{+shift}		or return '()';
	join "  ", map { shift(@a).", ".shift(@a) } 0 .. $
}

sub _or {
	
	my $d = 0;
	normalize [
		map {	$_->[2] ? --$d ? () : ($_)
				: $d++ ? () : ($_) }
		sort{ $a <=> $b } @{+shift}, @{+shift}
	];
}

sub _neg {
	normalize [
		BNum->new('-inf', 1, 0),
		map($_->flip, @{+shift}),
		BNum->new('inf', -1, 1),
	]
}

sub _and {
	my $d = 0;
	normalize [
		map {	$_->[2] ? --$d ? ($_) : ()
				: $d++ ? ($_) : () }
		sort{ $a <=> $b } @{+shift}, @{+shift}
	];
}

sub _diff { shift() & ~shift() }

sub has {
	my ($a, $b) = @_;
	for (my $i = 0; $i < $
		return 1 if $a->[$i] <= $b && $b <= $a->[$i + 1]
	}
	return 0
}

sub len {
	my ($a, $l) = shift;
	for (my $i = 0; $i < $
		$l += $a->[$i+1][0] - $a->[$i][0]
	}
	return $l
}

package main;
use List::Util 'reduce';

sub rng { RealSet->range(@_) }
my @sets = (
	rng(0, 1, '(]') | rng(0, 2, '[)'),
	rng(0, 2, '[)') & rng(0, 2, '(]'),
	rng(0, 3, '[)') - rng(0, 1, '()'),
	rng(0, 3, '[)') - rng(0, 1, '[]'),
);

for my $i (0 .. $
	print "Set $i = ", $sets[$i], ": ";
	for (0 .. 2) {
		print "has $_; " if $sets[$i]->has($_);
	}
	print "\n";
}


print "\n
sub brev { 
	my $x = shift;
	return $x if length $x < 60;
	substr($x, 0, 30)." ... ".substr($x, -30, 30)
}


my $x = reduce { $a | $b }
	map(rng(sqrt($_ + 1./6), sqrt($_ + 5./6), '()'), 0 .. 101);
$x &= rng(0, 10, '()');

print "A\t", '= {x | 0 < x < 10 and |sin( x)| > 1/2 }',
	"\n\t= ", brev($x), "\n";

my $y = reduce { $a | $b }
	map { rng($_ + 1./6, $_ + 5./6, '()') } 0 .. 11;
$y &= rng(0, 10, '()');

print "B\t", '= {x | 0 < x < 10 and |sin( x)| > 1/2 }',
	"\n\t= ", brev($y), "\n";

my $z = $x - $y;
print "A - B\t= ", brev($z), "\n\tlength = ", $z->len, "\n";
print $z ? "not empty\n" : "empty\n";