Skip to main content

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() y len(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 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 = 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_length declarado: voltea dentro de esa anchura.
  • Sin bit_length declarado: usa el bit_length() natural.
  • ~~x == x siempre.
  • HugeNat(0) sin anchura: bit_length()=0, no hay bits que voltear, devuelve HugeNat(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=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 = 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 por n.
  • Cada trozo conserva su bit_length fijo.
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.
  • 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.
  • HugeNat 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 HugeNat_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.4.1.tar.gz (29.8 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.4.1-py3-none-any.whl (18.5 kB view details)

Uploaded Python 3

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

Hashes for hugenats-0.4.1.tar.gz
Algorithm Hash digest
SHA256 a1692e0c47b284c145455eb8defca88808033f552f6427b7d0ac7df131047e1a
MD5 d9fab0a08c2a4e5374bf7a22cb70d929
BLAKE2b-256 28cd06baac9e7be1c41eb90768f2b97a2650986471d89472c8fdfb98ed9b0ee3

See more details on using hashes here.

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

Hashes for hugenats-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e5d4f0b35eda70ad6ab8d2376057553702e46ee0b086d2d5fe6b44e8d2d37ced
MD5 74af27086df08582da717a4d3a1aa19d
BLAKE2b-256 1a1665e8886ec88ab8d879555122fb8d3526b678a7695f9ab5847dc327d0594e

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