File Coverage

blib/lib/Math/Permutation.pm
Criterion Covered Total %
statement 196 294 66.6
branch 25 54 46.3
condition 6 17 35.2
subroutine 32 47 68.0
pod 31 31 100.0
total 290 443 65.4


line stmt bran cond sub pod time code
1             package Math::Permutation;
2              
3 8     8   61569 use 5.010;
  8         58  
4 8     8   39 use strict;
  8         12  
  8         180  
5 8     8   53 use warnings;
  8         30  
  8         226  
6 8     8   38 use Carp;
  8         12  
  8         859  
7 8     8   47 use List::Util qw/tail reduce any uniq none all sum first max min/;
  8         19  
  8         28999  
8              
9              
10             # supportive math function
11             sub _lcm {
12 9     9   17 return reduce { $a*$b/_gcd($a,$b) } @_;
  8     8   42  
13             }
14              
15             sub _gcd { # _gcd of two positive integers
16 9     9   26 my $x = min($_[0], $_[1]);
17 9         14 my $y = max($_[0], $_[1]);
18 9         15 while ($x != 0) {
19 9         22 ($x, $y) = ($y % $x, $x)
20             }
21 9         33 return $y;
22             }
23              
24             sub _factorial {
25 32     32   39 my $ans = 1;
26 32         73 for (1..$_[0]) {
27 256         293 $ans *= $_;
28             }
29 32         43 return $ans;
30             }
31              
32             sub eqv {
33 16     16 1 50 my $wrepr = $_[0]->{_wrepr};
34 16         19 my $wrepr2 = $_[1]->{_wrepr};
35 16         21 my $n = $_[0]->{_n};
36 16         18 my $n2 = $_[1]->{_n};
37 16 50       26 return 0 if $n != $n2;
38 16         19 my $check = 0;
39 16         27 for (0..$n-1) {
40 120 50       174 $check++ if $wrepr->[$_] == $wrepr2->[$_];
41             }
42 16 50       47 return $check == $n ? 1 : 0;
43             }
44              
45             sub clone {
46 16     16 1 48 my ($class) = @_;
47 16         30 my $wrepr = $_[1]->{_wrepr};
48 16         18 my $n = $_[1]->{_n};
49 16         36 $_[0]->{_wrepr} = $wrepr;
50 16         36 $_[0]->{_n} = $n;
51             }
52              
53             sub init {
54 24     24 1 1892 my ($class) = @_;
55 24         43 my $n = $_[1];
56 24         81 bless {
57             _wrepr => [1..$n],
58             _n => $n,
59             }, $class;
60             }
61              
62             sub wrepr {
63 64     64 1 14658 my ($class) = @_;
64 64   50     142 my $wrepr = $_[1] || [1];
65             # begin: checking
66 64         75 my $n = scalar $wrepr->@*;
67 64         75 my %check;
68 64         196 $check{$_} = 1 foreach $wrepr->@*;
69 64 50   280   292 unless (all {defined($check{$_})} (1..$n)) {
  280         439  
70 0         0 carp "Error in input representation. "
71             ."The permutation will initialize to identity permutation "
72             ."of $n elements.\n";
73 0         0 $wrepr = [1..$n];
74             }
75             # end: checking
76             bless {
77 64         282 _wrepr => $wrepr,
78             _n => $n,
79             }, $class;
80             }
81              
82             sub tabular {
83 0     0 1 0 my ($class) = @_;
84 0         0 my @domain = $_[1]->@*;
85 0         0 my @codomain = $_[2]->@*;
86 0         0 my $wrepr;
87 0         0 my $n = scalar @domain;
88             # begin: checking
89 0         0 my %check1, my %check2;
90 0         0 $check1{$_} = 1 foreach @domain;
91 0         0 $check2{$_} = 1 foreach @codomain;
92 0         0 my $check = 1;
93 0 0 0 0   0 unless ( (all {defined($check1{$_})} (1..$n))
  0   0     0  
94             && $n == scalar @codomain
95 0     0   0 && (all {defined($check2{$_})} (1..$n)) ) {
96 0         0 carp "Error in input representation. "
97             ."The permutation will initialize to identity permutation "
98             ."of $n elements.\n";
99 0         0 $wrepr = [1..$n];
100 0         0 $check = 0;
101             }
102             # end: checking
103 0 0       0 if ($check) {
104 0         0 my %w;
105 0         0 $w{$domain[$_]} = $codomain[$_] foreach 0..$n-1;
106 0         0 $wrepr = [ map {$w{$_}} 1..$n ];
  0         0  
107             }
108             bless {
109 0         0 _wrepr => $wrepr,
110             _n => $n,
111             }, $class;
112             }
113              
114              
115             sub cycles {
116 2     2 1 7 my ($class) = @_;
117 2         5 my @cycles = $_[1]->@*;
118 2         4 my $wrepr;
119             my @elements;
120 2         6 push @elements, @{$_} foreach @cycles;
  2         4  
121 2         10 my $n = int max @elements;
122             # begin: checking
123 2         5 my $check = 1;
124 2         4 for (@elements) {
125 2 50 33     14 if ($_ != int $_ || $_ <= 0) {
126 0         0 $check = 0;
127 0         0 last;
128             }
129             }
130 2         4 for (@cycles) {
131 2 50       4 if ((scalar uniq @{$_}) != (scalar @{$_})) {
  2         13  
  2         8  
132 0         0 $check = 0;
133 0         0 last;
134             }
135             }
136 2 50       6 if (!$check) {
137 0         0 carp "Error in input representation. "
138             ."The permutation will initialize to identity permutation "
139             ."of $n elements.\n";
140 0         0 $wrepr = [1..$n];
141             }
142             # end: checking
143 2 50       24 if ($check) {
144 2 50       12 if ((scalar uniq @elements) == (scalar @elements)) {
145 2         7 $wrepr = _cycles_to_wrepr($n, [@cycles]);
146             }
147             else {
148             # composition operation
149 0         0 my @ws;
150 0         0 @ws = map {_cycles_to_wrepr($n, [ $_ ] ) } @cycles;
  0         0  
151 0         0 my @qp;
152 0         0 my @p = $ws[-1]->@*;
153 0         0 for my $j (2..scalar @cycles) {
154 0         0 @qp = ();
155 0         0 my @q = $ws[-$j]->@*;
156 0         0 push @qp, $q[$p[$_-1]-1] for 1..$n;
157 0         0 @p = @qp;
158             }
159 0 0       0 @qp = map { $qp[$_] == 0 ? $_+1 : $qp[$_] } (0..$n-1);
  0         0  
160 0         0 $wrepr = [@qp];
161             }
162             }
163             bless {
164 2         13 _wrepr => $wrepr,
165             _n => $n,
166             }, $class;
167             }
168              
169             sub _cycles_to_wrepr {
170 10     10   11 my $n = $_[0];
171 10         18 my @cycles = $_[1]->@*;
172 10         10 my %hash;
173 10         62 $hash{$_} = 0 for (1..$n);
174 10         18 for my $c (@cycles) {
175 26 100       29 if (scalar @{$c} > 1) {
  26 50       39  
176 15         18 $hash{$c->[$_]} = $c->[$_+1] for (0..scalar @{$c} - 2);
  15         62  
177 15         30 $hash{$c->[-1]} = $c->[0];
178             }
179 11         18 elsif (scalar @{$c} == 1) {
180 11         22 $hash{$c->[0]} = $c->[0];
181             }
182             }
183 10 100       21 return [ map {$hash{$_} == 0 ? $_ : $hash{$_}} (1..$n) ];
  95         222  
184             }
185              
186             sub cycles_with_len {
187 2     2 1 159 my ($class) = @_;
188 2         5 my $n = $_[1];
189 2         6 my @cycles = $_[2]->@*;
190 2         4 my @elements;
191 2         5 push @elements, @{$_} foreach @cycles;
  0         0  
192 2 50   0   27 return (any {$n == $_} @elements) ? $_[0]->cycles([@cycles])
  0         0  
193             : $_[0]->cycles([@cycles, [$n]]);
194             }
195              
196             sub sprint_wrepr {
197 0     0 1 0 return "\"" . (join ",", $_[0]->{_wrepr}->@*) . "\"";
198             }
199              
200             sub sprint_tabular {
201 0     0 1 0 my $n = $_[0]->{_n};
202 0         0 my $digit_len = length $n;
203 0         0 return "|" . (join " ", map {sprintf("%*s", $digit_len, $_)} 1..$n )
204             . "|" . "\n"
205             ."|"
206 0         0 . (join " ", map {sprintf("%*s", $digit_len, $_)} $_[0]->{_wrepr}->@* )
207 0         0 . "|";
208             }
209              
210             sub sprint_cycles {
211 0     0 1 0 my @cycles = $_[0]->cyc->@*;
212 0         0 @cycles = grep { scalar @{$_} > 1 } @cycles;
  0         0  
  0         0  
213 0 0       0 return "()" if scalar @cycles == 0;
214 0         0 my @p_cycles = map {"(".(join " ", @{$_}). ")"} @cycles;
  0         0  
  0         0  
215 0         0 return join " ", @p_cycles;
216             }
217              
218             sub sprint_cycles_full {
219 0     0 1 0 my @cycles = $_[0]->cyc->@*;
220 0         0 my @p_cycles = map {"(".(join " ", @{$_}). ")"} @cycles;
  0         0  
  0         0  
221 0         0 return join " ", @p_cycles;
222             }
223              
224             sub swap {
225 99     99 1 241 my $i = $_[1];
226 99         97 my $j = $_[2];
227 99         113 my $wrepr = $_[0]->{_wrepr};
228 99         129 ($wrepr->[$i-1], $wrepr->[$j-1]) = ($wrepr->[$j-1], $wrepr->[$i-1]);
229 99         124 $_[0]->{_wrepr} = $wrepr;
230             }
231              
232             sub comp {
233 30     30 1 101 my $n = $_[0]->{_n};
234 30         50 my @p = $_[0]->{_wrepr}->@*;
235 30         43 my @q = $_[1]->{_wrepr}->@*;
236 30 50       47 return [] if scalar @q != $n;
237 30         34 my @qp;
238 30         104 push @qp, $q[$p[$_-1]-1] for 1..$n;
239 30         77 $_[0]->{_wrepr} = [@qp];
240             }
241              
242             sub inverse {
243 8     8 1 27 my $n = $_[0]->{_n};
244 8         16 my @cycles = $_[0]->cyc->@*;
245 8         11 my @new_cycles;
246 8         12 foreach (@cycles) {
247 24         26 push @new_cycles, [reverse @{$_}];
  24         40  
248             }
249 8         17 $_[0]->{_wrepr} = _cycles_to_wrepr($n, [@new_cycles]);
250             }
251              
252             sub nxt {
253 8     8 1 28 my $n = $_[0]->{_n};
254 8         14 my @w = $_[0]->{_wrepr}->@*;
255 8         12 my @rw = reverse @w;
256 8         9 my $ind = 1;
257 8   66     33 while ($ind <= $#rw && $rw[$ind-1] < $rw[$ind]) {
258 4         11 $ind++;
259             }
260 8 50       16 return [] if $ind == scalar @w;
261 8         30 my @suffix = tail $ind, @w;
262 8         41 my $i = 1;
263 8         41 $i++ until $w[-$ind-1] < $suffix[-$i];
264 8         18 ($w[-$ind-1], $suffix[-$i]) = ($suffix[-$i], $w[-$ind-1]);
265 8         32 $_[0]->{_wrepr} = [ @w[0..$n-$ind-1], reverse @suffix ];
266             }
267              
268             sub prev {
269 8     8 1 27 my $n = $_[0]->{_n};
270 8         16 my @w = $_[0]->{_wrepr}->@*;
271 8         11 my @rw = reverse @w;
272 8         12 my $ind = 1;
273 8   66     33 while ($ind <= $#rw && $rw[$ind-1] > $rw[$ind]) {
274 8         62 $ind++;
275             }
276 8 50       17 return [] if $ind == scalar @w;
277 8         23 my @suffix = tail $ind, @w;
278 8         9 my $i = 1;
279 8         20 $i++ until $w[-$ind-1] > $suffix[-$i];
280 8         19 ($w[-$ind-1], $suffix[-$i]) = ($suffix[-$i], $w[-$ind-1]);
281 8         33 $_[0]->{_wrepr} = [ @w[0..$n-$ind-1], reverse @suffix ];
282             }
283              
284             sub unrank {
285 0     0 1 0 my ($class) = @_;
286 0         0 my $n = $_[1];
287 0         0 my @list = (1..$n);
288 0         0 my $r = $_[2]-1;
289 0         0 my $fact = _factorial($n-1);
290 0         0 my @unused_list = sort {$a<=>$b} @list;
  0         0  
291 0         0 my @p = ();
292 0         0 for my $i (0..$n-1) {
293 0         0 my $q = int $r / $fact;
294 0         0 $r %= $fact;
295 0         0 push @p, $unused_list[$q];
296 0         0 splice @unused_list, $q, 1;
297 0 0       0 $fact = int $fact / ($n-1-$i) if $i != $n-1;
298             }
299 0         0 my $wrepr = [@p];
300 0         0 bless {
301             _wrepr => $wrepr,
302             _n => $n,
303             }, $class;
304             }
305              
306             # Fisher-Yates shuffle
307             sub random {
308 32     32 1 8188 my ($class) = @_;
309 32         70 my $n = $_[1];
310 32         65 my @ori = (1..$n);
311 32         65 my @w;
312 32         61 for (1..$n) {
313 264         521 my $roll = int (rand() * scalar @ori);
314 264         343 push @w, $ori[$roll];
315 264         327 ($ori[$roll], $ori[-1]) = ($ori[-1], $ori[$roll]);
316 264         336 pop @ori;
317             }
318             bless {
319 32         133 _wrepr => [@w],
320             _n => $n,
321             }, $class;
322             }
323              
324             sub cyc {
325 24     24 1 33 my $w = $_[0]->{_wrepr};
326 24         30 my $n = $_[0]->{_n};
327 24         29 my %hash;
328 24         135 $hash{$_} = $w->[$_-1] foreach 1..$n;
329 24         33 my @cycles;
330 24         56 while (scalar %hash != 0) {
331 104     104   333 my $c1 = first {1} %hash;
  104         141  
332 104         188 my @cycle;
333 104         118 my $c = $c1;
334 104         144 do {
335 184         218 push @cycle, $c;
336 184         188 my $pre_c = $c;
337 184         205 $c = $hash{$c};
338 184         326 delete $hash{$pre_c};
339             } while ($c != $c1);
340 104         234 push @cycles, [@cycle];
341             }
342 24         77 return [@cycles];
343             }
344              
345              
346              
347             sub sigma {
348 0     0 1 0 return $_[0]->{_wrepr}->[$_[1]-1];
349             }
350              
351             sub rule {
352 0     0 1 0 return $_[0]->{_wrepr};
353             }
354              
355             sub elems {
356 0     0 1 0 return $_[0]->{_n};
357             }
358              
359             sub rank {
360 32     32 1 151 my @list = $_[0]->{_wrepr}->@*;
361 32         42 my $n = scalar @list;
362 32         51 my $fact = _factorial($n-1);
363 32         37 my $r = 1;
364 32         75 my @unused_list = sort {$a<=>$b} @list;
  622         778  
365 32         86 for my $i (0..$n-2) {
366 256     903   679 my $q = first { $unused_list[$_] == $list[$i] } 0..$#unused_list;
  903         1048  
367 256         517 $r += $q*$fact;
368 256         344 splice @unused_list, $q, 1;
369 256         408 $fact = int $fact / ($n-$i-1);
370             }
371 32         59 return $r;
372             }
373              
374             # rank() and unrank($n, $i) using
375             # O(n^2) solution, translation of Python code on
376             # https://tryalgo.org/en/permutations/2016/09/05/permutation-rank/
377              
378             sub index {
379 0     0 1 0 my $n = $_[0]->{_n};
380 0         0 my @w = $_[0]->{_wrepr}->@*;
381 0         0 my $ans = 0;
382 0         0 for my $j (0..$n-2) {
383 0 0       0 $ans += ($j+1) if $w[$j] > $w[$j+1];
384             }
385 0         0 return $ans;
386             }
387              
388             sub order {
389 8     8 1 25 my @cycles = $_[0]->cyc->@*;
390 8         18 return _lcm(map {scalar @{$_}} @cycles);
  17         18  
  17         32  
391             }
392              
393             sub is_even {
394 8     8 1 15 my @cycles = $_[0]->cyc->@*;
395 8         16 my $num_of_two_swaps = sum(map { scalar @{$_} - 1 } @cycles);
  63         63  
  63         87  
396 8 100       34 return $num_of_two_swaps % 2 == 0 ? 1 : 0;
397             }
398              
399             sub is_odd {
400 8 100   8 1 25 return $_[0]->is_even ? 0 : 1;
401             }
402              
403             sub sgn {
404 0 0   0 1 0 return $_[0]->is_even ? 1 : -1;
405             }
406              
407             sub inversion {
408 24     24 1 92 my $n = $_[0]->{_n};
409 24         53 my @w = $_[0]->{_wrepr}->@*;
410 24         26 my @inv;
411 24         48 for my $k (1..$n) {
412 96         96 my $i = 0;
413 96         91 my $j = 0;
414 96         145 while ($w[$j] != $k) {
415 144 100       191 $i++ if $w[$j] > $k;
416 144         199 $j++;
417             }
418 96         115 push @inv, $i;
419             }
420 24         98 return [@inv];
421             }
422              
423             sub matrix {
424 0     0 1 0 my $mat;
425 0         0 my $n = $_[0]->{_n};
426 0         0 my @w = $_[0]->{_wrepr}->@*;
427 0         0 for my $i (0..$n-1) {
428 0         0 for my $j (0..$n-1) {
429 0         0 $mat->[$i]->[$j] = 0;
430             }
431             }
432 0         0 $mat->[$w[$_]-1]->[$_] = 1 for (0..$n-1);
433 0         0 return $mat;
434             }
435              
436             sub fixed_points {
437 40     40 1 123 my @fp;
438 40         90 for (1..$_[0]->{_n}) {
439 184 50       352 push @fp, $_ if $_[0]->{_wrepr}->[$_-1] == $_;
440             }
441 40         91 return [@fp];
442             }
443              
444             =head1 NAME
445              
446             Math::Permutation - pure Perl implementation of functions related to the permutations
447              
448             =head1 VERSION
449              
450             Version 0.01
451              
452             =cut
453              
454             our $VERSION = '0.01';
455              
456              
457             =head1 SYNOPSIS
458              
459             use Math::Permutation;
460              
461             my $foo = Math::Permutation->cycles([[1,2,6,7], [3,4,5]]);
462             say $foo->sprint_wrepr;
463             # "2,6,4,5,3,7,1"
464              
465             my $bar = Math::Permutation->unrank(5, 19);
466             $bar->comp($foo);
467             say $bar->sprint_cycles;
468             # (2 5 4 3)
469             # Note that there is no canonical cycle representation in this module,
470             # so each time the output may be slightly different.
471              
472             my $goo = Math::Permutation->clone($foo);
473             say $goo->sprint_cycles;
474             # (1 2 6 7) (4 5 3)
475              
476             $foo->inverse;
477             say $foo->sprint_cycles;
478             # (4 3 5) (1 7 6 2)
479              
480             $foo->comp($goo);
481             say $foo->sprint_cycles;
482             # ()
483              
484             say $bar->rank; # 19
485             $bar->prev;
486             say $bar->rank; # 18
487             say $goo->rank; # 1264
488             $goo->nxt;
489             say $goo->rank; # 1265
490              
491             say $goo->is_even; # 0
492             say $goo->sgn; # -1
493              
494             use Data::Dump qw/dump/;
495             say $bar->sprint_wrepr;
496             dump $bar->matrix;
497              
498             # "1,4,5,3,2"
499             # [
500             # [1, 0, 0, 0, 0],
501             # [0, 0, 0, 0, 1],
502             # [0, 0, 0, 1, 0],
503             # [0, 1, 0, 0, 0],
504             # [0, 0, 1, 0, 0],
505             # ]
506              
507            
508            
509              
510             =head1 METHODS
511              
512             =head2 INITALIZE/GENERATE NEW PERMUTATION
513              
514             =over 4
515              
516             =item $p->init($n)
517              
518             Initialize $p with the identity permutation of $n elements.
519              
520             =item $p->wrepr([$a, $b, $c, ..., $m])
521              
522             Initialize $p with word representation of a permutation, a.k.a. one-line form.
523              
524             =item $p->tabular([$a, $b, ... , $m], [$pa, $pb, $pc, ..., $pm])
525              
526             Initialize $p with the rules of a permutation, with input of permutation on the first list,
527             the output of permutation. If the first list is [1..$n], it is often called two-line form,
528             and the second list would be the word representation.
529              
530             =item $p->cycles([[$a, $b, $c], [$d, $e], [$f, $g]])
531              
532             =item $p->cycles_with_len($n, [[$a, $b, $c], [$d, $e], [$f, $g]])
533              
534             Initialize $p by the cycle notation. If the length is not specific, the length would be the largest element in the cycles.
535              
536             =item $p->unrank($n, $i)
537              
538             Initialize $p referring to the lexicological rank of all $n-permutations. $i must be between 1 and $n!.
539              
540             =item $p->random($n)
541              
542             Initialize $p by a randomly selected $n-permutation.
543              
544             =back
545              
546             =head2 DISPLAY THE PERMUTATION
547              
548             =over 4
549              
550             =item $p->sprint_wrepr()
551              
552             Return a string displaying the word representation of $p.
553              
554             =item $p->sprint_tabular()
555              
556             Return a two-line string displaying the tabular form of $p.
557              
558             =item $p->sprint_cycles()
559              
560             Return a string with cycles of $p. One-cycles are omitted.
561              
562             =item $p->sprint_cycles_full()
563              
564             Return a string with cycles of $p. One-cycles are included.
565              
566             =back
567              
568             =head2 CHECK EQUIVALENCE BETWEEN PERMUTATIONS
569              
570             =over 4
571              
572             =item $p->eqv($q)
573              
574             Check if the permutation $q is equivalent to $p. Return 1 if yes, 0 otherwise.
575              
576             =back
577              
578             =head2 CLONE THE PERMUTATION
579              
580             =over 4
581              
582             =item $p->clone($q)
583              
584             Clone the permutation $q into $p.
585              
586             =back
587              
588             =head2 MODIFY THE PERMUTATION
589              
590             =over 4
591              
592             =item $p->swap($i, $j)
593              
594             Swap the values of $i-th position and $j-th position.
595              
596             =item $p->comp($q)
597              
598             Composition of $p and $q, sometimes called multiplication of the permutations.
599             The resultant is $q $p (first do $p, then do $q).
600              
601             $p and $q must be permutations of same number of elements.
602              
603             =item $p->inverse()
604              
605             Inverse of $p.
606              
607             =item $p->nxt()
608              
609             The next permutation under the lexicological order of all $n-permutations.
610              
611             Caveat: may return [].
612              
613             =item $p->prev()
614              
615             The previous permutation under the lexicological order of all $n-permutations.
616              
617             Caveat: may return [].
618              
619             =back
620              
621             =head2 PRORERTIES OF THE CURRENT PERMUTATION
622              
623             =over 4
624              
625             =item $p->sigma($i)
626              
627             Return what $i is mapped to under $p.
628              
629             =item $p->rule()
630              
631             Return the word representation of $p as a list.
632              
633             =item $p->cyc()
634              
635             Return the cycle representation of $p as a list of list(s).
636              
637             =item $p->elems()
638              
639             Return the length of $p.
640              
641             =item $p->rank()
642              
643             Return the lexicological rank of $p. See $p->unrank($n, $i).
644              
645             =item $p->index()
646              
647             Return the permutation index of $p.
648              
649             =item $p->order()
650              
651             Return the order of $p, i.e. how many times the permutation acts on itself
652             and return the identity permutation.
653              
654             =item $p->is_even()
655              
656             Return whether $p is an even permutation. Return 1 or 0.
657              
658             =item $p->is_odd()
659              
660             Return whether $p is an odd permutation. Return 1 or 0.
661              
662             =item $p->sgn()
663              
664             Return the signature of $p. Return +1 if $p is even, -1 if $p is odd.
665              
666             Another view is the determinant of the permutation matrix of $p.
667              
668             =item $p->inversion()
669              
670             Return the inversion sequence of $p as a list.
671              
672             =item $p->matrix()
673              
674             Return the permutation matrix of $p.
675              
676             =item $p->fixed_points()
677              
678             Return the list of fixed points of $p.
679              
680             =back
681              
682             =head1 METHODS TO BE INPLEMENTED
683              
684             =over 4
685              
686             =item sqrt()
687              
688             Caveat: may return [].
689              
690             =item longest_increasing()
691              
692             =item longest_decreasing()
693              
694             =item coxeter_decomposition()
695              
696             =item comp( more than one permutations )
697              
698             =item reverse()
699              
700             ref: Chapter 1, Patterns in Permutations and Words
701              
702             =item complement()
703              
704             ref: Chapter 1, Patterns in Permutations and Words
705              
706             =item is_irreducible()
707              
708             ref: Chapter 1, Patterns in Permutations and Words
709              
710             =item num_of_occurrences_of_pattern()
711              
712             ref: Chapter 1, Patterns in Permutations and Words
713              
714             =item contains_pattern()
715              
716             ref: Chapter 1, Patterns in Permutations and Words
717              
718             =item avoids_pattern()
719              
720             ref: Chapter 1, Patterns in Permutations and Words
721              
722             including barred patterns
723              
724             ref: Section 1.2, Patterns in Permutations and Words
725              
726             Example: [ -3, -1, 5, -2, 4 ]
727              
728             =back
729              
730             =head1 AUTHOR
731              
732             Cheok-Yin Fung, C<< >>
733              
734             =head1 BUGS
735              
736             Please report any bugs or feature requests to L.
737              
738             =head1 SUPPORT
739              
740             You can find documentation for this module with the perldoc command.
741              
742             perldoc Math::Permutation
743              
744              
745             You can also look for information at:
746              
747             =over 4
748              
749             =item * RT: CPAN's request tracker (report bugs here)
750              
751             L
752              
753             =item * CPAN Ratings
754              
755             L
756              
757             =item * Search CPAN
758              
759             L
760              
761             =back
762              
763              
764             =head1 REFERENCES
765              
766             The module has gained ideas from various sources:
767              
768             Opensource resources:
769              
770             =over 4
771              
772             =item * L
773              
774             =item * L
775              
776             =item * L
777              
778             =back
779              
780             General resources:
781              
782             =over 4
783              
784             =item * L
785              
786             =item * I, Michael Artin
787              
788             =item * I, Sergey Kitaev
789              
790             =back
791              
792             =head1 LICENSE AND COPYRIGHT
793              
794             This software is Copyright (c) 2022 by Cheok-Yin Fung.
795              
796             This is free software, licensed under:
797              
798             MIT License
799              
800              
801             =cut
802              
803             1; # End of Math::Permutation