Failed Conditions
Push — PHPSecLib_Rid ( ab345c...1a2f9f )
by Florent
10:59
created

BigInteger::divide()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
c 5
b 1
f 0
dl 0
loc 13
rs 9.4285
cc 2
eloc 7
nc 2
nop 1
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Util;
13
14
final class BigInteger
15
{
16
    /**
17
     * Holds the BigInteger's value.
18
     *
19
     * @var resource
20
     */
21
    private $value;
22
23
    /**
24
     * Converts base-10 and binary strings (base-256) to BigIntegers.
25
     *
26
     * @param $x base-10 number or base-$base number if $base set.
27
     * @param int $base
28
     */
29
    public function __construct($x = 0, $base = 10)
30
    {
31
        if(is_resource($x) && get_resource_type($x) == 'GMP integer') {
32
            $this->value = $x;
33
            
34
            return;
35
        }
36
        
37
        $this->value = gmp_init(0);
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init(0) of type object<GMP> is incompatible with the declared type resource of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
38
39
        // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
40
        // '0' is the only value like this per http://php.net/empty
41
        if (empty($x) && (abs($base) !== 256 || $x !== '0')) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $x (integer) and '0' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
42
            return;
43
        }
44
45
        if (256 === $base) {
46
            $x = '0x'.bin2hex($x);
47
            $base = 16;
48
        }
49
        $this->value = gmp_init($x, $base);
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init($x, $base) of type object<GMP> is incompatible with the declared type resource of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
50
    }
51
52
    /**
53
     * Converts a BigInteger to a byte string (eg. base-256).
54
     *
55
     * @return string
56
     */
57
    public function toBytes()
58
    {
59
        if (gmp_cmp($this->value, gmp_init(0)) === 0) {
60
            return '';
61
        }
62
63
        $temp = gmp_strval(gmp_abs($this->value), 16);
64
        $temp = (strlen($temp) & 1) ? '0'.$temp : $temp;
65
        $temp = hex2bin($temp);
66
67
        return ltrim($temp, chr(0));
68
    }
69
70
    /**
71
     * Adds two BigIntegers.
72
     *
73
     * @param \Jose\Util\BigInteger $y
74
     *
75
     * @return \Jose\Util\BigInteger
76
     */
77
    public function add(BigInteger $y)
78
    {
79
        $value = gmp_add($this->value, $y->value);
80
        
81
        return new static($value);
0 ignored issues
show
Documentation introduced by
$value is of type resource, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
82
    }
83
84
    /**
85
     * Subtracts two BigIntegers.
86
     *
87
     * @param \Jose\Util\BigInteger $y
88
     *
89
     * @return \Jose\Util\BigInteger
90
     */
91
    public function subtract(BigInteger $y)
92
    {
93
        $value = gmp_sub($this->value, $y->value);
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
94
        
95
        return new static();
96
    }
97
98
    /**
99
     * Multiplies two BigIntegers.
100
     *
101
     * @param \Jose\Util\BigInteger $x
102
     *
103
     * @return \Jose\Util\BigInteger
104
     */
105
    public function multiply(BigInteger $x)
106
    {
107
        $value = gmp_mul($this->value, $x->value);
108
        
109
        return new static($value);
0 ignored issues
show
Documentation introduced by
$value is of type resource, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
110
    }
111
112
    /**
113
     * Divides two BigIntegers.
114
     * 
115
     * @param \Jose\Util\BigInteger $y
116
     *
117
     * @return \Jose\Util\BigInteger[]
118
     */
119
    public function divide(BigInteger $y)
120
    {
121
        list($quotient_value, $remainder_value) = gmp_div_qr($this->value, $y->value);
122
        
123
        $quotient = new static($quotient_value);
124
        $remainder = new static($remainder_value);
125
126
        if (gmp_sign($remainder->value) < 0) {
127
            $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
128
        }
129
130
        return [$quotient, $remainder];
131
    }
132
133
    /**
134
     * Performs modular exponentiation.
135
     *
136
     * @param \Jose\Util\BigInteger $e
137
     * @param \Jose\Util\BigInteger $n
138
     *
139
     * @return \Jose\Util\BigInteger
140
     */
