1: <?php
2: namespace NGS;
3:
4: require_once(__DIR__.'/Utils.php');
5:
6: use NGS\Utils;
7:
8: 9: 10: 11: 12: 13: 14: 15:
16: class BigDecimal
17: {
18:
19: const DEFAULT_SCALE = 20;
20:
21:
22: protected $value;
23:
24:
25: protected $scale;
26:
27: 28: 29: 30: 31: 32:
33: public function __construct($value=0, $scale=null)
34: {
35: if ($value instanceof \NGS\BigDecimal) {
36: $this->setScale($scale!==null ? $scale : $value->scale);
37: $this->setValue($value->value);
38: }
39: else {
40: $this->setScale($scale!==null ? $scale : self::DEFAULT_SCALE);
41: $this->setValue($value);
42: }
43: }
44:
45: 46: 47: 48: 49: 50: 51: 52:
53: public static function toArray(array $items, $allowNullValues=false)
54: {
55: $results = array();
56: try {
57: foreach ($items as $key => $val) {
58: if ($allowNullValues && $val===null) {
59: $results[] = null;
60: } elseif ($val === null) {
61: throw new \InvalidArgumentException('Null value found in provided array');
62: } elseif (!$val instanceof \NGS\BigDecimal) {
63: $results[] = new \NGS\BigDecimal($val);
64: } else {
65: $results[] = $val;
66: }
67: }
68: }
69: catch(\Exception $e) {
70: throw new \InvalidArgumentException('Element at index '.$key.' could not be converted to BigDecimal!', 42, $e);
71: }
72: return $results;
73: }
74:
75: 76: 77: 78: 79: 80: 81: 82: 83:
84: public static function toArrayWithScale(array $items, $scale, $allowNullValues=false)
85: {
86: $results = array();
87: try {
88: foreach ($items as $key => $val) {
89: if ($allowNullValues && $val===null) {
90: $results[] = null;
91: } elseif ($val === null) {
92: throw new InvalidArgumentException('Null value found in provided array');
93: } else {
94: $results[] = new \NGS\BigDecimal($val, $scale);
95: }
96: }
97: }
98: catch(\Exception $e) {
99: throw new \InvalidArgumentException('Element at index '.$key.' could not be converted to BigDecimal!', 42, $e);
100: }
101: return $results;
102: }
103:
104: public static function round($number, $scale=0)
105: {
106: $fix = '5';
107: for ($i=0;$i<$scale;$i++)
108: $fix="0{$fix}";
109:
110: $number = bcadd($number, "0.$fix", $scale+1);
111: var_dump($number);
112: return bcdiv($number, "1.0", $scale);
113: }
114:
115: 116: 117: 118: 119:
120: protected function setScale($scale)
121: {
122: if (!is_int($scale)) {
123: throw new \InvalidArgumentException('BigDecimal scale was not int, type was: "'.Utils::getType($scale).'"');
124: }
125: elseif ($scale<0) {
126: throw new \InvalidArgumentException('BigDecimal scale cannot be negative value: "'.Utils::getType($scale).'"');
127: }
128: $this->scale = $scale;
129: }
130:
131: 132: 133:
134: protected function setValue($value)
135: {
136: if ($value === null) {
137: throw new \InvalidArgumentException('BigDecimal value cannot be null');
138: }
139: elseif (is_string($value)) {
140: if (!filter_var($value, FILTER_VALIDATE_INT)
141: && !preg_match('/^[-+]?\\d+([.]\\d+)?$/u', $value)) {
142: throw new \InvalidArgumentException('Invalid characters in BigDecimal constructor string: '.$value);
143: }
144: $this->value = bcadd($value, 0, $this->scale);
145: }
146: elseif (is_int($value)) {
147: $this->value = bcadd($value, 0, $this->scale);
148: }
149: elseif (is_float($value)) {
150: $this->value = bcadd($value, 0, $this->scale);
151: }
152: else {
153: throw new \InvalidArgumentException('Invalid type for BigDecimal value, type was: "'.Utils::getType($value).'"');
154: }
155: }
156:
157: 158: 159: 160: 161:
162: public function getValue()
163: {
164: return $this->value;
165: }
166:
167: 168: 169: 170: 171:
172: public function getScale()
173: {
174: return $this->scale;
175: }
176:
177: 178: 179:
180: public function __get($name)
181: {
182: if($name==='value') {
183: return $this->value;
184: }
185: else if($name==='scale') {
186: return $this->scale;
187: }
188: else {
189: throw new \InvalidArgumentException('Cannot get undefined property "'.$name.'"');
190: }
191: }
192:
193: 194: 195: 196: 197:
198: public function __toString()
199: {
200: return $this->value;
201: }
202:
203: 204: 205: 206: 207: 208:
209: public function toStringWith($scale)
210: {
211: return $this->format($scale);
212: }
213:
214: 215: 216: 217: 218: 219:
220: public function format($scale)
221: {
222: return number_format($this->value, $scale);
223: }
224:
225:
226:
227: protected function _add(BigDecimal $other)
228: {
229: return new BigDecimal(bcadd($this->value, $other->value, $this->scale), $this->scale);
230: }
231:
232: 233: 234: 235: 236: 237:
238: public function add($other)
239: {
240: return $this->_add(new BigDecimal($other, $this->scale));
241: }
242:
243: protected function _sub(BigDecimal $other)
244: {
245: return new BigDecimal(bcsub($this->value, $other->value, $this->scale), $this->scale);
246: }
247:
248: 249: 250: 251: 252: 253:
254: public function sub($other)
255: {
256: return $this->_sub(new BigDecimal($other, $this->scale));
257: }
258:
259:
260:
261: protected function _comp(BigDecimal $other)
262: {
263: return bccomp($this->value, $other->value, $this->scale);
264: }
265:
266: 267: 268: 269: 270: 271: 272:
273: public function comp($other)
274: {
275: return $this->_comp(new BigDecimal($other, $this->scale));
276: }
277:
278: protected function _gt(BigDecimal $other)
279: {
280: return $this->comp($other) > 0;
281: }
282:
283: 284: 285: 286: 287: 288:
289: public function gt($other)
290: {
291: return $this->_gt(new BigDecimal($other, $this->scale));
292: }
293:
294: protected function _gte(BigDecimal $other) {
295: return $this->comp($other) >= 0;
296: }
297:
298: 299: 300: 301: 302: 303:
304: public function gte($other)
305: {
306: return $this->_gte(new BigDecimal($other, $this->scale));
307: }
308:
309: protected function _lt(BigDecimal $other)
310: {
311: return $this->comp($other) < 0;
312: }
313:
314: 315: 316: 317: 318: 319:
320: public function lt($other)
321: {
322: return $this->_lt(new BigDecimal($other, $this->scale));
323: }
324:
325: protected function _lte(BigDecimal $other)
326: {
327: return $this->comp($other) <= 0;
328: }
329:
330: 331: 332: 333: 334: 335:
336: public function lte($other)
337: {
338: return $this->_lte(new BigDecimal($other, $this->scale));
339: }
340:
341:
342:
343: protected function _mul(BigDecimal $other)
344: {
345: return new BigDecimal(bcmul($this->value, $other->value, $this->scale), $this->scale);
346: }
347:
348: 349: 350: 351: 352: 353:
354: public function mul($other)
355: {
356: return $this->_mul(new BigDecimal($other, $this->scale));
357: }
358:
359: protected function _div(BigDecimal $other)
360: {
361: return new BigDecimal(bcdiv($this->value, $other->value, $this->scale), $this->scale);
362: }
363:
364: 365: 366: 367: 368: 369:
370: public function div($other)
371: {
372: return $this->_div(new BigDecimal($other, $this->scale));
373: }
374:
375: protected function _mod(BigDecimal $other)
376: {
377: return new BigDecimal(bcmod($this->value, $other->value, $this->scale), $this->scale);
378: }
379:
380: 381: 382: 383: 384: 385:
386: public function mod($other)
387: {
388: return $this->_mod(new BigDecimal($other, $this->scale));
389: }
390:
391:
392:
393: protected function _pow(BigDecimal $other)
394: {
395: return new BigDecimal(bcpow($this->value, $other->value, $this->scale));
396: }
397:
398: 399: 400: 401: 402: 403:
404: public function pow($other)
405: {
406: return $this->_pow(self::from($other));
407: }
408:
409: 410: 411: 412: 413:
414: public function sqrt()
415: {
416: return new BigDecimal(bcsqrt($this->value, $this->scale), $this->scale);
417: }
418: }
419: