1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// SPDX-FileCopyrightText: 2026 John Moxley
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Integer exponentiation policy -- the square-and-multiply algorithm matcher.
//!
//! `Uint<N>::pow` / `Uint<N>::wrapping_pow` and the `Int<N>` siblings
//! delegate to [`dispatch`], which follows the canonical policy shape (see
//! `docs/ARCHITECTURE.md` -> "Policy file structure"):
//!
//! 1. an [`Algorithm`] enum -- the real pow algorithm(s), no `Default`
//! variant;
//! 2. a [`Select`] verdict -- a settled algorithm or "the value decides";
//! 3. a `const fn` [`select`] keyed on `N`, total over the key;
//! 4. dispatch via an inline `const { select::<N>() }` block, then an
//! **exhaustive** `match algo` -- no `_`, no panic.
//!
//! Because `select` is `const` and keyed only on the const generic `N`,
//! the `const { ... }` block folds per monomorphisation and the unchosen arm
//! is dead-arm-eliminated in release: each concrete `Uint<N>` compiles to a
//! direct call to the square-and-multiply kernel, no runtime branch.
//!
//! # Algorithm
//!
//! The algorithm fn
//! [`crate::int::algos::pow::pow_square_and_multiply::pow_square_and_multiply`]
//! is the binary square-and-multiply loop computing via the const
//! [`crate::int::algos::sqr::sqr_low_fixed::sqr_low_fixed`] (square step) and
//! [`crate::int::algos::mul::mul_schoolbook::mul_low_fixed`] (multiply step) kernels.
//! Binary exponentiation by squaring is optimal for the small fixed
//! exponents `pow` is used with in this crate (root iterations: `k-1`,
//! `k` <= ~10). There is no width-specific crossover and no value-split. The
//! layering points DOWN -- the algorithm calls the kernels, never a
//! pow/sqr/mul method on `Uint<N>`.
//!
//! The `pow_ab` N-way dispatch-seam A/B confirms `SquareAndMultiply` beats
//! the `Schoolbook` reference at EVERY width across the exponent sweep
//! (e2..e31): 1.18x at `N == 1` growing to ~3-4x at the wide tiers (the
//! O(exp) vs O(log exp) gap widening with both width and exponent). So
//! `select` returns `SquareAndMultiply` for all `N` -- confirmed optimal.
//!
//! As with [`crate::int::policy::cube`], the only further axis is the
//! `LimbSize` u128 packing of the square/multiply steps, but
//! `pow_square_and_multiply` is `const fn` (reached from `const fn`
//! `Int<N>::wrapping_pow`) and the u128 `sqr_low_limb` / `mul_low_limb`
//! kernels are not `const`, so a u128 pow is ineligible without de-const-ing
//! the public API.
//!
//! A `Schoolbook` reference arm is registered for the naive repeated-multiply
//! algorithm (via [`crate::int::algos::pow::pow_schoolbook::pow_schoolbook`]).
//! It is unrouted (not returned by `select`) and marked `#[allow(dead_code)]`
//! so the exhaustive match stays warning-clean.
//!
//! The `ByValue` arm of [`Select`] is present for canonical-shape
//! uniformity; `select` never returns it.
//!
//! # Const-ness
//!
//! `dispatch` IS `const fn`: the algorithm fn computes via const kernels,
//! so the type's `const fn` `wrapping_pow` can delegate through it. The
//! `ByValue` arm returns the default algorithm tag without invoking the fn
//! pointer (calling a fn pointer is not permitted in `const fn`; merely
//! matching the variant is fine).
use cratepow_schoolbook;
use cratepow_square_and_multiply;
use crateUint;
// -- 1. the real pow algorithms -- NAMED, no `Default` --------------------
/// The exponentiation algorithms this policy chooses between. Variants are
/// the CamelCase of each algorithm fn's name minus the `pow_` function
/// prefix -- strict 1:1 with the fns.
// -- 2. the verdict --------------------------------------------------------
/// A settled algorithm, or "the value decides". The pow picker always
/// returns `ByAlgorithm`: the choice is fully determined by `N` (which is
/// constant, and the same algorithm wins at every `N`). `ByValue` is part of
/// the canonical shape for uniformity across functions; `select` never
/// returns it.
// -- 3. the matcher: const, keyed on `N`, total over the key --------------
/// Pick the pow algorithm for storage limb count `N`. Total over the key;
/// square-and-multiply is width-independent so `SquareAndMultiply` wins at
/// every `N`.
const
// -- 4. the dispatcher: fold the verdict, then dispatch --------------------
/// Integer exponentiation dispatcher for `Uint<N>`.
///
/// Resolves the compile-time algorithm verdict via
/// `const { select::<N>() }` (folds per monomorphisation; dead arms are
/// eliminated in release) then dispatches exhaustively over [`Algorithm`].
///
/// Must be `const fn`: `Int<N>::wrapping_pow` is itself `const fn`. The
/// `ByValue` arm returns the default algorithm tag without invoking the fn
/// pointer, satisfying the `const fn` constraint.
pub const