| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Set::Scalar::Base; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 22 |  |  | 22 |  | 158 | use strict; | 
|  | 22 |  |  |  |  | 37 |  | 
|  | 22 |  |  |  |  | 987 |  | 
| 4 |  |  |  |  |  |  | # local $^W = 1; | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | require Exporter; | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 22 |  |  | 22 |  | 108 | use vars qw($VERSION @ISA @EXPORT_OK); | 
|  | 22 |  |  |  |  | 36 |  | 
|  | 22 |  |  |  |  | 7781 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | $VERSION = '1.29'; | 
| 11 |  |  |  |  |  |  | @ISA = qw(Exporter); | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | BEGIN { | 
| 14 | 22 |  |  | 22 |  | 1393 | eval 'require Scalar::Util'; | 
| 15 | 22 | 50 |  |  |  | 131 | unless ($@) { | 
| 16 | 22 |  |  |  |  | 9287 | import Scalar::Util qw(blessed refaddr); | 
| 17 |  |  |  |  |  |  | } else { | 
| 18 |  |  |  |  |  |  | # Use the pure Perl emulations (directly snagged from Scalar::Util). | 
| 19 | 0 |  |  |  |  | 0 | eval 'sub UNIVERSAL::a_sub_not_likely_to_be_here { ref($_[0]) }'; | 
| 20 |  |  |  |  |  |  | *blessed = sub ($) { | 
| 21 | 0 |  |  |  |  | 0 | local($@, $SIG{__DIE__}, $SIG{__WARN__}); | 
| 22 |  |  |  |  |  |  | length(ref($_[0])) | 
| 23 | 0 | 0 |  |  |  | 0 | ? eval { $_[0]->a_sub_not_likely_to_be_here } | 
|  | 0 |  |  |  |  | 0 |  | 
| 24 |  |  |  |  |  |  | : undef | 
| 25 | 0 |  |  |  |  | 0 | }; | 
| 26 |  |  |  |  |  |  | *refaddr = sub ($) { | 
| 27 | 0 | 0 |  |  |  | 0 | my $pkg = ref($_[0]) or return undef; | 
| 28 | 0 | 0 |  |  |  | 0 | if (blessed($_[0])) { | 
| 29 | 0 |  |  |  |  | 0 | bless $_[0], 'Scalar::Util::Fake'; | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  | else { | 
| 32 | 0 |  |  |  |  | 0 | $pkg = undef; | 
| 33 |  |  |  |  |  |  | } | 
| 34 | 0 |  |  |  |  | 0 | "$_[0]" =~ /0x(\w+)/; | 
| 35 | 0 |  |  |  |  | 0 | my $i = do { local $^W; hex $1 }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 36 | 0 | 0 |  |  |  | 0 | bless $_[0], $pkg if defined $pkg; | 
| 37 | 0 |  |  |  |  | 0 | $i; | 
| 38 | 0 |  |  |  |  | 0 | }; | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  | } | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | @EXPORT_OK = qw(_make_elements | 
| 43 |  |  |  |  |  |  | as_string | 
| 44 |  |  |  |  |  |  | as_string_callback | 
| 45 |  |  |  |  |  |  | _compare is_equal | 
| 46 |  |  |  |  |  |  | _binary_underload | 
| 47 |  |  |  |  |  |  | _unary_underload | 
| 48 |  |  |  |  |  |  | _strval); | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | use overload | 
| 51 |  |  |  |  |  |  | '+'		=> \&_union_overload, | 
| 52 |  |  |  |  |  |  | '*'		=> \&_intersection_overload, | 
| 53 |  |  |  |  |  |  | '-'		=> \&_difference_overload, | 
| 54 |  |  |  |  |  |  | 'neg'	=> \&_complement_overload, | 
| 55 |  |  |  |  |  |  | '%'		=> \&_symmetric_difference_overload, | 
| 56 |  |  |  |  |  |  | '/'		=> \&_unique_overload, | 
| 57 |  |  |  |  |  |  | 'eq'	=> \&is_equal, | 
| 58 |  |  |  |  |  |  | '=='	=> \&is_equal, | 
| 59 |  |  |  |  |  |  | '!='	=> \&is_disjoint, | 
| 60 |  |  |  |  |  |  | '<=>'	=> \&compare, | 
| 61 |  |  |  |  |  |  | '<'		=> \&is_proper_subset, | 
| 62 |  |  |  |  |  |  | '>'		=> \&is_proper_superset, | 
| 63 |  |  |  |  |  |  | '<='	=> \&is_subset, | 
| 64 |  |  |  |  |  |  | '>='	=> \&is_superset, | 
| 65 |  |  |  |  |  |  | 'bool'	=> \&size, | 
| 66 | 8 |  |  | 8 |  | 113 | '@{}'	=> sub { [ $_[0]->members ] }, | 
| 67 | 1 |  |  | 1 |  | 19 | '='         => sub { $_[0]->clone($_[1]) }, | 
| 68 | 22 |  |  | 22 |  | 52380 | 'cmp'       => sub { "$_[0]" cmp "$_[1]" }; | 
|  | 22 |  |  | 0 |  | 30405 |  | 
|  | 22 |  |  |  |  | 521 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 22 |  |  | 22 |  | 5153 | use constant OVERLOAD_BINARY_2ND_ARG  => 1; | 
|  | 22 |  |  |  |  | 54 |  | 
|  | 22 |  |  |  |  | 2592 |  | 
| 71 | 22 |  |  | 22 |  | 115 | use constant OVERLOAD_BINARY_REVERSED => 2; | 
|  | 22 |  |  |  |  | 46 |  | 
|  | 22 |  |  |  |  | 90730 |  | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | sub _binary_underload { # Handle overloaded binary operators. | 
| 74 | 4792 |  |  | 4792 |  | 5794 | my (@args) = @{ $_[0] }; | 
|  | 4792 |  |  |  |  | 16682 |  | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 4792 | 50 |  |  |  | 13324 | if (@args == 3) { | 
| 77 | 4792 | 100 |  |  |  | 12021 | $args[1] = (ref $args[0])->new( $args[1] ) unless ref $args[1]; | 
| 78 | 4792 | 100 |  |  |  | 13051 | @args[0, 1] = @args[1, 0] if $args[OVERLOAD_BINARY_REVERSED]; | 
| 79 | 4792 |  |  |  |  | 7574 | pop @args; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 4792 |  |  |  |  | 16777 | return @args; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | sub _unary_underload { # Handle overloaded unary operators. | 
| 86 | 775 | 50 |  | 775 |  | 1218 | if (@{ $_[0] } == 3) { | 
|  | 775 |  |  |  |  | 5600 |  | 
| 87 | 775 |  |  |  |  | 915 | pop @{ $_[0] }; | 
|  | 775 |  |  |  |  | 1327 |  | 
| 88 | 775 |  |  |  |  | 878 | pop @{ $_[0] }; | 
|  | 775 |  |  |  |  | 1540 |  | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 1 |  |  | 1 |  | 1 | sub _new_hook { | 
| 93 |  |  |  |  |  |  | # Just an empty stub. | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | sub new { | 
| 97 | 7435 |  |  | 7435 | 0 | 12475 | my $class = shift; | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 7435 |  |  |  |  | 12696 | my $self = { }; | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 7435 |  | 33 |  |  | 47546 | bless $self, ref $class || $class; | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 7435 |  |  |  |  | 32864 | $self->_new_hook( \@_ ); | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 7435 |  |  |  |  | 26059 | return $self; | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | sub _strval { | 
| 109 | 66989 |  |  | 66989 |  | 95221 | my $class = ref $_[0]; | 
| 110 | 66989 | 100 |  |  |  | 283433 | return $_[0] unless $class; | 
| 111 | 18291 |  |  |  |  | 112376 | sprintf "%s(%s)", $class, refaddr $_[0]; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | sub _make_elements { | 
| 115 | 20463 | 50 |  | 20463 |  | 48034 | return map { (defined $_ ? _strval($_) : "") => $_ } @_; | 
|  | 48485 |  |  |  |  | 132195 |  | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | sub _invalidate_cached { | 
| 119 | 34531 |  |  | 34531 |  | 66787 | my $self = shift; | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 34531 |  |  |  |  | 42035 | delete @{ $self }{ "as_string" }; | 
|  | 34531 |  |  |  |  | 131614 |  | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 706 |  |  | 706 |  | 1611 | sub _insert_hook { | 
| 125 |  |  |  |  |  |  | # Just an empty stub. | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub _insert { | 
| 129 | 13376 |  |  | 13376 |  | 19319 | my $self     = shift; | 
| 130 | 13376 |  |  |  |  | 16707 | my $elements = shift; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 13376 |  |  |  |  | 34588 | $self->_insert_hook( $elements ); | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | sub _insert_elements { | 
| 136 | 25364 |  |  | 25364 |  | 32490 | my $self     = shift; | 
| 137 | 25364 |  |  |  |  | 29297 | my $elements = shift; | 
| 138 |  |  |  |  |  |  |  | 
| 139 | 25364 |  |  |  |  | 56421 | @{ $self->{'elements'} }{ keys %$elements } = values %$elements; | 
|  | 25364 |  |  |  |  | 83558 |  | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 25364 |  |  |  |  | 63061 | $self->_invalidate_cached; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub universe { | 
| 145 | 32955 |  |  | 32955 | 0 | 44719 | my $self = shift; | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 32955 |  |  |  |  | 106945 | return $self->{'universe'}; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | sub size { | 
| 151 | 34970 |  |  | 34970 | 0 | 45166 | my $self = shift; | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 34970 |  |  |  |  | 41194 | return scalar keys %{ $self->{'elements'} }; | 
|  | 34970 |  |  |  |  | 157658 |  | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | sub elements { | 
| 157 | 13167 |  |  | 13167 | 0 | 16066 | my $self = shift; | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 3 |  |  |  |  | 51 | return @_ ? | 
| 160 | 3 |  |  |  |  | 20 | @{ $self->{'elements'} }{ map { _strval($_) } @_ } : | 
|  | 13164 |  |  |  |  | 62025 |  | 
| 161 | 13167 | 100 |  |  |  | 38572 | values %{ $self->{'elements'} }; | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | *members = \&elements; | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub element { | 
| 167 | 3 |  |  | 3 | 0 | 23 | my $self = shift; | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 3 |  |  |  |  | 12 | $self->elements( shift ); | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | *member   = \&element; | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | sub has { | 
| 175 | 3 |  |  | 3 | 0 | 24 | my $self = shift; | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 3 |  |  |  |  | 16 | my @has = map { exists $self->{'elements'}->{ $_ } } @_; | 
|  | 3 |  |  |  |  | 16 |  | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 3 | 50 |  |  |  | 17 | return wantarray ? @has : @_ > 1 ? grep { $_ } @has : $has[0]; | 
|  | 0 | 50 |  |  |  | 0 |  | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | *contains = \&has; | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub each { | 
| 185 | 7 |  |  | 7 | 0 | 43 | my $self = shift; | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 7 |  |  |  |  | 8 | my ($k, $e) = each %{ $self->{'elements'} }; | 
|  | 7 |  |  |  |  | 21 |  | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 7 |  |  |  |  | 20 | return $e; | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub _empty_clone { | 
| 193 | 6016 |  |  | 6016 |  | 7526 | my $self     = shift; | 
| 194 | 6016 |  |  |  |  | 6907 | my $original = shift; | 
| 195 |  |  |  |  |  |  |  | 
| 196 | 6016 |  |  |  |  | 10741 | $self->{'universe'} = $original->{'universe'}; | 
| 197 | 6016 |  |  |  |  | 13840 | $self->{'null'    } = $original->{'null'    }; | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | sub _clone { | 
| 201 | 5990 |  |  | 5990 |  | 8369 | my $self     = shift; | 
| 202 | 5990 |  |  |  |  | 6820 | my $original = shift; | 
| 203 |  |  |  |  |  |  |  | 
| 204 | 5990 |  |  |  |  | 10933 | $self->_empty_clone($original); | 
| 205 |  |  |  |  |  |  |  | 
| 206 | 5990 |  |  |  |  | 18065 | $self->_insert( $original->{'elements'} ); | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | sub clone { | 
| 210 | 5990 |  |  | 5990 | 0 | 7422 | my $self  = shift; | 
| 211 | 5990 |  |  |  |  | 38936 | my $clone = (ref $self)->new; | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 5990 |  |  |  |  | 14962 | $clone->_clone( $self ); | 
| 214 |  |  |  |  |  |  |  | 
| 215 | 5990 |  |  |  |  | 13293 | return $clone; | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | *copy = \&clone; | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | sub empty_clone { | 
| 221 | 26 |  |  | 26 | 0 | 29 | my $self  = shift; | 
| 222 | 26 |  |  |  |  | 62 | my $clone = (ref $self)->new; | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 26 |  |  |  |  | 56 | $clone->_empty_clone( $self ); | 
| 225 |  |  |  |  |  |  |  | 
| 226 | 26 |  |  |  |  | 66 | return $clone; | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | sub clear { | 
| 230 | 4 |  |  | 4 | 0 | 3 | my $self = shift; | 
| 231 |  |  |  |  |  |  |  | 
| 232 | 4 |  |  |  |  | 3 | undef %{ $self }; | 
|  | 4 |  |  |  |  | 10 |  | 
| 233 | 4 |  |  |  |  | 5 | undef @{ $self }{ "as_string" }; | 
|  | 4 |  |  |  |  | 13 |  | 
| 234 |  |  |  |  |  |  | } | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | sub _union ($$) { | 
| 237 | 2388 |  |  | 2388 |  | 3799 | my ($this, $that) = @_; | 
| 238 |  |  |  |  |  |  |  | 
| 239 | 2388 |  |  |  |  | 5288 | my $this_universe = $this->universe; | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 2388 | 50 |  |  |  | 5904 | return (undef,          1, undef) | 
| 242 |  |  |  |  |  |  | unless $this_universe == $that->universe; | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 2388 | 100 |  |  |  | 6551 | return ($this->clone,   0, ref $this) | 
| 245 |  |  |  |  |  |  | if $that->is_null; | 
| 246 |  |  |  |  |  |  |  | 
| 247 | 1768 | 100 |  |  |  | 6488 | return ($that->clone,   0, ref $that) | 
| 248 |  |  |  |  |  |  | if $this->is_null; | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 1455 | 100 |  |  |  | 3451 | return ($this, 1, ref $this) | 
| 251 |  |  |  |  |  |  | if $this->is_universal; | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 1106 | 100 |  |  |  | 2647 | return ($that, 1, ref $that) | 
| 254 |  |  |  |  |  |  | if $that->is_universal; | 
| 255 |  |  |  |  |  |  |  | 
| 256 | 833 |  |  |  |  | 2688 | my $union = $this->clone; | 
| 257 |  |  |  |  |  |  |  | 
| 258 | 833 |  |  |  |  | 1809 | $union->insert( $that->elements ); | 
| 259 |  |  |  |  |  |  |  | 
| 260 | 833 |  |  |  |  | 1935 | return ($union, $union->is_universal, ref $this); | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | sub _union_overload { | 
| 264 | 2383 |  |  | 2383 |  | 7325 | my ($this, $that) = _binary_underload( \@_ ); | 
| 265 |  |  |  |  |  |  |  | 
| 266 | 2383 |  |  |  |  | 6258 | my ($union, $is_universal, $class) = $this->_union( $that ); | 
| 267 |  |  |  |  |  |  |  | 
| 268 | 2383 |  |  |  |  | 11288 | return $union; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub union { | 
| 272 | 9 |  |  | 9 | 0 | 41 | my $self = shift; | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 9 |  |  |  |  | 35 | my $union = $self->clone; | 
| 275 |  |  |  |  |  |  |  | 
| 276 | 9 |  |  |  |  | 13 | my $is_universal; | 
| 277 |  |  |  |  |  |  | my $class; | 
| 278 |  |  |  |  |  |  |  | 
| 279 | 9 |  |  |  |  | 23 | foreach my $next ( @_ ) { | 
| 280 | 5 | 50 |  |  |  | 21 | unless ($next->is_null) { | 
| 281 | 5 |  |  |  |  | 22 | ($union, $is_universal, $class) = $union->_union( $next ); | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 5 | 100 |  |  |  | 26 | last if $is_universal; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 9 | 100 | 100 |  |  | 33 | $union = $self | 
| 288 |  |  |  |  |  |  | if $is_universal && $union->size == $self->size; | 
| 289 |  |  |  |  |  |  |  | 
| 290 | 9 |  |  |  |  | 45 | return $union; | 
| 291 |  |  |  |  |  |  | } | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | sub _intersection ($$) { | 
| 294 | 4152 |  |  | 4152 |  | 5778 | my $this = shift; | 
| 295 | 4152 |  |  |  |  | 4768 | my $that = shift; | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 4152 | 50 |  |  |  | 9160 | return (undef,        1) | 
| 298 |  |  |  |  |  |  | unless $this->universe == $that->universe; | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 4152 | 100 | 100 |  |  | 12308 | return ($this->null,  1) | 
| 301 |  |  |  |  |  |  | if $this->is_null || $that->is_null; | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 2597 | 100 |  |  |  | 6797 | return ($this->clone, 0) | 
| 304 |  |  |  |  |  |  | if $that->is_universal; | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 2134 | 100 |  |  |  | 5501 | return ($that->clone, 0) | 
| 307 |  |  |  |  |  |  | if $this->is_universal; | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 1938 |  |  |  |  | 5112 | my $intersection = $this->clone; | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 1938 |  |  |  |  | 5555 | my %intersection = _make_elements $intersection->elements; | 
| 312 |  |  |  |  |  |  |  | 
| 313 | 1938 |  |  |  |  | 3426 | delete @intersection{ keys %{{ _make_elements $that->elements }} }; | 
|  | 1938 |  |  |  |  | 3909 |  | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 1938 |  |  |  |  | 10279 | $intersection->delete( values %intersection ); | 
| 316 |  |  |  |  |  |  |  | 
| 317 | 1938 |  |  |  |  | 4998 | return ($intersection, $intersection->is_null); | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | sub _intersection_overload { | 
| 321 | 2381 |  |  | 2381 |  | 6164 | my ($this, $that) = _binary_underload( \@_ ); | 
| 322 |  |  |  |  |  |  |  | 
| 323 | 2381 |  |  |  |  | 7166 | my ($intersection) = $this->_intersection( $that ); | 
| 324 |  |  |  |  |  |  |  | 
| 325 | 2381 |  |  |  |  | 14786 | return $intersection; | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | sub intersection { | 
| 329 | 1962 |  |  | 1962 | 0 | 2596 | my $self = shift; | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 1962 |  |  |  |  | 8691 | my $intersection = $self->clone; | 
| 332 |  |  |  |  |  |  |  | 
| 333 | 1962 |  |  |  |  | 2455 | my $is_null; | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 1962 |  |  |  |  | 3854 | foreach my $next ( @_ ) { | 
| 336 | 1962 | 100 |  |  |  | 3902 | unless ($next->is_universal) { | 
| 337 | 1771 |  |  |  |  | 4346 | ($intersection, $is_null) =	$intersection->_intersection( $next ); | 
| 338 |  |  |  |  |  |  |  | 
| 339 | 1771 | 100 |  |  |  | 6370 | last if $is_null; | 
| 340 |  |  |  |  |  |  | } | 
| 341 |  |  |  |  |  |  | } | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 1962 | 100 | 100 |  |  | 6979 | $intersection = $self | 
| 344 |  |  |  |  |  |  | if $is_null && $intersection->size == $self->size; | 
| 345 |  |  |  |  |  |  |  | 
| 346 | 1962 |  |  |  |  | 4040 | return $intersection; | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | sub _difference ($$) { | 
| 350 | 12 |  |  | 12 |  | 15 | my $this = shift; | 
| 351 | 12 |  |  |  |  | 14 | my $that = shift; | 
| 352 |  |  |  |  |  |  |  | 
| 353 | 12 | 50 |  |  |  | 23 | return undef        unless $this->universe == $that->universe; | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 12 | 100 | 100 |  |  | 33 | return $this->null  if $this->is_null || $that->is_universal; | 
| 356 | 10 | 50 |  |  |  | 27 | return $this->clone if $that->is_null; | 
| 357 |  |  |  |  |  |  |  | 
| 358 | 10 |  |  |  |  | 24 | my $difference = $this->clone; | 
| 359 |  |  |  |  |  |  |  | 
| 360 | 10 |  |  |  |  | 31 | my %that = _make_elements $that->elements; | 
| 361 |  |  |  |  |  |  |  | 
| 362 | 10 |  |  |  |  | 59 | $difference->delete( values %that ); | 
| 363 |  |  |  |  |  |  |  | 
| 364 | 10 |  |  |  |  | 48 | return $difference; | 
| 365 |  |  |  |  |  |  | } | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | sub _difference_overload { | 
| 368 | 8 |  |  | 8 |  | 31 | my ($this, $that) = _binary_underload( \@_ ); | 
| 369 |  |  |  |  |  |  |  | 
| 370 | 8 |  |  |  |  | 24 | return $this->_difference( $that ); | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | sub difference { | 
| 374 | 4 |  |  | 4 | 0 | 25 | my $this = shift; | 
| 375 |  |  |  |  |  |  |  | 
| 376 | 4 | 50 |  |  |  | 13 | return $this->null if $this->is_null; | 
| 377 |  |  |  |  |  |  |  | 
| 378 | 4 | 50 |  |  |  | 23 | return $this->clone unless @_; | 
| 379 |  |  |  |  |  |  |  | 
| 380 | 4 |  |  |  |  | 6 | my $that = shift; | 
| 381 |  |  |  |  |  |  |  | 
| 382 | 4 |  |  |  |  | 16 | $that = $that->union( @_ ); | 
| 383 |  |  |  |  |  |  |  | 
| 384 | 4 | 50 |  |  |  | 13 | return undef unless defined $that; | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 4 | 50 |  |  |  | 13 | return $this->null if $that->is_universal; | 
| 387 |  |  |  |  |  |  |  | 
| 388 | 4 |  |  |  |  | 23 | my $difference = $this->_difference( $that ); | 
| 389 |  |  |  |  |  |  |  | 
| 390 | 4 | 100 |  |  |  | 19 | $difference = $this | 
| 391 |  |  |  |  |  |  | if $difference->size == $this->size; | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 4 |  |  |  |  | 15 | return $difference; | 
| 394 |  |  |  |  |  |  | } | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | sub _symmetric_difference ($$) { | 
| 397 | 7 |  |  | 7 |  | 9 | my $this = shift; | 
| 398 | 7 |  |  |  |  | 9 | my $that = shift; | 
| 399 |  |  |  |  |  |  |  | 
| 400 | 7 | 50 |  |  |  | 14 | return (undef, 1) unless $this->universe == $that->universe; | 
| 401 |  |  |  |  |  |  |  | 
| 402 | 7 | 50 |  |  |  | 25 | return $that->clone      if $this->is_null; | 
| 403 | 7 | 50 |  |  |  | 18 | return $this->clone      if $that->is_null; | 
| 404 |  |  |  |  |  |  |  | 
| 405 | 7 | 50 |  |  |  | 23 | return $that->complement if $this->is_universal; | 
| 406 | 7 | 50 |  |  |  | 17 | return $this->complement if $that->is_universal; | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 7 |  |  |  |  | 18 | my $symmetric_difference = $this->clone; | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 7 |  |  |  |  | 17 | $symmetric_difference->invert( $that->elements ); | 
| 411 |  |  |  |  |  |  |  | 
| 412 | 7 |  |  |  |  | 36 | return $symmetric_difference; | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | sub _symmetric_difference_overload { | 
| 416 | 7 |  |  | 7 |  | 53 | my ($this, $that ) = _binary_underload( \@_ ); | 
| 417 |  |  |  |  |  |  |  | 
| 418 | 7 |  |  |  |  | 23 | return $this->_symmetric_difference( $that ); | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | sub symmetric_difference { | 
| 422 | 2 |  |  | 2 | 0 | 18 | my $this = shift; | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 2 |  |  |  |  | 10 | my $symmetric_difference = $this->clone; | 
| 425 |  |  |  |  |  |  |  | 
| 426 | 2 |  |  |  |  | 5 | foreach my $next ( @_ ) { | 
| 427 | 2 |  |  |  |  | 10 | $symmetric_difference->invert( $next->elements ); | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  |  | 
| 430 | 2 |  |  |  |  | 6 | return $symmetric_difference; | 
| 431 |  |  |  |  |  |  | } | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | *symmdiff = \&symmetric_difference; | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | sub _complement { | 
| 436 | 775 |  |  | 775 |  | 972 | my $self       = shift; | 
| 437 | 775 |  |  |  |  | 1814 | my $complement = (ref $self)->new( $self->universe->elements ); | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 775 |  |  |  |  | 2685 | $complement->delete( $self->elements ); | 
| 440 |  |  |  |  |  |  |  | 
| 441 | 775 |  |  |  |  | 4180 | return $complement; | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | sub _complement_overload { | 
| 445 | 775 |  |  | 775 |  | 2990 | _unary_underload( \@_ ); | 
| 446 |  |  |  |  |  |  |  | 
| 447 | 775 |  |  |  |  | 1147 | my $self = shift; | 
| 448 |  |  |  |  |  |  |  | 
| 449 | 775 |  |  |  |  | 1943 | return $self->_complement; | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | sub complement { | 
| 453 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 454 |  |  |  |  |  |  |  | 
| 455 | 0 |  |  |  |  | 0 | return $self->_complement; | 
| 456 |  |  |  |  |  |  | } | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | sub _unique { | 
| 459 | 4 |  |  | 4 |  | 14 | my $universe = $_[0]->universe; | 
| 460 | 4 |  |  |  |  | 6 | my %frequency; | 
| 461 |  |  |  |  |  |  |  | 
| 462 | 4 |  |  |  |  | 7 | for my $set ( @_ ) { | 
| 463 | 8 | 50 |  |  |  | 83 | if ($set->universe == $universe) { | 
| 464 | 8 |  |  |  |  | 10 | foreach my $element ( keys %{ $set->{'elements'} } ) { | 
|  | 8 |  |  |  |  | 24 |  | 
| 465 | 35 |  |  |  |  | 58 | $frequency{ $element }++; | 
| 466 |  |  |  |  |  |  | } | 
| 467 |  |  |  |  |  |  | } else { | 
| 468 | 0 |  |  |  |  | 0 | return (ref $_[0])->new(); | 
| 469 |  |  |  |  |  |  | } | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  |  | 
| 472 | 4 |  |  |  |  | 11 | return (ref $_[0])->new(grep { $frequency{ $_ } == 1 } keys %frequency); | 
|  | 24 |  |  |  |  | 41 |  | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | sub _unique_overload { | 
| 476 | 0 |  |  | 0 |  | 0 | my ($this, $that) = _binary_underload( \@_ ); | 
| 477 |  |  |  |  |  |  |  | 
| 478 | 0 |  |  |  |  | 0 | return $this->_unique( $that ); | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | sub unique { | 
| 482 | 4 |  |  | 4 | 0 | 26 | my $this = shift; | 
| 483 |  |  |  |  |  |  |  | 
| 484 | 4 |  |  |  |  | 15 | return $this->_unique( @_ ); | 
| 485 |  |  |  |  |  |  | } | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | sub _make_cartesian_product_iterator { | 
| 488 | 6 |  |  | 6 |  | 6 | my @iter; | 
| 489 |  |  |  |  |  |  | my @value; | 
| 490 | 6 |  |  |  |  | 13 | for my $set (@_) { | 
| 491 | 12 | 50 |  |  |  | 99 | return unless $set->isa('Set::Scalar'); | 
| 492 | 12 |  |  |  |  | 30 | my @member = $set->members; | 
| 493 | 12 |  |  |  |  | 18 | my %member; | 
| 494 | 12 |  |  |  |  | 51 | @member{@member} = @member; | 
| 495 | 12 |  |  |  |  | 21 | push @iter, \%member; | 
| 496 | 12 |  |  |  |  | 13 | push @value, scalar CORE::each(%{ $iter[-1] }); | 
|  | 12 |  |  |  |  | 48 |  | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  | return sub { | 
| 499 | 54 | 100 |  | 54 |  | 138 | return unless @iter; | 
| 500 | 48 |  |  |  |  | 110 | my @now = @value; | 
| 501 | 48 |  |  |  |  | 53 | my $ix; | 
| 502 | 48 |  |  |  |  | 178 | for ($ix = $#iter; $ix >= 0; $ix--) { | 
| 503 | 64 |  |  |  |  | 60 | my $next = CORE::each(%{ $iter[$ix] }); | 
|  | 64 |  |  |  |  | 115 |  | 
| 504 | 64 | 100 |  |  |  | 124 | if (defined $next) { | 
| 505 | 42 |  |  |  |  | 53 | $value[$ix] = $next; | 
| 506 | 42 |  |  |  |  | 59 | last; | 
| 507 |  |  |  |  |  |  | } else { | 
| 508 | 22 |  |  |  |  | 22 | keys %{ $iter[$ix] };  # Reset the iterator. | 
|  | 22 |  |  |  |  | 32 |  | 
| 509 | 22 |  |  |  |  | 20 | $value[$ix] = CORE::each(%{ $iter[$ix] }); | 
|  | 22 |  |  |  |  | 79 |  | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  | } | 
| 512 | 48 | 100 |  |  |  | 98 | if ($ix < 0) { | 
| 513 | 6 |  |  |  |  | 19 | @iter = ();  # All done. | 
| 514 |  |  |  |  |  |  | } | 
| 515 | 48 |  |  |  |  | 187 | return @now; | 
| 516 | 6 |  |  |  |  | 30 | }; | 
| 517 |  |  |  |  |  |  | } | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | sub cartesian_product_iterator { | 
| 520 | 6 | 100 |  | 6 | 0 | 16 | shift unless ref $_[0]; | 
| 521 | 6 |  |  |  |  | 25 | return &_make_cartesian_product_iterator; | 
| 522 |  |  |  |  |  |  | } | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | sub cartesian_product { | 
| 525 | 6 |  |  | 6 | 0 | 33 | my $iterator = &cartesian_product_iterator; | 
| 526 | 6 | 50 |  |  |  | 16 | return unless defined $iterator; | 
| 527 | 6 |  |  |  |  | 23 | my $product = $_[0]->empty_clone; | 
| 528 | 6 |  |  |  |  | 11 | while (my @member = $iterator->()) { | 
| 529 | 48 |  |  |  |  | 149 | $product->insert(\@member); | 
| 530 |  |  |  |  |  |  | } | 
| 531 | 6 |  |  |  |  | 54 | return $product; | 
| 532 |  |  |  |  |  |  | } | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | sub _make_power_set_iterator { | 
| 535 | 3 | 50 |  | 3 |  | 18 | return unless $_[0]->isa('Set::Scalar'); | 
| 536 | 3 |  |  |  |  | 18 | my @member = $_[0]->members; | 
| 537 | 3 |  |  |  |  | 7 | my @iter   = (0) x @member; | 
| 538 |  |  |  |  |  |  | return sub { | 
| 539 | 17 | 100 |  | 17 |  | 35 | return unless @iter; | 
| 540 | 16 |  |  |  |  | 14 | my $ix; | 
| 541 | 16 |  |  |  |  | 35 | for ($ix = 0; $ix < @iter; $ix++) { | 
| 542 | 28 | 100 |  |  |  | 44 | if ($iter[$ix]++ == 0) { | 
| 543 | 14 |  |  |  |  | 17 | last; | 
| 544 |  |  |  |  |  |  | } else { | 
| 545 | 14 |  |  |  |  | 29 | $iter[$ix] = 0; | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  | } | 
| 548 | 16 | 100 |  |  |  | 29 | if ($ix == @iter) { | 
| 549 | 2 |  |  |  |  | 3 | @iter = ();  # All done. | 
| 550 |  |  |  |  |  |  | } | 
| 551 | 16 |  |  |  |  | 27 | return map { $member[$_] } grep { $iter[$_] } 0..$#iter; | 
|  | 24 |  |  |  |  | 47 |  | 
|  | 42 |  |  |  |  | 52 |  | 
| 552 | 3 |  |  |  |  | 13 | }; | 
| 553 |  |  |  |  |  |  | } | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | sub power_set_iterator { | 
| 556 | 3 | 100 |  | 3 | 0 | 7 | shift unless ref $_[0]; | 
| 557 | 3 |  |  |  |  | 10 | return &_make_power_set_iterator; | 
| 558 |  |  |  |  |  |  | } | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | sub power_set { | 
| 561 | 3 |  |  | 3 | 0 | 18 | my $iterator = &power_set_iterator; | 
| 562 | 3 | 50 |  |  |  | 8 | return unless defined $iterator; | 
| 563 | 3 |  |  |  |  | 9 | my $power = $_[0]->empty_clone; | 
| 564 | 3 |  |  |  |  | 3 | my @member; | 
| 565 | 3 |  |  |  |  | 4 | do { | 
| 566 | 17 |  |  |  |  | 25 | @member = $iterator->(); | 
| 567 | 17 |  |  |  |  | 36 | $power->insert($_[0]->empty_clone->insert(@member)); | 
| 568 |  |  |  |  |  |  | } while (@member); | 
| 569 | 3 |  |  |  |  | 20 | return $power; | 
| 570 |  |  |  |  |  |  | } | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | sub is_universal { | 
| 573 | 10131 |  |  | 10131 | 0 | 15197 | my $self = shift; | 
| 574 |  |  |  |  |  |  |  | 
| 575 | 10131 |  |  |  |  | 19023 | return $self->size == $self->universe->size; | 
| 576 |  |  |  |  |  |  | } | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | sub is_null { | 
| 579 | 13306 |  |  | 13306 | 0 | 19218 | my $self = shift; | 
| 580 |  |  |  |  |  |  |  | 
| 581 | 13306 |  |  |  |  | 32678 | return $self->size == 0; | 
| 582 |  |  |  |  |  |  | } | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | *is_empty = \&is_null; | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | sub null { | 
| 587 | 1457 |  |  | 1457 | 0 | 1981 | my $self = shift; | 
| 588 |  |  |  |  |  |  |  | 
| 589 | 1457 |  |  |  |  | 3771 | return $self->universe->null; | 
| 590 |  |  |  |  |  |  | } | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | *empty = \&null; | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | sub _compare { | 
| 595 | 9120 |  |  | 9120 |  | 13480 | my $a = shift; | 
| 596 | 9120 |  |  |  |  | 10586 | my $b = shift; | 
| 597 |  |  |  |  |  |  |  | 
| 598 | 9120 | 100 |  |  |  | 64993 | return "$a" eq "$b" ? 'equal' : 'different'; | 
| 599 |  |  |  |  |  |  | } | 
| 600 |  |  |  |  |  |  |  | 
| 601 |  |  |  |  |  |  | sub compare { | 
| 602 | 2081 |  |  | 2081 | 0 | 2937 | my $a = shift; | 
| 603 | 2081 |  |  |  |  | 3443 | my $b = shift; | 
| 604 |  |  |  |  |  |  |  | 
| 605 | 2081 | 100 | 33 |  |  | 29221 | return _compare("$a", "$b") | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 606 |  |  |  |  |  |  | unless ref $a && $a->isa(__PACKAGE__) && | 
| 607 |  |  |  |  |  |  | ref $b && $b->isa(__PACKAGE__); | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 1957 | 50 |  |  |  | 5235 | return 'disjoint universes' unless $a->universe == $b->universe; | 
| 610 |  |  |  |  |  |  |  | 
| 611 | 1957 |  |  |  |  | 6808 | my $c = $a->intersection($b); | 
| 612 |  |  |  |  |  |  |  | 
| 613 | 1957 |  |  |  |  | 5153 | my $na = $a->size; | 
| 614 | 1957 |  |  |  |  | 16584 | my $nb = $b->size; | 
| 615 | 1957 |  |  |  |  | 4484 | my $nc = $c->size; | 
| 616 |  |  |  |  |  |  |  | 
| 617 | 1957 | 100 | 100 |  |  | 17957 | return 'proper superset' if $na && $nb == 0; | 
| 618 | 1954 | 100 | 100 |  |  | 6908 | return 'proper subset'   if $na == 0 && $nb; | 
| 619 | 1953 | 100 | 66 |  |  | 12529 | return 'disjoint'        if $na && $nb && $nc == 0; | 
|  |  |  | 100 |  |  |  |  | 
| 620 | 1949 | 100 | 100 |  |  | 13182 | return 'equal'           if $na == $nc && $nb == $nc; | 
| 621 | 6 | 100 |  |  |  | 22 | return 'proper superset' if $nb == $nc; | 
| 622 | 3 | 100 |  |  |  | 11 | return 'proper subset'   if $na == $nc; | 
| 623 | 1 |  |  |  |  | 4 | return 'proper intersect'; | 
| 624 |  |  |  |  |  |  | } | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | sub is_disjoint { | 
| 627 | 2 |  |  | 2 | 0 | 20 | my $a = shift; | 
| 628 | 2 |  |  |  |  | 4 | my $b = shift; | 
| 629 |  |  |  |  |  |  |  | 
| 630 | 2 |  | 33 |  |  | 6 | return $a->compare($b) eq 'disjoint' || | 
| 631 |  |  |  |  |  |  | $a->compare($b) eq 'disjoint universes'; | 
| 632 |  |  |  |  |  |  | } | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | sub is_equal { | 
| 635 | 2065 |  |  | 2065 | 0 | 292150 | my $a = shift; | 
| 636 | 2065 |  |  |  |  | 2852 | my $b = shift; | 
| 637 |  |  |  |  |  |  |  | 
| 638 | 2065 |  |  |  |  | 5550 | return $a->compare($b) eq 'equal'; | 
| 639 |  |  |  |  |  |  | } | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | sub is_proper_subset { | 
| 642 | 3 |  |  | 3 | 0 | 19 | my $a = shift; | 
| 643 | 3 |  |  |  |  | 5 | my $b = shift; | 
| 644 |  |  |  |  |  |  |  | 
| 645 | 3 |  |  |  |  | 6 | return $a->compare($b) eq 'proper subset'; | 
| 646 |  |  |  |  |  |  | } | 
| 647 |  |  |  |  |  |  |  | 
| 648 |  |  |  |  |  |  | sub is_proper_superset { | 
| 649 | 3 |  |  | 3 | 0 | 23 | my $a = shift; | 
| 650 | 3 |  |  |  |  | 4 | my $b = shift; | 
| 651 |  |  |  |  |  |  |  | 
| 652 | 3 |  |  |  |  | 16 | return $a->compare($b) eq 'proper superset'; | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | sub is_properly_intersecting { | 
| 656 | 0 |  |  | 0 | 0 | 0 | my $a = shift; | 
| 657 | 0 |  |  |  |  | 0 | my $b = shift; | 
| 658 |  |  |  |  |  |  |  | 
| 659 | 0 |  |  |  |  | 0 | return $a->compare($b) eq 'proper intersect'; | 
| 660 |  |  |  |  |  |  | } | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | sub is_subset { | 
| 663 | 3 |  |  | 3 | 0 | 21 | my $a = shift; | 
| 664 | 3 |  |  |  |  | 4 | my $b = shift; | 
| 665 |  |  |  |  |  |  |  | 
| 666 | 3 |  |  |  |  | 10 | my $c = $a->compare($b); | 
| 667 |  |  |  |  |  |  |  | 
| 668 | 3 |  | 66 |  |  | 18 | return $c eq 'equal' || $c eq 'proper subset'; | 
| 669 |  |  |  |  |  |  | } | 
| 670 |  |  |  |  |  |  |  | 
| 671 |  |  |  |  |  |  | sub is_superset { | 
| 672 | 3 |  |  | 3 | 0 | 19 | my $a = shift; | 
| 673 | 3 |  |  |  |  | 5 | my $b = shift; | 
| 674 |  |  |  |  |  |  |  | 
| 675 | 3 |  |  |  |  | 12 | my $c = $a->compare($b); | 
| 676 |  |  |  |  |  |  |  | 
| 677 | 3 |  | 66 |  |  | 21 | return $c eq 'equal' || $c eq 'proper superset'; | 
| 678 |  |  |  |  |  |  | } | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | sub cmp { | 
| 681 | 0 |  |  | 0 | 0 | 0 | return "$_[0]" cmp "$_[1]"; | 
| 682 |  |  |  |  |  |  | } | 
| 683 |  |  |  |  |  |  |  | 
| 684 |  |  |  |  |  |  | sub have_same_universe { | 
| 685 | 0 |  |  | 0 | 0 | 0 | my $self     = shift; | 
| 686 | 0 |  |  |  |  | 0 | my $universe = $self->universe; | 
| 687 |  |  |  |  |  |  |  | 
| 688 | 0 |  |  |  |  | 0 | foreach my $set ( @_ ) { | 
| 689 | 0 | 0 |  |  |  | 0 | return 0 unless $set->universe == $universe; | 
| 690 |  |  |  |  |  |  | } | 
| 691 |  |  |  |  |  |  |  | 
| 692 | 0 |  |  |  |  | 0 | return 1; | 
| 693 |  |  |  |  |  |  | } | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | sub _elements_have_reference { | 
| 696 | 147 |  |  | 147 |  | 170 | my $self     = shift; | 
| 697 | 147 |  |  |  |  | 151 | my $elements = shift; | 
| 698 |  |  |  |  |  |  |  | 
| 699 | 147 |  |  |  |  | 247 | foreach my $element (@$elements) { | 
| 700 | 380 | 100 |  |  |  | 793 | return 1 if ref $element; | 
| 701 |  |  |  |  |  |  | } | 
| 702 |  |  |  |  |  |  |  | 
| 703 | 122 |  |  |  |  | 233 | return 0; | 
| 704 |  |  |  |  |  |  | } | 
| 705 |  |  |  |  |  |  |  | 
| 706 | 22 |  |  | 22 |  | 229 | use constant RECURSIVE_SELF => 1; | 
|  | 22 |  |  |  |  | 310 |  | 
|  | 22 |  |  |  |  | 1383 |  | 
| 707 | 22 |  |  | 22 |  | 112 | use constant RECURSIVE_DEEP => 2; | 
|  | 22 |  |  |  |  | 36 |  | 
|  | 22 |  |  |  |  | 27707 |  | 
| 708 |  |  |  |  |  |  |  | 
| 709 |  |  |  |  |  |  | sub _elements_as_string { | 
| 710 | 147 |  |  | 147 |  | 213 | my $self    = shift; | 
| 711 | 147 |  |  |  |  | 5857 | my $history = shift; | 
| 712 |  |  |  |  |  |  |  | 
| 713 | 147 |  |  |  |  | 319 | my @elements = $self->elements; | 
| 714 | 147 |  |  |  |  | 304 | my $self_id  = _strval($self); | 
| 715 | 147 |  |  |  |  | 203 | my %history; | 
| 716 |  |  |  |  |  |  |  | 
| 717 | 147 | 100 |  |  |  | 303 | %history = %{ $history } if defined $history; | 
|  | 19 |  |  |  |  | 69 |  | 
| 718 |  |  |  |  |  |  |  | 
| 719 | 147 |  |  |  |  | 420 | my $have_reference = $self->_elements_have_reference(\@elements); | 
| 720 |  |  |  |  |  |  |  | 
| 721 | 147 |  |  |  |  | 176 | my @simple_elements; | 
| 722 |  |  |  |  |  |  | my @complex_elements; | 
| 723 | 0 |  |  |  |  | 0 | my $recursive; | 
| 724 |  |  |  |  |  |  |  | 
| 725 | 147 |  |  |  |  | 204 | foreach my $element (@elements) { | 
| 726 | 398 |  |  |  |  | 616 | my $element_id = _strval($element); | 
| 727 |  |  |  |  |  |  |  | 
| 728 | 398 | 100 | 66 |  |  | 1589 | if (exists $history{ $element_id }) { | 
|  |  | 100 |  |  |  |  |  | 
| 729 | 12 | 100 |  |  |  | 30 | if ($element_id eq $self_id) { | 
| 730 | 7 |  |  |  |  | 13 | $recursive = RECURSIVE_SELF; | 
| 731 |  |  |  |  |  |  | } else { | 
| 732 | 5 |  |  |  |  | 14 | $recursive = RECURSIVE_DEEP; | 
| 733 |  |  |  |  |  |  | } | 
| 734 |  |  |  |  |  |  | } elsif (blessed $element && $element->isa(__PACKAGE__)) { | 
| 735 | 19 |  |  |  |  | 101 | local $history{ $element_id } = 1; | 
| 736 | 19 |  |  |  |  | 51 | push @complex_elements, $element->as_string( \%history ); | 
| 737 |  |  |  |  |  |  | } else { | 
| 738 | 367 |  |  |  |  | 699 | push @simple_elements, $element; | 
| 739 |  |  |  |  |  |  | } | 
| 740 |  |  |  |  |  |  | } | 
| 741 |  |  |  |  |  |  |  | 
| 742 | 147 |  |  |  |  | 585 | @elements =     sort @simple_elements; | 
| 743 | 147 |  |  |  |  | 277 | push @elements, sort @complex_elements; | 
| 744 |  |  |  |  |  |  |  | 
| 745 | 147 |  |  |  |  | 371 | return (join($self->_element_separator, @elements), | 
| 746 |  |  |  |  |  |  | $have_reference, | 
| 747 |  |  |  |  |  |  | $recursive); | 
| 748 |  |  |  |  |  |  | } | 
| 749 |  |  |  |  |  |  |  | 
| 750 |  |  |  |  |  |  | my $AS_STRING_CALLBACK = sub { | 
| 751 |  |  |  |  |  |  | my $self = shift; | 
| 752 |  |  |  |  |  |  |  | 
| 753 |  |  |  |  |  |  | my $string = ''; | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  | if (exists $self->{'as_string'}) { | 
| 756 |  |  |  |  |  |  | $string = $self->{'as_string'}; | 
| 757 |  |  |  |  |  |  | } else { | 
| 758 |  |  |  |  |  |  | ($string, my $have_reference, my $is_recursive) = | 
| 759 |  |  |  |  |  |  | $self->_elements_as_string(@_ ? shift : | 
| 760 |  |  |  |  |  |  | { _strval($self) => 1 }); | 
| 761 |  |  |  |  |  |  |  | 
| 762 |  |  |  |  |  |  | $string .= $self->_element_separator . "..." if $is_recursive; | 
| 763 |  |  |  |  |  |  |  | 
| 764 |  |  |  |  |  |  | $string = sprintf $self->_set_format, $string; | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  | $self->{'as_string'} = $string unless $have_reference; | 
| 767 |  |  |  |  |  |  | } | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | return $string; | 
| 770 |  |  |  |  |  |  | }; | 
| 771 |  |  |  |  |  |  |  | 
| 772 |  |  |  |  |  |  | my $as_string_callback = $AS_STRING_CALLBACK; | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | sub as_string_callback { | 
| 775 | 5 |  |  | 5 | 0 | 28 | my $arg = shift; | 
| 776 |  |  |  |  |  |  |  | 
| 777 | 5 | 100 |  |  |  | 11 | if (ref $arg) { | 
| 778 | 2 | 50 |  |  |  | 5 | if (@_) { | 
| 779 | 2 |  |  |  |  | 5 | $arg->{'as_string_callback'} = shift; | 
| 780 | 2 | 100 |  |  |  | 30 | delete $arg->{'as_string_callback'} | 
| 781 |  |  |  |  |  |  | unless defined $arg->{'as_string_callback'}; | 
| 782 |  |  |  |  |  |  | } else { | 
| 783 | 0 |  |  |  |  | 0 | return $arg->{'as_string_callback'}; | 
| 784 |  |  |  |  |  |  | } | 
| 785 |  |  |  |  |  |  | } else { | 
| 786 | 3 | 100 |  |  |  | 6 | if (@_) { | 
| 787 | 2 |  |  |  |  | 4 | $as_string_callback = shift; | 
| 788 | 2 | 50 |  |  |  | 9 | $as_string_callback = $AS_STRING_CALLBACK | 
| 789 |  |  |  |  |  |  | unless defined $as_string_callback; | 
| 790 |  |  |  |  |  |  | } else { | 
| 791 | 1 |  |  |  |  | 3 | return $as_string_callback; | 
| 792 |  |  |  |  |  |  | } | 
| 793 |  |  |  |  |  |  | } | 
| 794 |  |  |  |  |  |  | } | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | sub as_string { | 
| 797 | 208 |  |  | 208 | 0 | 378 | my $self = shift; | 
| 798 |  |  |  |  |  |  |  | 
| 799 | 208 | 100 |  |  |  | 477 | if (exists $self->{'as_string_callback'}) { | 
| 800 | 4 |  |  |  |  | 11 | return $self->{'as_string_callback'}->($self, @_); | 
| 801 |  |  |  |  |  |  | } else { | 
| 802 | 204 |  |  |  |  | 427 | return $as_string_callback->($self, @_); | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  | } | 
| 805 |  |  |  |  |  |  |  | 
| 806 |  |  |  |  |  |  | sub _element_separator { | 
| 807 | 159 |  |  | 159 |  | 191 | my $self = shift; | 
| 808 |  |  |  |  |  |  |  | 
| 809 | 159 | 50 |  |  |  | 2794 | return $self->{'display'}->{'element_separator'} | 
| 810 |  |  |  |  |  |  | if exists $self->{'display'}->{'element_separator'}; | 
| 811 |  |  |  |  |  |  |  | 
| 812 | 159 |  |  |  |  | 336 | my $universe = $self->universe; | 
| 813 |  |  |  |  |  |  |  | 
| 814 | 159 | 50 |  |  |  | 419 | return $universe->{'display'}->{'element_separator'} | 
| 815 |  |  |  |  |  |  | if exists $universe->{'display'}->{'element_separator'}; | 
| 816 |  |  |  |  |  |  |  | 
| 817 | 159 |  |  |  |  | 586 | return (ref $self)->ELEMENT_SEPARATOR; | 
| 818 |  |  |  |  |  |  | } | 
| 819 |  |  |  |  |  |  |  | 
| 820 |  |  |  |  |  |  | sub _set_format { | 
| 821 | 147 |  |  | 147 |  | 174 | my $self = shift; | 
| 822 |  |  |  |  |  |  |  | 
| 823 | 147 | 50 |  |  |  | 375 | return $self->{'display'}->{'set_format'} | 
| 824 |  |  |  |  |  |  | if exists $self->{'display'}->{'set_format'}; | 
| 825 |  |  |  |  |  |  |  | 
| 826 | 147 |  |  |  |  | 276 | my $universe = $self->universe; | 
| 827 |  |  |  |  |  |  |  | 
| 828 | 147 | 50 |  |  |  | 370 | return $universe->{'display'}->{'set_format'} | 
| 829 |  |  |  |  |  |  | if exists $universe->{'display'}->{'set_format'}; | 
| 830 |  |  |  |  |  |  |  | 
| 831 | 147 |  |  |  |  | 606 | return (ref $self)->SET_FORMAT; | 
| 832 |  |  |  |  |  |  | } | 
| 833 |  |  |  |  |  |  |  | 
| 834 |  |  |  |  |  |  | =pod | 
| 835 |  |  |  |  |  |  |  | 
| 836 |  |  |  |  |  |  | =head1 NAME | 
| 837 |  |  |  |  |  |  |  | 
| 838 |  |  |  |  |  |  | Set::Scalar::Base - base class for Set::Scalar | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 841 |  |  |  |  |  |  |  | 
| 842 |  |  |  |  |  |  | B. | 
| 843 |  |  |  |  |  |  |  | 
| 844 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 845 |  |  |  |  |  |  |  | 
| 846 |  |  |  |  |  |  | B | 
| 847 |  |  |  |  |  |  | See the L. | 
| 848 |  |  |  |  |  |  |  | 
| 849 |  |  |  |  |  |  | =head1 AUTHOR | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  | Jarkko Hietaniemi | 
| 852 |  |  |  |  |  |  |  | 
| 853 |  |  |  |  |  |  | =cut | 
| 854 |  |  |  |  |  |  |  | 
| 855 |  |  |  |  |  |  | 1; |