spigot-0.2017-01-15.gdad1bbc6/0000755000000000000000000000000013036756601012162 5ustar spigot-0.2017-01-15.gdad1bbc6/.gitignore0000644000000000000000000000040612514752325014151 0ustar *.o *.d *.pyc *.obj *.exe /build.log /build.out /spigot /spigot.1 /spigot.html notes test.out python/build .deps/ Makefile Makefile.in aclocal.m4 autom4te.cache/ compile config.h config.h.in config.log config.status configure depcomp install-sh missing stamp-h1 spigot-0.2017-01-15.gdad1bbc6/Buildscr0000644000000000000000000000304113025565031013643 0ustar # -*- sh -*- # # bob script for spigot. module spigot set Version $(!builddate).$(vcsid) # Make up a version number and substitute it in to the configure # script and version.h. in spigot do sed -i '/AC_INIT/s/NOVERSION/$(Version)/' configure.ac in spigot do echo '/* Generated by automated build script */' > version.h in spigot do echo '$#define VER "version $(Version)"' >> version.h # use perl to avoid inconsistent behaviour of echo '\v' in spigot do perl -e 'print "\n\\versionid spigot version $$ARGV[0]\n"' $(Version) >> manual.but # Build the configure script. in spigot do ./autogen.sh # Run 'make test' with both bigint implementations. A side effect of # this is that Makefile gets produced, so that the following 'make # dist' will work. in spigot do ./configure --without-gmp && make test in spigot do ./configure --with-gmp && make test # Build the tarball and HTML docs. in spigot do make dist in spigot do make spigot.html # Build on Windows. delegate windows in spigot with visualstudio do/win cl /O2 /DNDEBUG /EHsc /Fespigot.exe *.cpp # Code-sign this Windows binary, if the local bob config provides # a script to do so. We assume here that the script accepts an -i # option to provide a 'more info' URL, and that it signs the file # in place. ifneq "$(winsigncode)" "" in spigot do $(winsigncode) -i http://www.chiark.greenend.org.uk/~sgtatham/winurl/ spigot.exe return spigot/spigot.exe enddelegate # Deliver output. deliver spigot/spigot-*.tar.gz $@ deliver spigot/spigot.html $@ deliver spigot/spigot.exe $@ spigot-0.2017-01-15.gdad1bbc6/LICENCE0000644000000000000000000000210112514752325013140 0ustar spigot is copyright 2007-2014 Simon Tatham. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. spigot-0.2017-01-15.gdad1bbc6/Makefile.am0000644000000000000000000000240713025573323014215 0ustar bin_PROGRAMS = spigot man1_MANS = spigot.1 spigot_SOURCES = consts.cpp erf.cpp exp.cpp expr.cpp gamma.cpp \ arithmetic.cpp io.cpp main.cpp monotone.cpp noexcept.cpp baseout.cpp \ spigot.cpp sqrt.cpp trig.cpp unary.cpp lambertw.cpp algebraic.cpp \ expint.cpp trigint.cpp zeta.cpp enforce.cpp misc.cpp cfracout.cpp \ bigint.h cr.h error.h expr.h funcs.h baseout.h spigot.h bi_gmp.h \ bi_internal.h version.h cfracout.h holefiller.h doc_DATA = spigot.html EXTRA_DIST = defs.but manual.but manpage.but test.sh spigot.html \ spigot.1 LICENCE test: spigot test.sh $(srcdir)/test.sh ./spigot # If Halibut is available to rebuild the documentation from its .but # source, then the docs are treated as derived files in the obvious # way. If Halibut is not available (the typical case if someone has # downloaded the source archive and rerun autogen.sh), the man pages # are treated as source files by this makefile. if HAVE_HALIBUT # Evil Perlery here to get some red text in one of the code examples, # which Halibut doesn't support natively. spigot.html: defs.but manual.but manpage.but halibut --html=- $^ | perl -pe 's!(.* \(\d+\^-.*)!$$1!' > $@ spigot.1: defs.but manpage.but halibut --man=$@ $^ CLEANFILES = spigot.html spigot.1 endif spigot-0.2017-01-15.gdad1bbc6/TODO.txt0000644000000000000000000001532513025565031013467 0ustar Possible future thoughts for spigot =================================== This file is spigot's informal to-do list. I don't necessarily intend to actually do all of these things; they're just thoughts and ponderings, with discussion of the implementation strategy and/or difficulties that I didn't want to lose having thought of them. Waiting for input files to lengthen ----------------------------------- Currently, the only two options when encountering EOF on an input file are to bomb out with an exception, or to assume that means the number represented is exactly accurate. One could imagine finding uses for a third option, similar to 'tail -f': if we see EOF, wait for more data to be added to the end of the file, then read that and continue. Could be used in a situation where another instance of spigot (or some other program) is in the process of appending to the file in parallel with the one reading it. Not sure how best to set it up, though; one would like to use some kind of inotify-type facility, if available, to avoid the need to poll tediously. And that's likely to involve a load of faff with autoconf... Interactive mode ---------------- spigot's command-line invocation is all very well if you want to pipe lots of its output into something, but if you're using it for interactive computation, it is a bit unwieldy, because it plays so badly with Unix shell syntax and Unix command-line conventions - you'd like to just type the expression you want to evaluate, but instead you keep having to remember that some of them need to be single-quoted or prefixed with '--' or both. It would be nice to also support an interactive mode, with a nice readline-based command prompt, and some means of controlling infinite streams of output, along the lines of printing one line of digits by default and permitting some keystroke like Tab at the command prompt to go back and add more data to the previous computation. Then you could just type spigot-syntax expressions directly at the command line, with no surrounding quoting hassle. The most hairy part of this idea is the bit where we come back from the command prompt loop and need to generate more data from a spigot that's stopped computing. Individual spigots' input sides are defined as coroutines, but the system as a whole is not interruptible and resumable at an absolutely general point, and would take some upheaval to make it so. So this might be a feature that's most easily implemented by C++11 multithreading - put each actual computation into a std::thread, funnel its output back to the main loop via some inter-thread data structure, leave it active but in a suspended state if we might need to ask for more data later, and ask it to terminate if not. Then the only upheaval you'd need would be to add a check into every inner loop (of which there aren't all that many - iterate_spigot_algorithm might even be the only one needed) so that threads would know if they'd been asked to pause or stop. There'd also need to be some sort of syntax available at the internal command line to set all the same output and rounding options as are available on the external command line, of course. And that syntax would have to be impossible to confuse with an expression to be evaluated. Cleanups: memory leaks ---------------------- Quite a few of the top-level functions implementing spigot operations do some preliminary checks for special cases, and if they find one, just return some easy thing like spigot_integer(0). When that happens, they ought properly to delete the spigot(s) they were passed as operands, because those now won't be subsumed into the returned spigot and hence won't be cleaned up when that gets deleted. It would be nice to make a pass through all the code and sort those out. (Especially if I ever do implement the interactive mode also mentioned in this file - if spigot is going to run for longer than one evaluation, it becomes more important not to leak memory.) Cleanups: verify that matrices are refining ------------------------------------------- In principle, more or less every matrix emitted by a spigot Source should transform the starting interval into a (not necessarily proper) subinterval of itself, rather than into a larger interval or an interval that overlaps only part of the source interval. On the rare occasion that that doesn't happen (typically because of some anomalous starting matrix), we're supposed to have emitted the 'force' flag beforehand, to force the Source's consumer to absorb the next matrix too so that we don't stop after a non-refining step. It would probably be a good thing to add assertions to actually verify that every time a Source returns with an unset 'force' flag, the source interval actually _has_ been refined to a subinterval of itself. (This is probably more fiddly than it sounds, computationally: we'd have to deal with the interval's image being mapped the other way up, and forbid poles in the interval - except in the case of a pole at one end if the starting interval extended to infinity anyway. Lots of ways to get it wrong. Be careful.) Experiment: evaluating transcendentals using a chain of Core2 ------------------------------------------------------------- spigot's method of evaluating a transcendental function f at an irrational x works by evaluating f at a sequence of rationals converging to x (typically much easier, because the underlying implementation of f will typically be based on sequences of matrices which represent rationals naturally), and combining the results using MonotoneHelper to produce bounds on the value of f(x). That's fine, and pragmatically seems to work. But Imperial College's paper on _their_ exact real system suggests a different method, which (translated into spigot terms) basically looks as if they lazily construct an infinite chain of Core2, one for each element in the series implementing f. So where I evaluate f at a rational by generating a 2x2 matrix for each term of the series, they would evaluate f at an irrational by generating a 4x2 Core2 tensor in each case, with one of its inputs being a copy of the input x and the other input being the next Core2 in the chain. If I ever have leisure, it might be fun to give that strategy a try, just out of sheer curiosity - to see if it works, and to see how it compares in speed terms. I think the way to do this in spigot would be: invent a class called (say) LazySource. This would return its starting interval, wait until it gets asked for a matrix, and _then_ construct a SourceGenerator(Core2(x->clone(), another LazySource)), with the Core2 starting tensor derived from the series implementation of whatever transcendental function we're computing. And the next LazySource that we'd construct would contain enough mutated state to know that it's the next term in the series. spigot-0.2017-01-15.gdad1bbc6/algebraic.cpp0000644000000000000000000002655513025565031014605 0ustar /* * Generate an arbitrary algebraic number. */ #include #include #include #include #include "spigot.h" #include "error.h" #include "funcs.h" class Algebraic : public Source { /* * This class assumes that the root we're looking for is the * unique one in the interval (0,1). */ std::vector P; std::vector P_orig; public: Algebraic(const std::vector &aP) { P = aP; P_orig = aP; } virtual Algebraic *clone() { return new Algebraic(P_orig); } virtual bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 1; return false; } virtual bool gen_matrix(bigint *matrix) { /* * Scale up our polynomial P by replacing it with Q such that * Q(x) = P(x/2) (up to a constant). This means our root will * now be in the interval [0,2]. */ bigint factor = 1; for (int i = P.size(); i-- > 0 ;) { P[i] *= factor; factor *= 2; } /* * Now evaluate P at 1 (i.e. just sum the terms) to find out * whether the root lies in [0,1] or [1,2], i.e. which side of * 1/2 it was on before we scaled up. */ bigint sum = 0; for (int i = 0; i < (int)P.size(); ++i) sum += P[i]; int digit = (bigint_sign(sum) == bigint_sign(P[0])) ? 1 : 0; if (digit) { /* * If the root is in [1,2], we need to transform P again, * by finding Q such that Q(x) = P(x+1), which brings the * root back down into [0,1]. */ for (int j = P.size() - 1; j-- > 0 ;) for (int i = j; i+1 < (int)P.size(); ++i) P[i] += P[i+1]; } /* * Return an appropriate binary-digit matrix. */ matrix[0] = 1; matrix[1] = digit; matrix[2] = 0; matrix[3] = 2; return false; } }; /* * Semantically sensible interface, which takes a vector of integer * polynomial coefficients along with a pair of rational endpoints. */ Spigot *spigot_algebraic(const std::vector &aP, bigint nlo, bigint nhi, bigint d) { std::vector P = aP; int n = P.size(); #if 0 printf("spigot_algebraic: nlo="); bigint_print(nlo); printf(" nhi="); bigint_print(nhi); printf(" d="); bigint_print(d); printf(" P=["); for (int i = 0; i < n; ++i) { if (i > 0) putchar(' '); bigint_print(P[i]); } printf("]\n"); #endif /* * Trim leading zero coefficients. */ while (n > 0 && P[n-1] == 0) P.pop_back(); /* * Check we still have a worthwhile polynomial. */ if (n < 2) throw spigot_error("degenerate polynomial"); #if 0 printf("trimmed: P=["); for (int i = 0; i < n; ++i) { if (i > 0) putchar(' '); bigint_print(P[i]); } printf("]\n"); #endif /* * Scale P so that the root is in the interval (nlo,nhi) rather * than (nlo/d,nhi/d). */ { bigint factor = 1; for (int i = P.size(); i-- > 0 ;) { P[i] *= factor; factor *= d; } } #if 0 printf("scaled #1: P=["); for (int i = 0; i < n; ++i) { if (i > 0) putchar(' '); bigint_print(P[i]); } printf("]\n"); #endif /* * Scale again, this time to make P monic. That way, if it has any * rational roots, they will be integers. */ d *= P[n-1]; nlo *= P[n-1]; nhi *= P[n-1]; { bigint factor = 1; for (int i = n-1; i-- > 0 ;) { P[i] *= factor; factor *= P[n-1]; } } P[n-1] = 1; /* * Swap round nlo and nhi if they're backwards. Partly in case the * user specified them the wrong way round, and also because the * scaling above might have negated both. */ if (nlo > nhi) { bigint tmp = nhi; nhi = nlo; nlo = tmp; } #if 0 printf("scaled #2: nlo="); bigint_print(nlo); printf(" nhi="); bigint_print(nhi); printf(" d="); bigint_print(d); printf(" P=["); for (int i = 0; i < n; ++i) { if (i > 0) putchar(' '); bigint_print(P[i]); } printf("]\n"); #endif /* * Sanity-check that P(nlo) and P(nhi) at least have opposite * signs. */ bigint plo = 0, phi = 0; for (int i = n; i-- > 0 ;) { plo = plo * nlo + P[i]; phi = phi * nhi + P[i]; } #if 0 printf("signs: plo %d, phi %d\n", bigint_sign(plo), bigint_sign(phi)); #endif if (bigint_sign(plo) * bigint_sign(phi) != -1) throw spigot_error("bad interval for polynomial root"); /* * Binary-search between nlo and nhi to find a unit interval * containing the root. If we hit the root exactly, return a * rational. */ while (nhi - nlo > 1) { bigint nmid = fdiv(nhi + nlo, 2); bigint p = 0; for (int i = n; i-- > 0 ;) p = p * nmid + P[i]; if (p == 0) { #if 0 printf("rational root: "); bigint_print(nmid); printf("/"); bigint_print_nl(d); #endif return spigot_rational(nmid, d); } if (bigint_sign(p) == bigint_sign(phi)) nhi = nmid; else nlo = nmid; } /* * Now we're searching for a root in the range (nlo,nlo+1). * Transform P again so that the root is translated to (0,1). */ #if 0 printf("integer part: nlo="); bigint_print_nl(nlo); #endif for (int j = n-1; j-- > 0 ;) for (int i = j; i+1 < n; ++i) P[i] += nlo * P[i+1]; #if 0 printf("translated: P=["); for (int i = 0; i < n; ++i) { if (i > 0) putchar(' '); bigint_print(P[i]); } printf("]\n"); #endif Spigot *ret = new Algebraic(P); if (nlo != 0 || d != 1) ret = spigot_mobius(ret, 1, nlo, 0, d); return ret; } typedef std::pair qval; typedef std::vector qpoly; #if 0 // only used for ad-hoc printf-debugging static void dprint_qval(const qval &a) { dprint("%b/%b", &a.first, &a.second); } static void dprint_qpoly(const qpoly &a) { dprint("poly %d:", (int)a.size()); for (size_t i = 0; i < a.size(); ++i) { dprint_qval(qval(a[i])); } } #endif static qval a_minus_b_times_c(const qval &a, const qval &b, const qval &c) { bigint n = a.first*b.second*c.second - a.second*b.first*c.first; bigint d = a.second*b.second*c.second; bigint g = gcd(n, d); return qval(n / g, d / g); } /* * Divide two polynomials over Q. Returns the quotient; modifies n to * be the remainder. */ static qpoly divide_poly(qpoly &n, qpoly &d) { assert(d.size() > 0); assert(d[d.size()-1].first != 0); qpoly ret; while (n.size() >= d.size()) { // dprint("n,d degrees %d,%d", (int)n.size(), (int)d.size()); size_t shift = n.size() - d.size(); qval &nfirst = n[n.size() - 1]; qval &dfirst = d[d.size() - 1]; qval mult(nfirst.first * dfirst.second, nfirst.second * dfirst.first); for (size_t i = 0; i < d.size(); ++i) { n[i+shift] = a_minus_b_times_c(n[i+shift], d[i], mult); } while (n.size() > 0 && n[n.size() - 1].first == 0) n.resize(n.size() - 1); if (mult.first != 0) { if (ret.size() < shift+1) ret.resize(shift+1, qval(0, 1)); ret[shift] = mult; } } return ret; } /* * Scale a polynomial by a constant to make the coefficients all * integers and as small as possible. */ static void scale_poly(qpoly &P) { bigint lcm_of_denoms = 1; for (size_t i = 0; i < P.size(); ++i) { lcm_of_denoms = lcm_of_denoms * P[i].second / gcd(lcm_of_denoms, P[i].second); } for (size_t i = 0; i < P.size(); ++i) { P[i].first = P[i].first * lcm_of_denoms / P[i].second; P[i].second = 1; } bigint gcd_of_nums = P[0].first; for (size_t i = 1; i < P.size(); ++i) { gcd_of_nums = gcd(gcd_of_nums, P[i].first); } for (size_t i = 0; i < P.size(); ++i) { P[i].first /= gcd_of_nums; } } /* * Reduce a polynomial to one with no repeated roots, by dividing off * the gcd of P and its derivative. */ static void reduce_poly(std::vector &Porig) { qpoly P, Pprime; // Copy P into a qpoly. for (size_t i = 0; i < Porig.size(); ++i) { P.push_back(qval(Porig[i], 1)); } // And differentiate P. for (size_t i = 1; i < Porig.size(); ++i) { Pprime.push_back(qval((int)i * Porig[i], 1)); } // dprint("P:"); dprint_qpoly(P); // dprint("Pprime:"); dprint_qpoly(Pprime); // Now do Euclid's algorithm between P and Pprime, i.e. repeatedly // reduce one of them mod the other. qpoly *a = &P, *b = &Pprime; while (b->size() > 0) { // dprint("a,b degrees %d,%d", (int)a->size(), (int)b->size()); // dprint("a:"); dprint_qpoly(*a); // dprint("b:"); dprint_qpoly(*b); divide_poly(*a, *b); scale_poly(*a); qpoly *tmp = a; a = b; b = tmp; } // dprint("gcd:"); dprint_qpoly(*a); // Now *a is the gcd. Make another copy of the original P, and // divide that off. qpoly Pcopy; for (size_t i = 0; i < Porig.size(); ++i) { Pcopy.push_back(qval(Porig[i], 1)); } qpoly ret = divide_poly(Pcopy, *a); // dprint("ret:"); dprint_qpoly(ret); scale_poly(ret); // Done. Copy into Porig for return. Porig.resize(0); for (size_t i = 0; i < ret.size(); ++i) { Porig.push_back(ret[i].first); } } /* * Interface convenient for expr.cpp, which will pass us a vector of * Coreables which include the endpoints _and_ the polynomial * coefficients. */ Spigot *spigot_algebraic_wrapper(const std::vector &args) { std::vector P; bigint nlo, dlo, nhi, dhi, d; if (args.size() < 2) throw spigot_error("expected at least two arguments to 'algebraic'"); if (!args[0]->is_rational(&nlo, &dlo) || !args[1]->is_rational(&nhi, &dhi)) { // We could, I suppose, attempt to tolerate any old real // number as an interval bound, by being prepared to fetch // convergents to it until we got one on the right side of the // input real at which P had the right sign. throw spigot_error("expected rational bounds for 'algebraic'"); } // Reduce dlo and dhi to a single common d. { bigint a = bigint_abs(dlo), b = bigint_abs(dhi); while (b != 0) { bigint t = b; b = a % b; a = t; } d = dlo * dhi / a; nlo *= dhi / a; nhi *= dlo / a; } // Get the polynomial coefficients themselves. for (size_t i = 2; i < args.size(); ++i) { // Again, we could be more tolerant here: we could cope with // rational polynomial coefficients as well as integers, by // recording all their denominators and scaling up afterwards. bigint ncoeff, dcoeff; if (!args[i]->is_rational(&ncoeff, &dcoeff) || dcoeff != 1) throw spigot_error("expected integer coefficients for 'algebraic'"); P.push_back(ncoeff); } // Reduce the polynomial to one without repeated roots. reduce_poly(P); return spigot_algebraic(P, nlo, nhi, d); } spigot-0.2017-01-15.gdad1bbc6/arithmetic.cpp0000644000000000000000000004771213025565031015023 0ustar /* * arithmetic.cpp: basic arithmetic operations. Implemented by means * of a special-purpose spigot core, which takes two sources and can * fetch a matrix from either one. */ #include #include #include #include "spigot.h" #include "funcs.h" #include "error.h" class Core2 : public Core { Source *sx, *sy; bool same; // means sy is just the same as sx bool started; int orig_matrix[8]; bigint matrix[8]; bigint xbot, xtop, ybot, ytop; bool xtop_inf, ytop_inf; int eval_endpoint(bigint out[2], const bigint T[8], const bigint &x, bool xinf, const bigint &y, bool yinf); public: Core2(Source *ax, Source *ay, const int m[8], bool same = false); ~Core2(); virtual void premultiply(const bigint matrix[4]); virtual Core *clone(); virtual void refine(); virtual int max_endpoints(); virtual int endpoints(bigint *endpoints); }; /* * Core2 works by having an eight-element 'matrix' (really a tensor, * I'm told, though that's beyond my maths) in which (a,b,c,d,e,f,g,h) * represents a sort of 'dyadic Mobius transformation' * * a x y + b x + c y + d * T(x,y) = --------------------- * e x y + f x + g y + h * * We need to be able to do three things to this matrix: premultiply * in an output transform provided by our owning Generator, * postmultiply in an input transform from the x source, and ditto the * y source. All of these operations involve an ordinary 4-element * matrix as the other parameter. So let's suppose we have a matrix * (p,q,r,s) representing the ordinary Mobius transformation * * p x + q * M(x) = ------- * r x + s * * Then premultiplication means finding a replacement 8-element matrix * representing T'(x,y) = M(T(x,y)), and the two postmultiplications * similarly find T(M(x),y) and T(x,M(y)) respectively. * * Getting Maxima to do the tedious algebra for us, the following * commands will produce the rational functions we need, from which * the numerator and denominator terms are easily collected back up * into coefficients of xy, x, y and 1: * * T : lambda([x,y], (a*x*y + b*x + c*y + d) / (e*x*y + f*x + g*y + h)); * M : lambda([x], (p*x + q) / (r*x + s)); * factor(M(T(x,y))); * factor(T(M(x),y)); * factor(T(x,M(y))); */ static void tensor_pre(bigint out[8], const bigint M[4], const bigint T[8]) { const bigint &p = M[0], &q = M[1], &r = M[2], &s = M[3]; bigint a = T[0], b = T[1], c = T[2], d = T[3]; bigint e = T[4], f = T[5], g = T[6], h = T[7]; /* * Maxima generates M(T(x,y)) as: * * e q x y + a p x y + g q y + c p y + f q x + b p x + h q + d p * ------------------------------------------------------------- * e s x y + a r x y + g s y + c r y + f s x + b r x + h s + d r */ out[0] = a*p + e*q; out[1] = b*p + f*q; out[2] = c*p + g*q; out[3] = d*p + h*q; out[4] = a*r + e*s; out[5] = b*r + f*s; out[6] = c*r + g*s; out[7] = d*r + h*s; } static void tensor_postx(bigint out[8], const bigint T[8], const bigint M[4]) { const bigint &p = M[0], &q = M[1], &r = M[2], &s = M[3]; bigint a = T[0], b = T[1], c = T[2], d = T[3]; bigint e = T[4], f = T[5], g = T[6], h = T[7]; /* * Maxima generates T(M(x),y) as: * * c r x y + a p x y + c s y + a q y + d r x + b p x + d s + b q * ------------------------------------------------------------- * g r x y + e p x y + g s y + e q y + h r x + f p x + h s + f q */ out[0] = a*p + c*r; out[1] = b*p + d*r; out[2] = a*q + c*s; out[3] = b*q + d*s; out[4] = e*p + g*r; out[5] = f*p + h*r; out[6] = e*q + g*s; out[7] = f*q + h*s; } static void tensor_posty(bigint out[8], const bigint T[8], const bigint M[4]) { const bigint &p = M[0], &q = M[1], &r = M[2], &s = M[3]; bigint a = T[0], b = T[1], c = T[2], d = T[3]; bigint e = T[4], f = T[5], g = T[6], h = T[7]; /* * Maxima generates T(x,M(y)) as: * * b r x y + a p x y + d r y + c p y + b s x + a q x + d s + c q * ------------------------------------------------------------- * f r x y + e p x y + h r y + g p y + f s x + e q x + h s + g q */ out[0] = a*p + b*r; out[1] = a*q + b*s; out[2] = c*p + d*r; out[3] = c*q + d*s; out[4] = e*p + f*r; out[5] = e*q + f*s; out[6] = g*p + h*r; out[7] = g*q + h*s; } Core2::Core2(Source *ax, Source *ay, const int m[8], bool asame) : sx(ax) , sy(ay) , started(false) , same(asame) { for (int i = 0; i < 8; i++) matrix[i] = orig_matrix[i] = m[i]; dprint("hello Core2 %p %p %8m [same=%B]", sx, sy, matrix, same); } Core2::~Core2() { delete sx; if (sy) delete sy; } void Core2::premultiply(const bigint inmatrix[4]) { dprint("premultiply: %4m %8m", inmatrix, matrix); tensor_pre(matrix, inmatrix, matrix); dprint("matrix after premult %8m", matrix); } Core *Core2::clone() { return new Core2(sx->clone(), sy ? sy->clone() : NULL, orig_matrix, same); } void Core2::refine() { bool force_absorb_x = false, force_absorb_y = false; dprint("refine started"); if (!started) { /* * Fetch the interval bounds. */ force_absorb_x = sx->gen_interval(&xbot, &xtop); xtop_inf = (xtop == 0); /* special case meaning infinity */ if (same) { ybot = xbot; ytop = xtop; ytop_inf = xtop_inf; } else { force_absorb_y = sy->gen_interval(&ybot, &ytop); ytop_inf = (ytop == 0); } started = true; dprint("Core2 init: intervals [%b,%b] [%b,%b]", &xbot, &xtop, &ybot, &ytop); } do { bigint inmatrix[4]; if (same) { /* * If our two sources are the same source, then we have no * choice anyway about which one to fetch a matrix from, * because there is no possible answer except 'both at * once'. */ force_absorb_x = true; force_absorb_y = false; } else if (!force_absorb_x && !force_absorb_y) { /* * We need to fetch something from _one_ of our two * sources, but which? To answer that, we'll evaluate our * current interval endpoints and see which ones are the * furthest apart. */ bigint ends[10]; int n_ends = endpoints(ends); if (n_ends == 5) { /* * This is the special case in which both starting * intervals are infinite and something exceptionally * annoying has happened to the tensor. Grab another * matrix from both sources in the hope that things * settle down. */ force_absorb_x = force_absorb_y = true; } else { /* * OK, only four endpoints, which are respectively * from (xbot,ybot), (xbot,ytop), (xtop,ybot) and * (xtop,ytop). * * If there's a pole between either pair of endpoints * differing in the x value, then we should fetch from * x to try to get rid of it. Similarly y. * * (A pole _at_ any endpoint is treated as between * that endpoint and everything else, and causes a * fetch from both sources.) */ if (bigint_sign(ends[1]) * bigint_sign(ends[5]) != 1 || bigint_sign(ends[3]) * bigint_sign(ends[7]) != 1) force_absorb_x = true; if (bigint_sign(ends[1]) * bigint_sign(ends[3]) != 1 || bigint_sign(ends[5]) * bigint_sign(ends[7]) != 1) force_absorb_y = true; if (!force_absorb_x && !force_absorb_y) { /* * If that still hasn't settled the matter, we'll * have to look at the actual numeric differences. */ bigint exy = fdiv(ends[0], ends[1]); bigint exY = fdiv(ends[2], ends[3]); bigint eXy = fdiv(ends[4], ends[5]); bigint eXY = fdiv(ends[6], ends[7]); bigint xdiff = bigint_abs(eXy-exy) + bigint_abs(eXY-exY); bigint ydiff = bigint_abs(exY-exy) + bigint_abs(eXY-eXy); dprint("decide: xdiff=%b ydiff=%b", &xdiff, &ydiff); if (xdiff >= ydiff) force_absorb_x = true; if (ydiff >= xdiff) force_absorb_y = true; } } } if (force_absorb_x) { force_absorb_x = sx->gen_matrix(inmatrix); dprint("postmultiply x: %8m %4m", matrix, inmatrix); tensor_postx(matrix, matrix, inmatrix); dprint("matrix after postmult %8m", matrix); if (same) { dprint("postmultiply y: %8m %4m", matrix, inmatrix); tensor_posty(matrix, matrix, inmatrix); dprint("matrix after postmult %8m", matrix); } } if (force_absorb_y) { force_absorb_y = sy->gen_matrix(inmatrix); dprint("postmultiply y: %8m %4m", matrix, inmatrix); tensor_posty(matrix, matrix, inmatrix); dprint("matrix after postmult %8m", matrix); } } while (force_absorb_x || force_absorb_y); } int Core2::max_endpoints() { /* * We usually return 4 endpoints, but in one special case if both * starting intervals have infinite top ends, we might have to * return 5. */ return 5; } int Core2::eval_endpoint(bigint out[2], const bigint T[8], const bigint &x, bool xinf, const bigint &y, bool yinf) { const bigint &a = T[0], &b = T[1], &c = T[2], &d = T[3]; const bigint &e = T[4], &f = T[5], &g = T[6], &h = T[7]; /* * Evaluating the image of an input point under the tensor T is * annoyingly fiddly because x or y or both or neither could be * infinite, and if either one is then we have multiple special * cases in turn (similarly to Core1 in spigot.cpp). */ if (!xinf) { if (!yinf) { /* * The easy finite case. A spot of factorisation reduces * the number of multiplications by x. */ out[0] = (a*y + b) * x + (c*y + d); out[1] = (e*y + f) * x + (g*y + h); dprint(" finite endpoint %b / %b", &out[0], &out[1]); return 1; } else { /* * x is finite, but y is infinite. So we either have * (ax+c)/(ex+g), or if that comes to 0/0, we fall back to * (bx+d)/(fx+h). */ out[0] = a*x + c; out[1] = e*x + g; if (out[0] == 0 && out[1] == 0) { out[0] = b*x + d; out[1] = f*x + h; } dprint(" x-infinite endpoint %b / %b", &out[0], &out[1]); return 1; } } else { if (!yinf) { /* * y is finite, but x is infinite. So we either have * (ay+b)/(ey+f), or if that comes to 0/0, we fall back to * (cy+d)/(gy+h). */ out[0] = a*y + b; out[1] = e*y + f; if (out[0] == 0 && out[1] == 0) { out[0] = c*y + d; out[1] = g*y + h; } dprint(" y-infinite endpoint %b / %b", &out[0], &out[1]); return 1; } else { /* * Both x and y are infinite. In this case, our opening * bid is just a/e, and our final fallback if all of * a,b,c,e,f,g are zero is d/h; but in between, there's a * more interesting case. */ if (a != 0 || e != 0) { out[0] = a; out[1] = e; dprint(" xy-infinite endpoint a/e %b / %b", &out[0], &out[1]); return 1; } else if (b == 0 && c == 0 && f == 0 && g == 0) { out[0] = d; out[1] = h; dprint(" xy-infinite endpoint d/h %b / %b", &out[0], &out[1]); return 1; } else { /* * a,e are zero, but at least one of b,c,f,g is not. * * If c,g are zero too, then we have no dependency on * y at all, and simply return as if we were computing * (bx+d)/(fx+h) - which we also know comes to just * b/f since we only get to that case if at least one * of b,f is nonzero. * * Similarly, if b,f are both zero, then we return * c/g, by the same reasoning in mirror symmetry. * * But if one of c,g is nonzero _and_ one of b,f is * nonzero, then what do we do? We're essentially * asking for the limit of (bx+cy+d)/(fx+gy+h) as x * and y tend to infinity, and that could come to * either b/f or c/g depending on which of x,y tends * to infinity 'fastest'. (In fact, it could also come * to (thing between b,c) / (thing between f,g) if x * and y tended to infinity in some particular * relationship, but that's exactly the sort of thing * the code in iterate_spigot_algorithm can handle - * we only have to give it the two extreme endpoints * of that range.) * * So in fact, what we do is to optionally return * _both_ b/f and c/g, which gives Core2 the * possibility of returning five endpoints rather than * just four. */ int i = 0; if (b != 0 || f != 0) { out[2*i+0] = b; out[2*i+1] = f; dprint(" xy-infinite endpoint b/f %b / %b", &out[2*i+0], &out[2*i+1]); i++; } if (c != 0 || g != 0) { out[2*i+0] = c; out[2*i+1] = g; dprint(" xy-infinite endpoint c/g %b / %b", &out[2*i+0], &out[2*i+1]); i++; } return i; } } } } int Core2::endpoints(bigint *endpoints) { dprint("endpoints for %8m", matrix); /* * All of these calls to eval_endpoint return 1 endpoint only, * because eval_endpoint can only return 2 if both inputs are * infinite (and not necessarily even then). * * Note that the code in refine() which decides which source to * fetch from depends on the order of these calls, so don't switch * them around casually! */ eval_endpoint(endpoints + 0, matrix, xbot, false, ybot, false); eval_endpoint(endpoints + 2, matrix, xbot, false, ytop, ytop_inf); eval_endpoint(endpoints + 4, matrix, xtop, xtop_inf, ybot, false); /* * The final call to eval_endpoint can return 2 instead of 1, so * we add its return value to the previous 3 endpoints to get our * final count. */ int ret = eval_endpoint(endpoints + 6, matrix, xtop, xtop_inf, ytop, ytop_inf); return ret + 3; } Spigot *spigot_add(Spigot *a, Spigot *b) { bigint an, ad, bn, bd; bool arat = a->is_rational(&an, &ad); bool brat = b->is_rational(&bn, &bd); if (arat && brat) { delete a; delete b; return spigot_rational(an * bd + bn * ad, ad * bd); } else if (arat) { delete a; return spigot_mobius(b, ad, an, 0, ad); } else if (brat) { delete b; return spigot_mobius(a, bd, bn, 0, bd); } int m[8] = {0,1,1,0,0,0,0,1}; /* (0xy+1x+1y+0)/(0xy+0x+0y+1) == x+y */ return new Core2(a->toSource(), b->toSource(), m); } Spigot *spigot_sub(Spigot *a, Spigot *b) { bigint an, ad, bn, bd; bool arat = a->is_rational(&an, &ad); bool brat = b->is_rational(&bn, &bd); if (arat && brat) { delete a; delete b; return spigot_rational(an * bd - bn * ad, ad * bd); } else if (arat) { delete a; return spigot_mobius(b, -ad, an, 0, ad); } else if (brat) { delete b; return spigot_mobius(a, bd, -bn, 0, bd); } int m[8] = {0,1,-1,0,0,0,0,1}; /* (0xy+1x-1y+0)/(0xy+0x+0y+1) == x-y */ return new Core2(a->toSource(), b->toSource(), m); } Spigot *spigot_mul(Spigot *a, Spigot *b) { bigint an, ad, bn, bd; bool arat = a->is_rational(&an, &ad); bool brat = b->is_rational(&bn, &bd); if (arat && brat) { delete a; delete b; return spigot_rational(an * bn, ad * bd); } else if (arat) { delete a; return spigot_mobius(b, an, 0, 0, ad); } else if (brat) { delete b; return spigot_mobius(a, bn, 0, 0, bd); } int m[8] = {1,0,0,0,0,0,0,1}; /* (1xy+0x+0y+0)/(0xy+0x+0y+1) == xy */ return new Core2(a->toSource(), b->toSource(), m); } Spigot *spigot_quadratic(Spigot *a, int a2, int a1, int a0) { bigint an, ad; bool arat = a->is_rational(&an, &ad); if (arat) { delete a; return spigot_rational(a2 * an * an + a1 * an * ad + a0 * ad * ad, ad * ad); } int m[8] = {a2,a1,0,a0,0,0,0,1}; return new Core2(a->toSource(), NULL, m, true); } Spigot *spigot_square(Spigot *a) { return spigot_quadratic(a, 1, 0, 0); } Spigot *spigot_invsquare(Spigot *a) { bigint an, ad; bool arat = a->is_rational(&an, &ad); /* * Of course in principle I could have written a completely * general spigot_quadratic_rational_function() which took all six * of the coefficients that define an arbitrary function of the * form * * n2 x^2 + n1 x + n0 * x |-> ------------------ * d2 x^2 + d1 x + d0 * * and then I could have implemented both spigot_invsquare *and* * spigot_square as one-line wrappers on it. The reason I didn't * is because the expression for the rational special case in * spigot_quadratic_rational_function() would have been more * horrible than I felt like dealing with! */ if (arat) { delete a; if (an == 0) { delete a; throw spigot_error("division by zero"); } return spigot_rational(ad * ad, an * an); } int m[8] = {0,0,0,1,1,0,0,0}; return new Core2(a->toSource(), NULL, m, true); } Spigot *spigot_div(Spigot *a, Spigot *b) { bigint an, ad, bn, bd; bool arat = a->is_rational(&an, &ad); bool brat = b->is_rational(&bn, &bd); if (brat) { delete b; if (bn == 0) { delete a; throw spigot_error("division by zero"); } if (arat) { delete a; return spigot_rational(an * bd, ad * bn); } else { return spigot_mobius(a, bd, 0, 0, bn); } } else if (arat) { delete a; return spigot_mobius(b, 0, an, ad, 0); } int m[8] = {0,1,0,0,0,0,1,0}; /* (0xy+1x+0y+0)/(0xy+0x+1y+0) = x/y */ return new Core2(a->toSource(), b->toSource(), m); } Spigot *spigot_combine(Spigot *a, Spigot *b, int nxy, int nx, int ny, int nc, int dxy, int dx, int dy, int dc) { /* * No special-case checking in this function - it's only called * from the internals of other spigot routines, and they're * assumed to take responsibility for interesting cases. */ int m[8] = {nxy, nx, ny, nc, dxy, dx, dy, dc}; return new Core2(a->toSource(), b->toSource(), m); } spigot-0.2017-01-15.gdad1bbc6/autogen.sh0000755000000000000000000000006112514752667014170 0ustar #!/bin/sh autoreconf -i && rm -rf autom4te.cache spigot-0.2017-01-15.gdad1bbc6/baseout.cpp0000644000000000000000000020375013025565031014330 0ustar /* * baseout.cpp: handle all forms of output which ultimately come down * to representing a number in ordinary positional notation, with or * without a scientific-notation exponent, and with all kinds of * top-level formatting (e.g. as a decimal string or an IEEE bit * pattern). */ #include #include #include #include #include #include #include "spigot.h" #include "error.h" #include "funcs.h" #include "cr.h" #include "baseout.h" /* ---------------------------------------------------------------------- * Low-level utility stuff. */ char digitchar(int d, char a = 'a') { return (d >= 10 ? a-10 : '0') + d; } /* * Small helper class to handle intervals of integers, with rigorous * attention paid to open/closed endpoints. */ struct Interval { bigint lo, hi; bool lo_open, hi_open; bool lo_inf, hi_inf; void init(bigint alo, bigint ahi, bool alo_open, bool ahi_open, bool alo_inf = false, bool ahi_inf = false) { lo = alo; hi = ahi; lo_open = alo_open; hi_open = ahi_open; lo_inf = alo_inf; hi_inf = ahi_inf; } Interval() : lo(0), hi(0), lo_open(true), hi_open(true), lo_inf(false), hi_inf(false) {} Interval(bigint alo, bigint ahi, bool alo_open, bool ahi_open) : lo(alo), hi(ahi), lo_open(alo_open), hi_open(ahi_open), lo_inf(false), hi_inf(false) {} Interval(const Interval &a) : lo(a.lo), hi(a.hi), lo_open(a.lo_open), hi_open(a.hi_open), lo_inf(a.lo_inf), hi_inf(a.hi_inf) {} const Interval &operator=(const Interval &a) { init(a.lo, a.hi, a.lo_open, a.hi_open, a.lo_inf, a.hi_inf); return *this; } Interval intersect(const Interval &b) const { Interval ret; int s; s = (lo_inf ? (b.lo_inf ? 0 : -1) : (b.lo_inf ? +1 : bigint_sign(lo - b.lo))); if (s > 0) { ret.lo = lo; ret.lo_open = lo_open; ret.lo_inf = lo_inf; } else if (s < 0) { ret.lo = b.lo; ret.lo_open = b.lo_open; ret.lo_inf = b.lo_inf; } else { ret.lo = b.lo; ret.lo_open = lo_open || b.lo_open; ret.lo_inf = b.lo_inf; } s = (hi_inf ? (b.hi_inf ? 0 : +1) : (b.hi_inf ? -1 : bigint_sign(hi - b.hi))); if (s < 0) { ret.hi = hi; ret.hi_open = hi_open; ret.hi_inf = hi_inf; } else if (s > 0) { ret.hi = b.hi; ret.hi_open = b.hi_open; ret.hi_inf = b.hi_inf; } else { ret.hi = b.hi; ret.hi_open = hi_open || b.hi_open; ret.hi_inf = b.hi_inf; } return ret; } bool empty() const { return !lo_inf && !hi_inf && (lo > hi || (lo == hi && (lo_open || hi_open))); } void translate(bigint origin) { lo += origin; hi += origin; } void scale(bigint scale) { lo *= scale; hi *= scale; } void remove_lower_bound() { lo_inf = true; } bool has_lower_bound() const { return !lo_inf; } }; static int floor_log(bigint i, int base, bool *exact) { /* * Helper function for deciding on exponents. Returns the integer * part (literally, floor) part of the log of i to the base base, * which must be 2 or 10. Input must be positive. */ /* * We start by finding a rational approximation to 1/log2(base). * Since we have spigot available to compute these for us, that's * how we do it! */ bigint lognum, logdenom; if ((base & (base-1)) == 0) { // Special case: base is a power of 2, so its log is an // integer. lognum = 1; logdenom = 0; for (int b = base; b > 1; b >>= 1) ++logdenom; } else { // Memoise the approximate logarithm we get, so we can reuse // it if called on again to generate output in the same base. typedef std::map > our_map_t; static our_map_t log2s; our_map_t::iterator it = log2s.find(base); if (it != log2s.end()) { lognum = it->second.first; logdenom = it->second.second; } else { Spigot *sp = spigot_reciprocal(spigot_log2(spigot_integer(base))); ConvergentsGenerator convs(sp); while (1) { /* * We need to err on the low side, which means taking * only every other convergent. (They oscillate * between erring high and low, starting with the * integer part which is always low.) */ convs.get_convergent(&lognum, &logdenom); if (logdenom > 200) break; convs.get_convergent(&lognum, &logdenom); } log2s[base] = std::pair(lognum, logdenom); } } assert(i > 0); int ret = bigint_approxlog2(i) * lognum / logdenom; bigint power = bigint_power(base, ret); *exact = (i % power == 0); bigint residual = i / power; while (residual >= base) { if (residual % (unsigned)base != 0) *exact = false; residual /= base; ret++; } return ret; } static RoundingMode signedify(RoundingMode rmode, int sign) { if (sign < 0) { return (rmode == ROUND_UP ? ROUND_TOWARD_ZERO : rmode == ROUND_DOWN ? ROUND_AWAY_FROM_ZERO : rmode == ROUND_TO_NEAREST_UP ? ROUND_TO_NEAREST_TOWARD_ZERO : rmode == ROUND_TO_NEAREST_DOWN ? ROUND_TO_NEAREST_AWAY_FROM_ZERO : rmode); } else { return (rmode == ROUND_UP ? ROUND_AWAY_FROM_ZERO : rmode == ROUND_DOWN ? ROUND_TOWARD_ZERO : rmode == ROUND_TO_NEAREST_UP ? ROUND_TO_NEAREST_AWAY_FROM_ZERO : rmode == ROUND_TO_NEAREST_DOWN ? ROUND_TO_NEAREST_TOWARD_ZERO : rmode); } } static bool is_directed(RoundingMode rmode) { return (rmode == ROUND_UP || rmode == ROUND_TOWARD_ZERO || rmode == ROUND_DOWN || rmode == ROUND_AWAY_FROM_ZERO); } /* ---------------------------------------------------------------------- * Abstract base class for formatters. */ /* * 'Formatter' is an abstract base class for things which accept a * stream of data in the form (sign, exponent, digit, digit, ...) and * turn it into a string. * * Formatters provide information on the base they want to use (for * exponent and digit), and whether they have a digit limit. They * receive digits _after_ rounding, so they can concentrate entirely * on gluing them into strings. */ class Formatter { std::string text; protected: void write(char c) { text.push_back(c); } void write(std::string s) { text += s; } public: Formatter() : text("") {} virtual ~Formatter() {} std::string current_text() const { return text; } void consume_text(size_t nchars) { text.erase(0, nchars); } /* * Return the desired number bases for exponent and digits. * * Constraints: 2 <= ebase <= dbase. * * The semantics will be that the exponent will be returned as the * largest power of ebase less than or equal to the number. Hence, * setting ebase < dbase means that not every non-zero digit can * occur in the leading position. * * (So if you implement printf("%a") by setting ebase=2 and * dbase=16, that gives you output in which the leading digit is * always 1. Of course if you wanted the version with the exponent * always a multiple of 4 and all leading digits possible, you * could do that just as easily by using ebase=16 and quadrupling * the received exponent.) * * If you were to set ebase > dbase, that would have to mean that * more than one digit was required before the point in some * situations (e.g. ebase=10 and dbase=2 would have to represent * 975, say, as 1001.11 * 10^2), which would complicate the * formatting interface (you'd have to communicate where the point * went, _separately_ from what the exponent was!) and so I'm * comfortable with disallowing that until I hear of a reason why * it's needed. */ virtual unsigned ebase() = 0; virtual unsigned dbase() = 0; /* * Indicate whether this formatter has a lower limit on the * exponent it can accept. Some will take any exponent; others * find it easier to cap the exponent below, so as not to have to * faff around with inventing lots of leading zeroes. */ virtual bool has_min_exp() = 0; virtual int min_exp() = 0; /* * Called by the generator to provide the sign and exponent, then * the digits. sign_exp() is called once, then digit() repeatedly, * and done() once we've finished outputting digits (if we ever * do). The argument to done() indicates whether we've stopped * outputting digits because the representation in the target base * turned out to be exact (in which case we pass 'true') or just * because we reached the digit limit ('false'). */ virtual void sign_exp(int s, int e) = 0; virtual void digit(unsigned d) = 0; virtual void done(bool exact) = 0; /* * Called by the generator in place of sign_exp() and digit(), if * an exact zero needs to be formatted. * * In case it makes a difference, the formatter should respond to * this by preparing enough text to use as tentative output * representing a potential zero, and to the subsequent done() by * appending the rest of the text to make a full and complete * output (e.g. a trailing exponent). */ virtual void exact_zero() = 0; /* * Indicate whether this formatter has a finite number of digits * remaining to output. Some can keep going for ever; others * impose a limit, at which rounding takes place. */ virtual bool has_digit_limit() = 0; virtual unsigned digits_left() = 0; /* * Indicates whether this formatter has prematurely decided on its * entire output. This can happen in the case of IEEE overflow. */ virtual bool complete() { return false; } /* * Tweaks which permit the formatter to scale up the first digit, * or to round at a partial digit position. */ virtual bigint first_digit_scale() { return 1; } virtual bigint round_digit_scale() { return 1; } /* * Formatters constantly have to be copied, because in situations * where the input number is taking a while to decide which side * of a digit boundary it's on, we run multiple formatters in * parallel for the various possibilities. */ virtual Formatter *clone() = 0; }; /* ---------------------------------------------------------------------- * The main core of base output. */ /* * BaseOutputGenerator is the place where the top-level work happens. * At one end, this retrieves data from an input spigot by being a * subclass of Generator; at the other end, it maintains a number of * currently active Formatters with partially constructed strings, and * outputs digits whenever they all agree on what comes next. In * between, it does sign and exponent finding, and rounding. */ class BaseOutputGenerator : public OutputGenerator { Formatter *mainfmt; struct CurrFmt { /* * This structure defines a currently active formatter, and * all the things BaseOutputGenerator needs to know to * remember how and when to pass it further digits. */ Formatter *fmt; /* * The first digit has an unusual range of possibilities, so * remember if this is it or not. Also, the range of * possibilities depends on whether we're outputting the * formatter's minimum exponent (because that's when a leading * zero digit is permitted). */ bool at_first_digit, at_min_exp; /* How to convert the spigot's current scale to output digits: * you compute (value-base)/(2*halfscale). */ bigint base; bigint halfscale; /* * Record the running parity of the number so far, for RTE/RTO. * * In an even (digit) base, 'round to even/odd' can be * regarded as simply rounding to whichever answer has an even * or odd _last digit_. But if the base is odd, that doesn't * make sense - e.g. what if you're in base 7, and stuck * between outputting a digit 6 or rounding up to 0? _Both_ * are even, so round-to-even has two choices that it still * can't decide between, and round-to-odd has none. * * The answer is to stop thinking about the last digit, and * start thinking about the whole number. Rounding to even/odd * should properly be seen as rounding so that the _entire * number output_ is an even or odd multiple of the last place * value. (In particular, if you're rounding to the nearest * _integer_, you want to break ties by picking the even or * odd _integer_ out of the options.) * * In an even base, of course, the two notions are the same. * But in an odd base, it means we have to track the parity of * the whole number so far, which handily is easy because the * parity of a number in an odd base is equal to the parity of * the sum of the digits, or the parity of the number of odd * digits, or other equivalent formulations including the * parity of the XOR of the digits. * * 'parity' does this tracking, as we step along the number. * Only its low bit is meaningful. */ unsigned parity; /* * 'done' is true if this formatter has output its rounding * digit and hence finished. In that situation, 'base' always * points at the rounding boundary separating us from the * other formatter, and the other two booleans here indicate * which side of that boundary this formatter expects to be * on, and whether an exact tie counts as valid. */ bool done; bool round_down; // if true, we expect to be < base bool round_open; // if false, = counts as the right side /* * Remember what exponent this formatter started off with, and * whether either end of its interval is an exponent boundary, * so that we can recognise the case later on when a rounding * decision is being made between two numbers of different * exponents. */ int exponent; bool just_below_exponent_boundary, just_above_exponent_boundary; int digits_since_done; } currfmts[2], newfmts[2]; int n_currfmts; unsigned ebase, dbase; bool has_min_exp; int min_exp; RoundingMode rmode; bool exponent_finding_phase; bool done; int crState; // for coroutine-structured progress() int sign, exponent; int scale; void dump_formatters(const char *prefix); private: int compute_exponent(int sign, int eadj, const bigint &i, bool add_epsilon, bool maximum); public: BaseOutputGenerator(Coreable *acore, Formatter *fmt, RoundingMode armode); ~BaseOutputGenerator(); void progress(); bool get_definite_output(std::string &out); bool get_tentative_output(std::string &out, int *digits); }; BaseOutputGenerator::BaseOutputGenerator(Coreable *acore, Formatter *fmt, RoundingMode armode) : OutputGenerator(acore), mainfmt(fmt), n_currfmts(0), rmode(armode), exponent_finding_phase(true), done(false), crState(-1) { dprint("hello BaseOutputGenerator %p", acore); } BaseOutputGenerator::~BaseOutputGenerator() { delete mainfmt; while (n_currfmts-- > 0) delete currfmts[n_currfmts].fmt; } void BaseOutputGenerator::dump_formatters(const char *prefix) { if (!debugging) return; for (int i = 0; i < n_currfmts; ++i) { if (currfmts[i].done) { dprint("%s: formatter %d: done base=%b rdown=%B ropen=%B \"%s\"", prefix, i, &currfmts[i].base, currfmts[i].round_down, currfmts[i].round_open, currfmts[i].fmt->current_text().c_str()); } else { dprint("%s: formatter %d: base=%b scale=2*%b parity=%d" " \"%s\" (%d digits)", prefix, i, &currfmts[i].base, &currfmts[i].halfscale, (int)(currfmts[i].parity & 1), currfmts[i].fmt->current_text().c_str(), (!currfmts[i].fmt->has_digit_limit() ? 9999 : currfmts[i].fmt->digits_left())); } } } int BaseOutputGenerator::compute_exponent(int sign, int eadj, const bigint &i, bool add_epsilon, bool maximum) { assert(i >= 0); bool exact; int e = floor_log(i, ebase, &exact); if (exact) { /* * If the number is an _exact_ power of ebase, then there is * only one possible exponent for it, irrespective of rounding. */ if (!add_epsilon) return e; /* * If the number is epsilon less than an exact power of ebase, * then we should consider its exponent to be one lower. */ if (maximum) e--; } bool special_case = false; RoundingMode real_rmode = signedify(rmode, sign); if (ebase == 2 && is_directed(real_rmode)) { Formatter *fmt = mainfmt->clone(); fmt->sign_exp(sign, eadj + e); special_case = (fmt->has_digit_limit() && fmt->digits_left() == 1); delete fmt; } if (special_case) { return real_rmode == ROUND_TOWARD_ZERO ? e : e+1; } if (maximum) { bigint threshold = bigint_power(ebase, e+1); if (dbase > 2) { threshold = fdiv(threshold * (dbase-1), dbase); } else { threshold = fdiv(threshold * 3, 4); } if (i >= threshold) { e++; } } return e; } void BaseOutputGenerator::progress() { bigint lo, hi; crBegin; ebase = mainfmt->ebase(); dbase = mainfmt->dbase(); has_min_exp = mainfmt->has_min_exp(); min_exp = mainfmt->min_exp(); assert(ebase <= dbase); dprint("ebase=%d dbase=%d", (int)ebase, (int)dbase); if (has_min_exp) dprint("min_exp=%d", (int)min_exp); else dprint("no min_exp"); /* * Start by determining the sign and exponent, which we do by * iterating the input spigot until the two ends of its interval * have the same sign and differ by at most a factor of ebase. * * While we do this, we arrange for a formatter to be active and * contain an exact representation of zero, so that that can be * used as tentative output if the exponent search drags on. */ n_currfmts = 1; currfmts[0].fmt = mainfmt->clone(); currfmts[0].fmt->exact_zero(); currfmts[0].fmt->done(true); exponent = 0; while (1) { bool lo_open, hi_open; iterate_to_bounds(&lo, &hi, &lo_open, &hi_open, 0, NULL, true); dprint("exponent-finding: got (%b,%b)", &lo, &hi); int e1, e2; if (lo > 0 && (e1 = compute_exponent(+1, exponent, lo, lo_open, false), e2 = compute_exponent(+1, exponent, hi, hi_open, true), e2 - e1 <= 1)) { /* * Positive number. */ sign = +1; scale = e2; dprint("positive, scale=%d", (int)scale); break; } else if (hi < 0 && (e1 = compute_exponent(-1, exponent, -hi, hi_open, false), e2 = compute_exponent(-1, exponent, -lo, lo_open, true), e2 - e1 <= 1)) { /* * Negative number. */ sign = -1; scale = e2; dprint("negative, scale=%d", (int)scale); break; } else if (hi == 0 && lo == 0) { /* * The number is exactly zero, so the temporary formatter * we set up for that possibility has in fact got the * right answer already, so just leave it there and finish. */ dprint("exact zero"); exponent_finding_phase = false; done = true; crReturnV; assert(!"Called again after completing an exact zero"); } else if (hi - lo <= 2) { /* * If the interval has narrowed as far as we can * reasonably expect it to, but none of the above cases * has yielded a useful result, then the exponent is * probably too small (or else the number is * _non-obviously_ zero). Premultiply a matrix that zooms * in, remember that we've done so, and keep trying. */ if (has_min_exp && exponent <= min_exp) { /* * An exception is if we're already at the formatter's * lower exponent limit, in which case we must stop * zooming in and just start generating zero digits * anyway. * * Well, almost. Actually we still have to go round * this loop until the _sign_ is available, but after * that we can start generating digits. */ if (lo > 0 || (lo == 0 && lo_open)) { dprint("min exp limit reached, positive"); sign = +1; scale = 0; break; } else if (hi < 0 || (hi == 0 && hi_open)) { dprint("min exp limit reached, negative"); sign = -1; scale = 0; break; } } bigint matrix[4]; matrix[0] = ebase; matrix[1] = matrix[2] = 0; matrix[3] = 1; dprint("premultiplying exponent-finding zoom %4m", matrix); core->premultiply(matrix); exponent--; dprint("zoomed in, decremented exponent to %d", exponent); } /* * Return from the coroutine every time we go round this loop, * so that we're conveniently interruptible. */ crReturnV; } dprint("exponent found: exponent=%d scale=%d sign=%d", exponent, scale, sign); if (has_min_exp && exponent + scale < min_exp) { scale = min_exp - exponent; dprint("adjusted for min exp %d: exponent=%d scale=%d sign=%d", min_exp, exponent, scale, sign); } /* * Do sign-dependent processing, now we know the sign: translate * signed roundings (towards +inf or -inf) into sign-independent * ones (towards or away from zero), and premultiply a negation * matrix if the number is negative. */ if (sign < 0) { bigint matrix[4]; matrix[0] = -1; matrix[1] = matrix[2] = 0; matrix[3] = 1; dprint("premultiplying negation %4m", matrix); core->premultiply(matrix); } rmode = signedify(rmode, sign); /* * Discard our temporary zero formatter. */ delete currfmts[0].fmt; exponent_finding_phase = false; /* * Find the overall exponent, by combining the log of the * integer-part interval we got back with the number of times * we've already zoomed in. */ exponent += (int)scale; dprint("final exponent=%d", exponent); /* * If the integer part was large, start by zooming back out. Also * zoom if we need to scale up the first digit. */ { bigint matrix[4]; matrix[0] = mainfmt->first_digit_scale(); matrix[1] = matrix[2] = 0; matrix[3] = bigint_power(ebase, scale); if (matrix[0] != matrix[3]) { dprint("premultiplying initial zoom %4m", matrix); core->premultiply(matrix); } } /* * We've now zoomed the spigot so that ebase^exponent in the scale * of the output number is represented by 1 here. But we're going * to worry about the exponent one below that, so we must zoom in * by ebase again; also we zoom in by an extra factor of 2 so that * we can get intervals at half-digit granularity. */ { bigint matrix[4]; matrix[0] = ebase*2; matrix[1] = matrix[2] = 0; matrix[3] = 1; dprint("premultiplying pre-main-loop zoom %4m", matrix); core->premultiply(matrix); } /* * Now we know the number's sign, and we've got its exponent to * within a range of two possibilities. Begin our main loop by * initialising one formatter to each of the exponent choices. * * Our formatters are in sorted order always, from smallest to * largest. */ n_currfmts = 0; if (!has_min_exp || exponent > min_exp) { // omit this exponent if too low currfmts[n_currfmts].fmt = mainfmt->clone(); currfmts[n_currfmts].done = false; currfmts[n_currfmts].fmt->sign_exp(sign, exponent - 1); currfmts[n_currfmts].base = 0; currfmts[n_currfmts].halfscale = 1; currfmts[n_currfmts].parity = 0; currfmts[n_currfmts].at_first_digit = true; currfmts[n_currfmts].at_min_exp = (has_min_exp && exponent-1 == min_exp); currfmts[n_currfmts].exponent = exponent - 1; currfmts[n_currfmts].just_below_exponent_boundary = true; currfmts[n_currfmts].just_above_exponent_boundary = false; currfmts[n_currfmts].digits_since_done = 0; n_currfmts++; } currfmts[n_currfmts].fmt = mainfmt->clone(); currfmts[n_currfmts].done = false; currfmts[n_currfmts].fmt->sign_exp(sign, exponent); currfmts[n_currfmts].base = 0; currfmts[n_currfmts].halfscale = ebase; currfmts[n_currfmts].parity = 0; currfmts[n_currfmts].at_first_digit = true; currfmts[n_currfmts].at_min_exp = (has_min_exp && exponent == min_exp); currfmts[n_currfmts].exponent = exponent; currfmts[n_currfmts].just_below_exponent_boundary = false; currfmts[n_currfmts].just_above_exponent_boundary = (n_currfmts > 0); currfmts[n_currfmts].digits_since_done = 0; n_currfmts++; while (1) { dump_formatters("digit loop"); /* * Fetch enough data to narrow down to the next digit * position, no matter which formatter's scale we're working * at. */ bool lo_open, hi_open; bool force_refine; force_refine = false; { assert(n_currfmts > 0); bigint min_halfscale = currfmts[0].halfscale; if (n_currfmts > 1 && min_halfscale > currfmts[1].halfscale) min_halfscale = currfmts[1].halfscale; while (1) { iterate_to_bounds(&lo, &hi, &lo_open, &hi_open, 0, NULL, force_refine); /* * We make the termination condition of this loop as * carefully loose as possible, to get the maximum * output in cases where limited data is available * (e.g. FileReaders). * * We need our interval to overlap at most two * possible digits' catchment areas. For this, it has * to be contained in either a closed interval between * two exact digit boundaries (which are at least * 2*min_halfscale apart), or a closed interval * between two points exactly midway between digit * boundaries. That will get us what we want * regardless of the rounding mode. * * Also, we don't want it to be the full width of one * of those intervals _and_ be closed at both ends. * (Because that raises the possibility that we might * manage to overlap three digit catchment intervals, * in the case where each endpoint of the width-2 * closed interval would round-to-even outwards.) */ bigint tmp_lo = fdiv(lo, min_halfscale); bool tmp_lo_open = lo_open || (tmp_lo * min_halfscale != lo); bigint tmp_hi = fdiv(hi + min_halfscale-1, min_halfscale); bool tmp_hi_open = hi_open || (tmp_hi * min_halfscale != hi); int s = bigint_sign(tmp_hi - tmp_lo - 2); if (s < 0 || (s == 0 && (tmp_lo_open || tmp_hi_open))) break; force_refine = true; } } dprint("digit loop: got %s%b,%b%s", lo_open?"(":"[", &lo, &hi, hi_open?")":"]"); /* * Iterate over all active formatters, and see what each one * makes of this new piece of data. */ int n_newfmts; n_newfmts = 0; for (int i = 0; i < n_currfmts; ++i) { CurrFmt &cfmt = currfmts[i]; if (cfmt.done) { /* * This formatter is a static entry in the list * representing a successfully rounded output. Check * whether the number has been shown to be on the * wrong side of the rounding boundary; if so, discard * this formatter, and otherwise, just propagate it * into the newfmts list without change. */ int sign; bool open; bigint base; if (i > 0 && cfmt.just_above_exponent_boundary) { base = currfmts[i-1].base; } else { base = cfmt.base; } if (cfmt.round_down) { sign = bigint_sign(lo - base); open = lo_open; } else { sign = bigint_sign(base - hi); open = hi_open; } dprint("check done formatter %d, sign=%d", i, sign); if (sign < 0 || (sign == 0 && !cfmt.round_open && !open)) { cfmt.digits_since_done++; newfmts[n_newfmts++] = cfmt; cfmt.fmt = NULL; // prevent deletion later } continue; } bool rounding = (cfmt.fmt->has_digit_limit() && cfmt.fmt->digits_left() == 1); bigint this_scale = cfmt.halfscale; bigint digit_scale = 1; if (rounding) { digit_scale = cfmt.fmt->round_digit_scale(); this_scale *= digit_scale; dprint("rounding this digit with scale %b", &digit_scale); } else { dprint("not rounding this digit"); } bigint this_lo = fdiv(lo - cfmt.base, this_scale); bool this_lo_open = lo_open || (this_lo * this_scale != lo - cfmt.base); bigint this_hi = fdiv(hi - cfmt.base + this_scale-1, this_scale); bool this_hi_open = hi_open || (this_hi * this_scale != hi - cfmt.base); /* * If there's any possibility of this number being rounded * up to slightly more than its current value at a later * digit position, then we must presume that it could * round up to the high end of our current interval, i.e. * we must treat the interval as closed at the top end for * the purposes of this formatter. * * Rounding up is a possibility if we're not in * round-toward-zero mode, and have a nonzero digit limit * which we haven't yet reached. */ if (rmode != ROUND_TOWARD_ZERO && cfmt.fmt->has_digit_limit() && cfmt.fmt->digits_left() > 1) this_hi_open = false; Interval curr_int(this_lo, this_hi, this_lo_open, this_hi_open); dprint("formatter %d: scaled interval %s%b,%b%s", i, this_lo_open?"(":"[", &this_lo, &this_hi, this_hi_open?")":"]"); /* * If the interval we're now sitting on is _exactly_ zero, * then we've output all the digits of a terminating base * representation, and we should notify the formatter. */ if (this_lo == 0 && this_hi == 0) { dprint("formatter %d: marking as exact", i); cfmt.done = true; cfmt.digits_since_done = 0; cfmt.fmt->done(true); newfmts[n_newfmts++] = cfmt; cfmt.fmt = NULL; // prevent deletion later continue; } /* * Enumerate the possible next digits that might go to * this formatter, based on the number's interval. */ bigint dmin = this_lo / 2U, dmax = (this_hi + 1) / 2U; bool potentially_still_above_exponent_boundary = false; bool potentially_still_below_exponent_boundary = false; if (cfmt.at_first_digit) { bigint first_digit_scale = cfmt.fmt->first_digit_scale() / digit_scale; bigint first_digit_min = (cfmt.at_min_exp ? (bigint)0 : first_digit_scale); bigint first_digit_max = ebase * first_digit_scale; if (dmax >= first_digit_max) dmax = first_digit_max - 1; if (dmin < first_digit_min) dmin = first_digit_min; if (cfmt.just_above_exponent_boundary && !cfmt.at_min_exp && dmin == first_digit_scale) potentially_still_above_exponent_boundary = true; if (cfmt.just_below_exponent_boundary && dmax == first_digit_max - 1) potentially_still_below_exponent_boundary = true; } else { bigint scaled_dbase = (bigint)dbase / digit_scale; if (dmax >= scaled_dbase) dmax = scaled_dbase - 1; if (dmin < 0) dmin = 0; if (cfmt.just_above_exponent_boundary && !cfmt.at_min_exp && dmin == 0) potentially_still_above_exponent_boundary = true; if (cfmt.just_below_exponent_boundary && dmax == scaled_dbase - 1) potentially_still_below_exponent_boundary = true; } dprint("formatter %d: digit range [%b,%b]", i, &dmin, &dmax); for (bigint d = dmin; d <= dmax; ++d) { assert(d >= 0); assert(d < (bigint)dbase); /* * Determine the catchment interval for this digit. * Usually this is just [d,d+1), unless we're at the * rounding point right now, in which case the shape * of the catchment interval varies a lot depending on * rounding mode. */ Interval catchment; if (!rounding || rmode == ROUND_TOWARD_ZERO) { catchment.init(d*2, d*2+2, false, true); } else if (rmode == ROUND_AWAY_FROM_ZERO) { catchment.init(d*2-2, d*2, true, false); } else { bool cmin_open, cmax_open; switch (rmode) { case ROUND_TO_NEAREST_EVEN: /* * If the number is the last digit of an even * multiple of this place value, its catchment * interval is closed at both ends, otherwise * it's open at both ends. */ cmin_open = cmax_open = (((unsigned)d ^ cfmt.parity) & 1) != 0; break; case ROUND_TO_NEAREST_ODD: /* * Exact reverse of ROUND_TO_NEAREST_EVEN, of * course. */ cmin_open = cmax_open = (((unsigned)d ^ cfmt.parity) & 1) == 0; break; case ROUND_TO_NEAREST_TOWARD_ZERO: cmin_open = true; cmax_open = false; break; default /*case ROUND_TO_NEAREST_AWAY_FROM_ZERO */ : cmin_open = false; cmax_open = true; break; } catchment.init(d*2-1, d*2+1, cmin_open, cmax_open); } /* * If this formatter is just above an exponent * boundary, and there's one recorded just below it * which is just below the same boundary, then we * discard the lower bound on this catchment area, * because only the next formatter down can correctly * compute the rounding boundary between exponents. */ if (potentially_still_above_exponent_boundary && d == dmin && n_newfmts > 0 && newfmts[n_newfmts-1].just_below_exponent_boundary) catchment.remove_lower_bound(); if (rounding) { if (catchment.has_lower_bound()) { dprint("catchment interval for digit %b: %s%b,%b%s", &d, catchment.lo_open?"(":"[", &catchment.lo, &catchment.hi, catchment.hi_open?")":"]"); } else { dprint("catchment interval for digit %b: (-inf,%b%s",&d, &catchment.hi, catchment.hi_open?")":"]"); } } /* * Intersect, and if the resulting interval is * non-empty, create a new formatter for the next * digit which has the right parameters. */ Interval out = curr_int.intersect(catchment); if (!out.empty()) { /* * Special case: there's one situation in which we * might find ourselves attempting to produce a * third formatter, which is if we've just crossed * an exponent boundary and the lower-exponent * formatter had two possibilities already. * * In that situation, we discard the current * option: it's only included due to unwillingness * to trust the lower bound of a formatter just * above an exponent boundary, and the fact that * the next formatter down is still considering a * digit lower than 9 (or more generally dmax) is * enough to show that we can't really be in this * exponent's catchment area. */ if (n_newfmts == 2 && potentially_still_above_exponent_boundary && d == dmin && newfmts[n_newfmts-1].exponent != cfmt.exponent) { dprint("formatter %d: would output digit %d, but " "deferring to lower exponent", i, (int)d); continue; } dprint("formatter %d: output digit %d", i, (int)d); assert(n_newfmts < 2); CurrFmt &newfmt = newfmts[n_newfmts]; n_newfmts++; newfmt.fmt = cfmt.fmt->clone(); newfmt.fmt->digit(d * digit_scale); newfmt.at_first_digit = false; newfmt.exponent = cfmt.exponent; newfmt.just_above_exponent_boundary = potentially_still_above_exponent_boundary && d == dmin; newfmt.just_below_exponent_boundary = potentially_still_below_exponent_boundary && d == dmax; newfmt.at_min_exp = cfmt.at_min_exp; if (rounding) { /* * This formatter is now 'done', meaning that * we've sent it all the digits we're going * to. However, that's conditional on the * number turning out to be in the right * interval for this formatter after all - in * some cases we can have two formatters in * the done state, representing what will be * output if the number ends up rounded down * and up respectively, and then we have to * keep processing until we find out which one * is out of range, at which point we discard * it and return just the other one. */ newfmt.done = true; newfmt.digits_since_done = 0; newfmt.fmt->done(false); if (n_newfmts == 1) { newfmt.base = catchment.hi * this_scale + cfmt.base; newfmt.round_down = true; newfmt.round_open = catchment.hi_open; } else { newfmt.base = catchment.lo * this_scale + cfmt.base; newfmt.round_down = false; newfmt.round_open = catchment.lo_open; } newfmt.halfscale = cfmt.halfscale; } else { newfmt.done = false; newfmt.base = cfmt.base + d*2*cfmt.halfscale; newfmt.halfscale = cfmt.halfscale; /* Fold the just-generated digit in to 'parity', * if we're in an odd base; keep parity set to * even, in an even base. Bit-twiddling to do * this without any serious arithmetic. */ newfmt.parity = cfmt.parity ^ ((unsigned)d & dbase); } } /* * Loop on to the next possible digit. */ } } /* * Replace the old set of live formatters with the new set. */ while (n_currfmts > 0) { --n_currfmts; if (currfmts[n_currfmts].fmt) delete currfmts[n_currfmts].fmt; } while (n_currfmts < n_newfmts) { currfmts[n_currfmts] = newfmts[n_currfmts]; n_currfmts++; } assert(n_currfmts > 0); /* * End condition: if all formatters are done and have the same * string in them, we really have finished. (Usually this * occurs because there's only one formatter left, but in the * special case of IEEE overflow we can find we have two * formatters marked as done and agreeing on all their text * despite not having even figured out which of their * exponents is live yet.) */ done = true; for (int i = 0; i < n_currfmts; ++i) { if (!currfmts[i].done && !currfmts[i].fmt->complete()) { done = false; break; } } if (done) { for (int i = 1; i < n_currfmts; ++i) { if (currfmts[0].fmt->current_text() != currfmts[i].fmt->current_text()) { done = false; break; } } } if (done) { crReturnV; assert(!"Called again after finishing output"); } /* * Premultiply in a translation and scaling matrix, and scale * up all the formatters' intervals and bases. */ { bigint newbase = currfmts[0].base; for (int i = 0; i < n_currfmts; ++i) { CurrFmt &cfmt = currfmts[i]; cfmt.base = (cfmt.base - newbase) * dbase; } bigint matrix[4]; matrix[0] = dbase; matrix[1] = -newbase * dbase; matrix[2] = 0; matrix[3] = 1; dprint("premultiplying post-digit zoom %4m", matrix); core->premultiply(matrix); } crReturnV; } crEnd; } bool BaseOutputGenerator::get_definite_output(std::string &out) { if (done) return false; progress(); assert(n_currfmts > 0); dump_formatters("output check"); if (exponent_finding_phase) { /* * Special case: during exponent-finding, we only have one * CurrFmt active and yet we never generate definite output. */ out = ""; dprint("no output available"); return true; } else if (n_currfmts == 1) { /* * If there's only one formatter live, then everything it's * produced is the only possible option and hence definite * output. */ out = currfmts[0].fmt->current_text(); dprint("unilateral \"%s\"", out.c_str()); currfmts[0].fmt->consume_text(out.size()); return true; } else { /* * With two formatters, we can only output what they agree on. */ assert(n_currfmts == 2); std::string out0 = currfmts[0].fmt->current_text(); std::string out1 = currfmts[1].fmt->current_text(); size_t len = 0; while (len < out0.size() && len < out1.size() && out0[len] == out1[len]) ++len; out = out0.substr(0, len); dprint("bilateral \"%s\"", out.c_str()); currfmts[0].fmt->consume_text(len); currfmts[1].fmt->consume_text(len); return true; } } bool BaseOutputGenerator::get_tentative_output(std::string &out, int *digits) { if (done) return false; if (exponent_finding_phase) { /* * Special case: during exponent-finding, we have exactly one * CurrFmt active and its output is all tentative. */ assert(n_currfmts == 1); out = currfmts[0].fmt->current_text(); char buf[256]; sprintf(buf, " (%u^%d)", ebase, exponent); *digits = -exponent; out += buf; return true; } else if (n_currfmts == 2) { /* * Multiple live formatters are precisely the condition under * which we generate tentative output. * * If we haven't rounded yet, then the upper one of the two * formatters must contain what the answer might turn out to * exactly be; if we have, then we pick whichever formatter * has its rounding boundary closed. */ int which, zeroes = 0; for (which = 0; which < 2; ++which) if (currfmts[which].done && !currfmts[which].round_open) { zeroes = currfmts[which].digits_since_done; break; // found a closed done formatter } if (which == 2) which = 1; // otherwise just go with #1 out = currfmts[which].fmt->current_text(); size_t last_nonzero = out.size(); /* * Trim trailing zeroes from the upper formatter, and count * them, to use as our accuracy indicator. */ while (last_nonzero > 0 && out[last_nonzero-1] == '0') last_nonzero--; zeroes += out.size() - last_nonzero; /* * Also trim a decimal point, if that's now the last thing in * the string, because it's ugly to print it unnecessarily. */ if (last_nonzero > 0 && out[last_nonzero-1] == '.') last_nonzero--; out.resize(last_nonzero); char buf[256]; sprintf(buf, " (%u^-%d)", dbase, zeroes); *digits = zeroes; out += buf; return true; } else { return false; } } /* ---------------------------------------------------------------------- * Formatter for spigot's default output mode: just plain positional * notation, with a point if necessary but no exponent. */ class BaseFormatter : public Formatter { unsigned base; char a; // either 'a' or 'A' bool started, written_point; int our_min_exp; unsigned int_digits; unsigned min_int_digits; bool has_dlimit; unsigned digits; public: BaseFormatter(int abase, char aa, bool ahas_dlimit, unsigned adigits, unsigned amin_int_digits) : Formatter() { base = abase; a = aa; started = written_point = false; int_digits = 0; has_dlimit = ahas_dlimit; digits = adigits; min_int_digits = amin_int_digits; if (min_int_digits < 1) min_int_digits = 1; /* * Minimum exponent: if we've been asked for a minimum number * of integer digits, the easiest way to achieve that is by * increasing the minimum exponent. Also, if we're rounding to * a position above the decimal point, that too is most easily * achieved by increasing the min exp. */ our_min_exp = 0; if (our_min_exp < (int)min_int_digits-1) our_min_exp = (int)min_int_digits-1; if (our_min_exp < -(int)digits) our_min_exp = -(int)digits; } virtual BaseFormatter *clone() { return new BaseFormatter(*this); } virtual unsigned ebase() { return base; } virtual unsigned dbase() { return base; } virtual bool has_min_exp() { return true; } virtual int min_exp() { return our_min_exp; } virtual void sign_exp(int s, int e) { if (s < 0) write('-'); int_digits = e+1; digits += int_digits; } virtual void digit(unsigned d) { // Skip this digit if it's a leading zero that we don't want if (started || int_digits <= min_int_digits || d != 0) { started = true; if (!written_point && int_digits == 0) { write('.'); written_point = true; } write(digitchar(d, a)); } if (int_digits > 0) --int_digits; --digits; } virtual void done(bool) { while (int_digits > 0) digit(0); } virtual void exact_zero() { sign_exp(0, our_min_exp); digit(0); } virtual bool has_digit_limit() { return has_dlimit; } virtual unsigned digits_left() { return digits; } }; OutputGenerator *base_format(Spigot *spig, int base, int uppercase, bool has_digitlimit, int digitlimit, RoundingMode rmode, int minintdigits) { return new BaseOutputGenerator (spig, new BaseFormatter(base, uppercase?'A':'a', has_digitlimit, digitlimit, minintdigits), rmode); } /* ---------------------------------------------------------------------- * Formatter for hex bit patterns of IEEE 754-style floats, with * optional extra exponent digits. */ class IEEEFormatter : public Formatter { int expscale, signexpdigits; int mantdigits_left; unsigned expbias; int last_expdigit; bool seen_first_digit; bool has_dlimit; unsigned digits; unsigned roundscale; bool overflow; public: IEEEFormatter(int abits, bool ahas_dlimit, int adigits, bool parity_rounding) : Formatter() { int expbits; expbits = (abits == 16 ? 5 : abits == 32 ? 8 : abits == 64 ? 11 : abits == 128 ? 15 : -1); expbias = (1 << (expbits-1)) - 1; if (ahas_dlimit) { /* * Enforce that negative digit limits don't get too * excessive. In most rounding modes, we can cope with up * to the number of mantissa bits (i.e. removing all but * the implicit leading 1), so -S -d-23, or -D -d-52. But * in parity-based rounding modes (i.e. tie-breaking to * even or odd), we decrease that by one, because at that * extreme _all_ numbers count as even and so we can't * round sensibly. */ int most_neg_dlimit = -(abits - expbits - 1); if (parity_rounding) most_neg_dlimit++; if (adigits < most_neg_dlimit) throw spigot_error("minimum -d value in this mode is %d", most_neg_dlimit); } signexpdigits = (expbits + 1 + 3) / 4; expscale = 1 << (signexpdigits*4 - expbits - 1); mantdigits_left = (abits / 4) - signexpdigits; seen_first_digit = false; has_dlimit = ahas_dlimit; if (has_dlimit) { digits = (bigint)mantdigits_left + fdiv((bigint)adigits + 3, 4); roundscale = 1 << (3U & -adigits); } else { digits = 0; roundscale = 1; } overflow = false; } virtual IEEEFormatter *clone() { return new IEEEFormatter(*this); } virtual unsigned ebase() { return 2; } virtual unsigned dbase() { return 16; } virtual bool has_min_exp() { return true; } virtual int min_exp() { return -(expbias - 1); } virtual void sign_exp(int s, int e) { assert(e > -(int)expbias); unsigned biasexp = e + expbias; assert(biasexp > 0); unsigned signexp = biasexp * expscale; if (signexp >= (1U << (4*signexpdigits - 1)) - expscale) { /* * Overflow case. */ signexp = (1 << (4*signexpdigits-1)) - expscale; if (s < 0) signexp |= 1 << (4*signexpdigits-1); for (int i = signexpdigits - 1; i >= 0; i--) { write(digitchar((signexp >> (4*i)) & 0xF)); } for (int i = mantdigits_left; i-- > 0 ;) { write('0'); } overflow = true; return; } if (s < 0) signexp |= 1 << (4*signexpdigits - 1); /* * We have to decide whether to print the final exponent * digit, or defer it. It must be deferred if any bits of * mantissa live in the same nibble, and also if there's a * possibility of denormalisation. Otherwise we can write it * out now. */ bool print_last_expdigit_now = (biasexp > 1 && expscale == 1); int last_expdigit_to_print = print_last_expdigit_now ? 0 : 1; for (int i = signexpdigits - 1; i >= last_expdigit_to_print; i--) { write(digitchar((signexp >> (4*i)) & 0xF)); } if (print_last_expdigit_now) { last_expdigit = -1; // means we've already printed it } else { last_expdigit = signexp & 0xF; } // The mantissa digit that ends up combined with the exponent // must still be counted in our digit counts. mantdigits_left++; digits++; } virtual void digit(unsigned d) { if (overflow) return; if (!seen_first_digit) { seen_first_digit = true; /* * The first digit is a special case. It will either need * to be combined with the last digit of the exponent * field, or else just dropped completely. */ if (last_expdigit >= 0) { if ((int)d < expscale) { /* * Denormalisation. In this case the exponent * vanishes completely, so we ignore last_expdigit * and just print d unchanged. */ } else { /* * Otherwise, trim the leading bit from d and then * combine it with the remaining exponent bits. */ d = (d & ~expscale) | last_expdigit; } } else { /* * In the case where we elide the leading digit * completely, we expect that it's always just the * leading 1 bit. If it's bigger than that, then we * should have folded it into the last exponent digit; * if it's smaller, we should have adjusted the * exponent field for denormalisation; either way, we * ought not to be in this case. */ assert(d == 1); mantdigits_left--; digits--; return; // do not output anything } } if (mantdigits_left >= 0 && mantdigits_left-- == 0) write('.'); // print a point before the mantissa extension write(digitchar(d)); digits--; } virtual void done(bool) { while (mantdigits_left-- > 0) write('0'); } virtual void exact_zero() { sign_exp(+1, -(expbias - 1)); while (mantdigits_left-- > 0) write('0'); } virtual bool complete() { return overflow; } virtual bool has_digit_limit() { return has_dlimit; } virtual unsigned digits_left() { return digits; } virtual bigint first_digit_scale() { return expscale; } virtual bigint round_digit_scale() { return roundscale; } }; OutputGenerator *ieee_format(Spigot *spig, int bits, bool has_digitlimit, int digitlimit, RoundingMode rmode) { return new BaseOutputGenerator (spig, new IEEEFormatter(bits, has_digitlimit, digitlimit, (rmode == ROUND_TO_NEAREST_EVEN || rmode == ROUND_TO_NEAREST_ODD)), rmode); } /* ---------------------------------------------------------------------- * Formatter for all 'printf'-style output. */ class PrintfFormatter : public Formatter { bool hashflag; size_t fieldwidth; int precision; int intdigits, mindigits; enum { E_MODE, F_MODE, G_MODE } mode; enum { SPACE_BEFORE, ZERO_MIDDLE, SPACE_AFTER } padmode; int expbase, expmult, digitbase; char expchr, poschr, a; std::string prefix, expstr, digits; bool output_started, finished; bool seen_point, stripzeroes; size_t padlen; int saved_zeroes; bool has_dlimit; int dlimit; public: PrintfFormatter(int width, int aprecision, int flags, int specifier, bool nibble_mode) { precision = aprecision; hashflag = (flags & 8); // '#' modifier fieldwidth = (width > 0 ? width : 0); if (flags & 1) // '-' modifier padmode = SPACE_AFTER; else if (flags & 16) // '0' modifier padmode = ZERO_MIDDLE; else padmode = SPACE_BEFORE; bool uppercase = isupper(specifier); if (strchr("aA", specifier)) { if (nibble_mode) { expbase = 16; expmult = 4; } else { expbase = 2; expmult = 1; } digitbase = 16; prefix = (uppercase ? "0X" : "0x"); expchr = (uppercase ? 'P' : 'p'); a = (uppercase ? 'A' : 'a'); mode = E_MODE; // %a is just a hex version of %e } else { expbase = digitbase = 10; expmult = 1; expchr = (uppercase ? 'E' : 'e'); if (precision < 0) precision = 6; mode = (strchr("gG", specifier) ? G_MODE : strchr("fF", specifier) ? F_MODE : E_MODE); } if (flags & 2) // '+' modifier poschr = '+'; else if (flags & 4) // ' ' modifier poschr = ' '; else poschr = '\0'; output_started = false; seen_point = false; saved_zeroes = 0; finished = false; stripzeroes = false; } virtual PrintfFormatter *clone() { return new PrintfFormatter(*this); } virtual unsigned ebase() { return expbase; }; virtual unsigned dbase() { return digitbase; }; virtual bool has_min_exp() { return (mode == F_MODE); } virtual int min_exp() { return 0; } virtual void sign_exp(int sign, int exponent) { exponent *= expmult; if (mode == G_MODE) { /* * Change into F_MODE or E_MODE depending on exponent. */ int P = (precision == 0 ? 1 : precision); int X = exponent; if (P > X && X >= -4) { mode = F_MODE; precision = P - (X + 1); } else { mode = E_MODE; precision = P - 1; } if (!hashflag) stripzeroes = true; } if (sign < 0) { prefix = "-" + prefix; } else if (poschr) { prefix = poschr + prefix; } // Initialise 'mindigits' to a temporary value, to prevent // undefined behaviour when digit() looks at it. We'll set it // to the proper value further on. mindigits = 0; if (mode == F_MODE) { // In 'f' mode, we don't start counting precision digits // until after the decimal point, so adjust for that. intdigits = (exponent > 0 ? exponent : 0) + 1; if (precision >= 0) precision += intdigits; // If the exponent is negative, output some leading // zeroes. (This can't happen in _real_ 'f' mode, because // we set min_exp in that case, but it can happen if we // just converted 'g' mode into 'f' mode above). while (exponent < 0) { digit(0); exponent++; precision--; assert(precision > 0); } } else { // In non-'f' mode, we print an exponent suffix. char expbuf[256]; #if defined _MSC_VER #define snprintf _snprintf #endif snprintf(expbuf, sizeof(expbuf), "%c%+.*d", expchr, expbase==10 ? 2 : 1, // min 2 digits for decimal exp exponent); expstr = expbuf; intdigits = 1; if (precision >= 0) precision++; // this doesn't include the int part } mindigits = precision; if (precision >= 0) { has_dlimit = true; dlimit = precision; } else { has_dlimit = false; } } private: void putstr(std::string s) { for (size_t i = 0; i < s.length(); ++i) write(s[i]); } size_t length_so_far() { return prefix.length() + expstr.length() + digits.length(); } void start_output() { size_t len = length_so_far(); padlen = (fieldwidth > len ? fieldwidth - len : 0); if (padmode == SPACE_BEFORE) for (size_t i = 0; i < padlen; i++) write(' '); putstr(prefix); if (padmode == ZERO_MIDDLE) for (size_t i = 0; i < padlen; i++) write('0'); putstr(digits); output_started = true; } void putch_inner(char c) { if (output_started) { write(c); } else { digits.push_back(c); if (length_so_far() >= fieldwidth) start_output(); } } void putch_middle(char c) { if (!hashflag && !seen_point && intdigits <= 0) { putch_inner('.'); seen_point = true; } putch_inner(c); if (mindigits > 0) mindigits--; if (!seen_point) intdigits--; if (hashflag && !seen_point && intdigits <= 0) { putch_inner('.'); seen_point = true; } } virtual void putch(char c) { if (stripzeroes && c == '0' && intdigits <= 0) { saved_zeroes++; } else { while (saved_zeroes > 0) { putch_middle('0'); saved_zeroes--; } putch_middle(c); } } public: virtual void digit(unsigned d) { putch(digitchar(d, a)); dlimit--; } virtual void done(bool) { for (int i = mindigits; i > 0; i--) putch('0'); if (!output_started) start_output(); putstr(expstr); if (padmode == SPACE_AFTER) for (size_t i = 0; i < padlen; i++) write(' '); finished = true; } virtual void exact_zero() { sign_exp(0, 0); digit(0); } virtual bool has_digit_limit() { return has_dlimit; } virtual unsigned digits_left() { return dlimit; } }; OutputGenerator *printf_format(Spigot *spig, RoundingMode rmode, int width, int precision, int flags, int specifier, bool nibble_mode) { return new BaseOutputGenerator (spig, new PrintfFormatter(width, precision, flags, specifier, nibble_mode), rmode); } spigot-0.2017-01-15.gdad1bbc6/baseout.h0000644000000000000000000000506413025565031013773 0ustar /* * baseout.h: header for baseout.cpp. */ /* * Rounding modes. */ enum RoundingMode { /* * The obvious two directed rounding modes. (If the caller * wants round-to-minus-inf or round-to-plus-inf, they must * work that out for themselves by considering the sign.) */ ROUND_TOWARD_ZERO, ROUND_AWAY_FROM_ZERO, /* * Four forms of round-to-nearest, differing in what they * do to exact ties. Again, the two directed tie-breaking * policies are signless. */ ROUND_TO_NEAREST_EVEN, ROUND_TO_NEAREST_ODD, ROUND_TO_NEAREST_TOWARD_ZERO, ROUND_TO_NEAREST_AWAY_FROM_ZERO, /* * Modes which depend on the sign. Inside baseout.cpp, these are * translated into one of the modes above once the sign of the * number is known. */ ROUND_UP, ROUND_DOWN, ROUND_TO_NEAREST_UP, ROUND_TO_NEAREST_DOWN }; /* * Construct an OutputGenerator for numbers written in standard * positional notation in any integer base from 2 to 36. */ OutputGenerator *base_format(Spigot *spig, int base, int uppercase, bool has_digitlimit, int digitlimit, RoundingMode rmode, int minintdigits); /* * Construct an OutputGenerator for numbers formatted like IEEE bit * patterns (with optional trailing digits for extra precision). */ OutputGenerator *ieee_format(Spigot *spig, int ieee_bits, bool has_digitlimit, int digitlimit, RoundingMode rmode); /* * Construct an OutputGenerator to format a string based on a * 'printf' float-type specification. * * 'width' and 'precision' are the numbers given in the specification, * or -1 in each case to indicate that none was provided. 'specifier' * is the char value of the main formatting directive (one of * 'e','f','g','a' or its uppercase equivalent). 'flags' is a bit-mask * in which the bit (1 << n) means the nth flag character in * PRINTF_FLAGSTR. * * 'nibble_mode' controls the choice of exponent in %a formats. If it * is false, the chosen exponent is always as large as possible, so * that the leading digit (for any nonzero number) is 1. If true, the * chosen exponent is always a multiple of 4, so that the hex digits * of the output align to the hex digits in ordinary -b16 mode. */ #define PRINTF_FLAGSTR "-+ #0" OutputGenerator *printf_format(Spigot *spig, RoundingMode rmode, int width, int precision, int flags, int specifier, bool nibble_mode); spigot-0.2017-01-15.gdad1bbc6/bi_gmp.h0000644000000000000000000002471213025565031013567 0ustar /* * bigint.h: Implementation of big integers by wrapping GMP in a C++ * class. * * (I tried using gmp++, but found it didn't quite give a good enough * illusion of bigints being a native type - I forget what, but there * was some way in which it did something confusing.) */ #include #include #define BIGINT_PROVIDER "gmp" class bigint { public: inline bigint() { mpz_init(z); } inline bigint(int x) { mpz_init_set_si(z, x); } inline bigint(unsigned x) { mpz_init_set_ui(z, x); } inline bigint(mpz_t x) { mpz_init_set(z, x); } inline bigint(const bigint &x) { mpz_init_set(z, x.z); } inline ~bigint() { mpz_clear(z); } inline operator int() const { return mpz_get_si(z); } inline operator unsigned() const { unsigned ret = mpz_get_ui(z); /* mpz_get_ui returns the absolute value, so we might need to negate */ if (mpz_cmp_si(z, 0) < 0) ret = -ret; return ret; } inline operator bool() const { return mpz_cmp_si(z, 0) != 0; } inline bigint &operator=(const bigint &a) { mpz_set(z,a.z); return *this; } inline bigint &operator+=(const bigint &a) { overwrite_add(*this, a); return *this; } inline bigint &operator+=(int a) { overwrite_add(*this, a); return *this; } inline bigint &operator-=(const bigint &a) { overwrite_sub(*this, a); return *this; } inline bigint &operator-=(int a) { overwrite_sub(*this, a); return *this; } inline bigint &operator*=(const bigint &a) { overwrite_mul(*this, a); return *this; } inline bigint &operator*=(int a) { overwrite_mul(*this, a); return *this; } inline bigint &operator*=(unsigned a) { overwrite_mul(*this, a); return *this; } inline bigint &operator/=(const bigint &a) { overwrite_div(*this, a); return *this; } inline bigint &operator/=(unsigned a) { overwrite_div(*this, a); return *this; } inline bigint &operator%=(const bigint &a) { overwrite_mod(*this, a); return *this; } inline bigint &operator%=(unsigned a) { overwrite_mod(*this, a); return *this; } inline bigint &operator<<=(unsigned a) { overwrite_shl(*this, a); return *this; } inline bigint &operator>>=(unsigned a) { overwrite_shr(*this, a); return *this; } inline bigint &operator++() { mpz_add_ui(z,z,1); return *this; } inline bigint &operator--() { mpz_sub_ui(z,z,1); return *this; } private: mpz_t z; inline void overwrite_add(const bigint &x, const bigint &a) { mpz_add(z,x.z,a.z); } inline void overwrite_add(const bigint &x, int a) { if (a > 0) mpz_add_ui(z,x.z,a); else mpz_sub_ui(z,x.z,-a); } inline void overwrite_sub(const bigint &x, const bigint &a) { mpz_sub(z,x.z,a.z); } inline void overwrite_sub(const bigint &x, int a) { overwrite_add(x,-a); } inline void overwrite_mul(const bigint &x, const bigint &a) { mpz_mul(z,x.z,a.z); } inline void overwrite_mul(const bigint &x, int a) { mpz_mul_si(z,x.z,a); } inline void overwrite_mul(const bigint &x, unsigned a) { mpz_mul_ui(z,x.z,a); } inline void overwrite_div(const bigint &x, const bigint &a) { mpz_tdiv_q(z,x.z,a.z); } inline void overwrite_div(const bigint &x, unsigned a) { mpz_tdiv_q_ui(z,x.z,a); } inline void overwrite_mod(const bigint &x, const bigint &a) { mpz_tdiv_r(z,x.z,a.z); } inline void overwrite_mod(const bigint &x, unsigned a) { mpz_tdiv_r_ui(z,x.z,a); } inline void overwrite_shl(const bigint &x, unsigned a) { mpz_mul_2exp(z,x.z,a); } inline void overwrite_shr(const bigint &x, unsigned a) { mpz_fdiv_q_2exp(z,x.z,a); } friend bigint operator+(const bigint &a, const bigint &b); friend bigint operator+(const bigint &a, int b); friend bigint operator+(int a, const bigint &b); friend bigint operator-(const bigint &a, const bigint &b); friend bigint operator-(const bigint &a, int b); friend bigint operator-(int a, const bigint &b); friend bigint operator*(const bigint &a, const bigint &b); friend bigint operator*(const bigint &a, int b); friend bigint operator*(const bigint &a, unsigned b); friend bigint operator*(int a, const bigint &b); friend bigint operator*(unsigned a, const bigint &b); friend bigint operator/(const bigint &a, const bigint &b); friend bigint operator/(const bigint &a, unsigned b); friend bigint fdiv(const bigint &a, const bigint &b); friend bigint operator%(const bigint &a, const bigint &b); friend bigint operator%(const bigint &a, unsigned b); friend bigint operator<<(const bigint &a, unsigned b); friend bigint operator>>(const bigint &a, unsigned b); friend bigint operator-(const bigint &a); friend bool operator==(const bigint &a, const bigint &b); friend bool operator!=(const bigint &a, const bigint &b); friend bool operator<(const bigint &a, const bigint &b); friend bool operator>(const bigint &a, const bigint &b); friend bool operator<=(const bigint &a, const bigint &b); friend bool operator>=(const bigint &a, const bigint &b); friend bool operator==(const bigint &a, int b); friend bool operator!=(const bigint &a, int b); friend bool operator<(const bigint &a, int b); friend bool operator>(const bigint &a, int b); friend bool operator<=(const bigint &a, int b); friend bool operator>=(const bigint &a, int b); friend bool operator==(int a, const bigint &b); friend bool operator!=(int a, const bigint &b); friend bool operator<(int a, const bigint &b); friend bool operator>(int a, const bigint &b); friend bool operator<=(int a, const bigint &b); friend bool operator>=(int a, const bigint &b); friend bigint bigint_abs(const bigint &a); friend int bigint_sign(const bigint &a); friend bigint bigint_sqrt(const bigint &a); friend void bigint_print(const bigint &a); friend char *bigint_decstring(const bigint &a); friend char *bigint_hexstring(const bigint &a); friend bigint bigint_power(const bigint &x, unsigned y); friend unsigned bigint_approxlog2(const bigint &a); friend int bigint_bit(const bigint &a, unsigned index); }; inline bigint operator+(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_add(a,b); return ret; } inline bigint operator+(const bigint &a, int b) { bigint ret; ret.overwrite_add(a,b); return ret; } inline bigint operator+(int a, const bigint &b) { bigint ret; ret.overwrite_add(a,b); return ret; } inline bigint operator-(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_sub(a,b); return ret; } inline bigint operator-(const bigint &a, int b) { bigint ret; ret.overwrite_sub(a,b); return ret; } inline bigint operator-(int a, const bigint &b) { bigint ret; mpz_neg(ret.z, b.z); return ret += a; } inline bigint operator*(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_mul(a,b); return ret; } inline bigint operator*(const bigint &a, int b) { bigint ret; ret.overwrite_mul(a,b); return ret; } inline bigint operator*(const bigint &a, unsigned b) { bigint ret; ret.overwrite_mul(a,b); return ret; } inline bigint operator*(int a, const bigint &b) { bigint ret; ret.overwrite_mul(b,a); return ret; } inline bigint operator*(unsigned a, const bigint &b) { bigint ret; ret.overwrite_mul(b,a); return ret; } inline bigint operator/(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_div(a,b); return ret; } inline bigint operator/(const bigint &a, unsigned b) { bigint ret; ret.overwrite_div(a,b); return ret; } inline bigint fdiv(const bigint &a, const bigint &b) { bigint ret; mpz_fdiv_q(ret.z, a.z, b.z); return ret; } inline bigint operator%(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_mod(a,b); return ret; } inline bigint operator%(const bigint &a, unsigned b) { bigint ret; ret.overwrite_mod(a,b); return ret; } inline bigint operator<<(const bigint &a, unsigned b) { bigint ret; ret.overwrite_shl(a,b); return ret; } inline bigint operator>>(const bigint &a, unsigned b) { bigint ret; ret.overwrite_shr(a,b); return ret; } inline bigint operator-(const bigint &a) { bigint ret; mpz_neg(ret.z,a.z); return ret; } inline bool operator==(const bigint &a, const bigint &b) { return 0 == mpz_cmp(a.z,b.z); } inline bool operator!=(const bigint &a, const bigint &b) { return 0 != mpz_cmp(a.z,b.z); } inline bool operator<(const bigint &a, const bigint &b) { return 0 > mpz_cmp(a.z,b.z); } inline bool operator>(const bigint &a, const bigint &b) { return 0 < mpz_cmp(a.z,b.z); } inline bool operator<=(const bigint &a, const bigint &b) { return 0 >= mpz_cmp(a.z,b.z); } inline bool operator>=(const bigint &a, const bigint &b) { return 0 <= mpz_cmp(a.z,b.z); } inline bool operator==(const bigint &a, int b) { return 0 == mpz_cmp_si(a.z,b); } inline bool operator!=(const bigint &a, int b) { return 0 != mpz_cmp_si(a.z,b); } inline bool operator<(const bigint &a, int b) { return 0 > mpz_cmp_si(a.z,b); } inline bool operator>(const bigint &a, int b) { return 0 < mpz_cmp_si(a.z,b); } inline bool operator<=(const bigint &a, int b) { return 0 >= mpz_cmp_si(a.z,b); } inline bool operator>=(const bigint &a, int b) { return 0 <= mpz_cmp_si(a.z,b); } inline bool operator==(int a, const bigint &b) { return 0 == mpz_cmp_si(b.z,a); } inline bool operator!=(int a, const bigint &b) { return 0 != mpz_cmp_si(b.z,a); } inline bool operator<(int a, const bigint &b) { return 0 < mpz_cmp_si(b.z,a); } inline bool operator>(int a, const bigint &b) { return 0 > mpz_cmp_si(b.z,a); } inline bool operator<=(int a, const bigint &b) { return 0 <= mpz_cmp_si(b.z,a); } inline bool operator>=(int a, const bigint &b) { return 0 >= mpz_cmp_si(b.z,a); } inline bigint bigint_abs(const bigint &a) { bigint ret; mpz_abs(ret.z,a.z); return ret; } inline int bigint_sign(const bigint &a) { return mpz_cmp_si(a.z, 0); } inline bigint bigint_sqrt(const bigint &a) { bigint ret; mpz_sqrt(ret.z,a.z); return ret; } inline void bigint_print(const bigint &a) { mpz_out_str(stdout,10,a.z); } inline char *bigint_decstring(const bigint &a) { return mpz_get_str(NULL,10,a.z); } inline char *bigint_hexstring(const bigint &a) { return mpz_get_str(NULL,16,a.z); } inline bigint bigint_power(const bigint &x, unsigned y) { bigint ret; mpz_pow_ui(ret.z,x.z,y); return ret; } /* mpz_sizeinbase with base==2 is documented, rather vaguely, as 'can * be used to locate the most significant 1 bit, counting from 1', * which I interpret as saying that it returns 1 + floor(log2(x)). We * want exactly floor(log2(x)), so subtract 1, clipping at zero. */ inline unsigned bigint_approxlog2(const bigint &a) { unsigned ret = mpz_sizeinbase(a.z, 2); return ret < 1 ? 0 : ret-1; } inline int bigint_bit(const bigint &a, unsigned index) { return mpz_tstbit(a.z, index); } spigot-0.2017-01-15.gdad1bbc6/bi_internal.h0000644000000000000000000006077613025565031014632 0ustar /* * Internal implementation of bigints, for spigot to use when GMP is * not available. */ #include #include #include #include #include #include #include #if defined _MSC_VER /* no stdint.h, at least in VS2010 */ typedef __int64 signed_dword; typedef unsigned __int64 dword; typedef unsigned __int32 word; #define WORDSIZE 32 #else #include typedef int64_t signed_dword; typedef uint64_t dword; typedef uint32_t word; #define WORDSIZE 32 #endif #define BIGINT_PROVIDER "internal" class bigint { std::vector v; int sign; // must be +1 or -1 at all times inline word word_at(size_t index) const { if (index >= v.size()) return 0; return v[index]; } static inline word add_words(word a, word b, word &carry) { dword sum = (dword)a + b + carry; carry = sum >> WORDSIZE; return sum; } inline void ensure_index_exists(size_t index) { if (index >= v.size()) v.resize(index + 1, 0); } inline word add_word_at_pos(size_t index, word a, word carry = 0) { ensure_index_exists(index); v[index] = add_words(v[index], a, carry); return carry; } inline void normalise() { // Normalise by removing leading zero words. size_t index; for (index = v.size(); index > 0 && v[index-1] == 0; index--) /* do nothing */; v.resize(index); } template inline void mag_add_in_place(Words mag, size_t n, size_t pos = 0) { word carry = 0; size_t index = 0; ensure_index_exists(n + pos + 1); while (index < n) { carry = add_word_at_pos(index + pos, mag[index], carry); index++; } while (carry) { carry = add_word_at_pos(index + pos, 0, carry); index++; } normalise(); } template inline void mag_sub_in_place(Words mag, size_t n, size_t pos = 0) { word carry = 1; size_t index = 0; ensure_index_exists(n+1); while (index < n) { carry = add_word_at_pos(index + pos, ~mag[index], carry); index++; } while (!carry && index < v.size()) { carry = add_word_at_pos(index + pos, ~(word)0, carry); index++; } if (!carry) { // Sign has flipped. sign = -sign; carry = 1; for (index = 0; index < v.size(); ++index) v[index] = add_words(0, ~v[index], carry); } normalise(); } inline void add_in_place(const bigint &that, int that_sign) { if (sign == that.sign * that_sign) mag_add_in_place(that.v, that.v.size()); else mag_sub_in_place(that.v, that.v.size()); } inline void add_word_in_place(word w, int w_sign) { if (sign == w_sign) mag_add_in_place(&w, 1); else mag_sub_in_place(&w, 1); } inline void add_int_in_place(int i, int i_sign) { // current implementation depends on an int fitting in a word enum { maxcheck = 1 / (int)(INT_MAX < ~(word)0) }; if (i < 0) add_word_in_place(-i, -i_sign); else add_word_in_place(+i, +i_sign); } inline void set_to_int(int i) { // current implementation depends on an int fitting in a word enum { maxcheck = 1 / (int)(INT_MAX < ~(word)0) }; if (i == 0) { v.resize(0); sign = +1; } else { v.resize(1); if (i < 0) { v[0] = -i; sign = -1; } else { v[0] = +i; sign = +1; } } } inline void set_to_word(word w, int input_sign = +1) { sign = input_sign; if (w) { v.resize(1); v[0] = w; } else { v.resize(0); } } inline void set_to_unsigned(unsigned u) { // current implementation depends on an unsigned fitting in a word enum { maxcheck = 1 / (int)(UINT_MAX <= ~(word)0) }; set_to_word(u); } inline void set_to(const bigint &b) { v.resize(b.v.size()); sign = b.sign; for (size_t index = 0; index < b.v.size(); ++index) v[index] = b.v[index]; } inline void multiply_word_in_place(word w, int w_sign = +1) { size_t i; if (w == 0) { v.resize(0); return; } sign *= w_sign; word carry = 0; for (i = 0; i < v.size(); ++i) { dword product = (dword)v[i] * w + carry; v[i] = product; carry = product >> WORDSIZE; } if (carry != 0) add_word_at_pos(i, carry); } inline void multiply_int_in_place(int i, int i_sign = +1) { if (i < 0) multiply_word_in_place(-i, -i_sign); else multiply_word_in_place(+i, +i_sign); } inline void multiply_overwrite_naive(const bigint &a, const bigint &b) { size_t maxsize, i, j; sign = a.sign * b.sign; maxsize = a.v.size() + b.v.size() + 1; v.resize(maxsize); for (i = 0; i < maxsize; i++) v[i] = 0; for (i = 0; i < a.v.size(); ++i) { word carry = 0; for (j = 0; j < b.v.size(); ++j) { dword product = (dword)a.v[i] * b.v[j]; carry = ((product >> WORDSIZE) + add_word_at_pos(i+j, product, carry)); } carry = add_word_at_pos(i+j, carry); assert(carry == 0); } normalise(); } inline void multiply_overwrite(const bigint &a, const bigint &b) { size_t asize = a.v.size(), bsize = b.v.size(); /* * Deal with really easy cases. */ if (asize < 2 || bsize < 2) { if (asize == 0 || bsize == 0) { v.resize(0); return; } if (asize == 1) { set_to(b); multiply_word_in_place(a.v[0], a.sign); return; } if (bsize == 1) { set_to(a); multiply_word_in_place(b.v[0], b.sign); return; } } #if 0 size_t minsize = (asize < bsize ? asize : bsize); if (minsize > 96) { /* * Karatsuba reduction. Currently conditioned out, because * my timings (of 'make test' and 'spigot -d10000 pi') * suggested it didn't seem to be helping, no matter what * I set the threshold to. */ size_t maxsize = (asize > bsize ? asize : bsize); size_t index = maxsize / 2; size_t sh = index * WORDSIZE; bigint atop = a >> (unsigned)sh, btop = b >> (unsigned)sh; bigint abot = a, bbot = b; abot.v.resize(index); bbot.v.resize(index); bigint top = atop * btop; bigint bottom = abot * bbot; bigint middle = (atop + abot) * (btop + bbot) - top - bottom; set_to(bottom); mag_add_in_place(middle.v, middle.v.size(), index); mag_add_in_place(top.v, top.v.size(), index * 2); } else #endif { multiply_overwrite_naive(a, b); } } template inline int compare(Words mag, size_t bsize, int bsign) const { size_t asize = v.size(); if (asize == 0 && bsize == 0) return 0; if (sign != bsign) return sign < bsign ? -1 : +1; if (asize != bsize) return sign * (asize < bsize ? -1 : +1); for (size_t index = asize; index-- > 0 ;) { if (v[index] != mag[index]) return sign * (v[index] < mag[index] ? -1 : +1); } return 0; } inline int compare_bigint(const bigint &b) const { return compare(b.v, b.v.size(), b.sign); } inline int compare_int(int i) const { // current implementation depends on an int fitting in a word enum { maxcheck = 1 / (int)(INT_MAX < ~(word)0) }; word w; if (i < 0) { w = -i; return compare(&w, 1, -1); } else { w = +i; return compare(&w, i > 0, +1); } } inline word short_divide(word d) { /* * Writes the quotient in place; returns the remainder. * Remainder is the absolute value; it should be given the * sign of the input. */ assert(d != 0); size_t index = v.size(); word r = 0; while (index >= 1) { dword n = ((dword)r << WORDSIZE) + word_at(index-1); dword q = n / d; r = n % d; v[index-1] = q; index--; } normalise(); return r; } template static void debug_pieces(int sign, size_t size, Words data, const char *s = "") { printf("%s%+d %3d [", s, sign, (int)size); for (size_t i = 0; i < size; ++i) { printf("%s%08x", i>0 ? " " : "", data[size-1-i]); } printf("]\n"); } void debug(const char *s = "") const { debug_pieces(sign, v.size(), v, s); } inline bigint long_divide(const bigint &d) { /* * Writes the remainder in place; returns the quotient. */ assert(d.v.size() != 0); /* catch div by zero */ size_t dsize = d.v.size(); size_t nsize = v.size(); bigint q; if (nsize > dsize) q.v.resize(nsize - dsize + 1, 0); /* just to avoid n increments */ q.sign = sign * d.sign; dword dtop = d.v[dsize - 1]; dtop <<= WORDSIZE; if (dsize > 1) dtop |= d.v[dsize - 2]; unsigned dshift = 0; for (int sh = WORDSIZE; sh != 0; sh >>= 1) { if (!(dtop >> (2*WORDSIZE-sh))) { dtop <<= sh; dshift += sh; } } dtop >>= WORDSIZE; if (nsize + 2 >= dsize) { /* otherwise skip this loop entirely */ for (size_t index = nsize - dsize + 2; index-- > 0 ;) { dword ntop = word_at(index + dsize); ntop <<= WORDSIZE; ntop |= word_at(index + dsize - 1); ntop <<= dshift; if (dshift > 0 && index + dsize >= 2) ntop |= word_at(index + dsize - 2) >> (WORDSIZE - dshift); dword qword = ntop / dtop; if (qword > ~(word)0) qword = ~(word)0; /* any apparent overflow will * not be real */ dword carry = 1; dword product = 0; for (size_t j = 0; j < dsize+1 || product; ++j) { product += qword * d.word_at(j); word prodword = product; product >>= WORDSIZE; carry = add_word_at_pos(index + j, ~prodword, carry); } /* * We might have overestimated qword by one above - * perhaps even by more than one, in some fiddly cases * where the digits after ntop are low and those after * dtop are high. A simple while loop should suffice * to correct it. */ while (!carry) { carry = 0; for (size_t j = 0; j < dsize+1; ++j) { carry = add_word_at_pos(index + j, d.word_at(j), carry); } qword--; } q.add_word_at_pos(index, qword); } } q.normalise(); normalise(); return q; } /* bitpos can be as small as -(WORDSIZE-1) and this function will * still be expected to work. */ inline word word_at_bit_pos(size_t bitpos) const { size_t index1 = (bitpos + WORDSIZE) / WORDSIZE; size_t sh = (bitpos + WORDSIZE) % WORDSIZE; word word0 = index1 > 0 ? word_at(index1 - 1) : 0; if (sh == 0) return word0; word word1 = word_at(index1); return (word0 >> sh) | (word1 << (WORDSIZE - sh)); } inline void shift_right_in_place(size_t sh) { if (v.size() < sh/WORDSIZE) { v.resize(0); } else { size_t max = v.size() - sh / WORDSIZE; for (size_t index = 0; index < max; ++index) v[index] = word_at_bit_pos(sh + index * WORDSIZE); v.resize(max); normalise(); } } inline bigint shift_left(size_t sh) const { bigint ret; ret.sign = sign; ret.v.resize(v.size() + (sh + WORDSIZE - 1) / WORDSIZE, 0); size_t start = sh / WORDSIZE; size_t offset = sh % WORDSIZE; for (size_t i = 0; i < v.size() + 1; ++i) ret.add_word_at_pos(i + start, word_at_bit_pos(i * WORDSIZE - offset)); ret.normalise(); return ret; } public: inline bigint() : sign(+1) { } inline bigint(int x) : sign(+1) { set_to_int(x); } inline bigint(unsigned x) : sign(+1) { set_to_unsigned(x); } inline bigint(const bigint &x) { set_to(x); } inline ~bigint() { } inline operator int() const { // current implementation depends on a word fitting in an unsigned enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) }; return (unsigned)word_at(0) * (unsigned)sign; } inline operator unsigned() const { // current implementation depends on a word fitting in an unsigned enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) }; return (unsigned)word_at(0) * (unsigned)sign; } inline operator bool() const { return v.size() != 0; } inline bigint &operator=(const bigint &a) { set_to(a); return *this; } inline bigint &operator+=(const bigint &a) { add_in_place(a, +1); return *this; } inline bigint &operator+=(int a) { add_int_in_place(a, +1); return *this; } inline bigint &operator-=(const bigint &a) { add_in_place(a, -1); return *this; } inline bigint &operator-=(int a) { add_int_in_place(a, -1); return *this; } inline bigint &operator*=(const bigint &a) { bigint ret = (*this) * a; set_to(ret); return *this; } inline bigint &operator*=(int a) { multiply_int_in_place(a); return *this; } inline bigint &operator*=(unsigned a) { multiply_word_in_place(a); return *this; } inline bigint &operator/=(const bigint &a) { set_to(long_divide(a)); return *this; } inline bigint &operator/=(unsigned a) { // current implementation depends on a word fitting in an unsigned enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) }; short_divide(a); return *this; } inline bigint &operator%=(const bigint &a) { long_divide(a); return *this; } inline bigint &operator%=(unsigned a) { // current implementation depends on a word fitting in an unsigned enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) }; set_to_word(short_divide(a), sign); return *this; } inline bigint &operator<<=(unsigned a) { set_to(shift_left(a)); return *this; } inline bigint &operator>>=(unsigned a) { shift_right_in_place(a); return *this; } inline bigint &operator++() { add_word_in_place(1, +1); return *this; } inline bigint &operator--() { add_word_in_place(1, -1); return *this; } private: friend bigint operator+(const bigint &a, const bigint &b); friend bigint operator+(const bigint &a, int b); friend bigint operator+(int a, const bigint &b); friend bigint operator-(const bigint &a, const bigint &b); friend bigint operator-(const bigint &a, int b); friend bigint operator-(int a, const bigint &b); friend bigint operator*(const bigint &a, const bigint &b); friend bigint operator*(const bigint &a, int b); friend bigint operator*(const bigint &a, unsigned b); friend bigint operator*(int a, const bigint &b); friend bigint operator*(unsigned a, const bigint &b); friend bigint operator/(const bigint &a, const bigint &b); friend bigint operator/(const bigint &a, unsigned b); friend bigint fdiv(const bigint &a, const bigint &b); friend bigint operator%(const bigint &a, const bigint &b); friend bigint operator%(const bigint &a, unsigned b); friend bigint operator<<(const bigint &a, unsigned b); friend bigint operator>>(const bigint &a, unsigned b); friend bigint operator-(const bigint &a); friend bool operator==(const bigint &a, const bigint &b); friend bool operator!=(const bigint &a, const bigint &b); friend bool operator<(const bigint &a, const bigint &b); friend bool operator>(const bigint &a, const bigint &b); friend bool operator<=(const bigint &a, const bigint &b); friend bool operator>=(const bigint &a, const bigint &b); friend bool operator==(const bigint &a, int b); friend bool operator!=(const bigint &a, int b); friend bool operator<(const bigint &a, int b); friend bool operator>(const bigint &a, int b); friend bool operator<=(const bigint &a, int b); friend bool operator>=(const bigint &a, int b); friend bool operator==(int a, const bigint &b); friend bool operator!=(int a, const bigint &b); friend bool operator<(int a, const bigint &b); friend bool operator>(int a, const bigint &b); friend bool operator<=(int a, const bigint &b); friend bool operator>=(int a, const bigint &b); friend bigint bigint_abs(const bigint &a); friend int bigint_sign(const bigint &a); friend bigint bigint_sqrt(const bigint &a); friend char *bigint_decstring(const bigint &a); friend char *bigint_hexstring(const bigint &a); friend bigint bigint_power(const bigint &x, unsigned y); friend unsigned bigint_approxlog2(const bigint &a); friend int bigint_bit(const bigint &a, unsigned index); }; inline bigint operator+(const bigint &a, const bigint &b) { bigint ret = a; return ret += b; } inline bigint operator+(const bigint &a, int b) { bigint ret = a; return ret += b; } inline bigint operator+(int a, const bigint &b) { bigint ret = b; return ret += a; } inline bigint operator-(const bigint &a, const bigint &b) { bigint ret = a; return ret -= b; } inline bigint operator-(const bigint &a, int b) { bigint ret = a; return ret -= b; } inline bigint operator-(int a, const bigint &b) { bigint ret = b; ret.sign = -ret.sign; return ret += a; } inline bigint operator*(const bigint &a, const bigint &b) { bigint ret; ret.multiply_overwrite(a, b); return ret; } inline bigint operator*(const bigint &a, int b) { bigint ret = a; return ret *= b; } inline bigint operator*(const bigint &a, unsigned b) { bigint ret = a; return ret *= b; } inline bigint operator*(int a, const bigint &b) { bigint ret = b; return ret *= a; } inline bigint operator*(unsigned a, const bigint &b) { bigint ret = b; return ret *= a; } inline bigint operator/(const bigint &a, const bigint &b) { bigint tmp = a; return tmp.long_divide(b); } inline bigint operator/(const bigint &a, unsigned b) { bigint ret = a; return ret /= b; } inline bigint fdiv(const bigint &n, const bigint &d) { bigint q, r = n; q = r.long_divide(d); if (bigint_sign(r) * bigint_sign(d) < 0) --q; return q; } inline bigint operator%(const bigint &a, const bigint &b) { bigint ret = a; ret.long_divide(b); return ret; } inline bigint operator%(const bigint &a, unsigned b) { bigint ret = a; return ret %= b; } inline bigint operator<<(const bigint &a, unsigned b) { return a.shift_left(b); } inline bigint operator>>(const bigint &a, unsigned b) { bigint ret = a; return ret >>= b; } inline bigint operator-(const bigint &a) { bigint ret = a; ret.sign = -ret.sign; return ret; } inline bool operator==(const bigint &a, const bigint &b) { return a.compare_bigint(b) == 0; } inline bool operator!=(const bigint &a, const bigint &b) { return a.compare_bigint(b) != 0; } inline bool operator<(const bigint &a, const bigint &b) { return a.compare_bigint(b) < 0; } inline bool operator>(const bigint &a, const bigint &b) { return a.compare_bigint(b) > 0; } inline bool operator<=(const bigint &a, const bigint &b) { return a.compare_bigint(b) <= 0; } inline bool operator>=(const bigint &a, const bigint &b) { return a.compare_bigint(b) >= 0; } inline bool operator==(const bigint &a, int b) { return a.compare_int(b) == 0; } inline bool operator!=(const bigint &a, int b) { return a.compare_int(b) != 0; } inline bool operator<(const bigint &a, int b) { return a.compare_int(b) < 0; } inline bool operator>(const bigint &a, int b) { return a.compare_int(b) > 0; } inline bool operator<=(const bigint &a, int b) { return a.compare_int(b) <= 0; } inline bool operator>=(const bigint &a, int b) { return a.compare_int(b) >= 0; } inline bool operator==(int a, const bigint &b) { return b.compare_int(a) == 0; } inline bool operator!=(int a, const bigint &b) { return b.compare_int(a) != 0; } inline bool operator<(int a, const bigint &b) { return b.compare_int(a) > 0; } inline bool operator>(int a, const bigint &b) { return b.compare_int(a) < 0; } inline bool operator<=(int a, const bigint &b) { return b.compare_int(a) >= 0; } inline bool operator>=(int a, const bigint &b) { return b.compare_int(a) <= 0; } inline bigint bigint_abs(const bigint &a) { bigint ret = a; ret.sign = +1; return ret; } inline int bigint_sign(const bigint &a) { return (a.v.size() == 0) ? 0 : a.sign; } inline bigint bigint_sqrt(const bigint &n) { bigint d, a, b, di; d = n; a = 0; b = 0; b.add_word_at_pos(n.v.size(), (word)1 << ((WORDSIZE-1) & ~1)); /* big power of 4 */ do { a >>= 1; di = 2*a + b; if (di <= d) { d -= di; a += b; } b >>= 2; } while (b); return a; } inline char *bigint_decstring(const bigint &a) { if (a.v.size() == 0) { char *buf = (char *)malloc(2); assert(buf); strcpy(buf, "0"); return buf; } bigint tmp = a; /* 146/485 is an overestimate of log10(2) */ size_t maxchars = (a.v.size() * WORDSIZE * 146 + 484) / 485; char *buf = (char *)malloc(maxchars+2); assert(buf); int start = 0; if (a.sign < 0 && a.v.size() > 0) { buf[start++] = '-'; } size_t index = maxchars+1; size_t length = 0; while (tmp.v.size() != 0) { assert(index > 0); buf[--index] = '0' + tmp.short_divide(10); length++; } memmove(buf + start, buf + index, length); buf[start + length] = '\0'; return buf; } inline void bigint_print(const bigint &a) { char *buf = bigint_decstring(a); fwrite(buf, 1, strlen(buf), stdout); delete[] buf; } inline char *bigint_hexstring(const bigint &a) { size_t asize = a.v.size(); if (asize == 0) { char *ret = (char *)malloc(2); assert(ret); strcpy(ret, "0"); return ret; } size_t nchars = asize * WORDSIZE / 4; word top = a.v[asize - 1]; for (size_t sh = WORDSIZE - 4; sh > 0; sh -= 4) { if (!(top >> sh)) nchars--; } if (a.sign < 0 && a.v.size() > 0) nchars++; char *ret = (char *)malloc(nchars + 1); assert(ret); ret[nchars] = '\0'; for (size_t digit = 0; digit < nchars; digit++) { word w = a.word_at(digit / (WORDSIZE / 4)); w >>= 4 * (digit % (WORDSIZE / 4)); ret[nchars - 1 - digit] = "0123456789abcdef"[w & 0xF]; } if (a.sign < 0 && a.v.size() > 0) ret[0] = '-'; return ret; } inline bigint bigint_power(const bigint &x, unsigned y) { bigint ret = 1; bigint curr = x; unsigned bit = 1; while (y) { if (y & bit) { ret *= curr; y &= ~bit; } bit <<= 1; curr *= curr; } return ret; } inline unsigned bigint_approxlog2(const bigint &a) { if (!a.v.size()) return 0; /* The value we will return if the top bit of the topmost word is set */ unsigned ret = a.v.size() * WORDSIZE - 1; /* Correct for the top bit of the topmost bit not being set */ word w = a.v[a.v.size() - 1]; for (unsigned sh = WORDSIZE/2; sh != 0; sh >>= 1) { if (!(w >> (WORDSIZE - sh))) { w <<= sh; ret -= sh; } } return ret; } inline int bigint_bit(const bigint &a, unsigned index) { word w = a.word_at(index / WORDSIZE); w >>= index % WORDSIZE; return w & 1; } spigot-0.2017-01-15.gdad1bbc6/bigint.h0000644000000000000000000000030212514752325013601 0ustar /* * Switch header which includes an appropriate bi_* header to get one * or other implementation of bigints. */ #ifdef HAVE_LIBGMP #include "bi_gmp.h" #else #include "bi_internal.h" #endif spigot-0.2017-01-15.gdad1bbc6/cfracout.cpp0000644000000000000000000004347513025565031014502 0ustar /* * cfracout.cpp: handle all forms of output which are based on * retrieving the continued fraction terms of the number, whether we * output them directly or turn them into rational convergents. */ #include #include #include "spigot.h" #include "cr.h" #include "cfracout.h" #include "error.h" class CfracOutputBaseClass : public OutputGenerator { protected: // things for the derived class to write output into std::string output, tentative_output; int tentative_digits; bool done; private: int crState; // for coroutine-structured progress() // local variables of progress() bool first_digit, refine; bool lo_open, hi_open; bigint lo, hi; bigint min_int_in_interval, max_int_in_interval; bigint term, scale; int digits; public: CfracOutputBaseClass(Coreable *acore); ~CfracOutputBaseClass() {} void progress(); bool get_definite_output(std::string &out) { if (done && output.size() == 0) return false; progress(); out = output; output = ""; return true; } bool get_tentative_output(std::string &out, int *digits) { if (done) return false; if (tentative_output.size() > 0) { out = tentative_output; *digits = tentative_digits; return true; } return false; } virtual void got_definite_term(bigint term) = 0; virtual void got_definite_term_inf() = 0; virtual void clear_tentative_term() = 0; virtual void set_tentative_term(bigint term, int digits) = 0; virtual void set_tentative_term_inf(int digits) = 0; }; CfracOutputBaseClass::CfracOutputBaseClass(Coreable *acore) : OutputGenerator(acore) , done(false) , crState(-1) { dprint("hello CfracOutputBaseClass %p", core); } void CfracOutputBaseClass::progress() { crBegin; first_digit = true; /* * The 'refine' flag is used to ensure that we squeeze as much * juice as we can out of any useful data returned from our Core. * We set it to true immediately after any call to * iterate_to_bounds, and back to false in any situation where the * results of that iterate_to_bounds causes us to have actually * made some progress. * * The rationale is that some Cores might return data in large * lumps which are expensive to generate, in which case whenever * iterate_to_bounds does something useful at all, it will have * provided enough information to get lots of extra terms or * digits out of the system, and it will be expensive to call it * again with refine=true, so we should delay as long as possible. * The basic rule is that we should always pass refine=false, * unless _nothing_ useful at all has happened since the last * attempt. */ refine = false; while (true) { /* * If we didn't need to get tentative output right, we could * just call iterate_to_floor_or_inf as CfracGenerator does, * to iterate until we were sure of the integer part of the * number. But instead, we have to do the same job in multiple * stages. */ /* * Stage 1: narrow the interval until there is at most one * integer contained in it. */ do { iterate_to_bounds(&lo, &hi, &lo_open, &hi_open, 0, NULL, refine); refine = true; min_int_in_interval = (lo_open ? lo+1 : lo); max_int_in_interval = (hi_open ? hi-1 : hi); dprint("stage 1, %s%b,%b%s contains integers [%b..%b]", lo_open ? "(" : "[", &lo, &hi, hi_open ? ")" : "]", &min_int_in_interval, &max_int_in_interval); } while (min_int_in_interval < max_int_in_interval); refine = false; if (min_int_in_interval > max_int_in_interval) { /* * If we're really lucky, we've narrowed to the point * where there's _no_ integer in the interval, in which * case we already know the next continued fraction term * and can deal with it all in one go. * * Premultiply in a matrix which represents subtracting * that number and then taking the reciprocal, as in the * simple CfracGenerator. */ term = max_int_in_interval; { bigint outmatrix[4]; outmatrix[0] = 0; outmatrix[1] = outmatrix[2] = 1; outmatrix[3] = -term; dprint("extract definite term %b: %4m", &lo, outmatrix); core->premultiply(outmatrix); } got_definite_term(term); clear_tentative_term(); first_digit = false; crReturnV; /* * And go straight back round the loop. */ continue; } /* * If there _is_ still an integer in the interval, * then that's our tentative continued fraction term - * i.e. if the number turns out to _be_ an integer, it * will have to be this one. */ term = min_int_in_interval; /* * Premultiply a matrix which subtracts off that term, but * don't take the reciprocal yet - we can't do that until * we know that the result of the subtraction is nonzero. */ { bigint outmatrix[4]; outmatrix[0] = 1; outmatrix[1] = -term; outmatrix[2] = 0; outmatrix[3] = 1; dprint("subtract off tentative term %b: %4m", &term, outmatrix); core->premultiply(outmatrix); } /* * Stage 2: now our interval surrounds zero. Keep narrowing it * until one of the following things happens: * * - the interval becomes entirely negative, i.e. not even * containing zero at the top. Then our definite term is * one less than the tentative term we output. * * - the interval becomes entirely positive. Then our * definite term is the same as the tentative term, but not * exact. * * - the interval is composed entirely of zero. Then our * tentative term was exact and we know it, so terminate. * * - the interval becomes entirely _non-negative_, i.e. it * may include zero at the bottom. Then our definite term * is the same as our tentative term, _but_ we can't take * the reciprocal yet until we narrow further and prove the * number isn't exact. * * Also, while we do this, we keep increasing the scale * parameter to iterate_to_bounds, which allows us to see how * close the tentative term is to being exact. */ scale = 1; digits = 0; while (true) { iterate_to_bounds(&lo, &hi, &lo_open, &hi_open, 0, &scale, refine); refine = true; dprint("stage 2, %s%b,%b%s", lo_open ? "(" : "[", &lo, &hi, hi_open ? ")" : "]"); if (hi == 0 && lo == 0) { /* * Exact zero. We're done. */ got_definite_term(term); got_definite_term_inf(); clear_tentative_term(); dprint("exact final term %b", &term); refine = false; crReturnV; assert(!"Should never come here"); } else if (hi < 0 || (hi == 0 && hi_open)) { /* * Interval is entirely negative, i.e. tentative term * was one too high. Reduce it by 1. * * In most cases, we should still get a positive * integer term. The exception is if we haven't output * the number's integer part yet, in which case the * output term could have any value at all. */ assert(first_digit || term > 1); term -= 1; /* * Correct for having subtracted off the wrong term by * adding 1 and then taking the reciprocal. */ { bigint outmatrix[4]; outmatrix[0] = 0; outmatrix[1] = outmatrix[2] = outmatrix[3] = 1; dprint("correcting for wrong tentative term: %4m", &term, outmatrix); core->premultiply(outmatrix); } got_definite_term(term); clear_tentative_term(); first_digit = false; refine = false; crReturnV; break; } else if (lo >= 0) { /* * Interval is entirely non-negative, so we know what * the definite term is. */ dprint("tentative term %b was correct", &term); got_definite_term(term); clear_tentative_term(); first_digit = false; refine = false; crReturnV; /* * Now if we produce any tentative output before * taking the reciprocal, it will be the special kind * of output that says the term we already generated * was the final one. */ /* * Now if zero is still at the bottom of the interval, * keep iterating until it isn't. */ while (lo == 0 && !lo_open) { iterate_to_bounds(&lo, &hi, &lo_open, &hi_open, 0, &scale, refine); refine = true; dprint("stage 3, %s%b,%b%s", lo_open ? "(" : "[", &lo, &hi, hi_open ? ")" : "]"); if (hi <= 1 && lo >= -1) { dprint("stage 3, tentative digits = %d", (int)digits); set_tentative_term_inf(digits); tentative_digits = digits; refine = false; crReturnV; digits++; scale *= 10; } } refine = false; /* * And now we're all positive, so we can take the * reciprocal. */ { bigint outmatrix[4]; outmatrix[0] = outmatrix[3] = 0; outmatrix[1] = outmatrix[2] = 1; dprint("taking reciprocal: %4m", &term, outmatrix); core->premultiply(outmatrix); } break; } /* * If we get here, we didn't manage to make any progress. * See if we've at least increased the number of tentative * digits we can print. */ if (hi <= 1 && lo >= -1) { dprint("stage 2, tentative digits = %d", (int)digits); set_tentative_term(term, digits); tentative_digits = digits; crReturnV; digits++; scale *= 10; refine = false; } } } crEnd; } class CfracOutputGenerator : public CfracOutputBaseClass { bool oneline; int nterms; // for choosing separator; doesn't increment after 2 bool has_digitlimit; int digitlimit; const char *next_sep() { if (nterms == 1) return ";"; else if (nterms > 1) return ","; else return ""; } public: CfracOutputGenerator(Coreable *acore, bool aoneline, bool ahas_digitlimit, int adigitlimit) : CfracOutputBaseClass(acore) , oneline(aoneline) , nterms(0) , has_digitlimit(ahas_digitlimit) , digitlimit(adigitlimit) { if (has_digitlimit && digitlimit < 0) throw spigot_error("cannot handle negative digit limit in " "continued fraction output mode"); } virtual void got_definite_term(bigint term) { char *dec; if (oneline) output += next_sep(); dec = bigint_decstring(term); output += dec; free(dec); if (!oneline) output.push_back('\n'); if (nterms <= 1) nterms++; if (has_digitlimit && digitlimit-- <= 0) done = true; } virtual void got_definite_term_inf() { done = true; } virtual void clear_tentative_term() { tentative_output = ""; } virtual void set_tentative_term(bigint term, int digits) { tentative_output = ""; if (oneline) tentative_output += next_sep(); char *dec = bigint_decstring(term); tentative_output += dec; free(dec); char buf[80]; sprintf(buf, " (10^%d)", digits); tentative_output += buf; } virtual void set_tentative_term_inf(int digits) { if (oneline) tentative_output = " "; else tentative_output = ""; char buf[80]; sprintf(buf, "(10^%d)", digits); tentative_output += buf; } }; OutputGenerator *cfrac_output(Spigot *spig, bool oneline, bool has_digitlimit, int digitlimit) { return new CfracOutputGenerator(spig, oneline, has_digitlimit, digitlimit); } class ConvergentsOutputGenerator : public CfracOutputBaseClass { bigint cvn, pcvn, cvd, pcvd; bool has_digitlimit; int digitlimit; static std::string format_rational(const bigint &n, const bigint &d) { std::string ret; char *dec; dec = bigint_decstring(n); ret = dec; free(dec); ret.push_back('/'); dec = bigint_decstring(d); ret += dec; free(dec); return ret; } public: ConvergentsOutputGenerator(Coreable *acore, bool ahas_digitlimit, int adigitlimit) : CfracOutputBaseClass(acore) , cvn(1) , pcvn(0) , cvd(0) , pcvd(1) , has_digitlimit(ahas_digitlimit) , digitlimit(adigitlimit) { if (has_digitlimit && digitlimit < 0) throw spigot_error("cannot handle negative digit limit in " "convergents output mode"); } virtual void got_definite_term(bigint term) { bigint newcvn = cvn * term + pcvn; bigint newcvd = cvd * term + pcvd; pcvn = cvn; pcvd = cvd; cvn = newcvn; cvd = newcvd; output += format_rational(cvn, cvd); output.push_back('\n'); if (has_digitlimit && digitlimit-- <= 0) done = true; } virtual void got_definite_term_inf() { done = true; } virtual void clear_tentative_term() { tentative_output = ""; } virtual void set_tentative_term(bigint term, int digits) { bigint newcvn = cvn * term + pcvn; bigint newcvd = cvd * term + pcvd; tentative_output = format_rational(newcvn, newcvd); char buf[80]; sprintf(buf, " (10^%d)", digits); tentative_output += buf; } virtual void set_tentative_term_inf(int digits) { char buf[80]; sprintf(buf, "(10^%d)", digits); tentative_output = buf; } }; OutputGenerator *convergents_output(Spigot *spig, bool has_digitlimit, int digitlimit) { return new ConvergentsOutputGenerator(spig, has_digitlimit, digitlimit); } class RationalOutputGenerator : public CfracOutputBaseClass { bigint cvn, pcvn, cvd, pcvd; static std::string format_rational(const bigint &n, const bigint &d) { std::string ret; char *dec; dec = bigint_decstring(n); ret = dec; free(dec); if (d != 1) { ret.push_back('/'); dec = bigint_decstring(d); ret += dec; free(dec); } return ret; } public: RationalOutputGenerator(Coreable *acore, bool has_digitlimit) : CfracOutputBaseClass(acore) , cvn(1) , pcvn(0) , cvd(0) , pcvd(1) { if (has_digitlimit) { throw spigot_error("cannot handle digit limit in " "rational output mode"); } if (acore->is_rational(&cvn, &cvd)) { output = format_rational(cvn, cvd); done = true; } } virtual void got_definite_term(bigint term) { bigint newcvn = cvn * term + pcvn; bigint newcvd = cvd * term + pcvd; pcvn = cvn; pcvd = cvd; cvn = newcvn; cvd = newcvd; } virtual void got_definite_term_inf() { if (!done) { output = format_rational(cvn, cvd); done = true; } } virtual void clear_tentative_term() { tentative_output = ""; } virtual void set_tentative_term(bigint term, int digits) { bigint newcvn = cvn * term + pcvn; bigint newcvd = cvd * term + pcvd; tentative_output = format_rational(newcvn, newcvd); char buf[80]; sprintf(buf, " (10^%d)", digits); tentative_output += buf; } virtual void set_tentative_term_inf(int digits) { tentative_output = format_rational(cvn, cvd); char buf[80]; sprintf(buf, " (10^%d)", digits); tentative_output += buf; } }; OutputGenerator *rational_output(Spigot *spig, bool has_digitlimit) { return new RationalOutputGenerator(spig, has_digitlimit); } spigot-0.2017-01-15.gdad1bbc6/cfracout.h0000644000000000000000000000073513025565031014137 0ustar /* * cfracout.h: header for cfracout.cpp. */ /* * Functions that construct OutputGenerators for the various * continued-fraction based output modes. */ OutputGenerator *cfrac_output(Spigot *spig, bool oneline, bool has_digitlimit, int digitlimit); OutputGenerator *convergents_output(Spigot *spig, bool has_digitlimit, int digitlimit); OutputGenerator *rational_output(Spigot *spig, bool has_digitlimit); spigot-0.2017-01-15.gdad1bbc6/configure.ac0000644000000000000000000000117013025600105014430 0ustar # autoconf input for spigot. AC_INIT([spigot], [NOVERSION], [anakin@pobox.com]) AC_CONFIG_SRCDIR([spigot.cpp]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE(foreign) AC_PROG_CXX AC_PROG_INSTALL AC_CHECK_PROG([HALIBUT],[halibut],[yes],[no]) AM_CONDITIONAL([HAVE_HALIBUT],[test "x$HALIBUT" = "xyes"]) AC_ARG_WITH(gmp, [use GMP for large integer arithmetic], [], [with_gmp=check]) AS_IF([test "x$with_gmp" != xno], [AC_CHECK_LIB(gmp,main)]) AC_SEARCH_LIBS(tigetstr,tinfo ncurses) AC_CHECK_HEADERS([ncurses.h curses.h unistd.h]) AC_CHECK_FUNCS([tcsetattr fdopen tiparm]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT spigot-0.2017-01-15.gdad1bbc6/consts.cpp0000644000000000000000000001327613025565031014201 0ustar /* * consts.cpp: spigot implementations of mathematical constants. */ #include "spigot.h" #include "funcs.h" #include "cr.h" class E : public Source { int k; public: E() : Source(), k(1) {} virtual E *clone() { return new E(); } /* * The spigot definition for e is derived from the obvious * series for e obtained by expanding the Taylor series for * exp(1): * * e = 1 + 1/1! + 1/2! + 1/3! + 1/4! + ... * = 1 + 1/1 * (1 + 1/2 * (1 + 1/3 * (1 + 1/4 * *...))) * * which gives us the infinite function composition * * (x |-> 1+x) o (x |-> 1+x/2) o (x |-> 1+x/3) o ... * * and hence the sequence of matrices * * ( 1 1 ) ( 1 2 ) ( 1 3 ) ... ( 1 k ) ... * ( 0 1 ) ( 0 2 ) ( 0 3 ) ( 0 k ) * * We must also adjust the limits 3 and 4. We picked the * interval [3,4] for the pi expansion because we know that each * of the functions in the infinite composition for pi maps * [3,4] to a subinterval of itself, meaning that the images of * the ends of that interval always bracket pi. However, this is * not true for the above series of functions for e. In fact, * there is _no_ interval which all of the above functions map * to a subinterval of itself, because the first function (x |-> * 1+x) translates every interval upwards! * * Fortunately, all the functions _other_ than the first one * have the property that they map [0,2] to a subinterval of * itself. So once the image of [0,2] under a `partial sum' of * at least two terms of this function series contains e, its * image under subsequent partial sums will be a subinterval of * that and hence will always contain e as well. So we need only * check that no spurious `digits' are output before that * happens, and then we're safe. And this is OK, because the * identity matrix maps [0,2] to [0,2] which doesn't have a * constant integer part, the first function maps it to [1,3] * which doesn't either, and the first two functions map it to * [2,3] which contains e and is late enough in the series to be * safe. */ bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 2; return true; } bool gen_matrix(bigint *matrix) { matrix[0] = 1; matrix[1] = k; matrix[2] = 0; matrix[3] = k; k++; return false; } }; class Apery : public Source { int crState; bigint k; /* * Apery's constant: zeta(3), or the sum of the reciprocals of the * cubes of the natural numbers. */ public: Apery() { crState = -1; } virtual Apery *clone() { return new Apery(); } bool gen_interval(bigint *low, bigint *high) { *low = -1; *high = +1; return true; } bool gen_matrix(bigint *matrix) { crBegin; /* * We spigotise the really simple series of Hjortnaes (source: * Wikipedia) * * 5 k-1 k!^2 * zeta(3) = - sum (-1) --------- * 2 k>=1 k^3 (2k)! * * into a sequence of spigot matrices in which each one adds * 1/k^3, and multiplies in a factor n/d converting k!^2/(2k)! * into the same thing for k+1. (The factor also includes the * sign flip.) */ /* * x |-> 5/2 x */ matrix[0] = 5; matrix[1] = matrix[2] = 0; matrix[3] = 2; crReturn(true); for (k = 1 ;; ++k) { { bigint k3 = k*k*k; bigint n = k*k, d = (2*k-1) * (2*k); /* * x |-> n/d (1/k3 - x) = (-n k3 x + n) / (0x + d k3) */ matrix[0] = -n*k3; matrix[1] = n; matrix[2] = 0; matrix[3] = d*k3; } crReturn(false); } crEnd; } }; class Phi : public CfracSource { virtual Phi *clone() { return new Phi(); } /* * We could set up a spigot definition for phi, but it's more * easily defined in terms of its continued fraction, which * consists of nothing but 1s. */ public: virtual bool gen_term(bigint *term) { *term = 1; return true; } }; Spigot *spigot_pi() { /* * Machin's formula: pi/4 = 4 atan(1/5) - atan(1/239). * * I used to compute pi using Jeremy Gibbons's original spigot * representation based on constructing a series of matrices * representing the formula * * 1 1.2 1.2.3 * pi = 2 + 2 - + 2 --- + 2 ----- + ... * 3 3.5 3.5.7 * * but it turned out that telling spigot to compute the expression * '16*atan(1/5) - 4*atan(1/239)' was then much faster than 'pi' * and gave the same result. * * By using a Gosper instance with custom coefficients, we improve * the speed by one more notch by doing the scaling and summing of * the two atans in one go. */ return spigot_combine(spigot_atan(spigot_rational(1, 5)), spigot_atan(spigot_rational(1, 239)), 0, 16, -4, 0, 0, 0, 0, 1); } Spigot *spigot_tau() { /* * Same implementation as pi above, but with coefficients doubled. */ return spigot_combine(spigot_atan(spigot_rational(1, 5)), spigot_atan(spigot_rational(1, 239)), 0, 32, -8, 0, 0, 0, 0, 1); } Spigot *spigot_e() { return new E(); } Spigot *spigot_phi() { return new Phi(); } Spigot *spigot_apery() { return new Apery(); } spigot-0.2017-01-15.gdad1bbc6/cr.h0000644000000000000000000000074512514752325012744 0ustar /* * cr.h: Coroutine mechanics for C++. * * For an explanation of the technique, see * * http://chiark.greenend.org.uk/~sgtatham/coroutines.html */ #include #include #define crBegin switch (crState) { case -1:; #define crEnd } assert(!"Should never get here"); abort(); #define crReturn(ret) do { \ crState = __LINE__; return ret; case __LINE__:; \ } while (0) #define crReturnV do { \ crState = __LINE__; return; case __LINE__:; \ } while (0) spigot-0.2017-01-15.gdad1bbc6/debian/0000755000000000000000000000000013635705451013406 5ustar spigot-0.2017-01-15.gdad1bbc6/debian/.gitignore0000644000000000000000000000013513025600105015354 0ustar autoreconf.after autoreconf.before .debhelper debhelper-build-stamp files *.substvars spigot spigot-0.2017-01-15.gdad1bbc6/debian/TODO0000644000000000000000000000043413025600124014057 0ustar This message from lintian is spurious W: spigot source: missing-runtime-test-file ./test.sh paragraph starting at line 1 as shown by the fact that autopkgtest can find and run the test. We should file a bug, but that is more conveniently done with the package in the archive. spigot-0.2017-01-15.gdad1bbc6/debian/changelog0000644000000000000000000000167313635705451015267 0ustar spigot (0.2017-01-15.gdad1bbc6-1build2) focal; urgency=medium * No-change rebuild for libgcc-s1 package name change. -- Matthias Klose Sun, 22 Mar 2020 16:58:33 +0100 spigot (0.2017-01-15.gdad1bbc6-1build1) cosmic; urgency=medium * No-change rebuild for ncurses soname changes. -- Matthias Klose Thu, 03 May 2018 14:19:19 +0000 spigot (0.2017-01-15.gdad1bbc6-1) unstable; urgency=medium * Merge from upstream: - Add a missing exit after an error message. - Permit overriding VER on the make command line. * Arrange to pass -DVER so that --version prints Debian version. * Add an autopkgtest to check we are using gmp integration. -- Ian Jackson Sun, 15 Jan 2017 20:37:21 +0000 spigot (0.2016-12-18.g8372d450-1) unstable; urgency=medium * Initial upload to Debian. -- Ian Jackson Sun, 18 Dec 2016 20:14:12 +0000 spigot-0.2017-01-15.gdad1bbc6/debian/clean0000644000000000000000000000012013025600105014363 0ustar Makefile.in aclocal.m4 compile config.h.in configure depcomp install-sh missing spigot-0.2017-01-15.gdad1bbc6/debian/compat0000644000000000000000000000000313025600105014564 0ustar 10 spigot-0.2017-01-15.gdad1bbc6/debian/control0000644000000000000000000000115713025600105014774 0ustar Source: spigot Build-Depends: autoconf, automake, debhelper (>=10), halibut, libncurses-dev, libgmp-dev Standards-Version: 3.9.8.0 Maintainer: Ian Jackson Section: math Priority: optional Homepage: http://www.chiark.greenend.org.uk/~sgtatham/spigot/ Package: spigot Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends} Description: Exact real calculator spigot is an exact real calculator: that is, you give it a mathematical expression to evaluate, and it computes it to any desired precision, by default simply printing digits to standard output until it is interrupted. spigot-0.2017-01-15.gdad1bbc6/debian/copyright0000644000000000000000000000234313025600105015322 0ustar Format: Upstream-Name: spigot Source: git://git.tartarus.org/simon/spigot.git Files: * Copyright: 2007-2016 Simon Tatham License: MIT Files: debian/* Copyright: 2016 Ian Jackson License: MIT License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. spigot-0.2017-01-15.gdad1bbc6/debian/rules0000755000000000000000000000022313036754420014456 0ustar #!/usr/bin/make -f version=$(shell dpkg-parsechangelog -Sversion || echo UNKNOWN-VERSION) export CPPFLAGS+=-DVER='"Debian:$(version)"' %: dh $@ spigot-0.2017-01-15.gdad1bbc6/debian/tests/0000755000000000000000000000000013036752002014535 5ustar spigot-0.2017-01-15.gdad1bbc6/debian/tests/bigint-provider0000755000000000000000000000016713036751565017610 0ustar #!/bin/bash set -e set -o pipefail LC_MESSAGES=C spigot --version \ | egrep '^ *big integer provider: gmp$' >/dev/null spigot-0.2017-01-15.gdad1bbc6/debian/tests/control0000644000000000000000000000007213036751326016147 0ustar Tests: test.sh Tests-Directory: . Tests: bigint-provider spigot-0.2017-01-15.gdad1bbc6/defs.but0000644000000000000000000000047113025565031013612 0ustar \cfg{man-mindepth}{1} \cfg{html-template-fragment}{%k} \cfg{html-leaf-contains-contents}{true} \define{spigot} \cw{spigot} \define{pi} \e{\u03C0{pi}} \define{epsilon} \e{\u03B5{epsilon}} \define{theta} \e{\u03B8{theta}} \define{dash} \u2013{-} \define{hdots} \u2026{...} \define{tendsto} \u2192{\cw{->}} spigot-0.2017-01-15.gdad1bbc6/enforce.cpp0000644000000000000000000000763513025565031014313 0ustar /* * enforce.cpp: deferred range checks. * * This file implements spigot_enforce(), which takes two spigot * inputs and returns one of them effectively unchanged. The point of * it is that it also continually checks to see which side of the * second input the main one falls on, and if it ever finds out that * that's the _wrong_ side, it throws an exception, which the caller * has provided in advance (with an appropriate error message) in case * it's needed. */ #include #include #include #include "spigot.h" #include "funcs.h" #include "error.h" #define STRING(a,b) b static const char *const relation_strings[] = { ENFORCE_RELATIONS(STRING) }; #undef STRING class Enforcer : public BinaryIntervalSource { Spigot *x_orig; EnforceRelation rel; Spigot *bound_orig; spigot_error err; BracketingGenerator bg_x, bg_bound; public: Enforcer(Spigot *x, EnforceRelation arel, Spigot *bound, spigot_error aerr) : x_orig(x->clone()), rel(arel), bound_orig(bound->clone()), err(aerr), bg_x(x), bg_bound(bound) { dprint("hello Enforcer %p %s %p", x, relation_strings[rel], bound); } virtual ~Enforcer() { delete x_orig; delete bound_orig; } virtual Enforcer *clone() { return new Enforcer(x_orig->clone(), rel, bound_orig->clone(), err); } virtual bool is_rational(bigint *n, bigint *d) { return x_orig->is_rational(n, d); } virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { bg_x.get_bracket_shift(ret_lo, ret_hi, ret_bits); dprint("got x bracket (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); bg_bound.set_denominator_lower_bound_shift(*ret_bits); bigint cmp_lo, cmp_hi; unsigned cmp_bits; bg_bound.get_bracket_shift(&cmp_lo, &cmp_hi, &cmp_bits); dprint("got bound bracket (%b,%b) / 2^%d", &cmp_lo, &cmp_hi, cmp_bits); assert(cmp_bits >= *ret_bits); unsigned ret_shift = cmp_bits - *ret_bits; bool ok; if (rel == ENFORCE_GT || rel == ENFORCE_GE) { ok = (*ret_hi << ret_shift) >= cmp_lo; } else /* if (rel == ENFORCE_LT || rel == ENFORCE_LE) */ { ok = (*ret_hi << ret_shift) <= cmp_lo; } if (!ok) throw err; } }; Spigot *spigot_enforce(Spigot *x, EnforceRelation rel, Spigot *bound, spigot_error err) { /* * Start by at least _trying_ to report the error up front, if * it's really obviously out of range: if we can detect the * problem exactly via rationals, or if it's so far out that even * a cursory check with get_approximate_approximant can tell. */ bigint xn, xd, bn, bd; if (x->is_rational(&xn, &xd) && bound->is_rational(&bn, &bd)) { // Use here that is_rational always returns a positive denominator if ((rel == ENFORCE_GT && xn*bd <= bn*xd) || (rel == ENFORCE_GE && xn*bd < bn*xd) || (rel == ENFORCE_LT && xn*bd >= bn*xd) || (rel == ENFORCE_LE && xn*bd > bn*xd)) throw err; } else { StaticGenerator diffgen(spigot_sub(x->clone(), bound->clone())); bigint lo, hi; bool lo_open, hi_open; diffgen.iterate_to_bounds(&lo, &hi, &lo_open, &hi_open, 0, NULL, true); if (((rel == ENFORCE_GT || rel == ENFORCE_GE) && hi <= 0 && !(hi == 0 && !hi_open && rel == ENFORCE_GE)) || ((rel == ENFORCE_LT || rel == ENFORCE_LE) && lo >= 0 && !(lo == 0 && !lo_open && rel == ENFORCE_LE))) { throw err; } } /* * Failing that, use the above Enforcer class, which will watch * for the number turning out to be on the wrong side of the * boundary later on after further information comes to light. */ return new Enforcer(x, rel, bound, err); } spigot-0.2017-01-15.gdad1bbc6/erf.cpp0000644000000000000000000002656613025565031013452 0ustar /* * erf.cpp: Error function and its relatives. */ #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" class ErfBaseRational : public Source { /* * This class computes the simplest erf-like function: just the * integral of exp(-x^2), without any scaling or factors of * sqrt(pi) anywhere. We put those on later. * * We compute this for rationals by translating its obvious * power series into a spigot description. This converges like * an absolute dog for large |x|, but I don't know of any more * efficient way to compute erf at those values. (There isn't * any sensible range reduction method, for instance.) * * Let x = n/d. Then we have * * n n^3 n^5 n^7 * erfbase(n/d) = - - -------- + -------- - -------- + ... * d 1! 3 d^3 2! 5 d^5 3! 7 d^7 * * n 1 n^2 1 n^2 1 n^2 * = - ( - - ----- ( - - ----- ( - - ----- ( ... ) ) ) * d 1 1.d^2 3 2.d^2 5 3.d^2 * * so our matrices go * * ( n 0 ) ( -1.n^2 1.d^2 ) ( -3.n^2 2.d^2 ) ( -5.n^2 3.d^2 ) ... * ( 0 d ) ( 0 1.1.d^2 ) ( 0 2.3.d^2 ) ( 0 3.5.d^2 ) */ bigint n, d, fn, fd, n2, d2, k, kodd; int crState; public: /* * For convenience of Phi, we also compute e^-fx^2 for some * factor f. (Multiplying a nice rational 1/2 into each matrix * here is much faster than scaling the input by _root_ 2.) */ ErfBaseRational(const bigint &an, const bigint &ad, const bigint &afn, const bigint &afd) : n(an), d(ad), fn(afn), fd(afd) { crState = -1; } virtual ErfBaseRational *clone() { return new ErfBaseRational(n, d, fn, fd); } bool gen_interval(bigint *low, bigint *high) { /* I totally made these numbers up, but they seem to work. Ahem. */ *low = 0; *high = 2; return true; } bool gen_matrix(bigint *matrix) { crBegin; /* * The initial anomalous matrix. */ matrix[1] = matrix[2] = 0; matrix[0] = n; matrix[3] = d; crReturn(false); /* * Then the regular series. */ k = 1; kodd = 1; n2 = fn*n*n; d2 = fd*d*d; while (1) { matrix[0] = -kodd*n2; matrix[1] = k*d2; matrix[2] = 0; matrix[3] = k*kodd*d2; crReturn(false); ++k; kodd += 2; } crEnd; } }; struct ErfBaseConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new ErfBaseConstructor(); }; Spigot *construct(const bigint &n, const bigint &d) { return new ErfBaseRational(n, d, 1, 1); } }; struct PhiBaseConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new PhiBaseConstructor(); }; Spigot *construct(const bigint &n, const bigint &d) { return new ErfBaseRational(n, d, 1, 2); } }; Spigot *spigot_erf(Spigot *a) { bigint n, d; if (a->is_rational(&n, &d) && n == 0) return spigot_integer(0); return spigot_div(spigot_monotone(new ErfBaseConstructor, a), spigot_sqrt(spigot_rational_mul(spigot_pi(),1,4))); } Spigot *spigot_erfc(Spigot *a) { bigint n, d; if (a->is_rational(&n, &d) && n == 0) return spigot_integer(1); return spigot_sub(spigot_integer(1), spigot_div(spigot_monotone(new ErfBaseConstructor, a), spigot_sqrt(spigot_rational_mul(spigot_pi(), 1,4)))); } Spigot *spigot_Phi(Spigot *a) { bigint n, d; if (a->is_rational(&n, &d) && n == 0) return spigot_rational(1, 2); return spigot_add(spigot_rational(1,2), spigot_div(spigot_monotone(new PhiBaseConstructor, a), spigot_sqrt(spigot_rational_mul(spigot_pi(), 2, 1)))); } Spigot *spigot_erfinv(Spigot *t) { /* * To compute inverse erf using spigot_monotone_invert, we must * start by finding an interval of numbers which reliably bracket * the right answer. That is, we need a number with erf(x) < a, * and one - ideally not much bigger - with erf(x) > a. * * Lemma 1: for all x,t, exp(-t^2) <= exp(x^2 - 2xt), with * equality only at x=t. * * Proof: exp is strictly increasing, so this is true iff it's * still true with the exps stripped off, i.e. we need to show * -t^2 <= x^2 - 2xt, which rearranges to 0 <= (x-t)^2, which is * clearly non-negative everywhere, and zero iff x-t = 0. [] * * Lemma 2: for x > 0, erfc(x) < 1/sqrt(pi) exp(-x^2)/x. * * Proof: erfc(x) = 2/sqrt(pi) int_x^inf exp(-t^2) dt * <= 2/sqrt(pi) int_x^inf exp(x^2-2xt) dt (by Lemma 1) * = 2/sqrt(pi) exp(x^2) int_x^inf exp(-2xt) dt * = 2/sqrt(pi) exp(x^2) [0 - exp(-2x^2)/-2x] * = 1/sqrt(pi) exp(-x^2)/x. * * And the <= is easily seen to be an < by further observing that * equality in the Lemma 1 inequality only holds at one single * point, so we cannot still have equality once we integrate. [] * * Lemma 3: for all x>0, erfc(x) < exp(-x^2). * * Proof: if x >= 1/sqrt(pi), then 1/(x sqrt(pi)) <= 1, so by Lemma 2, * erfc(x) < 1/(x sqrt(pi)) exp(-x^2) <= exp(-x^2). * * And on the remaining interval [0, 1/sqrt(pi)] it's easy to see * that the inequality holds, just by plotting the two graphs and * looking at them, or else by observing that exp(-x^2) has a * negative second derivative throughout that interval (its 2nd * derivative doesn't go positive again until x reaches 1/sqrt(2)) * while erfc(x) has a positive one, hence they curve away from * each other and can't cross over. [] * * Theorem: for 0 < t < 1, erf(sqrt(-log(1-t))) > t. * * Proof: let x = sqrt(-log(1-t)). Then we have * * erf(sqrt(-log(1-t))) = 1 - erfc(x) * > 1 - exp(-x^2) (by Lemma 3) * = 1 - exp(log(1-t)) * = 1 - (1-t) = t. [] * * Corollary: if we're looking for inverse-erf of some t > 0, then * sqrt(-log(1-t)) is an upper bound on the answer. (For t < 0, * just flip all the signs, of course.) * * To find a lower bound, we just do one iteration of * Newton-Raphson. Since erf has a negative 2nd derivative (for * x>0), this should always give us something on the far side of * the root. */ /* * Start by checking the input number's sign. To avoid an * exactness hazard at erfinv(0), we do this only approximately, * leaving a small interval around 0 where we aren't sure of the * sign. In that interval it's safe to choose very simple upper * and lower bounds anyway. */ int sign; { StaticGenerator test(t->clone()); bigint approx = test.get_approximate_approximant(64); if (approx < -2) sign = -1; else if (approx > +2) sign = 1; else sign = 0; } bigint nlo, nhi, d; if (sign == 0) { /* * The input number could be in the range [-1/16, +1/16]. * (get_approximate_approximant returned at least -2 and at * most +2, and guarantees to be within 2 of the real answer, * so the real answer is in the range [-4/64, +4/64].) * * erfinv(1/16) is just under 1/18, so +-1/18 will do as our * limits. */ nlo = -1; nhi = +1; d = 18; } else { bigint dlo, dhi; if (sign < 0) t = spigot_neg(t); // Find an upper bound. Spigot *upperbound; { BracketingGenerator boundgen (spigot_sqrt (spigot_neg(spigot_log(spigot_sub(spigot_integer(1), t->clone()))))); while (1) { bigint n1, n2; boundgen.get_bracket(&n1, &n2, &dhi); // dprint("upper bound: trying (%b, %b) / %b", &n1, &n2, &dhi); int s = parallel_sign_test (spigot_sub(spigot_erf(spigot_rational(n1, dhi)), t->clone()), spigot_sub(spigot_erf(spigot_rational(n2, dhi)), t->clone())); // dprint("s = %d", s); if (s == +1) { nhi = n1; break; } else if (s == +2) { nhi = n2; break; } } // Having found a nice rational upper bound, replace the // theoretical one with that, for speed and simplicity. upperbound = spigot_rational(nhi, dhi); } // dprint("upper bound (%b/%b)", &nhi, &dhi); // Now do a Newton-Raphson iteration to find a lower bound. { BracketingGenerator boundgen (spigot_sub(upperbound->clone(), spigot_div(spigot_sub(spigot_erf(upperbound->clone()), t->clone()), spigot_div(spigot_exp(spigot_neg(spigot_mul(upperbound->clone(), upperbound->clone()))), spigot_rational_mul(spigot_sqrt(spigot_pi()), 1, 2))))); while (1) { bigint n1, n2; boundgen.get_bracket(&n1, &n2, &dlo); // dprint("lower bound: trying (%b, %b) / %b", &n1, &n2, &dlo); int s = parallel_sign_test (spigot_sub(spigot_erf(spigot_rational(n1, dlo)), t->clone()), spigot_sub(spigot_erf(spigot_rational(n2, dlo)), t->clone())); // dprint("s = %d", s); if (s == -1) { nlo = n1; break; } else if (s == -2) { nlo = n2; break; } } } delete upperbound; // dprint("bounds (%b/%b, %b/%b)", &nlo, &dlo, &nhi, &dhi); nlo *= dhi; nhi *= dlo; d = dhi * dlo; } // Now go and do the full root-finding step with those bounds. // First we scale t by sqrt(pi)/2, to compensate for the fact that // erfbase_constructor doesn't do that for us. t = spigot_mul(t, spigot_sqrt(spigot_rational_mul(spigot_pi(),1,4))); Spigot *ret = spigot_monotone_invert(new ErfBaseConstructor, true, nlo, nhi, d, t); if (sign < 0) ret = spigot_neg(ret); return ret; } Spigot *spigot_erfcinv(Spigot *a) { return spigot_erfinv(spigot_sub(spigot_integer(1), a)); } Spigot *spigot_Phiinv(Spigot *a) { return spigot_mul(spigot_neg(spigot_sqrt(spigot_integer(2))), spigot_erfcinv(spigot_rational_mul(a, 2, 1))); } spigot-0.2017-01-15.gdad1bbc6/error.h0000644000000000000000000000534212514752325013467 0ustar /* * error.h: define classes used to throw exceptions out of spigot * functions. */ #include #include struct spigot_error_base { char *errmsg; spigot_error_base() { errmsg = NULL; } spigot_error_base(const spigot_error_base &pe) { errmsg = (char *)malloc(1+strlen(pe.errmsg)); strcpy(errmsg, pe.errmsg); } ~spigot_error_base() { if (errmsg) free(errmsg); errmsg = NULL; } }; /* * Derive distinguishable classes of error from the above base class. */ struct spigot_error : spigot_error_base { /* * spigot_error is used for any fatal error (no matter at parse * time, spigot setup time, or during main evaluation). Its usual * constructor takes printf-style parameters. */ spigot_error(const char *fmt, ...) { int len; va_list ap; #if defined _MSC_VER /* * Sigh, VS2010 does not provide an ANSI-compliant vsnprintf; * it just returns -1 if the output didn't fit, which is no * help in finding out how much bigger you need to make the * buffer. */ for (len = 512 ;; len *= 2) { int ret; errmsg = (char *)malloc(len+1); va_start(ap, fmt); ret = _vsnprintf(errmsg, len+1, fmt, ap); va_end(ap); if (ret >= 0 && ret < len) break; } #else /* * With sensible vsnprintf, we can try once without any buffer * at all to find out the required length, then malloc exactly * that. */ va_start(ap, fmt); len = vsnprintf(NULL, 0, fmt, ap); va_end(ap); errmsg = (char *)malloc(len+1); va_start(ap, fmt); vsnprintf(errmsg, len+1, fmt, ap); va_end(ap); #endif } }; struct spigot_eof : spigot_error_base { /* * spigot_eof is used to indicate that a file we were reading a * real number from has ended, and hence we can't generate any * further output. Its constructor takes a plain filename string, * and handlers for it will cope accordingly. */ spigot_eof(const char *filename) { errmsg = (char *)malloc(1+strlen(filename)); strcpy(errmsg, filename); } }; /* * This utility spigot function is defined in enforce.cpp, and * declared in this header file because it's dependent on having * spigot_error defined. */ #define ENFORCE_RELATIONS(X) \ X(ENFORCE_GT, ">"), X(ENFORCE_GE, ">="), \ X(ENFORCE_LT, "<"), X(ENFORCE_LE, "<=") #define ENUMERATE(a,b) a enum EnforceRelation { ENFORCE_RELATIONS(ENUMERATE) }; #undef ENUMERATE Spigot *spigot_enforce(Spigot *x, EnforceRelation rel, Spigot *bound, spigot_error err); spigot-0.2017-01-15.gdad1bbc6/exp.cpp0000644000000000000000000010074713025565031013464 0ustar /* * exp.cpp: Exponential functions (exp, log and derived stuff). */ #include #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" class ExpRational : public Source { /* * We compute the exponential of a rational by translating the * obvious power series for exp(x) into a spigot description. * * Let x = n/d. Then we have * * n n^2 n^3 * exp(n/d) = 1 + ---- + ------ + ------ + ... * 1! d 2! d^2 3! d^3 * * n n n n * = 1 + --- ( 1 + --- ( 1 + --- ( 1 + --- ( ... ) ) ) * 1.d 2.d 3.d 4.d * * so our matrices go * * ( n 1.d ) ( n 2.d ) ( n 3.d ) ... * ( 0 1.d ) ( 0 2.d ) ( 0 3.d ) */ bigint n, d, k; int crState; public: ExpRational(const bigint &an, const bigint &ad) : n(an), d(ad) { crState = -1; } virtual ExpRational *clone() { return new ExpRational(n, d); } bool gen_interval(bigint *low, bigint *high) { /* I totally made these numbers up, but they seem to work. Ahem. */ *low = 0; *high = 2; return true; } bool gen_matrix(bigint *matrix) { crBegin; k = 1; while (1) { matrix[0] = n; matrix[1] = matrix[3] = d * k; matrix[2] = 0; ++k; crReturn(false); } crEnd; } }; class LogRational : public Source { /* * We compute the logarithm of a rational by translating the * obvious power series for log(1+x) into a spigot description. * * Let x = 1 + n/d. Then we have * * n 2 n^2 3 n^3 * So log(1 + n/d) = ( - - ----- + ----- - ... * d d^2 d^3 * * n n 2.n * = - ( 1 - --- ( 1 - --- ( ... ) ) ) * d 2.d 3.d * * so our matrices go * * ( n 0 ) ( -n 2d ) ( -2n 3d ) ... * ( 0 d ) ( 0 2d ) ( 0 3d ) */ bigint in, n, d, k; int crState; public: LogRational(const bigint &an, const bigint &ad) : in(an), d(ad) { n = in - d; /* we're computing log(1+x), not log(x), so subtract one */ crState = -1; } virtual LogRational *clone() { return new LogRational(in, d); } bool gen_interval(bigint *low, bigint *high) { /* I totally made these numbers up, but they seem to work. Ahem. */ *low = 0; *high = 2; return true; } bool gen_matrix(bigint *matrix) { crBegin; /* * The initial anomalous matrix. */ matrix[1] = matrix[2] = 0; matrix[0] = n; matrix[3] = d; crReturn(false); /* * Then the regular series. */ k = 1; while (1) { matrix[0] = -n * k; ++k; matrix[1] = matrix[3] = d * k; matrix[2] = 0; crReturn(false); } crEnd; } }; struct ExpConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new ExpConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { /* * Handle the special case of exp(0). */ if (n == 0) return spigot_integer(1); return new ExpRational(n, d); } }; struct LogConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new LogConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new LogRational(n, d); } }; Spigot *spigot_exp(Spigot *a) { bigint n; /* * Range-reduce by subtracting some multiple of log(2). * * We absolutely don't want to do this _exactly_ right, by * rounding every number to the very nearest multiple of * 1/log(2): that would introduce an exactness hazard, so that * exp(log(2)/2) would hang without producing any output not * because it's fundamentally uncomputable but simply because * the range reducer couldn't decide which way to throw it. * * So instead, we find a rational approximation to the answer, * and range-reduce based on that. */ { /* * Start by finding n such that n/32 is close (enough) to * a/log(1/2). (LogRational will converge faster for 1/2 than * for 2, so we do this backwards.) */ StaticGenerator test(spigot_div(a->clone(), new LogRational(1, 2))); n = test.get_approximate_approximant(32); } /* * Now divide by 32 so as to round to the nearest integer n. */ n = fdiv(n + 16, 32); if (n != 0) { /* Subtract off that multiple of log(1/2). */ a = spigot_sub(a, spigot_mul(spigot_integer(n), new LogRational(1, 2))); } /* * Compute exp of the reduced number. */ Spigot *reduced = spigot_monotone(new ExpConstructor, a); /* * And now we must scale the answer by 2^-n. But first, check that * the result isn't out of range for an argument to bigint_power, * by converting the power to an unsigned and making sure it * didn't overflow in the process. */ bigint big_abs_n = bigint_abs(n); unsigned abs_n = (unsigned)big_abs_n; if ((bigint)abs_n != big_abs_n) throw spigot_error("cannot take exp of numbers this large"); bigint twon = bigint_power(2, abs_n); if (n > 0) { return spigot_rational_mul(reduced, 1, twon); } else { return spigot_rational_mul(reduced, twon, 1); } } Spigot *spigot_log(Spigot *a) { bigint term, n; /* * Spot really easy error cases before getting into the more * sophisticated test below. */ { bigint an, ad; if (a->is_rational(&an, &ad) && an <= 0) { if (an < 0) throw spigot_error("log of negative number"); else throw spigot_error("log of zero"); } } /* * Range-reduce by dividing by a power of two, of which we must * try to find approximately the right one without running into * any exactness hazards. * * For best convergence, we really want to get the input into the * range (2/3,4/3), because then we're computing log(1+x) with * |x|<1/3 always. But going over a little in either direction is * OK. So we bounce on both 96*a and 96/a until we've narrowed one * of their integer parts to a range that's at least _nearly_ * within the same power of 2 and whose low end is at least * (about) 64; then we know what to divide by. */ { StaticGenerator testpos(spigot_rational_mul(a->clone(), 96, 1)); StaticGenerator testneg(spigot_mobius(a->clone(), 0, 96, 1, 0)); bigint lo, hi; while (1) { testpos.iterate_to_bounds(&lo, &hi, NULL, NULL, 0, NULL, true); if (hi < 0) throw spigot_error("log of negative number"); if (lo == 0 && hi == 0) throw spigot_error("log of zero"); if (lo >= 63 && (hi - lo <= 2 || (bigint_approxlog2(lo + 1) == bigint_approxlog2(hi - 1)))) { n = bigint_approxlog2(hi + lo); // If (lo,hi) == (63,64) then we can just about hit // n=6, in which case simply subtracting 7 from n // wraps round and we'd end up trying to compute // 2^UINT_MAX. assert(n >= 6); if (n == 6) ++n; n -= 7; a = spigot_rational_mul(a, 1, bigint_power(2, n)); break; } testneg.iterate_to_bounds(&lo, &hi, NULL, NULL, 0, NULL, true); if (hi < 0) throw spigot_error("log of negative number"); if (lo == 0 && hi == 0) throw spigot_error("log of zero"); if (lo >= 63 && (hi - lo <= 2 || (bigint_approxlog2(lo + 1) == bigint_approxlog2(hi - 1)))) { n = bigint_approxlog2(hi + lo); // Same safety precaution as above. assert(n >= 6); if (n == 6) ++n; n -= 7; a = spigot_rational_mul(a, bigint_power(2, n), 1); n = -n; break; } } } Spigot *ret = spigot_monotone(new LogConstructor, a); /* Add n*log(2). (Well, subtract n*log(1/2), for faster convergence.) */ if (n != 0) ret = spigot_sub(ret, spigot_rational_mul(new LogRational(1, 2), n, 1)); return ret; } Spigot *spigot_exp2(Spigot *a) { return spigot_pow(spigot_integer(2), a); } Spigot *spigot_exp10(Spigot *a) { return spigot_pow(spigot_integer(10), a); } // Given integers a,b > 1: if log_b(a) is rational, set n,d to be its // numerator and denominator, and return true. Otherwise, return false. static bool exact_log_z(bigint a, bigint b, bigint *n, bigint *d) { bigint n0 = 1, n1 = 0; bigint d0 = 0, d1 = 1; assert(a > 1); assert(b > 1); /* * Algorithm is 'Euclid in the exponents'. * * If a is a rational power of b, this is equivalent to saying * that a = k^A and b = k^B, for some k. * * Proof: certainly a,b must have the same _set_ of prime * factors, i.e. we can write * a = \prod p_i^{a_i} * b = \prod p_i^{b_i} * over a common set {p_i}, with all a_i,b_i > 0. Moreover, * a_i/b_i must take the same value for all i. Let that value * be A/B, in lowest terms (i.e. A,B coprime). Then we have * Ba_i = Ab_i, i.e. A | Ba_i, but A does not divide B, so * A | a_i. Similarly, B | b_i, and of course a_i/A = b_i/B. So * if we define k_i = a_i/A = b_i/B for all i, then we can set * k = \prod p_i^{k_i} * and we have a = k^A and b = k^B as required. [] * * But more than one k might satisfy that equation. (The above * proof constructs the _maximal_ k, but a smaller one might work, * e.g. if the k derived above is a square then its square root * would do just as well.) However, any two possible values (say, * k and k') will give rise to exponent pairs (A,B) and (A',B') * that are just scalings of each other, i.e. their ratios A/B and * A'/B' will be the same. * * We can find that ratio without having to know k by running * Euclid's algorithm, because that will yield the same sequence * of quotients if you adjust its inputs by a constant factor. But * we must modify the details a little, because we can't run * unmodified Euclid directly on A,B when what we have is k^A and * k^B for unknown k. * * So, we start by expressing Euclid in a way that does not refer * to 'reducing mod B', obtaining the following algorithm to find * the gcd of two integers A,B > 0: * * - If A = 0, terminate. * - Otherwise, find the maximal q such that B - qA >= 0. * - Replace (A, B) with (B - qA, A), and repeat. * * Now all the operations we're performing on A,B are operations * we can still perform when what we actually have is a,b which * are the Ath and Bth powers of something we don't know. It's * clear that 'A=0' is true iff a=1; 'B - qA >= 0' is true iff * a^q | b; computing B - qA corresponds to computing b / a^q. So * we obtain *this* algorithm, which for anagram reasons surely * ought to go by the name 'Euclid's Logarithm': * * - If a = 1, terminate. * - Otherwise, find the maximal q such that a^q | b. * - Replace (a, b) with (b / a^q, a), and repeat. * * If this algorithm succeeds, then we have found the common base * k such that a,b are powers of k; it will be in the variable b * when the algorithm terminates with a=1 (just as the ordinary * gcd would have been in B when the ordinary algorithm hit A=0). * So we could then recover the exponents A and B by repeated * division starting from the original a,b. * * However, there's a neater way. Recall that Euclid can be * augmented so that every new remainder it generates comes with a * pair of coefficients expressing that remainder as a linear * combination of the original inputs. In normal uses, you want * the pair of coefficients corresponding to the gcd of the * inputs, because that does modular inversion. But the algorithm * also generates the pair of coefficients corresponding to * _zero_, and it does it in lowest terms, i.e. running Euclid on * integers A,B gives you the pair (A,B) reduced to lowest terms * as a by-product. That's usually a bit pointless, but in this * case it's just what we want, because we _didn't_ know the ratio * A/B already, and it is exactly the output value log_b(a) that * we're after! So all we have to do is to build up that pair of * integers from the sequence of quotient values q, in just the * same way we would in ordinary Euclid. */ while (a != 1) { // Find q and b/a^q simultaneously, by dividing off a as many // times as we can and counting how many times that was. bigint q; for (q = 0; b % a == 0; b /= a, ++q) /* empty loop body */; // Check that the resulting value of b is less than a. If the // algorithm has not managed to achieve this, that's the // failure condition: it can only happen if a,b were not in // fact powers of a common k at all. if (b >= a) return false; // Swap a,b, ready for the next iteration. { bigint tmp = a; a = b; b = tmp; } // Update our linear-combination coefficients. We only need // their absolute values for this application (because we know // our output must be a _positive_ rational). { bigint tmp = n0; n0 = n1; n1 = tmp + q * n1; } { bigint tmp = d0; d0 = d1; d1 = tmp + q * d1; } } // Done! n1/d1 is the rational we want. *n = n1; *d = d1; return true; } // Same as exact_log_z, but the inputs are rationals an/ad and bn/bd, // expected to already be in lowest terms. static bool exact_log_q(bigint an, bigint ad, bigint bn, bigint bd, bigint *n, bigint *d) { // Error cases. if (an < 0 || bn < 0) throw spigot_error("log of negative number"); if (an == 0 || bn == 0) throw spigot_error("log of zero"); if (bn == 1 && bd == 1) throw spigot_error("log to base 1 is meaningless"); // Trivial special case. if (an == 1 && ad == 1) { *n = 0; *d = 1; return true; } // Now neither input is 1, so we can know which side of 1 they're // on. Normalise so they're the same way up, by flipping the sign // of the number we'll end up returning. bigint sign = +1; if ((an > ad) != (bn > bd)) { bigint tmp = bn; bn = bd; bd = tmp; sign = -1; } // If the numerators are both 1, or the denominators are both 1, // then just run the integer log-finder on the other pair. do { /* do-while-0 just so we can 'break' - *marginally* nicer than goto */ bool ret; if (ad == 1 && bd == 1) { ret = exact_log_z(an, bn, n, d); } else if (an == 1 && bn == 1) { ret = exact_log_z(ad, bd, n, d); } else { break; } if (ret) *n *= sign; return ret; } while (0); // We've ruled out all the ways any numerator or denominator can // be 1 and still give a rational answer. If none of those apply, // then any remaining 1 anywhere indicates failure. if (an == 1 || ad == 1 || bn == 1 || bd == 1) return false; // The most general case: the numerators and the denominators must // both have the same rational log. bigint dn, dd; if (!exact_log_z(ad, bd, &dn, &dd)) return false; if (!exact_log_z(an, bn, n, d)) return false; if (!(*n == dn && *d == dd)) return false; *n *= sign; return true; } Spigot *spigot_log_to_base(Spigot *input, Spigot *base) { bigint in, id, bn, bd, n, d; /* Spot cases where we can prove the answer is an exact rational. */ if (input->is_rational(&in, &id) && base->is_rational(&bn, &bd) && exact_log_q(in, id, bn, bd, &n, &d)) { delete input; delete base; return spigot_rational(n, d); } /* Otherwise, log_b(a) == log(a)/log(b). */ return spigot_div(spigot_log(input), spigot_log(base)); } Spigot *spigot_log2(Spigot *a) { return spigot_log_to_base(a, spigot_integer(2)); } Spigot *spigot_log10(Spigot *a) { return spigot_log_to_base(a, spigot_integer(10)); } Spigot *spigot_log_wrapper(const std::vector &args) { /* * Implement operator overloading for the expression language. We * permit 'log' to take either one or two arguments, and invoke * spigot_log or spigot_log_to_base as appropriate. In C++ or * Python terms, this is like defining it as log(x, base=e) - the * second argument is optional and defaults to e if omitted. */ if (args.size() == 1) return spigot_log(args[0]); else if (args.size() == 2) return spigot_log_to_base(args[0], args[1]); else throw spigot_error("expected either one or two arguments to 'log'"); } Spigot *spigot_expm1(Spigot *a) { return spigot_sub(spigot_exp(a), spigot_integer(1)); } Spigot *spigot_log1p(Spigot *a) { return spigot_log(spigot_add(a, spigot_integer(1))); } Spigot *spigot_sinh(Spigot *a) { Spigot *ea = spigot_exp(a->clone()); Spigot *ema = spigot_exp(spigot_neg(a)); return spigot_rational_mul(spigot_sub(ea, ema), 1, 2); } Spigot *spigot_cosh(Spigot *a) { Spigot *ea = spigot_exp(a->clone()); Spigot *ema = spigot_exp(spigot_neg(a)); return spigot_rational_mul(spigot_add(ea, ema), 1, 2); } Spigot *spigot_tanh(Spigot *a) { Spigot *ea = spigot_exp(a->clone()); Spigot *ema = spigot_exp(spigot_neg(a)); return spigot_div(spigot_sub(ea, ema), spigot_add(ea->clone(), ema->clone())); } Spigot *spigot_asinh(Spigot *a) { /* * asinh(x) = log ( x + sqrt(x^2+1) ) */ return spigot_log( spigot_add(a, spigot_sqrt(spigot_quadratic(a->clone(), 1, 0, 1)))); } Spigot *spigot_acosh(Spigot *a) { /* * acosh(x) = log ( x + sqrt(x^2-1) ) */ return spigot_log( spigot_add(a, spigot_sqrt(spigot_quadratic(a->clone(), 1, 0, -1)))); } Spigot *spigot_atanh(Spigot *a) { /* * 1 1+x * atanh(x) = - log ( --- ) * 2 1-x */ Spigot *n = spigot_add(spigot_integer(1), a->clone()); Spigot *d = spigot_sub(spigot_integer(1), a); return spigot_rational_mul(spigot_log(spigot_div(n, d)), 1, 2); } /* ---------------------------------------------------------------------- * pow(), implemented using exp+log in the general case but with a lot * of faff beforehand. * * There are a lot of obvious special cases of pow which we'd like to * deal with directly: a rational raised to a rational power might * come out actually rational, in which case we'd prefer to return it * as an explicit rational than as an exp+log expression so that * further rational-consuming special cases can recognise it. Then we * can also convert easy things like 0^x, x^0, x^1 and x^-1 into * simpler computations. * * After that, you might think that a^b = exp(b*log(a)) is the * fallback. But not quite: there's still one thing to be careful of. * * To use the exp+log strategy, we have to start by checking the sign * of a, because if a<0, then we must enforce that b is a known * rational with an odd denominator, and use the sign of the numerator * to choose the output sign. * * And a sign check of a would introduce an exactness hazard: what if * a is non-obviously zero and b is positive, such as sin(pi)^pi? In * that situation, *starting* by checking the sign of a would * introduce an exactness hazard, whereas in fact we would like to be * able to keep narrowing an output interval about zero. * * So, in cases like that, we must begin generating output information * about a^b _before_ we find out the sign of a, and when (or if) we * do find it out, _then_ go and do exp+log. */ /* * This is a subfunction of pow() which deals with the general case * once we know we're going to have to take logs. * * It takes the sign of a as an extra input rather than finding it * itself; see the call site in spigot_pow() for explanation. */ static Spigot *spigot_pow_general_case(Spigot *a, Spigot *b, int a_sign) { if (a_sign >= 0) { /* a^b == a^(b*log(a)). */ return spigot_exp(spigot_mul(b, spigot_log(a))); } else { bigint bn, bd; if (!b->is_rational(&bn, &bd)) { throw spigot_error("negative number raised to" " not-obviously-rational power"); } while (bn % 2U == 0 && bd % 2U == 0) { bn /= 2; bd /= 2; } if (bd % 2U == 0) { throw spigot_error("negative number raised to" " power with even denominator"); } a = spigot_rational_mul(a, -1, 1); Spigot *absret = spigot_exp(spigot_mul(b, spigot_log(a))); if (bn % 2U == 0) return absret; else return spigot_neg(absret); } } /* * This class deals with the case where we had to defer the sign check * of a. It narrows an output interval conservatively about zero for * as long as we haven't distinguished a from 0 yet, and then if we * later do find out a's sign, it switches to returning the output of * spigot_pow_general_case. */ class PowPrefixWrapper : public BinaryIntervalSource { Spigot *a, *b; bigint xn; bool reciprocal; int a_sign; BracketingGenerator *bg; int crState; public: PowPrefixWrapper(Spigot *aa, Spigot *ab, bigint axn, bool areciprocal) : a(aa), b(ab), xn(axn), reciprocal(areciprocal) { bg = NULL; a_sign = 0; crState = -1; dprint("hello PowPrefixWrapper %p %p %b %B", a, b, &xn, reciprocal); } ~PowPrefixWrapper() { delete a; delete b; if (bg) delete bg; } virtual PowPrefixWrapper *clone() { return new PowPrefixWrapper(a->clone(), b->clone(), xn, reciprocal); } virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { crBegin; /* * Initially, we set up a BracketingGenerator for just a. */ bg = new BracketingGenerator(a->clone()); while (1) { /* * Fetch some information about a, and see what we can do * with it. */ { bigint nlo, nhi; unsigned dbits; bg->get_bracket_shift(&nlo, &nhi, &dbits); dprint("input bracket for a: (%b,%b) / 2^%d", &nlo, &nhi, (int)dbits); /* * If the interval tells us the sign of a, then we can * leave this preliminary loop and go to sensible * exp+log evaluation. */ if (nlo > 0) { dprint("a > 0"); a_sign = +1; break; } else if (nhi < 0) { dprint("a < 0"); a_sign = -1; break; } /* * Otherwise, approximate this interval by expanding * it to one of the form [-2^k,+2^k], and then turn * that into an output interval. */ int k, k2; k = -(int)dbits + bigint_approxlog2(bigint_abs(nlo)) + 1; k2 = -(int)dbits + bigint_approxlog2(bigint_abs(nhi)) + 1; if (k < k2) k = k2; dprint("expanded input bracket for a: [-2^%d, +2^%d]", k, k); /* * We have a in [-2^k, +2^k]. Compute a bounding * interval for a^b using xn and reciprocal. */ if (reciprocal) { k = fdiv(k + xn - 1, xn); } else { k = k * xn; } /* * Now we know a^b in [-2^k, +2^k], so we can safely * return that. */ *ret_lo = -1; *ret_hi = +1; *ret_bits = abs(k); } dprint("output bracket for a^b: (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); crReturnV; } /* * If we've broken out of this loop, then we now know the sign * of a, so we can switch over to returning binary intervals * from that. * * The implementation of BinaryIntervalSource will take care * of us potentially not passing a _nested_ sequence of * intervals, so we've got no need to deal with any fiddly * special cases in that area. */ delete bg; bg = new BracketingGenerator(spigot_pow_general_case(a->clone(), b->clone(), a_sign)); while (1) { bg->get_bracket_shift(ret_lo, ret_hi, ret_bits); dprint("passthrough bracket for a^b: (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); crReturnV; } crEnd; } }; Spigot *spigot_pow(Spigot *a, Spigot *b) { bigint an, ad, bn, bd; bool a_rat, b_rat; /* * Start with the special-case of _both_ inputs being rational * (and within a range we can handle, and a is not zero, which * this handler wouldn't like anyway). */ a_rat = a->is_rational(&an, &ad); b_rat = b->is_rational(&bn, &bd); if (a_rat && b_rat && an != 0 && bn != 0 && an != ad) { /* * Special case we can handle easily, at least if the numbers * aren't too excessive: a rational raised to a rational * power. We deal with the numerator of b by simply raising an * and ad to that power using integer exponentiation; then, if * b has a nontrivial denominator, we use spigot_algebraic to * take a root of it. * * Raising an integer to a power is nice and easy, and we * permit it as long as the output number isn't too excessive * (determined by the constant 'pow_bits_limit'). The * root-taking via spigot_algebraic is more costly, and we * have a smaller limit for that, after which it's cheaper to * use exp+log anyway. */ const int pow_bits_limit = 1000000; const int alg_degree_limit = 100; bigint an_abs = bigint_abs(an); bigint ad_abs = bigint_abs(ad); bigint bn_abs = bigint_abs(bn); bigint bd_abs = bigint_abs(bd); bool bn_ok = (bigint_approxlog2(an_abs) * bn_abs < pow_bits_limit && bigint_approxlog2(ad_abs) * bn_abs < pow_bits_limit); bool bd_ok = (bd_abs < alg_degree_limit); if (bn_ok && bd_ok) { bigint abs_n = bigint_power(an_abs, bn_abs); bigint abs_d = bigint_power(ad_abs, bn_abs); if (an < 0 && bd % 2U == 0) { throw spigot_error("negative number raised to" " power with even denominator"); } int sign = (an < 0 && (bn_abs % 2U) != 0) ? -1 : +1; if (bd == 1) { if (bn >= 0) return spigot_rational(sign * abs_n, abs_d); else return spigot_rational(sign * abs_d, abs_n); } else { std::vector P; P.push_back(sign * (bn >= 0 ? abs_n : abs_d)); P.resize((unsigned)bd, 0); P.push_back(-(bn >= 0 ? abs_d : abs_n)); return spigot_algebraic(P, 0, P[0], 1); } } } /* * A few other special cases where one of a,b has a particular * rational value (but the other doesn't have to). */ if (a_rat && an == 0) { /* * 0^positive = 0, 0^0 and 0^negative are errors. */ if (get_sign(b) > 0) return spigot_integer(0); else throw spigot_error("zero raised to non-positive power"); } if (b_rat && bn == 0) { /* * Apart from the above case, anything^0 = 1. */ return spigot_integer(1); } if (a_rat && an == 1*ad) { /* * So is 1^anything. */ return spigot_integer(1); } if (b_rat && bn == 1*bd) { /* * Anything^1 is just itself. */ return a; } if (b_rat && bn == -1*bd) { /* * Anything^-1 is its reciprocal. */ return spigot_reciprocal(a); } if (b_rat && bn == 2*bd) { /* * For anything^2, we have a nice fast spigot_square. */ return spigot_square(a); } if (b_rat && bn == -2*bd) { /* * Ditto invsquare. */ return spigot_invsquare(a); } /* * Now we're nearly ready to do exp+log, modulo a sign check of a, * and (as explained above) that's hazardous to do in the obvious * way. Instead we do some complicated faffing about to find out * _either_ the sign of a _or_ narrow down to a special case in * which we can use the PowPrefixWrapper class. */ /* pow has a discontinuity at (0,0), so a _parallel_ sign test of * a,b does not introduce any avoidable exactness hazard */ int s = parallel_sign_test(a->clone(), b->clone()); if (s == +1 || s == -1) { /* * The parallel sign test has delivered us the sign of a, so * we can go straight to the general case. */ return spigot_pow_general_case(a, b, s); } if (s == -2) { /* * If b < 0, then a |-> pow(a,b) is discontinuous at 0, so we * can't avoid a hazard anyway at a=0, and hence can safely * test the sign of a without introducing any further hazards. */ return spigot_pow_general_case(a, b, get_sign(a->clone())); } /* * The only remaining case is s == +2, i.e. we know b > 0 and we * don't yet know about the sign of a. * * This is _potentially_ our special case. But it'll be much * easier if we know that |a| < 1, and if that's not the case then * finding the sign of a will be unproblematic anyway. So let's * start by narrowing down to _that_ case. */ { StaticGenerator sg(a->clone()); bigint approx = sg.get_approximate_approximant(8); if (bigint_abs(approx) > 4) { /* * We know the sign of a. */ return spigot_pow_general_case(a, b, (approx > 0) ? +1 : -1); } /* * Otherwise, we know |a| < 1. */ } /* * Now we know that b > 0 and |a| < 1, so this really is * the special case we want to take care with. * * Next, find a nice lower bound on b, i.e. a number 0 < x < b * such that it's easy to compute an upper bound on epsilon^x for * epsilon a power of two. I think making x an integer or the * reciprocal of an integer should be nice enough. */ bool reciprocal; // if true, x=1/xn, else x=xn bigint xn; { StaticGenerator sg(b->clone()); bigint approx = sg.get_approximate_approximant(32); if (approx > 36) { // b is big enough that we can take x an integer reciprocal = false; xn = fdiv(approx - 4, 32); } else { // Otherwise, find an integer _upper_ bound on 1/b, and // use the reciprocal of that StaticGenerator sg2(spigot_reciprocal(b->clone())); approx = sg2.get_approximate_approximant(32); reciprocal = true; xn = fdiv(approx + 4 + 31, 32); } } return new PowPrefixWrapper(a, b, xn, reciprocal); } spigot-0.2017-01-15.gdad1bbc6/expint.cpp0000644000000000000000000003602513025565031014174 0ustar /* * expint.cpp: exponential integrals. */ #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" #include "holefiller.h" /* * Exponential integrals, logarithmic integrals, and the * Euler-Mascheroni constant (which we get for free as a by-product of * that lot). * * There's a slightly confusing family of exponential integral * functions. They're all based around the idea of wanting to * integrate e^x/x, but because that integrand has a pole at 0, things * get messy. * * If you're prepared to work in the complex numbers, then integrands * with poles are a very well understood problem, which you deal with * by introducing a branch cut. A typical place to put the branch cut * is along the negative real axis, which is less than helpful if you * want a function that restricts sensibly to R (or rather, R\{0}). * Also, even if you put the branch cut somewhere more out of the way, * you find that if you want your function to take real values on R^+ * then it ends up taking values on R^- of the form (real + pi i) or * (real - pi i), depending which way you swerved round the origin. * * This is all a bit unsatisfactory for people who are only interested * in R. So the usual answer is to define a function called Ei, which * acts as an indefinite integral of e^x/x on each side of zero, so * that a _definite_ integral over any interval not containing zero * can be given as the difference of two values of Ei. (If the * interval does contain zero, of course, you go back to the person * who asked for it and demand more detail about what exactly they * were after.) To define this function fully, we must choose an * additive constant for R^- and another one for R^+ (since the * disconnected domain permits a separate choice in each component). * * The standard definition of Ei, as implemented by octave-specfun * under the name expint_Ei and by Mathematica under the name * ExpIntegralEi, chooses the negative domain constant so that the * limit of Ei at -infinity is zero, and then chooses the positive * domain constant by 'taking the Cauchy principal value', which * basically means looking at the integral you'd get if you zeroed out * a small interval [-epsilon,+epsilon] about the pole at zero, and * then took the limit as epsilon -> 0. An equivalent way of putting * that is to say that Ei's negative and positive constants are chosen * so that lim_{e->0} (Ei(-e) - Ei(+e)) = 0. * * The other basic idea of an exponential integral takes two * parameters, a non-negative integer n and a positive real x. * MathWorld calls this the 'E_n function': * http://mathworld.wolfram.com/En-Function.html * and defines it as * * inf exp(-xt) * E_n(x) = int -------- dt * 1 t^n * * That's not really an illuminating definition as far as I'm * concerned. I prefer the following inductive definition: E_0 is just * e^{-x}/x, and then E_{n+1} is the indefinite integral of (-E_n), * with the constant chosen every time so that every E_n tends to zero * as x->inf. (The flipping signs are slightly annoying, but the point * of them seems to be to make all the E_n positive and decreasing, * rather than having the sign of E_n depend on the parity of n.) * * Gnuplot implements E_n(x) under the name expint(n,x), and * Mathematica under the name ExpIntegralE[n,x]. * * We implement Ei(x), En(n,x), and E1(x) = En(1,x). We also implement * a function that Wikipedia defines under the name 'Ein': * https://en.wikipedia.org/wiki/Exponential_integral which is defined * as the integral of (1-e^{-x})/x (with constant so that Ein(0)=0). * The point of that function is that it _doesn't_ have a pole at * zero, or indeed anywhere else: it's an actually sensible function * from R->R, and even from C->C, with an obvious power series that * converges everywhere (obtained from the power series of exp by * term-by-term integration). So it's easy to evaluate, and hence * makes a useful primitive to build the various other things out of. * * Also in this family are the logarithmic integrals li(x) and Li(x), * which are easy because they're basically just Ei(log x). Wikipedia * [https://en.wikipedia.org/wiki/Logarithmic_integral_function] * defines them to be translations of each other, such that li(0)=0 * but Li(2)=0. (So Li lets you integrate up to large numbers without * the slight arbitrariness of having to invent a way to handle * crossing the pole at 1.) */ /* ---------------------------------------------------------------------- * Continued-fraction expansion of E_n. */ class EnCfrac : public Source { /* * http://functions.wolfram.com/GammaBetaErf/ExpIntegralE/10/ * says that * * exp(-x) * E_n(x) = ------------------------- * x + n * --------------------- * 1 + 1 * ----------------- * x + n + 1 * ------------- * 1 + 2 * --------- * x + n + 2 * ----- * ... * * This class computes everything but the exp(-x) factor, which * the caller must multiply in afterwards. */ bigint xn, xd, n, nim1, i; int crState; public: EnCfrac(const bigint &axn, const bigint &axd, const bigint &an) : xn(axn), xd(axd), n(an) { crState = -1; } virtual EnCfrac *clone() { return new EnCfrac(xn, xd, n); } bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 0; return false; } bool gen_matrix(bigint *matrix) { crBegin; /* * An initial matrix representing x |-> 1/x. */ matrix[0] = 0; matrix[1] = 1; matrix[2] = 1; matrix[3] = 0; crReturn(false); /* * Then the regular series. */ i = 1; nim1 = n; // n + i - 1 while (1) { matrix[0] = xn; matrix[1] = nim1*xd; matrix[2] = xd; matrix[3] = 0; crReturn(false); matrix[0] = 1; matrix[1] = i; matrix[2] = 1; matrix[3] = 0; crReturn(false); ++i; ++nim1; } crEnd; } }; class EinSeries : public Source { /* * Ein is computed by the following power series: * * inf (-1)^{k+1} x^k * Ein(x) = sum -------------- * k=1 k k! * * Let x = n/d. Then we have * * n -n^2 +n^3 * Ein(n/d) = ------ + -------- + -------- + ... * 1 1! d 2 2! d^2 3 3! d^3 * * n -n 1 -n 1 * = - ( 1 + -- ( - + -- ( - + ... ))) * d 2d 2 3d 3 * * so our matrices go (with an anomalous non-negation of n in the * first one) * * ( n n ) ( -2n -n ) ( -3n -n ) ... * ( 0 d ) ( 0 4d ) ( 0 9d ) */ bigint n, d, k; int crState; public: EinSeries(const bigint &an, const bigint &ad) : n(an), d(ad) { crState = -1; } virtual EinSeries *clone() { return new EinSeries(n, d); } bool gen_interval(bigint *low, bigint *high) { *low = -1; *high = 1; return true; } bool gen_matrix(bigint *matrix) { crBegin; /* Anomalous initial matrix without n negated */ matrix[0] = n; matrix[1] = n; matrix[2] = 0; matrix[3] = d; crReturn(false); k = 2; while (1) { matrix[0] = -k*n; matrix[1] = -n; matrix[2] = 0; matrix[3] = k*k*d; ++k; crReturn(false); } crEnd; } }; struct EnCfracConstructor : MonotoneConstructor { bigint n; EnCfracConstructor(const bigint &an) : n(an) {} MonotoneConstructor *clone() { return new EnCfracConstructor(n); } Spigot *construct(const bigint &xn, const bigint &xd) { return new EnCfrac(xn, xd, n); } }; struct EinSeriesConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new EinSeriesConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new EinSeries(n, d); } }; static Spigot *spigot_En_internal(const bigint &n, Spigot *x) { Spigot *expintcfrac = spigot_monotone(new EnCfracConstructor(n), x->clone()); return spigot_mul(expintcfrac, spigot_exp(spigot_neg(x))); } Spigot *spigot_Ein(Spigot *x) { return spigot_monotone(new EinSeriesConstructor, x->clone()); } /* * Wikipedia shows the relation between E_1 and Ein as * * E_1(z) = -gamma - log(z) + Ein(z) * * where gamma is the Euler-Mascheroni constant. This means that using * our continued fraction for E_1 and our series for Ein, we can * _compute_ the Euler-Mascheroni constant! */ Spigot *spigot_eulergamma() { /* * This formula works for any z, so it's a matter of choosing one * which gives the best convergence. * * We care about generating a lot of digits of gamma, of course; * but we also care about not taking too long to _get started_, * because we'll also be using this as a component of some of the * user-facing exponential integral functions below. Benchmarking * suggests that as you turn up z, you get a gradual improvement * in the generation of lots of digits, but it gets more and more * gradual and at the same time the startup time begins to become * significant - by the time you're at z=4096, say, you're taking * (on my test machine) 12 seconds before _any_ digit is * generated, and not managing to overtake the z=512 run until * about 1000 digits have gone by, and even then, not by very * much. * * I suppose if you wanted a _really_ large number of digits, it * _might_ be worth rigging up a wrapper class which kept * restarting the computation from the beginning with bigger and * bigger z, akin to the strategies used by GammaResidualBase and * MonotoneHelper, but I think the gain would be marginal even so. * Based on my ad-hoc benchmarking, the sensible thing seems to be * to pick the largest value of z that we get to before the * startup time starts to become noticeable, and that's 256. */ Spigot *z = spigot_integer(256); Spigot *Ein_minus_En = spigot_sub(spigot_Ein(z->clone()), spigot_En_internal(1, z->clone())); return spigot_sub(Ein_minus_En, spigot_log(z)); } class EnHoleFiller : public HoleFiller { bigint n; virtual EnHoleFiller *clone() { return new EnHoleFiller(n, x->clone()); } virtual Spigot *xspecial(int i) { if (i == 0 && n > 1) return spigot_integer(0); return NULL; } virtual Spigot *yspecial(int i) { if (i == 0 && n > 1) return spigot_rational(1, n-1); return NULL; } virtual Spigot *ygeneral(Spigot *x) { return spigot_En_internal(n, x); } public: EnHoleFiller(const bigint &an, Spigot *ax) : HoleFiller(ax), n(an) {} }; Spigot *spigot_En(Spigot *n, Spigot *x) { bigint nn, nd; if (!n->is_rational(&nn, &nd) || nd != 1) throw spigot_error("first argument to En must be an integer"); if (nn < 0) throw spigot_error("first argument to En must be non-negative"); if (nn > 1) x = spigot_enforce(x, ENFORCE_GE, spigot_integer(0), spigot_error("second argument to En must be " "non-negative")); else x = spigot_enforce(x, ENFORCE_GT, spigot_integer(0), spigot_error("second argument to En must be " "positive")); return (new EnHoleFiller(nn, x))->replace(); } Spigot *spigot_E1(Spigot *x) { x = spigot_enforce(x, ENFORCE_GT, spigot_integer(0), spigot_error("argument to E1 must be positive")); return spigot_En_internal(1, x); } Spigot *spigot_Ei(Spigot *x) { Spigot *Ein_term = spigot_Ein(spigot_neg(x->clone())); return spigot_sub(spigot_add(spigot_eulergamma(), spigot_log(spigot_abs(x))), Ein_term); } // Unusually lowercase class name, because li and Li really are // different things :-) class liHoleFiller : public HoleFiller { virtual liHoleFiller *clone() { return new liHoleFiller(x->clone()); } virtual Spigot *xspecial(int i) { if (i == 0) return spigot_integer(0); return NULL; } virtual Spigot *yspecial(int i) { if (i == 0) return spigot_integer(0); return NULL; } virtual Spigot *ygeneral(Spigot *x) { return spigot_Ei(spigot_log(x)); } public: liHoleFiller(Spigot *ax) : HoleFiller(ax) {} }; Spigot *spigot_li(Spigot *x) { x = spigot_enforce(x, ENFORCE_GE, spigot_integer(0), spigot_error("argument to li must be non-negative")); return (new liHoleFiller(x))->replace(); } class LiHoleFiller : public HoleFiller { virtual LiHoleFiller *clone() { return new LiHoleFiller(x->clone()); } virtual Spigot *xspecial(int i) { if (i == 0) return spigot_integer(0); if (i == 1) return spigot_integer(2); return NULL; } virtual Spigot *yspecial(int i) { if (i == 0) return spigot_neg(spigot_li(spigot_integer(2))); if (i == 1) return spigot_integer(0); return NULL; } virtual Spigot *ygeneral(Spigot *x) { /* * Li is just li(x)-li(2), or equivalently, the integral from 2 to * x of t/log(t). But we can do better than actually subtracting * two values of li, because li is implemented in terms of Ei * which is in turn the sum of multiple terms, so we can simplify: * * Li(x) = li(x) - li(2) * = Ei(log x) - Ei(log 2) * = gamma + log |log x| - Ein(-log x) * - gamma - log |log 2| + Ein(-log 2) * = log |log x| - log |log 2| - Ein(-log x) + Ein(-log 2) * = log (|log_2 x|) - Ein(-log x) + Ein(-log 2) * * and now we don't have to compute gamma at all, plus the two * separate log terms have turned into a single log-base-2. */ Spigot *log_log2_x = spigot_log(spigot_abs(spigot_log2(x->clone()))); Spigot *Ein_log_x = spigot_Ein(spigot_neg(spigot_log(x))); Spigot *Ein_log_2 = spigot_Ein(spigot_log(spigot_rational(1,2))); return spigot_add(log_log2_x, spigot_sub(Ein_log_2, Ein_log_x)); } public: LiHoleFiller(Spigot *ax) : HoleFiller(ax) {} }; Spigot *spigot_Li(Spigot *x) { x = spigot_enforce(x, ENFORCE_GE, spigot_integer(0), spigot_error("argument to Li must be non-negative")); return (new LiHoleFiller(x))->replace(); } spigot-0.2017-01-15.gdad1bbc6/expr.cpp0000644000000000000000000013203313025565031013637 0ustar /* * expr.cpp: Expression parser. */ /* * To make it really easy to add new operators at strange levels of * precedence in future, I think I'll implement an operator- * precedence parser on this occasion. This is a sort of informal * combination of a shift-reduce and recursive approach. * * The central function expects to read a stream of 'operator' * tokens and 'atom' nonterminals from the input. An operator token * comes straight from the lexer, and comes with tags saying what * the precedence and associativity of that operator is when it's * binary, and what its precedence is when unary. An atom token is * either a literal or identifier straight from the lexer, or a * function call, or something in parentheses; in the latter two * cases, we recurse into subfunctions to do the hard work, and when * we come back we've got something we can treat as atomic for the * purposes of this particular interleaving of operators and atoms. * * Within the central function, we pile up our operator and atom * nonterminals on a stack. Before shifting any given operator, we * may choose to perform one or more 'reduce' operations which * convert several of these symbols into one: a unary reduce * converts an operator and an atom into an atom, and a binary * reduce turns A,O,A into A. When we see end-of-string (or closing * parenthesis or function-call-argument-terminating comma, if we * have ourselves been called recursively), we perform reduces until * we have only one symbol left, and return that to our caller. * * So the question is, when do we shift and when do we reduce? * * We can trivially identify unary operators as soon as we see them: * they're precisely the operators not preceded by an atom (either * following another operator, or at the start of the string). So we * always know whether a binary or a unary reduce is an available * option. * * We do a reduce if the operator we're about to shift has lower * precedence than the one that would be involved in the reduce. (If * we have "1+2" and see *, we don't reduce the 1+2 because the * 2*something will take precedence; but if we have "1*2" and see +, * then we know that's equivalent to 3 plus whatever it is.) Ties * are broken by associativity: if the two operators have the same * precedence, we reduce if that precedence level is * right-associative and otherwise we shift. This applies to both * binary and unary reduces. * * (I'm assuming here, incidentally, that all operators at the same * precedence level have the same associativity and the same arity. * This is easily enough enforced at setup time by defining the * precedence levels to store associativity and arity in their * bottom two bits.) */ #include #include #include #include #include #include #include #include #include "spigot.h" #include "funcs.h" #include "expr.h" #include "error.h" #define TOKENS(X) \ X(ERROR) \ X(EOS) \ X(LPAR) \ X(RPAR) \ X(COMMA) \ X(OPERATOR) \ X(IDENTIFIER) \ X(LET) \ X(IN) \ X(EQUALS) \ X(NUMBER) \ X(SPIGOT) #define TOKEN_ENUM_DEF(x) TOK_##x, enum tokentype { TOKENS(TOKEN_ENUM_DEF) TOK_MAX }; #define TOKEN_NAME_DEF(x) #x, static const char *toknames[] = { TOKENS(TOKEN_NAME_DEF) }; #define ASSOC_MASK 0x3000 #define LEFTASSOC 0x0000 #define RIGHTASSOC 0x1000 #define UNARY 0x2000 #define FUNCTION 0x3000 /* special value: we give arity, not precedence */ #define VARIADIC 0x00FF /* sentinel arity value meaning 'variable' */ #define OPERATORS(X) \ X(ADD, "+", 10|LEFTASSOC, spigot_add(a,b)) \ X(SUB, "-", 10|LEFTASSOC, spigot_sub(a,b)) \ X(MUL, "*", 20|LEFTASSOC, spigot_mul(a,b)) \ X(DIV, "/", 20|LEFTASSOC, spigot_div(a,b)) \ X(MOD, "%", 20|LEFTASSOC, spigot_mod(a,b)) \ X(MOD2, "mod", 20|LEFTASSOC, spigot_mod(a,b)) \ X(REM, "rem", 20|LEFTASSOC, spigot_rem(a,b)) \ X(POW, "^", 40|RIGHTASSOC, spigot_pow(a,b)) \ X(POW2, "**", 40|RIGHTASSOC, spigot_pow(a,b)) \ X(NEG, "-", 30|UNARY, spigot_neg(a)) \ X(NOP, "+", 30|UNARY, a) \ X(PI, "pi", 0|FUNCTION, spigot_pi()) \ X(TAU, "tau", 0|FUNCTION, spigot_tau()) \ X(E, "e", 0|FUNCTION, spigot_e()) \ X(PHI, "phi", 0|FUNCTION, spigot_phi()) \ X(APERY, "apery", 0|FUNCTION, spigot_apery()) \ X(EULERGAMMA, "eulergamma", 0|FUNCTION, spigot_eulergamma()) \ X(SQRT, "sqrt", 1|FUNCTION, spigot_sqrt(a)) \ X(HYPOT, "hypot", 2|FUNCTION, spigot_hypot(a,b)) \ X(CBRT, "cbrt", 1|FUNCTION, spigot_cbrt(a)) \ X(SIN, "sin", 1|FUNCTION, spigot_sin(a)) \ X(COS, "cos", 1|FUNCTION, spigot_cos(a)) \ X(TAN, "tan", 1|FUNCTION, spigot_tan(a)) \ X(ASIN, "asin", 1|FUNCTION, spigot_asin(a)) \ X(ACOS, "acos", 1|FUNCTION, spigot_acos(a)) \ X(ATAN, "atan", 1|FUNCTION, spigot_atan(a)) \ X(ATAN2, "atan2", 2|FUNCTION, spigot_atan2(a,b)) \ X(SIND, "sind", 1|FUNCTION, spigot_sind(a)) \ X(COSD, "cosd", 1|FUNCTION, spigot_cosd(a)) \ X(TAND, "tand", 1|FUNCTION, spigot_tand(a)) \ X(ASIND, "asind", 1|FUNCTION, spigot_asind(a)) \ X(ACOSD, "acosd", 1|FUNCTION, spigot_acosd(a)) \ X(ATAND, "atand", 1|FUNCTION, spigot_atand(a)) \ X(ATAN2D, "atan2d", 2|FUNCTION, spigot_atan2d(a,b)) \ X(EXP, "exp", 1|FUNCTION, spigot_exp(a)) \ X(LOG, "log", VARIADIC|FUNCTION, spigot_log_wrapper(args)) \ X(EXP2, "exp2", 1|FUNCTION, spigot_exp2(a)) \ X(EXP10, "exp10", 1|FUNCTION, spigot_exp10(a)) \ X(LOG2, "log2", 1|FUNCTION, spigot_log2(a)) \ X(LOG10, "log10", 1|FUNCTION, spigot_log10(a)) \ X(POW3, "pow", 2|FUNCTION, spigot_pow(a,b)) \ X(EXPM1, "expm1", 1|FUNCTION, spigot_expm1(a)) \ X(LOG1P, "log1p", 1|FUNCTION, spigot_log1p(a)) \ X(SINH, "sinh", 1|FUNCTION, spigot_sinh(a)) \ X(COSH, "cosh", 1|FUNCTION, spigot_cosh(a)) \ X(TANH, "tanh", 1|FUNCTION, spigot_tanh(a)) \ X(ASINH, "asinh", 1|FUNCTION, spigot_asinh(a)) \ X(ACOSH, "acosh", 1|FUNCTION, spigot_acosh(a)) \ X(ATANH, "atanh", 1|FUNCTION, spigot_atanh(a)) \ X(LGAMMA, "lgamma", 1|FUNCTION, spigot_lgamma(a)) \ X(TGAMMA, "gamma", 1|FUNCTION, spigot_gamma(a)) \ X(TGAMMA2, "tgamma", 1|FUNCTION, spigot_gamma(a)) \ X(ERF, "erf", 1|FUNCTION, spigot_erf(a)) \ X(ERFC, "erfc", 1|FUNCTION, spigot_erfc(a)) \ X(Phi, "Phi", 1|FUNCTION, spigot_Phi(a)) \ X(Phi2, "norm", 1|FUNCTION, spigot_Phi(a)) \ X(ERFINV, "erfinv", 1|FUNCTION, spigot_erfinv(a)) \ X(ERFINV2, "inverf", 1|FUNCTION, spigot_erfinv(a)) \ X(ERFCINV, "erfcinv", 1|FUNCTION, spigot_erfcinv(a)) \ X(ERFCINV2, "inverfc", 1|FUNCTION, spigot_erfcinv(a)) \ X(PhiINV, "Phiinv", 1|FUNCTION, spigot_Phiinv(a)) \ X(PhiINV2, "invPhi", 1|FUNCTION, spigot_Phiinv(a)) \ X(PhiINV3, "norminv", 1|FUNCTION, spigot_Phiinv(a)) \ X(PhiINV4, "invnorm", 1|FUNCTION, spigot_Phiinv(a)) \ X(LAMBERTWPOS, "W", 1|FUNCTION, spigot_lambertw_pos(a)) \ X(LAMBERTWNEG, "Wn", 1|FUNCTION, spigot_lambertw_neg(a)) \ X(En, "En", 2|FUNCTION, spigot_En(a,b)) \ X(E1, "E1", 1|FUNCTION, spigot_E1(a)) \ X(Ei, "Ei", 1|FUNCTION, spigot_Ei(a)) \ X(Ein, "Ein", 1|FUNCTION, spigot_Ein(a)) \ X(li, "li", 1|FUNCTION, spigot_li(a)) \ X(Li, "Li", 1|FUNCTION, spigot_Li(a)) \ X(Si, "Si", 1|FUNCTION, spigot_Si(a)) \ X(si, "si", 1|FUNCTION, spigot_si(a)) \ X(Cin, "Cin", 1|FUNCTION, spigot_Cin(a)) \ X(Ci, "Ci", 1|FUNCTION, spigot_Ci(a)) \ X(UFresnelS, "UFresnelS", 1|FUNCTION, spigot_UFresnelS(a)) \ X(UFresnelC, "UFresnelC", 1|FUNCTION, spigot_UFresnelC(a)) \ X(FresnelS, "FresnelS", 1|FUNCTION, spigot_FresnelS(a)) \ X(FresnelC, "FresnelC", 1|FUNCTION, spigot_FresnelC(a)) \ X(zeta, "zeta", 1|FUNCTION, spigot_zeta(a)) \ X(ABS, "abs", 1|FUNCTION, spigot_abs(a)) \ X(FRAC, "frac", 1|FUNCTION, spigot_frac(a)) \ X(CEIL, "ceil", 1|FUNCTION, spigot_ceil(a)) \ X(FLOOR, "floor", 1|FUNCTION, spigot_floor(a)) \ X(ALGEBRAIC, "algebraic", VARIADIC|FUNCTION, \ spigot_algebraic_wrapper(args)) #define OPERATOR_ENUM_DEF(name,string,prec,expr) OP_##name, enum { OPERATORS(OPERATOR_ENUM_DEF) OP_MAX }; #define OPERATOR_DATA_DEF(name,string,prec,expr) {string,prec}, static const struct { const char *text; int prec; } operators[] = { OPERATORS(OPERATOR_DATA_DEF) }; #define OPERATOR_CONSTRUCTOR_DEF(name,string,prec,expr) \ static Spigot *op_##name(const std::vector &args) \ { \ /* shorthands for args[0] and args[1] to make most \ * definitions nicer */ \ Spigot *a = (args.size() > 0 ? args[0] : NULL); \ Spigot *b = (args.size() > 1 ? args[1] : NULL); \ (void)a; (void)b; /* dodge unused-variable warnings */ \ return (expr); \ } OPERATORS(OPERATOR_CONSTRUCTOR_DEF) #define OPERATOR_CONSTRUCTOR_ARRAY(name,string,prec,expr) op_##name, typedef Spigot *(*op_fn_t)(const std::vector &args); static const op_fn_t operator_constructors[] = { OPERATORS(OPERATOR_CONSTRUCTOR_ARRAY) }; class token { public: tokentype type; std::string text; int opindex; bigint n, d; Spigot *spigot; inline token() : type(TOK_ERROR), opindex(-1), spigot(NULL) {} inline token(tokentype atype) : type(atype), opindex(-1), spigot(NULL) {} inline token(tokentype atype, const char *str, int len = -1) : type(atype), opindex(-1), spigot(NULL) { if (len < 0) len = strlen(str); text = std::string(str, len); } inline token(tokentype atype, int index) : type(atype), opindex(index), spigot(NULL) {} inline token(tokentype atype, bigint an, bigint ad) : type(atype), opindex(-1), n(an), d(ad), spigot(NULL) {} inline token(tokentype atype, Spigot *spig) : type(atype), opindex(-1), spigot(spig) {} inline token(const token &x) : type(x.type), text(x.text), opindex(x.opindex), n(x.n), d(x.d), spigot(x.spigot) {} inline token &operator=(const token &x) { type = x.type; opindex = x.opindex; text = x.text; n = x.n; d = x.d; spigot = x.spigot; return *this; } void debug(void) { printf("%s", toknames[type]); if (type == TOK_OPERATOR) { printf(" %d '%s'", opindex, operators[opindex].text); } else if (type == TOK_IDENTIFIER) { printf(" '%s'", text.c_str()); } else if (type == TOK_NUMBER) { putchar(' '); bigint_print(n); putchar('/'); bigint_print(d); } else if (type == TOK_SPIGOT) { printf(" %p", spigot); } putchar('\n'); } }; inline bool isidstart(char c) { return c && (isalpha((unsigned char)c) || c == '_'); } inline bool isidchar(char c) { return c && (isalnum((unsigned char)c) || c == '_'); } inline bool strczmatch(int lenc, const char *strc, const char *strz) { int lenz = strlen(strz); return lenc == lenz && !memcmp(strc, strz, lenc); } class Lexer { public: token currtok; const char *p; static inline int frombase(char c) { // This translates an alphanumeric out of any base up to 36. // Return value is from 0 to 35 for success, or 36 for // failure, so you can easily vet the answer as being within a // smaller base. return (c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'Z' ? c - ('A'-10) : c >= 'a' && c <= 'z' ? c - ('a'-10) : 36); } Lexer(const char *string) : currtok(), p(string) { advance(); } std::string parse_filename(const char *q, int *lenused) { std::string ret; const char *qorig = q; if (*q == '"' || *q == '\'') { char quote = *q++; while (*q != quote || q[1] == quote) { if (!*q) { throw spigot_error("unexpected end of expression in quoted" " filename string"); } if (*q == quote) q++; ret.push_back(*q); q++; } q++; /* eat closing quote */ } else { while (*q && !isspace((unsigned char)*q)) { ret.push_back(*q); q++; } } if (lenused) *lenused = q - qorig; return ret; } void advance() { while (*p && isspace((unsigned char)*p)) p++; if (!*p) { currtok = token(TOK_EOS); return; } if (*p == '(') { currtok = token(TOK_LPAR); p++; return; } if (*p == ')') { currtok = token(TOK_RPAR); p++; return; } if (*p == ',') { currtok = token(TOK_COMMA); p++; return; } if (*p == '=') { currtok = token(TOK_EQUALS); p++; return; } /* * Match non-identifier-shaped operators by maximal munch. */ int opmaxlen = 0, opindex = -1; for (int i = 0; i < OP_MAX; i++) { if (isidstart(operators[i].text[0])) continue; int oplen = strlen(operators[i].text); if (oplen > opmaxlen && !strncmp(p, operators[i].text, oplen)) { opmaxlen = oplen; opindex = i; } } if (opindex >= 0) { currtok = token(TOK_OPERATOR, opindex); p += opmaxlen; return; } int base_override = 0; if (isidstart(*p)) { const char *q = p; while (isidchar(*p)) p++; if (*p == ':') { /* * Various special cases of keywords followed by * colons. * * Start by extracting the keyword into a small string * we can process on its own. */ char keyword[32]; int param, pos; if (p-q >= (int)sizeof(keyword)) { /* * No keyword longer than this will be recognised * anyway, so rather than faff about with * allocating an arbitrary amount of space to * store a copy of it, we might as well just fill * 'keyword' with a string that can't possibly * have come out of the alphanumeric-matching loop * above. */ strcpy(keyword, "!"); } else { sprintf(keyword, "%.*s", (int)(p-q), q); } if (!strcmp(keyword, "ieee")) { /* * Special case: "ieee:" followed by a 4-, 8-, 16- * or 32-digit hex number is treated as an IEEE * half, single, double or quad precision * (respectively) floating-point bit pattern in * hex, which is expanded into a rational and * presented as TOK_NUMBER. If there's a following * '.' plus extra hex digits, those extend the * precision in the obvious way. * * So count and collect the hex digits. */ bigint value = 0; int ndigits = 0, expbits, sign = 1; p++; while (*p && isxdigit((unsigned char)*p)) { if (ndigits >= 32) { throw spigot_error("expected 4, 8, 16 or 32 hex " "digits after 'ieee:'"); } int val = frombase(*p); assert(val < 16); value *= 16; value += val; ndigits++; p++; } switch (ndigits) { case 4: expbits = 5; break; case 8: expbits = 8; break; case 16: expbits = 11; break; case 32: expbits = 15; break; default: throw spigot_error("expected 4, 8, 16 or 32 hex digits" " after 'ieee:'"); } if (*p == '.') { p++; while (*p && isxdigit((unsigned char)*p)) { int val = frombase(*p); assert(val < 16); value *= 16; value += val; ndigits++; p++; } } int exponent = value / bigint_power(2, 4*ndigits-expbits-1); value %= bigint_power(2, 4*ndigits-expbits-1); if (exponent & (1 << expbits)) sign = -1; exponent &= (1 << expbits)-1; if (exponent == (1 << expbits)-1) { throw spigot_error("IEEE %s not supported", value == 0 ? "infinity" : "NaN"); } if (exponent == 0) { exponent = 1; } else { value += bigint_power(2, 4*ndigits-expbits-1); } exponent -= (1 << (expbits-1))-1; /* unbias exponent */ exponent -= 4*ndigits-expbits-1; /* turn into int * 2^e */ if (exponent > 0) currtok = token(TOK_NUMBER, sign * value * bigint_power(2, exponent), 1); else currtok = token(TOK_NUMBER, sign * value, bigint_power(2, -exponent)); return; } else if (sscanf(keyword, "base%d%n", ¶m, &pos) > 0 && pos == (int)strlen(keyword)) { /* * Prefixes such as base2: or base19: permit input * in an arbitrary number base between 2 and 36. * We don't process these here; we fall through to * the main literal-processing code below, having * set a base override variable. */ if (param < 2 || param > 36) { throw spigot_error("base in keyword '%s:' should be " "between 2 and 36 inclusive", keyword); } base_override = param; assert(*p == ':'); p++; // advance past the colon } else if (!strcmp(keyword, "cfracfile") || !strcmp(keyword, "cfracxfile") || (sscanf(keyword, "base%dfile%n", ¶m, &pos)>0 && pos == (int)strlen(keyword)) || (sscanf(keyword, "base%dxfile%n", ¶m, &pos)>0 && pos == (int)strlen(keyword))) { bool cfrac = (keyword[0] == 'c'); bool exact = (keyword[strlen(keyword)-5] == 'x'); if (!cfrac && (param < 2 || param > 36)) { throw spigot_error("base in keyword '%s:' should be " "between 2 and 36 inclusive", keyword); } /* * We expect to see a file name here, which * we'll open and read from at spigot evaluation * time. Depending on which keyword was used * above, we'll expect it to contain either * continued fraction terms or digits in some * number base. */ p++; int lenused; std::string filename = parse_filename(p, &lenused); p += lenused; if (*q == 'c') { currtok = token( TOK_SPIGOT, spigot_cfracfile(filename.c_str(), exact)); } else { currtok = token( TOK_SPIGOT, spigot_basefile(param, filename.c_str(), exact)); } return; } else if (!strcmp(keyword, "cfracfd") || (sscanf(keyword, "base%dfd%n", ¶m, &pos) > 0 && pos == (int)strlen(keyword))) { /* * We expect to see an fd number here, from * which we'll read continued fraction terms or * a base representation in the same way as * above. */ #ifdef HAVE_FDS int fd; #endif p++; #ifdef HAVE_FDS fd = atoi(p); #endif p += strspn(p, "0123456789"); bool cfrac = (keyword[0] == 'c'); #ifdef HAVE_FDS if (!cfrac && (param < 2 || param > 36)) { throw spigot_error("base in keyword '%s:' should be " "between 2 and 36 inclusive", keyword); } if (cfrac) { currtok = token(TOK_SPIGOT, spigot_cfracfd(fd)); } else { int base = atoi(q+4); currtok = token(TOK_SPIGOT, spigot_basefd(base, fd)); } return; #else throw spigot_error("'%s:N' not supported in this" " build of spigot", cfrac ? "cfracfd" : "baseNfd"); #endif } else { throw spigot_error("unrecognised prefix keyword '%.*s:'", (int)(p-q), q); } } else { /* * Identifiers not prefixed by a colon must be checked * against keywords we know, and failing that, * returned as TOK_IDENTIFIER. */ for (int i = 0; i < OP_MAX; i++) { if ((operators[i].prec & ASSOC_MASK) == FUNCTION) continue; /* lex function name as id, not operator */ if (strczmatch(p-q, q, operators[i].text)) { currtok = token(TOK_OPERATOR, i); return; } } if (strczmatch(p-q, q, "let")) { currtok = token(TOK_LET); } else if (strczmatch(p-q, q, "in")) { currtok = token(TOK_IN); } else { currtok = token(TOK_IDENTIFIER, q, p-q); } return; } } if (*p == '.' || frombase(*p) < (base_override ? base_override : 10)) { int base, expbase; int expmarker; bool seendot = false; bigint n = 0, d = 1; if (base_override) { /* * We saw a baseN: prefix above, which means we're * expecting a number in that base, with no exponent * suffix. */ base = base_override; expbase = 0; expmarker = UCHAR_MAX + 1; // avoid ever matching below } else if (*p == '0' && tolower((unsigned char)p[1]) == 'x') { /* * Hex literal. */ p += 2; base = 16; expbase = 2; expmarker = 'p'; } else { /* * Decimal literal. */ base = expbase = 10; expmarker = 'e'; } while (*p == '.' || (*p && frombase(*p) < base)) { if (*p == '.') { if (!seendot) { seendot = true; p++; continue; } else { throw spigot_error("two dots in numeric literal"); } } else { int val = frombase(*p); n *= base; n += val; if (seendot) d *= base; p++; } } if (*p && tolower((unsigned char)*p) == expmarker) { int exponent = 0; bool expneg = false; p++; if (*p == '-' || *p == '+') { expneg = (*p == '-'); p++; } while (*p && isdigit((unsigned char)*p)) { exponent = 10 * exponent + frombase(*p); p++; } bigint mult = bigint_power(expbase, exponent); if (expneg) d *= mult; else n *= mult; } currtok = token(TOK_NUMBER, n, d); return; } throw spigot_error("unrecognised token"); } }; // Uniquely identify every function defined in an expression, no // matter where in the tree and nesting structure. typedef int FunctionID; struct FnArgHolder { std::vector args; FunctionID fnid; FnArgHolder *parent; FnArgHolder(FunctionID afnid, FnArgHolder *aparent) : fnid(afnid), parent(aparent) {} void add_arg(Spigot *arg) { args.push_back(arg); } ~FnArgHolder() { for (int i = 0; i < (int)args.size(); ++i) delete args[i]; } Spigot *lookup(FunctionID afnid, int argindex) { if (afnid == fnid) { assert(0 <= argindex); assert(argindex < (int)args.size()); return args[argindex]->clone(); } else { assert(parent); return parent->lookup(afnid, argindex); } } }; struct ASTNode { virtual ~ASTNode() {} virtual Spigot *evaluate(FnArgHolder *fnargs) = 0; }; struct ASTSpigot : ASTNode { Spigot *spigot; ASTSpigot(Spigot *aspigot) : spigot(aspigot) {} ~ASTSpigot() { delete spigot; } Spigot *evaluate(FnArgHolder *) { return spigot->clone(); } }; struct ASTOp : ASTNode { op_fn_t opfn; std::vector args; ASTOp(int opindex, const std::vector &aargs) : opfn(operator_constructors[opindex]), args(aargs) {} ~ASTOp() { for (int i = 0; i < (int)args.size(); ++i) delete args[i]; }; Spigot *evaluate(FnArgHolder *fnargs) { std::vector sargs; for (int i = 0; i < (int)args.size(); ++i) sargs.push_back(args[i]->evaluate(fnargs)); return opfn(sargs); } }; struct ASTRef : ASTNode { /* * Cross-reference to another piece of AST, which avoids deleting * the referred-to piece twice. */ ASTNode *target; ASTRef(ASTNode *atarget) : target(atarget) {} Spigot *evaluate(FnArgHolder *fnargs) { return target->evaluate(fnargs); } }; struct ASTFnArg : ASTNode { FunctionID fnid; int argindex; ASTFnArg(FunctionID afnid, int aargindex) : fnid(afnid), argindex(aargindex) {} Spigot *evaluate(FnArgHolder *fnargs) { return fnargs->lookup(fnid, argindex); } }; struct ASTFnCall : ASTNode { ASTNode *fnbody; FunctionID fnid; std::vector args; ASTFnCall(ASTNode *afnbody, FunctionID afnid, const std::vector &aargs) : fnbody(afnbody), fnid(afnid), args(aargs) {} ~ASTFnCall() { for (int i = 0; i < (int)args.size(); ++i) delete args[i]; }; Spigot *evaluate(FnArgHolder *fnargs) { FnArgHolder newfnargs(fnid, fnargs); for (int i = 0; i < (int)args.size(); ++i) newfnargs.add_arg(args[i]->evaluate(fnargs)); return fnbody->evaluate(&newfnargs); } }; struct Definition { virtual ~Definition() {} virtual bool is_function() = 0; virtual bool check_nargs(int nargs, int *expected) = 0; virtual ASTNode *make_call(const std::vector &aargs) = 0; }; struct BuiltinDefinition : Definition { int opindex; BuiltinDefinition(int aopindex) : opindex(aopindex) {} bool is_function() { int n = operators[opindex].prec & ~ASSOC_MASK; return n != 0; } bool check_nargs(int nargs, int *expected) { int n = operators[opindex].prec & ~ASSOC_MASK; if (n == VARIADIC || n == nargs) { return true; } else { *expected = n; return false; } } virtual ASTNode *make_call(const std::vector &args) { return new ASTOp(opindex, args); } }; struct Variable : Definition { ASTNode *value; Variable(ASTNode *avalue) : value(avalue) {} bool is_function() { return false; } bool check_nargs(int /*nargs_got*/, int * /*expected*/) { return true; } virtual ASTNode *make_call(const std::vector &) { return new ASTRef(value); } }; struct UserDefinedFunction : Definition { int nargs; ASTNode *fnbody; FunctionID fnid; UserDefinedFunction(int anargs, ASTNode *afnbody, FunctionID afnid) : nargs(anargs), fnbody(afnbody), fnid(afnid) {} bool is_function() { return true; } bool check_nargs(int nargs_got, int *expected) { *expected = nargs; return nargs == nargs_got; } virtual ASTNode *make_call(const std::vector &args) { return new ASTFnCall(fnbody, fnid, args); } }; struct Scope { Scope *parent; Definition *lookup(const char *varname) { Definition *ret = lookup_here(varname); if (!ret && parent) ret = parent->lookup(varname); return ret; } Scope(Scope *aparent) : parent(aparent) {} virtual Definition *lookup_here(const char *varname) = 0; }; struct DictScope : Scope { std::map names; DictScope(Scope *parent) : Scope(parent) {} ~DictScope() { std::map::iterator it; for (it = names.begin(); it != names.end(); ++it) delete it->second; } Definition *lookup_here(const char *varname) { std::map::iterator it; it = names.find(varname); if (it != names.end()) return it->second; else return NULL; } }; struct GlobalScopeWrapper : DictScope { GlobalScope *gs; GlobalScopeWrapper(GlobalScope *ags) : DictScope(NULL), gs(ags) { for (int i = 0; i < OP_MAX; i++) { if ((operators[i].prec & ASSOC_MASK) == FUNCTION) names[operators[i].text] = new BuiltinDefinition(i); } } Definition *lookup_here(const char *varname) { if (gs) { Spigot *spig = gs->lookup(varname); if (spig) return new Variable(new ASTSpigot(spig)); } return DictScope::lookup_here(varname); } }; struct LetScope : DictScope { LetScope(Scope *parent) : DictScope(parent) {} void add_var(const std::string &varname, ASTNode *node) { names[varname] = new Variable(node); } void add_fn(const std::string &fnname, int nargs, ASTNode *node, FunctionID fnid) { names[fnname] = new UserDefinedFunction(nargs, node, fnid); } }; struct FnArgScope : DictScope { int nargs; FunctionID fnid; FnArgScope(Scope *parent, FunctionID afnid) : DictScope(parent), nargs(0), fnid(afnid) {} void add_arg(const std::string &argname) { if (names.find(argname) != names.end()) throw spigot_error("parameter name '%s' repeated in function" " definition", argname.c_str()); names[argname] = new Variable(new ASTFnArg(fnid, nargs++)); } }; struct stack { ASTNode *atom; int opindex; }; void parse_recursive(Lexer &lexer, struct stack *stack, Scope *scope, FunctionID *curr_fnid) { struct stack *sp = stack; int index; while (1) { if (lexer.currtok.type == TOK_EOS || lexer.currtok.type == TOK_COMMA || lexer.currtok.type == TOK_IN || lexer.currtok.type == TOK_RPAR || lexer.currtok.type == TOK_OPERATOR) { /* * These are all the types of token which might require * reducing before we process them. */ if (lexer.currtok.type == TOK_OPERATOR) { index = lexer.currtok.opindex; lexer.advance(); got_op: /* * Distinguish binary from unary operators: a unary * operator is any operator appearing directly after * another operator or at start-of-expression. */ if (sp == stack || !sp[-1].atom) { /* * This should be a unary operator. If the lexer has * returned us a binary operator with the same * spelling, find the right one instead. */ if ((operators[index].prec & ASSOC_MASK) != UNARY) { for (int i = 0; i < OP_MAX; i++) if (!strcmp(operators[index].text, operators[i].text) && (operators[i].prec & ASSOC_MASK) == UNARY) { index = i; break; } } /* * And if that didn't work, we have a syntax error. */ if ((operators[index].prec & ASSOC_MASK) != UNARY) { throw spigot_error("expected unary operator"); } /* * If it did, though, unary operators get * unconditionally shifted. */ sp->atom = NULL; sp->opindex = index; sp++; continue; } else { /* * This should be a binary operator. */ if ((operators[index].prec & ASSOC_MASK) == UNARY) { throw spigot_error("expected binary operator"); } } } else { index = -1; /* this is not actually an operator */ } /* * Before we shift (or terminate), reduce any higher- * priority operators already on our stack. */ while (1) { if (sp - stack < 2) break; /* run out of candidate reduces */ assert(sp[-1].atom); assert(!sp[-2].atom); int thisprec = (index < 0 ? -1 : operators[index].prec); int thatprec = operators[sp[-2].opindex].prec; if ((thisprec &~ ASSOC_MASK) > (thatprec &~ ASSOC_MASK)) break; /* new operator is higher-priority */ if ((thisprec &~ ASSOC_MASK) == (thatprec &~ ASSOC_MASK) && (thisprec & ASSOC_MASK) == RIGHTASSOC) break; /* equal-prec but right-associative */ /* * Now we know we want to reduce. Split into unary * and binary cases. */ std::vector args; ASTNode *ret; if ((thatprec & ASSOC_MASK) == UNARY) { args.push_back(sp[-1].atom); ret = new ASTOp(sp[-2].opindex, args); sp--; } else { assert(sp - stack >= 3); assert(sp[-3].atom); args.push_back(sp[-3].atom); args.push_back(sp[-1].atom); ret = new ASTOp(sp[-2].opindex, args); sp -= 2; } sp[-1].atom = ret; } /* * Now we can shift the new operator, or terminate * the parse, depending. */ if (index >= 0) { sp->atom = NULL; sp->opindex = index; sp++; continue; } else { if (sp != stack+1 || !sp[-1].atom) { if (lexer.currtok.type == TOK_EOS) throw spigot_error("unexpected end of expression"); else if (lexer.currtok.type == TOK_COMMA) throw spigot_error("unexpected ','"); else if (lexer.currtok.type == TOK_IN) throw spigot_error("unexpected 'in'"); else throw spigot_error("unexpected ')'"); } return; } } /* * If we get here, it means we're about to parse an atom. * * One silly special case: if the previous thing on the * stack is also an atom, we pretend there was a * multiplication sign in between. */ if (sp > stack && sp[-1].atom) { index = OP_MUL; goto got_op; } if (lexer.currtok.type == TOK_NUMBER) { sp->atom = new ASTSpigot (spigot_rational(lexer.currtok.n, lexer.currtok.d)); sp->opindex = -1; sp++; lexer.advance(); continue; } if (lexer.currtok.type == TOK_SPIGOT) { sp->atom = new ASTSpigot(lexer.currtok.spigot); sp->opindex = -1; sp++; lexer.advance(); continue; } if (lexer.currtok.type == TOK_IDENTIFIER) { std::string id = lexer.currtok.text; std::vector args; Definition *def = scope->lookup(lexer.currtok.text.c_str()); if (!def) throw spigot_error("unrecognised identifier '%s'", lexer.currtok.text.c_str()); lexer.advance(); if (def->is_function()) { /* * This is a function call, so collect its * arguments. */ if (lexer.currtok.type != TOK_LPAR) { throw spigot_error("expected '(' after function name '%s'", id.c_str()); } lexer.advance(); while (1) { parse_recursive(lexer, sp, scope, curr_fnid); args.push_back(sp->atom); if (lexer.currtok.type == TOK_RPAR) { lexer.advance(); break; } else if (lexer.currtok.type == TOK_COMMA) { lexer.advance(); } else { throw spigot_error("expected ',' or ')'"); } } int expected_nargs; if (!def->check_nargs(args.size(), &expected_nargs)) throw spigot_error("expected %d arguments for function" " '%s', found %d", expected_nargs, id.c_str(), (int)args.size()); } sp->atom = def->make_call(args); sp->opindex = 0; sp++; continue; } if (lexer.currtok.type == TOK_LPAR) { lexer.advance(); parse_recursive(lexer, sp, scope, curr_fnid); sp++; if (lexer.currtok.type != TOK_RPAR) { throw spigot_error("expected ')'"); } lexer.advance(); continue; } if (lexer.currtok.type == TOK_LET) { LetScope ls(scope); lexer.advance(); while (1) { if (lexer.currtok.type != TOK_IDENTIFIER) throw spigot_error("expected identifier in let clause"); std::string name = lexer.currtok.text; lexer.advance(); FnArgScope fas(&ls, *curr_fnid); bool is_function = false; if (lexer.currtok.type == TOK_LPAR) { (*curr_fnid)++; is_function = true; lexer.advance(); while (1) { if (lexer.currtok.type != TOK_IDENTIFIER) throw spigot_error("expected identifier in " "function parameter list"); fas.add_arg(lexer.currtok.text); lexer.advance(); if (lexer.currtok.type == TOK_COMMA) { lexer.advance(); continue; } else if (lexer.currtok.type == TOK_RPAR) { lexer.advance(); break; } else { throw spigot_error("expected ',' or ')' after " "identifier in " "function parameter list"); } } } if (lexer.currtok.type != TOK_EQUALS) throw spigot_error("expected '=' after identifier in" " let clause"); lexer.advance(); parse_recursive(lexer, sp, &fas, curr_fnid); if (is_function) { ls.add_fn(name, fas.nargs, sp[0].atom, fas.fnid); } else { ls.add_var(name, sp[0].atom); } if (lexer.currtok.type == TOK_IN) { lexer.advance(); break; } else if (lexer.currtok.type == TOK_COMMA) { lexer.advance(); continue; } else { throw spigot_error("expected ',' or 'in' after definition" " in let clause"); } } parse_recursive(lexer, sp, &ls, curr_fnid); sp++; continue; } throw spigot_error("unrecognised token"); } } Spigot *expr_parse(const char *expr, GlobalScope *globalscope) { Lexer lexer(expr); struct stack *stack; GlobalScopeWrapper gsw(globalscope); FunctionID curr_fnid = 0; stack = (struct stack *)malloc(strlen(expr) * sizeof(struct stack)); parse_recursive(lexer, stack, &gsw, &curr_fnid); if (lexer.currtok.type != TOK_EOS) { throw spigot_error("expected end of string"); } ASTNode *ast = stack[0].atom; free(stack); /* * In the event of a parse failure, that function would never * even have returned. */ Spigot *ret = ast->evaluate(NULL); delete ast; return ret; } spigot-0.2017-01-15.gdad1bbc6/expr.h0000644000000000000000000000076013025565031013305 0ustar /* * expr.h: header for expr.cpp. */ /* * Class implementing a function that maps variable names to spigots. * Optionally provide one of these to expr_parse as a variable scope. * Naturally, it returns NULL if the variable name is unrecognised. * It's expected to be case sensitive (though, of course, it can * behave insensitively if it prefers). */ struct GlobalScope { virtual Spigot *lookup(const char *varname) = 0; }; Spigot *expr_parse(const char *expr, GlobalScope *scope); spigot-0.2017-01-15.gdad1bbc6/funcs.h0000644000000000000000000001101513025565031013440 0ustar /* * funcs.h: list of functions which return Spigot objects describing * mathematical constants, operators and functions. */ #include /* In consts.cpp: */ Spigot *spigot_pi(); Spigot *spigot_tau(); Spigot *spigot_e(); Spigot *spigot_phi(); Spigot *spigot_apery(); /* In rational.cpp: */ Spigot *spigot_integer(bigint const &n); Spigot *spigot_rational(bigint const &n, bigint const &d); /* In io.cpp: */ Spigot *spigot_basefile(int base, const char *filename, bool exact); Spigot *spigot_cfracfile(const char *filename, bool exact); #ifdef HAVE_FDS Spigot *spigot_basefd(int base, int fd); Spigot *spigot_cfracfd(int fd); #endif /* In gosper.cpp: */ Spigot *spigot_add(Spigot *a, Spigot *b); Spigot *spigot_sub(Spigot *a, Spigot *b); Spigot *spigot_mul(Spigot *a, Spigot *b); Spigot *spigot_square(Spigot *a); Spigot *spigot_invsquare(Spigot *a); Spigot *spigot_quadratic(Spigot *a, int a2, int a1, int a0); Spigot *spigot_div(Spigot *a, Spigot *b); Spigot *spigot_combine(Spigot *a, Spigot *b, int nxy, int nx, int ny, int nc, int dxy, int dx, int dy, int dc); /* In sqrt.cpp: */ Spigot *spigot_sqrt(Spigot *a); Spigot *spigot_hypot(Spigot *a, Spigot *b); Spigot *spigot_cbrt(Spigot *a); /* In unary.cpp: */ Spigot *spigot_reciprocal(Spigot *a); Spigot *spigot_neg(Spigot *a); Spigot *spigot_abs(Spigot *a); Spigot *spigot_mod(Spigot *a, Spigot *b); Spigot *spigot_rem(Spigot *a, Spigot *b); Spigot *spigot_rational_mul(Spigot *a, const bigint &n, const bigint &d); Spigot *spigot_mobius(Spigot *x, const bigint &a, const bigint &b, const bigint &c, const bigint &d); Spigot *spigot_frac(Spigot *a); Spigot *spigot_floor(Spigot *a); Spigot *spigot_ceil(Spigot *a); /* In trig.cpp: */ Spigot *spigot_sin(Spigot *a); Spigot *spigot_cos(Spigot *a); Spigot *spigot_tan(Spigot *a); Spigot *spigot_asin(Spigot *a); Spigot *spigot_acos(Spigot *a); Spigot *spigot_atan(Spigot *a); Spigot *spigot_atan2(Spigot *a, Spigot *b); Spigot *spigot_sind(Spigot *a); Spigot *spigot_cosd(Spigot *a); Spigot *spigot_tand(Spigot *a); Spigot *spigot_asind(Spigot *a); Spigot *spigot_acosd(Spigot *a); Spigot *spigot_atand(Spigot *a); Spigot *spigot_atan2d(Spigot *a, Spigot *b); /* In exp.cpp: */ Spigot *spigot_exp(Spigot *a); Spigot *spigot_log(Spigot *a); Spigot *spigot_exp2(Spigot *a); Spigot *spigot_exp10(Spigot *a); Spigot *spigot_log_to_base(Spigot *input, Spigot *base); Spigot *spigot_log_wrapper(const std::vector &args); Spigot *spigot_log2(Spigot *a); Spigot *spigot_log10(Spigot *a); Spigot *spigot_pow(Spigot *a, Spigot *b); Spigot *spigot_expm1(Spigot *a); Spigot *spigot_log1p(Spigot *a); Spigot *spigot_sinh(Spigot *a); Spigot *spigot_cosh(Spigot *a); Spigot *spigot_tanh(Spigot *a); Spigot *spigot_asinh(Spigot *a); Spigot *spigot_acosh(Spigot *a); Spigot *spigot_atanh(Spigot *a); /* In gamma.cpp: */ Spigot *spigot_lgamma(Spigot *a); Spigot *spigot_gamma(Spigot *a); /* In erf.cpp: */ Spigot *spigot_erf(Spigot *a); Spigot *spigot_erfc(Spigot *a); Spigot *spigot_Phi(Spigot *a); Spigot *spigot_erfinv(Spigot *a); Spigot *spigot_erfcinv(Spigot *a); Spigot *spigot_Phiinv(Spigot *a); /* In lambertw.cpp: */ Spigot *spigot_lambertw_pos(Spigot *x); Spigot *spigot_lambertw_neg(Spigot *x); /* In algebraic.cpp: */ Spigot *spigot_algebraic(const std::vector &aP, bigint nlo, bigint nhi, bigint d); Spigot *spigot_algebraic_wrapper(const std::vector &args); /* In expint.cpp: */ Spigot *spigot_eulergamma(); Spigot *spigot_En(Spigot *n, Spigot *x); Spigot *spigot_Ei(Spigot *x); Spigot *spigot_Ein(Spigot *x); Spigot *spigot_E1(Spigot *x); Spigot *spigot_li(Spigot *x); Spigot *spigot_Li(Spigot *x); /* In trigint.cpp: */ Spigot *spigot_Si(Spigot *x); Spigot *spigot_si(Spigot *x); Spigot *spigot_Cin(Spigot *x); Spigot *spigot_Ci(Spigot *x); Spigot *spigot_UFresnelS(Spigot *x); Spigot *spigot_UFresnelC(Spigot *x); Spigot *spigot_FresnelS(Spigot *x); Spigot *spigot_FresnelC(Spigot *x); /* In zeta.cpp: */ Spigot *spigot_zeta(Spigot *x); /* In monotone.cpp (a utility class used by other function implementations, * not suitable for direct use by clients): */ struct MonotoneConstructor { virtual ~MonotoneConstructor() {} virtual MonotoneConstructor *clone() = 0; virtual Spigot *construct(const bigint &n, const bigint &d) = 0; }; Spigot *spigot_monotone(MonotoneConstructor *f, Spigot *x); Spigot *spigot_monotone_invert(MonotoneConstructor *f, bool increasing, bigint n1, bigint n2, bigint d, Spigot *x); spigot-0.2017-01-15.gdad1bbc6/gamma.cpp0000644000000000000000000006053513025565031013752 0ustar /* * gamma.cpp: Gamma functions (lgamma, tgamma/gamma). */ #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" /* * Computing the gamma, or even log-gamma, function in a spigoty * fashion (i.e. generating more and more digits until told to stop) * is _hard_. * * I know of four basic strategies for gamma computation, which are * listed below. * * The Lanczos approximation has a generally good reputation on the * Internet. It computes the gamma function proper, by means of * taking some terms looking like Stirling's approximation * (sqrt(2*pi), z plus a small constant to the power of z plus * another small constant, exp(-z)) and then multiplying in a * corrective term generated by a series. Supposedly this yields * good convergence right across the plausible positive range of the * function (which, for these purposes, I'm defining as up to about * 180-200). However, for this spigot framework it's completely * unviable because the coefficients of the series that gives the * Lanczos corrective term are irrational: to translate any kind of * series into a sequence of spigot matrices I need the coefficients * to be rational. * * There's another technique which I pulled off Wikipedia that works * on the primary interval [1,2]: you pick a large x, and then sum * an infinite series involving powers of x divided by terms looking * like z(z+1)(z+2)...(z+n). Unfortunately, this series is not exact * - even after the series has been summed to infinity and has * converged, there's _still_ a fixed error term remaining, given by * an integral. One can make that error term arbitrarily small by * choosing x sufficiently large, but again, this is an unviable * approach for spigot computation since we need the series itself * to sum to the _right_ value. * * A third approach is based on the lower and upper _incomplete_ gamma * functions. These are two-argument functions defined as the integral * from 0 to x, and x to infinity (respectively), of the same function * of x,t of which gamma itself is the integral from 0 to inf. In * other words, lower_gamma(x,t) + upper_gamma(x,t) = gamma(t) for any * x. And these functions have convenient expressions as continued * fractions, which are readily translated into spigot form, and so * you can pick the simplest possible x (I like 1) and add them * together. This does well for small input values of gamma, but * becomes painfully slow for large ones. * * That leaves Stirling's approximation. Wikipedia provides a * series, described in detail below, which uses rational * coefficients and converges exactly to the difference between the * well-known Stirling approximation and the exact value of the * log(gamma) function. Just what we wanted, right? * * ... sort of. In fact, even _this_ series isn't usable in the * obvious way, because the form of the terms of the series consists * of coefficients which grow roughly as n!, divided by denominators * which are successively z+1, (z+1)(z+2), (z+1)(z+2)(z+3) and so * on. For large z this starts off converging impressively fast * (unsurprisingly since Stirling's approximation is asymptotic, and * differs less and less from the real value of log(gamma) as z * increases), but if you look a little more closely you find that * the nth term is about n/(z+n) times the (n-1)th term - and while * that starts off looking very attractive for large z and small n, * for any z you will eventually reach large enough n that one term * is really not much smaller than the previous one, whereupon * convergence grinds to a halt. (Wikipedia assures me the series * does converge in theory, but then so does the series * 1-1/2+1/3-1/4... for log(2) and I wouldn't want to sum that one * in practice either.) * * Of course, you can _make_ z as large as you like, if it wasn't: * simply use the recurrence relation gamma(z+1)=z*gamma(z) to * convert the problem of computing lgamma of a small number into * computing lgamma of a large number and subtracting off some nice * simple logarithms. But this still has the irritating feature that * you have to decide _in advance_ what precision you want the * answer to. The precision limit is imposed by the convergence * slowing down in this case, whereas in the other approach above * it's imposed by an unavoidable residual error, but either way, * it's not very practical for spigot computation. * * My solution is to do a similar trick to the one I did in * monotone.cpp, involving periodically junking the computation and * starting it again with better parameters. The plan is: we * construct a spigot class that computes the Stirling correction * series in the obvious way - except that it tracks the ratio * between successive terms of the series, and when that ratio * starts getting too small, it gives up on the computation and * returns something blatantly untrue. It also sets a flag in itself * to indicate that it has done so. * * So we then have a wrapper class which computes the full lgamma of * a rational: it sets up a complicated spigot tree to compute all * the obvious Stirling bits and pieces and add them together, and * one of the additive terms in this tree is an instance of the * above-described class. Then we fetch data from the front of the * whole spigot, and after retrieving each refinement we _check_ to * see whether the object that was summing the series has set its "I * lied" flag. If not, then we can be confident of the term we're * holding, and can safely output it. * * When we find that the "I lied" flag _is_ set, then we know the * coefficient we're holding was derived from false information, so * we throw it away. Then we also throw away the entire spigot tree, * discard the coefficient we're holding, and start again from * scratch by constructing another spigot tree based on lgamma of a * much larger number (hence a more convergent correction series) * plus some log terms to range-reduce back to the thing we actually * wanted lgamma of in the first place. And when that in turn sets its * "I lied" flag, we throw it away and build another one even further * out, and so on. * * If you think this is a really horrible strategy, I wouldn't * disagree. If nothing else, it still converges rather slowly, and * quickly reaches the point where just handling all those * corrective logs is too much effort. But I challenge anyone * reading this to find a better one. * * TL;DR: of the four gamma strategies, two are not viable within * spigot's constraints, one works well for small inputs, and the * other (really disgusting and complicated) works well for large. */ /* ---------------------------------------------------------------------- * Continued-fraction expansions of the lower and upper incomplete * gamma functions. * * Source for the actual expansions: * http://en.wikipedia.org/wiki/Incomplete_gamma_function#Evaluation_formulae * as of 2015-01-10. */ class LowerGammaBaseRational : public Source { /* * The lower incomplete gamma function is computed using the * following continued fraction, in which the even and odd terms * each have an obvious pattern and the two patterns interleave: * * z^s exp(-z) * gamma(s,z) = ------------------------------- * s - sz * --------------------------- * (s+1) + z * ------------------- * (s+2) - (s+1)z * ----------- * (s+3) + 2z * --- * ... * * This class computes the whole of the above formula, with z * fixed at 1, except that the numerator z^s exp(-z) is replaced * with 1; the caller must multiply in that factor afterwards. * (But since with z=1 it works out to just 1/e, that shouldn't be * hard.) */ bigint sn, sd, i; int crState; public: LowerGammaBaseRational(const bigint &asn, const bigint &asd) : sn(asn), sd(asd) { crState = -1; } virtual LowerGammaBaseRational *clone() { return new LowerGammaBaseRational(sn, sd); } bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 0; return false; } bool gen_matrix(bigint *matrix) { crBegin; /* * An initial matrix representing x |-> 1/x. */ matrix[0] = 0; matrix[1] = 1; matrix[2] = 1; matrix[3] = 0; crReturn(false); /* * Then the regular series. */ i = 0; while (1) { matrix[0] = sn + 2*i*sd; matrix[1] = -(sn + i*sd); matrix[2] = sd; matrix[3] = 0; crReturn(false); matrix[0] = sn + (2*i+1) * sd; matrix[1] = (i+1) * sd; matrix[2] = sd; matrix[3] = 0; crReturn(false); ++i; } crEnd; } }; class UpperGammaBaseRational : public Source { /* * The upper incomplete gamma function is computed using the * following continued fraction, in which the even and odd terms * each have an obvious pattern and the two patterns interleave: * * z^s exp(-z) * gamma(s,z) = ------------------- * z + 1-s * --------------- * 1 + 1 * ----------- * z + 2-s * ------- * 1 + 2 * --- * ... * * As above, this class computes this formula with z fixed at 1 * and the top-level numerator (namely 1/e) missing. */ bigint sn, sd, i; int crState; public: UpperGammaBaseRational(const bigint &asn, const bigint &asd) : sn(asn), sd(asd) { crState = -1; } virtual UpperGammaBaseRational *clone() { return new UpperGammaBaseRational(sn, sd); } bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 0; return false; } bool gen_matrix(bigint *matrix) { crBegin; /* * An initial matrix representing x |-> 1/x. */ matrix[0] = 0; matrix[1] = 1; matrix[2] = 1; matrix[3] = 0; crReturn(false); /* * Then the regular series. */ i = 1; while (1) { matrix[0] = sd; matrix[1] = i*sd - sn; matrix[2] = sd; matrix[3] = 0; crReturn(false); matrix[0] = 1; matrix[1] = i; matrix[2] = 1; matrix[3] = 0; crReturn(false); ++i; } crEnd; } }; struct LowerBaseConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new LowerBaseConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new LowerGammaBaseRational(n, d); } }; struct UpperBaseConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new UpperBaseConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new UpperGammaBaseRational(n, d); } }; static Spigot *spigot_gamma_by_incomplete(Spigot *z) { Spigot *lower = spigot_monotone(new LowerBaseConstructor, z->clone()); Spigot *upper = spigot_monotone(new UpperBaseConstructor, z); return spigot_div(spigot_add(lower, upper), spigot_e()); } static Spigot *spigot_lgamma_by_incomplete(Spigot *z) { return spigot_log(spigot_abs(spigot_gamma_by_incomplete(z))); } /* ---------------------------------------------------------------------- * Approach based on Stirling's approximation with periodic restarts. */ class GammaResidualBase : public Source { /* * Stirling's approximation for the gamma function involves some * logarithmic terms, which we can compute using functions * defined elsewhere in this program, plus a series sum which * converges to the error. * * This series, which I'm calling the 'gamma residual function', * converges to * * log(gamma(z)) - [ (z-1/2) log(z) - z + log(2*pi)/2 ] * * and the series itself is * * 1/12 1/12 59/360 29/60 * ----- + ---------- + --------------- + -------------------- + ... * (z+1) (z+1)(z+2) (z+1)(z+2)(z+3) (z+1)(z+2)(z+3)(z+4) * * where those obscure fractions on the top are defined as an * expression involving Stirling numbers of the first kind. * * Stirling numbers of the first kind are defined to be the * coefficients you get if you multiply out * x(x-1)(x-2)...(x-n+1). Formally, we have s(n,k) such that * * s(1,k) = 1 if k==1, 0 otherwise * s(n+1,k) = s(n,k-1) - n s(n,k) for n>=1 * * We're only interested in the absolute value, so we can turn * that minus sign into a plus. Now the coefficient for the nth * term of the above series is defined to be 1/2n times the sum, * over all k with s(n,k) nonzero, of k |s(n,k)| / (k+1)(k+2). * * Hideous, no? But it does converge. * * In order to convert this bizarrely defined series into a * spigot description, we need to be able to bound the * contribution from the remaining terms. To do this we observe * that the definition of the Stirling numbers tells us that the * sum of |s(n,k)| over all k for a given n is equal to n!; so * the corresponding series coefficient is bounded by the * smallest value of k/(k+1)(k+2) - which is given by k=1 and * hence is 1/6 - times n!. * * So we need to arrange that we multiply the interval size, in * each successive function in the spigot stream, by n * (reflecting the increasing size of the coefficient) and * divide by (z+n). This will clearly decrease it every time, so * we can reliably bound the amount which the series has left to * run. * * So our series is now rewritten (with the actual coefficient * values mercifully wrapped up as c_n, so that c_1=c_2=1/12, * c_3=59/360, c_4=29/60 and so on) as * * 1 c_1 2 c_2 3 c_3 4 * --- ( --- + --- ( --- + --- ( --- + --- ( ... ) ) ) ) * z+1 1! z+2 2! z+3 3! z+4 * * and from this format we can readily translate it into a * stream of spigot matrices. If c_n/n! is represented as the * rational a_n/b_n, and z itself is represented as p/q (I would * normally write 'n/d' for a rational here but I've already * used n in the above discussion), our matrices are * * ( b_1.q a_1.q ) ( 2.b_2.q 2.a_2.q ) ( 3.b_3.q 3.a_3.q ) * ( 0 b_1.(p+q) ) ( 0 b_2.(p+2q) ) ( 0 3.b_3.(p+3q) ) * * and so on. */ struct listnode { bigint s; struct listnode *next; listnode() { next = NULL; } listnode(bigint as) : s(as) { next = NULL; } }; bigint p, q, pnq; bigint nfact, nplus2fact; unsigned n, k; bigint coeff_num, coeff_denom; listnode *shead, *node; int crState; bool I_lied; public: GammaResidualBase(const bigint &ap, const bigint &aq) : p(ap), q(aq), pnq(ap), nfact(1), nplus2fact(6), n(1), shead(NULL) { crState = -1; shead = new listnode(1); I_lied = false; } virtual ~GammaResidualBase() { while (shead) { listnode *tmp = shead->next; delete shead; shead = tmp; } } virtual GammaResidualBase *clone() { return new GammaResidualBase(p, q); } bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 2; return true; } bool gen_matrix(bigint *matrix) { crBegin; while (1) { /* * Compute the coefficient c_n/n!. * * Our Stirling numbers are stored backwards, so that * shead represents s(n,n), shead->next is s(n,n-1) and * the final element in the list is s(n,1). This is just * because it happens to be more convenient to update * the list that way round. */ k = n; coeff_num = 0; for (node = shead; node; node = node->next, k--) coeff_num += nplus2fact / (k+1) / (k+2) * k * node->s; coeff_denom = nfact * nplus2fact * (2*n); { bigint a = coeff_num, b = coeff_denom; while (1) { if (b == 0) { coeff_num /= a; coeff_denom /= a; break; } a %= b; if (a == 0) { coeff_num /= b; coeff_denom /= b; break; } b %= a; } } //printf("[%d: ", n); //bigint_print(coeff_num);printf("/");bigint_print(coeff_denom);printf(" "); /* * Return a matrix. */ pnq += q; matrix[0] = coeff_denom * q * n; matrix[1] = coeff_num * q * n; matrix[2] = 0; matrix[3] = coeff_denom * pnq; #if 1 if (4*matrix[0] > 3*matrix[3]) { /* * Give up and return a constant matrix instead. */ I_lied = true; matrix[0] = matrix[2] = 0; matrix[1] = matrix[3] = 1; crReturn(false); } #else printf("<"); bigint_print(100*matrix[0]/matrix[3]); printf("/100>"); #endif //bigint_print(matrix[0]);printf(" "); //bigint_print(matrix[1]);printf(" "); //bigint_print(matrix[2]);printf(" "); //bigint_print(matrix[3]);printf("]\n"); crReturn(false); /* * Transform our linked list of Stirling numbers into * the next row. */ node = new listnode(1); node->next = shead; shead = node; while ((node = node->next) != NULL) { node->s *= n; if (node->next) node->s += node->next->s; } ++n; nfact *= n; nplus2fact *= (n+2); } crEnd; } friend class GammaWrapper; /* so it can get at the I_lied flag */ }; class GammaWrapper : public BinaryIntervalSource { bigint p, q; bigint n, nextn, currp, constn, constd; GammaResidualBase *b; BracketingGenerator *bg; bool sret; unsigned outbits; int crState; public: GammaWrapper(const bigint &ap, const bigint &aq) : p(ap), q(aq), constn(1), constd(1), b(NULL), bg(NULL) { crState = -1; n = p / q; dprint("hello GammaWrapper %b/%b", &p, &q); } virtual ~GammaWrapper() { if (bg) delete bg; } virtual GammaWrapper *clone() { return new GammaWrapper(p, q); } virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { crBegin; if (n < 80) nextn = 80; else nextn = n; outbits = 0; currp = p; while (1) { while (n < nextn) { constn *= q; constd *= currp; currp += q; ++n; } /* * Instantiate the gamma residual function for currp/q. */ dprint("initialising for %b/%b", &currp, &q); b = new GammaResidualBase(currp, q); /* * Now construct lgamma(z=p/q) out of that. */ { Spigot *z = spigot_rational(currp, q); /* (z-1/2) log z */ Spigot *t1 = spigot_mul(spigot_sub(z->clone(), spigot_rational(1,2)), spigot_log(spigot_abs(z->clone()))); /* minus z */ Spigot *t12 = spigot_sub(t1, z); /* plus log(2*pi)/2 */ Spigot *t3 = spigot_rational_mul (spigot_log(spigot_rational_mul(spigot_pi(), 2, 1)), 1, 2); /* plus log of all the stuff between p/q and currp/q */ Spigot *t34 = t3; if (constn != 1 || constd != 1) { Spigot *t4 = spigot_log (spigot_abs(spigot_rational(constn, constd))); t34 = spigot_add(t34, t4); }; /* plus the residual function. */ Spigot *t1234 = spigot_add(t12, t34); Spigot *s = spigot_add(t1234, b); bg = new BracketingGenerator(s); bg->set_denominator_lower_bound_shift(outbits); } while (1) { bg->get_bracket_shift(ret_lo, ret_hi, ret_bits); dprint("got bracket (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); if (b->I_lied) { dprint("but it was a lie!"); break; } outbits = *ret_bits; crReturnV; } delete bg; bg = NULL; nextn = nextn * 3U / 2U; } crEnd; } }; struct GammaConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new GammaConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new GammaWrapper(n, d); } }; static Spigot *spigot_lgamma_by_stirling(Spigot *z) { return spigot_monotone(new GammaConstructor, z); } static Spigot *spigot_gamma_by_stirling(Spigot *z) { bigint n, d; /* * lgamma computes log(|gamma|), so taking exp of that will always * be positive. So check whether this output ought to be negative. * gamma(x) < 0 if floor(x) is a negative odd integer. */ bigint i; { CfracGenerator cfg(z->clone()); cfg.get_term(&i); } bool negate = (i < 0 && (i % (unsigned)2) != 0); Spigot *absret = spigot_exp(spigot_lgamma_by_stirling(z)); if (negate) return spigot_neg(absret); else return absret; } /* ---------------------------------------------------------------------- * Top-level wrappers which handle special cases and then decide which * of the other two strategies to hand off to. * * In a comparative speed test (evaluating gamma to 40 sig figs for * lots of different inputs using both methods) I found that the * cross-over point was about |z|=35, for both positive and negative * inputs. */ static Spigot *spigot_gamma_special_cases(Spigot *z) { bigint n, d; if (z->is_rational(&n, &d) && d == 1) { if (n <= 0) { throw spigot_error("gamma of non-positive integer"); } /* * Special case for gamma of a positive integer: just * compute its factorial in the obvious way. */ bigint ret = 1, k = 1; while (k < n) { ret *= k; ++k; } return spigot_integer(ret); } /* No special case was possible */ return NULL; } Spigot *spigot_gamma(Spigot *z) { Spigot *ret; if ((ret = spigot_gamma_special_cases(z)) != NULL) return ret; bool small; { StaticGenerator test(z->clone()); bigint approx = test.get_approximate_approximant(2); small = (bigint_abs(approx) < 2*35); } if (small) return spigot_gamma_by_incomplete(z); else return spigot_gamma_by_stirling(z); } Spigot *spigot_lgamma(Spigot *z) { Spigot *ret; if ((ret = spigot_gamma_special_cases(z)) != NULL) return spigot_log(ret); bool small; { StaticGenerator test(z->clone()); bigint approx = test.get_approximate_approximant(2); small = (bigint_abs(approx) < 2*35); } if (small) return spigot_lgamma_by_incomplete(z); else return spigot_lgamma_by_stirling(z); } spigot-0.2017-01-15.gdad1bbc6/holefiller.h0000644000000000000000000002674413025565031014466 0ustar /* * holefiller.h: abstract wrapper class that fills in removable * singularities in functions. */ /* * This class is a wrapper that sits in front of another spigot, * somewhat like enforce.cpp's Enforcer. You provide it with a value * x, which might be one of a finite set of special x values; the * basic semantics are that if x does turn out to be one of those * values then there's a corresponding special y value for each one * that should be returned, and if x turns out to be anything else * then there's a 'general y' that should be returned instead. * * You provide the special x and y values and the general y value by * means of subclassing HoleFiller itself; they're all given by * virtual methods. * * The point of this is for situations in which you have a means of * implementing _most_ of a continuous function f, apart from a small * number of 'removable singularities' - points where the function * really is continuous, but for some annoying reason, you have to use * a different evaluation strategy to compute its value there. But you * can't just write the obvious code (if x == this then return that, * else do the general thing), because that causes an exactness hazard * in the case where x _turns out_ to be the special value but that * wasn't obvious up front. * * Enter HoleFiller. HoleFiller will keep track of which special * value(s) the input x has not yet been proved to miss, and it will * return intervals narrowing about the corresponding special y value * for as long as there's still one of those in play. But if it then * turns out that x is not a special value after all, it'll switch to * returning intervals about whatever the general fallback evaluator * returns. * * In order to do this correctly, it must not return an interval in * the first phase which rules out any value that f(x) might still * turn out to have. So another thing your subclass must provide is a * function which takes as input the currently known interval about * the special value to which we have restricted x, and returns an * interval about the corresponding special y which also contains any * other value that f(x) might take on the input interval. That way, * if we later abandon the special value, we won't have returned any * interval that excludes the real answer to f(x). */ class HoleFiller : public BinaryIntervalSource { std::vector bgs; BracketingGenerator *xbg, *ybg; int specindex; int crState; protected: Spigot *x; public: HoleFiller(Spigot *ax) : x(ax), xbg(NULL), ybg(NULL) { crState = -1; dprint("hello HoleFiller"); } ~HoleFiller() { for (int i = 0; i < (int)bgs.size(); i++) if (bgs[i]) delete bgs[i]; if (xbg) delete xbg; if (ybg) delete ybg; delete x; } virtual Spigot *replace() { /* * See if we can _immediately_ detect exact equality to a * special-case input. If so, delete ourself and return that y * value instead. */ Spigot *a, *diff; bigint n, d; for (int i = 0; (a = xspecial(i)) != NULL; i++) { diff = spigot_sub(x->clone(), a); if (diff->is_rational(&n, &d) && n == 0) { Spigot *ret = yspecial(i); delete diff; delete this; return ret; } } return this; } virtual Spigot *xspecial(int) = 0; virtual Spigot *yspecial(int) = 0; virtual Spigot *ygeneral(Spigot *) = 0; virtual bool combine(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits, const bigint &xnlo, const bigint &xnhi, const bigint &ynlo, const bigint &ynhi, unsigned dbits, int /*index*/) { /* * This function may be reimplemented by a subclass. Its job * is: given an interval (xnlo,xnhi)/dbits saying how close x * is to the (index)th special input, and an interval * (ynlo,ynhi)/dbits bracketing the corresponding special * output, return an interval that we can be sure the _real_ * output is in. * * We provide a default implementation here based on the * assumption that the function is Lipschitz-continuous within * a range of +-1/2 about the target, because that's really * easy and quite a weak constraint. If you felt like * squeezing more performance out then you could reimplement * this to be more aggressive; if you find yourself * implementing a function which is really steep at the * special point (perhaps even infinitely steep, e.g. if it * approaches the point in a sqrt-like way) then you _must_ * reimplement this to be more conservative. * * Return false if we can't get any information at all yet. */ bigint deviation = -xnlo; if (deviation < xnhi) deviation = xnhi; if (dbits < 1 || (deviation >> (dbits-1)) != 0) return false; *ret_bits = dbits; *ret_lo = ynlo - deviation; *ret_hi = ynhi + deviation; return true; } virtual HoleFiller *clone() = 0; virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { crBegin; /* * Set up a BracketingGenerator for x-a, for each special * input a. */ { Spigot *a; for (int i = 0; (a = xspecial(i)) != NULL; i++) bgs.push_back(new BracketingGenerator (spigot_sub(x->clone(), a))); } /* * Loop round and round, fetching more information about each * of those, until we narrow to 0 or 1 of them potentially * zero. * * This is a setup phase in which we don't return to the * caller at all, because if the input interval doesn't even * narrow to less than the distance between our special values * (if we have more than one of them), then no useful output * was going to be generated anyway. */ while (1) { int n = 0; // count the still-active bgs bigint nlo, nhi; unsigned dbits; for (int i = 0; i < (int)bgs.size(); i++) { bgs[i]->get_bracket_shift(&nlo, &nhi, &dbits); dprint("input bracket for specials[%d]: (%b,%b) / 2^%d", i, &nlo, &nhi, (int)dbits); if (nlo > 0 || nhi < 0) { /* * We know the sign of x-a, i.e. we know it's * nonzero. Discard this special value. */ delete bgs[i]; bgs[i] = NULL; } else if (nlo == 0 && nhi == 0) { /* * We know x-a is _exactly_ zero, i.e. this is * precisely a special value of the function. */ ybg = new BracketingGenerator(yspecial(i)); goto passthrough; // multilevel break (sorry) } else { /* * This one is still a possible. */ n++; } } if (n == 0) { /* * No remaining possibilities for special values, so * we just switch to constructing the general return * value. */ ybg = new BracketingGenerator(ygeneral(x->clone())); goto passthrough; } if (n == 1) { /* * There's one remaining possibility for a special * value. This is the interesting case. */ for (int i = 0; i < (int)bgs.size(); i++) { if (bgs[i]) { specindex = i; xbg = bgs[i]; bgs[i] = NULL; ybg = new BracketingGenerator(yspecial(i)); goto narrowing; } } } } narrowing: /* * Now we've got a single potential special value. Narrow an * interval about the SV output for as long as we can't prove * the input is not the SV input. */ while (true) { { bigint xnlo, xnhi, ynlo, ynhi; unsigned xdbits, ydbits; xbg->get_bracket_shift(&xnlo, &xnhi, &xdbits); dprint("narrowing input bracket: (%b,%b) / 2^%d", &xnlo, &xnhi, (int)xdbits); if (xnlo > 0 || xnhi < 0) { /* * Turns out we don't have the special value after * all; reinitialise ybg with the general value, and * go on to the passthrough phase. */ delete xbg; xbg = NULL; delete ybg; ybg = new BracketingGenerator(ygeneral(x->clone())); goto passthrough; } else if (xnlo == 0 && xnhi == 0) { /* * Turns out we have _exactly_ the special value. */ delete xbg; xbg = NULL; goto passthrough; } ybg->get_bracket_shift(&ynlo, &ynhi, &ydbits); dprint("narrowing output bracket: (%b,%b) / 2^%d", &ynlo, &ynhi, (int)ydbits); /* * Normalise the input and output brackets to the same * denominator, which we leave in ydbits. */ if (ydbits < xdbits) { ynlo <<= xdbits-ydbits; ynhi <<= xdbits-ydbits; ydbits = xdbits; } else if (ydbits > xdbits) { xnlo <<= ydbits-xdbits; xnhi <<= ydbits-xdbits; } dprint("narrowing normalised brackets: input (%b,%b) / 2^%d," " output (%b,%b) / 2^%d", &xnlo, &xnhi, (int)ydbits, // (intentionally not xdbits) &ynlo, &ynhi, (int)ydbits); /* * And let our subclass figure out how closely it can * afford to narrow the resulting interval. */ combine(ret_lo, ret_hi, ret_bits, xnlo, xnhi, ynlo, ynhi, ydbits, specindex); } dprint("combined output bracket: (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); crReturnV; } passthrough: /* * The simple part: now we know what kind of output we're * generating, just pass through the results of an ordinary * BracketingGenerator. */ // FIXME: here we'd really like to pass through an exact // termination, if we're a rational! while (1) { ybg->get_bracket_shift(ret_lo, ret_hi, ret_bits); dprint("passthrough bracket: (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); crReturnV; } crEnd; } }; spigot-0.2017-01-15.gdad1bbc6/io.cpp0000644000000000000000000002273312514752325013303 0ustar /* * io.cpp: Spigot definitions which provide numbers read from input * files or fds. * * (The fds part of that is conditional on running on an OS that * supports fds at all, to ease porting.) */ #include #include #include #include #include #include #include #include "spigot.h" #include "funcs.h" #include "error.h" struct FileBlock { unsigned char data[16384]; unsigned data_used; struct FileBlock *next; FileBlock() { data_used = 0; next = NULL; } }; struct FileReaderPos { FileBlock *current; unsigned currpos; }; struct FileReader { std::string filename; FILE *fp; FileBlock *head; FileReader(const std::string &afilename, FILE *afp) : filename(afilename) { fp = afp; head = new FileBlock; } ~FileReader() { while (head) { FileBlock *tmp = head->next; delete head; head = tmp; } if (fp) fclose(fp); } void newpos(FileReaderPos *pos) { pos->current = head; pos->currpos = 0; } int getch(FileReaderPos *pos) { /* * The simple cases of just returning some existing data, or */ if (pos->current && pos->currpos == pos->current->data_used && pos->current->next) pos->current = pos->current->next; if (pos->current && pos->currpos < pos->current->data_used) return pos->current->data[pos->currpos++]; /* * If we get here, then we know that pos->current points to * a block from which we've already returned all the data, * and that there is no pointer to a next block. So now we * must read from the file, if it's still open. */ if (!fp) return EOF; int c = fgetc(fp); if (c == EOF) { fclose(fp); fp = NULL; return EOF; } else { if (pos->current->data_used < sizeof(pos->current->data)) { pos->current->data[pos->current->data_used++] = c; pos->currpos++; } else { pos->current->next = new FileBlock; pos->current = pos->current->next; pos->current->data[0] = c; pos->currpos = pos->current->data_used = 1; } return c; } } }; class CfracFile : public CfracSource { FileReader *file; FileReaderPos pos; bool first_term; bool exact; public: CfracFile(FileReader *afile, bool aexact) { file = afile; file->newpos(&pos); first_term = true; exact = aexact; } virtual CfracFile *clone() { return new CfracFile(file, exact); } virtual bool gen_term(bigint *term) { int c, prevc; /* * We keep track of the character we saw just _before_ the * first digit, in case it was a minus sign (and we're in a * mood to recognise one - they're only supported in the * very first term of the continued fraction). */ c = EOF; do { prevc = c; c = file->getch(&pos); } while (c != EOF && !isdigit(c)); *term = 0; while (c != EOF && isdigit(c)) { *term = *term * 10 + (c - '0'); c = file->getch(&pos); } if (c == EOF) { if (exact) { /* * In exact mode, EOF is interpreted as the * termination of the continued fraction expansion, * i.e. we actually have a rational. */ return false; } else { /* * If we're in non-exact mode, throw the * out-of-precision exception. */ throw spigot_eof(file->filename.c_str()); } } if (prevc == '-' && first_term) *term = -*term; first_term = false; return true; } }; class BaseFile : public BaseSource { FileReader *file; FileReaderPos pos; bool first_digit; int base; bool exact; public: BaseFile(int abase, FileReader *afile, bool aexact) : BaseSource(abase, true), base(abase) { file = afile; file->newpos(&pos); first_digit = true; exact = aexact; assert(base > 0); } virtual BaseFile *clone() { return new BaseFile(base, file, exact); } int digitval(int c) { if (isdigit(c) && (c - '0') < base) { return c - '0'; } else if (c >= 'A' && c <= 'Z' && (c - 'A' + 10) < base) { return c - 'A' + 10; } else if (c >= 'a' && c <= 'z' && (c - 'a' + 10) < base) { return c - 'a' + 10; } else return -1; } virtual bool gen_digit(bigint *digit) { int c, dv; if (first_digit) { /* * Read the integer part of the number, complete with a * leading minus sign if desired. */ bool negate = false, seen_digit = false; *digit = 0; do { c = file->getch(&pos); if (c == '-' && !seen_digit) { negate = true; } else if ((dv = digitval(c)) >= 0) { seen_digit = true; *digit *= base; *digit += dv; } } while (c != '.' && c != EOF); if (negate) *digit = -1-*digit; first_digit = false; return true; } else { /* * Read a single digit from the fraction part of the * number. */ do { c = file->getch(&pos); if (c == EOF) { if (exact) { /* * In exact mode, EOF is interpreted as the * termination of the base expansion, i.e. we * actually have a rational. */ return false; } else { /* * If we're in non-exact mode, throw the * out-of-precision exception. */ throw spigot_eof(file->filename.c_str()); } } } while ((dv = digitval(c)) < 0); *digit = dv; return true; } } }; Spigot *spigot_basefile(int base, const char *filename, bool exact) { FILE *fp = fopen(filename, "r"); if (!fp) { throw spigot_error("unable to open '%s': %s", filename, strerror(errno)); } return new BaseFile(base, new FileReader(filename, fp), exact); } Spigot *spigot_cfracfile(const char *filename, bool exact) { FILE *fp = fopen(filename, "r"); if (!fp) { throw spigot_error("unable to open '%s': %s", filename, strerror(errno)); } return new CfracFile(new FileReader(filename, fp), exact); } #ifdef HAVE_FDS #include #include #include bool set_term_modes = false; struct fdrec { int fd; bool do_term_modes; struct termios old; FileReader *fr; }; fdrec *fds = NULL; int nfds = 0, fdsize = 0; FileReader *register_fd(int fd, bool do_term_modes) { for (int i = 0; i < nfds; i++) if (fds[i].fd == fd) return fds[i].fr; FILE *fp = fdopen(fd, "r"); if (!fp) { throw spigot_error("unable to access fd %d for reading: %s", fd, strerror(errno)); } char name[64]; #if defined _MSC_VER #define snprintf _snprintf #endif snprintf(name, sizeof(name), "", fd); FileReader *fr = new FileReader(name, fp); if (nfds >= fdsize) { fdsize = nfds * 3 / 2 + 16; fds = (fdrec *)realloc(fds, fdsize * sizeof(fdrec)); } fds[nfds].fd = fd; fds[nfds].do_term_modes = do_term_modes; fds[nfds].fr = fr; if (do_term_modes) tcgetattr(fds[nfds].fd, &fds[nfds].old); nfds++; return fr; } void cleanup() { if (set_term_modes) { for (int i = 0; i < nfds; i++) if (fds[i].do_term_modes) tcsetattr(fds[i].fd, TCSANOW, &fds[i].old); } } void sighandler(int sig) { cleanup(); signal(sig, SIG_DFL); raise(sig); } void begin_fd_input() { if (!set_term_modes) return; signal(SIGINT, sighandler); signal(SIGTERM, sighandler); atexit(cleanup); for (int i = 0; i < nfds; i++) if (fds[i].do_term_modes) { struct termios newattrs = fds[i].old; /* structure copy */ newattrs.c_lflag &= ~ICANON; newattrs.c_lflag &= ~ECHO; tcsetattr(fds[i].fd, TCSANOW, &newattrs); } } /* * The fd functions always pass exact=true, because the whole point of * using an fd in place of a file is that fds have a way to actually * _distinguish_ 'expansion has terminated' from 'more data not yet * available', namely EOF and blocking respectively. */ Spigot *spigot_basefd(int base, int fd) { FileReader *fr = register_fd(fd, true); return new BaseFile(base, fr, true); } Spigot *spigot_cfracfd(int fd) { FileReader *fr = register_fd(fd, true); return new CfracFile(fr, true); } #endif spigot-0.2017-01-15.gdad1bbc6/lambertw.cpp0000644000000000000000000002126513025565031014502 0ustar /* * Lambert W function, i.e. the inverse of f(t) = t e^t. * * This function allows you to solve several related kinds of * equation, by composing it with assorted operations: * * If t e^t = x then t = W(x) * If e^t / t = x then t = -W(-1/x) * If e^t + t = x then t = log(W(exp(x))) * If e^t - t = x then t = log(-W(-exp(-x))) * If t log t = x then t = exp(W(x)) * If t / log t = x then t = exp(-W(-1/x)) * If t + log t = x then t = W(exp(x)) * If t - log t = x then t = -W(-exp(-x)) * If t^t = x then t = exp(W(log x)) * If x^t = t then t = exp(-W(-log(x))) * * Since W has two branches (both starting at W(-1/e) = -1, but one * heads upwards and is valid for all x >= -1/e while the other heads * downwards and is only valid for -1/e <= x < 0), most of the above * types of equation can have different numbers of solutions depending * on whether the value passed to W is less than -1/e, greater than 0, * or in between the two. */ #include #include #include #include "spigot.h" #include "error.h" #include "funcs.h" struct XexConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new XexConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return spigot_mul(spigot_rational(n, d), spigot_exp(spigot_rational(n, d))); } }; Spigot *spigot_lambertw_pos(Spigot *x) { XexConstructor xex; /* * This is the positive branch of W, i.e. the one that starts from * W(-1/e) = -1 and goes upwards through the origin into the * positive x,y quadrant. * * We need to find two numbers such that f(lo) < x < f(hi). Since * this branch of W is basically log-like, there's no need to * worry too much about getting these well centred - it's not as * if they're going to be out by tens of orders of magnitude so * that we waste a lot of time at the start. * * Our upper bound will simply be (a good enough approximation to) * log(1+x), which is >= W(x) everywhere, with equality when x=0 * (which we therefore have to spot in advance and deal with). * * (Proof that log(1+x) >= W(x): taking f of both sides (which is * monotonic in the relevant range) we see that this is true iff * (1+x)log(1+x) - x >= 0, and the derivative of the LHS is just * log(1+x), which is negative for x<0 and positive for x>0, so * there is one global minimum at x=0 which is the equality case.) * * And our lower bound, more trivially still, is just -1. */ bigint n, d; if (x->is_rational(&n, &d) && n == 0) return spigot_integer(0); /* * Check the lower bound. * * We don't want to do this _up front_ with complete precision, * because it would be an exactness hazard. W is actually defined * at -1/e itself, so we'd like an expression of the form W(-1/e) * + stuff to evaluate the same as (-1) + stuff without hanging at * startup due to the bounds check in W. * * So we do a quick check for really obvious cases using an * approximant, and then defer the precise bounds check using * spigot_enforce. (Hence it might come up part way through * output, if for example the input to W is a stream of digits * from an input file which _turns out_ part way along to be just * less than -1/e.) */ { StaticGenerator test(spigot_sub(xex.construct(-1, 1), x->clone())); bigint approx = test.get_approximate_approximant(64); if (approx > 1) // immediately obviously out of range throw spigot_error("W of less than -1/e"); else if (approx > -2) // _might_ be out of range x = spigot_enforce(x, ENFORCE_GE, xex.construct(-1, 1), spigot_error("W of less than -1/e")); } bigint nhi, dhi; { BracketingGenerator boundgen(spigot_log1p(x->clone())); while (1) { bigint n1, n2; boundgen.get_bracket(&n1, &n2, &dhi); // dprint("upper bound: trying (%b, %b) / %b", &n1, &n2, &dhi); int s = parallel_sign_test (spigot_sub(xex.construct(n1, dhi), x->clone()), spigot_sub(xex.construct(n2, dhi), x->clone())); // dprint("s = %d", s); if (s == +1) { nhi = n1; break; } else if (s == +2) { nhi = n2; break; } } } // dprint("upper bound %b/%b", &nhi, &dhi); Spigot *ret = spigot_monotone_invert(new XexConstructor, true, -dhi, nhi, dhi, x); return ret; } Spigot *spigot_lambertw_neg(Spigot *x) { XexConstructor xex; /* * This is the negative branch of W, i.e. the one that starts from * W(-1/e) = -1, heads downwards, whizzes out to -infinity as x * approaches zero, and has no value at all for positive x. */ /* * Bounds checking for sensible error messages. */ { StaticGenerator test(x->clone()); if (test.get_sign() >= 0) throw spigot_error("Wn of a non-negative number"); } /* * Lower-bound check, same as the version above. */ { StaticGenerator test(spigot_sub(xex.construct(-1, 1), x->clone())); bigint approx = test.get_approximate_approximant(64); if (approx > 1) // immediately obviously out of range throw spigot_error("Wn of less than -1/e"); else if (approx > -2) // _might_ be out of range x = spigot_enforce(x, ENFORCE_GE, xex.construct(-1, 1), spigot_error("Wn of less than -1/e")); } /* * Find some bounds. * * We have -1/e <= x < 0 (probably - unless the deferred bounds * check above is going to kick in later). We seek t = Wn(x) < -1, * or rather, we seek two values of t such that f(t_0) < x and * f(t_1) > x. * * We have f(log(-x)) = log(-x) e^(log(-x)) = x (-log(-x)) < x * (since (-log(-x)) >= 1 given the range of x, and multiplying * the negative number x by more than 1 makes it smaller still). * So t_0 = log(-x) will do, although we'll just use t_0 = -1 if * it looks dangerously close to that. * * Also have f(2 log(-x)) = 2 log(-x) e^(2 log(-x)) = 2x^2 log(-x) * = x (2x log -x), and since 0 < 2x log -x < 2/e given the range * of x, this is multiplying the negative x by a positive number * _less_ than 1, which makes it larger. So t_1 = 2 log(-x) will * also do, and we've got only a factor of two separating our * bounds. */ // FIXME: we'll have to special-case one of these bounds to -1, I // think, to avoid a hazard when trying to find something between // it and -1/e. bigint nhi, dhi; { BracketingGenerator boundgen (spigot_rational_mul(spigot_log(spigot_neg(x->clone())), 2, 1)); while (1) { bigint n1, n2; boundgen.get_bracket(&n1, &n2, &dhi); // dprint("upper bound: trying (%b, %b) / %b", &n1, &n2, &dhi); int s = parallel_sign_test (spigot_sub(xex.construct(n1, dhi), x->clone()), spigot_sub(xex.construct(n2, dhi), x->clone())); // dprint("s = %d", s); if (s == +1) { nhi = n1; break; } else if (s == +2) { nhi = n2; break; } } } bigint nlo, dlo; { BracketingGenerator boundgen (spigot_log(spigot_neg(x->clone()))); while (1) { bigint n1, n2; boundgen.get_bracket(&n1, &n2, &dlo); if (n1 > -2*dlo) { // dprint("lower bound: got (%b, %b) / %b; just use -1", // &n1, &n2, &dlo); nlo = -1; dlo = +1; break; } // dprint("lower bound: trying (%b, %b) / %b", &n1, &n2, &dlo); int s = parallel_sign_test (spigot_sub(xex.construct(n1, dlo), x->clone()), spigot_sub(xex.construct(n2, dlo), x->clone())); // dprint("s = %d", s); if (s == -1) { nlo = n1; break; } else if (s == -2) { nlo = n2; break; } } } // dprint("bounds (%b/%b, %b/%b)", &nlo, &dlo, &nhi, &dhi); Spigot *ret = spigot_monotone_invert(new XexConstructor, false, nhi*dlo, nlo*dhi, dhi*dlo, x); return ret; } spigot-0.2017-01-15.gdad1bbc6/main.cpp0000644000000000000000000006507113036751047013622 0ustar /* * main.cpp: Main program for spigot. */ #include #include #include #include #include #include "spigot.h" #include "funcs.h" #include "expr.h" #include "error.h" #include "baseout.h" #include "cfracout.h" #include "version.h" #ifdef _WIN32 # include #else # if HAVE_UNISTD_H # include # if HAVE_NCURSES_H # include # include # elif HAVE_CURSES_H # include # include # else # define TENTATIVE_OUTPUT_DISABLED # endif # else # define TENTATIVE_OUTPUT_DISABLED # endif #endif void check_stdout() { fflush(stdout); if (ferror(stdout)) exit(1); } #ifndef TENTATIVE_OUTPUT_DISABLED static std::string current_tentative_output; static bool tentative_output_enabled = false; #ifdef _WIN32 /* * Windows implementation of colour control for tentative output, via * SetConsoleTextAttribute. */ static HANDLE console_handle; static WORD normal_attributes, tentative_attributes; void setup_tentative_output(bool required) { console_handle = GetStdHandle(STD_OUTPUT_HANDLE); if (console_handle == INVALID_HANDLE_VALUE) { if (required) fprintf(stderr, "spigot: unable to produce tentative output: GetStdHandle returned error code %d\n", (int)GetLastError()); return; } CONSOLE_SCREEN_BUFFER_INFO info; if (!GetConsoleScreenBufferInfo(console_handle, &info)) { if (required) fprintf(stderr, "spigot: unable to produce tentative output: GetConsoleScreenBufferInfo returned error code %d\n", (int)GetLastError()); return; } normal_attributes = info.wAttributes; tentative_attributes = normal_attributes; tentative_attributes &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY); tentative_attributes |= FOREGROUND_RED; tentative_output_enabled = true; } void backspace() { fputc('\b', stdout); } void print_in_tentative_colour(const char *s) { SetConsoleTextAttribute(console_handle, tentative_attributes); fputs(s, stdout); fflush(stdout); SetConsoleTextAttribute(console_handle, normal_attributes); } #else /* _WIN32 */ /* * Unix implementation of colour control for tentative output, via * ncurses/terminfo to send control sequences to the tty. */ static std::string term_backspace, term_tentative_colour, term_normal_colour; void setup_tentative_output(bool required) { char opname[20]; char *tstr; if (setupterm(NULL, 1, NULL) != OK) return; /* * OS X's clang gives warnings about passing a string literal to * tigetstr, because tigetstr is defined to take a char * rather * than the more sensible const char *. * * Of course I don't really believe that tigetstr would _modify_ * that string, so I could just cast my string literals to char * * to prevent the warning. But casts are ugly anyway, and TBH I * don't trust C compilers not to sooner or later start giving the * same warning even with the cast. So let's do this with a real, * writable char string. */ strcpy(opname, "cub1"); tstr = tigetstr(opname); if (tstr == NULL || tstr == (char *)-1) { if (required) fprintf(stderr, "spigot: unable to produce tentative output: terminal description does not provide 'cub1' backspace operation\n"); return; } term_backspace = tstr; term_backspace += " "; term_backspace += tstr; strcpy(opname, "sgr0"); tstr = tigetstr(opname); if (tstr == NULL || tstr == (char *)-1) { if (required) fprintf(stderr, "spigot: unable to produce tentative output: terminal description does not provide 'sgr0' operation to reset colour settings\n"); return; } term_normal_colour = tstr; strcpy(opname, "setaf"); tstr = tigetstr(opname); if (tstr == NULL || tstr == (char *)-1) { if (required) fprintf(stderr, "spigot: unable to produce tentative output: terminal description does not provide 'setaf' operation to set text colour\n"); return; } #if HAVE_TIPARM // Use the nice new tiparm if possible tstr = tiparm(tstr, 1); #else // Old versions of tparm were not variadic, so pass the right // number of args tstr = tparm(tstr, 1, 0, 0, 0, 0, 0, 0, 0, 0); #endif term_tentative_colour = tstr; tentative_output_enabled = true; } void backspace() { fputs(term_backspace.c_str(), stdout); } void print_in_tentative_colour(const char *s) { fputs(term_tentative_colour.c_str(), stdout); fputs(s, stdout); fputs(term_normal_colour.c_str(), stdout); } #endif /* _WIN32 */ void unwrite_tentative_output() { if (tentative_output_enabled) { for (size_t i = 0; i < current_tentative_output.size(); ++i) backspace(); check_stdout(); current_tentative_output = ""; } } void write_tentative_output(const std::string &s) { assert(s.size() > 0); if (tentative_output_enabled) { if (s == current_tentative_output) return; unwrite_tentative_output(); print_in_tentative_colour(s.c_str()); current_tentative_output = s; check_stdout(); } } #endif /* TENTATIVE_OUTPUT_DISABLED */ void write_definite_output(const std::string &s) { assert(s.size() > 0); #ifndef TENTATIVE_OUTPUT_DISABLED unwrite_tentative_output(); #endif fputs(s.c_str(), stdout); check_stdout(); } static bool tentative_test = false; static int tentative_test_digits; /* * We don't produce tentative output at all if the 'digits' parameter * returned from get_tentative_output is too small. Otherwise, we'd be * forever writing out 10 characters of red text and deleting it, more * or less between _any_ pair of digits of real output. */ #define MIN_TENTATIVE_DIGITS 3 void write_to_stdout(OutputGenerator *og, bool print_newline) { while (1) { std::string out; int digits; if (og->get_definite_output(out)) { if (out.size() > 0) { write_definite_output(out); } else if (og->get_tentative_output(out, &digits) && digits >= MIN_TENTATIVE_DIGITS) { if (tentative_test && digits >= tentative_test_digits) { write_definite_output("!"); write_definite_output(out); break; } #ifndef TENTATIVE_OUTPUT_DISABLED write_tentative_output(out); #endif } } else { break; } } if (print_newline) { putchar('\n'); check_stdout(); } } extern bool set_term_modes; /* for instructing io.cpp */ static void print_help(FILE *fp) { static const char *const help[] = { "usage: spigot [options] ", "where: -b BASE print output in specified number base (2..36)", " -B BASE like -b, but digits > 9 in upper case", " -c print output as list of continued fraction terms", " -l print output without line breaks (-c only)", " -C print output as list of rational convergents", " -R print output as a rational, if it is rational", " -S, -D, -Q, -H print output as hex bit pattern of IEEE float", " in single, double, quad (128-bit), half (16-bit)", " -d PREC limit precision to at most PREC digits after the", " point, or at most PREC terms / convergents", " -w DIGITS print at least DIGITS digits of the integer part", " --printf=FORMAT format output according to a printf-style floating", " point formatting directive", " --rz, --ri (-d, --printf) round towards / away from zero", " --ru, --rd (-d, --printf) round upwards / downwards", " --rn (-d, --printf) round to nearest, tie-break to even", " --rno (-d, --printf) round to nearest, tie-break to odd", " --rnz, --rni, --rnu, --rnd (-d, --printf) round to nearest, tie-break", " as if --rz, --ri, --ru, --rd", #ifndef TENTATIVE_OUTPUT_DISABLED " --tentative=STATE enable/disable tentative output (STATE can be 'on'", " 'off', or the default 'auto' i.e. only on ttys)", #endif #ifdef HAVE_FDS " -T set terminal devices into raw mode when reading", " input numbers from them", #endif " -n suppress trailing newline when output terminates", " also: spigot --version report version number and build options", " spigot --help display this help text", " spigot --licence display (MIT) licence text", }; for (size_t i = 0; i < lenof(help); ++i) fprintf(fp, "%s\n", help[i]); } static void print_licence(FILE *fp) { static const char *const licence[] = { /* To regenerate the below: perl -l12 -pe 's/"/\\"/g; s/^/"/; s/$/",/' LICENCE */ "spigot is copyright 2007-2014 Simon Tatham. All rights reserved.", "", "Permission is hereby granted, free of charge, to any person", "obtaining a copy of this software and associated documentation files", "(the \"Software\"), to deal in the Software without restriction,", "including without limitation the rights to use, copy, modify, merge,", "publish, distribute, sublicense, and/or sell copies of the Software,", "and to permit persons to whom the Software is furnished to do so,", "subject to the following conditions:", "", "The above copyright notice and this permission notice shall be", "included in all copies or substantial portions of the Software.", "", "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS", "BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN", "ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", "SOFTWARE.", }; for (size_t i = 0; i < lenof(licence); ++i) fprintf(fp, "%s\n", licence[i]); } static void print_version(FILE *fp) { fprintf(fp, "spigot, %s\n", VER); fprintf(fp, " big integer provider: %s\n", BIGINT_PROVIDER); fprintf(fp, " fd literals (cfracfd:F, baseNfd:F): %s\n", #ifdef HAVE_FDS "supported" #else "not supported" #endif ); } int main(int argc, char **argv) { bool doing_args = true; enum { MODE_BASE, MODE_CFRAC, MODE_IEEE, MODE_CONVERGENTS, MODE_PRINTF, MODE_RATIONAL } outmode = MODE_BASE; #ifndef TENTATIVE_OUTPUT_DISABLED enum { ON, OFF, AUTO } tentative_output_option = AUTO; #endif RoundingMode rmode = ROUND_TOWARD_ZERO; int base = 10; int digitlimit = -1, minintdigits = 0; bool got_digitlimit = false; int ieee_bits = -1; bool base_uppercase = false; bool oneline = false; bool print_newline = true; bool printf_nibble_mode = false; Spigot *sp = NULL; int printf_width = 0, printf_precision = 0; int printf_flags = 0, printf_specifier = 0; while (--argc > 0) { const char *p = *++argv; if (doing_args && p[0] == '-') { if (!strcmp(p, "--")) { doing_args = false; } else if (p[1] == '-') { /* * GNU-style long option. */ p += 2; if (!strcmp(p, "rz")) rmode = ROUND_TOWARD_ZERO; else if (!strcmp(p, "ri")) rmode = ROUND_AWAY_FROM_ZERO; else if (!strcmp(p, "rn") || !strcmp(p, "rne")) rmode = ROUND_TO_NEAREST_EVEN; else if (!strcmp(p, "rno")) rmode = ROUND_TO_NEAREST_ODD; else if (!strcmp(p, "rnz")) rmode = ROUND_TO_NEAREST_TOWARD_ZERO; else if (!strcmp(p, "rni")) rmode = ROUND_TO_NEAREST_AWAY_FROM_ZERO; else if (!strcmp(p, "ru") || !strcmp(p, "rp")) rmode = ROUND_UP; else if (!strcmp(p, "rd") || !strcmp(p, "rm")) rmode = ROUND_DOWN; else if (!strcmp(p, "rnu") || !strcmp(p, "rnp")) rmode = ROUND_TO_NEAREST_UP; else if (!strcmp(p, "rnd") || !strcmp(p, "rnm")) rmode = ROUND_TO_NEAREST_DOWN; else if (!strcmp(p, "nibble")) printf_nibble_mode = true; else if (!strcmp(p, "printf") || !strncmp(p, "printf=", 7)) { static const char flags[] = PRINTF_FLAGSTR; const char *fmt, *q; if (p[6]) { fmt = p+7; } else if (argc > 1) { --argc; fmt = *++argv; } else { fprintf(stderr, "option '--printf' expects a" " parameter\n"); return 1; } q = fmt; if (*q++ != '%') { fprintf(stderr, "expected %% at start of '--printf'" " parameter '%s'\n", fmt); return 1; } printf_flags = 0; while (*q && strchr(flags, *q)) { printf_flags |= 1 << (strchr(flags, *q) - flags); q++; } if (*q == '*') { fprintf(stderr, "'*' width specifier not supported in" " '--printf' parameter '%s'\n", fmt); return 1; } else if (isdigit((unsigned char)*q)) { char *ret; printf_width = strtol(q, &ret, 10); q = ret; } else { printf_width = -1; } if (*q == '.') { q++; if (*q == '*') { fprintf(stderr, "'*' precision specifier not " "supported in '--printf' parameter" " '%s'\n", fmt); return 1; } else if (isdigit((unsigned char)*q)) { char *ret; printf_precision = strtol(q, &ret, 10); q = ret; } else { fprintf(stderr, "expected number after '.' in" " '--printf' parameter '%s'\n", fmt); return 1; } } else { printf_precision = -1; } if (*q && strchr("hljzt", *q)) { fprintf(stderr, "integer length modifiers not " "supported in '--printf' parameter '%s'\n", fmt); return 1; } else if (*q && strchr("L", *q)) { // Quietly ignore a floating-point length // modifier, so that users can paste a // formatting directive out of their actual // program and have it Just Work as often as // possible. q++; } if (!*q || !strchr("eEfFgGaA", *q)) { fprintf(stderr, "expected floating-point conversion" " specifier in '--printf' parameter '%s'\n", fmt); return 1; } printf_specifier = *q++; if (*q) { fprintf(stderr, "expected nothing after conversion" " specifier in '--printf' parameter '%s'\n", fmt); return 1; } outmode = MODE_PRINTF; rmode = ROUND_TO_NEAREST_EVEN; #ifndef TENTATIVE_OUTPUT_DISABLED } else if (!strcmp(p, "tentative") || !strncmp(p, "tentative=", 10)) { const char *val; if (p[9]) { val = p+10; } else if (argc > 1) { --argc; val = *++argv; } else { fprintf(stderr, "option '--tentative' expects a" " parameter\n"); return 1; } if (!strcmp(val, "on") || !strcmp(val, "yes") || !strcmp(val, "true")) { tentative_output_option = ON; } else if (!strcmp(val, "off") || !strcmp(val, "no") || !strcmp(val, "false")) { tentative_output_option = OFF; } else if (!strcmp(val, "auto")) { tentative_output_option = AUTO; } #endif /* TENTATIVE_OUTPUT_DISABLED */ } else if (!strcmp(p, "tentative-test") || !strncmp(p, "tentative-test=", 15)) { const char *val; if (p[14]) { val = p+15; } else if (argc > 1) { --argc; val = *++argv; } else { fprintf(stderr, "option '--tentative-test' expects a" " parameter\n"); return 1; } tentative_test = true; tentative_test_digits = atoi(val); #ifndef TENTATIVE_OUTPUT_DISABLED /* * In --tentative-test mode, disable the usual * tentative output. * * (The --tentative-test option itself can remain * enabled even when we're compiling spigot * without the main tentative output feature, * because the test mode is purely computational. * The reasons why we can't always enable the * proper tentative output feature have to do with * ncurses and terminal devices and printing the * output sensibly, not with the underlying * mechanism for deciding what the tentative * output _should be_.) */ tentative_output_option = OFF; #endif } else if (!strcmp(p, "help")) { print_help(stdout); return 0; } else if (!strcmp(p, "licence") || !strcmp(p, "license")) { print_licence(stdout); return 0; } else if (!strcmp(p, "version")) { print_version(stdout); return 0; } else { fprintf(stderr, "unrecognised option '--%s'\n", p); return 1; } } else { /* * Short option(s). */ const char *val; p++; while (*p) { char c = *p++; switch (c) { case 'b': case 'B': case 'd': case 'w': /* * Options requiring an argument. */ val = p; p = ""; if (!*val) { if (--argc > 0) { val = *++argv; } else { fprintf(stderr, "option '-%c' expects a " "value\n", c); return 1; } } switch (c) { case 'b': case 'B': outmode = MODE_BASE; base = atoi(val); if (base < 2 || base > 36) { fprintf(stderr, "bases not in [2,...,36] are" " unsupported\n"); return 1; } base_uppercase = (c == 'B'); break; case 'd': digitlimit = atoi(val); got_digitlimit = true; break; case 'w': minintdigits = atoi(val); break; } break; case 'c': outmode = MODE_CFRAC; break; case 'C': outmode = MODE_CONVERGENTS; break; case 'R': outmode = MODE_RATIONAL; break; case 'F': case 'S': outmode = MODE_IEEE; ieee_bits = 32; break; case 'D': outmode = MODE_IEEE; ieee_bits = 64; break; case 'Q': outmode = MODE_IEEE; ieee_bits = 128; break; case 'H': outmode = MODE_IEEE; ieee_bits = 16; break; case 'l': oneline = true; break; case 'T': #ifdef HAVE_FDS set_term_modes = true; break; #else fprintf(stderr, "option '-T' not supported in this" " build of spigot\n"); return 1; #endif case 'n': print_newline = false; break; default: fprintf(stderr, "unrecognised option '-%c'\n", c); return 1; } } } } else { if (sp) { fprintf(stderr, "only one expression argument expected\n"); return 1; } else { try { sp = expr_parse(p, NULL); } catch (spigot_error err) { fprintf(stderr, "%s\n", err.errmsg); return 1; } } } } if (!sp) { fprintf(stderr, "expected an expression argument\n"); return 1; } #ifndef TENTATIVE_OUTPUT_DISABLED if (tentative_output_option == ON || (tentative_output_option == AUTO #ifndef _WIN32 && isatty(fileno(stdout)) #endif )) { /* * If the user has attempted to force tentative output *on*, * then we pass required=true to the setup function, which * will cause an error message if any of the necessary control * sequences can't be retrieved from terminfo. In auto mode, * however, we just silently disable tentative output in that * situation, the same as we do if the output channel isn't a * terminal. * * (On Windows, we just try to turn on tentative output * regardless, because it can only work on consoles anyway, so * setup_tentative_output will fail in the auto case if * standard output is not a console.) */ setup_tentative_output(tentative_output_option == ON); } #endif #ifdef HAVE_FDS { extern void begin_fd_input(); begin_fd_input(); } #endif try { OutputGenerator *og = NULL; switch (outmode) { case MODE_BASE: og = base_format (sp, base, base_uppercase, got_digitlimit, digitlimit, rmode, minintdigits); break; case MODE_PRINTF: og = printf_format (sp, rmode, printf_width, printf_precision, printf_flags, printf_specifier, printf_nibble_mode); break; case MODE_IEEE: og = ieee_format (sp, ieee_bits, got_digitlimit, digitlimit, rmode); break; case MODE_CFRAC: og = cfrac_output(sp, oneline, got_digitlimit, digitlimit); if (!oneline) print_newline = false; // we printed a newline already break; case MODE_CONVERGENTS: og = convergents_output(sp, got_digitlimit, digitlimit); print_newline = false; // we printed a newline already break; case MODE_RATIONAL: og = rational_output(sp, got_digitlimit); break; default: /* * Should never get here, since no other value of * outmode would have come to this branch of the outer * switch. But clang will give a warning anyway about * not all enum values appearing in this switch, so * here's a pointless default clause to placate it. */ og = NULL; } assert(og); write_to_stdout(og, print_newline); return 0; } catch (spigot_eof err) { if (print_newline) { putchar('\n'); check_stdout(); } fprintf(stderr, "end of file '%s'\n", err.errmsg); return 2; } catch (spigot_error err) { fprintf(stderr, "%s\n", err.errmsg); return 1; } return 0; } spigot-0.2017-01-15.gdad1bbc6/manpage.but0000644000000000000000000003605113025565031014304 0ustar \cfg{man-identity}{spigot}{1}{2014-12-17}{Simon Tatham}{Simon Tatham} \A{man} Unix man page for \spigot \H{man-name} NAME \cw{spigot} \dash command-line exact real calculator \H{man-synopsis} SYNOPSIS \c spigot [ options ] expression \e bbbbbb iiiiiii iiiiiiiiii \H{man-description} DESCRIPTION \spigot is an exact real calculator: that is, you give it a mathematical expression to evaluate, and it computes it to any desired precision, by default simply printing digits to standard output until it is interrupted. \spigot provides command-line options to control the format of the output, restrict it to a specified number of digits, and apply rounding at the end of those digits. It can produce output in any base between 2 and 36 (after that it runs out of digit characters), or as a continued fraction, and it can read input numbers from files in any of those formats as well. This man page gives only a brief summary of \spigot's functionality. For full detail, you should read the main manual \cw{spigot.html}; if that is not installed on your system, you can find it on the web at \W{http://www.chiark.greenend.org.uk/~sgtatham/spigot/spigot.html}\cw{http://www.chiark.greenend.org.uk/~sgtatham/spigot/spigot.html} \H{man-options} OPTIONS The following options control \spigot's basic output format: \dt \c{-b} \e{base}, \c{-B} \e{base} \dd Output the number in base \e{base}, which must be an integer between 2 and 36 inclusive. Digits above 9 are represented by lower case or upper case letters, for the options \c{-b} and \c{-B} respectively. The default is \c{-b}\_\c{10}. \dt \c{-c} \dd Output the number as a list of continued fraction coefficients, as decimal integers, by default one per line. \dt \c{-C} \dd Output the number's continued fraction convergents, one per line, in the form of two decimal integers with a \c{/} between them. \dt \c{-R} \dd Output the number's value as a rational, in the form of two decimal integers with a \c{/} between them, or just one decimal integer if the number is a rational. If \spigot does not know the number to be rational immediately, it will start evaluating it to see if it turns out rational later, so if it is not rational then \spigot will compute for ever. \dt \c{-S}, \c{-D}, \c{-Q}, \c{-H} \dd Output the number as a hex representation of an IEEE 754 bit pattern, in 32-bit single precision, 64-bit double, 128-bit quad or 16-bit half precision respectively. If that representation is not exact, a decimal point will be printed followed by further mantissa digits. \dt \c{--printf} \e{format}, \c{--printf=}\e{format} \dd Format the number in the same way that \c{printf}(\e{3}) would, given the formatting directive \e{format}. \e{format} must begin with a \c{%} and end with the associated conversion specifier, which must be a floating-point one (one of \cw{efgaEFGA}). The following options modify the details of those output formats: \dt \c{-d} \e{limit} \dd Limit the amount of data output. In \c{-b} mode, no more than \e{limit} digits after the decimal point are printed. In \c{-c} or \c{-C} mode, no more than \e{limit} continued fraction coefficients or convergents are printed, not counting the initial one representing the number's integer part. In the IEEE 754 output modes, no more than \e{limit} additional bits of precision are generated after the end of the official mantissa. \e{limit} may be negative. \dt \c{-l} \dd In \c{-c} mode, output continued fraction terms all on one line, separated by a \c{;} after the first term and \c{,} after each subsequent term. \dt \c{-w} \e{min-int-digits} \dd In \c{-b} mode, output at least \e{min-int-digits} of the number's integer part, by printing leading zeroes if necessary. \dt \c{--nibble} \dd In \c{--printf} mode with the \cq{a} or \cq{A} conversion specifier, choose the output exponent to always be a multiple of 4, instead of the default behaviour of choosing it as large as possible. \dt \c{-n} \dd In any mode where \spigot prints output on a single line, suppress the usual trailing newline if \spigot's output terminates. The following options control rounding, when \spigot's output is limited by the \c{-d} option. (Rounding does not occur in continued fraction modes.) \dt \c{--rz} \dd Round towards zero. This is the default. \dt \c{--ri} \dd Round away from zero. \dt \c{--ru} \dd Round up (towards positive infinity). \dt \c{--rd} \dd Round down (towards negative infinity). \dt \c{--rn}, \c{--rne} \dd Round to nearest, breaking ties toward an even last digit. \dt \c{--rno} \dd Round to nearest, breaking ties toward an odd last digit. \dt \c{--rnz}, \c{--rni}, \c{--rnu}, \c{--rnd} \dd Round to nearest, breaking ties as if rounding via \c{--rz}, \c{--ri}, \c{--ru} or \c{--rd} respectively. Miscellaneous options: \dt \c{--tentative=}\e{state} \dd Control the printing of \q{tentative output}. Tentative output is printed when \spigot does not know for sure what the next digit of the number is because it's starting to look as if it's exactly on a digit boundary. Tentative output is in red, and followed by an indication of about how many digits \spigot has examined beyond that point (i.e. how close to exact that digit is known to be); \spigot will retract it later if it finds out something definite. \lcont{ \e{state} can be \cq{on}, \cq{off} or \cq{auto}. \cq{auto} is the default, and means that \spigot should only print tentative output if its output is directed to a terminal device. } \dt \c{-T} \dd If instructed to read from a file descriptor which points to a terminal, put the terminal into raw mode (turning off \c{ICANON} and \c{ECHO} modes) while doing so. \H{man-expressions} EXPRESSIONS \spigot's expression language supports the following options, in order of priority from lowest to highest: \dt \c{+} and \c{-} \dd Addition and subtraction. (Left-associative.) \dt \c{*}, \c{/}, \c{%}, \c{mod}, \c{rem} \dd Multiplication, division and remainder. (Left-associative.) \c{%} and \c{mod} are synonyms, which both return a remainder between 0 and the denominator; \c{rem} returns a remainder of either sign, with absolute value at most half that of the denominator, and ties broken by rounding to even in IEEE 754 style. \dt Unary \c{-} and \c{+} \dd Negation and no-op. \dt \c{^}, \c{**} \dd Power. (Right-associative.) You can define variables and functions of your own in subexpressions using the \c{let} expression, as follows: \dt \c{let}\_\e{var}\cw{=}\e{value}\_\c{in}\_\e{expression} \dd Defines the name \e{var} to refer to the value of the expression \e{value}. The definition is in scope within \e{expression}, but not in any other parts of the \spigot input. \dt \c{let}\_\e{fn}\cw{(}\e{params}\cw{)=}\e{defn}\_\c{in}\_\e{expression} \dd Defines the syntax \e{fn}\cw{(}\e{args}\cw{)} to refer to the expression \e{defn} with the arguments substituted in for the parameters. \e{params} must be a comma-separated list of identifiers; \e{args} is a comma-separated list of expressions. A \c{let} expression can contain multiple definitions, separated by commas, e.g. \cq{let x=1,y=2 in x+y}. Each definition is in scope for subsequent definitions, so you can write \cq{let x=1,y=x+1 in}\_\e{expr}. But definitions are not in scope for \e{themselves}; in particular, functions may not be recursive. \spigot also provides the following built-in functions: \dt \c{sqrt}, \c{cbrt} \dd Square and cube roots. \dt \c{hypot}, \c{atan2} (two arguments) \dd Rectangular to polar coordinate conversions: the hypotenuse function (square root of the sum of the squared arguments), and two-variable inverse tangent. \dt \c{sin}, \c{cos}, \c{tan}, \c{asin}, \c{acos}, \c{atan} \dd Trigonometric functions and their inverses. \dt \c{sind}, \c{cosd}, \c{tand}, \c{asind}, \c{acosd}, \c{atand}, \c{atan2d} \dd Trigonometric functions and their inverses, equivalent to the versions without \cq{d} on the end except that angles are measured in degrees. \dt \c{sinh}, \c{cosh}, \c{tanh}, \c{asinh}, \c{acosh}, \c{atanh} \dd Hyperbolic functions and their inverses. \dt \c{exp}, \c{exp2}, \c{exp10}, \c{log}, \c{log2}, \c{log10} \dd Exponential and logarithmic functions: raise \e{e}, 2 and 10 to a power, or take a log with the same three bases. You can also provide a base of your choice as a second argument to \c{log}. \dt \c{expm1}, \c{log1p} \dd Shorthands for \c{exp(x)-1} and \c{log(1+x)}. \dt \c{pow} (two arguments) \dd Synonym for the \c{^} operator. \dt \c{gamma}, \c{tgamma}, \c{lgamma} \dd Gamma function (\c{gamma} and \c{tgamma} are synonyms for this), and the log of the absolute value of the gamma function. \dt \c{erf}, \c{erfc}, \c{Phi}, \c{norm} \dd Error-function relatives: the error function itself, 1 minus the error function, and \c{Phi} and \c{norm} are synonyms for the cumulative normal distribution function. \dt \c{erfinv}, \c{erfcinv}, \c{Phiinv}, \c{norminv} \dd Inverses of the above error-function relatives. \dt \c{W}, \c{Wn} \dd The Lambert W function, i.e. the inverse of \c{x}\_\c{exp(x)}. \c{W} is the branch with value at least \c{-1}, and \c{Wn} is the branch with value at most \c{-1}. \dt \c{Ei}, \c{En} (two arguments), \c{E1}, \c{Ein} \dd Exponential integrals, i.e. integrals of things like \c{exp(x)/x}. \c{Ei(x)} is the indefinite integral of \c{exp(x)/x} itself; \c{En(n,x)} (for non-negative integer \c{n}) is the result of integrating \c{exp(-x)/x} \c{n} times, flipping the sign each time; \c{E1(x)} is shorthand for \c{En(1,x)}; and \c{Ein(x)} is the integral of \c{(1-exp(-x))/x}. \dt \c{Li}, \c{li} \dd Logarithmic integrals, i.e. integrals of \c{1/log(x)}. \c{Li(x)} and \c{li(x)} are both the indefinite integral of \c{1/log(x)}; only their constants differ, in that \c{Li(2)} and \c{li(0)} are each defined to be zero. \dt \c{Si}, \c{si}, \c{Ci}, \c{Cin} \dd Sine and cosine integrals, i.e. integrals of \c{sin(x)/x} and \c{cos(x)/x}. \c{Si(x)} and \c{si(x)} are both the indefinite integral of \c{sin(x)/x}, differing only in the constant: \c{Si(0)=0}, but \c{si(x)} has limit 0 as \c{x} tends to positive infinity. \c{Ci(x)} is the indefinite integral of \c{cos(x)/x}, also with limit 0 at positive infinity; \c{Cin(x)} is the indefinite integral of \c{(1-cos(x))/x}, with \c{Cin(0)=0}. \dt \c{UFresnelS}, \c{UFresnelC}, \c{FresnelS}, \c{FresnelC} \dd Fresnel integrals. \c{UFresnelS} and \c{UFresnelC} are the indefinite integrals of \c{sin(x^2)} and \c{cos(x^2)}; \c{FresnelS} and \c{FresnelC} are the \q{normalised} versions, i.e. integrals of \c{sin(}\pi\_\c{x^2/2)} and \c{cos(}\pi\_\c{x^2/2)}. All are zero at the origin. \dt \c{zeta} \dd The Riemann zeta function (restricted to the real numbers). \dt \c{abs} \dd Absolute value. \dt \c{ceil}, \c{floor} \dd Ceiling and floor: smallest integer at least \e{x}, and largest integer at most \e{x}. \dt \c{frac} \dd Fractional part, i.e. \c{x}\_\c{-}\_\c{floor(x)}. \dt \c{algebraic} (variable number of arguments) \dd Return a root of an arbitrary polynomial with integer coefficients. The first two arguments are the rational bounds of an interval to search, and the rest give the polynomial's coefficients, with constant term first. \spigot supports the following names for built-in constants: \dt \c{pi}, \c{tau} \dd The circle constant \pi, and the often more useful 2 \pi. \dt \c{e} \dd The base of natural logarithms. \dt \c{phi} \dd The golden ratio, \c{(1+sqrt(5))/2}. \dt \c{eulergamma} \dd The Euler-Mascheroni constant: the limiting difference between the sum and the integral of \cw{1/n}. \dt \c{apery} \dd Ap\u00E9{e}ry's constant: the sum of the reciprocals of the cubes. Numbers can be input in the following formats: \b Decimal, with an optional C-style \c{e+}\e{exponent} or \c{e-}\e{exponent} for scientific notation \b Hex, with the prefix \c{0x}, and an optional C99-style \c{p+}\e{exponent} or \c{p-}\e{exponent} representing a power of 2 multiplier \b In any base between 2 and 36, with a prefix of the form \c{base}\e{N}\c{:}, e.g. \c{base7:0.123456} \b As an IEEE 754 hex bit pattern, consisting of exactly 4, 8, 16 or 32 hex digits with the prefix \c{ieee:}, followed by optional decimal point and extra mantissa digits \b From a file in base notation, by writing \c{base}\e{N}\c{file:} followed by a filename, e.g. \c{base10fd:pi.txt}. The filename is taken to be the maximal sequence of non-space characters following the prefix, unless it starts with \c{'} or \c{"}, in which case it is taken to be everything up to a matching closing quote, with doubled quote marks in between representing a literal quote character. \b From a file in continued fraction notation, by writing \c{cfracfile:} followed by a filename. \b Either of the above, but with \c{file:} replaced by \c{xfile:} to indicate that end of file should be taken as the number being exactly represented rather than running out of precision. \b From a file descriptor in any of those notations, by writing \c{base}\e{N}\c{fd:} or \c{cfracfd:} followed by an fd number, e.g. \c{base10fd:0} to read from standard input. \H{man-return} RETURN VALUE \spigot returns 0 if its output terminates (because the result is exact, or because it reached the specified \c{-d} limit) with no problems. In case of a parse error, or an invalid operand to a function, or any other kind of fatal error, \spigot returns 1. If \spigot is unable to generate output to the desired precision because more precision was needed from a number read from an input file using \c{base}\e{N}\c{file:} or \c{cfracfile:}, then \spigot returns 2, and prints an error message indicating which input file (in case there was more than one) ran out first. \H{man-bugs} LIMITATIONS Due to inherent limitations of its exact real arithmetic strategy, \spigot is generally unable to recognise when a number it is computing is exactly equal to a specific boundary value. One effect of this is that \spigot will not behave as you'd like if the output number has a terminating representation in the selected base. For example, asking for \cw{sin(asin(0.12345))} will not be able to print \cw{0.12345} and exit. Instead, \spigot will get as far as printing \cq{0.1234}, and then print tentative output (mentioned above) to indicate that it thinks the next digit \e{might} be exactly 5, but it will never reach a point where it's \e{sure} of that. Another effect is that if you ask \spigot to evaluate an expression in which an intermediate result is precisely on a point of discontinuity of the function it is passed to, then it may never manage to even \e{start} producing output. For example, \spigot will hang completely if you ask it for \cw{floor(sin(pi))}, since \cw{sin(pi)}\_\cw{=}\_\cw{0} is a point of discontinuity of the \cw{floor} function, and \spigot will never be able to work out that the value of the input to \cw{floor} is \e{exactly} zero, only that it seems to be closer and closer to zero the more it computes. (An exception is numbers that \spigot knows from first principles to be rational. For example, if you ask \spigot to evaluate the simpler expressions \cq{0.12345} or \cq{floor(0)}, it will print the complete output and terminate successfully, in both cases.) \H{man-licence} LICENCE \spigot is free software, distributed under the MIT licence. Type \cq{spigot --licence} to see the full licence text. spigot-0.2017-01-15.gdad1bbc6/manual.but0000644000000000000000000021115513025565031014151 0ustar \title \spigot: an exact real calculator This manual documents \spigot, a command-line exact real calculator. \C{introduction} What is \spigot? \spigot is a calculating program. It supports the usual arithmetic operations, square and cube roots, trigonometric and exponential functions, and a few other special functions such as \cw{erf}. \spigot differs from the average calculating program in that it is an \e{exact real} calculator. This means that it does not suffer from rounding errors; in principle, it can keep generating more and more digits of the number you asked for until it runs out of memory. In particular, if you ask for a complex expression such as \cw{sin(sqrt(pi))}, then most calculating systems would compute first \cw{pi}, then \cw{sqrt(pi)} and finally \cw{sin(sqrt(pi))}, accumulating a rounding error at each step, so that the final result had a build-up of error and you would have to do some additional error analysis to decide how much of the output you could trust. \spigot, on the other hand, does not output any digit until it is sure that digit is correct, so if you ask for (say) 100 digits of \cw{sin(sqrt(pi))} then you can be sure they are the \e{right} 100 digits. (The downside of doing this is that \spigot is \e{slow}, compared to the more usual methods of computer arithmetic. You wouldn't want to use even its individual arithmetic operations and functions for any kind of bulk computation. And because it evaluates a complicated expression as a whole, it's especially unsuited to any iterative algorithm which involves looking at the first result you get and then deciding what further arithmetic to do on it: normal arithmetic systems can do the next step of the algorithm with only the cost of the extra operations, but \spigot would have to re-evaluate the entire thing from the start every time. So \spigot can be useful if you really do need a lot of digits, or if you're doing a computation prone to numerical error, or as a cross-check on other calculating systems, but it wouldn't be usable outside that kind of specialist niche.) \spigot can evaluate expressions starting from numbers it can construct internally (integers, rationals, \pi, etc), and it can also read a number from a file or a pipe. In the latter mode, it operates \q{on-line}, i.e. it writes output as it goes along, and once it has read enough digits of an input number to know some of the output, it will print it. \C{examples} Examples of using \spigot The simplest thing \spigot can do is to compute well-known mathematical constants such as \pi. For example, try running this command: \c $ spigot pi \e bbbbbbbbb \c 3.1415926535897932384626433832795028841971693993751058209749445923078164 \c 062862089986280348253421170679821480865132823066470938446095505822317253 \c 594081284811174502841027019385211055596446229489549303819644288109756659 \c (and so on) \e iiiiiiiiiii In this default mode, \spigot generates unbounded output: it will continue writing digits of \pi to the terminal until you interrupt it (e.g. by pressing Ctrl-C). As \spigot generates more digits, it will slow gradually down and consume more and more memory to keep track of where it's got to, and one of time and memory will ultimately be the limit to how much data it can generate. But in principle, given unlimited time and memory, it could just keep on going. \spigot can also evaluate more complicated mathematical expressions. (Some will be much slower than others.) You could try some of these, for example: \c $ spigot pi*e \e bbbbbbbbbbb \c $ spigot pi/e \e bbbbbbbbbbb \c $ spigot pi^e \e bbbbbbbbbbb \c $ spigot -- '-pi^2' \e bbbbbbbbbbbbbbbbb \c $ spigot 'sin(sqrt(pi))' \e bbbbbbbbbbbbbbbbbbbbbb \c $ spigot 'exp(pi*sqrt(163))' \e bbbbbbbbbbbbbbbbbbbbbbbbbb (The above command lines assume that you are invoking \spigot from a POSIX shell, so that expressions containing parentheses need to be single-quoted in order for \spigot to receive them unmodified without the shell interfering. On other shells or operating systems, quoting conventions may vary.) Since \spigot supports a variety of command-line options beginning with \c{-}, if your expression also begins with a \c{-} (as one of the examples above does), then you need to precede it with the special argument word \c{--}, which instructs \spigot to treat further command-line arguments as an expression rather than an option. See \k{expr} for a full description of the range of expressions you can give to \spigot. \H{rounding} Digit limits and rounding In its default mode, \spigot keeps generating digits until you interrupt it, or until it determines that it has output the exactly correct answer and needs no more digits at all. You can limit \spigot to a finite number of digits by using the \cw{-d} option. For example: \c $ spigot -d40 pi \e bbbbbbbbbbbbbb \c 3.1415926535897932384626433832795028841971 (The argument to \cw{-d} counts decimal places, rather than significant figures. So here, there are 40 digits after the decimal point.) By default, \spigot does no rounding, i.e. it simply truncates the full decimal expansion of the number you asked for. (This is equivalent to rounding towards zero in all cases.) If you want to change that, there's a variety of rounding-mode options, detailed in \k{rmodeopts}. For example, you can ask \spigot to round the final digit towards whichever value is nearer to the true result using \cw{--rn}: \c $ spigot --rn -d40 pi \e bbbbbbbbbbbbbbbbbbb \c 3.1415926535897932384626433832795028841972 (This example rounds up, because the next digit following the 1 is a 6.) \H{bases} Generating output in different bases By default, \spigot's output is in base 10. You can use the \cw{-b} option to output in another base instead: \c $ spigot -b2 -d40 pi \e bbbbbbbbbbbbbbbbbb \c 11.0010010000111111011010101000100010000101 \c $ spigot -b3 -d40 pi \e bbbbbbbbbbbbbbbbbb \c 10.0102110122220102110021111102212222201112 \c $ spigot -b16 -d40 pi \e bbbbbbbbbbbbbbbbbbb \c 3.243f6a8885a308d313198a2e03707344a4093822 \c $ spigot -b36 -d40 pi \e bbbbbbbbbbbbbbbbbbb \c 3.53i5ab8p5fsa5jhk72i8asc47wwzlacljj9zn98l For bases larger than 10, letters of the alphabet are used as additional digits. If you use the \cw{-B} option in place of \cw{-b}, those letters will be displayed in upper case: \c $ spigot -B16 -d40 pi \e bbbbbbbbbbbbbbbbbbb \c 3.243F6A8885A308D313198A2E03707344A4093822 \H{cfrac} Generating continued fractions of numbers As well as generating numbers in ordinary base notation, \spigot can also generate them in the form of continued fractions. You can generate the continued fraction terms of a number, one per line, using the \cw{-c} option: \c $ spigot -c pi \e bbbbbbbbbbbb \c 3 \c 7 \c 15 \c 1 \c 292 \c 1 \c (and so on) \e iiiiiiiiiii (For those unfamiliar with continued fractions, this is equivalent to saying that \pi is equal to \cw{3 + 1 / (7 + 1 / (15 + 1 / ...))}.) This one-per-line format is useful for applications which are consuming the numbers automatically, but for readability, you might prefer to add \cw{-l} to replace the newlines with commas, so that you can see many more terms at once: \c $ spigot -c -l pi \e bbbbbbbbbbbbbbb \c 3;7,15,1,292,1,1,1,2,1,3,1,14,2,1,1,2,2,2,2,1,84,2,1,1,15,3,13,1,4,2,6,6 \c ,99,1,2,2,6,3,5,1,1,6,8,1,7,1,2,3,7,1,2,1,1,12,1,1,1,3,1,1,8,1,1,2,1,6,1 \c ,1,5,2,2,3,1,2,4,4,16,1,161,45,1,22,1,2,2,1,4,1,2,24,1,2,1,3,1,2,1,1,10, \c (and so on) \e iiiiiiiiiii (The semicolon emphasises that the first number is the integer part.) Finally, you can use \cw{-C} to output the continued fraction \e{convergents} (that is, the rational numbers obtained by evaluating successive truncations of the continued fraction, which are the best approximations to the target number by a particular metric): \c $ spigot -C pi \e bbbbbbbbbbbb \c 3/1 \c 22/7 \c 333/106 \c 355/113 \c 103993/33102 \c 104348/33215 \c (and so on) \e iiiiiiiiiii In any of these modes, the \cw{-d} option allows you to limit the number of continued fraction terms or convergents that \spigot outputs. Again, the integer part is not counted, so that for example \cw{-d3} gives you three terms or convergents \e{as well} as the integer one: \c $ spigot -c -d3 pi \e bbbbbbbbbbbbbbbb \c 3 \c 7 \c 15 \c 1 \c $ spigot -C -d3 pi \e bbbbbbbbbbbbbbbb \c 3/1 \c 22/7 \c 333/106 \c 355/113 \H{input} Reading input numbers from a file or another program As well as applying its collection of mathematical operators and functions to real numbers that it has derived itself from first principles, \spigot can also apply the same operations to numbers received as input from elsewhere, which makes it at least theoretically able to operate on \e{any} real number. To begin with, you can write a number into a file in ordinary decimal notation, and use a special syntax in the \spigot expression language to read from that file: \c $ echo 1.12358132134558914423337761098715972584418167651094617711 > z \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \c $ spigot -d30 'sin( base10file:z )' \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \c 0.901654985409730168388244848164 You might wonder why \spigot needs a feature like this: since the file contains only a finite amount of data, you surely could just have pasted the same number on to \spigot's command line directly. The difference is that if you write a decimal number on \spigot's command line, \spigot will assume it is \e{exact}, i.e. that the precise number you meant is the rational number with that terminating decimal expansion. But if you read from a file like this, \spigot will treat it as a prefix of some longer decimal expansion, and if it reaches the end of the file then it will report a special error saying that it can't compute any more output digits because it ran out of input. So this permits you to compute with imperfectly known inputs, without the risk of generating any incorrect digits of output due to the input imprecision. You can also tell \spigot to read from a file in number bases other than 10, or in continued fraction format. See \k{filefd} for full details of all the available options. But \spigot can also read input from a Unix file \e{descriptor} (assuming it's compiled on a system where that facility exists), which can point at an unbounded data source such as a pipe. So if you invoke \spigot with a file descriptor pointing at such a pipe, with a program at the other end of the pipe prepared to generate as many digits of the number as requested, then you need only use another special piece of expression syntax to tell \spigot to read a real number from it, as far as is necessary. For example, let's generate \W{http://en.wikipedia.org/wiki/Champernowne_constant}{the Champernowne constant}, which you get by concatenating all the positive integers in order immediately after the decimal point, with no leading zeroes. Here's a piece of Perl which generates that: \c $ perl -e 'print"0.";print while++$_' \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \c 0.1234567891011121314151617181920212223242526272829303132333435363738394 \c 041424344454647484950515253545556575859606162636465666768697071727374757 \c 677787980818283848586878889909192939495969798991001011021031041051061071 \c (and so on) \e iiiiiiiiiii Now we can use \spigot to compute with this number (or any other input real), by piping that script's output into \spigot, and telling \spigot to expect to read a base-10 number from file descriptor 0 (its standard input). As a really simple example, let's ask for \spigot to output the same number, but in continued fraction form: \c $ perl -e 'print"0.";print while++$_' | spigot -c -l base10fd:0 \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \c 0;8,9,1,149083,1,1,1,4,1,1,1,3,4,1,1,1,15,457540111391031076483646628242 \c 956118599603939710457555000662004393090262659256314937953207747128656313 \c 8641209375503552094607183089984575801469863148833592141783010987,6,1,1,2 \c 1,1,9,1,1,2,3,1,7,2,1,83,1,156,4,58,8,54,4457353800911178833959067671634 \c 293788437292958096324947188556700067877659324583930837874799958333344441 \c (and so on) \e iiiiiiiiiii (Apart from it being very easy to generate, another reason I chose this example constant is because it has interestingly large numbers in its continued fraction \dash the huge number spanning the first three lines occurs because there's an extremely good rational approximation to the constant at the point where numbers change from 2 to 3 digits. The number starting on the fourth line is the corresponding term at the 3/4 digit boundary, and goes on for far longer!) You can also use the expression \cw{base10fd:0} as part of a larger expression, so that you could compute mathematical functions of an input number, or add it to things (perhaps another input pipe, if you execute \spigot in such a way as to assign multiple input fds). \C{expr} \spigot's expression language This section gives a full specification of the language \spigot uses for mathematical expressions. \spigot's expression language is semantically simpler than most such languages. In deference to the limitations of efficient computing, most expression languages have to carefully distinguish a collection of numeric types representing different subsets of the real numbers with different tradeoffs between range, precision and performance. But \spigot, because its whole purpose is to deal in exact real numbers regardless of performance, does not have to do this. Every expression and subexpression in \spigot has the same type: \s{real}. So you don't have to remember to avoid writing \cw{1/2} for a half (which in, say, \cw{gnuplot}, would be interpreted as \e{integer} division and yield a rounded-down quotient of zero), or include any explicit syntax to change between formats as appropriate. Just write what looks like the obvious thing, and it will probably mean what you wanted. \H{operators} Operators \spigot's expression language provides the following mathematical operators: \b Addition, written as \e{a}\_\cw{+}\_\e{b} (for two subexpressions \e{a} and \e{b}). \b Subtraction, written as \e{a}\_\cw{-}\_\e{b}. \b Multiplication, written as \e{a}\_\cw{*}\_\e{b}. You can also write this by juxtaposition, i.e. just write \e{a}\_\e{b}. For example, \cw{(pi+2)(pi+3)} is a valid \spigot expression, synonymous with \cw{(pi+2)*(pi+3)}. \b Division, written as \e{a}\_\cw{/}\_\e{b}. As mentioned in the previous section, this is the real-number division operation, with no implicit rounding to integers in any circumstances. It is an error to use this operator with \e{b}\_\cw{=}\_\cw{0}. \b Modulus, written as \e{a}\_\cw{%}\_\e{b} or \e{a}\_\cw{mod}\_\e{b} (the two syntaxes are synonymous). This returns a value of the form \e{a}\_\cw{-}\_\e{nb}, with \e{n} chosen to be whichever integer puts the result in the range \cw{[0,}\e{b}\cw{)} (if \e{b} is positive) or \cw{(}\e{b}\cw{,0]} (if \e{b} is negative). \b Remainder, written as \e{a}\_\cw{rem}\_\e{b}. This also returns a value of the form \e{a}\_\cw{-}\_\e{nb}, but with slightly different semantics: this operation chooses \e{n} to be whichever integer puts the result in the range \cw{[-}\e{b}\cw{/2,+}\e{b}\cw{/2]}. In case of a tie (if the result is precisely half way between two integer multiples of \e{b}, so that two different values of \e{n} meet that constraint), the answer is chosen to also make \e{n} an even number. (This is the same semantics as the IEEE 754 remainder operation.) \b Power, written as \e{a}\_\cw{^}\_\e{b} or \e{a}\_\cw{**}\_\e{b} (the two syntaxes are synonymous). \b Negation, written as \cw{-}\_\e{a}. \b The construction \cw{+}\_\e{a} is supported for symmetry with \cw{-}\_\e{a}, but does nothing (just returns \e{a} unchanged). The priorities and associativity of these operators, from lowest to highest, are as follows. \b The addition and subtraction operators \c{+} and \c{-} have lowest priority, and associate left to right. (E.g. an expression such as \e{a}\cw{-}\e{b}\cw{+}\e{c}\cw{-}\e{d} is treated as if it were \cw{(((}\e{a}\cw{-}\e{b}\cw{)+}\e{c}\cw{)-}\e{d}\cw{)}.) \b The multiplication and division operators \c{*}, \c{/}, \c{%} and its synonym \c{mod}, and \c{rem} have the next higher priority, and also associate left to right. Multiplication by juxtaposition is included in this: when two numbers or parenthesised expressions appear adjacent in an expression, the parser imagines a \c{*} between them, and treats that \c{*} with the same priority as if it had been explicit. \b The negation and no-op operators \c{-} and \c{+} have the next higher priority. Since these are unary operators, there is no question of which way they associate: \cw{--}\e{a} has only one possible parse in any case, namely \cw{-(-}\e{a}\cw{)}. \b \c{^} and its synonym \c{**} have the highest priority. These operators associate right to left, i.e. \e{a}\cw{^}\e{b}\cw{^}\e{c} parses as if it had been \e{a}\cw{^(}\e{b}\cw{^}\e{c}\cw{)}. Note that since these operators have higher priority than the unary operators, \cw{-}\e{a}\cw{^}\e{b} parses as \cw{-(}\e{a}\cw{^}\e{b}\cw{)}, rather than the usually less helpful \cw{(-}\e{a}\cw{)^}\e{b}. \H{functions} Functions As well as infix and prefix operators, \spigot also supports a range of mathematical functions, all written in the style \cw{function(}\e{argument}\cw{)} or \cw{function(}\e{argument1}\cw{,}\e{argument2}\cw{)}. The following functions are provided: \b \cw{sqrt(}\e{x}\cw{)} returns the square root of \e{x}. It is an error to use this function with \e{x}\_\cw{<}\_\cw{0}. \b \cw{cbrt(}\e{x}\cw{)} returns the cube root of \e{x}. \b \cw{hypot(}\e{x}\cw{,}\e{y}\cw{)} returns the hypotenuse of a right triangle with legs \e{x} and \e{y}, or equivalently, the square root of \e{x}\cw{^2}\_\cw{+}\_\e{y}\cw{^2}. \b \cw{sin(}\e{x}\cw{)} returns the sine of \e{x}, interpreted in radians. \b \cw{cos(}\e{x}\cw{)} returns the cosine of \e{x}, interpreted in radians. \b \cw{tan(}\e{x}\cw{)} returns the tangent of \e{x}, interpreted in radians. In theory, it would be an error to use this function with \e{x} being an odd integer multiple of \pi\cw{/2}; in practice, \spigot will never actually notice. (See \k{hazards} for some discussion of this.) \b \cw{asin(}\e{x}\cw{)} returns an angle \theta in radians, in the range \cw{[-}\pi\cw{/2,+}\pi\cw{/2]}, such that \cw{sin(}\theta\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{[-1,+1]}. \b \cw{acos(}\e{x}\cw{)} returns an angle \theta in radians, in the range \cw{[0,+}\pi\cw{]}, such that \cw{cos(}\theta\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{[-1,+1]}. \b \cw{atan(}\e{x}\cw{)} returns an angle \theta in radians, in the range \cw{[-}\pi\cw{/2,+}\pi\cw{/2]}, such that \cw{tan(}\theta\cw{)}\_\cw{=}\_\e{x}. \b \cw{atan2(}\e{y}\cw{,}\e{x}\cw{)} returns an angle \theta in radians, in the range \cw{(-}\pi\cw{,+}\pi\cw{]}, such that the vector \cw{(sin(}\theta\cw{),cos(}\theta\cw{))} is a positive real multiple of the input vector \cw{(}\e{y}\cw{,}\e{x}\cw{)}. It is an error to use this function with \e{y}\_\cw{=}\_\e{x}\_\cw{=}\_\cw{0}. \b \cw{sind(}\e{x}\cw{)} returns the sine of \e{x}, interpreted in degrees. \b \cw{cosd(}\e{x}\cw{)} returns the cosine of \e{x}, interpreted in degrees. \b \cw{tand(}\e{x}\cw{)} returns the tangent of \e{x}, interpreted in degrees. It is an error to use this function with \e{x} being an odd integer multiple of 90. \b \cw{asind(}\e{x}\cw{)} returns an angle \theta in degrees, in the range \cw{[-90,+90]}, such that \cw{sind(}\theta\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{[-1,+1]}. \b \cw{acosd(}\e{x}\cw{)} returns an angle \theta in degrees, in the range \cw{[0,+180]}, such that \cw{cosd(}\theta\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{[-1,+1]}. \b \cw{atand(}\e{x}\cw{)} returns an angle \theta in degrees, in the range \cw{[-90,+90]}, such that \cw{tand(}\theta\cw{)}\_\cw{=}\_\e{x}. \b \cw{atan2d(}\e{y}\cw{,}\e{x}\cw{)} returns an angle \theta in degrees, in the range \cw{(-180,+180]}, such that the vector \cw{(sind(}\theta\cw{),cosd(}\theta\cw{))} is a positive real multiple of the input vector \cw{(}\e{y}\cw{,}\e{x}\cw{)}. It is an error to use this function with \e{y}\_\cw{=}\_\e{x}\_\cw{=}\_\cw{0}. \b \cw{exp(}\e{x}\cw{)} returns \e{e} to the power of \e{x}. \b \cw{exp2(}\e{x}\cw{)} returns 2 to the power of \e{x}. \b \cw{exp10(}\e{x}\cw{)} returns 10 to the power of \e{x}. \b \cw{log(}\e{x}\cw{)} returns the natural logarithm of \e{x}. It is an error to use this function if \e{x}\_\cw{<=}\_\cw{0}. \b \cw{log(}\e{x}\cw{,}\e{b}\cw{)} returns the logarithm of \e{x} to the base \e{b}. It is an error to use this form of the \c{log} function if \e{x}\_\cw{<=}\_\cw{0}, or if \e{b}\_\cw{<=}\_\cw{0}, or if \e{b}\_\cw{=}\_\cw{1}. \b \cw{log2(}\e{x}\cw{)} returns the logarithm of \e{x} to the base 2. It is an error to use this function if \e{x}\_\cw{<=}\_\cw{0}. \b \cw{log10(}\e{x}\cw{)} returns the logarithm of \e{x} to the base 10. It is an error to use this function if \e{x}\_\cw{<=}\_\cw{0}. \b \cw{expm1(}\e{x}\cw{)} returns the same as \cw{exp(}\e{x}\cw{)}\_\cw{-}\_\cw{1}. \b \cw{log1p(}\e{x}\cw{)} returns the same as \cw{log(1}\_\cw{+}\_\e{x}\cw{)}. It is an error to use this function if \e{x}\_\cw{<=}\_\cw{-1}. \b \cw{pow(}\e{x}\cw{,}\e{y}\cw{)} returns \e{x} raised to the power \e{y}. This is synonymous with the \c{^} and \c{**} operators described in \k{operators}, and included for convenience if pasting expressions out of languages such as C. \b \cw{sinh(}\e{x}\cw{)} returns the hyperbolic sine of \e{x}. \b \cw{cosh(}\e{x}\cw{)} returns the hyperbolic cosine of \e{x}. \b \cw{tanh(}\e{x}\cw{)} returns the hyperbolic tangent of \e{x}. \b \cw{asinh(}\e{x}\cw{)} returns the value \e{y} such that \cw{sinh(}\e{y}\cw{)}\_\cw{=}\_\e{x}. \b \cw{acosh(}\e{x}\cw{)} returns the non-negative value \e{y} such that \cw{cosh(}\e{y}\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x}\_\cw{<}\_\cw{1}. \b \cw{atanh(}\e{x}\cw{)} returns the value \e{y} such that \cw{tanh(}\e{y}\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{[-1,+1]}. \b \cw{gamma(}\e{x}\cw{)} and \cw{tgamma(}\e{x}\cw{)} return the gamma function of \e{x}. (The two function names are synonymous \dash \cw{tgamma} is provided for convenience if pasting expressions out of C.) It is an error to use this function if \e{x} is a negative integer or zero. \b \cw{lgamma(}\e{x}\cw{)} returns the logarithm of \cw{gamma(}\e{x}\cw{)}, or the logarithm of \cw{-gamma(}\e{x}\cw{)} if \cw{gamma(}\e{x}\cw{)}\_\cw{<}\_\cw{0}. It is an error to use this function if \e{x} is a negative integer or zero. \b \cw{erf(}\e{x}\cw{)} returns the error function of \e{x}. \b \cw{erfc(}\e{x}\cw{)} returns the same as \cw{1}\_\cw{-}\_\cw{erf(}\e{x}\cw{)}. \b \cw{Phi(}\e{x}\cw{)} and \cw{norm(}\e{x}\cw{)} return the cumulative normal distribution function of \e{x}. (The two names are synonymous.) \b \cw{erfinv(}\e{x}\cw{)} and \cw{inverf(}\e{x}\cw{)} both return the value \e{y} such that \cw{erf(}\e{y}\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{(-1,+1)}. \b \cw{erfcinv(}\e{x}\cw{)} and \cw{inverfc(}\e{x}\cw{)} both return the value \e{y} such that \cw{erfc(}\e{y}\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{(0,2)}. \b \cw{Phiinv(}\e{x}\cw{)}, \cw{norminv(}\e{x}\cw{)}, \cw{invPhi(}\e{x}\cw{)} and \cw{invnorm(}\e{x}\cw{)} all return the value \e{y} such that \cw{Phi(}\e{y}\cw{)}\_\cw{=}\_\e{x}. It is an error to use this function if \e{x} is not in the range \cw{(0,1)}. \b \cw{W(}\e{x}\cw{)} and \cw{Wn(}\e{x}\cw{)} are the two branches of the Lambert W function. Each of them returns a value \e{y} such that \e{y}\_\cw{exp(}\e{y}\cw{)}\_\cw{=}\_\e{x}. \cw{W(}\e{x}\cw{)} returns \e{y}\_\cw{>=}\_\cw{-1}, and it is an error to use it if \e{x}\_\cw{<}\_\cw{-1/e}; \cw{Wn(}\e{x}\cw{)} returns \e{y}\_\cw{<=}\_\cw{-1}, and it is an error to use it if \e{x} is not in the range \cw{[-1/e,0)}. \b \cw{Ei(}\e{x}\cw{)} is the indefinite integral of \cw{exp(}\e{x}\cw{)/}\e{x}, defined in the negative domain so that its limit at negative infinity is zero, and defined in the positive domain (to deal with the pole at \e{x}\_\cw{=}\_\cw{0}) so that the limit of \cw{Ei(-}\epsilon\cw{)-Ei(+}\epsilon\cw{)} is zero as \epsilon\_\tendsto\_\cw{0}. It is an error to use this function with \e{x}\_\cw{=}\_\cw{0}. \b \cw{En(}\e{n}\cw{,}\e{x}\cw{)} is defined for non-negative integer \e{n}, and is the \e{n}-times-iterated indefinite integral of \cw{exp(-}\e{x}\cw{)/}\e{x}. That is, \cw{En(0,}\e{x}\cw{)} is just equal to \cw{exp(-}\e{x}\cw{)/}\e{x}; and for all larger \e{n}, \cw{En(}\e{n}\cw{,}\e{x}\cw{)} is the indefinite integral of \cw{-En(}\e{n}\cw{-1,}\e{x}\cw{)}, defined so that its limit at infinity is zero. (The sign is flipped each time so that the function is positive for all \e{n}.) It is an error to use this function with \e{x}\_\cw{<}\_\cw{0}, or with \e{x}\_\cw{=}\_\cw{0} and \e{n} equal to either \cw{0} or \cw{1}. \b \cw{E1(}\e{x}\cw{)} is a shorthand for \cw{En(1,}\e{x}\e{)}, and is also equal to \cw{-Ei(-}\e{x}\cw{)}. It is an error to use this function with \e{x}\_\cw{<=}\_\cw{0}. \b \cw{Ein(}\e{x}\cw{)} is the indefinite integral of \cw{(1-exp(-}\e{x}\cw{))/}\e{x}, defined so that \cw{Ein(0)}\_\cw{=}\_\cw{0}. (This is the only one of the exponential integral family which is actually defined everywhere.) \b \cw{li(}\e{x}\cw{)} is the indefinite integral of \cw{1/log(}\e{x}\cw{)}, defined so that \cw{li(0)}\_\cw{=}\_\cw{0}, and dealing with the pole at \e{x}\_\cw{=}\_\cw{1} by defining the limit of \cw{li(1-}\epsilon\cw{)-li(1+}\epsilon\cw{)} to be zero as \epsilon\_\tendsto\_\cw{0} (the same trick as \cw{Ei} above). It is an error to use this function with \e{x}\_\cw{<}\_\cw{0}, or with \e{x}\_\cw{=}\_\cw{1}. \b \cw{Li(}\e{x}\cw{)} is also the indefinite integral of \cw{1/log(}\e{x}\cw{)}, but this time, defined so that \cw{Li(2)}\_\cw{=}\_\cw{0}. (That is, \cw{Li} and \cw{li} only differ by a constant.) Again, it is an error to use this function with \e{x}\_\cw{<}\_\cw{0} or with \e{x}\_\cw{=}\_\cw{1}. \b \cw{Si(}\e{x}\cw{)} is the indefinite integral of \cw{sin(}\e{x}\cw{)/}\e{x}, defined so that \cw{Si(0)}\_\cw{=}\_\cw{0}. \b \cw{si(}\e{x}\cw{)} is also the indefinite integral of \cw{sin(}\e{x}\cw{)/}\e{x}, defined so that its limit at positive infinity is zero. \b \cw{Ci(}\e{x}\cw{)} is the indefinite integral of \cw{cos(}\e{x}\cw{)/}\e{x}, defined so that its limit at positive infinity is zero. \b \cw{Cin(}\e{x}\cw{)} is the indefinite integral of \cw{(1-cos(}\e{x}\cw{))/}\e{x}, defined so that \cw{Cin(0)}\_\cw{=}\_\cw{0}. \b \cw{FresnelS(}\e{x}\cw{)} and \cw{FresnelC(}\e{x}\cw{)} are the normalised Fresnel integrals: the indefinite integral of \cw{sin(}\pi\_\e{x}\cw{^2/2)} and \cw{cos(}\pi\_\e{x}\cw{^2/2)} respectively, both defined to be zero at zero. \b \cw{UFresnelS(}\e{x}\cw{)} and \cw{UFresnelC(}\e{x}\cw{)} are the \q{unnormalised} Fresnel integrals, i.e. the integrals of \cw{sin(}\e{x}\cw{^2)} and \cw{cos(}\e{x}\cw{^2)}, also defined to be zero at zero. \b \cw{zeta(}\e{s}\cw{)} is the Riemann zeta function for real arguments. That is, for \e{s}\_\cw{>}\_\cw{1}, \cw{zeta(}\e{s}\cw{)} is the infinite sum of \e{n}\cw{^-}\e{s} over all natural numbers \e{n}. For \e{s}\_\cw{<}\_\cw{1}, the function is defined by analytic continuation. It is an error to use this function with \e{s}\_\cw{=}\_\cw{1}. \b \cw{abs(}\e{x}\cw{)} returns the absolute value of \e{x}, i.e. either \e{x} or \cw{-}\e{x}, whichever is non-negative. \b \cw{ceil(}\e{x}\cw{)} returns the smallest integer \e{n} such that \e{n}\_\cw{>=}\_\e{x}. \b \cw{floor(}\e{x}\cw{)} returns the largest integer \e{n} such that \e{n}\_\cw{<=}\_\e{x}. \b \cw{frac(}\e{x}\cw{)} returns the fractional part of \e{x}. This is always positive, even if \e{x} is negative; i.e. this is the same as \e{x}\_\cw{-}\_\cw{floor(}\e{x}\cw{)}. \b \cw{algebraic(}\e{lo}\cw{,}\e{hi}\cw{,}\e{a0}\cw{,}\e{a1}\cw{,}\hdots\cw{,}\e{an}\cw{)} returns a root of the polynomial \e{a0}\_\cw{+}\_\e{a1}\_\cw{x}\_\cw{+}\_\hdots\_\cw{+}\_\e{an}\_\cw{x^n} which lies within the interval \cw{(}\e{lo}\cw{,}\e{hi}\cw{)}. It is an error (which \spigot will not \e{reliably} detect) to use this function if the specified polynomial does not have a unique real root within that interval, and also an error if either of \e{lo} or \e{hi} is not obviously rational or any polynomial coefficient is not obviously an integer. Some of these functions are considerably slower than others. The inverses of \cw{erf} and \c{Phi} and the \cw{W} functions, in particular, are implemented by laborious interval-bisection and are very slow indeed. \H{literals} Numeric literals You can write actual numbers in \spigot expressions, of course. By default a string of digits, or a string of digits with a decimal point somewhere in it, will be interpreted in decimal. \spigot supports C-style scientific notation, in which a decimal number (with or without a decimal point) is suffixed with \c{e} followed by an optional \c{+} or \c{-} and then another decimal integer; this denotes the first number times 10 to the power of the second. For example, \c{1.2} means 1.2, but \c{1.2e10} (or \c{1.2e+10}) means 12000000000, and \c{1.2e-10} means 0.00000000012. \spigot also supports hex numbers, by prefixing \c{0x} or \c{0X} to a number, e.g. \c{0xabc} means 2748. Hex numbers can be suffixed with an exponent part similar to the decimal one described above, only using \c{p} in place of \c{e} as the separator character; this means the hex number should be multiplied by the specified power of \e{two}. For example, \c{0x1.2} means 1.125 (one and an eighth); then \c{0x1.2p1} (or \c{0x1.2p+1}) means twice that, i.e. 2.25, and \c{0x1.2p-1} means half of it, i.e. 0.5625. If you want to input numbers in bases other than 10 and 16, you can prefix a numeric literal with \c{base2:}, \c{base3:}, \hdots, \c{base36:}. For example, you could write \c{base2:11.011}, or \c{base36:ZYX.WVU}. No exponent suffix is recognised in this mode, because there's no solid convention for what it should look like or even what should be raised to the specified power (since the usual exponent-bases for bases 10 and 16 are 10 and 2 respectively). If you want to specify a number in scientific notation and an arbitrary base, write the exponent as an explicit power of something, e.g. \c{base7:1.234}\_\c{*}\_\c{7^13}. A final way you can specify literal numbers to \spigot is by specifying them as a hex number interpreted according to IEEE 754. You do this by writing \c{ieee:} followed by a number of hex digits, which must be exactly 4, 8, 16 or 32. These lengths correspond to the following formats: \b 8 hex digits means IEEE 754 single precision: a 32-bit format consisting of 1 sign bit, 8 exponent bits, and 23 mantissa bits. \b 16 hex digits means IEEE 754 double precision: a 64-bit format consisting of 1 sign bit, 11 exponent bits, and 52 mantissa bits. \b 32 hex digits means the \q{quad precision} or \q{binary128} format defined in the 2008 revision of IEEE 754: a 128-bit format consisting of 1 sign bit, 15 exponent bits, and 112 mantissa bits. \b 4 hex digits means the \q{half precision} or \q{binary16} format defined in the 2008 revision of IEEE 754: a terribly cute 16-bit format consisting of 1 sign bit, 5 exponent bits, and 10 mantissa bits. An IEEE hex bit pattern can be followed by a \c{.} and further hex digits, in which case those digits are treated as an extension to the mantissa field. For example, \c{ieee:3f800000} represents 1 (in IEEE single precision); \c{ieee:3f800001} is the next representable number, namely \cw{1+2^-23}; and \spigot will interpret \c{ieee:3f800000.8} as the number half way between those two, i.e. \cw{1+2^-24}. Infinities and NaNs are not permitted as IEEE format input. \H{constants} Built-in constants \spigot also supports a few mathematical constants under built-in names. \b \c{pi} means \pi, the ratio between a circle's radius and half its circumference. \b \c{tau} is a shorthand for \c{2*pi}: the ratio between a circle's radius and its circumference. (See the \W{http://www.tauday.com/tau-manifesto}{Tau Manifesto}.) \b \c{e} means \e{e}, the base of natural logarithms. \b \c{phi} means the golden ratio, i.e. \cw{(1+sqrt(5))/2}. \b \c{eulergamma} means the Euler-Mascheroni constant, i.e. the limit of the difference between \cw{log(}\e{n}\cw{)} and the sum of the reciprocals of the first \e{n} integers, as \e{n} tends to infinity. \b \c{apery} means Ap\u00E9{e}ry's constant, i.e. the sum of the reciprocals of the cubes of the natural numbers. (All of these numbers can be generated by other methods, such as \c{4*atan(1)} or \c{exp(1)}, but it's more convenient to have them available as predefined constants.) \H{filefd} Numbers read from files and file descriptors \spigot can read a number from a file in various formats, and use it as input to its range of mathematical operations and functions (or just output it directly in a different format). To read a number from a file in ordinary decimal, write \c{base10file:} followed by the file name. Any sequence of non-space characters following the \c{:} will be assumed to be the file name \dash even if they're punctuation or delimiters, e.g. if you write \c{sin(base10file:myfile)} then \spigot will interpret the \c{)} as part of the file name, and (even if a file with that strange name does exist) will complain that the expression is incomplete. If you need to read a file that does have a space in its name, there's a quoting syntax to permit it. If the first character after the \c{:} is either \c{'} or \c{"}, then \spigot will look for the next occurrence of the same character, and treat all characters in between as the file name. A doubled quote character will be treated as a literal quote. For example: \b \c{base10file:'my file'} will load the file \cq{my file}. \b \c{base10file:"my file"} will do the same. \b \c{base10file:'this isn''t sensible'} will load the file \cq{this isn't sensible} (the doubled \c{'} turns into a single one and does not terminate the quoted name). \b \c{base10file:"this isn't sensible"} is a neater way to specify the same name (since the \c{'} is not special in a filename quoted with \c{"}). If your file contains the number in a base other than 10, you can prefix its name with \c{base2file:}, \c{base3file:}, \hdots, \c{base36file:}. Bases larger than 36 are not supported, because after that the letters of the alphabet run out. When \spigot reads a file in base notation, it ignores newlines and white space interspersed between the digits. You can also prefix a file name with \c{cfracfile:}, in which case \spigot will expect it to contain a sequence of continued fraction coefficients (written in decimal). The coefficients can be separated by any non-digit characters you like (including newlines, like \spigot's \cw{-c} output mode, or \c{;} and \c{,} like \spigot's \cw{-c}\_\cw{-l} output mode); a minus sign is permitted before the very first coefficient to indicate a negative number, but ignored thereafter. As mentioned in \k{input}, \spigot will treat numbers read from files using any of the above keywords as if they are not an exact representation of a rational number, but a prefix of an infinitely long expansion of an irrational. So \spigot will compute the output value as far as the input precision permits, and if it reaches the point where it can't generate any further output without more input precision, it will stop, print an error message indicating which input file ran out first, and return a special exit status 2. If you really did want the contents of a file to be interpreted as the \e{exact} terminating expansion of a rational number, you can use the keywords \c{base10xfile:} (or likewise with bases other than 10) and \c{cfracxfile}, where the \c{x} stands for \q{exact}. The advantage of doing this rather than just specifying your expansion on \spigot's command line directly is performance: if your input file is extremely large, then \spigot will only have to read enough of it to generate the amount of output you asked for, whereas if you put the same number directly on the command line then \spigot would have to parse all of it before starting. If \spigot has been compiled with the feature enabled (which it typically will have been on Unix systems; use \c{spigot --version} to check), you can also read from a file descriptor in place of a file, by writing \c{base10fd:}\e{n} (or another base, as above) or \c{cfracfd:}\e{n}. If you do this, \spigot will expect its own file descriptor numbered \e{n} to already be open and readable, and will expect to read a number from there in the appropriate format. For example, if you write \c{base10fd:0}, then \spigot will expect to read a number in base 10 from its own standard input (so you might pipe the output of another program into it). To use other file descriptors, you could invoke \spigot with a shell redirection operator such as \c{3=}\_\cw{0}, and again, it can't work that out in the case where \cw{x} is \e{exactly} 0. In this kind of situation, there really is nothing \spigot can do but hang; even tentative output can't mitigate the situation. As in the previous section, if the number is \e{obviously} a rational, then \spigot can do better. If you actually ask it for \cw{floor(0)}, then it will handle that fine. But in the example above, the input to \cq{floor} is \e{non-obviously} zero, in the sense that in order to know it \spigot would have to actually \e{think about maths} rather than just computing. Therefore, if this happens to you, you can sometimes debug it by pulling out subexpressions and trying to evaluate them on their own; when you find one with a simple rational answer (probably signalled in turn by some red tentative output), see if you can prove that to be exactly the number you wanted, and substitute it in. For example: \c $ spigot 'floor(sin(pi))' \e bbbbbbbbbbbbbbbbbbbbbbb \c (spigot hangs) \e iiiiiiiiiiiiii \c $ spigot 'sin(pi)' \e bbbbbbbbbbbbbbbb \c 0 (10^-951) \e iiiiiiiiiii \c $ spigot 'floor(0)' \e bbbbbbbbbbbbbbbbb \c 0 Here, the user observes \spigot hanging, and suspects an exactness hazard somewhere in their expression. The only interesting subexpression is \cw{sin(pi)}, so the user evaluates that on its own, and gets back tentative output suggesting that the answer is exactly zero. The user thinks about it, realises that of course the answer \e{is} exactly zero, and simplifies the original expression by substituting a literal zero in place of the needlessly complicated \cw{sin(pi)}. (Of course, in this simple example case, once the user had spotted the exact zero it hardly needed to ask \spigot for floor of it! But if something more complicated were being done after the difficult point, it might well be easiest to substitute in the simple answer and try again with \spigot.) However, this debugging technique can only work in cases where the troublesome intermediate result is \e{rational}, because it's only rationals for which \spigot has a special-case handler. There would be no equivalent way to debug the \cw{tan(pi/2)} example above \dash \spigot actually \e{cannot} recognise an input value to \cq{tan} as being an odd multiple of \cw{pi/2}. (This is the reason, mentioned in \k{functions}, why the \c{tan} function can never return an error, even if you try to pass it an invalid input value. Because \e{all} the values where \c{tan} has no finite answer are irrational, so \spigot will always run into this exactness hazard which prevents it attempting to compute an infinity.) \H{hazards-internal} Internal exactness hazards A third place where this same problem can occur, potentially, is internal to \spigot itself. \spigot's internal implementation of its various functions will often need to locate the input value on one or other side of some boundary value, and if \spigot does that without sufficient caution, then it might hang forever when passed exactly the boundary value. \# When there are known exactness hazards, uncomment the commented pieces of text below :-) Also it would probably be friendly to replace this historical example with whatever one we find out actually still goes wrong. This \e{should} never happen. Exactness hazards internal to \spigot itself are \e{bugs}. I've found and fixed \#{nearly} all the ones I know of, but \#{unfortunately not quite all. The currently known remaining ones are listed below so you know to avoid them; also,} in case any more turn up, this section demonstrates how to recognise one, so that you can report it as a bug. Here's an example of an exactness hazard that \e{used} to exist. \spigot computes the power function \cw{a^b} by computing \cw{exp(b*log(a))}, in most cases. But if \cw{a}\_\cw{<}\_\cw{0}, then the right answer will have \e{magnitude} \cw{exp(b*log(abs(a)))}, but could be positive, negative or undefined depending on what kind of number \cw{b} is. So the implementation of \cw{pow}, having first ruled out a variety of special cases that don't require taking logs at all, would test the sign of \cw{a}, and run straight into a hazard if \cw{a} was non-obviously zero. For example, this could happen: \c $ spigot '0^pi' \e bbbbbbbbbbbbb \c 0 \c $ spigot 'sin(pi)' \e bbbbbbbbbbbbbbbb \c 0 (10^-951) \e iiiiiiiiiii \c $ spigot 'sin(pi)^pi' \e bbbbbbbbbbbbbbbbbbb \c (spigot used to just hang) \e iiiiiiiiiiiiiiiiiiiiiiiiii If spigot \e{knew} that \cw{a} was zero, then it could easily decide that zero to any power is zero. And it's at least capable of realising that \cw{sin(pi)} is \e{arbitrarily close} to zero \dash but it wasn't able to produce even tentative output for \cw{sin(pi)^pi}, because that internal sign test of \cw{a} hung forever trying to decide which side of zero \cw{sin(pi)} fell on. Now the bug is fixed, and the last of those commands produces perfectly good tentative output: \c $ spigot 'sin(pi)^pi' \e bbbbbbbbbbbbbbbbbbb \c 0 (10^-807) \e iiiiiiiiiii So if you suspect you're in a situation like this, where some spigot computation hangs (without even tentative output) for no reason you can see, try this diagnostic procedure: \b Narrow down to the smallest subexpression that exhibits the hang. \b Whatever function \cw{f()} is the outermost one in that subexpression, check that \spigot can evaluate each argument to \cw{f} at least as far as producing tentative output. (\cw{f} might be a literal \e{function}, written with brackets, such as \cw{sin(x)} or \cw{atan2(y,x)}, or it might be a mathematical operator such as addition or negation; the same procedure applies no matter how \cw{f} is written.) \b Think about whether the mathematical function represented by \cw{f} is continuous at the point in question. If not, then this is an unavoidable hang as described in \k{hazards-intermediate}, not an internal exactness hazard. \b If you think that \cw{f} is continuous at the specified point, and that \spigot is producing at least tentative output for all the inputs to \cw{f}, then please report it as a bug, including the exact \spigot commands you ran to demonstrate the failing subexpression and the successful evaluation of the outermost function's arguments. For example, you might have reported the above example as follows: \quote{ The following \spigot command hangs without even tentative output: \c $ spigot 'sin(pi)^pi' \e bbbbbbbbbbbbbbbbbbb I think this is an internal exactness hazard, because \spigot can evaluate the two operands to the power function safely (the former produces tentative output and the latter produces definite output): \c $ spigot 'sin(pi)' \e bbbbbbbbbbbbbbbb \c $ spigot 'pi' \e bbbbbbbbbbb And the power function is mathematically continuous at the point \cw{(0,}\pi\cw{)}, so I think \spigot should not hang in this case. } That example report contains everything needed to check the facts, reproduce the failure, and decide whether it is indeed a bug. \H{hazards-design} Design limitations One last class of hang in \spigot is due not to a bug in the implementation, but a lack of generality in the fundamental design, which couldn't really be fixed without writing a totally different program from scratch. As mentioned above, \spigot works by narrowing an interval of rationals bracketing the number. But sometimes you know that the number is somewhere within one of \e{two} intervals of rationals, and can keep narrowing both of those intervals without ever working out which one contains the number. And sometimes, depending on what you want to do with the number next, that ought in principle to be good enough. For example, it's possible to imagine a system that could cope with this: \c $ spigot 'abs(atan2(sin(pi),-1))' \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb because the more you compute the input values to \cw{atan2}, the closer you know the output is to \e{one of} \cw{+}\pi and \cw{-}\pi, even though you still don't know which \dash and since both of those values come out the same once they've been through the subsequent \cw{abs} function, you could imagine the system being clever enough to cope with the expression as a whole. But \spigot's design fundamentally is not. \C{output} Output and return values of \spigot Whatever output format you have chosen (see \k{outputopts}), \spigot will write data in the specified format to its standard output channel, i.e. Unix file descriptor 1. If \spigot successfully generates all the precision you asked for, it will print a terminating newline (if the output is any of the one-line types and you did not specify \c{-n}; see \k{miscopts}), and then it will terminate with exit status 0 (the usual signal for success). If \spigot cannot generate enough output precision because it was reading a number from an input file and that input file ran out, it will still print its terminating newline (subject to the same conditions as above) on standard output; it will also write an error message to its standard error channel (Unix file descriptor 2) indicating which input file ran out first (in case there was more than one), and terminate with exit status 2. If \spigot encounters any other failure to evaluate the expression, it will print an error message to its standard error, and terminate with exit status 1. \A{refs} References \spigot's central algorithm is derived from the paper \q{An unbounded spigot algorithm for the digits of \pi}, by Jeremy Gibbons. (American Mathematical Monthly, 113(4):318-328, 2006.) At the time of writing this, Jeremy Gibbons's web site has a \W{http://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/spigot.pdf}{PDF copy of the paper} available. Gibbons's algorithm is readily adapted to produce a continued fraction representation as output in place of base notation, and to accept numbers other than \pi as input. It forms the core of everything \spigot does, and hence it seemed appropriate to name the entire program after it. (Perhaps ironically, one part of Gibbons's algorithm that \spigot does \e{not} use any more is his representation of \pi! It turned out that once \spigot had grown the ability to compute arctangents and do arithmetic, a representation based on Machin's formula was much faster.) My algorithm for basic arithmetic is a sort of hybrid of Gibbons's spigot algorithm and William Gosper's algorithm for doing basic arithmetic on continued fractions (which I formerly used unmodified, but had to change it because using a continued fraction representation introduces exactness hazards). Gosper's algorithm comes originally from \q{HAKMEM}: Memo 239, Artificial Intelligence Laboratory, Massachusetts Institute of Technology, Cambridge, Mass., 1972. HAKMEM can also be \W{http://hdl.handle.net/1721.1/6086}{downloaded} at the time of writing this. Also, I haven't checked with great care, but the algorithm I constructed by combining both ideas may very well be the same as the one used by Imperial College's exact real arithmetic system (see the link below), in which case, they definitely had the same idea before I did. Other algorithms used in this program came from my own head (though I'm not aware that any of them came from my head \e{first}). \spigot is not the only exact real calculator around, and was not really written with the aim of filling any particular gap in the available software. (I mostly wrote it for the fun of playing with Gibbons's elegant algorithm, and only realised some time later that it had become useful enough to be worth publishing.) Hence, if \spigot is of interest to you, other implementations might also be of interest. Here are some that I managed to find: \b Keith Briggs's \W{http://keithbriggs.info/xr.1.html}{xr}: a library callable from C programs. \b The \W{http://www.doc.ic.ac.uk/exact-computation/}{Imperial College Exact Real Arithmetic Library}, also callable from C. \b David Plume's \W{http://www.cs.bham.ac.uk/~mhe/research.html}{exact real calculator}, written in Haskell. \b Hans Boehm's \W{http://www.hboehm.info/new_crcalc/CRCalc.html}{Constructive Reals Calculator}, presented as a Java applet. \A{licence} Copyright statement and licence spigot is copyright 2007-2014 Simon Tatham. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. spigot-0.2017-01-15.gdad1bbc6/misc.cpp0000644000000000000000000000725512514752325013631 0ustar /* * misc.cpp: miscellaneous utility functions. */ #include #include #include #include "spigot.h" void bigint_print_nl(const bigint &a) { bigint_print(a); putchar('\n'); } void debug_printv(FILE *fp, const char *fmt, va_list ap) { /* * This is a printf-type function, but with a very simple set of * formatting directives, including a bigint and a matrix of * bigints: * * %d expects an int * %s expects a string * %B expects a bool * %p expects a pointer * %b expects a 'const bigint *' * %4m expects a 'const bigint *' with 4 (or however many) bigints * %% a literal % sign, of course */ while (*fmt) { char c = *fmt++; if (c != '%') { fputc(c, fp); } else { char f = *fmt++; assert(f != '\0'); int param = 0; while (isdigit(f)) { param = 10 * param + (f - '0'); f = *fmt++; assert(f != '\0'); } switch (f) { case '%': fputc('%', fp); break; case 'd': { assert(param == 0); int i = va_arg(ap, int); fprintf(fp, "%d", i); } break; case 's': { assert(param == 0); const char *s = va_arg(ap, const char *); fprintf(fp, "%s", s); } break; case 'B': { assert(param == 0); bool b = (bool)va_arg(ap, int); fprintf(fp, "%s", b ? "true" : "false"); } break; case 'p': { assert(param == 0); void *p = va_arg(ap, void *); // Pointers are put in square brackets, to match // the line prefixes in dprintv in spigot.cpp. fprintf(fp, "[%p]", p); } break; case 'b': { assert(param == 0); const bigint *bi = va_arg(ap, const bigint *); bigint_print(*bi); } break; case 'm': { assert(param > 0); const bigint *mx = va_arg(ap, const bigint *); for (int i = 0; i < param; i++) { if (i > 0) fputc(' ', fp); else fputc('[', fp); bigint_print(mx[i]); } fputc(']', fp); } break; default: assert(!"Bad debug formatting directive"); break; } } } } void dprint(const char *fmt, ...) { va_list ap; va_start(ap, fmt); debug_printv(stdout, fmt, ap); va_end(ap); printf("\n"); } int get_sign(Spigot *x) { StaticGenerator test(x); return test.get_sign(); } int parallel_sign_test(Spigot *x1, Spigot *x2) { StaticGenerator test1(x1), test2(x2); while (1) { int s; if ((s = test1.try_get_sign()) != 0) return s; if ((s = test2.try_get_sign()) != 0) return 2*s; } } bigint gcd(bigint a, bigint b) { a = bigint_abs(a); b = bigint_abs(b); while (b != 0) { bigint t = b; b = a % b; a = t; } return a; } spigot-0.2017-01-15.gdad1bbc6/monotone.cpp0000644000000000000000000002267013025565031014524 0ustar /* * monotone.cpp: Reusable framework for constructing second-level * spigots which compute a continuous and locally monotonic function * of a real, given a means of computing that function for * rationals. * * The basic idea is pretty simple. We have a spigot-construction * function implementing some function f on rationals, which is * monotonic (one way or the other, it doesn't matter) on an * interval surrounding the target real x. We compute a pair of * successive continued fraction convergents x1,x2 to x; then we * construct spigots for f(x1) and f(x2), and extract continued * fraction terms from both until they stop agreeing with each * other. Since continued fraction convergents alternate above and * below the target real, we can therefore be sure that all the * output terms are valid for f(x) proper. Then we fetch two more * convergents and start again, throwing away the first few output * terms on the grounds that we've already output them. * * The vital criterion is that f should be monotonic in the region * around all the forthcoming convergents of x. (Otherwise f(x1) and * f(x2) might agree on a continued fraction term which isn't valid * for f(x).) If this isn't the case at the point you want to call * this function, extract a few more convergents until it is! */ #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" class MonotoneHelper : public BinaryIntervalSource { MonotoneConstructor *fcons; Spigot *xorig, *xcloned; BracketingGenerator x; BracketingGenerator *fx1, *fx2; int crState; unsigned inbits, outbits; bigint n1, n2, d, t1, t2; public: MonotoneHelper(MonotoneConstructor *afcons, Spigot *ax) : xorig(ax), xcloned(ax->clone()), x(xcloned) { fcons = afcons; fx1 = fx2 = NULL; crState = -1; dprint("hello MonotoneHelper %p", xcloned); } ~MonotoneHelper() { delete xorig; if (fx1) delete fx1; if (fx2) delete fx2; delete fcons; } virtual MonotoneHelper *clone() { return new MonotoneHelper(fcons->clone(), xorig->clone()); } virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { crBegin; /* * No point starting with really small d. Get at least a few * bits to be going on with. */ inbits = 16; /* * But on the output side, we take whatever we get. */ outbits = 0; while (1) { x.set_denominator_lower_bound_shift(inbits); inbits = inbits * 5 / 4; /* * Get a pair of bounds on x. */ x.get_bracket(&n1, &n2, &d); dprint("input bracket (%b,%b) / %b", &n1, &n2, &d); fx1 = new BracketingGenerator(fcons->construct(n1, d)); fx2 = new BracketingGenerator(fcons->construct(n2, d)); fx1->set_denominator_lower_bound_shift(outbits); fx2->set_denominator_lower_bound_shift(outbits); while (1) { { bigint nlo1, nhi1, nlo2, nhi2; unsigned dbits1, dbits2, dbits; fx1->get_bracket_shift(&nlo1, &nhi1, &dbits1); fx2->get_bracket_shift(&nlo2, &nhi2, &dbits2); dprint("output bracket 1 (%b,%b) / 2^%d", &nlo1, &nhi1, (int)dbits1); dprint("output bracket 2 (%b,%b) / 2^%d", &nlo2, &nhi2, (int)dbits2); if (dbits1 < dbits2) { nlo1 <<= dbits2-dbits1; nhi1 <<= dbits2-dbits1; dbits = dbits2; } else if (dbits2 < dbits1) { nlo2 <<= dbits1-dbits2; nhi2 <<= dbits1-dbits2; dbits = dbits1; } else { dbits = dbits1; // equality } dprint("evened output brackets (%b,%b),(%b,%b) / 2^%d", &nlo1, &nhi1, &nlo2, &nhi2, (int)dbits); if (nlo2 > nhi1 || nlo1 > nhi2) { /* * The output intervals have stopped overlapping, * which is as good a moment as any to decide it's * time to go and get more input detail. */ dprint("going round again"); break; } *ret_lo = (nlo1 < nlo2 ? nlo1 : nlo2); *ret_hi = (nhi1 > nhi2 ? nhi1 : nhi2); *ret_bits = outbits = dbits; dprint("returning (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); } crReturnV; } delete fx1; delete fx2; fx1 = fx2 = NULL; } crEnd; } }; Spigot *spigot_monotone(MonotoneConstructor *f, Spigot *x) { bigint n, d; if (x->is_rational(&n, &d)) { delete x; Spigot *ret = f->construct(n, d); delete f; return ret; } else return new MonotoneHelper(f, x); } class MonotoneInverter : public Source { MonotoneConstructor *fcons; bool increasing; bigint nlo, nhi, d; Spigot *target; int crState; int slo, shi; public: MonotoneInverter(MonotoneConstructor *afcons, bool aincreasing, bigint n1, bigint n2, bigint ad, Spigot *atarget) { fcons = afcons; increasing = aincreasing; target = atarget; if (ad < 0) { n1 = -n1; n2 = -n2; ad = -ad; } assert(n1 < n2); nlo = n1; nhi = n2; d = ad; crState = -1; } ~MonotoneInverter() { delete target; } virtual MonotoneInverter *clone() { return new MonotoneInverter(fcons->clone(), increasing, nlo, nhi, d, target->clone()); } virtual bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 1; return true; // the first matrix will probably expand this interval } virtual bool gen_matrix(bigint *matrix) { crBegin; /* * Start by converting our starting interval [0,1] into * [nlo/d, nhi/d]. */ matrix[0] = nhi - nlo; matrix[1] = nlo; matrix[2] = 0; matrix[3] = d; crReturn(false); /* * Work out what signs we expect to see at the two interval * ends. */ if (increasing) { slo = -1; shi = +1; } else { slo = +1; shi = -1; } /* * Now repeatedly narrow the interval. To avoid exactness * hazards, we trisect rather than bisecting: pick _two_ trial * points inside the existing interval, begin evaluating * f(x)-target with x equal to each of those points, and * whichever one we find out the sign of first, narrow the * interval to exclude either the first or last third. */ while (1) { { bigint ndiff = nhi - nlo; if (ndiff % 3U != 0) { d *= 3; nlo *= 3; nhi *= 3; } else { ndiff /= 3; } bigint n1 = nlo + ndiff; bigint n2 = n1 + ndiff; int s = parallel_sign_test (spigot_sub(fcons->construct(n1, d), target->clone()), spigot_sub(fcons->construct(n2, d), target->clone())); if (s == slo) { /* New interval is [n1, nhi]. */ nlo = n1; /* Narrow to the top 2/3 of prior interval */ matrix[0] = 2; matrix[1] = 1; matrix[2] = 0; matrix[3] = 3; } else if (s == -slo) { /* New interval is [nlo, n1]. */ nhi = n1; /* Narrow to the bottom 1/3 of prior interval */ matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 3; } else if (s == 2*slo) { /* New interval is [n2, nhi]. */ nlo = n2; /* Narrow to the top 1/3 of prior interval */ matrix[0] = 1; matrix[1] = 2; matrix[2] = 0; matrix[3] = 3; } else { /* New interval is [nlo, n2]. */ nhi = n2; /* Narrow to the bottom 2/3 of prior interval */ matrix[0] = 2; matrix[1] = 0; matrix[2] = 0; matrix[3] = 3; } } crReturn(false); } crEnd; } }; Spigot *spigot_monotone_invert(MonotoneConstructor *f, bool increasing, bigint n1, bigint n2, bigint d, Spigot *x) { return new MonotoneInverter(f, increasing, n1, n2, d, x); } spigot-0.2017-01-15.gdad1bbc6/noexcept.cpp0000644000000000000000000000033212514752325014510 0ustar /* * noexcept.cpp: a null implementation of spigot_check_exception() for * use in the command-line spigot program. */ #include #include #include "spigot.h" void spigot_check_exception() { } spigot-0.2017-01-15.gdad1bbc6/python/0000755000000000000000000000000013025565031013474 5ustar spigot-0.2017-01-15.gdad1bbc6/python/README.txt0000644000000000000000000000346012514752325015203 0ustar README for the spigot Python module =================================== This directory contains a native-code Python module, and a Python wrapper on that, which between them provide a programmatic Python interface to spigot's exact real computation system. The system permits parsing expressions from strings in spigot's native expression language, and also taking numbers as input from any of Python's natively supported formats. Unlike spigot proper, expressions can include variable names, which will be looked up in a dictionary provided to the parse function, so that you can parametrise your expression with variables you had lying around anyway without having to mess about constructing expression strings out of them. Results can be extracted in forms convenient to further processing, such as an iterator returning continued fraction terms as Python long integers, or convergents as pairs of long integers, or boolean comparison functions. A small test program is provided, and also a more interesting demo program 'powbegin.py' which answers a more or less frivolous question by using spigot's unlimited precision to implement a search strategy far more efficient than the obvious one. WARNING: This Python module is NOT FINISHED! I didn't spend a lot of time on devising the API; I just threw together whatever seemed simplest. It might very well get a redesign in the future, so beware of writing any applications based on it. On the other hand, if you do want to write applications based on this, please get in touch and let me know what you do and don't like about it - that will be useful if I ever do think about a redesign. This system reuses the spigot source files, but is independent of the main spigot autoconf build system. Use setup.py in this subdirectory to build the extension in the standard Python manner. spigot-0.2017-01-15.gdad1bbc6/python/powbegin.py0000755000000000000000000006020213025565031015663 0ustar #!/usr/bin/env python # Demo application of the spigot Python module. # # This program expects two integer arguments a,b, and will use a # technique based on continued fractions to efficiently search for the # smallest n such that a^n begins with the digits of b. (More exactly, # such that frac(log2(a^n)) is between frac(log2(b)) and # frac(log2(b+1)).) import sys, os, string, getopt, traceback, signal, itertools from spigot import Spigot def debug(dbgclass, *args): if dbgclass in dbgclasses_enabled: sys.stdout.write("[%s] %s\n" % (dbgclass, " ".join(map(str,args)))) def format_in_base(n, base): assert base <= 36 digits = string.digits + string.ascii_uppercase ret = "" while True: ret = digits[n % base] + ret n /= base if n == 0: return ret def make_f(x): def f(n): if n == 0: return Spigot(0) return Spigot("frac(x*n)", {"x":x, "n":Spigot(n)}) return f def floor_within_limits(spig, lo, hi): """Return floor(spig), clamped to the range [lo,hi].""" # The purpose of this function is to avoid an exactness hazard if # spig happens to non-obviously evaluate to an exact integer # _outside_ the target range. If it evaluates to one inside the # range then there's nothing we can do, but if it's out of range # then we can at least detect that without wasting time on # figuring out exactly _what_ out-of-range thing it might be. if lo is not None and lo == hi: return lo if lo is not None and spig < lo+1: return lo if hi is not None and spig >= hi: return hi return int(Spigot("floor(x)", {"x":spig})) def ceil_within_limits(spig, lo, hi): """Return ceil(spig), clamped to the range [lo,hi].""" return -floor_within_limits(-spig, -hi, -lo) class Firstupto(object): """Given some real k, return the smallest m such that frac(xm) < k. We expect our caller to give us k values in decreasing sequence, and we make use of that assumption to increase efficiency. If our caller knows that frac(xM) is exactly equal to k for some M, then passing that M as an extra parameter to our __call__ function will cause us to notice that and not suffer an exactness hazard when we reach it.""" def __init__(self, x): self.xconv = x.convergents() self.b_low = self.xconv.next()[1] try: self.b_step = self.xconv.next()[1] self.b_high = self.xconv.next()[1] except StopIteration: debug("firstupto", "continued fraction terminated extremely early") self.b_step = 1 self.b_high = self.b_low self.f = make_f(x) def __call__(self, k, M=None): debug("firstupto", "called with k =", float(k), "and M =", M) while True: debug("firstupto", "b = {", self.b_low, self.b_step, self.b_high, "}") fl = self.f(self.b_low) fs = self.f(self.b_step) debug("firstupto", "fl =", float(fl)) debug("firstupto", "fs =", float(fs)) if self.b_low != M: debug("firstupto", "normal expression: (k-fl)/(fs-1)") i_sp = Spigot("(k-fl)/(fs-1)", {"fl":fl, "fs":fs, "k":k}) else: # A roundabout alternative way to compute the same # thing, which avoids running into an exactness hazard # when the numerator (k-fl) of the above version would # have turned out to be exactly zero. fl1 = self.f(self.b_low+self.b_step) debug("firstupto", "fl1 =", float(fl1)) debug("firstupto", "b_low = M: (k-fl1)/(fs-1) + 1") i_sp = Spigot("(k-fl1)/(fs-1) + 1", {"fl1":fl1, "fs":fs, "k":k}) #debug("firstupto", "i_sp =", float(i_sp)) upper_bound = (self.b_high - self.b_low) / self.b_step + 1 if (M is not None and self.b_low <= M <= self.b_high and (M - self.b_low) % self.b_step == 0): # Special case: one of the values taken by the # arithmetic progression from b_low to b_high in steps # of b_step yields *exactly* k when given to f. That # means we can't return that value, so try each side # of it. i_avoid = (M - self.b_low) / self.b_step debug("firstupto", "have to avoid", i_avoid) debug("firstupto", "try [", 0, ",", i_avoid, "]") i = ceil_within_limits(i_sp, 0, i_avoid) if i == i_avoid: debug("firstupto", "try (", i_avoid, ",", upper_bound, "]") i = ceil_within_limits(i_sp, i_avoid+1, upper_bound) else: debug("firstupto", "try [", 0, ",", upper_bound, "]") i = ceil_within_limits(i_sp, 0, upper_bound) debug("firstupto", "i =", i) if self.b_low + i*self.b_step <= self.b_high: m = self.b_low + i*self.b_step debug("firstupto", "ok, m =", m, float(self.f(m))) return m else: debug("firstupto", "get more convergents") try: self.b_low = self.b_high self.b_step = self.xconv.next()[1] self.b_high = self.xconv.next()[1] except StopIteration: debug("firstupto", "continued fraction terminated") return None def writeaspowers(n, u, v): """Find unique integers ui,vi such that n = u^ui * v^vi. One of ui,vi might be negative. If no such ui,vi exist, return (None,None). We also return that if ui,vi are not unique, which can only happen if log(u,v) is rational.""" # Helper routine: straight Euclid's algorithm. def gcd(a,b): """Return the greatest common divisor of a and b.""" while b != 0: a, b = b, a % b return a # The main strategy of writeaspowers() is to try to find two # 'atomic' factors of u,v. By 'atomic' I don't necessarily mean # prime: a prime would do if we happen to find out, but finding # prime factors is hard in general, and we don't need anything # that specific for this purpose. What we want is some number # which you can divide off u a certain number of times and then it # becomes immediately _coprime_ to u - i.e. no smaller factor of # it will come off. And you can do the same to v. Then anything # expressible as a product of powers of u,v has that same # property, and we've acquired a linear relation saying what the # powers ui,vi have to be - namely, if our atom g has multiplicity # gu in u, gv in v and gn in n, then we know ui*gu + vi*gv = gn. # # The technique for finding such an 'atom' is to start with # gcd(u,v), and keep trying to prove it _is_ an atom, by dividing # it off u as many times as possible and then seeing if any # fraction of it still survives (i.e. if the gcd of our candidate # atom and whatever is left of u is non-trivial). If one does, we # replace our atom candidate with that smaller fraction, and try # again. So we reduce the size of our candidate in every # iteration, and hence the algorithm must terminate. # # Once we've divided off this atom, we'll then look for a second # atom between the remains of u,v, and since this search technique # will find as large an atom as possible, it follows that the # second atom will have _different_ ui and vi - i.e. the second # atom gives us a second independent linear relation, and between # those two relations, we'll have enough information to nail down # a single pair of values that ui and vi will have to have if they # exist at all. def find_common_atom(u,v): """Find a number 'atomically' dividing u and v. The returned value g should have the property that u/g^i and v/g^j, for some i,j, are both integers coprime to g.""" g = gcd(u,v) if g == 1: return None prev = max(u,v) while g < prev: prev = g # Try to 'split' our atom using each of u and v. If we # succeed in either attempt, g will reduce and the while # loop will go round again. If we fail, we'll terminate # the loop and return g. for w in u,v: # Divide off g as many times as we can. while w % g == 0: w /= g # Is there a nontrivial common factor between g and # what's left of w? gg = gcd(g, w) if gg > 1: # Yes, so let that be our new g. g = gg return g def find_multiplicity(u, g): """Return i such that u/g^i is an integer coprime to g. Returns None if no such i exists.""" ret = 0 # Divide off g as many times as we can, and count them. while u % g == 0: u /= g ret += 1 # Now we want the remains of u to be coprime to g. If not, # fail. if gcd(u, g) != 1: return None return ret def find_atom_and_multiplicities(u, v): """Find an atom between u and v, and its multiplicities. Returns a tuple (g,gu,gv) such that u/g^gu and v/g^gv are integers coprime to g.""" if u == 1 and v == 1: # Trivial case: nothing at all to find here. return None, None, None g = find_common_atom(u, v) if g == None: # We can make do with using u or v itself as an atom at a # pinch; it will have multiplicity 1 in one of u,v and 0 # in the other, which is good enough for a linear # relation. g = u if u != 1 else v return g, find_multiplicity(u, g), find_multiplicity(v, g) debug("writeaspowers", "try to write", n, "as powers of", u, "and", v) # Look for our first atom, g0. g0, g0u, g0v = find_atom_and_multiplicities(u, v) debug("writeaspowers", "found atom", g0) assert g0 is not None # Divide off g0 from both u and v, and look for a second atom in # the remainders. u0 = u / g0 ** g0u v0 = v / g0 ** g0v g1, g1u, g1v = find_atom_and_multiplicities(u0, v0) if g1 is None: # This can only happen if u and v are both exact powers of g0. # In that case, any power of g0**gcd(g0u,g0v) is representable # in infinitely many ways, and anything that is not such a # power is not representable at all. We don't distinguish # nonexistence from nonuniqueness in this function's API, so # we can just fail immediately in both cases. debug("writeaspowers", "one-atom failure case") return None, None debug("writeaspowers", "found another atom", g1) # Our atoms should be coprime. assert gcd(g0, g1) == 1 # Find the multiplicity of each atom in our target number n. If it # doesn't divide 'atomically' into n, there's no point going any # further - we'll have already proved that n is not any ratio of # powers of u,v. g0n = find_multiplicity(n, g0) if g0n is None: debug("writeaspowers", "no answer:", g0, "does not go evenly into", n) return None, None g1n = find_multiplicity(n, g1) if g1n is None: debug("writeaspowers", "no answer:", g1, "does not go evenly into", n) return None, None debug("writeaspowers", "multiplicities of", g0, g1, "in", n, ":", g0n, g1n) debug("writeaspowers", "multiplicities of", g0, g1, "in", u, ":", g0u, g1u) debug("writeaspowers", "multiplicities of", g0, g1, "in", v, ":", g0v, g1v) # Now we have our two linear relations in ui,vi, namely # # g0u * ui + g0v * vi = g0n # g1u * ui + g1v * vi = g1n # # I.e. we need to solve the matrix equation # # (g0u g0v) (ui) = (g0n) # (g1u g1v) (vi) (g1n) # # which has solution # # (ui) = 1/(g0u g1v - g0v g1u) ( g1v -g0v) (g0n) # (vi) (-g1u g0u) (g1n) det = g0u * g1v - g0v * g1u # We expect our two linear relations to be independent, i.e. the # determinant of the matrix is nonzero so the linear system has a # unique solution. I admit my proof of that was a _bit_ handwavy; # if I turn out to be wrong about it, the fix will be to go back # to the code that finds g1, check _there_ whether the two linear # relations are scalings of each other, and if so, amalgamate g0 # and g1 into a larger atom and try again to find a new g1. assert det != 0 # Numerators of the fractions giving ui and vi, with common # denominator det. ui_num = +g1v * g0n -g0v * g1n vi_num = -g1u * g0n +g0u * g1n debug("writeaspowers", "raw matrix solution is", ui_num, "/", det, "and", vi_num, "/", det) # Those fractions should have integer values, or else the solution # isn't valid. if ui_num % det or vi_num % det: return None, None ui = ui_num / det vi = vi_num / det debug("writeaspowers", "integer solution is", ui, "and", vi) # Now we know that ui,vi cannot be anything _other_ than the # values we've found. But we don't know that they _are_ the values # we've found - the remains of u,v other than our two atoms g0,g1 # might still come out wrong. So we now check by actually # computing the product u^ui v^vi, and seeing if it is equal to n. # # (Of course, the product could fail to be an integer, if one or # both of our putative ui,vi is negative, so we must check that # too.) num = u**max(ui,0) * v**max(vi,0) denom = u**max(-ui,0) * v**max(-vi,0) if num % denom != 0 or num / denom != n: debug("writeaspowers", "failure:", num, "/", denom, "!=", n) return None, None debug("writeaspowers", "success! Returning", ui, vi) return ui, vi def powbegin(a, b, base, minpower): debug("modmax", "a =", a, "b =", b, "base =", base, "minpower =", minpower) if minpower is None: # Default to choosing a minimum power which guarantees that # a^result >= b. It's pedantic and annoying to ask 'what power # of 2 begins with 100' and be told that 2^0 begins 1.00! I'd # much rather be told that 2^196 = 100433...06336. So the # default choice of minpower arranges that all the digits of b # will appear _before_ the decimal point. # # If a user really does want digits below the decimal point, # they can explicitly reset to the more straightforward # semantics by saying -m0. # # To calculate the right minimum power, spigot itself is the # easiest way - and using the dyadic log function guarantees # that this expression cannot hang. minpower = int(Spigot("ceil(log(b,a))", {"a":Spigot(a), "b":Spigot(b)})) debug("modmax", "auto minpower =", minpower) # If some power of a is equal to b times a power of the base, then # that's the absolute optimum answer, and we'll have to be aware # when we're coming up on it to avoid running into an exactness # hazard. exact_answer, _ = writeaspowers(b, a, base) debug("modmax", "exact_answer =", exact_answer) if exact_answer is not None: exact_answer -= minpower debug("modmax", " adjusted to", exact_answer) if exact_answer < 0: exact_answer = None # If a power of a is similarly equal to b+1 times a power of the # base, then that's a value that is definitely _not_ the answer # (it's on the precise upper bound of our legal interval, but the # interval is open at the top end), and we'll want to tell # firstmodbelow to make sure to avoid trying to return it. exact_non_answer, _ = writeaspowers(b+1, a, base) debug("modmax", "exact_non_answer =", exact_non_answer) if exact_non_answer is not None: exact_non_answer -= minpower debug("modmax", " adjusted to", exact_non_answer) if exact_non_answer < 0: exact_non_answer = None scope = {"a":Spigot(a), "b":Spigot(b), "base":Spigot(base), "m":Spigot(minpower)} x = Spigot("log(a,base)", scope) if exact_answer == 0: lowerbound = Spigot("0") else: lowerbound = Spigot("frac(log(b,base)-m log(a,base))", scope) if exact_non_answer == 0: y = Spigot("1") else: y = Spigot("frac(log(b+1,base)-m log(a,base))", scope) debug("modmax", "x =", float(x)) debug("modmax", "target interval [", float(lowerbound), ",", float(y), "]") if lowerbound > y: # Special case: minpower was already in the interval! return minpower f = make_f(x) firstupto = Firstupto(x) n = 0 while True: # Find m = firstupto(y - f(n)). k = y - f(n) debug("modmax", "n =", n) debug("modmax", "need k =", float(k)) if exact_non_answer is not None: debug("modmax", "exact_non_answer =", exact_non_answer) M = exact_non_answer - n else: M = None m = firstupto(k, M) debug("modmax", "got m =", m) if m is None or not f(m): # If firstupto returned None, or f(m) is actually zero, # that means the convergents of x ran out, so the value n # we already have is still the best we can do. So return # it, if it's good enough. if f(n) < lowerbound: debug("modmax", "convergents finished, no solution") return None debug("modmax", "convergents finished, returning", n) return n + minpower debug("modmax", "fm =", float(f(m))) i_sp = Spigot("(y-fn) / fm", {"fn":f(n), "fm":f(m), "y":y}) if (exact_non_answer is not None and exact_non_answer >= n and (exact_non_answer - n) % m == 0): i_avoid = (exact_non_answer - n) / m debug("modmax", "avoiding", i_avoid) i = floor_within_limits(i_sp, i_avoid, None) if i == i_avoid: i = floor_within_limits(i_sp, None, i_avoid - 1) else: i = floor_within_limits(i_sp, None, None) debug("modmax", "i =", i) # Our possible answers are n, n+m, ..., n+im. Pick the first of # those which is at least lower_bound, if any is. j_sp = Spigot("(lowerbound-fn) / fm", {"fn":f(n), "fm":f(m), "lowerbound":lowerbound}) j_limit = i+1 if (exact_answer is not None and exact_answer <= n + i*m and (exact_answer - n) % m == 0): j_limit = min(i, (exact_answer - n) / m) j = ceil_within_limits(j_sp, 0, j_limit) debug("modmax", "j =", j) if j <= i: debug("modmax", "done!") return n + j*m + minpower n = n + i*m base = 10 a = None b = None minpower = None testmode = soaktestmode = False dbgclasses_enabled = set() opts, args = getopt.gnu_getopt(sys.argv[1:], "b:d:m:tT") for opt, val in opts: if opt == "-b": base = long(val) elif opt == "-d": dbgclasses_enabled.add(val) elif opt == "-m": minpower = long(val) elif opt == "-t": testmode = True elif opt == "-T": soaktestmode = True for arg in args: if a == None: a = string.atol(arg, base) elif b == None: b = string.atol(arg, base) else: sys.stderr.write("powbegin.py: unexpected extra argument '%s'\n" % arg) sys.exit(1) if (a == None or b == None) and not (testmode or soaktestmode): sys.stderr.write("usage: powbegin.py \n") sys.exit(1) directed_test_cases = [ ((2, 13, 10, 0), 17), # nice easy basic case ((7, 10, 10, 0), 0), # b is a power of base (trivial answer!) ((7, 9, 10, 0), 13), # b+1 is a power of base ((7, 49, 10, 0), 2), # b is a power of a ((7, 48, 10, 0), 228), # b+1 is a power of a ((2, 3, 5, 0), 4), # same here, but exercises a different code path ((9, 3, 27, 0), 2), # log_{base}(a) is rational, answer exists ((9, 4, 27, 0), None), # log_{base}(a) is rational, answer does not exist ((14645, 42487246, 36, 0), 34473395), # a larger case to prove it goes fast ((49, 2022, 10, 0), 183), # regression test for a past bug ((7, 49, 10, 3), 73), # test minpower > 0 ((7, 49, 10, None), 2), # test auto-discovery of minpower ((2, 7, 10, 46), 46), # test minpower == exactly where we were aiming ((7, 49, 10, 2), 2), # same again but a different code path ] if testmode or soaktestmode: passes = 0 fails = 0 class TimeoutException(Exception): pass def sigalrm(signum, frame): raise TimeoutException def runtests(test_cases): global passes global fails for (a, b, base, minpower), expected in test_cases: desc = ( ("power of %d%s starting '%s' in base %d "+ "should be %s") % ( a, "" if minpower is None else " at least %d" % minpower, format_in_base(b, base), base, "None" if expected is None else "%d" % expected)) desc += " [-b%d %s %s%s]" % ( base, format_in_base(a, base), format_in_base(b, base), "" if minpower is None else " -m%d" % minpower) debug("test", "test case:", desc) try: signal.alarm(10) got = powbegin(a, b, base, minpower) if got != expected: print "test FAILED:", desc, "but got", got fails += 1 else: debug("test", "passed") passes += 1 except Exception as e: print "test FAILED:", desc, "but threw", repr(e) debug("test", "exception traceback:\n" + traceback.format_exc()) fails += 1 finally: signal.alarm(0) signal.signal(signal.SIGALRM, sigalrm) if testmode: debug("test", "running", len(directed_test_cases), "directed tests") runtests(directed_test_cases) if soaktestmode: debug("testgen", "generating soak tests") soak_test_dict = {} max_digits = 6 for base in 4,5,6,8,12: # numbers of the form: p, p^2, p^3, qp, qp^2 max_value = base**max_digits for a in range(2, 2*base+1): power_limit = next(power for power in itertools.count() if a**power >= max_value) debug("testgen", "doing powers of", a, "in base", base, "up to", power_limit) for power in range(power_limit-1, -1, -1): # Compute the actual value of a^power. b = a**power debug("testgen", a, "^", power, "in base", base, "=", format_in_base(b, base)) # Add zeroes on the end until it has the maximum # number of digits we handle. (This is because, # for example, if a larger power of a starts with # '10' and this one is simply '1', we'll expect # this smaller power to be returned even for the # prefix '10', because it will be treated as 1.0.) while b * base < max_value: b *= base # Insert each prefix of it into our list of # outputs. Since we process powers in descending # order, we can safely overwrite any value we find # in our dictionary already. while b > 0: debug("testgen", "inserting (", a, format_in_base(b, base), base, ") ->", power) soak_test_dict[(a, b, base, 0)] = power b /= base soak_test_cases = sorted(soak_test_dict.items()) debug("test", "running", len(soak_test_cases), "soak tests") runtests(soak_test_cases) print "passed", passes, "failed", fails sys.exit(1 if fails > 0 else 0) else: print powbegin(a, b, base, minpower) sys.exit(0) spigot-0.2017-01-15.gdad1bbc6/python/pyspig.cpp0000644000000000000000000003026213025565031015516 0ustar /* * pyspig.cpp: a Python extension which exposes the more sensible end * of spigot's internal API. */ #include #include #include "structmember.h" #include "spigot.h" #include "funcs.h" #include "expr.h" #include "error.h" #include "baseout.h" enum SpigState { SS_EMPTY, SS_FILLED, SS_FORMATTING, SS_CFRAC }; typedef struct { PyObject_HEAD Spigot *spig; CfracGenerator *cfg; OutputGenerator *og; bool og_finished; SpigState state; } SpigotPy; static PyObject *Spigot_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { SpigotPy *self; self = (SpigotPy *)type->tp_alloc(type, 0); if (self != NULL) { self->spig = NULL; self->cfg = NULL; self->og = NULL; self->state = SS_EMPTY; } return (PyObject *)self; } struct python_error {}; void spigot_check_exception() { if (PyErr_CheckSignals()) throw python_error(); } static void spigot_emplace(SpigotPy *self, Spigot *spig) { if (self) { if (self->cfg) delete self->cfg; if (self->spig) delete self->spig; if (self->og) delete self->og; self->spig = spig; self->cfg = NULL; self->og = NULL; self->state = (spig ? SS_FILLED : SS_EMPTY); } } static void Spigot_dealloc(SpigotPy *self) { spigot_emplace(self, NULL); self->ob_type->tp_free((PyObject *)self); } static PyObject *Spigot_parse(SpigotPy *self, PyObject *args); static PyObject *Spigot_clone(SpigotPy *self, PyObject *args); static PyObject *Spigot_base(SpigotPy *self, PyObject *args, PyObject *kwds); static PyObject *Spigot_ieee(SpigotPy *self, PyObject *args, PyObject *kwds); static PyObject *Spigot_readfmt(SpigotPy *self, PyObject *args); static PyObject *Spigot_cfracterm(SpigotPy *self, PyObject *args); static PyObject *Spigot_sign(SpigotPy *self, PyObject *args); static PyMethodDef Spigot_methods[] = { {"parse", (PyCFunction)Spigot_parse, METH_VARARGS, "Initialise a spigot by parsing an expression." }, {"clone", (PyCFunction)Spigot_clone, METH_VARARGS, "Initialise a spigot by cloning from another spigot." }, {"base", (PyCFunction)Spigot_base, METH_VARARGS, "Prepare a spigot for formatting data in a positional base." }, {"ieee", (PyCFunction)Spigot_ieee, METH_VARARGS, "Prepare a spigot for formatting data as an (optionally-extended)" " IEEE hex representation." }, {"readfmt", (PyCFunction)Spigot_readfmt, METH_NOARGS, "Read some data from a spigot that is in formatting mode." }, {"cfracterm", (PyCFunction)Spigot_cfracterm, METH_NOARGS, "Read a continued fraction term from a spigot." }, {"sign", (PyCFunction)Spigot_sign, METH_NOARGS, "Return the sign (-1, +1, or possibly 0) of the number in a spigot." }, {NULL} /* Sentinel */ }; static PyMemberDef Spigot_members[] = { {NULL} /* Sentinel */ }; static PyTypeObject spigot_SpigotType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "spigot.internal.Spigot", /*tp_name*/ sizeof(SpigotPy), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Spigot_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "An object containing a spigot representation of a real number", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Spigot_methods, /* tp_methods */ Spigot_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ Spigot_new, /* tp_new */ }; struct PythonGlobalScope : GlobalScope { PyObject *scope; PythonGlobalScope(PyObject *ascope) : scope(ascope) { } static Spigot *lookup_recursive(PyObject *scope, const char *varname) { if (!scope) return NULL; if (PySequence_Check(scope)) { int n = PySequence_Length(scope); for (int i = 0; i < n; i++) { PyObject *ith = PySequence_GetItem(scope, i); Spigot *ret = lookup_recursive(ith, varname); if (ret) return ret; } return NULL; } PyObject *found; if (PyMapping_Check(scope)) { if (PyMapping_HasKeyString(scope, (char *)varname)) found = PyMapping_GetItemString(scope, (char *)varname); else found = NULL; } else if (PyCallable_Check(scope)) { found = PyObject_CallFunction(scope, "s", varname); } else { /* FIXME: it would be nicer to throw a Python TypeError here */ return NULL; } if (!found) return NULL; if (!PyObject_IsInstance(found, (PyObject *)&spigot_SpigotType)) { /* * If this isn't the internal spigot class, it might be * the wrapper on the Python side which has an 'sp' * attribute. */ found = PyObject_GetAttrString(found, "sp"); if (!found || !PyObject_IsInstance(found, (PyObject *)&spigot_SpigotType)) { /* FIXME: it would be nicer to throw a Python TypeError here, * at least if found isn't the None object */ return NULL; } } return ((SpigotPy *)found)->spig->clone(); } virtual Spigot *lookup(const char *varname) { return lookup_recursive(scope, varname); } }; static PyObject *Spigot_parse(SpigotPy *self, PyObject *args) { const char *expr = NULL; PyObject *pyscope = NULL; if (!PyArg_ParseTuple(args, "s|O", &expr, &pyscope)) return NULL; try { PythonGlobalScope scope(pyscope); spigot_emplace(self, expr_parse(expr, &scope)); } catch (spigot_error err) { PyErr_SetString(PyExc_ValueError, err.errmsg); return NULL; } catch (python_error) { return NULL; } Py_RETURN_NONE; } static PyObject *Spigot_clone(SpigotPy *self, PyObject *args) { SpigotPy *from; if (!PyArg_ParseTuple(args, "O!", &spigot_SpigotType, (PyObject **)&from)) return NULL; spigot_emplace(self, from->spig ? from->spig->clone() : NULL); Py_RETURN_NONE; } static char *base_keywords[] = { "base", "digitlimit", "rmode", "minintdigits", "uppercase", NULL }; static PyObject *Spigot_base(SpigotPy *self, PyObject *args, PyObject *kwds) { int base = 10, uppercase = 0, digitlimit = 0; int rmode = -1, minintdigits = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiii", base_keywords, &base, &digitlimit, &rmode, &minintdigits, &uppercase)) return NULL; if (self->state == SS_EMPTY) { PyErr_SetString(PyExc_RuntimeError, "spigot not initialised"); return NULL; } else if (self->state != SS_FILLED) { PyErr_SetString(PyExc_RuntimeError, "spigot already formatting"); return NULL; } assert(!self->og); self->og = base_format(self->spig->clone(), base, uppercase, rmode != -1, digitlimit, (RoundingMode)rmode, minintdigits); self->og_finished = false; self->state = SS_FORMATTING; Py_RETURN_NONE; } static char *ieee_keywords[] = { "bits", "digitlimit", "rmode", NULL }; static PyObject *Spigot_ieee(SpigotPy *self, PyObject *args, PyObject *kwds) { int ieee_bits = 64, digitlimit = 0, rmode = -1; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", ieee_keywords, &ieee_bits, &digitlimit, &rmode)) return NULL; if (self->state == SS_EMPTY) { PyErr_SetString(PyExc_RuntimeError, "spigot not initialised"); return NULL; } else if (self->state != SS_FILLED) { PyErr_SetString(PyExc_RuntimeError, "spigot already formatting"); return NULL; } assert(!self->og); self->og = ieee_format(self->spig->clone(), ieee_bits, rmode != -1, digitlimit, (RoundingMode)rmode); self->og_finished = false; self->state = SS_FORMATTING; Py_RETURN_NONE; } static PyObject *Spigot_readfmt(SpigotPy *self, PyObject *args) { if (self->state != SS_FORMATTING) { PyErr_SetString(PyExc_RuntimeError, "spigot not formatting"); return NULL; } std::string out; if (self->og_finished) return PyString_FromString(""); try { while (true) { if (!self->og->get_definite_output(out)) { self->og_finished = true; return PyString_FromString(""); } if (out.size() > 0) return PyString_FromString(out.c_str()); } } catch (python_error) { return NULL; } } static PyObject *bigint_to_pylong(const bigint &n) { /* * I didn't find anything in the Python embedding docs that * permits the construction of a Python Long object from raw * binary data. Hex-formatted ASCII is therefore the most * efficient interchange format I can find. */ char *hex = bigint_hexstring(n); PyObject *ret = PyLong_FromString(hex, NULL, 16); free(hex); return ret; } static PyObject *Spigot_cfracterm(SpigotPy *self, PyObject *args) { if (self->state == SS_EMPTY) { PyErr_SetString(PyExc_RuntimeError, "spigot not initialised"); return NULL; } else if (self->state != SS_FILLED && self->state != SS_CFRAC) { PyErr_SetString(PyExc_RuntimeError, "spigot already formatting"); return NULL; } if (self->state != SS_CFRAC) { assert(self->spig && !self->cfg); self->cfg = new CfracGenerator(self->spig->clone()); self->state = SS_CFRAC; } try { bigint term; if (!self->cfg->get_term(&term)) { Py_RETURN_NONE; } return bigint_to_pylong(term); } catch (python_error) { return NULL; } } static PyObject *Spigot_sign(SpigotPy *self, PyObject *args) { if (self->state == SS_EMPTY) { PyErr_SetString(PyExc_RuntimeError, "spigot not initialised"); return NULL; } else if (self->state != SS_FILLED) { PyErr_SetString(PyExc_RuntimeError, "spigot already formatting"); return NULL; } try { StaticGenerator test(self->spig->clone()); return PyInt_FromLong((long)test.get_sign()); } catch (python_error) { return NULL; } } static PyMethodDef spigot_module_methods[] = { {NULL} /* Sentinel */ }; #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC initinternal(void) { PyObject* m; spigot_SpigotType.tp_new = PyType_GenericNew; if (PyType_Ready(&spigot_SpigotType) < 0) return; m = Py_InitModule3("spigot.internal", spigot_module_methods, "Module providing Python bindings for spigot-based exact real calculation."); Py_INCREF(&spigot_SpigotType); PyModule_AddObject(m, "Spigot", (PyObject *)&spigot_SpigotType); } spigot-0.2017-01-15.gdad1bbc6/python/pythangle.py0000755000000000000000000001336613025565031016055 0ustar #!/usr/bin/env python import sys import getopt from spigot import Spigot def gcd(a,b): while b != 0: a, b = b, a % b return a def spig_decimal(spig, length=20): s = "" spig.sp.base() while len(s) < length: s = s + spig.sp.readfmt() return s tan_expr = "tan(theta/2)" angle_expr = "atan(height/base)" limit = None print_angle = print_gaussian = False opts, args = getopt.gnu_getopt(sys.argv[1:], "dl:as") for opt, val in opts: if opt == "-d": tan_expr = "tand(theta/2)" angle_expr = "atand(height/base)" elif opt == "-l": limit = int(val) elif opt == "-a": print_angle = True elif opt == "-s": print_gaussian = True if len(args) != 1: usage = """\ usage: pythangle.py [options] options: -d interpret angle in degrees rather than radians -l LIMIT output only the first LIMIT approximations -a also print the actual angle of each approximation -s also print the Gaussian integer we squared to get it """ if len(args) == 0: sys.stdout.write(usage) sys.exit(0) else: sys.stderr.write(usage) sys.exit(1) # The basic idea of this program is as follows. # # A Pythagorean triple is obtained by squaring a Gaussian integer. # Proof: any Gaussian integer u+vi has a norm sqrt(u^2+v^2) which is # the square root of an integer. So its square, (u+vi)^2, must have a # norm which _is_ an integer, i.e. that norm together with (u+vi)^2's # real and imaginary parts form a Pythagorean triple. # # Squaring a Gaussian integer doubles its argument. So if we want a # Pythagorean triple (square Gaussian integer) with an argument near # theta, we should start by finding a Gaussian integer with an # argument near _half_ of theta, and then square it. And of course # finding Gaussian integers near to a given argument is the same # problem as finding pairs of integers near to a given ratio - it's # just that the argument / ratio are in a different representation - # so what we really do is look for rational approximations v/u to # tan(theta/2), each of which corresponds to a Gaussian integer u+vi # with argument close to theta/2, whose square is a Pythagorean triple # with argument close to theta itself. angle = Spigot(args[0]) tan_halfangle = Spigot(tan_expr, {"theta":angle}) # However, this isn't quite the whole story. A Gaussian integer u+vi # only gives a _primitive_ Pythagorean triple when squared if it is # not only in lowest terms itself (that is, gcd(u,v)=1) but also # exactly one of u,v is even. (Clearly 'lowest terms' rules out u,v # _both_ even, but u,v both odd causes trouble as well because the # real and imaginary parts both work out even in that situation.) # # But that seems odd. Suppose one of our continued-fraction # convergents does have u,v both odd (which is of course completely # plausible); then squaring it gives a Pythagorean triple with all # three values even, which means that there's a way to scale it down # by a factor of two. What's _that_ the square of? Surely that, also # being a Pythagorean triple, must be the square of _some_ Gaussian # integer - and yet it surely has to be sqrt(2) times (u+vi), which # isn't integral at all! # # The answer to this apparent paradox is that if squaring (u+vi) gives # some triple A+Bi with A,B both even, then it's not (A/2)+(B/2)i # which is the square of a smaller Gaussian integer: it's the number # you get by _swapping_ the real and imaginary parts, i.e. # (B/2)+(A/2)i. Specifically, this will turn out to be the square of # (u-v)/2 + (u+v)/2 i. (Which is a Gaussian integer, if u,v are both # odd, because u-v and u+v are both even.) In argument terms, swapping # the parts of the Pythagorean triple subtracts its argument from # pi/2, and hence the corresponding transformation to its square root # u+vi is to subtract _its_ argument from pi/4, which we achieve by # dividing (1+i) by (u+vi). (with an abs() to arrange that large # angles don't end up negative) # # So we also seek rational approximations to tan(pi/4 - theta/2) as # well as to tan(theta/2), and between both of those, we hope to find # a larger set of usefully small primitive Pythagorean triples. tan_other_halfangle = Spigot("abs((1-x)/(1+x))", {"x":tan_halfangle}) # FIXME. Finish up this other_halfangle business in some way. Known # problems: sign of tan_other_halfangle can go wrong (in which case, # surely, also sign of tan_halfangle too?); need to comment the # reasoning behind what we're even doing here. def iterator(spig, swap=False): for v, u in spig.convergents(): base, height, hypot = (u*u-v*v, 2*u*v, u*u+v*v) if base == 0 or height == 0: continue d = reduce(gcd, [base, height, hypot]) base /= d height /= d hypot /= d if swap: base, height = height, base yield base, height, hypot, u, v def merge(iter1, iter2): val1 = next(iter1) val2 = next(iter2) while True: minhypot = min(val1[2], val2[2]) if val1[2] == minhypot: yield val1 else: yield val2 while val1[2] <= minhypot: val1 = next(iter1) while val2[2] <= minhypot: val2 = next(iter2) for base, height, hypot, v, u in merge( iterator(tan_halfangle), iterator(tan_other_halfangle, swap=True)): output = "%d %d %d" % (base, height, hypot) if print_angle: angle = Spigot(angle_expr, {"height":Spigot(height), "base":Spigot(base)}) output += " angle=" + spig_decimal(angle) if print_gaussian: output += " source=%d+%di" % (u, v) print output if limit is not None: limit -= 1 if limit == 0: break spigot-0.2017-01-15.gdad1bbc6/python/setup.py0000755000000000000000000000412013025565031015206 0ustar #!/usr/bin/env python # Handy cheat-sheet (specific to the x86_64 Linux systems on which I # usually debug this): # # To build and test: # # ./setup.py build # # To build so as to debug the native-code spigot library inside # Python: # # CFLAGS="-O0" ./setup.py build --debug # # To run a single test program using the spigot module straight from # the build directory that setup.py created: # # ./testenv test.py # ./testenv powbegin.py 2 100 # # To set up your environment so that spigot-usnig programs can be run # directly: # # eval $(./testenv) # ./test.py # for example from distutils.core import setup, Extension setup(name="spigot", version="1.0", description="Exact real calculation by spigot algorithm", author="Simon Tatham", author_email="anakin@pobox.com", packages=["spigot"], package_dir={'spigot':'spig'}, ext_modules=[Extension("spigot.internal", ["pyspig.cpp", "../spigot.cpp", "../misc.cpp", "../consts.cpp", "../arithmetic.cpp", "../sqrt.cpp", "../unary.cpp", "../monotone.cpp", "../trig.cpp", "../expr.cpp", "../exp.cpp", "../gamma.cpp", "../io.cpp", "../erf.cpp", "../baseout.cpp", "../lambertw.cpp", "../algebraic.cpp", "../enforce.cpp", "../expint.cpp", "../trigint.cpp", "../zeta.cpp", ], include_dirs=[".."], libraries=["gmp"], define_macros=[("HAVE_LIBGMP",None)], )]) spigot-0.2017-01-15.gdad1bbc6/python/spig/0000755000000000000000000000000012514752325014444 5ustar spigot-0.2017-01-15.gdad1bbc6/python/spig/__init__.py0000755000000000000000000001046012514752325016561 0ustar # User-visible side of the spigot Python package. Uses Python operator # overloading to build a wrapper around the C++ spigot class that # makes it look more or less like a Python numeric type. import spigot.internal import struct import types def mkintspig(x): ret = spigot.internal.Spigot() if isinstance(x, Spigot): ret.clone(x) elif type(x) == types.FloatType: bitpat = struct.unpack(">Q", struct.pack(">d",1))[0] if 0x7ff & ~(bitpat >> 52) == 0: raise ValueError("cannot construct spigot from infinity or NaN") ret.parse("ieee:%x" % bitpat) elif type(x) == types.IntType or type(x) == types.LongType: ret.parse("%d" % x) else: raise TypeError("cannot convert %s to spigot" % repr(type(x))) return ret class Spigot(object): def __init__(self, x, scope=None): if scope != None or type(x) == types.StringType: self.sp = spigot.internal.Spigot() self.sp.parse(x, scope) else: self.sp = mkintspig(x) def __add__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("x+y", {"x":self.sp, "y":s2.sp}) def __radd__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("y+x", {"x":self.sp, "y":s2.sp}) def __sub__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("x-y", {"x":self.sp, "y":s2.sp}) def __rsub__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("y-x", {"x":self.sp, "y":s2.sp}) def __mul__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("x*y", {"x":self.sp, "y":s2.sp}) def __rmul__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("y*x", {"x":self.sp, "y":s2.sp}) def __div__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("x/y", {"x":self.sp, "y":s2.sp}) def __rdiv__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("y/x", {"x":self.sp, "y":s2.sp}) def __pow__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("x^y", {"x":self.sp, "y":s2.sp}) def __rpow__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) return Spigot("y^x", {"x":self.sp, "y":s2.sp}) def __neg__(self): return Spigot("-x", {"x":self.sp}) def __pos__(self): return self def __abs__(self): return Spigot("abs(x)", {"x":self.sp}) def __long__(self): sp = spigot.internal.Spigot() sp.clone(self.sp) intpart = sp.cfracterm() if intpart < 0: next = sp.cfracterm() if next != None: intpart = intpart + 1 return intpart def __int__(self): return int(self.__long__()) def __float__(self): sp = spigot.internal.Spigot() sp.clone(self.sp) sp.ieee(64, 0, 2) # FIXME: symbolic rounding mode constant out = "" while 1: s = sp.readfmt() if s == "": break out = out + s return struct.unpack(">d", struct.pack(">Q", long(out, base=16)))[0] def __cmp__(self, s2): if not isinstance(s2, Spigot): s2 = Spigot(s2) sp = spigot.internal.Spigot() sp.parse("x-y", {"x":self.sp, "y":s2.sp}) return sp.sign() def __nonzero__(self): sp = spigot.internal.Spigot() sp.clone(self.sp) return sp.sign() != 0 def continued_fraction(self): "Generator which returns a number's continued fraction term by term." sp = spigot.internal.Spigot() sp.clone(self.sp) while True: term = sp.cfracterm() if term is None: return yield term def convergents(self): """Generator which returns a number's continued fraction convergents as successive (numerator, denominator) 2-tuples.""" n0, n1, d0, d1 = 0, 1, 1, 0 for term in self.continued_fraction(): n0, n1 = n1, term*n1+n0 d0, d1 = d1, term*d1+d0 yield (n1, d1) spigot-0.2017-01-15.gdad1bbc6/python/test.py0000644000000000000000000000136412514752325015037 0ustar import sys, itertools from spigot import Spigot def spigprint(spig): s = "" spig.sp.base() while len(s) < 40: s = s + spig.sp.readfmt() print s pi = Spigot("pi") def funcscope(name): if name == "y": return pi return None pisquared = Spigot("x^2", [{'x':pi}, funcscope]) spigprint(pisquared) pisquared = Spigot("y^2", [{'x':pi}, funcscope]) spigprint(pisquared) test = Spigot("pi^e - exp(pi)") print test.sp.sign() test = Spigot("tan(1e100)") print float(test) test = Spigot("e") print ",".join(["%d" % n for n in itertools.islice(test.continued_fraction(), 0, 30)]) test = Spigot("pi") print ",".join(["%d/%d" % (n,d) for (n,d) in itertools.islice(test.convergents(), 0, 10)]) spigot-0.2017-01-15.gdad1bbc6/python/testenv0000755000000000000000000000213713025565031015115 0ustar #!/usr/bin/env python import sys import os import glob import pipes # Set up PYTHONPATH to use the output of 'setup.py build', which will # be in a subdirectory called 'lib.' of the 'build' # directory. The will depend on the particular host # architecture, so we use glob to find it, and hope to find only one # of them. builddir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "build") libdirs = glob.glob(os.path.join(builddir, "lib.*")) if len(libdirs) > 1: sys.stderr.write("Found multiple possible lib directories:" + "".join( ["\n %s" % libdir for libdir in libdirs]) + "\n") sys.exit(1) if len(libdirs) == 0: sys.stderr.write("Found no possible lib directory in %s\n" % builddir) sys.exit(1) if "PYTHONPATH" in os.environ: os.environ["PYTHONPATH"] = libdirs[0] + ":" + os.environ["PYTHONPATH"] else: os.environ["PYTHONPATH"] = libdirs[0] if len(sys.argv) > 1: os.execvp("python", ["python"] + sys.argv[1:]) else: sys.stdout.write( pipes.quote("PYTHONPATH=" + os.environ["PYTHONPATH"]) + "; export PYTHONPATH\n") spigot-0.2017-01-15.gdad1bbc6/spigot.cpp0000644000000000000000000007250413025565031014174 0ustar /* * spigot.cpp: Implementation of the Spigot base classes, i.e. * methods which convert between a spigot description and the * various other output types. * * This file is where the implementation of the core spigot * algorithm lives. */ #include #include #include #include "spigot.h" #include "cr.h" /* ---------------------------------------------------------------------- * Class definitions private to this source file. */ class Core1 : public Core { Source *source; bool started; bigint matrix[4]; bigint bot, top; bool top_is_infinite; void endpoints_internal(bigint *endpoints, const bigint *finalmatrix); public: Core1(Source *asource); ~Core1(); virtual void premultiply(const bigint matrix[4]); virtual Core *clone(); virtual void refine(); virtual int max_endpoints(); virtual int endpoints(bigint *endpoints); /* * Because Core1 has a unique child Source, it passes through the * special-case is_rational function to that Source. */ virtual bool is_rational(bigint *n, bigint *d); }; class GeneratorSource : public BinaryIntervalSource { Core *core_orig, *core_cloned; BracketingGenerator bg; int crState; public: GeneratorSource(Core *acore); virtual ~GeneratorSource(); virtual GeneratorSource *clone(); virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits); }; class RationalSource : public Source { bigint n, d; public: RationalSource(const bigint &an, const bigint &ad); virtual RationalSource *clone(); virtual bool gen_interval(bigint *low, bigint *high); virtual bool gen_matrix(bigint *matrix); virtual bool is_rational(bigint *n, bigint *d); }; /* ---------------------------------------------------------------------- * Utility function. */ void matmul(bigint outmatrix[4], const bigint a[4], const bigint b[4]) { bigint newm0 = a[0]*b[0] + a[1]*b[2]; bigint newm1 = a[0]*b[1] + a[1]*b[3]; bigint newm2 = a[2]*b[0] + a[3]*b[2]; outmatrix[3] = a[2]*b[1] + a[3]*b[3]; outmatrix[0] = newm0; outmatrix[1] = newm1; outmatrix[2] = newm2; } /* ---------------------------------------------------------------------- * Diagnostics. */ Debuggable::Debuggable() : debugging(false) { } Debuggable::~Debuggable() { } void Debuggable::dprintv(const char *fmt, va_list ap) { if (!debugging) return; printf("[%p] ", this); debug_printv(stdout, fmt, ap); putchar('\n'); } /* ---------------------------------------------------------------------- * Administrivia: tiny functions in abstract base classes. */ bool Coreable::is_rational(bigint * /*n*/, bigint * /*d*/) { return false; } Core *Source::toCore() { return new Core1(this); } Core *Core::toCore() { return this; } Source *Source::toSource() { return this; } Source *Core::toSource() { return new GeneratorSource(this); } /* ---------------------------------------------------------------------- * Core1, the primary default implementation of Core. */ Core1::Core1(Source *asource) : source(asource) , started(false) { dprint("hello Core1 %p", asource); } Core1::~Core1() { delete source; } void Core1::premultiply(const bigint inmatrix[4]) { dprint("premultiply: %4m %4m", inmatrix, matrix); matmul(matrix, inmatrix, matrix); dprint("matrix after premult %4m", matrix); } Core *Core1::clone() { return new Core1(source->clone()); } void Core1::refine() { bool force_absorb = false; dprint("refine started"); if (!started) { /* * Initialise with the identity matrix, and fetch the * interval bounds. */ matrix[0] = matrix[3] = 1; matrix[1] = matrix[2] = 0; source->gen_interval(&bot, &top); top_is_infinite = (top == 0); /* special case meaning infinity */ started = true; dprint("Core1 init: interval [%b,%b]", &bot, &top); } do { bigint inmatrix[4]; force_absorb = source->gen_matrix(inmatrix); dprint("postmultiply: %4m %4m", matrix, inmatrix); matmul(matrix, matrix, inmatrix); dprint("matrix after postmult %4m", matrix); } while (force_absorb); } int Core1::max_endpoints() { return 2; } int Core1::endpoints(bigint *endpoints) { dprint("endpoints for %4m", matrix); endpoints[0] = matrix[0] * bot + matrix[1]; endpoints[1] = matrix[2] * bot + matrix[3]; dprint(" 1st endpoint %b / %b", &endpoints[0], &endpoints[1]); if (!top_is_infinite) { endpoints[2] = matrix[0] * top + matrix[1]; endpoints[3] = matrix[2] * top + matrix[3]; dprint(" finite 2nd endpoint %b / %b", &endpoints[2], &endpoints[3]); } else { /* * To evaluate a Mobius function at infinity, we take the * limit as x -> inf of its value at x. * * If at least one of a,c is nonzero, then the constant terms * b,d become insignificant, and the limit of (ax+b)/(cx+d) is * the same as the limit of ax/cx. But if a,c are both zero, * so that we have a constant function, then (ax+b)/(cx+d) = * b/d for any finite x, and hence the limit at infinity is * b/d as well. */ if (matrix[0] == 0 && matrix[2] == 0) { endpoints[2] = matrix[1]; endpoints[3] = matrix[3]; } else { endpoints[2] = matrix[0]; endpoints[3] = matrix[2]; } dprint(" infinite 2nd endpoint %b / %b", &endpoints[2], &endpoints[3]); } return 2; } bool Core1::is_rational(bigint *n, bigint *d) { return source->is_rational(n, d); } /* ---------------------------------------------------------------------- * The Generator class. */ Generator::Generator(Coreable *acore) : started(false), we_are_now_constant(false) { core = acore->toCore(); max_endpoints = core->max_endpoints(); assert(max_endpoints > 0); endpoints = new bigint[2 * max_endpoints]; dprint("hello Generator %p", core); } Generator::~Generator() { delete core; delete[] endpoints; } void Generator::ensure_started() { if (!started) { core->refine(); started = true; } } int Generator::iterate_spigot_algorithm(bool force) { /* * This is the main loop of the spigot algorithm. It bounces on * core->refine() until the core's current state describes a * sensible interval rather than something with a pole in it, then * returns the current set of endpoints so that one of its wrapper * functions can check in turn whether there's enough detail to * satisfy the client. * * Returns the number of endpoints currently known. * * If 'force' is true, core->refine() will be called at least * once; otherwise we'll check first to see if we have enough data * already. */ ensure_started(); dprint("iterate_spigot_algorithm beginning"); while (1) { if (!we_are_now_constant && force) core->refine(); force = true; spigot_check_exception(); int n_endpoints = core->endpoints(endpoints); dprint("iterate_spigot_algorithm got endpoints:"); for (int i = 0; i < n_endpoints; ++i) dprint(" %d: %b / %b", i, &endpoints[2*i], &endpoints[2*i+1]); int d_sign = bigint_sign(endpoints[1]); bool d_signs_ok = (d_sign != 0); bool all_constant = true; for (int i = 1; i < n_endpoints; ++i) { if (endpoints[2*i] != endpoints[0] || endpoints[2*i+1] != endpoints[1]) all_constant = false; if (bigint_sign(endpoints[2*i+1]) != bigint_sign(endpoints[1])) d_signs_ok = false; } /* * We return if the interval is bounded, or if we have a * constant function. (The constant function returning * infinity counts as bounded, and is the _only_ thing * touching the point at infinity which does so!) */ if (d_signs_ok || all_constant) { /* * If our interval is a single point, set the * we_are_now_constant flag, which will inhibit any * further calls to core->refine(). (This is necessary * because spigot sources which return a matrix intended * to terminate the stream in this way cannot deal * gracefully with being called again afterwards.) */ if (all_constant) we_are_now_constant = true; dprint("iterate_spigot_algorithm returning"); return n_endpoints; } } } static void divide_with_exactness(bigint *q, bool *remainder, const bigint &n, const bigint &d) { *q = fdiv(n, d); *remainder = (*q) * d != n; } void Generator::iterate_to_bounds(bigint *rlo, bigint *rhi, bool *rlo_open, bool *rhi_open, int minwidth, const bigint *scale, bool force_refine) { /* * Returns a bounding interval for the number, bounded by the two * integers 'lo' and 'hi'. * * Also optionally (if lo_open and hi_open are non-NULL) returns * information about whether those endpoints are open or closed, * i.e. distinguishes between (a,b), (a,b], [a,b) and [a,b]. * * If minwidth is nonzero, this function iterates until hi-lo is * at most that. (Passing minwidth==1 risks never returning due to * an exactness hazard, but minwidth>=2 should reliably return.) * If minwidth is zero, we just return whatever bounding interval * we first get (though we do at least one refinement step first). * * If scale is provided, the bounding interval is not for the * number itself but for the number times 'scale'. (scale must be * positive.) */ while (1) { int n_endpoints = iterate_spigot_algorithm(force_refine); force_refine = true; // for next time bigint &lo = *rlo, &hi = *rhi; bool lo_extra, hi_extra; // In this mode, we never expect the constant-inf output function. assert(endpoints[1] != 0); if (scale) { for (int i = 0; i < n_endpoints; ++i) endpoints[2*i+0] *= *scale; } divide_with_exactness(&lo, &lo_extra, endpoints[0], endpoints[1]); hi = lo; hi_extra = lo_extra; for (int i = 1; i < n_endpoints; ++i) { bigint tmp; bool tmp_extra; divide_with_exactness(&tmp, &tmp_extra, endpoints[2*i+0], endpoints[2*i+1]); if (tmp < lo || (tmp == lo && !tmp_extra && lo_extra)) { lo = tmp; lo_extra = tmp_extra; } if (tmp > hi || (tmp == hi && tmp_extra && !hi_extra)) { hi = tmp; hi_extra = tmp_extra; } } /* * Our numbers are currently in the form (integer, boolean * saying whether there's a nonzero fraction part to go * with it). For the lower interval bound, that's * equivalent to (integer bound, boolean saying if it's an * open bound). For the higher bound, we must round the * integer part up if the fraction is nonzero. */ if (hi_extra) ++hi; if (!minwidth || hi - lo <= minwidth) { /* * OK, return. */ if (rlo_open) *rlo_open = lo_extra; if (rhi_open) *rhi_open = hi_extra; return; } } } bool Generator::iterate_to_floor_or_inf(bigint *rfloor, bool *rconstant) { /* * Iterates until the number has a constant integer part (that is, * really a constant _floor_), and returns it. * * Also fills in 'constant', if non-NULL, indicating whether the * number is _exactly_ its constant integer part. * * A second possibility is that the spigot matrix might have * turned into the constant function always returning infinity * (which signals termination in continued fraction output mode). * Returning the boolean 'false' indicates this; 'true' indicates * that a sensible integer was returned. */ bool force = false; while (1) { int n_endpoints = iterate_spigot_algorithm(force); force = true; // for next time // iterate_spigot_algorithm has ensured that all the // denominators of the endpoints have the same sign. Hence, we // need only test the first denominator for zero to tell // whether they're all zero and we have the constant-inf // function. if (endpoints[1] == 0) return false; bigint &n = *rfloor; // See if everything has the same integer part. bool ok = true; n = fdiv(endpoints[0], endpoints[1]); bool constant = endpoints[0] == n * endpoints[1]; for (int i = 1; i < n_endpoints; ++i) { bigint tmp = fdiv(endpoints[2*i+0], endpoints[2*i+1]); if (rconstant && endpoints[2*i+0] != n * endpoints[2*i+1]) constant = false; if (tmp != n) { ok = false; break; } } // If so, return it (it's already in *rfloor). Otherwise go // round again. if (ok) { if (rconstant) *rconstant = constant; return true; } } } bigint Generator::iterate_to_floor(bool *constant) { /* * Wrapper around iterate_to_floor_or_inf, which asserts that the * inf case never comes up. */ bigint ret; #ifndef NDEBUG bool finite = #endif iterate_to_floor_or_inf(&ret, constant); assert(finite); return ret; } /* ---------------------------------------------------------------------- * CfracGenerator: convert a Core into a stream of continued fraction * terms. */ CfracGenerator::CfracGenerator(Coreable *acore) : Generator(acore) , done(false) { dprint("hello CfracGenerator %p", core); } bool CfracGenerator::get_term(bigint *term) { bool ret; bigint outmatrix[4]; if (done) return false; ret = iterate_to_floor_or_inf(term); if (!ret) { /* * An infinite return value means that the continued * fraction has terminated. At this point, and forever * hereafter, we return false. */ dprint("terminated"); done = true; return false; } else { /* * Otherwise, we return this term, and premultiply in a * matrix which represents subtracting it off and taking the * reciprocal: that is, x |-> 1/(x-a) = (0x+1)/(1x-a), * represented by the matrix (0 1) * (1 -a). */ outmatrix[0] = 0; outmatrix[1] = outmatrix[2] = 1; outmatrix[3] = -*term; dprint("extract cfrac term %b: %4m", term, outmatrix); core->premultiply(outmatrix); return true; } } /* ---------------------------------------------------------------------- * ConvergentsGenerator: wrap CfracGenerator so that it returns * rational convergents reconstituted from the raw continued fraction * terms. */ ConvergentsGenerator::ConvergentsGenerator(Coreable *acore) : cfg(acore) , cvn(1) , pcvn(0) , cvd(0) , pcvd(1) { } bool ConvergentsGenerator::get_convergent(bigint *n, bigint *d) { bigint term; bool ret; /* * Fetch a continued fraction term, if one is available, and * fold it in to the current stored convergent. */ if ((ret = cfg.get_term(&term))) { bigint newcvn = cvn * term + pcvn; bigint newcvd = cvd * term + pcvd; pcvn = cvn; pcvd = cvd; cvn = newcvn; cvd = newcvd; } /* * Return the current convergent. */ *n = cvn; *d = cvd; /* * Tell our caller whether this convergent is any different from * the last one, in case they want to stop outputting if so. */ return ret; } /* ---------------------------------------------------------------------- * BracketingGenerator: generate pairs of rationals converging to a * target, avoiding exactness hazards. */ BracketingGenerator::BracketingGenerator(Coreable *acore) : Generator(acore) , bits(0) , prevbits(0) , started(false) { dprint("hello BracketingGenerator %p", core); } void BracketingGenerator::set_denominator_lower_bound_shift(unsigned newbits) { if (bits < newbits) bits = newbits; } void BracketingGenerator::set_denominator_lower_bound(bigint ad) { set_denominator_lower_bound_shift(bigint_approxlog2(ad) + 1); } void BracketingGenerator::get_bracket_shift(bigint *ret_nlo, bigint *ret_nhi, unsigned *dbits) { bigint matrix[4]; ensure_started(); // prerequisite for calling core->premultiply bits += 2; matrix[0] = (bigint)1 << (bits - prevbits); matrix[1] = matrix[2] = 0; matrix[3] = 1; core->premultiply(matrix); prevbits = bits; *dbits = bits; iterate_to_bounds(ret_nlo, ret_nhi, NULL, NULL, 2, NULL, false); dprint("returning: (%b,%b) / 2^%d", ret_nlo, ret_nhi, (int)bits); } void BracketingGenerator::get_bracket(bigint *ret_nlo, bigint *ret_nhi, bigint *ret_d) { unsigned dbits; get_bracket_shift(ret_nlo, ret_nhi, &dbits); *ret_d = (bigint)1 << dbits; } /* ---------------------------------------------------------------------- * StaticGenerator: answer queries which don't require modifying the * underlying core. */ StaticGenerator::StaticGenerator(Coreable *acore) : Generator(acore) { } bigint StaticGenerator::get_floor(bool *constant) { return iterate_to_floor(constant); } bigint StaticGenerator::get_approximate_approximant(const bigint &d) { bigint n1, n2; /* * This function returns an integer somewhere near d*x * (specifically, within 2 of it), but avoids exactness hazards by * not necessarily returning the exact floor(d*x). Useful in range * reduction, when 'nearly' is generally good enough and 'exactly' * is hazardous. */ iterate_to_bounds(&n1, &n2, NULL, NULL, 2, &d, false); return n1; } int StaticGenerator::get_sign() { bigint a, b; bool a_open, b_open; int a_sign, b_sign; /* * Treat rationals specially, since they might be exactly zero. */ if (core->is_rational(&a, &b)) { assert(b > 0); return bigint_sign(a); } /* * Otherwise, just iterate until we can agree on the sign of the * number. */ while (1) { iterate_to_bounds(&a, &b, &a_open, &b_open, 0, NULL, true); a_sign = bigint_sign(a); b_sign = bigint_sign(b); if (a_sign > 0 || (a_sign == 0 && a_open)) return +1; if (b_sign < 0 || (b_sign == 0 && b_open)) return -1; if (a_sign == 0 && b_sign == 0) return 0; } } int StaticGenerator::try_get_sign() { bigint a, b; bool a_open, b_open; int a_sign, b_sign; /* * Like get_sign(), but only does a single iteration, and returns * 0 if a (nonzero) sign was not successfully obtained. */ iterate_to_bounds(&a, &b, &a_open, &b_open, 0, NULL, true); a_sign = bigint_sign(a); b_sign = bigint_sign(b); if (a_sign > 0 || (a_sign == 0 && a_open)) return +1; if (b_sign < 0 || (b_sign == 0 && b_open)) return -1; return 0; } void StaticGenerator::premultiply(const bigint matrix[4]) { core->premultiply(matrix); } /* ---------------------------------------------------------------------- * BaseSource: abstract implementation of a spigot data source which * requires its subclass to provide a stream of digits in a specified * base. * * A number expressed in base b as * * intpart . d1 d2 d3 ... * * is expressed as a spigot composition of the functions * * ( x |-> intpart + x/b ) o ( x |-> d1 + x/b) o ( x |-> d2 + x/b) o ... * * applied to the initial interval [0,1]. */ BaseSource::BaseSource(bigint abase, bool anegate) : first_digit(true) , base(abase) , negate(anegate) { assert(base > 0); dprint("hello BaseSource %b", &base); } bool BaseSource::gen_interval(bigint *low, bigint *high) { *low = 0; *high = base; /* * Force absorption of the initial integer part, which probably * won't be within that interval. */ return true; } bool BaseSource::gen_matrix(bigint *matrix) { bigint digit; if (!gen_digit(&digit)) { /* * The positional expansion has terminated. We therefore * return the matrix corresponding to the zero function, * which is (0 0) representing x |-> (0x+0)/(0x+1). * (0 1) */ matrix[0] = matrix[1] = matrix[2] = 0; matrix[3] = 1; dprint("termination matrix %4m", matrix); } else { if (!negate || digit >= 0 || !first_digit) { /* * We want a function which maps x to x/b + k. As a * Mobius transformation, this is equivalent to * (1x + kb)/(0x + b), so we output the matrix (1 kb) * (0 b). */ matrix[0] = 1; matrix[1] = digit * base; matrix[2] = 0; matrix[3] = base; dprint("normal digit %b matrix %4m", &digit, matrix); } else { /* * Special case of a negative first digit and * display-mode input, just as in the output side. So * digit < 0; the 'real' digit we've received is * digit+1; and we therefore want to map x to * (digit+1) - x/b. Our matrix is therefore (-1 b(digit+1)) * ( 0 b ). */ matrix[0] = -1; matrix[1] = (digit + 1) * base; matrix[2] = 0; matrix[3] = base; dprint("neg first digit %b matrix %4m", &digit, matrix); } first_digit = false; } return false; } /* ---------------------------------------------------------------------- * CfracSource: abstract implementation of a spigot data source which * requires its subclass to provide a stream of continued fraction * terms. * * A number whose continued fraction expansion is * * t1 + 1/(t2 + 1/(t3 + 1/(...))) * * is expressed as a spigot composition of the functions * * ( x |-> t1 + 1/x ) o ( x |-> t2 + 1/x ) o ( x |-> t3 + 1/x ) o ... * * applied to the initial interval [0,infinity]. */ bool CfracSource::gen_interval(bigint *low, bigint *high) { *low = 0; *high = 0; /* represents infinity */ /* * Force absorption of the first matrix, in case the integer part * is negative. */ return true; } bool CfracSource::gen_matrix(bigint *matrix) { if (gen_term(&matrix[0])) { /* * We want a function which maps x to k + 1/x. As a Mobius * transformation, this is equivalent to (kx + 1)/(1x + 0), * so we output the matrix (k 1) * (1 0). */ matrix[1] = matrix[2] = 1; matrix[3] = 0; dprint("normal term %b matrix %4m", &matrix[0], matrix); } else { /* * The positional expansion has terminated. We therefore * return the matrix corresponding to the function that * always returns infinity, which is (0 1) representing * (0 0) * x |-> (0x+1)/(0x+0). * * This is the correct way to terminate a continued * fraction: the previous term (k+1/x) becomes * (k+1/infinity), which is exactly k. */ matrix[0] = matrix[2] = matrix[3] = 0; matrix[1] = 1; dprint("termination matrix %4m", matrix); } return false; } /* ---------------------------------------------------------------------- * BinaryIntervalSource: turn a stream of rational intervals with * power-of-2 denominators into a spigot source. */ BinaryIntervalSource::BinaryIntervalSource() : started(false) { dprint("hello BinaryIntervalSource"); } bool BinaryIntervalSource::gen_interval(bigint *low, bigint *high) { *low = 0; *high = 1; /* * Those numbers are almost certainly lies, so enforce the * absorption of at least one matrix. */ return true; } bool BinaryIntervalSource::gen_matrix(bigint *matrix) { bigint this_lo, this_hi; unsigned this_bits; gen_bin_interval(&this_lo, &this_hi, &this_bits); dprint("got interval (%b,%b) / 2^%d", &this_lo, &this_hi, (int)this_bits); if (!started) { /* * Initial matrix just maps the starting [0,1] directly to the * first interval we just received. */ matrix[0] = this_hi - this_lo; matrix[1] = this_lo; matrix[2] = 0; matrix[3] = (bigint)1 << this_bits; dprint("initial matrix %4m", matrix); started = true; lo = this_lo; hi = this_hi; bits = this_bits; return false; } /* * Start by putting our old and new intervals over a common * denominator, i.e. equalising bits and this_bits. */ if (bits < this_bits) { lo <<= (this_bits - bits); hi <<= (this_bits - bits); bits = this_bits; } if (this_bits < bits) { this_lo <<= (bits - this_bits); this_hi <<= (bits - this_bits); this_bits = bits; } dprint("old interval (%b,%b) / 2^%d", &lo, &hi, (int)bits); /* * Now intersect the new interval with the old one, in case the * client isn't passing us a stream of strictly nested intervals. */ if (this_lo < lo) this_lo = lo; if (this_hi > hi) this_hi = hi; dprint("new interval (%b,%b) / 2^%d", &this_lo, &this_hi, (int)bits); assert(this_lo <= this_hi); /* * Construct a matrix which narrows from the old interval to the * new one. */ matrix[0] = this_hi - this_lo; matrix[1] = this_lo - lo; matrix[2] = 0; matrix[3] = hi - lo; if (matrix[3] == 0) matrix[3] = 1; dprint("refining matrix %4m", matrix); /* * And update our current interval. */ lo = this_lo; hi = this_hi; return false; } /* ---------------------------------------------------------------------- * RationalSource: turn an exact rational number provided as input * into a spigot source. * * This is a trivial spigot description: it consists of the single * function (x |-> n/d). */ RationalSource::RationalSource(const bigint &an, const bigint &ad) : n(an), d(ad) { assert(d > 0); } RationalSource *RationalSource::clone() { return new RationalSource(n, d); } bool RationalSource::gen_interval(bigint *low, bigint *high) { // It really doesn't matter what these are *low = 0; *high = 1; return true; } bool RationalSource::gen_matrix(bigint *matrix) { /* * We want a constant function which maps any x to n/d. As a * Mobius transformation, this is equivalent to (0x + n)/(0x + * d), so we output the matrix (0 n) * (0 d). */ matrix[1] = n; matrix[3] = d; matrix[0] = matrix[2] = 0; return false; } bool RationalSource::is_rational(bigint *rn, bigint *rd) { *rn = n; *rd = d; return true; } /* * Trivial constructors for integers and rationals, which might as * well go here rather than put them in a pointless extra source file. */ Spigot *spigot_integer(bigint const &n) { return new RationalSource(n, 1); } Spigot *spigot_rational(bigint const &an, bigint const &ad) { assert(ad != 0); /* * Reduce the fraction to its lowest terms. This is partly a * space-saving measure, and partly functional: reliably doing * this means that other parts of the code can depend on it. For * instance, spigot_cbrt() will recognise a rational that's a * perfect cube divided by another perfect cube, but can't easily * recognise one that's (say) twice a perfect cube over twice * another perfect cube. */ bigint a = gcd(an, ad); /* * Normalise the sign of d to 1. */ if (ad < 0) { a = -a; } return new RationalSource(an / a, ad / a); } /* ---------------------------------------------------------------------- * GeneratorSource: turn a Core back into a Source, by reconstructing * a stream of matrices converging to the same value. * * This is done by the completely trivial method of feeding the output * of a BracketingGenerator straight to our base class of * BinaryIntervalSource. */ GeneratorSource::GeneratorSource(Core *acore) : core_orig(acore) , core_cloned(acore->clone()) , bg(core_cloned) { crState = -1; dprint("hello GeneratorSource %p", core_cloned); } GeneratorSource::~GeneratorSource() { delete core_orig; } GeneratorSource *GeneratorSource::clone() { return new GeneratorSource(core_orig->clone()); } void GeneratorSource::gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { bg.get_bracket_shift(ret_lo, ret_hi, ret_bits); } spigot-0.2017-01-15.gdad1bbc6/spigot.h0000644000000000000000000003372413025565031013642 0ustar /* * spigot.h: Declare the base classes for spigot-based exact real * arithmetic. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bigint.h" #define lenof(x) (sizeof(x)/sizeof(*(x))) #if defined HAVE_FDOPEN && defined HAVE_TCSETATTR /* * The code which permits expressions to contain atoms of the form * 'base10fd:3' and 'cfracfd:4', identifying a Unix file descriptor * from which to read a number formatted in a particular way, is * conditionalised on autoconf having told us we've got the necessary * API functions available. This should remove one obstacle to * compiling this program on non-Unix. */ #define HAVE_FDS #endif #include #include #include /* * Below are the main class definitions for the central parts of the * spigot algorithm. * * There are three basic components, which I've called Source, Core * and Generator. * * A Source is something which delivers a description of a specific * real number, in the spigot representation of a stream of matrices * and a starting interval. * * The Generator base class contains the top-level loop of the main * spigot algorithm, which checks to see if it has enough information * to produce some output, and if so, produces it and premultiplies in * a matrix representing the extraction of that output, and otherwise, * fetches more stuff from the Source. A derived class of Generator * exists for each possible format the output might be needed in: * digits in a base-n positional system, continued fraction terms, * random queries useful to clients doing further computation on the * numbers, and there's also a special-purpose Generator which * reconstitutes the number as a Source again so that it can be fed to * further computations. * * But Generator doesn't contain the whole spigot algorithm. About * half of it is separated out into the Core class. The interface * between Generator and Core consists of three key methods: Generator * can ask Core where the current endpoints of its output interval lie * (in a form rich enough to also spot things like constant functions * and poles), it can tell Core to go and absorb more data from its * Source to refine those endpoints, and it can pass a matrix back to * Core to premultiply in when it's extracting output. * * Splitting the algorithm up at that point turns out to be a good * idea purely on legibility grounds, but the main purpose is to * permit alternative Cores. The primary default Core takes a single * Source, but this system also permits a two-input Core to be slotted * in in place of the default one, to do arithmetic on two input * numbers, without having to affect the Generator side of the * mechanism. */ class Debuggable { /* * Everything in this hierarchy derives from Debuggable, but * that's not very interesting; it's only there to provide a * convenient means of doing diagnostic print statements which all * automatically include their 'this' pointer. * * All the diagnostics are disabled completely (and hence should * be optimised away at all their call sites) unless you change * the #if 0 below to #if 1; and even then, nothing will be * printed unless you call enable_debug() on some particular * object. */ protected: bool debugging; public: Debuggable(); virtual ~Debuggable(); void dprintv(const char *fmt, va_list ap); #if 0 void enable_debug() { debugging = true; } void dprint(const char *fmt, ...) { if (!debugging) return; va_list ap; va_start(ap, fmt); dprintv(fmt, ap); va_end(ap); } #else inline void enable_debug() { } inline void dprint(const char * /*fmt*/, ...) { } #endif }; class Core; class Source; class BaseSource; class CfracSource; class Coreable : public Debuggable { /* * Cores and Sources are more or less interchangeable, because any * Source is easily turned into a Core by putting a Core1 on the * front (the default one-source core, defined in spigot.cpp), and * conversely any Core can be turned back into a Source by putting * a SourceGenOuter on the front (also in spigot.cpp). Hence, we * define this common base class of Core and Source which makes * that interchangeability explicit; in each descendant, one of * toCore and toSource just returns 'this' and the other puts an * appropriate shim on the front. */ public: virtual Coreable *clone() = 0; virtual Core *toCore() = 0; virtual Source *toSource() = 0; /* * Lots of parts of this program can do something more useful if * they *know* a number is rational. Hence, all Cores and Sources * provide this virtual function, which will give their rational * value if they're known to have one. */ virtual bool is_rational(bigint *n, bigint *d); }; /* * 'Coreable' is a sensible name in terms of how the above class fits * into the complicated structure. But this is also the type that most * of the actual function implementations will be passing around all * over the place, so I'm giving it the alias name 'Spigot'. (Partly * because historically that's what the big main class was called, so * it's easier not to have to change it throughout the code; but also * I think it's realistically the best candidate for the primary * class.) */ typedef Coreable Spigot; class Core : public Coreable { /* * Ancestor of all Core classes. The boilerplate toCore and * toSource methods are filled in here, but all the routines for * doing interesting work are left to the concrete descendant. */ public: virtual Core *clone() = 0; virtual void premultiply(const bigint matrix[4]) = 0; virtual void refine() = 0; virtual int max_endpoints() = 0; virtual int endpoints(bigint *endpoints) = 0; virtual Core *toCore(); virtual Source *toSource(); }; class Source : public Coreable { /* * Ancestor of all Source classes. The boilerplate toCore and * toSource methods are filled in here, but all the routines for * doing interesting work are left to the concrete descendant. * * You can override this class and fill in the three pure virtual * methods, or some still-abstract descendants are provided below * in case that's easier. */ public: virtual Source *clone() = 0; virtual bool gen_interval(bigint *low, bigint *high) = 0; virtual bool gen_matrix(bigint *matrix) = 0; virtual Core *toCore(); virtual Source *toSource(); }; class BaseSource : public Source { /* * Abstract base class for turning a stream of digits in a * specified base into a Source. Override this class and fill in * gen_digit() (and clone() from the parent). */ bool first_digit; bigint base; bool negate; public: BaseSource(bigint base, bool negate); virtual bool gen_digit(bigint *digit) = 0; virtual bool gen_interval(bigint *low, bigint *high); virtual bool gen_matrix(bigint *matrix); }; class CfracSource : public Source { /* * Abstract base class for turning a stream of continued fraction * terms into a Source. Override this class and fill in * gen_term() (and clone() from the parent). */ public: virtual bool gen_term(bigint *term) = 0; virtual bool gen_interval(bigint *low, bigint *high); virtual bool gen_matrix(bigint *matrix); }; class BinaryIntervalSource : public Source { /* * Abstract base class for turning a stream of binary intervals * (that is, intervals of rationals with a power-of-2 denominator) * into a Source. Override this class and fill in * gen_bin_interval() (and clone() from the parent). */ bigint lo, hi; unsigned bits; bool started; public: BinaryIntervalSource(); virtual void gen_bin_interval(bigint *lo, bigint *hi, unsigned *bits) = 0; virtual bool gen_interval(bigint *low, bigint *high); virtual bool gen_matrix(bigint *matrix); }; class Generator : public Debuggable { /* * Ancestor of all Generator classes. This base class contains the * main function iterate_spigot_algorithm that everything ends up * coming back to, but exposes no way to get the results out. So * it's conceptually an abstract base class, even though it * doesn't happen to have any pure virtual methods; regardless of * that, you have to override it to get anything useful done, * because you have to add public methods that produce output. * * Generator objects have the common feature that you pass them a * Core (or at least Coreable) at construction time, and they take * ownership of it - they expect it to have been be dynamically * allocated, and they will delete it when destructed. */ bool started; bool we_are_now_constant; int max_endpoints; bigint *endpoints; private: int iterate_spigot_algorithm(bool force); protected: Core *core; void ensure_started(); void iterate_to_bounds(bigint *rlo, bigint *rhi, bool *rlo_open, bool *rhi_open, int minwidth, const bigint *scale, bool force_refine); bool iterate_to_floor_or_inf(bigint *rfloor, bool *rconstant = NULL); bigint iterate_to_floor(bool *constant = NULL); public: Generator(Coreable *acore); virtual ~Generator(); }; class CfracGenerator : public Generator { /* * Generator that produces continued fraction terms of the number. * The first call to get_term() returns the integer part; * subsequent calls return the following terms. * * No special handling is done for negative numbers. So the * integer part returned will always be floor of the actual * number, and the next term has positive sense, i.e. if the * numbers returned are a, b, c, ... then the actual number is * equal to * * a + 1 * ----------- * b + 1 * ------- * c + 1 * --- * ... */ bool done; public: CfracGenerator(Coreable *acore); bool get_term(bigint *retterm); }; class ConvergentsGenerator { /* * Class that returns continued-fraction _convergents_ to the * number, i.e. the actual rational numbers resulting from * successive truncations of the continued fraction. * * This class _looks_ like a generator, and you can treat it as * one for all practical purposes, but it isn't actually descended * from the Generator base class because I need to ensure that * nobody accidentally calls get_term() on the CfracGenerator it's * based on. (Otherwise a term will be missed by the tracker in * this class, and all the subsequent convergents will come out * wrong). Therefore, it _has_ a CfracGenerator rather than being * one. * * (I suppose that makes the class's name a misnomer - but it * would surely be even uglier to call it something like * ConvergentsNotReallyAGenerator.) */ CfracGenerator cfg; bigint cvn, pcvn, cvd, pcvd; public: ConvergentsGenerator(Coreable *acore); bool get_convergent(bigint *n, bigint *d); }; class BracketingGenerator : public Generator { /* * Class that returns pairs of increasingly precise rational * numbers over a common denominator bracketing the target number, * but which avoids exactness hazards by not insisting that they * be the very best rationals for a given denominator. * * The denominators are always powers of two, and at your option * you can receive them as bigints (via get_bracket) or as plain * bit-shift counts (get_bracket_shift). */ unsigned bits, prevbits; bool started; public: BracketingGenerator(Coreable *acore); void get_bracket(bigint *nlo, bigint *nhi, bigint *d); void get_bracket_shift(bigint *nlo, bigint *nhi, unsigned *dbits); void set_denominator_lower_bound(bigint d); void set_denominator_lower_bound_shift(unsigned dbits); }; class StaticGenerator : public Generator { /* * This generator never premultiplies anything into its core, * unless explicitly told to. Therefore, its various methods can * safely be called in any interleaving without interfering with * one another; the only one that modifies state is the * premultiply() method, and you know when you're calling that. */ public: StaticGenerator(Coreable *acore); bigint get_floor(bool *constant); bigint get_approximate_approximant(const bigint &d); virtual int get_sign(); /* +1, -1, or 0 if exactly zero */ virtual int try_get_sign(); /* +1, -1, or 0 if not yet sure */ void premultiply(const bigint matrix[4]); /* Also, in this derived class we expose iterate_to_bounds() as public. */ using Generator::iterate_to_bounds; }; /* * Abstract base class of the various generators used in main() to * produce spigot's final output. */ class OutputGenerator : public Generator { protected: OutputGenerator(Coreable *acore) : Generator(acore) {} public: virtual ~OutputGenerator() {}; virtual bool get_definite_output(std::string &out) = 0; virtual bool get_tentative_output(std::string &out, int *digits) = 0; }; /* * Function called periodically during the inner loop of * iterate_spigot_algorithm, which can check for an interruption of * some sort and handle it by throwing a C++ exception to terminate * the iteration. */ void spigot_check_exception(); /* * Functions in misc.cpp. */ void bigint_print_nl(const bigint &a); void debug_printv(FILE *fp, const char *fmt, va_list ap); void dprint(const char *fmt, ...); /* Exact sign test. Will take ownership of x and deletes it. */ int get_sign(Spigot *x); /* Tries to find the sign of one of x1,x2, whichever gets there first. * Returns +1 or -1 if the sign is of x1, or +2 or -2 if it's x2. Will * take ownership of both x1,x2 and delete them. */ int parallel_sign_test(Spigot *x1, Spigot *x2); bigint gcd(bigint a, bigint b); spigot-0.2017-01-15.gdad1bbc6/sqrt.cpp0000644000000000000000000006312212514752325013662 0ustar /* * sqrt.cpp: Square roots. */ #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" class SqrtInteger : public CfracSource { /* * To compute the square root of an arbitrary non-square * integer, we use the following magic algorithm to find its * continued fraction. The continued fraction is periodic, so * this costs us a bounded amount of work, after which we can * cheerfully return the same sequence of matrices over and over * again. * * The algorithm, taken from Wikipedia * (http://en.wikipedia.org/wiki/Methods_of_computing_square_roots * as of 2007-04-28), is as follows: * * - Let m <- 0, d <- 1, and a <- floor(sqrt(n)). * - Repeatedly: * + output a as a continued fraction term. * + let m <- d*a-m. * + let d <- (n-m^2)/d. (This division always yields an * exact integer; see below.) * + let a <- floor((sqrt(n)+m)/d). (We can safely replace * sqrt(n) with floor(sqrt(n)) here without affecting the * result.) * + check if (m,d,a) repeats a set of values which it * previously had at this point. If so, we need not * continue calculating. * * Proof by induction that every value of d is an integer: * - Base case: d_0 is an integer, because it's defined to be * 1. * - Suppose d_i and all previous d_j are integers. * + d_i was computed as (n-m_i^2)/d_{i-1}, and hence if * it's an integer then d_{i-1} is a factor of (n-m_i^2), * and so d_i is _also_ a factor of that. * + Now m_{i+1} = d_i a_i - m_i. So n - m_{i+1}^2 is equal to * n - (m_i - d_i a_i)^2 = n - m_i^2 + 2 d_i a_i m_i - d_i^2 a_i^2. * + And d_i divides n - m_i^2 (by construction), and * divides any multiple of d_i (obviously), so it divides * that. Hence d_{i+1} will be an integer too. [] */ bigint n, m, d, a, a0; bigint **list; int listlen, listsize; int i, j; int crState; public: SqrtInteger(const bigint &radicand) { n = radicand; crState = -1; listsize = 16; listlen = 0; list = (bigint **)malloc(listsize * 3 * sizeof(bigint *)); } ~SqrtInteger() { for (int i = 0; i < listlen; i++) { delete list[3*i+0]; delete list[3*i+1]; delete list[3*i+2]; } free(list); } virtual SqrtInteger *clone() { return new SqrtInteger(n); } virtual bool gen_term(bigint *term) { crBegin; if (n < 0) { throw spigot_error("square root of negative number"); } a = a0 = bigint_sqrt(n); if (a * a == n) { /* * Special case of an exactly square number. */ *term = a; crReturn(true); crReturn(false); assert(!"Should never get here"); } m = 0; d = 1; while (1) { *term = a; crReturn(true); if (listlen >= listsize) { listsize = listlen * 5 / 4 + 16; list = (bigint **)realloc(list, listsize * 3 * sizeof(bigint *)); } list[listlen*3+0] = new bigint(m); list[listlen*3+1] = new bigint(d); list[listlen*3+2] = new bigint(a); listlen++; m = d * a - m; d = (n - m*m) / d; a = (a0 + m) / d; for (i = 0; i < listlen; i++) { if (m == *list[i*3+0] && d == *list[i*3+1] && a == *list[i*3+2]) { /* * The current m,d,a repeats at position i. * Repeat from there until the end of the list * for ever. */ while (1) { for (j = i; j < listlen; j++) { *term = *list[j*3+2]; crReturn(true); } } } } } crEnd; } }; class SqrtReal : public Source { /* * We use a variant of the usual iterative binary square root * algorithm, adjusted for this environment by removing exactness * hazards. * * Suppose, at a given point in the algorithm, we know our input * to be in the interval [k,k+4], and we've already given a * promise that our output is in the interval [r,r+2]. Hence, we * must have arranged that r^2 <= k and that (r+2)^2 >= k+4. * * Now we get more data, which tells us our input is in the * interval [k+d,k+d+1] for some input 'digit' d, which is at * least 0 and at most 3. We want to correspondingly narrow our * output to an interval [r+x,r+x+1], for some x which is at least * 0 and at most 1 and which preserves the above invariants. That * is, we want * * (r+x )^2 <= (k+d ) <=> r^2 + 2xr + x^2 <= k + d * (r+x+1)^2 >= (k+d+1) <=> r^2 + 2xr + x^2 + 2r + 2x + 1 >= k + d + 1 * * or, simplifying a bit, * * r^2 + 2xr + x^2 <= k + d <= r^2 + 2xr + x^2 + 2r + 2x * * We usually optimise slightly by not keeping a copy of the * largest number involved, namely k; instead we store the error * term e = k - r^2. So we start each step with the invariant that * * 0 <= e <= 4r * * and we seek to refine this by finding x such that * * 2xr + x^2 <= e + d <= 2xr + x^2 + 2r + 2x * * In the standard binary square root algorithm, the input digit d * would be one of {0,1,2,3} and the output bit x would be either * 0 or 1, so that the above inequality would become one of: * * 0 <= e + d <= 2r (if we choose x = 0) * 2r + 1 <= e + d <= 4r + 3 (if we choose x = 1) * * and it's clear that given the input invariants and given that * r, e and d are all integers, exactly one of those possibilities * must hold, so we always have a unique choice of x. * * But in _this_ scenario, we have to avoid exactness hazards on * input, which means we have to permit some extra values of d. * Namely, we'll add in the half-integers between 0 and 3, so that * d can be any of {0,0.5,1,1.5,2,2.5,3}. This raises the * possibility that e+d might manage to fall _between_ 2r and * 2r+1, in which case we wouldn't be able to choose an integer * output digit. Hence, we must permit the output digit 1/2 as * well, which gives us one more possible inequality on output: * * r + 1/4 <= e + d <= 3r + 3/4 (if we choose x = 1/2) * * Finally, after each step, we adjust r to its new value r+x, and * e to its new value e + d - 2xr - x^2 (in which 'r' still means * the old value). Then we scale up r by 2, and e by 4. (Because r * is in the same units as the output, and e the input.) */ Spigot *x_orig; StaticGenerator sg; bigint r, e; int crState; // For debugging the algorithm, we can store k itself if // necessary, and check that e, r, k remain in the right // relationship throughout. Uncomment this #define to do so. //#define SQRT_STORE_K #ifdef SQRT_STORE_K bigint k; #endif public: SqrtReal(Spigot *ax) : x_orig(ax->clone()), sg(ax) { crState = -1; dprint("hello SqrtReal %p", ax); } ~SqrtReal() { delete x_orig; } virtual SqrtReal *clone() { return new SqrtReal(x_orig->clone()); } virtual bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 2; return true; // force absorption of first matrix } virtual bool gen_matrix(bigint *matrix) { crBegin; /* * We kick off by finding out any old information we can about * the input number's integer part, deciding on a starting * scale factor, and mapping the number to something in the * interval [0,8]. (Conceptually [0,4], but we'll need the * next iteration of the spigot to give us output at * half-integer granularity.) * * We can throw an immediate error if the _high_ end of the * interval is negative, but if the low end of the interval is * negative, we ignore that fact and just pretend it's zero. * If the number does subsequently turn out to be negative, * we'll find that we've extracted a negative digit later in * the algorithm, and we can throw our error then. This * strategy permits us to take the square root of a non- * obviously zero number without any exactness hazard. */ { // Get some info on the integer part. bigint lo, hi; sg.iterate_to_bounds(&lo, &hi, NULL, NULL, 0, NULL, true); if (hi < 0) throw spigot_error("square root of negative number"); dprint("initial integer part range (%b,%b)", &lo, &hi); // Find a power of 4 at least as big as hi. unsigned bits = bigint_approxlog2(hi-1); bits -= (bits % 2); bits += 2; if (bits < 4) bits = 4; // bound below at 16 dprint("bits=%d", (int)bits); // Scale the input down into the range [0,8) (plus // possible negative numbers we haven't spotted yet). bigint outmatrix[4]; outmatrix[0] = 1; outmatrix[1] = outmatrix[2] = 0; outmatrix[3] = (bigint)1 << (bits - 3); dprint("premultiplying initial scale matrix %4m", outmatrix); sg.premultiply(outmatrix); // And emit an initial matrix which maps the starting // interval [0,2] into the range [0, 2^(bits/2)]. matrix[0] = (bigint)1 << (bits/2 - 1); matrix[1] = matrix[2] = 0; matrix[3] = 1; dprint("returning initial scale matrix %4m", matrix); } crReturn(false); /* * Now we can initialise the main algorithm. */ r = 0; e = 0; #ifdef SQRT_STORE_K k = 0; #endif while (1) { { // Iterate the input spigot until we find a bounding // interval of width at most 2. We're starting off in // the interval range [0,8], so narrowing down to a // width-2 subinterval of that will reliably give us a // half-integer-valued digit. bigint lo, hi; bool hi_open; sg.iterate_to_bounds(&lo, &hi, NULL, &hi_open, 2, NULL, false); dprint("integer part range (%b,%b)", &lo, &hi); // If the number has turned out negative, bomb out. If // it still only _might_ be negative, then just take // our digit to be zero, and we'll throw the error // later on when (if) we have final proof. if (hi < 0 || (hi == 0 && hi_open)) throw spigot_error("square root of negative number"); if (lo < 0) lo = 0; if (lo > 6) // clip digits at the top as well lo = 6; // OK. 'lo' is our actual digit value. (Or rather, // twice it: it's stored as 1-bit fixed point, so that // it ranges from 0 to 6 for our possible digits 0, // 1/2, ..., 5/2, 3.) bigint &d_scale2 = lo; dprint("step: r=%b e=%b d=%b/2" #ifdef SQRT_STORE_K " k=%b" #endif , &r, &e, &d_scale2 #ifdef SQRT_STORE_K , &k #endif ); assert(0 <= e); assert(e <= 4*r); #ifdef SQRT_STORE_K assert(e == k - r*r); #endif // Premultiply in a matrix which zooms the new input // interval back up to [0,8]. { bigint outmatrix[4]; outmatrix[0] = 4; outmatrix[1] = -4*lo; outmatrix[2] = 0; outmatrix[3] = 1; dprint("premultiplying digit-extract matrix %4m", outmatrix); sg.premultiply(outmatrix); } // Now figure out what digit to output. Note that r // and e are as described above, but d has twice its // nominal value. bigint e_plus_d_scale2 = 2*e + d_scale2; if (e_plus_d_scale2 <= 4*r) { // e+d <= 2r // Output digit 0. matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 2; e = 2 * e_plus_d_scale2; r = r*2; dprint("output digit 0: %4m", matrix); } else if (2*r < e_plus_d_scale2 && // r+1/4 <= e+d e_plus_d_scale2 < 6*r+2) { // e+d <= 3r+3/4 // Output digit 1/2. matrix[0] = 1; matrix[1] = 1; matrix[2] = 0; matrix[3] = 2; e = 2 * e_plus_d_scale2 - 4*r - 1; r = r*2 + 1; dprint("output digit 1/2: %4m", matrix); } else if (4*r+2 <= e_plus_d_scale2 && // 2r+1 <= e+d e_plus_d_scale2 <= 8*r+6) { // e+d <= 4r+3 // Output digit 1. matrix[0] = 1; matrix[1] = 2; matrix[2] = 0; matrix[3] = 2; e = 2 * e_plus_d_scale2 - 8*r - 4; r = r*2 + 2; dprint("output digit 1: %4m", matrix); } else { assert(!"SqrtReal: no output digit worked!"); } #ifdef SQRT_STORE_K k = 4*k + 2*d_scale2; #endif } crReturn(false); } crEnd; } }; /* * Integer cube root function. */ bigint cuberoot(bigint n) { int sign = 1; if (n < 0) { sign = -1; n = -n; } bigint T1 = 0; bigint T2 = 0; bigint x = 0; int as = bigint_approxlog2(n) / 3 + 2; bigint T0 = bigint_power(2, 3*as); while (as >= 0) { bigint T = T0 + T1 + T2; if (n >= T) { n = n - T; x = x + bigint_power(2, as); T2 = T2 + T1; T1 = T1 + 3*T0; T2 = T2 + T1; } T0 >>= 3; T1 >>= 2; T2 >>= 1; --as; } return x * sign; } class CbrtReal : public Source { /* * Cube root algorithm, basically like the square root one above * but with all the details fiddled. In particular, I think this * time it'll be convenient to put our base value k at the centre * of the interval, since cube roots can handle either positive or * negative numbers, and also because then most of the cases * become symmetric and hence easy to check against each other. * And since that will involve a lot of annoying 1/2s, I'll also * scale the whole thing up by a factor of two. * * So suppose, at a given point in the algorithm, we know our * input to be in the interval [k-8,k+8], and we've already given * a promise that our output is in the interval [r-2,r+2]. Hence, * we must have arranged that (r-2)^3 <= k-8 and that (r+2)^3 >= * k+8. * * Now we get more data, which tells us our input is in the * interval [k+d-1,k+d+1] for some input 'digit' d, which is at * least -7 and at most +7. We want to correspondingly narrow our * output to an interval [r+x-1,r+x+1], for some x which is at * least -1 and at most 1, and which preserves the above * invariants. That is, we want * * (r+x-1)^3 <= (k+d-1) * (r+x+1)^3 >= (k+d+1) * * which, similarly to the above, we rewrite as * * (r+x-1)^3+1 <= k+d <= (r+x+1)^3-1 * * and, as with the square root, we store e = k-r^3 in place of k, * so that becomes * * (r+x-1)^3 - r^3 + 1 <= e + d <= (r+x+1)^3 - r^3 - 1 (*) * * So we start each step with the invariant that * * -6r^2 + 12r <= e <= 6r^2 + 12r * * and we seek to refine this by finding an x which satisfies the * inequality marked (*) above. * * Again similarly to the square root above, we're permitted three * output digits: the two most extreme values -1 and +1, and 0 in * the middle. Expanding out (*) for each of those options gives * us the following inequalities: * * -6r^2 + 12r - 7 <= e + d <= -1 (if we choose x = -1) * -3r^2 + 3r <= e + d <= 3r^2 + 3r (if we choose x = 0) * 1 <= e + d <= 6r^2 - 12r + 7 (if we choose x = +1) * * Finally, after each step, we adjust r to its new value r+x, and * e to its new value e + d - 3xr^2 - 3x^2r - x^3 (in which 'r' * still means the old value). Then we scale up r by 2, and e by * 8. */ Spigot *x_orig; StaticGenerator sg; bigint r, r2, e; int crState; // For debugging the algorithm, we can store k itself if // necessary, and check that e, r, k remain in the right // relationship throughout. Uncomment this #define to do so. //#define CBRT_STORE_K #ifdef CBRT_STORE_K bigint k; #endif public: CbrtReal(Spigot *ax) : x_orig(ax->clone()), sg(ax) { crState = -1; dprint("hello CbrtReal %p", ax); } ~CbrtReal() { delete x_orig; } virtual CbrtReal *clone() { return new CbrtReal(x_orig->clone()); } virtual bool gen_interval(bigint *low, bigint *high) { *low = -2; *high = 2; return true; // force absorption of first matrix } virtual bool gen_matrix(bigint *matrix) { crBegin; /* * We kick off by finding out any old information we can about * the input number's integer part, deciding on a starting * scale factor, and mapping the number to something in the * interval [-8,8]. */ { // Get some info on the integer part. bigint lo, hi; sg.iterate_to_bounds(&lo, &hi, NULL, NULL, 0, NULL, true); dprint("initial integer part range (%b,%b)", &lo, &hi); // Find a power of 2 at least as big as the absolute value // of either limit. unsigned bits = 0; if (hi >= 0) { unsigned bits2 = bigint_approxlog2(hi - 1); if (bits < bits2) bits = bits2; } if (lo <= 0) { unsigned bits2 = bigint_approxlog2(1 - lo); if (bits < bits2) bits = bits2; } // Round up to the next power of 8, and to at least 8. bits -= (bits % 3); bits += 3; if (bits < 3) bits = 3; dprint("bits=%d", (int)bits); // Scale the input down into the range [-8,8]. bigint outmatrix[4]; outmatrix[0] = 1; outmatrix[1] = outmatrix[2] = 0; outmatrix[3] = (bigint)1 << (bits - 3); dprint("premultiplying initial scale matrix %4m", outmatrix); sg.premultiply(outmatrix); // And emit an initial matrix which maps the starting // interval [-2,2] into the range [-2^(bits/3), 2^(bits/3)]. matrix[0] = (bigint)1 << (bits/3 - 1); matrix[1] = matrix[2] = 0; matrix[3] = 1; dprint("returning initial scale matrix %4m", matrix); } crReturn(false); /* * Now we can initialise the main algorithm. To avoid lots of * needless multiplication, we keep running track of r^2 as * well as r. */ r = 0; r2 = 0; e = 0; #ifdef CBRT_STORE_K k = 0; #endif while (1) { { // Iterate the input spigot until the range of integer // parts has narrowed to at most two. bigint lo, hi; sg.iterate_to_bounds(&lo, &hi, NULL, NULL, 2, NULL, false); dprint("integer part range (%b,%b)", &lo, &hi); // Our input digit will be the midpoint of that // width-2 interval. (If it is width 2; if it's // already narrower than that, then either integer // will do. The important thing is that // [digit-1,digit+1] contains the number.) bigint d = (lo + hi) / 2U; // We know the input was in in the range [-8,+8], so // if the spigot gave us an interval jammed up against // the edge of that range so that d has ended up // outside the range {-7,...,7}, clip it into range // because we know better. if (d < -7) d = -7; if (d > +7) d = +7; dprint("step: r=%b e=%b d=%b" #ifdef CBRT_STORE_K " k=%b" #endif , &r, &e, &d #ifdef CBRT_STORE_K , &k #endif ); assert(-6*r2 + 12*r <= e); assert(e <= 6*r2 + 12*r); #ifdef CBRT_STORE_K assert(r2 == r*r); assert(e == k - r*r*r); #endif // Premultiply in a matrix which zooms the new input // interval back up to [-8,8]. { bigint outmatrix[4]; outmatrix[0] = 8; outmatrix[1] = -8*d; outmatrix[2] = 0; outmatrix[3] = 1; dprint("premultiplying digit-extract matrix %4m", outmatrix); sg.premultiply(outmatrix); } // Now figure out what digit to output. We can almost // always output a -1 or 1 digit; only that // inconvenient zero in the middle needs to use the 0 // digit. bigint e_plus_d = e + d; if (e_plus_d < 0) { // Output digit -1. matrix[0] = 1; matrix[1] = -2; matrix[2] = 0; matrix[3] = 2; e = 8 * (e_plus_d + 3*r2 - 3*r + 1); r2 = 4 * (r2 - 2*r + 1); r = 2 * (r-1); dprint("output digit -1: %4m", matrix); } else if (e_plus_d > 0) { // Output digit +1. matrix[0] = 1; matrix[1] = 2; matrix[2] = 0; matrix[3] = 2; e = 8 * (e_plus_d - 3*r2 - 3*r - 1); r2 = 4 * (r2 + 2*r + 1); r = 2 * (r+1); dprint("output digit +1: %4m", matrix); } else { // Output digit 0. matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 2; e = 8 * e_plus_d; r2 = 4 * r2; r = 2 * r; dprint("output digit 0: %4m", matrix); } #ifdef CBRT_STORE_K k = 8 * (k + d); #endif } crReturn(false); } crEnd; } }; Spigot *spigot_sqrt(Spigot *a) { bigint an, ad; if (a->is_rational(&an, &ad)) { delete a; if (ad == 1) { /* * Square root of integer. */ return new SqrtInteger(an); } else { bigint ans, ads; /* * Special cases involving perfect squares. */ if (an < 0) throw spigot_error("square root of negative number"); ans = bigint_sqrt(an); ads = bigint_sqrt(ad); bool nexact = (ans*ans == an); bool dexact = (ads*ads == ad); if (nexact && dexact) return spigot_rational(ans, ads); else if (nexact) return spigot_rational_mul(spigot_reciprocal (new SqrtInteger(ad)), ans, 1); else if (dexact) return spigot_rational_mul(new SqrtInteger(an), 1, ads); /* * Square root of rational, computed by dividing two * square roots of integers. */ return spigot_div(new SqrtInteger(an), new SqrtInteger(ad)); } } else { return new SqrtReal(a); } } Spigot *spigot_hypot(Spigot *a, Spigot *b) { Spigot *ac = a->clone(); Spigot *bc = b->clone(); return spigot_sqrt(spigot_add(spigot_mul(a, ac), spigot_mul(b, bc))); } Spigot *spigot_cbrt(Spigot *a) { bigint an, ad; if (a->is_rational(&an, &ad)) { /* * Special case of the cube of a rational. */ bigint anr = cuberoot(an); bigint adr = cuberoot(ad); bool nexact = (anr*anr*anr == an); bool dexact = (adr*adr*adr == ad); if (nexact && dexact) return spigot_rational(anr, adr); } return new CbrtReal(a); } spigot-0.2017-01-15.gdad1bbc6/test.sh0000755000000000000000000023433613025565031013504 0ustar #!/bin/bash SPIGOT=spigot PREFIX=() nargs=0 processing_opts=y while test $# -gt 0; do case "$processing_opts:$1" in y:--) processing_opts=n;; y:--help) cat < ] options: --valgrind=LOG run spigot under valgrind, sending logs to LOG --help print this text EOF exit 0;; y:--valgrind | y:--valgrind=*) case "$1" in --valgrind=*) optval="${1##*=}";; *) if test $# -lt 2; then echo "test.sh: option '$1' expects an argument" >&2 exit 1 fi optval="$2"; shift;; esac exec 3>"$optval" PREFIX=(valgrind --log-fd=3);; # For options taking a value, follow the pattern of --valgrind # above. For options not taking a value, just add a case of # the form # y:--option) do stuff;; y:-*) echo "test.sh: unknown option '$1'" >&2; exit 1;; ?:*) case $nargs in 0) SPIGOT="$1";; *) echo "test.sh: unexpected extra argument '$1'" >&2; exit 1;; esac nargs=$((nargs+1));; esac shift done pass=0 fail=0 real_testcase() { local line="$1" local expectedstatus=0 local expected="$2" case "$3" in exit:*) expectedstatus=${3#exit:}; shift;; esac shift 2 local actual actual=$("${PREFIX[@]}" "$SPIGOT" "$@" 2>/dev/null) local actualstatus=$? if test "z$actual" != "z$expected" -o \ "z$actualstatus" != "z$expectedstatus"; then echo -n "Failed test at line $line: spigot" for arg in "$@"; do case $arg in *[!0-9a-zA-Z\-]*) echo -n " '${arg/'/'\\''/}'";; *) echo -n " $arg";; esac done echo echo "Expected: status $expectedstatus, output '$expected'" echo " Actual: status $actualstatus, output '$actual'" echo fail=$((fail+1)) else pass=$((pass+1)) fi } shopt -s expand_aliases alias testcase='real_testcase $LINENO' # Start by dumping the program's version and combing it for knowledge # of which parts of this test suite we can run. echo ---- general info echo "Program under test:" echo "| $SPIGOT" echo "Version dump:" HAVE_FDS=false while IFS= read versionline; do echo "| $versionline" case "$versionline" in ' fd literals'*': supported') HAVE_FDS=true esac done < <("$SPIGOT" --version) echo ---- core tests # Simple tests of rationals testcase '4b000000.8' -S -d40 -- '8388608.5' testcase '-2' -d70 -- '-2' testcase '0' -d70 -- '0' testcase '0.2' -d70 -- '1/5' # IEEEish representations of pi testcase '4248.7ed5110b46' -H -d40 -- 'pi' testcase '40490fda.a22168c234' -S -d40 -- 'pi' testcase '400921fb54442d18.469898cc51' -D -d40 -- 'pi' testcase '4000921fb54442d18469898cc51701b8.39a252049c' -Q -d40 -- 'pi' # A quick check that baseN: input is working testcase '48413765' base36:STOAT testcase '3.1415920257568359375' base2:11.0010010000111111011 testcase '1.3610416955035047827413403586668340818282' -d40 base7:1.234560123456 # Maths functions and arithmetic testcase '3.1415926535897932384626433832795028841971693993751058209749445923078164' -d70 -- 'pi' testcase '-3.1415926535897932384626433832795028841971693993751058209749445923078164' -d70 -- '-pi' testcase '2.7182818284590452353602874713526624977572470936999595749669676277240766' -d70 -- 'e' testcase '1.6180339887498948482045868343656381177203091798057628621354486227052604' -d70 -- 'phi' testcase '1.2020569031595942853997381615114499907649862923404988817922715553418382' -d70 -- 'apery' testcase '5.8598744820488384738229308546321653819544164930750653959419122200318930' -d70 -- 'pi+e' testcase '0.4233108251307480031023559119268403864399223056751462460079769645837397' -d70 -- 'pi-e' testcase '-0.4233108251307480031023559119268403864399223056751462460079769645837397' -d70 -- 'e-pi' testcase '1.1557273497909217179100931833126962991208510231644158204997065353272886' -d70 -- 'pi/e' testcase '8.5397342226735670654635508695465744950348885357651149618796011301792286' -d70 -- 'pi*e' testcase '-1.1557273497909217179100931833126962991208510231644158204997065353272886' -d70 -- 'pi/-e' testcase '1.7724538509055160272981674833411451827975494561223871282138077898529112' -d70 -- 'sqrt(pi)' testcase '1772.4538509055160272981674833411451827975494561223871282138077898529112845' -d70 -- 'sqrt(pi*1000000)' testcase '2506.6282746310005024157652848110452530069867406099383166299235763422936546' -d70 -- 'sqrt(pi*2000000)' testcase '3544.9077018110320545963349666822903655950989122447742564276155797058225691' -d70 -- 'sqrt(pi*4000000)' testcase '0.4012319619908143541857543436532949583238702611292440683194415381168718' -d70 -- 'tan(1e100)' testcase '23.1406926327792690057290863679485473802661062426002119934450464095243423' -d70 -- 'exp(pi)' testcase '22.4591577183610454734271522045437350275893151339966922492030025540669260' -d70 -- 'pi^e' testcase '-1.1902899496825317329277337748293183376011789860294520729111666738297077' -d70 -- 'atan(-2.5)' testcase '-1.1071487177940905030170654601785370400700476454014326466765392074337103' -d70 -- 'atan(-2)' testcase '-0.8329812666744317054176935618363612385158513444371084208534231225032752' -d70 -- 'atan(-1.1)' testcase '-0.7853981633974483096156608458198757210492923498437764552437361480769541' -d70 -- 'atan(-1)' testcase '-0.7328151017865065916407920727342802519857556793582560863105069319282124' -d70 -- 'atan(-0.9)' testcase '-0.4636476090008061162142562314612144020285370542861202638109330887201978' -d70 -- 'atan(-0.5)' testcase '-0.0996686524911620273784461198780205902432783225043146480155087768100277' -d70 -- 'atan(-0.1)' testcase '0.0996686524911620273784461198780205902432783225043146480155087768100277' -d70 -- 'atan(+0.1)' testcase '0.4636476090008061162142562314612144020285370542861202638109330887201978' -d70 -- 'atan(+0.5)' testcase '0.7328151017865065916407920727342802519857556793582560863105069319282124' -d70 -- 'atan(+0.9)' testcase '0.7853981633974483096156608458198757210492923498437764552437361480769541' -d70 -- 'atan(+1)' testcase '0.8329812666744317054176935618363612385158513444371084208534231225032752' -d70 -- 'atan(+1.1)' testcase '1.1071487177940905030170654601785370400700476454014326466765392074337103' -d70 -- 'atan(+2)' testcase '1.1902899496825317329277337748293183376011789860294520729111666738297077' -d70 -- 'atan(+2.5)' testcase '1.3956124250860895286281253196025868375979065151994069826175167060317390' -d70 -- 'exp(1/3)' testcase '1.0100501670841680575421654569028600338073622015242925151644040312543741' -d70 -- 'exp(1/100)' testcase '0.7165313105737892504256040969253796674531120598214791571408702071273040' -d70 -- 'exp(-1/3)' testcase '0.9900498337491680535739059771800365577720790812538374668838787452931477' -d70 -- 'exp(-1/100)' testcase '0.3271946967961522441733440852676206060643014068937597915900562770705763' -d70 -- 'sin(1/3)' testcase '0.0099998333341666646825424382690997290389643853601691510338791124794097' -d70 -- 'sin(1/100)' testcase '-0.3271946967961522441733440852676206060643014068937597915900562770705763' -d70 -- 'sin(-1/3)' testcase '-0.0099998333341666646825424382690997290389643853601691510338791124794097' -d70 -- 'sin(-1/100)' testcase '0.9449569463147376643882840076758806078458526995651407376776457337500995' -d70 -- 'cos(1/3)' testcase '0.9999500004166652777802579337522066732124705839802771111222757686464600' -d70 -- 'cos(1/100)' testcase '0.9449569463147376643882840076758806078458526995651407376776457337500995' -d70 -- 'cos(-1/3)' testcase '0.9999500004166652777802579337522066732124705839802771111222757686464600' -d70 -- 'cos(-1/100)' testcase '1.2572741156691850593845221141104482939061663100396581735341816226271627' -d70 -- 'pi^(1/5)' testcase '1.0986122886681096913952452369225257046474905578227494517346943336374942' -d70 -- 'log(3)' testcase '363.7393755555634901440799933696556380278239210628872747276794488767759444' -d70 -- 'lgamma(101)' testcase '363.7393755555634901440799933696556380278239210628872747276794488767759444' -d70 -- 'log(93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000)' # 100! == gamma(101) testcase '-1.1197695149986341866866770558453996158951621864033028823756818639144375' -d70 -- 'asin(-0.9)' testcase '-0.9272952180016122324285124629224288040570741085722405276218661774403957' -d70 -- 'asin(-0.8)' testcase '-0.7753974966107530637403533527149871135557887386411619935977199637327202' -d70 -- 'asin(-0.7)' testcase '-0.6435011087932843868028092287173226380415105911153123828656061187135124' -d70 -- 'asin(-0.6)' testcase '-0.5235987755982988730771072305465838140328615665625176368291574320513027' -d70 -- 'asin(-0.5)' testcase '-0.4115168460674880193847378976173356048557011351270258517839467807000952' -d70 -- 'asin(-0.4)' testcase '-0.3046926540153975079720029612275291669545600317067763873929779487464729' -d70 -- 'asin(-0.3)' testcase '-0.2013579207903307914551255522176234102400380814022283862572512434556093' -d70 -- 'asin(-0.2)' testcase '-0.1001674211615597963455231794526933185686759722296295413910238550364026' -d70 -- 'asin(-0.1)' testcase '0.1001674211615597963455231794526933185686759722296295413910238550364026' -d70 -- 'asin(0.1)' testcase '0.2013579207903307914551255522176234102400380814022283862572512434556093' -d70 -- 'asin(0.2)' testcase '0.3046926540153975079720029612275291669545600317067763873929779487464729' -d70 -- 'asin(0.3)' testcase '0.4115168460674880193847378976173356048557011351270258517839467807000952' -d70 -- 'asin(0.4)' testcase '0.5235987755982988730771072305465838140328615665625176368291574320513027' -d70 -- 'asin(0.5)' testcase '0.6435011087932843868028092287173226380415105911153123828656061187135124' -d70 -- 'asin(0.6)' testcase '0.7753974966107530637403533527149871135557887386411619935977199637327202' -d70 -- 'asin(0.7)' testcase '0.9272952180016122324285124629224288040570741085722405276218661774403957' -d70 -- 'asin(0.8)' testcase '1.1197695149986341866866770558453996158951621864033028823756818639144375' -d70 -- 'asin(0.9)' testcase '0.6435011087932843868028092287173226380415105911153123828656061187135124' -d70 -- 'atan2(3,4)' testcase '-0.3805063771123648863035879168104331044974057136581008375763056223242004' -d70 -- 'atan2(-2,5)' testcase '1.7126933813990605420441733087423045251063664584160169828656023255173498' -d70 -- 'atan2(7,-1)' testcase '-2.5535900500422256872170323026544174565954621533191814672488973844718962' -d70 -- 'atan2(-6,-9)' testcase '0' -d70 -- 'atan2(0,1)' testcase '3.1415926535897932384626433832795028841971693993751058209749445923078164' -d70 -- 'atan2(0,-1)' testcase '1.5707963267948966192313216916397514420985846996875529104874722961539082' -d70 -- 'atan2(1,0)' testcase '-1.5707963267948966192313216916397514420985846996875529104874722961539082' -d70 -- 'atan2(-1,0)' testcase '5' -d70 -- 'hypot(3,4)' testcase '5.3851648071345040312507104915403295562951201616447888376803886700166459' -d70 -- 'hypot(-2,5)' testcase '7.0710678118654752440084436210484903928483593768847403658833986899536623' -d70 -- 'hypot(7,-1)' testcase '10.8166538263919678793576638024114878387538897215357386381313591686815008' -d70 -- 'hypot(-6,-9)' testcase '1' -d70 -- 'hypot(0,1)' testcase '1' -d70 -- 'hypot(0,-1)' testcase '1' -d70 -- 'hypot(1,0)' testcase '1' -d70 -- 'hypot(-1,0)' testcase '-74.2032105777887589770094719960645655996194090044258169806612697896696906' -d70 -- 'sinh(-5)' testcase '-0.1001667500198440258237293835219050235149209168785588833683029861925940' -d70 -- 'sinh(-.1)' testcase '0.1001667500198440258237293835219050235149209168785588833683029861925940' -d70 -- 'sinh(.1)' testcase '74.2032105777887589770094719960645655996194090044258169806612697896696906' -d70 -- 'sinh(5)' testcase '74.2099485247878444441061080444877140238682585894531720660915753212423741' -d70 -- 'cosh(-5)' testcase '1.0050041680558035989879784429683416447096262778589598354245603032483739' -d70 -- 'cosh(-.1)' testcase '1.0050041680558035989879784429683416447096262778589598354245603032483739' -d70 -- 'cosh(.1)' testcase '74.2099485247878444441061080444877140238682585894531720660915753212423741' -d70 -- 'cosh(5)' testcase '-0.9999092042625951312109904475344730210898126159905478627364288722625610' -d70 -- 'tanh(-5)' testcase '-0.0996679946249558171183050836783521835389620957767344369304764385439917' -d70 -- 'tanh(-.1)' testcase '0.0996679946249558171183050836783521835389620957767344369304764385439917' -d70 -- 'tanh(.1)' testcase '0.9999092042625951312109904475344730210898126159905478627364288722625610' -d70 -- 'tanh(5)' testcase '-2.3124383412727526202535623413644143836582450726465592371672289900991325' -d70 -- 'asinh(-5)' testcase '-0.0998340788992075633273031247047694432677129117088250107423826956515917' -d70 -- 'asinh(-.1)' testcase '0.0998340788992075633273031247047694432677129117088250107423826956515917' -d70 -- 'asinh(.1)' testcase '2.3124383412727526202535623413644143836582450726465592371672289900991325' -d70 -- 'asinh(5)' testcase '0.4435682543851151891329110663524980866490011660999754638934209558076688' -d70 -- 'acosh(1.1)' testcase '2.2924316695611776878007873113480154316218682400157102476050164448313478' -d70 -- 'acosh(5)' testcase '4.6050701709847571594505725515130629155479434834765415773570077019244073' -d70 -- 'acosh(50)' testcase '-1.4722194895832202300045137159439267686186896306495644092689801182046463' -d70 -- 'atanh(-.9)' testcase '-0.1003353477310755806357265520600389452633628691459591358745895209277925' -d70 -- 'atanh(-.1)' testcase '0.1003353477310755806357265520600389452633628691459591358745895209277925' -d70 -- 'atanh(.1)' testcase '1.4722194895832202300045137159439267686186896306495644092689801182046463' -d70 -- 'atanh(.9)' testcase '-1353.2868196796119012120782386750901706773155970518514189830847088458916602' -d70 -- 'cbrt(-2478389475)' testcase '1353.2868196796119012120782386750901706773155970518514189830847088458916602' -d70 -- 'cbrt(2478389475)' testcase '-1.2599210498948731647672106072782283505702514647015079800819751121552996' -d70 -- 'cbrt(-2)' testcase '1.2599210498948731647672106072782283505702514647015079800819751121552996' -d70 -- 'cbrt(2)' testcase '1.4645918875615232630201425272637903917385968556279371743572559371383936' -d70 -- 'cbrt(pi)' testcase '-1.4645918875615232630201425272637903917385968556279371743572559371383936' -d70 -- 'cbrt(-pi)' testcase '146.4591887561523263020142527263790391738596855627937174357255937138393649' -d70 -- 'cbrt(pi*1000000)' testcase '184.5270148644028419096803879588988026780036266215951662725508973334767943' -d70 -- 'cbrt(pi*2000000)' testcase '232.4894703019252951141799628955674133718676185523674068779515445375023295' -d70 -- 'cbrt(pi*4000000)' testcase '292.9183775123046526040285054527580783477193711255874348714511874276787299' -d70 -- 'cbrt(pi*8000000)' testcase '0.1415926535897932384626433832795028841971693993751058209749445923078164' -d70 -- 'frac(pi)' testcase '0.8584073464102067615373566167204971158028306006248941790250554076921835' -d70 -- 'frac(-pi)' testcase '0.1013211836423377714438794632097276389043587746722465488456090318941731' -d70 -- 'pi^-2' # Check the rational^integer special-case code for pow() testcase '2048' 2^11 testcase '4096' 2^12 testcase '0.00048828125' 2^-11 testcase '0.000244140625' 2^-12 testcase '2.7777777777777777777777777777777777777777' -d40 -- '(-3/5)^-2' testcase '-1.6666666666666666666666666666666666666666' -d40 -- '(-3/5)^-1' testcase '1' -d40 -- '(-3/5)^0' testcase '-0.6' -d40 -- '(-3/5)^1' testcase '0.36' -d40 -- '(-3/5)^2' testcase '8' 'exp2(3)' testcase '0.01' 'exp10(-2)' # Check IEEE 754 tie-breaking in rem testcase '-0.5' -- '-2.5 rem 1' testcase '0.5' -- '-1.5 rem 1' testcase '-0.5' -- '-0.5 rem 1' testcase '0.5' -- '+0.5 rem 1' testcase '-0.5' -- '+1.5 rem 1' testcase '0.5' -- '+2.5 rem 1' # Tests of all rounding modes and no rounding mode testcase '4c000000' -S -- '33554432' testcase '4c000000.4' -S -- '33554433' testcase '4c000000.8' -S -- '33554434' testcase '4c000000.c' -S -- '33554435' testcase '4c000001' -S -- '33554436' testcase '4c000001.4' -S -- '33554437' testcase '4c000001.8' -S -- '33554438' testcase '4c000001.c' -S -- '33554439' testcase '4c000002' -S -- '33554440' testcase 'cc000000' -S -- '-33554432' testcase 'cc000000.4' -S -- '-33554433' testcase 'cc000000.8' -S -- '-33554434' testcase 'cc000000.c' -S -- '-33554435' testcase 'cc000001' -S -- '-33554436' testcase 'cc000001.4' -S -- '-33554437' testcase 'cc000001.8' -S -- '-33554438' testcase 'cc000001.c' -S -- '-33554439' testcase 'cc000002' -S -- '-33554440' testcase '4c000000' -S -d0 --rz -- '33554432' testcase '4c000000' -S -d0 --rz -- '33554433' testcase '4c000000' -S -d0 --rz -- '33554434' testcase '4c000000' -S -d0 --rz -- '33554435' testcase '4c000001' -S -d0 --rz -- '33554436' testcase '4c000001' -S -d0 --rz -- '33554437' testcase '4c000001' -S -d0 --rz -- '33554438' testcase '4c000001' -S -d0 --rz -- '33554439' testcase '4c000002' -S -d0 --rz -- '33554440' testcase 'cc000000' -S -d0 --rz -- '-33554432' testcase 'cc000000' -S -d0 --rz -- '-33554433' testcase 'cc000000' -S -d0 --rz -- '-33554434' testcase 'cc000000' -S -d0 --rz -- '-33554435' testcase 'cc000001' -S -d0 --rz -- '-33554436' testcase 'cc000001' -S -d0 --rz -- '-33554437' testcase 'cc000001' -S -d0 --rz -- '-33554438' testcase 'cc000001' -S -d0 --rz -- '-33554439' testcase 'cc000002' -S -d0 --rz -- '-33554440' testcase '4c000000' -S -d0 --ri -- '33554432' testcase '4c000001' -S -d0 --ri -- '33554433' testcase '4c000001' -S -d0 --ri -- '33554434' testcase '4c000001' -S -d0 --ri -- '33554435' testcase '4c000001' -S -d0 --ri -- '33554436' testcase '4c000002' -S -d0 --ri -- '33554437' testcase '4c000002' -S -d0 --ri -- '33554438' testcase '4c000002' -S -d0 --ri -- '33554439' testcase '4c000002' -S -d0 --ri -- '33554440' testcase 'cc000000' -S -d0 --ri -- '-33554432' testcase 'cc000001' -S -d0 --ri -- '-33554433' testcase 'cc000001' -S -d0 --ri -- '-33554434' testcase 'cc000001' -S -d0 --ri -- '-33554435' testcase 'cc000001' -S -d0 --ri -- '-33554436' testcase 'cc000002' -S -d0 --ri -- '-33554437' testcase 'cc000002' -S -d0 --ri -- '-33554438' testcase 'cc000002' -S -d0 --ri -- '-33554439' testcase 'cc000002' -S -d0 --ri -- '-33554440' testcase '4c000000' -S -d0 --ru -- '33554432' testcase '4c000001' -S -d0 --ru -- '33554433' testcase '4c000001' -S -d0 --ru -- '33554434' testcase '4c000001' -S -d0 --ru -- '33554435' testcase '4c000001' -S -d0 --ru -- '33554436' testcase '4c000002' -S -d0 --ru -- '33554437' testcase '4c000002' -S -d0 --ru -- '33554438' testcase '4c000002' -S -d0 --ru -- '33554439' testcase '4c000002' -S -d0 --ru -- '33554440' testcase 'cc000000' -S -d0 --ru -- '-33554432' testcase 'cc000000' -S -d0 --ru -- '-33554433' testcase 'cc000000' -S -d0 --ru -- '-33554434' testcase 'cc000000' -S -d0 --ru -- '-33554435' testcase 'cc000001' -S -d0 --ru -- '-33554436' testcase 'cc000001' -S -d0 --ru -- '-33554437' testcase 'cc000001' -S -d0 --ru -- '-33554438' testcase 'cc000001' -S -d0 --ru -- '-33554439' testcase 'cc000002' -S -d0 --ru -- '-33554440' testcase '4c000000' -S -d0 --rd -- '33554432' testcase '4c000000' -S -d0 --rd -- '33554433' testcase '4c000000' -S -d0 --rd -- '33554434' testcase '4c000000' -S -d0 --rd -- '33554435' testcase '4c000001' -S -d0 --rd -- '33554436' testcase '4c000001' -S -d0 --rd -- '33554437' testcase '4c000001' -S -d0 --rd -- '33554438' testcase '4c000001' -S -d0 --rd -- '33554439' testcase '4c000002' -S -d0 --rd -- '33554440' testcase 'cc000000' -S -d0 --rd -- '-33554432' testcase 'cc000001' -S -d0 --rd -- '-33554433' testcase 'cc000001' -S -d0 --rd -- '-33554434' testcase 'cc000001' -S -d0 --rd -- '-33554435' testcase 'cc000001' -S -d0 --rd -- '-33554436' testcase 'cc000002' -S -d0 --rd -- '-33554437' testcase 'cc000002' -S -d0 --rd -- '-33554438' testcase 'cc000002' -S -d0 --rd -- '-33554439' testcase 'cc000002' -S -d0 --rd -- '-33554440' testcase '4c000000' -S -d0 --rne -- '33554432' testcase '4c000000' -S -d0 --rne -- '33554433' testcase '4c000000' -S -d0 --rne -- '33554434' testcase '4c000001' -S -d0 --rne -- '33554435' testcase '4c000001' -S -d0 --rne -- '33554436' testcase '4c000001' -S -d0 --rne -- '33554437' testcase '4c000002' -S -d0 --rne -- '33554438' testcase '4c000002' -S -d0 --rne -- '33554439' testcase '4c000002' -S -d0 --rne -- '33554440' testcase 'cc000000' -S -d0 --rne -- '-33554432' testcase 'cc000000' -S -d0 --rne -- '-33554433' testcase 'cc000000' -S -d0 --rne -- '-33554434' testcase 'cc000001' -S -d0 --rne -- '-33554435' testcase 'cc000001' -S -d0 --rne -- '-33554436' testcase 'cc000001' -S -d0 --rne -- '-33554437' testcase 'cc000002' -S -d0 --rne -- '-33554438' testcase 'cc000002' -S -d0 --rne -- '-33554439' testcase 'cc000002' -S -d0 --rne -- '-33554440' testcase '4c000000' -S -d0 --rno -- '33554432' testcase '4c000000' -S -d0 --rno -- '33554433' testcase '4c000001' -S -d0 --rno -- '33554434' testcase '4c000001' -S -d0 --rno -- '33554435' testcase '4c000001' -S -d0 --rno -- '33554436' testcase '4c000001' -S -d0 --rno -- '33554437' testcase '4c000001' -S -d0 --rno -- '33554438' testcase '4c000002' -S -d0 --rno -- '33554439' testcase '4c000002' -S -d0 --rno -- '33554440' testcase 'cc000000' -S -d0 --rno -- '-33554432' testcase 'cc000000' -S -d0 --rno -- '-33554433' testcase 'cc000001' -S -d0 --rno -- '-33554434' testcase 'cc000001' -S -d0 --rno -- '-33554435' testcase 'cc000001' -S -d0 --rno -- '-33554436' testcase 'cc000001' -S -d0 --rno -- '-33554437' testcase 'cc000001' -S -d0 --rno -- '-33554438' testcase 'cc000002' -S -d0 --rno -- '-33554439' testcase 'cc000002' -S -d0 --rno -- '-33554440' testcase '4c000000' -S -d0 --rnz -- '33554432' testcase '4c000000' -S -d0 --rnz -- '33554433' testcase '4c000000' -S -d0 --rnz -- '33554434' testcase '4c000001' -S -d0 --rnz -- '33554435' testcase '4c000001' -S -d0 --rnz -- '33554436' testcase '4c000001' -S -d0 --rnz -- '33554437' testcase '4c000001' -S -d0 --rnz -- '33554438' testcase '4c000002' -S -d0 --rnz -- '33554439' testcase '4c000002' -S -d0 --rnz -- '33554440' testcase 'cc000000' -S -d0 --rnz -- '-33554432' testcase 'cc000000' -S -d0 --rnz -- '-33554433' testcase 'cc000000' -S -d0 --rnz -- '-33554434' testcase 'cc000001' -S -d0 --rnz -- '-33554435' testcase 'cc000001' -S -d0 --rnz -- '-33554436' testcase 'cc000001' -S -d0 --rnz -- '-33554437' testcase 'cc000001' -S -d0 --rnz -- '-33554438' testcase 'cc000002' -S -d0 --rnz -- '-33554439' testcase 'cc000002' -S -d0 --rnz -- '-33554440' testcase '4c000000' -S -d0 --rni -- '33554432' testcase '4c000000' -S -d0 --rni -- '33554433' testcase '4c000001' -S -d0 --rni -- '33554434' testcase '4c000001' -S -d0 --rni -- '33554435' testcase '4c000001' -S -d0 --rni -- '33554436' testcase '4c000001' -S -d0 --rni -- '33554437' testcase '4c000002' -S -d0 --rni -- '33554438' testcase '4c000002' -S -d0 --rni -- '33554439' testcase '4c000002' -S -d0 --rni -- '33554440' testcase 'cc000000' -S -d0 --rni -- '-33554432' testcase 'cc000000' -S -d0 --rni -- '-33554433' testcase 'cc000001' -S -d0 --rni -- '-33554434' testcase 'cc000001' -S -d0 --rni -- '-33554435' testcase 'cc000001' -S -d0 --rni -- '-33554436' testcase 'cc000001' -S -d0 --rni -- '-33554437' testcase 'cc000002' -S -d0 --rni -- '-33554438' testcase 'cc000002' -S -d0 --rni -- '-33554439' testcase 'cc000002' -S -d0 --rni -- '-33554440' testcase '4c000000' -S -d0 --rnu -- '33554432' testcase '4c000000' -S -d0 --rnu -- '33554433' testcase '4c000001' -S -d0 --rnu -- '33554434' testcase '4c000001' -S -d0 --rnu -- '33554435' testcase '4c000001' -S -d0 --rnu -- '33554436' testcase '4c000001' -S -d0 --rnu -- '33554437' testcase '4c000002' -S -d0 --rnu -- '33554438' testcase '4c000002' -S -d0 --rnu -- '33554439' testcase '4c000002' -S -d0 --rnu -- '33554440' testcase 'cc000000' -S -d0 --rnu -- '-33554432' testcase 'cc000000' -S -d0 --rnu -- '-33554433' testcase 'cc000000' -S -d0 --rnu -- '-33554434' testcase 'cc000001' -S -d0 --rnu -- '-33554435' testcase 'cc000001' -S -d0 --rnu -- '-33554436' testcase 'cc000001' -S -d0 --rnu -- '-33554437' testcase 'cc000001' -S -d0 --rnu -- '-33554438' testcase 'cc000002' -S -d0 --rnu -- '-33554439' testcase 'cc000002' -S -d0 --rnu -- '-33554440' testcase '4c000000' -S -d0 --rnd -- '33554432' testcase '4c000000' -S -d0 --rnd -- '33554433' testcase '4c000000' -S -d0 --rnd -- '33554434' testcase '4c000001' -S -d0 --rnd -- '33554435' testcase '4c000001' -S -d0 --rnd -- '33554436' testcase '4c000001' -S -d0 --rnd -- '33554437' testcase '4c000001' -S -d0 --rnd -- '33554438' testcase '4c000002' -S -d0 --rnd -- '33554439' testcase '4c000002' -S -d0 --rnd -- '33554440' testcase 'cc000000' -S -d0 --rnd -- '-33554432' testcase 'cc000000' -S -d0 --rnd -- '-33554433' testcase 'cc000001' -S -d0 --rnd -- '-33554434' testcase 'cc000001' -S -d0 --rnd -- '-33554435' testcase 'cc000001' -S -d0 --rnd -- '-33554436' testcase 'cc000001' -S -d0 --rnd -- '-33554437' testcase 'cc000002' -S -d0 --rnd -- '-33554438' testcase 'cc000002' -S -d0 --rnd -- '-33554439' testcase 'cc000002' -S -d0 --rnd -- '-33554440' # Miscellaneous tests testcase '3fc00000' -S -d10 -- 'cbrt(ieee:40580000)' testcase '262537412640768743.9999999999992500725971981856888793538563' -d40 -- 'exp(pi*sqrt(163))' testcase '19.9990999791894757672664429846690444960689368432251061724701018172165259' -d70 -- 'exp(pi)-pi' # xkcd #217 testcase '7feffffffffffe50.ea4b00a2671de9ec' -D -d64 -- 'gamma(ieee:406573fae561f647)' testcase '119.7954512922427202889689745352405411702723' -d40 -- 'gamma(5.999)' testcase '120' -d40 -- 'gamma(6)' testcase '120.2049197671270571426949011864693528903251' -d40 -- 'gamma(6.001)' testcase '7fa4ab78644182e7.36582dd9ca' -D -d40 -- 'gamma(ieee:40655fffffffffff)' testcase '7fa4ab7864418638.f2203ba4f8' -D -d40 -- 'gamma(ieee:4065600000000000)' testcase '7fa4ab786441898a.ade84970af' -D -d40 -- 'gamma(ieee:4065600000000001)' testcase '0.8427007929497148693412206350826092592960' -d40 -- 'erf(1)' testcase '-0.8427007929497148693412206350826092592960' -d40 -- 'erf(-1)' testcase '0.1572992070502851306587793649173907407039' -d40 -- 'erfc(1)' testcase '1.8427007929497148693412206350826092592960' -d40 -- 'erfc(-1)' testcase '0.8413447460685429485852325456320379224779' -d40 -- 'Phi(1)' testcase '0.1586552539314570514147674543679620775220' -d40 -- 'Phi(-1)' testcase 'c0600000' -S -d32 -- '-3.5' testcase 'c0490fda.a22168c2' -S -d32 -- '-pi' testcase '0' -d70 -- 'abs(0)' testcase '0.4' -d70 -- 'abs(0.4)' testcase '0.4' -d70 -- 'abs(-0.4)' testcase '3.1415926535897932384626433832795028841971693993751058209749445923078164' -d70 -- 'abs(pi)' testcase '3.1415926535897932384626433832795028841971693993751058209749445923078164' -d70 -- 'abs(-pi)' testcase '11.383358' --printf=%f 'W(1000000)' testcase '-5.997807' --printf=%f 'invnorm(0.000000001)' testcase '4.47550205634926123052849152389606512569258015067144' -d50 -- 'sqrt(-log(0.000000002))' testcase '21430172143725346418968500981200036211228096234110672148875007767407021022498722449863967576313917162551893458351062936503742905713846280871969155149397149607869135549648461970842149210124742283755908364306092949967163882534797535118331087892154125829142392955373084335320859663305248773674411336138752' 2^1001 testcase '25372623506747431611343734828921129692212922067271259852235159388169369957552784264144600916821700667857864' 293847358972094572309547230945702354**3 testcase '0.1104916011' -d10 'W(0.1234)' testcase '-1' '0*exp(0)-1' testcase '8.7744375108173427218060841592172476313972' -d40 '15*log2(1.5)' testcase '0' 'asin(0)' testcase '1.5707963267948966192313216916397514420985' -d40 'asin(1)' testcase '-1.5707963267948966192313216916397514420985' -d40 'asin(-1)' testcase '0' 'acos(1)' testcase '3.8082593202564599051293100499461695508638' -d40 'pi + (2/3)' testcase '3.8082593202564599051293100499461695508638' -d40 '(2/3) + pi' testcase '2.4749259869231265717959767166128362175305' -d40 'pi - (2/3)' testcase '-2.4749259869231265717959767166128362175305' -d40 '(2/3) - pi' testcase '2.0943951023931954923084289221863352561314' -d40 'pi * (2/3)' testcase '2.0943951023931954923084289221863352561314' -d40 '(2/3) * pi' testcase '4.7123889803846898576939650749192543262957' -d40 'pi / (2/3)' testcase '0.2122065907891937810251783511633524827126' -d40 '(2/3) / pi' testcase '1' 'ceil(1+sqrt(0))' testcase '1' 'ceil(1-sqrt(0))' testcase '1' 'ceil(1)' testcase '-0.41551544396166582316' -d20 'log(0.66)' testcase '0;1,75,1,2,4,2,23,2,6,1,4,1,1,2,26,1,13,1,2,4' -d20 -cl -- 'pi^2/10' testcase '1' '1^0.5' testcase '1' '1^1.5' testcase '-0.1' --printf=%.17g -- '-0.1' testcase '0' 'log2(1)' testcase '1' 'log2(2)' testcase '2' 'log2(4)' testcase '3' 'log2(8)' testcase '456' 'log2(2^456)' testcase '-1' 'log2(1/2)' testcase '-2' 'log2(1/4)' testcase '-3' 'log2(1/8)' testcase '-456' 'log2(2^-456)' testcase '0' 'log10(1)' testcase '1' 'log10(10)' testcase '2' 'log10(100)' testcase '3' 'log10(1000)' testcase '456' 'log10(10^456)' testcase '-1' 'log10(1/10)' testcase '-2' 'log10(1/100)' testcase '-3' 'log10(1/1000)' testcase '-456' 'log10(10^-456)' # some tests of dyadic log testcase '2' -R 'log(4,2)' testcase '1/2' -R 'log(2,4)' testcase '3/5' -R 'log(1000,100000)' testcase '3/5' -R 'log(1/1000,1/100000)' testcase '-3/5' -R 'log(1000,1/100000)' testcase '-3/5' -R 'log(1/1000,100000)' testcase '5/3' -R 'log(100000,1000)' testcase '5/3' -R 'log(1/100000,1/1000)' testcase '-5/3' -R 'log(1/100000,1000)' testcase '-5/3' -R 'log(100000,1/1000)' testcase '2/3' -R 'log(25/9,125/27)' testcase '-2/3' -R 'log(25/9,27/125)' testcase '0.72105705434887015680' -d20 'log(6,12)' # and test the fallback! # Zero inputs to the erf family testcase '0' 'erf(0)' testcase '1' 'erfc(0)' testcase '0.5' 'Phi(0)' # Basic tests of functions implemented via MonotoneInverter. We don't # ask for many digits of these, because they're slooow :-/ testcase '1.8213863677' -d10 -- 'inverf(0.99)' testcase '-1.8213863677' -d10 -- 'inverf(-0.99)' testcase '0.4769362762' -d10 -- 'inverf(0.5)' testcase '-0.4769362762' -d10 -- 'inverf(-0.5)' testcase '0.0088625012' -d10 -- 'inverfc(0.99)' testcase '-0.0088625012' -d10 -- 'inverfc(1.01)' testcase '2.3263478740' -d10 -- 'invPhi(0.99)' testcase '-2.3263478740' -d10 -- 'invPhi(0.01)' testcase '3.9297432688046' -d13 -- 'W(200)' testcase '0.5671432904097' -d13 -- 'W(1)' testcase '0' -d13 -- 'W(0)' testcase '-0.9999201984840' -d13 -- 'W(-0.36787944)' testcase '-1.0000798057616' -d13 -- 'Wn(-0.36787944)' testcase '-3.5771520639572' -d13 -- 'Wn(-0.1)' testcase '-19.0660024220655' -d13 -- 'Wn(-0.0000001)' # Tests of sign of gamma/lgamma testcase '-0.038984275923083330038784240972' -d30 'lgamma(1.9)' testcase '0.066376239734742971188716739867' -d30 'lgamma(0.9)' testcase '2.368961332728788655206708194551' -d30 'lgamma(-0.1)' testcase '2.273651152924463795162756071270' -d30 'lgamma(-1.1)' testcase '1.531713808195086482680149545589' -d30 'lgamma(-2.1)' testcase '0.961765831907387419407574802125' -d30 'gamma(1.9)' testcase '1.068628702119319354897305335694' -d30 'gamma(0.9)' testcase '-10.686287021193193548973053356944' -d30 'gamma(-0.1)' testcase '9.714806382902903226339139415404' -d30 'gamma(-1.1)' testcase '-4.626098277572811060161494959716' -d30 'gamma(-2.1)' # Tests of algebraic() testcase '0.00887298334620741688517926539978239961083292170529' -d50 'algebraic(.005,1,1,-1000,100000)' testcase '0.00112701665379258311482073460021760038916707829470' -d50 'algebraic(0,.005,1,-1000,100000)' testcase '-2.12706205858986632920549972846925001013654801639064' -d50 'algebraic(-10,0,1,-20,0,0,0,1)' testcase '0.05000001562502441411590589761771708837482484349643' -d50 'algebraic(0,1,1,-20,0,0,0,1)' testcase '2.10205331612766865422237271458814061473591808060490' -d50 'algebraic(1,10,1,-20,0,0,0,1)' # An example of spotting a rational root. testcase '1;2,3' -d100 -cl 'algebraic(1,2,-10,307,-210,0,0,-10,7)' # Coping with repeated roots: a polynomial and its square should work # the same. testcase '1.61803398874989484820458683436563811772030917980576' -d50 'algebraic(0,2,-1,-1,+1)' testcase '-0.61803398874989484820458683436563811772030917980576' -d50 'algebraic(-2,0,-1,-1,+1)' testcase '1.61803398874989484820458683436563811772030917980576' -d50 'algebraic(0,2,+1,+2,-1,-2,+1)' testcase '-0.61803398874989484820458683436563811772030917980576' -d50 'algebraic(-2,0,+1,+2,-1,-2,+1)' # Make sure negating the whole polynomial doesn't confuse matters. testcase '1.61803398874989484820458683436563811772030917980576' -d50 'algebraic(0,2,-1,-1,+1)' testcase '1.61803398874989484820458683436563811772030917980576' -d50 'algebraic(0,2,+1,+1,-1)' # A well-known algebraic number: Conway's constant. (Continued # fraction terms from OEIS A014967; polynomial coefficients from OEIS # A137275.) testcase '1;3,3,2,2,54,5,2,1,16,1,30,1,1,1,2,2,1,14,1,6,24,107,5,1,1,26,2,41,10,1,1,5,17,5,1,8,3,94,38,1,18,1,1,2,1,64,18,1,6,1,2,2,1,23,1,4,4,1,1,3,4,1,10,1,28,4,12,1,1238,13,1,1,58,1,2,4,1,3,7,1,3,1,4,1,1,1,1,1,1,100' -d90 -cl 'algebraic(0,2,-6,3,-6,12,-4,7,-7,1,0,5,-2,-4,-12,2,7,12,-7,-10,-4,3,9,-7,0,-8,14,-3,9,2,-3,-10,-2,-6,1,10,-3,1,7,-7,7,-12,-5,8,6,10,-8,-8,-7,-3,9,1,6,6,-2,-3,-10,-2,3,5,2,-1,-1,-1,-1,-1,1,2,2,-1,-2,-1,0,1)' # Check that the special case of pow() implemented via algebraic is # working. testcase '0.17677669529663688110' -d20 -- '(+2)^(-10/4)' testcase '0.21022410381342863575' -d20 -- '(+2)^(-9/4)' testcase '0.25' -d20 -- '(+2)^(-8/4)' testcase '0.29730177875068026667' -d20 -- '(+2)^(-7/4)' testcase '0.35355339059327376220' -d20 -- '(+2)^(-6/4)' testcase '0.42044820762685727151' -d20 -- '(+2)^(-5/4)' testcase '0.5' -d20 -- '(+2)^(-4/4)' testcase '0.59460355750136053335' -d20 -- '(+2)^(-3/4)' testcase '0.70710678118654752440' -d20 -- '(+2)^(-2/4)' testcase '0.84089641525371454303' -d20 -- '(+2)^(-1/4)' testcase '1' -d20 -- '(+2)^(0/4)' testcase '1.18920711500272106671' -d20 -- '(+2)^(1/4)' testcase '1.41421356237309504880' -d20 -- '(+2)^(2/4)' testcase '1.68179283050742908606' -d20 -- '(+2)^(3/4)' testcase '2' -d20 -- '(+2)^(4/4)' testcase '2.37841423000544213343' -d20 -- '(+2)^(5/4)' testcase '2.82842712474619009760' -d20 -- '(+2)^(6/4)' testcase '3.36358566101485817212' -d20 -- '(+2)^(7/4)' testcase '4' -d20 -- '(+2)^(8/4)' testcase '4.75682846001088426686' -d20 -- '(+2)^(9/4)' testcase '5.65685424949238019520' -d20 -- '(+2)^(10/4)' testcase '0.25' -d20 -- '(+2)^(-10/5)' testcase '0.28717458874925875169' -d20 -- '(+2)^(-9/5)' testcase '0.32987697769322356484' -d20 -- '(+2)^(-8/5)' testcase '0.37892914162759952058' -d20 -- '(+2)^(-7/5)' testcase '0.43527528164806206956' -d20 -- '(+2)^(-6/5)' testcase '0.5' -d20 -- '(+2)^(-5/5)' testcase '0.57434917749851750339' -d20 -- '(+2)^(-4/5)' testcase '0.65975395538644712968' -d20 -- '(+2)^(-3/5)' testcase '0.75785828325519904117' -d20 -- '(+2)^(-2/5)' testcase '0.87055056329612413913' -d20 -- '(+2)^(-1/5)' testcase '1' -d20 -- '(+2)^(0/5)' testcase '1.14869835499703500679' -d20 -- '(+2)^(1/5)' testcase '1.31950791077289425937' -d20 -- '(+2)^(2/5)' testcase '1.51571656651039808234' -d20 -- '(+2)^(3/5)' testcase '1.74110112659224827827' -d20 -- '(+2)^(4/5)' testcase '2' -d20 -- '(+2)^(5/5)' testcase '2.29739670999407001359' -d20 -- '(+2)^(6/5)' testcase '2.63901582154578851874' -d20 -- '(+2)^(7/5)' testcase '3.03143313302079616469' -d20 -- '(+2)^(8/5)' testcase '3.48220225318449655654' -d20 -- '(+2)^(9/5)' testcase '4' -d20 -- '(+2)^(10/5)' testcase '0.25' -d20 -- '(-2)^(-8/4)' testcase '-0.5' -d20 -- '(-2)^(-4/4)' testcase '1' -d20 -- '(-2)^(0/4)' testcase '-2' -d20 -- '(-2)^(4/4)' testcase '4' -d20 -- '(-2)^(8/4)' testcase '0.25' -d20 -- '(-2)^(-10/5)' testcase '-0.28717458874925875169' -d20 -- '(-2)^(-9/5)' testcase '0.32987697769322356484' -d20 -- '(-2)^(-8/5)' testcase '-0.37892914162759952058' -d20 -- '(-2)^(-7/5)' testcase '0.43527528164806206956' -d20 -- '(-2)^(-6/5)' testcase '-0.5' -d20 -- '(-2)^(-5/5)' testcase '0.57434917749851750339' -d20 -- '(-2)^(-4/5)' testcase '-0.65975395538644712968' -d20 -- '(-2)^(-3/5)' testcase '0.75785828325519904117' -d20 -- '(-2)^(-2/5)' testcase '-0.87055056329612413913' -d20 -- '(-2)^(-1/5)' testcase '1' -d20 -- '(-2)^(0/5)' testcase '-1.14869835499703500679' -d20 -- '(-2)^(1/5)' testcase '1.31950791077289425937' -d20 -- '(-2)^(2/5)' testcase '-1.51571656651039808234' -d20 -- '(-2)^(3/5)' testcase '1.74110112659224827827' -d20 -- '(-2)^(4/5)' testcase '-2' -d20 -- '(-2)^(5/5)' testcase '2.29739670999407001359' -d20 -- '(-2)^(6/5)' testcase '-2.63901582154578851874' -d20 -- '(-2)^(7/5)' testcase '3.03143313302079616469' -d20 -- '(-2)^(8/5)' testcase '-3.48220225318449655654' -d20 -- '(-2)^(9/5)' testcase '4' -d20 -- '(-2)^(10/5)' # Check other special cases of pow. testcase '1' -d70 'pi^0' testcase '0' -d70 '0^pi' testcase '3.1415926535897932384626433832795028841971693993751058209749445923078164' -d70 'pi^1' testcase '0.3183098861837906715377675267450287240689192914809128974953346881177935' -d70 'pi^-1' # Tests of degree-scaled trig functions testcase '0' -d6 'sind(-360)' testcase '0.5' -d6 'sind(-330)' testcase '0.866025' -d6 'sind(-300)' testcase '1' -d6 'sind(-270)' testcase '0.866025' -d6 'sind(-240)' testcase '0.5' -d6 'sind(-210)' testcase '0' -d6 'sind(-180)' testcase '-0.5' -d6 'sind(-150)' testcase '-0.866025' -d6 'sind(-120)' testcase '-1' -d6 'sind(-90)' testcase '-0.866025' -d6 'sind(-60)' testcase '-0.5' -d6 'sind(-30)' testcase '0' -d6 'sind(0)' testcase '0.5' -d6 'sind(30)' testcase '0.866025' -d6 'sind(60)' testcase '1' -d6 'sind(90)' testcase '0.866025' -d6 'sind(120)' testcase '0.5' -d6 'sind(150)' testcase '0' -d6 'sind(180)' testcase '-0.5' -d6 'sind(210)' testcase '-0.866025' -d6 'sind(240)' testcase '-1' -d6 'sind(270)' testcase '-0.866025' -d6 'sind(300)' testcase '-0.5' -d6 'sind(330)' testcase '0' -d6 'sind(360)' testcase '1' -d6 'cosd(-360)' testcase '0.866025' -d6 'cosd(-330)' testcase '0.5' -d6 'cosd(-300)' testcase '0' -d6 'cosd(-270)' testcase '-0.5' -d6 'cosd(-240)' testcase '-0.866025' -d6 'cosd(-210)' testcase '-1' -d6 'cosd(-180)' testcase '-0.866025' -d6 'cosd(-150)' testcase '-0.5' -d6 'cosd(-120)' testcase '0' -d6 'cosd(-90)' testcase '0.5' -d6 'cosd(-60)' testcase '0.866025' -d6 'cosd(-30)' testcase '1' -d6 'cosd(0)' testcase '0.866025' -d6 'cosd(30)' testcase '0.5' -d6 'cosd(60)' testcase '0' -d6 'cosd(90)' testcase '-0.5' -d6 'cosd(120)' testcase '-0.866025' -d6 'cosd(150)' testcase '-1' -d6 'cosd(180)' testcase '-0.866025' -d6 'cosd(210)' testcase '-0.5' -d6 'cosd(240)' testcase '0' -d6 'cosd(270)' testcase '0.5' -d6 'cosd(300)' testcase '0.866025' -d6 'cosd(330)' testcase '1' -d6 'cosd(360)' testcase '0' -d6 'tand(-180)' testcase '0.414213' -d6 'tand(-315/2)' testcase '1' -d6 'tand(-135)' testcase '2.414213' -d6 'tand(-225/2)' testcase '-2.414213' -d6 'tand(-135/2)' testcase '-1' -d6 'tand(-45)' testcase '-0.414213' -d6 'tand(-45/2)' testcase '0' -d6 'tand(0)' testcase '0.414213' -d6 'tand(45/2)' testcase '1' -d6 'tand(45)' testcase '2.414213' -d6 'tand(135/2)' testcase '-2.414213' -d6 'tand(225/2)' testcase '-1' -d6 'tand(135)' testcase '-0.414213' -d6 'tand(315/2)' testcase '0' -d6 'tand(180)' testcase '-90' -d6 'asind(-1)' testcase '-48.590377' -d6 'asind(-0.75)' testcase '-30' -d6 'asind(-0.5)' testcase '-14.477512' -d6 'asind(-0.25)' testcase '0' -d6 'asind(0)' testcase '14.477512' -d6 'asind(0.25)' testcase '30' -d6 'asind(0.5)' testcase '48.590377' -d6 'asind(0.75)' testcase '90' -d6 'asind(1)' testcase '180' -d6 'acosd(-1)' testcase '138.590377' -d6 'acosd(-0.75)' testcase '120' -d6 'acosd(-0.5)' testcase '104.477512' -d6 'acosd(-0.25)' testcase '90' -d6 'acosd(0)' testcase '75.522487' -d6 'acosd(0.25)' testcase '60' -d6 'acosd(0.5)' testcase '41.409622' -d6 'acosd(0.75)' testcase '0' -d6 'acosd(1)' testcase '-45' -d6 'atand(-1)' testcase '-26.565051' -d6 'atand(-0.5)' testcase '0' -d6 'atand(0)' testcase '26.565051' -d6 'atand(0.5)' testcase '45' -d6 'atand(1)' testcase '36.869897' -d6 'atan2d(3,4)' testcase '-21.801409' -d6 'atan2d(-2,5)' testcase '98.130102' -d6 'atan2d(7,-1)' testcase '-146.309932' -d6 'atan2d(-6,-9)' testcase '0' -d6 'atan2d(0,1)' testcase '180' -d6 'atan2d(0,-1)' testcase '90' -d6 'atan2d(1,0)' testcase '-90' -d6 'atan2d(-1,0)' testcase '45' -d6 'atan2d(1,1)' testcase '-45' -d6 'atan2d(-1,1)' testcase '135' -d6 'atan2d(1,-1)' testcase '-135' -d6 'atan2d(-1,-1)' # Tests of --printf testcase '3.354680e-05' --printf=%e -- 'pi^-9' testcase '1.053904e-04' --printf=%e -- 'pi^-8' testcase '9.242692e+05' --printf=%e -- 'pi^12' testcase '2.903677e+06' --printf=%e -- 'pi^13' testcase '0.000034' --printf=%f -- 'pi^-9' testcase '0.000105' --printf=%f -- 'pi^-8' testcase '924269.181523' --printf=%f -- 'pi^12' testcase '2903677.270613' --printf=%f -- 'pi^13' testcase '3.35468e-05' --printf=%g -- 'pi^-9' testcase '0.00010539' --printf=%g -- 'pi^-8' testcase '924269' --printf=%g -- 'pi^12' testcase '2.90368e+06' --printf=%g -- 'pi^13' testcase '0x1.921fb54443p+1' --printf=%.10a -- 'pi' testcase '0x1.921fb54443p+0' --printf=%.10a -- 'pi/2' testcase '0x1.921fb54443p-1' --printf=%.10a -- 'pi/4' testcase '0x1.921fb54443p-2' --printf=%.10a -- 'pi/8' testcase '0x1.921fb54443p-3' --printf=%.10a -- 'pi/16' testcase '0x1.921fb54443p-15' --printf=%.10a -- 'pi/65536' testcase '0x1.921fb54443p+17' --printf=%.10a -- 'pi*65536' testcase '0x1.1969364d16p-15' --printf=%.10a -- 'pi^-9' testcase '0x1.ba0a12450cp-14' --printf=%.10a -- 'pi^-8' testcase '0x1.c34da5cf0ap+19' --printf=%.10a -- 'pi^12' testcase '0x1.6273ea2a37p+21' --printf=%.10a -- 'pi^13' testcase '-0x1.921fb54443p-15' --printf=%.10a -- '-pi/65536' testcase '-0x1.c34da5cf0ap+19' --printf=%.10a -- '-pi^12' testcase ' -0x1.921fb54443p-15' --printf='% 50.10a' -- '-pi/65536' testcase '-0x1.c34da5cf0ap+19 ' --printf='%+-50.10a' -- '-pi^12' testcase '-0x00000000000000000000000000000001.c34da5cf0ap+19' --printf='%050.10a' -- '-pi^12' testcase '-0x00000000000000000000000000000001.c34da5cf0ap+19' --printf='% 050.10a' -- '-pi^12' testcase '-0x1.c34da5cf0ap+19 ' --printf='%-050.10a' -- '-pi^12' testcase '3e-05' --printf=%.0e -- 'pi^-9' testcase '1e-04' --printf=%.0e -- 'pi^-8' testcase '9e+05' --printf=%.0e -- 'pi^12' testcase '3e+06' --printf=%.0e -- 'pi^13' testcase '0' --printf=%.0f -- 'pi^-9' testcase '0' --printf=%.0f -- 'pi^-8' testcase '924269' --printf=%.0f -- 'pi^12' testcase '2903677' --printf=%.0f -- 'pi^13' testcase '3e-05' --printf=%.0g -- 'pi^-9' testcase '0.0001' --printf=%.0g -- 'pi^-8' testcase '9e+05' --printf=%.0g -- 'pi^12' testcase '3e+06' --printf=%.0g -- 'pi^13' testcase '3.4e-05' --printf=%.1e -- 'pi^-9' testcase '1.1e-04' --printf=%.1e -- 'pi^-8' testcase '9.2e+05' --printf=%.1e -- 'pi^12' testcase '2.9e+06' --printf=%.1e -- 'pi^13' testcase '0.0' --printf=%.1f -- 'pi^-9' testcase '0.0' --printf=%.1f -- 'pi^-8' testcase '924269.2' --printf=%.1f -- 'pi^12' testcase '2903677.3' --printf=%.1f -- 'pi^13' testcase '3e-05' --printf=%.1g -- 'pi^-9' testcase '0.0001' --printf=%.1g -- 'pi^-8' testcase '9e+05' --printf=%.1g -- 'pi^12' testcase '3e+06' --printf=%.1g -- 'pi^13' testcase '3.e-05' --printf=%#.0e -- 'pi^-9' testcase '1.e-04' --printf=%#.0e -- 'pi^-8' testcase '9.e+05' --printf=%#.0e -- 'pi^12' testcase '3.e+06' --printf=%#.0e -- 'pi^13' testcase '0.' --printf=%#.0f -- 'pi^-9' testcase '0.' --printf=%#.0f -- 'pi^-8' testcase '924269.' --printf=%#.0f -- 'pi^12' testcase '2903677.' --printf=%#.0f -- 'pi^13' testcase '3.e-05' --printf=%#.0g -- 'pi^-9' testcase '0.0001' --printf=%#.0g -- 'pi^-8' testcase '9.e+05' --printf=%#.0g -- 'pi^12' testcase '3.e+06' --printf=%#.0g -- 'pi^13' testcase '3.4e-05' --printf=%#.1e -- 'pi^-9' testcase '1.1e-04' --printf=%#.1e -- 'pi^-8' testcase '9.2e+05' --printf=%#.1e -- 'pi^12' testcase '2.9e+06' --printf=%#.1e -- 'pi^13' testcase '0.0' --printf=%#.1f -- 'pi^-9' testcase '0.0' --printf=%#.1f -- 'pi^-8' testcase '924269.2' --printf=%#.1f -- 'pi^12' testcase '2903677.3' --printf=%#.1f -- 'pi^13' testcase '3.e-05' --printf=%#.1g -- 'pi^-9' testcase '0.0001' --printf=%#.1g -- 'pi^-8' testcase '9.e+05' --printf=%#.1g -- 'pi^12' testcase '3.e+06' --printf=%#.1g -- 'pi^13' testcase '0x1p+0' --printf=%a ieee:3f800000 testcase '0x1.fffffep+127' --printf=%a ieee:7f7fffff testcase '0x1p-149' --printf=%a ieee:00000001 testcase '-0x1p+0' --printf=%a ieee:bff0000000000000 testcase '-0x1.fffffffffffffp+1023' --printf=%a ieee:ffefffffffffffff testcase '-0x1p-1074' --printf=%a ieee:8000000000000001 testcase '0X1P+0' --printf=%A ieee:3f800000 testcase '3.354680E-05' --printf=%E -- 'pi^-9' testcase '1.053904E-04' --printf=%E -- 'pi^-8' testcase '9.242692E+05' --printf=%E -- 'pi^12' testcase '2.903677E+06' --printf=%E -- 'pi^13' testcase '0.000034' --printf=%F -- 'pi^-9' testcase '0.000105' --printf=%F -- 'pi^-8' testcase '924269.181523' --printf=%F -- 'pi^12' testcase '2903677.270613' --printf=%F -- 'pi^13' testcase '3.35468E-05' --printf=%G -- 'pi^-9' testcase '0.00010539' --printf=%G -- 'pi^-8' testcase '924269' --printf=%G -- 'pi^12' testcase '2.90368E+06' --printf=%G -- 'pi^13' testcase '0X1.921FB54443P+1' --printf=%.10A -- 'pi' testcase '1.250000e+00' --printf=%e -- 1.25 testcase '1.250000' --printf=%f -- 1.25 testcase '1.25' --printf=%g -- 1.25 testcase '1.25e+07' --printf=%g -- 12500000 testcase '0x1.4p+0' --printf=%a -- 1.25 testcase '1.250000e+00' --printf=%#e -- 1.25 testcase '1.250000' --printf=%#f -- 1.25 testcase '1.25000' --printf=%#g -- 1.25 testcase '1.25000e+07' --printf=%#g -- 12500000 testcase '0x1.4p+0' --printf=%#a -- 1.25 testcase '1.2500000000e+00' --printf=%.10e -- 1.25 testcase '1.2500000000' --printf=%.10f -- 1.25 testcase '1.25' --printf=%.10g -- 1.25 testcase '12500000' --printf=%.10g -- 12500000 testcase '0x1.4000000000p+0' --printf=%.10a -- 1.25 testcase '1.2500000000e+00' --printf=%#.10e -- 1.25 testcase '1.2500000000' --printf=%#.10f -- 1.25 testcase '1.250000000' --printf=%#.10g -- 1.25 testcase '12500000.00' --printf=%#.10g -- 12500000 testcase '0x1.4000000000p+0' --printf=%#.10a -- 1.25 # Regression tests of former exactness hazards, now fixed. testcase '1.7320508075688772935274463415058723669428052538103806280558069794519330' -d70 -- '(sqrt(2) - sqrt(2)) + sqrt(3)' testcase '1.4142135623730950488016887242096980785696718753769480731766797379907324' -d70 -- 'sin(pi) + sqrt(2)' testcase '2.4142135623730950488016887242096980785696718753769480731766797379907324' -d70 -- 'sin(pi/2) + sqrt(2)' testcase '0.4142135623730950488016887242096980785696718753769480731766797379907324' -d70 -- 'sin(3*pi/2) + sqrt(2)' testcase '1.7320508075688772935274463415058723669428052538103806280558069794519330' -d70 -- 'abs(sqrt(2) - sqrt(2)) + sqrt(3)' testcase '1.9142135623730950488016887242096980785696718753769480731766797379907324' -d70 -- '(sin(pi/6) % 10) + sqrt(2)' testcase '0.7071067811865475244008443621048490392848359376884740365883398689953662' -d70 -- 'sqrt(sin(pi/6))' testcase '1.7320508075688772935274463415058723669428052538103806280558069794519330' -d70 -- 'sqrt(sqrt(2) - sqrt(2)) + sqrt(3)' testcase '2.8030395704817853465773186861049226872619959522386784412112159095083408' -d70 -- 'cbrt(cbrt(2) - cbrt(-2)) + cbrt(3)' testcase '1.414213' -d6 -- 'erfinv(sin(pi)) + sqrt(2)' testcase '1.4142135623730950488016887242096980785696718753769480731766797379907324' -d70 -- 'atan(sin(pi)) + sqrt(2)' testcase '1.4142135623730950488016887242096980785696718753769480731766797379907324' -d70 -- 'atan2(sin(pi), 1) + sqrt(2)' testcase '1.5707963267948966192313216916397514420985846996875529104874722961539082' -d70 -- 'atan2(1, sin(pi))' testcase '-1.5707963267948966192313216916397514420985846996875529104874722961539082' -d70 -- 'atan2(-1, sin(pi))' testcase '0.41421356237309504880' -d20 -- 'W(-1/e) + sqrt(2)' testcase '0.41421356237309504880' -d20 -- 'Wn(-1/e) + sqrt(2)' testcase '1.41421356237309504880' -d20 -- 'pow(sin(pi), pi) + sqrt(2)' testcase '1.41421356237309504880' -d20 -- 'pow(sin(pi), 1/pi) + sqrt(2)' # Regression tests added after the base output and rounding rewrite. # Some of these failed in the previous version of the code and now # pass; others passed originally and pass again now, but failed part # way through the rewrite so I kept them on the grounds that they # exercise interesting pieces of the new code. testcase '3.14159265358979323846264338327950288419716940' -d44 --rn pi testcase '0x1p-16495' --printf=%a ieee:00000000000000000000000000000000.8 testcase '0.12345' -d5 --rn 'sin(asin(0.12345))' testcase '0.1235' -d4 --rn 'sin(asin(0.12345)) + pi*1e-100' testcase '0.1234' -d4 --rn 'sin(asin(0.12345)) - pi*1e-100' testcase '11.0010010000111111011010101000100010000101' -b2 -d40 pi testcase '10.0102110122220102110021111102212222201112' -b3 -d40 pi testcase '3.0210033312222020201122030020310301030121' -b4 -d40 pi testcase '3.0663651432036134110263402244652226643520' -b7 -d40 pi testcase '3.243f6a8885a308d313198a2e03707344a4093822' -b16 -d40 pi testcase '3.53I5AB8P5FSA5JHK72I8ASC47WWZLACLJJ9ZN98L' -B36 -d40 pi testcase '5.0661' -b7 -d4 --rn '12341.5*7^-4' testcase '5.0661' -b7 -d4 --rn '12342.5*7^-4' testcase '5.0663' -b7 -d4 --rn '12343.5*7^-4' testcase '5.0663' -b7 -d4 --rn '12344.5*7^-4' testcase '5.0665' -b7 -d4 --rn '12345.5*7^-4' testcase '5.0665' -b7 -d4 --rn '12346.5*7^-4' testcase '5.1000' -b7 -d4 --rn '12347.5*7^-4' testcase '5.1000' -b7 -d4 --rn '12348.5*7^-4' testcase '5.1002' -b7 -d4 --rn '12349.5*7^-4' testcase '5.1002' -b7 -d4 --rn '12350.5*7^-4' testcase '5.1004' -b7 -d4 --rn '12351.5*7^-4' testcase '5.1004' -b7 -d4 --rn '12352.5*7^-4' testcase '13.3050' -b6 -d4 --rn '12341.5*6^-4' testcase '13.3050' -b6 -d4 --rn '12342.5*6^-4' testcase '13.3052' -b6 -d4 --rn '12343.5*6^-4' testcase '13.3052' -b6 -d4 --rn '12344.5*6^-4' testcase '13.3054' -b6 -d4 --rn '12345.5*6^-4' testcase '13.3054' -b6 -d4 --rn '12346.5*6^-4' testcase '13.3100' -b6 -d4 --rn '12347.5*6^-4' testcase '13.3100' -b6 -d4 --rn '12348.5*6^-4' testcase '13.3102' -b6 -d4 --rn '12349.5*6^-4' testcase '13.3102' -b6 -d4 --rn '12350.5*6^-4' testcase '13.3104' -b6 -d4 --rn '12351.5*6^-4' testcase '13.3104' -b6 -d4 --rn '12352.5*6^-4' testcase '13.3110' -b6 -d4 --rn '12353.5*6^-4' testcase '13.3110' -b6 -d4 --rn '12354.5*6^-4' testcase '0.4999999999999999999999999999999999999999' -d40 '1/2 - pi/1e80' testcase '0.5000000000000000000000000000000000000000' -d40 '1/2 + pi/1e80' testcase '0.5' -d40 '1/2' testcase '3' -d0 pi testcase '3.1' -d1 pi testcase '3.14' -d2 pi testcase '0003.141' -w4 -d3 -- 'pi' testcase '-0003.141' -w4 -d3 -- '-pi' testcase '0314.159' -w4 -d3 -- 'pi*1e2' testcase '3141.592' -w4 -d3 -- 'pi*1e3' testcase '31415.926' -w4 -d3 -- 'pi*1e4' testcase '31415' -d0 -- 'pi*1e4' testcase '31410' -d-1 -- 'pi*1e4' testcase '30000' -d-4 -- 'pi*1e4' testcase '0' -d-5 -- 'pi*1e4' testcase '100000' -d-5 --ru -- 'pi*1e4' testcase '1000000' -d-6 --ru -- 'pi*1e4' testcase '0' -d-5 --rn -- 'pi*1e4' testcase '100000' -d-5 --rn -- 'pi*2e4' testcase '0' --printf=%g 0 testcase '0.000000e+00' --printf=%e 0 testcase '0.000000' --printf=%f 0 testcase '0.00000' --printf=%#g 0 testcase '0x0p+0' --printf=%a 0 testcase '0x0.p+0' --printf=%#a 0 testcase '0' 0 testcase '000' -w3 0 testcase '0000' -H 0 testcase '00000000' -S 0 testcase '0000000000000000' -D 0 testcase '00000000000000000000000000000000' -Q 0 testcase '3f800000' -S -d0 --rn ieee:3f7fffff.8 testcase '9999' -d0 --rn 9999.5-pi*1e-100 testcase '9999' -d0 --rno 9999.5 testcase '10000' -d0 --rn 9999.5 testcase '10000' -d0 --rno 9999.5+pi*1e-100 testcase '66666' -d0 -b7 --rn 7^5-0.5 testcase '66666' -d0 -b7 --rno 7^5-0.5-pi*1e-100 testcase '100000' -d0 -b7 --rno 7^5-0.5 testcase '100000' -d0 -b7 --rn 7^5-0.5+pi*1e-100 testcase '0x1.921fb54443p+0' --printf=%.10a pi/2 testcase '0x1.921fb54443p+1' --printf=%.10a pi testcase '0x1.921fb54443p+2' --printf=%.10a pi*2 testcase '0x1.921fb54443p+3' --printf=%.10a pi*4 testcase '0x1.921fb54443p+4' --printf=%.10a pi*8 testcase '0x1.921fb54443p+0' --nibble --printf=%.10a pi/2 testcase '0x3.243f6a8886p+0' --nibble --printf=%.10a pi testcase '0x6.487ed5110bp+0' --nibble --printf=%.10a pi*2 testcase '0xc.90fdaa2217p+0' --nibble --printf=%.10a pi*4 testcase '0x1.921fb54443p+4' --nibble --printf=%.10a pi*8 testcase '40400000' -S -d-22 --rd pi testcase '40800000' -S -d-22 --ru pi testcase '40000000' -S -d-23 --rd pi testcase '40800000' -S -d-23 --ru pi testcase '4008000000000000' -D -d-51 --rd pi testcase '4010000000000000' -D -d-51 --ru pi testcase '4000000000000000' -D -d-52 --rd pi testcase '4010000000000000' -D -d-52 --ru pi testcase '7f800000' -S --rn -d0 'ieee:7f7fffff.8' testcase '7f7fffff' -S --rn -d0 'ieee:7f7fffff.8-pi/1e100' testcase '7f7fffff' -S --rno -d0 'ieee:7f7fffff.8' testcase '7f800000' -S --rno -d0 'ieee:7f7fffff.8+pi/1e100' testcase '7ff0000000000000' -D --rn -d0 'ieee:7fefffffffffffff.8' testcase '7fefffffffffffff' -D --rn -d0 'ieee:7fefffffffffffff.8-pi/1e100' testcase '7fefffffffffffff' -D --rno -d0 'ieee:7fefffffffffffff.8' testcase '7ff0000000000000' -D --rno -d0 'ieee:7fefffffffffffff.8+pi/1e100' testcase '0.2' --printf=%.1f '0.25' testcase '0.2' --printf=%.1f 'sin(asin(0.25))-pi/1e100' testcase '0.3' --printf=%.1f 'sin(asin(0.25))+pi/1e100' testcase '7800' -H 0x8000 testcase '7c00' -H 0x10000 testcase '7c00' -H 0x20000 testcase '7c00' -H 0x40000 testcase '7c00' -H 0x80000 testcase '7c00' -H 0x100000 testcase '7c00' -H 0x200000 testcase '7c00' -H 0x400000 testcase '7c00' -H 0x800000 testcase 'f800' -H -- -0x8000 testcase 'fc00' -H -- -0x10000 testcase 'fc00' -H -- -0x20000 testcase 'fc00' -H -- -0x40000 testcase 'fc00' -H -- -0x80000 testcase 'fc00' -H -- -0x100000 testcase 'fc00' -H -- -0x200000 testcase 'fc00' -H -- -0x400000 testcase 'fc00' -H -- -0x800000 testcase '7ffeffffffffffffffffffffffffdfff.0000000000' -Q -d40 'ieee:7ffeffffffffffffffffffffffffffff * exp(-2^-100)' testcase '7feffffffffffff7.0000000000' -D -d40 'ieee:7fefffffffffffff * exp(-2^-50)' testcase '7f7fffef.00008ffffc' -S -d40 'ieee:7f7fffff * exp(-2^-20)' testcase '7bf7.04fe2b2a8e' -H -d40 'ieee:7bff * exp(-2^-8)' testcase '7fff0000000000000000000000000000' -Q -d40 'ieee:7ffeffffffffffffffffffffffffffff * exp(+2^-100)' testcase '7ff0000000000000' -D -d40 'ieee:7fefffffffffffff * exp(+2^-50)' testcase '7f800000' -S -d40 'ieee:7f7fffff * exp(+2^-20)' testcase '7c00' -H -d40 'ieee:7bff * exp(+2^-8)' testcase '0.779601' --printf=%g 'W(1.7)' # A few small tests of -R output. testcase '1' -R ieee:3f800000 testcase '-1' -R ieee:bf800000 testcase '-4/9' -R 4/-9 # This one isn't known-rational at the start, because the sqrt class # doesn't check for exactly square rationals (currently), but it turns # out rational later. So this case exercises the ConvergentsGenerator # code path. testcase '4' -R 'sqrt(16)' # Tests of tentative output. These use the undocumented # --tentative-test option, which causes spigot to print its tentative # output after a ! and terminate once the digit count exceeds the # argument to the option. So we can test that we're generating the # _right_ tentative output, or any at all in a particular tricky case. testcase '0.1234!5 (10^-50)' --tentative-test=50 'sin(asin(0.12345))' testcase '!0 (10^-50)' --tentative-test=50 'sin(pi)' # The answer below is actually _wrong_ - the real answer is not # exactly 0.12345 - but because we asked for tentative output after # only 50 digits, and really there are 100 zeroes before getting to # something interesting, we test that tentative output _can_ be # printed in cases where it is wrong and would be retracted later. testcase '0.1234!5 (10^-50)' -d110 --tentative-test=50 'sin(asin(0.12345)) + pi*1e-100' # And now we test the same expression with the tentative-digits limit # raised, demonstrating that although spigot has the tentative value # 0.12345 in mind at _some_ point in the computation, it eventually # recognises that that's not right, and retracts it. testcase '0.12345000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031415926535' -d110 --tentative-test=200 'sin(asin(0.12345)) + pi*1e-100' # Check that in cases where the output requires zeroes after the # disputed digit, they appear. testcase '1234!5000 (10^-50)' --tentative-test=50 'sin(asin(0.12345)) * 100000000' testcase '3!e000000 (16^-50)' -S --tentative-test=50 'sin(asin(0.125))' testcase '3e123!400 (16^-50)' -S --tentative-test=50 'sin(asin(ieee:3e123400))' # Tentative output in continued fraction modes. testcase $'0\n!2 (10^40)' --tentative-test=40 -c 'sin(pi/6)' testcase $'0\n!4 (10^40)' --tentative-test=40 -c 'sin(pi/6)^2' testcase $'0\n1\n!3 (10^40)' --tentative-test=40 -c 'sin(pi/3)^2' testcase $'!1 (10^40)' --tentative-test=40 -c 'sin(pi/2)^2' testcase $'0\n!2 (10^40)' --tentative-test=40 -c 'sin(pi/4)^2' testcase $'3\n!(10^40)' --tentative-test=40 -c '3 + abs(sin(pi))' testcase $'!3 (10^40)' --tentative-test=40 -c '3 - abs(sin(pi))' testcase $'3\n!7 (10^40)' --tentative-test=40 -c '22/7 + abs(sin(pi))' testcase $'3\n7\n!(10^40)' --tentative-test=40 -c '22/7 - abs(sin(pi))' testcase $'3\n7\n16\n!(10^40)' --tentative-test=40 -c '355/113 + abs(sin(pi))' testcase $'3\n7\n!16 (10^40)' --tentative-test=40 -c '355/113 - abs(sin(pi))' testcase '0!;2 (10^40)' --tentative-test=40 -cl 'sin(pi/6)' testcase '0!;4 (10^40)' --tentative-test=40 -cl 'sin(pi/6)^2' testcase '0;1!,3 (10^40)' --tentative-test=40 -cl 'sin(pi/3)^2' testcase '!1 (10^40)' --tentative-test=40 -cl 'sin(pi/2)^2' testcase '0!;2 (10^40)' --tentative-test=40 -cl 'sin(pi/4)^2' testcase '3! (10^40)' --tentative-test=40 -cl '3 + abs(sin(pi))' testcase '!3 (10^40)' --tentative-test=40 -cl '3 - abs(sin(pi))' testcase '3!;7 (10^40)' --tentative-test=40 -cl '22/7 + abs(sin(pi))' testcase '3;7! (10^40)' --tentative-test=40 -cl '22/7 - abs(sin(pi))' testcase '3;7,16! (10^40)' --tentative-test=40 -cl '355/113 + abs(sin(pi))' testcase '3;7!,16 (10^40)' --tentative-test=40 -cl '355/113 - abs(sin(pi))' testcase $'0/1\n!1/2 (10^40)' --tentative-test=40 -C 'sin(pi/6)' testcase $'0/1\n!1/4 (10^40)' --tentative-test=40 -C 'sin(pi/6)^2' testcase $'0/1\n1/1\n!3/4 (10^40)' --tentative-test=40 -C 'sin(pi/3)^2' testcase $'!1/1 (10^40)' --tentative-test=40 -C 'sin(pi/2)^2' testcase $'0/1\n!1/2 (10^40)' --tentative-test=40 -C 'sin(pi/4)^2' testcase $'3/1\n!(10^40)' --tentative-test=40 -C '3 + abs(sin(pi))' testcase $'!3/1 (10^40)' --tentative-test=40 -C '3 - abs(sin(pi))' testcase $'3/1\n!22/7 (10^40)' --tentative-test=40 -C '22/7 + abs(sin(pi))' testcase $'3/1\n22/7\n!(10^40)' --tentative-test=40 -C '22/7 - abs(sin(pi))' testcase $'3/1\n22/7\n355/113\n!(10^40)' --tentative-test=40 -C '355/113 + abs(sin(pi))' testcase $'3/1\n22/7\n!355/113 (10^40)' --tentative-test=40 -C '355/113 - abs(sin(pi))' testcase '!1/2 (10^40)' --tentative-test=40 -R 'sin(pi/6)' testcase '!1/4 (10^40)' --tentative-test=40 -R 'sin(pi/6)^2' testcase '!3/4 (10^40)' --tentative-test=40 -R 'sin(pi/3)^2' testcase '!1 (10^40)' --tentative-test=40 -R 'sin(pi/2)^2' testcase '!1/2 (10^40)' --tentative-test=40 -R 'sin(pi/4)^2' testcase '!355/113 (10^40)' --tentative-test=40 -R '355/113 + abs(sin(pi))' testcase '!355/113 (10^40)' --tentative-test=40 -R '355/113 - abs(sin(pi))' # Tests of baseNfile: and cfracfile: syntaxes, inexact and exact. testcase '0.3333333333' -d10 base7file:<(echo 0.2222222222222222222222) testcase '1.4142135623' -d10 cfracfile:<(echo '1;2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2') testcase '0.5000' exit:2 -d10 base2file:<(echo 0.100000000000001) testcase '0.5000305175' -d10 base2xfile:<(echo 0.100000000000001) testcase '1.00' exit:2 -d10 cfracfile:<(echo '1;1000') testcase '1.001' -d10 cfracxfile:<(echo '1;1000') # Tests of 'let' in expressions. testcase '3' -d40 'let x=1 in x+2' testcase '13.8429592019974901448229600419452604080771' -d40 'let x=pi, y=e in (x+y)/(x-y)' testcase '2' -d40 'let x=1, y=x+1 in y' testcase '2' -d40 'let x=1, x=x+1 in x' testcase '10' -d40 'let x=3, y=5, f(x,y)=x in f(10,20)' testcase '20' -d40 'let x=3, y=5, f(x,y)=y in f(10,20)' testcase '4' -d40 'let x=3, y=5, f(x,y)=x in f(x+1,y+1)' testcase '6' -d40 'let x=3, y=5, f(x,y)=y in f(x+1,y+1)' testcase '13.8429592019974901448229600419452604080771' -d40 'let f(x,y)=(x+y)/(x-y) in f(pi,e)' testcase '7.2831853071795864769252867665590057683943' -d40 'let f(x)=x+1, g(x)=f(2*x) in g(pi)' testcase '11012' -d40 'let f(x)=x+1 in let a=f(10), f(x)=x+2, b=f(10) in 1000*a+b' testcase '303' -d40 'let f(x)=x+1 in let f(x)=3*f(x) in f(100)' testcase '3.7182818284590452353602874713526624977572' -d40 'let f(e)=e+1 in f(e)' testcase '300' -d40 'let f(x)=x+1, f=3 in f(100)' # f(100) here means multiplication, not function call! testcase '12' -d40 'let f(a,x)=let g(y)=(y+x)/a in g(100) in f(10,20)' testcase '10004000800080005' -d40 'let f(x) = x^2+1 in f(f(f(100)))' # Exponential and logarithmic integrals. testcase '0.5772156649015328606065120900824024310421593359399235988057672348848677' -d70 eulergamma testcase '2492.2289762418' -d10 'Ei(10)' testcase '1.8951178163' -d10 'Ei(1)' testcase '-1.6228128139' -d10 'Ei(0.1)' testcase '-1.8229239584' -d10 'Ei(-0.1)' testcase '-0.2193839343' -d10 'Ei(-1)' testcase '-0.000004156968929' -d15 'Ei(-10)' testcase '1.8229239584' -d10 'E1(0.1)' testcase '0.2193839343' -d10 'E1(1)' testcase '0.000004156968929' -d15 'E1(10)' testcase '1.8229239584' -d10 'En(1,0.1)' testcase '0.2193839343' -d10 'En(1,1)' testcase '0.000004156968929' -d15 'En(1,10)' testcase '0.1484955067' -d10 'En(2,1)' testcase '0.7225450221' -d10 'En(2,0.1)' testcase '1' -d10 'En(2,0)' testcase '!1 (10^-40)' --tentative-test=40 -d50 'En(2,sin(pi))' testcase '0.0704542374' -d10 'En(5,1)' testcase '0.2190159522' -d10 'En(5,0.1)' testcase '0.25' -d10 'En(5,0)' testcase '0.2!5 (10^-40)' --tentative-test=40 -d50 'En(5,sin(pi))' testcase '0.2' -d10 'En(6,0)' testcase '0.!2 (10^-40)' --tentative-test=40 -d50 'En(6,sin(pi))' testcase '0.1999975000' -d10 'En(6,1e-5)' testcase '0' -d10 'li(0)' testcase '!0 (10^-40)' --tentative-test=40 -d50 'li(sin(pi))' testcase '-0.0001281549' -d10 'li(0.001)' testcase '1.0451637801' -d10 'li(2)' testcase '9.9052999776' -d10 'li(20)' testcase '0' -d10 'Li(2)' testcase '!0 (10^-40)' --tentative-test=40 -d50 'Li(2+sin(pi))' testcase '-1.0451637801' -d10 'Li(0)' testcase '-1.0451637801' -d10 'Li(sin(pi))' testcase '5762208.3302842513' -d10 'Li(1e8)' # Trigonometric integrals. testcase '-0.468169978584882240403351110810' -d30 'FresnelS(-10)' testcase '-0.648125058746010538024507851688' -d30 'FresnelS(-1.234)' testcase '-0.000000000523598775598206592491' -d30 'FresnelS(-0.001)' testcase '0' -d30 'FresnelS(0)' testcase '0.000000523598774675493020221233' -d30 'FresnelS(0.01)' testcase '0.438259147390354766076756696625' -d30 'FresnelS(1)' testcase '0.562390079733005789072678230744' -d30 'FresnelS(5.1)' testcase '-0.499898694205515723614151847735' -d30 'FresnelC(-10)' testcase '-0.692133432902403221351920381407' -d30 'FresnelC(-1.234)' testcase '-0.000999999999999753259889972794' -d30 'FresnelC(-0.001)' testcase '0' -d30 'FresnelC(0)' testcase '0.009999999975325989025462104314' -d30 'FresnelC(0.01)' testcase '0.779893400376822829474206413652' -d30 'FresnelC(1)' testcase '0.499782109976894181293579077211' -d30 'FresnelC(5.1)' testcase '-0.583670899929623342157572409285' -d30 'UFresnelS(-10)' testcase '-0.529969705039466704545154127767' -d30 'UFresnelS(-1.234)' testcase '-0.000000000333333333333309523809' -d30 'UFresnelS(-0.001)' testcase '0' -d30 'UFresnelS(0)' testcase '0.000000333333333095238095313852' -d30 'UFresnelS(0.01)' testcase '0.310268301723381101808152423165' -d30 'UFresnelS(1)' testcase '0.562611566336036411136279337208' -d30 'UFresnelS(5.1)' testcase '-0.601125184813444348131191161140' -d30 'UFresnelC(-10)' testcase '-0.976986384379953668845617811664' -d30 'UFresnelC(-1.234)' testcase '-0.000999999999999900000000000004' -d30 'UFresnelC(-0.001)' testcase '0' -d30 'UFresnelC(0)' testcase '0.009999999990000000004629629628' -d30 'UFresnelC(0.01)' testcase '0.904524237900272081474788366832' -d30 'UFresnelC(1)' testcase '0.700767400881106138280013523028' -d30 'UFresnelC(5.1)' testcase '-1.658347594218874049330971879389' -d30 'Si(-10)' testcase '-1.134254091714359356886171055513' -d30 'Si(-1.234)' testcase '-0.000999999944444446111111082766' -d30 'Si(-0.001)' testcase '0' -d30 'Si(0)' testcase '0.009999944444611110827664705285' -d30 'Si(0.01)' testcase '0.946083070367183014941353313823' -d30 'Si(1)' testcase '1.531253204712921712461203236721' -d30 'Si(5.1)' testcase '-3.229143921013770668562293571029' -d30 'si(-10)' testcase '-2.705050418509255976117492747153' -d30 'si(-1.234)' testcase '-1.571796326739341065342432774406' -d30 'si(-0.001)' testcase '-1.570796326794896619231321691639' -d30 'si(0)' testcase '-1.560796382350285508403656986354' -d30 'si(0.01)' testcase '-0.624713256427713604289968377816' -d30 'si(1)' testcase '-0.039543122081974906770118454917' -d30 'si(5.1)' testcase '-4.027979520982392072243975614535' -d30 'Ci(0.01)' testcase '0.337403922900968134662646203889' -d30 'Ci(1)' testcase '-0.183476263159298893723361322846' -d30 'Ci(5.1)' testcase '2.925257190900033917259036374719' -d30 'Cin(-10)' testcase '0.357335883694896440220840894146' -d30 'Cin(-1.234)' testcase '0.000000249999989583333564814811' -d30 'Cin(-0.001)' testcase '0' -d30 'Cin(0)' testcase '0.000024999895833564814504795249' -d30 'Cin(0.01)' testcase '0.239811742000564725943865886193' -d30 'Cin(1)' testcase '2.389932467791111841956661813040' -d30 'Cin(5.1)' testcase '1.851937051982466170361053370157' -d30 'Si(pi)' testcase '1.418151576132628450245780162299' -d30 'Si(pi*2)' testcase '1.674761798979961265948438707462' -d30 'Si(pi*3)' testcase '1.492161225584460055488343913762' -d30 'Si(pi*4)' testcase '1.633964846102835213309961782908' -d30 'Si(pi*5)' testcase '0.281140725187569551129731678518' -d30 'si(pi)' testcase '-0.152644750662268168985541529340' -d30 'si(pi*2)' testcase '0.103965472185064646717117015822' -d30 'si(pi*3)' testcase '-0.078635101210436563742977777877' -d30 'si(pi*4)' testcase '0.063168519307938594078640091268' -d30 'si(pi*5)' testcase '-3.422733378777362789592375061797' -d30 'si(-pi)' testcase '-2.988947902927525069477101853939' -d30 'si(-pi*2)' testcase '-3.245558125774857885179760399102' -d30 'si(-pi*3)' testcase '-3.062957552379356674719665605402' -d30 'si(-pi*4)' testcase '-3.204761172897731832541283474548' -d30 'si(-pi*5)' testcase '0.472000651439568650777606107614' -d30 'Ci(pi/2)' testcase '-0.198407560692358042506401068142' -d30 'Ci(3*pi/2)' testcase '0.123772275403259569610748691063' -d30 'Ci(5*pi/2)' testcase '-0.089564025928478673491319535090' -d30 'Ci(7*pi/2)' testcase '0.070065077309319066303471205339' -d30 'Ci(9*pi/2)' testcase '-0.057501075399484511100509763606' -d30 'Ci(11*pi/2)' testcase '3.141592653589793238462643383279' -d30 'Cin(sin(pi)) + pi' testcase '0.894831469484144958801022013416' -d30 'UFresnelS(sqrt(pi))' testcase '0.430407724669015767337892650970' -d30 'UFresnelS(sqrt(2*pi))' testcase '0.788258965566705486697496726361' -d30 'UFresnelS(sqrt(3*pi))' testcase '0.486247023312110655335878920337' -d30 'UFresnelS(sqrt(4*pi))' testcase '0.752442667454683164307301913028' -d30 'UFresnelS(sqrt(5*pi))' testcase '0.511729831595120356358288653577' -d30 'UFresnelS(sqrt(6*pi))' testcase '0.977451424291329743058620573233' -d30 'UFresnelC(sqrt(pi/2))' testcase '0.402384257301103419813804613186' -d30 'UFresnelC(sqrt(3*pi/2))' testcase '0.803132272418514862882983513822' -d30 'UFresnelC(sqrt(5*pi/2))' testcase '0.476749034202717824359201776500' -d30 'UFresnelC(sqrt(7*pi/2))' testcase '0.759158428492692762089738477936' -d30 'UFresnelC(sqrt(9*pi/2))' testcase '0.506665396355144388535735316687' -d30 'UFresnelC(sqrt(11*pi/2))' testcase '0.713972214021939613629086444554' -d30 'FresnelS(sqrt(2))' testcase '0.343415678363698242195300815958' -d30 'FresnelS(2)' testcase '0.628939658540111772818446657589' -d30 'FresnelS(sqrt(6))' testcase '0.387968992637084042320995325783' -d30 'FresnelS(sqrt(8))' testcase '-0.713972214021939613629086444554' -d30 'FresnelS(-sqrt(2))' testcase '-0.343415678363698242195300815958' -d30 'FresnelS(-2)' testcase '-0.628939658540111772818446657589' -d30 'FresnelS(-sqrt(6))' testcase '-0.387968992637084042320995325783' -d30 'FresnelS(-sqrt(8))' testcase '0.779893400376822829474206413652' -d30 'FresnelC(1)' testcase '0.321056186410678069571149251917' -d30 'FresnelC(sqrt(3))' testcase '0.640806840445253944938767034088' -d30 'FresnelC(sqrt(5))' testcase '0.380390693768025745103558897763' -d30 'FresnelC(sqrt(7))' testcase '-0.779893400376822829474206413652' -d30 'FresnelC(-1)' testcase '-0.321056186410678069571149251917' -d30 'FresnelC(-sqrt(3))' testcase '-0.640806840445253944938767034088' -d30 'FresnelC(-sqrt(5))' testcase '-0.380390693768025745103558897763' -d30 'FresnelC(-sqrt(7))' # Zeta function. testcase '!-1/12 (10^40)' --tentative-test=40 -R 'zeta(-1)' testcase '1.6449340668482264364724151666460251892189499012067984377355582293700074' -d70 'zeta(2)' # = pi^2/6 testcase '1.2020569031595942853997381615114499907649862923404988817922715553418382' -d70 'zeta(3)' # also apery testcase '1.0823232337111381915160036965411679027747509519187269076829762154441206' -d70 'zeta(4)' # = pi^4/90 testcase '1.1762417383825827588721504519380520911697389900216558349605083462304087' -d70 'zeta(pi)' testcase '1.2690096043357171157655698666008611088564044625771904883358159287081652' -d70 'zeta(e)' testcase '3.6009377504588624212922075784754112775567733003375252609915700424205701' -d70 'zeta(4/3)' testcase '3;1,1,1,1,42,18,10,1,3,1,4,1,3,5,1,1,1,1,4,1,1,1,58,1,6,1,3,4,3,1,2,1,1,3,1,1,3,2,3,15,1,2,4,1,14,1,2,2,2,3,2,1,1,4,1,1,5,1,5,1,7,2,4,3,1,4,1,73,1,1' -cl -d70 'zeta(4/3)' testcase '-0.5009199427132187018136921122104145232842867514468597245378462629827225' -d70 'zeta(0.001)' testcase '-0.4990820636452369673701648425135672589037487050379100968862651878869631' -d70 'zeta(-0.001)' # Approximations to zeta^{-1}(2), from https://oeis.org/A107311 . We # don't need to generate many actual output digits from this, because # spigot will have to look all the way to the interesting part anyway # before it can decide whether the integer part is a 2 or a 1. testcase '2.0000000000' -d10 'zeta(1.72864723899818361813510301029769146423410984933503573232128590842317859653571008677274608108898264401)' testcase '1.9999999999' -d10 'zeta(1.72864723899818361813510301029769146423410984933503573232128590842317859653571008677274608108898264402)' if ! $HAVE_FDS; then echo ---- SKIPPING fd tests else echo ---- fd tests # Continued fraction of Champernowne's constant testcase '0;8,9,1,149083,1,1,1,4,1,1,1,3,4,1,1,1,15,4575401113910310764836466282429561185996039397104575550006620043930902626592563149379532077471286563138641209375503552094607183089984575801469863148833592141783010987,6,1,1,21,1,9,1,1,2,3,1,7,2,1,83,1,156,4,58,8,54' -cl -d39 base10fd:0 < <(perl -e 'print "0."; for ($i=1;;$i++) { print "$i" or exit 0 }') # Sine of Champernowne's constant. This is more interesting, # because the internal spigot for base10fd:0 is evaluated multiple # times, so this tests the FileReader's ability to remember what's # already come down its pipe and output it again for further # clients. testcase '0.12314341528265342194575406793995557881004957708147111362905086215948340176187120702779950739421270540745425462726799197837200042174849376893947958414813033644993043499462565832476998778044852939140517' -d200 'sin(base10fd:0)' < <(perl -e 'print "0."; for ($i=1;;$i++) { print "$i" or exit 0 }') # Sinc function of Champernowne's constant. This time we # _explicitly_ mention the same fd twice in the expression, rather # than constructing the two FileReaders by spigot cloning. testcase '0.9974616720494645805173311707960024456968929015358390562540783832156712' -d70 'sin(base10fd:0)/base10fd:0' < <(perl -e 'print "0."; for ($i=1;;$i++) { print "$i" or exit 0 }') # What MathWorld calls the 'Continued Fraction Constant'. testcase '0.69777465796400798200679059255175259948665826299802123236863008281653085276464111299696565418267656872398282187739641339311319229611953258394826715402336857207708468793165325967680260969934477352791348' -d200 cfracfd:0 < <(perl -e 'print "0;"; for ($i=1;;$i++) { print "$i," or exit 0 }') fi echo ---- results echo "pass $pass" echo "fail $fail" echo "total $((pass+fail))" test $fail -eq 0 spigot-0.2017-01-15.gdad1bbc6/trig.cpp0000644000000000000000000005116113025565031013630 0ustar /* * trig.cpp: Trigonometric functions (sin, cos, tan). */ #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" class SinRational : public Source { /* * We compute the sine of a rational by translating the obvious * power series for sin(x) into a spigot description. * * Let x = n/d. Then we have * * n n^3 n^5 * sin(n/d) = - - ------ + ------ - ... * d 3! d^5 5! d^5 * * n n^2 n^2 * = - ( 1 - ------- ( 1 - ------- ( ... ) ) ) * d 2.3.d^2 4.5.d^2 * * so our matrices go * * ( n 0 ) ( -n^2 2.3.d^2 ) ( -n^2 4.5.d^2 ) ... * ( 0 d ) ( 0 2.3.d^2 ) ( 0 4.5.d^2 ) */ bigint n, d, n2, d2, k, kk; bool negate; int crState; public: SinRational(const bigint &an, const bigint &ad, bool anegate) : n(an), d(ad), negate(anegate) { crState = -1; } virtual SinRational *clone() { return new SinRational(n, d, negate); } bool gen_interval(bigint *low, bigint *high) { /* I totally made these numbers up, but they seem to work. Ahem. */ *low = 0; *high = 2; return false; } bool gen_matrix(bigint *matrix) { crBegin; /* * The initial anomalous matrix. */ matrix[1] = matrix[2] = 0; matrix[0] = (negate ? -n : n); matrix[3] = d; crReturn(false); /* * Then the regular series. */ k = 1; n2 = n*n; d2 = d*d; while (1) { kk = ++k; kk *= ++k; matrix[0] = -n2; matrix[1] = matrix[3] = d2 * kk; matrix[2] = 0; crReturn(false); } crEnd; } }; class CosSqrtRational : public Source { /* * The cosine series is closely related to the sine series. * * Let x = n/d. Then we have * * n^2 n^4 * cos(n/d) = 1 - ------ + ------ - ... * 2! d^2 4! d^4 * * n^2 n^2 * = ( 1 - ------- ( 1 - ------- ( ... ) ) ) * 1.2.d^2 3.4.d^2 * * so our matrices go * * ( -n^2 1.2.d^2 ) ( -n^2 3.4.d^2 ) ... * ( 0 1.2.d^2 ) ( 0 3.4.d^2 ) * * However, we're going to need this to be a monotonic function in * the interval it works in, and it's not, because of the turning * point at zero. To deal with this, we arrange that this class * receives n^2 and d^2 as parameters, i.e. it gets its input * pre-squared. And then we can do the squaring step in the input * value to spigot_monotone. */ bigint n2, d2, k, kk; bool negate; int crState; public: CosSqrtRational(const bigint &an2, const bigint &ad2, bool anegate) : n2(an2), d2(ad2), negate(anegate) { crState = -1; } virtual CosSqrtRational *clone() { return new CosSqrtRational(n2, d2, negate); } bool gen_interval(bigint *low, bigint *high) { /* I totally made these numbers up, but they seem to work. Ahem. */ *low = 0; *high = 2; return false; } bool gen_matrix(bigint *matrix) { crBegin; /* * Negate the entire thing if necessary. */ if (negate) { matrix[1] = matrix[2] = 0; matrix[0] = -1; matrix[3] = 1; crReturn(false); } k = 0; while (1) { kk = ++k; kk *= ++k; matrix[0] = -n2; matrix[1] = matrix[3] = d2 * kk; matrix[2] = 0; crReturn(false); } crEnd; } }; class AtanRational : public Source { /* * We compute atan by the continued fraction method, which is * * atan(z) = z/(1+z^2/(3+4z^2/(5+...))) * * So let x = n/d. Then we have * * atan(n/d) = (x |-> n/d(1+x)) o * (x |-> n^2/(d^2(3+x) o (x |-> 4n^2/(d^2(5+x) o ... * * so our matrices go * * ( 0 n ) ( 0 n^2 ) ( 0 4n^2 ) ( 0 9n^2 ) ( 0 16n^2 ) ... * ( d d ) ( d^2 3d^2 ) ( d^2 5d^2 ) ( d^2 7d^2 ) ( d^2 9d^2 ) */ bigint n, d, n2, d2, k, k2; int crState; public: AtanRational(const bigint &an, const bigint &ad) : n(an), d(ad) { crState = -1; } virtual AtanRational *clone() { return new AtanRational(n, d); } bool gen_interval(bigint *low, bigint *high) { *low = 0; *high = 0; return false; } bool gen_matrix(bigint *matrix) { crBegin; /* * The initial anomalous matrix. */ matrix[0] = 0; matrix[1] = n; matrix[2] = matrix[3] = d; crReturn(false); /* * Then the regular series. */ k = 3; k2 = 1; n2 = n*n; d2 = d*d; while (1) { matrix[0] = 0; matrix[1] = n2 * k2; matrix[2] = d2; matrix[3] = d2 * k; k2 += k; k += 2; crReturn(false); } crEnd; } }; struct SinConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new SinConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new SinRational(n, d, false); } }; struct SinNegConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new SinNegConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new SinRational(n, d, true); } }; struct CosSqrtConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new CosSqrtConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new CosSqrtRational(n, d, false); } }; struct CosSqrtNegConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new CosSqrtNegConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new CosSqrtRational(n, d, true); } }; struct AtanConstructor : MonotoneConstructor { MonotoneConstructor *clone() { return new AtanConstructor(); } Spigot *construct(const bigint &n, const bigint &d) { return new AtanRational(n, d); } }; Spigot *spigot_sincos(Spigot *a, int is_cos) { bigint n; int quadrant; /* * Range-reduce by subtracting some multiple of pi/2. * * We absolutely don't want to do this _exactly_ right, by * rounding every number to the very nearest multiple of pi/2: * that would introduce an exactness hazard, so that sin(pi/4) * would hang without producing any output not because it's * fundamentally uncomputable but simply because the range * reducer couldn't decide which way to throw it. * * So instead, we find a near-enough rational approximation to the * answer, and range-reduce based on that. */ { /* * Start by finding n such that n/64 is close (enough) to * a/pi. */ StaticGenerator test(spigot_div(a->clone(), spigot_pi())); n = test.get_approximate_approximant(64); } /* * What we really want is the integer part of a / (pi/2), which is * close (enough) to (2*n/64) = n/32. Except that we want to round * to nearest, so add 16 first. */ n = fdiv(n + 16, 32); if (n != 0) { /* Subtract off that multiple of pi/2. */ a = spigot_sub(a, spigot_mul(spigot_rational(n, 2), spigot_pi())); } quadrant = (int)((n % 4U) + is_cos) & 3; if (quadrant == 0) return spigot_monotone(new SinConstructor, a); else if (quadrant == 1) return spigot_monotone(new CosSqrtConstructor, spigot_square(a)); else if (quadrant == 2) return spigot_monotone(new SinNegConstructor, a); else /* (quadrant == 3) */ return spigot_monotone(new CosSqrtNegConstructor, spigot_square(a)); } Spigot *spigot_sin(Spigot *a) { return spigot_sincos(a, 0); } Spigot *spigot_cos(Spigot *a) { return spigot_sincos(a, 1); } Spigot *spigot_tan(Spigot *a) { Spigot *ac = a->clone(); return spigot_div(spigot_sin(a), spigot_cos(ac)); } Spigot *spigot_atan(Spigot *a) { /* * The only range reduction we need for atan is to spot numbers * with really big magnitude. Ideally, we'd spot anything with a * magnitude bigger than 1, and reduce it by taking the reciprocal * (using the identity atan(x) = pi/2 - atan(1/x), or the same * with -pi/2 for negative numbers). * * But, as usual, we don't need to get the right side of 1 in all * cases - it's fine to let borderline cases go in either * direction. So we avoid exactness hazards by doing only an * approximate check. */ bool take_reciprocal = false; int sign = 0; { StaticGenerator test(a->clone()); bigint n = test.get_approximate_approximant(32); if (n > 32) { sign = +1; take_reciprocal = true; } else if (n < -32) { sign = -1; take_reciprocal = true; } } if (take_reciprocal) a = spigot_reciprocal(a); a = spigot_monotone(new AtanConstructor, a); if (take_reciprocal) a = spigot_sub(spigot_rational_mul(spigot_pi(), sign, 2), a); return a; } Spigot *spigot_asin(Spigot *a) { /* * The obvious way to compute asin(a) is as atan(a/sqrt(1-a^2)). * This is fine unless a is 1 or -1, or an expression which * non-obviously converges to that, in which case the argument to * atan becomes infinite and things go wrong. So we range-reduce * by first spotting things close to 1, and we generate those a * different way. */ bool invert = false; int sign = 0; { StaticGenerator test(a->clone()); bigint n = test.get_approximate_approximant(32); if (n > 16) { sign = +1; invert = true; } else if (n < -16) { sign = -1; invert = true; } } Spigot *sqrt_1_minus_a_squared = spigot_sqrt(spigot_quadratic(a->clone(), -1, 0, 1)); if (!invert) { return spigot_atan(spigot_div(a, sqrt_1_minus_a_squared)); } else { /* * For numbers close to 1 or -1, we invert the argument to * atan, i.e. we compute atan(sqrt(1-a^2)/a). Then we have to * subtract the result from pi/2 or -pi/2. */ return spigot_sub(spigot_rational_mul(spigot_pi(), sign, 2), spigot_atan(spigot_div(sqrt_1_minus_a_squared, a))); } } Spigot *spigot_acos(Spigot *a) { /* * Simplest way to get acos right is just to subtract asin from * pi/2, having first special-cased for acos(1) = 0 exactly. */ bigint n, d; if (a->is_rational(&n, &d) && n == d) { return spigot_integer(0); } return spigot_sub(spigot_rational_mul(spigot_pi(), 1, 2), spigot_asin(a)); } static Spigot *spigot_atan2_inner(Spigot *y, Spigot *x, Spigot *(*pi)(), Spigot *(*scale)(Spigot *)) { bigint yn, yd, xn, xd; bool yrat, xrat, yzero, xzero; /* * Start by spotting exact zeroes among the inputs. */ yrat = y->is_rational(&yn, &yd); yzero = yrat && yn == 0; xrat = x->is_rational(&xn, &xd); xzero = xrat && xn == 0; if (yzero && xzero) { throw spigot_error("atan2(0,0) has no answer"); } if (yzero) { if (get_sign(x->clone()) < 0) return pi(); else return spigot_integer(0); } if (xzero) { if (get_sign(y->clone()) < 0) return spigot_rational_mul(pi(), -1, 2); else return spigot_rational_mul(pi(), 1, 2); } if (yrat && xrat && yd == xd && bigint_abs(yn) == bigint_abs(xn)) { /* * For the case where we're returning an answer in degrees * (see below), we must give special handling to the case * where |y|=|x|, because it'll have to be a multiple of 45 * degrees. */ if (yn > 0 && xn > 0) return spigot_rational_mul(pi(), 1, 4); else if (yn > 0 && xn < 0) return spigot_rational_mul(pi(), 3, 4); else if (yn < 0 && xn > 0) return spigot_rational_mul(pi(), -1, 4); else if (yn < 0 && xn < 0) return spigot_rational_mul(pi(), -3, 4); else assert(!"Should have hit one of those cases"); } /* * OK, those are the silly answers. Now for the sensible ones. We * begin by doing a _parallel_ sign test of y and x, in case one * of them is non-obviously zero. (If both are non-obviously zero, * we really do have to hang - there's nothing we can do.) */ int s = parallel_sign_test(y->clone(), x->clone()); if (s == +2) { /* * If x is positive, we're in the right half-plane, and can * just use normal atan. */ return scale(spigot_atan(spigot_div(y, x))); } else if (s == +1) { /* * If y is positive, we're in the top half-plane, so we can * return pi/2 + atan(-x/y) = pi/2 - atan(x/y). */ return spigot_sub(spigot_rational_mul(pi(), 1, 2), scale(spigot_atan(spigot_div(x, y)))); } else if (s == -1) { /* * If y is negative, we're in the bottom half-plane, so we can * do the same thing with -pi/2. */ return spigot_sub(spigot_rational_mul(pi(), -1, 2), scale(spigot_atan(spigot_div(x, y)))); } else { /* * If x is negative, we really do need to know the exact sign * of y before we can decide whether to adjust atan(y/x) by * plus or minus pi. */ if (get_sign(y->clone()) >= 0) /* top left quadrant */ return spigot_add(scale(spigot_atan(spigot_div(y, x))), pi()); else /* bottom left quadrant */ return spigot_sub(scale(spigot_atan(spigot_div(y, x))), pi()); } } static Spigot *spigot_identity(Spigot *x) { return x; } Spigot *spigot_atan2(Spigot *y, Spigot *x) { return spigot_atan2_inner(y, x, spigot_pi, spigot_identity); } /* ---------------------------------------------------------------------- * Trig functions taking arguments in degrees. * * These are basically equivalent to obvious things like sin(x*pi/180) * or asin(x)*180/pi, except that we take a little care to handle * rational input values which give rise to rational output values. * * The full set of such values is helpfully given for us by Niven's * theorem[1], which says (paraphrased) that the only rational values * of sin(x) arising from x being a rational multiple of pi are -1, * -1/2, 0, 1/2 and 1. * * This implies that the only input values we need to treat * interestingly for sind and cosd are multiples of 30 degrees. * * For tand, we can prove a corollary of Niven's theorem: the only * rational values of tan(x) arising from x being a rational multiple * of pi are -1, 0 and 1 (and infinity, which kind of counts in the * kind of sense that it's kind of a reciprocal of a rational, and * also counts in the more practical sense that we have to care about * it here). * * Proof: suppose tan(x) = p/q. Then the complex number q+ip has * argument x. If x is a rational multiple of pi, that implies that * q+ip has the same argument as a root of unity, i.e. q+ip is a real * multiple of a root of unity. So (q+ip)^2 is also a real multiple of * a root of unity. But (q+ip)^2 has modulus (p^2+q^2), which is an * integer, and hence it's a _rational_ multiple of a root of unity, * i.e. (q+ip)^2/(p^2+q^2) actually is a root of unity which has * _both_ real and imaginary parts rational. By Niven's theorem, those * real and imaginary parts must have absolute value 0, 1/2 or 1; the * 1/2 case is ruled out by _both_ parts having to be rational (any * angle which has sine 1/2 or -1/2 has an irrational cosine and vice * versa), so in fact both real and imaginary parts of that number * must be 0 or -1 or +1. Hence, (q+ip)^2 must be a real multiple of a * 4th root of unity; so (q+ip) must be a real multiple of an 8th root * of unity, i.e. its argument is a multiple of 45 degrees and so its * tangent is either 0, +1, -1, or infinite. [] * * [1] http://en.wikipedia.org/wiki/Niven%27s_theorem */ Spigot *spigot_sind(Spigot *x) { bigint n, d; if (x->is_rational(&n, &d) && d == 1 && n % 30U == 0) { // Reduce mod 360 using fdiv(), to get a non-negative result. switch ((int)(n - 360 * fdiv(n, 360))) { case 0: case 180: return spigot_integer(0); case 90: return spigot_integer(1); case 270: return spigot_integer(-1); case 30: case 150: return spigot_rational(1, 2); case 210: case 330: return spigot_rational(-1, 2); } } return spigot_sin(spigot_mul(spigot_pi(), spigot_rational_mul(x, 1, 180))); } Spigot *spigot_cosd(Spigot *x) { bigint n, d; if (x->is_rational(&n, &d) && d == 1 && n % 30U == 0) { // Reduce mod 360 using fdiv(), to get a non-negative result. switch ((int)(n - 360 * fdiv(n, 360))) { case 90: case 270: return spigot_integer(0); case 0: return spigot_integer(1); case 180: return spigot_integer(-1); case 60: case 300: return spigot_rational(1, 2); case 120: case 240: return spigot_rational(-1, 2); } } return spigot_cos(spigot_mul(spigot_pi(), spigot_rational_mul(x, 1, 180))); } Spigot *spigot_tand(Spigot *x) { bigint n, d; if (x->is_rational(&n, &d) && d == 1 && n % 45U == 0) { // Reduce mod 180 using fdiv(), to get a non-negative result. switch ((int)(n - 180 * fdiv(n, 180))) { case 0: case 180: return spigot_integer(0); case 45: return spigot_integer(1); case 135: return spigot_integer(-1); case 90: throw spigot_error("tand of an odd multiple of 90 degrees"); } } return spigot_tan(spigot_mul(spigot_pi(), spigot_rational_mul(x, 1, 180))); } Spigot *spigot_asind(Spigot *x) { bigint n, d; if (x->is_rational(&n, &d) && (bigint)2 % d == 0) { if (d == 1 && n == 0) return spigot_integer(0); else if (d == 1 && n == -1) return spigot_integer(-90); else if (d == 1 && n == +1) return spigot_integer(+90); else if (d == 2 && n == -1) return spigot_integer(-30); else if (d == 2 && n == +1) return spigot_integer(+30); } return spigot_div(spigot_rational_mul(spigot_asin(x), 180, 1), spigot_pi()); } Spigot *spigot_acosd(Spigot *x) { bigint n, d; if (x->is_rational(&n, &d) && (bigint)2 % d == 0) { if (d == 1 && n == 0) return spigot_integer(90); else if (d == 1 && n == -1) return spigot_integer(180); else if (d == 1 && n == +1) return spigot_integer(+0); else if (d == 2 && n == -1) return spigot_integer(120); else if (d == 2 && n == +1) return spigot_integer(60); } return spigot_div(spigot_rational_mul(spigot_acos(x), 180, 1), spigot_pi()); } Spigot *spigot_atand(Spigot *x) { bigint n, d; if (x->is_rational(&n, &d) && d == 1) { if (n == 0) return spigot_integer(0); else if (n == -1) return spigot_integer(-45); else if (n == +1) return spigot_integer(+45); } return spigot_div(spigot_rational_mul(spigot_atan(x), 180, 1), spigot_pi()); } static Spigot *one_hundred_and_eighty() { return spigot_integer(180); } static Spigot *radians_to_degrees(Spigot *x) { return spigot_div(spigot_rational_mul(x, 180, 1), spigot_pi()); } Spigot *spigot_atan2d(Spigot *y, Spigot *x) { /* * Most of the above functions are wrappers around the radian * versions with extra special-case handling on the front. This * one has enough complicated special cases already that I decided * it was easier to make the main version parametric in pi :-) */ return spigot_atan2_inner(y, x, one_hundred_and_eighty, radians_to_degrees); } spigot-0.2017-01-15.gdad1bbc6/trigint.cpp0000644000000000000000000003312513025565031014343 0ustar #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" /* * Family of integrals of trigonometric functions. Of course the * actual trig functions themselves - sin, cos, tan - are trivial to * integrate; but if you make small tweaks to turn them into things * like sin(x)/x and sin(x^2), you need a fresh set of special * functions to act as their indefinite integrals. * * All of these have just the one implementation strategy, which is to * take the power series of the integrand and integrate it term by * term. This does pretty badly for large input values, because unlike * the ordinary trig functions with their exact periodicity, there's * no useful property which you can use to range-reduce down to a nice * small interval on which the series converges readily. But I don't * know of any useful way to do better; if anyone reading this does * know one, please do help out! * * (Since for large x all of these functions end up spiralling round * and round some centre point in the (sin,cos) plane and gradually * tending inwards towards the centre point as x->inf, you'd like to * think you could do some sort of large-scale approximation based on * saying 'weeellll, for x in _this_ region, the current radius of the * twirling is about _that_, so we can approximate the function as a * circle of that radius about the limit point, and by finding the * nearest root of the integrand we can easily enough know how far * round that circle we are, hence we must be about _here_' and then * you 'just' have to find a nicely converging way to compute some * kind of correction term. Sadly, every treatment of that idea that * I've actually seen ends up talking about 'asymptotic series', i.e. * ones which for any given x only give you so much precision before * ceasing to converge; so I suspect there wouldn't in fact be a * correction formula which you can keep computing out to arbitrary * precision.) */ class TrigIntSeries : public Source { /* * This class computes four different power series, all obtained * by integrating a sin or cos power series term by term. * * The sine integral, i.e. integral of sin(x)/x. Take the series * for sin(x), divide by x (i.e. just shift every term down one * place), and integrate (i.e. shift every term back up and divide * it by its new index). * * sin x = x - x^3/3! + x^5/5! - x^7/7! + ... * sin x / x = 1 - x^2/3! + x^4/5! - x^6/7! + ... * \int sin x / x = x - x^3/(3*3!) + x^5/(5*5!) - x^7/(7*7!) + ... * * but since we'll be using this class with spigot_monotone, we * must make sure it's monotonic. To arrange this, we subtract off * the first term (effectively making it the integral of * sin(x)/x-1, which is always non-positive), and will add it back * on in post-production: * * - x^3/(3*3!) + x^5/(5*5!) - x^7/(7*7!) + ... * * The cosine integral, i.e. integral of cos(x)/x. Do the same to * the series for cos(x). But the initial constant term causes a * problem, which we deal with by just pretending it isn't there - * i.e. the series we're going to compute here will be the * integral of (cos(x)-1)/x, and then we can add on the integral * of 1/x (i.e. log x) in post-production. So that series * (helpfully already monotonic) is * * (cos x - 1) = x^2/2! + x^4/4! - x^6/6! + ... * (cos x - 1) / x = x /2! + x^3/4! - x^5/6! + ... * \int (cos x - 1) / x = x^2/(2*2!) + x^4/(4*4!) - x^6/(6*6!) + ... * * The Fresnel sin integral, i.e. integral of sin(x^2). This time * we start with the series for sin(x) and space it out by * inserting more zero terms, then integrate _that_ term by term: * * sin x = x - x^3/3! + x^5/5! - x^7/7! + ... * sin x^2 = x^2 - x^6/3! + x^10/5! - x^14/7! + ... * \int sin x^2 = x^3/3 - x^7/(7*3!) + x^11/(11*5!) - x^15/(15*7!) + ... * * Again, we need it to be monotonic; in this case, the simplest * thing is to put an x term back _in_ to make it so. * * -x + x^3/3 - x^7/(7*3!) + x^11/(11*5!) - x^15/(15*7!) + ... * * The Fresnel cos integral, i.e. integral of cos(x^2). Do exactly * the same to cos, which introduces no further trouble: * * cos x = 1 - x^2/2! + x^4/4! - x^6/6! + ... * cos x^2 = 1 - x^4/2! + x^8/4! - x^12/6! + ... * \int cos x^2 = x - x^5/(5*2!) + x^9/(9*4!) - x^13/(13*6!) + ... * * and then monotonise it by removing the x term again: * * - x^5/(5*2!) + x^9/(9*4!) - x^13/(13*6!) + ... * * All of these power series are variants on a general schema, in * which each term consists of a power of x in arithmetic * progression (starting at x^1, x^2 or x^3 and then going up by 2 * or 4 every time); a factorial on the denominator (starting at * 0! or 1!, and again going up by either 2 or 4 places each * time); and another denominator factor in arithmetic progression * (starting at 1, 2 or 3, and going up by 2 or 4 each time). And * they all have alternating signs. In other words, we can write * them all in the form * * X/(f0*k0) - XY/(f0*f1*k1) + XY^2/(f0*f1*f2*k2) - ... * * where k0,k1,k2,... are integers in arithmetic progression, * f0,f1,f2 are the successive factors you multiply in to make up * each factorial, X is the starting power of x, and Y is the * power of x we multiply in each time. * * (The one exception to this schema is the anomalous -x term we * added in for the Fresnel sin integral. We deal with that by * manually adjusting its initial matrix.) * * We spigotise a power series in this format by writing X=n/d, * Y=N/D, and then that power series is rewritten in factorised * form as * * n 1 N 1 N 1 N * ---- ( -- - ---- ( -- - ---- ( -- - ---- ( ... )))) * f0*d k0 f1*D k1 f2*D k2 f3*D * * which gives a series of matrices looking like this: * * ( n 0 ) ( -N*k0 f1*D ) ( -N*k1 f2*D ) ( -N*k2 f3*D ) ... * ( 0 f0*d ) ( 0 f1*D*k0 ) ( 0 f2*D*k1 ) ( 0 f3*D*k2 ) */ public: enum Type { SI, CI, SF, CF }; private: bigint norig, dorig; Type type; bigint n, d, N, D, i, f0, fbase, fstep, kbase, kstep; int crState; public: TrigIntSeries(const bigint &an, const bigint &ad, Type atype) : norig(an), dorig(ad), type(atype) { crState = -1; switch (type) { case SI: n = an*an*an; d = ad*ad*ad; N = an*an; D = ad*ad; kbase = 3; kstep = 2; f0 = -6; fbase = 3; fstep = 2; break; case CI: n = an*an; d = ad*ad; N = an*an; D = ad*ad; kbase = 2; kstep = 2; f0 = 2; fbase = 2; fstep = 2; break; case SF: n = an*an*an; d = ad*ad*ad; N = an*an*an*an; D = ad*ad*ad*ad; kbase = 3; kstep = 4; f0 = 1; fbase = 1; fstep = 2; break; case CF: n = an*an*an*an*an; d = ad*ad*ad*ad*ad; N = an*an*an*an; D = ad*ad*ad*ad; kbase = 5; kstep = 4; f0 = -2; fbase = 2; fstep = 2; break; } dprint("hello TrigIntSeries %b/%b %b/%b k=%b[%b] f0=%b f=%b[%b]", &n, &d, &N, &D, &kbase, &kstep, &f0, &fbase, &fstep); } bigint kval(const bigint &i) { return kbase + kstep * i; } bigint fval(const bigint &i) { bigint ret = 1; bigint base = fbase + fstep * (i-1); for (bigint j = 1; j <= fstep; ++j) ret *= ++base; return ret; } virtual TrigIntSeries *clone() { return new TrigIntSeries(norig, dorig, type); } bool gen_interval(bigint *low, bigint *high) { *low = -1; *high = 1; return true; } bool gen_matrix(bigint *matrix) { crBegin; /* Anomalous initial matrix */ matrix[0] = n; matrix[1] = 0; matrix[2] = 0; matrix[3] = f0 * d; if (type == SF) // the x term matrix[1] = matrix[3] * -norig / dorig; crReturn(false); i = 0; while (1) { { bigint k = kval(i); dprint("k%b = %b", &i, &k); ++i; bigint f = fval(i); dprint("f%b = %b", &i, &f); matrix[0] = -N*k; matrix[1] = f*D; matrix[2] = 0; matrix[3] = matrix[1]*k; } crReturn(false); } crEnd; } }; struct TrigIntSeriesConstructor : MonotoneConstructor { TrigIntSeries::Type type; TrigIntSeriesConstructor(TrigIntSeries::Type atype) : type(atype) {} MonotoneConstructor *clone() { return new TrigIntSeriesConstructor(type); } Spigot *construct(const bigint &n, const bigint &d) { return new TrigIntSeries(n, d, type); } }; Spigot *spigot_Si(Spigot *x) { /* * Si(x) is 'the' sine integral, according to Mathematica: the * integral of sin(x)/x, with Si(0)=0. * * The TrigIntSeries class computes the monotonic function * Si(x)-x, so we must add x back on now. */ Spigot *series = spigot_monotone (new TrigIntSeriesConstructor(TrigIntSeries::SI), x->clone()); return spigot_add(series, x); } Spigot *spigot_si(Spigot *x) { /* * si(x) is just Si(x) translated so that its limit at +inf is * zero, rather than Si(0)=0. Turns out the difference is pi/2. */ return spigot_sub(spigot_Si(x), spigot_rational_mul(spigot_pi(), 1, 2)); } Spigot *spigot_Cin(Spigot *x) { /* * Cin is defined by Wikipedia as the integral of (1-cos x)/x, * which is well defined everywhere, and hence a nice thing to * provide in addition to Ci. (Not to mention that it's simpler to * compute, being just a power series.) * * Cin is mostly monotonic already, so TrigIntSeries just computes * it directly. The only turning point we need to watch out for is * at the origin - but since it's an even function, we deal with * that trivially by taking |x|. */ return spigot_monotone(new TrigIntSeriesConstructor(TrigIntSeries::CI), spigot_abs(x)); } Spigot *spigot_Ci(Spigot *x) { /* * Ci is the actual integral of cos(x)/x. Since the integrand has * a pole at zero, we only permit positive arguments (and as far * as I can see, convention is _not_ to carry through to negative * x via the Cauchy principal value trick as Ei, li and Li do). * Instead we translate to make Ci(x) tend to 0 as x->inf, which * involves a constant term of gamma. */ x = spigot_enforce(x, ENFORCE_GT, spigot_integer(0), spigot_error("Ci of a non-positive number")); Spigot *extra = spigot_add(spigot_eulergamma(), spigot_log(x->clone())); return spigot_sub(extra, spigot_Cin(x)); } Spigot *spigot_UFresnelS(Spigot *x) { /* * UFresnel is my ugly name for the 'unnormalised' Fresnel * integrals, i.e. the straight-up integrals of sin(x^2) and * cos(x^2). Most software I've seen (Maple, Mathematica, various * special-function packages I found on the web for assorted * languages) seem to agree that the 'normalised' Fresnel * integrals below are the ones that deserve supporting (and * certainly the ones that deserve the sensible name). I expose * the unnormalised ones as spigot functions too, partly because * they're easier to compute (they're _just_ a nice power series * with rational coefficients) so good if you can get away with * using them, and mostly because I have to imagine that if you're * integrating something more complicated and get one of these as * an answer then this is the form it'll naturally drop out in. * * As above, TrigIntSeries computed UFresnelS(x)-x, so we add x * after spigot_monotone has finished its work. */ Spigot *series = spigot_monotone (new TrigIntSeriesConstructor(TrigIntSeries::SF), x->clone()); return spigot_add(series, x); } Spigot *spigot_UFresnelC(Spigot *x) { /* * UFresnelC: same discussion as for UFresnelS. */ Spigot *series = spigot_monotone (new TrigIntSeriesConstructor(TrigIntSeries::CF), x->clone()); return spigot_add(series, x); } Spigot *spigot_FresnelS(Spigot *x) { /* * The 'normalised' Fresnel integrals are the integrals of * sin(pi/2 x^2) and cos(pi/2 x^2), which are obtained from the * unnormalised version by scaling before and after, i.e. * FresnelS(x) = sqrt(2/pi) UFresnelS(sqrt(pi/2) x). * * All the hard work has been done in UFresnelS, so we just do * that scaling here in the simplest possible way. */ Spigot *xt, *yt; xt = spigot_mul(x, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2))); yt = spigot_UFresnelS(xt); return spigot_div(yt, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2))); } Spigot *spigot_FresnelC(Spigot *x) { /* * FresnelC: same discussion as for FresnelS. */ Spigot *xt, *yt; xt = spigot_mul(x, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2))); yt = spigot_UFresnelC(xt); return spigot_div(yt, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2))); } spigot-0.2017-01-15.gdad1bbc6/unary.cpp0000644000000000000000000001331113025565031014014 0ustar /* * unary.cpp: Simple unary operations on continued fractions. */ #include #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" class Abs : public BinaryIntervalSource { Spigot *x_orig; BracketingGenerator bg_x; public: Abs(Spigot *x) : x_orig(x->clone()), bg_x(x) { dprint("hello Abs %p", x); } virtual ~Abs() { delete x_orig; } virtual Abs *clone() { return new Abs(x_orig->clone()); } virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { bg_x.get_bracket_shift(ret_lo, ret_hi, ret_bits); dprint("got x bracket (%b,%b) / 2^%d", ret_lo, ret_hi, (int)*ret_bits); /* * Adjust the interval we got to ensure it's all positive. */ if (*ret_hi < 0) { /* * The whole interval was negative, so just reflect it. */ bigint tmp = -*ret_hi; *ret_hi = -*ret_lo; *ret_lo = tmp; } else if (*ret_lo < 0) { /* * The interval crosses zero, so replace it with an * interval with one end at zero and the other end at the * maximum extent. */ bigint tmp = -*ret_lo; if (*ret_hi < tmp) *ret_hi = tmp; *ret_lo = 0; } } }; class Prepend : public Source { /* * This class prepends one extra spigot matrix to the stream * provided by another spigot, which permits us to apply any * Mobius transformation. */ Source *x; bigint a, b, c, d; bool x_force; int crState; public: Prepend(Spigot *ax, bigint aa, bigint ab, bigint ac, bigint ad) : a(aa), b(ab), c(ac), d(ad) { x = ax->toSource(); crState = -1; } ~Prepend() { delete x; } virtual Prepend *clone() { return new Prepend(x->clone(), a, b, c, d); } virtual bool gen_interval(bigint *low, bigint *high) { x_force = x->gen_interval(low, high); return true; /* force the absorption of our prefix matrix */ } virtual bool gen_matrix(bigint *matrix) { crBegin; matrix[0] = a; matrix[1] = b; matrix[2] = c; matrix[3] = d; crReturn(x_force); while (1) { crReturn(x->gen_matrix(matrix)); } crEnd; } }; Spigot *spigot_reciprocal(Spigot *a) { bigint n, d; if (a->is_rational(&n, &d)) { if (n == 0) { throw spigot_error("reciprocal of zero"); } return spigot_rational(d, n); } else { return new Prepend(a, 0, 1, 1, 0); } } Spigot *spigot_rational_mul(Spigot *a, const bigint &n, const bigint &d) { bigint an, ad; if (a->is_rational(&an, &ad)) { return spigot_rational(an * n, ad * d); } else { return new Prepend(a, n, 0, 0, d); } } Spigot *spigot_mobius(Spigot *x, const bigint &a, const bigint &b, const bigint &c, const bigint &d) { bigint an, ad; if (x->is_rational(&an, &ad)) { return spigot_rational(a*an + b*ad, c*an + d*ad); } else { return new Prepend(x, a, b, c, d); } } Spigot *spigot_frac(Spigot *a) { /* * To get the fractional part of a number, we do the completely * obvious thing of finding its floor and subtracting that off. * This has an obvious exactness hazard, but not at any point of * continuity, so it can't be helped - we really do need to know * which side of the boundary we're on. */ StaticGenerator sg(a->clone()); bool constant; bigint intpart = sg.get_floor(&constant); if (constant) return spigot_integer(0); else return spigot_mobius(a, 1, -intpart, 0, 1); } static Spigot *spigot_ieee_rem_1(Spigot *a) { /* * Take the IEEE 754-style remainder of a with 1. That is, find * the nearest integer n to a, and return a-n; if a is equidistant * between two integers, take n to be the even one. * * We do this by the obvious approach of finding the integer part * of a+1/2. As in spigot_frac above, exactness hazards in this * are unavoidable. */ StaticGenerator sg(spigot_add(spigot_rational(1, 2), a->clone())); bool constant; bigint intpart = sg.get_floor(&constant); if (constant) { /* * a+1/2 is _exactly_ equal to intpart, so this is the * half-way case, and we want either a-intpart or * a-(intpart-1). * * If intpart is even, then we want a-intpart = a-(a+1/2) = * -1/2; otherwise we want a-(intpart-1) = a-(a+1/2-1) = +1/2. */ if (intpart % 2U == 0) return spigot_rational(-1, 2); else return spigot_rational(1, 2); } else { return spigot_mobius(a, 1, -intpart, 0, 1); } } Spigot *spigot_mod(Spigot *a, Spigot *b) { return spigot_mul(b, spigot_frac(spigot_div(a, b->clone()))); } Spigot *spigot_rem(Spigot *a, Spigot *b) { return spigot_mul(b, spigot_ieee_rem_1(spigot_div(a, b->clone()))); } Spigot *spigot_neg(Spigot *a) { return spigot_rational_mul(a, -1, 1); } Spigot *spigot_abs(Spigot *a) { bigint n, d; if (a->is_rational(&n, &d)) return spigot_rational(bigint_abs(n), bigint_abs(d)); return new Abs(a); } Spigot *spigot_floor(Spigot *a) { CfracGenerator cfg(a); bigint i; cfg.get_term(&i); return spigot_integer(i); } Spigot *spigot_ceil(Spigot *a) { CfracGenerator cfg(spigot_neg(a)); bigint i; cfg.get_term(&i); return spigot_integer(-i); } spigot-0.2017-01-15.gdad1bbc6/version.h0000644000000000000000000000066613036752737014036 0ustar /* * This header file provides the version #define for a particular * build of spigot. * * When my automated build system does a full build, Buildscr * completely overwrites this file with information appropriate to * that build. The information _here_ is default stuff used for local * development runs of 'make'. */ #ifndef VER /* so that you can make CPPFLAGS+='-DVER="\"version\""' */ #define VER "unidentified build" #endif spigot-0.2017-01-15.gdad1bbc6/zeta.cpp0000644000000000000000000003275313025565031013634 0ustar #include #include #include "spigot.h" #include "funcs.h" #include "cr.h" #include "error.h" /* * Riemann zeta function zeta(s), for real s * ----------------------------------------- * * The basic method is taken from [1], section 1.2, Proposition 2, * which defines a formula for computing zeta(s) with a good error * bound by summing 2n terms of the obvious series n^{-s}, except that * terms n+1,...,2n are multiplied by gradually dwindling correction * factors derived from binomial coefficients. This tapering-off of * the sum causes the output value to be wrong by a factor of * 1-2^{1-s}, but that's easily multiplied back in afterwards; and the * absolute error in approximating (1-2^{1-s})zeta(s) with a 2n-term * version of this series is bounded by a formula that works out (when * s is real) to a maximum absolute error of 8^{-n}, which is very * nice indeed compared to the incredibly slow convergence of just * summing the obvious infinite series for zeta and waiting * * This isn't a very spigot-shaped algorithm, because of the need to * commit up front to the value of n, i.e. how much precision you * want. To spigotise it, we must loop round for larger and larger n; * things fall out reasonably nicely if we make n a power of two, in * that we get approximations in the form of dyadic rationals which we * can feed to a BinaryIntervalSource. * * But that inelegance is more than made up for by the fact that the * formula works even for s < 1! In spite of the fact that the full * infinite sum of n^{-s} diverges in that part of the domain, the * tapering-off series defined by Gourdon & Sebah still keeps working. * * [1] "Numerical evaluation of the Riemann Zeta-function", Xavier * Gourdon and Pascal Sebah, 2003. As of 2015-11-05, available at http://numbers.computation.free.fr/Constants/Miscellaneous/zetaevaluations.pdf */ class IntInterval { /* * Small class to permit error-bound tracking via interval * arithmetic. */ bigint lo, hi; public: IntInterval() {} IntInterval(const bigint &alo, const bigint &ahi) : lo(alo), hi(ahi) { assert(lo <= hi); } IntInterval(const bigint &n) : lo(n), hi(n) {} IntInterval(int n) : lo(n), hi(n) {} IntInterval(unsigned n) : lo(n), hi(n) {} static IntInterval with_error(const bigint &n, const bigint &err) { return IntInterval(n - err, n + err); } void increase_error(const bigint &moreerr) { lo -= moreerr; hi += moreerr; } void get_median_and_error(bigint *median, bigint *error) { *median = fdiv(lo + hi, 2); *error = hi - *median; // fdiv guarantees hi-med >= med-lo } bigint max_magnitude() { bigint ret = bigint_abs(lo); bigint ret2 = bigint_abs(hi); return ret > ret2 ? ret : ret2; } const IntInterval &operator=(const IntInterval &that) { lo = that.lo; hi = that.hi; return *this; } const IntInterval &operator+=(const IntInterval &that) { lo += that.lo; hi += that.hi; return *this; } const IntInterval &operator-=(const IntInterval &that) { lo -= that.lo; hi -= that.hi; return *this; } const IntInterval &operator*=(const IntInterval &that) { bigint newlo, newhi, n; newlo = newhi = lo * that.lo; n = lo * that.hi; if (newlo > n) newlo = n; if (newhi < n) newhi = n; n = hi * that.lo; if (newlo > n) newlo = n; if (newhi < n) newhi = n; n = hi * that.hi; if (newlo > n) newlo = n; if (newhi < n) newhi = n; lo = newlo; hi = newhi; return *this; } const IntInterval &operator*=(const bigint &that) { lo *= that; hi *= that; return *this; } const IntInterval &operator/=(const bigint &that) { lo = fdiv(lo, that); hi = -fdiv(-hi, that); return *this; } const IntInterval &operator>>=(const bigint &that) { lo >>= that; hi = -((-hi) >> that); return *this; } IntInterval operator+(const IntInterval &that) const { IntInterval ret = *this; ret += that; return ret; } IntInterval operator-(const IntInterval &that) const { IntInterval ret = *this; ret -= that; return ret; } IntInterval operator*(const IntInterval &that) const { IntInterval ret = *this; ret *= that; return ret; } IntInterval operator*(const bigint &that) const { IntInterval ret = *this; ret *= that; return ret; } IntInterval operator/(const bigint &that) const { IntInterval ret = *this; ret /= that; return ret; } IntInterval operator>>(const bigint &that) const { IntInterval ret = *this; ret >>= that; return ret; } friend IntInterval operator*(const bigint &a, const IntInterval &b); }; IntInterval operator*(const bigint &a, const IntInterval &b) { return b * a; } /* * Internal helper function returning a dyadic rational approximation * to zeta(s) * (1-2^(1-s)). */ static void spigot_zeta_series_approx(StaticGenerator *gen_minus_s, StaticGenerator *gen_2_to_the_minus_s, unsigned log2_n, bigint *retn, bigint *err, unsigned *dshift) { bigint cn, termlimit, bcoeff, d, n, n2, r, sign; IntInterval sum; /* * Gourdon & Sebah's formula combines 2n values of n^{-s} to give * an approximation to zeta(s) with absolute error at most * 2^{-3n). There will be further error arising from the way we * compute the n^{-s} values, which we'll track carefully as we go * along below; but right now we need to choose a denominator to * multiply all of this by, and it should be 2^{3n} plus a bit. */ unsigned three_n = 3U << log2_n; unsigned i = three_n + log2_n; // Add another factor of 2^n from the series coefficients unsigned d_extra = (1U << log2_n); *dshift = i + d_extra; d = (bigint)1 << i; cn = (bigint)1 << (1U << log2_n); termlimit = cn; sign = +1; sum = 0; bcoeff = 1; n = (bigint)1 << log2_n; n2 = 2*n; /* * Array to hold the (-s)th powers of 1,2,...,n2. We leave the 0th * array element uninitialised (or rather default-initialised) and * will never care about it. */ std::vector powers((unsigned)n2 + 1); /* * Initialise the first two elements of powers[] before we start * our main loop summing the series. The rest will be computed as * we go along. We'll also need the value of -s itself. */ powers[1] = d; /* get_approximate_approximant returns a value within 2 of the * true one. Set up the initial IntIntervals accordingly. */ powers[2] = IntInterval::with_error (gen_2_to_the_minus_s->get_approximate_approximant(d), 2); IntInterval minus_s = IntInterval::with_error (gen_minus_s->get_approximate_approximant(d), 2); /* * Main loop which constructs each powers[] value and adds them * together with the Gourdon & Sebah series coefficients. Most * addition is done using the IntInterval class defined above. */ for (unsigned k = 1; (bigint)k <= n2; ++k) { /* * Construct powers[k]. */ if (k <= 2) { /* Already done before this loop started */ } else if (k % 2 == 0) { /* * We handle even numbers by multiplying 2^{-s} by * (k/2)^{-s}. * * We could do absolutely everything using the binomial * theorem technique in the else clause below, but that * would accumulate error linearly along the powers[] * array. Handling even numbers using this more accurate * technique means that any given value in powers[] will * be derived from at most O(log n) successive binomial- * theorem adjustments, instead of O(n). * * (We could go in completely the other direction, by * computing accurate values for all primes via spigotry, * and then extend this simple multiplication approach to * all composite numbers. But that would slow things down * a lot, according to my experimentation, because spigots * are very slow. This seems a reasonable compromise.) */ powers[k] = (powers[k/2] * powers[2]) >> i; } else { /* * Odd number. In this case we adjust from the previous * value, by using the generalised binomial theorem to * compute a correction factor. We want to turn (k-1)^{-s} * into k^{-s}; the ratio between those values works out * to (1 + 1/(k-1))^{-s}, which the binomial theorem * expands (writing 'prev' for k-1) as * * -s -s -s(-s-1) -s(-s-1)(-s-2) * (1+prev) = 1 + ------- + --------- + -------------- + ... * 1! prev 2! prev^2 3! prev^3 * * We sum that series until either a term becomes zero (if * -s was a positive integer), meaning the binomial series * was finite and has terminated, or because the terms of * an infinite one gets small enough that we're confident * we can bound the error introduced by leaving out the * tail of the series. In the latter case, we must work * out that error bound. * * The jth term of this series has the form * * -s(-s-1)(-s-2)...(-s-j+1) * ------------------------- * j! prev^j * * If we can show that there's some x, with |x|<1, such * that each term's magnitude is at most x times the * previous one, then we can bound the truncation error by * the sum of a GP, which makes the truncation error at * most the size of the first omitted term times 1/(1-x). * * Well, each new term differs from the previous one by * the factor (-s-j+1)/(j prev), so we need that to be * bounded by something less than 1, i.e. we need to know * |j prev| > |-s-j+1|. But if those two numbers are still * _almost_ equal, the 1/(1-x) factor might still be a bit * big, so we might want to carry on summing further * terms. I think a reasonable stopping point is to wait * until the inter-term ratio is at most 2/3, so that the * series tail is at most 3 times the size of the last * included term; that means I need to have 2 |j prev| > 3 * |-s-j+1|. */ unsigned prev = k-1; IntInterval term = powers[prev]; // first term of the series IntInterval total = 0; // sum of the series IntInterval nfactor = minus_s; // inter-term ratio numerator bigint dfactor = prev; // inter-term ratio denominator bool ok_to_terminate = false; while (!ok_to_terminate || term.max_magnitude() > termlimit) { total += term; term = (term * nfactor / dfactor) >> i; nfactor -= (bigint)1 << i; dfactor += prev; if (!ok_to_terminate && 2*dfactor*d > 3*nfactor.max_magnitude()) ok_to_terminate = true; } /* Max error from truncating the binomial series, as * analysed above. */ total.increase_error(term.max_magnitude() * 3); powers[k] = total; } sum += sign * cn * powers[k]; if ((bigint)k >= n) { cn -= bcoeff; bcoeff = bcoeff * (n2-(bigint)k) / ((bigint)k-n+1); } sign = -sign; } /* 2^{-3n} error inherent in this formula for zeta */ sum.increase_error((bigint)1 << (i - three_n + d_extra)); sum.get_median_and_error(retn, err); } class ZetaSeries : public BinaryIntervalSource { Spigot *s; unsigned log2_n; StaticGenerator gen_minus_s, gen_2_to_the_minus_s; public: ZetaSeries(Spigot *as) : s(as->clone()), log2_n(3), gen_minus_s(spigot_neg(as->clone())), gen_2_to_the_minus_s(spigot_exp2 (spigot_neg(as->clone()))) { dprint("hello ZetaSeries"); } virtual ~ZetaSeries() { delete s; } virtual ZetaSeries *clone() { return new ZetaSeries(s); } virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits) { bigint n, err; unsigned dshift; spigot_zeta_series_approx(&gen_minus_s, &gen_2_to_the_minus_s, log2_n, &n, &err, &dshift); *ret_bits = dshift; *ret_lo = n - err; *ret_hi = n + err; log2_n++; } }; Spigot *spigot_zeta(Spigot *s) { Spigot *multiplier = spigot_sub(spigot_integer(1), spigot_pow(spigot_integer(2), spigot_sub(spigot_integer(1), s->clone()))); return spigot_div(new ZetaSeries(s), multiplier); }