├── .gitignore ├── Changelog ├── LICENSE ├── Makefile ├── README.md ├── src ├── Makefile ├── README ├── db-cache.c ├── db-cache.h ├── db-merge.c ├── db-prt.c ├── mp-arith.c ├── mp-arith.h ├── mp-binomial.c ├── mp-binomial.h ├── mp-cache.c ├── mp-cache.h ├── mp-complex.h ├── mp-consts.c ├── mp-consts.h ├── mp-euler.c ├── mp-euler.h ├── mp-gamma.c ├── mp-gamma.h ├── mp-genfunc.c ├── mp-genfunc.h ├── mp-gkw.c ├── mp-gkw.h ├── mp-hyper.c ├── mp-hyper.h ├── mp-misc.c ├── mp-misc.h ├── mp-multiplicative.c ├── mp-multiplicative.h ├── mp-polylog.c ├── mp-polylog.h ├── mp-quest.c ├── mp-quest.h ├── mp-topsin.c ├── mp-topsin.h ├── mp-trig.c ├── mp-trig.h ├── mp-zerofind.c ├── mp-zerofind.h ├── mp-zeroiso.c ├── mp-zeroiso.h ├── mp-zeta.c └── mp-zeta.h └── tests ├── Makefile ├── polylog-bug.c ├── unit-test.c └── zero-iso.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.tar.gz 3 | 4 | # build directory stuff. 5 | src/db-merge 6 | src/db-prt 7 | src/db-zeta.db 8 | src/libanant.a 9 | src/mp-quest 10 | src/mp-topsin 11 | -------------------------------------------------------------------------------- /Changelog: -------------------------------------------------------------------------------- 1 | 2 | Changelog 3 | --------- 4 | 5 | Version 0.3.0 (2 February 2024) 6 | * Implement Sagraloff-Yap complex root isolation algorithm. 7 | 8 | Version 0.2.5 (23 October 2023) 9 | * Fix serious arithmetic bug added during hurried optimizations. 10 | * Fix dumb deadlock added during hurried thread-safety work. 11 | * Improve documentation for polylog algorithm internals. 12 | * Rework polylog branch-cut locations to point to left & right. 13 | 14 | Version 0.2.4 (8 March 2019) 15 | * Add generator of completely multiplicative functions. 16 | * Add Euler resummation. 17 | * Add Newton series summation. 18 | 19 | Version 0.2.3 (30 June 2017) 20 | * Add ordinary, exponential generating functions. 21 | * Add set-partition function. 22 | 23 | Version 0.2.2 (5 November 2016) 24 | * Add a 'make install' target. 25 | * Add topologist's sine function series. 26 | * Add generalized harmonic number. 27 | * Provide thread safety for many (not all) of the caching functions. 28 | 29 | Version 0.2.1 (6 April 2012): 30 | * Trite performance improvements of complex utilities. 31 | * Bug fix in caching: failed initialization. 32 | * Unit test fixes & improvements. 33 | 34 | Version 0.2.0 (12 December 2010): 35 | * Add the complex zero-finding (Powell's algorithm) routine. 36 | * Fix some minor(?) memory leaks in the gamma function. 37 | * Add domain check to fp_log; return error if log(0) requested. 38 | * Add routines to get real, imaginary parts as float pts. 39 | * Routine to multiply by complex by float pt. 40 | * Fix divide-by-zero when question mark is passed a zero. 41 | * Fix a bug due to rounding-overflow in the question-mark function. 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | cd src; make 4 | 5 | install: 6 | cd src; make install 7 | 8 | VERSION=0.2.5 9 | VERDIR=anant-${VERSION} 10 | 11 | dist: 12 | rm -rf ${VERDIR} 13 | mkdir ${VERDIR} 14 | mkdir ${VERDIR}/src 15 | mkdir ${VERDIR}/tests 16 | cp LICENSE ${VERDIR} 17 | cp Makefile ${VERDIR} 18 | cp README ${VERDIR} 19 | cp src/*.c ${VERDIR}/src 20 | cp src/*.h ${VERDIR}/src 21 | cp src/Makefile ${VERDIR}/src 22 | cp tests/*.c ${VERDIR}/tests 23 | cp tests/Makefile ${VERDIR}/tests 24 | tar -zcvf ${VERDIR}.tar.gz ${VERDIR} 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Anant -- Algorithmic 'n Analytic Number Theory 3 | =============================================== 4 | 5 |

6 | Version 0.3.0
7 | Linas Vepstas February 2024
8 | linasvepstas@gmail.com 9 |

