| 
 | 1 | +<?php  | 
 | 2 | + | 
 | 3 | +/*  | 
 | 4 | + * Author: Mizanur rahman <[email protected]>  | 
 | 5 | + *  | 
 | 6 | + * Actual algorithm from: Steven Skiena  | 
 | 7 | + */  | 
 | 8 | + | 
 | 9 | + | 
 | 10 | +define("MAXDIGITS", 1000);  | 
 | 11 | +define("PLUS", 1);  | 
 | 12 | +define("MINUS", -1);  | 
 | 13 | + | 
 | 14 | + | 
 | 15 | +class BigInteger  | 
 | 16 | +{  | 
 | 17 | + | 
 | 18 | +    public $digits;  | 
 | 19 | +    public $lastDigit;  | 
 | 20 | +    public $signBit;  | 
 | 21 | + | 
 | 22 | +    public function __construct(string $number)  | 
 | 23 | +    {  | 
 | 24 | +        if (strlen($number) == 0) throw new Exception("Number must be present");  | 
 | 25 | + | 
 | 26 | +        if (!preg_match("/(-)?(\d)*/", $number)) {  | 
 | 27 | +            throw new Exception("Only valid digits are allowed");  | 
 | 28 | +        }  | 
 | 29 | + | 
 | 30 | +        $this->signBit = PLUS;  | 
 | 31 | + | 
 | 32 | +        if ($number[0] == "-")  | 
 | 33 | +            $this->signBit = MINUS;  | 
 | 34 | + | 
 | 35 | +        for ($i = 0; $i < MAXDIGITS; $i++) $this->digits[$i] = 0;  | 
 | 36 | + | 
 | 37 | +        $this->lastDigit = -1;  | 
 | 38 | + | 
 | 39 | +        for ($t = strlen($number); $t > 0; $t--) {  | 
 | 40 | +            $this->lastDigit++;  | 
 | 41 | +            $this->digits[$this->lastDigit] = $number[$t - 1];  | 
 | 42 | +        }  | 
 | 43 | + | 
 | 44 | +        if ($number == "0") $this->lastDigit = 0;  | 
 | 45 | +    }  | 
 | 46 | +}  | 
 | 47 | + | 
 | 48 | +function printBigInteger(BigInteger &$n)  | 
 | 49 | +{  | 
 | 50 | +    if ($n->signBit == MINUS) echo "- ";  | 
 | 51 | +    for ($i = $n->lastDigit; $i >= 0; $i--)  | 
 | 52 | +        echo $n->digits[$i];  | 
 | 53 | +    echo "\n";  | 
 | 54 | +}  | 
 | 55 | + | 
 | 56 | +function intToBigInteger(string $s, BigInteger &$n)  | 
 | 57 | +{  | 
 | 58 | +    if ($s >= 0)  | 
 | 59 | +        $n->signBit = PLUS;  | 
 | 60 | +    else  | 
 | 61 | +        $n->signBit = MINUS;  | 
 | 62 | + | 
 | 63 | +    for ($i = 0; $i < MAXDIGITS; $i++) $n->digits[$i] = 0;  | 
 | 64 | + | 
 | 65 | +    $n->lastDigit = -1;  | 
 | 66 | + | 
 | 67 | +    $t = abs($s);  | 
 | 68 | + | 
 | 69 | +    while ($t > 0) {  | 
 | 70 | +        $n->lastDigit++;  | 
 | 71 | +        $n->digits[$n->lastDigit] = ($t % 10);  | 
 | 72 | +        $t /= 10;  | 
 | 73 | +    }  | 
 | 74 | + | 
 | 75 | +    if ($s == 0) $n->lastDigit = 0;  | 
 | 76 | +}  | 
 | 77 | + | 
 | 78 | +function initBigInteger(BigInteger &$n)  | 
 | 79 | +{  | 
 | 80 | +    intToBigInteger(0, $n);  | 
 | 81 | +}  | 
 | 82 | + | 
 | 83 | +function zeroJustify(BigInteger &$n)  | 
 | 84 | +{  | 
 | 85 | +    while (($n->lastDigit > 0) && ($n->digits[$n->lastDigit] == 0))  | 
 | 86 | +        $n->lastDigit--;  | 
 | 87 | + | 
 | 88 | +    if (($n->lastDigit == 0) && ($n->digits[0] == 0))  | 
 | 89 | +        $n->signBit = PLUS;    /* hack to avoid -0 */  | 
 | 90 | +}  | 
 | 91 | + | 
 | 92 | + | 
 | 93 | +function digitShift(BigInteger $n, int $d)        /* multiply n by 10^d */  | 
 | 94 | +{  | 
 | 95 | +    if (($n->lastDigit == 0) && ($n->digits[0] == 0)) return;  | 
 | 96 | + | 
 | 97 | +    for ($i = $n->lastDigit; $i >= 0; $i--)  | 
 | 98 | +        $n->digits[$i + $d] = $n->digits[$i];  | 
 | 99 | + | 
 | 100 | +    for ($i = 0; $i < $d; $i++) $n->digits[$i] = 0;  | 
 | 101 | + | 
 | 102 | +    $n->lastDigit = $n->lastDigit + $d;  | 
 | 103 | +}  | 
 | 104 | + | 
 | 105 | +function compareBigInteger(BigInteger &$a, BigInteger &$b)  | 
 | 106 | +{  | 
 | 107 | + | 
 | 108 | +    if (($a->signBit == MINUS) && ($b->signBit == PLUS)) return (PLUS);  | 
 | 109 | +    if (($a->signBit == PLUS) && ($b->signBit == MINUS)) return (MINUS);  | 
 | 110 | + | 
 | 111 | +    if ($b->lastDigit > $a->lastDigit) return (PLUS * $a->signBit);  | 
 | 112 | +    if ($a->lastDigit > $b->lastDigit) return (MINUS * $a->signBit);  | 
 | 113 | + | 
 | 114 | +    for ($i = $a->lastDigit; $i >= 0; $i--) {  | 
 | 115 | +        if ($a->digits[$i] > $b->digits[$i]) return (MINUS * $a->signBit);  | 
 | 116 | +        if ($b->digits[$i] > $a->digits[$i]) return (PLUS * $a->signBit);  | 
 | 117 | +    }  | 
 | 118 | + | 
 | 119 | +    return 0;  | 
 | 120 | +}  | 
 | 121 | + | 
 | 122 | +function addBigInteger(BigInteger &$a, BigInteger &$b, BigInteger &$c)  | 
 | 123 | +{  | 
 | 124 | +    initBigInteger($c);  | 
 | 125 | + | 
 | 126 | +    if ($a->signBit == $b->signBit)  | 
 | 127 | +        $c->signBit = $a->signBit;  | 
 | 128 | +    else {  | 
 | 129 | +        if ($a->signBit == MINUS) {  | 
 | 130 | +            $a->signBit = PLUS;  | 
 | 131 | +            subtractBigInteger($b, $a, $c);  | 
 | 132 | +            $a->signBit = MINUS;  | 
 | 133 | +        } else {  | 
 | 134 | +            $b->signBit = PLUS;  | 
 | 135 | +            subtractBigInteger($a, $b, $c);  | 
 | 136 | +            $b->signBit = MINUS;  | 
 | 137 | +        }  | 
 | 138 | +        return;  | 
 | 139 | +    }  | 
 | 140 | + | 
 | 141 | +    $c->lastDigit = max($a->lastDigit, $b->lastDigit) + 1;  | 
 | 142 | +    $carry = 0;  | 
 | 143 | + | 
 | 144 | + | 
 | 145 | +    for ($i = 0; $i <= $c->lastDigit; $i++) {  | 
 | 146 | +        $c->digits[$i] = ($carry + $a->digits[$i] + $b->digits[$i]) % 10;  | 
 | 147 | +        $carry = intval(($carry + $a->digits[$i] + $b->digits[$i]) / 10);  | 
 | 148 | +    }  | 
 | 149 | + | 
 | 150 | +    zeroJustify($c);  | 
 | 151 | +}  | 
 | 152 | + | 
 | 153 | +function subtractBigInteger(BigInteger &$a, BigInteger &$b, BigInteger &$c)  | 
 | 154 | +{  | 
 | 155 | +    initBigInteger($c);  | 
 | 156 | + | 
 | 157 | +    if (($a->signBit == MINUS) || ($b->signBit == MINUS)) {  | 
 | 158 | +        $b->signBit = -1 * $b->signBit;  | 
 | 159 | +        addBigInteger($a, $b, $c);  | 
 | 160 | +        $b->signBit = -1 * $b->signBit;  | 
 | 161 | +        return;  | 
 | 162 | +    }  | 
 | 163 | + | 
 | 164 | +    if (compareBigInteger($a, $b) == PLUS) {  | 
 | 165 | +        subtractBigInteger($b, $a, $c);  | 
 | 166 | +        $c->signBit = MINUS;  | 
 | 167 | +        return;  | 
 | 168 | +    }  | 
 | 169 | + | 
 | 170 | +    $c->lastDigit = max($a->lastDigit, $b->lastDigit);  | 
 | 171 | +    $borrow = 0;  | 
 | 172 | + | 
 | 173 | +    for ($i = 0; $i <= ($c->lastDigit); $i++) {  | 
 | 174 | +        $v = ($a->digits[$i] - $borrow - $b->digits[$i]);  | 
 | 175 | +        if ($a->digits[$i] > 0)  | 
 | 176 | +            $borrow = 0;  | 
 | 177 | +        if ($v < 0) {  | 
 | 178 | +            $v = $v + 10;  | 
 | 179 | +            $borrow = 1;  | 
 | 180 | +        }  | 
 | 181 | + | 
 | 182 | +        $c->digits[$i] = $v % 10;  | 
 | 183 | +    }  | 
 | 184 | + | 
 | 185 | +    zeroJustify($c);  | 
 | 186 | +}  | 
 | 187 | + | 
 | 188 | + | 
 | 189 | +function multiplyBigInteger(BigInteger &$a, BigInteger &$b, BigInteger &$c)  | 
 | 190 | +{  | 
 | 191 | +    $numbers = [];  | 
 | 192 | +    for ($i = 0; $i <= $b->lastDigit; $i++) {  | 
 | 193 | +        $carry = 0;  | 
 | 194 | +        $tmp = clone $c;  | 
 | 195 | +        $tmp->lastDigit = 0;  | 
 | 196 | +        for ($j = 0; $j <= $a->lastDigit; $j++) {  | 
 | 197 | +            $tmp->digits[$tmp->lastDigit++] = ($carry + $a->digits[$j] * $b->digits[$i]) % 10;  | 
 | 198 | +            $carry = intval(($carry + $a->digits[$j] * $b->digits[$i]) / 10);  | 
 | 199 | + | 
 | 200 | +        }  | 
 | 201 | +        if ($carry > 0) {  | 
 | 202 | +            $tmp->digits[$tmp->lastDigit] = $carry;  | 
 | 203 | +        }  | 
 | 204 | + | 
 | 205 | +        digitShift($tmp, $i);  | 
 | 206 | +        zeroJustify($tmp);  | 
 | 207 | +        $numbers[] = $tmp;  | 
 | 208 | +    }  | 
 | 209 | + | 
 | 210 | +    foreach ($numbers as $number) {  | 
 | 211 | +        $tmp = clone $c;  | 
 | 212 | +        addBigInteger($number, $tmp, $c);  | 
 | 213 | +    }  | 
 | 214 | + | 
 | 215 | +    $c->signBit = $a->signBit * $b->signBit;  | 
 | 216 | + | 
 | 217 | +    zeroJustify($c);  | 
 | 218 | +}  | 
 | 219 | + | 
 | 220 | + | 
 | 221 | +function divideBigInteger(BigInteger &$a, BigInteger &$b, BigInteger &$c)  | 
 | 222 | +{  | 
 | 223 | + | 
 | 224 | +    $row = clone $c;  | 
 | 225 | +    $tmp = clone $c;  | 
 | 226 | + | 
 | 227 | +    $c->signBit = $a->signBit * $b->signBit;  | 
 | 228 | + | 
 | 229 | +    $asign = $a->signBit;  | 
 | 230 | +    $bsign = $b->signBit;  | 
 | 231 | + | 
 | 232 | +    $a->signBit = PLUS;  | 
 | 233 | +    $b->signBit = PLUS;  | 
 | 234 | + | 
 | 235 | +    $c->lastDigit = $a->lastDigit;  | 
 | 236 | + | 
 | 237 | +    for ($i = $a->lastDigit; $i >= 0; $i--) {  | 
 | 238 | +        digitShift($row, 1);  | 
 | 239 | +        $row->digits[0] = $a->digits[$i];  | 
 | 240 | +        $c->digits[$i] = 0;  | 
 | 241 | +        while (compareBigInteger($row, $b) != PLUS) {  | 
 | 242 | +            $c->digits[$i]++;  | 
 | 243 | +            subtractBigInteger($row, $b, $tmp);  | 
 | 244 | +            $row = clone $tmp;  | 
 | 245 | +        }  | 
 | 246 | +    }  | 
 | 247 | + | 
 | 248 | +    zeroJustify($c);  | 
 | 249 | + | 
 | 250 | +    $a->signBit = $asign;  | 
 | 251 | +    $b->signBit = $bsign;  | 
 | 252 | +}  | 
 | 253 | + | 
 | 254 | +function factorial (int $n, BigInteger &$c) {  | 
 | 255 | + | 
 | 256 | +    intToBigInteger("1", $c);  | 
 | 257 | +      | 
 | 258 | +    if($n > 0) {  | 
 | 259 | +        for($i = 1;$i<=$n;$i++) {  | 
 | 260 | +            $tmp = clone $c;  | 
 | 261 | +            $number = new BigInteger($i);  | 
 | 262 | +            $result = new BigInteger("0");  | 
 | 263 | +            multiplyBigInteger($number, $tmp, $result);  | 
 | 264 | +            $c = $result;  | 
 | 265 | +        }  | 
 | 266 | +    }  | 
 | 267 | +    zeroJustify($c);  | 
 | 268 | +}  | 
 | 269 | + | 
 | 270 | + | 
 | 271 | +$first = new BigInteger("12345678");  | 
 | 272 | +$second = new BigInteger("2222");  | 
 | 273 | +$result = new BigInteger("0");  | 
 | 274 | + | 
 | 275 | +printBigInteger($first);  | 
 | 276 | +printBigInteger($second);  | 
 | 277 | + | 
 | 278 | +//divideBigInteger($first, $second, $result);  | 
 | 279 | +$start = microtime(false);  | 
 | 280 | +factorial(100, $result);  | 
 | 281 | +$end = microtime(false);  | 
 | 282 | +printBigInteger($result);  | 
 | 283 | + | 
 | 284 | + | 
 | 285 | + | 
0 commit comments