Skip to main content

Polars plugin for 256-bit U256 and signed I256 (two's complement) integers backed by ruint

Project description

polars-u256-plugin

PyPI CI License: MIT

Polars plugin for 256-bit integer arithmetic.

🙋 Why

Since Polars tops out at Int128, this will fail:

melania_bags = 2**129
pl.DataFrame(x=[melania_bags])

# OverflowError: int value too large for Polars integer types

with polars_u256_plugin any of these work:

import polars_u256_plugin as u256

df = pl.select(
    x=u256.from_int(melania_bags),
    y=u256.lit(melania_bags),
    z=u256.from_hex(hex(melania_bags)),
)

In addition, you can perform full 256-bit arithmetic, bitwise ops, comparisons, and aggregations in Polars expressions with safe overflow handling (invalid ops yield null).

See examples/example.py and examples/benchmarks.py for more.

🚀 Quickstart

pip install polars_u256_plugin

✨ Features

Core Arithmetic & Operations

  • Full 256-bit precision: Built on ruint, ensuring exact arithmetic without precision loss
  • Operators: Arithmetic (+, -, *, /, %, **), bitwise (&, |, ^, ~, <<, >>), comparisons (==, <, <=, >, >=)
  • Aggregation: sum()
  • Series ops: cumsum(), diff()

Data Types & Precision

  • U256 (unsigned): Full 0 to 2²⁵⁶-1 range for token balances, wei amounts, and large counters
  • I256 (signed): Two's complement signed integers with proper overflow handling and Euclidean division/remainder operations
  • Exact decimal arithmetic: Enables high-precision financial calculations by storing scaled integers (e.g., storing cents as integers scaled by 10²)
  • Safe overflow behavior: Operations return null on overflow rather than wrapping, preventing silent errors

Integration & Usability

  • Native Polars expressions: Full integration with select(), with_columns(), group_by().agg(), and all other Polars operations
  • Operator overloading: Natural syntax like pl.col("balance").u256 + pl.col("amount").u256
  • Expression namespaces: Fluent API via .u256 and .i256 namespaces for operations like .to_hex(), .sum()
  • Multiple input formats: Accept Python integers, hex strings (0x...), and raw bytes with automatic coercion

Performance & Storage

  • Vectorized operations: All arithmetic implemented in Rust using ruint for optimal performance
  • Efficient storage: Uses 32-byte big-endian binary representation via Polars BinaryView for memory efficiency
  • Lazy evaluation: Full compatibility with Polars lazy evaluation and query optimization
  • Benchmark tested: Significantly faster than Python object fallback methods for large datasets

Blockchain & DeFi Ready

  • Ethereum compatibility: Native handling of wei values, token amounts, and smart contract integers
  • Hex string support: Seamless conversion to/from hexadecimal for blockchain data interoperability
  • No precision loss: Maintains full 256-bit precision throughout complex calculation pipelines
  • Production tested: Powers real-world blockchain analytics and DeFi applications

API

Initialization

u256.from_int(value)        # Python int → u256 expression  
u256.from_int(pl.col())     # Convert int column to u256 (preferred)
u256.from_hex(pl.col())     # Hex strings → u256
u256.lit(value)             # Create u256 literal (int/hex/bytes)

Helpers & Constants

# Validation helpers
u256.validate_hex(pl.col("hex_str"))     # bool: valid hex/binary → u256
u256.validate_range(pl.col("int_col"))   # bool: fits in unsigned 256-bit

# Common constants (as expressions)
u256.MAX_VALUE   # 2**256 - 1
u256.MIN_VALUE   # 0
i256.MAX_VALUE   # 2**255 - 1
i256.MIN_VALUE   # -2**255

Arithmetic

u256.add(a, b)              # Addition
u256.sub(a, b)              # Subtraction  
u256.mul(a, b)              # Multiplication
u256.div(a, b)              # Division (null on div by zero)
u256.mod(a, b)              # Modulo
u256.pow(a, b)              # Exponentiation

Comparisons

u256.eq(a, b)               # Equal
u256.lt(a, b), u256.le(a, b) # Less than, less/equal
u256.gt(a, b), u256.ge(a, b) # Greater than, greater/equal

Bitwise Operations

u256.bitand(a, b)           # Bitwise AND
u256.bitor(a, b)            # Bitwise OR
u256.bitxor(a, b)           # Bitwise XOR
u256.bitnot(a)              # Bitwise NOT
u256.shl(a, bits)           # Left shift
u256.shr(a, bits)           # Right shift

Aggregations & Conversions

u256.sum(col)               # Sum aggregation
u256.to_hex(col)            # → hex strings (0x...)
u256.to_int(col)            # → Python int (if fits in i64)

Group-By Aggregations

df.group_by("account_id").agg(
    u256.sum(u256.from_int(pl.col("tx_amount"))).alias("total_spent")
)

# Multiple aggregations
df.group_by("token_address").agg([
    u256.sum(u256.from_int(pl.col("balance"))).alias("total_supply"),
    pl.len().alias("holder_count"),
    pl.col("last_updated").max()
])

# Mixed with regular Polars aggregations
df.group_by("wallet").agg([
    u256.sum(u256.from_int(pl.col("wei_balance"))).alias("total_wei"),
    pl.col("gas_used").sum(),
    pl.col("block_number").max()
]).with_columns(
    # Convert to readable hex for display
    u256.to_hex(pl.col("total_wei")).alias("total_wei_hex")
)

Display Utilities

u256.format_u256_dataframe(df, cols)    # Format u256 columns as hex
u256.print_u256_dataframe(df)           # Print with hex formatting
df.with_u256_display("col")             # Add hex display columns
df.show_u256_hex("col")                 # Replace binary with hex
df.u256.from_int(["balance", "amount"], replace=True)  # Convert int cols → u256
df.u256.to_hex(["balance"], replace=False)              # Add balance_hex

Fluent API (.u256 namespace)

# Arithmetic operators: +, -, *, /, //, %, **
pl.col("balance").u256 + pl.col("amount").u256
pl.col("value").u256 * 2

# Bitwise operators: &, |, ^, ~, <<, >>  
pl.col("flags").u256 & 0xFF

# Comparisons: ==, <, <=, >, >=
pl.col("a").u256 < pl.col("b").u256

# Methods
pl.col("value").u256.to_hex()
pl.col("data").u256.sum()
# Aggregations
pl.col("data").u256.min()
pl.col("data").u256.max()
pl.col("data").u256.mean()

I256 (Signed) - Complete API

Supported operations (signed, two's complement): arithmetic (+, -, *, /, %, //=euclid), comparisons, aggregations (sum/min/max/mean), series ops (cumsum/diff), and helpers.

i256.div_euclid(a, b)       # Euclidean division
i256.rem_euclid(a, b)       # Euclidean remainder
i256.from_int(value_or_col) # Python int or Polars Expr; supports negatives
i256.to_int(col)            # Returns signed integers

# Namespace: .i256 (same operators as .u256)
pl.col("balance").i256 + pl.col("amount").i256

Performance

Rows: 5,000,000
== Aggregations ==
u64: sum                        0.001s

u256: sum only (preconv)        0.042s
u256: conversion only           0.035s
u256: from_int+sum              0.081s

i128: sum only (preconv)        0.001s
i128: conversion only           0.006s

Decimal(0): sum only (preconv)  0.001s
Decimal(0): conversion only     0.014s

Python Object: map+sum          0.215s

See examples/benchmarks.py for more.

Tip: preconvert once (with_columns(x256=u256.from_int(...))) and reuse.

Implementation Notes

  • Storage: U256/I256 are 32-byte big-endian binary columns.
  • Semantics:
    • Division: u256 uses truncating integer division; i256 has truncating div and Euclidean div_euclid/rem_euclid.
    • Remainder: i256.mod carries dividend sign; Euclidean remainder is non-negative.
    • Errors: div/mod by zero → null.
    • Overflow: u256 add/mul overflow → null; i256 add/sub/mul wrap modulo 2²⁵⁶.
    • to_int: returns null if the value does not fit in signed 64-bit.

I256 (signed)

  • Two’s complement over 256 bits.
  • div truncates toward zero; mod carries dividend sign.
  • Euclidean: i256.div_euclid(...) / i256.rem_euclid(...).

Note: Constructing a DataFrame directly with Python ints > 64 -bit may result in non-integer dtype (e.g., Object) depending on your Polars build. In such cases, prefer providing values as hex strings and from_hex, or generate constants in expressions via from_int(pl.lit(...)).

Credits

  • ruint: High-performance Rust library for arbitrary-precision unsigned integers
  • polars-evm: Utilities for working with EVM data in polars

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

polars_u256_plugin-0.2.2.tar.gz (55.6 kB view details)

Uploaded Source

Built Distributions

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

polars_u256_plugin-0.2.2-cp39-abi3-win_amd64.whl (4.2 MB view details)

Uploaded CPython 3.9+Windows x86-64

polars_u256_plugin-0.2.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.2 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

polars_u256_plugin-0.2.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.9 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

polars_u256_plugin-0.2.2-cp39-abi3-macosx_11_0_arm64.whl (3.7 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

polars_u256_plugin-0.2.2-cp39-abi3-macosx_10_12_x86_64.whl (4.1 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file polars_u256_plugin-0.2.2.tar.gz.

File metadata

  • Download URL: polars_u256_plugin-0.2.2.tar.gz
  • Upload date:
  • Size: 55.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: maturin/1.9.4

File hashes

Hashes for polars_u256_plugin-0.2.2.tar.gz
Algorithm Hash digest
SHA256 e6e177fb9e4d8cc589682edb006c976ed0c4ba8ac472f922e79d0fae16d62e68
MD5 70db2e65f7a562b8edd392c2b9afa952
BLAKE2b-256 c8110900af9f0ec3aa31c6fda27fb6b9e15bac601b75a56789af030c49332b30

See more details on using hashes here.

File details

Details for the file polars_u256_plugin-0.2.2-cp39-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for polars_u256_plugin-0.2.2-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 c4d741e6642b782fc247e5a52c3c5e285ca5cb208b1708ed32e695052ba1a7ac
MD5 05c6c55c616cca5f4ade2eaaf2876129
BLAKE2b-256 31ca8ea29adc3aa6ffe8fbec98fa73fd5832c0eebf2cdeda20889c2a6c8dd701

See more details on using hashes here.

File details

Details for the file polars_u256_plugin-0.2.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for polars_u256_plugin-0.2.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e942a0a99f51bd7175f0a4d1383bdbb51c0e384794be85bd95d942632c70233c
MD5 9e2cfd704e8e107e2a1e2d632c06cff5
BLAKE2b-256 915bcc6e528ee81b7b3c6db043af529a6dc4c85991ba174e4e40d61e4162efa5

See more details on using hashes here.

File details

Details for the file polars_u256_plugin-0.2.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for polars_u256_plugin-0.2.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5fa4d84a94093811a7755966b013df2a5ddd94030f9c84fff09b90d6dd98b89d
MD5 14607f8e40c8b5332c28a2b40d31188a
BLAKE2b-256 e87dee9c178355c9150a2c592816c31f9cbaf82ba3292190821638a568c38e5b

See more details on using hashes here.

File details

Details for the file polars_u256_plugin-0.2.2-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for polars_u256_plugin-0.2.2-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 aa4dfa7c81e4a97acd973797658ec074527f1de53cef5131645ebc49863de08f
MD5 648955411f6477c8feb074bc89698669
BLAKE2b-256 69859b0c5ec355376b0eadcbf49e28a45a92f7c67d1257b75cf53de30f42735e

See more details on using hashes here.

File details

Details for the file polars_u256_plugin-0.2.2-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for polars_u256_plugin-0.2.2-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 2f6257c60d3a88a1b46047e679b373540fd1648b0db54623ed3970a035bc95e6
MD5 55466ae16326cad92196964230dc71e0
BLAKE2b-256 e5f02b520001aa6b8f4fd263b3f8c4a65cb2ad6279b4d47e243b5a15179f4dc2

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