Skip to content

Commit

Permalink
fix localisation of decimals in tokenToDisplayString and treeToJME
Browse files Browse the repository at this point in the history
fixes #1091 and #1092

Numbas.jme.tokenToDisplayString now uses the jmeifier, which uses the
preferred number notation setting.

The JMEifier now uses the 'plain' notation' when rendering decimals, so
they use the correct notation inside the `dec()` function.
  • Loading branch information
christianp committed Mar 6, 2024
1 parent 81ffc88 commit 00b14e4
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 53 deletions.
33 changes: 28 additions & 5 deletions runtime/scripts/jme-display.js
Original file line number Diff line number Diff line change
Expand Up @@ -1740,7 +1740,7 @@ var typeToJME = Numbas.jme.display.typeToJME = {
}
},
'decimal': function(tree,tok,bits) {
return this.jmeDecimal(tok.value, number_options(tok));
return this.decimal(tok.value, number_options(tok));
},
'number': function(tree,tok,bits,settings) {
return this.number(tok.value, number_options(tok));
Expand Down Expand Up @@ -2154,6 +2154,29 @@ JMEifier.prototype = {
return math.niceNumber(n,options);
},

/** Call {@link Numbas.math.niceNumber} with the scope's symbols for the imaginary unit and circle constant.
*
* @param {number} n
* @param {Numbas.math.niceNumber_settings} options
* @returns {string}
*/
niceDecimal: function(n, options) {
options = options || {};
if(this.common_constants.imaginary_unit) {
options.imaginary_unit = this.common_constants.imaginary_unit.name;
}
if(this.common_constants.pi) {
options.circle_constant = {
scale: this.common_constants.pi.scale,
symbol: this.common_constants.pi.constant.name
};
}
if(this.common_constants.infinity) {
options.infinity = this.common_constants.infinity.name;
}
return math.niceComplexDecimal(n,options);
},

/** Write a number in JME syntax as a fraction, using {@link Numbas.math.rationalApproximation}.
*
* @memberof Numbas.jme.display
Expand Down Expand Up @@ -2280,17 +2303,17 @@ JMEifier.prototype = {
* @param {Numbas.math.niceNumber_settings} options
* @returns {JME}
*/
jmeDecimal: function(n, options) {
decimal: function(n, options) {
if(n instanceof Numbas.math.ComplexDecimal) {
var re = this.jmeDecimal(n.re,options);
var re = this.decimal(n.re,options);
if(n.isReal()) {
return re;
}
var imaginary_unit = 'sqrt(-1)';
if(this.common_constants.imaginary_unit) {
imaginary_unit = this.common_constants.imaginary_unit.name;
}
var im = this.jmeDecimal(n.im,options)+'*'+imaginary_unit;
var im = this.decimal(n.im,options)+'*'+imaginary_unit;
if(n.re.isZero()) {
if(n.im.eq(1)) {
return imaginary_unit;
Expand All @@ -2313,7 +2336,7 @@ JMEifier.prototype = {
}
}
} else if(n instanceof Decimal) {
var out = math.niceDecimal(n, this.settings.plaindecimal ? {} : options);
var out = math.niceDecimal(n, Object.assign({}, this.settings.plaindecimal ? {} : options, {style: 'plain'}));
if(this.settings.plaindecimal) {
return out;
} else {
Expand Down
16 changes: 4 additions & 12 deletions runtime/scripts/jme.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,18 +493,10 @@ var jme = Numbas.jme = /** @lends Numbas.jme */ {
var f = v.value.reduced();
return f.toString();
},
'decimal': function(v) {
var d = v.value;
var re = d.re.toString();
if(d.isReal()) {
return re;
}
var im = d.im.absoluteValue().toString();
if(d.im.lessThan(0)) {
return re + ' - '+im+'i';
} else {
return re + ' + '+im+'i';
}
'decimal': function(v, scope) {
var jmeifier = new Numbas.jme.display.JMEifier({},scope);
var options = Numbas.jme.display.number_options(v);
return jmeifier.niceDecimal(v.value, options);
},
'string': function(v,display) {
return v.value;
Expand Down
51 changes: 33 additions & 18 deletions tests/jme-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -8892,18 +8892,10 @@ var jme = Numbas.jme = /** @lends Numbas.jme */ {
var f = v.value.reduced();
return f.toString();
},
'decimal': function(v) {
var d = v.value;
var re = d.re.toString();
if(d.isReal()) {
return re;
}
var im = d.im.absoluteValue().toString();
if(d.im.lessThan(0)) {
return re + ' - '+im+'i';
} else {
return re + ' + '+im+'i';
}
'decimal': function(v, scope) {
var jmeifier = new Numbas.jme.display.JMEifier({},scope);
var options = Numbas.jme.display.number_options(v);
return jmeifier.niceDecimal(v.value, options);
},
'string': function(v,display) {
return v.value;
Expand Down Expand Up @@ -11401,7 +11393,7 @@ var TNum = types.TNum = function(num) {

function number_to_decimal(n, precisionType, precision) {
var dp = 15;
if(precisionType == 'dp') {
if(precisionType == 'dp' && isFinite(precision)) {
dp = Math.max(dp, -precision);
}
var re,im;
Expand Down Expand Up @@ -18491,7 +18483,7 @@ var typeToJME = Numbas.jme.display.typeToJME = {
}
},
'decimal': function(tree,tok,bits) {
return this.jmeDecimal(tok.value, number_options(tok));
return this.decimal(tok.value, number_options(tok));
},
'number': function(tree,tok,bits,settings) {
return this.number(tok.value, number_options(tok));
Expand Down Expand Up @@ -18905,6 +18897,29 @@ JMEifier.prototype = {
return math.niceNumber(n,options);
},

/** Call {@link Numbas.math.niceNumber} with the scope's symbols for the imaginary unit and circle constant.
*
* @param {number} n
* @param {Numbas.math.niceNumber_settings} options
* @returns {string}
*/
niceDecimal: function(n, options) {
options = options || {};
if(this.common_constants.imaginary_unit) {
options.imaginary_unit = this.common_constants.imaginary_unit.name;
}
if(this.common_constants.pi) {
options.circle_constant = {
scale: this.common_constants.pi.scale,
symbol: this.common_constants.pi.constant.name
};
}
if(this.common_constants.infinity) {
options.infinity = this.common_constants.infinity.name;
}
return math.niceComplexDecimal(n,options);
},

/** Write a number in JME syntax as a fraction, using {@link Numbas.math.rationalApproximation}.
*
* @memberof Numbas.jme.display
Expand Down Expand Up @@ -19031,17 +19046,17 @@ JMEifier.prototype = {
* @param {Numbas.math.niceNumber_settings} options
* @returns {JME}
*/
jmeDecimal: function(n, options) {
decimal: function(n, options) {
if(n instanceof Numbas.math.ComplexDecimal) {
var re = this.jmeDecimal(n.re,options);
var re = this.decimal(n.re,options);
if(n.isReal()) {
return re;
}
var imaginary_unit = 'sqrt(-1)';
if(this.common_constants.imaginary_unit) {
imaginary_unit = this.common_constants.imaginary_unit.name;
}
var im = this.jmeDecimal(n.im,options)+'*'+imaginary_unit;
var im = this.decimal(n.im,options)+'*'+imaginary_unit;
if(n.re.isZero()) {
if(n.im.eq(1)) {
return imaginary_unit;
Expand All @@ -19064,7 +19079,7 @@ JMEifier.prototype = {
}
}
} else if(n instanceof Decimal) {
var out = math.niceDecimal(n, this.settings.plaindecimal ? {} : options);
var out = math.niceDecimal(n, Object.assign({}, this.settings.plaindecimal ? {} : options, {style: 'plain'}));
if(this.settings.plaindecimal) {
return out;
} else {
Expand Down
19 changes: 19 additions & 0 deletions tests/jme/jme-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,16 @@ Numbas.queueScript('jme_tests',['qunit','jme','jme-rules','jme-display','jme-cal
assert.equal(Numbas.jme.tokenToDisplayString(Numbas.jme.builtinScope.evaluate('vector(5pi)')),'vector(5 pi)','vector(5pi)')
assert.equal(Numbas.jme.tokenToDisplayString(Numbas.jme.builtinScope.evaluate('precround(2,3)')),'2.000','precround(2,3)')
assert.equal(Numbas.jme.tokenToDisplayString(Numbas.jme.builtinScope.evaluate('siground(21,3)')),'21.0','siground(21,3)')

try {
assert.equal(Numbas.jme.tokenToDisplayString(Numbas.jme.builtinScope.evaluate('1.2')),'1.2','1.2 in locale en-GB')
assert.equal(Numbas.jme.tokenToDisplayString(Numbas.jme.builtinScope.evaluate('dec(1.2)')),'1.2','dec(1.2) in locale en-GB')
Numbas.locale.set_preferred_locale('nb-NO');
assert.equal(Numbas.jme.tokenToDisplayString(Numbas.jme.builtinScope.evaluate('1.2')),'1,2','1.2 in locale nb-NO')
assert.equal(Numbas.jme.tokenToDisplayString(Numbas.jme.builtinScope.evaluate('dec(1.2)')),'1,2','dec(1.2) in locale nb-NO')
} finally {
Numbas.locale.set_preferred_locale('en-GB');
}
});

QUnit.test('tree to JME', function(assert) {
Expand All @@ -1986,6 +1996,15 @@ Numbas.queueScript('jme_tests',['qunit','jme','jme-rules','jme-display','jme-cal
assert.equal(jmeifier.number({complex: true, im: -Math.PI, re: 0}),'-pi*i','jmeNumber on -pi*i puts an asterisk in');
assert.equal(jmeifier.number({complex: true, im: Math.PI, re: 1}),'1 + pi*i','jmeNumber on 1 + pi*i puts an asterisk in');
assert.equal(jmeifier.number({complex: true, im: Math.PI, re: 0}),'pi*i','jmeNumber on pi*i puts an asterisk in');
try {
assert.equal(jmeifier.decimal(new math.ComplexDecimal(new Decimal(1.2))),'dec("1.2")','jmeNumber on dec(1.2) in locale en-GB');
Numbas.locale.set_preferred_locale('nb-NO');
assert.equal(jmeifier.decimal(new math.ComplexDecimal(new Decimal(1.2))),'dec("1.2")','jmeNumber on dec(1.2) in locale nb-NO');
assert.equal(jmeifier.decimal(new math.ComplexDecimal(new Decimal(1.2), new Decimal(-3.4))),'dec("1.2") - dec("3.4")*i','jmeNumber on dec(1.2) - dec(3.4)*i in locale nb-NO');
assert.equal(jmeifier.decimal(new math.ComplexDecimal(new Decimal(1.2)), {style: 'plain-eu'}),'dec("1.2")','jmeNumber on dec(1.2) in locale nb-NO, even when forcing style: plain-eu');
} finally {
Numbas.locale.set_preferred_locale('en-GB');
}
assert.equal(Numbas.jme.display.treeToJME({tok:Numbas.jme.builtinScope.evaluate('dec(1)+dec("-15.460910528400001612")*i')}), 'dec("1") - dec("1.5460910528400001612e+1")*i', 'jmeDecimal shows negative imaginary parts properly');
assert.equal(simplifyExpression('-1*x*3'),'-1x*3','pull minus to left of product');
assert.equal(simplifyExpression('2*pi*i','basic'),'2pi*i','2*pi*i unchanged by basic rules');
Expand Down
51 changes: 33 additions & 18 deletions tests/numbas-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -8483,18 +8483,10 @@ var jme = Numbas.jme = /** @lends Numbas.jme */ {
var f = v.value.reduced();
return f.toString();
},
'decimal': function(v) {
var d = v.value;
var re = d.re.toString();
if(d.isReal()) {
return re;
}
var im = d.im.absoluteValue().toString();
if(d.im.lessThan(0)) {
return re + ' - '+im+'i';
} else {
return re + ' + '+im+'i';
}
'decimal': function(v, scope) {
var jmeifier = new Numbas.jme.display.JMEifier({},scope);
var options = Numbas.jme.display.number_options(v);
return jmeifier.niceDecimal(v.value, options);
},
'string': function(v,display) {
return v.value;
Expand Down Expand Up @@ -10992,7 +10984,7 @@ var TNum = types.TNum = function(num) {

function number_to_decimal(n, precisionType, precision) {
var dp = 15;
if(precisionType == 'dp') {
if(precisionType == 'dp' && isFinite(precision)) {
dp = Math.max(dp, -precision);
}
var re,im;
Expand Down Expand Up @@ -18082,7 +18074,7 @@ var typeToJME = Numbas.jme.display.typeToJME = {
}
},
'decimal': function(tree,tok,bits) {
return this.jmeDecimal(tok.value, number_options(tok));
return this.decimal(tok.value, number_options(tok));
},
'number': function(tree,tok,bits,settings) {
return this.number(tok.value, number_options(tok));
Expand Down Expand Up @@ -18496,6 +18488,29 @@ JMEifier.prototype = {
return math.niceNumber(n,options);
},

/** Call {@link Numbas.math.niceNumber} with the scope's symbols for the imaginary unit and circle constant.
*
* @param {number} n
* @param {Numbas.math.niceNumber_settings} options
* @returns {string}
*/
niceDecimal: function(n, options) {
options = options || {};
if(this.common_constants.imaginary_unit) {
options.imaginary_unit = this.common_constants.imaginary_unit.name;
}
if(this.common_constants.pi) {
options.circle_constant = {
scale: this.common_constants.pi.scale,
symbol: this.common_constants.pi.constant.name
};
}
if(this.common_constants.infinity) {
options.infinity = this.common_constants.infinity.name;
}
return math.niceComplexDecimal(n,options);
},

/** Write a number in JME syntax as a fraction, using {@link Numbas.math.rationalApproximation}.
*
* @memberof Numbas.jme.display
Expand Down Expand Up @@ -18622,17 +18637,17 @@ JMEifier.prototype = {
* @param {Numbas.math.niceNumber_settings} options
* @returns {JME}
*/
jmeDecimal: function(n, options) {
decimal: function(n, options) {
if(n instanceof Numbas.math.ComplexDecimal) {
var re = this.jmeDecimal(n.re,options);
var re = this.decimal(n.re,options);
if(n.isReal()) {
return re;
}
var imaginary_unit = 'sqrt(-1)';
if(this.common_constants.imaginary_unit) {
imaginary_unit = this.common_constants.imaginary_unit.name;
}
var im = this.jmeDecimal(n.im,options)+'*'+imaginary_unit;
var im = this.decimal(n.im,options)+'*'+imaginary_unit;
if(n.re.isZero()) {
if(n.im.eq(1)) {
return imaginary_unit;
Expand All @@ -18655,7 +18670,7 @@ JMEifier.prototype = {
}
}
} else if(n instanceof Decimal) {
var out = math.niceDecimal(n, this.settings.plaindecimal ? {} : options);
var out = math.niceDecimal(n, Object.assign({}, this.settings.plaindecimal ? {} : options, {style: 'plain'}));
if(this.settings.plaindecimal) {
return out;
} else {
Expand Down

0 comments on commit 00b14e4

Please sign in to comment.