File Coverage

blib/lib/UDCode.pm
Criterion Covered Total %
statement 36 36 100.0
branch 16 18 88.8
condition 3 3 100.0
subroutine 5 5 100.0
pod 2 4 50.0
total 62 66 93.9


line stmt bran cond sub pod time code
1             package UDCode;
2              
3             $VERSION = "1.03";
4              
5 5     5   39781 use base 'Exporter';
  5         11  
  5         3046  
6             @EXPORT = qw(is_udcode ud_pair);
7              
8             =head1 NAME
9              
10             UDCode - Does a set of code words form a uniquely decodable code?
11              
12             =head1 SYNOPSIS
13              
14             use UDCode;
15              
16             if (is_udcode(@words)) { ... }
17              
18             my ($x1, $x2) = ud_pair(@words);
19              
20             =head1 DESCRIPTION
21              
22             A code is a set of strings, called the I. A code is
23             "uniquely decodable" if any string I that is a concatenation of
24             code words is so in I.
25              
26             For example, the code C<"ab", "abba", "b"> is I uniquely
27             decodable, because C<"abba" . "b" eq "ab" . "b" . "ab">. But the code
28             C<"a", "ab", "abb"> I uniquely decodable, because there is no such
29             pair of sequences of code words.
30              
31             =head2 C
32              
33             C returns true if and only if the specified code is
34             uniquely decodable.
35              
36             =cut
37              
38             sub is_udcode {
39 14     14 1 36 my $N = my ($a, $b) = ud_pair(@_);
40 14         61 return $N == 0;
41             }
42              
43             =head2 C
44              
45             If C<@words> is not a uniquely decodable code, then C
46             returns a proof of that fact, in the form of two distinct sequences of
47             code words whose concatenations are equal.
48              
49             If C<@words> is not uniquely decodable, then C returns
50             references to two arrays of code words, C<$a>, and C<$b>, such that:
51              
52             join("", @$a) eq join("", @$b)
53              
54             For example, given C<@words = qw(ab abba b)>, C might return
55             the two arrays C<["ab", "b", "ab"]> and C<["abba", "b"]>.
56              
57             If C<@words> is uniquely decodable, C returns false.
58              
59             =cut
60              
61             sub ud_pair {
62             # Code words
63 17     17 1 3258 my @c = @_;
64              
65             # $h{$x} = [$y, $z] means that $x$y eq $z
66 17         20 my %h;
67              
68             # Queue
69             my @q;
70              
71 17         26 for my $c1 (@c) {
72 48         58 for my $c2 (@c) {
73 146 100       271 next if $c1 eq $c2;
74 78 100       114 if (is_prefix_of($c1, $c2)) {
75 10         21 my $x = subtract($c1, $c2);
76 10         32 $h{$x} = [[$c1], [$c2]];
77 10         27 push @q, $x;
78             }
79             }
80             }
81              
82 17         38 while (@q) {
83 19         24 my $x = shift @q;
84 19 50       42 return unless defined $x;
85              
86 19         19 my ($a, $b) = @{$h{$x}};
  19         35  
87 19         29 for my $c (@c) {
88 49 50       74 die unless defined $b; # Can't happen
89             # $a$x eq $b
90              
91 49         46 my $y;
92 49 100       73 if (is_prefix_of($c, $x)) {
    100          
93 11         16 $y = subtract($c, $x);
94 11 100       28 next if exists $h{$y}; # already tried this
95 10         29 $h{$y} = [[@$a, $c], $b];
96 10         17 push @q, $y;
97             } elsif (is_prefix_of($x, $c)) {
98 11         18 $y = subtract($x, $c);
99 11 100       25 next if exists $h{$y}; # already tried this
100 9         33 $h{$y} = [$b, [@$a, $c]];
101 9         13 push @q, $y;
102             }
103              
104 46 100 100     174 return @{$h{""}} if defined($y) && $y eq "";
  7         41  
105             }
106             }
107 10         31 return; # failure
108             }
109              
110             sub is_prefix_of {
111 172     172 0 1006 index($_[1], $_[0]) == 0;
112             }
113              
114             sub subtract {
115 36     36 0 520 substr($_[1], length($_[0]));
116             }
117              
118             =head1 AUTHOR
119              
120             Mark Jason Dominus (C)
121              
122             =head1 COPYRIGHT
123              
124             This software is hereby released into the public domain. You may use,
125             modify, or distribute it for any purpose whatsoever without restriction.
126              
127             =cut
128              
129             unless (caller) {
130             my ($a, $b) = ud_pair("ab", "abba", "b");
131             print "@$a == @$b\n";
132             }
133              
134             1;
135