File Coverage

blib/lib/Math/Transform/List.pm
Criterion Covered Total %
statement 63 63 100.0
branch 21 28 75.0
condition 6 15 40.0
subroutine 3 3 100.0
pod 0 1 0.0
total 93 110 84.5


line stmt bran cond sub pod time code
1             =head1 Name
2              
3             Math::Transform::List - Generate specified transformations of a list.
4              
5             =head1 Synopsis
6              
7             use Math::Transform::List;
8              
9             transform {say "@_"} [qw(a b c)], [1..3];
10              
11             # a b c
12             # b c a
13             # c b a
14              
15             transform {say "@_"} [qw(a b c d)], [1..2], [3..4];
16              
17             # a b c d
18             # b a c d
19             # a b d c
20             # b a d c
21              
22             transform {say "@_"} [qw(a b c d)], [[1, 3], [2, 4]];
23              
24             # a b c d
25             # c d a b
26              
27             =cut
28              
29 3     3   162496 use strict;
  3         7  
  3         2602  
30              
31             package Math::Transform::List;
32              
33              
34             sub transform(&$@)
35 14     14 0 3750 {my $s = shift; # Subroutine to process each transformation
36              
37              
38             # List to be transformed
39              
40 14         36 my $L = shift;
41 14 50       59 ref($L) or die "transform(2): $L not a reference";
42 14         29 {my @L = @$L;
  14         56  
43 14 100       80 if (@L == 0)
    100          
44 1         14 {return 0
45             }
46             elsif (@L == 1)
47 1         6 {&$s(@L);
48 1         12 return 1
49             }
50             }
51 12         49 my ($N1, $N) = (scalar(@$L)-1, scalar(@$L));
52              
53              
54             # Transformations - check
55              
56 12         54 for(0..$#_)
57 18         39 {my @p = @{$_[$_]};
  18         54  
58 18         38 my $p = grep {ref($_)} @{$_[$_]};
  72         160  
  18         48  
59 18 50 66     87 $p == 0 or $p == @p or die "Transform, transformation(@p) must be all references to cycles or one cycle";
60             }
61              
62              
63             # Transformations - load
64              
65 12         26 my $T;
66 12         36 for(0..$#_)
67 18 50       58 {ref($_[$_]) or die "transform(".(3+$_).") ".($_[$_])." not a reference";
68 18         30 my @P = @{$_[$_]};
  18         49  
69 18         52 for my $P(0..$#P)
70 72         120 {my $p = $P[$P];
71 72 100       170 if (ref($p))
72 3         8 {my @Q = @$p;
73 3         13 for my $Q(0..$#Q)
74 8         15 {my $q = $Q[$Q];
75 8 50 33     80 ("$q" =~ /\A\d+\Z/ and $q > 0 and $q <= $N) or die "transform(".($_+3)."->$P->$Q): $q not a number between 1 and $N";
      33        
76 8 100       24 if ($Q)
77 5         12 {my $q1 = $Q[$Q-1];
78 5 50       16 !defined($T->[$_][$q1-1]) or die "transform(".($_+3)."->$P->$Q): transformation from $q1 to $q already defined";
79 5         17 $T->[$_][$q1-1] = $q-1;
80             }
81             }
82 3         9 $T->[$_][$Q[-1]-1] = $Q[0]-1;
83             }
84             else
85 69 50 33     487 {("$p" =~ /\A\d+\Z/ and $p > 0 and $p <= $N) or die "transform(".($_+3)."->$P): $p not a number between 1 and $N";
      33        
86 69 100       160 if ($P)
87 53         101 {my $p1 = $P[$P-1];
88 53 50       126 !defined($T->[$_][$p1]) or die "transform(".($_+3)."->$P): transformation from $p1 to $p already defined";
89 53         145 $T->[$_][$p1-1] = $p-1;
90             }
91             }
92             }
93 18         245960 $T->[$_][$P[-1]-1] = $P[0]-1;
94             }
95              
96              
97             # Set unset transforms
98              
99 12         54 for my $a(0..$#_)
100 18         43 {for my $b(0..$N1)
101 93 100       210 {$T->[$a][$b] = $b unless defined $T->[$a][$b];
102             }
103             }
104              
105              
106             # Initialize transformer
107              
108 12         61 my @T = ([0..$N1]); # Transforms stack
109 12         29 my $S; # Transforms already processed
110              
111              
112             # Generate transformations
113              
114 12         21 my $n = 0;
115 12         40 for(;@T;)
116 279         476 {my $a = pop @T;
117 279         474 for my $b(@$T)
118 511         912 {my @C = map {$b->[$a->[$_]]} 0..$N1;
  2978         4982  
119              
120 511 100       2386 unless ($S->{"@C"}++)
121 267         768 {push @T, [@C];
122 267         532 &$s(map {$L->[$C[$_]]} 0..$N1);
  1807         3244  
123 267         1693 $n++;
124             }
125             }
126             }
127              
128              
129 12         265940 $n # Number of transformations
130             }
131              
132              
133             # Export details
134              
135             require 5.16.0;
136             require Exporter;
137              
138 3     3   23 use vars qw(@ISA @EXPORT $VERSION);
  3         7  
  3         397  
139              
140             @ISA = qw(Exporter);
141             @EXPORT = qw(transform);
142             $VERSION = 20170808; # Monday 26 Jan 2015
143              
144             =head1 Description
145              
146             Generate and process all the all the transformations of a list using the
147             standard Perl metaphor.
148              
149             C returns the number of transformations in both scalar and
150             array context.
151              
152             C is easy to use and fast. It is written in 100% Pure Perl.
153              
154             Please note that the order in which the transformations are generated is not
155             guaranteed, so please do not rely on it.
156              
157             The parameters to C are:
158              
159             1: The code to be executed for each transformation.
160              
161             2: A reference to the list to be transformed. This list is transformed
162             as specified by the transformations. Each transformation of the list is
163             handed to the code supplied in parameter 1 to be processed.
164              
165             3: One or more transformations to be applied to the list. The
166             transformations are applied repeatedly in all orders until no new
167             transformations of the list are generated. Each new transformation of the
168             list is handed to the code supplied in parameter 1 for processing.
169              
170             Transformations are represented as permutations in cyclic format based from
171             1 not 0. Two representations can be used to specify transformations.
172              
173             3a: Single cycle.
174              
175             [1,2,3]
176              
177             The first element of the list will be replaced by the second, the second by
178             the third, and the third by the first.
179              
180             3a: Multi cycle.
181              
182             [[1,3], [2,4]]
183              
184             The first element of the list will be replaced by the third and vice versa,
185             while simultaneously the second element is replaced by the fourth and vice
186             versa.
187              
188             transform {say "@_"} [qw(a b c d)], [[1, 3], [2, 4]];
189              
190             # a b c d
191             # c d a b
192              
193             If you want to produce all possible transformations of a list you should
194             consider L as it is faster and easier to use than the
195             equivalent:
196              
197             transform {} [1..$n], [1,2], [1..$n];
198              
199             =head1 Export
200              
201             The C function is exported.
202              
203             =head1 Installation
204              
205             Standard Module::Build process for building and installing modules:
206              
207             perl Build.PL
208             ./Build
209             ./Build test
210             ./Build install
211              
212             Or, if you're on a platform (like DOS or Windows) that doesn't require the
213             "./" notation, you can do this:
214              
215             perl Build.PL
216             Build
217             Build test
218             Build install
219              
220             =head1 Author
221              
222             PhilipRBrenan@appaapps.com
223              
224             http://www.appaapps.com
225              
226             =head1 Acknowledgements
227              
228             With much help and good natured advice from Philipp Rumpf to whom I am
229             indebted.
230              
231             =head1 See Also
232              
233             =over
234              
235             =item L
236              
237             =item L
238              
239             =item L
240              
241             =item L
242              
243             =back
244              
245             =head1 Copyright
246              
247             Copyright (c) 2009 Philip R Brenan.
248              
249             This module is free software. It may be used, redistributed and/or modified
250             under the same terms as Perl itself.
251              
252             =cut