Merge branch 'm36_MDL-51080_MAriaDB_Wrong_DB_Type' of https://github.com/scara/moodle
[moodle.git] / lib / evalmath / evalmath.class.php
1 <?php
3 /*
4 ================================================================================
6 EvalMath - PHP Class to safely evaluate math expressions
7 Copyright (C) 2005 Miles Kaufmann <http://www.twmagic.com/>
9 ================================================================================
11 NAME
12     EvalMath - safely evaluate math expressions
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     ?>
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!
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.
41     $m->e($expr)
42         A synonym for $m->evaluate().
44     $m->vars()
45         Returns an associative array of all user-defined variables and values.
47     $m->funcs()
48         Returns an array of all user-defined functions.
50 PARAMETERS
51     $m->suppress_errors
52         Set to true to turn off warnings when evaluating expressions
54     $m->last_error
55         If the last evaluation failed, contains a string describing the error.
56         (Useful when suppress_errors is on).
58 AUTHOR INFORMATION
59     Copyright 2005, Miles Kaufmann.
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:
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.
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.
87 */
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  */
96 class EvalMath {
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_]*';
101     var $suppress_errors = false;
102     var $last_error = null;
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');
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));
118     var $fcsynonyms = array('if' => 'ifthenelse');
120     var $allowimplicitmultiplication;
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     }
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     }
140     function e($expr) {
141         return $this->evaluate($expr);
142     }
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     }
184     function vars() {
185         return $this->v;
186     }
188     function funcs() {
189         $output = array();
190         foreach ($this->f as $fnn=>$dat)
191             $output[] = $fnn . '(' . implode(',', $dat['args']) . ')';
192         return $output;
193     }
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     }
203     //===================== HERE BE INTERNAL METHODS ====================\\
205     // Convert infix to postfix notation
206     function nfx($expr) {
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); // right-associative operator?
215         $ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2, '>'=>3, '<'=>3, '<='=>3, '>='=>3, '=='=>3); // operator precedence
217         $expecting_op = false; // we use this in syntax-checking the expression
218                                // and determining when a - is a negation
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         }
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.
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             }
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()) {
400         if ($tokens == false) return false;
402         $stack = new EvalMathStack;
404         foreach ($tokens as $token) { // nice and easy
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                 }
462             // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
463             } elseif ($token == "_") {
464                 $stack->push(-1*$stack->pop());
465             // if the token is a number or variable, push it on the stack
466             } else {
467                 if (is_numeric($token)) {
468                     $stack->push($token);
469                 } elseif (array_key_exists($token, $this->v)) {
470                     $stack->push($this->v[$token]);
471                 } elseif (array_key_exists($token, $vars)) {
472                     $stack->push($vars[$token]);
473                 } else {
474                     return $this->trigger(get_string('undefinedvariable', 'mathslib', $token));
475                 }
476             }
477         }
478         // when we're out of tokens, the stack should have a single element, the final result
479         if ($stack->count != 1) return $this->trigger(get_string('internalerror', 'mathslib'));
480         return $stack->pop();
481     }
483     // trigger an error, but nicely, if need be
484     function trigger($msg) {
485         $this->last_error = $msg;
486         if (!$this->suppress_errors) trigger_error($msg, E_USER_WARNING);
487         return false;
488     }
492 // for internal use
493 class EvalMathStack {
495     var $stack = array();
496     var $count = 0;
498     function push($val) {
499         $this->stack[$this->count] = $val;
500         $this->count++;
501     }
503     function pop() {
504         if ($this->count > 0) {
505             $this->count--;
506             return $this->stack[$this->count];
507         }
508         return null;
509     }
511     function last($n=1) {
512         if ($this->count - $n >= 0) {
513             return $this->stack[$this->count-$n];
514         }
515         return null;
516     }
520 // spreadsheet functions emulation
521 class EvalMathFuncs {
522     /**
523      * MDL-14274 new conditional function.
524      * @param boolean $condition boolean for conditional.
525      * @param variant $then value if condition is true.
526      * @param unknown $else value if condition is false.
527      * @author Juan Pablo de Castro <juan.pablo.de.castro@gmail.com>
528      * @return unknown
529      */
530     static function ifthenelse($condition, $then, $else) {
531         if ($condition == true) {
532             return $then;
533         } else {
534             return $else;
535         }
536     }
537     static function average() {
538         $args = func_get_args();
539         return (call_user_func_array(array('self', 'sum'), $args) / count($args));
540     }
542     static function max() {
543         $args = func_get_args();
544         $res = array_pop($args);
545         foreach($args as $a) {
546             if ($res < $a) {
547                 $res = $a;
548             }
549         }
550         return $res;
551     }
553     static function min() {
554         $args = func_get_args();
555         $res = array_pop($args);
556         foreach($args as $a) {
557             if ($res > $a) {
558                 $res = $a;
559             }
560         }
561         return $res;
562     }
564     static function mod($op1, $op2) {
565         return $op1 % $op2;
566     }
568     static function pi() {
569         return pi();
570     }
572     static function power($op1, $op2) {
573         return pow($op1, $op2);
574     }
576     static function round($val, $precision = 0) {
577         return round($val, $precision);
578     }
580     static function sum() {
581         $args = func_get_args();
582         $res = 0;
583         foreach($args as $a) {
584            $res += $a;
585         }
586         return $res;
587     }
589     protected static $randomseed = null;
591     static function set_random_seed($randomseed) {
592         self::$randomseed = $randomseed;
593     }
595     static function get_random_seed() {
596         if (is_null(self::$randomseed)){
597             return microtime();
598         } else {
599             return self::$randomseed;
600         }
601     }
603     static function rand_int($min, $max){
604         if ($min >= $max) {
605             return false; //error
606         }
607         $noofchars = ceil(log($max + 1 - $min, '16'));
608         $md5string = md5(self::get_random_seed());
609         $stringoffset = 0;
610         do {
611             while (($stringoffset + $noofchars) > strlen($md5string)){
612                 $md5string .= md5($md5string);
613             }
614             $randomno = hexdec(substr($md5string, $stringoffset, $noofchars));
615             $stringoffset += $noofchars;
616         } while (($min + $randomno) > $max);
617         return $min + $randomno;
618     }
620     static function rand_float() {
621         $randomvalues = unpack('v', md5(self::get_random_seed(), true));
622         return array_shift($randomvalues) / 65536;
623     }