Skip to content

Commit 9a7ee9b

Browse files
committed
[Anizoptera] Math component - simple arbitrary precision math for big integers (gmp, bcmath and native realizations)
1 parent 98b9a92 commit 9a7ee9b

11 files changed

+697
-3
lines changed

BigMath.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace Aza\Components\Math;
4+
use Aza\Components\Math\BigMath\BCMath;
5+
use Aza\Components\Math\BigMath\GMPMath;
6+
use Aza\Components\Math\BigMath\PHPMath;
7+
8+
/**
9+
* Simple math for big integers
10+
*
11+
* @project Anizoptera CMF
12+
* @package system.math
13+
* @subpackage BigMath
14+
* @author Amal Samally <amal.samally at gmail.com>
15+
* @license MIT
16+
*/
17+
abstract class BigMath
18+
{
19+
/**
20+
* Get an instance of the big math class
21+
*
22+
* This is NOT a singleton. It simply loads the proper strategy
23+
* given the current server configuration
24+
*
25+
* @return BigMath A big math instance
26+
*/
27+
public static function createFromServerConfiguration()
28+
{
29+
//@codeCoverageIgnoreStart
30+
if (extension_loaded('bcmath')) {
31+
return new BCMath();
32+
} else if (extension_loaded('gmp')) {
33+
return new GMPMath();
34+
}
35+
return new PHPMath();
36+
//@codeCoverageIgnoreEnd
37+
}
38+
39+
40+
/**
41+
* Add two numbers together
42+
*
43+
* @param string $left The left argument
44+
* @param string $right The right argument
45+
*
46+
* @return string A base-10 string of the sum of the two arguments
47+
*/
48+
abstract public function add($left, $right);
49+
50+
/**
51+
* Subtract two numbers
52+
*
53+
* @param string $left The left argument
54+
* @param string $right The right argument
55+
*
56+
* @return string A base-10 string of the difference of the two arguments
57+
*/
58+
abstract public function subtract($left, $right);
59+
}

BigMath/BCMath.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Aza\Components\Math\BigMath;
4+
use Aza\Components\Math\BigMath;
5+
6+
/**
7+
* Simple math for big integers using bcmath
8+
*
9+
* @project Anizoptera CMF
10+
* @package system.math
11+
* @subpackage BigMath
12+
* @author Amal Samally <amal.samally at gmail.com>
13+
* @license MIT
14+
*/
15+
class BCMath extends BigMath
16+
{
17+
/**
18+
* {@inheritdoc}
19+
*/
20+
public function add($left, $right)
21+
{
22+
return bcadd($left, $right, 0);
23+
}
24+
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function subtract($left, $right)
29+
{
30+
return bcsub($left, $right, 0);
31+
}
32+
}

BigMath/GMPMath.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Aza\Components\Math\BigMath;
4+
use Aza\Components\Math\BigMath;
5+
6+
/**
7+
* Simple math for big integers using GMP
8+
*
9+
* @project Anizoptera CMF
10+
* @package system.math
11+
* @subpackage BigMath
12+
* @author Amal Samally <amal.samally at gmail.com>
13+
* @license MIT
14+
*/
15+
class GMPMath extends BigMath
16+
{
17+
/**
18+
* {@inheritdoc}
19+
*/
20+
public function add($left, $right)
21+
{
22+
return gmp_strval(gmp_add($left, $right));
23+
}
24+
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function subtract($left, $right)
29+
{
30+
return gmp_strval(gmp_sub($left, $right));
31+
}
32+
}

