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         8  
2              
3             # PODNAME: MarpaX::Languages::M4::Impl::Default::BaseConversion
4              
5             # ABSTRACT: Base conversion util class
6              
7 1     1   3284 class MarpaX::Languages::M4::Impl::Default::BaseConversion {
  1     1   29  
  1     1   7  
  1     1   2  
  1     1   63  
  1     1   8  
  1     1   3  
  1     1   11  
  1     1   402  
  1     1   2  
  1     1   6  
  1     1   62  
  1     1   2  
  1     1   95  
  1         6  
  1         2  
  1         82  
  1         31  
  1         5  
  1         2  
  1         7  
  1         4948  
  1         2  
  1         9  
  1         473  
  1         2  
  1         8  
  1         136  
  1         2  
  1         9  
  1         73  
  1         2  
  1         7  
  1         211  
  1         3  
  1         8  
  1         852  
  1         3  
  1         7  
  1         2059  
  1         4  
  1         5  
  1         1  
  1         23  
  1         4  
  1         2  
  1         49  
  1         5  
  1         3  
  1         111  
  1         6512  
  0         0  
8 1     1   8 use Types::Common::Numeric -all;
  1         3  
  1         12  
9 1     1   6417 use Bit::Vector;
  1         2  
  1         82  
10 1     1   13 use Carp qw/croak/;
  1         2  
  1         216  
11              
12 1         29 our $VERSION = '0.018'; # VERSION
13              
14 1         4 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         8 our @nums = ( 0 .. 9, 'a' .. 'z' );
24 1         4 our %nums = map { $nums[$_] => $_ } 0 .. $#nums;
  36         75  
25              
26             # Adaptation of http://www.perlmonks.org/?node_id=27148
27 1 50   1   7811 method bitvector_fr_base (ClassName $class: PositiveInt $bits, PositiveInt|Undef $base, Str $input, Bool $binary?) {
  1 50   1   3  
  1 50   1   110  
  1 50   1   6  
  1 50   1   2  
  1 50   1   192  
  1 50   344   6  
  1 50       2  
  1 50       134  
  1 50       9  
  1 50       3  
  1 50       138  
  1 50       5  
  1 50       2  
  1 50       159  
  1 50       9  
  1         3  
  1         563  
  1         8  
  344         1220  
  344         684  
  344         562  
  344         512  
  344         1951  
  344         1019  
  344         845  
  344         1016  
  344         885  
  344         558  
  344         963  
  344         916  
  344         861  
  344         495  
  344         1002  
  344         848  
  344         794  
  344         605  
  344         905  
  344         819  
  344         783  
  344         510  
  344         904  
  344         671  
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       773 if ($binary) {
45 1         6 return Bit::Vector->new_Bin( $bits, $input );
46             }
47 343 100       1172 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         14 return Bit::Vector->new_Dec( $bits, length($input) );
54             }
55              
56 342         2687 my $b = Bit::Vector->new_Dec( $bits + 1, $base );
57 342         1379 my $v = Bit::Vector->new( $bits + 1 );
58 342         893 my $i = 0;
59 342         2194 for ( lc($input) =~ /./g ) {
60 424         798 ++$i;
61             {
62 424         789 my $s = $v->Shadow;
  424         1319  
63 424         1704 $s->Multiply( $v, $b );
64 424         1628 $v = $s;
65             }
66 424         1043 my $num = $nums{$_};
67 424 50       1070 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         641 my $s = $v->Shadow;
  424         998  
88 424         1470 my $n = Bit::Vector->new_Dec( $bits + 1, $num );
89 424         1635 $s->add( $v, $n, 0 );
90 424         1366 $v = $s;
91             }
92             }
93 342         1241 $v->Resize($bits);
94 342         1283 return $v;
95             }
96              
97 1 50 33 1   6745 method bitvector_to_base (ClassName $class: PositiveInt $base, ConsumerOf['Bit::Vector'] $v, Int $min --> Str) {
  1 50 33 1   3  
  1 50   1   155  
  1 50   1   7  
  1 50   1   2  
  1 50   162   148  
  1 50       5  
  1 50       3  
  1 50       172  
  1 50       9  
  1 50       3  
  1 50       185  
  1 50       6  
  1         2  
  1         588  
  1         298  
  162         1983  
  162         379  
  162         280  
  162         320  
  162         858  
  162         504  
  162         569  
  162         534  
  162         286  
  162         476  
  162         502  
  162         513  
  162         293  
  162         323  
  162         696  
  162         715  
  162         796  
  162         618  
  162         497  
  162         298  
  162         1510  
  162         374  
98              
99 162         1194 my $b = Bit::Vector->new_Dec( $v->Size(), $base );
100 162         687 $v = $v->Clone();
101             #
102             # Per construction $base is in the range [1..61]
103             #
104 162         389 my $rep = '';
105 162         382 my $s = '';
106 162 100       886 my $signed = ( $v->Sign() < 0 ) ? true : false;
107 162         637 my $abs;
108 162 100       452 if ($signed) {
109 9         39 $abs = $v->Shadow;
110 9         36 $abs->Negate($v);
111             }
112             else {
113 153         263 $abs = $v;
114             }
115              
116 162 100       427 if ( $base == 1 ) {
117 1         8 my $rep = '1' x $abs->to_Dec();
118             #
119             # Adapt to width
120             #
121 1 50       5 if ( length($rep) < $min ) {
122 1         4 $rep = ( '0' x ( $min - length($rep) ) ) . $rep;
123             }
124 1 50       3 if ($signed) {
125 0         0 $rep = "-$rep";
126             }
127 1         21 return $rep;
128             }
129              
130 161         1026 while ( $abs->to_Dec() ne "0" ) {
131 231         428 my $mod;
132             {
133 231         422 my $s = $abs->Shadow;
  231         604  
134 231         1289 $abs->Shadow->Divide( $abs, $b, $s );
135 231         767 $mod = $s;
136             }
137             #
138             # Why abs() ? Because when $v is equal to 2^(n-1), number remains the same.
139             #
140 231         1041 $s = $nums[ abs($mod->to_Dec()) ] . $s;
141             {
142 231         500 my $s = $abs->Shadow;
  231         685  
143 231         1096 $s->Divide( $abs, $b, $abs->Shadow );
144 231         1165 $abs = $s;
145             }
146             }
147 161 100       486 if ($signed) {
148 9         27 $s = "-$s";
149             }
150 161 100       532 if ( substr( $s, 0, 1 ) eq '-' ) {
151 9         23 $rep .= '-';
152 9         26 substr( $s, 0, 1, '' );
153             }
154 161         649 for ( $min -= length($s); --$min >= 0; ) {
155 19         47 $rep .= '0';
156             }
157 161         350 $rep .= $s;
158              
159 161         3594 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.018
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