File Coverage

blib/lib/Math/NoCarry.pm
Criterion Covered Total %
statement 57 57 100.0
branch 17 20 85.0
condition 5 9 55.5
subroutine 7 7 100.0
pod 3 3 100.0
total 89 96 92.7


line stmt bran cond sub pod time code
1             package Math::NoCarry;
2 5     5   547155 use strict;
  5         9  
  5         177  
3              
4 5     5   47 use warnings;
  5         7  
  5         243  
5 5     5   31 no warnings;
  5         9  
  5         204  
6              
7 5     5   24 use Exporter qw(import);
  5         12  
  5         3622  
8              
9             our @EXPORT_OK = qw(add subtract multiply);
10             our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
11              
12             our $VERSION = '1.117';
13              
14             =encoding utf8
15              
16             =head1 NAME
17              
18             Math::NoCarry - Perl extension for no-carry arithmetic
19              
20             =head1 SYNOPSIS
21              
22             use Math::NoCarry qw(:all);
23              
24             my $sum = add( 123, 456 );
25              
26             my $difference = subtract( 123, 456 );
27              
28             my $product = multiply( 123, 456 );
29              
30             =head1 DESCRIPTION
31              
32             No-carry arithmetic doesn't allow you to carry digits to the
33             next column. For example, if you add 8 and 4, you normally
34             expect the answer to be 12, but that 1 digit is a carry.
35             In no-carry arithmetic you can't do that, so the sum of
36             8 and 4 is just 2. In effect, this is addition modulo 10
37             in each column. I discard all of the carry digits in
38             this example:
39              
40             1234
41             + 5678
42             ------
43             6802
44              
45             For multiplication, the result of pair-wise multiplication
46             of digits is the modulo 10 value of their normal, everyday
47             multiplication.
48              
49             123
50             x 456
51             -----
52             8 6 x 3
53             2 6 x 2
54             6 6 x 1
55              
56             5 5 x 3
57             0 5 x 2
58             5 5 x 1
59              
60             2 4 x 3
61             8 4 x 2
62             + 4 4 x 1
63             -------
64             43878
65              
66             Since multiplication and subtraction are actually types of
67             additions, you can multiply and subtract like this as well.
68              
69             No carry arithmetic is both associative and commutative.
70              
71             =head2 Functions
72              
73             As of version 1.11, all of these functions are exportable on
74             demand, or with the tag C<:all> to get them all at once.
75              
76             =over 4
77              
78             =item multiply( A, B )
79              
80             Returns the no carry product of A and B.
81              
82             Return A if it is the only argument ( A x 1 );
83              
84             =cut
85              
86             sub multiply {
87 57 100   57 1 197301 return $_[0] if $#_ < 1;
88              
89 14         33 @_ = map { $_ += 0 } @_;
  28         104  
90              
91 14   100     285 my $sign = ($_[0] > 0 and $_[1] < 0 ) ||
92             ($_[1] > 0 and $_[0] < 0 );
93              
94 14         61 my @p0 = reverse split //, abs $_[0];
95 14         1134 my @p1 = reverse split //, abs $_[1];
96              
97 14         29 my @m;
98              
99 14         46 foreach my $i ( 0 .. $#p0 ) {
100 42         76 foreach my $j ( 0 .. $#p1 ) {
101 126         344 push @m, ( ( $p1[$j] * $p0[$i] ) % 10 ) * ( 10**($i+$j) );
102             }
103             }
104              
105 14         38 while( @m > 1 ) {
106 112         225 unshift @m, Math::NoCarry::add( shift @m, shift @m );
107             }
108              
109 14 100       39 $m[0] *= -1 if $sign;
110              
111 14         72 return $m[0];
112             }
113              
114             =item add( A, B )
115              
116             Returns the no carry sum of the positive numbers A and B.
117              
118             Returns A if it is the only argument ( A + 0 )
119              
120             Returns false if either number is negative.
121              
122             =cut
123              
124             sub add {
125 128 100   128 1 154666 return $_[0] if $#_ < 1;
126              
127 118         184 @_ = map { local $^W; $_ += 0 } @_;
  236         428  
  236         734  
128              
129 118 50 33     393 return unless( $_[0] >= 0 and $_[1] >= 0 );
130              
131 118         448 my @addends = map scalar reverse, @_;
132              
133 118         223 my $string = '';
134              
135 118         156 my $max = length $addends[0];
136 118 100       220 $max = length $addends[1] if length $addends[1] > $max;
137              
138 118         236 for( my $i = 0; $i < $max ; $i++ ) {
139 380 100       501 my @digits = map { local $^W = 0; substr( $_, $i, 1) or 0 } @addends;
  760         1384  
  760         2162  
140              
141 380         595 my $sum = ( $digits[0] + $digits[1] ) % 10;
142              
143 380         798 $string .= $sum;
144             }
145              
146 118         553 $string =~ s/0*$//;
147              
148 118         205 $string = scalar reverse $string;
149              
150 118         441 return $string;
151             }
152              
153             =item subtract( A, B )
154              
155             Returns the no carry difference of the positive numbers A and B.
156              
157             Returns A if it is the only argument ( A - 0 )
158              
159             Returns false if either number is negative.
160              
161             =cut
162              
163             sub subtract {
164 16 100   16 1 192697 return $_[0] if $#_ < 1;
165              
166 6 50 33     17 return unless( $_[0] >= 0 and $_[1] >= 0);
167              
168 6         11 my @addends = map scalar reverse, @_;
169              
170 6         7 my $string = '';
171              
172 6         6 my $max = length $addends[0];
173 6 50       9 $max = length $addends[1] if length $addends[1] > $max;
174              
175 6         8 for( my $i = 0; $i < $max ; $i++ ) {
176 18         17 my @digits = map { substr $_, $i, 1 } @addends;
  36         41  
177              
178 18 100       24 $digits[0] += 10 if $digits[0] < $digits[1];
179              
180 18         18 my $sum = ( $digits[0] - $digits[1] ) % 10;
181              
182 18         28 $string .= $sum;
183             }
184              
185 6         10 return scalar reverse $string;
186             }
187              
188             1;
189              
190             __END__