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   151430 use strict;
  7         33  
  7         204  
3 7     7   56 use warnings;
  7         22  
  7         184  
4 7     7   40 use Exporter 'import';
  7         26  
  7         3471  
5             our @EXPORT_OK=qw(nCk factorial enumerate_substitution_maps);
6             our $VERSION = '1.1.1'; # VERSION
7             # ABSTRACT: some combinatorial functions
8              
9              
10             sub nCk {
11 2575     2575 1 5074 my ($n, $k) = @_;
12             # from http://blog.plover.com/math/choose.html
13              
14 2575 50       5476 return 0 if $k > $n;
15 2575 100       8842 return 1 if $k == 0;
16              
17 1047         1858 my $ret = 1;
18 1047         1972 for my $d (1..$k) {
19 1509         2451 $ret *= $n;
20 1509         2523 $ret /= $d;
21 1509         2833 --$n;
22             }
23              
24 1047         3690 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   4841 my ($subs) = @_;
31             my %keyed = map {
32 3699         6516 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 3699         6447 map { "${_},$this_sub->{$_}" } sort keys %{$this_sub},
  9699         31933  
  3699         11150  
38             ) => $this_sub
39 2414         4481 } @{$subs};
  2414         4973  
40 2414         9013 return [values %keyed];
41             }
42              
43             sub _recursive_enumeration {
44 3951     3951   8998 my ($table,$keys,$subs) = @_;
45 3951 100       6240 return $subs unless @{$keys};
  3951         15366  
46 2414         5149 my ($first_key,@rest_keys) = @{$keys};
  2414         6952  
47 2414         4616 my @next_subs;
48 2414         4309 for my $value (@{$table->{$first_key}}) {
  2414         5709  
49 2547         4804 for my $sub (@{$subs}) {
  2547         5116  
50             # if we already have a reverse mapping for this, keep it
51             push @next_subs, $sub
52 3137 100       7979 if exists $sub->{$value};
53             # and add this new one
54 3137         5514 push @next_subs, { %{$sub}, $value => $first_key };
  3137         11809  
55             }
56             }
57              
58 2414         6566 my $deduped_next_subs = _dedupe(\@next_subs);
59 2414         8771 return _recursive_enumeration($table,\@rest_keys,\@next_subs);
60             }
61              
62              
63             sub enumerate_substitution_maps {
64 1537     1537 1 28221 my ($table) = @_;
65              
66             return _recursive_enumeration(
67             $table,
68 1537         3419 [keys %{$table}],
  1537         8389  
69             [{}], # it needs an accumulator with an initial empty element
70             );
71             }
72              
73              
74             sub factorial {
75 25161     25161 1 39268 my $ret=1;
76 25161         73761 $ret*=$_ for 1..$_[0];
77 25161         52114 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.1
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