10 | 11 | 12 | This project contains ad-hoc implementations of assorted analytic 13 | functions of interest in number theory, including the gamma function, 14 | the Riemann zeta function, the polylogarithm, and the Minkowski 15 | question mark function. The implementation uses the Gnu Multi-Precision 16 | library (GMP) to perform all low-level operations. The code herein 17 | is licensed under the terms of the Gnu GPLv3 license. 18 | 19 | This project is *NOT* meant to be a replacement for other, more 20 | established multi-precision systems, such as MPFR or PARI/GP. It is 21 | meant to be a staging area for implementations of functions that have 22 | not (yet!) received much attention in the more established packages. 23 | Users are strongly encouraged to port the contents of this package 24 | to other systems. Please! 25 | Seriously: the stuff here is still cutting-edge, fifteen years on. 26 | I really would like to see the "serious" math packages actually 27 | implement these functions! 28 | 29 | A note about floating-point exceptions: many of the special functions 30 | computed here have poles at various values. These will show up as 31 | mysterious floating-point exceptions deep within the code. If you 32 | get an exception, make sure you are not evaluating a function at a pole! 33 | 34 | This package has its origins as a collection of tools & utilities for 35 | the benefit of the author. As such, it was never really intended for 36 | public consumption, and thus, will not have the usual amenities of 37 | established projects, such as clear documentation, a website, unit 38 | test cases, or even a robust build system. Caveat Emptor! It works 39 | great for me, and I use it all the time; but I am me, and not you. 40 | 41 | There are several publications that describe this code, or make use of 42 | it. The most notable is this: 43 | 44 | Vepstas, L. (2008) ["An efficient algorithm for accelerating 45 | the convergence of oscillatory series, useful for computing the 46 | polylogarithm and Hurwitz zeta functions"] 47 | (http://link.springer.com/article/10.1007/s11075-007-9153-8). 48 | *Numerical Algorithms* vol. **47** issue 3: pp. 211-252. 49 | [arXiv:math.CA/0702243](https://arxiv.org/abs/math/0702243). 50 | doi:10.1007/s11075-007-9153-8. 51 | 52 | The most fun thing created with this code is this: 53 | [Linas' Art Gallery](https://linas.org/art-gallery). 54 | 55 | The most verbose thing created with this code is this: 56 | [The Modular Group and Fractals](https://linas.org/math/sl2z.html) 57 | An exploration of the relationship between Fractals, the Riemann Zeta, 58 | the Modular Group Gamma, the Farey Fractions and the Minkowski Question 59 | Mark. 60 | 61 | Supported functions 62 | ------------------- 63 | Many of the functions below are not "difficult"; what makes the code 64 | here unique is that many of these use caching and partial computation 65 | to avoid repeated computations. In some cases, this caching allows the 66 | functions to be particularly fast when called in "sequential" order, 67 | as would naturally occur in summations. This can provide speedups of 68 | over a thousand-fold, when used correctly! 69 | 70 | Another, apparently "unique" feature, as compared to established 71 | libraries, is that most of the functions are handled on the complex 72 | plane. Thus, the system comes with basic complex-number support. 73 | 74 | Some, but not all of the code is protected by spinlocks, and so can be 75 | used in parallel, i.e. in a multi-threaded setting. This can also give 76 | a big boost in performance. This code is used regularly on a 24-core 77 | CPU. It works. 78 | 79 | Arbitrary precision constants 80 | ----------------------------- 81 | * sqrt(3)/2, log(2) 82 | * e, e^pi 83 | * pi, 2pi, pi/2, sqrt(2pi), log(2pi), 2/pi 84 | * Euler-Mascheroni const 85 | * Riemann zeta(1/2) 86 | 87 | Combinatorial functions: 88 | ------------------------ 89 | * Rising pochhammer symbol (integer) 90 | * Partition function (integer) 91 | * Reciprocal factorial 92 | * Sequential binomial coefficient 93 | * Stirling Numbers of the First Kind 94 | * Stirling Numbers of the Second Kind 95 | * Bernoulli Numbers 96 | * Binomial transform of power sum 97 | * Rising pochhammer symbol (real) i.e. (s)_n for real s 98 | * Rising pochhammer symbol (complex) i.e. (s)_n for complex s 99 | * Binomial coefficient (complex) i.e. (s choose n) for complex s 100 | * High-speed, cached sequential-access binomial coefficient. 101 | 102 | Elementary functions: 103 | --------------------- 104 | * pow, exp, log, sine, cosine, tangent for real, complex arguments 105 | * arctan, arctan2 for real argument 106 | * log(1-x) for real, complex x 107 | * sqrt for complex argument 108 | 109 | Classical functions: 110 | -------------------- 111 | * gamma (factorial) for real, complex argument 112 | * polylogarithm, using multiple algorithms: Borwein-style, Euler-Maclaurin 113 | * polylogarithm on multiple sheets (monodromy) 114 | * Periodic zeta function 115 | * Hurwitz zeta function, using multiple algorithms; complex arguments. 116 | * Riemann zeta function, using multiple algorithms: Borwein, Hasse, brute-force 117 | for integer, real and complex arguments. 118 | * Confluent hypergeometric function, complex arguments 119 | 120 | Number-theoretic functions: 121 | --------------------------- 122 | * General complex-valued harmonic number 123 | * Gauss-Kuzmin-Wirsing operator matrix elements 124 | * Minkowski Question Mark function (Stern-Brocot tree), and its inverse 125 | * Taylor's series coefficients for the topologist's sin -- sin(2pi/(1+x)) 126 | 127 | Utilities: 128 | ---------- 129 | * Fill in values of completely multiplicative arithmetic function. 130 | * Ordinary and exponential generating functions for arithmetic functions. 131 | * Euler re-summation of convergent series (speeds convergence). 132 | * Newton series interpolation of arithmetic series. 133 | * Powell's method for zero-finding on complex plane (noise-cancelling variant). 134 | * Root isolation on complex plane using Sagraloff-Yap (2011) algorithm. 135 | 136 | 137 | Pre-requisites, Compiling, Installing, Testing 138 | ---------------------------------------------- 139 | This package requires a copy of the Berkeley DB database to be 140 | available. The database is used to cache certain intermediate values, to 141 | improve performance of various internal algorithms. 142 | 143 | This package has minimal build support. `cd` to the src directory, and 144 | `make`. If you want to install the files somewhere, you will have to do 145 | this by hand, or custom-tailor to suit your needs. 146 | 147 | There is a unit test, rather ad-hoc in nature, and it is not 148 | "user-friendly". It will report some errors in the last few decimal 149 | places of various routines, depending on how it was invoked. It is up 150 | to you to figure out if these are serious errors or not. Caveat Emptor! 151 | 152 | (I mean, its 'error-free' for me, i.e. 'good enough'. There is nothing 153 | in here that is horribly broken, as far as I know. If quibbling over 154 | the last few decimal places is important to you, you might have a 155 | different opinion. If you're reasonably careful, and actually think 156 | about what you are actually doing, things will go well.) 157 | 158 | Patches to improve the build system (and anything else that annoys you) 159 | are gladly accepted. 160 | 161 | Precision 162 | --------- 163 | Most of the algorithms deal with precision issues in a fairly ad-hoc 164 | kind of way. Many/most routines require an argument specifying the 165 | number of decimal places of desired precision, and will typically 166 | return answers that are accurate from about 90% to 100% of the specified 167 | precision. However, many of the algorithms use internal, intermediate 168 | results that need to be maintained at a higher level of precision than 169 | the "desired" precision. Thus, correct usage requires that the user 170 | specify an mpf_set_default_prec() that is 10% to 50% larger than the 171 | desired precision of the results. The proper amount to use is up to 172 | you to figure out! A reasonable rule-of-thumb seems to be to use 173 | mpf_set_default_prec(5*desired_decimal_places) -- noting that log_2(10) 174 | is 2.3. 175 | 176 | Again -- this may strike you as hacky; its good enough for what I need. 177 | Patches to improve the state of things are accepted. 178 | 179 | Example Usage 180 | ------------- 181 | The below provides an example of how to use the functions in this 182 | library. 183 | 184 | // Standard include headers 185 | #include 186 | #include 187 | #include "mp-polylog.h" 188 | #include "mp-misc.h" 189 | 190 | int main() 191 | { 192 | cpx_t plog, ess, zee; // Complex variant of mpf_t 193 | int nbits; 194 | int decimal_prec; 195 | 196 | // decimal_prec is the number of decimal places of desired 197 | // precision. 198 | // 199 | // 3.3 is equal to log_2(10), and is used to convert decimal 200 | // places to number of binary bits. 201 | // 202 | // The +600 adds some extra "padding precision" for 203 | // intermediate calculations. Most algorithms require some 204 | // fair amount of additional bits of precision to be used in 205 | // computing intermediate results. The precise amount needed 206 | // is somewhat ad-hoc, and not well-characterized for the 207 | // different functions; typically, an extra 20% to 50% is 208 | // needed. 209 | // 210 | decimal_prec = 500; 211 | nbits = 3.3*decimal_prec + 600; 212 | mpf_set_default_prec (nbits); 213 | 214 | // Initialization 215 | cpx_init (plog); 216 | cpx_init (ess); 217 | cpx_init (zee); 218 | 219 | // Set values for which computation will be done. 220 | cpx_set_d(ess, 2.1, 0.0); 221 | cpx_set_d(zee, 0.5, 0.0); 222 | 223 | // Compute ... 224 | int rc = cpx_polylog(plog, ess, zee, decimal_prec); 225 | 226 | // Check for error conditions 227 | if (0 != rc) 228 | { 229 | printf("Error occurred during computation! rc=%d\n", rc); 230 | return 1; 231 | } 232 | cpx_prt("Answer is ", plog); 233 | printf("\n"); 234 | 235 | return 0; 236 | } 237 | 238 | 239 | Current repository: 240 | ------------------- 241 | in Git, on Github: 242 | https://github.com/linas/anant 243 | 244 | Older versions (2005 through 2012) can be found in Bazaar, on Launchpad: 245 | https://launchpad.net/anant 246 | 247 | Source tarballs are available there too. 248 | 249 | Related projects: 250 | ----------------- 251 | * [C library for arbitrary-precision interval arithmetic](http://arblib.org/) 252 | from Fredrik Johansson. This library includes Airy and Bessel 253 | functions, the error function, as well as the modular j-function and 254 | others. [Github repo](https://github.com/fredrik-johansson/arb/) 255 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Build the Anant library. 3 | # This is a very low-brow makefile; could be replaced by CMake, 4 | # I guess. 5 | # 6 | # CC = cc -pg 7 | CC = cc 8 | 9 | 10 | EXES= db-merge db-prt 11 | 12 | TESTS= 13 | # TESTS += mp-euler 14 | # TESTS += mp-multiplicative 15 | # TESTS += mp-topsin 16 | 17 | MPLIB=libanant.a 18 | 19 | all: $(MPLIB) $(EXES) $(TESTS) 20 | 21 | MPOBJS= db-cache.o mp-arith.o mp-binomial.o mp-cache.o mp-consts.o \ 22 | mp-euler.o mp-gamma.o mp-genfunc.o mp-gkw.o mp-hyper.o mp-misc.o \ 23 | mp-multiplicative.o mp-polylog.o \ 24 | mp-quest.o mp-topsin.o mp-trig.o mp-zerofind.o mp-zeroiso.o mp-zeta.o 25 | 26 | cache-fill: cache-fill.o $(MPLIB) 27 | db-merge: db-merge.o $(MPLIB) 28 | db-prt: db-prt.o $(MPLIB) 29 | 30 | # library objects 31 | db-cache.o: db-cache.h 32 | mp-arith.o: mp-arith.h mp-cache.h mp-misc.h 33 | mp-binomial.o: mp-binomial.h mp-cache.h mp-complex.h mp-misc.h mp-trig.h 34 | mp-cache.o: mp-cache.h mp-complex.h 35 | mp-consts.o: mp-consts.h mp-binomial.h mp-complex.h mp-trig.h mp-zeta.h 36 | mp-euler.o: mp-euler.h mp-binomial.h mp-complex.h 37 | mp-gamma.o: mp-gamma.h mp-binomial.h mp-complex.h mp-consts.h mp-misc.h mp-trig.h mp-zeta.h 38 | mp-genfunc.o: mp-genfunc.h mp-complex.h mp-consts.h 39 | mp-gkw.o: mp-gkw.h mp-binomial.h mp-complex.h mp-misc.h mp-zeta.h 40 | mp-hyper.o: mp-hyper.h mp-complex.h mp-misc.h 41 | mp-misc.o: mp-misc.h mp-complex.h 42 | mp-multiplicative.o: mp-multiplicative.h mp-complex.h 43 | mp-polylog.o: mp-polylog.h mp-binomial.h mp-cache.h mp-complex.h mp-consts.h mp-gamma.h mp-misc.h mp-trig.h mp-zeta.h 44 | mp-quest.o: mp-quest.h 45 | mp-topsin.o: mp-topsin.h 46 | mp-trig.o: mp-trig.h mp-binomial.h mp-cache.h mp-complex.h mp-misc.h 47 | mp-zerofind.o: mp-zerofind.h mp-complex.h 48 | mp-zeroiso.o: mp-zeroiso.h mp-complex.h 49 | mp-zeta.o: mp-zeta.h db-cache.h mp-binomial.h mp-cache.h mp-complex.h mp-consts.h mp-trig.h 50 | 51 | cache-fill.o: db-cache.h mp-zeta.h mp-misc.h 52 | db-merge.o: db-cache.h mp-misc.h 53 | 54 | $(MPLIB): $(MPOBJS) 55 | ar cruU $(MPLIB) $^ 56 | 57 | clean: 58 | rm -f core tmp junk glop a.out *.o 59 | 60 | realclean: clean 61 | rm -f $(EXES) *.png *.jpeg *.tex *.dat *.ps *.eps 62 | 63 | install: 64 | cp -p $(MPLIB) /usr/local/lib 65 | mkdir -p /usr/local/include/anant 66 | cp -p mp-*.h /usr/local/include/anant 67 | 68 | .c.o: 69 | $(CC) -c -g -O2 -Wall -I. $< 70 | 71 | .o: 72 | $(CC) -g -o $* $^ $(MPLIB) -lgmp -ldb -lpthread -lm 73 | -------------------------------------------------------------------------------- /src/README: -------------------------------------------------------------------------------- 1 | 2 | 3 | Development status: 4 | 5 | thread-safety: 6 | -------------- 7 | mp-consts.c: done 8 | 9 | precision management: 10 | --------------------- 11 | All precision is ad-hoc. 12 | -------------------------------------------------------------------------------- /src/db-cache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * db-cache.c 3 | * 4 | * File cache for pre-computed bignum values. 5 | * Copyright (C) 2006 Linas Vepstas 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 | * 02110-1301 USA 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include "db-cache.h" 36 | 37 | void fp_cache_put (const char * dbname, const mpf_t val, int idx, int nprec) 38 | { 39 | DB *db; 40 | 41 | db = dbopen (dbname, O_RDWR|O_EXCL, 42 | S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, DB_HASH, NULL); 43 | 44 | if (!db && (ENOENT == errno)) 45 | { 46 | db = dbopen (dbname, O_RDWR|O_CREAT|O_EXCL, 47 | S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, DB_HASH, NULL); 48 | } 49 | 50 | if (!db) 51 | { 52 | int norr = errno; 53 | fprintf (stderr, "Error: cannot open the cache file, idx=%d\n", idx); 54 | fprintf (stderr, "\t(%d) %s\n", norr, strerror(norr)); 55 | return; 56 | } 57 | 58 | /* Save the value data to the file */ 59 | DBT vkey; 60 | char buf[50]; 61 | sprintf (buf, "val[%d]", idx); 62 | vkey.data = &buf; 63 | vkey.size = strlen(buf)+1; 64 | 65 | /* the printf is floating point */ 66 | size_t allo = nprec*sizeof(char)+50; 67 | char *vstr= malloc (allo); 68 | gmp_snprintf (vstr, allo, "%.*Fg", nprec, val); 69 | 70 | DBT vdat; 71 | vdat.data = vstr; 72 | vdat.size = strlen(vstr)+1; 73 | 74 | db->put (db, &vkey, &vdat, 0); 75 | free (vstr); 76 | 77 | /* Save the precision data to the file */ 78 | DBT pkey; 79 | sprintf (buf, "prec[%d]", idx); 80 | pkey.data = &buf; 81 | pkey.size = strlen(buf)+1; 82 | 83 | DBT pdat; 84 | pdat.data = ⊀ 85 | pdat.size = sizeof(int); 86 | 87 | db->put (db, &pkey, &pdat, 0); 88 | db->close (db); 89 | } 90 | 91 | int fp_cache_get (const char * dbname, mpf_t val, int idx, int nprec) 92 | { 93 | DB *db; 94 | 95 | db = dbopen (dbname, O_RDONLY, 0, DB_HASH, NULL); 96 | 97 | if (!db) return 0; 98 | 99 | /* Look for the stored precision of data in the file */ 100 | DBT pkey; 101 | char buf[50]; 102 | sprintf (buf, "prec[%d]", idx); 103 | pkey.data = &buf; 104 | pkey.size = strlen(buf)+1; 105 | 106 | DBT pdat; 107 | 108 | /* if not key, then nothing */ 109 | int rc = db->get (db, &pkey, &pdat, 0); 110 | if (rc) 111 | { 112 | db->close (db); 113 | return 0; 114 | } 115 | 116 | /* check to see if we have enough precision */ 117 | int have_prec = *((int *)pdat.data); 118 | if (nprec > have_prec) 119 | { 120 | db->close (db); 121 | return 0; 122 | } 123 | 124 | /* Get the value data from the file */ 125 | DBT vkey; 126 | sprintf (buf, "val[%d]", idx); 127 | vkey.data = &buf; 128 | vkey.size = strlen(buf)+1; 129 | 130 | rc = db->get (db, &vkey, &pdat, 0); 131 | if (rc) 132 | { 133 | db->close (db); 134 | return 0; 135 | } 136 | 137 | // printf ("found nprec=%d for %d = %s\n", have_prec, idx, pdat.data); 138 | mpf_set_str (val, pdat.data, 10); 139 | 140 | db->close (db); 141 | return have_prec; 142 | } 143 | 144 | /* ========================== END OF FILE ============= */ 145 | -------------------------------------------------------------------------------- /src/db-cache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * db-cache.h 3 | * 4 | * File cache for pre-computed bignum values. Stores values, 5 | * with indicated precision, so we don't waste too much compute 6 | * time. 7 | * 8 | * Copyright (C) 2006 Linas Vepstas 9 | * 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU Lesser General Public 12 | * License as published by the Free Software Foundation; either 13 | * version 2.1 of the License, or (at your option) any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * Lesser General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Lesser General Public 21 | * License along with this library; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 23 | * 02110-1301 USA 24 | */ 25 | 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * fp_cache_put -- put mpf_t value into the database. 34 | * @prec: number of decimal places of precision to store. 35 | * @idx: index under which to store the value 36 | */ 37 | void fp_cache_put (const char * dbname, const mpf_t val, int idx, int nprec); 38 | 39 | /** 40 | * fp_cache_get -- get mpf_t from database 41 | * Returns 0 if no value in the database, or if the value in the 42 | * database has fewer than nprec digits. Thus, nprec is a minimum 43 | * requirement. 44 | */ 45 | int fp_cache_get (const char * dbname, mpf_t val, int idx, int nprec); 46 | 47 | #ifdef __cplusplus 48 | }; 49 | #endif 50 | 51 | -------------------------------------------------------------------------------- /src/db-merge.c: -------------------------------------------------------------------------------- 1 | /* 2 | * db-merge.c 3 | * 4 | * Merge two file caches containing pre-computed bignum values. 5 | * The value with the highest precision is kept. 6 | * 7 | * Linas Vepstas July 2006 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "db-cache.h" 17 | #include "mp-misc.h" 18 | 19 | int 20 | main (int argc, char * argv[]) 21 | { 22 | if (argc<5) 23 | { 24 | fprintf (stderr,"Usage: %s \n", argv[0]); 25 | exit (1); 26 | } 27 | char * dbout = argv[1]; 28 | char * dbina = argv[2]; 29 | char * dbinb = argv[3]; 30 | int from = atoi (argv[4]); 31 | int too = atoi (argv[5]); 32 | 33 | /* Compute number of binary bits this corresponds to. */ 34 | int prec = 10000; 35 | double v = ((double) prec) *log(10.0) / log(2.0); 36 | int bits = (int) (v + 100); 37 | /* set the precision (number of binary bits) */ 38 | mpf_set_default_prec (bits); 39 | 40 | printf ("Merging %s and %s into %s from %d to %d\n", dbina, dbinb, dbout, from, too); 41 | 42 | mpf_t vala, valb; 43 | mpf_init (vala); 44 | mpf_init (valb); 45 | int n; 46 | for (n=from; n<= too; n++) 47 | { 48 | int preca = fp_cache_get (dbina, vala, n, 10); 49 | int precb = fp_cache_get (dbinb, valb, n, 10); 50 | 51 | if (0 >= preca && 0 >= precb) continue; 52 | 53 | if (precb < preca) 54 | { 55 | if (strcmp (dbina, dbout)) 56 | { 57 | fp_cache_put (dbout, vala, n, preca); 58 | printf ("%d to %d from %s\t", n, preca, dbina); 59 | fp_prt ("", vala); 60 | printf ("\n"); 61 | } 62 | } 63 | else 64 | { 65 | if (strcmp (dbinb, dbout)) 66 | { 67 | fp_cache_put (dbout, valb, n, precb); 68 | printf ("%d to %d from %s\t", n, precb, dbinb); 69 | fp_prt ("", valb); 70 | printf ("\n"); 71 | } 72 | } 73 | } 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /src/db-prt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * db-merge.c 3 | * 4 | * Merge two file caches containing pre-computed bignum values. 5 | * The value with the highest precision is kept. 6 | * 7 | * Linas Vepstas July 2006 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "db-cache.h" 16 | 17 | int 18 | main (int argc, char * argv[]) 19 | { 20 | if (argc<4) 21 | { 22 | fprintf (stderr,"Usage: %s \n", argv[0]); 23 | exit (1); 24 | } 25 | char * db = argv[1]; 26 | int from = atoi (argv[2]); 27 | int maxidx = atoi (argv[3]); 28 | 29 | /* Compute number of binary bits this corresponds to. */ 30 | int prec = 100; 31 | double v = ((double) prec) *log(10.0) / log(2.0); 32 | int bits = (int) (v + 100); 33 | /* set the precision (number of binary bits) */ 34 | mpf_set_default_prec (bits); 35 | 36 | printf ("printout of %s up to n=%d\n", db, maxidx); 37 | 38 | mpf_t val; 39 | mpf_init (val); 40 | int n; 41 | int lastprec=-1; 42 | int lastn = 0; 43 | for (n=from; n<= maxidx; n++) 44 | { 45 | int prec = fp_cache_get (db, val, n, 10); 46 | if (prec != lastprec) 47 | { 48 | if (-1 != lastprec) 49 | { 50 | if (0 != lastn) 51 | { 52 | printf ("range %d : %d is to precision %d\n", lastn, n-1, lastprec); 53 | } 54 | else 55 | { 56 | printf ("range %d : %d is missing\n", lastn, n-1); 57 | } 58 | } 59 | lastprec = prec; 60 | lastn = n; 61 | } 62 | } 63 | if ((-1 != lastprec) && (0 != lastn)) 64 | { 65 | printf ("range %d : %d is to precision %d\n", lastn, n-1, lastprec); 66 | } 67 | else 68 | { 69 | printf ("range %d : %d is missing\n", lastn, n-1); 70 | } 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /src/mp-arith.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-arith.c 3 | * 4 | * High-precison number-theoretic arithmetic series, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2016 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include "mp-cache.h" 31 | #include "mp-misc.h" 32 | 33 | /* ======================================================================= */ 34 | /* 35 | * sigma_one 36 | * Sum of divisors. That is, sigma_one(n) = sum_{d|n} d 37 | * See https://en.wikipedia.org/wiki/Divisor_function 38 | * Uses cached values. 39 | * 40 | * Brute force, very simple. There is a simple and much faster recursive 41 | * algorithm for this, if we have a list of prime numbers on hand. 42 | * However, the below is adequate for current purposes (mostly because 43 | * practical work with the partition function below never gets past 44 | * a value of about n=50K or 100K or so, so the brute-force sum is tenable). 45 | */ 46 | static void sigma_one_z_nocache (mpz_t sum, unsigned int n) 47 | { 48 | mpz_set_ui (sum, n); 49 | unsigned int d; 50 | unsigned int ns = n / 2; 51 | for (d=1; d<=ns; d++) 52 | { 53 | if (n%d) continue; 54 | mpz_add_ui (sum, sum, d); 55 | } 56 | } 57 | 58 | void sigma_one_z (mpz_t sum, unsigned int n) 59 | { 60 | DECLARE_I_CACHE(sigone); 61 | 62 | if (i_one_d_cache_check(&sigone, n)) 63 | { 64 | mpz_init (sum); 65 | i_one_d_cache_fetch(&sigone, sum, n); 66 | return; 67 | } 68 | 69 | sigma_one_z_nocache(sum, n); 70 | i_one_d_cache_store(&sigone, sum, n); 71 | } 72 | 73 | // =========================================================== 74 | /* 75 | * partition function. 76 | * See https://en.wikipedia.org/wiki/Partition_(number_theory) 77 | * Uses cached values. 78 | * 79 | * The problem here is that the partition function overflows 80 | * a 64-bit int around n=400, and a 128-bit int around n=1400. 81 | * 82 | * Brute force, simple. 83 | */ 84 | void partition_z (mpz_t sum, unsigned int n) 85 | { 86 | DECLARE_I_CACHE(parti); 87 | 88 | if (0 == n) 89 | { 90 | mpz_set_ui(sum, 1); 91 | return; 92 | } 93 | 94 | if (i_one_d_cache_check(&parti, n)) 95 | { 96 | i_one_d_cache_fetch(&parti, sum, n); 97 | return; 98 | } 99 | 100 | mpz_t sig; mpz_init (sig); 101 | mpz_t part; mpz_init(part); 102 | mpz_t term; mpz_init(term); 103 | 104 | mpz_set_ui (sum, 0); 105 | unsigned int k; 106 | for (k=0; k 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * sigma_one 33 | * Sum of divisors. That is, sigma_one(n) = sum_{d|n} d 34 | * See https://en.wikipedia.org/wiki/Divisor_function 35 | * Uses cached values. 36 | * 37 | * Brute force, simple. 38 | */ 39 | void sigma_one_z (mpz_t poch, unsigned int n); 40 | 41 | /** 42 | * Partition function. 43 | * See https://en.wikipedia.org/wiki/Partition_(number_theory) 44 | * Uses cached values. 45 | * 46 | * The reason we need a GMP variant is that the partition function 47 | * overflows a 64-bit int around n=400, and a 128-bit int around n=1400. 48 | * 49 | * Brute force, simple. 50 | */ 51 | void partition_z (mpz_t poch, unsigned int n); 52 | 53 | #ifdef __cplusplus 54 | }; 55 | #endif 56 | 57 | -------------------------------------------------------------------------------- /src/mp-binomial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-binomial.c 3 | * 4 | * High-precison factorials and binomials, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005, 2006 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include "mp-cache.h" 31 | #include "mp-binomial.h" 32 | #include "mp-misc.h" 33 | #include "mp-trig.h" 34 | 35 | /* ======================================================================= */ 36 | /* i_poch_rising 37 | * rising pochhammer symbol, for integer values. 38 | * 39 | * Brute force, simple. 40 | */ 41 | 42 | void i_poch_rising (mpz_t poch, unsigned int k, unsigned int n) 43 | { 44 | mpz_t acc; 45 | mpz_init (acc); 46 | 47 | mpz_set_ui (poch, 1); 48 | unsigned int i; 49 | for (i=0; i= n) 68 | { 69 | mpz_set_ui (fact, 1); 70 | return; 71 | } 72 | int hit = i_one_d_cache_check (&cache, n); 73 | if (hit) 74 | { 75 | i_one_d_cache_fetch (&cache, fact, n); 76 | } 77 | else 78 | { 79 | i_poch_rising (fact, 1, n); 80 | i_one_d_cache_store (&cache, fact, n); 81 | } 82 | } 83 | #else 84 | #define i_factorial mpz_fac_ui 85 | #endif /* USE_LOCAL_FACTORIAL */ 86 | 87 | /* ====================================================================== */ 88 | 89 | /* Compute and cache inverse factorial. */ 90 | void fp_inv_factorial (mpf_t inv, unsigned int k, unsigned int prec) 91 | { 92 | DECLARE_FP_CACHE (infac); 93 | 94 | if (prec <= fp_one_d_cache_check (&infac, k)) 95 | { 96 | fp_one_d_cache_fetch (&infac, inv, k); 97 | return; 98 | } 99 | 100 | mpz_t fac; 101 | mpz_init (fac); 102 | mpz_fac_ui (fac, k); 103 | mpf_set_z (inv, fac); 104 | mpf_ui_div (inv, 1, inv); 105 | 106 | mpz_clear (fac); 107 | fp_one_d_cache_store (&infac, inv, k, prec); 108 | } 109 | 110 | /* ====================================================================== */ 111 | /* i_binomial 112 | * Binomial coefficient (n k) 113 | */ 114 | 115 | // #define USE_LOCAL_BINOMIAL 116 | #ifdef USE_LOCAL_BINOMIAL 117 | static void i_binomial_compute (mpz_t bin, unsigned int n, unsigned int k) 118 | { 119 | mpz_t top, bot; 120 | 121 | if (2*k < n) k = n-k; 122 | 123 | mpz_init (top); 124 | mpz_init (bot); 125 | i_poch_rising (top, k+1, n-k); 126 | i_factorial (bot, n-k); 127 | 128 | mpz_divexact (bin, top, bot); 129 | 130 | mpz_clear (top); 131 | mpz_clear (bot); 132 | } 133 | 134 | static void i_binomial_recurse (mpz_t bin, unsigned int n, unsigned int k) 135 | { 136 | mpz_t top, bot; 137 | 138 | mpz_init (top); 139 | mpz_init (bot); 140 | 141 | i_binomial (bot,n-1,k-1); 142 | i_binomial (top,n-1,k); 143 | mpz_add (bin, top, bot); 144 | 145 | mpz_clear (top); 146 | mpz_clear (bot); 147 | } 148 | 149 | /** 150 | * i_binomial - return the binomial coefficient 151 | * Uses a cached value if avalable. 152 | */ 153 | void i_binomial (mpz_t bin, unsigned int n, unsigned int k) 154 | { 155 | DECLARE_I_CACHE (cache); 156 | 157 | if (k > n || 0 > k) 158 | { 159 | mpz_set_ui (bin, 0); 160 | return; 161 | } 162 | 163 | if (1 >= n) 164 | { 165 | mpz_set_ui (bin, 1); 166 | return; 167 | } 168 | 169 | if (2*k < n) k = n-k; 170 | int hit = i_triangle_cache_check (&cache, n, k); 171 | if (hit) 172 | { 173 | i_triangle_cache_fetch (&cache, bin, n, k); 174 | } 175 | else 176 | { 177 | // i_binomial_compute (bin, n, k); 178 | i_binomial_recurse (bin, n, k); 179 | i_triangle_cache_store (&cache, bin, n, k); 180 | } 181 | } 182 | #else 183 | #define i_binomial mpz_bin_uiui 184 | 185 | #endif /* USE_LOCAL_BINOMIAL */ 186 | 187 | /* ======================================================================= */ 188 | 189 | /** 190 | * i_binomial_sequence -- returns binomial, assumes purely sequeintial access 191 | * 192 | * This routine assumes that the binomial coefficients will be 193 | * accessed in an utterly sequential mode, with k running from 194 | * zero to n, and n running from zero to infinity. For sequential access, 195 | * this routine is very very fast. Otherwise, random access is used 196 | * which is considerably slower. 197 | */ 198 | void i_binomial_sequence (mpz_t bin, unsigned int n, unsigned int k) 199 | { 200 | static int curr_n=0, last_k=0; 201 | DECLARE_I_CACHE (a_cache); 202 | DECLARE_I_CACHE (b_cache); 203 | static i_cache *curr_cache = &a_cache; 204 | static i_cache *next_cache = &b_cache; 205 | 206 | /* Gap in access sequence; fill in the gap */ 207 | if (k > last_k+1 || (n > curr_n && k !=0)) 208 | { 209 | int j,m; 210 | if (n == curr_n) 211 | { 212 | for (j=last_k+1; j n || 0 > k) 288 | { 289 | mpz_set_ui (bin, 0); 290 | return; 291 | } 292 | 293 | /* If we got to here, it must be some random access. */ 294 | i_binomial (bin, n, k); 295 | fprintf (stderr, "booooo! n=%d k=%d currn=%d lastk=%d\n", n,k, curr_n, last_k); 296 | } 297 | 298 | /* ======================================================================= */ 299 | /* stirling_first - Stirling Numbers of the First kind, 300 | * normalized so that they are all positive. 301 | * Uses dynamically-sized cache. 302 | */ 303 | void i_stirling_first (mpz_t s, unsigned int n, unsigned int k) 304 | { 305 | DECLARE_I_CACHE (cache); 306 | 307 | /* Trivial case (not in the cache) */ 308 | if (0==k) 309 | { 310 | if (0==n) 311 | { 312 | mpz_set_ui (s, 1); 313 | } 314 | else 315 | { 316 | mpz_set_ui (s, 0); 317 | } 318 | return; 319 | } 320 | 321 | if (n= n) 401 | { 402 | mpz_set_ui (s, 1); 403 | return; 404 | } 405 | 406 | int hit = i_triangle_cache_check (&cache, n, m); 407 | if (hit) 408 | { 409 | i_triangle_cache_fetch (&cache, s, n, m); 410 | } 411 | else 412 | { 413 | i_stirbin_sum_compute (s, n, m); 414 | i_triangle_cache_store (&cache, s, n, m); 415 | } 416 | } 417 | 418 | /* ======================================================================= */ 419 | /* stirling_second - Stirling Numbers of the Second kind, 420 | * Uses dynamically-sized cache. 421 | */ 422 | void i_stirling_second (mpz_t s, unsigned int n, unsigned int k) 423 | { 424 | DECLARE_I_CACHE (cache); 425 | 426 | /* Trivial case (not in the cache) */ 427 | if (0==k) 428 | { 429 | if (0==n) 430 | { 431 | mpz_set_ui (s, 1); 432 | } 433 | else 434 | { 435 | mpz_set_ui (s, 0); 436 | } 437 | return; 438 | } 439 | 440 | if (n= n) 549 | { 550 | mpf_set_ui (harm, 1); 551 | return; 552 | } 553 | int have_prec = fp_one_d_cache_check (&cache, n); 554 | if (prec <= have_prec) 555 | { 556 | fp_one_d_cache_fetch (&cache, harm, n); 557 | return; 558 | } 559 | 560 | unsigned int istart = n-1; 561 | have_prec = fp_one_d_cache_check (&cache, istart); 562 | while ((have_prec < prec) && (1 < istart)) 563 | { 564 | istart--; 565 | have_prec = fp_one_d_cache_check (&cache, istart); 566 | } 567 | 568 | unsigned int i; 569 | fp_harmonic (harm, istart, prec); 570 | 571 | mpf_t term; 572 | mpf_init (term); 573 | for (i=istart+1; i<=n; i++) 574 | { 575 | mpf_set_ui (term, 1); 576 | mpf_div_ui (term, term, i); 577 | mpf_add (harm, harm, term); 578 | fp_one_d_cache_store (&cache, harm, i, prec); 579 | } 580 | mpf_clear (term); 581 | } 582 | 583 | /* ======================================================================= */ 584 | /** 585 | * fp_poch_rising 586 | * Rising pochhammer symbol (x)_n, for real values of x and integer n. 587 | * 588 | * Brute force, simple. 589 | */ 590 | 591 | void fp_poch_rising_d (mpf_t poch, double x, unsigned int n) 592 | { 593 | mpf_t term; 594 | mpf_init (term); 595 | 596 | mpf_set_ui (poch, 1); 597 | unsigned int i; 598 | for (i=0; i= k) { 744 | cpx_set_ui (bin, 1, 0); 745 | return; 746 | } 747 | 748 | cpx_t top, bot; 749 | cpx_init (top); 750 | cpx_init (bot); 751 | 752 | cpx_set (bot, ess); 753 | mpf_sub_ui (bot[0].re, bot[0].re, k-1); 754 | 755 | cpx_poch_rising (top, bot, k); 756 | 757 | mpz_t ifac; 758 | mpz_init (ifac); 759 | i_factorial (ifac, k); 760 | 761 | mpf_t fac; 762 | mpf_init(fac); 763 | mpf_set_z (fac, ifac); 764 | 765 | cpx_div_mpf (bin, top, fac); 766 | 767 | mpf_clear (fac); 768 | mpz_clear (ifac); 769 | 770 | cpx_clear (top); 771 | cpx_clear (bot); 772 | } 773 | 774 | void cpx_binomial_sum_cache (cpx_t bin, const cpx_t ess, unsigned int k) 775 | { 776 | DECLARE_CPX_CACHE (cache); 777 | static int cache_bits = 0; 778 | static cpx_t cache_s; 779 | 780 | if (!cache_bits) 781 | { 782 | cpx_init (cache_s); 783 | cpx_set_ui (cache_s, 1, 0); 784 | } 785 | int prec = mpf_get_default_prec(); 786 | if (cache_bits < prec) 787 | { 788 | cpx_set_prec (cache_s, prec); 789 | cache_bits = prec; 790 | } 791 | 792 | /* First, check if this is the same s value as before */ 793 | if (!cpx_eq (cache_s, ess, prec)) 794 | { 795 | cpx_one_d_cache_clear (&cache); 796 | cpx_set (cache_s, ess); 797 | } 798 | 799 | /* Check the local cache */ 800 | int have_prec = cpx_one_d_cache_check (&cache, k); 801 | if (have_prec >= prec) 802 | { 803 | cpx_one_d_cache_fetch (&cache, bin, k); 804 | return; 805 | } 806 | 807 | cpx_t sn; 808 | cpx_init (sn); 809 | cpx_add_ui (sn, ess, k, 0); 810 | cpx_binomial (bin, sn, k); 811 | cpx_one_d_cache_store (&cache, bin, k, prec); 812 | cpx_clear (sn); 813 | } 814 | 815 | 816 | /* =============================== END OF FILE =========================== */ 817 | -------------------------------------------------------------------------------- /src/mp-binomial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-binomial.h 3 | * 4 | * High-precison factorials and binomials, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005,2006 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /* i_poch_rising 33 | * Rising pochhammer symbol, for integer values. 34 | * 35 | * Brute force, simple. 36 | */ 37 | void i_poch_rising (mpz_t poch, unsigned int k, unsigned int n); 38 | 39 | /** 40 | * i_factorial -- the factorial 41 | */ 42 | // #define USE_LOCAL_FACTORIAL 43 | #ifdef USE_LOCAL_FACTORIAL 44 | void i_factorial (mpz_t fact, unsigned int n); 45 | #else 46 | #define i_factorial mpz_fac_ui 47 | #endif /* USE_LOCAL_FACTORIAL */ 48 | 49 | /** 50 | * fp_inv_factorial -- Return 1/n! 51 | * Returns a cached value to improve performance. 52 | */ 53 | void fp_inv_factorial (mpf_t invfac, unsigned int n, unsigned int prec); 54 | 55 | /* i_binomial 56 | * Binomial coefficient (n k) $ {n \choose k} $ 57 | */ 58 | // #define USE_LOCAL_BINOMIAL 59 | #ifdef USE_LOCAL_BINOMIAL 60 | void i_binomial (mpz_t bin, unsigned int n, unsigned int k); 61 | #else 62 | #define i_binomial mpz_bin_uiui 63 | #endif /* USE_LOCAL_BINOMIAL */ 64 | 65 | /** 66 | * i_binomial_sequence -- returns binomial, assumes purely sequential access. 67 | * 68 | * Returns $ {n \choose k} $ 69 | * 70 | * This routine assumes that the binomial coefficients will be 71 | * accessed in an utterly sequential mode, with k running from 72 | * zero to n, and n running from zero to infinity. For sequential access, 73 | * this routine is very, very fast. If the access is not sequential, 74 | * the correct answer is still returned; just that a random access 75 | * compute method is used, which is considerably slower. 76 | */ 77 | void i_binomial_sequence (mpz_t bin, unsigned int n, unsigned int k); 78 | 79 | /** 80 | * stirling_first - Stirling Numbers of the First kind, 81 | * normalized so that they are all positive. 82 | * Uses dynamically-sized cache. 83 | */ 84 | void i_stirling_first (mpz_t s, unsigned int n, unsigned int k); 85 | /* A funny off-by-one sum of stirling and binomial */ 86 | void i_stirbin_sum (mpz_t s, unsigned int n, unsigned int m); 87 | 88 | /** 89 | * stirling_second - Stirling Numbers of the Second kind, 90 | * Uses dynamically-sized cache. 91 | */ 92 | void i_stirling_second (mpz_t s, unsigned int n, unsigned int k); 93 | 94 | /* binomial transform of power sum */ 95 | void fp_bin_xform_pow (mpf_t bxp, unsigned int n, unsigned int s); 96 | 97 | /** 98 | * fp_harmonic -- The harmonic number, H_n = sum_k=1^n 1/k 99 | * Caches values, for speed. 100 | */ 101 | void fp_harmonic (mpf_t harm, unsigned int n, unsigned int prec); 102 | 103 | /** 104 | * fp_poch_rising 105 | * Rising pochhammer symbol (x)_n, for real values of x and integer n. 106 | * p = x(x+1)(x+2)...(x+n-1) = Gamma(x+n)/Gamma(x) 107 | * 108 | * Brute force, simple. 109 | */ 110 | void fp_poch_rising (mpf_t poch, mpf_t x, unsigned int n); 111 | void fp_poch_rising_d (mpf_t poch, double x, unsigned int n); 112 | 113 | /** 114 | * cpx_poch_rising 115 | * Rising pochhammer symbol (s)_n, for complex s and integer n. 116 | * 117 | * Brute force, simple. 118 | */ 119 | void cpx_poch_rising_d (cpx_t poch, double re_s, double im_s, unsigned int n); 120 | 121 | /** 122 | * cpx_poch_rising 123 | * Rising pochhammer symbol (s)_n, for complex s and integer n. 124 | * 125 | * Brute force, simple. 126 | */ 127 | void cpx_poch_rising (cpx_t poch, const cpx_t s, unsigned int n); 128 | 129 | /** 130 | * fp_binomial_d 131 | * Binomial coefficient, for double-precision s. 132 | */ 133 | void fp_binomial_d (mpf_t bin, double s, unsigned int k); 134 | 135 | /** 136 | * cpx_binomial-- Complex binomial coefficient 137 | * Compute the binomial coefficient (s k) for complex s. 138 | * That is, $ {s \choose k} $ 139 | */ 140 | void cpx_binomial_d (cpx_t bin, double re_s, double im_s, unsigned int k); 141 | void cpx_binomial (cpx_t bin, const cpx_t s, unsigned int k); 142 | 143 | /** 144 | * cpx_binomial_sum_cache-- Complex binomial coefficient 145 | * Compute the binomial coefficient for (s+k, k) 146 | * This routine caches computed values, and thus can be considerably 147 | * faster if called again with the same k and s values. 148 | */ 149 | void cpx_binomial_sum_cache (cpx_t bin, const cpx_t s, unsigned int k); 150 | 151 | #ifdef __cplusplus 152 | }; 153 | #endif 154 | 155 | -------------------------------------------------------------------------------- /src/mp-cache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-cache.c 3 | * 4 | * Array cachine functions for arrays of high precision quantities 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include "mp-cache.h" 32 | 33 | /* ======================================================================= */ 34 | /* Cache management */ 35 | 36 | /** i_one_d_cache_check() -- check if mpz_t value is in the cache 37 | * Returns true if the value is in the cache, else returns false. 38 | * This assumes a 1-dimensional cache layout (simple aray) 39 | */ 40 | int i_one_d_cache_check (i_cache *c, unsigned int n) 41 | { 42 | if (c->disabled) return 0; 43 | pthread_spin_lock(&c->lock); 44 | 45 | if ((n <= c->nmax) && 0 != c->nmax) 46 | { 47 | int gotit = c->ticky[n]; 48 | pthread_spin_unlock(&c->lock); 49 | return gotit; 50 | } 51 | 52 | unsigned int newsize = 1.5*n+2; 53 | mpz_t* new_cache = (mpz_t *) malloc (newsize * sizeof (mpz_t)); 54 | if (c->nmax) memcpy(new_cache, c->cache, (c->nmax+1) * sizeof(mpz_t)); 55 | 56 | char* new_ticky = (char *) malloc (newsize * sizeof(char)); 57 | if (c->nmax) memcpy(new_ticky, c->ticky, (c->nmax+1) * sizeof(char)); 58 | 59 | unsigned int en; 60 | unsigned int nstart = c->nmax+1; 61 | if (0 == c->nmax) nstart = 0; 62 | for (en = nstart; en < newsize; en++) 63 | { 64 | mpz_init (new_cache[en]); 65 | new_ticky[en] = 0; 66 | } 67 | 68 | /* Now swap out the old and new */ 69 | mpz_t* old_cache = c->cache; 70 | c->cache = new_cache; 71 | char* old_ticky = c->ticky; 72 | c->ticky = new_ticky; 73 | c->nmax = newsize-1; 74 | pthread_spin_unlock(&c->lock); 75 | 76 | if (old_cache) free(old_cache); 77 | if (old_ticky) free(old_ticky); 78 | return 0; 79 | } 80 | 81 | /* ======================================================================= */ 82 | /** i_triangle_cache_check() -- check if mpz_t value is in the cache 83 | * Returns true if the value is in the cache, else returns false. 84 | * This assumes a trianglular cache layout (two indecies) 85 | * with 0 <= k <=n 86 | */ 87 | int i_triangle_cache_check (i_cache *c, unsigned int n, unsigned int k) 88 | { 89 | if (c->disabled) return 0; 90 | pthread_spin_lock(&c->lock); 91 | 92 | if ((n <= c->nmax) && 0 != c->nmax) 93 | { 94 | unsigned int idx = n * (n+1) /2 ; 95 | int gotit = c->ticky[idx+k]; 96 | pthread_spin_unlock(&c->lock); 97 | return gotit; 98 | } 99 | 100 | if (0 == n) n = 1; 101 | unsigned int newsize = (n+1)*(n+2)/2; 102 | mpz_t* new_cache = (mpz_t *) malloc (newsize * sizeof (mpz_t)); 103 | if (c->nmax) memcpy(new_cache, c->cache, (c->nmax+1) * sizeof(mpz_t)); 104 | 105 | char* new_ticky = (char *) malloc (newsize * sizeof(char)); 106 | if (c->nmax) memcpy(new_ticky, c->ticky, (c->nmax+1) * sizeof(char)); 107 | 108 | unsigned int en; 109 | unsigned int nstart = c->nmax + 1; 110 | if (0 == c->nmax) nstart = 0; 111 | for (en = nstart; en <= n; en++) 112 | { 113 | unsigned int j; 114 | unsigned int idx = en * (en+1) /2; 115 | for (j=0; j<=en; j++) 116 | { 117 | mpz_init (new_cache[idx+j]); 118 | new_ticky[idx+j] = 0; 119 | } 120 | } 121 | 122 | /* Now swap out the old and new */ 123 | mpz_t* old_cache = c->cache; 124 | c->cache = new_cache; 125 | char* old_ticky = c->ticky; 126 | c->ticky = new_ticky; 127 | c->nmax = n; 128 | pthread_spin_unlock(&c->lock); 129 | 130 | if (old_cache) free(old_cache); 131 | if (old_ticky) free(old_ticky); 132 | return 0; 133 | } 134 | 135 | void i_one_d_cache_clear (i_cache *c) 136 | { 137 | pthread_spin_lock(&c->lock); 138 | unsigned int i; 139 | for (i=0; inmax; i++) 140 | { 141 | c->ticky[i] = 0; 142 | } 143 | pthread_spin_unlock(&c->lock); 144 | } 145 | 146 | /* ======================================================================= */ 147 | /* Cache management */ 148 | /* pure cut-n-paste of he integer variant */ 149 | 150 | /** q_one_d_cache_check() -- check if mpq_t value is in the cache 151 | * Returns true if the value is in the cache, else returns false. 152 | * This assumes a 1-dimensional cache layout (simple aray) 153 | */ 154 | int q_one_d_cache_check (q_cache *c, unsigned int n) 155 | { 156 | if ((n > c->nmax) || 0==c->nmax ) 157 | { 158 | unsigned int newsize = 1.5*n+2; 159 | c->cache = (mpq_t *) realloc (c->cache, newsize * sizeof (mpq_t)); 160 | c->ticky = (char *) realloc (c->ticky, newsize * sizeof (char)); 161 | 162 | unsigned int en; 163 | unsigned int nstart = c->nmax+1; 164 | if (0 == c->nmax) nstart = 0; 165 | for (en = nstart; en < newsize; en++) 166 | { 167 | mpq_init (c->cache[en]); 168 | c->ticky[en] = 0; 169 | } 170 | c->nmax = newsize-1; 171 | return 0; 172 | } 173 | 174 | return (c->ticky[n]); 175 | } 176 | 177 | /* ======================================================================= */ 178 | /* Cache management */ 179 | /* Almost a cut-n-paste of above, but using fp instead */ 180 | 181 | /** fp_one_d_cache_check() -- check if mpf_t value is in the cache 182 | * If there is a cached value, this returns the precision of the 183 | * value in the cache; else it returns zero. 184 | * This assumes a 1-dimensional cache layout (simple array) 185 | */ 186 | int fp_one_d_cache_check (fp_cache *c, unsigned int n) 187 | { 188 | pthread_spin_lock(&c->lock); 189 | if ((n <= c->nmax) && 0 != c->nmax) 190 | { 191 | int prec = c->precision[n]; 192 | pthread_spin_unlock(&c->lock); 193 | return prec; 194 | } 195 | 196 | unsigned int newsize = 1.5*n+2; 197 | mpf_t* new_cache = (mpf_t *) malloc (newsize * sizeof(mpf_t)); 198 | if (c->nmax) memcpy(new_cache, c->cache, (c->nmax+1) * sizeof(mpf_t)); 199 | 200 | int* new_prec = (int *) malloc (newsize * sizeof(int)); 201 | if (c->nmax) memcpy(new_prec, c->precision, (c->nmax+1) * sizeof(int)); 202 | 203 | unsigned int en; 204 | unsigned int nstart = c->nmax+1; 205 | if (0 == c->nmax) nstart = 0; 206 | for (en = nstart; en < newsize; en++) 207 | { 208 | mpf_init (new_cache[en]); 209 | new_prec[en] = 0; 210 | } 211 | 212 | /* Now swap out the old and new */ 213 | mpf_t* old_cache = c->cache; 214 | c->cache = new_cache; 215 | int* old_prec = c->precision; 216 | c->precision = new_prec; 217 | c->nmax = newsize-1; 218 | pthread_spin_unlock(&c->lock); 219 | 220 | if (old_cache) free(old_cache); 221 | if (old_prec) free(old_prec); 222 | return 0; 223 | } 224 | 225 | void fp_one_d_cache_clear (fp_cache *c) 226 | { 227 | unsigned int i; 228 | pthread_spin_lock(&c->lock); 229 | for (i=0; inmax; i++) 230 | { 231 | c->precision[i] = 0; 232 | } 233 | pthread_spin_unlock(&c->lock); 234 | } 235 | 236 | /* ======================================================================= */ 237 | /** fp_triangle_cache_check() -- check if mpf_t value is in the cache 238 | * If there is a cached value, this returns the precision of the 239 | * value in the cache; else it returns zero. 240 | * This assumes a trianglular cache layout (two indecies) 241 | * with 0 <= k <=n 242 | */ 243 | int fp_triangle_cache_check (fp_cache *c, unsigned int n, unsigned int k) 244 | { 245 | if ((n > c->nmax) || 0==c->nmax ) 246 | { 247 | if (0 == n) n = 1; 248 | unsigned int newsize = (n+1)*(n+2)/2; 249 | c->cache = (mpf_t *) realloc (c->cache, newsize * sizeof (mpf_t)); 250 | c->precision = (int *) realloc (c->precision, newsize * sizeof (int)); 251 | 252 | unsigned int en; 253 | unsigned int nstart = c->nmax + 1; 254 | if (0 == c->nmax) nstart = 0; 255 | for (en = nstart; en <= n; en++) 256 | { 257 | unsigned int j; 258 | unsigned int idx = en * (en+1) /2 ; 259 | for (j=0; j<=en; j++) 260 | { 261 | mpf_init (c->cache[idx+j]); 262 | c->precision[idx+j]=0; 263 | } 264 | } 265 | c->nmax = n; 266 | return 0; 267 | } 268 | unsigned int idx = n * (n+1) /2 ; 269 | return c->precision[idx+k]; 270 | } 271 | 272 | /* ======================================================================= */ 273 | /* Cache management */ 274 | /* A cut-n-paste of above, but using cpx instead */ 275 | 276 | /** cpx_one_d_cache_check() -- check if cpx_t value is in the cache 277 | * If there is a cached value, this returns the precision of the 278 | * value in the cache; else it returns zero. 279 | * This assumes a 1-dimensional cache layout (simple array) 280 | */ 281 | int cpx_one_d_cache_check (cpx_cache *c, unsigned int n) 282 | { 283 | pthread_spin_lock(&c->lock); 284 | if ((n <= c->nmax) && 0 != c->nmax) 285 | { 286 | int prec = c->precision[n]; 287 | pthread_spin_unlock(&c->lock); 288 | return prec; 289 | } 290 | 291 | unsigned int newsize = 1.5*n+2; 292 | cpx_t* new_cache = (cpx_t *) malloc (newsize * sizeof(cpx_t)); 293 | if (c->nmax) memcpy(new_cache, c->cache, (c->nmax+1) * sizeof(cpx_t)); 294 | 295 | int* new_prec = (int *) malloc (newsize * sizeof(int)); 296 | if (c->nmax) memcpy(new_prec, c->precision, (c->nmax+1) * sizeof(int)); 297 | 298 | unsigned int en; 299 | unsigned int nstart = c->nmax+1; 300 | if (0 == c->nmax) nstart = 0; 301 | for (en = nstart; en < newsize; en++) 302 | { 303 | cpx_init (new_cache[en]); 304 | new_prec[en] = 0; 305 | } 306 | 307 | /* Now swap out the old and new */ 308 | cpx_t* old_cache = c->cache; 309 | c->cache = new_cache; 310 | int* old_prec = c->precision; 311 | c->precision = new_prec; 312 | c->nmax = newsize-1; 313 | pthread_spin_unlock(&c->lock); 314 | 315 | if (old_cache) free(old_cache); 316 | if (old_prec) free(old_prec); 317 | return 0; 318 | } 319 | 320 | void cpx_one_d_cache_clear (cpx_cache *c) 321 | { 322 | unsigned int i; 323 | pthread_spin_lock(&c->lock); 324 | for (i=0; inmax; i++) 325 | { 326 | c->precision[i] = 0; 327 | } 328 | pthread_spin_unlock(&c->lock); 329 | } 330 | 331 | /* =============================== END OF FILE =========================== */ 332 | 333 | -------------------------------------------------------------------------------- /src/mp-cache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-cache.h 3 | * 4 | * Array cachine functions for arrays of high precision quantities 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | #include "mp-complex.h" 28 | 29 | /* ======================================================================= */ 30 | /* Cache management */ 31 | 32 | typedef struct 33 | { 34 | unsigned int nmax; 35 | mpz_t *cache; 36 | char *ticky; 37 | short disabled; 38 | pthread_spinlock_t lock; 39 | } i_cache; 40 | 41 | 42 | #define DECLARE_I_CACHE(name) \ 43 | static i_cache name = {.nmax=0, .cache=NULL, .ticky=NULL, .disabled = 0}; \ 44 | __attribute__((constructor)) \ 45 | void i_cache_ctor##name () { \ 46 | pthread_spin_init(&name.lock, 0); } 47 | 48 | /** i_one_d_cache_check() -- check if mpz_t value is in the cache 49 | * Returns true if the value is in the cache, else returns false. 50 | * This assumes a 1-dimensional cache layout (simple aray) 51 | */ 52 | int i_one_d_cache_check (i_cache *c, unsigned int n); 53 | 54 | /** 55 | * i_one_d_cache_fetch - fetch value from cache 56 | */ 57 | static inline void i_one_d_cache_fetch (i_cache *c, mpz_t val, unsigned int n) 58 | { 59 | if (c->disabled) return; 60 | pthread_spin_lock(&c->lock); 61 | mpz_set(val, c->cache[n]); 62 | pthread_spin_unlock(&c->lock); 63 | } 64 | 65 | /** 66 | * i_one_d_cache_store - store value in cache 67 | */ 68 | static inline void i_one_d_cache_store (i_cache *c, const mpz_t val, unsigned int n) 69 | { 70 | if (c->disabled) return; 71 | pthread_spin_lock(&c->lock); 72 | mpz_set (c->cache[n], val); 73 | c->ticky[n] = 1; 74 | pthread_spin_unlock(&c->lock); 75 | } 76 | 77 | void i_one_d_cache_clear (i_cache *c); 78 | 79 | /* ======================================================================= */ 80 | /** i_triangle_cache_check() -- check if mpz_t value is in the cache 81 | * Returns true if the value is in the cache, else returns false. 82 | * This assumes a trianglular cache layout (two indecies) 83 | * with 0 <= k <=n 84 | */ 85 | int i_triangle_cache_check (i_cache *c, unsigned int n, unsigned int k); 86 | 87 | /** 88 | * i_triangle_cache_fetch - fetch value from cache 89 | */ 90 | static inline void i_triangle_cache_fetch (i_cache *c, mpz_t val, unsigned int n, unsigned int k) 91 | { 92 | pthread_spin_lock(&c->lock); 93 | unsigned int idx = n * (n+1) /2 ; 94 | mpz_set (val, c->cache[idx+k]); 95 | pthread_spin_unlock(&c->lock); 96 | } 97 | 98 | 99 | /** 100 | * i_triangle_cache_store - store value in cache 101 | */ 102 | static inline void i_triangle_cache_store (i_cache *c, const mpz_t val, unsigned int n, unsigned int k) 103 | { 104 | pthread_spin_lock(&c->lock); 105 | unsigned int idx = n * (n+1) /2 ; 106 | mpz_set (c->cache[idx+k], val); 107 | c->ticky[idx+k] = 1; 108 | pthread_spin_unlock(&c->lock); 109 | } 110 | 111 | /* ======================================================================= */ 112 | /* Cache management */ 113 | /* pure cut-n-paste of the integer variant */ 114 | 115 | typedef struct 116 | { 117 | unsigned int nmax; 118 | mpq_t *cache; 119 | char *ticky; 120 | } q_cache; 121 | 122 | #define DECLARE_Q_CACHE(name) \ 123 | static q_cache name = {.nmax=0, .cache=NULL, .ticky=NULL} 124 | 125 | /** q_one_d_cache_check() -- check if mpq_t value is in the cache 126 | * Returns true if the value is in the cache, else returns false. 127 | * This assumes a 1-dimensional cache layout (simple aray) 128 | */ 129 | int q_one_d_cache_check (q_cache *c, unsigned int n); 130 | 131 | /** 132 | * q_one_d_cache_fetch - fetch value from cache 133 | */ 134 | static inline void q_one_d_cache_fetch (q_cache *c, mpq_t val, unsigned int n) 135 | { 136 | mpq_set (val, c->cache[n]); 137 | } 138 | 139 | /** 140 | * q_one_d_cache_store - store value in cache 141 | */ 142 | static inline void q_one_d_cache_store (q_cache *c, const mpq_t val, unsigned int n) 143 | { 144 | mpq_set (c->cache[n], val); 145 | c->ticky[n] = 1; 146 | } 147 | 148 | /* ======================================================================= */ 149 | /* Cache management */ 150 | /* Almost a cut-n-paste of above, but using fp instead */ 151 | 152 | typedef struct 153 | { 154 | unsigned int nmax; 155 | mpf_t *cache; 156 | int *precision; /* base-10 precision of cached value */ 157 | pthread_spinlock_t lock; 158 | } fp_cache; 159 | 160 | 161 | #define DECLARE_FP_CACHE(name) \ 162 | static fp_cache name = {.nmax=0, .cache=NULL, .precision=NULL, }; \ 163 | __attribute__((constructor)) \ 164 | void fp_cache_ctor##name () { \ 165 | pthread_spin_init(&name.lock, 0); } 166 | 167 | /** fp_one_d_cache_check() -- check if mpf_t value is in the cache 168 | * If there is a cached value, this returns the precision of the 169 | * value in the cache; else it returns zero. 170 | * This assumes a 1-dimensional cache layout (simple array) 171 | */ 172 | int fp_one_d_cache_check (fp_cache *c, unsigned int n); 173 | 174 | /** 175 | * fp_one_d_cache_fetch - fetch value from cache 176 | */ 177 | static inline void fp_one_d_cache_fetch (fp_cache *c, mpf_t val, unsigned int n) 178 | { 179 | pthread_spin_lock(&c->lock); 180 | mpf_set(val, c->cache[n]); 181 | pthread_spin_unlock(&c->lock); 182 | } 183 | 184 | /** 185 | * fp_one_d_cache_store - store value in cache 186 | */ 187 | static inline void fp_one_d_cache_store (fp_cache *c, const mpf_t val, unsigned int n, int prec) 188 | { 189 | pthread_spin_lock(&c->lock); 190 | mpf_set_prec (c->cache[n], 3.22*prec+50); 191 | mpf_set (c->cache[n], val); 192 | c->precision[n] = prec; 193 | pthread_spin_unlock(&c->lock); 194 | } 195 | 196 | void fp_one_d_cache_clear (fp_cache *c); 197 | 198 | /* ======================================================================= */ 199 | /** fp_triangle_cache_check() -- check if mpf_t value is in the cache 200 | * If there is a cached value, this returns the precision of the 201 | * value in the cache; else it returns zero. 202 | * This assumes a trianglular cache layout (two indecies) 203 | * with 0 <= k <=n 204 | */ 205 | int fp_triangle_cache_check (fp_cache *c, unsigned int n, unsigned int k); 206 | 207 | /** 208 | * fp_triangle_cache_fetch - fetch value from cache 209 | */ 210 | static inline void fp_triangle_cache_fetch (fp_cache *c, mpf_t val, unsigned int n, unsigned int k) 211 | { 212 | unsigned int idx = n * (n+1) /2 ; 213 | mpf_set (val, c->cache[idx+k]); 214 | } 215 | 216 | /** 217 | * fp_triangle_cache_store - store value in cache 218 | */ 219 | static inline void fp_triangle_cache_store (fp_cache *c, const mpf_t val, 220 | unsigned int n, unsigned int k, int prec) 221 | { 222 | unsigned int idx = n * (n+1) /2 ; 223 | mpf_set_prec (c->cache[idx+k], 3.22*prec+50); 224 | mpf_set (c->cache[idx+k], val); 225 | c->precision[idx+k] = prec; 226 | } 227 | 228 | /* ======================================================================= */ 229 | /* Cache management */ 230 | /* A cut-n-paste of above, but using cpx instead */ 231 | 232 | typedef struct 233 | { 234 | unsigned int nmax; 235 | cpx_t *cache; 236 | int *precision; /* base-10 precision of cached value */ 237 | pthread_spinlock_t lock; 238 | } cpx_cache; 239 | 240 | 241 | #define DECLARE_CPX_CACHE(name) \ 242 | static cpx_cache name = {.nmax=0, .cache=NULL, .precision=NULL, }; \ 243 | __attribute__((constructor)) \ 244 | void cpx_cache_ctor##name () { \ 245 | pthread_spin_init(&name.lock, 0); } 246 | 247 | /** cpx_one_d_cache_check() -- check if cpx_t value is in the cache 248 | * If there is a cached value, this returns the precision of the 249 | * value in the cache; else it returns zero. 250 | * This assumes a 1-dimensional cache layout (simple array) 251 | */ 252 | int cpx_one_d_cache_check (cpx_cache *c, unsigned int n); 253 | 254 | /** 255 | * cpx_one_d_cache_fetch - fetch value from cache 256 | */ 257 | static inline void cpx_one_d_cache_fetch (cpx_cache *c, cpx_t val, unsigned int n) 258 | { 259 | pthread_spin_lock(&c->lock); 260 | cpx_set(val, c->cache[n]); 261 | pthread_spin_unlock(&c->lock); 262 | } 263 | 264 | /** 265 | * cpx_one_d_cache_store - store value in cache 266 | */ 267 | static inline void cpx_one_d_cache_store (cpx_cache *c, const cpx_t val, unsigned int n, int prec) 268 | { 269 | pthread_spin_lock(&c->lock); 270 | cpx_set_prec (c->cache[n], 3.22*prec+50); 271 | cpx_set (c->cache[n], val); 272 | c->precision[n] = prec; 273 | pthread_spin_unlock(&c->lock); 274 | } 275 | 276 | void cpx_one_d_cache_clear (cpx_cache *c); 277 | 278 | /* =============================== END OF FILE =========================== */ 279 | -------------------------------------------------------------------------------- /src/mp-complex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-complex.h 3 | * 4 | * Utility routines for handling complex values 5 | * 6 | * Copyright (C) 2006 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #include 25 | 26 | #ifndef __MP_COMPLEX_H__ 27 | #define __MP_COMPLEX_H__ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | typedef struct { 34 | mpf_t re; 35 | mpf_t im; 36 | } __cpx_struct; 37 | 38 | typedef __cpx_struct cpx_t[1]; 39 | 40 | static inline void cpx_init (cpx_t z) 41 | { 42 | mpf_init (z[0].re); 43 | mpf_init (z[0].im); 44 | } 45 | 46 | static inline void cpx_init2 (cpx_t z, mp_bitcnt_t bits) 47 | { 48 | mpf_init2 (z[0].re, bits); 49 | mpf_init2 (z[0].im, bits); 50 | } 51 | 52 | static inline void cpx_clear (cpx_t z) 53 | { 54 | mpf_clear (z[0].re); 55 | mpf_clear (z[0].im); 56 | } 57 | 58 | static inline void cpx_set (cpx_t z, const cpx_t y) 59 | { 60 | mpf_set (z[0].re, y[0].re); 61 | mpf_set (z[0].im, y[0].im); 62 | } 63 | 64 | static inline void cpx_set_ui (cpx_t z, unsigned long x, unsigned long y) 65 | { 66 | mpf_set_ui (z[0].re, x); 67 | mpf_set_ui (z[0].im, y); 68 | } 69 | 70 | static inline void cpx_set_d (cpx_t z, double x, double y) 71 | { 72 | mpf_set_d (z[0].re, x); 73 | mpf_set_d (z[0].im, y); 74 | } 75 | 76 | static inline void cpx_set_mpf (cpx_t z, const mpf_t x, const mpf_t y) 77 | { 78 | mpf_set (z[0].re, x); 79 | mpf_set (z[0].im, y); 80 | } 81 | 82 | static inline void cpx_add (cpx_t sum, const cpx_t a, const cpx_t b) 83 | { 84 | mpf_add (sum[0].re, a[0].re, b[0].re); 85 | mpf_add (sum[0].im, a[0].im, b[0].im); 86 | } 87 | 88 | static inline void cpx_add_d (cpx_t sum, const cpx_t a, double rb, double ib) 89 | { 90 | mpf_t tmp; 91 | mpf_init (tmp); 92 | mpf_set_d (tmp, rb); 93 | mpf_add (sum[0].re, a[0].re, tmp); 94 | mpf_set_d (tmp, ib); 95 | mpf_add (sum[0].im, a[0].im, tmp); 96 | mpf_clear (tmp); 97 | } 98 | 99 | static inline void cpx_add_ui (cpx_t sum, const cpx_t a, unsigned long rb, unsigned long ib) 100 | { 101 | if (rb) mpf_add_ui (sum[0].re, a[0].re, rb); 102 | else mpf_set (sum[0].re, a[0].re); 103 | if (ib) mpf_add_ui (sum[0].im, a[0].im, ib); 104 | else mpf_set (sum[0].im, a[0].im); 105 | } 106 | 107 | static inline void cpx_add_mpf (cpx_t sum, const cpx_t a, const mpf_t b) 108 | { 109 | mpf_add (sum[0].re, a[0].re, b); 110 | mpf_set (sum[0].im, a[0].im); 111 | } 112 | 113 | static inline void cpx_sub (cpx_t dif, const cpx_t a, const cpx_t b) 114 | { 115 | mpf_sub (dif[0].re, a[0].re, b[0].re); 116 | mpf_sub (dif[0].im, a[0].im, b[0].im); 117 | } 118 | 119 | static inline void cpx_sub_ui (cpx_t sum, const cpx_t a, unsigned long rb, unsigned long ib) 120 | { 121 | if (rb) mpf_sub_ui (sum[0].re, a[0].re, rb); 122 | else mpf_set (sum[0].re, a[0].re); 123 | if (ib) mpf_sub_ui (sum[0].im, a[0].im, ib); 124 | else mpf_set (sum[0].im, a[0].im); 125 | } 126 | 127 | static inline void cpx_ui_sub (cpx_t sum, unsigned long ra, unsigned long ia, const cpx_t b) 128 | { 129 | mpf_ui_sub (sum[0].re, ra, b[0].re); 130 | mpf_ui_sub (sum[0].im, ia, b[0].im); 131 | } 132 | 133 | static inline void cpx_sub_mpf (cpx_t sum, const cpx_t a, const mpf_t b) 134 | { 135 | mpf_sub (sum[0].re, a[0].re, b); 136 | mpf_set (sum[0].im, a[0].im); 137 | } 138 | 139 | static inline void cpx_neg (cpx_t neg, const cpx_t a) 140 | { 141 | mpf_neg (neg[0].re, a[0].re); 142 | mpf_neg (neg[0].im, a[0].im); 143 | } 144 | 145 | static inline void cpx_conj (cpx_t neg, const cpx_t a) 146 | { 147 | mpf_neg (neg[0].im, a[0].im); 148 | } 149 | 150 | /** 151 | * cpx_mul -- prod = a * b 152 | */ 153 | static inline void cpx_mul (cpx_t prod, const cpx_t a, const cpx_t b) 154 | { 155 | mp_bitcnt_t bits = mpf_get_prec(prod[0].re) + 8; 156 | mpf_t pre, pim, tmp; 157 | mpf_init2 (pre, bits); 158 | mpf_init2 (pim, bits); 159 | mpf_init2 (tmp, bits); 160 | 161 | mpf_mul (tmp, a[0].im, b[0].im); 162 | mpf_mul (pre, a[0].re, b[0].re); 163 | mpf_sub (pre, pre, tmp); 164 | 165 | mpf_mul (tmp, a[0].im, b[0].re); 166 | mpf_mul (pim, a[0].re, b[0].im); 167 | mpf_add (pim, pim, tmp); 168 | 169 | mpf_set (prod[0].re, pre); 170 | mpf_set (prod[0].im, pim); 171 | 172 | mpf_clear (pre); 173 | mpf_clear (pim); 174 | mpf_clear (tmp); 175 | } 176 | 177 | /** 178 | * cpx_times_i -- z = a*i 179 | */ 180 | static inline void cpx_times_i (cpx_t z, const cpx_t a) 181 | { 182 | mp_bitcnt_t bits = mpf_get_prec(z[0].re) + 8; 183 | mpf_t tmp; 184 | mpf_init2 (tmp, bits); 185 | mpf_set (tmp, a[0].re); 186 | mpf_neg (z[0].re, a[0].im); 187 | mpf_set (z[0].im, tmp); 188 | mpf_clear (tmp); 189 | } 190 | 191 | /** 192 | * cpx_times_mpf -- prod = a * b 193 | */ 194 | static inline void cpx_times_mpf (cpx_t prod, const cpx_t a, const mpf_t b) 195 | { 196 | mpf_mul (prod[0].re, a[0].re, b); 197 | mpf_mul (prod[0].im, a[0].im, b); 198 | } 199 | 200 | /** 201 | * cpx_times_ui -- prod = a * b 202 | */ 203 | static inline void cpx_times_ui (cpx_t prod, const cpx_t a, unsigned long b) 204 | { 205 | mpf_mul_ui (prod[0].re, a[0].re, b); 206 | mpf_mul_ui (prod[0].im, a[0].im, b); 207 | } 208 | 209 | /** 210 | * cpx_times_d -- prod = a * b 211 | */ 212 | static inline void cpx_times_d (cpx_t prod, const cpx_t a, double b) 213 | { 214 | mpf_t tmp; 215 | mpf_init (tmp); 216 | mpf_set_d (tmp, b); 217 | cpx_times_mpf(prod, a, tmp); 218 | mpf_clear (tmp); 219 | } 220 | 221 | /** 222 | * cpx_recip -- recip = 1/z 223 | */ 224 | static inline void cpx_recip (cpx_t recip, const cpx_t z) 225 | { 226 | mp_bitcnt_t bits = mpf_get_prec(recip[0].re) + 8; 227 | mpf_t mag,tmp; 228 | mpf_init2 (mag, bits); 229 | mpf_init2 (tmp, bits); 230 | mpf_mul (mag, z[0].re, z[0].re); 231 | mpf_mul (tmp, z[0].im, z[0].im); 232 | mpf_add (mag, mag, tmp); 233 | mpf_ui_div (mag, 1, mag); 234 | mpf_mul (recip[0].re, z[0].re, mag); 235 | mpf_mul (recip[0].im, z[0].im, mag); 236 | mpf_neg (recip[0].im, recip[0].im); 237 | 238 | mpf_clear (mag); 239 | mpf_clear (tmp); 240 | } 241 | 242 | /** 243 | * cpx_div -- ratio = a/b 244 | */ 245 | static inline void cpx_div (cpx_t ratio, const cpx_t a, const cpx_t b) 246 | { 247 | mp_bitcnt_t bits = mpf_get_prec(ratio[0].re) + 8; 248 | cpx_t recip; 249 | cpx_init2 (recip, bits); 250 | 251 | cpx_recip (recip, b); 252 | cpx_mul (ratio, a, recip); 253 | cpx_clear (recip); 254 | } 255 | 256 | /** 257 | * cpx_div_mpf -- ratio = a/b 258 | */ 259 | static inline void cpx_div_mpf (cpx_t ratio, const cpx_t a, const mpf_t b) 260 | { 261 | mpf_div (ratio[0].re, a[0].re, b); 262 | mpf_div (ratio[0].im, a[0].im, b); 263 | } 264 | 265 | static inline void cpx_div_ui (cpx_t ratio, const cpx_t a, unsigned long b) 266 | { 267 | mpf_div_ui (ratio[0].re, a[0].re, b); 268 | mpf_div_ui (ratio[0].im, a[0].im, b); 269 | } 270 | 271 | /** 272 | * Divide a by 2^n place result in ratio 273 | */ 274 | static inline void cpx_div_2exp (cpx_t ratio, const cpx_t a, unsigned long n) 275 | { 276 | mpf_div_2exp (ratio[0].re, a[0].re, n); 277 | mpf_div_2exp (ratio[0].im, a[0].im, n); 278 | } 279 | 280 | /** 281 | * cpx_mod_sq -- modulus squared of z 282 | */ 283 | static inline void cpx_mod_sq (mpf_t mod, const cpx_t z) 284 | { 285 | mp_bitcnt_t bits = mpf_get_prec(mod) + 8; 286 | mpf_t tmp; 287 | mpf_init2 (tmp, bits); 288 | 289 | mpf_mul (tmp, z[0].im, z[0].im); 290 | mpf_mul (mod, z[0].re, z[0].re); 291 | mpf_add (mod, mod, tmp); 292 | mpf_clear (tmp); 293 | } 294 | 295 | /** 296 | * cpx_abs -- Absolute value of z (modulus of z) 297 | */ 298 | static inline void cpx_abs (mpf_t mod, const cpx_t z) 299 | { 300 | cpx_mod_sq (mod, z); 301 | mpf_sqrt (mod, mod); 302 | } 303 | 304 | /** 305 | * Return true if the first nbits of both the imaginary and real parts are equal 306 | */ 307 | static inline int cpx_eq (const cpx_t a, const cpx_t b, unsigned int nbits) 308 | { 309 | return (mpf_eq(a[0].re, b[0].re, nbits)) && (mpf_eq(a[0].im, b[0].im, nbits)); 310 | } 311 | 312 | /** 313 | * Get the real, imaginary parts, as double-precision floats 314 | */ 315 | static inline double cpx_get_re (const cpx_t z) 316 | { 317 | return mpf_get_d(z[0].re); 318 | } 319 | 320 | static inline double cpx_get_im (const cpx_t z) 321 | { 322 | return mpf_get_d(z[0].im); 323 | } 324 | 325 | 326 | static inline void cpx_set_prec (cpx_t a, unsigned int nbits) 327 | { 328 | mpf_set_prec (a[0].re, nbits); 329 | mpf_set_prec (a[0].im, nbits); 330 | } 331 | 332 | #ifdef __cplusplus 333 | }; 334 | #endif 335 | 336 | #endif /* __MP_COMPLEX_H__ */ 337 | -------------------------------------------------------------------------------- /src/mp-consts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-consts.c 3 | * 4 | * High-precison constants, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005, 2006 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include "mp-binomial.h" 33 | #include "mp-complex.h" 34 | #include "mp-consts.h" 35 | #include "mp-misc.h" 36 | #include "mp-trig.h" 37 | #include "mp-zeta.h" 38 | 39 | /* ======================================================================= */ 40 | // multi-threading locks. 41 | // All the constants share one lock, there should be no contention. 42 | 43 | static pthread_spinlock_t mp_const_lock; 44 | static pthread_spinlock_t mp_pi_lock; 45 | static pthread_spinlock_t mp_euler_lock; 46 | static pthread_spinlock_t mp_zeta_lock; 47 | __attribute__((constructor)) void fp_e_ctor(void) 48 | { 49 | pthread_spin_init(&mp_const_lock, PTHREAD_PROCESS_PRIVATE); 50 | pthread_spin_init(&mp_pi_lock, PTHREAD_PROCESS_PRIVATE); 51 | pthread_spin_init(&mp_euler_lock, PTHREAD_PROCESS_PRIVATE); 52 | pthread_spin_init(&mp_zeta_lock, PTHREAD_PROCESS_PRIVATE); 53 | } 54 | 55 | /* ======================================================================= */ 56 | /** 57 | * fp_half_sqrt_three - return 0.5*sqrt(3)= 0.86602... 58 | */ 59 | void fp_half_sqrt_three (mpf_t sqt, unsigned int prec) 60 | { 61 | static unsigned int precision=0; 62 | static mpf_t cached_sqt; 63 | 64 | pthread_spin_lock(&mp_const_lock); 65 | if (precision >= prec) 66 | { 67 | mpf_set (sqt, cached_sqt); 68 | pthread_spin_unlock(&mp_const_lock); 69 | return; 70 | } 71 | 72 | if (0 == precision) 73 | { 74 | mpf_init (cached_sqt); 75 | } 76 | 77 | mp_bitcnt_t bits = ((double) prec) * 3.322 + 50; 78 | mpf_set_prec (cached_sqt, bits); 79 | 80 | mpf_set_ui (sqt, 3); 81 | mpf_sqrt (sqt, sqt); 82 | mpf_div_ui (sqt, sqt, 2); 83 | mpf_set (cached_sqt, sqt); 84 | 85 | precision = prec; 86 | pthread_spin_unlock(&mp_const_lock); 87 | } 88 | 89 | /* ======================================================================= */ 90 | /** 91 | * fp_e - return e=2.718281828... 92 | * @prec - number of decimal places of precision 93 | * 94 | * Uses simple, brute-force summation 95 | */ 96 | 97 | extern void fp_exp_helper (mpf_t ex, const mpf_t z, unsigned int prec); 98 | 99 | void fp_e (mpf_t e, unsigned int prec) 100 | { 101 | static unsigned int precision=0; 102 | static mpf_t cached_e; 103 | 104 | pthread_spin_lock(&mp_const_lock); 105 | if (precision >= prec) 106 | { 107 | mpf_set (e, cached_e); 108 | pthread_spin_unlock(&mp_const_lock); 109 | return; 110 | } 111 | 112 | if (0 == precision) 113 | { 114 | mpf_init (cached_e); 115 | } 116 | 117 | mp_bitcnt_t bits = ((double) prec) * 3.322 + 50; 118 | mpf_set_prec (cached_e, bits); 119 | 120 | mpf_t one; 121 | mpf_init2 (one, bits); 122 | mpf_set_ui (one, 1); 123 | fp_exp_helper (cached_e, one, prec); 124 | mpf_set (e, cached_e); 125 | 126 | mpf_clear (one); 127 | precision = prec; 128 | pthread_spin_unlock(&mp_const_lock); 129 | } 130 | 131 | /* ======================================================================= */ 132 | /** 133 | * fp_pi - return pi=3.14159... 134 | * @prec - number of decimal places of precision 135 | * 136 | * Uses simple, brute-force Machin formula 137 | */ 138 | void fp_pi (mpf_t pi, unsigned int prec) 139 | { 140 | static unsigned int precision=0; 141 | static mpf_t cached_pi; 142 | 143 | pthread_spin_lock(&mp_pi_lock); 144 | if (precision >= prec) 145 | { 146 | mpf_set (pi, cached_pi); 147 | pthread_spin_unlock(&mp_pi_lock); 148 | return; 149 | } 150 | 151 | if (0 == precision) 152 | { 153 | mpf_init (cached_pi); 154 | } 155 | mp_bitcnt_t bits = ((double) prec) * 3.322 + 50; 156 | mpf_set_prec (cached_pi, bits); 157 | 158 | /* Simple-minded Machin formula */ 159 | mpf_t tmp; 160 | mpf_init2 (tmp, bits); 161 | mpf_set_ui (tmp, 1); 162 | mpf_div_ui (tmp, tmp, 5); 163 | fp_arctan (pi, tmp, prec); 164 | 165 | mpf_mul_ui (pi, pi, 4); 166 | 167 | mpf_set_ui (tmp, 1); 168 | mpf_div_ui (tmp, tmp, 239); 169 | fp_arctan (tmp, tmp, prec); 170 | 171 | mpf_sub (pi, pi, tmp); 172 | mpf_mul_ui (pi, pi, 4); 173 | mpf_clear (tmp); 174 | 175 | mpf_set (cached_pi, pi); 176 | precision = prec; 177 | pthread_spin_unlock(&mp_pi_lock); 178 | } 179 | 180 | /* ======================================================================= */ 181 | /** 182 | * fp_two_pi - return 2pi = 2 * 3.14159... 183 | * @prec - number of decimal places of precision 184 | * 185 | * The idea is that it caches the value to avoid recomputation 186 | */ 187 | void fp_two_pi (mpf_t two_pi, unsigned int prec) 188 | { 189 | static unsigned int precision=0; 190 | static mpf_t cached_two_pi; 191 | 192 | pthread_spin_lock(&mp_const_lock); 193 | if (precision >= prec) 194 | { 195 | mpf_set (two_pi, cached_two_pi); 196 | pthread_spin_unlock(&mp_const_lock); 197 | return; 198 | } 199 | 200 | if (0 == precision) 201 | { 202 | mpf_init (cached_two_pi); 203 | } 204 | mpf_set_prec (cached_two_pi, 3.322*prec +50); 205 | 206 | fp_pi (two_pi, prec); 207 | mpf_mul_ui (two_pi, two_pi, 2); 208 | mpf_set (cached_two_pi, two_pi); 209 | precision = prec; 210 | pthread_spin_unlock(&mp_const_lock); 211 | } 212 | 213 | /* ======================================================================= */ 214 | /** 215 | * fp_two_over_pi - return 2/pi = 2 / 3.14159... 216 | * @prec - number of decimal places of precision 217 | * 218 | * The idea is that it caches the value to avoid recomputation 219 | */ 220 | void fp_two_over_pi (mpf_t two_over_pi, unsigned int prec) 221 | { 222 | static unsigned int precision=0; 223 | static mpf_t cached_two_over_pi; 224 | 225 | pthread_spin_lock(&mp_const_lock); 226 | if (precision >= prec) 227 | { 228 | mpf_set (two_over_pi, cached_two_over_pi); 229 | pthread_spin_unlock(&mp_const_lock); 230 | return; 231 | } 232 | 233 | if (0 == precision) 234 | { 235 | mpf_init (cached_two_over_pi); 236 | } 237 | mpf_set_prec (cached_two_over_pi, 3.322*prec +50); 238 | 239 | fp_pi (two_over_pi, prec); 240 | mpf_ui_div (two_over_pi, 2, two_over_pi); 241 | mpf_set (cached_two_over_pi, two_over_pi); 242 | precision = prec; 243 | pthread_spin_unlock(&mp_const_lock); 244 | } 245 | 246 | /* ======================================================================= */ 247 | /** 248 | * fp_pi_half - return pi/2 = 0.5 * 3.14159... 249 | * @prec - number of decimal places of precision 250 | * 251 | * The idea is that it caches the value to avoid recomputation 252 | */ 253 | void fp_pi_half (mpf_t pih, unsigned int prec) 254 | { 255 | static unsigned int precision=0; 256 | static mpf_t cached_pih; 257 | 258 | pthread_spin_lock(&mp_const_lock); 259 | if (precision >= prec) 260 | { 261 | mpf_set (pih, cached_pih); 262 | pthread_spin_unlock(&mp_const_lock); 263 | return; 264 | } 265 | 266 | if (0 == precision) 267 | { 268 | mpf_init (cached_pih); 269 | } 270 | mpf_set_prec (cached_pih, 3.322*prec +50); 271 | 272 | fp_pi (pih, prec); 273 | mpf_div_ui (pih, pih, 2); 274 | mpf_set (cached_pih, pih); 275 | precision = prec; 276 | pthread_spin_unlock(&mp_const_lock); 277 | } 278 | 279 | /* ======================================================================= */ 280 | /** 281 | * fp_sqrt_two_pi - return sqrt(2pi) = sqrt (2 * 3.14159...) 282 | * @prec - number of decimal places of precision 283 | * 284 | * The idea is that it caches the value to avoid recomputation 285 | */ 286 | void fp_sqrt_two_pi (mpf_t sqtpi, unsigned int prec) 287 | { 288 | static unsigned int precision=0; 289 | static mpf_t cached_sqtpi; 290 | 291 | pthread_spin_lock(&mp_const_lock); 292 | if (precision >= prec) 293 | { 294 | mpf_set (sqtpi, cached_sqtpi); 295 | pthread_spin_unlock(&mp_const_lock); 296 | return; 297 | } 298 | 299 | if (0 == precision) 300 | { 301 | mpf_init (cached_sqtpi); 302 | } 303 | mpf_set_prec (cached_sqtpi, 3.322*prec +50); 304 | 305 | fp_two_pi (sqtpi, prec); 306 | mpf_sqrt (sqtpi, sqtpi); 307 | mpf_set (cached_sqtpi, sqtpi); 308 | precision = prec; 309 | pthread_spin_unlock(&mp_const_lock); 310 | } 311 | 312 | /* ======================================================================= */ 313 | /** 314 | * fp_log_two_pi - return log(2pi) = log(2 * 3.14159...) 315 | * @prec - number of decimal places of precision 316 | * 317 | * The idea is that it caches the value to avoid recomputation 318 | */ 319 | void fp_log_two_pi (mpf_t ltp, unsigned int prec) 320 | { 321 | static unsigned int precision=0; 322 | static mpf_t cached_ltp; 323 | 324 | pthread_spin_lock(&mp_const_lock); 325 | if (precision >= prec) 326 | { 327 | mpf_set (ltp, cached_ltp); 328 | pthread_spin_unlock(&mp_const_lock); 329 | return; 330 | } 331 | 332 | if (0 == precision) 333 | { 334 | mpf_init (cached_ltp); 335 | } 336 | mpf_set_prec (cached_ltp, 3.322*prec +50); 337 | 338 | fp_two_pi (ltp, prec); 339 | fp_log (ltp, ltp, prec); 340 | mpf_set (cached_ltp, ltp); 341 | precision = prec; 342 | pthread_spin_unlock(&mp_const_lock); 343 | } 344 | 345 | /* ======================================================================= */ 346 | /** 347 | * fp_log2 - return log(2)=0.69... 348 | * @prec - number of decimal places of precision 349 | * 350 | * Uses simple, brute-force summation 351 | */ 352 | 353 | void fp_log2 (mpf_t l2, unsigned int prec) 354 | { 355 | static unsigned int precision=0; 356 | static mpf_t cached_log2; 357 | 358 | pthread_spin_lock(&mp_const_lock); 359 | if (precision >= prec) 360 | { 361 | mpf_set (l2, cached_log2); 362 | pthread_spin_unlock(&mp_const_lock); 363 | return; 364 | } 365 | 366 | if (0 == precision) 367 | { 368 | mpf_init (cached_log2); 369 | } 370 | mp_bitcnt_t bits = ((double) prec) * 3.322 + 50; 371 | mpf_set_prec (cached_log2, bits); 372 | 373 | mpf_t two; 374 | mpf_init2 (two, bits); 375 | mpf_set_ui (two, 2); 376 | fp_log (cached_log2, two, prec); 377 | mpf_set (l2, cached_log2); 378 | 379 | mpf_clear (two); 380 | precision = prec; 381 | pthread_spin_unlock(&mp_const_lock); 382 | } 383 | 384 | /* ======================================================================= */ 385 | /** 386 | * fp_e_pi - return e^pi 387 | * @prec - number of decimal places of precision 388 | * 389 | * Uses simple, low-brow formula 390 | */ 391 | void fp_e_pi (mpf_t e_pi, unsigned int prec) 392 | { 393 | static unsigned int precision=0; 394 | static mpf_t cached_e_pi; 395 | 396 | pthread_spin_lock(&mp_const_lock); 397 | if (precision >= prec) 398 | { 399 | mpf_set (e_pi, cached_e_pi); 400 | pthread_spin_unlock(&mp_const_lock); 401 | return; 402 | } 403 | 404 | if (0 == precision) 405 | { 406 | mpf_init (cached_e_pi); 407 | } 408 | mpf_set_prec (cached_e_pi, 3.322*prec +50); 409 | 410 | fp_pi (e_pi, prec); 411 | fp_exp (e_pi, e_pi, prec); 412 | 413 | mpf_set (cached_e_pi, e_pi); 414 | precision = prec; 415 | pthread_spin_unlock(&mp_const_lock); 416 | } 417 | 418 | 419 | /* ======================================================================= */ 420 | /** 421 | * fp_euler - return Euler-Mascheroni const 422 | * @prec - number of decimal places of precision 423 | * 424 | */ 425 | static void fp_euler_mascheroni_limit (mpf_t gam, unsigned int n, unsigned int prec) 426 | { 427 | mp_bitcnt_t bits = ((double) prec) * 3.322 + 50; 428 | mpf_t maxterm; 429 | mpf_init2 (maxterm, bits); 430 | mpf_set_ui (maxterm, 1); 431 | 432 | mpf_t z_n, twon, term, tmp, fact; 433 | mpf_init2 (z_n, bits); 434 | mpf_init2 (twon, bits); 435 | mpf_init2 (term, bits); 436 | mpf_init2 (tmp, bits); 437 | mpf_init2 (fact, bits); 438 | mpf_set_ui (twon, 1); 439 | mpf_mul_2exp(twon, twon, n); 440 | mpf_mul (z_n, twon, twon); 441 | mpf_set_ui (fact, 1); 442 | mpf_div_ui (fact, fact, 2); 443 | mpf_set (gam, twon); 444 | 445 | /* The k=1 term is handled above in init */ 446 | unsigned int k=2; 447 | while (1) 448 | { 449 | fp_harmonic (tmp, k, prec); 450 | mpf_mul (term, z_n, tmp); 451 | mpf_mul (term, term, fact); 452 | 453 | mpf_add (gam, gam, term); 454 | 455 | /* XXX in fact, we can terminate this sum a lot earlier, 456 | * if we wanted to, by comparing the sum to the number of 457 | * desired digits ... */ 458 | /* Don't go no farther than this */ 459 | if (mpf_cmp (term, maxterm) < 0) break; 460 | 461 | k ++; 462 | mpf_mul (z_n, z_n, twon); 463 | mpf_div_ui (fact, fact, k); 464 | } 465 | 466 | fp_exp (tmp, twon, prec); 467 | mpf_div (gam, gam, tmp); 468 | 469 | fp_log2 (tmp, prec); 470 | mpf_mul_ui (tmp, tmp, n); 471 | mpf_sub (gam, gam, tmp); 472 | 473 | mpf_clear (z_n); 474 | mpf_clear (twon); 475 | mpf_clear (term); 476 | mpf_clear (tmp); 477 | mpf_clear (fact); 478 | 479 | mpf_clear (maxterm); 480 | } 481 | 482 | static void fp_euler_mascheroni_compute (mpf_t gam, unsigned int prec) 483 | { 484 | /* power value, goes as log log n */ 485 | // double en = log (prec*log(10)) / log (2.0); 486 | double en = log (prec*3.322); 487 | // en = 1.442695041 * (en - log (en)); 488 | en = 1.442695041 * en; 489 | int n = (int) (en+1.0); 490 | fp_euler_mascheroni_limit (gam, n, prec); 491 | } 492 | 493 | void fp_euler_mascheroni (mpf_t gam, unsigned int prec) 494 | { 495 | static unsigned int precision=0; 496 | static mpf_t cached_gam; 497 | 498 | pthread_spin_lock(&mp_euler_lock); 499 | if (precision >= prec) 500 | { 501 | mpf_set (gam, cached_gam); 502 | pthread_spin_unlock(&mp_euler_lock); 503 | return; 504 | } 505 | 506 | if (0 == precision) 507 | { 508 | mpf_init (cached_gam); 509 | } 510 | mpf_set_prec (cached_gam, 3.322*prec +50); 511 | 512 | fp_euler_mascheroni_compute (gam, prec); 513 | mpf_set (cached_gam, gam); 514 | precision = prec; 515 | pthread_spin_unlock(&mp_euler_lock); 516 | } 517 | 518 | /* ======================================================================= */ 519 | /** 520 | * fp_zeta_half - return zeta (1/2) 521 | * @prec - number of decimal places of precision 522 | * 523 | */ 524 | static void fp_zeta_half_compute (mpf_t gam, unsigned int prec) 525 | { 526 | cpx_t ess, zeta; 527 | cpx_init (ess); 528 | cpx_init (zeta); 529 | 530 | mpf_set_d (ess[0].re, 0.5); 531 | mpf_set_ui (ess[0].im, 0); 532 | 533 | cpx_borwein_zeta (zeta, ess, prec); 534 | mpf_set (gam, zeta[0].re); 535 | 536 | cpx_clear (ess); 537 | cpx_clear (zeta); 538 | } 539 | 540 | void fp_zeta_half (mpf_t gam, unsigned int prec) 541 | { 542 | static unsigned int precision=0; 543 | static mpf_t cached_gam; 544 | 545 | pthread_spin_lock(&mp_zeta_lock); 546 | if (precision >= prec) 547 | { 548 | mpf_set (gam, cached_gam); 549 | pthread_spin_unlock(&mp_zeta_lock); 550 | return; 551 | } 552 | 553 | if (0 == precision) 554 | { 555 | mpf_init (cached_gam); 556 | } 557 | mpf_set_prec (cached_gam, 3.322*prec +50); 558 | 559 | fp_zeta_half_compute (gam, prec); 560 | mpf_set (cached_gam, gam); 561 | precision = prec; 562 | pthread_spin_unlock(&mp_zeta_lock); 563 | } 564 | 565 | /* =============================== END OF FILE =========================== */ 566 | -------------------------------------------------------------------------------- /src/mp-consts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-consts.h 3 | * 4 | * High-precison constants, using the 5 | * Gnu Multiple-precision library. 6 | * Uses caching so as to speed up subsequent derefs. 7 | * 8 | * Copyright (C) 2005 Linas Vepstas 9 | * 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU Lesser General Public 12 | * License as published by the Free Software Foundation; either 13 | * version 2.1 of the License, or (at your option) any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * Lesser General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Lesser General Public 21 | * License along with this library; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 23 | * 02110-1301 USA 24 | */ 25 | 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * fp_half_sqrt_three - return sqrt(3)/2 = 0.86602... 34 | */ 35 | void fp_half_sqrt_three (mpf_t sqt, unsigned int prec); 36 | 37 | /** 38 | * fp_e - return e=2.718281828... 39 | * @prec - number of decimal places of precision 40 | * 41 | * Uses simple, brute-force summation 42 | */ 43 | void fp_e (mpf_t e, unsigned int prec); 44 | 45 | /** 46 | * fp_pi - return pi=3.14159... 47 | * @prec - number of decimal places of precision 48 | * 49 | * Uses simple, brute-force Machin formula 50 | */ 51 | void fp_pi (mpf_t pi, unsigned int prec); 52 | void fp_two_pi (mpf_t pi, unsigned int prec); 53 | void fp_pi_half (mpf_t pihalf, unsigned int prec); 54 | void fp_sqrt_two_pi (mpf_t sqtpi, unsigned int prec); 55 | void fp_log_two_pi (mpf_t ltpi, unsigned int prec); 56 | void fp_two_over_pi (mpf_t tpi, unsigned int prec); 57 | 58 | /** 59 | * fp_e_pi - return e^pi 60 | * @prec - number of decimal places of precision 61 | * 62 | * Uses simple, low-brow formula 63 | */ 64 | void fp_e_pi (mpf_t e_pi, unsigned int prec); 65 | 66 | /** 67 | * fp_log2 - return log(2)=0.693147181... 68 | * @prec - number of decimal places of precision 69 | * 70 | * Uses simple, brute-force summation 71 | */ 72 | void fp_log2 (mpf_t l2, unsigned int prec); 73 | 74 | /** 75 | * fp_euler - return Euler-Mascheroni const 76 | * @prec - number of decimal places of precision 77 | * 78 | */ 79 | void fp_euler_mascheroni (mpf_t gam, unsigned int prec); 80 | 81 | /** 82 | * fp_zeta_half - return zeta(1/2) 83 | * @prec - number of decimal places of precision 84 | * 85 | * Current algorithm is terribly slow. 86 | */ 87 | void fp_zeta_half (mpf_t zeta, unsigned int prec); 88 | 89 | #ifdef __cplusplus 90 | }; 91 | #endif 92 | 93 | -------------------------------------------------------------------------------- /src/mp-euler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-euler.c 3 | * 4 | * Euler resummation of slowly convergent (alternating) sequences. 5 | * 6 | * Copyright (C) 2019 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include "mp-binomial.h" 28 | #include "mp-euler.h" 29 | 30 | unsigned int cpx_euler_sum(cpx_t result, 31 | void (*func)(cpx_t, unsigned long, int), 32 | unsigned int ndigits, 33 | unsigned int maxterms, 34 | int nprec) 35 | { 36 | mp_bitcnt_t bits = ((double) nprec) * 3.322 + 50; 37 | 38 | mpf_t fbin; 39 | mpf_init2(fbin, bits); 40 | 41 | cpx_t term, fval; 42 | cpx_init2(term, bits); 43 | cpx_init2(fval, bits); 44 | 45 | mpz_t bin; 46 | mpz_init(bin); 47 | 48 | /* Compute desired accuracy. This is used as a termination 49 | * condition. We ask for twice-as-many digits, since we 50 | * square the term. 51 | */ 52 | mpf_t epsi; 53 | mpf_init(epsi); 54 | mpf_set_ui(epsi, 1); 55 | mpf_div_2exp(epsi, epsi, (int)(2 * 3.322*ndigits)); 56 | 57 | mpf_t asum, aterm; 58 | mpf_init(asum); 59 | mpf_init(aterm); 60 | 61 | // zero out; we're accumulating into this 62 | cpx_set_ui(result, 0, 0); 63 | 64 | bool almost_done = false; 65 | int n = 0; 66 | for (; n mpf_cmp(aterm, epsi)) 90 | { 91 | if (almost_done) break; 92 | almost_done = true; 93 | } 94 | else almost_done = false; 95 | } 96 | 97 | mpf_clear(asum); 98 | mpf_clear(aterm); 99 | mpf_clear(epsi); 100 | 101 | mpz_clear(bin); 102 | cpx_clear(term); 103 | cpx_clear(fval); 104 | 105 | mpf_clear(fbin); 106 | return n; 107 | } 108 | 109 | // ============================================================ 110 | 111 | unsigned int cpx_newton_series(cpx_t result, 112 | void (*func)(cpx_t, unsigned long, int), 113 | cpx_t zee, 114 | unsigned int ndigits, 115 | unsigned int maxterms, 116 | int nprec) 117 | { 118 | mp_bitcnt_t bits = ((double) nprec) * 3.322 + 50; 119 | 120 | mpf_t fbin; 121 | mpf_init2(fbin, bits); 122 | 123 | cpx_t term, fval, zeem1, zbin; 124 | cpx_init2(term, bits); 125 | cpx_init2(fval, bits); 126 | cpx_init2(zeem1, bits); 127 | cpx_init2(zbin, bits); 128 | 129 | // zeem1 = z-1 130 | cpx_set(zeem1, zee); 131 | cpx_sub_ui(zeem1, zeem1, 1, 0); 132 | 133 | mpz_t bin; 134 | mpz_init(bin); 135 | 136 | mpf_t asum, aterm; 137 | mpf_init(asum); 138 | mpf_init(aterm); 139 | 140 | /* Compute desired accuracy. This is used as a termination 141 | * condition. We ask for twice-as-many digits, since we 142 | * square the term. 143 | */ 144 | mpf_t epsi; 145 | mpf_init(epsi); 146 | mpf_set_ui(epsi, 1); 147 | mpf_div_2exp(epsi, epsi, (int)(2 * 3.322*ndigits)); 148 | 149 | // For integer values of zee equal to n, the binomial coefficient 150 | // will be exacly zero. We want to break out of the loop in such 151 | // cases. So... is zee an integer, and if so, which? 152 | int is_int = 0; 153 | if (0 > mpf_cmp(zeem1[0].im, epsi)) 154 | { 155 | mpf_set_d(aterm, 0.5); 156 | mpf_add(aterm, zeem1[0].re, aterm); 157 | mpf_floor(aterm, aterm); 158 | mpf_sub(asum, zeem1[0].re, aterm); 159 | mpf_mul(asum, asum, asum); // square to remove sign 160 | if (0 > mpf_cmp(asum, epsi)) 161 | { 162 | is_int = mpf_get_ui(aterm); 163 | } 164 | } 165 | 166 | cpx_set_ui(result, 0, 0); 167 | 168 | bool almost_done = false; 169 | int n = 0; 170 | for (; n mpf_cmp(aterm, epsi)) 202 | { 203 | if (almost_done) break; 204 | almost_done = true; 205 | } 206 | else almost_done = false; 207 | } 208 | 209 | mpf_clear(asum); 210 | mpf_clear(aterm); 211 | mpf_clear(epsi); 212 | 213 | mpz_clear(bin); 214 | cpx_clear(zeem1); 215 | cpx_clear(term); 216 | cpx_clear(fval); 217 | cpx_clear(zbin); 218 | mpf_clear(fbin); 219 | return n; 220 | } 221 | 222 | // ============================================================ 223 | // #define EULER_TEST 224 | #ifdef EULER_TEST 225 | 226 | // Quick-n-dirty unit test. Just sum $ sum_{n=0}^\infty x^n $ 227 | 228 | #include 229 | 230 | // Return x^n 231 | mpf_t ex; 232 | void test_func(cpx_t f, unsigned long n, int nprec) 233 | { 234 | mpf_pow_ui(f[0].re, ex, n-1); 235 | mpf_set_ui(f[0].im, 0); 236 | } 237 | 238 | 239 | int main (int argc, char * argv[]) 240 | { 241 | int prec, nbits; 242 | prec = 120; 243 | nbits = 3.3*prec; 244 | mpf_set_default_prec (nbits+200); 245 | 246 | double xx = 0.95; 247 | mpf_init2(ex, nbits); 248 | mpf_set_d(ex, xx); 249 | 250 | cpx_t result; 251 | cpx_init2(result, nbits); 252 | 253 | unsigned int nterm; 254 | nterm = cpx_euler_sum(result, test_func, 60, 30000000, prec); 255 | 256 | printf("summed to %u terms\n", nterm); 257 | 258 | mpf_t sum; 259 | mpf_init(sum); 260 | cpx_abs(sum, result); 261 | printf("got=%f expected=%f\n", mpf_get_d(sum), 1.0/(1.0-xx)); 262 | } 263 | #endif 264 | 265 | // ============================================================ 266 | 267 | // #define NEWTON_TEST 268 | #ifdef NEWTON_TEST 269 | 270 | // Quick-n-dirty unit test. Verify interplants. 271 | 272 | #include 273 | 274 | // Return x^n 275 | mpf_t ex; 276 | void test_func(cpx_t f, unsigned long n, int nprec) 277 | { 278 | mpf_pow_ui(f[0].re, ex, n-1); 279 | mpf_set_ui(f[0].im, 0); 280 | } 281 | 282 | 283 | int main (int argc, char * argv[]) 284 | { 285 | int prec, nbits; 286 | prec = 120; 287 | nbits = 3.3*prec; 288 | mpf_set_default_prec (nbits+200); 289 | 290 | double xx = 0.95; 291 | mpf_init2(ex, nbits); 292 | mpf_set_d(ex, xx); 293 | 294 | cpx_t zee, result; 295 | cpx_init2(zee, nbits); 296 | cpx_init2(result, nbits); 297 | 298 | mpf_t sum; 299 | mpf_init(sum); 300 | 301 | double xn = 1.0; 302 | for (int n=1; n<20; n++) 303 | { 304 | cpx_set_ui(zee, n, 0); 305 | unsigned int nterm; 306 | nterm = cpx_newton_series(result, test_func, zee, 20, 30000000, prec); 307 | 308 | printf("summed to %u terms\n", nterm); 309 | cpx_abs(sum, result); 310 | printf("n=%d got=%f expected=%f\n", n, mpf_get_d(sum), xn); 311 | 312 | xn *= xx; 313 | } 314 | } 315 | #endif 316 | -------------------------------------------------------------------------------- /src/mp-euler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-euler.h 3 | * 4 | * Euler resummation of slowly convergent (alternating) sequences. 5 | * 6 | * Copyright (C) 2019 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #ifndef __MP_EULER_H__ 25 | #define __MP_EULER_H__ 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include "mp-complex.h" 32 | 33 | /* =============================================== */ 34 | /** 35 | * cpx_euler_sum 36 | * 37 | * Implement Euler (re-)summation (aka Euler transformation) for 38 | * complex-valued arithmetic functions. That is, given f(n) defined 39 | * on positive integers n, return 40 | * 41 | * $ sum_{n=0}^\infty 2^{-(n+1)} \sum_{k=0}^n {n \choose k} f(k+1) $ 42 | * 43 | * Note the sign convention used here: no minus signs; so that sign 44 | * alternation (if any) is in the f(n). 45 | * 46 | * See Wikipedia for more about Euler summation. 47 | * 48 | * See also Jonathan Sondow, "Analytic continuation of Riemann's zeta 49 | * function and values at negative integers via Euler's transformation 50 | * of series" (1994) Proceedings of the American Mathematical Society, 51 | * Vol. 120, pp 421-424. 52 | * http://www.ams.org/journals/proc/1994-120-02/S0002-9939-1994-1172954-7/S0002-9939-1994-1172954-7.pdf 53 | * for a particularly simple and direct statement, including a direct 54 | * application. 55 | * 56 | * The Euler transformation is an extremely simple kind of resummation. 57 | * In certain cases, it can work marvels for conditionally convergent 58 | * alternating series. Far more powerful resummation techniques exist; 59 | * see, for example, the references to P. Borwein convergent algo given 60 | * in the polylog code. 61 | * 62 | * The Euler transformation can also completely fail to accelerate 63 | * series convergence, if the series is not quite of the right form. 64 | * I am not aware of any results indicating when it works, and when it 65 | * fails, or why. Its a fairly serious question, having reprecussions 66 | * in number theory. (Viz, why the Hasse/Sondow resummation converges 67 | * rapidly and uniformly, while nearby sequences completely fail to do 68 | * so.) 69 | * 70 | * @func function providing values to be resummed. 71 | * It will only be called for positive integers. 72 | * 'nprec' is the suggested decimal precisiton at which 'fun' 73 | * should perform its calculations. 74 | * @ndigits Terminate summation when each term contributes less than 75 | * 10^ndigits to the sum. This is approximately that accuracy 76 | * that will be attained. Roughly. 77 | * @maxterms Maximum number of terms to sum. If few terms are needed 78 | * to reach the desired precision, that's fine; summation will 79 | * stop. Otherwise, is summation has not converged, it will be 80 | * terminated at `maxterms`. 81 | * @nprec number of digits of decimal precision to which intermediate 82 | * terms will be maintained. 83 | * 84 | * @returns number of terms actually summed. 85 | */ 86 | unsigned int cpx_euler_sum(cpx_t result, 87 | void (*func)(cpx_t f, unsigned long p, int nprec), 88 | unsigned int ndigits, 89 | unsigned int maxterms, 90 | int nprec); 91 | 92 | /* =============================================== */ 93 | /** 94 | * cpx_newton_series 95 | * 96 | * Implement a Newton series interpolation for complex-valued arithmetic 97 | * functions. That is, given f(n) defined on positive integers n, return 98 | * 99 | * $ f(z) = sum_{n=0}^\infty (-1)^n {z-1 \choose n} 100 | * \sum_{k=0}^n (-1)^k {n \choose k} f(k+1) $ 101 | * 102 | * Note the sign convention above: there are two sources of alternating 103 | * signs. 104 | * 105 | * See Wikipedia for more about finite differences and Newton series. 106 | * 107 | * See also Philippe Flajolet and Robert Sedgewick, "Mellin Transforms 108 | * and Asymptotics: Finite Differences and Rice's Integrals" (1995) 109 | * Theoretical Computer Science, vol. 144 pages 101-124. 110 | * https://www.sciencedirect.com/science/article/pii/030439759400281M 111 | * for general thoughts. 112 | * 113 | */ 114 | unsigned int cpx_newton_series(cpx_t result, 115 | void (*func)(cpx_t f, unsigned long p, int nprec), 116 | cpx_t zee, 117 | unsigned int ndigits, 118 | unsigned int maxterms, 119 | int nprec); 120 | 121 | #ifdef __cplusplus 122 | }; 123 | #endif 124 | 125 | #endif /* __MP_EULER_H__ */ 126 | -------------------------------------------------------------------------------- /src/mp-gamma.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-gamma.c 3 | * 4 | * Compute gamma function for various complex arguments 5 | * 6 | * Copyright (C) 2006 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include "mp-binomial.h" 30 | #include "mp-complex.h" 31 | #include "mp-consts.h" 32 | #include "mp-gamma.h" 33 | #include "mp-misc.h" 34 | #include "mp-trig.h" 35 | #include "mp-zeta.h" 36 | 37 | /* ================================================= */ 38 | /* 39 | * fp_lngamma -- compute log of gamma for real argument 40 | * 41 | * Uses simple, quickly converging algo-- A&S 6.1.33 42 | * Slightly modified: 43 | * ln Gamma (z+2)=z(1-gamma)+sum_{n=2}^\infty (zeta(n)-1) z^n/n 44 | * Valid input must have 0 < x < 4 45 | * and ideally 1.5 < x < 2.5 for fastest convergence 46 | */ 47 | static void reduced_lngamma (mpf_t gam, const mpf_t ex, int prec) 48 | { 49 | int n; 50 | mpf_t z, zn, term; 51 | 52 | mpf_init (z); 53 | mpf_init (zn); 54 | mpf_init (term); 55 | 56 | /* make copy of input argument now! */ 57 | mpf_set (z, ex); 58 | 59 | mpf_set_ui (gam, 0); 60 | 61 | mpf_sub_ui (z, z, 2); 62 | mpf_mul (zn, z,z); 63 | 64 | /* Use 10^{-prec} for smallest term in sum */ 65 | mpf_t maxterm; 66 | mpf_init (maxterm); 67 | fp_epsilon (maxterm, prec); 68 | 69 | n=2; 70 | while (1) 71 | { 72 | fp_zeta (term, n, prec); 73 | mpf_sub_ui (term, term, 1); 74 | mpf_mul (term, term, zn); 75 | mpf_div_ui (term, term, n); 76 | if (n%2) 77 | { 78 | mpf_sub (gam, gam, term); 79 | } 80 | else 81 | { 82 | mpf_add (gam, gam, term); 83 | } 84 | 85 | /* don't go no farther than this */ 86 | mpf_abs(term, term); 87 | if (mpf_cmp (term, maxterm) < 0) break; 88 | 89 | mpf_mul (zn,zn,z); 90 | n++; 91 | } 92 | 93 | fp_euler_mascheroni (term, prec); 94 | mpf_sub_ui (term, term, 1); 95 | mpf_mul (term, term, z); 96 | mpf_sub (gam, gam, term); 97 | 98 | mpf_clear (z); 99 | mpf_clear (zn); 100 | mpf_clear (term); 101 | mpf_clear (maxterm); 102 | } 103 | 104 | /* ================================================= */ 105 | /* 106 | * cpx_lngamma -- compute log of gamma for complex argument 107 | * 108 | * Same code as above, extended for complex arguments 109 | * Uses simple, quickly converging algo-- A&S 6.1.33 110 | * Slightly modified: 111 | * ln Gamma (z+2)=z(1-gamma)+sum_{n=2}^\infty (zeta(n)-1) z^n/n 112 | * Valid input must have |ex-2| < 2 113 | * and ideally |ex-2| < 0.5 for fastest convergence 114 | */ 115 | static void cpx_reduced_lngamma (cpx_t gam, const cpx_t ex, int prec) 116 | { 117 | int n; 118 | cpx_t z, zn, term; 119 | 120 | cpx_init (z); 121 | cpx_init (zn); 122 | cpx_init (term); 123 | 124 | /* make copy of input argument now! */ 125 | cpx_set (z, ex); 126 | 127 | cpx_set_ui (gam, 0, 0); 128 | 129 | cpx_sub_ui (z, z, 2, 0); 130 | cpx_mul (zn, z, z); 131 | 132 | /* Use 10^{-prec} for smallest term in sum */ 133 | mpf_t maxterm; 134 | mpf_init (maxterm); 135 | fp_epsilon (maxterm, 2*prec); 136 | 137 | n=2; 138 | while (1) 139 | { 140 | mpf_set_ui (term[0].im, 0); 141 | fp_zeta (term[0].re, n, prec); 142 | mpf_sub_ui (term[0].re, term[0].re, 1); 143 | mpf_div_ui (term[0].re, term[0].re, n); 144 | cpx_mul (term, term, zn); 145 | 146 | if (n%2) 147 | { 148 | cpx_sub (gam, gam, term); 149 | } 150 | else 151 | { 152 | cpx_add (gam, gam, term); 153 | } 154 | 155 | /* don't go no farther than this */ 156 | cpx_mod_sq(term[0].re, term); 157 | if (mpf_cmp (term[0].re, maxterm) < 0) break; 158 | 159 | cpx_mul (zn,zn,z); 160 | n++; 161 | } 162 | 163 | mpf_set_ui (term[0].im, 0); 164 | fp_euler_mascheroni (term[0].re, prec); 165 | mpf_sub_ui (term[0].re, term[0].re, 1); 166 | cpx_mul (term, term, z); 167 | cpx_sub (gam, gam, term); 168 | 169 | cpx_clear (z); 170 | cpx_clear (zn); 171 | cpx_clear (term); 172 | mpf_clear (maxterm); 173 | } 174 | 175 | /* ================================================= */ 176 | /* 177 | * gamma function, valid only for 0 < x < 4 178 | * Otherwise explodes... 179 | */ 180 | static inline void reduced_gamma (mpf_t gam, const mpf_t ex, int prec) 181 | { 182 | reduced_lngamma (gam, ex, prec); 183 | fp_exp (gam, gam, prec); 184 | } 185 | 186 | /* ================================================= */ 187 | /* 188 | * fp_gamma 189 | * Use pochhammer to get into range of 1.5 < z < 2.5, 190 | * Then use the reduced summation formula. The range 191 | * of 1.5 < z < 2.5 is particularly quickly convergent 192 | */ 193 | void fp_gamma (mpf_t gam, const mpf_t z, int prec) 194 | { 195 | mpf_t zee; 196 | mpf_init (zee); 197 | 198 | /* make a copy of the input arg NOW! */ 199 | mpf_set (zee, z); 200 | 201 | /* double-presision used, this code doesn't need to 202 | * be all that accurate; just need a reasonable int part. 203 | */ 204 | double flo = mpf_get_d (zee); 205 | if (flo > 2.5) 206 | { 207 | unsigned int intpart = (unsigned int) floor (flo-1.0); 208 | 209 | /* The goal of the next if statement is to make sure that 210 | * -0.5 2.5) 297 | { 298 | unsigned int intpart = (unsigned int) floor (flo-1.0); 299 | 300 | /* The goal of the next if statement is to make sure that 301 | * -0.5 25 | #include "mp-complex.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * fp_gamma -- compute Gamma(x)=factorial(x-1) for real argument 33 | * 34 | * Uses simple, quickly converging algo-- A&S 6.1.33 35 | * 36 | * The caching version skips the calculation, if called again with 37 | * the same value of ex (up to the nprec precision bits). 38 | */ 39 | void fp_gamma (mpf_t gam, const mpf_t ex, int prec); 40 | void fp_gamma_cache (mpf_t gam, const mpf_t ex, int prec); 41 | 42 | /** 43 | * cpx_gamma -- compute Gamma(x)=factorial(x-1) for complex argument 44 | * 45 | * Uses simple, quickly converging algo-- A&S 6.1.33 46 | * 47 | * The caching version skips the calculation, if called again with 48 | * the same value of ex (up to the nprec precision bits). 49 | * 50 | * Note: This code will throw a floating-point exeception, from deep 51 | * inside the algo, if called with a value of ex that is zero or a 52 | * negative integer. Of course, gamma is infinite here; none-the-less 53 | * don't be surprised when this happens! 54 | */ 55 | void cpx_gamma (cpx_t gam, const cpx_t ex, int prec); 56 | void cpx_gamma_cache (cpx_t gam, const cpx_t ex, int prec); 57 | 58 | #ifdef __cplusplus 59 | }; 60 | #endif 61 | -------------------------------------------------------------------------------- /src/mp-genfunc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Assorted generating functions for arithmetic functions. 3 | * Implementation in bignums. 4 | * 5 | * Linas Vepstas - April 2016, October 2016 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "mp-genfunc.h" 18 | 19 | /* 20 | * Ordinary generating function for function func. 21 | * Computes ogf(z) = sum_{n=1}^\infty func(n) z^n 22 | * 23 | * Assumes that func(n) is some arithmetic series. 24 | * Assumes that ogf(z) does not converge for |z|>=1 25 | * Assumes that ogf(z) converges poorly near |z|=1, so that 26 | * some termination measures are taken, so that the sum does 27 | * not run forever. The termination measures assume that 28 | * func(n) is bounded by n. i.e. this is to gaurantee good 29 | * data when the system is not overflowing. 30 | */ 31 | void cpx_ordinary_genfunc(cpx_t sum, cpx_t z, int prec, long (*func)(long)) 32 | { 33 | mpf_t zabs, gabs, epsi; 34 | mpf_init (gabs); 35 | mpf_init (zabs); 36 | mpf_init (epsi); 37 | mpf_set_ui(epsi, 1); 38 | mpf_div_2exp(epsi, epsi, (int)(3.321*prec)); 39 | 40 | cpx_set_ui(sum, 0, 0); 41 | 42 | // Falls apart if z is zero. 43 | cpx_abs(gabs, z); 44 | if (0 > mpf_cmp(gabs, epsi)) return; 45 | 46 | // Not defined for |z| > 1 47 | mpf_sub_ui(gabs, gabs, 1); 48 | mpf_neg(gabs, gabs); 49 | if (0 > mpf_cmp(gabs, epsi)) return; 50 | 51 | // Limit the number of iterations as we approach the edge. 52 | // This assumes that func(n) is bounded by n 53 | double dist_to_circle = mpf_get_d(gabs); 54 | int niter = ceil (2.302585*prec / dist_to_circle); 55 | niter += ceil (log(niter) / dist_to_circle); // assume func bounded by n 56 | 57 | cpx_t zn, term; 58 | cpx_init(zn); 59 | cpx_init(term); 60 | cpx_set(zn, z); 61 | 62 | for (int n=1; n < niter ; n++) 63 | { 64 | long funv = func(n); 65 | if (0 != funv) 66 | { 67 | cpx_times_ui(term, zn, labs(funv)); 68 | if (funv < 0) 69 | cpx_sub(sum, sum, term); 70 | else 71 | cpx_add(sum, sum, term); 72 | } 73 | cpx_mul(zn, zn, z); 74 | 75 | #if SLOW_VERSION_NOT_USING_NITER 76 | // The following checks the loop termination condition, 77 | // which is that the size of the term is less than epsilon. 78 | cpx_abs(gabs, zn); 79 | mpf_mul_ui(gabs, gabs, n); 80 | 81 | cpx_abs(zabs, sum); 82 | mpf_div(gabs, gabs, zabs); 83 | 84 | // if (n * zn < epsi * sum) return; 85 | if (0 > mpf_cmp(gabs, epsi)) return; 86 | #endif 87 | } 88 | } 89 | 90 | 91 | /** 92 | * Exponential generating function for the arithmetic series 93 | * 94 | * Computes egf(z) = exp(-|z|) sum_{n=1}^infty func(n) z^n / n! 95 | * 96 | * Note the assumption about the leading asymptotic behavior of 97 | * the series. This is a reasonable assumption in many cases, but 98 | * not always. Your mileage may vary. 99 | */ 100 | void cpx_exponential_genfunc(cpx_t sum, cpx_t z, int prec, long (*func)(long)) 101 | { 102 | mp_bitcnt_t bits = ((double) prec) * 3.322 + 50; 103 | 104 | mpf_t zabs, gabs, epsi, fact; 105 | mpf_init2 (gabs, bits); 106 | mpf_init2 (zabs, bits); 107 | mpf_init2 (epsi, bits); 108 | mpf_init2 (fact, bits); 109 | mpf_set_ui(fact, 1); 110 | mpf_set_ui(epsi, 1); 111 | mpf_div_2exp(epsi, epsi, (int)(3.322*prec)); 112 | 113 | cpx_set_ui(sum, 0, 0); 114 | 115 | // falls apart if z is zero. 116 | cpx_abs(gabs, z); 117 | if (0 > mpf_cmp(gabs, epsi)) return; 118 | 119 | cpx_t zn, term; 120 | cpx_init2(zn, bits); 121 | cpx_init2(term, bits); 122 | cpx_set(zn, z); 123 | 124 | for (int n=1; ; n++) 125 | { 126 | long funv = func(n); 127 | if (0 != funv) 128 | { 129 | cpx_times_ui(term, zn, labs(funv)); 130 | cpx_times_mpf(term, term, fact); 131 | if (funv < 0) 132 | cpx_sub(sum, sum, term); 133 | else 134 | cpx_add(sum, sum, term); 135 | 136 | // The following checks the loop termination condition, 137 | // which is that the size of the term is less than epsilon. 138 | cpx_abs(gabs, term); 139 | mpf_mul_ui(gabs, gabs, n); 140 | 141 | cpx_abs(zabs, sum); 142 | mpf_mul(zabs, zabs, epsi); 143 | 144 | // if (n * zn/n! < epsi * sum) return; 145 | if (0 > mpf_cmp(gabs, zabs)) break; 146 | } 147 | 148 | cpx_mul(zn, zn, z); 149 | mpf_div_ui(fact, fact, n+1); 150 | } 151 | 152 | // Remove the leading exponential order. 153 | cpx_abs(gabs, z); 154 | mpf_neg(gabs, gabs); 155 | fp_exp(gabs, gabs, prec); 156 | 157 | cpx_times_mpf(sum, sum, gabs); 158 | } 159 | 160 | /** 161 | * Exponential generating function for the arithmetic series. 162 | * Same as above, except that func returns an real value, stored 163 | * in mpf_t 164 | */ 165 | void cpx_exponential_genfunc_mpf(cpx_t sum, cpx_t z, int prec, 166 | void (*func)(mpf_t, long)) 167 | { 168 | mpf_t zabs, gabs, epsi, fact, func_val, fabs; 169 | mpf_init (gabs); 170 | mpf_init (zabs); 171 | mpf_init (epsi); 172 | mpf_init (fact); 173 | mpf_init (func_val); 174 | mpf_init (fabs); 175 | mpf_set_ui(fact, 1); 176 | mpf_set_ui(epsi, 1); 177 | mpf_div_2exp(epsi, epsi, (int)(3.321*prec)); 178 | 179 | cpx_set_ui(sum, 0, 0); 180 | 181 | // falls apart if z is zero. 182 | cpx_abs(gabs, z); 183 | if (0 > mpf_cmp(gabs, epsi)) return; 184 | 185 | cpx_t zn, term; 186 | cpx_init(zn); 187 | cpx_init(term); 188 | cpx_set(zn, z); 189 | 190 | int n; 191 | for (n=1; ; n++) 192 | { 193 | func(func_val, n); 194 | 195 | // The below is a weird hack to check for a value of zero 196 | // returned by func. It fails, if func is trying to return 197 | // a very small but non-zero value; that is why it a hack. 198 | // Currently, none of our functions return small values, 199 | // so this is OK, for now. 200 | mpf_abs(fabs, func_val); 201 | if (0 < mpf_cmp(fabs, epsi)) 202 | { 203 | cpx_times_mpf(term, zn, func_val); 204 | cpx_times_mpf(term, term, fact); 205 | cpx_add(sum, sum, term); 206 | 207 | // The following checks the loop termination condition, 208 | // which is that the size of the term is less than epsilon. 209 | cpx_abs(gabs, term); 210 | // mpf_mul_ui(gabs, gabs, n); 211 | mpf_mul(gabs, gabs, fabs); 212 | 213 | cpx_abs(zabs, sum); 214 | mpf_mul(zabs, zabs, epsi); 215 | 216 | // if (n * zn/n! < epsi * sum) return; 217 | if (0 > mpf_cmp(gabs, zabs)) break; 218 | } 219 | 220 | cpx_mul(zn, zn, z); 221 | mpf_div_ui(fact, fact, n+1); 222 | } 223 | 224 | // Remove the leading exponential order. 225 | cpx_abs(gabs, z); 226 | mpf_neg(gabs, gabs); 227 | fp_exp(gabs, gabs, prec); 228 | 229 | cpx_times_mpf(sum, sum, gabs); 230 | 231 | mpf_clear (gabs); 232 | mpf_clear (zabs); 233 | mpf_clear (epsi); 234 | mpf_clear (fact); 235 | mpf_clear (func_val); 236 | mpf_clear (fabs); 237 | } 238 | 239 | /** 240 | * Twist the z value by gamma. 241 | */ 242 | void cpx_exponential_twist(cpx_t sum, cpx_t z, int prec, long (*func)(long)) 243 | { 244 | mp_bitcnt_t bits = ((double) prec) * 3.322 + 50; 245 | 246 | cpx_t zt; 247 | cpx_init2(zt, bits); 248 | 249 | mpf_t r, t, g, gd, pi; 250 | mpf_init2(r, bits); 251 | mpf_init2(t, bits); 252 | mpf_init2(g, bits); 253 | mpf_init2(gd, bits); 254 | mpf_init2(pi, bits); 255 | fp_pi(pi, prec); 256 | 257 | // Decompose z = r exp itheta 258 | cpx_abs(r, z); 259 | fp_arctan2(t, z->im, z->re, prec); 260 | mpf_div(t, t, pi); 261 | 262 | // t runs between -1 and 1. 263 | int sgn = mpf_sgn (t); 264 | if (sgn < 0) 265 | { 266 | mpf_neg(t, t); 267 | } 268 | 269 | // rescale so that t runs between 0 and 1/2 270 | mpf_div_ui(t, t, 2); 271 | 272 | // --------------------------------------- 273 | // Compute gamma = g(t) = t/(n+t) 274 | 275 | unsigned long n = 1; 276 | mpf_add_ui (g, t, n); 277 | mpf_div (g, t, g); 278 | 279 | // --------------------------------------- 280 | // rescale r as well. Need g-dot(t) = n/(n+t)^2 281 | mpf_add_ui (gd, t, n); 282 | mpf_ui_div (gd, 1, gd); 283 | mpf_mul (gd, gd, gd); 284 | mpf_mul_ui (gd, gd, n); 285 | 286 | // -------------------------------------- 287 | // g runs between 0 and 1/3 = 1/(n+2) 288 | mpf_set(t, g); 289 | 290 | // rescale back up that t is doubled again 291 | mpf_mul_ui(t, t, 2); 292 | 293 | // Multiply by pi, so that t runs over a sub-range of 294 | // -pi to pi. For a single power of g, that means that 295 | // t runs over 0 to 2pi/3 = 2pi/(n+2) 296 | mpf_mul(t, t, pi); 297 | 298 | // -------------------------------------- 299 | // Rescale -- take power r^gdot 300 | fp_log (r, r, prec); 301 | mpf_mul (r, r, gd); 302 | fp_exp(r, r, prec); 303 | 304 | // -------------------------------------- 305 | // reconstruct z. 306 | // zt = r exp (it) 307 | cpx_set_ui(zt, 0, 1); // zt = i 308 | cpx_times_mpf(zt, zt, t); // zt = itheta 309 | cpx_exp(zt, zt, prec); // zt = exp(itheta) 310 | cpx_times_mpf(zt, zt, r); // zt = r exp(itheta) 311 | 312 | cpx_exponential_genfunc(sum, zt, prec, func); 313 | 314 | cpx_clear(zt); 315 | mpf_clear(r); 316 | mpf_clear(t); 317 | mpf_clear(g); 318 | mpf_clear(gd); 319 | mpf_clear(pi); 320 | } 321 | -------------------------------------------------------------------------------- /src/mp-genfunc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Assorted generating functions for arithmetic functions. 3 | * Implementation in bignums. 4 | * 5 | * October 2016 6 | */ 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /* 15 | * Ordinary generating function for arithmetic functions. 16 | */ 17 | void cpx_ordinary_genfunc(cpx_t sum, cpx_t z, int prec, 18 | long (*func)(long)); 19 | 20 | /* 21 | * Exponential generating function for arithmetic functions. 22 | * The second form expects func to return a value of type mpf_t 23 | * in the first argument. 24 | */ 25 | void cpx_exponential_genfunc(cpx_t sum, cpx_t z, int prec, 26 | long (*func)(long)); 27 | void cpx_exponential_genfunc_mpf(cpx_t sum, cpx_t z, int prec, 28 | void (*func)(mpf_t, long)); 29 | 30 | /* 31 | * Dyadic Farey rescaling. 32 | */ 33 | void cpx_exponential_twist(cpx_t sum, cpx_t z, int prec, 34 | long (*func)(long)); 35 | #ifdef __cplusplus 36 | }; 37 | #endif 38 | -------------------------------------------------------------------------------- /src/mp-gkw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-gkw.c 3 | * 4 | * FUNCTION: 5 | * Compute matrix elts of the GKW operator. 6 | * 7 | * HISTORY: 8 | * Linas Jan 2010 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "mp-binomial.h" 16 | #include "mp-misc.h" 17 | #include "mp-zeta.h" 18 | 19 | 20 | // Return the matrix element for the matrix element G_mp of the GKW 21 | // operator, expanded at the x=1 location. 22 | void 23 | gkw(mpf_t acc, int m, int p, unsigned int prec) 24 | { 25 | mpf_t one, term, fbin; 26 | mpz_t bin; 27 | int k; 28 | 29 | mpf_init (term); 30 | mpf_init (one); 31 | mpf_init (fbin); 32 | 33 | mpz_init (bin); 34 | 35 | // long double acc = 0.0L; 36 | mpf_set_ui (acc, 0); 37 | mpf_set_ui (one, 1); 38 | 39 | for (k=0; k<=p; k++) 40 | { 41 | // long double term = zetam1 (k+m+2); 42 | fp_zeta (term, k+m+2, prec); 43 | mpf_sub(term, term, one); 44 | 45 | // term *= binomial (m+k+1,m); 46 | i_binomial (bin, m+k+1, m); 47 | mpf_set_z (fbin, bin); 48 | mpf_mul (term, term, fbin); 49 | 50 | // term *= binomial (p,k); 51 | i_binomial (bin, p, k); 52 | mpf_set_z (fbin, bin); 53 | mpf_mul (term, term, fbin); 54 | 55 | if (k%2 == 0) mpf_add (acc, acc, term); 56 | else mpf_sub (acc, acc, term); 57 | } 58 | } 59 | 60 | 61 | // Return the continuous-valued version of the GKW operator. 62 | // (the matrix elts occur at integer values) 63 | // This implementation uses GMP multi-precision 64 | void 65 | gkw_smooth(mpf_t acc, double m, double p, unsigned int prec) 66 | { 67 | mpf_t one, term, bin; 68 | int k; 69 | 70 | mpf_init (term); 71 | mpf_init (one); 72 | mpf_init (bin); 73 | 74 | cpx_t ess, zeta; 75 | cpx_init(ess); 76 | cpx_init(zeta); 77 | 78 | // long double acc = 0.0L; 79 | mpf_set_ui (acc, 0); 80 | mpf_set_ui (one, 1); 81 | 82 | int ip = (int) floor(p); 83 | for (k=0; k<= ip; k++) 84 | { 85 | double km2, km1; 86 | 87 | printf ("duuude k=%d---------------------------------\n", k); 88 | // long double term = zetam1 (k+m+2); 89 | // fp_zeta (term, k+m+2, prec); 90 | km2 = k + m + 2.0L; 91 | cpx_set_d (ess, km2, 0.0); 92 | cpx_borwein_zeta(zeta, ess, prec); 93 | mpf_sub(term, zeta[0].re, one); 94 | printf ("duuude k+m+2=%f\n", km2); 95 | cpx_prt("cpx zeta = ", zeta); 96 | printf("\n"); 97 | fp_prt("zeta -1 = ", term); 98 | printf("\n"); 99 | 100 | // term *= binomial (m+k+1,m); 101 | km1 = m+k+1.0L; 102 | fp_binomial_d (bin, km1, k+1); 103 | mpf_mul (term, term, bin); 104 | 105 | // term *= binomial (p,k); 106 | fp_binomial_d (bin, p, k); 107 | mpf_mul (term, term, bin); 108 | 109 | if (k%2 == 0) mpf_add (acc, acc, term); 110 | else mpf_sub (acc, acc, term); 111 | } 112 | } 113 | 114 | /* --------------------------- END OF LIFE ------------------------- */ 115 | -------------------------------------------------------------------------------- /src/mp-gkw.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-gkw.h 3 | * 4 | * FUNCTION: 5 | * Compute matrix elts of the GKW operator. 6 | * 7 | * HISTORY: 8 | * Linas Jan 2010 9 | */ 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | // Return the matrix element for the matrix G_mp of the GKW 18 | // operator, expanded at the x=1 location. 19 | void gkw(mpf_t elt, int m, int p, unsigned int prec); 20 | 21 | // Return the continuous-valued version of the GKW operator. 22 | // (the matrix elts occur at integer values) 23 | // This implementation uses GMP multi-precision 24 | void gkw_smooth(mpf_t elt, double m, double p, unsigned int prec); 25 | 26 | #ifdef __cplusplus 27 | }; 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /src/mp-hyper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-hyper.c 3 | * 4 | * High-precison Hypergeometric functions, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2006 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include "mp-complex.h" 31 | #include "mp-misc.h" 32 | 33 | /* ======================================================================= */ 34 | /** 35 | * cpx_confluent -- Confluent hypergeometric function 36 | * Implemented using a brute-force, very simple algo, with 37 | * no attempts at optimization. Also, does not assume any 38 | * precomputed constants. 39 | */ 40 | 41 | void 42 | cpx_confluent (cpx_t em, cpx_t a, cpx_t b, cpx_t z, unsigned int prec) 43 | { 44 | mpf_t fact; 45 | cpx_t zee, z_n, term; 46 | cpx_t ay, be, poch_a, poch_b; 47 | 48 | mpf_init (fact); 49 | 50 | cpx_init (zee); 51 | cpx_init (z_n); 52 | cpx_init (term); 53 | cpx_init (ay); 54 | cpx_init (be); 55 | cpx_init (poch_a); 56 | cpx_init (poch_b); 57 | 58 | /* Make copy of arguments now! */ 59 | cpx_set (zee, z); 60 | cpx_set (z_n, zee); 61 | 62 | cpx_set (ay, a); 63 | cpx_set (poch_a, a); 64 | cpx_set (be, b); 65 | cpx_set (poch_b, b); 66 | 67 | cpx_set_ui (em, 1, 0); 68 | mpf_set_ui (fact, 1); 69 | 70 | mpf_t mag; 71 | mpf_init (mag); 72 | 73 | /* Use 10^{-2 * prec} for smallest term in sum */ 74 | mpf_t maxterm; 75 | mpf_init (maxterm); 76 | fp_epsilon (maxterm, prec); 77 | mpf_mul (maxterm, maxterm, maxterm); 78 | 79 | unsigned int n=1; 80 | while(1) 81 | { 82 | cpx_div_mpf (term, z_n, fact); 83 | cpx_mul (term, term, poch_a); 84 | cpx_div (term, term, poch_b); 85 | cpx_add (em, em, term); 86 | 87 | /* Don't go no farther than this */ 88 | cpx_mod_sq (mag, term); 89 | if (mpf_cmp (mag, maxterm) < 0) break; 90 | 91 | n++; 92 | cpx_mul (z_n, z_n, zee); 93 | mpf_mul_ui (fact, fact, n); 94 | 95 | mpf_add_ui (ay[0].re, ay[0].re, 1); 96 | mpf_add_ui (be[0].re, be[0].re, 1); 97 | cpx_mul (poch_a, poch_a, ay); 98 | cpx_mul (poch_b, poch_b, be); 99 | } 100 | 101 | mpf_clear (fact); 102 | mpf_clear (mag); 103 | mpf_clear (maxterm); 104 | 105 | cpx_clear (zee); 106 | cpx_clear (z_n); 107 | cpx_clear (term); 108 | cpx_clear (ay); 109 | cpx_clear (be); 110 | cpx_clear (poch_a); 111 | cpx_clear (poch_b); 112 | } 113 | 114 | /* =============================== END OF FILE =========================== */ 115 | 116 | -------------------------------------------------------------------------------- /src/mp-hyper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-hyper.h 3 | * 4 | * High-precision Hypergeometric functions, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2006 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include "mp-complex.h" 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * cpx_confluent -- Confluent hypergeometric function 34 | * Implemented using a brute-force, very simple algo, with 35 | * no attempts at optimization. Also, does not assume any 36 | * precomputed constants. 37 | */ 38 | 39 | void cpx_confluent (cpx_t em, cpx_t a, cpx_t b, cpx_t z, unsigned int prec); 40 | 41 | #ifdef __cplusplus 42 | }; 43 | #endif 44 | 45 | -------------------------------------------------------------------------------- /src/mp-misc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-misc.c 3 | * 4 | * High-precison misc functions, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005,2006,2007 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include "mp-complex.h" 31 | #include "mp-misc.h" 32 | 33 | void i_prt (const char * str, mpz_t val) 34 | { 35 | printf ("%s", str); 36 | mpz_out_str (stdout, 10, val); 37 | } 38 | 39 | void fp_prt (const char * str, mpf_t val) 40 | { 41 | printf ("%s", str); 42 | mpf_out_str (stdout, 10, 60, val); 43 | } 44 | 45 | void cpx_prt (const char * str, const cpx_t val) 46 | { 47 | printf ("%s", str); 48 | mpf_out_str (stdout, 10, 30, val[0].re); 49 | printf (" + i "); 50 | mpf_out_str (stdout, 10, 30, val[0].im); 51 | } 52 | 53 | void ecpx_prt (const char * str, const cpx_t val) 54 | { 55 | fprintf (stderr, "%s", str); 56 | mpf_out_str (stderr, 10, 30, val[0].re); 57 | fprintf (stderr, " + i "); 58 | mpf_out_str (stderr, 10, 30, val[0].im); 59 | } 60 | 61 | /* ===================================================== */ 62 | /** 63 | * fp_epsilon - return 10^{-prec} 64 | */ 65 | void fp_epsilon (mpf_t eps, int prec) 66 | { 67 | static int cache_prec = -1; 68 | static mpf_t cache_eps; 69 | 70 | if (-1 == cache_prec) 71 | { 72 | mpf_init (cache_eps); 73 | } 74 | 75 | if (prec == cache_prec) 76 | { 77 | mpf_set (eps, cache_eps); 78 | return; 79 | } 80 | if (cache_prec < prec) 81 | { 82 | mpf_set_prec (cache_eps, 3.322*prec+50); 83 | } 84 | 85 | /* double mex = ((double) prec) * log (10.0) / log(2.0); */ 86 | double mex = ((double) prec) * 3.321928095; 87 | unsigned int imax = (unsigned int) (mex +1.0); 88 | mpf_t one; 89 | mpf_init (one); 90 | mpf_set_ui (one, 1); 91 | mpf_div_2exp (cache_eps, one, imax); 92 | 93 | mpf_set (eps, cache_eps); 94 | cache_prec = prec; 95 | mpf_clear (one); 96 | } 97 | 98 | /* ===================================================== */ 99 | 100 | /* prec is the decimal precison (number of decimal places) */ 101 | /* nterms is the number of an's to compute */ 102 | void set_bits (int prec, int nterms) 103 | { 104 | /* Compute number of binary bits this corresponds to. */ 105 | double v = ((double) prec) *log(10.0) / log(2.0); 106 | 107 | /* The variable-precision calculations are touchy about this */ 108 | /* XXX this should be stirling's approx for binomial */ 109 | int bits = (int) (v + 300 + 3*nterms); 110 | 111 | /* Set the precision (number of binary bits) */ 112 | mpf_set_default_prec (bits); 113 | } 114 | 115 | /* ===================================================== */ 116 | 117 | int get_prec (cpx_t epsi, unsigned int prec) 118 | { 119 | long rex, imx; 120 | mpf_get_d_2exp (&rex, epsi[0].re); 121 | mpf_get_d_2exp (&imx, epsi[0].im); 122 | rex = -0.30103 *rex; 123 | imx = -0.30103 *imx; 124 | if (imx && imx < rex) rex = imx; 125 | if (0 == rex) rex = imx; 126 | if (0 == rex) rex = prec; 127 | if (mpf_cmp_d (epsi[0].re, 0.1) > 0) rex = 0; 128 | if (mpf_cmp_d (epsi[0].re, -0.1) < 0) rex = 0; 129 | 130 | if (mpf_cmp_d (epsi[0].im, 0.1) > 0) rex = 0; 131 | if (mpf_cmp_d (epsi[0].im, -0.1) < 0) rex = 0; 132 | 133 | return rex; 134 | } 135 | 136 | /* Print number of digits by which value differs from 137 | * previous call to this routine. 138 | */ 139 | 140 | int last_change(const cpx_t curr, unsigned int prec) 141 | { 142 | static cpx_t prev; 143 | static int init = 0; 144 | 145 | if (!init) 146 | { 147 | init = 1; 148 | cpx_init (prev); 149 | } 150 | 151 | /* Set the precision (number of binary bits) */ 152 | int nbits = 3.322*prec+5; 153 | cpx_set_prec (prev, nbits); 154 | 155 | cpx_sub (prev, prev, curr); 156 | 157 | printf ("prec=%d ", prec); 158 | 159 | long rex = get_prec (prev, prec); 160 | printf ("change=%ld\n", rex); 161 | 162 | cpx_set (prev, curr); 163 | return rex; 164 | } 165 | 166 | /* =============================== END OF FILE =========================== */ 167 | -------------------------------------------------------------------------------- /src/mp-misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-misc.h 3 | * 4 | * High-precison misc functions, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005,2006,2007 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | */ 24 | 25 | #include 26 | #include "mp-complex.h" 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | void i_prt (const char * str, mpz_t val); 33 | void fp_prt (const char * str, mpf_t val); 34 | void cpx_prt (const char * str, const cpx_t val); 35 | void ecpx_prt (const char * str, const cpx_t val); 36 | 37 | /** 38 | * fp_epsilon - return 10^{-prec} 39 | * 40 | * Intended for use as a max-precision iteration stopper 41 | * Value is cached. 42 | */ 43 | void fp_epsilon (mpf_t eps, int prec); 44 | 45 | /* prec is the decimal precison (number of decimal places) */ 46 | /* nterms is the number of an's to compute */ 47 | void set_bits (int prec, int nterms); 48 | 49 | /** 50 | * Print number of digits by which value differs from 51 | * previous call to this routine. 52 | */ 53 | 54 | int last_change(const cpx_t curr, unsigned int prec); 55 | 56 | /** 57 | * get_prec -- return log_10 (epsi) approximately rounded 58 | */ 59 | int get_prec (cpx_t epsi, unsigned int prec); 60 | 61 | #ifdef __cplusplus 62 | }; 63 | #endif 64 | 65 | -------------------------------------------------------------------------------- /src/mp-multiplicative.c: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-multiplicative.c 3 | * 4 | * Fill in values of completely multiplicative arithmetic function. 5 | * 6 | * Copyright (C) 2019 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | // Tail-recursive helper function 28 | static void plicplic(cpx_t result, 29 | void (*func)(cpx_t f, unsigned long p, int nprec), 30 | unsigned long m, unsigned long n, 31 | int nprec) 32 | { 33 | // m is prime 34 | if (n+1 == m) 35 | { 36 | func(result, m, nprec); 37 | return; 38 | } 39 | 40 | // m is not prime, and not divisible. 41 | if (m%n != 0) 42 | { 43 | plicplic(result, func, m, n+1, nprec); 44 | return; 45 | } 46 | mp_bitcnt_t bits = ((double) nprec) * 3.322 + 50; 47 | cpx_t fq, fn; 48 | cpx_init2(fq, bits); 49 | cpx_init2(fn, bits); 50 | 51 | unsigned long q = m/n; 52 | cpx_multiplicative(fq, func, q, nprec); 53 | cpx_multiplicative(fn, func, n, nprec); 54 | 55 | cpx_mul(result, fq, fn); 56 | cpx_clear(fq); 57 | cpx_clear(fn); 58 | } 59 | 60 | void cpx_multiplicative(cpx_t result, 61 | void (*func)(cpx_t f, unsigned long p, int nprec), 62 | unsigned long n, 63 | int nprec) 64 | { 65 | /* handle trivial case */ 66 | if (n <= 3) 67 | { 68 | func(result, n, nprec); 69 | return; 70 | } 71 | plicplic(result, func, n, 2, nprec); 72 | } 73 | 74 | // ============================================================ 75 | 76 | DECLARE_CPX_CACHE(primca) 77 | DECLARE_CPX_CACHE(prodca) 78 | 79 | void cpx_multiplicative_clear_cache(void) 80 | { 81 | cpx_one_d_cache_clear(&primca); 82 | cpx_one_d_cache_clear(&prodca); 83 | } 84 | 85 | void cpx_multiplicative_cached(cpx_t result, 86 | void (*func)(cpx_t, unsigned long, int), 87 | unsigned long n, 88 | int nprec) 89 | { 90 | void wrapper(cpx_t res, unsigned long nn, int pr) 91 | { 92 | if (cpx_one_d_cache_check(&primca, nn)) 93 | { 94 | cpx_one_d_cache_fetch(&primca, res, nn); 95 | return; 96 | } 97 | func(res, nn, pr); 98 | cpx_one_d_cache_store(&primca, res, nn, pr); 99 | } 100 | 101 | if (cpx_one_d_cache_check(&prodca, n)) 102 | { 103 | cpx_one_d_cache_fetch(&prodca, result, n); 104 | return; 105 | } 106 | cpx_multiplicative(result, wrapper, n, nprec); 107 | cpx_one_d_cache_store(&prodca, result, n, nprec); 108 | } 109 | 110 | // ============================================================ 111 | // #define TEST 112 | #ifdef TEST 113 | #include 114 | 115 | void test_func(cpx_t f, unsigned long p, int nprec) 116 | { 117 | // f(p) = p 118 | printf("Called with %lu\n", p); 119 | cpx_set_ui(f, p, 0); 120 | } 121 | 122 | int main (int argc, char * argv[]) 123 | { 124 | int prec, nbits; 125 | prec = 120; 126 | nbits = 3.3*prec; 127 | mpf_set_default_prec (nbits+200); 128 | 129 | cpx_t result; 130 | cpx_init2(result, nbits); 131 | 132 | for (unsigned long n=1; n<60; n++) 133 | { 134 | // cpx_multiplicative(result, test_func, n, prec); 135 | cpx_multiplicative_cached(result, test_func, n, prec); 136 | printf("N=%lu result=%f + i%f\n", n, 137 | cpx_get_re(result), cpx_get_im(result)); 138 | } 139 | } 140 | #endif 141 | -------------------------------------------------------------------------------- /src/mp-multiplicative.h: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-multiplicative.h 3 | * 4 | * Fill in values of completely multiplicative arithmetic function. 5 | * 6 | * Copyright (C) 2019 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #ifndef __MP_MULTIPLICATIVE_H__ 25 | #define __MP_MULTIPLICATIVE_H__ 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include "mp-complex.h" 32 | 33 | /* =============================================== */ 34 | /** 35 | * cpx_multiplicative. 36 | * 37 | * Fill in values of completely multiplicative arithmetic function. 38 | * That is, given a function defined only on the prime numbers, 39 | * provide a values for on composite integers, by factoring them. 40 | * 41 | * A completely multiplicative function is a complex-valued function 42 | * on the positive integers that is a homomorphism under multiplication 43 | * i.e. preserves multiplication, i.e. f(mn) = f(m) f(n) for positive 44 | * integers m,n. Thus, it is sufficient to specify f(p) for prime p, 45 | * all other values are determined by the values on the primes. 46 | * 47 | * Given only f(p), this computes the values at all other integers. 48 | * 49 | * Two variants are provided: a caching and a non-caching version. 50 | * The caching version will call f(p) only one per prime p. The 51 | * non-caching version might call f(p) repeatedly. 52 | * 53 | * @func function providing values on the prime numbers. 54 | * It will only be called for a prime-number argument. 55 | * 'nprec' is the suggested decimal precision at which 'fun' 56 | * should perform its calculations. 57 | * @n number at which results should be computed. 58 | * @nprec number of digits of decimal precision to which intermediate 59 | * terms will be maintained. 60 | */ 61 | void cpx_multiplicative(cpx_t result, 62 | void (*func)(cpx_t f, unsigned long p, int nprec), 63 | unsigned long n, 64 | int nprec); 65 | 66 | void cpx_multiplicative_cached(cpx_t result, 67 | void (*func)(cpx_t f, unsigned long p, int nprec), 68 | unsigned long n, 69 | int nprec); 70 | void cpx_multiplicative_clear_cache(void); 71 | 72 | 73 | #ifdef __cplusplus 74 | }; 75 | #endif 76 | 77 | #endif /* __MP_MULTIPLICATIVE_H__ */ 78 | -------------------------------------------------------------------------------- /src/mp-polylog.h: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-polylog.h 3 | * 4 | * Implement Borwein-style polylogarithm. 5 | * Also implement the "periodic zeta" and 6 | * the Hurwitz zeta function. 7 | * 8 | * Copyright (C) 2006,2007 Linas Vepstas 9 | * 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU Lesser General Public 12 | * License as published by the Free Software Foundation; either 13 | * version 2.1 of the License, or (at your option) any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * Lesser General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Lesser General Public 21 | * License along with this library; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 23 | * 02110-1301 USA 24 | */ 25 | #include "mp-complex.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * cpx_polylog_nint -- compute the polylogarithm at negetive integers 33 | * 34 | * Li_{-n}(z) 35 | * At the negative integers, the polylog is a rational function, 36 | * meromorphic everywhere except for multiple poles at z=1. 37 | */ 38 | void cpx_polylog_nint (cpx_t plog, unsigned int negn, const cpx_t zee); 39 | 40 | /** 41 | * cpx_polylog_sum -- compute the polylogarithm by direct summation 42 | * 43 | * Li_s(z) = sum_{n=1}^infty z^n/ n^s 44 | * 45 | * The magnitude of z must be less than one in order for the 46 | * summation to be caqrried out. 47 | * 48 | * Caches intermediate results, so that overall performance is 49 | * considerably better if z is varied while s is held fixed. 50 | */ 51 | void cpx_polylog_sum (cpx_t plog, const cpx_t ess, const cpx_t zee, int prec); 52 | 53 | /** 54 | * cpx_polylog -- polylogarithm 55 | * 56 | * Li_s(z) = sum_{n=1}^infty z^n/ n^s 57 | * 58 | * Works for general complex s, z; lightly tested, may be buggy. 59 | * Watch out for branchpoint at z=1. 60 | * 61 | * Returns a non-zero value if algo was unable to evaluate at 62 | * the given point. 63 | * 64 | * Possible bug: This may work badly when Re s is negative integer, 65 | * and Im s isn't zero. This is because an internal estimator for 66 | * the number of terms to compute fails for this case. 67 | * 68 | * Actual bug: This will crash when s is a positive integer, and 69 | * magnitude of z is greater than about 1.4 or so. This is because 70 | * computations in this range use a certain inversion formula that 71 | * is singular at these points (it computes gamma(1-s) which has 72 | * poles). 73 | * 74 | * If you really just want the polylog at the postive integers, 75 | * then the algorithm implemented here is probably not your best 76 | * choice, its a bit of an overkill. There are simpler algos that 77 | * would work better, faster, for this special case. 78 | */ 79 | int cpx_polylog (cpx_t plog, const cpx_t ess, const cpx_t zee, int prec); 80 | 81 | /** 82 | * cpx_polylog_euler -- compute the polylogarithm from Hurwitz Euler. 83 | * 84 | * Combine two Hurwitz Euler-Maclaurin evaluations to obtain the polylogarithm. 85 | * 86 | * Note: the current Hurwitz Euler-Maclaurin implementations aren't very 87 | * accurate in the critical strip, so this may be dodgy. 88 | * 89 | * Note: the current algorithm will crash when ess is a positive 90 | * integer. This is because the algo uses gamma(1-s) to combine 91 | * parts together, and of course, gamma is infinite at the neg integers. 92 | * This could be fixed with a fancier algo but has not been, yet. 93 | * XXX FIXME per the above remarks. 94 | */ 95 | void cpx_polylog_euler (cpx_t zeta, const cpx_t ess, const cpx_t zee, int prec); 96 | 97 | /** 98 | * cpx_polylog_sheet -- give the branch difference for the polylog. 99 | * XXX FIXME This API is kind-of broken, it does not quite make sense. 100 | * 101 | * M is the monodromy number of going around z=0 102 | * N is the monodromy number of going around z=1 103 | * 104 | * For M=0, the branch difference is that given between the principle 105 | * sheet, and the N'th winding around the z=1 branch point. 106 | * Thus, for example, the (0,1)'th sheet of Li_s(z) is given by 107 | * (2pi i)^s ((ln z)/(2pi i))^{s-1} / Gamma (s) 108 | * 109 | * For N=0, the monodromy M has no effect. 110 | * For N!=0, the monodromy is given with respect to the N=1 sheet. 111 | */ 112 | void cpx_polylog_sheet(cpx_t delta, const cpx_t ess, const cpx_t zee, int z0_dromy, int z1_dromy, int prec); 113 | 114 | /** Attempt to fix above API. Partial success, only. */ 115 | void cpx_polylog_sheet_g1_action(cpx_t delta, const cpx_t ess, const cpx_t zee, int sheet, int direction, int prec); 116 | 117 | /** Simple global offset for winding around the log at z=0 */ 118 | void cpx_polylog_g0_action(cpx_t delta, const cpx_t ess, int direction, int prec); 119 | 120 | /** New improved API. Add this to the result from cpx_polylog() */ 121 | void cpx_polylog_g1_action(cpx_t delta, const cpx_t ess, const cpx_t zee, int direction, int prec); 122 | 123 | /** 124 | * cpx_periodic_zeta -- Periodic zeta function 125 | * 126 | * F(s,q) = sum_{n=1}^infty exp(2pi iqn)/ n^s 127 | * = Li_s (exp(2pi iq)) 128 | * where 129 | * Li_s(z) is the polylogarithm 130 | * 131 | * Periodic zeta function is defined as F(s,q) by Tom Apostol, chapter 12 132 | */ 133 | void cpx_periodic_zeta (cpx_t z, const cpx_t ess, const mpf_t que, int prec); 134 | 135 | /** 136 | * cpx_periodic_beta -- Periodic beta function 137 | * 138 | * Similar to periodic zeta, but with different normalization 139 | * 140 | * beta = 2 Gamma(s+1) (2\pi)^{-s} F(s,q) 141 | * 142 | * Caches intermediate terms, and so performance is much better 143 | * if s is held const, while q is varied. 144 | */ 145 | void cpx_periodic_beta (cpx_t zee, const cpx_t ess, const mpf_t que, int prec); 146 | 147 | /** 148 | * cpx_hurwitz_zeta -- Hurwitz zeta function 149 | * Returns zeta = sum_{n=0}^infty 1/(n+q)^s 150 | * Accepts complex s, real-valued q. 151 | * 152 | * Built up from the fast polylogarithm algo 153 | * Caches intermediate terms, and so performance is much better 154 | * if s is held const, while q is varied. 155 | * The input value of q must be postive; 156 | * the algo gets slow if q is very large. 157 | */ 158 | void cpx_hurwitz_zeta (cpx_t hzeta, const cpx_t ess, const mpf_t que, int prec); 159 | 160 | /** 161 | * cpx_hurwitz_taylor -- Hurwitz zeta function taylor series 162 | * 163 | * Implement the Hurwitz zeta as a taylor expansion about q=0. 164 | * See source code for detailed explanations about the algo and 165 | * the domain of validity. Returns non-zero if argument is out of 166 | * the domain of convergence. 167 | */ 168 | int cpx_hurwitz_taylor (cpx_t hzeta, const cpx_t ess, const cpx_t que, int prec); 169 | 170 | /** 171 | * cpx_hurwitz_euler -- Hurwitz zeta function via Euler-Maclaurin algo 172 | * 173 | * This function computes the value of the Hurwitz zeta function 174 | * using an Euler-Maclaurin summation to obtain an estimate. 175 | * 176 | * The algorithm appears to work in principle (well, it gets 4 or 5 177 | * digits right), but we are doing a really really bad error estimate. 178 | * So it doesn't work at higher precision, at least not on the critical 179 | * strip. 180 | */ 181 | void cpx_hurwitz_euler_fp(cpx_t hzeta, cpx_t ess, mpf_t que, int prec); 182 | void cpx_hurwitz_euler(cpx_t hzeta, cpx_t ess, cpx_t que, int prec); 183 | 184 | #ifdef __cplusplus 185 | }; 186 | #endif 187 | 188 | -------------------------------------------------------------------------------- /src/mp-quest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-quest.c 3 | * 4 | * High-precision Minkowski Question Mark, Stern-Brocot Tree, etc. 5 | * using the Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2010 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "mp-quest.h" 33 | 34 | void question_mark (mpf_t qmark, const mpf_t x, unsigned int prec) 35 | { 36 | mpf_t ox, h, bits, one, low_bound; 37 | long bitsdone, ibits; 38 | int place; 39 | 40 | mpf_set_ui(qmark, 0); 41 | 42 | /* if x == 0 then we are done */ 43 | if (0 == mpf_sgn(x)) return; 44 | 45 | mpf_init(ox); 46 | mpf_init(h); 47 | mpf_init(bits); 48 | mpf_init(one); 49 | mpf_init(low_bound); 50 | 51 | mpf_set(h, x); 52 | mpf_set_ui(one, 1); 53 | 54 | /* Get the number of binary bits from prec = log_2 10 * prec */ 55 | long nbits = (long) floor (3.321 * prec); 56 | mpf_div_2exp(low_bound, one, nbits-2); 57 | 58 | bitsdone = -1; 59 | place = 1; 60 | while (bitsdone < nbits) 61 | { 62 | // Compute h(x) = 1/x - floor(1/x); 63 | mpf_ui_div(ox, 1, h); 64 | mpf_floor(bits, ox); 65 | mpf_sub(h, ox, bits); 66 | 67 | // bit is just floor(1/x) 68 | ibits = mpf_get_si(bits); 69 | bitsdone += ibits; 70 | 71 | // shift right by bits 72 | mpf_div_2exp(ox, one, bitsdone); 73 | 74 | // Add or subtract dyadic parts 75 | if (place%2 == 1) 76 | { 77 | mpf_add(qmark, qmark, ox); 78 | } 79 | else 80 | { 81 | mpf_sub(qmark, qmark, ox); 82 | } 83 | 84 | // #define DEBUG 1 85 | #ifdef DEBUG 86 | { 87 | double h_f, q_f, b_f; 88 | h_f = mpf_get_d(h); 89 | q_f = mpf_get_d(qmark); 90 | b_f = mpf_get_d(bits); 91 | printf("duuude place=%d, bitsdone=%ld h=%g q=%g bits=%f s=%d\n", 92 | place, bitsdone, h_f, q_f, b_f, mpf_sgn(h)); 93 | } 94 | #endif 95 | // If the remainder is zero, we are done. 96 | // Due to rounding precision, we can't test for explicit zero; 97 | // instead we test for h less than the requested precision. 98 | // if (0 == mpf_sgn(h)) break; 99 | if (mpf_cmp(h, low_bound) < 0) break; 100 | 101 | place ++; 102 | } 103 | 104 | mpf_clear(ox); 105 | mpf_clear(h); 106 | mpf_clear(bits); 107 | mpf_clear(one); 108 | mpf_clear(low_bound); 109 | } 110 | 111 | void question_inverse (mpf_t qinv, const mpf_t x, unsigned int prec) 112 | { 113 | int i, istart, n; 114 | unsigned long idx, last_idx; 115 | mpf_t mantissa; 116 | mpz_t bits; 117 | 118 | mpf_init(mantissa); 119 | mpz_init(bits); 120 | 121 | /* Get the number of binary bits from prec = log_2 10 * prec */ 122 | int nbits = (int) floor (3.321 * prec); 123 | nbits -= 3; 124 | 125 | int *bitcnt = (int *) malloc ((nbits+1) * sizeof(int)); 126 | memset (bitcnt, 0, (nbits+1) * sizeof(int)); 127 | 128 | mpf_mul_2exp(mantissa, x, nbits); 129 | mpz_set_f(bits, mantissa); 130 | 131 | /* Count the number of contiguous bits */ 132 | idx = 0; 133 | last_idx = 0; 134 | n = 0; 135 | // printf("duude nbits=%d\n", nbits); 136 | while (n <= nbits) 137 | { 138 | if (n%2 == 0) 139 | { 140 | idx = mpz_scan0(bits, idx); 141 | } 142 | else 143 | { 144 | idx = mpz_scan1(bits, idx); 145 | } 146 | if (ULONG_MAX == idx) 147 | { 148 | bitcnt[n] = nbits - last_idx; 149 | n++; 150 | break; 151 | } 152 | bitcnt[n] = idx - last_idx; 153 | last_idx = idx; 154 | n++; 155 | } 156 | 157 | /* Compute the corresponding continued fraction */ 158 | mpf_set_ui(qinv, 0); 159 | istart = 2; /* skip over trailing zeroes */ 160 | if (0 != bitcnt[0]) 161 | { 162 | istart = 0; 163 | } 164 | for (i = istart ; i 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * Minkowski Question Mark function. Valid argument range 34 | * is 0 =< x =< 1. 35 | */ 36 | void question_mark (mpf_t qmark, const mpf_t x, unsigned int prec); 37 | 38 | /** 39 | * Inverse of the Minkowski Question Mark function. Valid argument range 40 | * is 0 =< x =< 1. Implemented with a simple, fast bit-counting 41 | * algorithm. 42 | */ 43 | void question_inverse (mpf_t qinv, const mpf_t x, unsigned int prec); 44 | 45 | #ifdef __cplusplus 46 | }; 47 | #endif 48 | 49 | /* =============================== END OF FILE =========================== */ 50 | -------------------------------------------------------------------------------- /src/mp-topsin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-topsin.c 3 | * 4 | * High-precision Topologist's Sine function, 5 | * using the Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2014 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "mp-binomial.h" 34 | #include "mp-consts.h" 35 | #include "mp-topsin.h" 36 | 37 | void topsin_series (mpf_t a_k, unsigned int k, unsigned int prec) 38 | { 39 | unsigned int n; 40 | mpf_t fourpi, numer, fact, low_bound, term, xterm; 41 | mpz_t bino; 42 | 43 | mpf_set_ui(a_k, 0); 44 | 45 | /* If k == 0 then we are done */ 46 | if (0 == k) return; 47 | 48 | mpf_init(fourpi); 49 | mpf_init(numer); 50 | mpf_init(fact); 51 | mpf_init(low_bound); 52 | mpf_init(term); 53 | mpf_init(xterm); 54 | mpz_init(bino); 55 | 56 | fp_two_pi(numer, prec); 57 | mpf_neg(numer, numer); 58 | 59 | // fourpi is actually -4pi^2 60 | mpf_mul(fourpi, numer, numer); 61 | mpf_neg(fourpi, fourpi); 62 | 63 | mpf_set_ui(fact, 1); 64 | 65 | /* Get the number of binary bits from prec = log_2 10 * prec */ 66 | long nbits = (long) floor (3.321 * prec); 67 | mpf_div_2exp(low_bound, fact, nbits+32); 68 | 69 | for (n=0; n<2023123123; n++) 70 | { 71 | i_binomial(bino, 2*n+k, 2*n); 72 | mpf_set_z(term, bino); 73 | mpf_mul(xterm, term, numer); 74 | mpf_div(term, xterm, fact); 75 | 76 | mpf_add(a_k, a_k, term); 77 | 78 | // #define DEBUG 1 79 | #ifdef DEBUG 80 | { 81 | double h_f, q_f, b_f; 82 | h_f = mpf_get_d(h); 83 | q_f = mpf_get_d(qmark); 84 | b_f = mpf_get_d(bits); 85 | printf("duuude place=%d, bitsdone=%ld h=%g q=%g bits=%f s=%d\n", 86 | place, bitsdone, h_f, q_f, b_f, mpf_sgn(h)); 87 | } 88 | #endif 89 | // If the term is small enough, we are done. 90 | mpf_abs(xterm, term); 91 | if (mpf_cmp(xterm, low_bound) < 0) break; 92 | 93 | // Now iterate 94 | mpf_mul(numer, numer, fourpi); 95 | mpf_mul_ui(fact, fact, (2*n+3)*2*(n+1)); 96 | } 97 | 98 | if (k%2 == 0) mpf_neg(a_k, a_k); 99 | 100 | mpf_clear(fourpi); 101 | mpf_clear(numer); 102 | mpf_clear(fact); 103 | mpf_clear(low_bound); 104 | mpf_clear(term); 105 | mpf_clear(xterm); 106 | mpz_clear(bino); 107 | } 108 | 109 | /* ================================================================ */ 110 | 111 | // #define RUN_TEST 112 | #ifdef RUN_TEST 113 | 114 | #include "mp-trig.h" 115 | #include 116 | 117 | bool sum_test(double xf, int prec) 118 | { 119 | bool fail = false; 120 | int k; 121 | mpf_t a_k, x, xn, term, sum, sino, low_bound; 122 | 123 | mpf_init(a_k); 124 | mpf_init(x); 125 | mpf_init(xn); 126 | mpf_init(term); 127 | mpf_init(sum); 128 | mpf_init(sino); 129 | mpf_init(low_bound); 130 | 131 | long nbits = (long) floor (3.321 * prec); 132 | mpf_set_ui(x, 1); 133 | mpf_div_2exp(low_bound, x, nbits+32); 134 | 135 | // Sum the series sum_k=1^\infty a_k x^k 136 | // It should equal sin(2pi/(1+x)) 137 | mpf_set_d(x, xf); 138 | mpf_set_ui(sum, 0); 139 | mpf_set(xn, x); 140 | for (k=1; k<100000; k++) 141 | { 142 | topsin_series(a_k, k, prec); 143 | mpf_mul(term, a_k, xn); 144 | mpf_add(sum, sum, term); 145 | 146 | // If the term is small enough, we are done. 147 | mpf_abs(term, term); 148 | if (mpf_cmp(term, low_bound) < 0) break; 149 | 150 | mpf_mul(xn, xn, x); 151 | } 152 | 153 | // Now compute sin(2pi/(1+x)) 154 | mpf_add_ui(term, x, 1); 155 | fp_two_pi(sino, prec); 156 | mpf_div(term, sino, term); 157 | 158 | fp_sine(sino, term, prec); 159 | 160 | // the sum and the sine should be equal 161 | mpf_sub(term, sino, sum); 162 | double zero = mpf_get_d(term); 163 | 164 | double lim = pow(10.0, -prec); 165 | if (fabs(zero) > lim) 166 | { 167 | printf("Error: Expecting precision 1.0e-%d got %g at x=%f\n", prec, zero, xf); 168 | fail = true; 169 | } 170 | 171 | mpf_clear(a_k); 172 | mpf_clear(x); 173 | mpf_clear(xn); 174 | mpf_clear(term); 175 | mpf_clear(sum); 176 | mpf_clear(sino); 177 | mpf_clear(low_bound); 178 | 179 | return fail; 180 | } 181 | 182 | int main (int argc, char * argv[]) 183 | { 184 | mpf_t a_k; 185 | int prec, nbits; 186 | 187 | prec = 50; 188 | 189 | /* Set the precision (number of binary bits) */ 190 | /* We need more bits than what what is available, for intermediate calcs */ 191 | nbits = 3.3*prec; 192 | mpf_set_default_prec (nbits+200); 193 | 194 | mpf_init(a_k); 195 | 196 | // a_1 should be -2pi 197 | topsin_series(a_k, 1, prec); 198 | double twopi = mpf_get_d(a_k); 199 | twopi += 2.0*M_PI; 200 | if (fabs(twopi) > 1.0e-16) printf("Error at k=1: %g\n", twopi); 201 | 202 | // a_2 should be +2pi 203 | topsin_series(a_k, 2, prec); 204 | twopi = mpf_get_d(a_k); 205 | twopi -= 2.0*M_PI; 206 | if (fabs(twopi) > 1.0e-16) printf("Error at k=2: %g\n", twopi); 207 | 208 | // a_3 should be 2pi (3-2pi^2) / 3 = -35.05851693322 209 | 210 | double x; 211 | bool fail = false; 212 | for (x=0.95; x>-0.95; x -= 0.018756) 213 | { 214 | bool result = sum_test(x, prec); 215 | fail = fail || result; 216 | printf("."); fflush(stdout); 217 | } 218 | printf("\n"); 219 | 220 | if (fail) printf("Error: test failed\n"); 221 | else printf("Success: test worked\n"); 222 | 223 | return 0; 224 | } 225 | #endif 226 | 227 | // #define PRINT_OUT_AK 228 | #ifdef PRINT_OUT_AK 229 | int main (int argc, char * argv[]) 230 | { 231 | mpf_t a_k; 232 | int prec, nbits; 233 | 234 | prec = 120; 235 | 236 | /* Set the precision (number of binary bits) */ 237 | /* We need more bits than what what is available, for intermediate calcs */ 238 | nbits = 3.3*prec; 239 | mpf_set_default_prec (nbits+200); 240 | 241 | mpf_init(a_k); 242 | 243 | printf("#\n# The topsin series a_k\n#\n"); 244 | 245 | int k; 246 | double akprev=0.0; 247 | for (k=0; k<95001; k++) 248 | { 249 | topsin_series(a_k, k, prec); 250 | double ak = mpf_get_d(a_k); 251 | 252 | printf("%d %20.16g %20.16g\n", k, ak, ak+akprev); 253 | akprev = ak; 254 | } 255 | 256 | return 0; 257 | } 258 | #endif 259 | 260 | /* =============================== END OF FILE =========================== */ 261 | -------------------------------------------------------------------------------- /src/mp-topsin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-topsin.h 3 | * 4 | * High-precison Topologists Sine function coefficients 5 | * using the Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2014 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | * 24 | */ 25 | 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * Series coeffcients of the topologists sine function. This computes 34 | * and returns the coefficients a_k from the series 35 | * 36 | * sin(2pi / (1+x)) = sum_k=0^\infty a_k x^k 37 | * 38 | * The singularity is placed at x=-1 because that is it's natural 39 | * location for number-theoretic applications. 40 | */ 41 | void topsin_series (mpf_t a_k, unsigned int k, unsigned int prec); 42 | 43 | 44 | #ifdef __cplusplus 45 | }; 46 | #endif 47 | 48 | /* =============================== END OF FILE =========================== */ 49 | -------------------------------------------------------------------------------- /src/mp-trig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-trig.h 3 | * 4 | * High-precision Elementary trigonometric functions, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Copyright (C) 2005 Linas Vepstas 8 | * 9 | * This library is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation; either 12 | * version 2.1 of the License, or (at your option) any later version. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 | * 02110-1301 USA 23 | * 24 | */ 25 | 26 | #include 27 | #include "mp-complex.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | /** 34 | * i_pow - raise n to the m power 35 | */ 36 | 37 | void i_pow (mpz_t p, unsigned int n, unsigned int m); 38 | 39 | /** 40 | * fp_inv_pow - raise n to the -m power, where m must be positive. 41 | */ 42 | void fp_inv_pow (mpf_t p, unsigned int n, unsigned int m); 43 | 44 | /** 45 | * fp_exp - Floating point exponential 46 | * Implemented using a brute-force, very simple algo, with 47 | * no attempts at optimization. Also, does not assume any 48 | * precomputed constants. 49 | * 50 | * The complex exp is built up from the real trig functions. 51 | * The complex trig functions are built up from the complex exp. 52 | * In all cases, the most basic idents are used, so these 53 | * are not speedy!. 54 | */ 55 | void fp_exp (mpf_t ex, const mpf_t z, unsigned int prec); 56 | void fp_sine (mpf_t sine, const mpf_t z, unsigned int prec); 57 | void fp_cosine (mpf_t cosine, const mpf_t z, unsigned int prec); 58 | void cpx_exp (cpx_t ex, const cpx_t z, unsigned int prec); 59 | void cpx_sine (cpx_t sine, const cpx_t z, unsigned int prec); 60 | void cpx_cosine (cpx_t cosine, const cpx_t z, unsigned int prec); 61 | void cpx_tangent (cpx_t tang, const cpx_t z, unsigned int prec); 62 | 63 | /** 64 | * fp_log - Floating point logarithm 65 | * Implemented using a brute-force, simple algo, with 66 | * minor attempts at optimization. 67 | * 68 | * fp_log_m1 computes -log(1-z) using Taylor's expansion for small z. 69 | * Does not perform any other optimizations -- just simply sums the 70 | * Taylor series, and that's all. 71 | * 72 | * fp_log_ui takes integer arguments, and keeps previous 73 | * results cached for improved performance. 74 | */ 75 | void fp_log_m1 (mpf_t lg, const mpf_t z, unsigned int prec); 76 | void fp_log (mpf_t lg, const mpf_t z, unsigned int prec); 77 | void fp_log_ui (mpf_t lg, unsigned int z, unsigned int prec); 78 | void cpx_log_m1 (cpx_t lg, const cpx_t z, unsigned int prec); 79 | void cpx_log (cpx_t lg, const cpx_t z, unsigned int prec); 80 | 81 | /** 82 | * fp_arctan - Floating point arctangent 83 | * Implemented using a brute-force, very simple algo, with 84 | * no attempts at optimization. Very slow near y=x 85 | */ 86 | void fp_arctan (mpf_t atn, const mpf_t z, unsigned int prec); 87 | void fp_arctan2 (mpf_t atn, const mpf_t y, const mpf_t x, unsigned int prec); 88 | 89 | /** 90 | * cpx_sqrt 91 | * Simple implementation of complex square-root 92 | */ 93 | void cpx_sqrt (cpx_t sqrt, const cpx_t zee, int prec); 94 | 95 | /** 96 | * cpx_pow-- return q^s for complex q, s. 97 | * 98 | * Brute-force algo, this thing is pretty slow, as it requires 99 | * a logarithm, an exp, sin and cos to be computed, each of which 100 | * are kinda slow ... 101 | */ 102 | void cpx_pow (cpx_t powc, const cpx_t q, const cpx_t ess, int prec); 103 | 104 | /** 105 | * cpx_mpf_pow-- return q^s for complex s, real, positive q. 106 | * 107 | * Brute-force algo, this thing is pretty slow, as it requires 108 | * a logarithm, an exp, sin and cos to be computed, each of which 109 | * are kinda slow ... 110 | */ 111 | void cpx_mpf_pow (cpx_t powc, const mpf_t q, const cpx_t ess, int prec); 112 | 113 | /** 114 | * cpx_pow_ui-- return q^n for complex q, positive integer n. 115 | * Should be fairly speedy, as it uses a log(n) implementation 116 | */ 117 | void cpx_pow_ui (cpx_t powc, const cpx_t q, unsigned int n); 118 | 119 | /** 120 | * cpx_ui_pow -- return k^s for complex s, integer k. 121 | * 122 | * Uses a brute-force algo: it requires a logarithm, an exp, sin 123 | * and cos to be computed, each of which are kinda slow ... 124 | */ 125 | void cpx_ui_pow (cpx_t powc, unsigned int k, const cpx_t ess, int prec); 126 | 127 | /** 128 | * cpx_ui_pow_cache -- return k^s for complex s, integer k. 129 | * 130 | * If s is held fixed, and k varied, then the values are cached, 131 | * allowing improved algorithm speeds. If s is changed from call 132 | * to call, then the cache is cleared. 133 | */ 134 | void cpx_ui_pow_cache (cpx_t powc, unsigned int k, const cpx_t ess, int prec); 135 | 136 | /** 137 | * cpx_harmonic -- return the generalized harmonic number 138 | * H_n(s) = sum_k=1^n k^-s 139 | * 140 | * This is implemented as a brute-force summation, based on above. 141 | */ 142 | void cpx_harmonic(cpx_t hns, unsigned int n, const cpx_t ess, int prec); 143 | 144 | /** 145 | * fp_pow_rc-- return (k+q)^s for complex s, integer k, real q. 146 | * 147 | * If q is held fixed, and k varied, then the values are cached, 148 | * allowing improved algorithm speeds. 149 | */ 150 | 151 | void fp_pow_rc (cpx_t diri, int k, const mpf_t q, const cpx_t ess, int prec); 152 | void cpx_pow_rc (cpx_t diri, int k, const cpx_t q, const cpx_t ess, int prec); 153 | 154 | #ifdef __cplusplus 155 | }; 156 | #endif 157 | 158 | /* =============================== END OF FILE =========================== */ 159 | 160 | -------------------------------------------------------------------------------- /src/mp-zerofind.h: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-zerofind.h 3 | * 4 | * Locate complex zeros of a function. 5 | * 6 | * Copyright (C) 2010 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #ifndef __MP_ZEROFIND_H__ 25 | #define __MP_ZEROFIND_H__ 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include "mp-complex.h" 32 | 33 | /* =============================================== */ 34 | /** 35 | * cpx_find_zero. 36 | * Numerically locate the zero of a complex-valued function. 37 | * 38 | * @func function whose zeros are to be found. 39 | * func takes z as input, returns f as output. 40 | * 'nprec' is the suggested decimal precision at which 'fun' 41 | * should perform its calculations. 42 | * @initial_z initial suggestion for the location of the zero. 43 | * @e1, @e2 initial suggestions for a bounding ellipse. These are 44 | * taken to be two vectors, specifying the major and minor axes of 45 | * an ellipse, centered at 'initial_z'. The true zero is presumed 46 | * to lie inside of, or at least, close to, this initial ellipse. 47 | * @ndigits number of decimal digits of accuracy to which the zero 48 | * should be searched for. 49 | * @nprec number of digits of decimal precision to which intermediate 50 | * terms will be maintained. 51 | * 52 | * @returns 0 if result is valid, else an error code. 53 | * 54 | * This implements a search by fitting to a conic sections -- i.e. assumes 55 | * a simple zero. 56 | * 57 | * Note that there are superior algos for finding roots of analytic 58 | * functions. This is rather a quick and easy hack that fits my current 59 | * needs. 60 | */ 61 | int cpx_find_zero(cpx_t result, 62 | void (*func)(cpx_t f, cpx_t z, int nprec), 63 | cpx_t initial_z, 64 | cpx_t e1, cpx_t e2, 65 | int ndigits, int nprec); 66 | 67 | /** Reentrant version of above. Passes user-defined args to function. */ 68 | int cpx_find_zero_r(cpx_t result, 69 | void (*func)(cpx_t f, cpx_t z, int nprec, void*), 70 | cpx_t initial_z, 71 | cpx_t e1, cpx_t e2, 72 | int ndigits, int nprec, void* args); 73 | 74 | #ifdef __cplusplus 75 | }; 76 | #endif 77 | 78 | #endif /* __MP_ZEROFIND_H__ */ 79 | -------------------------------------------------------------------------------- /src/mp-zeroiso.c: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-zeroiso.c 3 | * 4 | * Isolate complex zeros of a polynomial. 5 | * 6 | * Copyright (C) 2024 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "mp-complex.h" 29 | #include "mp-zeroiso.h" 30 | 31 | /* =============================================== */ 32 | 33 | // Bounding box 34 | typedef struct box 35 | { 36 | cpx_t boxll; 37 | cpx_t boxur; 38 | struct box* next; 39 | } box_t; 40 | 41 | static box_t* box_new(box_t* head, cpx_t boxll, cpx_t boxur) 42 | { 43 | box_t* b = (box_t*) malloc(sizeof(box_t)); 44 | cpx_init(b->boxll); 45 | cpx_init(b->boxur); 46 | cpx_set(b->boxll, boxll); 47 | cpx_set(b->boxur, boxur); 48 | b->next = head; 49 | head = b; 50 | return b; 51 | } 52 | 53 | static box_t* box_delete(box_t* head) 54 | { 55 | cpx_clear(head->boxll); 56 | cpx_clear(head->boxur); 57 | box_t* next = head->next; 58 | free(head); 59 | return next; 60 | } 61 | 62 | // Split box into four 63 | static box_t* box_split(box_t* head) 64 | { 65 | cpx_t center; 66 | cpx_init(center); 67 | cpx_add(center, head->boxll, head->boxur); 68 | cpx_div_ui(center, center, 2); 69 | 70 | cpx_t cll; 71 | cpx_init(cll); 72 | cpx_t cur; 73 | cpx_init(cur); 74 | 75 | // The LL and UR quadrants. 76 | box_t* orig = head; 77 | cpx_set(cll, orig->boxll); // ??? Compiler complains if I don't use indirection. 78 | head = box_new(head, cll, center); 79 | cpx_set(cur, orig->boxur); 80 | head = box_new(head, center, cur); 81 | 82 | // The UL quadrant 83 | mpf_set(cll[0].re, orig->boxll[0].re); 84 | mpf_set(cll[0].im, center[0].im); 85 | 86 | mpf_set(cur[0].re, center[0].re); 87 | mpf_set(cur[0].im, orig->boxur[0].im); 88 | 89 | head = box_new(head, cll, cur); 90 | 91 | // Clobber the original for the LR quadrant 92 | mpf_set(orig->boxll[0].re, center[0].re); 93 | // mpf_set(orig->boxll[0].im, unchanged. 94 | // mpf_set(orig->boxur[0].re, unchanged. 95 | mpf_set(orig->boxur[0].im, center[0].im); 96 | 97 | cpx_clear(center); 98 | cpx_clear(cll); 99 | cpx_clear(cur); 100 | return head; 101 | } 102 | 103 | static void box_midpoint(box_t* box, cpx_t center) 104 | { 105 | cpx_add(center, box->boxll, box->boxur); 106 | cpx_div_ui(center, center, 2); 107 | } 108 | 109 | static void box_radius(box_t* box, mpf_t radius) 110 | { 111 | mpf_t rim; 112 | mpf_init(rim); 113 | mpf_sub(radius, box->boxur[0].re, box->boxll[0].re); 114 | mpf_sub(rim, box->boxur[0].im, box->boxll[0].im); 115 | 116 | // The larger of the two. 117 | // mpf_cmp(a,b) is +1 if a>b 118 | if (0 < mpf_cmp(rim, radius)) 119 | mpf_set(radius, rim); 120 | 121 | mpf_mul_ui(radius, radius, 3); 122 | mpf_div_ui(radius, radius, 4); 123 | mpf_clear(rim); 124 | } 125 | 126 | /* =============================================== */ 127 | 128 | // Test function. Implemented according to what the paper says. 129 | // Offset should be zero or one. 130 | static void test_fun(mpf_t bound, 131 | void (*poly)(cpx_t f, int deriv, cpx_t z, void* args), 132 | int degree, cpx_t center, mpf_t radius, int offset, void* args) 133 | { 134 | cpx_t eval; 135 | cpx_init(eval); 136 | 137 | mpf_t term, rk, fact; 138 | mpf_init(term); 139 | mpf_init(rk); 140 | mpf_init(fact); 141 | 142 | mpf_set_ui(fact, 1); 143 | mpf_set_ui(rk, 1); 144 | 145 | mpf_set_ui(bound, 0); 146 | for (int k=1; k<=degree-offset; k++) 147 | { 148 | mpf_mul_ui(fact, fact, k); 149 | mpf_mul(rk, rk, radius); 150 | 151 | poly(eval, k+offset, center, args); 152 | cpx_abs(term, eval); 153 | mpf_mul(term, term, rk); 154 | mpf_div(term, term, fact); 155 | mpf_add(bound, bound, term); 156 | } 157 | 158 | // Divide by norm 159 | poly(eval, offset, center, args); 160 | cpx_abs(term, eval); 161 | if (0 != mpf_sgn(term)) 162 | mpf_div(bound, bound, term); 163 | else 164 | fprintf(stderr, "Error: polynomial has double-zero at z=%g +i %g\n" 165 | "Results will be incomplete/incorrect\n", 166 | cpx_get_re(center), cpx_get_im(center)); 167 | 168 | cpx_clear(eval); 169 | mpf_clear(fact); 170 | mpf_clear(rk); 171 | mpf_clear(term); 172 | } 173 | 174 | /* =============================================== */ 175 | 176 | static mpf_t one; 177 | static mpf_t hsqrt2; 178 | static bool init = false; 179 | 180 | // Test pedicate. Implemented according to what the paper says. 181 | static bool test_predicate( 182 | void (*poly)(cpx_t f, int deriv, cpx_t z, void* args), 183 | int degree, cpx_t center, mpf_t radius, int offset, void* args) 184 | { 185 | if (false == init) 186 | { 187 | mpf_init(one); 188 | mpf_set_ui(one, 1); 189 | mpf_init(hsqrt2); 190 | mpf_sqrt_ui(hsqrt2, 2); 191 | mpf_div_ui(hsqrt2, hsqrt2, 2); 192 | init = true; 193 | } 194 | mpf_t est; 195 | mpf_init(est); 196 | 197 | bool test = false; 198 | if (0 == offset) 199 | { 200 | test_fun(est, poly, degree, center, radius, 0, args); 201 | // mpf_cmp(a,b) is +1 if a>b 202 | test = (0 < mpf_cmp(one, est)); 203 | } 204 | else 205 | { 206 | mpf_t rad; 207 | mpf_init(rad); 208 | mpf_mul_ui(rad, radius, 4*degree); 209 | 210 | test_fun(est, poly, degree, center, rad, 1, args); 211 | // mpf_cmp(a,b) is +1 if a>b 212 | test = (0 < mpf_cmp(hsqrt2, est)); 213 | 214 | mpf_clear(rad); 215 | } 216 | 217 | mpf_clear(est); 218 | return test; 219 | } 220 | 221 | /* =============================================== */ 222 | 223 | // Return true if two dists overlap. 224 | // Disk centers are ca, cb, disk radii are ra, rb 225 | bool disk_intersect(cpx_t ca, mpf_t ra, cpx_t cb, mpf_t rb) 226 | { 227 | cpx_t diff; 228 | cpx_init(diff); 229 | mpf_t dist; 230 | mpf_init(dist); 231 | 232 | cpx_sub(diff, ca, cb); 233 | cpx_abs(dist, diff); 234 | mpf_sub(dist, dist, ra); 235 | // mpf_cmp(a,b) is +1 if a>b 236 | bool rc = (0 < mpf_cmp(rb, dist)); 237 | 238 | cpx_clear(diff); 239 | mpf_clear(dist); 240 | 241 | return rc; 242 | } 243 | 244 | /* =============================================== */ 245 | 246 | /** 247 | * Implements 248 | * Michael Sagraloff, Chee K. Yap, "A Simple But Exact and Efficient 249 | * Algorithm for Complex Root Isolation" (2011) 250 | * https://cs.nyu.edu/exact/doc/complex.pdf 251 | */ 252 | int cpx_isolate_roots( 253 | void (*poly)(cpx_t f, int deriv, cpx_t z, void* args), 254 | int degree, 255 | cpx_t boxll, cpx_t boxur, 256 | cpx_t* centers, mpf_t* radii, 257 | void* args) 258 | { 259 | cpx_t midpoint; 260 | cpx_init(midpoint); 261 | 262 | mpf_t radius; 263 | mpf_init(radius); 264 | 265 | int nfound = 0; 266 | 267 | // Avoid user error with swapped box coordinates 268 | if (0 < mpf_cmp(boxll[0].re, boxur[0].re)) 269 | { 270 | mpf_set(radius, boxll[0].re); 271 | mpf_set(boxll[0].re, boxur[0].re); 272 | mpf_set(boxur[0].re, radius); 273 | } 274 | if (0 < mpf_cmp(boxll[0].im, boxur[0].im)) 275 | { 276 | mpf_set(radius, boxll[0].im); 277 | mpf_set(boxll[0].im, boxur[0].im); 278 | mpf_set(boxur[0].im, radius); 279 | } 280 | 281 | box_t* head = box_new(NULL, boxll, boxur); 282 | while (NULL != head) 283 | { 284 | box_midpoint(head, midpoint); 285 | box_radius(head, radius); 286 | 287 | // First test; if it passes, the box is empty and discard it. 288 | bool result = test_predicate(poly, degree, midpoint, radius, 0, args); 289 | if (result) 290 | { 291 | head = box_delete(head); 292 | continue; 293 | } 294 | 295 | // Second test; if it fails, split box into four and try again. 296 | result = test_predicate(poly, degree, midpoint, radius, 1, args); 297 | if (false == result) 298 | { 299 | head = box_split(head); 300 | continue; 301 | } 302 | 303 | // Found a box with one root in it. 304 | mpf_mul_ui(radius, radius, 2*degree); 305 | 306 | bool replaced = false; 307 | for (int n=0; nb 312 | if (0 < mpf_cmp(radii[n], radius)) 313 | { 314 | cpx_set(centers[n], midpoint); 315 | mpf_set(radii[n], radius); 316 | } 317 | replaced = true; 318 | break; 319 | } 320 | } 321 | if (false == replaced) 322 | { 323 | cpx_set(centers[nfound], midpoint); 324 | mpf_set(radii[nfound], radius); 325 | nfound++; 326 | } 327 | 328 | // We are done with this one. 329 | head = box_delete(head); 330 | } 331 | 332 | cpx_clear(midpoint); 333 | mpf_clear(radius); 334 | return nfound; 335 | } 336 | -------------------------------------------------------------------------------- /src/mp-zeroiso.h: -------------------------------------------------------------------------------- 1 | /** 2 | * mp-zeroiso.h 3 | * 4 | * Isolate complex zeros of a polynomial. 5 | * 6 | * Copyright (C) 2024 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #ifndef __MP_ZEROISO_H__ 25 | #define __MP_ZEROISO_H__ 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include "mp-complex.h" 32 | 33 | /* =============================================== */ 34 | /** 35 | * cpx_isolate_roots. 36 | * Isolate all zeros of a complex-valued square-free polynomial. 37 | * Returns a list of disks, such that each disk contains exactly 38 | * one zero (one root). 39 | * 40 | * Implements 41 | * Michael Sagraloff, Chee K. Yap, "A Simple But Exact and Efficient 42 | * Algorithm for Complex Root Isolation" (2011) 43 | * https://cs.nyu.edu/exact/doc/complex.pdf 44 | * 45 | * Requirements: 46 | * 1) The polynomial must be square-free, i.e. it cannot have 47 | * degenerate zeros, i.e. two zero located at the same place. 48 | * 2) A means of evaluating the derivative, to all orders, must be 49 | * provided. 50 | * 51 | * @poly polynomial whose zeros are to be found. 52 | * poly takes z and k as input, returns f^(k)(z) as output. 53 | * @degree Degree of the polynomial. 54 | * @boxll, @boxur bounding box lower-left and upper-right coordinates. 55 | * The search for zeros will be performed inside this box. 56 | * 57 | * Fills in: 58 | * @centers Array of centers of disks, each disk containing one zero. 59 | * @radii Array of radii of the disks. 60 | * Both of these must be provided by the caller, and must be of length 61 | * at least equal to degree of the polynomial. 62 | * 63 | * Returns number of zeros found. 64 | */ 65 | int cpx_isolate_roots( 66 | void (*poly)(cpx_t f, int deriv, cpx_t z, void* args), 67 | int degree, 68 | cpx_t boxll, cpx_t boxur, 69 | cpx_t* centers, mpf_t* radii, 70 | void* args); 71 | 72 | #ifdef __cplusplus 73 | }; 74 | #endif 75 | 76 | #endif /* __MP_ZEROISO_H__ */ 77 | -------------------------------------------------------------------------------- /src/mp-zeta.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mp-zeta.h 3 | * 4 | * High-precison Riemann zeta function, using the 5 | * Gnu Multiple-precision library. 6 | * 7 | * Also, high-precision values of the series a_n 8 | * 9 | * Copyright (C) 2005 Linas Vepstas 10 | * 11 | * This library is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public 22 | * License along with this library; if not, write to the Free Software 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 24 | * 02110-1301 USA 25 | */ 26 | 27 | #include 28 | #include "mp-complex.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | /** Bernoulli number B_n given as a fixed-point number. */ 35 | void q_bernoulli (mpq_t bern, int n); 36 | 37 | /** Compute and return the "exact" result for the zeta function for 38 | * any value of even n. Computed to `prec` decimal places. 39 | * Zeta at the even values of n can be given exactly as a product of 40 | * a Bernoulli number and factors of pi. 41 | * This proceeds by computing the Bernoulli number first. 42 | */ 43 | void fp_zeta_even (mpf_t zeta, unsigned int n, int prec); 44 | 45 | /** fp_zeta 46 | * Floating-point-valued Riemann zeta for positive integer arguments. 47 | * Return value is placed in the arg "zeta". 48 | * 49 | * Calculations performed to `prec` decimal digits. 50 | */ 51 | void fp_zeta (mpf_t zeta, unsigned int s, int prec); 52 | 53 | /** Same, using Helmut Hasse convergent algo. */ 54 | void fp_hasse_zeta (mpf_t zeta, unsigned int s, int prec); 55 | 56 | /** Same, using P. Borwein convergent algo. */ 57 | void fp_borwein_zeta (mpf_t zeta, unsigned int s, int prec); 58 | 59 | /** 60 | * cpx_borwein_zeta -- use the Borwein algorithm for complex argument 61 | * 62 | * Compute and return a value for the Riemann zeta function, for 63 | * a complex value of 's'. Uses the P. Borwein algorithm for 64 | * rapid computation. 65 | */ 66 | void cpx_borwein_zeta (cpx_t zeta, const cpx_t ess, int prec); 67 | 68 | /** 69 | * cpx_borwein_zeta_cache -- Caching Riemann zeta for complex argument 70 | * 71 | * Compute and return a value for the Riemann zeta function, for 72 | * a complex value of 's+n'. Uses the P. Borwein algorithm for 73 | * rapid computation. 74 | * 75 | * If the value of 's' is held constant, while the value of 'n' 76 | * is varied, then this routine caches the computed values, 77 | * returning the cached values on the second and later calls, 78 | * thus avoiding the overhead of repeated recalculation. 79 | */ 80 | void cpx_borwein_zeta_cache (cpx_t zeta, const cpx_t ess, unsigned int n, int prec); 81 | 82 | /** Brute-force summation. */ 83 | void fp_zeta_brute (mpf_t zeta, unsigned int s, int prec); 84 | 85 | /* Stieltjes constants */ 86 | // void stieltjes_gamma (mpf_t gam, int n); 87 | 88 | /** 89 | * Compute a_sub_n 90 | * the w argument is for the power bit -- 91 | */ 92 | void a_sub_n (mpf_t a_n, mpf_t w, unsigned int n, unsigned int prec); 93 | void b_sub_n (mpf_t b_n, unsigned int n, unsigned int prec); 94 | 95 | /** 96 | * compute a_sub_s for complex-valued s 97 | */ 98 | void a_sub_s (mpf_t re_a, mpf_t im_a, double re_s, double im_s, unsigned int prec); 99 | void b_sub_s_d (mpf_t re_a, mpf_t im_a, double re_s, double im_s, 100 | unsigned int prec, int nterms, double eps); 101 | void b_sub_s (mpf_t re_a, mpf_t im_a, mpf_t re_s, mpf_t im_s, 102 | unsigned int prec, int nterms, double eps); 103 | 104 | #ifdef __cplusplus 105 | }; 106 | #endif 107 | 108 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # build assorted tests for the Gnu MP code 3 | # 4 | # CC = cc -pg 5 | CC = cc 6 | 7 | 8 | EXES= polylog-bug unit-test zero-iso 9 | 10 | MPLIB=../src/libanant.a 11 | INC=../src 12 | 13 | all: $(EXES) 14 | 15 | polylog-bug.o: $(INC)/mp-binomial.h $(INC)/mp-complex.h \ 16 | $(INC)/mp-misc.h $(INC)/mp-polylog.h 17 | unit-test.o: $(INC)/mp-zeta.h $(INC)/mp-binomial.h $(INC)/mp-complex.h \ 18 | $(INC)/mp-consts.h $(INC)/mp-gamma.h $(INC)/mp-misc.h \ 19 | $(INC)/mp-polylog.h $(INC)/mp-trig.h 20 | zero-iso.o: $(INC)/mp-complex.h $(INC)/mp-zeroiso.h 21 | 22 | polylog-bug: polylog-bug.o $(MPLIB) 23 | zero-iso: zero-iso.o $(MPLIB) 24 | 25 | unit-test: unit-test.o $(MPLIB) 26 | $(CC) -o unit-test $^ -lgmp -lgsl -lgslcblas -ldb -lm -lc 27 | 28 | clean: 29 | rm -f core tmp junk glop a.out *.o 30 | 31 | realclean: clean 32 | rm -f $(EXES) *.png *.jpeg *.tex *.dat *.ps *.eps 33 | 34 | .c.o: 35 | $(CC) -c -g -O2 -Wall -I. -I../src $< 36 | 37 | .o: 38 | $(CC) -g -o $* $^ $(MPLIB) -lgmp -ldb -lm 39 | -------------------------------------------------------------------------------- /tests/polylog-bug.c: -------------------------------------------------------------------------------- 1 | /* polylog-bug.c -- Ad-hoc bug report -- Test calc of Dilogarithm. */ 2 | 3 | /* Last change: 27-Mar-2012. Ken Roberts */ 4 | 5 | /* Calc Li_2(u+iv) 6 | * then Li_2(u+iv) 7 | * again. Expect consistent results. 8 | * Looking for possible bug re caching 9 | * of info within cpx_polylog logic. 10 | * 11 | * This exhibits a caching bug in anant-0.2.0 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "mp-complex.h" 27 | #include "mp-polylog.h" 28 | 29 | ulong gprec; 30 | ulong aprec; 31 | 32 | int main ( 33 | int argc, 34 | char * argv[]) 35 | { 36 | 37 | double u, v; 38 | cpx_t s, z, pl1b, pl1e; 39 | int res; 40 | 41 | gprec = 1000; 42 | mpf_set_default_prec (gprec); 43 | aprec = 50; 44 | 45 | cpx_init (s); 46 | cpx_init (z); 47 | 48 | cpx_set_ui (s, 2, 0); 49 | 50 | u = 0.000005; 51 | v = 1.377128; 52 | 53 | cpx_set_d (z, u, v); 54 | 55 | printf ("\n"); 56 | gmp_printf ("s = %10Ff + i %10Ff\n", 57 | s[0].re, s[0].im); 58 | gmp_printf ("z = %10Ff + i %10Ff\n", 59 | z[0].re, z[0].im); 60 | 61 | cpx_init (pl1b); 62 | cpx_init (pl1e); 63 | 64 | res = cpx_polylog (pl1b, s, z, aprec); 65 | gmp_printf ("Rtn %d, Li_2(z) = %10Ff + i %10Ff\n", 66 | res, pl1b[0].re, pl1b[0].im); 67 | 68 | res = cpx_polylog (pl1e, s, z, aprec); 69 | gmp_printf ("Rtn %d, Li_2(z) = %10Ff + i %10Ff\n", 70 | res, pl1e[0].re, pl1e[0].im); 71 | 72 | printf ("\n"); 73 | exit (0); 74 | }; 75 | -------------------------------------------------------------------------------- /tests/zero-iso.c: -------------------------------------------------------------------------------- 1 | /* 2 | * zero-iso.c 3 | * 4 | * Debug unit test for zero isolation. 5 | * 6 | * Copyright (C) 2024 Linas Vepstas 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 | * 02110-1301 USA 22 | */ 23 | 24 | #include 25 | #include "mp-zeroiso.h" 26 | 27 | /* ==================================================================== */ 28 | 29 | // Simple degree two polynomial, (x-1)(x-1/2) 30 | void poly2(cpx_t f, int deriv, cpx_t z, void* args) 31 | { 32 | cpx_t zn; 33 | cpx_init(zn); 34 | if (0 == deriv) 35 | { 36 | // + 1/2 37 | cpx_set_ui(f, 1, 0); 38 | cpx_div_ui(f, f, 2); 39 | 40 | // - 3x/2 41 | cpx_times_ui(zn, z, 3); 42 | cpx_div_ui(zn, zn, 2); 43 | cpx_sub(f, f, zn); 44 | 45 | // + x^2 46 | cpx_mul(zn, z, z); 47 | cpx_add(f, f, zn); 48 | } 49 | else if (1 == deriv) 50 | { 51 | // - 3/2 52 | cpx_set_ui(f, 3, 0); 53 | cpx_div_ui(f, f, 2); 54 | cpx_neg(f, f); 55 | 56 | // +2x 57 | cpx_times_ui(zn, z, 2); 58 | cpx_add(f, f, zn); 59 | } 60 | else if (2 == deriv) 61 | { 62 | cpx_set_ui(f, 2, 0); 63 | } 64 | else 65 | { 66 | fprintf(stderr, "Error: unexpected derivative %d\n", deriv); 67 | cpx_set_ui(f, 0, 0); 68 | } 69 | 70 | //printf("Poly call der= %d center= %f %f\n", 71 | // deriv, cpx_get_re(z), cpx_get_im(z)); 72 | cpx_clear(zn); 73 | } 74 | 75 | /* ==================================================================== */ 76 | 77 | // Degree n polynomial, (x-1)(x-1/2)(x-1/3)(x-1/4) etc. 78 | // Computed recursively, so slow-ish for high orders. 79 | void polyn(cpx_t f, int deriv, cpx_t z, void* args) 80 | { 81 | int order = *((int*) args); 82 | 83 | cpx_t zn; 84 | cpx_init(zn); 85 | if (0 == deriv) 86 | { 87 | // Compute (z - 1/order) 88 | cpx_set_ui(zn, 1, 0); 89 | cpx_div_ui(zn, zn, order); 90 | cpx_sub(zn, z, zn); 91 | cpx_set_ui(f, 0, 1); 92 | 93 | // Recurse to get lower orders 94 | order --; 95 | if (0 < order) 96 | polyn(f, 0, z, &order); 97 | 98 | // Multiply (z - 1/order) * poly(lower orders) 99 | cpx_mul(f, f, zn); 100 | } 101 | else if (1 == order) 102 | { 103 | // Deriv of (z - 1/order) is just one, so we are done. 104 | cpx_set_ui(f, 0, 1); 105 | } 106 | else if (deriv == order) 107 | { 108 | // Deriv of (z - 1/order) is just one, so add lower 109 | order--; 110 | polyn(f, deriv-1, z, &order); 111 | } 112 | else if (deriv < order) 113 | { 114 | // Recurse to get lower orders 115 | // Compute (z - 1/order) 116 | cpx_set_ui(zn, 1, 0); 117 | cpx_div_ui(zn, zn, order); 118 | cpx_sub(zn, z, zn); 119 | cpx_set_ui(f, 0, 1); 120 | 121 | order --; 122 | polyn(f, deriv, z, &order); 123 | 124 | // Multiply (z - 1/order) * deriv(lower orders) 125 | cpx_mul(f, f, zn); 126 | 127 | // Deriv of (z - 1/order) is just one, so add lower 128 | polyn(zn, deriv-1, z, &order); 129 | cpx_add(f, f, zn); 130 | } 131 | else 132 | { 133 | fprintf(stderr, "Error: derivative %d greater than order %d\n", 134 | deriv, order); 135 | cpx_set_ui(f, 0, 0); 136 | } 137 | 138 | cpx_clear(zn); 139 | } 140 | 141 | /* ==================================================================== */ 142 | 143 | int main (int argc, char * argv[]) 144 | { 145 | cpx_t boxll, boxur; 146 | cpx_init(boxll); 147 | cpx_init(boxur); 148 | 149 | #define DEG 20 150 | cpx_t centers[DEG]; 151 | mpf_t radii[DEG]; 152 | for (int i=0; i