Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd916ab7

Browse files
committed
test: 'Number' canonical-form tests
1 parentc275e52 commitd916ab7

File tree

2 files changed

+218
-10
lines changed

2 files changed

+218
-10
lines changed

‎src/compute-engine/global-types.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,9 @@ export interface BoxedExpression {
451451
*
452452
* A symbol has a `"Symbol"` operator.
453453
*
454-
* A number has a `"Number"`, `"Real"`, `"Rational"` or `"Integer"` operator.
454+
* A number has a `"Number"`, `"Real"`, `"Rational"` or `"Integer"` operator; amongst some others.
455+
* Practically speaking, for fully canonical and valid expressions, all of these are likely to
456+
* collapse to `"Number"`.
455457
*
456458
*/
457459
readonlyoperator:string;

‎test/compute-engine/canonical-form.test.ts‎

Lines changed: 215 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import{ComputeEngine}from'../../src/compute-engine';
1+
import{ComputeEngine,NumericType}from'../../src/compute-engine';
22
import{_BoxedExpression}from'../../src/compute-engine/boxed-expression/abstract-boxed-expression';
33
import{check,exprToString,engineasTEST_ENGINE}from'../utils';
44
importtype{
@@ -504,8 +504,8 @@ describe('CANONICAL FORMS', () => {
504504
`);
505505

506506
/*
507-
* Constant-symbol value cases.
508-
*/
507+
* Constant-symbol value cases.
508+
*/
509509
// p === +Infinity (& 'holdUntil: never')
510510
expect(checkPower('1^{p}')).toMatchInlineSnapshot(`
511511
box = ["Power", 1, "p"]
@@ -666,6 +666,212 @@ describe('CANONICAL FORMS', () => {
666666
`);
667667
});
668668
});
669+
670+
describe('Number',()=>{
671+
constce=TEST_ENGINE;
672+
letexpr:string|SemiBoxedExpression;
673+
674+
constnonCanon=(input:string|SemiBoxedExpression)=>
675+
typeofinput==='string'
676+
?ce.parse(input,{canonical:false})
677+
:ce.box(input,{canonical:false});
678+
constcheckNumber=(input:string|SemiBoxedExpression)=>
679+
checkForms(input,['Number'],ce);
680+
constcanonNumber=(input:string|SemiBoxedExpression)=>
681+
typeofinput==='string'
682+
?ce.parse(input,{canonical:'Number'})
683+
:ce.box(input,{canonical:'Number'});
684+
685+
//*note*: BoxedNumbers may get JSON serialized as ['Rational',...] ('check' outputs JSON): so
686+
//need to additionally test for the result 'operator' as 'Number' for desired result.
687+
test(`'Rational' or 'Divide' (expr.) -> number (when valid)`,()=>{
688+
expr='1/3';
689+
//(•see above note: both 'canonForms' & 'canonical' are serialized as 'Rational', but may still
690+
//be numbers)
691+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
692+
box = ["Divide", 1, 3]
693+
canonForms = ["Rational", 1, 3]
694+
canonical = ["Rational", 1, 3]
695+
`);
696+
expect(canonNumber(expr).operator).toBe('Number');
697+
698+
expr='3/7';
699+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
700+
box = ["Divide", 3, 7]
701+
canonForms = ["Rational", 3, 7]
702+
canonical = ["Rational", 3, 7]
703+
`);
704+
expect(canonNumber(expr).operator).toBe('Number');
705+
706+
//(case of direct number serialization (denominator of 1))
707+
expr='2/1';
708+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
709+
box = ["Divide", 2, 1]
710+
canonForms = 2
711+
canonical = 2
712+
`);
713+
expect(canonNumber(expr).operator).toBe('Number');
714+
715+
/*
716+
* BigNum (Decimal)
717+
*/
718+
//@note: as of time of writing (CE 0.29.1), the test engine which test-cases in this block use
719+
//has a precision set to `100`: so all BigNum/Int digits are output.
720+
constbigRational:SemiBoxedExpression=[
721+
'Divide',
722+
'318982460862894267352492496399',
723+
'-358796515092200247647243',
724+
];
725+
expect(checkNumber(bigRational)).toMatchInlineSnapshot(`
726+
box = [
727+
"Divide",
728+
{num: "318982460862894267352492496399"},
729+
{num: "-358796515092200247647243"}
730+
]
731+
canonForms = [
732+
"Rational",
733+
{num: "-318982460862894267352492496399"},
734+
{num: "358796515092200247647243"}
735+
]
736+
canonical = [
737+
"Rational",
738+
{num: "-318982460862894267352492496399"},
739+
{num: "358796515092200247647243"}
740+
]
741+
`);
742+
expect(canonNumber(expr).operator).toBe('Number');// ✔
743+
744+
/*
745+
* Control
746+
*/
747+
expr='13.19/7.7';
748+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
749+
box = ["Divide", 13.19, 7.7]
750+
canonForms = ["Divide", 13.19, 7.7]
751+
`);
752+
});
753+
754+
test(`'Rational' (expr.) -> Divide (when non-rational)`,()=>{
755+
expect(checkNumber(['Rational',1,'Pi'])).toMatchInlineSnapshot(`
756+
box = ["Rational", 1, "Pi"]
757+
canonForms = ["Divide", 1, "Pi"]
758+
canonical = ["Divide", 1, "Pi"]
759+
`);
760+
expect(checkNumber(['Rational',7.01,3])).toMatchInlineSnapshot(`
761+
box = ["Rational", 7.01, 3]
762+
canonForms = ["Divide", 7.01, 3]
763+
canonical = ["Divide", 7.01, 3]
764+
`);
765+
//(↓For full-canonical, additional simplifications applied (hence 'Multiply'))
766+
expect(checkNumber(['Rational','ExponentialE',2]))
767+
.toMatchInlineSnapshot(`
768+
box = ["Rational", "ExponentialE", 2]
769+
canonForms = ["Divide", "ExponentialE", 2]
770+
canonical = ["Multiply", ["Rational", 1, 2], "ExponentialE"]
771+
`);
772+
});
773+
774+
//@wip?
775+
// -Presently, 'Complex'-operator exprs. are the only ones cast as BoxedNumber for this form.
776+
// Later potential candidates include those related which take place in Add/Multiply (see
777+
// https://github.com/cortex-js/compute-engine/pull/238#discussion_r2033792056)
778+
describe("'Complex' exprs.",()=>{
779+
//@note: 'check' is not helpful here, since a `['Complex', ...]` expr. is 'pretty'-printed for
780+
//each variant: whereas we want to test the 'operator'/type
781+
test(`Convert to eqv. BoxedNumbers`,()=>{
782+
constexpComplexNum=(
783+
expr:string|SemiBoxedExpression,
784+
type?:NumericType
785+
)=>{
786+
expect(nonCanon(expr).operator).toMatchInlineSnapshot(`Complex`);
787+
expect(canonNumber(expr).operator).toBe(`Number`);
788+
expect(canonNumber(expr).type.matches(type??'complex')).toBe(true);
789+
};
790+
791+
// Imaginary (only)
792+
expr=['Complex',1];
793+
expComplexNum(expr,'imaginary');
794+
795+
expr=['Complex',['Rational',1,43]];
796+
expComplexNum(expr,'imaginary');
797+
798+
// finite_complex / complex
799+
expr=['Complex',3,4];
800+
expComplexNum(expr,'finite_complex');
801+
802+
//@fixme
803+
//(A present bug: that bignum args. get truncated when canonicalized as a complex-number:
804+
//regardless of set precision)
805+
//@note: precision is '100' for the engine used here...
806+
expr=['Complex','22975850700614579948873711',4];// bigIntRe
807+
expComplexNum(expr,'finite_complex');
808+
809+
expr=['Complex',Infinity,4];// bigIntRe
810+
expComplexNum(expr,'complex');
811+
});
812+
813+
test(`Convert to 'Add', when imaginary arg. is non-numeric`,()=>{
814+
expr=['Complex',4,'x'];
815+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
816+
box = ["Complex", 4, "x"]
817+
canonForms = ["Add", ["Multiply", ["Complex", 0, 1], "x"], 4]
818+
canonical = ["Add", ["Multiply", ["Complex", 0, 1], "x"], 4]
819+
`);
820+
821+
expr=['Complex',1,['Multiply','n',4]];
822+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
823+
box = ["Complex", 1, ["Multiply", "n", 4]]
824+
canonForms = ["Add", ["Multiply", ["Complex", 0, 4], "n"], 1]
825+
canonical = ["Add", ["Multiply", ["Complex", 0, 4], "n"], 1]
826+
`);
827+
});
828+
});
829+
830+
test("Cast 'Negate' wrapped numbers as number exprs. ",()=>{
831+
expect(checkNumber('-1')).toMatchInlineSnapshot(`
832+
box = -1
833+
canonForms = -1
834+
`);
835+
836+
expect(checkNumber('-3.6')).toMatchInlineSnapshot(`
837+
box = ["Negate", 3.6]
838+
canonForms = -3.6
839+
canonical = -3.6
840+
`);
841+
842+
expr=['Negate',['Complex',1,4]];
843+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
844+
box = ["Negate", ["Complex", 1, 4]]
845+
canonForms = ["Complex", -1, -4]
846+
canonical = ["Complex", -1, -4]
847+
`);
848+
expect(canonNumber(expr).operator).toBe('Number');
849+
850+
expr=['Negate',['Rational',9,16]];
851+
expect(checkNumber(expr)).toMatchInlineSnapshot(`
852+
box = ["Negate", ["Rational", 9, 16]]
853+
canonForms = ["Rational", -9, 16]
854+
canonical = ["Rational", -9, 16]
855+
`);
856+
expect(canonNumber(expr).operator).toBe('Number');
857+
});
858+
859+
//(↓Only replaces for the *unit*, including negated...)
860+
//@wip?: maybe 'InvisibleOperator' case of '3i' etc. should be covered
861+
test(`Replace 'ImaginaryUnit' symbol instances with a number`,()=>{
862+
expect(checkNumber('i')).toMatchInlineSnapshot(`
863+
box = i
864+
canonForms = ["Complex", 0, 1]
865+
canonical = ["Complex", 0, 1]
866+
`);
867+
868+
expect(checkNumber('-i')).toMatchInlineSnapshot(`
869+
box = ["Negate", "i"]
870+
canonForms = ["Complex", 0, -1]
871+
canonical = ["Complex", 0, -1]
872+
`);
873+
});
874+
});
669875
});
670876

