spigot-0.2017-01-15.gdad1bbc6/ 0000755 0000000 0000000 00000000000 13036756601 012162 5 ustar spigot-0.2017-01-15.gdad1bbc6/.gitignore 0000644 0000000 0000000 00000000406 12514752325 014151 0 ustar *.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/Buildscr 0000644 0000000 0000000 00000003041 13025565031 013643 0 ustar # -*- 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/LICENCE 0000644 0000000 0000000 00000002101 12514752325 013140 0 ustar 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.am 0000644 0000000 0000000 00000002407 13025573323 014215 0 ustar 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.txt 0000644 0000000 0000000 00000015325 13025565031 013467 0 ustar 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.cpp 0000644 0000000 0000000 00000026555 13025565031 014605 0 ustar /*
* 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.cpp 0000644 0000000 0000000 00000047712 13025565031 015023 0 ustar /*
* 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.sh 0000755 0000000 0000000 00000000061 12514752667 014170 0 ustar #!/bin/sh
autoreconf -i && rm -rf autom4te.cache
spigot-0.2017-01-15.gdad1bbc6/baseout.cpp 0000644 0000000 0000000 00000203750 13025565031 014330 0 ustar /*
* 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