is missing there.
If you install from git, you also need GNU autoconf (>=2.59), automake (>=1.8),
-libtool (>= 1.5), bison (>= 2.3), flex (>= 2.5.33) to be installed.
+libtool (>= 1.5), bison (>= 2.3), flex (>= 2.5.33), autogen (>= 5.6.0) to be
+installed.
INSTALLATION
ex g = chinrem_gcd(A, B);
}
+static void integer_coeff_braindamage()
+{
+ parser readme;
+ ex A = readme("3*x2 + 1");
+ ex B = readme("9*x2 + 1");
+ ex g = chinrem_gcd(A, B);
+ if (!g.is_equal(ex(1))) {
+ std::cerr << "expected 1, got " << g << std::endl;
+ throw std::logic_error("chinrem_gcd miscomputed integer content");
+ }
+}
+
int main(int argc, char** argv)
{
cout << "checking for bugs in poly_cra() and friends " << flush;
check_poly_cra();
check_extract_integer_content();
+ integer_coeff_braindamage();
cout << "not found.";
return 0;
}
AC_PATH_PROG(FIG2DEV, fig2dev, "")
AM_CONDITIONAL(CONFIG_FIG2DEV, [test ! -z "$FIG2DEV"])
+dnl generate boilerplate code for the (new) parser.
+dnl Only developers need this tool.
+AC_PATH_PROG(AUTOGEN, autogen, "")
+
dnl Output makefiles etc.
AC_CONFIG_FILES([
Makefile
parser/parse_binop_rhs.cpp \
parser/parser.cpp \
parser/parse_context.cpp \
+ parser/builtin_fcns.cpp \
parser/lexer.cpp \
parser/lexer.h \
parser/parser_compat.cpp \
parser/parser.h \
parser/parse_context.h
-EXTRA_DIST = function.pl version.h.in
+EXTRA_DIST = function.pl version.h.in \
+parser/default_reader.tpl parser/builtin_fcns.def
+
+# Files produced by autogen(1) from templates
+$(srcdir)/parser/builtin_fcns.cpp: $(srcdir)/parser/builtin_fcns.def $(srcdir)/parser/default_reader.tpl
+ set -e; if [ -n "$(AUTOGEN)" ]; then \
+ cd $(srcdir)/parser; \
+ $(AUTOGEN) -T default_reader.tpl builtin_fcns.def; \
+ elif [ -f $@ ]; then \
+ echo "WARNING: AutoGen is not available, the \"$@\" file WON'T be re-generated"; \
+ else \
+ echo "*** ERROR: the \"$@\" file does not exist, and AutoGen is not installed on your system"; \
+ echo "*** Please install AutoGen (http://www.gnu.org/software/autogen)"; \
+ exit 1; \
+ fi
+
# Files which are generated by perl scripts
$(srcdir)/function.h $(srcdir)/function.cpp: $(srcdir)/function.pl
static unsigned register_new(function_options const & opt);
static unsigned current_serial;
static unsigned find_function(const std::string &name, unsigned nparams);
- static std::vector<function_options> get_registered_functions() { return registered_functions(); };
unsigned get_serial() const {return serial;}
std::string get_name() const;
function::function(unsigned ser, const exvector & v, bool discardable)
: exprseq(v,discardable), serial(ser)
{
+ if ( ser >= registered_functions().size() ) {
+ throw std::runtime_error("function does not exist");
+ }
}
function::function(unsigned ser, std::auto_ptr<exvector> vp)
/** Return the print name of the function. */
std::string function::get_name() const
{
- GINAC_ASSERT(serial<registered_functions().size());
+ if ( serial >= registered_functions().size() ) {
+ throw std::runtime_error("unknown function");
+ }
return registered_functions()[serial].name;
}
--- /dev/null
+Autogen definitions ginacfcns;
+
+/* Thease are not functions, but anyway ... */
+function = { name = "sqrt"; };
+
+function = {
+ name = "pow";
+ args = 2;
+};
+
+function = {
+ name = "power";
+ args = 2;
+};
--- /dev/null
+[+ AutoGen5 template .cpp +][+
+COMMENT a part of GiNaC parser -- construct functions from a byte stream.
++][+
+(use-modules (ice-9 format))
+
+(define (sequence start end . step)
+ (let ((step (if (null? step) 1 (car step))))
+ (let loop ((n start))
+ (if (> n end) '() (cons n (loop (+ step n)))))))
++]/*
+[+ (dne " * " " * " ) +]
+ *
+ * If you want to change this file, edit either `[+ (def-file) +]' or
+ * `[+ (tpl-file) +]' file, and run the following command:
+ *
+ * autogen -T [+ (tpl-file) +] [+ (def-file) +]
+ */
+#include "parse_context.h"
+#include "power.h"
+#include "operators.h"
+#include "inifcns.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h> // for uintptr_t
+#endif
+
+namespace GiNaC
+{
+[+ FOR function +]
+static ex [+ (get "name") +]_reader(const exvector& ev)
+{
+ return GiNaC::[+ (get "name") +]([+
+ (let ((nargs (if (exist? "args")
+ (string->number (get "args")) 1)))
+ (format '#f "~{ev[~a]~^, ~}" (sequence 0 (- nargs 1)))) +]);
+}[+ ENDFOR +]
+
+// function::registered_functions() is protected, but we need to access it
+class registered_functions_hack : public function
+{
+public:
+ static const std::vector<function_options>& get_registered_functions()
+ {
+ return function::registered_functions();
+ }
+private:
+ registered_functions_hack();
+ registered_functions_hack(const registered_functions_hack&);
+ registered_functions_hack& operator=(const registered_functions_hack&);
+};
+
+// Encode an integer into a pointer to a function. Since functions
+// are aligned (the minimal alignment depends on CPU architecture)
+// we can distinguish between pointers and integers.
+static reader_func encode_serial_as_reader_func(unsigned serial)
+{
+ uintptr_t u = (uintptr_t)serial;
+ u = (u << 1) | (uintptr_t)1;
+ reader_func ptr = (reader_func)((void *)u);
+ return ptr;
+}
+
+const prototype_table& get_default_reader()
+{
+ using std::make_pair;
+ static bool initialized = false;
+ static prototype_table reader;
+ if (!initialized) {
+ [+ FOR function +]
+ reader[make_pair("[+ (get "name") +]", [+
+ (if (exist? "args") (get "args") "1")
+ +])] = [+ (get "name") +]_reader;[+
+ ENDFOR +]
+ std::vector<function_options>::const_iterator it =
+ registered_functions_hack::get_registered_functions().begin();
+ std::vector<function_options>::const_iterator end =
+ registered_functions_hack::get_registered_functions().end();
+ unsigned serial = 0;
+ for (; it != end; ++it) {
+ prototype proto = make_pair(it->get_name(), it->get_nparams());
+ reader[proto] = encode_serial_as_reader_func(serial);
+ ++serial;
+ }
+ initialized = true;
+ }
+ return reader;
+}
+
+const prototype_table& get_builtin_reader()
+{
+ using std::make_pair;
+ static bool initialized = false;
+ static prototype_table reader;
+ if (!initialized) {
+ [+ FOR function +]
+ reader[make_pair("[+ (get "name") +]", [+
+ (if (exist? "args") (get "args") "1")
+ +])] = [+ (get "name") +]_reader;[+
+ ENDFOR +]
+ enum {
+ log,
+ exp,
+ sin,
+ cos,
+ tan,
+ asin,
+ acos,
+ atan,
+ sinh,
+ cosh,
+ tanh,
+ asinh,
+ acosh,
+ atanh,
+ atan2,
+ Li2,
+ Li3,
+ zetaderiv,
+ Li,
+ S,
+ H,
+ lgamma,
+ tgamma,
+ beta,
+ factorial,
+ binomial,
+ Order,
+ NFUNCTIONS
+ };
+ std::vector<function_options>::const_iterator it =
+ registered_functions_hack::get_registered_functions().begin();
+ unsigned serial = 0;
+ for ( ; serial<NFUNCTIONS; ++it, ++serial ) {
+ prototype proto = make_pair(it->get_name(), it->get_nparams());
+ reader[proto] = encode_serial_as_reader_func(serial);
+ }
+ initialized = true;
+ }
+ return reader;
+}
+
+} // namespace GiNaC
#include "parse_context.h"
-#include "function.h"
-
#include <sstream>
#include <stdexcept>
return sy;
}
-const prototype_table& get_default_reader(bool force_init)
-{
- using std::make_pair;
- static bool initialized = false;
- static prototype_table reader;
- if ( !initialized || force_init ) {
- std::vector<function_options> flist = function::get_registered_functions();
- std::vector<function_options>::iterator i = flist.begin(), end = flist.end();
- for ( ; i != end; ++i ) {
- std::string name = i->get_name();
- unsigned narg = i->get_nparams();
- reader[make_pair(name, narg)] = function::find_function(name, narg);
- }
- initialized = true;
- }
- return reader;
-}
-
} // namespace GiNaC
* foo(x+y, z^2, t)), it looks up such a table to find out which
* function (or class) corresponds to the given name and has the given
* number of the arguments.
+ *
+ * N.B.
+ *
+ * 1. The function don't have to return a (GiNaC) function or class, it
+ * can return any expression.
+ * 2. Overloaded functions/ctors are paritally supported, i.e. there might
+ * be several functions with the same name, but they should take different
+ * number of arguments.
+ * 3. User can extend the parser via custom prototype tables. It's possible
+ * to read user defined classes, create abbreviations, etc.
+ *
+ * NOTE: due to a hack that allows user defined functions to be parsed, the map
+ * value of type reader_func is internally treated as an unsigned and not as a
+ * function pointer!! The unsigned has to correspond to the serial number of
+ * the defined GiNaC function.
*/
-typedef std::map<prototype, unsigned> prototype_table;
+typedef std::map<prototype, reader_func> prototype_table;
/**
- * Creates a default prototype table containing all defined GiNaC functions.
+ * Default prototype table.
+ *
+ * It supports all defined GiNaC functions and "pow", "sqrt", and "power".
+ */
+extern const prototype_table& get_default_reader();
+/**
+ * Builtin prototype table.
*
- * The data referenced by the return value is only created once when this
- * function is called for the first time. This might cause problems in very
- * rare stituations (i.e. if functions are added after this first call). In
- * that case, a new initialization can be forced with an "true" argument.
+ * It supports only the builtin GiNaC functions and "pow", "sqrt", and "power".
*/
-extern const prototype_table& get_default_reader(bool force_init = false);
+extern const prototype_table& get_builtin_reader();
} // namespace GiNaC
#include "lexer.h"
#include "debug.h"
#include "mul.h"
-#include "function.h"
#include "constant.h"
+#include "function.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h> // for uintptr_t
+#endif
#include <sstream>
#include <stdexcept>
namespace GiNaC {
+// <KLUDGE>
+// Find out if ptr is a pointer to a function or a specially crafted integer.
+// It's possible to distinguish between these because functions are aligned.
+// Returns true if ptr is a pointer and false otherwise.
+static bool decode_serial(unsigned& serial, const reader_func ptr)
+{
+ uintptr_t u = (uintptr_t)(void *)ptr;
+ if (u & 1) {
+ u >>= 1;
+ serial = (unsigned)u;
+ return false;
+ }
+ return true;
+}
+
+// Figures out if ptr is a pointer to function or a serial of GiNaC function.
+// In the former case calls that function, in the latter case constructs
+// GiNaC function with corresponding serial and arguments.
+static ex dispatch_reader_fcn(const reader_func ptr, const exvector& args)
+{
+ unsigned serial = 0; // dear gcc, could you please shut up?
+ bool is_ptr = decode_serial(serial, ptr);
+ if (is_ptr)
+ return ptr(args);
+ else
+ return function(serial, args);
+}
+// </KLUDGE>
+
+
/// identifier_expr: identifier | identifier '(' expression* ')'
ex parser::parse_identifier_expr()
{
Parse_error_("no function \"" << name << "\" with " <<
args.size() << " arguments");
}
- ex ret = function(reader->second, args);
+ // reader->second might be a pointer to a C++ function or a specially
+ // crafted serial of a GiNaC::function.
+ ex ret = dispatch_reader_fcn(reader->second, args);
return ret;
}
{
static const cln::cl_I n1(1);
const numeric icont_ = A.integer_content();
+ if (cln::instanceof(icont_.to_cl_N(), cln::cl_I_ring)) {
+ const cln::cl_I icont = cln::the<cln::cl_I>(icont_.to_cl_N());
+ if (icont != 1) {
+ Apr = (A/icont_).expand();
+ return icont;
+ } else {
+ Apr = A;
+ return n1;
+ }
+ }
if (cln::instanceof(icont_.to_cl_N(), cln::cl_RA_ring)) {
Apr = (A/icont_).expand();
// A is a polynomail over rationals, so GCD is defined
// up to arbitrary rational number.
return n1;
}
- GINAC_ASSERT(cln::instanceof(icont_.to_cl_N(), cln::cl_I_ring));
- const cln::cl_I icont = cln::the<cln::cl_I>(icont_.to_cl_N());
- if (icont != 1) {
- Apr = (A/icont_).expand();
- return icont;
- } else {
- Apr = A;
- return n1;
- }
+ GINAC_ASSERT(NULL == "expected polynomial over integers or rationals");
}
ex chinrem_gcd(const ex& A_, const ex& B_, const exvector& vars)
Cp = (Cp*numeric(nlc)).expand().smod(pnum);
exp_vector_t cp_deg = degree_vector(Cp, vars);
if (zerop(cp_deg))
- return numeric(g_lc);
+ return numeric(c);
if (zerop(q)) {
H = Cp;
n = cp_deg;
// public
+std::string symbol::get_name() const
+{
+ if (name.empty()) {
+ std::ostringstream s;
+ s << "symbol" << serial;
+ name = s.str();
+ }
+ return name;
+}
+
+// protected
+
void symbol::do_print(const print_context & c, unsigned level) const
{
- if (!name.empty())
- c.s << name;
- else
- c.s << "symbol" << serial;
+ c.s << get_name();
}
void symbol::do_print_latex(const print_latex & c, unsigned level) const
// non-virtual functions in this class
public:
void set_name(const std::string & n) { name = n; }
- std::string get_name() const { return name; }
+ std::string get_name() const;
virtual unsigned get_domain() const { return domain::complex; }
protected:
void do_print(const print_context & c, unsigned level) const;
protected:
unsigned serial; ///< unique serial number for comparison
- std::string name; ///< printname of this symbol
+ mutable std::string name; ///< printname of this symbol
std::string TeX_name; ///< LaTeX name of this symbol
private:
static unsigned next_serial;
GINAC_DECLARE_UNARCHIVER(realsymbol);
-/** Specialization of symbol to real domain */
+/** Specialization of symbol to real positive domain */
class possymbol : public realsymbol
{
public: