xref: /openbsd-src/gnu/usr.bin/perl/cpan/Math-BigInt/lib/Math/BigInt/Calc.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
1b8851fccSafresh1package Math::BigInt::Calc;
2b8851fccSafresh1
3b8851fccSafresh1use 5.006001;
4b8851fccSafresh1use strict;
5b8851fccSafresh1use warnings;
6b8851fccSafresh1
7b46d8ef2Safresh1use Carp qw< carp croak >;
89f11ffb7Safresh1use Math::BigInt::Lib;
99f11ffb7Safresh1
10*3d61058aSafresh1our $VERSION = '2.003002';
11eac174f2Safresh1$VERSION =~ tr/_//d;
129f11ffb7Safresh1
139f11ffb7Safresh1our @ISA = ('Math::BigInt::Lib');
14b8851fccSafresh1
15b8851fccSafresh1# Package to store unsigned big integers in decimal and do math with them
16eac174f2Safresh1#
17b8851fccSafresh1# Internally the numbers are stored in an array with at least 1 element, no
18b8851fccSafresh1# leading zero parts (except the first) and in base 1eX where X is determined
19b8851fccSafresh1# automatically at loading time to be the maximum possible value
20eac174f2Safresh1#
21b8851fccSafresh1# todo:
22b8851fccSafresh1# - fully remove funky $# stuff in div() (maybe - that code scares me...)
23b8851fccSafresh1
24b8851fccSafresh1##############################################################################
25b8851fccSafresh1# global constants, flags and accessory
26b8851fccSafresh1
27b8851fccSafresh1# constants for easier life
28eac174f2Safresh1
29eac174f2Safresh1my $MAX_EXP_F;      # the maximum possible base 10 exponent with "no integer"
30eac174f2Safresh1my $MAX_EXP_I;      # the maximum possible base 10 exponent with "use integer"
31eac174f2Safresh1
32eac174f2Safresh1my $MAX_BITS;       # the maximum possible number of bits for $AND_BITS etc.
33eac174f2Safresh1
34eac174f2Safresh1my $BASE_LEN;       # the current base exponent in use
35eac174f2Safresh1my $USE_INT;        # whether "use integer" is used in the computations
36eac174f2Safresh1
37eac174f2Safresh1my $BASE;           # the current base, e.g., 10000 if $BASE_LEN is 5
38eac174f2Safresh1my $MAX_VAL;        # maximum value for an element, i.e., $BASE - 1
39eac174f2Safresh1
40eac174f2Safresh1my $AND_BITS;       # maximum value used in binary and, e.g., 0xffff
41eac174f2Safresh1my $OR_BITS;        # ditto for binary or
42eac174f2Safresh1my $XOR_BITS;       # ditto for binary xor
43eac174f2Safresh1
44eac174f2Safresh1my $AND_MASK;       # $AND_BITS + 1, e.g., 0x10000 if $AND_BITS is 0xffff
45eac174f2Safresh1my $OR_MASK;        # ditto for binary or
46eac174f2Safresh1my $XOR_MASK;       # ditto for binary xor
47eac174f2Safresh1
48eac174f2Safresh1sub config {
49eac174f2Safresh1    my $self = shift;
50eac174f2Safresh1
51eac174f2Safresh1    croak "Missing input argument" unless @_;
52eac174f2Safresh1
53eac174f2Safresh1    # Called as a getter.
54eac174f2Safresh1
55eac174f2Safresh1    if (@_ == 1) {
56eac174f2Safresh1        my $param = shift;
57eac174f2Safresh1        croak "Parameter name must be a non-empty string"
58eac174f2Safresh1          unless defined $param && length $param;
59eac174f2Safresh1        return $BASE_LEN if $param eq 'base_len';
60eac174f2Safresh1        return $USE_INT  if $param eq 'use_int';
61eac174f2Safresh1        croak "Unknown parameter '$param'";
62eac174f2Safresh1    }
63eac174f2Safresh1
64eac174f2Safresh1    # Called as a setter.
65eac174f2Safresh1
66eac174f2Safresh1    my $opts;
67eac174f2Safresh1    while (@_) {
68eac174f2Safresh1        my $param = shift;
69eac174f2Safresh1        croak "Parameter name must be a non-empty string"
70eac174f2Safresh1          unless defined $param && length $param;
71eac174f2Safresh1        croak "Missing value for parameter '$param'"
72eac174f2Safresh1          unless @_;
73eac174f2Safresh1        my $value = shift;
74eac174f2Safresh1
75eac174f2Safresh1        if ($param eq 'base_len' || $param eq 'use_int') {
76eac174f2Safresh1            $opts -> {$param} = $value;
77eac174f2Safresh1            next;
78eac174f2Safresh1        }
79eac174f2Safresh1
80eac174f2Safresh1        croak "Unknown parameter '$param'";
81eac174f2Safresh1    }
82eac174f2Safresh1
83eac174f2Safresh1    $BASE_LEN = $opts -> {base_len} if exists $opts -> {base_len};
84eac174f2Safresh1    $USE_INT  = $opts -> {use_int}  if exists $opts -> {use_int};
85eac174f2Safresh1    __PACKAGE__ -> _base_len($BASE_LEN, $USE_INT);
86eac174f2Safresh1
87eac174f2Safresh1    return $self;
88eac174f2Safresh1}
89b8851fccSafresh1
909f11ffb7Safresh1sub _base_len {
91eac174f2Safresh1    #my $class = shift;                  # $class is not used
92eac174f2Safresh1    shift;
93b8851fccSafresh1
94eac174f2Safresh1    if (@_) {                           # if called as setter ...
95eac174f2Safresh1        my ($base_len, $use_int) = @_;
96eac174f2Safresh1
97eac174f2Safresh1        croak "The base length must be a positive integer"
98eac174f2Safresh1          unless defined($base_len) && $base_len == int($base_len)
99eac174f2Safresh1                 && $base_len > 0;
100eac174f2Safresh1
101eac174f2Safresh1        if ( $use_int && ($base_len > $MAX_EXP_I) ||
102eac174f2Safresh1            !$use_int && ($base_len > $MAX_EXP_F))
103eac174f2Safresh1        {
104eac174f2Safresh1            croak "The maximum base length (exponent) is $MAX_EXP_I with",
105eac174f2Safresh1              " 'use integer' and $MAX_EXP_F without 'use integer'. The",
106eac174f2Safresh1              " requested settings, a base length of $base_len ",
107eac174f2Safresh1              $use_int ? "with" : "without", " 'use integer', is invalid.";
108eac174f2Safresh1        }
109eac174f2Safresh1
110eac174f2Safresh1        $BASE_LEN = $base_len;
111eac174f2Safresh1        $BASE = 0 + ("1" . ("0" x $BASE_LEN));
112eac174f2Safresh1        $MAX_VAL = $BASE - 1;
113eac174f2Safresh1        $USE_INT = $use_int ? 1 : 0;
114eac174f2Safresh1
115eac174f2Safresh1        {
11656d68f1eSafresh1            no warnings "redefine";
117eac174f2Safresh1            if ($use_int) {
118eac174f2Safresh1                *_mul = \&_mul_use_int;
119eac174f2Safresh1                *_div = \&_div_use_int;
120eac174f2Safresh1            } else {
121eac174f2Safresh1                *_mul = \&_mul_no_int;
122eac174f2Safresh1                *_div = \&_div_no_int;
123eac174f2Safresh1            }
124eac174f2Safresh1        }
125eac174f2Safresh1    }
126b8851fccSafresh1
127eac174f2Safresh1    # Find max bits. This is the largest power of two that is both no larger
128eac174f2Safresh1    # than $BASE and no larger than the maximum integer (i.e., ~0). We need
129eac174f2Safresh1    # this limitation because _and(), _or(), and _xor() only work on one
130eac174f2Safresh1    # element at a time.
131eac174f2Safresh1
132eac174f2Safresh1    my $umax = ~0;                      # largest unsigned integer
133eac174f2Safresh1    my $tmp  = $umax < $BASE ? $umax : $BASE;
134eac174f2Safresh1
135eac174f2Safresh1    $MAX_BITS = 0;
136eac174f2Safresh1    while ($tmp >>= 1) {
137eac174f2Safresh1        $MAX_BITS++;
138eac174f2Safresh1    }
139eac174f2Safresh1
140eac174f2Safresh1    # Limit to 32 bits for portability. Is this really necessary? XXX
141eac174f2Safresh1
142eac174f2Safresh1    $MAX_BITS = 32 if $MAX_BITS > 32;
143eac174f2Safresh1
144eac174f2Safresh1    # Find out how many bits _and, _or and _xor can take (old default = 16).
145eac174f2Safresh1    # Are these tests really necessary? Can't we just use $MAX_BITS? XXX
146eac174f2Safresh1
147eac174f2Safresh1    for ($AND_BITS = $MAX_BITS ; $AND_BITS > 0 ; $AND_BITS--) {
148eac174f2Safresh1        my $x = CORE::oct('0b' . '1' x $AND_BITS);
149eac174f2Safresh1        my $y = $x & $x;
150eac174f2Safresh1        my $z = 2 * (2 ** ($AND_BITS - 1)) + 1;
151eac174f2Safresh1        last unless $AND_BITS < $MAX_BITS && $x == $z && $y == $x;
152eac174f2Safresh1    }
153eac174f2Safresh1
154eac174f2Safresh1    for ($XOR_BITS = $MAX_BITS ; $XOR_BITS > 0 ; $XOR_BITS--) {
155eac174f2Safresh1        my $x = CORE::oct('0b' . '1' x $XOR_BITS);
156eac174f2Safresh1        my $y = $x ^ $x;
157eac174f2Safresh1        my $z = 2 * (2 ** ($XOR_BITS - 1)) + 1;
158eac174f2Safresh1        last unless $XOR_BITS < $MAX_BITS && $x == $z && $y == $x;
159eac174f2Safresh1    }
160eac174f2Safresh1
161eac174f2Safresh1    for ($OR_BITS = $MAX_BITS ; $OR_BITS > 0 ; $OR_BITS--) {
162eac174f2Safresh1        my $x = CORE::oct('0b' . '1' x $OR_BITS);
163eac174f2Safresh1        my $y = $x | $x;
164eac174f2Safresh1        my $z = 2 * (2 ** ($OR_BITS - 1)) + 1;
165eac174f2Safresh1        last unless $OR_BITS < $MAX_BITS && $x == $z && $y == $x;
166eac174f2Safresh1    }
167eac174f2Safresh1
168eac174f2Safresh1    $AND_MASK = __PACKAGE__->_new(( 2 ** $AND_BITS ));
169eac174f2Safresh1    $XOR_MASK = __PACKAGE__->_new(( 2 ** $XOR_BITS ));
170eac174f2Safresh1    $OR_MASK  = __PACKAGE__->_new(( 2 ** $OR_BITS  ));
171eac174f2Safresh1
172b8851fccSafresh1    return $BASE_LEN unless wantarray;
173eac174f2Safresh1    return ($BASE_LEN, $BASE, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN, $MAX_VAL,
174eac174f2Safresh1            $MAX_BITS, $MAX_EXP_F, $MAX_EXP_I, $USE_INT);
175b8851fccSafresh1}
176b8851fccSafresh1
177b8851fccSafresh1sub _new {
178b8851fccSafresh1    # Given a string representing an integer, returns a reference to an array
179b8851fccSafresh1    # of integers, where each integer represents a chunk of the original input
1809f11ffb7Safresh1    # integer.
181b8851fccSafresh1
1829f11ffb7Safresh1    my ($class, $str) = @_;
1839f11ffb7Safresh1    #unless ($str =~ /^([1-9]\d*|0)\z/) {
184b46d8ef2Safresh1    #    croak("Invalid input string '$str'");
1859f11ffb7Safresh1    #}
186b8851fccSafresh1
187b8851fccSafresh1    my $input_len = length($str) - 1;
188b8851fccSafresh1
189b8851fccSafresh1    # Shortcut for small numbers.
1909f11ffb7Safresh1    return bless [ $str ], $class if $input_len < $BASE_LEN;
191b8851fccSafresh1
192b8851fccSafresh1    my $format = "a" . (($input_len % $BASE_LEN) + 1);
193b8851fccSafresh1    $format .= $] < 5.008 ? "a$BASE_LEN" x int($input_len / $BASE_LEN)
194b8851fccSafresh1                          : "(a$BASE_LEN)*";
195b8851fccSafresh1
1969f11ffb7Safresh1    my $self = [ reverse(map { 0 + $_ } unpack($format, $str)) ];
1979f11ffb7Safresh1    return bless $self, $class;
198b8851fccSafresh1}
199b8851fccSafresh1
2009f11ffb7Safresh1BEGIN {
201b8851fccSafresh1
202eac174f2Safresh1    # Compute $MAX_EXP_F, the maximum usable base 10 exponent.
203eac174f2Safresh1
204eac174f2Safresh1    # The largest element in base 10**$BASE_LEN is 10**$BASE_LEN-1. For instance,
205eac174f2Safresh1    # with $BASE_LEN = 5, the largest element is 99_999, and the largest carry is
206eac174f2Safresh1    #
207eac174f2Safresh1    #     int( 99_999 * 99_999 / 100_000 ) = 99_998
208eac174f2Safresh1    #
209eac174f2Safresh1    # so make sure that 99_999 * 99_999 + 99_998 is within the range of integers
210eac174f2Safresh1    # that can be represented accuratly.
211eac174f2Safresh1    #
212eac174f2Safresh1    # Note that on some systems with quadmath support, the following is within
213eac174f2Safresh1    # the range of numbers that can be represented exactly, but it still gives
214eac174f2Safresh1    # the incorrect value $r = 2 (even though POSIX::fmod($x, $y) gives the
215eac174f2Safresh1    # correct value of 1:
216eac174f2Safresh1    #
217eac174f2Safresh1    #     $x =  99999999999999999;
218eac174f2Safresh1    #     $y = 100000000000000000;
219eac174f2Safresh1    #     $r = $x * $x % $y;            # should be 1
220eac174f2Safresh1    #
221eac174f2Safresh1    # so also check for this.
222eac174f2Safresh1
223eac174f2Safresh1    for ($MAX_EXP_F = 1 ; ; $MAX_EXP_F++) {         # when $MAX_EXP_F = 5
224eac174f2Safresh1        my $MAX_EXP_FM1 = $MAX_EXP_F - 1;           #   = 4
225eac174f2Safresh1        my $bs = "1" . ("0" x $MAX_EXP_F);          #   = "100000"
226eac174f2Safresh1        my $xs = "9" x $MAX_EXP_F;                  #   =  "99999"
227eac174f2Safresh1        my $cs = ("9" x $MAX_EXP_FM1) . "8";        #   =  "99998"
228eac174f2Safresh1        my $ys = $cs . ("0" x $MAX_EXP_FM1) . "1";  #   = "9999800001"
229eac174f2Safresh1
230eac174f2Safresh1        # Compute and check the product.
231eac174f2Safresh1        my $yn = $xs * $xs;                         #   = 9999800001
232eac174f2Safresh1        last if $yn != $ys;
233eac174f2Safresh1
234eac174f2Safresh1        # Compute and check the remainder.
235eac174f2Safresh1        my $rn = $yn % $bs;                         #   = 1
236eac174f2Safresh1        last if $rn != 1;
237eac174f2Safresh1
238eac174f2Safresh1        # Compute and check the carry. The division here is exact.
239eac174f2Safresh1        my $cn = ($yn - $rn) / $bs;                 #   = 99998
240eac174f2Safresh1        last if $cn != $cs;
241eac174f2Safresh1
242eac174f2Safresh1        # Compute and check product plus carry.
243eac174f2Safresh1        my $zs = $cs . ("9" x $MAX_EXP_F);          #   = "9999899999"
244eac174f2Safresh1        my $zn = $yn + $cn;                         #   = 99998999999
245eac174f2Safresh1        last if $zn != $zs;
246eac174f2Safresh1        last if $zn - ($zn - 1) != 1;
247b8851fccSafresh1    }
248eac174f2Safresh1    $MAX_EXP_F--;                       # last test failed, so retract one step
249eac174f2Safresh1
250eac174f2Safresh1    # Compute $MAX_EXP_I, the maximum usable base 10 exponent within the range
251eac174f2Safresh1    # of what is available with "use integer". On older versions of Perl,
252eac174f2Safresh1    # integers are converted to floating point numbers, even though they are
253eac174f2Safresh1    # within the range of what can be represented as integers. For example, on
254eac174f2Safresh1    # some 64 bit Perls, 999999999 * 999999999 becomes 999999998000000000, not
255eac174f2Safresh1    # 999999998000000001, even though the latter is less than the maximum value
256eac174f2Safresh1    # for a 64 bit integer, 18446744073709551615.
257eac174f2Safresh1
258eac174f2Safresh1    my $umax = ~0;                      # largest unsigned integer
259eac174f2Safresh1    for ($MAX_EXP_I = int(0.5 * log($umax) / log(10));
260eac174f2Safresh1         $MAX_EXP_I > 0;
261eac174f2Safresh1         $MAX_EXP_I--)
262eac174f2Safresh1    {                                               # when $MAX_EXP_I = 5
263eac174f2Safresh1        my $MAX_EXP_IM1 = $MAX_EXP_I - 1;           #   = 4
264eac174f2Safresh1        my $bs = "1" . ("0" x $MAX_EXP_I);          #   = "100000"
265eac174f2Safresh1        my $xs = "9" x $MAX_EXP_I;                  #   =  "99999"
266eac174f2Safresh1        my $cs = ("9" x $MAX_EXP_IM1) . "8";        #   =  "99998"
267eac174f2Safresh1        my $ys = $cs . ("0" x $MAX_EXP_IM1) . "1";  #   = "9999800001"
268eac174f2Safresh1
269eac174f2Safresh1        # Compute and check the product.
270eac174f2Safresh1        my $yn = $xs * $xs;                         #   = 9999800001
271eac174f2Safresh1        next if $yn != $ys;
272eac174f2Safresh1
273eac174f2Safresh1        # Compute and check the remainder.
274eac174f2Safresh1        my $rn = $yn % $bs;                         #   = 1
275eac174f2Safresh1        next if $rn != 1;
276eac174f2Safresh1
277eac174f2Safresh1        # Compute and check the carry. The division here is exact.
278eac174f2Safresh1        my $cn = ($yn - $rn) / $bs;                 #   = 99998
279eac174f2Safresh1        next if $cn != $cs;
280eac174f2Safresh1
281eac174f2Safresh1        # Compute and check product plus carry.
282eac174f2Safresh1        my $zs = $cs . ("9" x $MAX_EXP_I);          #   = "9999899999"
283eac174f2Safresh1        my $zn = $yn + $cn;                         #   = 99998999999
284eac174f2Safresh1        next if $zn != $zs;
285eac174f2Safresh1        next if $zn - ($zn - 1) != 1;
286eac174f2Safresh1        last;
287b8851fccSafresh1    }
288b8851fccSafresh1
289eac174f2Safresh1    ($BASE_LEN, $USE_INT) = $MAX_EXP_F > $MAX_EXP_I
290eac174f2Safresh1                          ? ($MAX_EXP_F, 0) : ($MAX_EXP_I, 1);
291b8851fccSafresh1
292eac174f2Safresh1    __PACKAGE__ -> _base_len($BASE_LEN, $USE_INT);
293b8851fccSafresh1}
294b8851fccSafresh1
295b8851fccSafresh1###############################################################################
296b8851fccSafresh1
2979f11ffb7Safresh1sub _zero {
298b8851fccSafresh1    # create a zero
2999f11ffb7Safresh1    my $class = shift;
3009f11ffb7Safresh1    return bless [ 0 ], $class;
301b8851fccSafresh1}
302b8851fccSafresh1
3039f11ffb7Safresh1sub _one {
304b8851fccSafresh1    # create a one
3059f11ffb7Safresh1    my $class = shift;
3069f11ffb7Safresh1    return bless [ 1 ], $class;
307b8851fccSafresh1}
308b8851fccSafresh1
3099f11ffb7Safresh1sub _two {
3109f11ffb7Safresh1    # create a two
3119f11ffb7Safresh1    my $class = shift;
3129f11ffb7Safresh1    return bless [ 2 ], $class;
313b8851fccSafresh1}
314b8851fccSafresh1
3159f11ffb7Safresh1sub _ten {
3169f11ffb7Safresh1    # create a 10
3179f11ffb7Safresh1    my $class = shift;
318eac174f2Safresh1    my $self = $BASE_LEN == 1 ? [ 0, 1 ] : [ 10 ];
319eac174f2Safresh1    bless $self, $class;
320b8851fccSafresh1}
321b8851fccSafresh1
3229f11ffb7Safresh1sub _1ex {
323b8851fccSafresh1    # create a 1Ex
3249f11ffb7Safresh1    my $class = shift;
3259f11ffb7Safresh1
3269f11ffb7Safresh1    my $rem = $_[0] % $BASE_LEN;                # remainder
327eac174f2Safresh1    my $div = ($_[0] - $rem) / $BASE_LEN;       # parts
328b8851fccSafresh1
329eac174f2Safresh1    # With a $BASE_LEN of 6, 1e14 becomes
330eac174f2Safresh1    # [ 000000, 000000, 100 ] -> [ 0, 0, 100 ]
331eac174f2Safresh1    bless [ (0) x $div,  0 + ("1" . ("0" x $rem)) ], $class;
332b8851fccSafresh1}
333b8851fccSafresh1
3349f11ffb7Safresh1sub _copy {
335b8851fccSafresh1    # make a true copy
3369f11ffb7Safresh1    my $class = shift;
3379f11ffb7Safresh1    return bless [ @{ $_[0] } ], $class;
338b8851fccSafresh1}
339b8851fccSafresh1
340eac174f2Safresh1sub import {
341eac174f2Safresh1    my $self = shift;
342eac174f2Safresh1
343eac174f2Safresh1    my $opts;
344eac174f2Safresh1    my ($base_len, $use_int);
345eac174f2Safresh1    while (@_) {
346eac174f2Safresh1        my $param = shift;
347eac174f2Safresh1        croak "Parameter name must be a non-empty string"
348eac174f2Safresh1          unless defined $param && length $param;
349eac174f2Safresh1        croak "Missing value for parameter '$param'"
350eac174f2Safresh1          unless @_;
351eac174f2Safresh1        my $value = shift;
352eac174f2Safresh1
353eac174f2Safresh1        if ($param eq 'base_len' || $param eq 'use_int') {
354eac174f2Safresh1            $opts -> {$param} = $value;
355eac174f2Safresh1            next;
356eac174f2Safresh1        }
357eac174f2Safresh1
358eac174f2Safresh1        croak "Unknown parameter '$param'";
359eac174f2Safresh1    }
360eac174f2Safresh1
361eac174f2Safresh1    $base_len = exists $opts -> {base_len} ? $opts -> {base_len} : $BASE_LEN;
362eac174f2Safresh1    $use_int  = exists $opts -> {use_int}  ? $opts -> {use_int}  : $USE_INT;
363eac174f2Safresh1    __PACKAGE__ -> _base_len($base_len, $use_int);
364eac174f2Safresh1
365eac174f2Safresh1    return $self;
366eac174f2Safresh1}
367b8851fccSafresh1
368b8851fccSafresh1##############################################################################
369b8851fccSafresh1# convert back to string and number
370b8851fccSafresh1
371b8851fccSafresh1sub _str {
372b8851fccSafresh1    # Convert number from internal base 1eN format to string format. Internal
373b8851fccSafresh1    # format is always normalized, i.e., no leading zeros.
374b8851fccSafresh1
375b8851fccSafresh1    my $ary = $_[1];
376b8851fccSafresh1    my $idx = $#$ary;           # index of last element
377b8851fccSafresh1
378b8851fccSafresh1    if ($idx < 0) {             # should not happen
379b46d8ef2Safresh1        croak("$_[1] has no elements");
380b8851fccSafresh1    }
381b8851fccSafresh1
382b8851fccSafresh1    # Handle first one differently, since it should not have any leading zeros.
383b8851fccSafresh1    my $ret = int($ary->[$idx]);
384b8851fccSafresh1    if ($idx > 0) {
3859f11ffb7Safresh1        # Interestingly, the pre-padd method uses more time.
3869f11ffb7Safresh1        # The old grep variant takes longer (14 vs. 10 sec).
387b8851fccSafresh1        my $z = '0' x ($BASE_LEN - 1);
3889f11ffb7Safresh1        while (--$idx >= 0) {
389b8851fccSafresh1            $ret .= substr($z . $ary->[$idx], -$BASE_LEN);
390b8851fccSafresh1        }
391b8851fccSafresh1    }
392b8851fccSafresh1    $ret;
393b8851fccSafresh1}
394b8851fccSafresh1
3959f11ffb7Safresh1sub _num {
396b8851fccSafresh1    # Make a Perl scalar number (int/float) from a BigInt object.
397b8851fccSafresh1    my $x = $_[1];
398b8851fccSafresh1
3999f11ffb7Safresh1    return $x->[0] if @$x == 1;         # below $BASE
400b8851fccSafresh1
401b8851fccSafresh1    # Start with the most significant element and work towards the least
402b8851fccSafresh1    # significant element. Avoid multiplying "inf" (which happens if the number
403b8851fccSafresh1    # overflows) with "0" (if there are zero elements in $x) since this gives
404b8851fccSafresh1    # "nan" which propagates to the output.
405b8851fccSafresh1
406b8851fccSafresh1    my $num = 0;
407b8851fccSafresh1    for (my $i = $#$x ; $i >= 0 ; --$i) {
408b8851fccSafresh1        $num *= $BASE;
409b8851fccSafresh1        $num += $x -> [$i];
410b8851fccSafresh1    }
411b8851fccSafresh1    return $num;
412b8851fccSafresh1}
413b8851fccSafresh1
414b8851fccSafresh1##############################################################################
415b8851fccSafresh1# actual math code
416b8851fccSafresh1
417b8851fccSafresh1sub _add {
418b8851fccSafresh1    # (ref to int_num_array, ref to int_num_array)
419b8851fccSafresh1    #
420b8851fccSafresh1    # Routine to add two base 1eX numbers stolen from Knuth Vol 2 Algorithm A
421b8851fccSafresh1    # pg 231. There are separate routines to add and sub as per Knuth pg 233.
422b8851fccSafresh1    # This routine modifies array x, but not y.
423b8851fccSafresh1
424b8851fccSafresh1    my ($c, $x, $y) = @_;
425b8851fccSafresh1
4269f11ffb7Safresh1    # $x + 0 => $x
4279f11ffb7Safresh1
4289f11ffb7Safresh1    return $x if @$y == 1 && $y->[0] == 0;
4299f11ffb7Safresh1
4309f11ffb7Safresh1    # 0 + $y => $y->copy
4319f11ffb7Safresh1
4329f11ffb7Safresh1    if (@$x == 1 && $x->[0] == 0) {
433b8851fccSafresh1        @$x = @$y;
434b8851fccSafresh1        return $x;
435b8851fccSafresh1    }
436b8851fccSafresh1
437b8851fccSafresh1    # For each in Y, add Y to X and carry. If after that, something is left in
438b8851fccSafresh1    # X, foreach in X add carry to X and then return X, carry. Trades one
439b8851fccSafresh1    # "$j++" for having to shift arrays.
440eac174f2Safresh1
441b8851fccSafresh1    my $car = 0;
442b8851fccSafresh1    my $j = 0;
443eac174f2Safresh1    for my $i (@$y) {
444b8851fccSafresh1        $x->[$j] -= $BASE if $car = (($x->[$j] += $i + $car) >= $BASE) ? 1 : 0;
445b8851fccSafresh1        $j++;
446b8851fccSafresh1    }
447b8851fccSafresh1    while ($car != 0) {
448b8851fccSafresh1        $x->[$j] -= $BASE if $car = (($x->[$j] += $car) >= $BASE) ? 1 : 0;
449b8851fccSafresh1        $j++;
450b8851fccSafresh1    }
451b8851fccSafresh1    $x;
452b8851fccSafresh1}
453b8851fccSafresh1
454b8851fccSafresh1sub _inc {
455b8851fccSafresh1    # (ref to int_num_array, ref to int_num_array)
456b8851fccSafresh1    # Add 1 to $x, modify $x in place
457b8851fccSafresh1    my ($c, $x) = @_;
458b8851fccSafresh1
459b8851fccSafresh1    for my $i (@$x) {
460b8851fccSafresh1        return $x if ($i += 1) < $BASE; # early out
461b8851fccSafresh1        $i = 0;                         # overflow, next
462b8851fccSafresh1    }
463b8851fccSafresh1    push @$x, 1 if $x->[-1] == 0;       # last overflowed, so extend
464b8851fccSafresh1    $x;
465b8851fccSafresh1}
466b8851fccSafresh1
467b8851fccSafresh1sub _dec {
468b8851fccSafresh1    # (ref to int_num_array, ref to int_num_array)
469b8851fccSafresh1    # Sub 1 from $x, modify $x in place
470b8851fccSafresh1    my ($c, $x) = @_;
471b8851fccSafresh1
472b8851fccSafresh1    my $MAX = $BASE - 1;                # since MAX_VAL based on BASE
473b8851fccSafresh1    for my $i (@$x) {
474b8851fccSafresh1        last if ($i -= 1) >= 0;         # early out
475b8851fccSafresh1        $i = $MAX;                      # underflow, next
476b8851fccSafresh1    }
477b8851fccSafresh1    pop @$x if $x->[-1] == 0 && @$x > 1; # last underflowed (but leave 0)
478b8851fccSafresh1    $x;
479b8851fccSafresh1}
480b8851fccSafresh1
481b8851fccSafresh1sub _sub {
482b8851fccSafresh1    # (ref to int_num_array, ref to int_num_array, swap)
483b8851fccSafresh1    #
484b8851fccSafresh1    # Subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
485b8851fccSafresh1    # subtract Y from X by modifying x in place
486b8851fccSafresh1    my ($c, $sx, $sy, $s) = @_;
487b8851fccSafresh1
488b8851fccSafresh1    my $car = 0;
489b8851fccSafresh1    my $j = 0;
490b8851fccSafresh1    if (!$s) {
491eac174f2Safresh1        for my $i (@$sx) {
492b8851fccSafresh1            last unless defined $sy->[$j] || $car;
493b8851fccSafresh1            $i += $BASE if $car = (($i -= ($sy->[$j] || 0) + $car) < 0);
494b8851fccSafresh1            $j++;
495b8851fccSafresh1        }
496b8851fccSafresh1        # might leave leading zeros, so fix that
497b8851fccSafresh1        return __strip_zeros($sx);
498b8851fccSafresh1    }
499eac174f2Safresh1    for my $i (@$sx) {
500b8851fccSafresh1        # We can't do an early out if $x < $y, since we need to copy the high
501b8851fccSafresh1        # chunks from $y. Found by Bob Mathews.
502b8851fccSafresh1        #last unless defined $sy->[$j] || $car;
503b8851fccSafresh1        $sy->[$j] += $BASE
504b8851fccSafresh1          if $car = ($sy->[$j] = $i - ($sy->[$j] || 0) - $car) < 0;
505b8851fccSafresh1        $j++;
506b8851fccSafresh1    }
507b8851fccSafresh1    # might leave leading zeros, so fix that
508b8851fccSafresh1    __strip_zeros($sy);
509b8851fccSafresh1}
510b8851fccSafresh1
511eac174f2Safresh1sub _mul_use_int {
512b8851fccSafresh1    # (ref to int_num_array, ref to int_num_array)
513b8851fccSafresh1    # multiply two numbers in internal representation
514b8851fccSafresh1    # modifies first arg, second need not be different from first
515b8851fccSafresh1    # works for 64 bit integer with "use integer"
516b8851fccSafresh1    my ($c, $xv, $yv) = @_;
517b8851fccSafresh1    use integer;
51856d68f1eSafresh1
5199f11ffb7Safresh1    if (@$yv == 1) {
52056d68f1eSafresh1        # shortcut for two very short numbers (improved by Nathan Zook) works
52156d68f1eSafresh1        # also if xv and yv are the same reference, and handles also $x == 0
5229f11ffb7Safresh1        if (@$xv == 1) {
5239f11ffb7Safresh1            if (($xv->[0] *= $yv->[0]) >= $BASE) {
524b8851fccSafresh1                $xv->[0] =
525b8851fccSafresh1                  $xv->[0] - ($xv->[1] = $xv->[0] / $BASE) * $BASE;
5269f11ffb7Safresh1            }
527b8851fccSafresh1            return $xv;
528b8851fccSafresh1        }
529b8851fccSafresh1        # $x * 0 => 0
5309f11ffb7Safresh1        if ($yv->[0] == 0) {
531b8851fccSafresh1            @$xv = (0);
532b8851fccSafresh1            return $xv;
533b8851fccSafresh1        }
53456d68f1eSafresh1
535b8851fccSafresh1        # multiply a large number a by a single element one, so speed up
5369f11ffb7Safresh1        my $y = $yv->[0];
5379f11ffb7Safresh1        my $car = 0;
5389f11ffb7Safresh1        foreach my $i (@$xv) {
539b8851fccSafresh1            #$i = $i * $y + $car; $car = $i / $BASE; $i -= $car * $BASE;
5409f11ffb7Safresh1            $i = $i * $y + $car;
5419f11ffb7Safresh1            $i -= ($car = $i / $BASE) * $BASE;
542b8851fccSafresh1        }
543b8851fccSafresh1        push @$xv, $car if $car != 0;
544b8851fccSafresh1        return $xv;
545b8851fccSafresh1    }
54656d68f1eSafresh1
547b8851fccSafresh1    # shortcut for result $x == 0 => result = 0
54856d68f1eSafresh1    return $xv if @$xv == 1 && $xv->[0] == 0;
549b8851fccSafresh1
550b8851fccSafresh1    # since multiplying $x with $x fails, make copy in this case
5519f11ffb7Safresh1    $yv = $c->_copy($xv) if $xv == $yv;         # same references?
552b8851fccSafresh1
5539f11ffb7Safresh1    my @prod = ();
554eac174f2Safresh1    my ($prod, $car, $cty);
555eac174f2Safresh1    for my $xi (@$xv) {
5569f11ffb7Safresh1        $car = 0;
5579f11ffb7Safresh1        $cty = 0;
558b8851fccSafresh1        # looping through this if $xi == 0 is silly - so optimize it away!
55956d68f1eSafresh1        $xi = (shift(@prod) || 0), next if $xi == 0;
560eac174f2Safresh1        for my $yi (@$yv) {
561b8851fccSafresh1            $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
562b8851fccSafresh1            $prod[$cty++] = $prod - ($car = $prod / $BASE) * $BASE;
563b8851fccSafresh1        }
564b8851fccSafresh1        $prod[$cty] += $car if $car;    # need really to check for 0?
56556d68f1eSafresh1        $xi = shift(@prod) || 0;        # || 0 makes v5.005_3 happy
566b8851fccSafresh1    }
567b8851fccSafresh1    push @$xv, @prod;
568b8851fccSafresh1    $xv;
569b8851fccSafresh1}
570b8851fccSafresh1
571eac174f2Safresh1sub _mul_no_int {
572b8851fccSafresh1    # (ref to int_num_array, ref to int_num_array)
573b8851fccSafresh1    # multiply two numbers in internal representation
574b8851fccSafresh1    # modifies first arg, second need not be different from first
575b8851fccSafresh1    my ($c, $xv, $yv) = @_;
576b8851fccSafresh1
5779f11ffb7Safresh1    if (@$yv == 1) {
57856d68f1eSafresh1        # shortcut for two very short numbers (improved by Nathan Zook) works
57956d68f1eSafresh1        # also if xv and yv are the same reference, and handles also $x == 0
5809f11ffb7Safresh1        if (@$xv == 1) {
5819f11ffb7Safresh1            if (($xv->[0] *= $yv->[0]) >= $BASE) {
58256d68f1eSafresh1                my $rem = $xv->[0] % $BASE;
58356d68f1eSafresh1                $xv->[1] = ($xv->[0] - $rem) / $BASE;
58456d68f1eSafresh1                $xv->[0] = $rem;
5859f11ffb7Safresh1            }
586b8851fccSafresh1            return $xv;
587b8851fccSafresh1        }
588b8851fccSafresh1        # $x * 0 => 0
5899f11ffb7Safresh1        if ($yv->[0] == 0) {
590b8851fccSafresh1            @$xv = (0);
591b8851fccSafresh1            return $xv;
592b8851fccSafresh1        }
59356d68f1eSafresh1
594b8851fccSafresh1        # multiply a large number a by a single element one, so speed up
5959f11ffb7Safresh1        my $y = $yv->[0];
5969f11ffb7Safresh1        my $car = 0;
59756d68f1eSafresh1        my $rem;
5989f11ffb7Safresh1        foreach my $i (@$xv) {
5999f11ffb7Safresh1            $i = $i * $y + $car;
60056d68f1eSafresh1            $rem = $i % $BASE;
60156d68f1eSafresh1            $car = ($i - $rem) / $BASE;
60256d68f1eSafresh1            $i = $rem;
603b8851fccSafresh1        }
604b8851fccSafresh1        push @$xv, $car if $car != 0;
605b8851fccSafresh1        return $xv;
606b8851fccSafresh1    }
60756d68f1eSafresh1
608b8851fccSafresh1    # shortcut for result $x == 0 => result = 0
60956d68f1eSafresh1    return $xv if @$xv == 1 && $xv->[0] == 0;
610b8851fccSafresh1
611b8851fccSafresh1    # since multiplying $x with $x fails, make copy in this case
6129f11ffb7Safresh1    $yv = $c->_copy($xv) if $xv == $yv;         # same references?
613b8851fccSafresh1
6149f11ffb7Safresh1    my @prod = ();
615eac174f2Safresh1    my ($prod, $rem, $car, $cty);
616eac174f2Safresh1    for my $xi (@$xv) {
6179f11ffb7Safresh1        $car = 0;
6189f11ffb7Safresh1        $cty = 0;
619b8851fccSafresh1        # looping through this if $xi == 0 is silly - so optimize it away!
62056d68f1eSafresh1        $xi = (shift(@prod) || 0), next if $xi == 0;
621eac174f2Safresh1        for my $yi (@$yv) {
622b8851fccSafresh1            $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
62356d68f1eSafresh1            $rem = $prod % $BASE;
62456d68f1eSafresh1            $car = ($prod - $rem) / $BASE;
62556d68f1eSafresh1            $prod[$cty++] = $rem;
626b8851fccSafresh1        }
627b8851fccSafresh1        $prod[$cty] += $car if $car;    # need really to check for 0?
62856d68f1eSafresh1        $xi = shift(@prod) || 0;        # || 0 makes v5.005_3 happy
629b8851fccSafresh1    }
630b8851fccSafresh1    push @$xv, @prod;
631b8851fccSafresh1    $xv;
632b8851fccSafresh1}
633b8851fccSafresh1
634eac174f2Safresh1sub _div_use_int {
635b8851fccSafresh1    # ref to array, ref to array, modify first array and return remainder if
636b8851fccSafresh1    # in list context
63756d68f1eSafresh1
63856d68f1eSafresh1    # This version works on integers
63956d68f1eSafresh1    use integer;
64056d68f1eSafresh1
641b8851fccSafresh1    my ($c, $x, $yorg) = @_;
642b8851fccSafresh1
643b8851fccSafresh1    # the general div algorithm here is about O(N*N) and thus quite slow, so
644b8851fccSafresh1    # we first check for some special cases and use shortcuts to handle them.
645b8851fccSafresh1
646b8851fccSafresh1    # if both numbers have only one element:
6479f11ffb7Safresh1    if (@$x == 1 && @$yorg == 1) {
648b8851fccSafresh1        # shortcut, $yorg and $x are two small numbers
6499f11ffb7Safresh1        if (wantarray) {
6509f11ffb7Safresh1            my $rem = [ $x->[0] % $yorg->[0] ];
6519f11ffb7Safresh1            bless $rem, $c;
65256d68f1eSafresh1            $x->[0] = $x->[0] / $yorg->[0];
6539f11ffb7Safresh1            return ($x, $rem);
6549f11ffb7Safresh1        } else {
65556d68f1eSafresh1            $x->[0] = $x->[0] / $yorg->[0];
656b8851fccSafresh1            return $x;
657b8851fccSafresh1        }
658b8851fccSafresh1    }
65956d68f1eSafresh1
660b8851fccSafresh1    # if x has more than one, but y has only one element:
6619f11ffb7Safresh1    if (@$yorg == 1) {
662b8851fccSafresh1        my $rem;
6639f11ffb7Safresh1        $rem = $c->_mod($c->_copy($x), $yorg) if wantarray;
664b8851fccSafresh1
665b8851fccSafresh1        # shortcut, $y is < $BASE
6669f11ffb7Safresh1        my $j = @$x;
6679f11ffb7Safresh1        my $r = 0;
6689f11ffb7Safresh1        my $y = $yorg->[0];
6699f11ffb7Safresh1        my $b;
6709f11ffb7Safresh1        while ($j-- > 0) {
671b8851fccSafresh1            $b = $r * $BASE + $x->[$j];
672b8851fccSafresh1            $r = $b % $y;
67356d68f1eSafresh1            $x->[$j] = $b / $y;
674b8851fccSafresh1        }
67556d68f1eSafresh1        pop(@$x) if @$x > 1 && $x->[-1] == 0;   # remove any trailing zero
676b8851fccSafresh1        return ($x, $rem) if wantarray;
677b8851fccSafresh1        return $x;
678b8851fccSafresh1    }
67956d68f1eSafresh1
680b8851fccSafresh1    # now x and y have more than one element
681b8851fccSafresh1
68256d68f1eSafresh1    # check whether y has more elements than x, if so, the result is 0
6839f11ffb7Safresh1    if (@$yorg > @$x) {
684b8851fccSafresh1        my $rem;
6859f11ffb7Safresh1        $rem = $c->_copy($x) if wantarray;      # make copy
6869f11ffb7Safresh1        @$x = 0;                                # set to 0
687b8851fccSafresh1        return ($x, $rem) if wantarray;         # including remainder?
688b8851fccSafresh1        return $x;                              # only x, which is [0] now
689b8851fccSafresh1    }
69056d68f1eSafresh1
691b8851fccSafresh1    # check whether the numbers have the same number of elements, in that case
692b8851fccSafresh1    # the result will fit into one element and can be computed efficiently
6939f11ffb7Safresh1    if (@$yorg == @$x) {
69456d68f1eSafresh1        my $cmp = 0;
69556d68f1eSafresh1        for (my $j = $#$x ; $j >= 0 ; --$j) {
69656d68f1eSafresh1            last if $cmp = $x->[$j] - $yorg->[$j];
69756d68f1eSafresh1        }
69856d68f1eSafresh1
69956d68f1eSafresh1        if ($cmp == 0) {        # x = y
70056d68f1eSafresh1            @$x = 1;
70156d68f1eSafresh1            return $x, $c->_zero() if wantarray;
702b8851fccSafresh1            return $x;
703b8851fccSafresh1        }
704b8851fccSafresh1
70556d68f1eSafresh1        if ($cmp < 0) {         # x < y
70656d68f1eSafresh1            if (wantarray) {
70756d68f1eSafresh1                my $rem = $c->_copy($x);
70856d68f1eSafresh1                @$x = 0;
70956d68f1eSafresh1                return $x, $rem;
710b8851fccSafresh1            }
71156d68f1eSafresh1            @$x = 0;
712b8851fccSafresh1            return $x;
713b8851fccSafresh1        }
714b8851fccSafresh1    }
715b8851fccSafresh1
716b8851fccSafresh1    # all other cases:
717b8851fccSafresh1
7189f11ffb7Safresh1    my $y = $c->_copy($yorg);           # always make copy to preserve
719b8851fccSafresh1
72056d68f1eSafresh1    my $tmp;
72156d68f1eSafresh1    my $dd = $BASE / ($y->[-1] + 1);
72256d68f1eSafresh1    if ($dd != 1) {
72356d68f1eSafresh1        my $car = 0;
72456d68f1eSafresh1        for my $xi (@$x) {
725b8851fccSafresh1            $xi = $xi * $dd + $car;
72656d68f1eSafresh1            $xi -= ($car = $xi / $BASE) * $BASE;
727b8851fccSafresh1        }
7289f11ffb7Safresh1        push(@$x, $car);
7299f11ffb7Safresh1        $car = 0;
73056d68f1eSafresh1        for my $yi (@$y) {
731b8851fccSafresh1            $yi = $yi * $dd + $car;
73256d68f1eSafresh1            $yi -= ($car = $yi / $BASE) * $BASE;
733b8851fccSafresh1        }
7349f11ffb7Safresh1    } else {
735b8851fccSafresh1        push(@$x, 0);
736b8851fccSafresh1    }
737b8851fccSafresh1
738b8851fccSafresh1    # @q will accumulate the final result, $q contains the current computed
739b8851fccSafresh1    # part of the final result
740b8851fccSafresh1
74156d68f1eSafresh1    my @q = ();
74256d68f1eSafresh1    my ($v2, $v1) = @$y[-2, -1];
743b8851fccSafresh1    $v2 = 0 unless $v2;
7449f11ffb7Safresh1    while ($#$x > $#$y) {
74556d68f1eSafresh1        my ($u2, $u1, $u0) = @$x[-3 .. -1];
746b8851fccSafresh1        $u2 = 0 unless $u2;
747b8851fccSafresh1        #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
748b8851fccSafresh1        # if $v1 == 0;
74956d68f1eSafresh1        my $tmp = $u0 * $BASE + $u1;
75056d68f1eSafresh1        my $rem = $tmp % $v1;
75156d68f1eSafresh1        my $q = $u0 == $v1 ? $MAX_VAL : (($tmp - $rem) / $v1);
75256d68f1eSafresh1        --$q while $v2 * $q > ($u0 * $BASE + $u1 - $q * $v1) * $BASE + $u2;
7539f11ffb7Safresh1        if ($q) {
75456d68f1eSafresh1            my $prd;
75556d68f1eSafresh1            my ($car, $bar) = (0, 0);
75656d68f1eSafresh1            for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
757b8851fccSafresh1                $prd = $q * $y->[$yi] + $car;
758b8851fccSafresh1                $prd -= ($car = int($prd / $BASE)) * $BASE;
75956d68f1eSafresh1                $x->[$xi] += $BASE if $bar = (($x->[$xi] -= $prd + $bar) < 0);
760b8851fccSafresh1            }
7619f11ffb7Safresh1            if ($x->[-1] < $car + $bar) {
7629f11ffb7Safresh1                $car = 0;
7639f11ffb7Safresh1                --$q;
76456d68f1eSafresh1                for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
765b8851fccSafresh1                    $x->[$xi] -= $BASE
76656d68f1eSafresh1                      if $car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE);
767b8851fccSafresh1                }
768b8851fccSafresh1            }
769b8851fccSafresh1        }
7709f11ffb7Safresh1        pop(@$x);
7719f11ffb7Safresh1        unshift(@q, $q);
772b8851fccSafresh1    }
77356d68f1eSafresh1
7749f11ffb7Safresh1    if (wantarray) {
7759f11ffb7Safresh1        my $d = bless [], $c;
7769f11ffb7Safresh1        if ($dd != 1) {
77756d68f1eSafresh1            my $car = 0;
77856d68f1eSafresh1            my $prd;
77956d68f1eSafresh1            for my $xi (reverse @$x) {
780b8851fccSafresh1                $prd = $car * $BASE + $xi;
78156d68f1eSafresh1                $car = $prd - ($tmp = $prd / $dd) * $dd;
78256d68f1eSafresh1                unshift @$d, $tmp;
783b8851fccSafresh1            }
7849f11ffb7Safresh1        } else {
7859f11ffb7Safresh1            @$d = @$x;
786b8851fccSafresh1        }
787b8851fccSafresh1        @$x = @q;
788b8851fccSafresh1        __strip_zeros($x);
789b8851fccSafresh1        __strip_zeros($d);
790b8851fccSafresh1        return ($x, $d);
791b8851fccSafresh1    }
792b8851fccSafresh1    @$x = @q;
793b8851fccSafresh1    __strip_zeros($x);
794b8851fccSafresh1    $x;
795b8851fccSafresh1}
796b8851fccSafresh1
797eac174f2Safresh1sub _div_no_int {
798b8851fccSafresh1    # ref to array, ref to array, modify first array and return remainder if
799b8851fccSafresh1    # in list context
80056d68f1eSafresh1
801b8851fccSafresh1    my ($c, $x, $yorg) = @_;
802b8851fccSafresh1
803b8851fccSafresh1    # the general div algorithm here is about O(N*N) and thus quite slow, so
804b8851fccSafresh1    # we first check for some special cases and use shortcuts to handle them.
805b8851fccSafresh1
806b8851fccSafresh1    # if both numbers have only one element:
8079f11ffb7Safresh1    if (@$x == 1 && @$yorg == 1) {
808b8851fccSafresh1        # shortcut, $yorg and $x are two small numbers
8099f11ffb7Safresh1        my $rem = [ $x->[0] % $yorg->[0] ];
8109f11ffb7Safresh1        bless $rem, $c;
81156d68f1eSafresh1        $x->[0] = ($x->[0] - $rem->[0]) / $yorg->[0];
81256d68f1eSafresh1        return ($x, $rem) if wantarray;
813b8851fccSafresh1        return $x;
814b8851fccSafresh1    }
81556d68f1eSafresh1
816b8851fccSafresh1    # if x has more than one, but y has only one element:
8179f11ffb7Safresh1    if (@$yorg == 1) {
818b8851fccSafresh1        my $rem;
8199f11ffb7Safresh1        $rem = $c->_mod($c->_copy($x), $yorg) if wantarray;
820b8851fccSafresh1
821b8851fccSafresh1        # shortcut, $y is < $BASE
8229f11ffb7Safresh1        my $j = @$x;
8239f11ffb7Safresh1        my $r = 0;
8249f11ffb7Safresh1        my $y = $yorg->[0];
8259f11ffb7Safresh1        my $b;
8269f11ffb7Safresh1        while ($j-- > 0) {
827b8851fccSafresh1            $b = $r * $BASE + $x->[$j];
828b8851fccSafresh1            $r = $b % $y;
82956d68f1eSafresh1            $x->[$j] = ($b - $r) / $y;
830b8851fccSafresh1        }
83156d68f1eSafresh1        pop(@$x) if @$x > 1 && $x->[-1] == 0;   # remove any trailing zero
832b8851fccSafresh1        return ($x, $rem) if wantarray;
833b8851fccSafresh1        return $x;
834b8851fccSafresh1    }
83556d68f1eSafresh1
836b8851fccSafresh1    # now x and y have more than one element
837b8851fccSafresh1
83856d68f1eSafresh1    # check whether y has more elements than x, if so, the result is 0
8399f11ffb7Safresh1    if (@$yorg > @$x) {
840b8851fccSafresh1        my $rem;
8419f11ffb7Safresh1        $rem = $c->_copy($x) if wantarray;      # make copy
8429f11ffb7Safresh1        @$x = 0;                                # set to 0
843b8851fccSafresh1        return ($x, $rem) if wantarray;         # including remainder?
844b8851fccSafresh1        return $x;                              # only x, which is [0] now
845b8851fccSafresh1    }
84656d68f1eSafresh1
847b8851fccSafresh1    # check whether the numbers have the same number of elements, in that case
848b8851fccSafresh1    # the result will fit into one element and can be computed efficiently
8499f11ffb7Safresh1    if (@$yorg == @$x) {
85056d68f1eSafresh1        my $cmp = 0;
85156d68f1eSafresh1        for (my $j = $#$x ; $j >= 0 ; --$j) {
85256d68f1eSafresh1            last if $cmp = $x->[$j] - $yorg->[$j];
85356d68f1eSafresh1        }
85456d68f1eSafresh1
85556d68f1eSafresh1        if ($cmp == 0) {        # x = y
85656d68f1eSafresh1            @$x = 1;
85756d68f1eSafresh1            return $x, $c->_zero() if wantarray;
858b8851fccSafresh1            return $x;
859b8851fccSafresh1        }
860b8851fccSafresh1
86156d68f1eSafresh1        if ($cmp < 0) {         # x < y
86256d68f1eSafresh1            if (wantarray) {
86356d68f1eSafresh1                my $rem = $c->_copy($x);
8649f11ffb7Safresh1                @$x = 0;
86556d68f1eSafresh1                return $x, $rem;
866b8851fccSafresh1            }
86756d68f1eSafresh1            @$x = 0;
86856d68f1eSafresh1            return $x;
869b8851fccSafresh1        }
870b8851fccSafresh1    }
871b8851fccSafresh1
872b8851fccSafresh1    # all other cases:
873b8851fccSafresh1
8749f11ffb7Safresh1    my $y = $c->_copy($yorg);           # always make copy to preserve
875b8851fccSafresh1
87656d68f1eSafresh1    my $tmp = $y->[-1] + 1;
87756d68f1eSafresh1    my $rem = $BASE % $tmp;
87856d68f1eSafresh1    my $dd  = ($BASE - $rem) / $tmp;
87956d68f1eSafresh1    if ($dd != 1) {
88056d68f1eSafresh1        my $car = 0;
88156d68f1eSafresh1        for my $xi (@$x) {
882b8851fccSafresh1            $xi = $xi * $dd + $car;
88356d68f1eSafresh1            $rem = $xi % $BASE;
88456d68f1eSafresh1            $car = ($xi - $rem) / $BASE;
88556d68f1eSafresh1            $xi = $rem;
886b8851fccSafresh1        }
8879f11ffb7Safresh1        push(@$x, $car);
8889f11ffb7Safresh1        $car = 0;
88956d68f1eSafresh1        for my $yi (@$y) {
890b8851fccSafresh1            $yi = $yi * $dd + $car;
89156d68f1eSafresh1            $rem = $yi % $BASE;
89256d68f1eSafresh1            $car = ($yi - $rem) / $BASE;
89356d68f1eSafresh1            $yi = $rem;
894b8851fccSafresh1        }
8959f11ffb7Safresh1    } else {
896b8851fccSafresh1        push(@$x, 0);
897b8851fccSafresh1    }
898b8851fccSafresh1
899b8851fccSafresh1    # @q will accumulate the final result, $q contains the current computed
900b8851fccSafresh1    # part of the final result
901b8851fccSafresh1
90256d68f1eSafresh1    my @q = ();
90356d68f1eSafresh1    my ($v2, $v1) = @$y[-2, -1];
904b8851fccSafresh1    $v2 = 0 unless $v2;
9059f11ffb7Safresh1    while ($#$x > $#$y) {
90656d68f1eSafresh1        my ($u2, $u1, $u0) = @$x[-3 .. -1];
907b8851fccSafresh1        $u2 = 0 unless $u2;
908b8851fccSafresh1        #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
909b8851fccSafresh1        # if $v1 == 0;
91056d68f1eSafresh1        my $tmp = $u0 * $BASE + $u1;
91156d68f1eSafresh1        my $rem = $tmp % $v1;
91256d68f1eSafresh1        my $q = $u0 == $v1 ? $MAX_VAL : (($tmp - $rem) / $v1);
91356d68f1eSafresh1        --$q while $v2 * $q > ($u0 * $BASE + $u1 - $q * $v1) * $BASE + $u2;
9149f11ffb7Safresh1        if ($q) {
91556d68f1eSafresh1            my $prd;
91656d68f1eSafresh1            my ($car, $bar) = (0, 0);
91756d68f1eSafresh1            for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
918b8851fccSafresh1                $prd = $q * $y->[$yi] + $car;
91956d68f1eSafresh1                $rem = $prd % $BASE;
92056d68f1eSafresh1                $car = ($prd - $rem) / $BASE;
92156d68f1eSafresh1                $prd -= $car * $BASE;
92256d68f1eSafresh1                $x->[$xi] += $BASE if $bar = (($x->[$xi] -= $prd + $bar) < 0);
923b8851fccSafresh1            }
9249f11ffb7Safresh1            if ($x->[-1] < $car + $bar) {
9259f11ffb7Safresh1                $car = 0;
9269f11ffb7Safresh1                --$q;
92756d68f1eSafresh1                for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
928b8851fccSafresh1                    $x->[$xi] -= $BASE
92956d68f1eSafresh1                      if $car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE);
930b8851fccSafresh1                }
931b8851fccSafresh1            }
932b8851fccSafresh1        }
9339f11ffb7Safresh1        pop(@$x);
9349f11ffb7Safresh1        unshift(@q, $q);
935b8851fccSafresh1    }
93656d68f1eSafresh1
9379f11ffb7Safresh1    if (wantarray) {
9389f11ffb7Safresh1        my $d = bless [], $c;
9399f11ffb7Safresh1        if ($dd != 1) {
94056d68f1eSafresh1            my $car = 0;
94156d68f1eSafresh1            my ($prd, $rem);
94256d68f1eSafresh1            for my $xi (reverse @$x) {
943b8851fccSafresh1                $prd = $car * $BASE + $xi;
94456d68f1eSafresh1                $rem = $prd % $dd;
94556d68f1eSafresh1                $tmp = ($prd - $rem) / $dd;
94656d68f1eSafresh1                $car = $rem;
94756d68f1eSafresh1                unshift @$d, $tmp;
948b8851fccSafresh1            }
9499f11ffb7Safresh1        } else {
9509f11ffb7Safresh1            @$d = @$x;
951b8851fccSafresh1        }
952b8851fccSafresh1        @$x = @q;
953b8851fccSafresh1        __strip_zeros($x);
954b8851fccSafresh1        __strip_zeros($d);
955b8851fccSafresh1        return ($x, $d);
956b8851fccSafresh1    }
957b8851fccSafresh1    @$x = @q;
958b8851fccSafresh1    __strip_zeros($x);
959b8851fccSafresh1    $x;
960b8851fccSafresh1}
961b8851fccSafresh1
962b8851fccSafresh1##############################################################################
963b8851fccSafresh1# testing
964b8851fccSafresh1
965b8851fccSafresh1sub _acmp {
966b8851fccSafresh1    # Internal absolute post-normalized compare (ignore signs)
967b8851fccSafresh1    # ref to array, ref to array, return <0, 0, >0
968b8851fccSafresh1    # Arrays must have at least one entry; this is not checked for.
969b8851fccSafresh1    my ($c, $cx, $cy) = @_;
970b8851fccSafresh1
971b8851fccSafresh1    # shortcut for short numbers
972b8851fccSafresh1    return (($cx->[0] <=> $cy->[0]) <=> 0)
9739f11ffb7Safresh1      if @$cx == 1 && @$cy == 1;
974b8851fccSafresh1
975b8851fccSafresh1    # fast comp based on number of array elements (aka pseudo-length)
976b8851fccSafresh1    my $lxy = (@$cx - @$cy)
977b8851fccSafresh1      # or length of first element if same number of elements (aka difference 0)
978b8851fccSafresh1      ||
979b8851fccSafresh1        # need int() here because sometimes the last element is '00018' vs '18'
980b8851fccSafresh1        (length(int($cx->[-1])) - length(int($cy->[-1])));
981b8851fccSafresh1
982b8851fccSafresh1    return -1 if $lxy < 0;      # already differs, ret
983b8851fccSafresh1    return  1 if $lxy > 0;      # ditto
984b8851fccSafresh1
985b8851fccSafresh1    # manual way (abort if unequal, good for early ne)
986b8851fccSafresh1    my $a;
987b8851fccSafresh1    my $j = @$cx;
988b8851fccSafresh1    while (--$j >= 0) {
989b8851fccSafresh1        last if $a = $cx->[$j] - $cy->[$j];
990b8851fccSafresh1    }
991b8851fccSafresh1    $a <=> 0;
992b8851fccSafresh1}
993b8851fccSafresh1
994b8851fccSafresh1sub _len {
995b8851fccSafresh1    # compute number of digits in base 10
996b8851fccSafresh1
997b8851fccSafresh1    # int() because add/sub sometimes leaves strings (like '00005') instead of
998b8851fccSafresh1    # '5' in this place, thus causing length() to report wrong length
999b8851fccSafresh1    my $cx = $_[1];
1000b8851fccSafresh1
1001b8851fccSafresh1    (@$cx - 1) * $BASE_LEN + length(int($cx->[-1]));
1002b8851fccSafresh1}
1003b8851fccSafresh1
1004b8851fccSafresh1sub _digit {
1005b8851fccSafresh1    # Return the nth digit. Zero is rightmost, so _digit(123, 0) gives 3.
1006b8851fccSafresh1    # Negative values count from the left, so _digit(123, -1) gives 1.
1007b8851fccSafresh1    my ($c, $x, $n) = @_;
1008b8851fccSafresh1
1009b8851fccSafresh1    my $len = _len('', $x);
1010b8851fccSafresh1
1011b8851fccSafresh1    $n += $len if $n < 0;               # -1 last, -2 second-to-last
10129f11ffb7Safresh1
10139f11ffb7Safresh1    # Math::BigInt::Calc returns 0 if N is out of range, but this is not done
10149f11ffb7Safresh1    # by the other backend libraries.
10159f11ffb7Safresh1
1016b8851fccSafresh1    return "0" if $n < 0 || $n >= $len; # return 0 for digits out of range
1017b8851fccSafresh1
10189f11ffb7Safresh1    my $elem = int($n / $BASE_LEN);     # index of array element
10199f11ffb7Safresh1    my $digit = $n % $BASE_LEN;         # index of digit within the element
10209f11ffb7Safresh1    substr("0" x $BASE_LEN . "$x->[$elem]", -1 - $digit, 1);
1021b8851fccSafresh1}
1022b8851fccSafresh1
1023b8851fccSafresh1sub _zeros {
1024b8851fccSafresh1    # Return number of trailing zeros in decimal.
1025b8851fccSafresh1    # Check each array element for having 0 at end as long as elem == 0
1026b8851fccSafresh1    # Upon finding a elem != 0, stop.
1027b8851fccSafresh1
1028b8851fccSafresh1    my $x = $_[1];
1029b8851fccSafresh1
1030b8851fccSafresh1    return 0 if @$x == 1 && $x->[0] == 0;
1031b8851fccSafresh1
1032b8851fccSafresh1    my $zeros = 0;
10339f11ffb7Safresh1    foreach my $elem (@$x) {
10349f11ffb7Safresh1        if ($elem != 0) {
10359f11ffb7Safresh1            $elem =~ /[^0](0*)\z/;
10369f11ffb7Safresh1            $zeros += length($1);       # count trailing zeros
1037b8851fccSafresh1            last;                       # early out
1038b8851fccSafresh1        }
10399f11ffb7Safresh1        $zeros += $BASE_LEN;
1040b8851fccSafresh1    }
1041b8851fccSafresh1    $zeros;
1042b8851fccSafresh1}
1043b8851fccSafresh1
1044b8851fccSafresh1##############################################################################
1045b8851fccSafresh1# _is_* routines
1046b8851fccSafresh1
1047b8851fccSafresh1sub _is_zero {
1048b8851fccSafresh1    # return true if arg is zero
1049b8851fccSafresh1    @{$_[1]} == 1 && $_[1]->[0] == 0 ? 1 : 0;
1050b8851fccSafresh1}
1051b8851fccSafresh1
1052b8851fccSafresh1sub _is_even {
1053b8851fccSafresh1    # return true if arg is even
1054eac174f2Safresh1    $_[1]->[0] % 2 ? 0 : 1;
1055b8851fccSafresh1}
1056b8851fccSafresh1
1057b8851fccSafresh1sub _is_odd {
1058b8851fccSafresh1    # return true if arg is odd
1059eac174f2Safresh1    $_[1]->[0] % 2 ? 1 : 0;
1060b8851fccSafresh1}
1061b8851fccSafresh1
1062b8851fccSafresh1sub _is_one {
1063b8851fccSafresh1    # return true if arg is one
1064b8851fccSafresh1    @{$_[1]} == 1 && $_[1]->[0] == 1 ? 1 : 0;
1065b8851fccSafresh1}
1066b8851fccSafresh1
1067b8851fccSafresh1sub _is_two {
1068b8851fccSafresh1    # return true if arg is two
1069b8851fccSafresh1    @{$_[1]} == 1 && $_[1]->[0] == 2 ? 1 : 0;
1070b8851fccSafresh1}
1071b8851fccSafresh1
1072b8851fccSafresh1sub _is_ten {
1073b8851fccSafresh1    # return true if arg is ten
1074eac174f2Safresh1    if ($BASE_LEN == 1) {
1075eac174f2Safresh1        @{$_[1]} == 2 && $_[1]->[0] == 0 && $_[1]->[1] == 1 ? 1 : 0;
1076eac174f2Safresh1    } else {
1077b8851fccSafresh1        @{$_[1]} == 1 && $_[1]->[0] == 10 ? 1 : 0;
1078b8851fccSafresh1    }
1079eac174f2Safresh1}
1080b8851fccSafresh1
1081b8851fccSafresh1sub __strip_zeros {
1082b8851fccSafresh1    # Internal normalization function that strips leading zeros from the array.
1083b8851fccSafresh1    # Args: ref to array
10849f11ffb7Safresh1    my $x = shift;
1085b8851fccSafresh1
10869f11ffb7Safresh1    push @$x, 0 if @$x == 0;    # div might return empty results, so fix it
10879f11ffb7Safresh1    return $x if @$x == 1;      # early out
1088b8851fccSafresh1
1089b8851fccSafresh1    #print "strip: cnt $cnt i $i\n";
1090b8851fccSafresh1    # '0', '3', '4', '0', '0',
1091b8851fccSafresh1    #  0    1    2    3    4
1092b8851fccSafresh1    # cnt = 5, i = 4
1093b8851fccSafresh1    # i = 4
1094b8851fccSafresh1    # i = 3
1095b8851fccSafresh1    # => fcnt = cnt - i (5-2 => 3, cnt => 5-1 = 4, throw away from 4th pos)
1096b8851fccSafresh1    # >= 1: skip first part (this can be zero)
10979f11ffb7Safresh1
10989f11ffb7Safresh1    my $i = $#$x;
1099b8851fccSafresh1    while ($i > 0) {
11009f11ffb7Safresh1        last if $x->[$i] != 0;
1101b8851fccSafresh1        $i--;
1102b8851fccSafresh1    }
1103b8851fccSafresh1    $i++;
11049f11ffb7Safresh1    splice(@$x, $i) if $i < @$x;
11059f11ffb7Safresh1    $x;
1106b8851fccSafresh1}
1107b8851fccSafresh1
1108b8851fccSafresh1###############################################################################
1109b8851fccSafresh1# check routine to test internal state for corruptions
1110b8851fccSafresh1
1111b8851fccSafresh1sub _check {
1112b8851fccSafresh1    # used by the test suite
11139f11ffb7Safresh1    my ($class, $x) = @_;
1114b8851fccSafresh1
11159f11ffb7Safresh1    my $msg = $class -> SUPER::_check($x);
11169f11ffb7Safresh1    return $msg if $msg;
1117b8851fccSafresh1
11189f11ffb7Safresh1    my $n;
11199f11ffb7Safresh1    eval { $n = @$x };
11209f11ffb7Safresh1    return "Not an array reference" unless $@ eq '';
11219f11ffb7Safresh1
11229f11ffb7Safresh1    return "Reference to an empty array" unless $n > 0;
11239f11ffb7Safresh1
11249f11ffb7Safresh1    # The following fails with Math::BigInt::FastCalc because a
11259f11ffb7Safresh1    # Math::BigInt::FastCalc "object" is an unblessed array ref.
11269f11ffb7Safresh1    #
11279f11ffb7Safresh1    #return 0 unless ref($x) eq $class;
11289f11ffb7Safresh1
11299f11ffb7Safresh1    for (my $i = 0 ; $i <= $#$x ; ++ $i) {
11309f11ffb7Safresh1        my $e = $x -> [$i];
11319f11ffb7Safresh1
11329f11ffb7Safresh1        return "Element at index $i is undefined"
11339f11ffb7Safresh1          unless defined $e;
11349f11ffb7Safresh1
11359f11ffb7Safresh1        return "Element at index $i is a '" . ref($e) .
11369f11ffb7Safresh1          "', which is not a scalar"
11379f11ffb7Safresh1          unless ref($e) eq "";
11389f11ffb7Safresh1
11399f11ffb7Safresh1        # It would be better to use the regex /^([1-9]\d*|0)\z/, but that fails
11409f11ffb7Safresh1        # in Math::BigInt::FastCalc, because it sometimes creates array
11419f11ffb7Safresh1        # elements like "000000".
11429f11ffb7Safresh1        return "Element at index $i is '$e', which does not look like an" .
11439f11ffb7Safresh1          " normal integer" unless $e =~ /^\d+\z/;
11449f11ffb7Safresh1
11459f11ffb7Safresh1        return "Element at index $i is '$e', which is not smaller than" .
11469f11ffb7Safresh1          " the base '$BASE'" if $e >= $BASE;
11479f11ffb7Safresh1
11489f11ffb7Safresh1        return "Element at index $i (last element) is zero"
11499f11ffb7Safresh1          if $#$x > 0 && $i == $#$x && $e == 0;
1150b8851fccSafresh1    }
11519f11ffb7Safresh1
11529f11ffb7Safresh1    return 0;
1153b8851fccSafresh1}
1154b8851fccSafresh1
1155b8851fccSafresh1###############################################################################
1156b8851fccSafresh1
1157b8851fccSafresh1sub _mod {
1158b8851fccSafresh1    # if possible, use mod shortcut
1159b8851fccSafresh1    my ($c, $x, $yo) = @_;
1160b8851fccSafresh1
1161b8851fccSafresh1    # slow way since $y too big
1162b8851fccSafresh1    if (@$yo > 1) {
11639f11ffb7Safresh1        my ($xo, $rem) = $c->_div($x, $yo);
1164b8851fccSafresh1        @$x = @$rem;
1165b8851fccSafresh1        return $x;
1166b8851fccSafresh1    }
1167b8851fccSafresh1
1168b8851fccSafresh1    my $y = $yo->[0];
1169b8851fccSafresh1
1170b8851fccSafresh1    # if both are single element arrays
11719f11ffb7Safresh1    if (@$x == 1) {
1172b8851fccSafresh1        $x->[0] %= $y;
1173b8851fccSafresh1        return $x;
1174b8851fccSafresh1    }
1175b8851fccSafresh1
1176b8851fccSafresh1    # if @$x has more than one element, but @$y is a single element
1177b8851fccSafresh1    my $b = $BASE % $y;
1178b8851fccSafresh1    if ($b == 0) {
1179b8851fccSafresh1        # when BASE % Y == 0 then (B * BASE) % Y == 0
1180b8851fccSafresh1        # (B * BASE) % $y + A % Y => A % Y
1181b8851fccSafresh1        # so need to consider only last element: O(1)
1182b8851fccSafresh1        $x->[0] %= $y;
1183b8851fccSafresh1    } elsif ($b == 1) {
1184b8851fccSafresh1        # else need to go through all elements in @$x: O(N), but loop is a bit
1185b8851fccSafresh1        # simplified
1186b8851fccSafresh1        my $r = 0;
1187b8851fccSafresh1        foreach (@$x) {
1188b8851fccSafresh1            $r = ($r + $_) % $y; # not much faster, but heh...
1189b8851fccSafresh1            #$r += $_ % $y; $r %= $y;
1190b8851fccSafresh1        }
1191b8851fccSafresh1        $r = 0 if $r == $y;
1192b8851fccSafresh1        $x->[0] = $r;
1193b8851fccSafresh1    } else {
1194b8851fccSafresh1        # else need to go through all elements in @$x: O(N)
1195b8851fccSafresh1        my $r = 0;
1196b8851fccSafresh1        my $bm = 1;
1197b8851fccSafresh1        foreach (@$x) {
1198b8851fccSafresh1            $r = ($_ * $bm + $r) % $y;
1199b8851fccSafresh1            $bm = ($bm * $b) % $y;
1200b8851fccSafresh1
1201b8851fccSafresh1            #$r += ($_ % $y) * $bm;
1202b8851fccSafresh1            #$bm *= $b;
1203b8851fccSafresh1            #$bm %= $y;
1204b8851fccSafresh1            #$r %= $y;
1205b8851fccSafresh1        }
1206b8851fccSafresh1        $r = 0 if $r == $y;
1207b8851fccSafresh1        $x->[0] = $r;
1208b8851fccSafresh1    }
1209b8851fccSafresh1    @$x = $x->[0];              # keep one element of @$x
1210b8851fccSafresh1    return $x;
1211b8851fccSafresh1}
1212b8851fccSafresh1
1213b8851fccSafresh1##############################################################################
1214b8851fccSafresh1# shifts
1215b8851fccSafresh1
1216b8851fccSafresh1sub _rsft {
1217eac174f2Safresh1    my ($c, $x, $n, $b) = @_;
1218eac174f2Safresh1    return $x if $c->_is_zero($x) || $c->_is_zero($n);
1219b8851fccSafresh1
1220eac174f2Safresh1    # For backwards compatibility, allow the base $b to be a scalar.
1221eac174f2Safresh1
1222eac174f2Safresh1    $b = $c->_new($b) unless ref $b;
1223eac174f2Safresh1
1224eac174f2Safresh1    if ($c -> _acmp($b, $c -> _ten())) {
1225eac174f2Safresh1        return scalar $c->_div($x, $c->_pow($c->_copy($b), $n));
1226b8851fccSafresh1    }
1227b8851fccSafresh1
1228b8851fccSafresh1    # shortcut (faster) for shifting by 10)
1229b8851fccSafresh1    # multiples of $BASE_LEN
1230b8851fccSafresh1    my $dst = 0;                # destination
1231eac174f2Safresh1    my $src = $c->_num($n);     # as normal int
1232b8851fccSafresh1    my $xlen = (@$x - 1) * $BASE_LEN + length(int($x->[-1]));
1233b8851fccSafresh1    if ($src >= $xlen or ($src == $xlen and !defined $x->[1])) {
1234b8851fccSafresh1        # 12345 67890 shifted right by more than 10 digits => 0
1235b8851fccSafresh1        splice(@$x, 1);         # leave only one element
1236b8851fccSafresh1        $x->[0] = 0;            # set to zero
1237b8851fccSafresh1        return $x;
1238b8851fccSafresh1    }
1239b8851fccSafresh1    my $rem = $src % $BASE_LEN;   # remainder to shift
1240b8851fccSafresh1    $src = int($src / $BASE_LEN); # source
1241b8851fccSafresh1    if ($rem == 0) {
1242b8851fccSafresh1        splice(@$x, 0, $src);   # even faster, 38.4 => 39.3
1243b8851fccSafresh1    } else {
1244b8851fccSafresh1        my $len = @$x - $src;   # elems to go
1245b8851fccSafresh1        my $vd;
1246b8851fccSafresh1        my $z = '0' x $BASE_LEN;
1247b8851fccSafresh1        $x->[ @$x ] = 0;          # avoid || 0 test inside loop
1248b8851fccSafresh1        while ($dst < $len) {
1249b8851fccSafresh1            $vd = $z . $x->[$src];
1250b8851fccSafresh1            $vd = substr($vd, -$BASE_LEN, $BASE_LEN - $rem);
1251b8851fccSafresh1            $src++;
1252b8851fccSafresh1            $vd = substr($z . $x->[$src], -$rem, $rem) . $vd;
1253b8851fccSafresh1            $vd = substr($vd, -$BASE_LEN, $BASE_LEN) if length($vd) > $BASE_LEN;
1254b8851fccSafresh1            $x->[$dst] = int($vd);
1255b8851fccSafresh1            $dst++;
1256b8851fccSafresh1        }
1257b8851fccSafresh1        splice(@$x, $dst) if $dst > 0;       # kill left-over array elems
125856d68f1eSafresh1        pop(@$x) if $x->[-1] == 0 && @$x > 1; # kill last element if 0
1259b8851fccSafresh1    }                                        # else rem == 0
1260b8851fccSafresh1    $x;
1261b8851fccSafresh1}
1262b8851fccSafresh1
1263b8851fccSafresh1sub _lsft {
12649f11ffb7Safresh1    my ($c, $x, $n, $b) = @_;
1265b8851fccSafresh1
126656d68f1eSafresh1    return $x if $c->_is_zero($x) || $c->_is_zero($n);
12679f11ffb7Safresh1
126856d68f1eSafresh1    # For backwards compatibility, allow the base $b to be a scalar.
12699f11ffb7Safresh1
127056d68f1eSafresh1    $b = $c->_new($b) unless ref $b;
1271b8851fccSafresh1
127256d68f1eSafresh1    # If the base is a power of 10, use shifting, since the internal
127356d68f1eSafresh1    # representation is in base 10eX.
127456d68f1eSafresh1
127556d68f1eSafresh1    my $bstr = $c->_str($b);
127656d68f1eSafresh1    if ($bstr =~ /^1(0+)\z/) {
127756d68f1eSafresh1
127856d68f1eSafresh1        # Adjust $n so that we're shifting in base 10. Do this by multiplying
127956d68f1eSafresh1        # $n by the base 10 logarithm of $b: $b ** $n = 10 ** (log10($b) * $n).
128056d68f1eSafresh1
128156d68f1eSafresh1        my $log10b = length($1);
128256d68f1eSafresh1        $n = $c->_mul($c->_new($log10b), $n);
128356d68f1eSafresh1        $n = $c->_num($n);              # shift-len as normal int
128456d68f1eSafresh1
128556d68f1eSafresh1        # $q is the number of places to shift the elements within the array,
128656d68f1eSafresh1        # and $r is the number of places to shift the values within the
128756d68f1eSafresh1        # elements.
128856d68f1eSafresh1
128956d68f1eSafresh1        my $r = $n % $BASE_LEN;
129056d68f1eSafresh1        my $q = ($n - $r) / $BASE_LEN;
129156d68f1eSafresh1
129256d68f1eSafresh1        # If we must shift the values within the elements ...
129356d68f1eSafresh1
129456d68f1eSafresh1        if ($r) {
129556d68f1eSafresh1            my $i = @$x;                # index
129656d68f1eSafresh1            $x->[$i] = 0;               # initialize most significant element
1297b8851fccSafresh1            my $z = '0' x $BASE_LEN;
129856d68f1eSafresh1            my $vd;
129956d68f1eSafresh1            while ($i >= 0) {
130056d68f1eSafresh1                $vd = $x->[$i];
1301b8851fccSafresh1                $vd = $z . $vd;
130256d68f1eSafresh1                $vd = substr($vd, $r - $BASE_LEN, $BASE_LEN - $r);
130356d68f1eSafresh1                $vd .= $i > 0 ? substr($z . $x->[$i - 1], -$BASE_LEN, $r)
130456d68f1eSafresh1                              : '0' x $r;
1305b8851fccSafresh1                $vd = substr($vd, -$BASE_LEN, $BASE_LEN) if length($vd) > $BASE_LEN;
130656d68f1eSafresh1                $x->[$i] = int($vd);    # e.g., "0...048" -> 48 etc.
130756d68f1eSafresh1                $i--;
1308b8851fccSafresh1            }
130956d68f1eSafresh1
131056d68f1eSafresh1            pop(@$x) if $x->[-1] == 0;  # if most significant element is zero
1311b8851fccSafresh1        }
131256d68f1eSafresh1
131356d68f1eSafresh1        # If we must shift the elements within the array ...
131456d68f1eSafresh1
131556d68f1eSafresh1        if ($q) {
131656d68f1eSafresh1            unshift @$x, (0) x $q;
131756d68f1eSafresh1        }
131856d68f1eSafresh1
13199f11ffb7Safresh1    } else {
132056d68f1eSafresh1        $x = $c->_mul($x, $c->_pow($b, $n));
13219f11ffb7Safresh1    }
132256d68f1eSafresh1
132356d68f1eSafresh1    return $x;
1324b8851fccSafresh1}
1325b8851fccSafresh1
1326b8851fccSafresh1sub _pow {
1327b8851fccSafresh1    # power of $x to $y
1328b8851fccSafresh1    # ref to array, ref to array, return ref to array
1329b8851fccSafresh1    my ($c, $cx, $cy) = @_;
1330b8851fccSafresh1
1331b8851fccSafresh1    if (@$cy == 1 && $cy->[0] == 0) {
1332b8851fccSafresh1        splice(@$cx, 1);
1333b8851fccSafresh1        $cx->[0] = 1;           # y == 0 => x => 1
1334b8851fccSafresh1        return $cx;
1335b8851fccSafresh1    }
1336b8851fccSafresh1
1337b8851fccSafresh1    if ((@$cx == 1 && $cx->[0] == 1) || #    x == 1
1338b8851fccSafresh1        (@$cy == 1 && $cy->[0] == 1))   # or y == 1
1339b8851fccSafresh1    {
1340b8851fccSafresh1        return $cx;
1341b8851fccSafresh1    }
1342b8851fccSafresh1
1343b8851fccSafresh1    if (@$cx == 1 && $cx->[0] == 0) {
1344b8851fccSafresh1        splice (@$cx, 1);
1345b8851fccSafresh1        $cx->[0] = 0;           # 0 ** y => 0 (if not y <= 0)
1346b8851fccSafresh1        return $cx;
1347b8851fccSafresh1    }
1348b8851fccSafresh1
13499f11ffb7Safresh1    my $pow2 = $c->_one();
1350b8851fccSafresh1
13519f11ffb7Safresh1    my $y_bin = $c->_as_bin($cy);
1352b8851fccSafresh1    $y_bin =~ s/^0b//;
1353b8851fccSafresh1    my $len = length($y_bin);
1354b8851fccSafresh1    while (--$len > 0) {
13559f11ffb7Safresh1        $c->_mul($pow2, $cx) if substr($y_bin, $len, 1) eq '1'; # is odd?
13569f11ffb7Safresh1        $c->_mul($cx, $cx);
1357b8851fccSafresh1    }
1358b8851fccSafresh1
13599f11ffb7Safresh1    $c->_mul($cx, $pow2);
1360b8851fccSafresh1    $cx;
1361b8851fccSafresh1}
1362b8851fccSafresh1
1363b8851fccSafresh1sub _nok {
1364b8851fccSafresh1    # Return binomial coefficient (n over k).
1365b8851fccSafresh1    # Given refs to arrays, return ref to array.
1366b8851fccSafresh1    # First input argument is modified.
1367b8851fccSafresh1
1368b8851fccSafresh1    my ($c, $n, $k) = @_;
1369b8851fccSafresh1
1370b8851fccSafresh1    # If k > n/2, or, equivalently, 2*k > n, compute nok(n, k) as
1371b8851fccSafresh1    # nok(n, n-k), to minimize the number if iterations in the loop.
1372b8851fccSafresh1
1373b8851fccSafresh1    {
13749f11ffb7Safresh1        my $twok = $c->_mul($c->_two(), $c->_copy($k)); # 2 * k
13759f11ffb7Safresh1        if ($c->_acmp($twok, $n) > 0) {               # if 2*k > n
13769f11ffb7Safresh1            $k = $c->_sub($c->_copy($n), $k);         # k = n - k
1377b8851fccSafresh1        }
1378b8851fccSafresh1    }
1379b8851fccSafresh1
1380b8851fccSafresh1    # Example:
1381b8851fccSafresh1    #
1382b8851fccSafresh1    # / 7 \       7!       1*2*3*4 * 5*6*7   5 * 6 * 7       6   7
1383b8851fccSafresh1    # |   | = --------- =  --------------- = --------- = 5 * - * -
1384b8851fccSafresh1    # \ 3 /   (7-3)! 3!    1*2*3*4 * 1*2*3   1 * 2 * 3       2   3
1385b8851fccSafresh1
13869f11ffb7Safresh1    if ($c->_is_zero($k)) {
1387b8851fccSafresh1        @$n = 1;
13889f11ffb7Safresh1    } else {
1389b8851fccSafresh1
1390b8851fccSafresh1        # Make a copy of the original n, since we'll be modifying n in-place.
1391b8851fccSafresh1
13929f11ffb7Safresh1        my $n_orig = $c->_copy($n);
1393b8851fccSafresh1
1394b8851fccSafresh1        # n = 5, f = 6, d = 2 (cf. example above)
1395b8851fccSafresh1
13969f11ffb7Safresh1        $c->_sub($n, $k);
13979f11ffb7Safresh1        $c->_inc($n);
1398b8851fccSafresh1
13999f11ffb7Safresh1        my $f = $c->_copy($n);
14009f11ffb7Safresh1        $c->_inc($f);
1401b8851fccSafresh1
14029f11ffb7Safresh1        my $d = $c->_two();
1403b8851fccSafresh1
1404b8851fccSafresh1        # while f <= n (the original n, that is) ...
1405b8851fccSafresh1
14069f11ffb7Safresh1        while ($c->_acmp($f, $n_orig) <= 0) {
1407b8851fccSafresh1
1408b8851fccSafresh1            # n = (n * f / d) == 5 * 6 / 2 (cf. example above)
1409b8851fccSafresh1
14109f11ffb7Safresh1            $c->_mul($n, $f);
14119f11ffb7Safresh1            $c->_div($n, $d);
1412b8851fccSafresh1
1413b8851fccSafresh1            # f = 7, d = 3 (cf. example above)
1414b8851fccSafresh1
14159f11ffb7Safresh1            $c->_inc($f);
14169f11ffb7Safresh1            $c->_inc($d);
1417b8851fccSafresh1        }
1418b8851fccSafresh1
1419b8851fccSafresh1    }
1420b8851fccSafresh1
1421b8851fccSafresh1    return $n;
1422b8851fccSafresh1}
1423b8851fccSafresh1
14249f11ffb7Safresh1sub _fac {
1425b8851fccSafresh1    # factorial of $x
1426b8851fccSafresh1    # ref to array, return ref to array
1427b8851fccSafresh1    my ($c, $cx) = @_;
1428b8851fccSafresh1
1429eac174f2Safresh1    # We cache the smallest values. Don't assume that a single element has a
1430eac174f2Safresh1    # value larger than 9 or else it won't work with a $BASE_LEN of 1.
1431eac174f2Safresh1
1432eac174f2Safresh1    if (@$cx == 1) {
1433eac174f2Safresh1        my @factorials =
1434eac174f2Safresh1          (
1435eac174f2Safresh1           '1',
1436eac174f2Safresh1           '1',
1437eac174f2Safresh1           '2',
1438eac174f2Safresh1           '6',
1439eac174f2Safresh1           '24',
1440eac174f2Safresh1           '120',
1441eac174f2Safresh1           '720',
1442eac174f2Safresh1           '5040',
1443eac174f2Safresh1           '40320',
1444eac174f2Safresh1           '362880',
1445eac174f2Safresh1          );
1446eac174f2Safresh1        if ($cx->[0] <= $#factorials) {
1447eac174f2Safresh1            my $tmp = $c -> _new($factorials[ $cx->[0] ]);
1448eac174f2Safresh1            @$cx = @$tmp;
1449b8851fccSafresh1            return $cx;
1450b8851fccSafresh1        }
1451eac174f2Safresh1    }
1452eac174f2Safresh1
1453eac174f2Safresh1    # The old code further below doesn't work for small values of $BASE_LEN.
1454eac174f2Safresh1    # Alas, I have not been able to (or taken the time to) decipher it, so for
1455eac174f2Safresh1    # the case when $BASE_LEN is small, we call the parent class. This code
1456eac174f2Safresh1    # works in for every value of $x and $BASE_LEN. We could use this code for
1457eac174f2Safresh1    # all cases, but it is a little slower than the code further below, so at
1458eac174f2Safresh1    # least for now we keep the code below.
1459eac174f2Safresh1
1460eac174f2Safresh1    if ($BASE_LEN <= 2) {
1461eac174f2Safresh1        my $tmp = $c -> SUPER::_fac($cx);
1462eac174f2Safresh1        @$cx = @$tmp;
1463eac174f2Safresh1        return $cx;
1464eac174f2Safresh1    }
1465eac174f2Safresh1
1466eac174f2Safresh1    # This code does not work for small values of $BASE_LEN.
1467b8851fccSafresh1
1468b8851fccSafresh1    if ((@$cx == 1) &&          # we do this only if $x >= 12 and $x <= 7000
14699f11ffb7Safresh1        ($cx->[0] >= 12 && $cx->[0] < 7000)) {
1470b8851fccSafresh1
1471b8851fccSafresh1        # Calculate (k-j) * (k-j+1) ... k .. (k+j-1) * (k + j)
1472b8851fccSafresh1        # See http://blogten.blogspot.com/2007/01/calculating-n.html
1473b8851fccSafresh1        # The above series can be expressed as factors:
1474b8851fccSafresh1        #   k * k - (j - i) * 2
1475b8851fccSafresh1        # We cache k*k, and calculate (j * j) as the sum of the first j odd integers
1476b8851fccSafresh1
1477b8851fccSafresh1        # This will not work when N exceeds the storage of a Perl scalar, however,
14789f11ffb7Safresh1        # in this case the algorithm would be way too slow to terminate, anyway.
1479b8851fccSafresh1
1480b8851fccSafresh1        # As soon as the last element of $cx is 0, we split it up and remember
1481b8851fccSafresh1        # how many zeors we got so far. The reason is that n! will accumulate
1482b8851fccSafresh1        # zeros at the end rather fast.
1483b8851fccSafresh1        my $zero_elements = 0;
1484b8851fccSafresh1
1485b8851fccSafresh1        # If n is even, set n = n -1
14869f11ffb7Safresh1        my $k = $c->_num($cx);
14879f11ffb7Safresh1        my $even = 1;
14889f11ffb7Safresh1        if (($k & 1) == 0) {
14899f11ffb7Safresh1            $even = $k;
14909f11ffb7Safresh1            $k --;
1491b8851fccSafresh1        }
1492b8851fccSafresh1        # set k to the center point
1493b8851fccSafresh1        $k = ($k + 1) / 2;
1494b8851fccSafresh1        #  print "k $k even: $even\n";
1495b8851fccSafresh1        # now calculate k * k
1496b8851fccSafresh1        my $k2 = $k * $k;
14979f11ffb7Safresh1        my $odd = 1;
14989f11ffb7Safresh1        my $sum = 1;
1499b8851fccSafresh1        my $i = $k - 1;
1500b8851fccSafresh1        # keep reference to x
15019f11ffb7Safresh1        my $new_x = $c->_new($k * $even);
1502b8851fccSafresh1        @$cx = @$new_x;
15039f11ffb7Safresh1        if ($cx->[0] == 0) {
15049f11ffb7Safresh1            $zero_elements ++;
15059f11ffb7Safresh1            shift @$cx;
1506b8851fccSafresh1        }
15079f11ffb7Safresh1        #  print STDERR "x = ", $c->_str($cx), "\n";
1508b8851fccSafresh1        my $BASE2 = int(sqrt($BASE))-1;
1509b8851fccSafresh1        my $j = 1;
15109f11ffb7Safresh1        while ($j <= $i) {
15119f11ffb7Safresh1            my $m = ($k2 - $sum);
15129f11ffb7Safresh1            $odd += 2;
15139f11ffb7Safresh1            $sum += $odd;
15149f11ffb7Safresh1            $j++;
15159f11ffb7Safresh1            while ($j <= $i && ($m < $BASE2) && (($k2 - $sum) < $BASE2)) {
1516b8851fccSafresh1                $m *= ($k2 - $sum);
15179f11ffb7Safresh1                $odd += 2;
15189f11ffb7Safresh1                $sum += $odd;
15199f11ffb7Safresh1                $j++;
1520b8851fccSafresh1                #      print STDERR "\n k2 $k2 m $m sum $sum odd $odd\n"; sleep(1);
1521b8851fccSafresh1            }
15229f11ffb7Safresh1            if ($m < $BASE) {
15239f11ffb7Safresh1                $c->_mul($cx, [$m]);
15249f11ffb7Safresh1            } else {
15259f11ffb7Safresh1                $c->_mul($cx, $c->_new($m));
1526b8851fccSafresh1            }
15279f11ffb7Safresh1            if ($cx->[0] == 0) {
15289f11ffb7Safresh1                $zero_elements ++;
15299f11ffb7Safresh1                shift @$cx;
1530b8851fccSafresh1            }
15319f11ffb7Safresh1            #    print STDERR "Calculate $k2 - $sum = $m (x = ", $c->_str($cx), ")\n";
1532b8851fccSafresh1        }
1533b8851fccSafresh1        # multiply in the zeros again
1534b8851fccSafresh1        unshift @$cx, (0) x $zero_elements;
1535b8851fccSafresh1        return $cx;
1536b8851fccSafresh1    }
1537b8851fccSafresh1
15389f11ffb7Safresh1    # go forward until $base is exceeded limit is either $x steps (steps == 100
15399f11ffb7Safresh1    # means a result always too high) or $base.
15409f11ffb7Safresh1    my $steps = 100;
15419f11ffb7Safresh1    $steps = $cx->[0] if @$cx == 1;
15429f11ffb7Safresh1    my $r = 2;
15439f11ffb7Safresh1    my $cf = 3;
15449f11ffb7Safresh1    my $step = 2;
15459f11ffb7Safresh1    my $last = $r;
15469f11ffb7Safresh1    while ($r * $cf < $BASE && $step < $steps) {
15479f11ffb7Safresh1        $last = $r;
15489f11ffb7Safresh1        $r *= $cf++;
15499f11ffb7Safresh1        $step++;
1550b8851fccSafresh1    }
15519f11ffb7Safresh1    if ((@$cx == 1) && $step == $cx->[0]) {
1552b8851fccSafresh1        # completely done, so keep reference to $x and return
1553b8851fccSafresh1        $cx->[0] = $r;
1554b8851fccSafresh1        return $cx;
1555b8851fccSafresh1    }
1556b8851fccSafresh1
1557b8851fccSafresh1    # now we must do the left over steps
1558b8851fccSafresh1    my $n;                      # steps still to do
15599f11ffb7Safresh1    if (@$cx == 1) {
1560b8851fccSafresh1        $n = $cx->[0];
15619f11ffb7Safresh1    } else {
15629f11ffb7Safresh1        $n = $c->_copy($cx);
1563b8851fccSafresh1    }
1564b8851fccSafresh1
1565b8851fccSafresh1    # Set $cx to the last result below $BASE (but keep ref to $x)
15669f11ffb7Safresh1    $cx->[0] = $last;
15679f11ffb7Safresh1    splice (@$cx, 1);
1568b8851fccSafresh1    # As soon as the last element of $cx is 0, we split it up and remember
1569b8851fccSafresh1    # how many zeors we got so far. The reason is that n! will accumulate
1570b8851fccSafresh1    # zeros at the end rather fast.
1571b8851fccSafresh1    my $zero_elements = 0;
1572b8851fccSafresh1
1573b8851fccSafresh1    # do left-over steps fit into a scalar?
15749f11ffb7Safresh1    if (ref $n eq 'ARRAY') {
1575b8851fccSafresh1        # No, so use slower inc() & cmp()
1576b8851fccSafresh1        # ($n is at least $BASE here)
1577b8851fccSafresh1        my $base_2 = int(sqrt($BASE)) - 1;
1578b8851fccSafresh1        #print STDERR "base_2: $base_2\n";
15799f11ffb7Safresh1        while ($step < $base_2) {
15809f11ffb7Safresh1            if ($cx->[0] == 0) {
15819f11ffb7Safresh1                $zero_elements ++;
15829f11ffb7Safresh1                shift @$cx;
1583b8851fccSafresh1            }
15849f11ffb7Safresh1            my $b = $step * ($step + 1);
15859f11ffb7Safresh1            $step += 2;
15869f11ffb7Safresh1            $c->_mul($cx, [$b]);
1587b8851fccSafresh1        }
1588b8851fccSafresh1        $step = [$step];
15899f11ffb7Safresh1        while ($c->_acmp($step, $n) <= 0) {
15909f11ffb7Safresh1            if ($cx->[0] == 0) {
15919f11ffb7Safresh1                $zero_elements ++;
15929f11ffb7Safresh1                shift @$cx;
1593b8851fccSafresh1            }
15949f11ffb7Safresh1            $c->_mul($cx, $step);
15959f11ffb7Safresh1            $c->_inc($step);
1596b8851fccSafresh1        }
15979f11ffb7Safresh1    } else {
1598b8851fccSafresh1        # Yes, so we can speed it up slightly
1599b8851fccSafresh1
1600b8851fccSafresh1        #    print "# left over steps $n\n";
1601b8851fccSafresh1
1602b8851fccSafresh1        my $base_4 = int(sqrt(sqrt($BASE))) - 2;
1603b8851fccSafresh1        #print STDERR "base_4: $base_4\n";
1604b8851fccSafresh1        my $n4 = $n - 4;
16059f11ffb7Safresh1        while ($step < $n4 && $step < $base_4) {
16069f11ffb7Safresh1            if ($cx->[0] == 0) {
16079f11ffb7Safresh1                $zero_elements ++;
16089f11ffb7Safresh1                shift @$cx;
1609b8851fccSafresh1            }
16109f11ffb7Safresh1            my $b = $step * ($step + 1);
16119f11ffb7Safresh1            $step += 2;
16129f11ffb7Safresh1            $b *= $step * ($step + 1);
16139f11ffb7Safresh1            $step += 2;
16149f11ffb7Safresh1            $c->_mul($cx, [$b]);
1615b8851fccSafresh1        }
1616b8851fccSafresh1        my $base_2 = int(sqrt($BASE)) - 1;
1617b8851fccSafresh1        my $n2 = $n - 2;
1618b8851fccSafresh1        #print STDERR "base_2: $base_2\n";
16199f11ffb7Safresh1        while ($step < $n2 && $step < $base_2) {
16209f11ffb7Safresh1            if ($cx->[0] == 0) {
16219f11ffb7Safresh1                $zero_elements ++;
16229f11ffb7Safresh1                shift @$cx;
1623b8851fccSafresh1            }
16249f11ffb7Safresh1            my $b = $step * ($step + 1);
16259f11ffb7Safresh1            $step += 2;
16269f11ffb7Safresh1            $c->_mul($cx, [$b]);
1627b8851fccSafresh1        }
1628b8851fccSafresh1        # do what's left over
16299f11ffb7Safresh1        while ($step <= $n) {
16309f11ffb7Safresh1            $c->_mul($cx, [$step]);
16319f11ffb7Safresh1            $step++;
16329f11ffb7Safresh1            if ($cx->[0] == 0) {
16339f11ffb7Safresh1                $zero_elements ++;
16349f11ffb7Safresh1                shift @$cx;
1635b8851fccSafresh1            }
1636b8851fccSafresh1        }
1637b8851fccSafresh1    }
1638b8851fccSafresh1    # multiply in the zeros again
1639b8851fccSafresh1    unshift @$cx, (0) x $zero_elements;
1640b8851fccSafresh1    $cx;                        # return result
1641b8851fccSafresh1}
1642b8851fccSafresh1
16439f11ffb7Safresh1sub _log_int {
1644b8851fccSafresh1    # calculate integer log of $x to base $base
1645b8851fccSafresh1    # ref to array, ref to array - return ref to array
1646b8851fccSafresh1    my ($c, $x, $base) = @_;
1647b8851fccSafresh1
1648b8851fccSafresh1    # X == 0 => NaN
16499f11ffb7Safresh1    return if @$x == 1 && $x->[0] == 0;
16509f11ffb7Safresh1
1651b8851fccSafresh1    # BASE 0 or 1 => NaN
16529f11ffb7Safresh1    return if @$base == 1 && $base->[0] < 2;
16539f11ffb7Safresh1
16549f11ffb7Safresh1    # X == 1 => 0 (is exact)
16559f11ffb7Safresh1    if (@$x == 1 && $x->[0] == 1) {
16569f11ffb7Safresh1        @$x = 0;
16579f11ffb7Safresh1        return $x, 1;
1658b8851fccSafresh1    }
1659b8851fccSafresh1
16609f11ffb7Safresh1    my $cmp = $c->_acmp($x, $base);
16619f11ffb7Safresh1
16629f11ffb7Safresh1    # X == BASE => 1 (is exact)
16639f11ffb7Safresh1    if ($cmp == 0) {
16649f11ffb7Safresh1        @$x = 1;
16659f11ffb7Safresh1        return $x, 1;
16669f11ffb7Safresh1    }
16679f11ffb7Safresh1
16689f11ffb7Safresh1    # 1 < X < BASE => 0 (is truncated)
16699f11ffb7Safresh1    if ($cmp < 0) {
16709f11ffb7Safresh1        @$x = 0;
16719f11ffb7Safresh1        return $x, 0;
16729f11ffb7Safresh1    }
16739f11ffb7Safresh1
16749f11ffb7Safresh1    my $x_org = $c->_copy($x);  # preserve x
1675b8851fccSafresh1
1676b8851fccSafresh1    # Compute a guess for the result based on:
1677b8851fccSafresh1    # $guess = int ( length_in_base_10(X) / ( log(base) / log(10) ) )
16789f11ffb7Safresh1    my $len = $c->_len($x_org);
1679b8851fccSafresh1    my $log = log($base->[-1]) / log(10);
1680b8851fccSafresh1
1681b8851fccSafresh1    # for each additional element in $base, we add $BASE_LEN to the result,
1682b8851fccSafresh1    # based on the observation that log($BASE, 10) is BASE_LEN and
1683b8851fccSafresh1    # log(x*y) == log(x) + log(y):
16849f11ffb7Safresh1    $log += (@$base - 1) * $BASE_LEN;
1685b8851fccSafresh1
1686b8851fccSafresh1    # calculate now a guess based on the values obtained above:
1687eac174f2Safresh1    my $res = $c->_new(int($len / $log));
1688b8851fccSafresh1
1689eac174f2Safresh1    @$x = @$res;
16909f11ffb7Safresh1    my $trial = $c->_pow($c->_copy($base), $x);
16919f11ffb7Safresh1    my $acmp = $c->_acmp($trial, $x_org);
1692b8851fccSafresh1
16939f11ffb7Safresh1    # Did we get the exact result?
1694b8851fccSafresh1
16959f11ffb7Safresh1    return $x, 1 if $acmp == 0;
1696b8851fccSafresh1
16979f11ffb7Safresh1    # Too small?
16989f11ffb7Safresh1
16999f11ffb7Safresh1    while ($acmp < 0) {
17009f11ffb7Safresh1        $c->_mul($trial, $base);
17019f11ffb7Safresh1        $c->_inc($x);
17029f11ffb7Safresh1        $acmp = $c->_acmp($trial, $x_org);
1703b8851fccSafresh1    }
1704b8851fccSafresh1
17059f11ffb7Safresh1    # Too big?
1706b8851fccSafresh1
17079f11ffb7Safresh1    while ($acmp > 0) {
17089f11ffb7Safresh1        $c->_div($trial, $base);
17099f11ffb7Safresh1        $c->_dec($x);
17109f11ffb7Safresh1        $acmp = $c->_acmp($trial, $x_org);
1711b8851fccSafresh1    }
1712b8851fccSafresh1
17139f11ffb7Safresh1    return $x, 1 if $acmp == 0;         # result is exact
17149f11ffb7Safresh1    return $x, 0;                       # result is too small
1715b8851fccSafresh1}
1716b8851fccSafresh1
1717*3d61058aSafresh1sub _ilog2 {
1718*3d61058aSafresh1    # calculate int(log2($x))
1719*3d61058aSafresh1
1720*3d61058aSafresh1    # There is virtually nothing to gain from computing this any differently
1721*3d61058aSafresh1    # than _log_int(), but it is important that we don't use the method
1722*3d61058aSafresh1    # inherited from the parent, because that method is very slow for backend
1723*3d61058aSafresh1    # libraries whose internal representation uses base 10.
1724*3d61058aSafresh1
1725*3d61058aSafresh1    my ($c, $x) = @_;
1726*3d61058aSafresh1    ($x, my $is_exact) = $c -> _log_int($x, $c -> _two());
1727*3d61058aSafresh1    return wantarray ? ($x, $is_exact) : $x;
1728*3d61058aSafresh1}
1729*3d61058aSafresh1
1730*3d61058aSafresh1sub _ilog10 {
1731*3d61058aSafresh1    # calculate int(log10($x))
1732*3d61058aSafresh1
1733*3d61058aSafresh1    my ($c, $x) = @_;
1734*3d61058aSafresh1
1735*3d61058aSafresh1    # X == 0 => NaN
1736*3d61058aSafresh1    return if @$x == 1 && $x->[0] == 0;
1737*3d61058aSafresh1
1738*3d61058aSafresh1    # X == 1 => 0 (is exact)
1739*3d61058aSafresh1    if (@$x == 1 && $x->[0] == 1) {
1740*3d61058aSafresh1        @$x = 0;
1741*3d61058aSafresh1        return wantarray ? ($x, 1) : $x;
1742*3d61058aSafresh1    }
1743*3d61058aSafresh1
1744*3d61058aSafresh1    my $x_orig = $c -> _copy($x);
1745*3d61058aSafresh1    my $nm1 = $c -> _len($x) - 1;
1746*3d61058aSafresh1
1747*3d61058aSafresh1    my $xtmp = $c -> _new($nm1);
1748*3d61058aSafresh1    @$x = @$xtmp;
1749*3d61058aSafresh1
1750*3d61058aSafresh1    return $x unless wantarray;
1751*3d61058aSafresh1
1752*3d61058aSafresh1    # See if the original $x is an exact power of 10, in which case all but the
1753*3d61058aSafresh1    # most significan chunks are 0, and the most significant chunk is a power
1754*3d61058aSafresh1    # of 10.
1755*3d61058aSafresh1
1756*3d61058aSafresh1    my $is_pow10 = 1;
1757*3d61058aSafresh1    for my $i (0 .. $#$x_orig - 1) {
1758*3d61058aSafresh1        last unless $is_pow10 = $x_orig->[$i] == 0;
1759*3d61058aSafresh1    }
1760*3d61058aSafresh1    $is_pow10 &&= $x_orig->[-1] == 10**int(0.5 + log($x_orig->[-1]) / log(10));
1761*3d61058aSafresh1
1762*3d61058aSafresh1    return wantarray ? ($x, 1) : $x if $is_pow10;
1763*3d61058aSafresh1    return wantarray ? ($x, 0) : $x;
1764*3d61058aSafresh1}
1765*3d61058aSafresh1
1766*3d61058aSafresh1sub _clog2 {
1767*3d61058aSafresh1    # calculate ceil(log2($x))
1768*3d61058aSafresh1
1769*3d61058aSafresh1    my ($c, $x) = @_;
1770*3d61058aSafresh1
1771*3d61058aSafresh1    # X == 0 => NaN
1772*3d61058aSafresh1
1773*3d61058aSafresh1    return if @$x == 1 && $x->[0] == 0;
1774*3d61058aSafresh1
1775*3d61058aSafresh1    # X == 1 => 0 (is exact)
1776*3d61058aSafresh1
1777*3d61058aSafresh1    if (@$x == 1 && $x->[0] == 1) {
1778*3d61058aSafresh1        @$x = 0;
1779*3d61058aSafresh1        return wantarray ? ($x, 1) : $x;
1780*3d61058aSafresh1    }
1781*3d61058aSafresh1
1782*3d61058aSafresh1    my $base = $c -> _two();
1783*3d61058aSafresh1    my $acmp = $c -> _acmp($x, $base);
1784*3d61058aSafresh1
1785*3d61058aSafresh1    # X == BASE => 1 (is exact)
1786*3d61058aSafresh1
1787*3d61058aSafresh1    if ($acmp == 0) {
1788*3d61058aSafresh1        @$x = 1;
1789*3d61058aSafresh1        return wantarray ? ($x, 1) : $x;
1790*3d61058aSafresh1    }
1791*3d61058aSafresh1
1792*3d61058aSafresh1    # 1 < X < BASE => 0 (is truncated)
1793*3d61058aSafresh1
1794*3d61058aSafresh1    if ($acmp < 0) {
1795*3d61058aSafresh1        @$x = 0;
1796*3d61058aSafresh1        return wantarray ? ($x, 0) : $x;
1797*3d61058aSafresh1    }
1798*3d61058aSafresh1
1799*3d61058aSafresh1    # Compute a guess for the result based on:
1800*3d61058aSafresh1    # $guess = int( length_in_base_10(X) / (log(base) / log(10)) )
1801*3d61058aSafresh1
1802*3d61058aSafresh1    my $len    = $c -> _len($x);
1803*3d61058aSafresh1    my $log    = log(2) / log(10);
1804*3d61058aSafresh1    my $guess  = $c -> _new(int($len / $log));
1805*3d61058aSafresh1    my $x_orig = $c -> _copy($x);
1806*3d61058aSafresh1    @$x = @$guess;
1807*3d61058aSafresh1
1808*3d61058aSafresh1    my $trial = $c -> _pow($c -> _copy($base), $x);
1809*3d61058aSafresh1    $acmp = $c -> _acmp($trial, $x_orig);
1810*3d61058aSafresh1
1811*3d61058aSafresh1    # Too big?
1812*3d61058aSafresh1
1813*3d61058aSafresh1    while ($acmp > 0) {
1814*3d61058aSafresh1        $c -> _div($trial, $base);
1815*3d61058aSafresh1        $c -> _dec($x);
1816*3d61058aSafresh1        $acmp = $c -> _acmp($trial, $x_orig);
1817*3d61058aSafresh1    }
1818*3d61058aSafresh1
1819*3d61058aSafresh1    # Too small?
1820*3d61058aSafresh1
1821*3d61058aSafresh1    while ($acmp < 0) {
1822*3d61058aSafresh1        $c -> _mul($trial, $base);
1823*3d61058aSafresh1        $c -> _inc($x);
1824*3d61058aSafresh1        $acmp = $c -> _acmp($trial, $x_orig);
1825*3d61058aSafresh1    }
1826*3d61058aSafresh1
1827*3d61058aSafresh1    return wantarray ? ($x, 1) : $x if $acmp == 0;      # result is exact
1828*3d61058aSafresh1    return wantarray ? ($x, 0) : $x;                    # result is too small
1829*3d61058aSafresh1}
1830*3d61058aSafresh1
1831*3d61058aSafresh1sub _clog10 {
1832*3d61058aSafresh1    # calculate ceil(log2($x))
1833*3d61058aSafresh1    my ($c, $x) = @_;
1834*3d61058aSafresh1
1835*3d61058aSafresh1    # X == 0 => NaN
1836*3d61058aSafresh1    return if @$x == 1 && $x->[0] == 0;
1837*3d61058aSafresh1
1838*3d61058aSafresh1    # X == 1 => 0 (is exact)
1839*3d61058aSafresh1    if (@$x == 1 && $x->[0] == 1) {
1840*3d61058aSafresh1        @$x = 0;
1841*3d61058aSafresh1        return wantarray ? ($x, 1) : $x;
1842*3d61058aSafresh1    }
1843*3d61058aSafresh1
1844*3d61058aSafresh1    # Get the number of base 10 digits. $n is the desired output, except when
1845*3d61058aSafresh1    # $x is an exact power of 10, in which case $n is 1 too big.
1846*3d61058aSafresh1
1847*3d61058aSafresh1    my $n = $c -> _len($x);
1848*3d61058aSafresh1
1849*3d61058aSafresh1    # See if $x is an exact power of 10, in which case all but the most
1850*3d61058aSafresh1    # significan chunks are 0, and the most significant chunk is a power of 10.
1851*3d61058aSafresh1
1852*3d61058aSafresh1    my $is_pow10 = 1;
1853*3d61058aSafresh1    for my $i (0 .. $#$x - 1) {
1854*3d61058aSafresh1        last unless $is_pow10 = $x->[$i] == 0;
1855*3d61058aSafresh1    }
1856*3d61058aSafresh1    $is_pow10 &&= $x->[-1] == 10**int(0.5 + log($x->[-1]) / log(10));
1857*3d61058aSafresh1
1858*3d61058aSafresh1    $n-- if $is_pow10;
1859*3d61058aSafresh1
1860*3d61058aSafresh1    my $xtmp = $c ->_new($n);
1861*3d61058aSafresh1    @$x = @$xtmp;
1862*3d61058aSafresh1
1863*3d61058aSafresh1    return wantarray ? ($x, 1) : $x if $is_pow10;       # result is exact
1864*3d61058aSafresh1    return wantarray ? ($x, 0) : $x;                    # result is too small
1865*3d61058aSafresh1}
1866*3d61058aSafresh1
1867b8851fccSafresh1# for debugging:
1868b8851fccSafresh1use constant DEBUG => 0;
1869b8851fccSafresh1my $steps = 0;
1870b8851fccSafresh1sub steps { $steps };
1871b8851fccSafresh1
18729f11ffb7Safresh1sub _sqrt {
1873eac174f2Safresh1    # square-root of $x in-place
1874eac174f2Safresh1
1875b8851fccSafresh1    my ($c, $x) = @_;
1876b8851fccSafresh1
18779f11ffb7Safresh1    if (@$x == 1) {
1878b8851fccSafresh1        # fits into one Perl scalar, so result can be computed directly
1879b8851fccSafresh1        $x->[0] = int(sqrt($x->[0]));
1880b8851fccSafresh1        return $x;
1881b8851fccSafresh1    }
1882b8851fccSafresh1
1883eac174f2Safresh1    # Create an initial guess for the square root.
1884eac174f2Safresh1
1885eac174f2Safresh1    my $s;
1886eac174f2Safresh1    if (@$x % 2) {
1887eac174f2Safresh1        $s = [ (0) x ((@$x - 1) / 2), int(sqrt($x->[-1])) ];
1888eac174f2Safresh1    } else {
1889eac174f2Safresh1        $s = [ (0) x ((@$x - 2) / 2), int(sqrt($x->[-2] + $x->[-1] * $BASE)) ];
1890b8851fccSafresh1    }
1891b8851fccSafresh1
1892eac174f2Safresh1    # Newton's method for the square root of y:
1893eac174f2Safresh1    #
1894eac174f2Safresh1    #                      x(n) * x(n) - y
1895eac174f2Safresh1    #     x(n+1) = x(n) - -----------------
1896eac174f2Safresh1    #                          2 * x(n)
1897b8851fccSafresh1
1898eac174f2Safresh1    my $cmp;
1899eac174f2Safresh1    while (1) {
1900eac174f2Safresh1        my $sq = $c -> _mul($c -> _copy($s), $s);
1901eac174f2Safresh1        $cmp = $c -> _acmp($sq, $x);
1902b8851fccSafresh1
1903eac174f2Safresh1        # If x(n)*x(n) > y, compute
1904eac174f2Safresh1        #
1905eac174f2Safresh1        #                      x(n) * x(n) - y
1906eac174f2Safresh1        #     x(n+1) = x(n) - -----------------
1907eac174f2Safresh1        #                          2 * x(n)
1908b8851fccSafresh1
1909eac174f2Safresh1        if ($cmp > 0) {
1910eac174f2Safresh1            my $num = $c -> _sub($c -> _copy($sq), $x);
1911eac174f2Safresh1            my $den = $c -> _mul($c -> _two(), $s);
1912eac174f2Safresh1            my $delta = $c -> _div($num, $den);
1913eac174f2Safresh1            last if $c -> _is_zero($delta);
1914eac174f2Safresh1            $s = $c -> _sub($s, $delta);
1915b8851fccSafresh1        }
1916eac174f2Safresh1
1917eac174f2Safresh1        # If x(n)*x(n) < y, compute
1918eac174f2Safresh1        #
1919eac174f2Safresh1        #                      y - x(n) * x(n)
1920eac174f2Safresh1        #     x(n+1) = x(n) + -----------------
1921eac174f2Safresh1        #                          2 * x(n)
1922eac174f2Safresh1
1923eac174f2Safresh1        elsif ($cmp < 0) {
1924eac174f2Safresh1            my $num = $c -> _sub($c -> _copy($x), $sq);
1925eac174f2Safresh1            my $den = $c -> _mul($c -> _two(), $s);
1926eac174f2Safresh1            my $delta = $c -> _div($num, $den);
1927eac174f2Safresh1            last if $c -> _is_zero($delta);
1928eac174f2Safresh1            $s = $c -> _add($s, $delta);
1929eac174f2Safresh1        }
1930eac174f2Safresh1
1931eac174f2Safresh1        # If x(n)*x(n) = y, we have the exact result.
1932eac174f2Safresh1
1933eac174f2Safresh1        else {
1934eac174f2Safresh1            last;
1935eac174f2Safresh1        }
1936eac174f2Safresh1    }
1937eac174f2Safresh1
1938eac174f2Safresh1    $s = $c -> _dec($s) if $cmp > 0;    # never overshoot
1939eac174f2Safresh1    @$x = @$s;
1940eac174f2Safresh1    return $x;
1941b8851fccSafresh1}
1942b8851fccSafresh1
19439f11ffb7Safresh1sub _root {
19449f11ffb7Safresh1    # Take n'th root of $x in place.
19459f11ffb7Safresh1
1946b8851fccSafresh1    my ($c, $x, $n) = @_;
1947b8851fccSafresh1
19489f11ffb7Safresh1    # Small numbers.
19499f11ffb7Safresh1
1950eac174f2Safresh1    if (@$x == 1) {
1951eac174f2Safresh1        return $x if $x -> [0] == 0 || $x -> [0] == 1;
1952eac174f2Safresh1
1953eac174f2Safresh1        if (@$n == 1) {
1954eac174f2Safresh1            # Result can be computed directly. Adjust initial result for
1955eac174f2Safresh1            # numerical errors, e.g., int(1000**(1/3)) is 2, not 3.
19569f11ffb7Safresh1            my $y = int($x->[0] ** (1 / $n->[0]));
19579f11ffb7Safresh1            my $yp1 = $y + 1;
19589f11ffb7Safresh1            $y = $yp1 if $yp1 ** $n->[0] == $x->[0];
19599f11ffb7Safresh1            $x->[0] = $y;
1960b8851fccSafresh1            return $x;
1961b8851fccSafresh1        }
1962eac174f2Safresh1    }
1963b8851fccSafresh1
19649f11ffb7Safresh1    # If x <= n, the result is always (truncated to) 1.
1965b8851fccSafresh1
19669f11ffb7Safresh1    if ((@$x > 1 || $x -> [0] > 0) &&           # if x is non-zero ...
19679f11ffb7Safresh1        $c -> _acmp($x, $n) <= 0)               # ... and x <= n
1968b8851fccSafresh1    {
1969eac174f2Safresh1        my $one = $c -> _one();
19709f11ffb7Safresh1        @$x = @$one;
19719f11ffb7Safresh1        return $x;
19729f11ffb7Safresh1    }
19739f11ffb7Safresh1
19749f11ffb7Safresh1    # If $n is a power of two, take sqrt($x) repeatedly, e.g., root($x, 4) =
19759f11ffb7Safresh1    # sqrt(sqrt($x)), root($x, 8) = sqrt(sqrt(sqrt($x))).
19769f11ffb7Safresh1
19779f11ffb7Safresh1    my $b = $c -> _as_bin($n);
19789f11ffb7Safresh1    if ($b =~ /0b1(0+)$/) {
19799f11ffb7Safresh1        my $count = length($1);       # 0b100 => len('00') => 2
1980b8851fccSafresh1        my $cnt = $count;             # counter for loop
19819f11ffb7Safresh1        unshift @$x, 0;               # add one element, together with one
1982b8851fccSafresh1                                      #   more below in the loop this makes 2
19839f11ffb7Safresh1        while ($cnt-- > 0) {
19849f11ffb7Safresh1            # 'Inflate' $x by adding one element, basically computing
19859f11ffb7Safresh1            # $x * $BASE * $BASE. This gives us more $BASE_LEN digits for
19869f11ffb7Safresh1            # result since len(sqrt($X)) approx == len($x) / 2.
19879f11ffb7Safresh1            unshift @$x, 0;
19889f11ffb7Safresh1            # Calculate sqrt($x), $x is now one element to big, again. In the
19899f11ffb7Safresh1            # next round we make that two, again.
19909f11ffb7Safresh1            $c -> _sqrt($x);
1991b8851fccSafresh1        }
1992b8851fccSafresh1
19939f11ffb7Safresh1        # $x is now one element too big, so truncate result by removing it.
19949f11ffb7Safresh1        shift @$x;
19959f11ffb7Safresh1
1996b8851fccSafresh1        return $x;
1997b8851fccSafresh1    }
1998b8851fccSafresh1
19999f11ffb7Safresh1    my $DEBUG = 0;
2000b8851fccSafresh1
20019f11ffb7Safresh1    # Now the general case. This works by finding an initial guess. If this
20029f11ffb7Safresh1    # guess is incorrect, a relatively small delta is chosen. This delta is
20039f11ffb7Safresh1    # used to find a lower and upper limit for the correct value. The delta is
20049f11ffb7Safresh1    # doubled in each iteration. When a lower and upper limit is found,
20059f11ffb7Safresh1    # bisection is applied to narrow down the region until we have the correct
20069f11ffb7Safresh1    # value.
20079f11ffb7Safresh1
20089f11ffb7Safresh1    # Split x into mantissa and exponent in base 10, so that
20099f11ffb7Safresh1    #
20109f11ffb7Safresh1    #   x = xm * 10^xe, where 0 < xm < 1 and xe is an integer
20119f11ffb7Safresh1
20129f11ffb7Safresh1    my $x_str = $c -> _str($x);
20139f11ffb7Safresh1    my $xm    = "." . $x_str;
20149f11ffb7Safresh1    my $xe    = length($x_str);
20159f11ffb7Safresh1
20169f11ffb7Safresh1    # From this we compute the base 10 logarithm of x
20179f11ffb7Safresh1    #
20189f11ffb7Safresh1    #   log_10(x) = log_10(xm) + log_10(xe^10)
20199f11ffb7Safresh1    #             = log(xm)/log(10) + xe
20209f11ffb7Safresh1    #
20219f11ffb7Safresh1    # and then the base 10 logarithm of y, where y = x^(1/n)
20229f11ffb7Safresh1    #
20239f11ffb7Safresh1    #   log_10(y) = log_10(x)/n
20249f11ffb7Safresh1
20259f11ffb7Safresh1    my $log10x = log($xm) / log(10) + $xe;
20269f11ffb7Safresh1    my $log10y = $log10x / $c -> _num($n);
20279f11ffb7Safresh1
20289f11ffb7Safresh1    # And from this we compute ym and ye, the mantissa and exponent (in
20299f11ffb7Safresh1    # base 10) of y, where 1 < ym <= 10 and ye is an integer.
20309f11ffb7Safresh1
20319f11ffb7Safresh1    my $ye = int $log10y;
20329f11ffb7Safresh1    my $ym = 10 ** ($log10y - $ye);
20339f11ffb7Safresh1
20349f11ffb7Safresh1    # Finally, we scale the mantissa and exponent to incraese the integer
20359f11ffb7Safresh1    # part of ym, before building the string representing our guess of y.
20369f11ffb7Safresh1
20379f11ffb7Safresh1    if ($DEBUG) {
20389f11ffb7Safresh1        print "\n";
20399f11ffb7Safresh1        print "xm     = $xm\n";
20409f11ffb7Safresh1        print "xe     = $xe\n";
20419f11ffb7Safresh1        print "log10x = $log10x\n";
20429f11ffb7Safresh1        print "log10y = $log10y\n";
20439f11ffb7Safresh1        print "ym     = $ym\n";
20449f11ffb7Safresh1        print "ye     = $ye\n";
20459f11ffb7Safresh1        print "\n";
2046b8851fccSafresh1    }
2047b8851fccSafresh1
20489f11ffb7Safresh1    my $d = $ye < 15 ? $ye : 15;
20499f11ffb7Safresh1    $ym *= 10 ** $d;
20509f11ffb7Safresh1    $ye -= $d;
20519f11ffb7Safresh1
20529f11ffb7Safresh1    my $y_str = sprintf('%.0f', $ym) . "0" x $ye;
20539f11ffb7Safresh1    my $y = $c -> _new($y_str);
20549f11ffb7Safresh1
20559f11ffb7Safresh1    if ($DEBUG) {
20569f11ffb7Safresh1        print "ym     = $ym\n";
20579f11ffb7Safresh1        print "ye     = $ye\n";
20589f11ffb7Safresh1        print "\n";
20599f11ffb7Safresh1        print "y_str  = $y_str (initial guess)\n";
20609f11ffb7Safresh1        print "\n";
2061b8851fccSafresh1    }
2062b8851fccSafresh1
20639f11ffb7Safresh1    # See if our guess y is correct.
2064b8851fccSafresh1
20659f11ffb7Safresh1    my $trial = $c -> _pow($c -> _copy($y), $n);
20669f11ffb7Safresh1    my $acmp  = $c -> _acmp($trial, $x);
20679f11ffb7Safresh1
20689f11ffb7Safresh1    if ($acmp == 0) {
20699f11ffb7Safresh1        @$x = @$y;
2070b8851fccSafresh1        return $x;
2071b8851fccSafresh1    }
20729f11ffb7Safresh1
20739f11ffb7Safresh1    # Find a lower and upper limit for the correct value of y. Start off with a
20749f11ffb7Safresh1    # delta value that is approximately the size of the accuracy of the guess.
20759f11ffb7Safresh1
20769f11ffb7Safresh1    my $lower;
20779f11ffb7Safresh1    my $upper;
20789f11ffb7Safresh1
20799f11ffb7Safresh1    my $delta = $c -> _new("1" . ("0" x $ye));
20809f11ffb7Safresh1    my $two   = $c -> _two();
20819f11ffb7Safresh1
20829f11ffb7Safresh1    if ($acmp < 0) {
20839f11ffb7Safresh1        $lower = $y;
20849f11ffb7Safresh1        while ($acmp < 0) {
20859f11ffb7Safresh1            $upper = $c -> _add($c -> _copy($lower), $delta);
20869f11ffb7Safresh1
20879f11ffb7Safresh1            if ($DEBUG) {
20889f11ffb7Safresh1                print "lower  = $lower\n";
20899f11ffb7Safresh1                print "upper  = $upper\n";
20909f11ffb7Safresh1                print "delta  = $delta\n";
20919f11ffb7Safresh1                print "\n";
20929f11ffb7Safresh1            }
20939f11ffb7Safresh1            $acmp  = $c -> _acmp($c -> _pow($c -> _copy($upper), $n), $x);
20949f11ffb7Safresh1            if ($acmp == 0) {
20959f11ffb7Safresh1                @$x = @$upper;
20969f11ffb7Safresh1                return $x;
20979f11ffb7Safresh1            }
20989f11ffb7Safresh1            $delta = $c -> _mul($delta, $two);
20999f11ffb7Safresh1        }
21009f11ffb7Safresh1    }
21019f11ffb7Safresh1
21029f11ffb7Safresh1    elsif ($acmp > 0) {
21039f11ffb7Safresh1        $upper = $y;
21049f11ffb7Safresh1        while ($acmp > 0) {
21059f11ffb7Safresh1            if ($c -> _acmp($upper, $delta) <= 0) {
21069f11ffb7Safresh1                $lower = $c -> _zero();
21079f11ffb7Safresh1                last;
21089f11ffb7Safresh1            }
21099f11ffb7Safresh1            $lower = $c -> _sub($c -> _copy($upper), $delta);
21109f11ffb7Safresh1
21119f11ffb7Safresh1            if ($DEBUG) {
21129f11ffb7Safresh1                print "lower  = $lower\n";
21139f11ffb7Safresh1                print "upper  = $upper\n";
21149f11ffb7Safresh1                print "delta  = $delta\n";
21159f11ffb7Safresh1                print "\n";
21169f11ffb7Safresh1            }
21179f11ffb7Safresh1            $acmp  = $c -> _acmp($c -> _pow($c -> _copy($lower), $n), $x);
21189f11ffb7Safresh1            if ($acmp == 0) {
21199f11ffb7Safresh1                @$x = @$lower;
21209f11ffb7Safresh1                return $x;
21219f11ffb7Safresh1            }
21229f11ffb7Safresh1            $delta = $c -> _mul($delta, $two);
21239f11ffb7Safresh1        }
21249f11ffb7Safresh1    }
21259f11ffb7Safresh1
21269f11ffb7Safresh1    # Use bisection to narrow down the interval.
21279f11ffb7Safresh1
21289f11ffb7Safresh1    my $one = $c -> _one();
21299f11ffb7Safresh1    {
21309f11ffb7Safresh1
21319f11ffb7Safresh1        $delta = $c -> _sub($c -> _copy($upper), $lower);
21329f11ffb7Safresh1        if ($c -> _acmp($delta, $one) <= 0) {
21339f11ffb7Safresh1            @$x = @$lower;
21349f11ffb7Safresh1            return $x;
21359f11ffb7Safresh1        }
21369f11ffb7Safresh1
21379f11ffb7Safresh1        if ($DEBUG) {
21389f11ffb7Safresh1            print "lower  = $lower\n";
21399f11ffb7Safresh1            print "upper  = $upper\n";
21409f11ffb7Safresh1            print "delta   = $delta\n";
21419f11ffb7Safresh1            print "\n";
21429f11ffb7Safresh1        }
21439f11ffb7Safresh1
21449f11ffb7Safresh1        $delta = $c -> _div($delta, $two);
21459f11ffb7Safresh1        my $middle = $c -> _add($c -> _copy($lower), $delta);
21469f11ffb7Safresh1
21479f11ffb7Safresh1        $acmp  = $c -> _acmp($c -> _pow($c -> _copy($middle), $n), $x);
21489f11ffb7Safresh1        if ($acmp < 0) {
21499f11ffb7Safresh1            $lower = $middle;
21509f11ffb7Safresh1        } elsif ($acmp > 0) {
21519f11ffb7Safresh1            $upper = $middle;
21529f11ffb7Safresh1        } else {
21539f11ffb7Safresh1            @$x = @$middle;
21549f11ffb7Safresh1            return $x;
21559f11ffb7Safresh1        }
21569f11ffb7Safresh1
21579f11ffb7Safresh1        redo;
21589f11ffb7Safresh1    }
21599f11ffb7Safresh1
2160b8851fccSafresh1    $x;
2161b8851fccSafresh1}
2162b8851fccSafresh1
2163b8851fccSafresh1##############################################################################
2164b8851fccSafresh1# binary stuff
2165b8851fccSafresh1
21669f11ffb7Safresh1sub _and {
2167b8851fccSafresh1    my ($c, $x, $y) = @_;
2168b8851fccSafresh1
2169b8851fccSafresh1    # the shortcut makes equal, large numbers _really_ fast, and makes only a
2170b8851fccSafresh1    # very small performance drop for small numbers (e.g. something with less
2171b8851fccSafresh1    # than 32 bit) Since we optimize for large numbers, this is enabled.
21729f11ffb7Safresh1    return $x if $c->_acmp($x, $y) == 0; # shortcut
2173b8851fccSafresh1
21749f11ffb7Safresh1    my $m = $c->_one();
21759f11ffb7Safresh1    my ($xr, $yr);
2176b8851fccSafresh1    my $mask = $AND_MASK;
2177b8851fccSafresh1
21789f11ffb7Safresh1    my $x1 = $c->_copy($x);
21799f11ffb7Safresh1    my $y1 = $c->_copy($y);
21809f11ffb7Safresh1    my $z  = $c->_zero();
21819f11ffb7Safresh1
2182b8851fccSafresh1    use integer;
21839f11ffb7Safresh1    until ($c->_is_zero($x1) || $c->_is_zero($y1)) {
21849f11ffb7Safresh1        ($x1, $xr) = $c->_div($x1, $mask);
21859f11ffb7Safresh1        ($y1, $yr) = $c->_div($y1, $mask);
2186b8851fccSafresh1
21879f11ffb7Safresh1        $c->_add($z, $c->_mul([ 0 + $xr->[0] & 0 + $yr->[0] ], $m));
21889f11ffb7Safresh1        $c->_mul($m, $mask);
2189b8851fccSafresh1    }
2190b8851fccSafresh1
21919f11ffb7Safresh1    @$x = @$z;
21929f11ffb7Safresh1    return $x;
21939f11ffb7Safresh1}
21949f11ffb7Safresh1
21959f11ffb7Safresh1sub _xor {
2196b8851fccSafresh1    my ($c, $x, $y) = @_;
2197b8851fccSafresh1
21989f11ffb7Safresh1    return $c->_zero() if $c->_acmp($x, $y) == 0; # shortcut (see -and)
2199b8851fccSafresh1
22009f11ffb7Safresh1    my $m = $c->_one();
22019f11ffb7Safresh1    my ($xr, $yr);
2202b8851fccSafresh1    my $mask = $XOR_MASK;
2203b8851fccSafresh1
22049f11ffb7Safresh1    my $x1 = $c->_copy($x);
22059f11ffb7Safresh1    my $y1 = $c->_copy($y);      # make copy
22069f11ffb7Safresh1    my $z  = $c->_zero();
22079f11ffb7Safresh1
2208b8851fccSafresh1    use integer;
22099f11ffb7Safresh1    until ($c->_is_zero($x1) || $c->_is_zero($y1)) {
22109f11ffb7Safresh1        ($x1, $xr) = $c->_div($x1, $mask);
22119f11ffb7Safresh1        ($y1, $yr) = $c->_div($y1, $mask);
2212b8851fccSafresh1        # make ints() from $xr, $yr (see _and())
2213b8851fccSafresh1        #$b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
2214b8851fccSafresh1        #$b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
22159f11ffb7Safresh1        #$c->_add($x, $c->_mul($c->_new($xrr ^ $yrr)), $m) );
2216b8851fccSafresh1
22179f11ffb7Safresh1        $c->_add($z, $c->_mul([ 0 + $xr->[0] ^ 0 + $yr->[0] ], $m));
22189f11ffb7Safresh1        $c->_mul($m, $mask);
2219b8851fccSafresh1    }
2220b8851fccSafresh1    # the loop stops when the shorter of the two numbers is exhausted
2221b8851fccSafresh1    # the remainder of the longer one will survive bit-by-bit, so we simple
2222b8851fccSafresh1    # multiply-add it in
22239f11ffb7Safresh1    $c->_add($z, $c->_mul($x1, $m) ) if !$c->_is_zero($x1);
22249f11ffb7Safresh1    $c->_add($z, $c->_mul($y1, $m) ) if !$c->_is_zero($y1);
2225b8851fccSafresh1
22269f11ffb7Safresh1    @$x = @$z;
22279f11ffb7Safresh1    return $x;
2228b8851fccSafresh1}
2229b8851fccSafresh1
22309f11ffb7Safresh1sub _or {
2231b8851fccSafresh1    my ($c, $x, $y) = @_;
2232b8851fccSafresh1
22339f11ffb7Safresh1    return $x if $c->_acmp($x, $y) == 0; # shortcut (see _and)
2234b8851fccSafresh1
22359f11ffb7Safresh1    my $m = $c->_one();
22369f11ffb7Safresh1    my ($xr, $yr);
2237b8851fccSafresh1    my $mask = $OR_MASK;
2238b8851fccSafresh1
22399f11ffb7Safresh1    my $x1 = $c->_copy($x);
22409f11ffb7Safresh1    my $y1 = $c->_copy($y);      # make copy
22419f11ffb7Safresh1    my $z  = $c->_zero();
22429f11ffb7Safresh1
2243b8851fccSafresh1    use integer;
22449f11ffb7Safresh1    until ($c->_is_zero($x1) || $c->_is_zero($y1)) {
22459f11ffb7Safresh1        ($x1, $xr) = $c->_div($x1, $mask);
22469f11ffb7Safresh1        ($y1, $yr) = $c->_div($y1, $mask);
2247b8851fccSafresh1        # make ints() from $xr, $yr (see _and())
2248b8851fccSafresh1        #    $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
2249b8851fccSafresh1        #    $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
22509f11ffb7Safresh1        #    $c->_add($x, $c->_mul(_new( $c, ($xrr | $yrr) ), $m) );
22519f11ffb7Safresh1        $c->_add($z, $c->_mul([ 0 + $xr->[0] | 0 + $yr->[0] ], $m));
22529f11ffb7Safresh1        $c->_mul($m, $mask);
2253b8851fccSafresh1    }
2254b8851fccSafresh1    # the loop stops when the shorter of the two numbers is exhausted
2255b8851fccSafresh1    # the remainder of the longer one will survive bit-by-bit, so we simple
2256b8851fccSafresh1    # multiply-add it in
22579f11ffb7Safresh1    $c->_add($z, $c->_mul($x1, $m) ) if !$c->_is_zero($x1);
22589f11ffb7Safresh1    $c->_add($z, $c->_mul($y1, $m) ) if !$c->_is_zero($y1);
2259b8851fccSafresh1
22609f11ffb7Safresh1    @$x = @$z;
22619f11ffb7Safresh1    return $x;
2262b8851fccSafresh1}
2263b8851fccSafresh1
22649f11ffb7Safresh1sub _as_hex {
2265b8851fccSafresh1    # convert a decimal number to hex (ref to array, return ref to string)
2266b8851fccSafresh1    my ($c, $x) = @_;
2267b8851fccSafresh1
2268eac174f2Safresh1    return "0x0" if @$x == 1 && $x->[0] == 0;
2269b8851fccSafresh1
22709f11ffb7Safresh1    my $x1 = $c->_copy($x);
2271b8851fccSafresh1
2272eac174f2Safresh1    my $x10000 = [ 0x10000 ];
2273eac174f2Safresh1
2274b8851fccSafresh1    my $es = '';
2275eac174f2Safresh1    my $xr;
2276eac174f2Safresh1    until (@$x1 == 1 && $x1->[0] == 0) {        # _is_zero()
22779f11ffb7Safresh1        ($x1, $xr) = $c->_div($x1, $x10000);
2278eac174f2Safresh1        $es = sprintf('%04x', $xr->[0]) . $es;
2279b8851fccSafresh1    }
2280eac174f2Safresh1    #$es = reverse $es;
2281eac174f2Safresh1    $es =~ s/^0*/0x/;
2282eac174f2Safresh1    return $es;
2283b8851fccSafresh1}
2284b8851fccSafresh1
22859f11ffb7Safresh1sub _as_bin {
2286b8851fccSafresh1    # convert a decimal number to bin (ref to array, return ref to string)
2287b8851fccSafresh1    my ($c, $x) = @_;
2288b8851fccSafresh1
2289eac174f2Safresh1    return "0b0" if @$x == 1 && $x->[0] == 0;
2290eac174f2Safresh1
22919f11ffb7Safresh1    my $x1 = $c->_copy($x);
2292b8851fccSafresh1
2293eac174f2Safresh1    my $x10000 = [ 0x10000 ];
2294eac174f2Safresh1
2295b8851fccSafresh1    my $es = '';
2296eac174f2Safresh1    my $xr;
2297eac174f2Safresh1
2298eac174f2Safresh1    until (@$x1 == 1 && $x1->[0] == 0) {        # _is_zero()
22999f11ffb7Safresh1        ($x1, $xr) = $c->_div($x1, $x10000);
2300eac174f2Safresh1        $es = sprintf('%016b', $xr->[0]) . $es;
2301b8851fccSafresh1    }
2302eac174f2Safresh1    $es =~ s/^0*/0b/;
2303eac174f2Safresh1    return $es;
2304b8851fccSafresh1}
2305b8851fccSafresh1
23069f11ffb7Safresh1sub _as_oct {
2307b8851fccSafresh1    # convert a decimal number to octal (ref to array, return ref to string)
2308b8851fccSafresh1    my ($c, $x) = @_;
2309b8851fccSafresh1
2310eac174f2Safresh1    return "00" if @$x == 1 && $x->[0] == 0;
2311b8851fccSafresh1
23129f11ffb7Safresh1    my $x1 = $c->_copy($x);
2313b8851fccSafresh1
2314eac174f2Safresh1    my $x1000 = [ 1 << 15 ];    # 15 bits = 32768 = 0100000
2315eac174f2Safresh1
2316b8851fccSafresh1    my $es = '';
2317b8851fccSafresh1    my $xr;
2318eac174f2Safresh1    until (@$x1 == 1 && $x1->[0] == 0) {        # _is_zero()
23199f11ffb7Safresh1        ($x1, $xr) = $c->_div($x1, $x1000);
2320eac174f2Safresh1        $es = sprintf("%05o", $xr->[0]) . $es;
2321b8851fccSafresh1    }
2322eac174f2Safresh1    $es =~ s/^0*/0/;            # excactly one leading zero
2323eac174f2Safresh1    return $es;
2324b8851fccSafresh1}
2325b8851fccSafresh1
23269f11ffb7Safresh1sub _from_oct {
2327b8851fccSafresh1    # convert a octal number to decimal (string, return ref to array)
2328b8851fccSafresh1    my ($c, $os) = @_;
2329b8851fccSafresh1
2330eac174f2Safresh1    my $m = $c->_new(1 << 30);          # 30 bits at a time (<32 bits!)
2331eac174f2Safresh1    my $d = 10;                         # 10 octal digits at a time
2332b8851fccSafresh1
23339f11ffb7Safresh1    my $mul = $c->_one();
23349f11ffb7Safresh1    my $x = $c->_zero();
2335b8851fccSafresh1
2336b8851fccSafresh1    my $len = int((length($os) - 1) / $d);      # $d digit parts, w/o the '0'
23379f11ffb7Safresh1    my $val;
23389f11ffb7Safresh1    my $i = -$d;
23399f11ffb7Safresh1    while ($len >= 0) {
2340b8851fccSafresh1        $val = substr($os, $i, $d);             # get oct digits
2341b8851fccSafresh1        $val = CORE::oct($val);
23429f11ffb7Safresh1        $i -= $d;
23439f11ffb7Safresh1        $len --;
2344eac174f2Safresh1        my $adder = $c -> _new($val);
23459f11ffb7Safresh1        $c->_add($x, $c->_mul($adder, $mul)) if $val != 0;
23469f11ffb7Safresh1        $c->_mul($mul, $m) if $len >= 0;        # skip last mul
2347b8851fccSafresh1    }
2348b8851fccSafresh1    $x;
2349b8851fccSafresh1}
2350b8851fccSafresh1
23519f11ffb7Safresh1sub _from_hex {
2352b8851fccSafresh1    # convert a hex number to decimal (string, return ref to array)
2353b8851fccSafresh1    my ($c, $hs) = @_;
2354b8851fccSafresh1
23559f11ffb7Safresh1    my $m = $c->_new(0x10000000);       # 28 bit at a time (<32 bit!)
2356eac174f2Safresh1    my $d = 7;                          # 7 hexadecimal digits at a time
23579f11ffb7Safresh1    my $mul = $c->_one();
23589f11ffb7Safresh1    my $x = $c->_zero();
2359b8851fccSafresh1
2360b8851fccSafresh1    my $len = int((length($hs) - 2) / $d); # $d digit parts, w/o the '0x'
23619f11ffb7Safresh1    my $val;
23629f11ffb7Safresh1    my $i = -$d;
23639f11ffb7Safresh1    while ($len >= 0) {
2364b8851fccSafresh1        $val = substr($hs, $i, $d);     # get hex digits
2365b8851fccSafresh1        $val =~ s/^0x// if $len == 0; # for last part only because
2366b8851fccSafresh1        $val = CORE::hex($val);       # hex does not like wrong chars
23679f11ffb7Safresh1        $i -= $d;
23689f11ffb7Safresh1        $len --;
2369eac174f2Safresh1        my $adder = $c->_new($val);
2370b8851fccSafresh1        # if the resulting number was to big to fit into one element, create a
2371b8851fccSafresh1        # two-element version (bug found by Mark Lakata - Thanx!)
23729f11ffb7Safresh1        if (CORE::length($val) > $BASE_LEN) {
23739f11ffb7Safresh1            $adder = $c->_new($val);
2374b8851fccSafresh1        }
23759f11ffb7Safresh1        $c->_add($x, $c->_mul($adder, $mul)) if $val != 0;
23769f11ffb7Safresh1        $c->_mul($mul, $m) if $len >= 0; # skip last mul
2377b8851fccSafresh1    }
2378b8851fccSafresh1    $x;
2379b8851fccSafresh1}
2380b8851fccSafresh1
23819f11ffb7Safresh1sub _from_bin {
2382b8851fccSafresh1    # convert a hex number to decimal (string, return ref to array)
2383b8851fccSafresh1    my ($c, $bs) = @_;
2384b8851fccSafresh1
2385b8851fccSafresh1    # instead of converting X (8) bit at a time, it is faster to "convert" the
2386b8851fccSafresh1    # number to hex, and then call _from_hex.
2387b8851fccSafresh1
2388b8851fccSafresh1    my $hs = $bs;
2389b8851fccSafresh1    $hs =~ s/^[+-]?0b//;                                # remove sign and 0b
2390b8851fccSafresh1    my $l = length($hs);                                # bits
2391b8851fccSafresh1    $hs = '0' x (8 - ($l % 8)) . $hs if ($l % 8) != 0;  # padd left side w/ 0
2392b8851fccSafresh1    my $h = '0x' . unpack('H*', pack ('B*', $hs));      # repack as hex
2393b8851fccSafresh1
2394b8851fccSafresh1    $c->_from_hex($h);
2395b8851fccSafresh1}
2396b8851fccSafresh1
2397b8851fccSafresh1##############################################################################
2398b8851fccSafresh1# special modulus functions
2399b8851fccSafresh1
24009f11ffb7Safresh1sub _modinv {
2401e0680481Safresh1
2402b8851fccSafresh1    # modular multiplicative inverse
2403b8851fccSafresh1    my ($c, $x, $y) = @_;
2404b8851fccSafresh1
2405b8851fccSafresh1    # modulo zero
24069f11ffb7Safresh1    if ($c->_is_zero($y)) {
2407eac174f2Safresh1        return;
2408b8851fccSafresh1    }
2409b8851fccSafresh1
2410b8851fccSafresh1    # modulo one
24119f11ffb7Safresh1    if ($c->_is_one($y)) {
24129f11ffb7Safresh1        return $c->_zero(), '+';
2413b8851fccSafresh1    }
2414b8851fccSafresh1
24159f11ffb7Safresh1    my $u = $c->_zero();
24169f11ffb7Safresh1    my $v = $c->_one();
24179f11ffb7Safresh1    my $a = $c->_copy($y);
24189f11ffb7Safresh1    my $b = $c->_copy($x);
2419b8851fccSafresh1
2420b8851fccSafresh1    # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the result
2421b8851fccSafresh1    # ($u) at the same time. See comments in BigInt for why this works.
2422b8851fccSafresh1    my $q;
2423b8851fccSafresh1    my $sign = 1;
2424b8851fccSafresh1    {
24259f11ffb7Safresh1        ($a, $q, $b) = ($b, $c->_div($a, $b));          # step 1
24269f11ffb7Safresh1        last if $c->_is_zero($b);
2427b8851fccSafresh1
24289f11ffb7Safresh1        my $t = $c->_add(                               # step 2:
24299f11ffb7Safresh1                         $c->_mul($c->_copy($v), $q),   #  t =   v * q
2430b8851fccSafresh1                         $u);                           #      + u
2431b8851fccSafresh1        $u = $v;                                        #  u = v
2432b8851fccSafresh1        $v = $t;                                        #  v = t
2433b8851fccSafresh1        $sign = -$sign;
2434b8851fccSafresh1        redo;
2435b8851fccSafresh1    }
2436b8851fccSafresh1
2437b8851fccSafresh1    # if the gcd is not 1, then return NaN
2438eac174f2Safresh1    return unless $c->_is_one($a);
2439b8851fccSafresh1
2440b8851fccSafresh1    ($v, $sign == 1 ? '+' : '-');
2441b8851fccSafresh1}
2442b8851fccSafresh1
24439f11ffb7Safresh1sub _modpow {
2444b8851fccSafresh1    # modulus of power ($x ** $y) % $z
2445b8851fccSafresh1    my ($c, $num, $exp, $mod) = @_;
2446b8851fccSafresh1
2447b8851fccSafresh1    # a^b (mod 1) = 0 for all a and b
24489f11ffb7Safresh1    if ($c->_is_one($mod)) {
2449b8851fccSafresh1        @$num = 0;
2450b8851fccSafresh1        return $num;
2451b8851fccSafresh1    }
2452b8851fccSafresh1
2453b8851fccSafresh1    # 0^a (mod m) = 0 if m != 0, a != 0
2454b8851fccSafresh1    # 0^0 (mod m) = 1 if m != 0
24559f11ffb7Safresh1    if ($c->_is_zero($num)) {
24569f11ffb7Safresh1        if ($c->_is_zero($exp)) {
2457b8851fccSafresh1            @$num = 1;
2458b8851fccSafresh1        } else {
2459b8851fccSafresh1            @$num = 0;
2460b8851fccSafresh1        }
2461b8851fccSafresh1        return $num;
2462b8851fccSafresh1    }
2463b8851fccSafresh1
24649f11ffb7Safresh1    #  $num = $c->_mod($num, $mod);   # this does not make it faster
2465b8851fccSafresh1
24669f11ffb7Safresh1    my $acc = $c->_copy($num);
24679f11ffb7Safresh1    my $t = $c->_one();
2468b8851fccSafresh1
24699f11ffb7Safresh1    my $expbin = $c->_as_bin($exp);
24709f11ffb7Safresh1    $expbin =~ s/^0b//;
2471b8851fccSafresh1    my $len = length($expbin);
24729f11ffb7Safresh1    while (--$len >= 0) {
24739f11ffb7Safresh1        if (substr($expbin, $len, 1) eq '1') { # is_odd
24749f11ffb7Safresh1            $t = $c->_mul($t, $acc);
24759f11ffb7Safresh1            $t = $c->_mod($t, $mod);
2476b8851fccSafresh1        }
24779f11ffb7Safresh1        $acc = $c->_mul($acc, $acc);
24789f11ffb7Safresh1        $acc = $c->_mod($acc, $mod);
2479b8851fccSafresh1    }
2480b8851fccSafresh1    @$num = @$t;
2481b8851fccSafresh1    $num;
2482b8851fccSafresh1}
2483b8851fccSafresh1
2484b8851fccSafresh1sub _gcd {
2485b8851fccSafresh1    # Greatest common divisor.
2486b8851fccSafresh1
2487b8851fccSafresh1    my ($c, $x, $y) = @_;
2488b8851fccSafresh1
2489b8851fccSafresh1    # gcd(0, 0) = 0
2490b8851fccSafresh1    # gcd(0, a) = a, if a != 0
2491b8851fccSafresh1
2492b8851fccSafresh1    if (@$x == 1 && $x->[0] == 0) {
2493b8851fccSafresh1        if (@$y == 1 && $y->[0] == 0) {
2494b8851fccSafresh1            @$x = 0;
2495b8851fccSafresh1        } else {
2496b8851fccSafresh1            @$x = @$y;
2497b8851fccSafresh1        }
2498b8851fccSafresh1        return $x;
2499b8851fccSafresh1    }
2500b8851fccSafresh1
2501b8851fccSafresh1    # Until $y is zero ...
2502b8851fccSafresh1
2503b8851fccSafresh1    until (@$y == 1 && $y->[0] == 0) {
2504b8851fccSafresh1
2505b8851fccSafresh1        # Compute remainder.
2506b8851fccSafresh1
25079f11ffb7Safresh1        $c->_mod($x, $y);
2508b8851fccSafresh1
2509b8851fccSafresh1        # Swap $x and $y.
2510b8851fccSafresh1
25119f11ffb7Safresh1        my $tmp = $c->_copy($x);
2512b8851fccSafresh1        @$x = @$y;
2513b8851fccSafresh1        $y = $tmp;              # no deref here; that would modify input $y
2514b8851fccSafresh1    }
2515b8851fccSafresh1
2516b8851fccSafresh1    return $x;
2517b8851fccSafresh1}
2518b8851fccSafresh1
2519b8851fccSafresh11;
2520b8851fccSafresh1
2521b8851fccSafresh1=pod
2522b8851fccSafresh1
2523b8851fccSafresh1=head1 NAME
2524b8851fccSafresh1
2525eac174f2Safresh1Math::BigInt::Calc - pure Perl module to support Math::BigInt
2526b8851fccSafresh1
2527b8851fccSafresh1=head1 SYNOPSIS
2528b8851fccSafresh1
25299f11ffb7Safresh1    # to use it with Math::BigInt
25309f11ffb7Safresh1    use Math::BigInt lib => 'Calc';
25319f11ffb7Safresh1
25329f11ffb7Safresh1    # to use it with Math::BigFloat
25339f11ffb7Safresh1    use Math::BigFloat lib => 'Calc';
25349f11ffb7Safresh1
25359f11ffb7Safresh1    # to use it with Math::BigRat
25369f11ffb7Safresh1    use Math::BigRat lib => 'Calc';
2537b8851fccSafresh1
2538eac174f2Safresh1    # explicitly set base length and whether to "use integer"
2539eac174f2Safresh1    use Math::BigInt::Calc base_len => 4, use_int => 1;
2540eac174f2Safresh1    use Math::BigInt lib => 'Calc';
2541eac174f2Safresh1
2542b8851fccSafresh1=head1 DESCRIPTION
2543b8851fccSafresh1
25449f11ffb7Safresh1Math::BigInt::Calc inherits from Math::BigInt::Lib.
25459f11ffb7Safresh1
2546eac174f2Safresh1In this library, the numbers are represented interenally in base B = 10**N,
2547eac174f2Safresh1where N is the largest possible integer that does not cause overflow in the
2548eac174f2Safresh1intermediate computations. The base B elements are stored in an array, with the
2549eac174f2Safresh1least significant element stored in array element zero. There are no leading
2550eac174f2Safresh1zero elements, except a single zero element when the number is zero. For
2551eac174f2Safresh1instance, if B = 10000, the number 1234567890 is represented internally as
2552eac174f2Safresh1[7890, 3456, 12].
2553b8851fccSafresh1
2554eac174f2Safresh1=head1 OPTIONS
2555eac174f2Safresh1
2556eac174f2Safresh1When the module is loaded, it computes the maximum exponent, i.e., power of 10,
2557eac174f2Safresh1that can be used with and without "use integer" in the computations. The default
2558eac174f2Safresh1is to use this maximum exponent. If the combination of the 'base_len' value and
2559eac174f2Safresh1the 'use_int' value exceeds the maximum value, an error is thrown.
2560eac174f2Safresh1
2561eac174f2Safresh1=over 4
2562eac174f2Safresh1
2563eac174f2Safresh1=item base_len
2564eac174f2Safresh1
2565eac174f2Safresh1The base length can be specified explicitly with the 'base_len' option. The
2566eac174f2Safresh1value must be a positive integer.
2567eac174f2Safresh1
2568eac174f2Safresh1    use Math::BigInt::Calc base_len => 4;  # use 10000 as internal base
2569eac174f2Safresh1
2570eac174f2Safresh1=item use_int
2571eac174f2Safresh1
2572eac174f2Safresh1This option is used to specify whether "use integer" should be used in the
2573eac174f2Safresh1internal computations. The value is interpreted as a boolean value, so use 0 or
2574eac174f2Safresh1"" for false and anything else for true. If the 'base_len' is not specified
2575eac174f2Safresh1together with 'use_int', the current value for the base length is used.
2576eac174f2Safresh1
2577eac174f2Safresh1    use Math::BigInt::Calc use_int => 1;   # use "use integer" internally
2578eac174f2Safresh1
2579eac174f2Safresh1=back
2580eac174f2Safresh1
2581eac174f2Safresh1=head1 METHODS
2582eac174f2Safresh1
2583eac174f2Safresh1This overview constains only the methods that are specific to
2584eac174f2Safresh1C<Math::BigInt::Calc>. For the other methods, see L<Math::BigInt::Lib>.
2585eac174f2Safresh1
2586eac174f2Safresh1=over 4
2587eac174f2Safresh1
2588eac174f2Safresh1=item _base_len()
2589eac174f2Safresh1
2590eac174f2Safresh1Specify the desired base length and whether to enable "use integer" in the
2591eac174f2Safresh1computations.
2592eac174f2Safresh1
2593eac174f2Safresh1    Math::BigInt::Calc -> _base_len($base_len, $use_int);
2594eac174f2Safresh1
2595eac174f2Safresh1Note that it is better to specify the base length and whether to use integers as
2596eac174f2Safresh1options when the module is loaded, for example like this
2597eac174f2Safresh1
2598eac174f2Safresh1    use Math::BigInt::Calc base_len => 6, use_int => 1;
2599eac174f2Safresh1
2600eac174f2Safresh1=back
2601b8851fccSafresh1
2602b8851fccSafresh1=head1 SEE ALSO
2603b8851fccSafresh1
26049f11ffb7Safresh1L<Math::BigInt::Lib> for a description of the API.
26059f11ffb7Safresh1
2606eac174f2Safresh1Alternative libraries L<Math::BigInt::FastCalc>, L<Math::BigInt::GMP>,
2607eac174f2Safresh1L<Math::BigInt::Pari>, L<Math::BigInt::GMPz>, and L<Math::BigInt::BitVect>.
26089f11ffb7Safresh1
26099f11ffb7Safresh1Some of the modules that use these libraries L<Math::BigInt>,
26109f11ffb7Safresh1L<Math::BigFloat>, and L<Math::BigRat>.
2611b8851fccSafresh1
2612b8851fccSafresh1=cut
2613