141
    public function modPow(BigInteger $e, BigInteger $n)
142
    {
143
        $n = $n->abs();
144
145
        if ($e->compare(new static()) < 0) {
146
            $e = $e->abs();
147
148
            $temp = $this->modInverse($n);
149
            if ($temp === false) {
150
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\BigInteger::modPow of type Jose\Util\BigInteger.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
151
            }
152
153
            return $temp->modPow($e, $n);
154
        }
155
156
        $value = gmp_powm($this->value, $e->value, $n->value);
157
        
158
        return new static($value);
0 ignored issues
show
Documentation introduced by
$value is of type resource, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
159
    }
160
161
    /**
162
     * Calculates modular inverses.
163
     *
164
     * @param \Jose\Util\BigInteger $n
165
     *
166
     * @return \Jose\Util\BigInteger|bool
167
     */
168
    public function modInverse(BigInteger $n)
169
    {
170
        $value = gmp_invert($this->value, $n->value);
171
        
172
        return false === $value ? false : new static($value);
0 ignored issues
show
Documentation introduced by
$value is of type resource, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
173
    }
174
175
    /**
176
     * Absolute value.
177
     *
178
     * @return \Jose\Util\BigInteger
179
     */
180
    public function abs()
181
    {
182
        $value = gmp_abs($this->value);
183
        
184
        return new static($value);
0 ignored issues
show
Documentation introduced by
$value is of type resource, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
185
    }
186
187
    /**
188
     * Compares two numbers.
189
     *
190
     * @param \Jose\Util\BigInteger $y
191
     *
192
     * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
193
     */
194
    public function compare(BigInteger $y)
195
    {
196
        return gmp_cmp($this->value, $y->value);
197
    }
198
199
    /**
200
     * Logical Left Shift.
201
     *
202
     * @param int $shift
203
     *
204
     * @return \Jose\Util\BigInteger
205
     *
206
     */
207
    public function bitwise_leftShift($shift)
208
    {
209
        $two = gmp_init('2');
210
        $value = gmp_mul($this->value, gmp_pow($two, $shift));
211
        
212
        return new static($value);
0 ignored issues
show
Documentation introduced by
$value is of type resource, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
213
    }
214
215
    /**
216
     * Generates a random BigInteger.
217
     *
218
     * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
219
     *
220
     * @param int $size
221
     *
222
     * @return \Jose\Util\BigInteger
223
     */
224
    private static function _random_number_helper($size)
225
    {
226
        return new static(random_bytes($size), 256);
227
    }
228
229
    /**
230
     * Generate a random number.
231
     *
232
     * @param \Jose\Util\BigInteger $min
233
     * @param \Jose\Util\BigInteger $max
234
     *
235
     * @return \Jose\Util\BigInteger
236
     */
237
    public static function random(BigInteger $min, BigInteger $max)
238
    {
239
        $compare = $max->compare($min);
240
241
        if (!$compare) {
242
            return $min;
243
        } elseif ($compare < 0) {
244
            $temp = $max;
245
            $max = $min;
246
            $min = $temp;
247
        }
248
249
        $one = new static('1');
250
251
        $max = $max->subtract($min->subtract($one));
252
        $size = strlen(ltrim($max->toBytes(), chr(0)));
253
        
254
        $random_max = new static(chr(1).str_repeat("\0", $size), 256);
255
        $random = self::_random_number_helper($size);
256
257
        list($max_multiple) = $random_max->divide($max);
258
        $max_multiple = $max_multiple->multiply($max);
259
260
        while ($random->compare($max_multiple) >= 0) {
261
            $random = $random->subtract($max_multiple);
262
            $random_max = $random_max->subtract($max_multiple);
263
            $random = $random->bitwise_leftShift(8);
264
            $random = $random->add(self::_random_number_helper(1));
265
            $random_max = $random_max->bitwise_leftShift(8);
266
            list($max_multiple) = $random_max->divide($max);
267
            $max_multiple = $max_multiple->multiply($max);
268
        }
269
        list(, $random) = $random->divide($max);
270
271
        return $random->add($min);
272
    }
273
    
274
}
275