File Coverage

blib/lib/Math/Transform/List.pm
Criterion Covered Total %
statement 72 72 100.0
branch 19 26 73.0
condition 9 18 50.0
subroutine 6 6 100.0
pod 0 1 0.0
total 106 123 86.1


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