Metadata-Version: 2.4
Name: numberdb
Version: 0.0.1
Summary: Look a number up in NumberDB and find out whether it is already known
Author: Benjamin Matschke
License: MIT
Project-URL: Homepage, https://numberdb.org
Project-URL: Source, https://github.com/numberdb/numberdb-website
Project-URL: Issues, https://github.com/numberdb/numberdb-website/issues
Keywords: mathematics,number theory,constants,sagemath,numberdb
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# numberdb

Look a number up in [NumberDB](https://numberdb.org) and find out whether it is
already known, and where else it appears.

```console
$ pip install numberdb
```

```python
>>> import numberdb
>>> for result in numberdb.search('pi'):
...     print(result.exact_text, '--', result.table.title)
3.14159265358979323846264338327950288419716939937510582097494 -- Pi
3.14159265358979323846264338327950288419716939937510582097494 -- Complete elliptic integral ...
```

## In SageMath

The same package, with one import line:

```console
$ sage -pip install numberdb
```

```python
sage: import numberdb.sage as numberdb
sage: numberdb.search('{n: pi^n for n in [1..5]}')[0].value
3.141592653589794?
```

Everything below that line reads exactly as it would in plain Python — there is
no mode to set and nothing to pass at each call. `numberdb.sage` re-exports the
whole package, so it can stand in for it wholesale.

It uses the SageMath you already have and installs nothing. **There is no
`numberdb[sage]` extra, deliberately**: inside a full SageMath it would install
passagemath over the top, and the passagemath-flint wheel writes 383 files
under `sage/`, 349 of which already exist there — including compiled
extensions. pip reports no conflict, because Sage's own files belong to no pip
distribution.

If you have no Sage and still want Sage objects, install passagemath into a
*fresh* environment, never into an existing Sage.

Plain `import numberdb` never imports Sage at all, so it starts instantly; the
conversion is available per result as `.sage()` when you want it.

## What you get back

`search()` returns a list of results, each carrying:

| | |
|---|---|
| `.value` | the number in plain Python (decoded on demand) |
| `.exact_text` | how the database writes it — the form to quote or paste back into a search |
| `.str_short` | a short form, comparable across results |
| `.table` | where it lives (`.tid`, `.title`, `.url`) |
| `.param` | which entry of that table it is |
| `.sage()` | the number as a Sage object |
| `.url()` | where to read about it |

`.value` is one of `int`, `Fraction`, `RealInterval`, `ComplexInterval`,
`PAdic` or `Polynomial`. A `PAdic` carries a `Fraction` — Q_p is not Z_p, so a
value of negative valuation such as 1/5 in Q_5 has no integer form — and its
`precision` is **absolute**: the ball is everything congruent to `value` modulo
`prime ** precision`, matching the `O(p^k)` in its string form. Exact values stay exact: integers are Python `int` (unbounded —
the database holds integers of over a thousand digits), rationals are
`Fraction`, and interval endpoints are exact `Fraction`s rather than rounded
floats. Converting to `float` is your decision, never an accident of transport.

```python
>>> result.value
RealInterval(884279719003555/281474976710656, 7074237752028441/2251799813685248)
>>> float(result.value)          # the midpoint, explicitly lossy
3.141592653589793
```

The search itself may have something to say — that it was capped, or that part
of the expression was rejected:

```python
>>> results = numberdb.search('...')
>>> results.messages
['We only show the first 100 results.']
```

### When the server is newer than the package

NumberDB will learn new kinds of number. An older package still returns every
result: values are decoded when you ask for them, so an unfamiliar one costs
you that value and nothing else, and its `exact_text` is there regardless.

```python
>>> for result in numberdb.search('...'):
...     if result.is_readable:
...         use(result.value)
...     else:
...         print(result.exact_text)   # still perfectly readable
```

`results.unreadable` lists them, and every exception the package raises derives
from `numberdb.NumberDBError`, so one `except` covers it.

## Rate limits and API keys

Anonymous use is rate limited. A key raises the limit:

Keep it out of your worksheet — a shared notebook should not carry its
author's key:

```console
$ export NUMBERDB_API_KEY=...
```

or, if you must set it in code:

```python
>>> numberdb.configure(api_key='...')
```

For more than one server or key in a process, use a client directly:

```python
>>> client = numberdb.Client(api_key='...', base_url='http://localhost:8000/')
>>> numberdb.search('pi', client=client)
```

Exceeding the limit raises `numberdb.RateLimited`, which carries `.retry_after`
in seconds when the server supplies it.

## Pointing it somewhere else

The default is `https://numberdb.org`. Override it for a development server, or
a private instance:

```console
$ export NUMBERDB_URL=http://localhost:8000
```

```python
>>> client = numberdb.Client(base_url='https://example.org/numberdb')
>>> numberdb.search('pi', client=client)
```

A trailing slash is optional — a base URL with a path prefix keeps it either
way.

## Other calls

```python
>>> numberdb.table('T12')       # a whole table, as stored
>>> numberdb.tag('Irrational')  # the tables carrying a tag
```

## Why a package and not a file to copy

The API sends JSON, and turning it into numbers has to happen somewhere. Doing
it by hand is how the previous example client came to call `loads()` on
server-supplied bytes — which executes whatever those bytes say, handing code
execution to anyone able to answer the request. In this package decoding is a
fixed table: a response can select one of seven decoders and nothing else.

Being a package also means it is versioned. When the wire format changes, that
is a version bump and a clear error telling you to upgrade, rather than an
exception in the middle of your session.

## Licence

GPL-3.0-or-later, matching NumberDB.
