aboutsummaryrefslogtreecommitdiff
path: root/openssl-1.1.0h/test/recipes/bc.pl
blob: dbb5842bda7e5822a3c24c4bae1cc0103b968b86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#! /usr/bin/env perl
# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html


use strict;
use warnings;

use Math::BigInt;

sub calc {
    @_ = __adder(@_);
    if (scalar @_ != 1) { return "NaN"; }
    return shift;
}

sub __canonhex {
    my ($sign, $hex) = (shift =~ /^([+\-]?)(.*)$/);
    $hex = "0x".$hex if $hex !~ /^0x/;
    return $sign.$hex;
}

sub __adder {
    @_ = __multiplier(@_);
    while (scalar @_ > 1 && $_[1] =~ /^[\+\-]$/) {
	my $operand1 = Math::BigInt->from_hex(__canonhex(shift));
	my $operator = shift;
	@_ = __multiplier(@_);
	my $operand2 = Math::BigInt->from_hex(__canonhex(shift));
	if ($operator eq "+") {
	    $operand1->badd($operand2);
	} elsif ($operator eq "-") {
	    $operand1->bsub($operand2);
	} else {
	    die "SOMETHING WENT AWFULLY WRONG";
	}
	unshift @_, $operand1->as_hex();
    }
    return @_;
}

sub __multiplier {
    @_ = __power(@_);
    while (scalar @_ > 1 && $_[1] =~ /^[\*\/%]$/) {
	my $operand1 = Math::BigInt->from_hex(__canonhex(shift));
	my $operator = shift;
	@_ = __power(@_);
	my $operand2 = Math::BigInt->from_hex(__canonhex(shift));
	if ($operator eq "*") {
	    $operand1->bmul($operand2);
	} elsif ($operator eq "/") {
	    # Math::BigInt->bdiv() is documented to do floored division,
	    # i.e. 1 / -4 = -1, while bc and OpenSSL BN_div do truncated
	    # division, i.e. 1 / -4 = 0.  We need to make the operation
	    # work like OpenSSL's BN_div to be able to verify.
	    my $neg = ($operand1->is_neg()
		       ? !$operand2->is_neg() : $operand2->is_neg());
	    $operand1->babs();
	    $operand2->babs();
	    $operand1->bdiv($operand2);
	    if ($neg) { $operand1->bneg(); }
	} elsif ($operator eq "%") {
	    # Here's a bit of a quirk...
	    # With OpenSSL's BN, as well as bc, the result of -10 % 3 is -1
	    # while Math::BigInt, the result is 2.
	    # The latter is mathematically more correct, but...
	    my $o1isneg = $operand1->is_neg();
	    $operand1->babs();
	    # Math::BigInt does something different with a negative modulus,
	    # while OpenSSL's BN and bc treat it like a positive number...
	    $operand2->babs();
	    $operand1->bmod($operand2);
	    if ($o1isneg) { $operand1->bneg(); }
	} else {
	    die "SOMETHING WENT AWFULLY WRONG";
	}
	unshift @_, $operand1->as_hex();
    }
    return @_;
}

sub __power {
    @_ = __paren(@_);
    while (scalar @_ > 1 && $_[1] eq "^") {
	my $operand1 = Math::BigInt->from_hex(__canonhex(shift));
	shift;
	@_ = __paren(@_);
	my $operand2 = Math::BigInt->from_hex(__canonhex(shift));
	$operand1->bpow($operand2);
	unshift @_, $operand1->as_hex();
    }
    return @_;
}

# returns array ( $result, @remaining )
sub __paren {
    if (scalar @_ > 0 && $_[0] eq "(") {
	shift;
	my @result = __adder(@_);
	if (scalar @_ == 0 || $_[0] ne ")") {
	    return ("NaN");
	}
	shift;
	return @result;
    }
    return @_;
}

1;