/***************************************************************************
begin : Sun Oct 21 2001
copyright : (C) 2001 by Jan Rheinlaender
email : jrheinlaender@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "eqc.h"
#include "printing.h"
#include "operands.h"
#include <stdio.h>
#include "msgdriver.h" // *** changed in 1.3.1
//#include "../config/config.h" // *** added in 1.3.1
#include <sstream>
const int version_major = EQCLIB_MAJOR_VERSION;
const int version_minor = EQCLIB_MINOR_VERSION;
const int version_micro = EQCLIB_MICRO_VERSION;
bool eqc_init::called = false;
eqc_init eqc_init::eqc_initializer;
eqc_init::eqc_init() {
if (called) {
// Removed in 1.3.1, msg_error might not be initialized yet
//msg_error.prio(0) << "Attempt to call eqc::init() more than one time!" << endline;
return;
} else
called = true;
eqc::init();
}
bool eqc::initialized = false;
void eqc::init() { // *** rewrite in 1.2
if (initialized) return;
optstack::init(); // *** added in 1.3.1
// Former eqc options
optstack::options->registr(o_lang, t_str, new std::string("english"));
#if ((defined __MINGW32__) || (defined _MSC_VER)) // *** changed in 1.4.3
optstack::options->registr(o_path, t_str, new std::string("C:\\localtexmf\\tex\\latex\\eqc\\"));
#else
optstack::options->registr(o_path, t_str, new std::string("/usr/share/texmf/tex/latex/eqc/"));
#endif
optstack::options->registr(o_debug, t_int, new int(-1)); // *** added in 1.4.3
optstack::options->registr(o_comments, t_bool, new bool(false)); // *** added in 1.4.3
optstack::options->registr(o_tan, t_bool, new bool(false)); // *** added in 1.4.3
// Former equation options
optstack::options->registr(o_eqalign, t_align, new aligntype(none));
optstack::options->registr(o_eqchain, t_bool, new bool(true));
optstack::options->registr(o_eqraw, t_bool, new bool(false));
optstack::options->registr(o_eqparse, t_bool, new bool(false));
optstack::options->registr(o_eqginac, t_bool, new bool(false));
optstack::options->registr(o_eqsplit, t_int, new int(-1));
optstack::options->registr(o_eqsplittext, t_str, new std::string(""));
optstack::options->registr(o_vecautosize, t_uint, new unsigned(20)); // *** added in 1.2
optstack::options->registr(o_precision, t_uint, new unsigned(4));
optstack::options->registr(o_fixeddigits, t_bool, new bool(true));
optstack::options->registr(o_exponent, t_int, new int(0));
optstack::options->registr(o_lowsclimit, t_dbl, new double(0.01));
optstack::options->registr(o_highsclimit, t_dbl, new double(9999.0));
// Former Unit options
//exprvector v;
//v.push_back(equation(expression(), expression()));
//unitvec* o = Unit::create_conversions(v);
optstack::options->registr(o_units, t_vec, new unitvec);
// New options
optstack::options->registr(o_difftype, t_str, new std::string("dfdt")); // *** added in 1.4.1
initialized = true;
} // eqc::init()
// constructors
eqc::eqc() {
// Define how to reference the previous equation
previous = "prev"; // *** added in 1.4.1
} // eqc::eqc()
// *** added in 1.3.0, added numeric in 1.4.1
ex EulernumEvalf() {
return exp(numeric(1));
}
const constant Euler_number("Eulernum", EulernumEvalf, "e", domain::positive);
// methods
// *** store() and remove() removed in 0.7
bool eqc::check_eq(const std::string &label, std::string &name, expression &rhs) { // *** changed to std::string in 1.4.3
// *** added in 0.7, added subs_options::algebraic in 0.8, changed to use label in 1.0
// check if the equation is an assignment and if so, assure that the symbol
// is on the left hand side
// *** changed in 1.0
// Now the result of the last substitution is saved, and only newly added assignments
// (contained in recent_assgn) are substituted.
// Also changed order of subs() and expand() to benefit from expand_real_powers
name = "";
expression lhs;
try {
if (equations[label]->subsed_lhs.is_empty()) {
MSG_INFO(2) << "Doing full substitution of " << equations[label]->eq << endline;
//equations[label]->eq.rhs().print(print_tree(std::cout)); std::cout << std::endl;
lhs = expression(equations[label]->eq.lhs())
.subs(assignments, subs_options::algebraic)
.expand(expand_options::expand_function_args).evalf();
rhs = expression(equations[label]->eq.rhs())
.subs(assignments, subs_options::algebraic)
.expand(expand_options::expand_function_args).evalf();
} else {
lhs = equations[label]->subsed_lhs;
rhs = equations[label]->subsed_rhs;
}
} catch (std::exception &e) { // *** added in 0.9, for division by 0 etc.
msg::warn(0) << "Warning: " << e.what() << " (1) while checking equation " << label << endline;
return false;
}
try {
if (!recent_assgn.empty()) {
MSG_INFO(2) << "Substituting recent assignments in " << equations[label]->eq << endline;
lhs = expression(lhs).subs(recent_assgn, subs_options::algebraic)
.expand(expand_options::expand_function_args).evalf();
rhs = expression(rhs).subs(recent_assgn, subs_options::algebraic)
.expand(expand_options::expand_function_args).evalf();
}
equations[label]->subsed_lhs = lhs;
equations[label]->subsed_rhs = rhs;
// Swap lhs and rhs so that the lhs always has the symbol
if (!(is_a<symbol>(lhs) || is_a<ncsymbol>(lhs)) &&
(is_a<symbol>(rhs) || is_a<ncsymbol>(rhs))) rhs.swap(lhs); // *** changed in 1.0, in 1.4.3
if (is_a<symbol>(lhs)) {
name = ex_to<symbol>(lhs).get_name();
return(true);
} else if (is_a<ncsymbol>(lhs)) {
name = ex_to<ncsymbol>(lhs).get_name();
return(true);
} else
return(false);
} catch (std::exception &e) { // *** added in 0.9, for division by 0 etc.
msg::warn(0) << "Warning: " << e.what() << " (2) while checking equation " << label << endline;
return false;
}
} // check_eq()
bool eqc::store_assgn(const std::string &name, const expression &rhs, const std::string &label) { // *** added in 0.7
// If the symbol is a constant, then this is in fact just a coincidence
// (or an error). The assignment is not stored.
if (vars[name]->type == t_constant) { // *** changed in 0.7
throw (std::logic_error("Warning: equation " + label + " redefines constant " + name +
"! Not registering."));
}
// Store the equation label with the variable whose value it defines *** added in 0.7
// If the rhs is a quantity, store the value for later
if (is_quantity(rhs)) {
if (vars[name]->has_value()) { // *** 1.4.3 before it was has_value(sym)
msg::error(0) << "Warning: New value " << rhs << " given for " << name << " in "
<< label << ". Previous value was " << vars[name]->val << " in "
<< vars[name]->assignments.front() << ". Overwriting old value. " << endline; // *** Changed policy to overwrite (instead of ignore) in 1.4.3
vars[name]->assignments.push_front(label); // Put the label at the front *** 1.4.3 before that it was back
} else {
vars[name]->val = rhs;
//if (label != VALLABEL) { // *** removed in 1.0
// assignments[sym] = rhs; // *** changed to use exmap in 0.8
//}
vars[name]->assignments.push_front(label); // The first equation gives the value
MSG_INFO(2) << "Stored assignment " << name << " == " << rhs << endline;
}
return true;
} else {
vars[name]->assignments.push_back(label);
MSG_INFO(1) << "Stored assignment " << name << " == " << rhs << endline;
return false;
}
} // store_assgn()
void eqc::check_and_register(const equation &eq, const equation &from) {
// 1. Make a copy of the equation and give it a label if it has none
eqrec *e = new eqrec(eq); // *** changed to eqrec in 1.0
if (e->eq.getlabel() == "") e->eq.settemplabel();
std::string label = e->eq.getlabel();
MSG_INFO(2) << "eqc::check_and_register: Label '" << label << "'" << endline;
// 2. Check for special case VALLABEL and clear this equation if it is there
if (label == VALLABEL) {
// if (equations[VALLABEL] != NULL) delete(equations[VALLABEL]); // TODO: Would this cause problems??
equations[VALLABEL] = NULL; // TODO: Do equations.erase(VALLABEL) instead
while (!vars[VALSYM]->assignments.empty()) { // *** changed to while in 1.0
vars[VALSYM]->assignments.pop_back();
}
vars[VALSYM]->make_unknown(); // Clear old value *** changed in 1.4.3
}
// 3. Make the equation the PREVIOUS equation
// Remove the PREVIOUS equation if it is temporary and no assignment
// Most of the time this will remove the 'from' equation, which is probably OK
if (label != VALLABEL) {
if (equations[previous] != NULL) {
if (equations[previous]->eq.is_temporary()) {
expression lhs = equations[previous]->eq.lhs();
expression rhs = equations[previous]->eq.rhs();
if (!(is_a<symbol>(lhs) || is_a<symbol>(rhs) || is_a<ncsymbol>(lhs) || is_a<ncsymbol>(rhs))) { // *** added ncsymbol in 1.4.3
deleq(equations[previous]->eq.getlabel()); // *** added in 1.2
}
}
}
equations[previous] = e;
}
// 4. Register the equation *** changed in 0.7, moved here in 1.0
if (equations[label] != NULL) { // An equation with this label already exists
MSG_INFO(0) << "Equation " << label << ": " << equations[label]->eq << endline;
throw std::invalid_argument("Label " + label + " already exists");
}
equations[label] = e; // Register the equation
// 5. Check if this equation could be useful to find values of variables (i.e, it is an
// assignment for a variable) *** modified in 0.7
if (is_lib(label)) { // *** added in 0.7, changed to is_lib() in 1.0
// This is a generic library equation which can not be used for finding values
MSG_INFO(1) << "Found library equation " << label << ". Not storing in other_equations." << endline;
} else {
// 5.1 Try and see if we get a new assignment for a variable *** added in 0.7, removed in 1.0
if (e->eq.gettype() == original) {
// 5.2 The equation is completely new, there can be no 'from' equation, of course
other_equations.push_front(label); // *** changed push_back to push_front in 1.0
MSG_INFO(2) << "Added " << label << " to other_equations" << endline;
} else if (e->eq.gettype() == derived) {
// 5.3 The equation is derived from another equation
if (from.is_temporary()) deleq(from.getlabel());
other_equations.push_front(label); // *** changed push_back to push_front in 1.0
}
}
// 6. Print messages
if (!is_lib(label)) { // *** added in 0.7, changed to is_lib() in 1.0
((e->eq.is_temporary() || (e->eq.getlabel() == VALLABEL)) ?
msg::info(2) : msg::info(1))
<< "Registered equation " << label << ": " << e->eq << endline;
//equations[label]->eq.rhs().print(print_tree(std::cout));
} else {
MSG_INFO(2) << "Registered library equation " << label << ": " << e->eq << endline;
}
//std::ostringstream os;
//dumpeq(os);
//MSG_INFO(0) << os.str() << endline;
} // eqc::check_and_register
void eqc::remove_assignment(const std::string& name, std::list<std::string> &names) { // *** changed to std::string in 1.4.3
names.push_back(name);
for (std::map<std::string, symrec*>::iterator v = vars.begin(); v != vars.end(); v++) {
if (v->second != NULL) { // TODO: change to ASSERT
if (!v->second->assignments.empty() && v->second->has_value()) // *** changed to has_value() in 1.4.3
if (equations[v->second->assignments.front()]->eq.has(vars[name]->getsym())) { // changed to getsym() in 1.4.3
// The equation that is affected defines the value of the variable
v->second->make_unknown(); // *** changed in 1.4.3
MSG_INFO(1) << "Therefore erased value of " << v->second->getsym() << endline;
remove_assignment(v->first, names);
}
}
} // for (v ...)
} // eqc::remove_assignment()
void eqc::remove_subsed(const std::string& name) { // *** added in 1.0, changed to std::string in 1.4.3
for (std::map<const std::string, eqrec*>::iterator e = equations.begin(); // *** changed to eqmap in 1.0, to std::map in 1.4.2
e != equations.end(); e++) {
if ((e->second != NULL) && !is_lib(e->first)) {
MSG_INFO(3) << "Checking " << e->first << ": " << e->second->eq << endline;
if (!e->second->subsed_lhs.is_empty() && (e->first != previous) &&
e->second->eq.has(vars[name]->getsym())) {
e->second->subsed_lhs.clear();
e->second->subsed_rhs.clear();
MSG_INFO(3) << "Removed stored substitution result of " << e->second->eq
<< " because value of " << name << " was removed" << endline;
}
}
}
} // eqc::removed_subsed()
void eqc::deleq (const std::string &which) {
std::string label;
label = (which == previous) ? equations[previous]->eq.getlabel() : which;
if (equations[label] == NULL) { // An equation with this label does not exist
return; // This is frequently the case if an equation was never registered, therefore no error message
}
if (is_lib(label)) // *** added in 0.7, changed to is_lib() in 1.0
msg::warn(0) << "Warning: Requesting deletion of " << label
<< ", which is a library equation!" << endline;
MSG_INFO(1) << "Deleting equation with label: " << label << endline;
// Complete rewrite in 0.7
// 1. Search the equation in other_equations
std::list<std::string>::iterator l, ldel;
l = find(other_equations.begin(), other_equations.end(), label);
if (l != other_equations.end()) {
// 1.1 This equation is stored (only!) in other_equations
MSG_INFO(2) << "Removed " << label << " from other_equations." << endline;
other_equations.erase(l); // *** changed usage of erase(i, ++i) in 1.0
// Beware: l is invalidated after this!
} else {
// 2. Search the equation in the assignments for variables
// Note: This does not cover equations like x = VAL(y), even if y looses its value, x will still retain it
// At this point this is considered a FEATURE, see Feature Tracker #3526107
for (std::map<std::string, symrec*>::iterator v = vars.begin(); v != vars.end(); v++) {
if (v->second != NULL) { // TODO: change to ASSERT
l = find(v->second->assignments.begin(), v->second->assignments.end(), label);
if (l != v->second->assignments.end()) { // The label is contained in the list
if (l == v->second->assignments.begin()) {// The variable's value is defined by the equation
// 2.1 Remove the equation label from assignments and the value of the variable
v->second->assignments.erase(l); // *** changed usage of erase(i, ++i) in 1.0
// Beware: l is invalidated after this!
v->second->make_unknown(); // *** changed in 1.4.3
msg::info(1) << "Erased value of " << v->second->getsym() << endline;
// 2.2 Check if any other variables depend on the value of this variable
std::list<std::string> names; // This list collects the names of all variables that have no value any more
remove_assignment(v->first, names);
// 2.3 remove the assignment from the list of (global) assignments
// *** changed to use exmap in 0.8
for (std::list<std::string>::const_iterator i = names.begin(); i != names.end(); i++) {
MSG_INFO(2) << "Removing " << *i << " = " << assignments[vars[*i]->getsym()]
<< " from list of global assignments." << endline;
assignments.erase(vars[*i]->getsym());
recent_assgn.erase(vars[*i]->getsym());
remove_subsed(*i); // *** added in 1.0
}
} else { // The value of the variable is not affected by deleting this equation
v->second->assignments.erase(l); // *** changed usage of erase(i, ++i) in 1.0
// Beware: l is invalidated after this!
}
MSG_INFO(1) << "Removed " << label << " from list of assignments for "
<< expression(v->second->sym) << endline;
break; // Stop searching, since an equation can only be an assignment for one variable
}
} // if (v->second != NULL)
} // for (v ...)
}
// Remove the equation from the list of registered equations
equations.erase(label); // This was commented out before 1.0
msg::info(1) << "Unregistered equation " << label << endline;
} // eqc::deleq()
void eqc::deltempeq(const expression &ex1, const expression &ex2) { // *** added in 0.9
// TODO: If both are temporary equations, the first is arbitrarely removed from the compiler
if (is_a<equation>(ex1)) {
if (ex_to<equation>(ex1).is_temporary()) {
deleq(ex_to<equation>(ex1).getlabel());
return;
}
}
if (is_a<equation>(ex2)) {
if (ex_to<equation>(ex2).is_temporary())
deleq(ex_to<equation>(ex2).getlabel());
}
} // eqc::deltempeq()
void eqc::register_constant (const equation &eq) {
// *** replaced constants by assignments in 0.7
// *** changed to handle ncsymbols in 1.4.3
if (! (is_a<symbol>(eq.lhs()) || is_a<ncsymbol>(eq.lhs())) ) {
throw std::invalid_argument("Warning: Left hand side for 'constant' is no symbol! Ignoring.");
} else {
std::string varname = is_a<symbol>(eq.lhs()) ? ex_to<symbol>(eq.lhs()).get_name() : ex_to<ncsymbol>(eq.lhs()).get_name();
if ((vars[varname] != NULL) && (vars[varname]->type == t_constant)) // *** changed to type in 0.7
throw std::invalid_argument("Error: Constant already exists! Ignoring.");
// Automatically evaluate the RHS to a quantity (if possible)
// Changed in 1.4.5 because section 5.1 was removed from check_and_register()
//vars[varname]->val = eq.rhs().subs(assignments, subs_options::algebraic).
// subs(recent_assgn, subs_options::algebraic).evalf(); // *** added recent_assgn in 1.0
symbol valsym = ex_to<symbol>(getsym(VALSYM));
check_and_register(equation(valsym, eq.rhs(), relational::equal, none, original, VALLABEL));
vars[varname]->val = find_value_of(valsym);
vars[varname]->type = t_constant; // *** changed to type in 0.7
if (!is_quantity(vars[varname]->val))
throw std::invalid_argument("Warning: Constant $" + varname + "$ is no quantity.");
recent_assgn[vars[varname]->getsym()] = vars[varname]->val; // *** changed to recent_assgn in 1.0, to getsym() in 1.4.3
//assignments_changed = true; // *** added in 1.0
}
msg::info(0) << "Registered constant: " << eq << endline;
} // eqc::register_constant ()------------------------------------------------------
void eqc::register_function (const std::string &n, const lst &args, const unsigned hints) { // *** added in 0.7, added ex_to_symbol in 0.9
func::registr(n, args, ex_to<symbol>(getsym("x_{_x_}")), hints);
vars[n]->type = t_function;
} // eqc::register_function()
symrec::symrec(const symtype t, const std::string& varname, const std::string& latexname,
const bool is_a_nc, const bool is_a_vector) { // *** added in 1.4.3
type = t;
is_nc = is_a_nc;
is_vector = is_a_vector;
sym = symbol(varname, latexname);
if (is_a_nc) ncsym = ncsymbol(varname, latexname);
make_unknown();
} // symrec::symrec()
void symrec::make_unknown() { // *** added in 1.4.3
val = (is_nc ? expression(ncsym) : expression(sym));
} // symrec::make_unknown()
const expression symrec::getsym() { // *** added in 1.4.3
return (is_nc ? expression(ncsym) : expression(sym));
} // symrec::getsym();
const bool symrec::has_value() { // *** added in 1.4.3
if (is_nc) {
return(!(val.is_equal(ncsym)));
} else {
return(!(val.is_equal(sym)));
}
} // symrec::has_value()
// *** added non-commutative symbols (matrices) in 1.4.3
const expression eqc::getsym (const std::string& varname, const std::string& latexname,
const bool is_a_nc, const bool is_a_vector) {
// *** changed to return Pi directly in 0.9, to return Euler directly in 1.3.0
MSG_INFO(2) << "getsym() for " << varname << endline;
if ((varname == "\\pi") || (varname == "%pi")) { // *** moved this to the top in 1.0, added %pi for iMath support in 1.3.1
return Pi;
} else if (varname == "\\Euler") {
return Euler;
} else if ((varname == "\\e") || (varname == "%e")) { // added %e for iMath support in 1.3.1
return Euler_number;
} else if (varname == "i") { // *** added in 1.0
return I;
} else if (vars[varname] == NULL) { // create a new variable
MSG_INFO(1) << "Creating new variable " << varname
<< ((latexname != "") ? ("(" + latexname + ")") : "" ) << endline;
vars[varname] = new symrec(t_variable, varname, latexname, is_a_nc, is_a_vector); // *** changed in 1.4.3
return (vars[varname]->getsym());
} else {
return (vars[varname]->getsym());
}
} // eqc::getsym()
const symtype eqc::getsymtype(const std::string& varname) {
if (vars[varname] != NULL) {
if (vars[varname]->is_nc) // non-commutative symbol, e.g. vector or matrix
return (vars[varname]->is_vector ? t_vector : t_matrix);
else
return vars[varname]->type;
} else {
return t_none; // symbol does not exist
}
} // eqc::getsymtype()
const bool eqc::is_constant(const symbol &s) {
if (vars[s.get_name()] == NULL)
throw(std::range_error("is_constant: Symbol '" + s.get_name() + "' is not registered with the compiler"));
return(vars[s.get_name()]->type == t_constant); // *** changed to type in 0.7
} // eqc::is_constant()
const bool eqc::is_label(const std::string &s) {
return (equations[s] != NULL);
} // eqc::is_label()
const bool eqc::is_lib(const std::string &s) const { // *** added in 1.0
return (std::string(s, 0, 4) == "lib:");
} // eqc::is_lib()
const bool eqc::has_value(const symbol& s) { // *** added in 0.7, changed for matrix symbols in 1.4.3
if (vars[s.get_name()] == NULL)
throw(std::range_error("has_value: Symbol is not registered with the compiler"));
return (vars[s.get_name()]->has_value()); // *** changed in 1.4.3
} // eqc::has_value()
const expression eqc::get_value(const symbol &s) { // *** added in 1.4.1
symrec* v = vars[s.get_name()]; // *** added in 1.0
if (v == NULL)
throw(std::range_error("get_value: Symbol '" + s.get_name() + "' is not registered with the compiler"));
return (v->val);
} // eqc::get_value()
bool eqc::find_values(const symbol &var, numeric &val, expression &unit, expression &value, const lst &assgn) { // *** changed ex to expression in 0.4
// *** major rewrite in 0.7 for performance reasons, changes in 1.0 for same reason
// *** added parameter assgn in 1.2
bool found_value = false;
//msg::info().setlevel(0);
MSG_INFO(1) << "Searching value of " << var << endline;
// 1.0 Prepare the equation list if optional parameters are being used *** added in 1.2
std::list<std::string> templabels;
std::list<std::string>::iterator l;
eqrec *preveq = equations[previous]; // Needs to be restored later
if (assgn.nops() != 0) {
for (lst::const_iterator a = assgn.begin(); a != assgn.end(); a++) {
if (is_a<equation>(*a)) {
MSG_INFO(2) << "Creating temporary equation " << *a << endline;
try {
check_and_register(ex_to<equation>(*a));
templabels.push_back(equations[previous]->eq.getlabel());
} catch (std::exception &e) {
msg::error(0) << "Failed to register temporary equation. Reason: " << e.what() << endline;
for (l = templabels.begin(); l != templabels.end(); l++) deleq(*l);
throw (std::invalid_argument("Error: Could not find quantity of " + var.get_name()));
}
} else if (is_a<relational>(*a)) { // TODO: WHY WHY WHY??? added in 1.3.1
MSG_INFO(2) << "Creating temporary equation from relational " << *a << endline;
try {
relational r = ex_to<relational>(*a);
equation eqr(r.lhs(), r.rhs(), relational::equal, none, original);
check_and_register(eqr);
templabels.push_back(equations[previous]->eq.getlabel());
} catch (std::exception &e) {
msg::error(0) << "Failed to register temporary equation. Reason: " << e.what() << endline;
for (l = templabels.begin(); l != templabels.end(); l++) deleq(*l);
throw (std::invalid_argument("Error: Could not find quantity of " + var.get_name()));
}
} else {
MSG_INFO(2) << "Not an equation: " << *a << endline;
for (l = templabels.begin(); l != templabels.end(); l++) deleq(*l);
throw (std::invalid_argument("Error: Equation expected"));
}
}
}
// 1.1 Check if the variable is a constant or already has a value, then we can skip
// all the iterations *** changed in 1.0 to set found_value=true and continue
std::string varname = var.get_name();
if (has_value(var)) { // *** changed to has_value() only in 1.0
found_value = true; // *** added in 1.0, value is now returned at end of function
MSG_INFO(1) << "Variable/constant already has a value. Not iterating." << endline;
}
std::list<std::string>::iterator ldel; // *** added ldel in 1.0
std::map<std::string, symrec*>::iterator it_varname = vars.find(varname);
std::map<std::string, symrec*>::iterator v;
std::map<const std::string, eqrec*>::iterator e; // *** changed to std::map in 1.4.2
if (msg::info().checkprio(1)) {
msg::info() << "Available other equations: " << endline;
for (l = other_equations.begin(); l != other_equations.end(); l++) {
msg::info() << equations[*l]->eq.getlabel() << ": " << equations[*l]->eq << endline;
//equations[*l]->eq.rhs().print(print_tree(std::cout));
}
}
if (msg::info().checkprio(2)) {
msg::info() << "Available equations: " << endline;
for (e = equations.begin(); e != equations.end(); e++)
if (e->second != NULL) /// *** removed in 1.1 && !is_lib(e->first))
msg::info() << e->second->eq.getlabel() << ": " << e->second->eq << endline;
}
if (msg::info().checkprio(2)) {
msg::info() << "Available assignments: " << assignments << endline
<< "Recent assignments: " << recent_assgn << endline;
}
exmap new_assgn; // Collect new assignments found during an iteration loop *** changed in 1.0
unsigned num_it = 0; // just for the statistics... *** added in 0.9, changed to 1 in 1.0
expression result;
std::string name;
// 2. Find all possible assignments and further equations for the variable
do {
if (msg::info().checkprio(1) && !found_value)
msg::info() << "Starting iteration #" << ++num_it << endline;
//new_assignments = false; // *** removed in 1.0
// 2.1 Iterate over other_equations and check each equation whether it evaluates
// to an assignment or even a quantity
// For the special case of searching for the value of VALSYM (which means that the user
// asked for the value of an expression, not of a symbol, e.g. \val{\tan\alpha}),
// there are two possible solutions:
// a) The rhs of the equation VALLABEL: VALSYM = ... evaluates to the result
// b) The lhs or rhs of another equation correspond to this expression. If yes, we
// use this as a new assignment for VALSYM. Example:
// User: \val{\tan\alpha} -> eqc creates equation: VALSYM = \tan\alpha. Another equation exists
// with \tan\alpha = 3 \tan\beta, then this amounts to an assignment VALSYM = 3 \tan\beta
//
if (!found_value) {
for (l = other_equations.begin(); l != other_equations.end();) {
MSG_INFO(2) << "Investigating other equation " << equations[*l]->eq << endline;
if (check_eq(*l, name, result)) { // *** changed *equations[*l] to *l in 1.0, to name in 1.4.3
// The equation is an assignment. This also handles case a) for varname = VALSYM
try {
if (store_assgn(name, result, *l)) {
if (*l != VALLABEL) {
new_assgn[vars[name]->getsym()] = result; // *** changed in 1.0 (used to be done in store_assgn()) *** 1.4.3 changed to getsym()
MSG_INFO(1) << "Found value from other_equations: " << vars[name]->getsym() << " == " << result << endline;
}
if (name == varname) { // *** added in 1.0
MSG_INFO(1) << "Found value of " << varname << ". Stopping iteration." << endline;
other_equations.erase(l); // The equation is stored with the variable now
found_value = true;
break; // jump out of the for... loop
}
}
ldel = l; // *** changed handling of erase in 1.0
l++;
other_equations.erase(ldel); // The equation is stored with the variable now
} catch (std::exception &e) {
msg::error(0) << e.what() << endline;
}
/*} else { // *** added in 1.4.6
// Handle:
// x = ab
// ab = 3
// -> VAL(x) = 3
if (equations[*l]->eq.lhs().is_equal(equations[VALLABEL]->eq.rhs())) {
store_assgn(VALSYM, equations[*l]->eq.rhs(), *l); // *** changed to VALSYM in 1.4.3
ldel = l; // *** changed handling of erase in 1.0
l++;
other_equations.erase(ldel);
} else if (equations[*l]->eq.rhs().is_equal(equations[VALLABEL]->eq.rhs())) {
store_assgn(VALSYM, equations[*l]->eq.lhs(), *l); // *** changed to getsym() in 1.4.3
ldel = l; // *** changed handling of erase in 1.0
l++;
other_equations.erase(ldel);
} else {
l++;
}*/
} else
l++;
} // for(l ...)
}
// 2.1.5 For the special case of varname being VALSYM, we also need to look at all the equations
// because they might have been moved out of other_equations by now. The reason is that the
// symrec for VALSYM gets deleted every time
if (!found_value && (varname == VALSYM)) {
for (e = equations.begin(); e != equations.end(); e++) {
if (e->second == NULL) continue;
if (e->first == previous) continue; // Avoid "duplicate value" warning
MSG_INFO(2) << "VALSYM: Investigating equation '" << e->first << "': " << e->second->eq << endline;
if (e->first == VALLABEL) continue;
if (e->second->eq.lhs().is_equal(equations[VALLABEL]->eq.rhs())) {
store_assgn(VALSYM, equations[e->first]->eq.rhs(), e->first);
} else if (e->second->eq.rhs().is_equal(equations[VALLABEL]->eq.rhs())) {
store_assgn(VALSYM, equations[e->first]->eq.lhs(), e->first);
}
}
}
// 2.2 Iterate over the equations stored in vars[...]->assignments and check
// each equation whether it evaluates to an assignment
if (!found_value) { // *** added in 1.0
// *** changed to begin iteration with the variable we are searching the value for in 1.0
v = it_varname;
do {
if (v->second != NULL) { // TODO: replace this by ASSERT
if (!has_value(v->second->sym) && (v->second->type != t_function)) {
// For every variable that has no value yet
MSG_INFO(2) << "Investigating assignments for " << v->first << endline;
for (l = v->second->assignments.begin(); l != v->second->assignments.end(); l++) {
// For all the assignments stored with this variable
MSG_INFO(2) << "Investigating " << equations[*l]->eq << endline;
//equations[*l]->eq.print(print_tree(std::cout));
if (check_eq(*l, name, result)) { // *** changed *equations[*l] to *l in 1.0
// Note: Using store_assgn() here is not useful
MSG_INFO(2) << "Found assignment " << name << " == " << result << endline;
// If the result is a quantity, store the value for later
// If this is the symbol we are searching a value for, the search is finished
if (is_quantity(result) || is_a<matrix>(result.evalm())) { // *** added is_a<matrix> in 1.4.3
v->second->val = result; // A value was found for the variable
new_assgn[vars[name]->getsym()] = result; // *** changed to new_assgn in 1.0, to getsym() in 1.4.3
MSG_INFO(1) << "Found value from equations: " << name << " == " << result << endline;
// Move this equation label to the front of vars[...]->assignments
std::string label = *l;
ldel = l; // *** changed handling of erase in 1.0
l++;
v->second->assignments.erase(ldel);
v->second->assignments.push_front(label); // TODO: Use insert(ldel)?
if (v->first == varname) {
found_value = true;
MSG_INFO(1) << "Found value of " << varname << ". Stopping iteration." << endline;
}
break; // jump out of the for(l ...) loop, this variable has a value
} // if (is_quantity ...)
} // if (check_eq ...)
} // for (l ...)
} // if (has_value ...)
} // if (v->second != NULL)
if (found_value) break; // jump out of the do {} while(v ...) loop
v++;
if (v == vars.end()) v = vars.begin();
} while (v != it_varname); // do {} while()
} // if (!found_value)
// 2.3 All the assignments stored in recent_assgn have been applied to the equations in
// this iteration. Merge them with the global assignments and move the contents of
// new_assgn to recent_assgn
// *** added in 1.0
if (!found_value) { // If a value was found, the iteration was incomplete!
if (msg::info().checkprio(1)) {
msg::info() << "Completed iteration. Recent assignments that have been substituted: "
<< recent_assgn << endline <<
"New assignments for next iteration: " << new_assgn << endline;
}
assignments.insert(recent_assgn.begin(), recent_assgn.end());
recent_assgn.swap(new_assgn);
new_assgn.clear();
}
} while (!recent_assgn.empty() && !found_value);
// 2.4 Add the new assignments found to the recent assignments, for the next call of this function
new_assgn.erase(getsym(VALSYM));
recent_assgn.insert(new_assgn.begin(), new_assgn.end());
new_assgn.clear();
// 3. Check whether we found too few or too many assignments for the variable
if ((varname == VALSYM) && (vars[VALSYM]->assignments.size() > 1)) // *** added in 1.0
vars[VALSYM]->assignments.remove(VALLABEL);
if (!has_value(var) && vars[varname]->assignments.empty()) { // *** added !has_value() in 1.0
for (l = templabels.begin(); l != templabels.end(); l++) deleq(*l); // *** added in 1.2
throw (std::invalid_argument("Variable $" + varname + "$ does not have a value"));
}
// The variable does not have a value, but multiple assignments try to define one
if (!has_value(var) && vars[varname]->assignments.size() > 1) {
msg::warn(0) << "Warning: Variable " << varname << " has " << vars[varname]->assignments.size()
<< " possible values." << endline;
MSG_INFO(1) << "Possible equations for " << varname << " after search: " << endline;
for (l = vars[varname]->assignments.begin(); l != vars[varname]->assignments.end(); ) {
if ((varname != VALSYM) && (equations[*l]->subsed_lhs != vars[varname]->sym)) { // *** added in 1.0
MSG_INFO(1) << *l << ": " << equations[*l]->eq
<< ", removing because symbol is on right-hand side" << endline;
ldel = l;
l++;
vars[varname]->assignments.erase(ldel);
} else {
MSG_INFO(1) << *l << ": " << equations[*l]->eq << endline;
l++;
}
}
}
// 4. Find the best value for the variable
std::list<expression> values;
if (has_value(var)) {
// 4.1 The variable has a value which is a quantity
operands n(GINAC_MUL), d(GINAC_MUL);
operands::split_ex(vars[varname]->val, n, d);
val = n.get_coefficient(); // *** changed in 1.2, was .../d.get_coefficient();
unit = n.get_units()/d.get_units();
value = vars[varname]->val;
// If optional parameters were used, clean up
if (assgn.nops() != 0) { // *** added in 1.2
for (l = templabels.begin(); l != templabels.end(); l++) deleq(*l);
equations[previous] = preveq;
}
return true;
} else {
// 4.2 Collect all the possible values, detect if assignment is the wrong way around
// (symbol on the left-hand side).
// TODO: This doesn't work if we are searching for VALSYM
// *** changed in 0.8 to use exmap and subs_options::algebraic
for (l = vars[varname]->assignments.begin(); l != vars[varname]->assignments.end(); l++) {
values.push_back((equations[*l]->subsed_rhs == vars[varname]->sym) ?
equations[*l]->subsed_lhs : equations[*l]->subsed_rhs); // *** changed to subsed_rhs in 1.0
}
}
// 5. The variable does not have a quantity. Return the best of the values found
// TODO: Choose the equation with the least
// unknown variables in it or the last one defined by the user?
if (msg::info().checkprio(0)) {
msg::info() << "Possible values for " << varname << " after simplification: ";
std::ostringstream os;
copy(values.begin(), values.end(), std::ostream_iterator<expression>(os, "; "));
msg::info() << os.str();
msg::info() << endline;
//values.begin()->print(print_tree(std::cout));
}
value = values.front(); // Returns the last equation defined by the user
if (assgn.nops() != 0) { // *** added in 1.2
for (l = templabels.begin(); l != templabels.end(); l++) deleq(*l);
equations[previous] = preveq;
}
return false;
} // eqc::find_values()
const expression eqc::find_value_of(const symbol &var, const lst &assignments) {
// *** added optional parameter in 1.2
numeric v;
expression u, value;
find_values(var, v, u, value, assignments);
try {
return (value.evalm()); // *** added evalm in 1.3.0
} catch (std::exception& e) {
// evalm() will throw an exception if an ncmul contains non-commutative symbols
return (value);
}
} // eqc::find_value_of()
const expression eqc::find_quantity_of(const symbol &var, const lst &assignments) {
// *** added optional parameters in 1.2
numeric v;
expression u, value;
if (!find_values(var, v, u, value, assignments)) { // *** changed in 1.2
msg::error(0) << "Value found: " << value << endline;
throw (std::invalid_argument("Variable $" + var.get_name() + "$ does not have a quantity"));
}
return (value);
} // eqc::find_quantity_of()
const numeric eqc::find_numval_of(const symbol &var, const lst &assignments) {
// *** added optional parameter in 1.2
// Note: This function discards any units the result might contain!
numeric v;
expression u, value;
if (!find_values(var, v, u, value, assignments)) {
msg::error(0) << "Value found: " << value << endline;
throw (std::invalid_argument("Variable $" + var.get_name() + "$ does not have a numeric value"));
}
if (u != 1)
msg::error(0) << "Warning: Numerical value requested, but units were found for "
<< var << endline;
return (v);
} //eqc::find_numval_of()
const expression eqc::find_units_of(const symbol &var, const lst &assignments) {
// *** added optional parameter in 1.2
numeric v;
expression u, value;
if (!find_values(var, v, u, value, assignments)) {
msg::error(0) << "Value found: " << value << endline;
throw (std::invalid_argument("Error: Variable $" + var.get_name() + "$ does not have units"));
}
if (v != 1)
msg::error(0) << "Warning: Units requested, but numerical value was found for "
<< var << endline;
return (u);
} //eqc::find_units_of()
void eqc::clear() {
std::map<unsigned, exrec*>::iterator i, idel;
for (i = remember_split.begin(); i != remember_split.end(); ) { // *** added in 1.0
if (i->second != NULL) { // TODO: change this to ASSERT
MSG_INFO(1) << i->second->hits << " hits for " << i->second->e << endline;
//delete(i->second); // TODO: ERASE_HACK
//i->second = NULL;
idel = i; // *** changed handling of erase in 1.0
i++;
remember_split.erase(idel);
} else
i++;
}
other_equations.clear(); // *** changed in 0.6
std::map<const std::string, eqrec*>::iterator eq, eqdel; // *** changed to eqmap in 1.0, to std::map in 1.4.2
for (eq = equations.begin(); eq != equations.end(); ) {
if (eq->second != NULL) { // TODO: change to ASSERT
if (!is_lib(eq->first)) { // *** added in 0.7, changed to is_lib() in 1.0
MSG_INFO(3) << "Deleting equation " << eq->second->eq.getlabel()
<< ": " << eq->second->eq << endline;
//delete(i->second); // TODO: ERASE_HACK *** added in 1.0
//i->second = NULL;
eqdel = eq; // *** changed handling of erase in 1.0
eq++;
equations.erase(eqdel);
} else {
eq++;
}
} else
eq++;
}
// Clear only variables and non-library functions
// *** made code more compact and added code to handle assignments in 0.7
assignments.clear(); // *** changed in 0.8 to use exmap
recent_assgn.clear(); // *** added in 1.0
std::map<std::string, symrec*>::iterator v, vdel; // *** added in 1.0
for (v = vars.begin(); v != vars.end();) {
if (v->second != NULL) { // TODO: change to ASSERT
if (v->second->type == t_variable) { // *** changed to ==t_variable in 0.9
MSG_INFO(3) << "Deleting variable " << v->second->sym.get_name() << endline;
v->second->make_unknown(); // *** changed in 1.1, variables referenced by, changed in 1.4.3
v->second->assignments.clear(); // *** library equations may not be deleted!
v++;
} else if (v->second->type == t_function) { // *** added in 0.9
if (!func(v->second->sym.get_name()).is_lib()) {
MSG_INFO(3) << "Deleting function " << v->second->sym.get_name() << endline;
func::remove(v->second->sym.get_name()); // *** added in 1.4.1
//delete (i->second); // TODO: ERASE_HACK *** added in 1.0
//i->second = NULL;
vdel = v;// *** changed usage of erase(i, ++i) in 1.0
v++;
vars.erase(vdel);
} else {
MSG_INFO(3) << "Keeping " << v->second->sym.get_name() << endline;
v++;
}
} else {
MSG_INFO(3) << "Keeping " << v->second->sym.get_name() << endline;
assignments[v->second->getsym()] = v->second->val; // *** added in 0.7, changed in 0.8 to use exmap, in 1.4.3 to getsym()
v++;
}
} else
v++;
}
func::clear(); // *** added in 0.5
} //eqc::clear()
void eqc::clearall () {
std::map<unsigned, exrec*>::iterator i, idel;
for (i = remember_split.begin(); i != remember_split.end(); ) { // *** added in 1.0
if (i->second != NULL) { // TODO: change to ASSERT
MSG_INFO(1) << i->second->hits << " hits for " << i->second->e << endline;
idel = i;
i++;
remember_split.erase(idel);
} else
i++;
}
other_equations.clear(); // *** changed in 0.6
equations.clear();
assignments.clear(); // *** changed in 0.8 to use exmap
recent_assgn.clear(); // *** added in 1.0
vars.clear();
Unit::clear(); // *** added in 0.5
func::clearall(); // *** added in 0.5
equation::clear(); // *** added in 0.8
// Clear options *** chaanged in 1.2
optstack::options->clearall();
initialized = false; // *** added in 1.3.1 for iMath
} //eqc::clearall()
// *** Moved functionality from ltxfileparse.yy to here in 1.1
void eqc::print(std::ostream &os) const {
os << "List of variables:" << std::endl;
for (std::map<std::string, symrec*>::const_iterator variable = vars.begin();
variable != vars.end(); variable++) {
// variable->second->sym.print(os);
if (variable->second != NULL) // TODO: change to ASSERT *** added in 1.0
os << variable->second->sym.get_name() << std::endl;
}
os << "List of equations:" << std::endl;
for (std::map<const std::string, eqrec*>::const_iterator eq = equations.begin(); // *** changed to eqmap in 1.0, to std::map in 1.4.2
eq != equations.end(); eq++) {
if (eq->second != NULL) // TODO: change to ASSERT *** added in 1.0
os << eq->first << ": " << ex(eq->second->eq) << std::endl;
}
}
void eqc::dumpeq(std::ostream &os) { // *** added in 0.8
os << "\\allowdisplaybreaks[1]" << std::endl
<< "\\begin{align*}" << std::endl;
std::list<std::string>::iterator l;
equation eq;
for (std::map<std::string, symrec*>::iterator v = vars.begin(); v != vars.end(); v++) {
if (v->second != NULL) { // TODO: change to ASSERT *** added in 1.0
if (!(v->second->sym.get_name() == VALSYM)) { // *** added in 0.9
for (l = v->second->assignments.begin(); l != v->second->assignments.end(); l++) {
eq = equations[*l]->eq;
os << latex << eq.lhs() << " &= " << latex << eq.rhs();
if (!eq.is_temporary())
os << "\\quad\\tag*{" << ltx_label(eq.getlabel()) << " \\eqref{" << eq.getlabel() << "}}";
os << "\\\\" << std::endl;
}
}
}
}
os << "\\end{align*}" << std::endl
<< "\\begin{align*}" << std::endl;
for (l = other_equations.begin(); l != other_equations.end(); l++) {
eq = equations[*l]->eq;
os << latex << eq.lhs() << " &= " << latex << eq.rhs();
if (!eq.is_temporary())
os << "\\quad\\tag*{" << ltx_label(eq.getlabel()) << " \\eqref{" << eq.getlabel() << "}}";
os << "\\\\" << std::endl;
}
os << "\\end{align*}" << std::endl;
} // eqc::dumpeq()
// *** removed const return value in 0.8
equation &eqc::operator[](const std::string &label) {
if (equations[label] != NULL) return equations[label]->eq;
throw std::range_error("equation label " + label + " does not exist.");
}