units: Add factoring variable to limit simplifications

When factoring out units, we don't need to run through each
simplification in `expression::simplify`. We should also not lookup
names as symbols, but just let symbols as is.

Fixes: #988
Fixes: #976

Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
Christophe de Dinechin 2024-06-20 00:59:04 +02:00
parent 05b9a6acee
commit 5c551ce194
7 changed files with 23 additions and 15 deletions

View file

@ -1343,7 +1343,7 @@ algebraic_p arithmetic::evaluate(id op,
x = expression::make(op, x, y);
if (x)
if (expression_p expr = x->as<expression>())
if (Settings.AutoSimplify())
if (!unit::factoring && Settings.AutoSimplify())
x = expr->simplify();
return x;
}

View file

@ -1248,9 +1248,10 @@ algebraic_p expression::simplify_products() const
if (inner->is_algebraic())
return algebraic_p(inner);
// Save auto-simplify and set it
// Save auto-simplify and set it, and evaluate units normally
settings::SaveAutoSimplify sas(true);
save<bool> save(unit::mode, false);
save<bool> smode(unit::mode, false);
save<bool> sexpr(unit::factoring, true);
// Need a GC pointer since stack operations may move us
expression_g eq = this;

View file

@ -64,7 +64,7 @@ algebraic_p function::symbolic(id op, algebraic_r x)
if (!x)
return nullptr;
expression_p result = expression::make(op, x);
if (result && Settings.AutoSimplify())
if (result && !unit::factoring && Settings.AutoSimplify())
result = result->simplify();
return result;
}

View file

@ -51,16 +51,19 @@ EVAL_BODY(symbol)
// Evaluate a symbol by looking it up
// ----------------------------------------------------------------------------
{
if (unit::mode)
if (unit_p u = unit::lookup(o))
if (rt.push(u))
return OK;
if (object_p found = directory::recall_all(o, false))
if (!unit::factoring)
{
if (unit::ignore)
if (unit_p uval = found->as<unit>())
found = uval->value();
return program::run_program(found);
if (unit::mode)
if (unit_p u = unit::lookup(o))
if (rt.push(u))
return OK;
if (object_p found = directory::recall_all(o, false))
{
if (unit::ignore)
if (unit_p uval = found->as<unit>())
found = uval->value();
return program::run_program(found);
}
}
if (object_g eq = expression::make(o))
if (rt.push(eq))

View file

@ -4140,7 +4140,7 @@ void tests::units_and_conversions()
.test(SHIFT, KEY5, KEY4, KEY2, F2, F3).expect("42km/h")
.test(ADD).expect("101.545728km/h");
step("Unit parsing on command line")
.test(CLEAR, "12_km/s^2", ENTER).expect("12km/s²");
.test(CLEAR, "12_km/s^2", ENTER).expect("12km/s↑2");
step("Parsing degrees as a unit")
.test(CLEAR, "DEG", ENTER).noerror()
.test("1∡90", ENTER).expect("1∡90°")
@ -6904,7 +6904,7 @@ void tests::insertion_of_variables_constants_and_units()
.expect("6.0221367⁳²³mol⁻¹");
step("Programmatic equation lookup (text)")
.test(CLEAR, "\"IdealGas\" LIBEQ", ENTER)
.expect("'(PPa)·(Vm³)=(nmol)·R·(TK)'");
.expect("'(PPa)·(Vm↑3)=(nmol)·R·(TK)'");
step("Programmatic library lookup (text)")
.test(CLEAR, "\"LibraryHelp\" XLIB", ENTER)
.expect("\"To modify the library, edit the config/library.csv file\"");

View file

@ -256,6 +256,9 @@ HELP_BODY(unit)
// This variable is true while evaluating a uexpr
bool unit::mode = false;
// This variable is true while factoring out a uexpr (limit simplifications)
bool unit::factoring = false;
// This variable is true to ignore units while solving an equation
bool unit::ignore = false;

View file

@ -62,6 +62,7 @@ struct unit : complex
unit_p custom_cycle(symbol_r sym) const;
static bool mode; // Set to true to evaluate units
static bool factoring; // Set to true when factoring out units
static bool ignore; // Set to true to drop units from variables
public: