// This file is part of fityk program. Copyright (C) Marcin Wojdyr // Licence: GNU General Public License version 2 // $Id: mgr.cpp 294 2007-05-16 03:18:25Z wojdyr $ #include "mgr.h" #include "common.h" #include "var.h" #include "datatrans.h" #include "ast.h" #include "ui.h" #include "func.h" #include "sum.h" #include "settings.h" #include "logic.h" //VariableManager::get_or_make_variable() handles @0.F[1].a #include #include #include #include #include using namespace std; VariableManager::~VariableManager() { purge_all_elements(functions); purge_all_elements(variables); } void VariableManager::unregister_sum(Sum const *s) { vector::iterator k = find(sums.begin(), sums.end(), s); assert (k != sums.end()); sums.erase(k); } void VariableManager::sort_variables() { for (vector::iterator i = variables.begin(); i != variables.end(); ++i) (*i)->set_var_idx(variables); int pos = 0; while (pos < size(variables)) { int M = variables[pos]->get_max_var_idx(); if (M > pos) { swap(variables[pos], variables[M]); for (vector::iterator i = variables.begin(); i != variables.end(); ++i) (*i)->set_var_idx(variables); } else ++pos; } } /// takes string parsable by FuncGrammar and: /// if the string refers to one variable -- returns its name /// else makes variable and returns its name string VariableManager::get_or_make_variable(string const& func) { assert(!func.empty()); string tmp1, tmp2; if (parse(func.c_str(), VariableLhsG).full) // $foo return string(func, 1); else if (parse(func.c_str(), (FunctionLhsG | !lexeme_d['@' >> uint_p >> '.'] >> (str_p("F[")|"Z[") >> int_p >> ch_p(']') ) [assign_a(tmp1)] >> '.' >> lexeme_d[alpha_p >> *(alnum_p|'_')][assign_a(tmp2)] ).full) { // %bar.bleh return F->find_function_any(tmp1)->get_param_varname(tmp2); } else // anything else return assign_variable("", func); } namespace { void parse_and_set_domain(Variable *var, string const& domain_str) { string::size_type lb = domain_str.find('['); string::size_type pm = domain_str.find("+-"); string::size_type rb = domain_str.find(']'); string ctr_str = strip_string(string(domain_str, lb+1, pm-(lb+1))); string sigma_str(domain_str, pm+2, rb-(pm+2)); fp sigma = strtod(sigma_str.c_str(), 0); if (!ctr_str.empty()) { fp ctr = strtod(ctr_str.c_str(), 0); var->domain.set(ctr, sigma); } else var->domain.set_sigma(sigma); } string::size_type skip_variable_value(string const& s, string::size_type pos) { string::size_type new_pos; if (s[pos] == '{') new_pos = s.find('}', pos) + 1; else { char const* s_c = s.c_str(); char *endptr; strtod(s_c+pos, &endptr); new_pos = endptr - s_c; } while (new_pos < s.size() && isspace(s[new_pos])) ++new_pos; return new_pos; } inline string strip_tilde_variable(string s) { string::size_type pos = 0; while ((pos = s.find('~', pos)) != string::npos) { s.erase(pos, 1); assert(pos < s.size()); pos = skip_variable_value(s, pos); if (pos < s.size()) { if (s[pos] == '[') { string::size_type right_b = s.find(']', pos); assert(right_b != string::npos); s.erase(pos, right_b-pos+1); } } } return s; } } //anonymous namespace string VariableManager::assign_variable(string const &name, string const &rhs) { Variable *var = 0; string nonempty_name = name.empty() ? next_var_name() : name; if (rhs.empty()) {// mirror-variable var = new Variable(nonempty_name, -2); return put_into_variables(var); } tree_parse_info<> info = ast_parse(rhs.c_str(), FuncG, space_p); assert(info.full); const_tm_iter_t const &root = info.trees.begin(); if (root->value.id() == FuncGrammar::variableID && *root->value.begin() == '~') { //simple variable string val_str = string(root->value.begin()+1, root->value.end()); string domain_str; string::size_type pos = skip_variable_value(val_str, 0); if (pos < val_str.size() && val_str[pos] == '[') { domain_str = string(val_str, pos); val_str.erase(pos); } fp val = get_constant_value(val_str); int nr; // avoid changing order of parameters in case of "$_1 = ~1.23" int old_pos = find_variable_nr(name); if (old_pos != -1 && variables[old_pos]->is_simple()) { nr = variables[old_pos]->get_nr(); parameters[nr] = val; //variable at old_pos will be deleted soon } else { nr = parameters.size(); parameters.push_back(val); } var = new Variable(nonempty_name, nr); if (!domain_str.empty()) parse_and_set_domain(var, domain_str); } else { vector vars=find_tokens_in_ptree(FuncGrammar::variableID, info); if (contains_element(vars, "x")) throw ExecuteError("variable can't depend on x."); for (vector::const_iterator i = vars.begin(); i != vars.end(); i++) if ((*i)[0]!='~' && (*i)[0]!='{' && (*i)[0]!='$' && (*i)[0]!='%' && (*i)[0]!='@' && (((*i)[0]!='F' && (*i)[0]!='Z') || i->size() < 2 || (*i)[1]!='[')) throw ExecuteError("`" + *i + "' can't be used as variable."); vector op_trees = calculate_deriv(root, vars); // ~14.3 -> $var4 for (vector::iterator i = vars.begin(); i != vars.end(); ++i) { *i = get_or_make_variable(*i); } var = new Variable(nonempty_name, vars, op_trees); } return put_into_variables(var); } bool VariableManager::is_variable_referred(int i, vector const &ignore_vars, string *first_referrer) { for (int j = i+1; j < size(variables); ++j) { if (variables[j]->is_directly_dependent_on(i) && !contains_element(ignore_vars, variables[j]->name)) { if (first_referrer) *first_referrer = variables[j]->xname; return true; } } for (vector::iterator j = functions.begin(); j != functions.end(); ++j) { if ((*j)->is_directly_dependent_on(i)) { if (first_referrer) *first_referrer = (*j)->xname; return true; } } return false; } vector VariableManager::get_variable_references(string const &name) { int idx = find_variable_nr(name); vector refs; for (vector::const_iterator i = variables.begin(); i != variables.end(); ++i) if ((*i)->is_directly_dependent_on(idx)) refs.push_back((*i)->xname); for (vector::const_iterator i = functions.begin(); i != functions.end(); ++i) if ((*i)->is_directly_dependent_on(idx)) refs.push_back((*i)->xname); return refs; } void VariableManager::remove_unreferred() { // remove auto-delete marked variables, which are not referred by others for (int i = variables.size()-1; i >= 0; --i) if (variables[i]->is_auto_delete()) { if (!is_variable_referred(i)) { delete variables[i]; variables.erase(variables.begin() + i); } } // re-index all functions and variables (in any case) for (vector::iterator i = variables.begin(); i != variables.end(); ++i) (*i)->set_var_idx(variables); for (vector::iterator i = functions.begin(); i != functions.end(); ++i) { (*i)->set_var_idx(variables); } // remove unreferred parameters for (int i = size(parameters)-1; i >= 0; --i) { bool del=true; for (int j = 0; j < size(variables); ++j) if (variables[j]->get_nr() == i) { del=false; break; } if (del) { parameters.erase(parameters.begin() + i); // take care about parameter indices in variables and functions for (vector::iterator j = variables.begin(); j != variables.end(); ++j) (*j)->erased_parameter(i); for (vector::iterator j = functions.begin(); j != functions.end(); ++j) (*j)->erased_parameter(i); } } } string VariableManager::get_variable_info(string const &s, bool extended_print) { return find_variable(s)->get_info(parameters, extended_print); } /// puts Variable into "variables" vector, checking dependencies string VariableManager::put_into_variables(Variable* new_var) { auto_ptr var(new_var); string var_name = var->name; var->set_var_idx(variables); int old_pos = find_variable_nr(var->name); if (old_pos == -1) { var->recalculate(variables, parameters); variables.push_back(var.release()); } else { if (var->is_dependent_on(old_pos, variables)) { //check for loops throw ExecuteError("detected loop in variable dependencies of " + var->xname); } delete variables[old_pos]; variables[old_pos] = var.release(); if (variables[old_pos]->get_max_var_idx() > old_pos) { sort_variables(); } remove_unreferred(); } use_parameters(); return var_name; } string VariableManager::assign_variable_copy(string const& name, Variable const* orig, map const& varmap) { Variable *var=0; assert(!name.empty()); if (orig->is_simple()) { fp val = orig->get_value(); parameters.push_back(val); int nr = parameters.size() - 1; var = new Variable(name, nr); } else { vector vars; for (int i = 0; i != orig->get_vars_count(); ++i) { assert(varmap.count(orig->get_var_idx(i))); vars.push_back(varmap.find(orig->get_var_idx(i))->second); } vector new_op_trees; for (vector::const_iterator i = orig->get_op_trees().begin(); i != orig->get_op_trees().end(); ++i) new_op_trees.push_back((*i)->copy()); var = new Variable(name, vars, new_op_trees); } return put_into_variables(var); } void VariableManager::delete_variables(vector const &names) { const int n = names.size(); vector nrs (n); for (int i = 0; i < n; ++i) { int k = find_variable_nr(names[i]); if (k == -1) throw ExecuteError("undefined variable: $" + names[i]); string first_referrer; if (is_variable_referred(k, names, &first_referrer)) throw ExecuteError("can't delete $" + names[i] + " because " + first_referrer + " depends on it."); nrs[i] = k; } sort(nrs.begin(), nrs.end()); for (int i = n-1; i >= 0; --i) { int k = nrs[i]; delete variables[k]; variables.erase(variables.begin() + k); } remove_unreferred(); } void VariableManager::delete_funcs(vector const &names) { if (names.empty()) return; for (vector::const_iterator i=names.begin(); i != names.end(); ++i){ int k = find_function_nr(*i); if (k == -1) throw ExecuteError("undefined function: %" + *i); delete functions[k]; functions.erase(functions.begin() + k); } remove_unreferred(); for (vector::iterator i = sums.begin(); i != sums.end(); ++i) (*i)->find_function_indices(); } bool VariableManager::is_function_referred(int n) const { for (vector::const_iterator i = sums.begin(); i != sums.end(); ++i) { if (contains_element((*i)->get_ff_idx(), n) || contains_element((*i)->get_zz_idx(), n)) return true; } return false; } void VariableManager::auto_remove_functions() { int func_size = functions.size(); for (int i = func_size - 1; i >= 0; --i) if (functions[i]->is_auto_delete() && !is_function_referred(i)) { delete functions[i]; functions.erase(functions.begin() + i); } if (func_size != size(functions)) { remove_unreferred(); for (vector::iterator i = sums.begin(); i != sums.end(); ++i) (*i)->find_function_indices(); } } void VariableManager::delete_funcs_and_vars(vector const &xnames) { vector vars, funcs; for (vector::const_iterator i = xnames.begin(); i != xnames.end(); ++i) { if ((*i)[0] == '$') vars.push_back(string(*i, 1)); else if ((*i)[0] == '%') funcs.push_back(string(*i, 1)); else assert(0); } delete_funcs(funcs); delete_variables(vars); } int VariableManager::find_function_nr(string const &name) const { string only_name = !name.empty() && name[0]=='%' ? string(name,1) : name; for (int i = 0; i < size(functions); ++i) if (functions[i]->name == only_name) return i; return -1; } const Function* VariableManager::find_function(string const &name) const { int n = find_function_nr(name); if (n == -1) throw ExecuteError("undefined function: " + (name[0]=='%' ? name : "%"+name)); return functions[n]; } int VariableManager::find_variable_nr(string const &name) { for (int i = 0; i < size(variables); ++i) if (variables[i]->name == name) return i; return -1; } Variable const* VariableManager::find_variable(string const &name) { int n = find_variable_nr(name); if (n == -1) throw ExecuteError("undefined variable: $" + name); return variables[n]; } int VariableManager::find_nr_var_handling_param(int p) { assert(p >= 0 && p < size(parameters)); for (size_t i = 0; i < variables.size(); ++i) if (variables[i]->get_nr() == p) return i; assert(0); return 0; } int VariableManager::find_parameter_variable(int par) { for (int i = 0; i < size(variables); ++i) if (variables[i]->get_nr() == par) return i; return -1; } void VariableManager::use_parameters() { use_external_parameters(parameters); } void VariableManager::use_external_parameters(vector const &ext_param) { for (vector::iterator i = variables.begin(); i != variables.end(); ++i) (*i)->recalculate(variables, ext_param); for (vector::iterator i = functions.begin(); i != functions.end(); ++i) (*i)->do_precomputations(variables); } void VariableManager::put_new_parameters(vector const &aa) { for (size_t i = 0; i < min(aa.size(), parameters.size()); ++i) parameters[i] = aa[i]; use_parameters(); } vector VariableManager::get_vars_from_kw(string const &function, vector const &vars) { string formula = Function::get_formula(function); if (formula.empty()) throw ExecuteError("Undefined type of function: " + function); vector tnames = Function::get_varnames_from_formula(formula); vector tvalues = Function::get_defvalues_from_formula(formula); int n = tnames.size(); size_t vsize = vars.size(); vector vars_names(vsize), vars_rhs(vsize); for (size_t i = 0; i < vsize; ++i) { string::size_type eq = vars[i].find('='); assert(eq != string::npos); vars_names[i] = string(vars[i], 0, eq); vars_rhs[i] = string(vars[i], eq+1); } vector vv(n); for (int i = 0; i < n; ++i) { string const& tname = tnames[i]; // (1st try) variables given in vars int tname_idx = index_of_element(vars_names, tname); if (tname_idx != -1) { vv[i] = vars_rhs[tname_idx]; continue; } // (2nd try) use default parameter value if (!tvalues[i].empty()) { for (size_t j = 0; j < vsize; ++j) replace_words(tvalues[i], vars_names[j], vars_rhs[j]); try { fp v = get_transform_expression_value( strip_tilde_variable(tvalues[i]), 0); vv[i] = "~" + S(v); continue; } catch (ExecuteError &) {} //nothing } // (3rd try) name else if (tname == "hwhm") { int fwhm_idx = index_of_element(vars_names, "fwhm"); if (fwhm_idx != -1) { fp v = get_transform_expression_value("0.5*" + strip_tilde_variable(vars_rhs[fwhm_idx]), 0); vv[i] = "~" + S(v); continue; } } throw ExecuteError("Can't create function " + function + " because " + tname + " is unknown."); } return vv; } vector VariableManager::make_varnames(string const &function, vector const &vars) { vector varnames; bool has_eq = (vars.empty() || vars[0].find('=') != string::npos); for (vector::const_iterator i = vars.begin(); i != vars.end(); ++i) if ((i->find('=') != string::npos) != has_eq) throw ExecuteError("Either use keywords for all parameters" " or for none"); vector vv = (!has_eq ? vars : get_vars_from_kw(function, vars)); for (int i = 0; i < size(vv); ++i) varnames.push_back(get_or_make_variable(vv[i])); return varnames; } string VariableManager::assign_func(string const &name, string const &function, vector const &vars, bool parse_vars) { Function *func = 0; try { func = Function::factory(F, name.empty() ? next_func_name() : name, function, parse_vars ? make_varnames(function, vars) : vars); } catch (ExecuteError &) { remove_unreferred(); throw; } return do_assign_func(func); } string VariableManager::do_assign_func(Function* func) { func->set_var_idx(variables); //if there is already function with the same name -- replace bool found = false; for (int i = 0; i < size(functions); ++i) { if (functions[i]->name == func->name) { delete functions[i]; functions[i] = func; if (!silent) F->msg("New function %" + func->name +" replaced the old one."); remove_unreferred(); found = true; break; } } if (!found) { functions.push_back(func); if (!silent) F->msg("New function %" + func->name + " was created."); } func->do_precomputations(variables); return func->name; } string VariableManager::make_var_copy_name(Variable const* v) { if (v->name[0] == '_') return next_var_name(); //for other names append "01" or increase the last two digits in name int vs = v->name.size(); int appendix = 0; string core = v->name; if (vs > 2 && is_int(string(v->name, vs-2, 2))) { // foo02 appendix = atoi(v->name.c_str()+vs-2); core.resize(vs-2); } while (true) { ++appendix; string new_varname = core + S(appendix/10) + S(appendix%10); if (find_variable_nr(new_varname) == -1) return new_varname; } } string VariableManager::assign_func_copy(string const &name, string const &orig) { Function const* of = find_function(orig); map varmap; for (int i = 0; i < size(variables); ++i) { if (!of->is_dependent_on(i, variables)) continue; Variable const* var_orig = variables[i]; string new_varname = make_var_copy_name(var_orig); assign_variable_copy(new_varname, var_orig, varmap); varmap[i] = new_varname; } vector varnames; for (int i = 0; i != of->get_vars_count(); ++i) { assert(varmap.count(of->get_var_idx(i))); varnames.push_back(varmap[of->get_var_idx(i)]); } return assign_func(name, of->type_name, varnames, false); } void VariableManager::substitute_func_param(string const &name, string const ¶m, string const &var) { int nr = find_function_nr(name); if (nr == -1) throw ExecuteError("undefined function: %" + name); Function* k = functions[nr]; k->substitute_param(k->get_param_nr(param), get_or_make_variable(var)); k->set_var_idx(variables); k->do_precomputations(variables); remove_unreferred(); } fp VariableManager::variation_of_a (int n, fp variat) const { assert (0 <= n && n < size(get_parameters())); Domain const& dom = get_variable(n)->domain; fp ctr = dom.is_ctr_set() ? dom.get_ctr() : parameters[n]; fp sgm = dom.is_set() ? dom.get_sigma() : ctr * F->get_settings()->get_f("variable-domain-percent") / 100.; return ctr + sgm * variat; } string VariableManager::next_var_name() { while (1) { string t = "_" + S(++var_autoname_counter); if (find_variable_nr(t) == -1) return t; } } string VariableManager::next_func_name() { while (1) { string t = "_" + S(++func_autoname_counter); if (find_function_nr(t) == -1) return t; } } //TODO: remove it, use dtor+ctor void VariableManager::do_reset() { var_autoname_counter = 0; func_autoname_counter = 0; purge_all_elements(functions); purge_all_elements(variables); parameters.clear(); //don't delete sums, they should unregister itself for (vector::iterator i = sums.begin(); i != sums.end(); ++i) (*i)->find_function_indices(); }