Metadata-Version: 2.4
Name: numberwords-sutejas
Version: 0.1.0
Summary: Convert numbers into their English word representation.
License-Expression: MIT
Keywords: numbers,words,english,spell,converter,num2words
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Dynamic: license-file

# numberwords

A small, dependency-free Python library that converts numbers into their
English word representation.

```python
>>> from numberwords import to_words
>>> to_words(456)
'four hundred fifty six'
```

It is a clean, readable alternative to libraries such as `num2words`,
with a deliberately tiny API and no third-party runtime dependencies.

## Features

- Integers, floats and `decimal.Decimal` values.
- Negative numbers ("minus four hundred fifty six").
- The full international short-scale system, from `thousand` up to
  `decillion` (`10**36 - 1`).
- Decimal numbers with fractional digits spelled out and meaningful
  trailing zeroes preserved ("twelve point zero five").
- Optional capitalization and optional use of "and" between hundreds
  and the remainder.
- A small command-line interface.
- Clear exceptions for unsupported or non-finite input.

## Installation

The package has no runtime dependencies. The PyPI distribution is named
`numberwords-sutejas` (the import package is `numberwords`; the two names
are deliberately separate). Install it with:

```console
pip install numberwords-sutejas
```

Or install from the project root with pip:

```console
pip install .
```

To install in editable mode while developing:

```console
pip install -e .
```

Build artifacts (wheels / sdists) can be produced with:

```console
python -m build
```

Regardless of how it is installed, the import name is unchanged:

```python
from numberwords import to_words
```

## Basic usage

```python
from numberwords import to_words

to_words(0)          # "zero"
to_words(19)         # "nineteen"
to_words(42)         # "forty two"
to_words(100)        # "one hundred"
to_words(101)        # "one hundred one"
to_words(456)        # "four hundred fifty six"
to_words(999)        # "nine hundred ninety nine"
to_words(1000)       # "one thousand"
to_words(1234)       # "one thousand two hundred thirty four"
to_words(1000000)    # "one million"
```

The default style separates words with single spaces and does **not**
insert "and" between hundreds and the remainder.

## API reference

### `to_words(value, *, capitalize=False, use_and=False) -> str`

Convert `value` to its English word representation.

| Argument | Type | Description |
| --- | --- | --- |
| `value` | `int`, `float`, `decimal.Decimal` | The number to convert. |
| `capitalize` | `bool` (keyword-only) | Capitalize the first word of the result. |
| `use_and` | `bool` (keyword-only) | Insert "and" between hundreds and the remainder. |

Both options are keyword-only, which keeps ``to_words(value)`` ergonomic
while making option misuse obvious.

## Supported input types

- `int` — any size up to `MAX_SUPPORTED` (see below).
- `float` — converted from its own shortest round-trip representation.
- `decimal.Decimal` — the recommended way to get exact decimal behavior;
  fractional trailing zeroes are preserved.
- Subclasses of `int` (and other `numbers.Integral` implementations)
  are accepted.

Explicitly rejected:

- `bool` — `True`/`False` raise `TypeError`. They are rejected rather
  than silently treated as `1`/`0` because `bool` is a subclass of
  `int` in Python and accidental use is almost always a bug.
- `str`, `bytes`, `None`, lists, and other arbitrary objects — `TypeError`.
- Non-finite values (`float("nan")`, `float("inf")`, `Decimal("NaN")`,
  `Decimal("Infinity")`, ...) — `ValueError`.

## Negative numbers

Negative integers and decimals are prefixed with "minus".

```python
to_words(-1)        # "minus one"
to_words(-456)      # "minus four hundred fifty six"
to_words(-12.5)     # "minus twelve point five"
```

Signed zero (`-0`, `-0.0`, `Decimal("-0")`) is always treated as plain
zero — you will never see "minus zero".

## Large numbers

The implementation uses the international short-scale system:

| Scale | Value |
| --- | --- |
| thousand | 10³ |
| million | 10⁶ |
| billion | 10⁹ |
| trillion | 10¹² |
| quadrillion | 10¹⁵ |
| quintillion | 10¹⁸ |
| sextillion | 10²¹ |
| septillion | 10²⁴ |
| octillion | 10²⁷ |
| nonillion | 10³⁰ |
| decillion | 10³³ |

The largest supported value is `MAX_SUPPORTED = 10**36 - 1`
("nine hundred ninety nine decillion nine hundred ninety nine ...").
Values beyond that raise `ValueError` naming the limit.

```python
from numberwords import MAX_SUPPORTED

to_words(10**33)          # "one decillion"
to_words(MAX_SUPPORTED)   # "... decillion nine hundred ninety nine"
to_words(10**36)          # ValueError: number exceeds the maximum supported ...
```

