Test Failed
Pull Request — master (#127)
by Jordan
06:46
created

BaseConversionProvider   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 71
c 1
b 0
f 0
dl 0
loc 115
rs 10
wmc 20

4 Methods

Rating   Name   Duplication   Size   Complexity  
A convertFromBaseTen() 0 5 1
A _fromBase() 0 18 3
C _toBase() 0 41 12
A convertStringToBaseTen() 0 12 4
1
<?php
2
3
namespace Samsara\Fermat\Provider;
4
5
use Samsara\Fermat\Enums\NumberBase;
6
use Samsara\Fermat\Numbers;
7
use Samsara\Fermat\Types\Base\Interfaces\Numbers\DecimalInterface;
8
9
/**
10
 *
11
 */
12
class BaseConversionProvider
13
{
14
15
    private static array $chars = [
16
        '0',
17
        '1',
18
        '2',
19
        '3',
20
        '4',
21
        '5',
22
        '6',
23
        '7',
24
        '8',
25
        '9',
26
        'A',
27
        'B',
28
        'C',
29
        'D',
30
        'E',
31
        'F'
32
    ];
33
34
    /**
35
     * @param DecimalInterface $number
36
     * @param NumberBase|null $toBase
37
     * @return string
38
     */
39
    public static function convertFromBaseTen(DecimalInterface $number, ?NumberBase $toBase = null): string
40
    {
41
        $base = $toBase ?? $number->getBase();
42
43
        return self::_toBase($number, $base->value);
44
    }
45
46
    /**
47
     * @param string $number
48
     * @param NumberBase $fromBase
49
     * @return string
50
     */
51
    public static function convertStringToBaseTen(string $number, NumberBase $fromBase): string
52
    {
53
        if (str_contains($number, '.')) {
54
            $sign = str_starts_with($number, '-') ? '-' : '';
55
            [$intPart, $decPart] = explode('.', $number);
56
            $intPart = self::_fromBase($intPart, $fromBase->value);
57
            $decPart = strrev(self::_fromBase(strrev($decPart), $fromBase->value));
58
59
            return $sign.$intPart.'.'.$decPart;
60
        } else {
61
            $sign = str_starts_with($number, '-') ? '-' : '';
62
            return $sign.self::_fromBase($number, $fromBase->value);
63
        }
64
    }
65
66
    private static function _toBase(DecimalInterface $input, int $base): string
67
    {
68
        $intPart = '0';
69
        $decPart = '0';
70
        $baseNum = Numbers::make(Numbers::IMMUTABLE, $base, $input->getScale());
71
        $inputInt = Numbers::make(Numbers::IMMUTABLE, $input->getWholePart());
72
        $inputDec = Numbers::make(Numbers::IMMUTABLE, strrev($input->getDecimalPart()));
73
        $runningTotal = Numbers::makeZero();
74
75
        if ($inputInt->isGreaterThan(0)) {
76
            for ($pos = 0; $runningTotal->isLessThan($inputInt); $pos++) {
77
                $basePow = $pos ?
78
                    $baseNum->pow($pos) :
79
                    $baseNum->pow($pos+1);
80
                $intPart = $pos ? $intPart : '';
81
                $mod = $pos ?
82
                    (int)gmp_strval(gmp_div_q($inputInt->getAsBaseTenRealNumber(), $basePow->getAsBaseTenRealNumber())) :
83
                    (int)gmp_strval(gmp_div_r($inputInt->getAsBaseTenRealNumber(), $basePow->getAsBaseTenRealNumber()));
84
                $intPart = self::$chars[$mod] . $intPart;
85
                $runningTotal = $pos ?
86
                    $runningTotal->add($basePow->multiply($mod)) :
87
                    $runningTotal->add($mod);
88
            }
89
        }
90
91
        if ($inputDec->isGreaterThan(0)) {
92
            $runningTotal = Numbers::makeZero();
93
            for ($pos = 0; $runningTotal->isLessThan($decPart); $pos++) {
94
                $basePow = $baseNum->pow($pos);
95
                $decPart = $pos ? $decPart : '';
96
                $mod = $pos ?
97
                    (int)gmp_strval(gmp_div_q($inputDec->getAsBaseTenRealNumber(), $baseNum->pow($pos)->getAsBaseTenRealNumber())) :
98
                    $inputDec->modulo($baseNum->pow($pos))->asInt();
0 ignored issues
show
Bug introduced by
The method modulo() does not exist on Samsara\Fermat\Values\MutableFraction. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

98
                    $inputDec->/** @scrutinizer ignore-call */ 
99
                               modulo($baseNum->pow($pos))->asInt();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method modulo() does not exist on Samsara\Fermat\Values\ImmutableFraction. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

98
                    $inputDec->/** @scrutinizer ignore-call */ 
99
                               modulo($baseNum->pow($pos))->asInt();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
99
                $decPart = self::$chars[$mod] . $decPart;
100
                $runningTotal = $runningTotal->add($basePow->multiply($mod));
101
            }
102
        }
103
104
        $sign = $input->isNegative() ? '-' : '';
105
106
        return $sign.$intPart.'.'.strrev($decPart);
107
    }
108
109
    private static function _fromBase(string $number, int $base): string
110
    {
111
        if (str_starts_with($number, '-')) {
112
            $number = trim($number, '-');
113
        }
114
115
        $output = Numbers::makeZero();
116
        $input = str_split($number);
117
        $input = array_reverse($input);
0 ignored issues
show
Bug introduced by
It seems like $input can also be of type true; however, parameter $array of array_reverse() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
        $input = array_reverse(/** @scrutinizer ignore-type */ $input);
Loading history...
118
        $pos = 0;
119
        $base = Numbers::make(Numbers::IMMUTABLE, $base);
120
121
        foreach ($input as $char) {
122
            $output = $output->add($base->pow($pos)->multiply(array_search($char, self::$chars)));
123
            $pos++;
124
        }
125
126
        return $output->getAsBaseTenRealNumber();
127
    }
128
129
}