File Coverage

blib/lib/MarpaX/Languages/M4/Impl/Default/BaseConversion.pm
Criterion Covered Total %
statement 209 220 95.0
branch 47 84 55.9
condition 2 6 33.3
subroutine 31 31 100.0
pod n/a
total 289 341 84.7


line stmt bran cond sub pod time code
1 1     1   6 use Moops;
  1         2  
  1         9  
2              
3             # PODNAME: MarpaX::Languages::M4::Impl::Default::BaseConversion
4              
5             # ABSTRACT: Base conversion util class
6              
7 1     1   3246 class MarpaX::Languages::M4::Impl::Default::BaseConversion {
  1     1   42  
  1     1   12  
  1     1   6  
  1     1   63  
  1     1   6  
  1     1   2  
  1     1   8  
  1     1   376  
  1     1   2  
  1     1   8  
  1     1   64  
  1     1   2  
  1     1   47  
  1         5  
  1         3  
  1         84  
  1         32  
  1         6  
  1         2  
  1         6  
  1         4652  
  1         2  
  1         9  
  1         406  
  1         3  
  1         8  
  1         141  
  1         2  
  1         9  
  1         74  
  1         3  
  1         6  
  1         225  
  1         2  
  1         10  
  1         899  
  1         3  
  1         7  
  1         1923  
  1         3  
  1         8  
  1         4  
  1         40  
  1         6  
  1         2  
  1         48  
  1         5  
  1         2  
  1         108  
  1         6345  
  0         0  
8 1     1   6 use Types::Common::Numeric -all;
  1         3  
  1         9  
9 1     1   5859 use Bit::Vector;
  1         2  
  1         84  
10 1     1   7 use Carp qw/croak/;
  1         2  
  1         159  
11              
12 1         11 our $VERSION = '0.019'; # VERSION
13              
14 1         2 our $AUTHORITY = 'cpan:JDDPAUSE'; # AUTHORITY
15              
16             #
17             # We handle bases [0..31].
18             #
19              
20             #
21             # Eval: constants for radix and the grammar
22             #
23 1         7 our @nums = ( 0 .. 9, 'a' .. 'z' );
24 1         3 our %nums = map { $nums[$_] => $_ } 0 .. $#nums;
  36         71  
25              
26             # Adaptation of http://www.perlmonks.org/?node_id=27148
27 1 50   1   7411 method bitvector_fr_base (ClassName $class: PositiveInt $bits, PositiveInt|Undef $base, Str $input, Bool $binary?) {
  1 50   1   2  
  1 50   1   90  
  1 50   1   5  
  1 50   1   2  
  1 50   1   165  
  1 50   344   6  
  1 50       2  
  1 50       101  
  1 50       6  
  1 50       2  
  1 50       90  
  1 50       5  
  1 50       3  
  1 50       124  
  1 50       7  
  1         2  
  1         562  
  1         7  
  344         1349  
  344         665  
  344         589  
  344         576  
  344         2047  
  344         1092  
  344         1000  
  344         1012  
  344         908  
  344         719  
  344         1003  
  344         991  
  344         886  
  344         745  
  344         1292  
  344         1106  
  344         990  
  344         583  
  344         1033  
  344         1010  
  344         919  
  344         580  
  344         1277  
  344         649  
28             #
29             # Per def the caller is responsabible to make sure input can contain only [0..9a-zA-Z].
30             # Thus it is safe to call lc()
31             #
32             # Note that we use $bits + 1, because Bit::Vector->Multiply() treats its arguments
33             # as SIGNED.
34             # Therefore we cannot reach the case where all bits would be setted to 1.
35             # We resize at the very end.
36             #
37             #
38             # Radix 1, i.e. the unary numeral system is a special case. GNU M4 say that the
39             # '1' is used to represent it, leading zeroes being ignored, and all remaining digits
40             # must be 1.
41             # The "value" is then just a count of them (== unary system).
42             #
43              
44 344 100       874 if ($binary) {
45 1         8 return Bit::Vector->new_Bin( $bits, $input );
46             }
47 343 100       1439 if ( $base == 1 ) {
48 1         7 $input =~ s/^0*//;
49 1 50       6 if ( $input =~ /[^1]/ ) {
50 0         0 croak
51             "radix 1 imposes eventual leading zeroes followed by zero or more '1' character(s)";
52             }
53 1         15 return Bit::Vector->new_Dec( $bits, length($input) );
54             }
55              
56 342         3039 my $b = Bit::Vector->new_Dec( $bits + 1, $base );
57 342         1445 my $v = Bit::Vector->new( $bits + 1 );
58 342         661 my $i = 0;
59 342         2315 for ( lc($input) =~ /./g ) {
60 424         970 ++$i;
61             {
62 424         628 my $s = $v->Shadow;
  424         1685  
63 424         1996 $s->Multiply( $v, $b );
64 424         1673 $v = $s;
65             }
66 424         1359 my $num = $nums{$_};
67 424 50       1122 if ( $num >= $base ) {
68 0         0 my $range = '';
69 0 0       0 if ( $base <= 10 ) {
70 0         0 $range = '[0-' . ( $base - 1 ) . ']';
71             }
72             else {
73 0         0 $range = '[0-9';
74 0 0       0 if ( $base == 11 ) {
75 0         0 $range .= 'a] (case independant)';
76             }
77             else {
78 0         0 $range
79             .= 'a-'
80             . $nums[ $base - 1 ]
81             . '] (case independant)';
82             }
83             }
84 0         0 croak "character '$_' is not in the range $range";
85             }
86             {
87 424         747 my $s = $v->Shadow;
  424         1305  
88 424         1746 my $n = Bit::Vector->new_Dec( $bits + 1, $num );
89 424         1978 $s->add( $v, $n, 0 );
90 424         1660 $v = $s;
91             }
92             }
93 342         1319 $v->Resize($bits);
94 342         1419 return $v;
95             }
96              
97 1 50 33 1   5999 method bitvector_to_base (ClassName $class: PositiveInt $base, ConsumerOf['Bit::Vector'] $v, Int $min --> Str) {
  1 50 33 1   2  
  1 50   1   77  
  1 50   1   6  
  1 50   1   3  
  1 50   162   131  
  1 50       7  
  1 50       2  
  1 50       94  
  1 50       5  
  1 50       4  
  1 50       130  
  1 50       6  
  1         1  
  1         588  
  1         242  
  162         2132  
  162         389  
  162         382  
  162         439  
  162         1035  
  162         676  
  162         665  
  162         614  
  162         306  
  162         691  
  162         662  
  162         598  
  162         314  
  162         296  
  162         877  
  162         903  
  162         1026  
  162         649  
  162         616  
  162         411  
  162         1829  
  162         350  
98              
99 162         1447 my $b = Bit::Vector->new_Dec( $v->Size(), $base );
100 162         779 $v = $v->Clone();
101             #
102             # Per construction $base is in the range [1..61]
103             #
104 162         473 my $rep = '';
105 162         453 my $s = '';
106 162 100       1255 my $signed = ( $v->Sign() < 0 ) ? true : false;
107 162         889 my $abs;
108 162 100       695 if ($signed) {
109 9         63 $abs = $v->Shadow;
110 9         57 $abs->Negate($v);
111             }
112             else {
113 153         347 $abs = $v;
114             }
115              
116 162 100       503 if ( $base == 1 ) {
117 1         10 my $rep = '1' x $abs->to_Dec();
118             #
119             # Adapt to width
120             #
121 1 50       6 if ( length($rep) < $min ) {
122 1         5 $rep = ( '0' x ( $min - length($rep) ) ) . $rep;
123             }
124 1 50       4 if ($signed) {
125 0         0 $rep = "-$rep";
126             }
127 1         23 return $rep;
128             }
129              
130 161         1229 while ( $abs->to_Dec() ne "0" ) {
131 231         554 my $mod;
132             {
133 231         395 my $s = $abs->Shadow;
  231         765  
134 231         1531 $abs->Shadow->Divide( $abs, $b, $s );
135 231         899 $mod = $s;
136             }
137             #
138             # Why abs() ? Because when $v is equal to 2^(n-1), number remains the same.
139             #
140 231         1347 $s = $nums[ abs($mod->to_Dec()) ] . $s;
141             {
142 231         449 my $s = $abs->Shadow;
  231         747  
143 231         1159 $s->Divide( $abs, $b, $abs->Shadow );
144 231         1225 $abs = $s;
145             }
146             }
147 161 100       588 if ($signed) {
148 9         34 $s = "-$s";
149             }
150 161 100       697 if ( substr( $s, 0, 1 ) eq '-' ) {
151 9         31 $rep .= '-';
152 9         52 substr( $s, 0, 1, '' );
153             }
154 161         708 for ( $min -= length($s); --$min >= 0; ) {
155 19         62 $rep .= '0';
156             }
157 161         331 $rep .= $s;
158              
159 161         3945 return $rep;
160             }
161             }
162              
163             1;
164              
165             __END__
166              
167             =pod
168              
169             =encoding UTF-8
170              
171             =head1 NAME
172              
173             MarpaX::Languages::M4::Impl::Default::BaseConversion - Base conversion util class
174              
175             =head1 VERSION
176              
177             version 0.019
178              
179             =head1 AUTHOR
180              
181             Jean-Damien Durand <jeandamiendurand@free.fr>
182              
183             =head1 COPYRIGHT AND LICENSE
184              
185             This software is copyright (c) 2015 by Jean-Damien Durand.
186              
187             This is free software; you can redistribute it and/or modify it under
188             the same terms as the Perl 5 programming language system itself.
189              
190             =cut