Skip to main content

Wrapper de int para numeros naturales grandes con slicing por bits

Project description

HugeNats

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/hugenats

Instalacion

pip install HugeNats

Dependencias: numpy, numba.


Creacion

import numpy as np
from hugenats import HugeNats

Desde int

x = HugeNats(42)
x = HugeNats(0)           # cero es valido
# HugeNats(-1)             # ValueError: solo naturales

Desde float

Acepta floats que sean enteros exactos, no negativos y <= 2**53.

x = HugeNats(3.0)          # HugeNats(3)
# HugeNats(3.5)             # ValueError: debe ser entero exacto
# HugeNats(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 = HugeNats([1, 2, 3])
# Equivale a: 1 * 2^128 + 2 * 2^64 + 3

x = HugeNats([0xDEAD, 0xBEEF])
# limb MS = 0xDEAD, limb LS = 0xBEEF

Los ceros a la izquierda (mayor peso) se recortan automaticamente:

HugeNats([0, 0, 5]) == HugeNats(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 HugeNats.

limbs = np.array([0xFFFFFFFFFFFFFFFF, 0x1], dtype=np.uint64)
x = HugeNats(limbs)
# limb 0 (LS) = 0xFFFF...F, limb 1 (MS) = 0x1

Desde otro HugeNats

Copia el valor y hereda bit_length fijo (si lo tiene), salvo que se pase uno nuevo.

a = HugeNats(42, bit_length=16)
b = HugeNats(a)                   # copia, hereda bit_length=16
c = HugeNats(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() y len(x) devuelven siempre la anchura fijada.
padded = HugeNats(0b1011, bit_length=8)     # 00001011, ancho logico 8
cut    = HugeNats(0b110101, bit_length=4)    # 0101, truncado a 4 bits

padded.bit_length()  # 8
len(padded)          # 8  (equivale a bit_length())

Desde bytes

x = HugeNats.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 = HugeNats.from_words_be(words_be, bit_length=128)

Desde core Numba (limbs LE + nbits)

x = HugeNats.from_core(limbs, nbits)                    # LE (default)
x = HugeNats.from_core(limbs_be, nbits, word_order="be") # BE

Conversiones

x = HugeNats(42)

int(x)    # 42
float(x)  # 42.0
bool(x)   # True  (False solo para HugeNats(0))
str(x)    # '42'
hash(x)   # consistente con int
len(x)    # bit_length()

HugeNats(0b1011).weight   # 3  (bits a 1, atajo de bit_count())

Aritmetica

Todas las operaciones aceptan HugeNats o int >= 0 y devuelven HugeNats. Los operadores reflejados estan implementados (2 + HugeNats(3) funciona).

a = HugeNats(10)
b = HugeNats(7)

a + b       # HugeNats(17)
a - b       # HugeNats(3)      -- ValueError si resultado < 0
a * b       # HugeNats(70)
a // b      # HugeNats(1)
a % b       # HugeNats(3)
divmod(a,b) # (HugeNats(1), HugeNats(3))
a ** 3      # HugeNats(1000)
pow(a, 3, 7)# HugeNats(6)     -- exponenciacion modular
2 ** a      # HugeNats(1024)  -- reflejado
abs(a)      # HugeNats(10)    -- identidad
+a          # HugeNats(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 HugeNats, int, y para == tambien listas/tuplas/arrays.

HugeNats(100) < HugeNats(200)   # True
HugeNats(100) >= 50             # True
HugeNats([1, 2]) == [1, 2]     # True (lista BE)

Operaciones bitwise

a = HugeNats(0b1100)
b = HugeNats(0b1010)

a & b    # HugeNats(0b1000)   AND
a | b    # HugeNats(0b1110)   OR
a ^ b    # HugeNats(0b0110)   XOR
a << 2   # HugeNats(0b110000) shift left
a >> 1   # HugeNats(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 self lo tiene: el resultado hereda de self.
  • Si ambos difieren: resultado sin ancho fijo.
  • Los shifts preservan siempre el ancho de self y aplican mascara.
a = HugeNats(0xFF, bit_length=8)
b = HugeNats(0x0F, bit_length=8)

(a & b).bit_length()                    # 8  (heredado)
(HugeNats(0x80, bit_length=8) << 1)      # HugeNats(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 HugeNats con ese ancho fijado.

  • Con bit_length declarado: voltea dentro de esa anchura.
  • Sin bit_length declarado: usa el bit_length() natural.
  • ~~x == x siempre.
  • HugeNats(0) sin anchura: bit_length()=0, no hay bits que voltear, devuelve HugeNats(0, bit_length=0).
~HugeNats(0b1011, bit_length=4)    # HugeNats(4, bit_length=4)  -> 0b0100
~HugeNats(0, bit_length=8)         # HugeNats(255, bit_length=8) -> todo unos
~HugeNats(0xFF, bit_length=8)      # HugeNats(0, bit_length=8)

x = HugeNats(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 = HugeNats(0b100101)

x.rotl(2)    # HugeNats(22, bit_length=6)   rota a la izquierda
x.rotr(2)    # HugeNats(25, bit_length=6)   rota a la derecha

HugeNats(0).rotl(5)   # HugeNats(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 = HugeNats(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=None o 1: 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 = HugeNats(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 por n.
  • Cada trozo conserva su bit_length fijo.
x = HugeNats(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 = HugeNats(0b11, bit_length=2)
b = HugeNats(0b01, bit_length=2)
r = a.append(b)                   # bits: 01|11 -> 7, bit_length=4

# Reconstruir desde split
x = HugeNats(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 = HugeNats(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 = HugeNats(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)

HugeNats 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 = HugeNats(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 HugeNats desde (limbs, nbits). Fija bit_length a nbits.

y = HugeNats.from_core(limbs, nbits)
y = HugeNats.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 = HugeNats(0xAAAABBBBCCCCDDDD_1111222233334444, bit_length=128)

be = x.to_words_be()
y = HugeNats.from_words_be(be, 128)
assert int(y) == int(x)

Extraccion directa a uint64[]

APIs que evitan crear objetos HugeNats 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 = HugeNats(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 HugeNats. Para encadenar operaciones sobre arrays crudos.

HugeNats.bitwise_or(a_be, b_be, bit_length)

OR element-wise. Entrada y salida BE.

HugeNats.shift_left(a_be, k, bit_length)

Shift left por k bits. Trunca al bit_length. Entrada y salida BE.

HugeNats.extract_bits(a_be, start, width)

Extrae width bits desde posicion start. Entrada y salida BE.

HugeNats.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 HugeNats
arr = x.to_words_be()
shifted = HugeNats.shift_left(arr, 32, 256)
combined = HugeNats.bitwise_or(shifted, arr, 256)
chunk = HugeNats.extract_bits(combined, 64, 128)
result = HugeNats.from_words_be(chunk, 128)

Kernels Numba (hugenats.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 hugenats.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 HugeNats
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
HugeNats(value, bit_length=None) constructor HugeNats int, float, list(BE), ndarray(LE), HugeNats
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
weight propiedad int bits a 1 (peso de Hamming, atajo de bit_count())
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 HugeNats como int.from_bytes
to_core(copy, word_order) instancia (uint64[], int) LE o BE
from_core(limbs, nbits, word_order) classmethod HugeNats LE o BE
to_words_be() instancia uint64[] solo BE
from_words_be(words, bit_length) classmethod HugeNats solo BE
split(n) instancia tuple[HugeNats] LSB primero
append(*others) instancia HugeNats 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 HugeNats 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.
  • bool no se acepta como shift en <<, >>, rotl, rotr.
  • Sin bit_length explicito, los ceros de mayor peso se recortan.
  • Con bit_length explicito, la anchura logica se conserva siempre.
  • HugeNats es inmutable (usa __slots__, sin __dict__). Los limbs se cachean internamente.
  • split(n) requiere que bit_length() sea divisible por n.

Desarrollo

pip install -r requirements.txt
pytest -q

Demostraciones interactivas en HugeNats_demo.ipynb.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hugenats-0.5.0.tar.gz (36.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hugenats-0.5.0-py3-none-any.whl (24.6 kB view details)

Uploaded Python 3

File details

Details for the file hugenats-0.5.0.tar.gz.

File metadata

  • Download URL: hugenats-0.5.0.tar.gz
  • Upload date:
  • Size: 36.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hugenats-0.5.0.tar.gz
Algorithm Hash digest
SHA256 e26aac4e9d26e9a54477fbe21000c8ac1276ed62a983198699b9543908be299e
MD5 d51adba3c82c2203ada8ebe969da707f
BLAKE2b-256 09c799953bffbac34b68a530dea816d328365c730712eaea956de37391809db1

See more details on using hashes here.

File details

Details for the file hugenats-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: hugenats-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 24.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hugenats-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 30a9c60c2f8406de303f5e25980b89d8e415788099f8aeb726991f9a897a7067
MD5 a3c2a5d7267dcb2fa7c1d267089cb4c0
BLAKE2b-256 5f8b27d0f08e94ffb74d97904e9980ed094d9891a2e18ce85c9567493ef726b7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page