// 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 <stdlib.h>
#include <ctype.h>
#include <boost/spirit/core.hpp>
#include <algorithm>
#include <memory>
using namespace std;
VariableManager::~VariableManager()
{
purge_all_elements(functions);
purge_all_elements(variables);
}
void VariableManager::unregister_sum(Sum const *s)
{
vector<Sum*>::iterator k = find(sums.begin(), sums.end(), s);
assert (k != sums.end());
sums.erase(k);
}
void VariableManager::sort_variables()
{
for (vector<Variable*>::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<Variable*>::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<string> vars=find_tokens_in_ptree(FuncGrammar::variableID, info);
if (contains_element(vars, "x"))
throw ExecuteError("variable can't depend on x.");
for (vector<string>::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<OpTree*> op_trees = calculate_deriv(root, vars);
// ~14.3 -> $var4
for (vector<string>::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<string> 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<Function*>::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<string> VariableManager::get_variable_references(string const &name)
{
int idx = find_variable_nr(name);
vector<string> refs;
for (vector<Variable*>::const_iterator i = variables.begin();
i != variables.end(); ++i)
if ((*i)->is_directly_dependent_on(idx))
refs.push_back((*i)->xname);
for (vector<Function*>::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<Variable*>::iterator i = variables.begin();
i != variables.end(); ++i)
(*i)->set_var_idx(variables);
for (vector<Function*>::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<Variable*>::iterator j = variables.begin();
j != variables.end(); ++j)
(*j)->erased_parameter(i);
for (vector<Function*>::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<Variable> 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<int,string> 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<string> 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<OpTree*> new_op_trees;
for (vector<OpTree*>::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<string> const &names)
{
const int n = names.size();
vector<int> 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<string> const &names)
{
if (names.empty())
return;
for (vector<string>::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<Sum*>::iterator i = sums.begin(); i != sums.end(); ++i)
(*i)->find_function_indices();
}
bool VariableManager::is_function_referred(int n) const
{
for (vector<Sum*>::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<Sum*>::iterator i = sums.begin(); i != sums.end(); ++i)
(*i)->find_function_indices();
}
}
void VariableManager::delete_funcs_and_vars(vector<string> const &xnames)
{
vector<string> vars, funcs;
for (vector<string>::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<fp> const &ext_param)
{
for (vector<Variable*>::iterator i = variables.begin();
i != variables.end(); ++i)
(*i)->recalculate(variables, ext_param);
for (vector<Function*>::iterator i = functions.begin();
i != functions.end(); ++i)
(*i)->do_precomputations(variables);
}
void VariableManager::put_new_parameters(vector<fp> const &aa)
{
for (size_t i = 0; i < min(aa.size(), parameters.size()); ++i)
parameters[i] = aa[i];
use_parameters();
}
vector<string> VariableManager::get_vars_from_kw(string const &function,
vector<string> const &vars)
{
string formula = Function::get_formula(function);
if (formula.empty())
throw ExecuteError("Undefined type of function: " + function);
vector<string> tnames = Function::get_varnames_from_formula(formula);
vector<string> tvalues = Function::get_defvalues_from_formula(formula);
int n = tnames.size();
size_t vsize = vars.size();
vector<string> 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<string> 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<string> VariableManager::make_varnames(string const &function,
vector<string> const &vars)
{
vector<string> varnames;
bool has_eq = (vars.empty() || vars[0].find('=') != string::npos);
for (vector<string>::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<string> 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<string> 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<int,string> 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<string> 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<Sum*>::iterator i = sums.begin(); i != sums.end(); ++i)
(*i)->find_function_indices();
}
syntax highlighted by Code2HTML, v. 0.9.1