diff --git a/lib/questionlib.php b/lib/questionlib.php index db7be64e8b8..7b74499c194 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -209,39 +209,44 @@ function get_grade_options() { } /** - * match grade options - * if no match return error or match nearest + * Check whether a given grade is one of a list of allowed options. If not, + * depending on $matchgrades, either return the nearest match, or return false + * to signal an error. * @param array $gradeoptionsfull list of valid options * @param int $grade grade to be tested * @param string $matchgrades 'error' or 'nearest' - * @return mixed either 'fixed' value or false if erro + * @return mixed either 'fixed' value or false if error. */ -function match_grade_options($gradeoptionsfull, $grade, $matchgrades='error') { +function match_grade_options($gradeoptionsfull, $grade, $matchgrades = 'error') { + if ($matchgrades == 'error') { - // if we just need an error... + // (Almost) exact match, or an error. foreach ($gradeoptionsfull as $value => $option) { - // slightly fuzzy test, never check floats for equality :-) + // Slightly fuzzy test, never check floats for equality. if (abs($grade - $value) < 0.00001) { - return $grade; + return $value; // Be sure the return the proper value. } } - // didn't find a match so that's an error + // Didn't find a match so that's an error. return false; + } else if ($matchgrades == 'nearest') { - // work out nearest value - $hownear = array(); + // Work out nearest value + $best = false; + $bestmismatch = 2; foreach ($gradeoptionsfull as $value => $option) { - if ($grade==$value) { - return $grade; + $newmismatch = abs($grade - $value); + if ($newmismatch < $bestmismatch) { + $best = $value; + $bestmismatch = $newmismatch; } - $hownear[ $value ] = abs( $grade - $value ); } - // reverse sort list of deltas and grab the last (smallest) - asort( $hownear, SORT_NUMERIC ); - reset( $hownear ); - return key( $hownear ); + return $best; + } else { - return false; + // Unknow option passed. + throw new coding_exception('Unknown $matchgrades ' . $matchgrades . + ' passed to match_grade_options'); } } diff --git a/lib/tests/questionlib_test.php b/lib/tests/questionlib_test.php index 2e5d431cb63..d7d767d8343 100644 --- a/lib/tests/questionlib_test.php +++ b/lib/tests/questionlib_test.php @@ -55,4 +55,19 @@ class questionlib_testcase extends basic_testcase { array(0 => 't1', 1 => 't2', 2 => 't3')); } + public function test_match_grade_options() { + $gradeoptions = question_bank::fraction_options_full(); + + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.3333333, 'error')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.333333, 'error')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33333, 'error')); + $this->assertFalse(match_grade_options($gradeoptions, 0.3333, 'error')); + + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.3333333, 'nearest')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.333333, 'nearest')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33333, 'nearest')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33, 'nearest')); + + $this->assertEquals(-0.1428571, match_grade_options($gradeoptions, -0.15, 'nearest')); + } }