1package Math::BigInt::CalcEmu;
2
3use 5.005;
4use strict;
5# use warnings;	# dont use warnings for older Perls
6use vars qw/$VERSION/;
7
8$VERSION = '0.05';
9
10package Math::BigInt;
11
12# See SYNOPSIS below.
13
14my $CALC_EMU;
15
16BEGIN
17  {
18  $CALC_EMU = Math::BigInt->config()->{'lib'};
19  # register us with MBI to get notified of future lib changes
20  Math::BigInt::_register_callback( __PACKAGE__, sub { $CALC_EMU = $_[0]; } );
21  }
22
23sub __emu_band
24  {
25  my ($self,$x,$y,$sx,$sy,@r) = @_;
26
27  return $x->bzero(@r) if $y->is_zero() || $x->is_zero();
28
29  my $sign = 0;					# sign of result
30  $sign = 1 if $sx == -1 && $sy == -1;
31
32  my ($bx,$by);
33
34  if ($sx == -1)				# if x is negative
35    {
36    # two's complement: inc and flip all "bits" in $bx
37    $bx = $x->binc()->as_hex();			# -1 => 0, -2 => 1, -3 => 2 etc
38    $bx =~ s/-?0x//;
39    $bx =~ tr/0123456789abcdef/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
40    }
41  else
42    {
43    $bx = $x->as_hex();				# get binary representation
44    $bx =~ s/-?0x//;
45    $bx =~ tr/fedcba9876543210/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
46    }
47  if ($sy == -1)				# if y is negative
48    {
49    # two's complement: inc and flip all "bits" in $by
50    $by = $y->copy()->binc()->as_hex();		# -1 => 0, -2 => 1, -3 => 2 etc
51    $by =~ s/-?0x//;
52    $by =~ tr/0123456789abcdef/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
53    }
54  else
55    {
56    $by = $y->as_hex();				# get binary representation
57    $by =~ s/-?0x//;
58    $by =~ tr/fedcba9876543210/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
59    }
60  # now we have bit-strings from X and Y, reverse them for padding
61  $bx = reverse $bx;
62  $by = reverse $by;
63
64  # padd the shorter string
65  my $xx = "\x00"; $xx = "\x0f" if $sx == -1;
66  my $yy = "\x00"; $yy = "\x0f" if $sy == -1;
67  my $diff = CORE::length($bx) - CORE::length($by);
68  if ($diff > 0)
69    {
70    # if $yy eq "\x00", we can cut $bx, otherwise we need to padd $by
71    $by .= $yy x $diff;
72    }
73  elsif ($diff < 0)
74    {
75    # if $xx eq "\x00", we can cut $by, otherwise we need to padd $bx
76    $bx .= $xx x abs($diff);
77    }
78
79  # and the strings together
80  my $r = $bx & $by;
81
82  # and reverse the result again
83  $bx = reverse $r;
84
85  # One of $x or $y was negative, so need to flip bits in the result.
86  # In both cases (one or two of them negative, or both positive) we need
87  # to get the characters back.
88  if ($sign == 1)
89    {
90    $bx =~ tr/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/0123456789abcdef/;
91    }
92  else
93    {
94    $bx =~ tr/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/fedcba9876543210/;
95    }
96
97  # leading zeros will be stripped by _from_hex()
98  $bx = '0x' . $bx;
99  $x->{value} = $CALC_EMU->_from_hex( $bx );
100
101  # calculate sign of result
102  $x->{sign} = '+';
103  $x->{sign} = '-' if $sign == 1 && !$x->is_zero();
104
105  $x->bdec() if $sign == 1;
106
107  $x->round(@r);
108  }
109
110sub __emu_bior
111  {
112  my ($self,$x,$y,$sx,$sy,@r) = @_;
113
114  return $x->round(@r) if $y->is_zero();
115
116  my $sign = 0;					# sign of result
117  $sign = 1 if ($sx == -1) || ($sy == -1);
118
119  my ($bx,$by);
120
121  if ($sx == -1)				# if x is negative
122    {
123    # two's complement: inc and flip all "bits" in $bx
124    $bx = $x->binc()->as_hex();			# -1 => 0, -2 => 1, -3 => 2 etc
125    $bx =~ s/-?0x//;
126    $bx =~ tr/0123456789abcdef/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
127    }
128  else
129    {
130    $bx = $x->as_hex();				# get binary representation
131    $bx =~ s/-?0x//;
132    $bx =~ tr/fedcba9876543210/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
133    }
134  if ($sy == -1)				# if y is negative
135    {
136    # two's complement: inc and flip all "bits" in $by
137    $by = $y->copy()->binc()->as_hex();		# -1 => 0, -2 => 1, -3 => 2 etc
138    $by =~ s/-?0x//;
139    $by =~ tr/0123456789abcdef/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
140    }
141  else
142    {
143    $by = $y->as_hex();				# get binary representation
144    $by =~ s/-?0x//;
145    $by =~ tr/fedcba9876543210/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
146    }
147  # now we have bit-strings from X and Y, reverse them for padding
148  $bx = reverse $bx;
149  $by = reverse $by;
150
151  # padd the shorter string
152  my $xx = "\x00"; $xx = "\x0f" if $sx == -1;
153  my $yy = "\x00"; $yy = "\x0f" if $sy == -1;
154  my $diff = CORE::length($bx) - CORE::length($by);
155  if ($diff > 0)
156    {
157    $by .= $yy x $diff;
158    }
159  elsif ($diff < 0)
160    {
161    $bx .= $xx x abs($diff);
162    }
163
164  # or the strings together
165  my $r = $bx | $by;
166
167  # and reverse the result again
168  $bx = reverse $r;
169
170  # one of $x or $y was negative, so need to flip bits in the result
171  # in both cases (one or two of them negative, or both positive) we need
172  # to get the characters back.
173  if ($sign == 1)
174    {
175    $bx =~ tr/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/0123456789abcdef/;
176    }
177  else
178    {
179    $bx =~ tr/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/fedcba9876543210/;
180    }
181
182  # leading zeros will be stripped by _from_hex()
183  $bx = '0x' . $bx;
184  $x->{value} = $CALC_EMU->_from_hex( $bx );
185
186  # calculate sign of result
187  $x->{sign} = '+';
188  $x->{sign} = '-' if $sign == 1 && !$x->is_zero();
189
190  # if one of X or Y was negative, we need to decrement result
191  $x->bdec() if $sign == 1;
192
193  $x->round(@r);
194  }
195
196sub __emu_bxor
197  {
198  my ($self,$x,$y,$sx,$sy,@r) = @_;
199
200  return $x->round(@r) if $y->is_zero();
201
202  my $sign = 0;					# sign of result
203  $sign = 1 if $x->{sign} ne $y->{sign};
204
205  my ($bx,$by);
206
207  if ($sx == -1)				# if x is negative
208    {
209    # two's complement: inc and flip all "bits" in $bx
210    $bx = $x->binc()->as_hex();			# -1 => 0, -2 => 1, -3 => 2 etc
211    $bx =~ s/-?0x//;
212    $bx =~ tr/0123456789abcdef/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
213    }
214  else
215    {
216    $bx = $x->as_hex();				# get binary representation
217    $bx =~ s/-?0x//;
218    $bx =~ tr/fedcba9876543210/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
219    }
220  if ($sy == -1)				# if y is negative
221    {
222    # two's complement: inc and flip all "bits" in $by
223    $by = $y->copy()->binc()->as_hex();		# -1 => 0, -2 => 1, -3 => 2 etc
224    $by =~ s/-?0x//;
225    $by =~ tr/0123456789abcdef/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
226    }
227  else
228    {
229    $by = $y->as_hex();				# get binary representation
230    $by =~ s/-?0x//;
231    $by =~ tr/fedcba9876543210/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/;
232    }
233  # now we have bit-strings from X and Y, reverse them for padding
234  $bx = reverse $bx;
235  $by = reverse $by;
236
237  # padd the shorter string
238  my $xx = "\x00"; $xx = "\x0f" if $sx == -1;
239  my $yy = "\x00"; $yy = "\x0f" if $sy == -1;
240  my $diff = CORE::length($bx) - CORE::length($by);
241  if ($diff > 0)
242    {
243    $by .= $yy x $diff;
244    }
245  elsif ($diff < 0)
246    {
247    $bx .= $xx x abs($diff);
248    }
249
250  # xor the strings together
251  my $r = $bx ^ $by;
252
253  # and reverse the result again
254  $bx = reverse $r;
255
256  # one of $x or $y was negative, so need to flip bits in the result
257  # in both cases (one or two of them negative, or both positive) we need
258  # to get the characters back.
259  if ($sign == 1)
260    {
261    $bx =~ tr/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/0123456789abcdef/;
262    }
263  else
264    {
265    $bx =~ tr/\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00/fedcba9876543210/;
266    }
267
268  # leading zeros will be stripped by _from_hex()
269  $bx = '0x' . $bx;
270  $x->{value} = $CALC_EMU->_from_hex( $bx );
271
272  # calculate sign of result
273  $x->{sign} = '+';
274  $x->{sign} = '-' if $sx != $sy && !$x->is_zero();
275
276  $x->bdec() if $sign == 1;
277
278  $x->round(@r);
279  }
280
281##############################################################################
282##############################################################################
283
2841;
285__END__
286
287=head1 NAME
288
289Math::BigInt::CalcEmu - Emulate low-level math with BigInt code
290
291=head1 SYNOPSIS
292
293	use Math::BigInt::CalcEmu;
294
295=head1 DESCRIPTION
296
297Contains routines that emulate low-level math functions in BigInt, e.g.
298optional routines the low-level math package does not provide on it's own.
299
300Will be loaded on demand and called automatically by BigInt.
301
302Stuff here is really low-priority to optimize, since it is far better to
303implement the operation in the low-level math libary directly, possible even
304using a call to the native lib.
305
306=head1 METHODS
307
308=head2 __emu_bxor
309
310=head2 __emu_band
311
312=head2 __emu_bior
313
314=head1 LICENSE
315
316This program is free software; you may redistribute it and/or modify it under
317the same terms as Perl itself.
318
319=head1 AUTHORS
320
321(c) Tels http://bloodgate.com 2003, 2004 - based on BigInt code by
322Tels from 2001-2003.
323
324=head1 SEE ALSO
325
326L<Math::BigInt>, L<Math::BigFloat>, L<Math::BigInt::BitVect>,
327L<Math::BigInt::GMP> and L<Math::BigInt::Pari>.
328
329=cut
330