File Coverage

blib/lib/Math/Pandigital.pm
Criterion Covered Total %
statement 44 44 100.0
branch 22 22 100.0
condition 9 9 100.0
subroutine 10 10 100.0
pod 1 2 50.0
total 86 87 98.8


line stmt bran cond sub pod time code
1             package Math::Pandigital;
2              
3 2     2   565692 use Moo;
  2         258628  
  2         13  
4 2     2   11217 use MooX::Types::MooseLike::Base qw( Int Bool ArrayRef RegexpRef );
  2         15103  
  2         288  
5              
6 2     2   37 use strict;
  2         4  
  2         68  
7 2     2   11 use warnings;
  2         4  
  2         67  
8              
9 2     2   9 use Carp;
  2         4  
  2         1496  
10              
11             our $VERSION = '0.04';
12              
13             has base => ( is => 'ro', isa => Int, default => sub { 10; } );
14             has unique => ( is => 'ro', isa => Bool, default => sub { 0; } );
15             has zeroless => ( is => 'ro', isa => Bool, default => sub { 0; } );
16              
17              
18             has _digits_array => (
19             is => 'ro',
20             isa => ArrayRef,
21             lazy => 1,
22             builder => '_build_digits_array'
23             );
24              
25             has _digits_regexp => (
26             is => 'ro',
27             isa => RegexpRef,
28             lazy => 1,
29             builder => '_build_digits_regexp'
30             );
31              
32             has _min_length => (
33             is => 'ro',
34             isa => Int,
35             lazy => 1,
36             builder => '_build_min_length'
37             );
38              
39             sub BUILD {
40 18     18 0 4442 my $self = shift;
41 18 100       81 if( $self->base != 16 ) {
42 16 100 100     351 if( $self->base <= 0 || $self->base > 10 ) {
43 3         812 croak "Base must be 1 .. 10, or 16";
44             }
45             }
46 15 100 100     249 croak "Unary base must be zeroless."
47             if $self->base == 1 && ! $self->zeroless;
48 14         319 return;
49             }
50              
51             sub _build_digits_array {
52 11     11   9161 my $self = shift;
53 11         27 my $base = $self->base;
54              
55             # Special case for unary: 1 is the only reasonable digit.
56 11 100       213 return [1] if $base == 1;
57              
58             # Determine our set's lowest value.
59 9 100       27 my $start = $self->zeroless ? 1 : 0;
60              
61             # A hexidecimal set.
62 9 100       137 return [ $start .. 9, 'A' .. 'F' ] if $base == 16;
63              
64             # Base 2 - 10 set.
65 8         195 return [ $start .. $base - 1 ];
66             }
67              
68             sub _build_digits_regexp {
69 11     11   3837 my $self = shift;
70              
71             # Calculate the quantifier.
72 11         515 my $min_length = $self->_min_length;
73 11 100       744 my $quantifier = $self->unique ? "{$min_length}" : "{$min_length,}";
74              
75             # Compose a regex string with character class and quantifier.
76             # Will look similar to "(?i:^[0123456789]{4,})", for example.
77 11         280 my $re_str =
78 11         17 join( '', '(?i:^[', @{ $self->_digits_array() }, "]$quantifier)\$" );
79              
80             # Turn it into a Regexp object and return.
81 11         979 return qr/$re_str/;
82             }
83              
84             sub _build_min_length {
85 11     11   742 my $self = shift;
86              
87             # Special case for unary
88 11 100       85 return 1 if $self->base == 1;
89            
90             # Calculate the minimum possible input length for $value to qualify.
91 9 100       234 return $self->base - ( $self->zeroless ? 1 : 0 );
92             }
93              
94             sub is_pandigital {
95 20     20 1 4889 my ( $self, $value ) = @_;
96 20         42 $value =~ s/^0+//; # Strip insignificant leading zeros from strings.
97              
98             # The regexp test is done before we proceed to even more work.
99 20 100       592 return if not $value =~ $self->_digits_regexp; # Case insensitive.
100              
101             # Next, count individual digits to verify we have enough of each digit, and
102             # that we don't violate unique settings.
103 12         330 my $unique = $self->unique;
104 12         18 my %hash;
105 12         58 for my $digit ( split //, uc $value ) {
106 102 100 100     447 return if ++$hash{$digit} > 1 && $unique;
107             }
108 11         463 return keys %hash == $self->_min_length;
109              
110             }
111              
112             1;
113              
114             __END__