// This file is part of fityk program. Copyright (C) Marcin Wojdyr // Licence: GNU General Public License version 2 // $Id: logic.cpp 322 2007-07-24 00:17:11Z wojdyr $ #include #include #include #include #include #include #include "common.h" #include "logic.h" #include "data.h" #include "sum.h" #include "ui.h" #include "fit.h" #include "guess.h" #include "settings.h" #include "mgr.h" #include "func.h" using namespace std; DataWithSum::DataWithSum(Ftk *F, Data* data_) : data(data_ ? data_ : new Data(F)), sum(new Sum(F)) {} bool DataWithSum::has_any_info() const { return get_data()->has_any_info() || get_sum()->has_any_info(); } Ftk::Ftk() : VariableManager(this), default_relative_domain_width(0.1) { // reading numbers won't work with decimal points different than '.' setlocale(LC_NUMERIC, "C"); ui = new UserInterface(this); initialize(); AL = this; } Ftk::~Ftk() { destroy(); delete ui; } // initializations common for ctor and reset() void Ftk::initialize() { fit_container = new FitMethodsContainer(this); // Settings ctor is using FitMethodsContainer settings = new Settings(this); view = View(0, 180, -50, 1e3); append_ds(); activate_ds(0); get_settings()->do_srand(); UdfContainer::initialize_udfs(); } // cleaning common for dtor and reset() void Ftk::destroy() { dsds.clear(); VariableManager::do_reset(); delete fit_container; delete settings; } // reset everything but UserInterface (and related settings) void Ftk::reset() { string verbosity = get_settings()->getp("verbosity"); string autoplot = get_settings()->getp("autoplot"); destroy(); ui->keep_quiet = true; initialize(); get_settings()->setp("verbosity", verbosity); get_settings()->setp("autoplot", autoplot); ui->keep_quiet = false; } void Ftk::activate_ds(int d) { if (d < 0 || d >= size(dsds)) throw ExecuteError("there is no such dataset: @" + S(d)); active_ds = d; } int Ftk::append_ds(Data *data) { DataWithSum* ds = new DataWithSum(this, data); dsds.push_back(ds); return dsds.size() - 1; } void Ftk::remove_ds(int d) { if (d < 0 || d >= size(dsds)) throw ExecuteError("there is no such dataset: @" + S(d)); delete dsds[d]; dsds.erase(dsds.begin() + d); if (dsds.empty()) append_ds(); if (active_ds == d) activate_ds( d==size(dsds) ? d-1 : d ); } const Function* Ftk::find_function_any(string const &fstr) const { if (fstr.empty()) return 0; return VariableManager::find_function(find_function_name(fstr)); } string Ftk::find_function_name(string const &fstr) const { if (fstr[0] == '%' || islower(fstr[0])) return fstr; int pos = 0; int pref = -1; if (fstr[0] == '@') { pos = fstr.find(".") + 1; pref = strtol(fstr.c_str()+1, 0, 10); } vector const &names = get_ds(pref)->get_sum()->get_names(fstr[pos]); int idx_ = strtol(fstr.c_str()+pos+2, 0, 10); int idx = (idx_ >= 0 ? idx_ : idx_ + names.size()); if (!is_index(idx, names)) throw ExecuteError("There is no item with index " + S(idx_)); return names[idx]; } void Ftk::dump_all_as_script(string const &filename) { ofstream os(filename.c_str(), ios::out); if (!os) { warn ("Can't open file: " + filename); return; } os << fityk_version_line << "## dumped at: " << time_now() << endl; os << "set verbosity = quiet #the rest of the file is not shown\n"; os << "set autoplot = never\n"; os << "reset\n"; os << "# ------------ settings ------------\n"; os << get_settings()->set_script() << endl; os << "# ------------ variables and functions ------------\n"; for (vector::const_iterator i = variables.begin(); i != variables.end(); ++i) os << (*i)->xname << " = " << (*i)->get_formula(parameters) << endl; os << endl; vector const& udfs = UdfContainer::get_udfs(); for (vector::const_iterator i = udfs.begin(); i != udfs.end(); ++i) if (!i->is_builtin) os << "define " << i->formula << endl; os << endl; for (vector::const_iterator i = functions.begin(); i != functions.end(); ++i) { if ((*i)->has_outdated_type()) { string new_formula = Function::get_formula((*i)->type_name); if (!new_formula.empty()) os << "undefine " << (*i)->type_name << endl; os << "define " << (*i)->type_formula << endl; os << (*i)->get_basic_assignment() << endl; os << "undefine " << (*i)->type_name << endl; if (!new_formula.empty()) os << "define " << new_formula << endl; } else os << (*i)->get_basic_assignment() << endl; } os << endl; os << "# ------------ datasets and sums ------------\n"; for (int i = 0; i != get_ds_count(); ++i) { Data const* data = get_data(i); if (i != 0) os << "@+\n"; if (!data->get_title().empty()) os << "@" << i << ".title = '" << data->get_title() << "'\n"; int m = data->points().size(); os << "M=" << m << " in @" << i << endl; os << "X=" << data->get_x_max() << " in @" << i << " # =max(x), prevents sorting." << endl; for (int j = 0; j != m; ++j) { Point const& p = data->points()[j]; os << "X[" << j << "]=" << p.x << ", Y[" << j << "]=" << p.y << ", S[" << j << "]=" << p.sigma << ", A[" << j << "]=" << (p.is_active ? 1 : 0) << " in @" << i << endl; } os << endl; Sum const* sum = get_sum(i); if (!sum->get_ff_names().empty()) os << "@" << i << ".F = " << join_vector(concat_pairs("%", sum->get_ff_names()), " + ") << endl; if (!sum->get_zz_names().empty()) os << "@" << i << ".Z = " << join_vector(concat_pairs("%", sum->get_zz_names()), " + ") << endl; os << endl; } os << "plot " << view.str() << " in @" << active_ds << endl; os << "set autoplot = " << get_settings()->getp("autoplot") << endl; os << "set verbosity = " << get_settings()->getp("verbosity") << endl; } int Ftk::check_ds_number(int n) const { if (n == -1) { if (get_ds_count() == 1) return 0; else throw ExecuteError("Dataset must be specified."); } if (n < 0 || n >= get_ds_count()) throw ExecuteError("There is no dataset @" + S(n)); return n; } /// Send warning to UI. void Ftk::warn(std::string const &s) const { get_ui()->output_message(os_warn, s); } /// Send implicitely requested message to UI. void Ftk::rmsg(std::string const &s) const { get_ui()->output_message(os_normal, s); } /// Send message to UI. void Ftk::msg(std::string const &s) const { if (get_ui()->get_verbosity() >= 0) get_ui()->output_message(os_normal, s); } /// Send verbose message to UI. void Ftk::vmsg(std::string const &s) const { if (get_ui()->get_verbosity() >= 1) get_ui()->output_message(os_normal, s); } int Ftk::get_verbosity() const { return settings->get_e("verbosity"); } /// execute command(s) from string Commands::Status Ftk::exec(std::string const &s) { return get_ui()->exec_and_log(s); } Fit* Ftk::get_fit() { int nr = get_settings()->get_e("fitting-method"); return get_fit_container()->get_method(nr); } void Ftk::import_dataset(int slot, string const& filename, string const& type, vector const& cols) { const int new_dataset = -1; if (slot == new_dataset) { vector next_files; if (this->get_ds_count() != 1 || this->get_data(0)->has_any_info() || this->get_sum(0)->has_any_info()) { // load data into new slot auto_ptr data(new Data(this)); next_files = data->load_file(filename, type, cols); slot = this->append_ds(data.release()); } else { // there is only one and empty slot -- load data there next_files = this->get_data(-1)->load_file(filename, type, cols); this->view.set_datasets(vector1(this->get_ds(0))); this->view.fit(); slot = 0; } for (vector::const_iterator i = next_files.begin(); i != next_files.end(); ++i) this->import_dataset(new_dataset, *i, type, cols); } else { // slot number was specified -- load data there this->get_data(slot)->load_file(filename, type, cols); if (this->get_ds_count() == 1) { this->view.set_datasets(vector1(this->get_ds(0))); this->view.fit(); } } this->activate_ds(slot); } //================================================================== const fp View::relative_x_margin = 1./20.; const fp View::relative_y_margin = 1./20.; string View::str() const { return "[" + (left!=right ? S(left) + ":" + S(right) : string(" ")) + "] [" + (bottom!=top ? S(bottom) + ":" + S(top) : string (" ")) + "]"; } void View::fit(int flag) { if (flag&fit_left || flag&fit_right) { fp x_min=0, x_max=0; get_x_range(x_min, x_max); if (x_min == x_max) { x_min -= 0.1; x_max += 0.1; } fp x_margin = (x_max - x_min) * relative_x_margin; if (flag&fit_left) left = x_min - x_margin; if (flag&fit_right) right = x_max + x_margin; } if (flag&fit_top || flag&fit_bottom) { fp y_min=0, y_max=0; get_y_range(y_min, y_max); if (y_min == y_max) { y_min -= 0.1; y_max += 0.1; } fp y_margin = (y_max - y_min) * relative_y_margin; if (flag&fit_bottom) bottom = y_min - y_margin; if (flag&fit_top) top = y_max + y_margin; } } void View::get_x_range(fp &x_min, fp &x_max) { if (datas.empty()) throw ExecuteError("Can't find x-y axes ranges for plot"); x_min = datas.front()->get_x_min(); x_max = datas.front()->get_x_max(); for (vector::const_iterator i = datas.begin()+1; i != datas.end(); ++i) { x_min = min(x_min, (*i)->get_x_min()); x_max = max(x_max, (*i)->get_x_max()); } } void View::get_y_range(fp &y_min, fp &y_max) { if (datas.empty()) throw ExecuteError("Can't find x-y axes ranges for plot"); y_min = y_max = (datas.front()->get_n() > 0 ? datas.front()->get_y(0) : 0); bool min_max_set = false; for (vector::const_iterator i = datas.begin(); i != datas.end(); ++i) { vector::const_iterator f = (*i)->get_point_at(left); vector::const_iterator l = (*i)->get_point_at(right); //first we are searching for minimal and max. y in active points for (vector::const_iterator i = f; i < l; i++) { if (i->is_active && is_finite(i->y)) { min_max_set = true; if (i->y > y_max) y_max = i->y; if (i->y < y_min) y_min = i->y; } } } if (!min_max_set || y_min == y_max) { //none or 1 active point, so now we // search for min. and max. y in all points for (vector::const_iterator i = datas.begin(); i != datas.end(); ++i) { vector::const_iterator f = (*i)->get_point_at(left); vector::const_iterator l = (*i)->get_point_at(right); for (vector::const_iterator i = f; i < l; i++) { if (!is_finite(i->y)) continue; min_max_set = true; if (i->y > y_max) y_max = i->y; if (i->y < y_min) y_min = i->y; } } } for (vector::const_iterator i = sums.begin(); i != sums.end(); ++i) { Sum *sum = *i; if (!sum->has_any_info()) continue; // estimated sum maximum fp sum_y_max = sum->approx_max(left, right); if (sum_y_max > y_max) y_max = sum_y_max; if (sum_y_max < y_min) y_min = sum_y_max; } // include or not include zero const fp show_zero_factor = 0.1; if (y_min > 0 && y_max - y_min > show_zero_factor * y_max) y_min = 0; else if (y_max < 0 && y_max - y_min > show_zero_factor * fabs(y_min)) y_max = 0; } void View::parse_and_set(std::vector const& lrbt) { assert(lrbt.size() == 4); string const &left = lrbt[0]; string const &right = lrbt[1]; string const &bottom = lrbt[2]; string const &top = lrbt[3]; fp l=0., r=0., b=0., t=0.; int flag = 0; if (left.empty()) flag |= fit_left; else if (left != ".") { flag |= change_left; l = strtod(left.c_str(), 0); } if (right.empty()) flag |= fit_right; else if (right != ".") { flag |= change_right; r = strtod(right.c_str(), 0); } if (bottom.empty()) flag |= fit_bottom; else if (bottom != ".") { flag |= change_bottom; b = strtod(bottom.c_str(), 0); } if (top.empty()) flag |= fit_top; else if (top != ".") { flag |= change_top; t = strtod(top.c_str(), 0); } set(l, r, b, t, flag); fit(flag); } //TODO set_datasets() is should not be public, datasets // should be always passed by fit() and parse_and_set() void View::set_datasets(vector const& dd) { assert(!dd.empty()); datas.clear(); sums.clear(); for (vector::const_iterator i = dd.begin(); i != dd.end(); ++i) datas.push_back((*i)->get_data()); sums.push_back(dd.front()->get_sum()); } // the use of this global variable in libfityk will be eliminated, // because it's not thread safe. Ftk* AL = 0;