diff --git a/lexical-util/src/i256.rs b/lexical-util/src/i256.rs
new file mode 100644
index 00000000..d9da8012
--- /dev/null
+++ b/lexical-util/src/i256.rs
@@ -0,0 +1,27 @@
+//! An signed 256-bit integer type.
+//!
+//! This aims to have feature parity with Rust's signed
+//! integer types, such as [i32][core::i32]. The documentation
+//! is based off of [i32][core::i32] for each method/member.
+
+// TODO: Document
+// TODO: Feature gate this...
+
+// FIXME: Add support for [Saturating][core::num::Saturating] and
+// [Wrapping][core::num::Wrapping] when we drop support for <1.74.0.
+
+/// The 256-bit signed integer type.
+///
+/// This has the same binary representation as Apache Arrow's types,
+/// and therefore can safely be transmuted from one to the other.
+#[allow(non_camel_case_types)]
+#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
+#[repr(C)]
+pub struct i256 {
+ lo: u128,
+ hi: i128,
+}
+
+impl i256 {
+ // TODO: Here
+}
diff --git a/lexical-util/src/lib.rs b/lexical-util/src/lib.rs
index 22e2cec5..3e77b949 100644
--- a/lexical-util/src/lib.rs
+++ b/lexical-util/src/lib.rs
@@ -163,12 +163,14 @@ pub mod error;
pub mod extended_float;
pub mod f16;
pub mod format;
+pub mod i256;
pub mod iterator;
pub mod mul;
pub mod num;
pub mod options;
pub mod result;
pub mod step;
+pub mod u256;
mod api;
mod feature_format;
diff --git a/lexical-util/src/u256.rs b/lexical-util/src/u256.rs
new file mode 100644
index 00000000..03731985
--- /dev/null
+++ b/lexical-util/src/u256.rs
@@ -0,0 +1,637 @@
+//! An unsigned 256-bit integer type.
+//!
+//! This aims to have feature parity with Rust's unsigned
+//! integer types, such as [u32][core::u32]. The documentation
+//! is based off of [u32][core::u32] for each method/member.
+
+use core::cmp::Ordering;
+use core::fmt;
+use core::iter::{Product, Sum};
+use core::ops::*;
+use core::num::{ParseIntError, TryFromIntError};
+use core::str::FromStr;
+use crate::i256::i256;
+// TODO: Document
+// TODO: Feature gate this...
+
+// FIXME: Add support for [Saturating][core::num::Saturating] and
+// [Wrapping][core::num::Wrapping] when we drop support for <1.74.0.
+
+/// The 256-bit unsigned integer type.
+///
+/// This has the same binary representation as Apache Arrow's types,
+/// and therefore can safely be transmuted from one to the other.
+#[allow(non_camel_case_types)]
+#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
+#[repr(C)]
+pub struct u256 {
+ lo: u128,
+ hi: u128,
+}
+
+impl u256 {
+ /// The smallest value that can be represented by this integer type.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// assert_eq!(u256::MIN, 0);
+ /// ```
+ pub const MIN: u256 = u256 { lo: 0, hi: 0 };
+
+ /// The largest value that can be represented by this integer type
+ /// (2256 - 1).
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// assert_eq!(u256::MAX, 0); // TODO, need to negate...
+ /// ```
+ pub const MAX: u256 = u256 { lo: u128::MAX, hi: u128::MAX };
+
+ /// The size of this integer type in bits.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// assert_eq!(u256::BITS, 256);
+ /// ```
+ pub const BITS: u32 = 256;
+
+ /// Returns the number of ones in the binary representation of `self`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// assert_eq!(u256::BITS, 256); // TODO: Fix...
+ /// ```
+ #[inline(always)]
+ pub const fn count_ones(self) -> u32 {
+ // TODO: `ctpop`
+ todo!();
+ }
+
+ /// Returns the number of zeros in the binary representation of `self`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// assert_eq!(u256::BITS, 256); // TODO: Fix...
+ /// ```
+ #[inline(always)]
+ pub const fn count_zeros(self) -> u32 {
+ Self::BITS - self.count_ones()
+ }
+
+ /// Returns the number of leading zeros in the binary representation of `self`.
+ ///
+ /// Depending on what you're doing with the value, you might also be
+ /// interested in the `ilog2` function which returns a consistent
+ /// number, even if the type widens.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// let n = u256::MAX >> 2;
+ /// assert_eq!(n.leading_zeros(), 2);
+ ///
+ /// let zero = u256::MIN;
+ /// assert_eq!(zero.leading_zeros(), 256);
+ ///
+ /// let max = u256::MAX;
+ /// assert_eq!(max.leading_zeros(), 0);
+ /// ```
+ #[inline(always)]
+ pub const fn leading_zeros(self) -> u32 {
+ // TODO: ctlz
+ todo!();
+ }
+
+ /// Returns the number of trailing zeros in the binary representation of `self`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// let n = u256::MAX >> 2; // TODO: This is wrong
+ /// assert_eq!(n.trailing_zeros(), 2); // TODO: This is wrong
+ ///
+ /// let zero = u256::MIN;
+ /// assert_eq!(zero.trailing_zeros(), 256);
+ ///
+ /// let max = u256::MAX;
+ /// assert_eq!(max.trailing_zeros(), 0);
+ /// ```
+ #[inline(always)]
+ pub const fn trailing_zeros(self) -> u32 {
+ todo!();
+ }
+
+ // TODO: MOre here...
+}
+
+impl u256 {
+ /// Create the 256-bit unsigned integer to a `u8`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_u8(value: u8) -> Self {
+ Self { lo: value as u128, hi: 0 }
+ }
+
+ /// Create the 256-bit unsigned integer from a `u16`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_u16(value: u16) -> Self {
+ Self { lo: value as u128, hi: 0 }
+ }
+
+ /// Create the 256-bit unsigned integer from a `u32`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_u32(value: u32) -> Self {
+ Self { lo: value as u128, hi: 0 }
+ }
+
+ /// Create the 256-bit unsigned integer from a `u64`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_u64(value: u64) -> Self {
+ Self { lo: value as u128, hi: 0 }
+ }
+
+ /// Create the 256-bit unsigned integer from a `u128`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_u128(value: u128) -> Self {
+ Self { lo: value as u128, hi: 0 }
+ }
+
+ /// Create the 256-bit unsigned integer to an `i8`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_i8(value: i8) -> Self {
+ todo!();
+ }
+
+ /// Create the 256-bit unsigned integer from an `i16`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_i16(value: i16) -> Self {
+ todo!();
+ }
+
+ /// Create the 256-bit unsigned integer from an `i32`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_i32(value: i32) -> Self {
+ todo!();
+ }
+
+ /// Create the 256-bit unsigned integer from an `i64`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_i64(value: i64) -> Self {
+ todo!();
+ }
+
+ /// Create the 256-bit unsigned integer from an `i128`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_i128(value: i128) -> Self {
+ todo!();
+ }
+
+ /// Create the 256-bit unsigned integer from an `i256`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn from_i256(value: i256) -> Self {
+ todo!();
+ }
+
+ /// Convert the 256-bit unsigned integer to an `u8`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_u8(&self) -> u8 {
+ self.lo as u8
+ }
+
+ /// Convert the 256-bit unsigned integer to an `u16`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_u16(&self) -> u16 {
+ self.lo as u16
+ }
+
+ /// Convert the 256-bit unsigned integer to an `u32`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_u32(&self) -> u32 {
+ self.lo as u32
+ }
+
+ /// Convert the 256-bit unsigned integer to an `u64`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_u64(&self) -> u64 {
+ self.lo as u64
+ }
+
+ /// Convert the 256-bit unsigned integer to an `u128`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_u128(&self) -> u128 {
+ self.lo as u128
+ }
+
+ /// Convert the 256-bit unsigned integer to an `i8`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_i8(&self) -> i8 {
+ self.lo as i8
+ }
+
+ /// Convert the 256-bit unsigned integer to an `i16`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_i16(&self) -> i16 {
+ self.lo as i16
+ }
+
+ /// Convert the 256-bit unsigned integer to an `i32`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_i32(&self) -> i32 {
+ self.lo as i32
+ }
+
+ /// Convert the 256-bit unsigned integer to an `i64`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_i64(&self) -> i64 {
+ self.lo as i64
+ }
+
+ /// Convert the 256-bit unsigned integer to an `i128`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_i128(&self) -> i128 {
+ self.lo as i128
+ }
+
+ /// Convert the 256-bit unsigned integer to an `i256`, as if by an `as` cast.
+ #[inline(always)]
+ pub const fn as_i256(&self) -> i256 {
+ todo!();
+ }
+}
+
+macro_rules! op_impl {
+ ($trait:ident, $assign:ident, $op:ident, $op_assign:ident) => {
+ impl $trait<&u256> for u256 {
+ type Output = ::Output;
+
+ #[inline(always)]
+ fn $op(self, rhs: &Self) -> Self::Output {
+ self.$op(*rhs)
+ }
+ }
+
+ impl $assign for u256 {
+ #[inline(always)]
+ fn $op_assign(&mut self, other: Self) {
+ *self = self.$op(other);
+ }
+ }
+
+ impl $assign<&u256> for u256 {
+ #[inline(always)]
+ fn $op_assign(&mut self, other: &Self) {
+ *self = self.$op(other);
+ }
+ }
+ };
+}
+
+impl Add for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn add(self, other: Self) -> Self::Output {
+ todo!();
+ }
+}
+
+op_impl!(Add, AddAssign, add, add_assign);
+
+impl fmt::Binary for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}
+
+impl BitAnd for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn bitand(self, rhs: Self) -> Self::Output {
+ Self::Output { hi: self.hi.bitand(rhs.hi), lo: self.lo.bitand(rhs.lo) }
+ }
+}
+
+op_impl!(BitAnd, BitAndAssign, bitand, bitand_assign);
+
+impl BitOr for u256 {
+ type Output = u256;
+
+ #[inline(always)]
+ fn bitor(self, rhs: Self) -> Self::Output {
+ Self::Output { hi: self.hi.bitor(rhs.hi), lo: self.lo.bitor(rhs.lo) }
+ }
+}
+
+op_impl!(BitOr, BitOrAssign, bitor, bitor_assign);
+
+impl BitXor for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn bitxor(self, rhs: Self) -> Self::Output {
+ Self::Output { hi: self.hi.bitxor(rhs.hi), lo: self.lo.bitxor(rhs.lo) }
+ }
+}
+
+op_impl!(BitXor, BitXorAssign, bitxor, bitxor_assign);
+
+impl fmt::Debug for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}
+
+impl fmt::Display for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}
+
+impl Div for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn div(self, rhs: Self) -> Self::Output {
+ todo!();
+ }
+}
+
+op_impl!(Div, DivAssign, div, div_assign);
+
+impl From for u256 {
+ #[inline(always)]
+ fn from(small: bool) -> Self {
+ Self { lo: small as u128, hi: 0 }
+ }
+}
+
+impl From for u256 {
+ #[inline(always)]
+ fn from(c: char) -> Self {
+ Self { lo: c as u128, hi: 0 }
+ }
+}
+
+macro_rules! from_impl {
+ ($t:ty, $op:ident) => {
+ impl From<$t> for u256 {
+ #[inline(always)]
+ fn from(small: $t) -> Self {
+ Self::$op(small)
+ }
+ }
+ };
+}
+
+from_impl!(u8, from_u8);
+from_impl!(u16, from_u16);
+from_impl!(u32, from_u32);
+from_impl!(u64, from_u64);
+from_impl!(u128, from_u128);
+
+impl FromStr for u256 {
+ type Err = ParseIntError;
+
+ /// Parses a string s to return a value of this type.
+ ///
+ /// This is not optimized, since all optimization is done in
+ /// the lexical implementation.
+ #[inline(always)]
+ fn from_str(src: &str) -> Result {
+ todo!();
+ }
+}
+
+impl fmt::LowerExp for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}
+
+impl fmt::LowerHex for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}
+
+impl Mul for u256 {
+ type Output = u256;
+
+ #[inline(always)]
+ fn mul(self, rhs: Self) -> Self::Output {
+ todo!();
+ }
+}
+
+op_impl!(Mul, MulAssign, mul, mul_assign);
+
+impl Not for u256 {
+ type Output = u256;
+
+ #[inline(always)]
+ fn not(self) -> Self::Output {
+ todo!();
+ }
+}
+
+impl Not for &u256 {
+ type Output = ::Output;
+
+ #[inline(always)]
+ fn not(self) -> Self::Output {
+ u256::not(*self)
+ }
+}
+
+impl fmt::Octal for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}
+
+impl Ord for u256 {
+ #[inline(always)]
+ fn cmp(&self, other: &Self) -> Ordering {
+ todo!();
+ }
+}
+
+impl PartialOrd for u256 {
+ #[inline(always)]
+ fn partial_cmp(&self, other: &Self) -> Option {
+ todo!();
+ }
+}
+
+impl Product for u256 {
+ #[inline(always)]
+ fn product>(iter: I) -> Self {
+ todo!();
+ }
+}
+
+impl Rem for u256 {
+ type Output = u256;
+
+ #[inline(always)]
+ fn rem(self, rhs: Self) -> Self::Output {
+ todo!();
+ }
+}
+
+op_impl!(Rem, RemAssign, rem, rem_assign);
+
+impl Shl for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn shl(self, other: Self) -> Self::Output {
+ todo!();
+ }
+}
+
+impl Shl<&u256> for u256 {
+ type Output = ::Output;
+
+ #[inline(always)]
+ fn shl(self, other: &u256) -> Self::Output {
+ self.shl(*other)
+ }
+}
+
+impl Shr for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn shr(self, other: Self) -> Self::Output {
+ todo!();
+ }
+}
+
+macro_rules! shift_impl {
+ ($($t:ty)*) => ($(
+ impl Shl<$t> for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn shl(self, other: $t) -> Self::Output {
+ todo!();
+ }
+ }
+
+ impl Shl<&$t> for u256 {
+ type Output = ::Output;
+
+ #[inline(always)]
+ fn shl(self, other: &$t) -> Self::Output {
+ self.shl(*other)
+ }
+ }
+
+ impl ShlAssign<$t> for u256 {
+ #[inline(always)]
+ fn shl_assign(&mut self, other: $t) {
+ *self = self.shl(other);
+ }
+ }
+
+ impl ShlAssign<&$t> for u256 {
+ #[inline(always)]
+ fn shl_assign(&mut self, other: &$t) {
+ *self = self.shl(other);
+ }
+ }
+
+ impl Shr<$t> for u256 {
+ type Output = Self;
+
+ #[inline(always)]
+ fn shr(self, other: $t) -> Self::Output {
+ todo!();
+ }
+ }
+
+ impl Shr<&$t> for u256 {
+ type Output = ::Output;
+
+ #[inline(always)]
+ fn shr(self, other: &$t) -> Self::Output {
+ self.shr(*other)
+ }
+ }
+
+ impl ShrAssign<$t> for u256 {
+ #[inline(always)]
+ fn shr_assign(&mut self, other: $t) {
+ *self = self.shr(other);
+ }
+ }
+
+ impl ShrAssign<&$t> for u256 {
+ #[inline(always)]
+ fn shr_assign(&mut self, other: &$t) {
+ *self = self.shr(other);
+ }
+ }
+ )*);
+}
+
+shift_impl! { i8 i16 i32 i64 i128 i256 isize u8 u16 u32 u64 u128 usize }
+
+impl Sub for u256 {
+ type Output = u256;
+
+ #[inline(always)]
+ fn sub(self, rhs: Self) -> Self::Output {
+ todo!();
+ }
+}
+
+op_impl!(Sub, SubAssign, sub, sub_assign);
+
+impl Sum for u256 {
+ #[inline(always)]
+ fn sum>(iter: I) -> Self {
+ todo!();
+ }
+}
+
+macro_rules! try_from_impl {
+ ($($t:ty)*) => ($(
+ impl TryFrom<$t> for u256 {
+ type Error = TryFromIntError;
+
+ #[inline(always)]
+ fn try_from(u: $t) -> Result>::Error> {
+ todo!();
+ }
+ }
+ )*);
+}
+
+try_from_impl! { i8 i16 i32 i64 i128 i256 isize }
+
+impl fmt::UpperExp for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}
+
+impl fmt::UpperHex for u256 {
+ #[inline(always)]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ todo!();
+ }
+}