File Coverage

blib/lib/Math/BaseMulti.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Math::BaseMulti;
2             # ABSTRACT: creating identifiers with a per digit base
3              
4 2     2   32123 use Moose;
  0            
  0            
5             our $VERSION = '1.01'; # VERSION
6              
7             has 'digits' => (
8             is => 'ro',
9             traits => [ 'Array' ],
10             isa => 'ArrayRef[ArrayRef[Str]]',
11             required => 1,
12             handles => {
13             '_num_digits' => 'count',
14             },
15             );
16              
17             has '_reverse_digits' => (
18             is => 'ro',
19             isa => 'ArrayRef[HashRef]',
20             lazy => 1,
21             default => sub {
22             my $self = shift;
23             my @reverse;
24             foreach my $digit ( @{$self->digits} ) {
25             my %map;
26             @map{@$digit} = (0..$#$digit);
27             unshift(@reverse, \%map);
28             }
29             return(\@reverse);
30             },
31             );
32              
33             has '_base_list' => (
34             is => 'ro',
35             isa => 'ArrayRef[Int]',
36             lazy => 1,
37             default => sub {
38             my $self = shift;
39             my @b;
40             foreach my $digits ( @{$self->digits} ) {
41             push(@b, scalar(@$digits) );
42             }
43             return(\@b);
44             },
45             );
46              
47             has '_reverse_radix_list' => (
48             is => 'ro',
49             isa => 'ArrayRef[Int]',
50             lazy => 1,
51             default => sub {
52             my $self = shift;
53             my @r = reverse @{ $self->_radix_list };
54             return( \@r );
55             },
56             );
57              
58             has '_radix_list' => (
59             is => 'ro',
60             isa => 'ArrayRef[Int]',
61             traits => [ 'Array' ],
62             lazy => 1,
63             default => sub {
64             my $self = shift;
65             my @bases = @{$self->_base_list};
66             my @r = (1);
67             my $radix = 1;
68             my $base;
69            
70             while( $base = pop(@bases) ) {
71             $radix = $radix * $base;
72             unshift(@r, $radix);
73             }
74             shift(@r);
75             return( \@r );
76             },
77             );
78              
79             has 'leading_zero' => (
80             is => 'rw',
81             isa => 'Bool',
82             default => 0,
83             );
84              
85             sub to {
86             my $self = shift;
87             my $v = shift;
88             my $str = '';
89             my $left;
90             my $cur;
91             my $trailing = 0;
92              
93             for( my $i=0 ; $i < $self->_num_digits ; $i++ ) {
94             my $r = $self->_radix_list->[$i];
95             if( $v < $r ) {
96             if( $trailing || $self->leading_zero ) {
97             $str .= $self->digits->[$i]->[0];
98             }
99             next;
100             }
101             $trailing = 1;
102             $cur = int( $v / $r );
103             if( $cur > ($self->_base_list->[$i] - 1) ) {
104             die('value is too big for conversion!');
105             }
106             $str .= $self->digits->[$i]->[$cur];
107             $v = $v % $r;
108             }
109              
110             return($str);
111             }
112              
113             sub from {
114             my $self = shift;
115             my @str = reverse( split(//, shift) );
116             my $int = 0;
117              
118             if( scalar(@str) > $self->_num_digits ) {
119             die('string is too long for conversion!');
120             }
121              
122             foreach my $i (0..$#str) {
123             my $digit_value = $self->_reverse_digits->[$i]->{ $str[$i] };
124             if( !defined $digit_value ) {
125             die("character ".$str[$i]." is not a valid digit at position ".($i+1)." (from right)" );
126             }
127             $int += $self->_reverse_digits->[$i]->{ $str[$i] } * $self->_reverse_radix_list->[$i];
128             }
129             return($int);
130             }
131              
132             1;
133              
134             __END__
135              
136             =pod
137              
138             =head1 NAME
139              
140             Math::BaseMulti - a perl module for creating identifiers with a per digit base
141              
142             =head1 SYNOPSIS
143              
144             use Math::BaseMulti;
145              
146             $mbm = Math::BaseMulti->new(
147             digits => [
148             [ 0..9, 'A'..'Z' ],
149             [ 0..9, 'A'..'Z' ],
150             [ 0..9, 'A'..'Z' ],
151             [ 0..9 ],
152             ],
153             );
154              
155             $mbm->to( 10 ); # will return "10"
156             $mbm->to( 1000 ); # will return "2S0"
157             $mbm->from( 'BA0' ); # will return 133310
158              
159             --
160              
161             $mbm = Math::BaseMulti->new(
162             digits => [
163             [ 'S' ],
164             [ 'N' ],
165             [ 0..9,'A'..'F','H','J','K','M','N','P','R'..'Z' ],
166             [ 0..9,'A'..'F','H','J','K','M','N','P','R'..'Z' ],
167             [ 0..9,'A'..'F','H','J','K','M','N','P','R'..'Z' ],
168             [ 0..9,'A'..'F','H','J','K','M','N','P','R'..'Z' ],
169             [ 0..9,'A'..'F','H','J','K','M','N','P','R'..'Z' ],
170             [ 'A'..'Z' ],
171             ],
172             leading_zero => 1,
173             );
174              
175             $mbm->to( 0 ); # will return "SN00000A"
176             $mbm->to( 1 ); # will return "SN00000B"
177             $mbm->to( 1000 ); # will return "SN00017M"
178              
179             =head1 DESCRIPTION
180              
181             Math::BaseMulti can be used to create identifiers with a base defined per digit.
182              
183             The module provides conversion to/from such identifiers.
184              
185             =head1 METHODS
186              
187             =head2 new()
188              
189             Creates an object instance.
190              
191             Accepts parameters 'digits' and 'leading_zero'. For description see methods below.
192              
193             =head2 from()
194              
195             Expects a string in the format of defined by the parameter 'digits' and
196             converts it to an Int value.
197              
198             =head2 to()
199              
200             Expects an Int value and converts it to a string in the format defined by the
201             'digits' parameter.
202              
203             =head2 digits()
204              
205             Accepts an array of arrays.
206              
207             Each element in the the first array repersents a digit. From high to low. (Little-Endian)
208             Each subarray contains a list of possible characters. The value will be the index of the
209             character in this array. first element => 0, second element => 1, ...
210              
211             =head2 leading_zero()
212              
213             Defines if to() should always add padding zeros values.
214              
215             =head1 DEPENDENCIES
216              
217             Math::BaseMulti requires Moose.
218              
219             =head1 COPYRIGHT AND LICENSE
220              
221             Copyright 2012-2013 by Markus Benning
222              
223             This library is free software; you can redistribute it and/or modify
224             it under the same terms as Perl itself.
225              
226             =cut
227