/** * Validate the formula. * @param string $formula * @return boolean true if calculation possible, false otherwise */ function validate_formula($formulastr) { global $CFG; require_once $CFG->libdir . '/mathslib.php'; $formulastr = grade_item::normalize_formula($formulastr, $this->courseid); if (empty($formulastr)) { return true; } if (strpos($formulastr, '=') !== 0) { return get_string('errorcalculationnoequal', 'grades'); } // get used items if (preg_match_all('/##gi(\\d+)##/', $formulastr, $matches)) { $useditems = array_unique($matches[1]); // remove duplicates } else { $useditems = array(); } // MDL-11902 // unset the value if formula is trying to reference to itself // but array keys does not match itemid if (!empty($this->id)) { $useditems = array_diff($useditems, array($this->id)); //unset($useditems[$this->id]); } // prepare formula and init maths library $formula = preg_replace('/##(gi\\d+)##/', '\\1', $formulastr); $formula = new calc_formula($formula); if (empty($useditems)) { $grade_items = array(); } else { $gis = implode(',', $useditems); $sql = "SELECT gi.*\n FROM {$CFG->prefix}grade_items gi\n WHERE gi.id IN ({$gis}) and gi.courseid={$this->courseid}"; // from the same course only! if (!($grade_items = get_records_sql($sql))) { $grade_items = array(); } } $params = array(); foreach ($useditems as $itemid) { // make sure all grade items exist in this course if (!array_key_exists($itemid, $grade_items)) { return false; } // use max grade when testing formula, this should be ok in 99.9% // division by 0 is one of possible problems $params['gi' . $grade_items[$itemid]->id] = $grade_items[$itemid]->grademax; } // do the calculation $formula->set_params($params); $result = $formula->evaluate(); // false as result indicates some problem if ($result === false) { // TODO: add more error hints return get_string('errorcalculationunknown', 'grades'); } else { return true; } }
public function test_scientific_notation() { $formula = new calc_formula('=10e10'); $this->assertWithinMargin($formula->evaluate(), 100000000000.0, 100000000000.0 * 1.0E-15); $formula = new calc_formula('=10e-10'); $this->assertWithinMargin($formula->evaluate(), 1.0E-9, 100000000000.0 * 1.0E-15); $formula = new calc_formula('=10e+10'); $this->assertWithinMargin($formula->evaluate(), 100000000000.0, 100000000000.0 * 1.0E-15); $formula = new calc_formula('=10e10*5'); $this->assertWithinMargin($formula->evaluate(), 500000000000.0, 100000000000.0 * 1.0E-15); $formula = new calc_formula('=10e10^2'); $this->assertWithinMargin($formula->evaluate(), 1.0E+22, 1.0E+22 * 1.0E-15); }
public function test_rand_float() { $formula = new calc_formula('=rand_float()'); $result = $formula->evaluate(); $this->assertTrue(is_float($result)); }
/** * Tests special chars */ function test__specialchars() { $formula = new calc_formula('=gi1 + gi2 + gi11', array('gi1' => 10, 'gi2' => 20, 'gi11' => 30)); $res = $formula->evaluate(); $this->assertEqual($res, 60, 'sum is: %s'); }
/** * */ protected function process_calculations($text) { global $CFG; // HACK removing occurences of [[entryid]] because they are // currently not resolved in new entries. $text = str_replace('[[entryid]]', '', $text); if (preg_match_all("/%%F\\d*:=[^%]*%%/", $text, $matches)) { require_once "{$CFG->libdir}/mathslib.php"; sort($matches[0]); // List of formulas. $formulas = array(); // Formula replacements. $replacements = array(); // Register all formulas according to formula identifier. foreach ($matches[0] as $pattern) { $cleanpattern = trim($pattern, '%'); list($fid, $formula) = explode(':=', $cleanpattern, 2); // Skip an empty formula. if (empty($formula) and $formula !== 0) { continue; } isset($formulas[$fid]) or $formulas[$fid] = array(); // Enclose formula in brackets to preserve precedence. $formulas[$fid][] = "({$formula})"; $replacements[$pattern] = $formula; } // Process group formulas in formulas (e.g. _F1_). // We put 0 if we can't find a replacement so that the formula could be calculated. foreach ($formulas as $fid => $formulae) { foreach ($formulae as $key => $formula) { if (preg_match_all("/_F\\d+_/", $formula, $frefs)) { foreach ($frefs[0] as $fref) { $fref = trim($fref, '_'); if (isset($formulas[$fref])) { $replace = implode(',', $formulas[$fref]); } else { $replace = 0; } $formula = str_replace("_{$fref}_", $replace, $formula); } $formulae[$key] = $formula; } } $formulas[$fid] = $formulae; } // Process group formulas in replacements (e.g. _F1_). // We put 0 if we can't find a replacement so that the formula could be calculated. foreach ($replacements as $pattern => $formula) { if (preg_match_all("/_F\\d+_/", $formula, $frefs)) { foreach ($frefs[0] as $fref) { $fref = trim($fref, '_'); if (isset($formulas[$fref])) { $replace = implode(',', $formulas[$fref]); } else { $replace = 0; } $formula = str_replace("_{$fref}_", $replace, $formula); } $replacements[$pattern] = $formula; } } // Calculate. foreach ($replacements as $pattern => $formula) { // Number of decimals can be set as ;n at the end of the formula. $decimals = null; if (strpos($formula, ';')) { list($formula, $decimals) = explode(';', $formula); } $calc = new \calc_formula("={$formula}"); $result = $calc->evaluate(); if ($result === false) { // False as result indicates some problem. // We remove the formula altogether. $replacements[$pattern] = ''; } else { // Set decimals. if (is_numeric($decimals)) { $result = sprintf("%4.{$decimals}f", $result); } $replacements[$pattern] = $result; } } $text = str_replace(array_keys($replacements), $replacements, $text); } return $text; }
/** * Returns user's calculated grades for the specified grade item. * * @param grade_item $gradeitem * @param array $userids The user ids whose grades should be retrieved. * @return array|null */ protected function get_user_grades_calculated($gradeitem, array $userids) { global $CFG; if (!($df = $this->df)) { return null; } require_once "{$CFG->libdir}/mathslib.php"; $formula = $gradeitem->gradecalc; // Patterns container. $patterns = array(); // Users container. $users = array_fill_keys($userids, array()); // Grades container. $grades = array(); foreach ($users as $userid => $unused) { $grades[$userid] = (object) array('id' => $userid, 'userid' => $userid, 'rawgrade' => null); // Num entries pattern. if (strpos($formula, '##numentries##') !== false) { $patterns['##numentries##'] = 0; if ($numentries = $df->get_entries_count_per_user($df::COUNT_ALL, $userid)) { foreach ($numentries as $userid => $count) { $users[$userid]['##numentries##'] = $count->numentries; } } } // Extract grading field patterns from the formula. if (preg_match_all("/##\\d*:[^#]+##/", $formula, $matches)) { // Get the entry ids per user. $entryids = $df->get_entry_ids_per_user($userid); foreach ($matches[0] as $pattern) { $patterns[$pattern] = 0; list($targetval, $fieldpattern) = explode(':', trim($pattern, '#'), 2); // Get the field from the pattern. if (!($field = $df->field_manager->get_field_by_pattern("[[{$fieldpattern}]]"))) { continue; } $uservalues = null; // Get user values for the pattern. // The field must either has helper\contentperuser component, // or be an instance of interface grading. $helper = "dataformfield_{$field->type}\\helper\\contentperuser"; if (class_exists($helper)) { $uservalues = $helper::get_content($field, $fieldpattern, $entryids); } else { if ($field instanceof mod_dataform\interfaces\grading) { // BC - this method for grading user values is depracated. $uservalues = $field->get_user_values($fieldpattern, $entryids, $userid); } } // Leave pattern value at 0 if no user values. if (!$uservalues) { continue; } // Register pattern values for users. foreach ($uservalues as $userid => $values) { // Keep only target val if specified. if ($targetval) { foreach ($values as $key => $value) { if ($value != $targetval) { unset($values[$key]); } } } if ($values) { $users[$userid][$pattern] = implode(',', $values); } } } } } // For each user calculate the formula and create a grade object. foreach ($grades as $userid => $grade) { // If no values, no grade for this user. if (empty($users[$userid])) { continue; } $values = $users[$userid]; $replacements = array_merge($patterns, $values); $calculation = str_replace(array_keys($replacements), $replacements, $formula); $calc = new calc_formula("={$calculation}"); $result = $calc->evaluate(); // False as result indicates some problem. if ($result !== false) { $grade->rawgrade = $result; } } return $grades; }
public function test_scientific_notation() { $formula = new calc_formula('=10e10'); $this->assertEquals($formula->evaluate(), 1e11, '', 1e11*1e-15); $formula = new calc_formula('=10e-10'); $this->assertEquals($formula->evaluate(), 1e-9, '', 1e11*1e-15); $formula = new calc_formula('=10e+10'); $this->assertEquals($formula->evaluate(), 1e11, '', 1e11*1e-15); $formula = new calc_formula('=10e10*5'); $this->assertEquals($formula->evaluate(), 5e11, '', 1e11*1e-15); $formula = new calc_formula('=10e10^2'); $this->assertEquals($formula->evaluate(), 1e22, '', 1e22*1e-15); }