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   7 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   3083 class MarpaX::Languages::M4::Impl::Default::BaseConversion {
  1     1   30  
  1     1   6  
  1     1   2  
  1     1   57  
  1     1   6  
  1     1   1  
  1     1   9  
  1     1   300  
  1     1   2  
  1     1   6  
  1     1   58  
  1     1   2  
  1     1   44  
  1         5  
  1         2  
  1         82  
  1         31  
  1         6  
  1         2  
  1         5  
  1         4446  
  1         2  
  1         10  
  1         471  
  1         2  
  1         9  
  1         149  
  1         2  
  1         10  
  1         80  
  1         2  
  1         8  
  1         217  
  1         3  
  1         7  
  1         877  
  1         3  
  1         7  
  1         1842  
  1         3  
  1         4  
  1         2  
  1         22  
  1         4  
  1         1  
  1         46  
  1         4  
  1         2  
  1         103  
  1         5921  
  0         0  
8 1     1   6 use Types::Common::Numeric -all;
  1         2  
  1         8  
9 1     1   5567 use Bit::Vector;
  1         2  
  1         60  
10 1     1   6 use Carp qw/croak/;
  1         3  
  1         176  
11              
12 1         12 our $VERSION = '0.020'; # 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         8 our @nums = ( 0 .. 9, 'a' .. 'z' );
24 1         4 our %nums = map { $nums[$_] => $_ } 0 .. $#nums;
  36         73  
25              
26             # Adaptation of http://www.perlmonks.org/?node_id=27148
27 1 50   1   7118 method bitvector_fr_base (ClassName $class: PositiveInt $bits, PositiveInt|Undef $base, Str $input, Bool $binary?) {
  1 50   1   2  
  1 50   1   97  
  1 50   1   6  
  1 50   1   2  
  1 50   1   145  
  1 50   344   6  
  1 50       2  
  1 50       96  
  1 50       5  
  1 50       1  
  1 50       87  
  1 50       5  
  1 50       2  
  1 50       91  
  1 50       5  
  1         2  
  1         550  
  1         7  
  344         1183  
  344         613  
  344         657  
  344         628  
  344         2086  
  344         1096  
  344         839  
  344         1286  
  344         927  
  344         529  
  344         1052  
  344         1011  
  344         975  
  344         505  
  344         1180  
  344         1248  
  344         1315  
  344         591  
  344         994  
  344         971  
  344         881  
  344         528  
  344         995  
  344         583  
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       741 if ($binary) {
45 1         7 return Bit::Vector->new_Bin( $bits, $input );
46             }
47 343 100       1371 if ( $base == 1 ) {
48 1         7 $input =~ s/^0*//;
49 1 50       7 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         3189 my $b = Bit::Vector->new_Dec( $bits + 1, $base );
57 342         1286 my $v = Bit::Vector->new( $bits + 1 );
58 342         670 my $i = 0;
59 342         2187 for ( lc($input) =~ /./g ) {
60 424         738 ++$i;
61             {
62 424         595 my $s = $v->Shadow;
  424         1409  
63 424         2036 $s->Multiply( $v, $b );
64 424         1547 $v = $s;
65             }
66 424         1236 my $num = $nums{$_};
67 424 50       1061 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         765 my $s = $v->Shadow;
  424         1250  
88 424         1679 my $n = Bit::Vector->new_Dec( $bits + 1, $num );
89 424         1760 $s->add( $v, $n, 0 );
90 424         1540 $v = $s;
91             }
92             }
93 342         1320 $v->Resize($bits);
94 342         1448 return $v;
95             }
96              
97 1 50 33 1   5822 method bitvector_to_base (ClassName $class: PositiveInt $base, ConsumerOf['Bit::Vector'] $v, Int $min --> Str) {
  1 50 33 1   3  
  1 50   1   80  
  1 50   1   5  
  1 50   1   3  
  1 50   162   133  
  1 50       6  
  1 50       2  
  1 50       151  
  1 50       7  
  1 50       2  
  1 50       132  
  1 50       7  
  1         1  
  1         584  
  1         266  
  162         2322  
  162         407  
  162         311  
  162         310  
  162         1289  
  162         603  
  162         660  
  162         650  
  162         370  
  162         698  
  162         598  
  162         467  
  162         333  
  162         342  
  162         901  
  162         819  
  162         928  
  162         755  
  162         735  
  162         353  
  162         1868  
  162         404  
98              
99 162         1290 my $b = Bit::Vector->new_Dec( $v->Size(), $base );
100 162         818 $v = $v->Clone();
101             #
102             # Per construction $base is in the range [1..61]
103             #
104 162         401 my $rep = '';
105 162         496 my $s = '';
106 162 100       1235 my $signed = ( $v->Sign() < 0 ) ? true : false;
107 162         708 my $abs;
108 162 100       685 if ($signed) {
109 9         43 $abs = $v->Shadow;
110 9         39 $abs->Negate($v);
111             }
112             else {
113 153         275 $abs = $v;
114             }
115              
116 162 100       632 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         21 return $rep;
128             }
129              
130 161         1047 while ( $abs->to_Dec() ne "0" ) {
131 231         562 my $mod;
132             {
133 231         435 my $s = $abs->Shadow;
  231         649  
134 231         1543 $abs->Shadow->Divide( $abs, $b, $s );
135 231         678 $mod = $s;
136             }
137             #
138             # Why abs() ? Because when $v is equal to 2^(n-1), number remains the same.
139             #
140 231         1213 $s = $nums[ abs($mod->to_Dec()) ] . $s;
141             {
142 231         405 my $s = $abs->Shadow;
  231         708  
143 231         1312 $s->Divide( $abs, $b, $abs->Shadow );
144 231         1195 $abs = $s;
145             }
146             }
147 161 100       533 if ($signed) {
148 9         31 $s = "-$s";
149             }
150 161 100       635 if ( substr( $s, 0, 1 ) eq '-' ) {
151 9         30 $rep .= '-';
152 9         33 substr( $s, 0, 1, '' );
153             }
154 161         722 for ( $min -= length($s); --$min >= 0; ) {
155 19         49 $rep .= '0';
156             }
157 161         421 $rep .= $s;
158              
159 161         3836 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.020
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