// 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 <stdio.h>
#include <fstream>
#include <time.h>
#include <stdlib.h>
#include <ctype.h>
#include <locale.h>
#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<string> 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<Variable*>::const_iterator i = variables.begin();
i != variables.end(); ++i)
os << (*i)->xname << " = " << (*i)->get_formula(parameters) << endl;
os << endl;
vector<UdfContainer::UDF> const& udfs = UdfContainer::get_udfs();
for (vector<UdfContainer::UDF>::const_iterator i = udfs.begin();
i != udfs.end(); ++i)
if (!i->is_builtin)
os << "define " << i->formula << endl;
os << endl;
for (vector<Function*>::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<int> const& cols)
{
const int new_dataset = -1;
if (slot == new_dataset) {
vector<string> 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> 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<string>::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<Data*>::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<Data*>::const_iterator i = datas.begin(); i != datas.end();
++i) {
vector<Point>::const_iterator f = (*i)->get_point_at(left);
vector<Point>::const_iterator l = (*i)->get_point_at(right);
//first we are searching for minimal and max. y in active points
for (vector<Point>::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<Data*>::const_iterator i = datas.begin(); i != datas.end();
++i) {
vector<Point>::const_iterator f = (*i)->get_point_at(left);
vector<Point>::const_iterator l = (*i)->get_point_at(right);
for (vector<Point>::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<Sum*>::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<std::string> 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<DataWithSum*> const& dd)
{
assert(!dd.empty());
datas.clear();
sums.clear();
for (vector<DataWithSum*>::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;
syntax highlighted by Code2HTML, v. 0.9.1