diff --git a/marking_scripts/numberentry.jme b/marking_scripts/numberentry.jme index a7b76545b..32c0e90b4 100644 --- a/marking_scripts/numberentry.jme +++ b/marking_scripts/numberentry.jme @@ -45,7 +45,7 @@ scientific_precision_offset (A number in scientific notation has 1 more signific studentPrecision: max(settings["precision"], switch( - student_is_scientific, countsigfigs(studentAnswer)-scientific_precision_offset, + student_is_scientific, countsigfigs(cleanedStudentAnswer)-scientific_precision_offset, settings["precisionType"]="dp", max(settings["precision"],countdp(cleanedStudentAnswer)), settings["precisionType"]="sigfig", max(settings["precision"],countsigfigs(cleanedStudentAnswer)), 0 diff --git a/runtime/scripts/math.js b/runtime/scripts/math.js index 10a3c2c6a..952e7df5d 100644 --- a/runtime/scripts/math.js +++ b/runtime/scripts/math.js @@ -1250,9 +1250,10 @@ var math = Numbas.math = /** @lends Numbas.math */ { return dp; } }, - /** Calculate the significant figures precision of a number. + /** + * Calculate the significant figures precision of a number. * - * @param {number|string} n + * @param {number|string} n - if a string, only the "plain" number format or scientific notation is expected. Strings representing numbers should be cleaned first, using `Numbas.util.cleanNumber`. * @param {boolean} [max] - Be generous with calculating sig. figs. for whole numbers. e.g. '1000' could be written to 4 sig figs. * @returns {number} */ @@ -1260,9 +1261,9 @@ var math = Numbas.math = /** @lends Numbas.math */ { n += ''; var m; if(max) { - m = n.match(/^-?(?:(\d0*)$|(?:([1-9]\d*[1-9]0*)$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)[Ee][+\-]?\d+)$)/i); + m = n.match(/^-?(?:(\d0*)$|(?:([1-9]\d*[1-9]0*)$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)\s*[Ee]\s*[+\-]?\d+)$)/i); } else { - m = n.match(/^-?(?:(\d)0*$|(?:([1-9]\d*[1-9])0*$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)[Ee][+\-]?\d+)$)/i); + m = n.match(/^-?(?:(\d)0*$|(?:([1-9]\d*[1-9])0*$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)\s*[Ee]\s*[+\-]?\d+)$)/i); } if(!m) return 0; diff --git a/runtime/scripts/util.js b/runtime/scripts/util.js index 71f0c8833..d16a645ee 100644 --- a/runtime/scripts/util.js +++ b/runtime/scripts/util.js @@ -1538,7 +1538,7 @@ var numberNotationStyles = util.numberNotationStyles = { }, // Significand-exponent ("scientific") style 'scientific': { - re: /^(\d[ \d]*)(\x2E\d[ \d]*)?[eE]([\-+]?\d[ \d]*)/, + re: /^(\d[ \d]*)(\x2E\d[ \d]*)?\s*[eE]\s*([\-+]?\d[ \d]*)/, clean: function(m) { return Numbas.math.unscientific(m[0]); }, diff --git a/tests/jme-runtime.js b/tests/jme-runtime.js index 7e4adec06..ad8514280 100644 --- a/tests/jme-runtime.js +++ b/tests/jme-runtime.js @@ -1946,7 +1946,7 @@ var numberNotationStyles = util.numberNotationStyles = { }, // Significand-exponent ("scientific") style 'scientific': { - re: /^(\d[ \d]*)(\x2E\d[ \d]*)?[eE]([\-+]?\d[ \d]*)/, + re: /^(\d[ \d]*)(\x2E\d[ \d]*)?\s*[eE]\s*([\-+]?\d[ \d]*)/, clean: function(m) { return Numbas.math.unscientific(m[0]); }, @@ -3471,9 +3471,10 @@ var math = Numbas.math = /** @lends Numbas.math */ { return dp; } }, - /** Calculate the significant figures precision of a number. + /** + * Calculate the significant figures precision of a number. * - * @param {number|string} n + * @param {number|string} n - if a string, only the "plain" number format or scientific notation is expected. Strings representing numbers should be cleaned first, using `Numbas.util.cleanNumber`. * @param {boolean} [max] - Be generous with calculating sig. figs. for whole numbers. e.g. '1000' could be written to 4 sig figs. * @returns {number} */ @@ -3481,9 +3482,9 @@ var math = Numbas.math = /** @lends Numbas.math */ { n += ''; var m; if(max) { - m = n.match(/^-?(?:(\d0*)$|(?:([1-9]\d*[1-9]0*)$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)[Ee][+\-]?\d+)$)/i); + m = n.match(/^-?(?:(\d0*)$|(?:([1-9]\d*[1-9]0*)$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)\s*[Ee]\s*[+\-]?\d+)$)/i); } else { - m = n.match(/^-?(?:(\d)0*$|(?:([1-9]\d*[1-9])0*$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)[Ee][+\-]?\d+)$)/i); + m = n.match(/^-?(?:(\d)0*$|(?:([1-9]\d*[1-9])0*$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)\s*[Ee]\s*[+\-]?\d+)$)/i); } if(!m) return 0; diff --git a/tests/jme/jme-tests.mjs b/tests/jme/jme-tests.mjs index 6a7327cd8..ed117c229 100644 --- a/tests/jme/jme-tests.mjs +++ b/tests/jme/jme-tests.mjs @@ -420,6 +420,7 @@ Numbas.queueScript('jme_tests',['qunit','jme','jme-rules','jme-display','jme-cal assert.equal(Numbas.math.countSigFigs('1.23E6'),3,"math.countSigFigs('1.23e6')==3"); assert.equal(Numbas.math.countSigFigs('1.23E-6'),3,"math.countSigFigs('1.23e-6')==3"); assert.equal(Numbas.math.countSigFigs('1.20e6',5),3,"math.countSigFigs('1.20e6',5)==3 (the max setting doesn't have any meaning for E notation)"); + assert.equal(Numbas.math.countSigFigs('1,20',5),0,"math.countSigFigs('1,20',5)==1 (only plain notation is expected, so the comma used by Europen notation isn't recognised)"); assert.ok(Numbas.math.eq(NaN,NaN),'NaN = NaN'); assert.notOk(Numbas.math.eq({complex:true,re:1,im:1},{complex:true,re:1,im:2})); assert.notOk(Numbas.math.eq(Infinity,1)); diff --git a/tests/marking_scripts.js b/tests/marking_scripts.js index ea08bc31b..20b5c190f 100644 --- a/tests/marking_scripts.js +++ b/tests/marking_scripts.js @@ -9,7 +9,7 @@ Numbas.queueScript('marking_scripts',['marking'],function() { "multipleresponse": "numAnswers: len(settings[\"matrix\"])\n\nnumChoices: if(numAnswers=1,1,len(settings[\"matrix\"][0]))\n\nnumTicks (How many options did the student tick?):\n sum(map(sum(map(if(x,1,0),x,row)),row,studentAnswer))\n\nwrongNumber:\n assert(numTicks >= settings[\"minAnswers\"] and (settings[\"maxAnswers\"]=0 or numTicks<=settings[\"maxAnswers\"]),\n if(settings[\"warningType\"]=\"prevent\",\n fail(translate(\"part.mcq.wrong number of choices\"))\n ,\n incorrect(translate(\"part.mcq.wrong number of choices\"));\n end()\n )\n )\n\ntick_indexes (Indexes of choice/answer pairs):\n flatten(map(\n map([x,y], x, shuffleAnswers),\n y,\n shuffleChoices\n ))\n\nonly_ticked_score_ticks (The score for each choice/answer pair):\n map(\n if(studentAnswer[x][y],\n let(distractor,settings[\"distractors\"][x][y], credit, if(marks=0,0,settings[\"matrix\"][x][y]/marks),\n switch(\n credit<>0,\n if(not isnonemptyhtml(distractor),\n add_credit(credit,translate(if(credit>0,'part.mcq.correct choice','part.mcq.incorrect choice')))\n ,\n add_credit(credit,distractor)\n )\n ,\n if(isnonemptyhtml(distractor),negative_feedback(distractor),if(marks<>0,negative_feedback(translate('part.mcq.incorrect choice')),false))\n );credit\n )\n ,\n 0\n ),\n [x,y],\n tick_indexes\n )\n\n\nlayout_tick_indexes (Indexes of choice/answer pairs shown in the layout):\n filter(layout[tick[0]][tick[1]],tick,tick_indexes)\n\nbinary_score_ticks (Scores and feedback for each choice/answer pair, in the \"binary\" marking method):\n let(\n per_tick, 1/len(layout_tick_indexes),\n scores,map(\n let(distractor,settings[\"distractors\"][x][y],\n should_tick, settings[\"matrix\"][x][y]>0,\n if(studentAnswer[x][y]=should_tick,\n per_tick\n ,\n assert(not isnonemptyhtml(distractor),negative_feedback(distractor));\n 0\n )\n ),\n [x,y],\n layout_tick_indexes\n ),\n total, sum(scores),\n switch(\n total=1,correct(),\n total=0 or settings[\"markingMethod\"]=\"all-or-nothing\",incorrect(),\n set_credit(total,translate('part.marking.partially correct'))\n )\n )\n\nscore_ticks:\n switch(\n settings[\"markingMethod\"] in [\"score per matched cell\",\"all-or-nothing\"], apply(binary_score_ticks);binary_score_ticks,\n apply(only_ticked_score_ticks);only_ticked_score_ticks\n )\n\ntotal_score: \n sum(score_ticks)\n\nmark:\n assert(marks>0,correct()); // any answer is correct when 0 marks are available\n assert(settings[\"markingMethod\"]<>\"sum ticked cells\" or numTicks>0,\n warn(translate(\"part.marking.nothing entered\"));\n fail(translate(\"part.marking.nothing entered\"))\n );\n apply(wrongNumber);\n apply(score_ticks)\n\ninterpreted_answer (The student's answer, to be reused by other parts):\n studentAnswer\n", "numberentry": -"studentNumber (The student's answer, parsed as a number):\n if(settings[\"allowFractions\"],\n parsedecimal_or_fraction(studentAnswer,settings[\"notationStyles\"])\n ,\n parsedecimal(studentAnswer,settings[\"notationStyles\"])\n )\n\nisInteger (Is the student's answer an integer?):\n countdp(studentAnswer)=0\n\nisFraction (Is the student's answer a fraction?):\n \"/\" in studentAnswer\n\nnumerator (The numerator of the student's answer, or 0 if not a fraction):\n if(isFraction,\n parsenumber(split(studentAnswer,\"/\")[0],settings[\"notationStyles\"])\n ,\n 0\n )\n\ndenominator (The numerator of the student's answer, or 0 if not a fraction):\n if(isFraction,\n parsenumber(split(studentAnswer,\"/\")[1],settings[\"notationStyles\"])\n ,\n 0\n )\n\ncancelled (Is the student's answer a cancelled fraction?):\n assert(isFraction and gcd(numerator,denominator)=1,\n assert(not settings[\"mustBeReduced\"],\n multiply_credit(settings[\"mustBeReducedPC\"],translate(\"part.numberentry.answer not reduced\"))\n );\n false\n )\n\ncleanedStudentAnswer:\n cleannumber(studentAnswer, settings[\"notationStyles\"])\n\nstudent_is_scientific (Is the student's answer written in scientific notation?):\n not isnan(matchnumber(studentAnswer, [\"scientific\"])[1])\n\nscientific_precision_offset (A number in scientific notation has 1 more significant digit than decimal places):\n award(1,settings[\"precisionType\"]=\"dp\")\n\nstudentPrecision:\n max(settings[\"precision\"],\n switch(\n student_is_scientific, countsigfigs(studentAnswer)-scientific_precision_offset,\n settings[\"precisionType\"]=\"dp\", max(settings[\"precision\"],countdp(cleanedStudentAnswer)),\n settings[\"precisionType\"]=\"sigfig\", max(settings[\"precision\"],countsigfigs(cleanedStudentAnswer)),\n 0\n )\n )\n\nraw_minvalue:\n switch(\n student_is_scientific, siground(settings[\"minvalue\"],studentPrecision+scientific_precision_offset),\n settings[\"precisionType\"]=\"dp\", precround(settings[\"minvalue\"],studentPrecision),\n settings[\"precisionType\"]=\"sigfig\", siground(settings[\"minvalue\"],studentPrecision),\n settings[\"minvalue\"]\n )\n\nraw_maxvalue:\n switch(\n student_is_scientific, siground(settings[\"maxvalue\"],studentPrecision+scientific_precision_offset),\n settings[\"precisionType\"]=\"dp\", precround(settings[\"maxvalue\"],studentPrecision),\n settings[\"precisionType\"]=\"sigfig\", siground(settings[\"maxvalue\"],studentPrecision),\n settings[\"maxvalue\"]\n )\n\nminvalue: min(raw_minvalue,raw_maxvalue)\n\nmaxvalue: max(raw_minvalue,raw_maxvalue)\n\nvalidNumber (Is the student's answer a valid number?):\n if(isnan(studentNumber),\n warn(translate(\"part.numberentry.answer invalid\"));\n fail(translate(\"part.numberentry.answer invalid\"))\n ,\n true\n )\n\nnumberInRange (Is the student's number in the allowed range?):\n if(studentNumber>=minvalue and studentNumber<=maxvalue,\n correct()\n ,\n incorrect();\n end()\n )\n\ncorrectPrecision (Has the student's answer been given to the desired precision?): \n if(\n if(student_is_scientific,\n togivenprecision_scientific(studentAnswer,settings['precisionType'],settings['precision']),\n togivenprecision(cleanedStudentAnswer,settings['precisionType'],settings['precision'],settings[\"strictPrecision\"])\n )\n ,\n true\n ,\n multiply_credit(settings[\"precisionPC\"],settings[\"precisionMessage\"]);\n false\n )\n\nmark (Mark the student's answer):\n apply(validNumber);\n apply(numberInRange);\n assert(numberInRange,end());\n if(isFraction,\n apply(cancelled)\n ,\n apply(correctPrecision)\n )\n \ninterpreted_answer (The student's answer, to be reused by other parts):\n apply(validNumber);\n studentNumber\n\n", +"studentNumber (The student's answer, parsed as a number):\n if(settings[\"allowFractions\"],\n parsedecimal_or_fraction(studentAnswer,settings[\"notationStyles\"])\n ,\n parsedecimal(studentAnswer,settings[\"notationStyles\"])\n )\n\nisInteger (Is the student's answer an integer?):\n countdp(studentAnswer)=0\n\nisFraction (Is the student's answer a fraction?):\n \"/\" in studentAnswer\n\nnumerator (The numerator of the student's answer, or 0 if not a fraction):\n if(isFraction,\n parsenumber(split(studentAnswer,\"/\")[0],settings[\"notationStyles\"])\n ,\n 0\n )\n\ndenominator (The numerator of the student's answer, or 0 if not a fraction):\n if(isFraction,\n parsenumber(split(studentAnswer,\"/\")[1],settings[\"notationStyles\"])\n ,\n 0\n )\n\ncancelled (Is the student's answer a cancelled fraction?):\n assert(isFraction and gcd(numerator,denominator)=1,\n assert(not settings[\"mustBeReduced\"],\n multiply_credit(settings[\"mustBeReducedPC\"],translate(\"part.numberentry.answer not reduced\"))\n );\n false\n )\n\ncleanedStudentAnswer:\n cleannumber(studentAnswer, settings[\"notationStyles\"])\n\nstudent_is_scientific (Is the student's answer written in scientific notation?):\n not isnan(matchnumber(studentAnswer, [\"scientific\"])[1])\n\nscientific_precision_offset (A number in scientific notation has 1 more significant digit than decimal places):\n award(1,settings[\"precisionType\"]=\"dp\")\n\nstudentPrecision:\n max(settings[\"precision\"],\n switch(\n student_is_scientific, countsigfigs(cleanedStudentAnswer)-scientific_precision_offset,\n settings[\"precisionType\"]=\"dp\", max(settings[\"precision\"],countdp(cleanedStudentAnswer)),\n settings[\"precisionType\"]=\"sigfig\", max(settings[\"precision\"],countsigfigs(cleanedStudentAnswer)),\n 0\n )\n )\n\nraw_minvalue:\n switch(\n student_is_scientific, siground(settings[\"minvalue\"],studentPrecision+scientific_precision_offset),\n settings[\"precisionType\"]=\"dp\", precround(settings[\"minvalue\"],studentPrecision),\n settings[\"precisionType\"]=\"sigfig\", siground(settings[\"minvalue\"],studentPrecision),\n settings[\"minvalue\"]\n )\n\nraw_maxvalue:\n switch(\n student_is_scientific, siground(settings[\"maxvalue\"],studentPrecision+scientific_precision_offset),\n settings[\"precisionType\"]=\"dp\", precround(settings[\"maxvalue\"],studentPrecision),\n settings[\"precisionType\"]=\"sigfig\", siground(settings[\"maxvalue\"],studentPrecision),\n settings[\"maxvalue\"]\n )\n\nminvalue: min(raw_minvalue,raw_maxvalue)\n\nmaxvalue: max(raw_minvalue,raw_maxvalue)\n\nvalidNumber (Is the student's answer a valid number?):\n if(isnan(studentNumber),\n warn(translate(\"part.numberentry.answer invalid\"));\n fail(translate(\"part.numberentry.answer invalid\"))\n ,\n true\n )\n\nnumberInRange (Is the student's number in the allowed range?):\n if(studentNumber>=minvalue and studentNumber<=maxvalue,\n correct()\n ,\n incorrect();\n end()\n )\n\ncorrectPrecision (Has the student's answer been given to the desired precision?): \n if(\n if(student_is_scientific,\n togivenprecision_scientific(studentAnswer,settings['precisionType'],settings['precision']),\n togivenprecision(cleanedStudentAnswer,settings['precisionType'],settings['precision'],settings[\"strictPrecision\"])\n )\n ,\n true\n ,\n multiply_credit(settings[\"precisionPC\"],settings[\"precisionMessage\"]);\n false\n )\n\nmark (Mark the student's answer):\n apply(validNumber);\n apply(numberInRange);\n assert(numberInRange,end());\n if(isFraction,\n apply(cancelled)\n ,\n apply(correctPrecision)\n )\n \ninterpreted_answer (The student's answer, to be reused by other parts):\n apply(validNumber);\n studentNumber\n\n", "patternmatch": "regex_match (Match the student's answer with the correct answer, interpreted as a regular expression):\n match_regex(settings[\"correctAnswer\"],studentAnswer,\"u\")\n\nregex_match_case_insensitive (Match the student's answer with the correct answer, interpreted as a case-insensitive regular expression):\n match_regex(settings[\"correctAnswer\"],studentAnswer,\"iu\")\n\nexact_match (Is the student's answer exactly the correct answer?):\n studentAnswer=settings[\"correctAnswer\"]\n\nexact_match_case_insensitive (Is the student's answer exactly the correct answer?):\n lower(studentAnswer)=lower(settings[\"correctAnswer\"])\n\nmatches (Does the student's answer match the correct answer?):\n switch(\n settings[\"matchMode\"]=\"regex\", len(regex_match)>0,\n settings[\"matchMode\"]=\"exact\", exact_match,\n false\n )\n \nmatches_case_insensitive (Does the student's answer match the correct answer, ignoring case?):\n switch(\n settings[\"matchMode\"]=\"regex\", len(regex_match_case_insensitive)>0,\n settings[\"matchMode\"]=\"exact\", exact_match_case_insensitive,\n false\n )\n \n\nmark:\n assert(len(studentAnswer)>0,\n warn(translate(\"part.marking.nothing entered\"));\n fail(translate(\"part.marking.nothing entered\"))\n );\n if(settings[\"caseSensitive\"],\n if(matches,\n correct(),\n if(matches_case_insensitive,\n set_credit(settings[\"partialCredit\"],translate(\"part.patternmatch.correct except case\")),\n incorrect()\n )\n )\n ,\n if(matches_case_insensitive,\n correct()\n ,\n incorrect()\n )\n )\n\ninterpreted_answer (The student's answer, to be reused by other parts):\n studentAnswer\n" }; diff --git a/tests/numbas-runtime.js b/tests/numbas-runtime.js index 003b1dbb9..f43018418 100644 --- a/tests/numbas-runtime.js +++ b/tests/numbas-runtime.js @@ -1947,7 +1947,7 @@ var numberNotationStyles = util.numberNotationStyles = { }, // Significand-exponent ("scientific") style 'scientific': { - re: /^(\d[ \d]*)(\x2E\d[ \d]*)?[eE]([\-+]?\d[ \d]*)/, + re: /^(\d[ \d]*)(\x2E\d[ \d]*)?\s*[eE]\s*([\-+]?\d[ \d]*)/, clean: function(m) { return Numbas.math.unscientific(m[0]); }, @@ -3472,9 +3472,10 @@ var math = Numbas.math = /** @lends Numbas.math */ { return dp; } }, - /** Calculate the significant figures precision of a number. + /** + * Calculate the significant figures precision of a number. * - * @param {number|string} n + * @param {number|string} n - if a string, only the "plain" number format or scientific notation is expected. Strings representing numbers should be cleaned first, using `Numbas.util.cleanNumber`. * @param {boolean} [max] - Be generous with calculating sig. figs. for whole numbers. e.g. '1000' could be written to 4 sig figs. * @returns {number} */ @@ -3482,9 +3483,9 @@ var math = Numbas.math = /** @lends Numbas.math */ { n += ''; var m; if(max) { - m = n.match(/^-?(?:(\d0*)$|(?:([1-9]\d*[1-9]0*)$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)[Ee][+\-]?\d+)$)/i); + m = n.match(/^-?(?:(\d0*)$|(?:([1-9]\d*[1-9]0*)$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)\s*[Ee]\s*[+\-]?\d+)$)/i); } else { - m = n.match(/^-?(?:(\d)0*$|(?:([1-9]\d*[1-9])0*$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)[Ee][+\-]?\d+)$)/i); + m = n.match(/^-?(?:(\d)0*$|(?:([1-9]\d*[1-9])0*$)|([1-9]\d*\.\d+$)|(0\.0+$)|(?:0\.0*([1-9]\d*))|(?:(\d*(?:\.\d+)?)\s*[Ee]\s*[+\-]?\d+)$)/i); } if(!m) return 0; @@ -21264,6 +21265,8 @@ if(res) { \ this.markingFeedback = result.markingFeedback.slice(); this.finalised_result = result.finalised_result; this.adaptiveMarkingUsed = result.adaptiveMarkingUsed; + this.best_alternative = result.best_alternative; + this.script_result = result.script_result; this.marking_values = result.values; this.credit = result.credit; this.answered = result.answered; @@ -21556,6 +21559,8 @@ if(res) { \ return { warnings: this.warnings.slice(), markingFeedback: this.markingFeedback.slice(), + best_alternative: altres.best_alternative, + script_result: res.script_result, finalised_result: res.finalised_result, values: res.values, credit: this.credit, diff --git a/tests/parts/part-tests.mjs b/tests/parts/part-tests.mjs index 5f1b34823..b0dcf3d52 100644 --- a/tests/parts/part-tests.mjs +++ b/tests/parts/part-tests.mjs @@ -261,6 +261,17 @@ Numbas.queueScript('part_tests',['qunit','json','jme','localisation','parts/numb var res = await mark_part(p,'13000'); assert.equal(res.credit,1,'"13000" correct'); }); + QUnit.test('Answer is 123, only scientific notation allowed', async function(assert) { + var p = createPartFromJSON({type:'numberentry', minValue: '123', maxValue: '123', notationStyles: ['scientific']}); + var res = await mark_part(p,'1.23e2'); + assert.equal(res.credit, 1,'"1.23e2" is correct'); + var res = await mark_part(p,'1.23e+2'); + assert.equal(res.credit, 1,'"1.23e+2" is correct'); + var res = await mark_part(p,'1.23 e 2'); + assert.equal(res.credit, 1,'"1.23 e 2" is correct'); + var res = await mark_part(p,'123'); + assert.equal(res.credit, 0,'"123" is incorrect'); + }); QUnit.test('Don\'t mark infinity correct', async function(assert) { var p = createPartFromJSON({"type":"numberentry","useCustomName":false,"customName":"","marks":1,"showCorrectAnswer":true,"showFeedbackIcon":true,"scripts":{},"variableReplacements":[],"variableReplacementStrategy":"originalfirst","adaptiveMarkingPenalty":0,"customMarkingAlgorithm":"","extendBaseMarkingAlgorithm":true,"unitTests":[],"minValue":"1","maxValue":"1","correctAnswerFraction":false,"allowFractions":false,"mustBeReduced":false,"mustBeReducedPC":0,"showFractionHint":true,"notationStyles":["plain","en","si-en"],"correctAnswerStyle":"plain"});