]> www.ginac.de Git - ginac.git/commitdiff
Added new functions (polylogarithm[classical/multiple/harmonic/nielsens] and multiple...
authorJens Vollinga <vollinga@thep.physik.uni-mainz.de>
Thu, 14 Aug 2003 18:47:45 +0000 (18:47 +0000)
committerJens Vollinga <vollinga@thep.physik.uni-mainz.de>
Thu, 14 Aug 2003 18:47:45 +0000 (18:47 +0000)
ginac/Makefile.am
ginac/inifcns.h
ginac/inifcns_nstdsums.cpp [new file with mode: 0644]

index f0896bd4d802447fd52bfc63ca53000829c75eef..df67f1979203b5ea5882bb4c47300b6cd5a830b7 100644 (file)
@@ -3,7 +3,8 @@
 lib_LTLIBRARIES = libginac.la
 libginac_la_SOURCES = add.cpp archive.cpp basic.cpp constant.cpp ex.cpp \
   expair.cpp expairseq.cpp exprseq.cpp fail.cpp function.cpp inifcns.cpp \
-  inifcns_trans.cpp inifcns_zeta.cpp inifcns_gamma.cpp matrix.cpp mul.cpp \
+  inifcns_trans.cpp inifcns_zeta.cpp inifcns_gamma.cpp inifcns_nstdsums.cpp \
+  matrix.cpp mul.cpp \
   normal.cpp numeric.cpp operators.cpp power.cpp registrar.cpp relational.cpp \
   symbol.cpp pseries.cpp utils.cpp ncmul.cpp structure.cpp exprseq_suppl.cpp \
   lst.cpp lst_suppl.cpp input_parser.yy input_lexer.ll input_lexer.h \
index f4c00dd0e7041d9d18c08dfcda96f050a9537cab..258ee2e2ef681f725ea05768a53127778055cd9e 100644 (file)
@@ -142,6 +142,18 @@ DECLARE_FUNCTION_2P(binomial)
 /** Order term function (for truncated power series). */
 DECLARE_FUNCTION_1P(Order)
 
+/** Polylogarithm and multiple polylogarithm. */
+DECLARE_FUNCTION_2P(Li)
+
+/** Nielsen's generalized polylogarithm. */
+DECLARE_FUNCTION_3P(S)
+
+/** Harmonic polylogarithm. */
+DECLARE_FUNCTION_2P(H)
+
+/** Multiple zeta value. */
+DECLARE_FUNCTION_1P(mZeta)
 ex lsolve(const ex &eqns, const ex &symbols, unsigned options = solve_algo::automatic);
 
 /** Check whether a function is the Order (O(n)) function. */
diff --git a/ginac/inifcns_nstdsums.cpp b/ginac/inifcns_nstdsums.cpp
new file mode 100644 (file)
index 0000000..879bb63
--- /dev/null
@@ -0,0 +1,631 @@
+/** @file inifcns_nstdsums.cpp
+ *
+ *  Implementation of some special functions that have a representation as nested sums.
+ *  The functions are: 
+ *    classical polylogarithm              Li(n,x)
+ *    multiple polylogarithm               Li(lst(n_1,...,n_k),lst(x_1,...,x_k)
+ *    nielsen's generalized polylogarithm  S(n,p,x)
+ *    harmonic polylogarithm               H(lst(m_1,...,m_k),x)
+ *    multiple zeta value                  mZeta(lst(m_1,...,m_k))
+ *
+ *  Some remarks:
+ *    - All formulae used can be looked up in the following publication:
+ *      Nielsen's Generalized Polylogarithms, K.S.Kolbig, SIAM J.Math.Anal. 17 (1986), pp. 1232-1258.
+ *      This document will be referenced as [Kol] throughout this source code.
+ *    - Classical polylogarithms (Li) and nielsen's generalized polylogarithms (S) can be numerically
+ *     evaluated in the whole complex plane except for S(n,p,-1) when p is not unit (no formula yet
+ *     to tackle these points). And of course, there is still room for speed optimizations ;-).
+ *    - The remaining functions can only be numerically evaluated with arguments lying in the unit sphere
+ *      at the moment. Sorry. The evaluation especially for mZeta is very slow ... better not use it
+ *      right now.
+ *    - The functions have no series expansion. To do it, you have to convert these functions
+ *      into the appropriate objects from the nestedsums library, do the expansion and convert the
+ *      result back. 
+ *    - Numerical testing of this implementation has been performed by doing a comparison of results
+ *      between this software and the commercial M.......... 4.1.
+ *
+ */
+
+/*
+ *  GiNaC Copyright (C) 1999-2003 Johannes Gutenberg University Mainz, Germany
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdexcept>
+#include <vector>
+#include <cln/cln.h>
+
+#include "inifcns.h"
+#include "lst.h"
+#include "numeric.h"
+#include "operators.h"
+#include "relational.h"
+
+
+namespace GiNaC {
+
+       
+//////////////////////
+// helper functions //
+//////////////////////
+
+
+// helper function for classical polylog Li
+static cln::cl_N Li_series(int n, const cln::cl_N& x, const cln::float_format_t& prec)
+{
+       // Note: argument must be in the unit circle
+       cln::cl_N aug, acc;
+       cln::cl_N num = cln::complex(cln::cl_float(1, prec), 0);
+       cln::cl_N den = 0;
+       int i = 1;
+       do {
+               num = num * x;
+               cln::cl_R ii = i;
+               den = cln::expt(ii, n);
+               i++;
+               aug = num / den;
+               acc = acc + aug;
+       } while (acc != acc+aug);
+       return acc;
+}
+
+
+// helper function for classical polylog Li
+static cln::cl_N Li_projection(int n, const cln::cl_N& x, const cln::float_format_t& prec)
+{
+       return Li_series(n, x, prec);
+}
+
+
+// helper function for classical polylog Li
+static numeric Li_num(int n, const numeric& x)
+{
+       if (n == 1) {
+               // just a log
+               return -cln::log(1-x.to_cl_N());
+       }
+       if (x.is_zero()) {
+               return 0;
+       }
+       if (x == 1) {
+               // [Kol] (2.22)
+               return cln::zeta(n);
+       }
+       else if (x == -1) {
+               // [Kol] (2.22)
+               return -(1-cln::expt(cln::cl_I(2),1-n)) * cln::zeta(n);
+       }
+       
+       // what is the desired float format?
+       // first guess: default format
+       cln::float_format_t prec = cln::default_float_format;
+       const cln::cl_N value = x.to_cl_N();
+       // second guess: the argument's format
+       if (!x.real().is_rational())
+               prec = cln::float_format(cln::the<cln::cl_F>(cln::realpart(value)));
+       else if (!x.imag().is_rational())
+               prec = cln::float_format(cln::the<cln::cl_F>(cln::imagpart(value)));
+       
+       // [Kol] (5.15)
+       if (cln::abs(value) > 1) {
+               cln::cl_N result = -cln::expt(cln::log(-value),n) / cln::factorial(n);
+               // check if argument is complex. if it is real, the new polylog has to be conjugated.
+               if (cln::zerop(cln::imagpart(value))) {
+                       if (n & 1) {
+                               result = result + conjugate(Li_projection(n, cln::recip(value), prec));
+                       }
+                       else {
+                               result = result - conjugate(Li_projection(n, cln::recip(value), prec));
+                       }
+               }
+               else {
+                       if (n & 1) {
+                               result = result + Li_projection(n, cln::recip(value), prec);
+                       }
+                       else {
+                               result = result - Li_projection(n, cln::recip(value), prec);
+                       }
+               }
+               cln::cl_N add;
+               for (int j=0; j<n-1; j++) {
+                       add = add + (1+cln::expt(cln::cl_I(-1),n-j)) * (1-cln::expt(cln::cl_I(2),1-n+j))
+                                       * Li_num(n-j,1).to_cl_N() * cln::expt(cln::log(-value),j) / cln::factorial(j);
+               }
+               result = result - add;
+               return result;
+       }
+       else {
+               return Li_projection(n, value, prec);
+       }
+}
+
+
+// helper function for S(n,p,x)
+static cln::cl_N numeric_nielsen(int n, int step)
+{
+       if (step) {
+               cln::cl_N res;
+               for (int i=1; i<n; i++) {
+                       res = res + numeric_nielsen(i, step-1) / cln::cl_I(i);
+               }
+               return res;
+       }
+       else {
+               return 1;
+       }
+}
+
+
+// forward declaration needed by function C below
+static numeric S_num(int n, int p, const numeric& x);
+
+       
+// helper function for S(n,p,x)
+// [Kol] (7.2)
+static cln::cl_N C(int n, int p)
+{
+       cln::cl_N result;
+
+       for (int k=0; k<p; k++) {
+               for (int j=0; j<=(n+k-1)/2; j++) {
+                       if (k == 0) {
+                               if (n & 1) {
+                                       if (j & 1) {
+                                               result = result - 2 * cln::expt(cln::pi(),2*j) * S_num(n-2*j,p,1).to_cl_N() / cln::factorial(2*j);
+                                       }
+                                       else {
+                                               result = result + 2 * cln::expt(cln::pi(),2*j) * S_num(n-2*j,p,1).to_cl_N() / cln::factorial(2*j);
+                                       }
+                               }
+                       }
+                       else {
+                               if (k & 1) {
+                                       if (j & 1) {
+                                               result = result + cln::factorial(n+k-1)
+                                                       * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+                                                       / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+                                       }
+                                       else {
+                                               result = result - cln::factorial(n+k-1)
+                                                       * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+                                                       / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+                                       }
+                               }
+                               else {
+                                       if (j & 1) {
+                                               result = result - cln::factorial(n+k-1) * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+                                                       / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+                                       }
+                                       else {
+                                               result = result + cln::factorial(n+k-1)
+                                                       * cln::expt(cln::pi(),2*j) * S_num(n+k-2*j,p-k,1).to_cl_N()
+                                                       / (cln::factorial(k) * cln::factorial(n-1) * cln::factorial(2*j));
+                                       }
+                               }
+                       }
+               }
+       }
+       int np = n+p;
+       if ((np-1) & 1) {
+               if (((np)/2+n) & 1) {
+                       result = -result - cln::expt(cln::pi(),np) / (np * cln::factorial(n-1) * cln::factorial(p));
+               }
+               else {
+                       result = -result + cln::expt(cln::pi(),np) / (np * cln::factorial(n-1) * cln::factorial(p));
+               }
+       }
+
+       return result;
+}
+
+
+// helper function for S(n,p,x)
+// [Kol] remark to (9.1)
+static cln::cl_N a_k(int k)
+{
+       cln::cl_N result;
+
+       if (k == 0) {
+               return 1;
+       }
+
+       result = result;
+       for (int m=2; m<=k; m++) {
+               result = result + cln::expt(cln::cl_N(-1),m) * cln::zeta(m) * a_k(k-m);
+       }
+
+       return -result / k;
+}
+
+
+// helper function for S(n,p,x)
+// [Kol] remark to (9.1)
+static cln::cl_N b_k(int k)
+{
+       cln::cl_N result;
+
+       if (k == 0) {
+               return 1;
+       }
+
+       result = result;
+       for (int m=2; m<=k; m++) {
+               result = result + cln::expt(cln::cl_N(-1),m) * cln::zeta(m) * b_k(k-m);
+       }
+
+       return result / k;
+}
+
+
+// helper function for S(n,p,x)
+static cln::cl_N S_series(int n, int p, const cln::cl_N& x, const cln::float_format_t& prec)
+{
+       n++;
+       int i = p;
+       p--;
+       cln::cl_N aug, acc;
+       cln::cl_N num = cln::expt(x,p);
+       cln::cl_N converter = cln::complex(cln::cl_float(1, prec), 0);
+       cln::cl_N den = 0;
+       do {
+               num = num * x;
+               den = cln::expt(cln::cl_I(i), n);
+               aug = num / den * numeric_nielsen(i, p);
+               i++;
+               acc = acc + aug;
+       } while (acc != acc+aug);
+
+       return acc;
+}
+
+
+// helper function for S(n,p,x)
+static cln::cl_N S_projection(int n, int p, const cln::cl_N& x, const cln::float_format_t& prec)
+{
+       // [Kol] (5.3)
+       if (cln::abs(cln::realpart(x)) > cln::cl_F("0.5")) {
+
+               cln::cl_N result = cln::expt(cln::cl_I(-1),p) * cln::expt(cln::log(x),n)
+                       * cln::expt(cln::log(1-x),p) / cln::factorial(n) / cln::factorial(p);
+
+               for (int s=0; s<n; s++) {
+                       cln::cl_N res2;
+                       for (int r=0; r<p; r++) {
+                               res2 = res2 + cln::expt(cln::cl_I(-1),r) * cln::expt(cln::log(1-x),r)
+                                       * S_series(p-r,n-s,1-x,prec) / cln::factorial(r);
+                       }
+                       result = result + cln::expt(cln::log(x),s) * (S_num(n-s,p,1).to_cl_N() - res2) / cln::factorial(s);
+               }
+
+               return result;
+       }
+       
+       return S_series(n, p, x, prec);
+}
+
+
+// helper function for S(n,p,x)
+static numeric S_num(int n, int p, const numeric& x)
+{
+       if (x == 1) {
+               if (n == 1) {
+                   // [Kol] (2.22) with (2.21)
+                       return cln::zeta(p+1);
+               }
+
+               if (p == 1) {
+                   // [Kol] (2.22)
+                       return cln::zeta(n+1);
+               }
+
+               // [Kol] (9.1)
+               cln::cl_N result;
+               for (int nu=0; nu<n; nu++) {
+                       for (int rho=0; rho<=p; rho++) {
+                               result = result + b_k(n-nu-1) * b_k(p-rho) * a_k(nu+rho+1)
+                                       * cln::factorial(nu+rho+1) / cln::factorial(rho) / cln::factorial(nu+1);
+                       }
+               }
+               result = result * cln::expt(cln::cl_I(-1),n+p-1);
+
+               return result;
+       }
+       else if (x == -1) {
+               // [Kol] (2.22)
+               if (p == 1) {
+                       return -(1-cln::expt(cln::cl_I(2),-n)) * cln::zeta(n+1);
+               }
+               throw std::runtime_error("don't know how to evaluate this function!");
+       }
+
+       // what is the desired float format?
+       // first guess: default format
+       cln::float_format_t prec = cln::default_float_format;
+       const cln::cl_N value = x.to_cl_N();
+       // second guess: the argument's format
+       if (!x.real().is_rational())
+               prec = cln::float_format(cln::the<cln::cl_F>(cln::realpart(value)));
+       else if (!x.imag().is_rational())
+               prec = cln::float_format(cln::the<cln::cl_F>(cln::imagpart(value)));
+
+
+       // [Kol] (5.3)
+       if (cln::realpart(value) < -0.5) {
+
+               cln::cl_N result = cln::expt(cln::cl_I(-1),p) * cln::expt(cln::log(value),n)
+                       * cln::expt(cln::log(1-value),p) / cln::factorial(n) / cln::factorial(p);
+
+               for (int s=0; s<n; s++) {
+                       cln::cl_N res2;
+                       for (int r=0; r<p; r++) {
+                               res2 = res2 + cln::expt(cln::cl_I(-1),r) * cln::expt(cln::log(1-value),r)
+                                       * S_num(p-r,n-s,1-value).to_cl_N() / cln::factorial(r);
+                       }
+                       result = result + cln::expt(cln::log(value),s) * (S_num(n-s,p,1).to_cl_N() - res2) / cln::factorial(s);
+               }
+
+               return result;
+               
+       }
+       // [Kol] (5.12)
+       else if (cln::abs(value) > 1) {
+               
+               cln::cl_N result;
+
+               for (int s=0; s<p; s++) {
+                       for (int r=0; r<=s; r++) {
+                               result = result + cln::expt(cln::cl_I(-1),s) * cln::expt(cln::log(-value),r) * cln::factorial(n+s-r-1)
+                                       / cln::factorial(r) / cln::factorial(s-r) / cln::factorial(n-1)
+                                       * S_num(n+s-r,p-s,cln::recip(value)).to_cl_N();
+                       }
+               }
+               result = result * cln::expt(cln::cl_I(-1),n);
+
+               cln::cl_N res2;
+               for (int r=0; r<n; r++) {
+                       res2 = res2 + cln::expt(cln::log(-value),r) * C(n-r,p) / cln::factorial(r);
+               }
+               res2 = res2 + cln::expt(cln::log(-value),n+p) / cln::factorial(n+p);
+
+               result = result + cln::expt(cln::cl_I(-1),p) * res2;
+
+               return result;
+       }
+       else {
+               return S_projection(n, p, value, prec);
+       }
+}
+
+
+// helper function for multiple polylogarithm
+static cln::cl_N numeric_zsum(int n, std::vector<cln::cl_N>& x, std::vector<cln::cl_N>& m)
+{
+       cln::cl_N res;
+       if (x.empty()) {
+               return 1;
+       }
+       for (int i=1; i<n; i++) {
+               std::vector<cln::cl_N>::iterator be;
+               std::vector<cln::cl_N>::iterator en;
+               be = x.begin();
+               be++;
+               en = x.end();
+               std::vector<cln::cl_N> xbuf(be, en);
+               be = m.begin();
+               be++;
+               en = m.end();
+               std::vector<cln::cl_N> mbuf(be, en);
+               res = res + cln::expt(x[0],i) / cln::expt(i,m[0]) * numeric_zsum(i, xbuf, mbuf);
+       }
+       return res;
+}
+
+
+// helper function for harmonic polylogarithm
+static cln::cl_N numeric_harmonic(int n, std::vector<cln::cl_N>& m)
+{
+       cln::cl_N res;
+       if (m.empty()) {
+               return 1;
+       }
+       for (int i=1; i<n; i++) {
+               std::vector<cln::cl_N>::iterator be;
+               std::vector<cln::cl_N>::iterator en;
+               be = m.begin();
+               be++;
+               en = m.end();
+               std::vector<cln::cl_N> mbuf(be, en);
+               res = res + cln::recip(cln::expt(i,m[0])) * numeric_harmonic(i, mbuf);
+       }
+       return res;
+}
+
+
+/////////////////////////////
+// end of helper functions //
+/////////////////////////////
+
+
+// Polylogarithm and multiple polylogarithm
+
+static ex Li_eval(const ex& x1, const ex& x2)
+{
+       if (x2.is_zero()) {
+               return 0;
+       }
+       else {
+               return Li(x1,x2).hold();
+       }
+}
+
+static ex Li_evalf(const ex& x1, const ex& x2)
+{
+       // classical polylogs
+       if (is_a<numeric>(x1) && is_a<numeric>(x2)) {
+               return Li_num(ex_to<numeric>(x1).to_int(), ex_to<numeric>(x2));
+       }
+       // multiple polylogs
+       else if (is_a<lst>(x1) && is_a<lst>(x2)) {
+               for (int i=0; i<x1.nops(); i++) {
+                       if (!is_a<numeric>(x1.op(i)))
+                               return Li(x1,x2).hold();
+                       if (!is_a<numeric>(x2.op(i)))
+                               return Li(x1,x2).hold();
+                       if (x2 >= 1)
+                               return Li(x1,x2).hold();
+               }
+
+               cln::cl_N m_1 = ex_to<numeric>(x1.op(x1.nops()-1)).to_cl_N();
+               cln::cl_N x_1 = ex_to<numeric>(x2.op(x2.nops()-1)).to_cl_N();
+               std::vector<cln::cl_N> x;
+               std::vector<cln::cl_N> m;
+               const int nops = ex_to<numeric>(x1.nops()).to_int();
+               for (int i=nops-2; i>=0; i--) {
+                       m.push_back(ex_to<numeric>(x1.op(i)).to_cl_N());
+                       x.push_back(ex_to<numeric>(x2.op(i)).to_cl_N());
+               }
+
+               cln::cl_N res;
+               cln::cl_N resbuf;
+               for (int i=nops; true; i++) {
+                       resbuf = res;
+                       res = res + cln::expt(x_1,i) / cln::expt(i,m_1) * numeric_zsum(i, x, m);
+                       if (cln::zerop(res-resbuf))
+                               break;
+               }
+
+               return numeric(res);
+
+       }
+
+       return Li(x1,x2).hold();
+}
+
+REGISTER_FUNCTION(Li, eval_func(Li_eval).evalf_func(Li_evalf).do_not_evalf_params());
+
+
+// Nielsen's generalized polylogarithm
+
+static ex S_eval(const ex& x1, const ex& x2, const ex& x3)
+{
+       if (x2 == 1) {
+               return Li(x1+1,x3);
+       }
+       return S(x1,x2,x3).hold();
+}
+
+static ex S_evalf(const ex& x1, const ex& x2, const ex& x3)
+{
+       if (is_a<numeric>(x1) && is_a<numeric>(x2) && is_a<numeric>(x3)) {
+               if ((x3 == -1) && (x2 != 1)) {
+                       // no formula to evaluate this ... sorry
+                       return S(x1,x2,x3).hold();
+               }
+               return S_num(ex_to<numeric>(x1).to_int(), ex_to<numeric>(x2).to_int(), ex_to<numeric>(x3));
+       }
+       return S(x1,x2,x3).hold();
+}
+
+REGISTER_FUNCTION(S, eval_func(S_eval).evalf_func(S_evalf).do_not_evalf_params());
+
+
+// Harmonic polylogarithm
+
+static ex H_eval(const ex& x1, const ex& x2)
+{
+       return H(x1,x2).hold();
+}
+
+static ex H_evalf(const ex& x1, const ex& x2)
+{
+       if (is_a<lst>(x1) && is_a<numeric>(x2)) {
+               for (int i=0; i<x1.nops(); i++) {
+                       if (!is_a<numeric>(x1.op(i)))
+                               return H(x1,x2).hold();
+               }
+
+               cln::cl_N m_1 = ex_to<numeric>(x1.op(x1.nops()-1)).to_cl_N();
+               cln::cl_N x_1 = ex_to<numeric>(x2).to_cl_N();
+               std::vector<cln::cl_N> m;
+               const int nops = ex_to<numeric>(x1.nops()).to_int();
+               for (int i=nops-2; i>=0; i--) {
+                       m.push_back(ex_to<numeric>(x1.op(i)).to_cl_N());
+               }
+
+               cln::cl_N res;
+               cln::cl_N resbuf;
+               for (int i=nops; true; i++) {
+                       resbuf = res;
+                       res = res + cln::expt(x_1,i) / cln::expt(i,m_1) * numeric_harmonic(i, m);
+                       if (cln::zerop(res-resbuf))
+                               break;
+               }
+
+               return numeric(res);
+
+       }
+
+       return H(x1,x2).hold();
+}
+
+REGISTER_FUNCTION(H, eval_func(H_eval).evalf_func(H_evalf).do_not_evalf_params());
+
+
+// Multiple zeta value
+
+static ex mZeta_eval(const ex& x1)
+{
+       return mZeta(x1).hold();
+}
+
+static ex mZeta_evalf(const ex& x1)
+{
+       if (is_a<lst>(x1)) {
+               for (int i=0; i<x1.nops(); i++) {
+                       if (!is_a<numeric>(x1.op(i)))
+                               return mZeta(x1).hold();
+               }
+
+               cln::cl_N m_1 = ex_to<numeric>(x1.op(x1.nops()-1)).to_cl_N();
+               std::vector<cln::cl_N> m;
+               const int nops = ex_to<numeric>(x1.nops()).to_int();
+               for (int i=nops-2; i>=0; i--) {
+                       m.push_back(ex_to<numeric>(x1.op(i)).to_cl_N());
+               }
+
+               cln::float_format_t prec = cln::default_float_format;
+               cln::cl_N res = cln::complex(cln::cl_float(0, prec), 0);
+               cln::cl_N resbuf;
+               for (int i=nops; true; i++) {
+                       // to infinity and beyond ... timewise
+                       resbuf = res;
+                       res = res + cln::recip(cln::expt(i,m_1)) * numeric_harmonic(i, m);
+                       if (cln::zerop(res-resbuf))
+                               break;
+               }
+
+               return numeric(res);
+
+       }
+
+       return mZeta(x1).hold();
+}
+
+REGISTER_FUNCTION(mZeta, eval_func(mZeta_eval).evalf_func(mZeta_evalf).do_not_evalf_params());
+
+
+} // namespace GiNaC
+