Adding more scales later is a one-line change: append the next short-scale
name to the `_SCALES` tuple in `numberwords/converter.py`. `MAX_SUPPORTED`
is derived from that tuple automatically.

## Decimal numbers

The fractional part is spelled out one digit at a time, preceded by the
word "point". Meaningful zeroes are preserved instead of rounding the
fractional part into an integer.

```python
to_words(12.5)          # "twelve point five"
to_words(12.05)         # "twelve point zero five"
to_words(123.001)       # "one hundred twenty three point zero zero one"
```

### A note on `float` precision

Binary floats cannot represent every decimal exactly. `numberwords`
spells out the digits of the float's own shortest round-trip
representation — the same text `str(value)` produces. So:

```python
to_words(0.1 + 0.2)     # "zero point three zero zero zero zero zero zero zero zero zero zero zero zero zero zero four"
```

is the *literal* expansion of `0.30000000000000004`, not a silent
rounding to "zero point three". If you need exact decimal arithmetic,
use `decimal.Decimal`:

```python
from decimal import Decimal

to_words(Decimal("0.05"))       # "zero point zero five"
to_words(Decimal("12.100"))     # "twelve point one zero zero"
```

Also note that a whole-valued float keeps its fractional signalling:

```python
to_words(1.0)   # "one point zero"
to_words(1)     # "one"
```

## `capitalize` option

```python
to_words(456)                  # "four hundred fifty six"
to_words(456, capitalize=True) # "Four hundred fifty six"
```

Only the first word is capitalized; the rest stays lowercase.

## `use_and` option

```python
to_words(456)              # "four hundred fifty six"
to_words(456, use_and=True) # "four hundred and fifty six"
```

When enabled, "and" is inserted before a remainder below one hundred
that follows either a hundreds word or a larger scale group:

```python
to_words(101, use_and=True)            # "one hundred and one"
to_words(1234, use_and=True)           # "one thousand two hundred and thirty four"
to_words(1001, use_and=True)           # "one thousand and one"
to_words(1000, use_and=True)           # "one thousand"            (no "and")
to_words(1100, use_and=True)           # "one thousand one hundred" (no "and")
```

## Command-line interface

```console
$ python -m numberwords 456
four hundred fifty six
```

Options:

```console
$ python -m numberwords -c 456        # capitalize: Four hundred fifty six
$ python -m numberwords -a 999        # use "and":   nine hundred and ninety nine
$ python -m numberwords 1 2 3         # one value per line
$ python -m numberwords --version
```

Pass a non-finite or invalid value and the CLI reports the error on
stderr and exits with status 1:

```console
$ python -m numberwords 1e40
error: number exceeds the maximum supported value of 999,999,999,999,999,999,999,999,999,999,999,999
```

## Rules and guarantees

- The output is always non-empty, with no leading/trailing spaces and
  no double spaces.
- Zero-valued base-1000 groups are skipped entirely: the word "zero"
  appears only for the number zero itself (or explicit fractional
  digits like `12.005`).
- The default style uses spaces, not hyphens.

## Examples

| Input | Output |
| --- | --- |
| `0` | `zero` |
| `1` | `one` |
| `19` | `nineteen` |
| `20` | `twenty` |
| `21` | `twenty one` |
| `99` | `ninety nine` |
| `100` | `one hundred` |
| `101` | `one hundred one` |
| `110` | `one hundred ten` |
| `456` | `four hundred fifty six` |
| `999` | `nine hundred ninety nine` |
| `1000` | `one thousand` |
| `1001` | `one thousand one` |
| `1234` | `one thousand two hundred thirty four` |
| `1_000_000` | `one million` |
| `12.5` | `twelve point five` |
| `12.05` | `twelve point zero five` |
| `-456` | `minus four hundred fifty six` |

## Limitations

- English is the only supported language (the design isolates the word
  tables so more languages could be added later).
- Only the international short-scale system is implemented.
- Numbers larger than `10**36 - 1` raise `ValueError` (extend
  `_SCALES` to raise the ceiling).
- `float` input reflects the float's exact binary representation rather
  than an idealized decimal; use `Decimal` when exactness matters.
- The "and" rule is a single, documented convention; regional variants
  of English may differ.

## Development

Run the test suite with pytest:

```console
python -m pytest
```

The suite covers every number from 0–999, grouped thousands/millions,
each short-scale name, internal zeroes, negatives, decimals, `Decimal`
input, capitalization, `use_and`, invalid input, the largest supported
value, boundary conditions, spacing invariants and the CLI.

The package imports are restricted to the Python standard library; the
test dependency (`pytest`) is used only for development.

## License

MIT — see [LICENSE](LICENSE).
