File Coverage

blib/lib/Set/IntSpan/Partition.pm
Criterion Covered Total %
statement 13 32 40.6
branch 0 4 0.0
condition n/a
subroutine 5 8 62.5
pod n/a
total 18 44 40.9


line stmt bran cond sub pod time code
1             package Set::IntSpan::Partition;
2            
3 3     3   112676 use 5.008000;
  3         12  
  3         123  
4 3     3   17 use strict;
  3         7  
  3         109  
5 3     3   16 use warnings;
  3         11  
  3         111  
6 3     3   16 use base qw(Exporter);
  3         6  
  3         1370  
7            
8             our $VERSION = '0.02';
9            
10             our %EXPORT_TAGS = ( 'all' => [ qw(
11            
12             ) ] );
13            
14             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
15            
16             our @EXPORT = qw(
17             intspan_partition
18             intspan_partition_map
19             );
20            
21             sub _uniq (@) {
22 0     0     my %h;
23 0 0         return map { $h{$_}++ == 0 ? $_ : () } @_;
  0            
24             }
25            
26             sub _add {
27 0     0     my $rest = shift;
28            
29 0           my @parts = map {
30 0           my $old = $_;
31            
32 0           my $right = $rest->diff($old);
33 0           my $left = $old->diff($rest);
34 0           my $both = $old->intersect($rest);
35            
36 0           $rest = $right;
37            
38 0           grep { !$_->empty } $left, $both
  0            
39            
40             } @_;
41            
42 0 0         push @parts, $rest unless $rest->empty;
43 0           return @parts;
44             }
45            
46             sub intspan_partition {
47 0     0     my @parts = ();
48            
49 0           @parts = _add($_, @parts) for @_;
50            
51             # TODO: It's not really possible to get non-unique
52             # items into the list? But play it safe for now.
53 0           return _uniq @parts;
54             }
55            
56             sub intspan_partition_map {
57            
58 3     3   3200 use Heap::Simple qw//;
  0            
  0            
59             use List::Util qw/min max/;
60             use List::MoreUtils qw/uniq/;
61            
62             my $heap = Heap::Simple->new(order => sub {
63             my ($x, $y) = @_;
64             return 1 if $x->[0] < $y->[0];
65             return 0 if $x->[0] > $y->[0];
66             return 1 if $x->[1] < $y->[1];
67             return 0;
68             });
69            
70             for (my $ix = 0; $ix < @_; ++$ix) {
71             my $obj = $_[$ix];
72             for ($obj->spans) {
73             $heap->insert([ $_->[0], $_->[1], [$ix] ]);
74             }
75             }
76            
77             my @result;
78            
79             while (1) {
80             my $x = $heap->extract_first;
81             my $y = $heap->extract_first;
82            
83             last unless defined $x;
84             push @result, $x unless defined $y;
85             last unless defined $y;
86            
87             if ($x->[1] < $y->[0]) {
88             push @result, $x;
89             $heap->insert($y);
90             next;
91             }
92            
93             my $min = min($x->[1], $y->[0]);
94             my $max = max(min($y->[0], $x->[1]), min($x->[1], $y->[1]));
95             my $XandY = [ $min, $max, [ @{$x->[2]}, @{$y->[2]} ] ];
96             my $prefX = [ $x->[0], $XandY->[0] - 1, $x->[2] ];
97             my $suffX = [ $XandY->[1] + 1, $x->[1], $x->[2] ];
98             my $onlyY = [ $XandY->[1] + 1, $y->[1], $y->[2] ];
99            
100             for ($prefX, $suffX, $onlyY, $XandY) {
101             next unless $_->[0] <= $_->[1];
102             $heap->insert($_);
103             }
104             }
105            
106             # group spans back into classes
107             my %group;
108             for my $item (@result) {
109             my $key = join ',', uniq sort @{ $item->[2] };
110             push @{ $group{$key} }, $item;
111             }
112            
113             my %map;
114             while (my ($k, $v) = each %group) {
115             my $class = Set::IntSpan->new([map {
116             [ $_->[0], $_->[1] ]
117             } @$v]);
118             push @{ $map{$_} }, $class for uniq map { @{ $_->[2] } } @$v;
119             }
120            
121             return %map;
122             }
123            
124             1;
125            
126             __END__