Metadata-Version: 2.5
Name: mixradix
Version: 0.2.0
Summary: Add, subtract and convert integers of any size in mixed radix bases (like 24:60:60), with a CLI
Project-URL: Homepage, https://github.com/yilmazhasan/mixedradix
Project-URL: Source, https://github.com/yilmazhasan/mixedradix/tree/main/pypi
Project-URL: Issues, https://github.com/yilmazhasan/mixedradix/issues
Author: yilmazhasan
License-Expression: MIT OR Apache-2.0
License-File: LICENSE-APACHE
License-File: LICENSE-MIT
Keywords: base,calculator,cli,mixed-radix,radix,time
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# mixradix

Add, subtract and convert integers of any size in **mixed radix** bases,
where every digit position has its own radix: hours:minutes:seconds,
pounds:shillings:pence, feet:inches, or plain hexadecimal. Zero dependencies,
fully typed, with a command line tool.

```sh
pipx install mixradix        # or: pip install mixradix / uv tool install mixradix
```

```sh
$ mixradix add 23:59:59 1 -b 24:60:60
1:0:0:0
$ mixradix sub 0:15 0:30 -b 24:60/wrap
23:45
$ mixradix convert 100000 -t 24:60:60
1:3:46:40
```

Also available for JavaScript and TypeScript as
[`mixradix` on npm](https://www.npmjs.com/package/mixradix), with the
same behaviour and CLI.

## Bases

A base lists one radix per digit position, most significant first, like the
digits of a number:

| Base | Meaning |
|---|---|
| `10`, `16`, `2` | plain decimal, hexadecimal, binary |
| `24:60:60` | hours:minutes:seconds (days above that have no limit) |
| `20:12` | pounds:shillings:pence |
| `24:60/wrap` | a 24-hour clock that wraps around |
| `2:3:4/repeat` | `...2:2:3:4`: the leftmost radix repeats |

The part after `/` is the **overflow**: what happens left of the leftmost
radix.

| Overflow | What is left of the leftmost radix | Default for |
|---|---|---|
| `repeat` | the same radix again, forever | one radix (`16`) |
| `extend` | one more digit with no limit | several radices (`24:60:60`) |
| `wrap` | nothing: values wrap around modulo the product of the radices | |

So 30 days in `24:60:60` is `30:0:0:0`, and in `24:60:60/wrap` it is `0:0:0`.

## Numbers

- **One character per digit** (`ff`, `1011`, case-insensitive) in a base with
  one radix of at most 36, like `16` or `2`.
- **Decimal digits separated by `:`** (`1:23:59`) in every base.
- In a base like `24:60:60`, text without `:` is a single digit: `45` is 45
  seconds.
- A leading `-` or `+` sets the sign. With `wrap`, values are never negative.

Every digit must be below its position's radix, so `1:60:0` is rejected in
`24:60:60`.

## Command line

```text
mixradix add <NUMBER> <NUMBER>... [-b BASE] [-t TO] [--json]
mixradix sub <NUMBER> <NUMBER>... [-b BASE] [-t TO] [--json]   (alias: subtract)
mixradix convert <NUMBER> -t TO [-b BASE] [--json]
mixradix info <BASE> [--json]
```

- `-b`, `--base`: the base the numbers are written in. Defaults to `10`.
- `-t`, `--to`: the base to write the result in. Defaults to `--base`.
- Negative numbers work as they are: `-5`, `-1:30`, `-ff`. Only one that
  starts with an option letter, like `-b1` in base 16, goes after `--`.

```sh
$ mixradix add 1:30 2:45 0:50 -b 60                  # sum of durations
5:5
$ mixradix add 1:19:11 1 -b 20:12                    # pounds:shillings:pence
2:0:0
$ mixradix convert ff -b 16 -t 2
11111111
$ mixradix convert -90 -t 60 --json
{"result":"-1:30","value":-90,"digits":[1,30],"negative":true,"base":"60"}
$ mixradix info 24:60/wrap
base:         24:60/wrap
overflow:     wrap (values wrap around modulo 1440)
digits:       exactly 2
place values: 60, 1
range:        0 to 1439
```

Exit codes: `0` on success, `1` for an invalid base or number, `2` for a
usage error. `python -m mixradix` works too.

## Library

```python
from mixradix import MixedRadix, MixedRadixNumber, add, sub, convert

# Quick string in, string out.
add("23:59:59", "1", base="24:60:60")        # '1:0:0:0'
sub("1:0:0", "1", base="24:60:60", to=10)    # '3599'
convert(5000, to="24:60:60")                 # '1:23:20'

# Bases and numbers.
time = MixedRadix("24:60:60")                # or MixedRadix([24, 60, 60])
t = time.parse("1:23:59")                    # or time.from_int(5039)
t.value, t.digits                            # 5039, (1, 23, 59)
str(t + 1), str(t - "0:23:59")               # '1:24:0', '1:0:0'
str(t.to(10))                                # '5039'
t == 5039                                    # True: comparison is by value

clock = MixedRadix([24, 60], overflow="wrap")
str(clock.parse("23:30") + "1:0")            # '0:30'

MixedRadixNumber("ff", 16).value             # 255
```

| API | What it does |
|---|---|
| `MixedRadix(base, overflow=None)` | Base from `"24:60:60"`, `16`, or `[24, 60, 60]` |
| `.radices`, `.overflow`, `.max_digits`, `.modulus` | Describe the base |
| `.radix_at(p)`, `.place_value(p)` | Radix and place value of position `p` (0 = least significant) |
| `.parse(text)`, `.from_int(n)`, `.from_digits(digits, negative=False)` | Make a number (digits most significant first) |
| `MixedRadixNumber(number, base=10)` | Number from text or an int |
| `.value`, `.digits`, `.is_negative`, `.base` | Read a number |
| `a + b`, `a - b`, `-a`, `abs(a)`, `int(a)`, `<`, `==` | Arithmetic and comparison on values |
| `.to(base)` | Same value in another base |
| `add(*numbers, base=10, to=None)`, `sub(...)`, `convert(number, to, base=10)` | String in, string out |

Operands can be other numbers in any base, ints, or text in the left
operand's base. The result uses the left operand's base.

Errors are `MixedRadixError` (a `ValueError`) with subclasses
`InvalidBaseError`, `InvalidNumberError` and `DigitOutOfRangeError`.

## License

MIT OR Apache-2.0, at your option.
