--- /dev/null
+/**
+ * @file numeric_archive.cpp Check for a bug in numeric::archive
+ *
+ * numeric::archive used to fail if the real part of a complex number
+ * is a rational number and the imaginary part is a floating point one.
+ */
+#include <cln/cln.h>
+#include <iostream>
+#include <stdexcept>
+#include <vector>
+#include <iterator>
+#include <algorithm>
+#include <sstream>
+#include "ginac.h"
+using namespace cln;
+using namespace GiNaC;
+
+struct archive_unarchive_check
+{
+ cl_N operator()(const cl_N& n) const
+ {
+ ex e = numeric(n);
+ archive ar;
+ ar.archive_ex(e, "test");
+ lst l;
+ ex check = ar.unarchive_ex(l, "test");
+ if (!check.is_equal(e)) {
+ std::ostringstream s;
+ s << __FILE__ << ':' << __LINE__ << ": expected: " << e << ", got " << check;
+ throw std::logic_error(s.str());
+ }
+ return n;
+ }
+};
+
+int main(int argc, char** argv)
+{
+ const cl_I one(1);
+ std::cout << "checking if numeric::archive handles complex numbers properly" << std::endl;
+ const cl_R three_fp = cl_float(3.0, default_float_format);
+ std::vector<cl_N> numbers;
+ numbers.push_back(complex(one, three_fp));
+ numbers.push_back(complex(three_fp, one));
+ numbers.push_back(complex(three_fp, three_fp));
+ numbers.push_back(complex(one, one));
+ std::for_each(numbers.begin(), numbers.end(), archive_unarchive_check());
+ return 0;
+}
// archiving
//////////
-numeric::numeric(const archive_node &n, lst &sym_lst) : inherited(n, sym_lst)
+/**
+ * Construct a floating point number from sign, mantissa, and exponent
+ */
+static const cln::cl_F make_real_float(const cln::cl_idecoded_float& dec)
{
- cln::cl_N ctorval = 0;
+ cln::cl_F x = cln::cl_float(dec.mantissa, cln::default_float_format);
+ x = cln::scale_float(x, dec.exponent);
+ cln::cl_F sign = cln::cl_float(dec.sign, cln::default_float_format);
+ x = cln::float_sign(sign, x);
+ return x;
+}
+
+/**
+ * Read serialized floating point number
+ */
+static const cln::cl_F read_real_float(std::istream& s)
+{
+ cln::cl_idecoded_float dec;
+ s >> dec.sign >> dec.mantissa >> dec.exponent;
+ const cln::cl_F x = make_real_float(dec);
+ return x;
+}
+numeric::numeric(const archive_node &n, lst &sym_lst) : inherited(n, sym_lst)
+{
+ value = 0;
+
// Read number as string
std::string str;
if (n.find_string("number", str)) {
std::istringstream s(str);
- cln::cl_idecoded_float re, im;
+ cln::cl_R re, im;
char c;
s.get(c);
switch (c) {
- case 'R': // Integer-decoded real number
- s >> re.sign >> re.mantissa >> re.exponent;
- ctorval = re.sign * re.mantissa * cln::expt(cln::cl_float(2.0, cln::default_float_format), re.exponent);
+ case 'R':
+ // real FP (floating point) number
+ re = read_real_float(s);
+ value = re;
+ break;
+ case 'C':
+ // both real and imaginary part are FP numbers
+ re = read_real_float(s);
+ im = read_real_float(s);
+ value = cln::complex(re, im);
+ break;
+ case 'H':
+ // real part is a rational number,
+ // imaginary part is a FP number
+ s >> re;
+ im = read_real_float(s);
+ value = cln::complex(re, im);
break;
- case 'C': // Integer-decoded complex number
- s >> re.sign >> re.mantissa >> re.exponent;
- s >> im.sign >> im.mantissa >> im.exponent;
- ctorval = cln::complex(re.sign * re.mantissa * cln::expt(cln::cl_float(2.0, cln::default_float_format), re.exponent),
- im.sign * im.mantissa * cln::expt(cln::cl_float(2.0, cln::default_float_format), im.exponent));
+ case 'J':
+ // real part is a FP number,
+ // imaginary part is a rational number
+ re = read_real_float(s);
+ s >> im;
+ value = cln::complex(re, im);
break;
- default: // Ordinary number
+ default:
+ // both real and imaginary parts are rational
s.putback(c);
- s >> ctorval;
+ s >> value;
break;
}
}
- value = ctorval;
setflag(status_flags::evaluated | status_flags::expanded);
}
+static void write_real_float(std::ostream& s, const cln::cl_R& n)
+{
+ const cln::cl_idecoded_float dec = cln::integer_decode_float(cln::the<cln::cl_F>(n));
+ s << dec.sign << ' ' << dec.mantissa << ' ' << dec.exponent;
+}
+
void numeric::archive(archive_node &n) const
{
inherited::archive(n);
// Write number as string
+
+ const cln::cl_R re = cln::realpart(value);
+ const cln::cl_R im = cln::imagpart(value);
+ const bool re_rationalp = cln::instanceof(re, cln::cl_RA_ring);
+ const bool im_rationalp = cln::instanceof(im, cln::cl_RA_ring);
+
+ // Non-rational numbers are written in an integer-decoded format
+ // to preserve the precision
std::ostringstream s;
- if (this->is_crational())
+ if (re_rationalp && im_rationalp)
s << value;
- else {
- // Non-rational numbers are written in an integer-decoded format
- // to preserve the precision
- if (this->is_real()) {
- cln::cl_idecoded_float re = cln::integer_decode_float(cln::the<cln::cl_F>(value));
- s << "R";
- s << re.sign << " " << re.mantissa << " " << re.exponent;
- } else {
- cln::cl_idecoded_float re = cln::integer_decode_float(cln::the<cln::cl_F>(cln::realpart(cln::the<cln::cl_N>(value))));
- cln::cl_idecoded_float im = cln::integer_decode_float(cln::the<cln::cl_F>(cln::imagpart(cln::the<cln::cl_N>(value))));
- s << "C";
- s << re.sign << " " << re.mantissa << " " << re.exponent << " ";
- s << im.sign << " " << im.mantissa << " " << im.exponent;
- }
+ else if (zerop(im)) {
+ // real FP (floating point) number
+ s << 'R';
+ write_real_float(s, re);
+ } else if (re_rationalp) {
+ s << 'H'; // just any unique character
+ // real part is a rational number,
+ // imaginary part is a FP number
+ s << re << ' ';
+ write_real_float(s, im);
+ } else if (im_rationalp) {
+ s << 'J';
+ // real part is a FP number,
+ // imaginary part is a rational number
+ write_real_float(s, re);
+ s << ' ' << im;
+ } else {
+ // both real and imaginary parts are floating point
+ s << 'C';
+ write_real_float(s, re);
+ s << ' ';
+ write_real_float(s, im);
}
n.add_string("number", s.str());
}