Wrapper de int para numeros naturales grandes con slicing por bits
Project description
HugeNat
HugeNat es un wrapper ligero sobre int que mantiene la semántica de los enteros de Python para números naturales (ℕ₀) y añade indexado/slicing por bits, vistas NumPy y un núcleo listo para Numba.
Repositorio: https://github.com/nand0san/huge_nat
Instalación
pip install HugeNats
Creación rápida
import numpy as np
from hugenat import HugeNat
# Desde un entero no negativo
x = HugeNat(123456789)
# Desde un float exactamente entero (sin parte decimal, <= 2**53)
f = HugeNat(3.0) # → HugeNat(3)
# HugeNat(3.5) → ValueError: El float debe ser un entero exacto
# HugeNat(float(2**54)) → ValueError: demasiado grande para representación exacta
# Desde limbs (uint64, little-endian: limb 0 es LSB)
limbs = np.array([0xFFFFFFFFFFFFFFFF, 0x1], dtype=np.uint64)
y = HugeNat(limbs)
# Desde una lista/tupla de enteros (se convierten a uint64 y se recortan ceros finales)
z = HugeNat([1, 2, 3])
# Con anchura lógica fija: conserva ceros a la izquierda o trunca por arriba
padded = HugeNat(0b1011, bit_length=8) # 00001011 (ancho lógico 8)
cut = HugeNat(0b110101, bit_length=4) # 0101 (se recorta a 4 bits)
API tipo int
int(x),float(x),bool(x),hash(x),str(x)reflejan al entero interno.- Métodos compatibles:
bit_length(),bit_count(),to_bytes(),from_bytes(). - Aritmética y bitwise aceptan solo naturales (
HugeNatoint >= 0) y devuelven siempreHugeNat. - Las restas que producirían un valor negativo lanzan
ValueError. -xlanzaValueError(los naturales no tienen negación).- Si se declara
bit_lengthal construir,bit_length()devuelve esa anchura lógica fijada. - Nota: las operaciones aritméticas y bitwise no preservan
_fixed_nbitsen el resultado. Si necesitas ancho fijo tras una operación, construye conbit_lengthexplícito. - Operadores unarios:
abs(x)y+xdevuelven una copia (identidad para naturales, preservanbit_lengthfijo). divmod(a, b)devuelve(HugeNat, HugeNat).- Todos los operadores reflejados están implementados, incluyendo
**(2 ** HugeNat(3)->HugeNat(8)). boolno se acepta como shift en<<,>>,rotlnirotr; usaintexplícito.
a = HugeNat(10)
b = HugeNat(7)
int(a + b) # 17
int(a * b) # 70
int(a // b) # 1
int(a % b) # 3
int(a << 3) # 80
int(a | b), int(a & b), int(a ^ b)
float(a) # 10.0
divmod(a, b) # (HugeNat(1), HugeNat(3))
2 ** HugeNat(3) # HugeNat(8)
abs(a) # HugeNat(10)
-a # ValueError: HugeNat no permite valores negativos
Indexado de bits
- Convención:
LSB = índice 0. Índices negativos son relativos abit_length(). - Fuera de rango devuelve
0. ~xno está definido y lanzaTypeError.
x = HugeNat(0b1101101) # 109
x[0] # 1 (LSB)
x[-1] # 1 (MSB)
x[100] # 0
Slicing de bits
stepen{None, 1}usa ruta rápida: normaliza como Python y rellena con ceros si el límite superior excedebit_length().- Cualquier otro
step(salvo 0) usa ruta general con semántica completa de slicing de listas y reempaquetado LSB-first. - Con
step < 0, si el índice inicial cae por encima debit_length(), esos bits se consideran ceros implícitos. step == 0->ValueError.
x = HugeNat(0b1101101)
x[0:3] # bits 0..2 -> 0b101 (5)
x[2:5] # 0b110 (6)
x[0:7:2] # toma cada 2 bits -> 0b1011 (11)
x[5:0:-2] # slicing con paso negativo
x[3:10] # incluye ceros implícitos por encima de bit_length()
Anchura lógica fija (bit_length al construir)
Por defecto, HugeNat se comporta como antes: la anchura es int(x).bit_length().
Si pasas bit_length=..., fijas la anchura lógica del objeto:
- si el valor tiene más bits, se trunca por arriba;
- si tiene menos, se consideran ceros a la izquierda;
bit_length()devuelve siempre la anchura fijada.
x = HugeNat(0b1011, bit_length=8)
int(x) # 11
x.bit_length() # 8
x[-1] # 0 (bit más alto lógico)
x[-8] # 1
División en trozos (split(n))
split(n) divide el número en n trozos de igual cantidad de bits.
ndebe serintyn >= 1.bit_length()debe ser divisible porn, si no lanzaValueError.- Devuelve una tupla de
HugeNaten ordenLSB -> MSB(trozo de menor peso primero). - Cada trozo conserva su anchura lógica fija (
bit_lengthdel trozo). - Si
bit_length() == 0, devuelventrozosHugeNat(0, bit_length=0).
x = HugeNat(0b1011010110010001, bit_length=16)
parts = x.split(4)
[int(p) for p in parts] # [0b0001, 0b1001, 0b0101, 0b1011] -> [1, 9, 5, 11]
[p.bit_length() for p in parts] # [4, 4, 4, 4]
Concatenación de bits (append)
append(B, C, ...) concatena los bits de uno o más HugeNat sobre los de self.
- Los bits de
selfquedan en las posiciones bajas (LSB), los deBjusto encima, los deCencima deB, etc. - Se respeta el
bit_length()de cada operando, incluidos los ceros altos declarados. - El resultado tiene
bit_length= suma de los anchos de todos los operandos. - Es la operación inversa de
split:parts[0].append(*parts[1:])reconstruye el valor original.
a = HugeNat(0b11, bit_length=2) # 11
b = HugeNat(0b01, bit_length=2) # 01
r = a.append(b)
int(r), r.bit_length() # (7, 4) -> bits: 01|11
# Con ceros altos preservados
a = HugeNat(0b1, bit_length=4) # 0001
b = HugeNat(0b1, bit_length=4) # 0001
r = a.append(b)
int(r), r.bit_length() # (17, 8) -> bits: 0001|0001
# Múltiples operandos
a = HugeNat(0b10, bit_length=2)
b = HugeNat(0b01, bit_length=2)
c = HugeNat(0b11, bit_length=2)
r = a.append(b, c)
int(r), r.bit_length() # (54, 6) -> bits: 11|01|10
# Inversa de split
x = HugeNat(0xDEADBEEFCAFEBABE, bit_length=64)
parts = x.split(4)
reconstructed = parts[0].append(*parts[1:])
assert int(reconstructed) == int(x)
Array de bits
bits(order="msb->lsb" | "lsb->msb", length=None) devuelve np.ndarray de uint8.
x = HugeNat(0b1011)
np.asarray(x.bits()) # array([1, 0, 1, 1], dtype=uint8)
np.asarray(x.bits(order="lsb->msb")) # array([1, 1, 0, 1], dtype=uint8)
np.asarray(x.bits(length=8)) # padding a la izquierda: 00001011
Cadena de bits agrupados
bits_str(order="msb->lsb" | "lsb->msb", group=64, sep=" ") para depurar o mostrar.
x = HugeNat(0x0123456789ABCDEFFEDCBA9876543210)
x.bits_str(group=4) # grupos de 4 bits
x.bits_str(group=8) # grupos de 1 byte
x.bits_str(order="lsb->msb", group=8)
Bytes ida y vuelta
x = HugeNat(2**20 + 123)
length = (x.bit_length() + 7) // 8
b = x.to_bytes(length=length, byteorder="big", signed=False)
y = HugeNat.from_bytes(b, byteorder="big", signed=False)
assert int(y) == int(x)
Rotaciones de bits
Las rotaciones usan el ancho lógico (bit_length()):
x = HugeNat(0b100101)
int(x.rotl(2)) # 0b010110
int(x.rotr(2)) # 0b011001
HugeNat(0).rotl(5) # -> HugeNat(0)
NOT bit a bit (~)
~x voltea todos los bits dentro del ancho lógico del valor y devuelve un HugeNat con ese mismo ancho fijado.
- Con
bit_lengthdeclarado: voltea dentro de esa anchura. - Sin
bit_lengthdeclarado: usa elbit_length()natural (número de bits significativos). HugeNat(0)sin anchura declarada tienebit_length() = 0; no hay bits que voltear, devuelveHugeNat(0, bit_length=0).~~x == xsiempre.
~HugeNat(0b1011, bit_length=4) # → HugeNat(4, bit_length=4) → 0b0100
~HugeNat(0b1011) # → HugeNat(4, bit_length=4) (bit_length natural = 4)
~HugeNat(0, bit_length=8) # → HugeNat(255, bit_length=8) → 0xFF
~HugeNat(0xFF, bit_length=8) # → HugeNat(0, bit_length=8)
# Uso típico: máscara de bits
x = HugeNat(0b10110010, bit_length=8)
mask = ~x # → 0b01001101, bit_length=8
Núcleo Numba-friendly
to_core() devuelve siempre limbs: uint64[::1] (1D contiguo, little-endian por palabra; limb 0 contiene los bits 0..63) y nbits: int.
from_core(limbs, nbits) reconstruye el valor y fija la anchura lógica a nbits (equivale a construir con bit_length=nbits), por lo que puede haber ceros relevantes en la zona alta.
Ejemplo Numba (histograma/unique de nibbles de 4 bits, empezando en el LSB) con signatura estricta y retorno tipado. Observa que seen:uint8 y counts:uint32 requieren types.Tuple, no UniTuple.
import numpy as np
from numba import njit, types
x = HugeNat(2**127 + 0xF00D)
limbs, nbits = x.to_core()
# Asegurar contigüidad y tipos exactos para la signatura
limbs = np.ascontiguousarray(limbs, dtype=np.uint64)
nbits = np.int64(nbits)
RET = types.Tuple((types.Array(types.uint8, 1, "C"), types.Array(types.uint32, 1, "C")))
SIG = RET(types.Array(types.uint64, 1, "C"), types.int64)
@njit(SIG, cache=False)
def unique_nibbles_core(limbs, nbits):
counts = np.zeros(16, dtype=np.uint32)
seen = np.zeros(16, dtype=np.uint8)
if nbits <= 0 or limbs.size == 0:
return seen, counts
n_nibbles = nbits >> 2 # solo nibbles completos
for k in range(n_nibbles):
bitpos = k << 2 # desplaza 4 bits desde el LSB
limb_i = bitpos >> 6
off = bitpos & 63
x0 = limbs[limb_i]
if off <= 60:
nib = (x0 >> np.uint64(off)) & np.uint64(0xF)
else:
lo = x0 >> np.uint64(off)
hi = np.uint64(0)
if limb_i + 1 < limbs.size:
hi = limbs[limb_i + 1] << np.uint64(64 - off)
nib = (lo | hi) & np.uint64(0xF)
idx = int(nib)
counts[idx] += np.uint32(1)
seen[idx] = np.uint8(1)
return seen, counts
seen, counts = unique_nibbles_core(limbs, nbits)
unique_values = np.nonzero(seen)[0].astype(np.uint8)
# Vuelta al wrapper para seguir trabajando en Python
y = HugeNat.from_core(limbs, nbits)
assert int(y) == int(x)
unique_values, counts[unique_values]
Contrato de dominio
- Solo enteros
>= 0o arrays 1D de limbsuint64(little-endian). Valores negativos o dimensiones distintas lanzanValueError. - Sin
bit_lengthexplícito, los ceros de mayor peso se recortan automáticamente. - Con
bit_lengthexplícito, la anchura lógica se conserva aunque haya ceros a la izquierda. - Las restas que producirían negativos lanzan
ValueError.
Desarrollo
- Dependencias de desarrollo:
pytest,numpy,numba. - Ejecuta la batería completa:
pytest -q.
Las demostraciones completas viven en HugeNat_demo.ipynb y cubren todos los ejemplos anteriores.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hugenats-0.1.7.tar.gz.
File metadata
- Download URL: hugenats-0.1.7.tar.gz
- Upload date:
- Size: 20.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a325a13bd80a9b9a8b380e3a52ae2b3d0c8fab928512b1efcb03cb2cae08be8
|
|
| MD5 |
a00d6dc938763fa02563a196e0a894aa
|
|
| BLAKE2b-256 |
6bed2b2cc2b4834c65156c6bafa2230d86cf1f3e1c30c2a8da0c20d6fa341c80
|
File details
Details for the file hugenats-0.1.7-py3-none-any.whl.
File metadata
- Download URL: hugenats-0.1.7-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
37731acaf5a1295a2608fcb98b35e536d772d23b4a5f2e099987e7ee9e25033c
|
|
| MD5 |
3c5508b62735957f5b5a28b52117714f
|
|
| BLAKE2b-256 |
6b5678fd5d1a83738a33aa3f980c63d1a7349168ae5bf0642a1aecc4de60a62e
|