File Coverage

blib/lib/Data/Password/zxcvbn/Combinatorics.pm
Criterion Covered Total %
statement 47 47 100.0
branch 7 8 87.5
condition n/a
subroutine 8 8 100.0
pod 3 3 100.0
total 65 66 98.4


line stmt bran cond sub pod time code
1             package Data::Password::zxcvbn::Combinatorics;
2 7     7   155068 use strict;
  7         28  
  7         225  
3 7     7   36 use warnings;
  7         23  
  7         231  
4 7     7   44 use Exporter 'import';
  7         22  
  7         3864  
5             our @EXPORT_OK=qw(nCk factorial enumerate_substitution_maps);
6             our $VERSION = '1.1.0'; # VERSION
7             # ABSTRACT: some combinatorial functions
8              
9              
10             sub nCk {
11 2575     2575 1 4925 my ($n, $k) = @_;
12             # from http://blog.plover.com/math/choose.html
13              
14 2575 50       5279 return 0 if $k > $n;
15 2575 100       8575 return 1 if $k == 0;
16              
17 1047         1851 my $ret = 1;
18 1047         2040 for my $d (1..$k) {
19 1509         2278 $ret *= $n;
20 1509         2438 $ret /= $d;
21 1509         2550 --$n;
22             }
23              
24 1047         3647 return $ret;
25             }
26              
27             # given as array of simple str-str hashrefs, returns a list without
28             # duplicates
29             sub _dedupe {
30 2414     2414   5088 my ($subs) = @_;
31             my %keyed = map {
32 4008         6362 my $this_sub=$_;
33             # build a string representing the substitution, use it as a
34             # hash key, so duplicates get eliminated
35             join(
36             '-',
37 4008         6278 map { "${_},$this_sub->{$_}" } sort keys %{$this_sub},
  10196         33068  
  4008         11451  
38             ) => $this_sub
39 2414         3948 } @{$subs};
  2414         4654  
40 2414         9510 return [values %keyed];
41             }
42              
43             sub _recursive_enumeration {
44 3951     3951   10626 my ($table,$keys,$subs) = @_;
45 3951 100       5764 return $subs unless @{$keys};
  3951         16409  
46 2414         4761 my ($first_key,@rest_keys) = @{$keys};
  2414         7292  
47 2414         4502 my @next_subs;
48 2414         4117 for my $value (@{$table->{$first_key}}) {
  2414         6193  
49 2547         4390 for my $sub (@{$subs}) {
  2547         5320  
50             # if we already have a reverse mapping for this, keep it
51             push @next_subs, $sub
52 3457 100       9224 if exists $sub->{$value};
53             # and add this new one
54 3457         5580 push @next_subs, { %{$sub}, $value => $first_key };
  3457         13517  
55             }
56             }
57              
58 2414         6657 my $deduped_next_subs = _dedupe(\@next_subs);
59 2414         8873 return _recursive_enumeration($table,\@rest_keys,\@next_subs);
60             }
61              
62              
63             sub enumerate_substitution_maps {
64 1537     1537 1 28023 my ($table) = @_;
65              
66             return _recursive_enumeration(
67             $table,
68 1537         3212 [keys %{$table}],
  1537         9797  
69             [{}], # it needs an accumulator with an initial empty element
70             );
71             }
72              
73              
74             sub factorial {
75 25100     25100 1 40460 my $ret=1;
76 25100         72356 $ret*=$_ for 1..$_[0];
77 25100         51337 return $ret;
78             }
79              
80             1;
81              
82             __END__
83              
84             =pod
85              
86             =encoding UTF-8
87              
88             =for :stopwords combinatorial
89              
90             =head1 NAME
91              
92             Data::Password::zxcvbn::Combinatorics - some combinatorial functions
93              
94             =head1 VERSION
95              
96             version 1.1.0
97              
98             =head1 DESCRIPTION
99              
100             This module provides a few combinatorial functions that are used
101             throughout the library.
102              
103             =head1 FUNCTIONS
104              
105             =head2 C<nCk>
106              
107             my $combinations = nCk($available,$taken);
108              
109             Returns the binomial coefficient:
110              
111             / $available \
112             | |
113             \ $taken /
114              
115             =head2 C<enumerate_substitution_maps>
116              
117             my $enumeration = enumerate_substitution_maps(\%substitutions);
118              
119             Given a hashref of arrayrefs, interprets it as a map of
120             substitutions. Returns an arrayref of hashrefs, containing all
121             reverse-substitutions.
122              
123             For example, given:
124              
125             {'a' => ['@', '4']}
126              
127             ("'a' can be replaced with either '@' or '4'")
128              
129             it returns:
130              
131             [{'@' => 'a'}, {'4' => 'a'}] ],
132              
133             ("in one case, '@' could have been substituted for 'a'; in the other,
134             '4' could have been substituted for 'a'")
135              
136             =head2 C<factorial>
137              
138             my $fact = factorial($number);
139              
140             Returns the factorial of the given number.
141              
142             =head1 AUTHOR
143              
144             Gianni Ceccarelli <gianni.ceccarelli@broadbean.com>
145              
146             =head1 COPYRIGHT AND LICENSE
147              
148             This software is copyright (c) 2022 by BroadBean UK, a CareerBuilder Company.
149              
150             This is free software; you can redistribute it and/or modify it under
151             the same terms as the Perl 5 programming language system itself.
152              
153             =cut