@@ -24,7 +24,7 @@ use warnings;
24
24
use Carp qw< carp croak > ;
25
25
use Scalar::Util qw< blessed refaddr > ;
26
26
27
- our $VERSION = ' 2.003003 ' ;
27
+ our $VERSION = ' 2.003004 ' ;
28
28
$VERSION =~ tr / _// d;
29
29
30
30
require Exporter;
@@ -2339,10 +2339,33 @@ sub bdiv {
2339
2339
# At this point, both the numerator and denominator are finite numbers, and
2340
2340
# the denominator (divisor) is non-zero.
2341
2341
2342
- # Division might return a non-integer result, so upgrade unconditionally, if
2343
- # upgrading is enabled.
2342
+ # Division in scalar context might return a non-integer result, so upgrade
2343
+ # if upgrading is enabled. In list context, we return the quotient and the
2344
+ # remainder, which are both integers, so upgrading is not necessary.
2345
+
2346
+ if ($upgrade && !$wantarray ) {
2347
+ my ($quo , $rem );
2348
+
2349
+ if ($wantarray ) {
2350
+ ($quo , $rem ) = $upgrade -> bdiv($x , $y , @r );
2351
+ } else {
2352
+ $quo = $upgrade -> bdiv($x , $y , @r );
2353
+ }
2344
2354
2345
- return $upgrade -> bdiv($x , $y , @r ) if defined $upgrade ;
2355
+ if ($quo -> is_int()) {
2356
+ $quo = $quo -> as_int();
2357
+ %$x = %$quo ;
2358
+ } else {
2359
+ %$x = %$quo ;
2360
+ bless $x , $upgrade ;
2361
+ }
2362
+
2363
+ if ($wantarray && $rem -> is_int()) {
2364
+ $rem = $rem -> as_int();
2365
+ }
2366
+
2367
+ return $wantarray ? ($x , $rem ) : $x ;
2368
+ }
2346
2369
2347
2370
$r [3] = $y ; # no push!
2348
2371
@@ -3214,69 +3237,13 @@ sub bnok {
3214
3237
}
3215
3238
3216
3239
sub buparrow {
3217
- my $a = shift ;
3218
- my $y = $a -> uparrow(@_ );
3219
- $a -> {value } = $y -> {value };
3220
- return $a ;
3240
+ my ($class , $a , $n , $b , @r ) = objectify(3, @_ );
3241
+ $a -> bhyperop($n + 2, $b , @r );
3221
3242
}
3222
3243
3223
3244
sub uparrow {
3224
- # Knuth's up-arrow notation buparrow(a, n, b)
3225
- #
3226
- # The following is a simple, recursive implementation of the up-arrow
3227
- # notation, just to show the idea. Such implementations cause "Deep
3228
- # recursion on subroutine ..." warnings, so we use a faster, non-recursive
3229
- # algorithm below with @_ as a stack.
3230
- #
3231
- # sub buparrow {
3232
- # my ($a, $n, $b) = @_;
3233
- # return $a ** $b if $n == 1;
3234
- # return $a * $b if $n == 0;
3235
- # return 1 if $b == 0;
3236
- # return buparrow($a, $n - 1, buparrow($a, $n, $b - 1));
3237
- # }
3238
-
3239
- my ($a , $b , $n ) = @_ ;
3240
- my $class = ref $a ;
3241
- croak(" a must be non-negative" ) if $a < 0;
3242
- croak(" n must be non-negative" ) if $n < 0;
3243
- croak(" b must be non-negative" ) if $b < 0;
3244
-
3245
- while (@_ >= 3) {
3246
-
3247
- # return $a ** $b if $n == 1;
3248
-
3249
- if ($_ [-2] == 1) {
3250
- my ($a , $n , $b ) = splice @_ , -3;
3251
- push @_ , $a ** $b ;
3252
- next ;
3253
- }
3254
-
3255
- # return $a * $b if $n == 0;
3256
-
3257
- if ($_ [-2] == 0) {
3258
- my ($a , $n , $b ) = splice @_ , -3;
3259
- push @_ , $a * $b ;
3260
- next ;
3261
- }
3262
-
3263
- # return 1 if $b == 0;
3264
-
3265
- if ($_ [-1] == 0) {
3266
- splice @_ , -3;
3267
- push @_ , $class -> bone();
3268
- next ;
3269
- }
3270
-
3271
- # return buparrow($a, $n - 1, buparrow($a, $n, $b - 1));
3272
-
3273
- my ($a , $n , $b ) = splice @_ , -3;
3274
- push @_ , ($a , $n - 1,
3275
- $a , $n , $b - 1);
3276
-
3277
- }
3278
-
3279
- pop @_ ;
3245
+ my ($class , $a , $n , $b , @r ) = objectify(3, @_ );
3246
+ $a -> hyperop($n + 2, $b , @r );
3280
3247
}
3281
3248
3282
3249
sub backermann {
@@ -3333,6 +3300,140 @@ sub ackermann {
3333
3300
$n ;
3334
3301
}
3335
3302
3303
+ sub bhyperop {
3304
+ my ($class , $a , $n , $b , @r ) = objectify(3, @_ );
3305
+
3306
+ my $tmp = $a -> hyperop($n , $b );
3307
+ $a -> {value } = $tmp -> {value };
3308
+ return $a -> round(@r );
3309
+ }
3310
+
3311
+ sub hyperop {
3312
+ my ($class , $a , $n , $b , @r ) = objectify(3, @_ );
3313
+
3314
+ croak(" a must be non-negative" ) if $a < 0;
3315
+ croak(" n must be non-negative" ) if $n < 0;
3316
+ croak(" b must be non-negative" ) if $b < 0;
3317
+
3318
+ # The following is a non-recursive implementation of the hyperoperator,
3319
+ # with special cases handled for speed.
3320
+
3321
+ my @stack = ($a , $n , $b );
3322
+ while (@stack > 1) {
3323
+ my ($a , $n , $b ) = splice @stack , -3;
3324
+
3325
+ # Special cases for $b
3326
+
3327
+ if ($b == 2 && $a == 2) {
3328
+ push @stack , $n == 0 ? Math::BigInt -> new(" 3" )
3329
+ : Math::BigInt -> new(" 4" );
3330
+ next ;
3331
+ }
3332
+
3333
+ if ($b == 1) {
3334
+ if ($n == 0) {
3335
+ push @stack , Math::BigInt -> new(" 2" );
3336
+ next ;
3337
+ }
3338
+ if ($n == 1) {
3339
+ push @stack , $a + 1;
3340
+ next ;
3341
+ }
3342
+ push @stack , $a ;
3343
+ next ;
3344
+ }
3345
+
3346
+ if ($b == 0) {
3347
+ if ($n == 1) {
3348
+ push @stack , $a ;
3349
+ next ;
3350
+ }
3351
+ if ($n == 2) {
3352
+ push @stack , Math::BigInt -> bzero();
3353
+ next ;
3354
+ }
3355
+ push @stack , Math::BigInt -> bone();
3356
+ next ;
3357
+ }
3358
+
3359
+ # Special cases for $a
3360
+
3361
+ if ($a == 0) {
3362
+ if ($n == 0) {
3363
+ push @stack , $b + 1;
3364
+ next ;
3365
+ }
3366
+ if ($n == 1) {
3367
+ push @stack , $b ;
3368
+ next ;
3369
+ }
3370
+ if ($n == 2) {
3371
+ push @stack , Math::BigInt -> bzero();
3372
+ next ;
3373
+ }
3374
+ if ($n == 3) {
3375
+ push @stack , $b == 0 ? Math::BigInt -> bone()
3376
+ : Math::BigInt -> bzero();
3377
+ next ;
3378
+ }
3379
+ push @stack , $b -> is_odd() ? Math::BigInt -> bzero()
3380
+ : Math::BigInt -> bone();
3381
+ next ;
3382
+ }
3383
+
3384
+ if ($a == 1) {
3385
+ if ($n == 0 || $n == 1) {
3386
+ push @stack , $b + 1;
3387
+ next ;
3388
+ }
3389
+ if ($n == 2) {
3390
+ push @stack , $b ;
3391
+ next ;
3392
+ }
3393
+ push @stack , Math::BigInt -> bone();
3394
+ next ;
3395
+ }
3396
+
3397
+ # Special cases for $n
3398
+
3399
+ if ($n == 4) { # tetration
3400
+ if ($b == 0) {
3401
+ push @stack , Math::BigInt -> bone();
3402
+ next ;
3403
+ }
3404
+ my $y = $a ;
3405
+ $y = $a ** $y for 2 .. $b ;
3406
+ push @stack , $y ;
3407
+ next ;
3408
+ }
3409
+
3410
+ if ($n == 3) { # exponentiation
3411
+ push @stack , $a ** $b ;
3412
+ next ;
3413
+ }
3414
+
3415
+ if ($n == 2) { # multiplication
3416
+ push @stack , $a * $b ;
3417
+ next ;
3418
+ }
3419
+
3420
+ if ($n == 1) { # addition
3421
+ push @stack , $a + $b ;
3422
+ next ;
3423
+ }
3424
+
3425
+ if ($n == 0) { # succession
3426
+ push @stack , $b + 1;
3427
+ next ;
3428
+ }
3429
+
3430
+ push @stack , $a , $n - 1, $a , $n , $b - 1;
3431
+ }
3432
+
3433
+ $a = pop @stack ;
3434
+ return $a -> round(@r );
3435
+ }
3436
+
3336
3437
sub bsin {
3337
3438
my ($class , $x , @r ) = ref ($_ [0]) ? (ref ($_ [0]), @_ ) : objectify(1, @_ );
3338
3439
@@ -6613,6 +6714,7 @@ Math::BigInt - arbitrary size integer math package
6613
6714
$x->bclog10(); # log19($x) rounded up to nearest int
6614
6715
$x->bnok($y); # x over y (binomial coefficient n over k)
6615
6716
$x->buparrow($n, $y); # Knuth's up-arrow notation
6717
+ $x->bhyperop($n, $y); # n'th hyperoprator
6616
6718
$x->backermann($y); # the Ackermann function
6617
6719
$x->bsin(); # sine
6618
6720
$x->bcos(); # cosine
@@ -7634,7 +7736,43 @@ integer representing the number of up-arrows. $n = 0 gives multiplication, $n =
7634
7736
1 gives exponentiation, $n = 2 gives tetration, $n = 3 gives hexation etc. The
7635
7737
following illustrates the relation between the first values of $n.
7636
7738
7637
- See L<https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation> .
7739
+ The buparrow() method is equivalent to the L<bhyperop()> method with an offset
7740
+ of two. The following two give the same result:
7741
+
7742
+ $x -> buparrow($n, $b);
7743
+ $x -> bhyperop($n + 2, $b);
7744
+
7745
+ See also L</bhyperop()> ,
7746
+ L<https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation> .
7747
+
7748
+ =item bhyperop()
7749
+
7750
+ =item hyperop()
7751
+
7752
+ $a -> bhyperop($n, $b); # modifies $a
7753
+ $x = $a -> hyperop($n, $b); # does not modify $a
7754
+
7755
+ H_n(a, b) = a[n]b is the I<n > th hyperoperator,
7756
+
7757
+ n = 0 : succession (b + 1)
7758
+ n = 1 : addition (a + b)
7759
+ n = 2 : multiplication (a * b)
7760
+ n = 3 : exponentiation (a ** b)
7761
+ n = 4 : tetration (a ** a ** ... ** a) (b occurrences of a)
7762
+ ...
7763
+
7764
+ / b+1 if n = 0
7765
+ | a if n = 1 and b = 0
7766
+ H_n(a, b) = a[n]b = | 0 if n = 2 and b = 0
7767
+ | 1 if n >= 3 and b = 0
7768
+ \ H_(n-1)(a, H_n(a, b-1)) otherwise
7769
+
7770
+ Note that the result can be a very large number, even for small operands. Also
7771
+ note that the backend library C<Math::BigInt::GMP > silently returns the
7772
+ incorrect result when the numbers are larger than it can handle. It is better
7773
+ to use C<Math::BigInt::GMPz > or C<Math::BigInt::Pari > .
7774
+
7775
+ See also L</buparrow()> , L<https://en.wikipedia.org/wiki/Hyperoperation> .
7638
7776
7639
7777
=item backermann()
7640
7778
0 commit comments