Wrapper de int para numeros naturales grandes con slicing por bits
Project description
HugeNat
Wrapper inmutable sobre int de Python para numeros naturales grandes (N >= 0) con indexado y slicing de bits, vistas NumPy y un nucleo listo para Numba.
Internamente es un int de Python. No reinventa aritmetica: delega en CPython y solo anade acceso por bits, vistas de limbs uint64[], y kernels Numba para pipelines numericos sin overhead de objetos.
Repositorio: https://github.com/nand0san/huge_nat
Instalacion
pip install HugeNats
Dependencias: numpy, numba.
Creacion
import numpy as np
from hugenat import HugeNat
Desde int
x = HugeNat(42)
x = HugeNat(0) # cero es valido
# HugeNat(-1) # ValueError: solo naturales
Desde float
Acepta floats que sean enteros exactos, no negativos y <= 2**53.
x = HugeNat(3.0) # HugeNat(3)
# HugeNat(3.5) # ValueError: debe ser entero exacto
# HugeNat(float(2**54)) # ValueError: demasiado grande
Desde lista o tupla (big-endian)
Las listas y tuplas se interpretan en orden big-endian: el primer elemento es el limb mas significativo. Es la convencion natural de lectura izquierda-a-derecha.
x = HugeNat([1, 2, 3])
# Equivale a: 1 * 2^128 + 2 * 2^64 + 3
x = HugeNat([0xDEAD, 0xBEEF])
# limb MS = 0xDEAD, limb LS = 0xBEEF
Los ceros a la izquierda (mayor peso) se recortan automaticamente:
HugeNat([0, 0, 5]) == HugeNat(5) # True
Desde np.ndarray (little-endian)
Los arrays NumPy se interpretan en orden little-endian: el elemento 0 es el limb menos significativo. Es el formato interno de HugeNat.
limbs = np.array([0xFFFFFFFFFFFFFFFF, 0x1], dtype=np.uint64)
x = HugeNat(limbs)
# limb 0 (LS) = 0xFFFF...F, limb 1 (MS) = 0x1
Desde otro HugeNat
Copia el valor y hereda bit_length fijo (si lo tiene), salvo que se pase uno nuevo.
a = HugeNat(42, bit_length=16)
b = HugeNat(a) # copia, hereda bit_length=16
c = HugeNat(a, bit_length=32) # copia, sobreescribe a bit_length=32
Con anchura logica fija (bit_length)
Por defecto la anchura es int(x).bit_length(). Si se pasa bit_length, se fija:
- Si el valor tiene mas bits, se trunca por arriba.
- Si tiene menos, se consideran ceros a la izquierda.
bit_length()ylen(x)devuelven siempre la anchura fijada.
padded = HugeNat(0b1011, bit_length=8) # 00001011, ancho logico 8
cut = HugeNat(0b110101, bit_length=4) # 0101, truncado a 4 bits
padded.bit_length() # 8
len(padded) # 8 (equivale a bit_length())
Desde bytes
x = HugeNat.from_bytes(b'\x10\x00\x7b', byteorder="big")
b = x.to_bytes(length=3, byteorder="big", signed=False)
Desde limbs big-endian + bit_length
words_be = np.array([0x0001, 0xFFFFFFFFFFFFFFFF], dtype=np.uint64)
x = HugeNat.from_words_be(words_be, bit_length=128)
Desde core Numba (limbs LE + nbits)
x = HugeNat.from_core(limbs, nbits) # LE (default)
x = HugeNat.from_core(limbs_be, nbits, word_order="be") # BE
Conversiones
x = HugeNat(42)
int(x) # 42
float(x) # 42.0
bool(x) # True (False solo para HugeNat(0))
str(x) # '42'
hash(x) # consistente con int
len(x) # bit_length()
Aritmetica
Todas las operaciones aceptan HugeNat o int >= 0 y devuelven HugeNat. Los operadores reflejados estan implementados (2 + HugeNat(3) funciona).
a = HugeNat(10)
b = HugeNat(7)
a + b # HugeNat(17)
a - b # HugeNat(3) -- ValueError si resultado < 0
a * b # HugeNat(70)
a // b # HugeNat(1)
a % b # HugeNat(3)
divmod(a,b) # (HugeNat(1), HugeNat(3))
a ** 3 # HugeNat(1000)
pow(a, 3, 7)# HugeNat(6) -- exponenciacion modular
2 ** a # HugeNat(1024) -- reflejado
abs(a) # HugeNat(10) -- identidad
+a # HugeNat(10) -- identidad
-a # ValueError: no permite negativos
Nota sobre pow: pow(base, exp, mod) usa la exponenciacion modular nativa de Python, muy eficiente para valores grandes (RSA, Diffie-Hellman, etc.).
Comparaciones
Soporta ==, !=, <, <=, >, >= entre HugeNat, int, y para == tambien listas/tuplas/arrays.
HugeNat(100) < HugeNat(200) # True
HugeNat(100) >= 50 # True
HugeNat([1, 2]) == [1, 2] # True (lista BE)
Operaciones bitwise
a = HugeNat(0b1100)
b = HugeNat(0b1010)
a & b # HugeNat(0b1000) AND
a | b # HugeNat(0b1110) OR
a ^ b # HugeNat(0b0110) XOR
a << 2 # HugeNat(0b110000) shift left
a >> 1 # HugeNat(0b110) shift right
~a # NOT (ver seccion dedicada)
Los shifts no aceptan bool como argumento; usar int explicito.
Preservacion de bit_length fijo
Las operaciones bitwise preservan _fixed_nbits automaticamente:
- Si ambos operandos tienen el mismo ancho fijo: el resultado lo hereda.
- Si solo
selflo tiene: el resultado hereda deself. - Si ambos difieren: resultado sin ancho fijo.
- Los shifts preservan siempre el ancho de
selfy aplican mascara.
a = HugeNat(0xFF, bit_length=8)
b = HugeNat(0x0F, bit_length=8)
(a & b).bit_length() # 8 (heredado)
(HugeNat(0x80, bit_length=8) << 1) # HugeNat(0, bit_length=8) -- truncado
La aritmetica (+, -, *, //, %, **) no preserva bit_length fijo.
NOT bit a bit (~)
~x voltea todos los bits dentro del ancho logico y devuelve un HugeNat con ese ancho fijado.
- Con
bit_lengthdeclarado: voltea dentro de esa anchura. - Sin
bit_lengthdeclarado: usa elbit_length()natural. ~~x == xsiempre.HugeNat(0)sin anchura:bit_length()=0, no hay bits que voltear, devuelveHugeNat(0, bit_length=0).
~HugeNat(0b1011, bit_length=4) # HugeNat(4, bit_length=4) -> 0b0100
~HugeNat(0, bit_length=8) # HugeNat(255, bit_length=8) -> todo unos
~HugeNat(0xFF, bit_length=8) # HugeNat(0, bit_length=8)
x = HugeNat(0b10110010, bit_length=8)
int(x & ~x) # 0 (siempre)
Rotaciones de bits
Rotan dentro del ancho logico (bit_length()). Shift grande se normaliza con mod bit_length().
x = HugeNat(0b100101)
x.rotl(2) # HugeNat(22, bit_length=6) rota a la izquierda
x.rotr(2) # HugeNat(25, bit_length=6) rota a la derecha
HugeNat(0).rotl(5) # HugeNat(0), sin error
Indexado de bits
Convencion: LSB = indice 0. Indices negativos cuentan desde el MSB. Fuera de rango devuelve 0 (no lanza excepcion).
x = HugeNat(0b1101101) # 109
x[0] # 1 (LSB)
x[-1] # 1 (MSB)
x[100] # 0 (fuera de rango)
x[-100] # 0 (fuera de rango)
Slicing de bits
El bit start pasa a ser el bit 0 del resultado.
step=Noneo1: ruta rapida, normaliza limites como Python.step != 0, != 1: ruta general con semantica completa de slicing de listas.- Bits por encima de
bit_length()se consideran ceros implicitos.
x = HugeNat(0b1101101)
int(x[0:3]) # 5 (bits 0..2 -> 0b101)
int(x[2:5]) # 6 (0b110)
int(x[0:7:2]) # 11 (cada 2 bits -> 0b1011)
int(x[5:0:-2]) # paso negativo
Division en trozos (split)
split(n) divide en n trozos de igual cantidad de bits. Devuelve tupla LSB-primero.
bit_length()debe ser divisible porn.- Cada trozo conserva su
bit_lengthfijo.
x = HugeNat(0b1011010110010001, bit_length=16)
parts = x.split(4)
[int(p) for p in parts] # [1, 9, 5, 11] (LSB -> MSB)
[p.bit_length() for p in parts] # [4, 4, 4, 4]
Concatenacion de bits (append)
append(B, C, ...) coloca los bits de self en las posiciones bajas (LSB), los de B encima, etc. Inversa de split.
a = HugeNat(0b11, bit_length=2)
b = HugeNat(0b01, bit_length=2)
r = a.append(b) # bits: 01|11 -> 7, bit_length=4
# Reconstruir desde split
x = HugeNat(0xDEADBEEF, bit_length=32)
parts = x.split(4)
assert int(parts[0].append(*parts[1:])) == int(x)
Vista de bits
Array NumPy (bits)
bits(order="msb->lsb", length=None) devuelve np.ndarray[uint8].
x = HugeNat(0b1011)
x.bits() # array([1, 0, 1, 1], uint8) MSB primero
x.bits(order="lsb->msb") # array([1, 1, 0, 1], uint8) LSB primero
x.bits(length=8) # padding: array([0, 0, 0, 0, 1, 0, 1, 1])
Cadena de texto (bits_str)
bits_str(order="msb->lsb", group=64, sep=" ") -- los grupos se alinean desde la derecha (el primer grupo puede ser mas corto).
x = HugeNat(0b101101)
x.bits_str(group=4) # '10 1101' (grupo corto a la izquierda)
x.bits_str(group=8) # '101101' (cabe en un grupo)
Nucleo Numba-friendly (to_core / from_core)
HugeNat es inmutable. Los limbs uint64[] se cachean internamente; llamadas repetidas con copy=False son O(1).
to_core(copy=True, word_order="le")
Devuelve (limbs: uint64[::1], nbits: int64).
copy=True(default): copia independiente, escribible.copy=False: vista read-only de la cache interna (sin alloc).word_order="le"(default): limb 0 = bits 0..63.word_order="be": MSB primero.
x = HugeNat(2**127 + 0xF00D)
limbs, nbits = x.to_core()
# Para Numba, asegurar contiguidad y tipos exactos:
limbs = np.ascontiguousarray(limbs, dtype=np.uint64)
nbits = np.int64(nbits)
from_core(limbs, nbits, word_order="le")
Reconstruye HugeNat desde (limbs, nbits). Fija bit_length a nbits.
y = HugeNat.from_core(limbs, nbits)
y = HugeNat.from_core(limbs_be, nbits, word_order="be")
API big-endian de palabras
to_words_be() / from_words_be(words_be, bit_length)
Para pipelines 100% big-endian. to_words_be() devuelve uint64[] con MSW primero. from_words_be requiere bit_length explicito.
x = HugeNat(0xAAAABBBBCCCCDDDD_1111222233334444, bit_length=128)
be = x.to_words_be()
y = HugeNat.from_words_be(be, 128)
assert int(y) == int(x)
Extraccion directa a uint64[]
APIs que evitan crear objetos HugeNat por trozo, devolviendo arrays directamente.
split_core(parts, word_order="le")
Array 2D (parts, words_per_part). Fila 0 = chunk LSB. Default LE.
split_array(parts, word_order="be")
Identico a split_core pero con default BE. Para pipelines big-endian.
split_u64(parts)
Array 1D (parts,) de uint64. Para chunks de hasta 64 bits.
extract_bits_core(start, width, word_order="le")
Extrae width bits desde posicion start como array 1D de uint64.
extract_bits_u64(start, width)
Extrae hasta 64 bits como un solo np.uint64.
x = HugeNat(0xDEADBEEFCAFEBABE, bit_length=64)
x.split_core(4) # shape (4, 1), LE
x.split_array(4) # shape (4, 1), BE
x.split_u64(4) # array([0xBABE, 0xCAFE, 0xBEEF, 0xDEAD])
x.extract_bits_core(0, 16) # array([0xBABE])
x.extract_bits_u64(48, 16) # np.uint64(0xDEAD)
Operaciones estaticas sobre arrays BE
Metodos estaticos que operan sobre uint64[] big-endian sin crear objetos HugeNat. Para encadenar operaciones sobre arrays crudos.
HugeNat.bitwise_or(a_be, b_be, bit_length)
OR element-wise. Entrada y salida BE.
HugeNat.shift_left(a_be, k, bit_length)
Shift left por k bits. Trunca al bit_length. Entrada y salida BE.
HugeNat.extract_bits(a_be, start, width)
Extrae width bits desde posicion start. Entrada y salida BE.
HugeNat.concat_array(parts_2d, part_bits, word_order="be")
Inversa de split_array. Concatena array 2D de partes en array 1D.
# Pipeline encadenado sin objetos HugeNat
arr = x.to_words_be()
shifted = HugeNat.shift_left(arr, 32, 256)
combined = HugeNat.bitwise_or(shifted, arr, 256)
chunk = HugeNat.extract_bits(combined, 64, 128)
result = HugeNat.from_words_be(chunk, 128)
Kernels Numba (hugenat.numba_core)
Funciones @njit que operan sobre (limbs, nbits) en little-endian. Pueden llamarse desde otras funciones @njit para componer pipelines sin overhead de objetos Python.
from hugenat.numba_core import (
split_equal_parts_core,
extract_range_core,
concat_parts_core,
bitwise_or_core,
shift_left_core,
)
| Kernel | Entrada | Salida | Descripcion |
|---|---|---|---|
split_equal_parts_core(limbs, nbits, parts) |
limbs LE, nbits, parts | array 2D (parts, words) |
Divide en partes iguales |
extract_range_core(limbs, nbits, start, width) |
limbs LE, nbits, start, width | (limbs_out, nbits_out) |
Extrae rango de bits |
concat_parts_core(parts_2d, chunk_bits) |
array 2D LE, chunk_bits | (limbs_out, nbits_out) |
Concatena partes |
bitwise_or_core(a, b) |
dos arrays LE | (limbs_out, nbits_out) |
OR element-wise |
shift_left_core(limbs, k) |
limbs LE, k bits | (limbs_out, nbits_out) |
Shift left |
Convencion de endianness
| Entrada | Orden | Razon |
|---|---|---|
list / tuple al constructor |
Big-endian (elemento 0 = MS) | Lectura natural izquierda-a-derecha |
np.ndarray al constructor |
Little-endian (elemento 0 = LS) | Formato interno de HugeNat |
to_core() default |
Little-endian | Formato interno |
to_words_be() |
Big-endian | API BE explicita |
split_core() default |
Little-endian | Coherente con to_core |
split_array() default |
Big-endian | Coherente con to_words_be |
Metodos estaticos (bitwise_or, etc.) |
Big-endian | API BE explicita |
Kernels Numba (numba_core) |
Little-endian | Formato nativo de computo |
Referencia rapida
| Metodo | Tipo | Devuelve | Notas |
|---|---|---|---|
HugeNat(value, bit_length=None) |
constructor | HugeNat | int, float, list(BE), ndarray(LE), HugeNat |
int(x), float(x), bool(x), str(x) |
conversion | tipo nativo | |
len(x) |
propiedad | int | = bit_length() |
bit_length() |
instancia | int | anchura logica |
bit_count() |
instancia | int | bits a 1 |
bits(order, length) |
instancia | uint8[] |
vista de bits |
bits_str(order, group, sep) |
instancia | str | texto agrupado |
to_bytes(length, byteorder, signed) |
instancia | bytes | como int.to_bytes |
from_bytes(data, byteorder, signed) |
classmethod | HugeNat | como int.from_bytes |
to_core(copy, word_order) |
instancia | (uint64[], int) |
LE o BE |
from_core(limbs, nbits, word_order) |
classmethod | HugeNat | LE o BE |
to_words_be() |
instancia | uint64[] |
solo BE |
from_words_be(words, bit_length) |
classmethod | HugeNat | solo BE |
split(n) |
instancia | tuple[HugeNat] | LSB primero |
append(*others) |
instancia | HugeNat | inversa de split |
split_core(parts, word_order) |
instancia | uint64[n, w] |
default LE |
split_array(parts, word_order) |
instancia | uint64[n, w] |
default BE |
split_u64(parts) |
instancia | uint64[n] |
chunks <= 64b |
extract_bits_core(start, width, word_order) |
instancia | uint64[] |
default LE |
extract_bits_u64(start, width) |
instancia | uint64 |
width <= 64 |
rotl(shift) / rotr(shift) |
instancia | HugeNat | rotacion |
bitwise_or(a_be, b_be, bit_length) |
static | uint64[] |
BE |
shift_left(a_be, k, bit_length) |
static | uint64[] |
BE |
extract_bits(a_be, start, width) |
static | uint64[] |
BE |
concat_array(parts_2d, part_bits, word_order) |
static | uint64[] |
default BE |
Contrato de dominio
- Solo enteros >= 0. Valores negativos o restas con resultado negativo lanzan
ValueError. boolno se acepta como shift en<<,>>,rotl,rotr.- Sin
bit_lengthexplicito, los ceros de mayor peso se recortan. - Con
bit_lengthexplicito, la anchura logica se conserva siempre. - HugeNat es inmutable (usa
__slots__, sin__dict__). Los limbs se cachean internamente. split(n)requiere quebit_length()sea divisible porn.
Desarrollo
pip install -r requirements.txt
pytest -q
Demostraciones interactivas en HugeNat_demo.ipynb.
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.4.1.tar.gz.
File metadata
- Download URL: hugenats-0.4.1.tar.gz
- Upload date:
- Size: 29.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1692e0c47b284c145455eb8defca88808033f552f6427b7d0ac7df131047e1a
|
|
| MD5 |
d9fab0a08c2a4e5374bf7a22cb70d929
|
|
| BLAKE2b-256 |
28cd06baac9e7be1c41eb90768f2b97a2650986471d89472c8fdfb98ed9b0ee3
|
File details
Details for the file hugenats-0.4.1-py3-none-any.whl.
File metadata
- Download URL: hugenats-0.4.1-py3-none-any.whl
- Upload date:
- Size: 18.5 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 |
e5d4f0b35eda70ad6ab8d2376057553702e46ee0b086d2d5fe6b44e8d2d37ced
|
|
| MD5 |
74af27086df08582da717a4d3a1aa19d
|
|
| BLAKE2b-256 |
1a1665e8886ec88ab8d879555122fb8d3526b678a7695f9ab5847dc327d0594e
|