Completed
Pull Request — master (#591)
by thomas
22:54 queued 09:12
created

Math::decodeCompact()   D

Complexity

Conditions 10
Paths 33

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 10.017

Importance

Changes 0
Metric Value
cc 10
eloc 19
c 0
b 0
f 0
nc 33
nop 3
dl 0
loc 29
ccs 17
cts 18
cp 0.9444
crap 10.017
rs 4.8196

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Math;
6
7
use Mdanter\Ecc\Math\GmpMath;
8
use Mdanter\Ecc\Util\NumberSize;
9
10
class Math extends GmpMath
11
{
12
13
    /**
14
     * @param \GMP $integer
15
     * @return bool
16
     */
17 42
    public function isEven(\GMP $integer): bool
18
    {
19 42
        return $this->cmp($this->mod($integer, gmp_init(2)), gmp_init(0)) === 0;
20
    }
21
22
    /**
23
     * @param \GMP $int
24
     * @param \GMP $otherInt
25
     * @return \GMP
26
     */
27 1695
    public function bitwiseOr(\GMP $int, \GMP $otherInt): \GMP
28
    {
29 1695
        return gmp_or($int, $otherInt);
30
    }
31
32
    /**
33
     * Similar to gmp_div_qr, return a tuple containing the
34
     * result and the remainder
35
     *
36
     * @param \GMP $dividend
37
     * @param \GMP $divisor
38
     * @return array
39
     */
40
    public function divQr(\GMP $dividend, \GMP $divisor): array
41
    {
42
        // $div = n / q
43
        $div = $this->div($dividend, $divisor);
44
        // $remainder = n - (n / q) * q
45
        $remainder = $this->sub($dividend, $this->mul($div, $divisor));
46
        return [$div, $remainder];
47
    }
48
49
    /**
50
     * @param int $compact
51
     * @param bool|false $isNegative
52
     * @param bool|false $isOverflow
53
     * @return \GMP
54
     */
55 210
    public function decodeCompact($compact, &$isNegative, &$isOverflow): \GMP
56
    {
57 210
        if ($compact < 0 || $compact > pow(2, 32) - 1) {
58
            throw new \RuntimeException('Compact integer must be 32bit');
59
        }
60
61 210
        $compact = gmp_init($compact, 10);
62 210
        $size = $this->rightShift($compact, 24);
63 210
        $word = $this->bitwiseAnd($compact, gmp_init(0x007fffff, 10));
64 210
        if ($this->cmp($size, gmp_init(3)) <= 0) {
65 13
            $positions = (int) $this->toString($this->mul(gmp_init(8, 10), $this->sub(gmp_init(3, 10), $size)));
66 13
            $word = $this->rightShift($word, $positions);
67
        } else {
68 197
            $positions = (int) $this->toString($this->mul(gmp_init(8, 10), $this->sub($size, gmp_init(3, 10))));
69 197
            $word = $this->leftShift($word, $positions);
70
        }
71
72
        // isNegative: $word !== 0 && $uint32 & 0x00800000 !== 0
73
        // isOverflow: $word !== 0 && (($size > 34) || ($word > 0xff && $size > 33) || ($word > 0xffff && $size > 32))
74 210
        $zero = gmp_init(0);
75 210
        $isNegative = ($this->cmp($word, $zero) !== 0) && ($this->cmp($this->bitwiseAnd($compact, gmp_init(0x00800000)), $zero) === 1);
76 210
        $isOverflow = $this->cmp($word, $zero) !== 0 && (
77 199
                ($this->cmp($size, gmp_init(34, 10)) > 0)
78 198
                || ($this->cmp($word, gmp_init(0xff, 10)) > 0 && $this->cmp($size, gmp_init(33, 10)) > 0)
79 210
                || ($this->cmp($word, gmp_init(0xffff, 10)) > 0 && $this->cmp($size, gmp_init(32, 10)) > 0)
80
            );
81
82 210
        return $word;
83
    }
84
85
    /**
86
     * @param \GMP $integer
87
     * @return \GMP
88
     */
89 18
    public function getLow64(\GMP $integer): \GMP
90
    {
91 18
        $bits = gmp_strval($integer, 2);
92 18
        $bits = substr($bits, 0, 64);
93 18
        $bits = str_pad($bits, 64, '0', STR_PAD_LEFT);
94 18
        return gmp_init($bits, 2);
95
    }
96
97
    /**
98
     * @param \GMP $int
99
     * @param int $byteSize
100
     * @return string
101
     */
102
    public function fixedSizeInt(\GMP $int, int $byteSize): string
103
    {
104
        $two = gmp_init(2);
105
        $maskShift = gmp_pow($two, 8);
106
        $mask = gmp_mul(gmp_init(255), gmp_pow($two, 256));
107
108
        $x = '';
109
        for ($i = $byteSize - 1; $i >= 0; $i--) {
110
            $mask = gmp_div($mask, $maskShift);
111
            $x .= pack('C', gmp_strval(gmp_div(gmp_and($int, $mask), gmp_pow($two, $i * 8)), 10));
112
        }
113
114
        return $x;
115
    }
116
117
    /**
118
     * @param \GMP $integer
119
     * @param bool $fNegative
120
     * @return \GMP
121
     */
122 18
    public function encodeCompact(\GMP $integer, bool $fNegative): \GMP
123
    {
124 18
        if (!is_bool($fNegative)) {
125
            throw new \InvalidArgumentException('CompactInteger::read() - flag must be boolean!');
126
        }
127
128 18
        $size = (int) NumberSize::bnNumBytes($this, $integer);
129 18
        if ($size <= 3) {
130 14
            $compact = $this->leftShift($this->getLow64($integer), (8 * (3 - $size)));
131
        } else {
132 4
            $compact = $this->rightShift($integer, 8 * ($size - 3));
133 4
            $compact = $this->getLow64($compact);
134
        }
135
136 18
        if ($this->cmp($this->bitwiseAnd($compact, gmp_init(0x00800000, 10)), gmp_init(0, 10)) > 0) {
137 1
            $compact = $this->rightShift($compact, 8);
138 1
            $size = $size + 1;
139
        }
140
141 18
        $compact = $this->bitwiseOr($compact, $this->leftShift(gmp_init($size, 10), 24));
142 18
        if ($fNegative && $this->cmp($this->bitwiseAnd($compact, gmp_init(0x007fffff, 10)), gmp_init(0, 10)) > 0) { /// ?
143 2
            $compact = $this->bitwiseOr($compact, gmp_init(0x00800000, 10));
144
        }
145
146 18
        return $compact;
147
    }
148
}
149