UInt64::twosComplementToUnsigned()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 15
rs 10
1
<?php
2
3
/**
4
 * This file is part of the reliforp/reli-prof package.
5
 *
6
 * (c) sji <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Reli\Lib\Integer;
15
16
use function decbin;
17
use function max;
18
use function pack;
19
use function str_pad;
20
use function strlen;
21
use function strrev;
22
use function unpack;
23
24
final class UInt64
25
{
26
    public string $packed_value;
27
28
    public function __construct(
29
        public int $hi,
30
        public int $lo
31
    ) {
32
        $this->packed_value = $this->pack($hi, $lo);
33
    }
34
35
    private function pack(int $hi, int $lo): string
36
    {
37
        return pack('ll', $lo, $hi);
38
    }
39
40
    public function __toString(): string
41
    {
42
        return $this->toString();
43
    }
44
45
    private function addDigitsViaString(string $a, string $b): string
46
    {
47
        $result = '';
48
        $carry = 0;
49
        $a = strrev($a);
50
        $b = strrev($b);
51
52
        $max_len = max(strlen($a), strlen($b));
53
        for ($i = 0; $i < $max_len; $i++) {
54
            $digit_a = (int)($a[$i] ?? 0);
55
            $digit_b = (int)($b[$i] ?? 0);
56
57
            $sum = $digit_a + $digit_b + $carry;
58
            $carry = (int)($sum >= 10);
59
60
            $result .= $sum % 10;
61
        }
62
63
        if ($carry) {
64
            $result .= '1';
65
        }
66
67
        return strrev($result);
68
    }
69
70
    private function twosComplementToUnsigned(string $binary_string): string
71
    {
72
        $binary_string = strrev($binary_string);
73
74
        $result = '0';
75
        $addend = '1';
76
77
        for ($i = 0; $i < strlen($binary_string); $i++) {
78
            if ($binary_string[$i] === '1') {
79
                $result = $this->addDigitsViaString($result, $addend);
80
            }
81
            $addend = $this->addDigitsViaString($addend, $addend);
82
        }
83
84
        return $result;
85
    }
86
87
    private function toString(): string
88
    {
89
        $int = $this->toInt();
90
        $binary_string = decbin($int);
91
        if ($int >= 0) {
92
            $binary_string = str_pad($binary_string, 64, '0', STR_PAD_LEFT);
93
        }
94
95
        return $this->twosComplementToUnsigned($binary_string);
96
    }
97
98
    public function toInt(): int
99
    {
100
        $result = unpack('Presult', $this->packed_value);
101
        if ($result === false) {
102
            throw new \LogicException('unpack failed');
103
        }
104
        return (int)$result['result'];
105
    }
106
107
    public function checkBitSet(int $bit_pos): bool
108
    {
109
        if ($bit_pos >= 32) {
110
            $bit_pos -= 32;
111
            return (bool)($this->hi & (1 << $bit_pos));
112
        }
113
        return (bool)($this->lo & (1 << $bit_pos));
114
    }
115
}
116