Arithmetic Nonmax
The arithmetic-nonmax crate provides NonMax* types for integer types that are guaranteed to never be their maximum value (MAX). Using these types enables Rust's "niche optimization," where Option<NonMax*> has the same size as the underlying integer type.
Additionally, arithmetic operations can be written intuitively. For example, in shortest path algorithms, using the Option<NonMax*> type allows you to leverage the type system while optimizing the memory layout. See Benchmarks for more details.
use ;
let a: NonMaxU32 = new.unwrap;
let b = non_max!; // Conveniently create values with the non_max! macro
let c = b - a; // Arithmetic operations between NonMax types
assert_eq!;
let d = a * 5; // Arithmetic operations with primitive integers
assert!;
assert_eq!; // Can be converted to string
let array = ;
let e = array; // Support indexing
let f = array.iter.;
assert_eq!;
Benchmarks
The following values are measured using iai and divan. NonMax achieves the same memory savings as the sentinel method (the fastest) while enabling more efficient operations and reduced instruction counts compared to the standard Option.
Instruction-based Benchmarks (benches/iai_comparison.rs)
We measured the number of instructions for operations on each type using iai. Here, we extract results for i32 (default integer type) and usize (indexing type), which are commonly used (other integer types show similar trends).
i32 (Default integer type)
| Operation (i32) | NonMax |
Option<i32> |
Raw (Sentinel) |
|---|---|---|---|
New (new) |
24 | 25 | 23 |
Get (get) |
24 | 29 | 23 |
Add (add) |
32 | 41 | 25 |
Compare (cmp) |
30 | 32 | 26 |
usize (Index type)
| Operation (usize) | NonMax |
Option<usize> |
Raw (Sentinel) |
|---|---|---|---|
New (new) |
24 | 25 | 23 |
Get (get) |
24 | 29 | 23 |
Add (add) |
30 | 41 | 25 |
Compare (cmp) |
30 | 32 | 26 |
Note: Values are measured with iai (x86_64 environment). Comparisons (cmp) measure the cost of infinity-aware comparisons (curr.is_none() || Some(next) < curr vs next < curr). For a full report, see bench_result_iai.txt.
Execution Time and Memory Usage Benchmarks (benches/comparison.rs)
We measured performance in practical algorithms and large-scale data operations using divan. Reducing the memory size by half (8 bytes → 4 bytes) improves cache efficiency, delivering faster execution speeds than the standard Option for large data.
Algorithms
| Algorithm (u32) | Metric | NonMax |
Option<u32> |
Sentinel (MAX) |
|---|---|---|---|---|
| Floyd-Warshall(V=400) | Execution Time | 51 ms | 56 ms | 45 ms |
| Memory Usage | 640 KB | 1.3 MB | 640 KB | |
| Dijkstra(V=100,000) | Execution Time | 44 ms | 45 ms | 43 ms |
| Memory Usage | 2.5 MB | 2.9 MB | 2.5 MB |
Large-scale Data Operations (N=1,000,000)
| Operation (u32) | NonMax |
Option<u32> |
Sentinel (MAX) |
|---|---|---|---|
Sort (sort) |
74 ms | 41 ms | 18 ms |
Sum (sum) |
0.54 ms | 0.64 ms | 0.15 ms |
Update (update) |
1.9 ms | 2.0 ms | 1.9 ms |
Note: Values are measured with divan (x86_64 environment, median). For a full report, see bench_result.txt.
Bundling for Competitive Programming
When submitting to online judges, you can bundle the library into a single file using the following command:
(; ; )
Examples
You can find programs that solve Aizu Online Judge problems using this library in the examples/ directory.
Note that you need to bundle the library into a single file when submitting to an online judge.
- GRL_1_A: Single Source Shortest Path: Uses
Option<NonMax<u32>>for distances, representing unreachable nodes asNone. - GRL_1_C: All Pairs Shortest Path: Uses
Option<NonMax<i32>>for distances, representing unreachable nodes asNone. - DSL_1_A: Disjoint Set: Manages parent indices in a Union-Find data structure using
Option<NonMaxUsize>. - DPL_1_A: Coin Changing Problem: Solves the coin change problem using DP with
Option<NonMax<u32>>.