671877
//
@@ -940,6 +1146,12 @@ describe('POLYNOMIAL ORDER', () => {
9401146
*
9411147
* Only prints 'canonical' if this differs from 'boxed'.
9421148
*
1149+
* <!--
1150+
* **NOTE**:
1151+
* -Unlike 'checkJson', does not temporarily set the engine's precision to 'auto' for printing.
1152+
* (Save oneself some headaches...)
1153+
* -->
1154+
*
9431155
*@param inExpr
9441156
*@param forms
9451157
*@param [engine]
@@ -961,9 +1173,6 @@ function checkForms(
9611173
engine??=TEST_ENGINE;
9621174

9631175
try{
964-
constprecision=engine.precision;
965-
engine.precision='auto';
966-
9671176
//Boxed, *non-canonical*
9681177
constboxed=
9691178
typeofinExpr==='string'
@@ -973,7 +1182,6 @@ function checkForms(
9731182
constboxedStr=exprToString(boxed);
9741183

9751184
if(!boxed.isValid){
976-
engine.precision=precision;
9771185
return`invalid =${exprToString(boxed)}`;
9781186
}
9791187

@@ -989,8 +1197,6 @@ function checkForms(
9891197
result.push('canonForms = '+partialCanonStr);
9901198
if(canonicalStr!==boxedStr)result.push('canonical = '+canonicalStr);
9911199

992-
engine.precision=precision;
993-
9941200
returnresult.join('\n');
9951201
}catch(e){
9961202
returne.toString();

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp