Skip to main content

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 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), 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 (HugeNat o int >= 0) y devuelven siempre HugeNat.
  • Las restas que producirían un valor negativo lanzan ValueError.
  • Si se declara bit_length al construir, bit_length() devuelve esa anchura lógica fijada.
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)

Indexado de bits

  • Convención: LSB = índice 0. Índices negativos son relativos a bit_length().
  • Fuera de rango devuelve 0.
  • ~x no está definido y lanza ValueError.
x = HugeNat(0b1101101)  # 109

x[0]    # 1 (LSB)
x[-1]   # 1 (MSB)
x[100]  # 0

Slicing de bits

  • step en {None, 1} usa ruta rápida: normaliza como Python y rellena con ceros si el límite superior excede bit_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 de bit_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.

  • n debe ser int y n >= 1.
  • bit_length() debe ser divisible por n, si no lanza ValueError.
  • Devuelve una tupla de HugeNat en orden LSB -> MSB (trozo de menor peso primero).
  • Cada trozo conserva su anchura lógica fija (bit_length del trozo).
  • Si bit_length() == 0, devuelve n trozos HugeNat(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]

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)

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 >= 0 o arrays 1D de limbs uint64 (little-endian). Valores negativos o dimensiones distintas lanzan ValueError.
  • Sin bit_length explícito, los ceros de mayor peso se recortan automáticamente.
  • Con bit_length explí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


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.1.6.tar.gz (16.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.1.6-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: hugenats-0.1.6.tar.gz
  • Upload date:
  • Size: 16.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.1.6.tar.gz
Algorithm Hash digest
SHA256 b88141dbc02031441db35bad896243a7876ac95872750be0a5a7cfea16c4f5f2
MD5 8d1d7bbf08682b7a4b1230f31b2c8931
BLAKE2b-256 4b77554908abe8d570d591d61413d2ba7c8beb8b3d8c25454a9f7f8ff15166f8

See more details on using hashes here.

File details

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

File metadata

  • Download URL: hugenats-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 10.3 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.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 ead5c18fb4ea490164d56b3eb1aad8cdecbfeb913eeb02a609e1361072ad068f
MD5 b8193d775c23510bce9c0d05ee4e13b2
BLAKE2b-256 0a30188b686c7a8349cd40f1b930b99d3c0437cbc3c396b3aef3b8a6dd839e9d

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