Skip to content

Latest commit

 

History

History
299 lines (202 loc) · 5.07 KB

File metadata and controls

299 lines (202 loc) · 5.07 KB

LoopInt

PyPI version Python versions Tests License Typing Downloads Ruff Linter & Formatter basedpyright

LoopInt is a lightweight Python library that provides a cyclic integer type with precise range semantics [left; right) and natural integer-like behavior.

It behaves like a regular integer but wraps around within a fixed interval. This makes it useful for cyclic counters, circular buffers, modular arithmetic, clock-like values, and more.


🔧 Features

  • Fixed interval semantics: [left; right)
  • Modular wrap-around on +, -, +=, -=
  • Behaves like int in:
    • comparisons
    • hashing
    • indexing (__index__)
    • formatting (__format__)
    • int() conversion
  • Safe arithmetic:
    • LoopInt + int supported
    • LoopInt += int supported
    • Adding two LoopInt objects is intentionally forbidden
  • Fully typed (Python ≥ 3.12)

📦 Installation

pip install loopint

Or with uv:

uv add loopint

🚀 Usage

from loopint import LoopInt

x = LoopInt(0, right=5)

print(int(x))      # 0

x += 7
print(int(x))      # 2   (wrap-around: 7 % 5 == 2)

y = x + 10
print(int(y))      # 2   (non-mutating)

assert x == 2
assert y == x

Custom range

x = LoopInt(0, right=2, left=-1)
print(int(x))  # 0

x += 1
print(int(x))  # 1

x += 2         # wraps in [-1; 2)
print(int(x))  # 0

📚 API Overview

LoopInt(current, /, right, *, left=0)

Create a cyclic integer constrained to the interval [left; right) with modular wrap-around.

Parameters:

  • current — initial value (any integer-like object supporting __index__)
  • right — exclusive upper bound
  • left — inclusive lower bound (default: 0)

Interval semantics:

value ∈ [left; right)
span = right − left
value = left + ((current − left) mod span)

🔹 Properties

value: int

Returns the visible integer value of the LoopInt instance.

Equivalent to:

int(loopint_instance)

Example:

x = LoopInt(5, right=8, left=2)
assert x.value == 5

left: int

Returns the inclusive lower bound of the interval.

Example:

LoopInt(0, right=5, left=-2).left  # → -2

right: int

Returns the exclusive upper bound of the interval.

Example:

LoopInt(0, right=5).right  # → 5

span: int

Size of the interval:

span = right - left

Example:

LoopInt(0, right=5, left=2).span  # → 3

🔹 Methods

to_string() -> str

Return the value as a string. Equivalent to:

str(int(self))

Example:

x = LoopInt(4, right=10)
x.to_string()   # → "4"

Arithmetic

__add__(other) -> LoopInt

Non-mutating addition with wrap-around.

x + n  # returns a new LoopInt

__iadd__(other) -> Self

In-place addition (+=) with wrap-around.

x += 3

__sub__(other) -> LoopInt

Non-mutating subtraction.


__isub__(other) -> Self

In-place subtraction (-=).


__neg__() -> LoopInt

Modular negation:

int(-x) == (-int(x)) mod span

Example:

x = LoopInt(1, right=5)
y = -x      # → 4

__radd__(other) -> LoopInt

Enables: int + LoopInt.

Example:

3 + LoopInt(2, right=5)  # → 0

__rsub__(other) -> LoopInt

Implements: other - self using modular arithmetic.

other - x == -(x - other)

Example:

3 - LoopInt(1, right=5)   # → 2
0 - LoopInt(1, right=5)   # → 4

Comparison & hashing

__eq__(other)

LoopInt compares equal to anything whose integer value equals int(self).

Examples:

LoopInt(0, right=5) == 0            # True
LoopInt(3, right=10) == LoopInt(3, right=100)  # True

__hash__() -> int

Hash is consistent with int(self):

hash(LoopInt(3, right=10)) == hash(3)

🧪 Testing

pytest .

Both unit tests and property-based tests (Hypothesis) are included.


📄 License

MIT License.


🤝 Contributing

Contributions are welcome! Feel free to open issues or pull requests.