BigMath/PHPMath.php

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
namespace Aza\Components\Math\BigMath;
4+
use Aza\Components\Math\BigMath;
5+
use Aza\Components\Math\NumeralSystem;
6+
7+
/**
8+
* Simple math for big integers implemented in PHP
9+
*
10+
* @project Anizoptera CMF
11+
* @package system.math
12+
* @subpackage BigMath
13+
* @author Amal Samally <amal.samally at gmail.com>
14+
* @license MIT
15+
*/
16+
class PHPMath extends BigMath
17+
{
18+
/**
19+
* {@inheritdoc}
20+
*/
21+
public function add($left, $right)
22+
{
23+
if (!$left) {
24+
return $right;
25+
} else if (!$right) {
26+
return $left;
27+
}
28+
29+
if ('-' === $left[0] && '-' === $right[0]) {
30+
$negative = '-';
31+
$left = substr($left, 1);
32+
$right = substr($right, 1);
33+
} else if ($left[0] === '-') {
34+
return $this->subtract($right, substr($left, 1));
35+
} else if ($right[0] === '-') {
36+
return $this->subtract($left, substr($right, 1));
37+
} else {
38+
$negative = '';
39+
}
40+
$left = $this->normalize($left);
41+
$right = $this->normalize($right);
42+
43+
$result = NumeralSystem::convert(
44+
$this->addBinary($left, $right),
45+
NumeralSystem::BINARY,
46+
10
47+
);
48+
49+
return $negative . $result;
50+
}
51+
52+
/**
53+
* {@inheritdoc}
54+
*/
55+
public function subtract($left, $right)
56+
{
57+
if (!$right) {
58+
return $left;
59+
} else if ('-' === $right[0]) {
60+
return $this->add($left, substr($right, 1));
61+
} else if ('-' === $left[0]) {
62+
return '-' . $this->add(substr($left, 1), $right);
63+
}
64+
65+
$left = $this->normalize($left);
66+
$right = $this->normalize($right);
67+
$results = $this->subtractBinary($left, $right);
68+
69+
$result = NumeralSystem::convert(
70+
$results[1],
71+
NumeralSystem::BINARY,
72+
10
73+
);
74+
75+
return '-0' === ($result = $results[0] . $result)
76+
? '0'
77+
: $result;
78+
}
79+
80+
/**
81+
* Add two binary strings together
82+
*
83+
* @param string $left The left argument
84+
* @param string $right The right argument
85+
*
86+
* @return string The binary result
87+
*/
88+
protected function addBinary($left, $right)
89+
{
90+
$len = max(strlen($left), strlen($right));
91+
$left = str_pad($left, $len, chr(0), STR_PAD_LEFT);
92+
$right = str_pad($right, $len, chr(0), STR_PAD_LEFT);
93+
$result = '';
94+
$carry = 0;
95+
for ($i = 0; $i < $len; $i++) {
96+
$sum = ord($left[$len - $i - 1])
97+
+ ord($right[$len - $i - 1])
98+
+ $carry;
99+
$result .= chr($sum % 256);
100+
$carry = $sum >> 8;
101+
}
102+
while ($carry) {
103+
$result .= chr($carry % 256);
104+
$carry >>= 8;
105+
}
106+
107+
return strrev($result);
108+
}
109+
110+
/**
111+
* Subtract two binary strings using 256's compliment
112+
*
113+
* @param string $left The left argument
114+
* @param string $right The right argument
115+
*
116+
* @return string The binary result
117+
*/
118+
protected function subtractBinary($left, $right)
119+
{
120+
$len = max(strlen($left), strlen($right));
121+
$left = str_pad($left, $len, chr(0), STR_PAD_LEFT);
122+
$right = str_pad($right, $len, chr(0), STR_PAD_LEFT);
123+
$right = $this->compliment($right);
124+
$result = $this->addBinary($left, $right);
125+
if (strlen($result) > $len) {
126+
// Positive Result
127+
$carry = substr($result, 0, -1 * $len);
128+
$result = substr($result, strlen($carry));
129+
130+
return array(
131+
'',
132+
$this->addBinary($result, $carry)
133+
);
134+
}
135+
136+
return array('-', $this->compliment($result));
137+
}
138+
139+
/**
140+
* Take the 256 base compliment
141+
*
142+
* @param string $string The binary string to compliment
143+
*
144+
* @return string The complimented string
145+
*/
146+
protected function compliment($string)
147+
{
148+
$result = '';
149+
$len = strlen($string);
150+
for ($i = 0; $i < $len; $i++) {
151+
$result .= chr(255 - ord($string[$i]));
152+
}
153+
154+
return $result;
155+
}
156+
157+
/**
158+
* Transform a string number into a binary string using base autodetection
159+
*
160+
* @param string $string The string to transform
161+
*
162+
* @return string The binary transformed number
163+
*/
164+
protected function normalize($string)
165+
{
166+
return NumeralSystem::convert(
167+
$string,
168+
10,
169+
NumeralSystem::BINARY
170+
);
171+
}
172+
}
173+
174+
// Initialize binary alphabet in converter
175+
NumeralSystem::initBinary();

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ and universal convertor between positional numeral systems (supported bases from
1313
Features:
1414

1515
* Functionality to work with large numbers (integers, floats) with arbitrary precision (requires [BCMath](http://php.net/bcmath)). Can work with floats (E notation too!) and without loosing precision (as far as possible). It supports all basic arithmetic, exponentiation, square root, modulus, bit shift, rounding, comparison, and some other operations.
16+
* Very simple math for big integers (only integers!, has a native PHP realization and can use BCMath or GMP for speedup).
1617
* Universal number (and huge number!) convertor between positional numeral systems (supported bases from 2 to 62 inclusive, and systems with custom alphabet; pure PHP realisation, but can use [GMP](http://php.net/gmp) and [core PHP](http://php.net/math) functions for speed optimization). Negative and huge integers are supported.
17-
* Convenient, fully documented and test covered API
18+
* Convenient, fully documented and test covered API.
1819

1920
AzaMath is a part of [Anizoptera CMF](https://github.com/Anizoptera), written by [Amal Samally](http://azagroup.ru/#amal) (amal.samally at gmail.com) and [AzaGroup](http://azagroup.ru/) team.
2021
Arbitrary precision arithmetic part is partially based on [Moontoast Math Library](https://github.com/moontoast/math).
@@ -27,7 +28,7 @@ Requirements
2728

2829
* PHP 5.3.3 (or later);
2930
* [BCMath (Binary Calculator Arbitrary Precision Mathematics)](http://php.net/bcmath) - Required only to work with arbitrary precision arithmetic operations;
30-
* [GMP (GNU Multiple Precision)](http://php.net/gmp) - Recommended. Used to speed up number systems conversions and (in future) arbitrary precision arithmetic operations;
31+
* [GMP (GNU Multiple Precision)](http://php.net/gmp) - Recommended. Used to speed up number systems conversions and arbitrary precision arithmetic operations;
3132

3233

3334
Installation

Tests/BigMath/BCMathTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Aza\Components\Math\Tests\BigMath;
4+
use Aza\Components\Math\BigMath\BCMath;
5+
use Aza\Components\Math\Tests\BigMathTest;
6+
7+
/**
8+
* BCMath testing
9+
*
10+
* @project Anizoptera CMF
11+
* @package system.math
12+
* @subpackage BigMath
13+
* @author Amal Samally <amal.samally at gmail.com>
14+
* @license MIT
15+
*
16+
* @requires extension bcmath
17+
*
18+
* @covers Aza\Components\Math\BigMath\BCMath
19+
*/
20+
class BCMathTest extends BigMathTest
21+
{
22+
/**
23+
* {@inheritdoc}
24+
*/
25+
public static function setUpBeforeClass()
26+
{
27+
self::$instance = new BCMath;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function testInstance()
34+
{
35+
$this->assertTrue(self::$instance instanceof BCMath);
36+
}
37+
}

Tests/BigMath/GMPMathTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Aza\Components\Math\Tests\BigMath;
4+
use Aza\Components\Math\BigMath\GMPMath;
5+
use Aza\Components\Math\Tests\BigMathTest;
6+
7+
/**
8+
* GMPMath testing
9+
*
10+
* @project Anizoptera CMF
11+
* @package system.math
12+
* @subpackage BigMath
13+
* @author Amal Samally <amal.samally at gmail.com>
14+
* @license MIT
15+
*
16+
* @requires extension gmp
17+
*
18+
* @covers Aza\Components\Math\BigMath\GMPMath
19+
*/
20+
class GMPMathTest extends BigMathTest
21+
{
22+
/**
23+
* {@inheritdoc}
24+
*/
25+
public static function setUpBeforeClass()
26+
{
27+
self::$instance = new GMPMath;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function testInstance()
34+
{
35+
$this->assertTrue(self::$instance instanceof GMPMath);
36+
}
37+
}

0 commit comments

Comments
 (0)