├── .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