File Coverage

blib/lib/Algorithm/DLX.pm
Criterion Covered Total %
statement 95 97 97.9
branch 7 8 87.5
condition 1 3 33.3
subroutine 12 12 100.0
pod 4 7 57.1
total 119 127 93.7


line stmt bran cond sub pod time code
1             package Algorithm::DLX;
2              
3 1     1   81183 use strict;
  1         1  
  1         28  
4 1     1   6 use warnings;
  1         1  
  1         121  
5              
6             our $VERSION = 0.03;
7              
8             # Node structure for DLX
9             package DLX::Node;
10             sub new {
11 95     95   195 my ($class, $row, $col) = @_;
12 95         419 my $self = {
13             row => $row,
14             col => $col,
15             left => undef,
16             right => undef,
17             up => undef,
18             down => undef,
19             column => undef,
20             };
21 95         205 bless $self, $class;
22 95         206 return $self;
23             }
24              
25             # Column structure for DLX
26             package DLX::Column;
27 1     1   11 use base 'DLX::Node';
  1         1  
  1         2121  
28             sub new {
29 32     32   65 my ($class, $col) = @_;
30 32         74 my $self = $class->SUPER::new(undef, $col);
31 32         78 $self->{size} = 0;
32 32         77 $self->{name} = $col;
33 32         57 $self->{column} = $self;
34 32         53 bless $self, $class;
35 32         66 return $self;
36             }
37              
38             # Main DLX package
39             package Algorithm::DLX;
40              
41             sub new {
42 5     5 1 220230 my ($class) = @_;
43 5         18 my $self = {
44             header => DLX::Column->new('header'),
45             solution => [],
46             solutions => [],
47             };
48              
49             # Initialize header links
50 5         13 $self->{header}->{left} = $self->{header};
51 5         10 $self->{header}->{right} = $self->{header};
52 5         10 bless $self, $class;
53              
54 5         12 return $self;
55             }
56              
57             sub add_column {
58 27     27 1 125 my ($self, $col_name) = @_;
59 27         55 my $col = DLX::Column->new($col_name);
60              
61 27         57 $col->{left} = $self->{header}->{left};
62 27         49 $col->{right} = $self->{header};
63 27         50 $self->{header}->{left}->{right} = $col;
64 27         43 $self->{header}->{left} = $col;
65 27         46 $col->{up} = $col;
66 27         42 $col->{down} = $col;
67              
68 27         103 return $col;
69             }
70              
71             sub add_row {
72 25     25 1 319 my ($self, $row, @cols) = @_;
73 25         36 my $first;
74              
75 25         65 for my $col (@cols) {
76 63         134 my $node = DLX::Node->new($row, $col->{name});
77 63         107 $node->{column} = $col;
78 63         105 $col->{size}++;
79 63         115 $node->{up} = $col->{up};
80 63         99 $node->{down} = $col;
81 63         146 $col->{up}->{down} = $node;
82 63         101 $col->{up} = $node;
83 63 100       121 if ($first) {
84 38         68 $node->{left} = $first->{left};
85 38         78 $node->{right} = $first;
86 38         90 $first->{left}->{right} = $node;
87 38         113 $first->{left} = $node;
88             } else {
89 25         36 $first = $node;
90 25         42 $node->{left} = $node;
91 25         53 $node->{right} = $node;
92             }
93             }
94             }
95              
96             sub cover {
97 42     42 0 100 my ($self, $col) = @_;
98              
99 42         83 $col->{right}->{left} = $col->{left};
100 42         80 $col->{left}->{right} = $col->{right};
101              
102 42         128 for (my $row = $col->{down}; $row != $col; $row = $row->{down}) {
103 38         89 for (my $node = $row->{right}; $node != $row; $node = $node->{right}) {
104 60         109 $node->{down}->{up} = $node->{up};
105 60         109 $node->{up}->{down} = $node->{down};
106 60         232 $node->{column}->{size}--;
107             }
108             }
109             }
110              
111             sub uncover {
112 42     42 0 74 my ($self, $col) = @_;
113              
114 42         104 for (my $row = $col->{up}; $row != $col; $row = $row->{up}) {
115 38         97 for (my $node = $row->{left}; $node != $row; $node = $node->{left}) {
116 60         142 $node->{column}->{size}++;
117 60         100 $node->{down}->{up} = $node;
118 60         214 $node->{up}->{down} = $node;
119             }
120             }
121              
122 42         79 $col->{right}->{left} = $col;
123 42         114 $col->{left}->{right} = $col;
124             }
125              
126             sub search {
127 21     21 0 41 my ($self, $k, $number_of_solutions) = @_;
128              
129 21 100       61 if ($self->{header}->{right} == $self->{header}) {
130 4         7 push @{$self->{solutions}}, [@{$self->{solution}}];
  4         9  
  4         12  
131 4         9 return;
132             }
133              
134 17 50 33     70 if ($number_of_solutions && @{$self->{solutions}} >= $number_of_solutions) {
  0         0  
135 0         0 return;
136             }
137              
138 17         33 my $col = $self->{header}->{right};
139 17         47 for (my $c = $col->{right}; $c != $self->{header}; $c = $c->{right}) {
140 48 100       168 $col = $c if $c->{size} < $col->{size};
141             }
142              
143 17         48 $self->cover($col);
144 17         44 for (my $row = $col->{down}; $row != $col; $row = $row->{down}) {
145 16         25 push @{$self->{solution}}, $row->{row};
  16         42  
146 16         36 for (my $node = $row->{right}; $node != $row; $node = $node->{right}) {
147 25         57 $self->cover($node->{column});
148             }
149 16         66 $self->search($k + 1, $number_of_solutions);
150 16         43 for (my $node = $row->{left}; $node != $row; $node = $node->{left}) {
151 25         52 $self->uncover($node->{column});
152             }
153 16         25 pop @{$self->{solution}};
  16         51  
154             }
155              
156 17         44 $self->uncover($col);
157             }
158              
159             sub solve {
160 5     5 1 37 my ($self, %params) = @_;
161              
162 5         10 my $number_of_solutions = $params{number_of_solutions};
163              
164 5         16 $self->search(0, $number_of_solutions);
165              
166 5         17 return $self->{solutions};
167             }
168              
169             1;
170              
171             __END__