Moodle
  1. Moodle
  2. MDL-18141

Calculated question formula validation allows syntactical incorrect code

    Details

    • Type: Bug Bug
    • Status: Development in progress
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: 1.9.4, 2.8.1
    • Fix Version/s: BACKEND
    • Component/s: Questions
    • Labels:

      Description

      Calculated question formula validation think that formulas like

      {a}

      ?

      {b}

      is valid, which then results in parse errors in eval'ed code.

      Easy workaround for this would be this method of syntax validation ($code is full code you need to execute, not just formula):
      function check_syntax($code)

      { return @eval('return true;' . $code); }

      The function will return true if code is syntactical correct and false if it is not.

      To get more accurate error reporting you'll need to use tools like http://netevil.org/blog/2006/nov/parser-and-lexer-generators-for-php
      This may not be easy at first, thought it'll provide you with an ability to create any possible features in you formulas and (eventually) avoid eval at all (this is good, as passing users input to eval may lead to very serious security vulnerabilities). Actually tools will made most complex job for you.

        Gliffy Diagrams

          Issue Links

            Activity

            Hide
            Pierre Pichet added a comment -

            $code is full code you need to execute, not just formula):
            I.e. with the

            {a}

            parameters replace by their real values .
            this is not so simple. the actual code replace everything by 1 as a first check.
            Not perfect but better than nothing.
            Working on this could include MDL-7198 proposal as a complete solution.

            Show
            Pierre Pichet added a comment - $code is full code you need to execute, not just formula): I.e. with the {a} parameters replace by their real values . this is not so simple. the actual code replace everything by 1 as a first check. Not perfect but better than nothing. Working on this could include MDL-7198 proposal as a complete solution.
            Hide
            Oleg Sychev added a comment -

            You may replace

            {a}

            with 1 for syntax check, it doesn't matter. Full code means that you'll need to add at least "return " or "$something = " before formula and a semicolon after, to receive valid PHP operator.

            This check should find you any sintactical incorrect things, but doesn't report what they are.

            Show
            Oleg Sychev added a comment - You may replace {a} with 1 for syntax check, it doesn't matter. Full code means that you'll need to add at least "return " or "$something = " before formula and a semicolon after, to receive valid PHP operator. This check should find you any sintactical incorrect things, but doesn't report what they are.
            Hide
            Pierre Pichet added a comment -

            Thanks for the cue.
            I will see how I can use it.

            Show
            Pierre Pichet added a comment - Thanks for the cue. I will see how I can use it.
            Hide
            Pierre Pichet added a comment -

            From Oleg ( MDL-18034)
            "P.S. I managed to get php code in eval using import, but first time failed to do something malicious with it. Will need to study calculated code more thorought to do it. I think either save_question_options should check formula validity, or it should be done before any eval to be sure (right now there is one eval before it isn't done)."

            Effectively there is no checking there.

            I will look at this.

            Show
            Pierre Pichet added a comment - From Oleg ( MDL-18034 ) "P.S. I managed to get php code in eval using import, but first time failed to do something malicious with it. Will need to study calculated code more thorought to do it. I think either save_question_options should check formula validity, or it should be done before any eval to be sure (right now there is one eval before it isn't done)." Effectively there is no checking there. I will look at this.
            Hide
            Pierre Pichet added a comment -

            There is already $question->import_process parameter that could be used when saving options to do the validate process on formulas that is done in edit_calculated_form,
            as it is used to import datassets
            if( isset($question->import_process)&&$question->import_process)

            { $this->import_datasets($question); }

            I will work on this, so get back to your main project...

            Show
            Pierre Pichet added a comment - There is already $question->import_process parameter that could be used when saving options to do the validate process on formulas that is done in edit_calculated_form, as it is used to import datassets if( isset($question->import_process)&&$question->import_process) { $this->import_datasets($question); } I will work on this, so get back to your main project...
            Hide
            Pierre Pichet added a comment -

            Once the filtering is done I don't think that there are problems with "malignant" code because only math functions are allowed.
            function qtype_calculated_find_formula_errors($formula) {
            /// Validates the formula submitted from the question edit page.
            /// Returns false if everything is alright.
            /// Otherwise it constructs an error message
            // Strip away dataset names
            while (ereg('

            {[[:alpha:]][^>}

            <

            {"\']*\\}

            ', $formula, $regs))

            { $formula = str_replace($regs[0], '1', $formula); }

            // Strip away empty space and lowercase it
            $formula = strtolower(str_replace(' ', '', $formula));

            $safeoperatorchar = '-+/%>:^~<?=&|!'; / */
            $operatorornumber = "[$safeoperatorchar.0-9eE]";

            while (ereg("(^|[$safeoperatorchar,(])([a-z0-9_]*)\\(($operatorornumber+(,$operatorornumber+((,$operatorornumber+)+)?)?)?
            )",
            $formula, $regs)) {

            switch ($regs[2]) {
            // Simple parenthesis
            case '':
            if ($regs[4] || strlen($regs[3])==0)

            { return get_string('illegalformulasyntax', 'quiz', $regs[0]); }

            break;

            // Zero argument functions
            case 'pi':
            if ($regs[3])

            { return get_string('functiontakesnoargs', 'quiz', $regs[2]); }

            break;

            // Single argument functions (the most common case)
            case 'abs': case 'acos': case 'acosh': case 'asin': case 'asinh':
            case 'atan': case 'atanh': case 'bindec': case 'ceil': case 'cos':
            case 'cosh': case 'decbin': case 'decoct': case 'deg2rad':
            case 'exp': case 'expm1': case 'floor': case 'is_finite':
            case 'is_infinite': case 'is_nan': case 'log10': case 'log1p':
            case 'octdec': case 'rad2deg': case 'sin': case 'sinh': case 'sqrt':
            case 'tan': case 'tanh':
            if ($regs[4] || empty($regs[3]))

            { return get_string('functiontakesonearg','quiz',$regs[2]); }

            break;

            // Functions that take one or two arguments
            case 'log': case 'round':
            if ($regs[5] || empty($regs[3]))

            { return get_string('functiontakesoneortwoargs','quiz',$regs[2]); }

            break;

            // Functions that must have two arguments
            case 'atan2': case 'fmod': case 'pow':
            if ($regs[5] || empty($regs[4]))

            { return get_string('functiontakestwoargs', 'quiz', $regs[2]); }

            break;

            // Functions that take two or more arguments
            case 'min': case 'max':
            if (empty($regs[4]))

            { return get_string('functiontakesatleasttwo','quiz',$regs[2]); }

            break;

            default:
            return get_string('unsupportedformulafunction','quiz',$regs[2]);
            }

            // Exchange the function call with '1' and then chack for
            // another function call...
            if ($regs[1])

            { // The function call is proceeded by an operator $formula = str_replace($regs[0], $regs[1] . '1', $formula); }

            else

            { // The function call starts the formula $formula = ereg_replace("^$regs[2]\\([^)]*\\)", '1', $formula); }

            }

            if (ereg("[^$safeoperatorchar.0-9eE]+", $formula, $regs))

            { return get_string('illegalformulasyntax', 'quiz', $regs[0]); }

            else

            { // Formula just might be valid return false; }

            }
            However the maths results can be bad but this is detected elsewhere when creating the datasets.

            Show
            Pierre Pichet added a comment - Once the filtering is done I don't think that there are problems with "malignant" code because only math functions are allowed. function qtype_calculated_find_formula_errors($formula) { /// Validates the formula submitted from the question edit page. /// Returns false if everything is alright. /// Otherwise it constructs an error message // Strip away dataset names while (ereg(' {[[:alpha:]][^>} < {"\']*\\} ', $formula, $regs)) { $formula = str_replace($regs[0], '1', $formula); } // Strip away empty space and lowercase it $formula = strtolower(str_replace(' ', '', $formula)); $safeoperatorchar = '-+/ %>:^~<?=&|!'; / */ $operatorornumber = " [$safeoperatorchar.0-9eE] "; while (ereg("(^| [$safeoperatorchar,(] )( [a-z0-9_] *)\\(($operatorornumber+(,$operatorornumber+((,$operatorornumber+)+)?)?)? )", $formula, $regs)) { switch ($regs [2] ) { // Simple parenthesis case '': if ($regs [4] || strlen($regs [3] )==0) { return get_string('illegalformulasyntax', 'quiz', $regs[0]); } break; // Zero argument functions case 'pi': if ($regs [3] ) { return get_string('functiontakesnoargs', 'quiz', $regs[2]); } break; // Single argument functions (the most common case) case 'abs': case 'acos': case 'acosh': case 'asin': case 'asinh': case 'atan': case 'atanh': case 'bindec': case 'ceil': case 'cos': case 'cosh': case 'decbin': case 'decoct': case 'deg2rad': case 'exp': case 'expm1': case 'floor': case 'is_finite': case 'is_infinite': case 'is_nan': case 'log10': case 'log1p': case 'octdec': case 'rad2deg': case 'sin': case 'sinh': case 'sqrt': case 'tan': case 'tanh': if ($regs [4] || empty($regs [3] )) { return get_string('functiontakesonearg','quiz',$regs[2]); } break; // Functions that take one or two arguments case 'log': case 'round': if ($regs [5] || empty($regs [3] )) { return get_string('functiontakesoneortwoargs','quiz',$regs[2]); } break; // Functions that must have two arguments case 'atan2': case 'fmod': case 'pow': if ($regs [5] || empty($regs [4] )) { return get_string('functiontakestwoargs', 'quiz', $regs[2]); } break; // Functions that take two or more arguments case 'min': case 'max': if (empty($regs [4] )) { return get_string('functiontakesatleasttwo','quiz',$regs[2]); } break; default: return get_string('unsupportedformulafunction','quiz',$regs [2] ); } // Exchange the function call with '1' and then chack for // another function call... if ($regs [1] ) { // The function call is proceeded by an operator $formula = str_replace($regs[0], $regs[1] . '1', $formula); } else { // The function call starts the formula $formula = ereg_replace("^$regs[2]\\([^)]*\\)", '1', $formula); } } if (ereg(" [^$safeoperatorchar.0-9eE] +", $formula, $regs)) { return get_string('illegalformulasyntax', 'quiz', $regs[0]); } else { // Formula just might be valid return false; } } However the maths results can be bad but this is detected elsewhere when creating the datasets.
            Hide
            Oleg Sychev added a comment -

            "Once the filtering is done I don't think that there are problems with "malignant" code because only math functions are allowed. " - are you sure that not malignant things can be done via modifying values of variables?

            Show
            Oleg Sychev added a comment - "Once the filtering is done I don't think that there are problems with "malignant" code because only math functions are allowed. " - are you sure that not malignant things can be done via modifying values of variables?
            Hide
            Oleg Sychev added a comment -

            I suggests adding syntactical check mentioned in description (not necessary as separate function) to qtype_calculated_find_formula_errors. It'll catch any additional errors, that are not identifyed by now.

            Show
            Oleg Sychev added a comment - I suggests adding syntactical check mentioned in description (not necessary as separate function) to qtype_calculated_find_formula_errors. It'll catch any additional errors, that are not identifyed by now.
            Hide
            Pierre Pichet added a comment -

            On import however, additional checking is needed as students could be allowed to import questions.
            I put this on my to-do list...

            Show
            Pierre Pichet added a comment - On import however, additional checking is needed as students could be allowed to import questions. I put this on my to-do list...
            Hide
            Pierre Pichet added a comment -
            Show
            Pierre Pichet added a comment - See http://moodle.org/mod/forum/discuss.php?d=122222 for a related problem.
            Hide
            Michael de Raadt added a comment -

            Thanks for reporting this issue.

            We have detected that this issue has been inactive for over a year has been recorded as affecting versions that are no longer supported.

            If you believe that this issue is still relevant to current versions (2.1 and beyond), please comment on the issue. Issues left inactive for a further month will be closed.

            Michael d;

            lqjjLKA0p6

            Show
            Michael de Raadt added a comment - Thanks for reporting this issue. We have detected that this issue has been inactive for over a year has been recorded as affecting versions that are no longer supported. If you believe that this issue is still relevant to current versions (2.1 and beyond), please comment on the issue. Issues left inactive for a further month will be closed. Michael d; lqjjLKA0p6
            Hide
            Oleg Sychev added a comment -

            Pierre - did you do anything about this problem in new Moodle version to have it fixed?

            I don't beleive it is fixed, but have no time to re-test for now...

            Show
            Oleg Sychev added a comment - Pierre - did you do anything about this problem in new Moodle version to have it fixed? I don't beleive it is fixed, but have no time to re-test for now...
            Hide
            Pierre Pichet added a comment -

            This has been fixed in calculated/question.php i.e. 2.1 when Tim build this file

             
            /**
                 * Evaluate an expression after the variable values have been substituted.
                 * @param string $expression the expression. A PHP expression with placeholders
                 *      like {a} for where the variables need to go.
                 * @return float the computed result.
                 */
                protected function calculate_raw($expression) {
                    // This validation trick from http://php.net/manual/en/function.eval.php
                    if (!@eval('return true; $result = ' . $expression . ';')) {
                        throw new moodle_exception('illegalformulasyntax', 'qtype_calculated', '', $expression);
                    }
                    return eval('return ' . $expression . ';');
                }
            

            However I have to check if this is applied when editing the questions.

            Show
            Pierre Pichet added a comment - This has been fixed in calculated/question.php i.e. 2.1 when Tim build this file   /** * Evaluate an expression after the variable values have been substituted. * @param string $expression the expression. A PHP expression with placeholders * like {a} for where the variables need to go. * @return float the computed result. */ protected function calculate_raw($expression) { // This validation trick from http://php.net/manual/en/function.eval.php if (!@eval('return true; $result = ' . $expression . ';')) { throw new moodle_exception('illegalformulasyntax', 'qtype_calculated', '', $expression); } return eval('return ' . $expression . ';'); } However I have to check if this is applied when editing the questions.
            Hide
            Pierre Pichet added a comment -

            Micheal,
            I will get through all my "not apparently active" bugs in the next 2 weeks.
            Thanks for the remainder.
            This was on my todo list for the semester so its promote at an higher level

            Show
            Pierre Pichet added a comment - Micheal, I will get through all my "not apparently active" bugs in the next 2 weeks. Thanks for the remainder. This was on my todo list for the semester so its promote at an higher level
            Hide
            Pierre Pichet added a comment -

            Testing on master, this has not been solved completely.
            i.e the error is not detected when editing the question.
            the

            {a}

            ?

            {b}

            :

            {c}

            structure is allowed in formulas but not correctly tested.

            Tim has commented somewhere that he plans to implement for calculated the evalmath.class.php library set
            for the new OU numericals questiontypes.

            So this bug should remain active

            Show
            Pierre Pichet added a comment - Testing on master, this has not been solved completely. i.e the error is not detected when editing the question. the {a} ? {b} : {c} structure is allowed in formulas but not correctly tested. Tim has commented somewhere that he plans to implement for calculated the evalmath.class.php library set for the new OU numericals questiontypes. So this bug should remain active
            Hide
            Michael de Raadt added a comment -

            Thanks for reporting this issue.

            We have detected that this issue has been inactive for over a year has been recorded as affecting versions that are no longer supported.

            If you believe that this issue is still relevant to current versions (2.3 and beyond), please comment on the issue. Issues left inactive for a further month will be closed.

            Michael d;

            4d6f6f646c6521

            Show
            Michael de Raadt added a comment - Thanks for reporting this issue. We have detected that this issue has been inactive for over a year has been recorded as affecting versions that are no longer supported. If you believe that this issue is still relevant to current versions (2.3 and beyond), please comment on the issue. Issues left inactive for a further month will be closed. Michael d; 4d6f6f646c6521
            Hide
            Oleg Sychev added a comment -

            Reproduced on qa.moodle.net

            {a}

            ?

            {b}

            as an answer gives parse errors when generating sets and saving the question.

            Parse error: syntax error, unexpected ';' in /html/question/type/calculated/questiontype.php(1259) : eval()'d code on line 1

            Parse error: syntax error, unexpected ';' in /html/question/type/calculated/questiontype.php(1073) : eval()'d code on line 1

            Notice: Undefined variable: ansvalue in /html/question/type/calculated/questiontype.php on line 1074

            Show
            Oleg Sychev added a comment - Reproduced on qa.moodle.net {a} ? {b} as an answer gives parse errors when generating sets and saving the question. Parse error: syntax error, unexpected ';' in /html/question/type/calculated/questiontype.php(1259) : eval()'d code on line 1 Parse error: syntax error, unexpected ';' in /html/question/type/calculated/questiontype.php(1073) : eval()'d code on line 1 Notice: Undefined variable: ansvalue in /html/question/type/calculated/questiontype.php on line 1074
            Hide
            Pierre Pichet added a comment -

            Exploring again this problem and other related that are not detected

            • if the user do not put an operator
              • between two params {a} {b}
              • two (...)(...)
              • or the combination could result in either an error .
                This could be detected by

                function qtype_calculated_find_formula_errors($formula) {
                    // Validates the formula submitted from the question edit page.
                    // Returns false if everything is alright
                    // otherwise it constructs an error message.
                    // Strip away dataset names.
                     $regs = array();
                     preg_match('~[)\}][ ]*[\{(]~',$formula, $regs);
                     if (isset($regs[0])) {
                     return " consecutive param or (...) ";
                     
                    }
                

            The (...)? true : false syntax can be tested by the following

                $regs = array();
                preg_match_all('~[\\?:]~',$formula, $regs);
                $count = 0;
                if ( $regs[0] != null ){
                    foreach ($regs[0] as $ri => $rs) {
                    //     echo "<p>ri $ri rs $rs <p>";
                        if ($ri == 0 & $rs == ":"){
                     //        echo "<p>ri $ri rs $rs";
                            return " : preceding ? ERROR in the formula<p>";                 
                        }
                        if ($rs == ":"){
                           $count += -1;
                        }
                        if ($rs == "?"){
                           $count += 1;
                        }                  
                    }
                    if ($count >= 0) return " ERROR in the formula ?: unbalanced more ?  in the formula";  
                    if ($count <= 0) return "  ERROR in the formula ?: unbalanced more :   in the formula";  
                }
            

            This "RAW" code seems to work.

            Show
            Pierre Pichet added a comment - Exploring again this problem and other related that are not detected if the user do not put an operator between two params {a} {b} two (...)(...) or the combination could result in either an error . This could be detected by function qtype_calculated_find_formula_errors($formula) { // Validates the formula submitted from the question edit page. // Returns false if everything is alright // otherwise it constructs an error message. // Strip away dataset names. $regs = array(); preg_match('~[)\}][ ]*[\{(]~',$formula, $regs); if (isset($regs[0])) { return " consecutive param or (...) "; } The (...)? true : false syntax can be tested by the following $regs = array(); preg_match_all('~[\\?:]~',$formula, $regs); $count = 0; if ( $regs[0] != null ){ foreach ($regs[0] as $ri => $rs) { // echo "<p>ri $ri rs $rs <p>"; if ($ri == 0 & $rs == ":"){ // echo "<p>ri $ri rs $rs"; return " : preceding ? ERROR in the formula<p>"; } if ($rs == ":"){ $count += -1; } if ($rs == "?"){ $count += 1; } } if ($count >= 0) return " ERROR in the formula ?: unbalanced more ? in the formula"; if ($count <= 0) return " ERROR in the formula ?: unbalanced more : in the formula"; } This "RAW" code seems to work.
            Hide
            Marina Glancy added a comment -

            Hi Pierre, this issue is reported over 1.9. Is it still valid?

            Show
            Marina Glancy added a comment - Hi Pierre, this issue is reported over 1.9. Is it still valid?
            Hide
            Pierre Pichet added a comment -

            Yes I am doing the tests on last week master ...

            In reviewing the code of calculatedsimple I did some tests and effectively in one of them I forgot to put a + between

            {a}

            {b}

            So I created the first regex and

            this led me to this "old" unsolved problem.

            So I am waiting for Tim comments about implementing this solution or something similar.

            Show
            Pierre Pichet added a comment - Yes I am doing the tests on last week master ... In reviewing the code of calculatedsimple I did some tests and effectively in one of them I forgot to put a + between {a} {b} So I created the first regex and this led me to this "old" unsolved problem. So I am waiting for Tim comments about implementing this solution or something similar.
            Hide
            Oleg Sychev added a comment -

            Pierre. All this (syntactical analysis of the formula) could be done with much less programming effort using specialised tools like PHPParserGenerator (or LemonPHP). If you wish, I could show you expamples in Preg question type where we use this technique for regular expression analysis and validation.

            We also writing similar code for programming language expressions as a part of Formal languages block project, but it still have several bugs that need fixing.

            Show
            Oleg Sychev added a comment - Pierre. All this (syntactical analysis of the formula) could be done with much less programming effort using specialised tools like PHPParserGenerator (or LemonPHP). If you wish, I could show you expamples in Preg question type where we use this technique for regular expression analysis and validation. We also writing similar code for programming language expressions as a part of Formal languages block project, but it still have several bugs that need fixing.
            Hide
            Pierre Pichet added a comment -

            Thanks for your offer.
            I am waiting for Tim comments about using some additional patches to the actual code or adding a more complete syntactical analysis as what he have done in varnumeric question type and as you propose.

            Show
            Pierre Pichet added a comment - Thanks for your offer. I am waiting for Tim comments about using some additional patches to the actual code or adding a more complete syntactical analysis as what he have done in varnumeric question type and as you propose.
            Hide
            Pierre Pichet added a comment - - edited

            I send the following email to Tim about using EvalMath as done in OU var_numeric types.

            At first EvalMath(true, true) is safer and a lot more versatile than the combination of qtype_calculated_find_formula_errors() and eval.

            I think that we could set EvalMath(true, true) so to reproduce the already available calculated functions and then adding the possibilities for a more flexible handling.
            This should allow to keep the existing calculated questions.
            Is there some fundamental problems to do this ?

            Here his comment :

            If making this changes does not break existing calculated questions, the there is no problem. It would be a good change.

            EvalMath is used by the Moodle gradebook (as well as varnumeric). I don't think there is any problem adding more functions. If you do then everyone benefits.

            So I will explore how to use EvalMath

            Show
            Pierre Pichet added a comment - - edited I send the following email to Tim about using EvalMath as done in OU var_numeric types. At first EvalMath(true, true) is safer and a lot more versatile than the combination of qtype_calculated_find_formula_errors() and eval. I think that we could set EvalMath(true, true) so to reproduce the already available calculated functions and then adding the possibilities for a more flexible handling. This should allow to keep the existing calculated questions. Is there some fundamental problems to do this ? Here his comment : If making this changes does not break existing calculated questions, the there is no problem. It would be a good change. EvalMath is used by the Moodle gradebook (as well as varnumeric). I don't think there is any problem adding more functions. If you do then everyone benefits. So I will explore how to use EvalMath
            Hide
            Pierre Pichet added a comment - - edited

            EvalMath first results are positive and let place to a more flexible equation handling ( here implicit * ).

            Set 20  ({x})({y})
            (4.7)(5.5) = 25.85
            Correct answer : 25.85 inside limits of true value
            Min: 25.5915 --- Max: 26.1085
            

            Show
            Pierre Pichet added a comment - - edited EvalMath first results are positive and let place to a more flexible equation handling ( here implicit * ). Set 20 ({x})({y}) (4.7)(5.5) = 25.85 Correct answer : 25.85 inside limits of true value Min: 25.5915 --- Max: 26.1085
            Hide
            Pierre Pichet added a comment - - edited

            There are few failures of the actual calculated PHP Unit tests mostly related ot how EvalMath reacts to extreme case
            like qtype_calculated_find_formula_errors(1) test false.

            This is not a good test as you could have one of the answer where the formula is 1 as long as there is a least one param

            {x..}

            in one of the other answers.

            So "If making this changes does not break existing calculated questions" should test true

            So first let's explore what features EvalMath can add to calculated ...

            Show
            Pierre Pichet added a comment - - edited There are few failures of the actual calculated PHP Unit tests mostly related ot how EvalMath reacts to extreme case like qtype_calculated_find_formula_errors(1) test false. This is not a good test as you could have one of the answer where the formula is 1 as long as there is a least one param {x..} in one of the other answers. So "If making this changes does not break existing calculated questions" should test true So first let's explore what features EvalMath can add to calculated ...
            Hide
            Pierre Pichet added a comment -

            Among the first things to do is to create a local class and fill-in the function list to include all the functions of calculated
            ( https://docs.moodle.org/28/en/Calculated_question_type) as 'octdec','decoct'
            that are not already in EvalMath

            class CalculatedEvalMath extends EvalMath {
             
                var $fb = array(  // built-in functions
                    'abs','acos','acosh','asin','arcsin','arcsinh','asinh',
                     ...'octdec','decoct' ... 'tan','tanh' );    
            }
            

            Having a local class should give us additional flexibility

            Show
            Pierre Pichet added a comment - Among the first things to do is to create a local class and fill-in the function list to include all the functions of calculated ( https://docs.moodle.org/28/en/Calculated_question_type ) as 'octdec','decoct' that are not already in EvalMath class CalculatedEvalMath extends EvalMath {   var $fb = array( // built-in functions 'abs','acos','acosh','asin','arcsin','arcsinh','asinh', ...'octdec','decoct' ... 'tan','tanh' ); } Having a local class should give us additional flexibility
            Hide
            Pierre Pichet added a comment -

            "Having a local class should give us additional flexibility"

            EvalMath design does not allow a conditional structure as

            (numeric result) ?(value to return if numeric result is true) : (value to return if numeric result is false)

            so we need a prefunction that will split the formula in the 3 parts, use EvalMath to calculate each segment and return the result. (or any error).

            Anyway this is safer than to use eval() as in actual code

            eval('$ansvalue = '.$formula.';');

            Show
            Pierre Pichet added a comment - "Having a local class should give us additional flexibility" EvalMath design does not allow a conditional structure as (numeric result) ?(value to return if numeric result is true) : (value to return if numeric result is false) so we need a prefunction that will split the formula in the 3 parts, use EvalMath to calculate each segment and return the result. (or any error). Anyway this is safer than to use eval() as in actual code eval('$ansvalue = '.$formula.';');
            Hide
            Pierre Pichet added a comment - - edited

            we need prefunction that will split the formula in the 3 parts, use EvalMath to calculate each segment and return the result. (or any error).
            Done with something like

            require_once($CFG->libdir . '/evalmath/evalmath.class.php');
             
            class CalculatedEvalMath extends EvalMath {
                var $fb = array(  // built-in functions will contain all those already in calculated
                    'abs','acos','acosh','asin','asinh','sin','sinh','arcsin','arcsinh',
                    'cos','cosh','arccos','arccosh',
                    'tan','tanh','arctan','atan','arctanh','atanh',
                    'sqrt','ln','log','log10','exp','floor','ceil',
                    'octdec','decoct' );
             
                function evaluatecalculated($expr) {          
                    $regs = array();
                    preg_match_all('~[\\?:]~',$expr, $regs);
                    $count = 0;
                    if ( $regs[0] != null ){
                        foreach ($regs[0] as $ri => $rs) {
                            echo "<p>ri $ri rs $rs <p>"; // for testing 
                            if ($rs == ":"){
                                $count += -1;
                                if ($count < 0 ) return $this->trigger(get_string('preceding:informula', 'qtype_calculated', $rs));    
                            }
                            if ($rs == "?"){    
                                $count += 1;    
                            }                      
                        }    
                        if ($count > 0) return  $this->trigger(get_string('unbalanced?informula', 'qtype_calculated',$expr));
                        $res = preg_split('~[\\?:]~',$expr, -1);            
                        // Convert each part of valuetotest ? valueiftrue : valueiffalse using evaluate. 
                        foreach ($res as $ri => $resa) {
                            echo "<p>before evaluate $ri rs rs".$res[$ri]."<p>";// for testing
                            $res[$ri] = $this->evaluate($resa);
                            if (is_null($res[$ri])) return  $this->trigger(get_string('unbalanced?informula', 'qtype_calculated',$expr));
                            echo "<p>after evaluate $ri rs rs".$res[$ri]."<p>";// for testing
                        } 
                        // Start at 0.
                        $rebuilt="";
                        $ires = 0;
                        if(count($res) > count($regs[0]) ){ //there should be value?value:value
                            $rebuilt .= $res[$ires];
                        foreach ($regs[0] as $ri => $resa) {
                            $rebuilt .= $resa;   
                            $ires++;
                            $rebuilt .= $res[$ires];
                        }   
                    }    
                    echo "<p>refait $refait<p>";// for testing     
                        $str = null;
                    // We can use eval without problems as $rebuilt = number ? number : number  ;   
                        eval('$str = '.$rebuilt.';');
                    return $str;                          
                    }
                return $this->evaluate($expr);    
                }
            }
            

            Show
            Pierre Pichet added a comment - - edited we need prefunction that will split the formula in the 3 parts, use EvalMath to calculate each segment and return the result. (or any error). Done with something like require_once($CFG->libdir . '/evalmath/evalmath.class.php');   class CalculatedEvalMath extends EvalMath { var $fb = array( // built-in functions will contain all those already in calculated 'abs','acos','acosh','asin','asinh','sin','sinh','arcsin','arcsinh', 'cos','cosh','arccos','arccosh', 'tan','tanh','arctan','atan','arctanh','atanh', 'sqrt','ln','log','log10','exp','floor','ceil', 'octdec','decoct' );   function evaluatecalculated($expr) { $regs = array(); preg_match_all('~[\\?:]~',$expr, $regs); $count = 0; if ( $regs[0] != null ){ foreach ($regs[0] as $ri => $rs) { echo "<p>ri $ri rs $rs <p>"; // for testing if ($rs == ":"){ $count += -1; if ($count < 0 ) return $this->trigger(get_string('preceding:informula', 'qtype_calculated', $rs)); } if ($rs == "?"){ $count += 1; } } if ($count > 0) return $this->trigger(get_string('unbalanced?informula', 'qtype_calculated',$expr)); $res = preg_split('~[\\?:]~',$expr, -1); // Convert each part of valuetotest ? valueiftrue : valueiffalse using evaluate. foreach ($res as $ri => $resa) { echo "<p>before evaluate $ri rs rs".$res[$ri]."<p>";// for testing $res[$ri] = $this->evaluate($resa); if (is_null($res[$ri])) return $this->trigger(get_string('unbalanced?informula', 'qtype_calculated',$expr)); echo "<p>after evaluate $ri rs rs".$res[$ri]."<p>";// for testing } // Start at 0. $rebuilt=""; $ires = 0; if(count($res) > count($regs[0]) ){ //there should be value?value:value $rebuilt .= $res[$ires]; foreach ($regs[0] as $ri => $resa) { $rebuilt .= $resa; $ires++; $rebuilt .= $res[$ires]; } } echo "<p>refait $refait<p>";// for testing $str = null; // We can use eval without problems as $rebuilt = number ? number : number ; eval('$str = '.$rebuilt.';'); return $str; } return $this->evaluate($expr); } }
            Hide
            Pierre Pichet added a comment -

            CalculateEvalMath 2 worlds side by side in some cases

            (1.4)(1.9) = 2.66
            Correct answer : 2.66 inside limits of true value
            Min: 2.6334 --- Max: 2.6866
            (1.4)*(1.9) = 2.66
            Correct answer : 2.66 inside limits of true value
            Min: 2.6334 --- Max: 2.6866
             
            or
             
            pow(6.6,2) = 43.56
            Correct answer : 43.56 inside limits of true value
            Min: 43.1244 --- Max: 43.9956
            power(6.6,2) = 43.56
            Correct answer : 43.56 inside limits of true value
            Min: 43.1244 --- Max: 43.9956
            

            So we could use the preceeding moodle versions calculateds and add to the next i.e. 2,9 version at least the functions that are already defined in EvalMath or other ones.
            There are choices also about what additional features(response format, partial grading etc) that could be added without loosing the user ....
            or losing the main advantage of calculated ( and complexity) that the variants responses are checked before being submitted to the student.

            So Tim, how do we handle this opportunity (forum ?).

            So let use the request a peer review feature

            Show
            Pierre Pichet added a comment - CalculateEvalMath 2 worlds side by side in some cases (1.4)(1.9) = 2.66 Correct answer : 2.66 inside limits of true value Min: 2.6334 --- Max: 2.6866 (1.4)*(1.9) = 2.66 Correct answer : 2.66 inside limits of true value Min: 2.6334 --- Max: 2.6866   or   pow(6.6,2) = 43.56 Correct answer : 43.56 inside limits of true value Min: 43.1244 --- Max: 43.9956 power(6.6,2) = 43.56 Correct answer : 43.56 inside limits of true value Min: 43.1244 --- Max: 43.9956 So we could use the preceeding moodle versions calculateds and add to the next i.e. 2,9 version at least the functions that are already defined in EvalMath or other ones. There are choices also about what additional features(response format, partial grading etc) that could be added without loosing the user .... or losing the main advantage of calculated ( and complexity) that the variants responses are checked before being submitted to the student. So Tim, how do we handle this opportunity (forum ?). So let use the request a peer review feature
            Hide
            Pierre Pichet added a comment -

            As I wrote in a preceeding comment
            What do we plan now and how ?

            Show
            Pierre Pichet added a comment - As I wrote in a preceeding comment What do we plan now and how ?
            Hide
            CiBoT added a comment -

            Fails against automated checks.

            Error: the repository field is empty. Nothing was checked.

            • Testing instructions are missing.

            More information about this report

            Show
            CiBoT added a comment - Fails against automated checks. Error: the repository field is empty. Nothing was checked. Testing instructions are missing. More information about this report
            Hide
            Pierre Pichet added a comment -

            A technical dificulty in handling the additional functions and parameters.

            To simplify I add all additional PHP math functions by rebuilding $fb from evalmath in calculated questiontype.php
            As I want to control all the buitl-in functions I just redefine

            class CalculatedEvalMath extends EvalMath {
             
                var $fb = array(  // built-in functions
                    'abs','acos','acosh','asin','asinh','sin','sinh','arcsin','arcsinh',
                    'cos','cosh','arccos','arccosh',
                    'tan','tanh','arctan','atan','arctanh','atanh',
                    'sqrt','ln','log','log10','exp','floor','ceil',
                    'octdec','decoct' );
            

            To add the pow I add them in evalmath file as $fc is called as

            line 386 of evalmath.class.php file
            call_user_func_array(array('EvalMathFuncs', $fnn), array_reverse($args));

            class EvalMath {
                var $fc = array( // calc functions emulation
                    'average'=>array(-1), 'max'=>array(-1),  'min'=>array(-1),
                    'mod'=>array(2),      'pi'=>array(0),    'power'=>array(2), 'pow' = array(2),
                    'round'=>array(1, 2), 'sum'=>array(-1), 'rand_int'=>array(2),
                    'rand_float'=>array(0));
            

            and
            // spreadsheet functions emulation

            class EvalMathFuncs {
                static function pow($op1, $op2) {
                    return pow($op1, $op2);
                }
            ...
            

            Do we need to copy all the evalmath.class.php file in a calculated file so to have a complete control ?

            Show
            Pierre Pichet added a comment - A technical dificulty in handling the additional functions and parameters. To simplify I add all additional PHP math functions by rebuilding $fb from evalmath in calculated questiontype.php As I want to control all the buitl-in functions I just redefine class CalculatedEvalMath extends EvalMath {   var $fb = array( // built-in functions 'abs','acos','acosh','asin','asinh','sin','sinh','arcsin','arcsinh', 'cos','cosh','arccos','arccosh', 'tan','tanh','arctan','atan','arctanh','atanh', 'sqrt','ln','log','log10','exp','floor','ceil', 'octdec','decoct' ); To add the pow I add them in evalmath file as $fc is called as line 386 of evalmath.class.php file call_user_func_array(array('EvalMathFuncs', $fnn), array_reverse($args)); class EvalMath { var $fc = array( // calc functions emulation 'average'=>array(-1), 'max'=>array(-1), 'min'=>array(-1), 'mod'=>array(2), 'pi'=>array(0), 'power'=>array(2), 'pow' = array(2), 'round'=>array(1, 2), 'sum'=>array(-1), 'rand_int'=>array(2), 'rand_float'=>array(0)); and // spreadsheet functions emulation class EvalMathFuncs { static function pow($op1, $op2) { return pow($op1, $op2); } ... Do we need to copy all the evalmath.class.php file in a calculated file so to have a complete control ?
            Hide
            Pierre Pichet added a comment -

            I just put the code on github (just a working issue)

            Show
            Pierre Pichet added a comment - I just put the code on github (just a working issue)
            Hide
            Pierre Pichet added a comment -

            Tim,
            There is no hurry about how set the final code. The main thing is that we can improve calculated keeping the old questions active.
            I will first prepare some docs in the Development section to set a more complete proposal on what could be the features of the "new and improved" calculated questions.
            Then we could start an issue in the quiz forum somewhere in january.(Although my 3 childrens are 45-50 years old, I watch TV and take christmas holidays )

            Show
            Pierre Pichet added a comment - Tim, There is no hurry about how set the final code. The main thing is that we can improve calculated keeping the old questions active. I will first prepare some docs in the Development section to set a more complete proposal on what could be the features of the "new and improved" calculated questions. Then we could start an issue in the quiz forum somewhere in january.(Although my 3 childrens are 45-50 years old, I watch TV and take christmas holidays )
            Hide
            Pierre Pichet added a comment -

            I just push to github a "crude" solution where I create local variant
            class CalculatedEvalMath extends EvalMath where I copy parts of the variables and code so I can handle all the functions I we will need and use a copy the class EvalMathFuncs as class CalcEvalMathFuncs .

            This is working as shown in editing a calculated simple question.
            Set 1 pow(

            {x}

            ,2)+pow...
            pow(9.8,2)+power(9.8,2) = 192.08
            Correct answer : 192.08 inside limits of true value
            Min: 190.1592 — Max: 194.0008
            Apart putting this in the questiontype.php file
            Is it the best way to do it ???

            Show
            Pierre Pichet added a comment - I just push to github a "crude" solution where I create local variant class CalculatedEvalMath extends EvalMath where I copy parts of the variables and code so I can handle all the functions I we will need and use a copy the class EvalMathFuncs as class CalcEvalMathFuncs . This is working as shown in editing a calculated simple question. Set 1 pow( {x} ,2)+pow... pow(9.8,2)+power(9.8,2) = 192.08 Correct answer : 192.08 inside limits of true value Min: 190.1592 — Max: 194.0008 Apart putting this in the questiontype.php file Is it the best way to do it ???
            Hide
            Pierre Pichet added a comment -

            Learning is a continuous process.
            There is a better solution which is simply something like

            class CalcEvalMathFuncs extends EvalMathFuncs {
             
                static function pow($op1, $op2) {
                    return pow($op1, $op2);
                }
             
            }
            

            I did not see that EvalMathFuncs is an array of functions until this morning and that can it be manipulated as so

            Show
            Pierre Pichet added a comment - Learning is a continuous process. There is a better solution which is simply something like class CalcEvalMathFuncs extends EvalMathFuncs {   static function pow($op1, $op2) { return pow($op1, $op2); }   } I did not see that EvalMathFuncs is an array of functions until this morning and that can it be manipulated as so
            Hide
            Pierre Pichet added a comment -

            So I updated github and I go back to PHP manuals as a student

            Show
            Pierre Pichet added a comment - So I updated github and I go back to PHP manuals as a student
            Hide
            Pierre Pichet added a comment -

            One of my concerns is about code stability and testing. If we define

            class CalcEvalMathFuncs extends EvalMathFuncs {
             
                static function pow($op1, $op2) {
                    return pow($op1, $op2);
                }
            

            we also inherit of all the functions defined in EvalMathFuncs . If there modification of that class i.e. new functions they will be available for the user of calculated but not necessarily tested for calculated requirements.
            One way to limit is to define in calculated

            var $fb = array( // built-in functions
            'abs','acos','acosh','asin','asinh','sin','sinh','arcsin','arcsinh',
            'cos','cosh','arccos','arccosh',
            'tan','tanh','arctan','atan','arctanh','atanh',
            'sqrt','ln','log','log10','exp','floor','ceil',
            'octdec','decoct' );

            var $fc = array( // calc functions emulation
            'average'=>array(-1), 'max'=>array(-1), 'min'=>array(-1),
            'mod'=>array(2), 'pi'=>array(0), 'power'=>array(2), 'pow'=>array(2),
            'round'=>array(1, 2), 'sum'=>array(-1), 'rand_int'=>array(2),
            'rand_float'=>array(0));

            so the preceeding

             class CalcEvalMathFuncs {
                static function average() {
                    $args = func_get_args();
                    return (call_user_func_array(array('self', 'sum'), $args) / count($args));
                }
            ...
                static function max() {
                    $args = func_get_args();
              
                 static function pow($op1, $op2) {
                     return pow($op1, $op2);
            ...
                 }
            }
            

            is another way to control which functions are allowed in calculated.

            Show
            Pierre Pichet added a comment - One of my concerns is about code stability and testing. If we define class CalcEvalMathFuncs extends EvalMathFuncs {   static function pow($op1, $op2) { return pow($op1, $op2); } we also inherit of all the functions defined in EvalMathFuncs . If there modification of that class i.e. new functions they will be available for the user of calculated but not necessarily tested for calculated requirements. One way to limit is to define in calculated var $fb = array( // built-in functions 'abs','acos','acosh','asin','asinh','sin','sinh','arcsin','arcsinh', 'cos','cosh','arccos','arccosh', 'tan','tanh','arctan','atan','arctanh','atanh', 'sqrt','ln','log','log10','exp','floor','ceil', 'octdec','decoct' ); var $fc = array( // calc functions emulation 'average'=>array(-1), 'max'=>array(-1), 'min'=>array(-1), 'mod'=>array(2), 'pi'=>array(0), 'power'=>array(2), 'pow'=>array(2), 'round'=>array(1, 2), 'sum'=>array(-1), 'rand_int'=>array(2), 'rand_float'=>array(0)); so the preceeding class CalcEvalMathFuncs { static function average() { $args = func_get_args(); return (call_user_func_array(array('self', 'sum'), $args) / count($args)); } ... static function max() { $args = func_get_args(); static function pow($op1, $op2) { return pow($op1, $op2); ... } } is another way to control which functions are allowed in calculated.
            Hide
            Tim Hunt added a comment -

            Sorry for the slow reply. It has been a busy week.

            We really don't want to copy anything from the EvalMathFuncs class.

            I am thinking that several of those functions would be useful additions to the gradebook. Therefore, my suggestion is to make a new MDL issue in order to add the new functions to the core EvalMathFuncs class. Then we submit that for integation as a separate change.

            Once that has been integrated, then we can change qtype_calculated to use it.

            Show
            Tim Hunt added a comment - Sorry for the slow reply. It has been a busy week. We really don't want to copy anything from the EvalMathFuncs class. I am thinking that several of those functions would be useful additions to the gradebook. Therefore, my suggestion is to make a new MDL issue in order to add the new functions to the core EvalMathFuncs class. Then we submit that for integation as a separate change. Once that has been integrated, then we can change qtype_calculated to use it.
            Hide
            Pierre Pichet added a comment -

            There is 2 lists of functions useable in EvalMath : primary function with 1 parameter that are listed in $fb

             var $fb = array( // built-in functions
            'sin','sinh','arcsin','asin','arcsinh','asinh',
            'cos','cosh','arccos','acos','arccosh','acosh',
            'tan','tanh','arctan','atan','arctanh','atanh',
            'sqrt','abs','ln','log','exp','floor','ceil');
            

            There is also the list of primary functions with 0 or more than 1 paramer that are listed in $fc

             var $fc = array( // calc functions emulation
            'average'=>array(-1), 'max'=>array(-1), 'min'=>array(-1),
            'mod'=>array(2), 'pi'=>array(0), 'power'=>array(2),
            'round'=>array(1, 2), 'sum'=>array(-1), 'rand_int'=>array(2),
            'rand_float'=>array(0));
            

            My objective is to be able to used all these functions already available in EvalMath

            +
            primary Math functions that are in actual calculated but NOT in EvalMath (like pow)

            + other functions that we will want to use now or later in calculated that would be added either in $fb or $fc folowing their parameters.

            This why I declare in $fb or $fc in class CalculatedEvalMath extends EvalMath
            and the code of new functions NOT already set in class EvalMathFuncs { are declared in local as

            class CalcEvalMathFuncs extends EvalMathFuncs {

            So the expansion of calculated available functions can be done without any interference from EvalMath addtional new functions. If the use of some new calculated function can be generalized in EvalMath then Evalmath code could be improved.
            But I will say that your proposal to improve EvalMath should be done once we will have created and tested a new efficient function in calculated question.
            For this issue I think that we should first merge EvalMath in calculated so that all actual old calculated remain valid and could benefit from the new functions as newly created calculated.

            My example is that an old calculated question with

             pow({x},2) can use side by side power({x},2)

            and obtains the same result.

            Show
            Pierre Pichet added a comment - There is 2 lists of functions useable in EvalMath : primary function with 1 parameter that are listed in $fb var $fb = array( // built-in functions 'sin','sinh','arcsin','asin','arcsinh','asinh', 'cos','cosh','arccos','acos','arccosh','acosh', 'tan','tanh','arctan','atan','arctanh','atanh', 'sqrt','abs','ln','log','exp','floor','ceil'); There is also the list of primary functions with 0 or more than 1 paramer that are listed in $fc var $fc = array( // calc functions emulation 'average'=>array(-1), 'max'=>array(-1), 'min'=>array(-1), 'mod'=>array(2), 'pi'=>array(0), 'power'=>array(2), 'round'=>array(1, 2), 'sum'=>array(-1), 'rand_int'=>array(2), 'rand_float'=>array(0)); My objective is to be able to used all these functions already available in EvalMath + primary Math functions that are in actual calculated but NOT in EvalMath (like pow) + other functions that we will want to use now or later in calculated that would be added either in $fb or $fc folowing their parameters. This why I declare in $fb or $fc in class CalculatedEvalMath extends EvalMath and the code of new functions NOT already set in class EvalMathFuncs { are declared in local as class CalcEvalMathFuncs extends EvalMathFuncs { So the expansion of calculated available functions can be done without any interference from EvalMath addtional new functions. If the use of some new calculated function can be generalized in EvalMath then Evalmath code could be improved. But I will say that your proposal to improve EvalMath should be done once we will have created and tested a new efficient function in calculated question. For this issue I think that we should first merge EvalMath in calculated so that all actual old calculated remain valid and could benefit from the new functions as newly created calculated. My example is that an old calculated question with pow({x},2) can use side by side power({x},2) and obtains the same result.
            Hide
            Tim Hunt added a comment -

            I still think I would extend core EvalMath. Adding new functions there will not break anything, and might be useful in the gradebook or variablenumeric question types. It also avoids having to copy a whole function just to change the call_user_func_array(array('CalcEvalMathFuncs', $fnn) bit.

            However, you are doing the work, so I am happy for you to do it the way you think is best.

            Just a suggestion, but rather than copying the var $fb and var $fc arrays, instead you can do:

            function __construct() {
                $fc['pow'] = 2;
                $fc['average'] = -1;
                $fc['max'] = -1;
            }
            

            That makes it easier to see which functions are being added.

            Show
            Tim Hunt added a comment - I still think I would extend core EvalMath. Adding new functions there will not break anything, and might be useful in the gradebook or variablenumeric question types. It also avoids having to copy a whole function just to change the call_user_func_array(array('CalcEvalMathFuncs', $fnn) bit. However, you are doing the work, so I am happy for you to do it the way you think is best. Just a suggestion, but rather than copying the var $fb and var $fc arrays, instead you can do: function __construct() { $fc [ 'pow' ] = 2; $fc [ 'average' ] = -1; $fc [ 'max' ] = -1; } That makes it easier to see which functions are being added.
            Hide
            Pierre Pichet added a comment - - edited

            I have added a page in docs to describe the things to do for each functions.

            https://docs.moodle.org/dev/Calculated_question_:_adding_new_math_functions#Available_functions

            Although it is not completed for all function, it illustrates which functions already exist in either Calculated or EvalMath so they should already be usable when the issue is completed.
            If they already exist in EvalMath then they should already work in the actual MDL-18141 code.
            Some need to be created in EvalMath , i.e. new .
            However they could be created only in CalculatedEvalMath. i.e this is the case of pow() .

            Show
            Pierre Pichet added a comment - - edited I have added a page in docs to describe the things to do for each functions. https://docs.moodle.org/dev/Calculated_question_:_adding_new_math_functions#Available_functions Although it is not completed for all function, it illustrates which functions already exist in either Calculated or EvalMath so they should already be usable when the issue is completed. If they already exist in EvalMath then they should already work in the actual MDL-18141 code. Some need to be created in EvalMath , i.e. new . However they could be created only in CalculatedEvalMath. i.e this is the case of pow() .
            Hide
            Pierre Pichet added a comment - - edited

            *I still think I would extend core EvalMath. Adding new functions there will not break anything, and might be useful in the gradebook or variablenumeric question types. *
            So as you agree, I will extend core EvalMath .
            However in order to test for bad values the actual calculated allow syntax involving

            • is_finite
            • is_infinite
            • is_nan

            So in actual 2.8 Moodle demo syntax like these are working OK.

            2.0*4-(2.0==5?0.8:0)+5.4 = 13.40
            Correct answer : 13.40 inside limits of true value
            Min: 13.27 --- Max: 13.53
            is_infinite((9.8+6.8)/1)?4:5 = 5.00
            Correct answer : 5.00 inside limits of true value
            Min: 4.95 --- Max: 5.05
            
            

            but not in the actual issue even if I add is_infinite in the available functions.
            I understand that this is quite tricky but most probably OK given the way EvalMath works.
            So let's put this on january todo list

            Show
            Pierre Pichet added a comment - - edited *I still think I would extend core EvalMath. Adding new functions there will not break anything, and might be useful in the gradebook or variablenumeric question types. * So as you agree, I will extend core EvalMath . However in order to test for bad values the actual calculated allow syntax involving is_finite is_infinite is_nan So in actual 2.8 Moodle demo syntax like these are working OK. 2.0*4-(2.0==5?0.8:0)+5.4 = 13.40 Correct answer : 13.40 inside limits of true value Min: 13.27 --- Max: 13.53 is_infinite((9.8+6.8)/1)?4:5 = 5.00 Correct answer : 5.00 inside limits of true value Min: 4.95 --- Max: 5.05 but not in the actual issue even if I add is_infinite in the available functions. I understand that this is quite tricky but most probably OK given the way EvalMath works. So let's put this on january todo list
            Hide
            Pierre Pichet added a comment -

            In calculated question

            • is_finite
            • is_infinite
            • is_nan
              could ( should) only be used in the ?: structure and can ( I tested) be filtered in calculated when handling the ?: structure.
              I don't think that they should be allowed in EvalMath as they are part of the error code in EvalMath.
            Show
            Pierre Pichet added a comment - In calculated question is_finite is_infinite is_nan could ( should) only be used in the ?: structure and can ( I tested) be filtered in calculated when handling the ?: structure. I don't think that they should be allowed in EvalMath as they are part of the error code in EvalMath.
            Hide
            Pierre Pichet added a comment -

            Just a little comment that life has modified todo january list to todo february list

            Show
            Pierre Pichet added a comment - Just a little comment that life has modified todo january list to todo february list

              People

              • Votes:
                0 Vote for this issue
                Watchers:
                4 Start watching this issue

                Dates

                • Created:
                  Updated: