Introduction
------------

``spigot`` is a Python module for exact real arithmetic. That is, it
provides a data type that represents a real number *exactly*, with no
rounding errors, and a full set of arithmetic operators and
mathematical functions that operate on that data type.

This ``spigot`` Python module is a repackaging of the `command-line
program of the same name
<https://www.chiark.greenend.org.uk/~sgtatham/spigot/>`_.

Exact real arithmetic
~~~~~~~~~~~~~~~~~~~~~

If you haven't encountered exact real arithmetic systems before, you
might well wonder if the concept even makes sense.

After all, a real number potentially contains an *infinite* amount of
information, doesn't it? Because every digit of its decimal expansion
can be chosen independently (well, apart from that small corner case
where :math:`0.\dot{9}=1`). But a computer has finite storage. So how
*can* you store the full precision of a real number such as
:math:`\pi`, let alone compute with it?

Half of the answer is: yes, 'most' real numbers contain an infinite
amount of information. But the ones you're actually interested in --
*especially* well-known constants, like :math:`\pi` -- can be
expressed in a finite amount of storage, precisely *because* they can
be defined unambiguously by stating whatever property of the number
made you interested in it. Those other, essentially random, real
numbers aren't going to come up in any concrete computation you're
doing, so it doesn't matter that we can't represent *those*.

The other half of the answer is that you don't need to *store* the
whole of :math:`\pi`'s decimal expansion at once. All you need is to
have an algorithm that can generate it on demand, to whatever
precision is needed. Put another way, as long as you have a program
that can generate better and better *approximations* to :math:`\pi`,
with no limit (except time and memory) on the precision of those
approximations, that's enough to be able to claim justifiably that the
program 'knows what the value of :math:`\pi` is'.

In particular, that's enough information that, if you have *two* real
numbers expressed in this form, you can reliably determine which one
is bigger (even if they differ by some very tiny amount). You just
keep computing a smaller and smaller bounding interval (that is, an
interval of numbers :math:`[a,b]` with the property that
:math:`a\leqslant x\leqslant b`) for each real, and sooner or later --
at least if the numbers *are* different -- those intervals will each
be smaller than half the distance between the two real numbers, which
means they won't be able to overlap any more, which means you can be
sure of which number is the smaller.

(However, beware if the numbers *aren't* different! If you try this
procedure with two numbers that actually, mathematically, have exactly
the same value, then it will never terminate, because your two
bounding intervals will continue to overlap no matter how small they
get. This is an example of an *exactness hazard*.)

Having a generator of better and better approximations to a number is
also enough information to compute with it -- to add or multiply it by
some other number, or to take its square root, or its cosine, or
whatever. As long as the function :math:`f` you're trying to compute
is *continuous*, that's enough to guarantee that software can compute
an arbitrarily precise approximation to :math:`f(x)` as long as it's
allowed to demand an approximation to :math:`x` that's good enough for
it to get the answer with enough precision.

OK, so what does that mean in practice?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

What it means in practice is that once you construct a
:class:`spigot.Spigot` object representing the real number you want,
you can ask that object to deliver approximations to that number, in
various forms, to whatever precision you want.

The simplest example is that Python's string-formatting system works
on ``Spigot`` objects just as well as on floating-point numbers, and
with the same format-specification language:

.. testsetup::

  import math
  import spigot
  import fractions

.. doctest::

  >>> "{:f}".format(spigot.pi)
  '3.14159265358979323846'
  >>> "{:f}".format(math.pi)
  '3.141593'

Those two strings come out with different numbers of digits, but
that's only because the ``Spigot`` and ``float`` types have different
*defaults* for the output precision. If you explicitly set the number
of digits, you can see the real difference:

.. doctest::

  >>> "{:.64f}".format(spigot.pi)
  '3.1415926535897932384626433832795028841971693993751058209749445923'
  >>> "{:.64f}".format(math.pi)
  '3.1415926535897931159979634685441851615905761718750000000000000000'

With this many output digits, it becomes obvious that the built-in
``math`` module's approximation to :math:`\pi` has limited precision.
(Even if you haven't memorised :math:`\pi` to 64 decimal places, you
might have a vague idea that it's unlikely to have sixteen consecutive
zeroes!) But ``spigot.pi`` continues to generate correct digits of
:math:`\pi` right up to the limit of the length of output you asked
for -- and it would still do so if you turned the precision up
further, to 640 digits, or 6400.

Of course, whenever you ask for a real number to be represented to a
finite number of digits of precision, you can't avoid *one* rounding
error, at the point where the infinitely long theoretical expansion
has to be truncated to fit in the output. But with ``spigot``, that is
the *only* rounding error you'll get -- and if you ask the same
``Spigot`` object to deliver a higher-precision string later, you'll
be able to prove to yourself that it did *know* what the right answer
was, even if you didn't give it space to tell it to you.

Once you have a ``Spigot`` object, you can compute with it further, by
using the usual arithmetic operators and a wide range of mathematical
functions provided by the ``spigot`` module. For example, having got a
``Spigot`` representing :math:`\pi`:

.. doctest::

  >>> x = spigot.pi
  >>> x
  <Spigot:3.1415926535897932385>

you can do ordinary arithmetic on it, combining it with Python
integers, fractions, or other ``Spigot`` objects:

.. doctest::

  >>> x * 2
  <Spigot:6.2831853071795864769>
  >>> x / fractions.Fraction(22,7)
  <Spigot:0.99959766250584330315>
  >>> x + spigot.e
  <Spigot:5.8598744820488384738>

and pass either ``x`` itself or a more complicated expression
involving it to various mathematical functions:

.. doctest::

  >>> spigot.sin(x/3)
  <Spigot:0.86602540378443864676>
  >>> spigot.exp(x-1)
  <Spigot:8.5129850740669498265>
  >>> spigot.cosh(x + spigot.sqrt(2))
  <Spigot:47.596984381884511907>

and you can see from the ``repr`` formatting that all of those results
are ``Spigot`` objects which store the results of each of those
computations to unlimited precision.

However, all this computation is not free. For a ``Spigot`` object
generated in this way to be able to know the exact real output value
of a complicated expression like this, it has to contain *the whole
expression* in a form that lets it compute it from the innermost input
values to the final returned output. So the more sequential arithmetic
you do on ``Spigot`` objects, the more complicated expressions you
will find you have incorporated into the objects you construct later
in the chain, and the more and more slowly they will generate their
output.

In other words, I don't recommend doing *iterative* computation with
``Spigot`` objects! Exact real calculation is expensive, and gets more
expensive the more complicated the numbers, so if you try to perform
(say) an iterated numerical algorithm on values of type ``Spigot``,
you will probably find that it doesn't get very far before you lose
patience.

Again, of course, there is an unavoidable rounding error whenever you
*display the output* of one of these exact real expressions, simply
because there isn't space on the screen (or in the universe) to
actually print all of the digits of the number. But ``spigot`` avoids
any rounding errors *other* than that one. Where ordinary
floating-point arithmetic could not compute
:math:`\cosh(\pi+\sqrt{2})` without suffering an accumulation of
rounding errors in computing each subexpression -- :math:`\pi` itself
would not be exactly right, :math:`\sqrt{2}` would not be quite
accurate either, the sum would include both the errors in the inputs
and a third error of its own, and the ``cosh`` function would add yet
another error on top of that -- the ``spigot`` version of the same
expression produces no rounding error other than the final one on
output, and even that one is easily controllable.

If you really don't like the rounding error on output, there is even a
way to dispense with it, using the Python iterator system. Given a
``Spigot`` representing a real number :math:`x`, you can request an
iterator that yields the characters of its decimal expansion one by
one, and if :math:`x` has a non-terminating expansion, then the
iterator will simply never stop yielding digits.

  >>> it = spigot.pi.base_format()
  >>> next(it)
  '3'
  >>> next(it)
  '.'
  >>> next(it)
  '1'
  >>> next(it)
  '4'
  >>> next(it)
  '1'

Now you've still got the iterator object ``it``, and you can ask it
for the next digits as soon as you're ready to do something with them.
With this system, you don't have to commit in advance to the number of
digits of precision you want: you can fetch a few, see if it's enough
for whatever you wanted to do, and if not, keep fetching more from the
same iterator until it is.

However, beware of using this function if the number you're generating
*doesn't* have an endless decimal expansion! In some situations, such
as when the ``Spigot`` object *knows* it's dealing with a nice simple
rational number, this will work as you expect and deliver a finite
sequence of digits followed by a ``StopIteration`` exception. But in
more difficult cases, you can find that an attempt to fetch the next
character from one of these iterators might hang indefinitely, while
the ``spigot`` core machinery keeps narrowing its bounding interval
for the number but never quite manages to narrow it far enough to be
completely sure of what the next digit might be. For example:

  >>> it = spigot.sin(spigot.asin(0.125)).base_format()
  >>> next(it)
  '0'
  >>> next(it)
  '.'
  >>> next(it)
  '1'
  >>> next(it)
  '2'

The true mathematical value of :math:`\sin(\sin^{-1}(0.125))` is, of
course, :math:`0.125`. A human mathematician can see this very
quickly, and even prove it formally, since it's true *by definition*
-- the ``asin`` (or :math:`\sin^{-1}`) function is precisely defined
to be the function for which that is true. But ``spigot`` does not
know that fact, because all it knows how to do with real numbers is to
mechanically compute with them, not how to *reason* about them as a
mathematician would.

So in the above snippet, if you were to call ``next(it)`` one more
time, expecting the answer ``'5'``, you'd be disappointed, because
``spigot`` would sit there, consuming more and more CPU time and
memory, and all it would ever find out was that the value of its real
number was in the range :math:`[0.124999999,0.125000001]` with
ever-longer strings of 9s and 0s. It would never manage to find out
that the next digit of the answer should be ``'5'``, let alone that
the true answer is :math:`0.125` *exactly*.

For more discussion of exactness hazards and the various circumstances
in which they can arise, see the `hazards chapter
<https://www.chiark.greenend.org.uk/~sgtatham/spigot/spigot.html#hazards>`_
of the manual for command-line ``spigot``. (But ignore the parts about
the 'tentative output' feature of the command-line tool; there is no
analogue of that in this Python module.)

References
~~~~~~~~~~

The name comes from the central algorithm which the whole program is
based on, which is Jeremy Gibbons's `spigot algorithm
<http://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/spigot.pdf>`_
for computing :math:`\pi`.

For further references on exact real arithmetic, both algorithmic
techniques and other implementations, see the `references appendix
<https://www.chiark.greenend.org.uk/~sgtatham/spigot/spigot.html#refs>`_
of the manual for command-line ``spigot``.
