/***************************************************************************
optstack.cpp - Class for managing options
-------------------
begin : Mon May 15 2006
copyright : (C) 2006 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 "../config/config.h" // *** added in 1.3.1
#include <sstream>
#include "optstack.h"
#include "msgdriver.h" // *** added in 1.3.1
#include <cmath>
// Define static members *** added in 1.3.1
optstack* optstack::options;
const bool option::operator==(const option& other) const {
switch (type) {
case t_vec: return value.vec == other.value.vec; // TODO: element-by-element comparison, not comparison of pointers
case t_str: return *value.str == *other.value.str;
case t_bool: return value.boolean == other.value.boolean;
case t_dbl: {
return (fabs(value.dbl - other.value.dbl) < 1E-10); // TODO: What limit is useful?
}
case t_align: return value.align == other.value.align;
case t_uint: return value.uinteger == other.value.uinteger;
case t_int: return value.integer == other.value.integer;
case t_ex: return *value.expr == *other.value.expr;
default:
return false;
}
}
const bool option::operator!=(const option& other) const {
return !(*this == other);
}
void option::print(std::ostream &os) const {
switch (type) {
case t_vec: {
os << "{";
for (unitvec::const_iterator i = value.vec->begin(); i != value.vec->end(); i++) {
os << (ex)*i << ";";
}
os << "}";
break;
}
case t_bool: os << value.boolean; break;
case t_dbl: os << value.dbl; break;
case t_align: os << value.align; break;
case t_uint: os << value.uinteger; break;
case t_int: os << value.integer; break;
case t_str: os << *value.str; break;
case t_ex: os << *value.expr; break;
default: throw std::invalid_argument("Internal error: Unknown option type");
}
}
//std::ostream &operator<<(std::ostream &os, const option_value &o) {
// o.print(os);
//return os;
//} // operator<<()
void optstack::init() { // *** added in 1.3.1
if (options == NULL) // *** added in 1.3.1
options = new optstack();
}
optstack::optstack() {
option_name_str[o_eqchain] = "eqchain";
option_name_str[o_eqraw] = "eqraw";
option_name_str[o_eqparse] = "eqparse";
option_name_str[o_eqginac] = "eqginac";
option_name_str[o_eqsplit] = "eqsplit";
option_name_str[o_eqsplittext] = "eqsplittext";
option_name_str[o_vecautosize] = "vecautosize";
option_name_str[o_eqalign] = "eqalign";
option_name_str[o_precision] = "precision";
option_name_str[o_fixeddigits] = "fixeddigits";
option_name_str[o_exponent] = "exponent";
option_name_str[o_lowsclimit] = "lowsclimit";
option_name_str[o_highsclimit] = "highsclimit";
option_name_str[o_lang] = "lang";
option_name_str[o_path] = "path";
option_name_str[o_units] = "units";
option_name_str[o_difftype] = "difftype"; // *** added in 1.4.1
/* option_name_str[o_include_init] = "include init.imath"; // *** the following added for iMath in 1.3.1
option_name_str[o_include_units]= "include units.imath";
option_name_str[o_include1] = "include file 1";
option_name_str[o_include2] = "include file 2";
option_name_str[o_include3] = "include file 3";
option_name_str[o_masterdoc] = "master document URL";*/
option_name_str[o_debug] = "debug level"; // *** added in 1.4.3
option_name_str[o_comments] = "remove comment lines"; // *** added in 1.4.3
option_name_str[o_tan] = "tangent function is \\tg (default \\tan)"; // *** added in 1.4.3
};
// TODO: The destructor should free the memory of the t_str and t_vec and t_ex pointers
void optstack::push(const option_name key, const option_value &value) {
if (opts[key].empty())
throw std::invalid_argument("Error: Option stack for '" + name(key) + "' is empty");
option o;
o.value = value;
o.type = opts[key].top().type;
opts[key].push(o);
if (msg::info().checkprio(4)) {
msg::info() << "Pushed option " << name(key) << " = ";
std::ostringstream os;
opts[key].top().print(os);
msg::info() << os.str() << endline;
}
} // optstack::push()
void optstack::pop(const option_name key) {
if (opts[key].empty())
throw std::invalid_argument("Error: Option stack for '" + name(key) + "' is empty");
option o = opts[key].top();
MSG_INFO(4) << "Popped option " << name(key) << endline;
if (msg::info().checkprio(5)) {
msg::info() << "Old value was ";
std::ostringstream os;
o.print(os);
msg::info() << os.str() << endline;
}
// TODO: Delete the string and vector pointers here?
opts[key].pop();
} //optstack::pop()
void optstack::dup(const option_name key) {
if (opts[key].empty())
throw std::invalid_argument("Error: Option stack for '" + name(key) + "' is empty");
opts[key].push(opts[key].top());
MSG_INFO(4) << "Duplicated option " << name(key) << endline;
} //optstack::dup()
const option_value& optstack::top(const option_name key) {
if (opts[key].empty())
throw std::invalid_argument("Error: Option stack for '" + name(key) + "' is empty");
if (msg::info().checkprio(4)) {
msg::info() << "Read option " << name(key) << " = ";
std::ostringstream os;
opts[key].top().print(os);
msg::info() << os.str() << endline;
}
return opts[key].top().value;
} // optstack::top()
const std::string optstack::name(const option_name key) {
if (option_name_str[key] == "")
throw std::invalid_argument("Internal error: No name given for this option");
return option_name_str[key];
} // optstack::name()
const numeric &get_val_from_ex(const ex &e) {
ex v;
if (!is_a<numeric>(e))
v = e.evalf();
else
v = e;
if (!is_a<numeric>(v))
throw std::invalid_argument("Option requires a numeric value");
return (ex_to<numeric>(v));
} // get_val_from_ex()
void optstack::set(const option_name key, const option_value &value) {
option_value o;
switch (opts[key].top().type) {
case t_uint: {
numeric v = get_val_from_ex(*value.expr);
if (!v.info(info_flags::posint))
throw std::invalid_argument("Error: Option " + name(key) + " must be a positive integer");
o.uinteger = (unsigned)v.to_int();
// TODO: Is this the right place for changing Digits?
if (key == o_precision) Digits = v.to_int() + 5;
break;
}
case t_int: {
numeric v = get_val_from_ex(*value.expr);
if (!v.info(info_flags::integer))
throw std::invalid_argument("Error: Option " + name(key) + " must be an integer");
o.integer = (int)v.to_int();
break;
}
case t_dbl: {
numeric v = get_val_from_ex(*value.expr);
if (!v.info(info_flags::real))
throw std::invalid_argument("Error: Option " + name(key) + " must be a double");
o.dbl = v.to_double();
break;
}
default:
o = value;
}
pop(key);
push(key, o);
if (msg::info().checkprio(2)) {
msg::info() << "Set option " << name(key) << " to ";
std::ostringstream os;
opts[key].top().print(os);
msg::info() << os.str() << endline;
}
} // optstack::set()
void optstack::set(const option_name key, const std::string &value) {
if (opts[key].top().type != t_str)
throw std::invalid_argument("Error: Option " + name(key) + " does not accept a string value");
pop(key);
option_value o;
o.str = new std::string(value);
push(key, o);
MSG_INFO(1) << "Setting string option " << name(key) << " to " << value << endline;
} // optstack::set()
/*
void optstack::set(const option_name key, const ex &value) {
ex v1;
if (!is_a<numeric>(value))
v1 = value.evalf();
else
v1 = value;
if (!is_a<numeric>(v1))
throw std::invalid_argument("Option " + name(key) + " requires a numeric value");
option_value o;
numeric v = ex_to<numeric>(value);
switch (opts[key].top().type) {
case t_uint: {
if (!v.info(info_flags::posint))
throw std::invalid_argument("Error: Option " + name(key) + " must be a positive integer");
o.uinteger = (unsigned)v.to_int();
// TODO: Is this the right place for changing Digits?
if (key == o_precision) Digits = v.to_int() + 5;
break;
}
case t_int: {
if (!v.info(info_flags::integer))
throw std::invalid_argument("Error: Option " + name(key) + " must be an integer");
o.integer = (int)v.to_int();
break;
}
case t_dbl: {
if (!v.info(info_flags::real))
throw std::invalid_argument("Error: Option " + name(key) + " must be a double");
o.dbl = v.to_double();
break;
}
default:
throw std::invalid_argument("Error: Option " + name(key) + " does not accept a numeric value");
}
pop(key);
push(key, o);
MSG_INFO(1)) {
1) << "Setting numeric option " << name(key) << " to ";
v.print(msg_info.stream());
1) << endline;
}
} // optstack::set()
*/
void optstack::set(const option_name key, const bool value) {
if (opts[key].top().type != t_bool)
throw std::invalid_argument("Error: Option " + name(key) + " does not accept a boolean value");
pop(key);
option_value o;
o.boolean = value;
push(key, o);
MSG_INFO(1) << "Setting boolean option " << name(key) << " to " << (value ? "true" : "false") << endline;
} // optstack::set()
const option_value& optstack::get(const option_name key) {
if (msg::info().checkprio(2)) {
msg::info() << "Got value of option " << name(key) << " = ";
std::ostringstream os;
opts[key].top().print(os);
msg::info() << os.str() << endline;
}
return top(key);
} // optstack::get()
void optstack::registr(const option_name key, const option_type type, void *value) {
option o;
o.type = type;
std::string t_name = "";
switch (type) {
case t_bool: o.value.boolean = *(bool *)value; t_name = "boolean"; break;
case t_str: o.value.str = (std::string*)value; t_name = "string"; break;
case t_align: o.value.align = *(aligntype*)value; t_name = "alignment"; break;
case t_int: o.value.integer = *(int*)value; t_name = "integer"; break;
case t_uint: o.value.uinteger = *(unsigned*)value; t_name = "unsigned integer"; break;
case t_vec: o.value.vec = (unitvec*)value; t_name = "vector"; break;
case t_dbl: o.value.dbl = *(double*)value; t_name = "double"; break;
default:
throw std::invalid_argument("Internal error: Unknown option to optstack::registr");
}
opts[key].push(o);
if (msg::info().checkprio(4)) {
msg::info() << "Registered option (type " << t_name << ") " << name(key) << " = ";
std::ostringstream os;
opts[key].top().print(os);
msg::info() << os.str() << endline;
}
dup(key); // Save the default value of this option so that the type can always be determined
} // optstack::registr()
void optstack::save(const option_name key) {
dup(key); // Duplicate value because setoption will remove one
MSG_INFO(1) << "Saved value of " << name(key) << endline;
} // optstack::save()
// *** added in 1.2
void optstack::restore(const option_name key) {
pop(key); // Remove current value of the option from top of stack
MSG_INFO(1) << "Restored value of " << name(key) << endline;
} // optstack::restore()
void optstack::clear(const option_name key) {
if (opts[key].empty())
throw std::invalid_argument("Error: Option stack for '" + name(key) + "' is empty");
for (unsigned i = 1; i < opts[key].size(); i++) opts[key].pop();
MSG_INFO(3) << "Cleared option " << name(key) << endline;
} // optstack::clear()
void optstack::clearall() {
for (std::map<const option_name, std::stack<option> >::const_iterator o = opts.begin();
o != opts.end(); o++) {
clear(o->first);
}
} // optstack::clearall()