Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]
1 <?php 2 3 /* 4 ================================================================================ 5 6 EvalMath - PHP Class to safely evaluate math expressions 7 Copyright (C) 2005 Miles Kaufmann <http://www.twmagic.com/> 8 9 ================================================================================ 10 11 NAME 12 EvalMath - safely evaluate math expressions 13 14 SYNOPSIS 15 <? 16 include('evalmath.class.php'); 17 $m = new EvalMath; 18 // basic evaluation: 19 $result = $m->evaluate('2+2'); 20 // supports: order of operation; parentheses; negation; built-in functions 21 $result = $m->evaluate('-8(5/2)^2*(1-sqrt(4))-8'); 22 // create your own variables 23 $m->evaluate('a = e^(ln(pi))'); 24 // or functions 25 $m->evaluate('f(x,y) = x^2 + y^2 - 2x*y + 1'); 26 // and then use them 27 $result = $m->evaluate('3*f(42,a)'); 28 ?> 29 30 DESCRIPTION 31 Use the EvalMath class when you want to evaluate mathematical expressions 32 from untrusted sources. You can define your own variables and functions, 33 which are stored in the object. Try it, it's fun! 34 35 METHODS 36 $m->evalute($expr) 37 Evaluates the expression and returns the result. If an error occurs, 38 prints a warning and returns false. If $expr is a function assignment, 39 returns true on success. 40 41 $m->e($expr) 42 A synonym for $m->evaluate(). 43 44 $m->vars() 45 Returns an associative array of all user-defined variables and values. 46 47 $m->funcs() 48 Returns an array of all user-defined functions. 49 50 PARAMETERS 51 $m->suppress_errors 52 Set to true to turn off warnings when evaluating expressions 53 54 $m->last_error 55 If the last evaluation failed, contains a string describing the error. 56 (Useful when suppress_errors is on). 57 58 AUTHOR INFORMATION 59 Copyright 2005, Miles Kaufmann. 60 61 LICENSE 62 Redistribution and use in source and binary forms, with or without 63 modification, are permitted provided that the following conditions are 64 met: 65 66 1 Redistributions of source code must retain the above copyright 67 notice, this list of conditions and the following disclaimer. 68 2. Redistributions in binary form must reproduce the above copyright 69 notice, this list of conditions and the following disclaimer in the 70 documentation and/or other materials provided with the distribution. 71 3. The name of the author may not be used to endorse or promote 72 products derived from this software without specific prior written 73 permission. 74 75 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 76 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 77 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 78 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 79 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 80 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 81 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 82 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 83 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 84 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 85 POSSIBILITY OF SUCH DAMAGE. 86 87 */ 88 89 /** 90 * This class was heavily modified in order to get usefull spreadsheet emulation ;-) 91 * skodak 92 * This class was modified to allow comparison operators (<, <=, ==, >=, >) 93 * and synonyms functions (for the 'if' function). See MDL-14274 for more details. 94 */ 95 96 class EvalMath { 97 98 /** @var string Pattern used for a valid function or variable name. Note, var and func names are case insensitive.*/ 99 private static $namepat = '[a-z][a-z0-9_]*'; 100 101 var $suppress_errors = false; 102 var $last_error = null; 103 104 var $v = array(); // variables (and constants) 105 var $f = array(); // user-defined functions 106 var $vb = array(); // constants 107 var $fb = array( // built-in functions 108 'sin','sinh','arcsin','asin','arcsinh','asinh', 109 'cos','cosh','arccos','acos','arccosh','acosh', 110 'tan','tanh','arctan','atan','arctanh','atanh', 111 'sqrt','abs','ln','log','exp','floor','ceil'); 112 113 var $fc = array( // calc functions emulation 114 'average'=>array(-1), 'max'=>array(-1), 'min'=>array(-1), 115 'mod'=>array(2), 'pi'=>array(0), 'power'=>array(2), 116 'round'=>array(1, 2), 'sum'=>array(-1), 'rand_int'=>array(2), 117 'rand_float'=>array(0), 'ifthenelse'=>array(3), 'cond_and'=>array(-1), 'cond_or'=>array(-1)); 118 var $fcsynonyms = array('if' => 'ifthenelse', 'and' => 'cond_and', 'or' => 'cond_or'); 119 120 var $allowimplicitmultiplication; 121 122 public function __construct($allowconstants = false, $allowimplicitmultiplication = false) { 123 if ($allowconstants){ 124 $this->v['pi'] = pi(); 125 $this->v['e'] = exp(1); 126 } 127 $this->allowimplicitmultiplication = $allowimplicitmultiplication; 128 } 129 130 /** 131 * Old syntax of class constructor. Deprecated in PHP7. 132 * 133 * @deprecated since Moodle 3.1 134 */ 135 public function EvalMath($allowconstants = false, $allowimplicitmultiplication = false) { 136 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 137 self::__construct($allowconstants, $allowimplicitmultiplication); 138 } 139 140 function e($expr) { 141 return $this->evaluate($expr); 142 } 143 144 function evaluate($expr) { 145 $this->last_error = null; 146 $expr = trim($expr); 147 if (substr($expr, -1, 1) == ';') $expr = substr($expr, 0, strlen($expr)-1); // strip semicolons at the end 148 //=============== 149 // is it a variable assignment? 150 if (preg_match('/^\s*('.self::$namepat.')\s*=\s*(.+)$/', $expr, $matches)) { 151 if (in_array($matches[1], $this->vb)) { // make sure we're not assigning to a constant 152 return $this->trigger(get_string('cannotassigntoconstant', 'mathslib', $matches[1])); 153 } 154 if (($tmp = $this->pfx($this->nfx($matches[2]))) === false) return false; // get the result and make sure it's good 155 $this->v[$matches[1]] = $tmp; // if so, stick it in the variable array 156 return $this->v[$matches[1]]; // and return the resulting value 157 //=============== 158 // is it a function assignment? 159 } elseif (preg_match('/^\s*('.self::$namepat.')\s*\(\s*('.self::$namepat.'(?:\s*,\s*'.self::$namepat.')*)\s*\)\s*=\s*(.+)$/', $expr, $matches)) { 160 $fnn = $matches[1]; // get the function name 161 if (in_array($matches[1], $this->fb)) { // make sure it isn't built in 162 return $this->trigger(get_string('cannotredefinebuiltinfunction', 'mathslib', $matches[1])); 163 } 164 $args = explode(",", preg_replace("/\s+/", "", $matches[2])); // get the arguments 165 if (($stack = $this->nfx($matches[3])) === false) return false; // see if it can be converted to postfix 166 for ($i = 0; $i<count($stack); $i++) { // freeze the state of the non-argument variables 167 $token = $stack[$i]; 168 if (preg_match('/^'.self::$namepat.'$/', $token) and !in_array($token, $args)) { 169 if (array_key_exists($token, $this->v)) { 170 $stack[$i] = $this->v[$token]; 171 } else { 172 return $this->trigger(get_string('undefinedvariableinfunctiondefinition', 'mathslib', $token)); 173 } 174 } 175 } 176 $this->f[$fnn] = array('args'=>$args, 'func'=>$stack); 177 return true; 178 //=============== 179 } else { 180 return $this->pfx($this->nfx($expr)); // straight up evaluation, woo 181 } 182 } 183 184 function vars() { 185 return $this->v; 186 } 187 188 function funcs() { 189 $output = array(); 190 foreach ($this->f as $fnn=>$dat) 191 $output[] = $fnn . '(' . implode(',', $dat['args']) . ')'; 192 return $output; 193 } 194 195 /** 196 * @param string $name 197 * @return boolean Is this a valid var or function name? 198 */ 199 public static function is_valid_var_or_func_name($name){ 200 return preg_match('/'.self::$namepat.'$/iA', $name); 201 } 202 203 //===================== HERE BE INTERNAL METHODS ====================\\ 204 205 // Convert infix to postfix notation 206 function nfx($expr) { 207 208 $index = 0; 209 $stack = new EvalMathStack; 210 $output = array(); // postfix form of expression, to be passed to pfx() 211 $expr = trim(strtolower($expr)); 212 // MDL-14274: new operators for comparison added. 213 $ops = array('+', '-', '*', '/', '^', '_', '>', '<', '<=', '>=', '==', '%'); 214 $ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>1, '%' => 0); // right-associative operator? 215 $ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2, '>'=>3, '<'=>3, '<='=>3, '>='=>3, '=='=>3, '%'=>1); // operator precedence 216 217 $expecting_op = false; // we use this in syntax-checking the expression 218 // and determining when a - is a negation 219 220 if (preg_match("/[^\%\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good 221 return $this->trigger(get_string('illegalcharactergeneral', 'mathslib', $matches[0])); 222 } 223 224 while(1) { // 1 Infinite Loop ;) 225 // MDL-14274 Test two character operators. 226 $op = substr($expr, $index, 2); 227 if (!in_array($op, $ops)) { 228 // MDL-14274 Get one character operator. 229 $op = substr($expr, $index, 1); // get the first character at the current index 230 } 231 // find out if we're currently at the beginning of a number/variable/function/parenthesis/operand 232 $ex = preg_match('/^('.self::$namepat.'\(?|\d+(?:\.\d*)?(?:(e[+-]?)\d*)?|\.\d+|\()/', substr($expr, $index), $match); 233 //=============== 234 if ($op == '-' and !$expecting_op) { // is it a negation instead of a minus? 235 $stack->push('_'); // put a negation on the stack 236 $index++; 237 } elseif ($op == '_') { // we have to explicitly deny this, because it's legal on the stack 238 return $this->trigger(get_string('illegalcharacterunderscore', 'mathslib')); // but not in the input expression 239 //=============== 240 } elseif ((in_array($op, $ops) or $ex) and $expecting_op) { // are we putting an operator on the stack? 241 if ($ex) { // are we expecting an operator but have a number/variable/function/opening parethesis? 242 if (!$this->allowimplicitmultiplication){ 243 return $this->trigger(get_string('implicitmultiplicationnotallowed', 'mathslib')); 244 } else {// it's an implicit multiplication 245 $op = '*'; 246 $index--; 247 } 248 } 249 // heart of the algorithm: 250 while($stack->count > 0 and ($o2 = $stack->last()) and in_array($o2, $ops) and ($ops_r[$op] ? $ops_p[$op] < $ops_p[$o2] : $ops_p[$op] <= $ops_p[$o2])) { 251 $output[] = $stack->pop(); // pop stuff off the stack into the output 252 } 253 // many thanks: http://en.wikipedia.org/wiki/Reverse_Polish_notation#The_algorithm_in_detail 254 $stack->push($op); // finally put OUR operator onto the stack 255 $index += strlen($op); 256 $expecting_op = false; 257 //=============== 258 } elseif ($op == ')' and $expecting_op) { // ready to close a parenthesis? 259 while (($o2 = $stack->pop()) != '(') { // pop off the stack back to the last ( 260 if (is_null($o2)) return $this->trigger(get_string('unexpectedclosingbracket', 'mathslib')); 261 else $output[] = $o2; 262 } 263 if (preg_match('/^('.self::$namepat.')\($/', $stack->last(2) ?? '', $matches)) { // did we just close a function? 264 $fnn = $matches[1]; // get the function name 265 $arg_count = $stack->pop(); // see how many arguments there were (cleverly stored on the stack, thank you) 266 $fn = $stack->pop(); 267 $output[] = array('fn'=>$fn, 'fnn'=>$fnn, 'argcount'=>$arg_count); // send function to output 268 if (in_array($fnn, $this->fb)) { // check the argument count 269 if($arg_count > 1) { 270 $a= new stdClass(); 271 $a->expected = 1; 272 $a->given = $arg_count; 273 return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a)); 274 } 275 } elseif ($this->get_native_function_name($fnn)) { 276 $fnn = $this->get_native_function_name($fnn); // Resolve synonyms. 277 278 $counts = $this->fc[$fnn]; 279 if (in_array(-1, $counts) and $arg_count > 0) {} 280 elseif (!in_array($arg_count, $counts)) { 281 $a= new stdClass(); 282 $a->expected = implode('/',$this->fc[$fnn]); 283 $a->given = $arg_count; 284 return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a)); 285 } 286 } elseif (array_key_exists($fnn, $this->f)) { 287 if ($arg_count != count($this->f[$fnn]['args'])) { 288 $a= new stdClass(); 289 $a->expected = count($this->f[$fnn]['args']); 290 $a->given = $arg_count; 291 return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a)); 292 } 293 } else { // did we somehow push a non-function on the stack? this should never happen 294 return $this->trigger(get_string('internalerror', 'mathslib')); 295 } 296 } 297 $index++; 298 //=============== 299 } elseif ($op == ',' and $expecting_op) { // did we just finish a function argument? 300 while (($o2 = $stack->pop()) != '(') { 301 if (is_null($o2)) return $this->trigger(get_string('unexpectedcomma', 'mathslib')); // oops, never had a ( 302 else $output[] = $o2; // pop the argument expression stuff and push onto the output 303 } 304 // make sure there was a function 305 if (!preg_match('/^('.self::$namepat.')\($/', $stack->last(2), $matches)) 306 return $this->trigger(get_string('unexpectedcomma', 'mathslib')); 307 $stack->push($stack->pop()+1); // increment the argument count 308 $stack->push('('); // put the ( back on, we'll need to pop back to it again 309 $index++; 310 $expecting_op = false; 311 //=============== 312 } elseif ($op == '(' and !$expecting_op) { 313 $stack->push('('); // that was easy 314 $index++; 315 $allow_neg = true; 316 //=============== 317 } elseif ($ex and !$expecting_op) { // do we now have a function/variable/number? 318 $expecting_op = true; 319 $val = $match[1]; 320 if (preg_match('/^('.self::$namepat.')\($/', $val, $matches)) { // may be func, or variable w/ implicit multiplication against parentheses... 321 if (in_array($matches[1], $this->fb) or 322 array_key_exists($matches[1], $this->f) or 323 $this->get_native_function_name($matches[1])){ // it's a func 324 $stack->push($val); 325 $stack->push(1); 326 $stack->push('('); 327 $expecting_op = false; 328 } else { // it's a var w/ implicit multiplication 329 $val = $matches[1]; 330 $output[] = $val; 331 } 332 } else { // it's a plain old var or num 333 $output[] = $val; 334 } 335 $index += strlen($val); 336 //=============== 337 } elseif ($op == ')') { 338 //it could be only custom function with no params or general error 339 if ($stack->last() != '(' or $stack->last(2) != 1) return $this->trigger(get_string('unexpectedclosingbracket', 'mathslib')); 340 if (preg_match('/^('.self::$namepat.')\($/', $stack->last(3), $matches)) { // did we just close a function? 341 $stack->pop();// ( 342 $stack->pop();// 1 343 $fn = $stack->pop(); 344 $fnn = $matches[1]; // get the function name 345 $fnn = $this->get_native_function_name($fnn); // Resolve synonyms. 346 $counts = $this->fc[$fnn]; 347 if (!in_array(0, $counts)){ 348 $a= new stdClass(); 349 $a->expected = $this->fc[$fnn]; 350 $a->given = 0; 351 return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a)); 352 } 353 $output[] = array('fn'=>$fn, 'fnn'=>$fnn, 'argcount'=>0); // send function to output 354 $index++; 355 $expecting_op = true; 356 } else { 357 return $this->trigger(get_string('unexpectedclosingbracket', 'mathslib')); 358 } 359 //=============== 360 } elseif (in_array($op, $ops) and !$expecting_op) { // miscellaneous error checking 361 return $this->trigger(get_string('unexpectedoperator', 'mathslib', $op)); 362 } else { // I don't even want to know what you did to get here 363 return $this->trigger(get_string('anunexpectederroroccured', 'mathslib')); 364 } 365 if ($index == strlen($expr)) { 366 if (in_array($op, $ops)) { // did we end with an operator? bad. 367 return $this->trigger(get_string('operatorlacksoperand', 'mathslib', $op)); 368 } else { 369 break; 370 } 371 } 372 while (substr($expr, $index, 1) == ' ') { // step the index past whitespace (pretty much turns whitespace 373 $index++; // into implicit multiplication if no operator is there) 374 } 375 376 } 377 while (!is_null($op = $stack->pop())) { // pop everything off the stack and push onto output 378 if ($op == '(') return $this->trigger(get_string('expectingaclosingbracket', 'mathslib')); // if there are (s on the stack, ()s were unbalanced 379 $output[] = $op; 380 } 381 return $output; 382 } 383 /** 384 * 385 * @param string $fnn 386 * @return string|boolean false if function name unknown. 387 */ 388 function get_native_function_name($fnn) { 389 if (array_key_exists($fnn, $this->fcsynonyms)) { 390 return $this->fcsynonyms[$fnn]; 391 } else if (array_key_exists($fnn, $this->fc)) { 392 return $fnn; 393 } else { 394 return false; 395 } 396 } 397 // evaluate postfix notation 398 function pfx($tokens, $vars = array()) { 399 400 if ($tokens == false) return false; 401 402 $stack = new EvalMathStack; 403 404 foreach ($tokens as $token) { // nice and easy 405 406 // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on 407 if (is_array($token)) { // it's a function! 408 $fnn = $token['fnn']; 409 $count = $token['argcount']; 410 if (in_array($fnn, $this->fb)) { // built-in function: 411 if (is_null($op1 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib')); 412 $fnn = preg_replace("/^arc/", "a", $fnn); // for the 'arc' trig synonyms 413 if ($fnn == 'ln') $fnn = 'log'; 414 eval('$stack->push(' . $fnn . '($op1));'); // perfectly safe eval() 415 } elseif ($this->get_native_function_name($fnn)) { // calc emulation function 416 $fnn = $this->get_native_function_name($fnn); // Resolve synonyms. 417 // get args 418 $args = array(); 419 for ($i = $count-1; $i >= 0; $i--) { 420 if (is_null($args[] = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib')); 421 } 422 $res = call_user_func_array(array('EvalMathFuncs', $fnn), array_reverse($args)); 423 if ($res === FALSE) { 424 return $this->trigger(get_string('internalerror', 'mathslib')); 425 } 426 $stack->push($res); 427 } elseif (array_key_exists($fnn, $this->f)) { // user function 428 // get args 429 $args = array(); 430 for ($i = count($this->f[$fnn]['args'])-1; $i >= 0; $i--) { 431 if (is_null($args[$this->f[$fnn]['args'][$i]] = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib')); 432 } 433 $stack->push($this->pfx($this->f[$fnn]['func'], $args)); // yay... recursion!!!! 434 } 435 // if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on 436 } elseif (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '==', '<=', '>=', '%'), true)) { 437 if (is_null($op2 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib')); 438 if (is_null($op1 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib')); 439 switch ($token) { 440 case '+': 441 $stack->push($op1+$op2); break; 442 case '-': 443 $stack->push($op1-$op2); break; 444 case '*': 445 $stack->push($op1*$op2); break; 446 case '/': 447 if ($op2 == 0) return $this->trigger(get_string('divisionbyzero', 'mathslib')); 448 $stack->push($op1/$op2); break; 449 case '^': 450 $stack->push(pow($op1, $op2)); break; 451 case '>': 452 $stack->push((int)($op1 > $op2)); break; 453 case '<': 454 $stack->push((int)($op1 < $op2)); break; 455 case '==': 456 $stack->push((int)($op1 == $op2)); break; 457 case '<=': 458 $stack->push((int)($op1 <= $op2)); break; 459 case '>=': 460 $stack->push((int)($op1 >= $op2)); break; 461 case '%': 462 $stack->push($op1%$op2); break; 463 } 464 // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on 465 } elseif ($token == "_") { 466 $stack->push(-1*$stack->pop()); 467 // if the token is a number or variable, push it on the stack 468 } else { 469 if (is_numeric($token)) { 470 $stack->push($token); 471 } elseif (array_key_exists($token, $this->v)) { 472 $stack->push($this->v[$token]); 473 } elseif (array_key_exists($token, $vars)) { 474 $stack->push($vars[$token]); 475 } else { 476 return $this->trigger(get_string('undefinedvariable', 'mathslib', $token)); 477 } 478 } 479 } 480 // when we're out of tokens, the stack should have a single element, the final result 481 if ($stack->count != 1) return $this->trigger(get_string('internalerror', 'mathslib')); 482 return $stack->pop(); 483 } 484 485 // trigger an error, but nicely, if need be 486 function trigger($msg) { 487 $this->last_error = $msg; 488 if (!$this->suppress_errors) trigger_error($msg, E_USER_WARNING); 489 return false; 490 } 491 492 } 493 494 // for internal use 495 class EvalMathStack { 496 497 var $stack = array(); 498 var $count = 0; 499 500 function push($val) { 501 $this->stack[$this->count] = $val; 502 $this->count++; 503 } 504 505 function pop() { 506 if ($this->count > 0) { 507 $this->count--; 508 return $this->stack[$this->count]; 509 } 510 return null; 511 } 512 513 function last($n=1) { 514 if ($this->count - $n >= 0) { 515 return $this->stack[$this->count-$n]; 516 } 517 return null; 518 } 519 } 520 521 522 // spreadsheet functions emulation 523 class EvalMathFuncs { 524 /** 525 * MDL-14274 new conditional function. 526 * @param boolean $condition boolean for conditional. 527 * @param variant $then value if condition is true. 528 * @param unknown $else value if condition is false. 529 * @author Juan Pablo de Castro <juan.pablo.de.castro@gmail.com> 530 * @return unknown 531 */ 532 static function ifthenelse($condition, $then, $else) { 533 if ($condition == true) { 534 return $then; 535 } else { 536 return $else; 537 } 538 } 539 540 static function cond_and() { 541 $args = func_get_args(); 542 foreach($args as $a) { 543 if ($a == false) { 544 return 0; 545 } 546 } 547 return 1; 548 } 549 550 static function cond_or() { 551 $args = func_get_args(); 552 foreach($args as $a) { 553 if($a == true) { 554 return 1; 555 } 556 } 557 return 0; 558 } 559 560 static function average() { 561 $args = func_get_args(); 562 return (call_user_func_array(array(self::class, 'sum'), $args) / count($args)); 563 } 564 565 static function max() { 566 $args = func_get_args(); 567 $res = array_pop($args); 568 foreach($args as $a) { 569 if ($res < $a) { 570 $res = $a; 571 } 572 } 573 return $res; 574 } 575 576 static function min() { 577 $args = func_get_args(); 578 $res = array_pop($args); 579 foreach($args as $a) { 580 if ($res > $a) { 581 $res = $a; 582 } 583 } 584 return $res; 585 } 586 587 static function mod($op1, $op2) { 588 return $op1 % $op2; 589 } 590 591 static function pi() { 592 return pi(); 593 } 594 595 static function power($op1, $op2) { 596 return pow($op1, $op2); 597 } 598 599 static function round($val, $precision = 0) { 600 return round($val, $precision); 601 } 602 603 static function sum() { 604 $args = func_get_args(); 605 $res = 0; 606 foreach($args as $a) { 607 $res += $a; 608 } 609 return $res; 610 } 611 612 protected static $randomseed = null; 613 614 static function set_random_seed($randomseed) { 615 self::$randomseed = $randomseed; 616 } 617 618 static function get_random_seed() { 619 if (is_null(self::$randomseed)){ 620 return microtime(); 621 } else { 622 return self::$randomseed; 623 } 624 } 625 626 static function rand_int($min, $max){ 627 if ($min >= $max) { 628 return false; //error 629 } 630 $noofchars = ceil(log($max + 1 - $min, '16')); 631 $md5string = md5(self::get_random_seed()); 632 $stringoffset = 0; 633 do { 634 while (($stringoffset + $noofchars) > strlen($md5string)){ 635 $md5string .= md5($md5string); 636 } 637 $randomno = hexdec(substr($md5string, $stringoffset, $noofchars)); 638 $stringoffset += $noofchars; 639 } while (($min + $randomno) > $max); 640 return $min + $randomno; 641 } 642 643 static function rand_float() { 644 $randomvalues = unpack('v', md5(self::get_random_seed(), true)); 645 return array_shift($randomvalues) / 65536; 646 } 647 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body