File Coverage

blib/lib/Math/Permutation.pm
Criterion Covered Total %
statement 199 331 60.1
branch 25 64 39.0
condition 6 17 35.2
subroutine 31 50 62.0
pod 33 33 100.0
total 294 495 59.3


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