Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
26 KiB
Jet and exact ring switch
One opened offset eta = x - r drives two cheap corrections. The binomial
jet returns shares of \f$\binom{x}{0},\ldots,\binom{x}{d}\f$ after a public
Chu–Vandermonde shift. The ring switch returns shares of x in any residue
group whose comparison payload is the destination modulus.
The same offset also drives [offset Horner](@ref offset_horner), [offset polynomials](@ref offset_poly), and [carry](@ref carry). [Prefix parity](@ref prefix_parity) reads a key's path. [Cleartext maps](@ref grotto_luts) evaluate fixed-point functions with no tree.
Binomial jet
\htmlonly
make_offset_jet_keys(center, degree) keys one incremental gt whose
payload is the vector of \f$\binom{\mathrm{center}}{k}\f$ in
\f$\mathbb{Z}/2^{64}\f$. After eta opens,
the same knot shift and carry cut as offset poly refine the pieces. On the
piece with carry kappa,
\f[ \binom{c+\kappa}{k} =\sum_j\binom{c}{j}\binom{\kappa}{k-j}. \f]
make_offset_jet_keys writes one incremental comparison for degree d
(d ≤ 16). The seed spine is Θ(n λ) bits, with n the center's bit
length and λ the seed width. Value words grow with the d+1 binomial
lanes. After η is public, offset_jet_shares evaluates that one key
on the K knots, the same order as one eval_sequence on the knots.
The Chu–Vandermonde
update after those walks is Θ(P · d²) arithmetic, where P is the
number of refined pieces (the knots, plus the domain minimum, plus the
carry cut when the input width is at most 62). offset_jet_dot is
Θ(d). No further round when the coefficients are public.
offset_jet_shares returns that shifted jet. Public dots are free:
- value of \f$\sum a_k\binom{x}{k}\f$ via
offset_jet_dot; - forward difference via
offset_jet_difference_coeff(Pascal); - hockey-stick prefix via
offset_jet_prefix_coeff.
The prefix needs \f$\binom{x}{k+1}\f$, so the key degree must be one larger than the polynomial degree. Degree 16 therefore prefix-sums polynomials through degree 15.
Binomials modulo \f$2^{64}\f$ use a falling factorial modulo \f$2^{64+v_2(k!)}\f$ (\f$v_2(16!)=15\f$), then multiply by the inverse of the odd part of \f$k!\f$. Dividing by \f$k!\f$ inside \f$\mathbb{Z}/2^{64}\f$ alone is not exact.
A Padé pair or one Newton correction is two dots against the same jet and one reciprocal after the shares are opened. Those are not separate APIs.
Code samples\n
- jet_and_ring.cpp \include{cpp} grotto/jet_and_ring.cpp
Exact ring switch
\htmlonly
For an unsigned \f$n\f$-bit limb (\f$n\le 64\f$) with representatives in \f$[0,2^n)\f$,
\f[ \eta + r = x + w\cdot 2^n,\qquad w=\mathbf{1}[r+\eta\ge 2^n]. \f]
In any modulus \f$M\f$,
\f[ x \equiv \eta + (r\bmod M) - w\cdot(2^n\bmod M)\pmod M. \f]
A uint64 comparison share is not a share mod \f$M\f$. The payload of the
wrap comparison is the destination element \f$2^n\bmod M\f$. The dealer keys
lt(2^n \bmod M) at the secret r and stores an additive split of r in
the residue group. After eta opens, each party evaluates at the public
query \f$2^n-1-\eta\f$. That indicator is hot exactly on wrap, including the
eta = 0 case. Party 0 adds public eta.
Destination groups:
grotto::zn64<Mod>andgrotto::zn128<Lo,Hi>([residue.hpp](@ref grotto/residue.hpp));dpf::field128;dpf::p256_scalar(NIST P-256 order, not the point group).
ring_switch_factor<Factor> reduces a share when Factor divides the
modulus. One switch into an lcm yields every factor by local reduction.
The dealer material is one lt key on that limb, Θ(n λ) bits for
limb width n ≤ 64 and seed width λ, plus two residue shares of r. After η is
public, each party does one point evaluation (Θ(n) expands) and a
constant amount of arithmetic in the destination group.
ring_switch_factor is local.
This is the exact neighbour of truncated Barrett nmod.
grotto::nmod(x_raw, x_bits, recip_raw, recip_bits, residue_bits) splits
x / M when recip_raw / 2^recip_bits is a positive approximation of 1/M.
The result is an nmod_result: quotient is floor(x/M), and residue
is the fractional part truncated onto residue_bits.
nmod_pow2(x_raw, x_bits, exp, residue_bits) is the same split when the
modulus is a power of two. Both are one product by a reciprocal of at
most 128 bits, so time and extra memory are constant in the word size.
\code{cpp} auto split = grotto::nmod_pow2(raw, 16, 0, 16); \endcode
Defined in\n @ref grotto/nmod.hpp
See also [representation shift and twisted jets](@ref repr_and_twist).
Offset Horner
\htmlonly
make_offset_horner_keys<Input, Degree>(center) keys one gt whose
payload is center^m for m = 0 .. Degree. Degree is at most 3
(offset_horner_max_degree). Pass dpf::verifiable{} for proof tokens.
After eta opens, offset_horner_eval<Party, Degree> returns that party's
share of the cubic at the wrapped point. Coefficients are one
std::array<uint64_t, Degree + 1> per knot, low degree first.
\code{cpp} const std::uint8_t center = 12; auto mat = grotto::make_offset_horner_keys<std::uint8_t, 2>(center); std::vectorstd::uint8_t knots{0}; std::vector<std::array<std::uint64_t, 3>> coeff{{4, 2, 1}}; auto s0 = grotto::offset_horner_eval<0, 2>(mat, knots, coeff, eta); \endcode
geneval_offset_horner runs the same cubic from Jack Doerner and abhi shelat shares of
x and of the center, on a dpf::ds_randomness tape.
Degree is at most 3. The seed spine is one comparison, Θ(n λ) bits,
and the value words hold the four powers. Evaluation after η opens is
one sequence-shaped walk on the knots plus O(1) arithmetic. The
geneval form generates that same comparison once, opens one correction
word per level, as in [geneval](@ref tour_ds), and does not store a
reusable key.
Defined in\n @ref grotto/offset_horner.hpp
Offset polynomial
\htmlonly
make_offset_poly_keys(center, degree) is offset Horner at a runtime
degree, at most 16 (offset_poly_max_degree). One incremental gt
whose payload is the vector of powers. offset_poly_eval<Party> dots the shifted powers.
offset_poly_clear is the same polynomial in the clear.
offset_poly_kappas is the public carry of each piece.
Shared coefficients use offset_poly_shift_share (the binomial map is
linear) and offset_poly_beaver_share for the dot.
Degree d is at most 16: one key, Θ(n λ) bits of seed spine plus
value words that grow with d. The clear and public-coefficient evals
are one sequence-shaped walk on the K knots, then O(d^2) arithmetic. A shared-coefficient dot is one Beaver
inner product: one opening round of the masked vectors, communication
linear in the flattened length (pieces times d+1 coefficients), and
one product share per coefficient in preprocessing. The shift of each
party's coefficient share is local.
\code{cpp} auto mat = grotto::make_offset_poly_keys(std::uint8_t{12}, 4); std::vectorstd::uint8_t knots{0}; std::vector<std::vectorstd::uint64_t> coeff{{4, 2, 1, 0, 0}}; auto s0 = grotto::offset_poly_eval<0>(mat, knots, coeff, eta); auto opened = s0 + grotto::offset_poly_eval<1>(mat, knots, coeff, eta); \endcode
Defined in\n @ref grotto/offset_poly.hpp
Carry
\htmlonly
A carry_request names the source width n, the shift s, the output
width out_n, a carry_mode (truncate_reduce, same_ring, extend,
window), and a sign_knowledge (unknown, nonnegative, negative).
plan_carry returns a carry_recipe whose flags are the steps that are
still live. plan_carry_in(n, s), plan_carry_out(n, s, sign), and
plan_carry_fused(n, s, out_n, sign) fill the common requests.
make_carry_keys(recipe) (and make_carry_in_keys, make_carry_out_keys,
make_carry_fused_keys) builds the dealer keys. finalize_carry_in_blinds
adjusts a truncate-reduce split. Online, eval_carry_in(keys, party, opened)
returns a carry_eval_share whose value is that party's share.
opened is (x0 + x1 + rin) mod 2^n. The other online entry points are
eval_carry_out_known, eval_carry_out_unknown, eval_carry_extend,
eval_carry_window, and eval_carry_fused.
Cleartext twins, for tests and for a public limb, are eval_carry_clear,
carry_in_clear, carry_out_clear, carry_asr, and carry_mask.
plan_carry is a constant-time inspection of the request. Each live
comparison flag becomes one DPF key whose domain is the limb width w
of that comparison, Θ(w λ) bits, and the online step is one point
walk of that key. A share-MSB AND adds one Beaver bit triple in
preprocessing and one opening round of a bit.
\code{cpp} auto keys = grotto::make_carry_in_keys(32, 8); auto share = grotto::eval_carry_in(keys, /party/ 0, opened); auto clear = grotto::eval_carry_clear(keys.recipe, x0, x1); \endcode
Defined in\n @ref grotto/carry_plan.hpp, @ref grotto/carry.hpp
Prefix parity
\htmlonly
prefix_parities(key, endpoints) walks a key to the sorted endpoints and
returns XOR shares of the prefix parities, plus the index of the first
endpoint on the wrap. segment_parities turns those into one share per
segment. all_segment_parities_from_prefix_parities is the same conversion
when you already hold the prefix array.
signed_prefix_parities(key, endpoints) needs a comparison channel
(assigned, if the payload was a wildcard). It returns one additive
uint64_t share per endpoint: for dpf::gt(1) that share opens to 1 when
the secret point is below the endpoint. signed_prefix_parities_into
writes a runtime-length buffer.
The prefix walk follows Storrier, Vadapalli, Lyons, and Henry, ePrint
2023/108: one key's prefix parity in place of a comparison per piece.
On m endpoints the walk resumes one path memoizer (Θ(n) nodes, n
the key depth). Expands are the nodes on those paths, O(m n) in the
worst case, and less when endpoints share a prefix or the zero-suffix
stop hits. signed_prefix_parities adds an O(n) sum of
value-correction words on each endpoint. Both calls are local.
segment_parities is the prefix walk plus an O(m) XOR of those bits.
\code{cpp} std::array<std::uint8_t, 2> ends{10, 40}; auto [bits, first] = grotto::prefix_parities(k0, ends); auto segs = grotto::segment_parities(k0, ends); auto signs = grotto::signed_prefix_parities(cmp0, ends); \endcode
Defined in\n @ref grotto/prefix_parity.hpp
Several LUTs, one comparison
\htmlonly
make_lut_union_plan(luts, eta) shifts every piecewise LUT by the public
eta, inserts the same domain-minimum and carry cuts as
[offset polynomial](@ref offset_poly), and sorts the union. A piece of one
LUT is a span of those union knots: [begin, end), or
[begin, end-of-union) ∪ [0, end) when the piece wraps. The span stores
that piece's binomial shift by its public kappa. Spans of one LUT
partition the union.
The interactive plan is one comparison, whatever the number of LUTs and whatever the number of union knots:
plan.comparisonsandplan.prefix_walksare 1.plan.depthandplan.geneval_rounds()are the bitlength of the input.plan.degreeis the widest polynomial. The payload is1, center, …, center^degree.schedule_lut_unionrecords onefss_cmpof that depth. The slot islut_union_slot_bytes: one AES block, orlanes * 8when the power vector is wider. Prefix parity of the union is local after that comparison.
lut_union_eval<Party> reads one make_offset_poly_keys key of degree
at least plan.degree and returns a share per LUT. geneval_lut_union
opens that comparison from XOR shares of the center, as in
geneval_offset_horner. Pass dpf::arith_input when the shares add to
the center in the input group.
piecewise_from_easy and piecewise_from_constant adapt the cleartext
tables. An easy_lut denominator other than 1 is a rounding division, so
piecewise_from_easy rejects it. Powers that are zero on every piece are
dropped, and the shared payload stays only as wide as the widest remaining
degree.
\code{cpp} grotto::piecewise_lutstd::uint8_t low{{0, 10}, {{1, 0}, {0, 2}}}; grotto::piecewise_lutstd::uint8_t high{{0, 4, 12}, {{3, 0}, {1, 1}, {9, 4}}}; const std::uint8_t center = 12; const std::uint8_t eta = 3; auto plan = grotto::make_lut_union_plan({low, high}, eta); auto mat = grotto::make_offset_poly_keysstd::uint8_t(center, plan.degree); auto s0 = grotto::lut_union_eval<0>(mat, plan); auto s1 = grotto::lut_union_eval<1>(mat, plan); dpf::protocol::composer composer(0); grotto::schedule_lut_union(composer, plan); // rounds == plan.depth \endcode
Code samples\n
- lut_union.cpp \include{cpp} grotto/lut_union.cpp
Defined in\n @ref grotto/lut_union.hpp
Cleartext maps
These functions take a raw fixed-point word (n << fractional_bits) and
return a raw word. They do not build a DPF. The type
grotto::fixedpoint itself is a domain and an output; see
[Input types](@ref input_types) and [Output types](@ref output_types).
Fixed-point product
\htmlonly
fixed_mul<IntegerBits, FractionalBits>(lhs, rhs) multiplies two
fixedpoint values and keeps that many integer bits (including the sign)
and fraction bits. Bits below the fraction are floored. The product type
is the result_type of fixed_mul_plan. The plan uses at most 8
limbs and refuses a wider window, so the product is a constant amount
of 64-bit arithmetic and O(1) extra memory.
\code{cpp} using q16 = grotto::fixedpoint<16, std::int32_t>; auto prod = grotto::fixed_mul<16, 16>(q16{1.5}, q16{2.0}); \endcode
Defined in\n @ref grotto/fixedpoint_mul.hpp
Lookup tables
\htmlonly
Constant, easy, principal, range, and window tables are included from
grotto.hpp. The dyadic table comes in through exact_steps.hpp, which
grotto.hpp also includes.
- Constant.
make_exact_constant_lut<Raw>(exact_constant::signum, fractional_bits)and the otherexact_constantnames (positive,negative,nonneg,nonpos,zero,nonzero,ilogb,ceil_ilogb,ilog10,clz,clrsb).make_threshold_lut,make_interval_lut, andmake_clipped_quotient_lutbuild aconstant_lut<Raw>you call astable(raw). - Easy. Few-piece polynomials with integer knots:
make_abs_lut,make_relu_lut,make_clip_lut,make_hardsigmoid_lut,make_hardswish_lut,make_leaky_relu_hundredth_lut, and the othermake_*_lutfactories in [easy_lut.hpp](@ref grotto/easy_lut.hpp). The result is aneasy_lut<Raw>.make_leaky_relu_lut(shift)is the dyadic slope1/2^shift. The Appendix D leaky ReLU is slope1/100. - Dyadic. Exact steps on powers of two:
make_signum_lut,make_msb_lut(index),make_ilogb_lut,make_ilog10_lut,make_clz_lut,make_clrsb_lut, and the sign predicatesmake_positive_lutthroughmake_nonzero_lut.ilog_of_zerois the sentinel for a zero argument.msb_bit_limitis 8. - Range.
eval_reduced(reduced::ln, fractional_bits, raw)and the otherreducednames (lg,log10,exp,exp2,exp10,sin,cos,tan,cot,sec,csc, the hyperbolics,sqrt,inv,rsqrt,invsq,expm1,log1p).split_positiveis the dyadic mantissa split those reductions use. - Window.
eval_window(window::gelu, fractional_bits, raw). Thewindownames coversmoothstep,sigmoid,tanh,erf,erfc,softplus,gelu,silu,asin,acos,probit,hardelish,lecun_tanh,one_minus_sigmoid, and the rest of the enum in [window_lut.hpp](@ref grotto/window_lut.hpp). - Principal.
eval_principal(principal::sin, fractional_bits, raw)on the closed principal interval. Precisions are 8, 12, …, 32 (principal_precision). Names:ln,exp,sin,tanf,tang,sinh,cosh,sqrt,coth,sec,gsec,csch,inv,rsqrt,invsq. - Wavelet. Haar and bior(5,3) compressed tables: [Wavelet lookup tables](@ref dwt_luts).
\code{cpp} auto sign = grotto::make_exact_constant_lutstd::int32_t( grotto::exact_constant::signum, 0); auto s = sign(std::int32_t{-3}); auto relu = grotto::make_relu_lutstd::int32_t(8); auto ln = grotto::eval_reduced(grotto::reduced::ln, 16, raw); auto gelu = grotto::eval_window(grotto:🪟:gelu, 16, raw); auto sine = grotto::eval_principal(grotto::principal::sin, 16, raw); \endcode
The degree-0 exact tables follow Storrier, Vadapalli, Lyons, and Henry,
[ePrint 2023/108](@ref bib_grotto), Appendix D.
Sign predicates are a constant number of cuts, Θ(1). clz and
ilogb cut once per bit of the raw width, Θ(w). ilog10 binary-searches
the raw domain once per decimal exponent, Θ(w²) probes. make_msb_lut
emits Θ(2^index) cuts and rejects index at or above 8.
make_clipped_quotient_lut is linear in (high-low)/modulus, capped at
2^16 pieces. Calling a constant or easy table binary-searches its P
pieces, O(log P). eval_principal and eval_window binary-search the
static knots and then run one cubic. eval_reduced adds a short series
on the small interval (expm1 24 terms, log1p 80). No DPF and no
communication.
Appendix D maps that were still cleartext
Appendix D of [ePrint 2023/108](@ref bib_grotto) lists HardELiSH, LeCun tanh, and leaky
ReLU with slope 1/100. Those three now have fixed-point evaluators.
one_minus_sigmoid is the sigmoid table complemented, which rounds out
the logistic pair.
The cubics were built on mocha2. Sollya chose the longest pieces whose
absolute error stays within half an ulp. Mathematica (Remez), Maple
(numapprox[minimax]), and MATLAB/Chebfun (minimax) fitted each
piece, and the shipped polynomial is the one with the lowest error
after the coefficients are rounded to k+16 fraction bits. Of the 443
cubics, Sollya won 229, Maple 83, MATLAB 74, and Mathematica 57.
Half an ulp at 16 fraction bits is 2^{-17} ≈ 7.63e-6. Appendix D's
own columns are tighter (4.2e-8) and therefore use more pieces
(HardELiSH 38, LeCun tanh 89). The counts below are this library's
half-ulp partitions.
| map | degree | pieces at k = 8, 12, 16, 20, 24, 28, 32 | evaluation |
|---|---|---|---|
window::hardelish |
3 on (-1, 0); exact quadratic on [0, 1] |
1, 2, 3, 6, 12, 23, 45 | Θ(log P) knot search and one cubic on (-1, 0). Elsewhere Θ(1): 0, round(x(x+1)/2), or x |
window::lecun_tanh |
3 | 3, 6, 11, 22, 44, 88, 177 | Θ(log P) on the positive knots of the absolute value, then a sign. Past the last knot the value is the constant ±round(1.7159 · 2^k) |
window::one_minus_sigmoid |
3 | same as sigmoid: 8, 16, 64, 128, 256, 1024, 2048 |
one sigmoid evaluation and one subtraction. Θ(log P) |
make_leaky_relu_hundredth_lut |
1 | 2 | Θ(1). Identity on the right, round(x/100) on the left. Error at most half a unit in the last place |
make_leaky_relu_lut(shift) is still the dyadic slope 1/2^shift.
The hundredth factory is the Appendix D slope and does not depend on
the fractional width.
grotto::polynomials::eval_horner evaluates a poly_constant,
poly_linear, poly_quadratic, or poly_cubic (a std::array of
coefficients, constant term first). piecewise_eval(polys, bounds, x)
picks the piece and calls that Horner step.
Defined in\n @ref grotto/constant_lut.hpp, @ref grotto/easy_lut.hpp, @ref grotto/dyadic_lut.hpp, @ref grotto/range_lut.hpp, @ref grotto/window_lut.hpp, @ref grotto/principal_lut.hpp, @ref grotto/piecewise.hpp
Wavelet lookup tables
\htmlonly
make_haar_dwt_lut and make_bior53_dwt_lut compress a real signal of
length \f$2^n\f$ and evaluate it as a fixed-point word. The construction
is the cleartext Haar and bior(5,3) lookup of Reis, Ugurbil, Wagh, Henry,
and de Vega, [ePrint 2025/013](@ref bib_wave), Equations (7) and (8).
sample_dwt_signal(domain_bits, fractional_bits, f) writes the grid
\f$i \cdot 2^{-f}\f$ for \f$i \in [0, 2^n)\f$.
Both builders run the depth-\f$j\f$ low-pass with the smooth edge
extension used for that paper's accuracy tables. PyWavelets calls these
filters haar and bior2.2; bior(5,3) is the same pair, named there by
vanishing moments. Building either table is \f$\Theta(N)\f$ arithmetic
and extra memory, \f$N = 2^n\f$.
Haar then multiplies the approximation coefficients by \f$2^{-j/2}\f$
and rounds down to \f$f\f$ fraction bits. On this grid that coefficient
is the mean of each block of \f$2^j\f$ samples. Evaluation reads
coeff[raw >> j], one indexing step, \f$\Theta(1)\f$.
bior(5,3) multiplies by \f$2^{j/2}\f$ and rounds down the same way.
Smooth extension prepends two coefficients, so the bin msb = raw >> j
lives at index msb + 2, and the next tap at msb + 3, wrapping in the
stored vector. With lsb = raw mod 2^j,
\f[ y = \bigl\lfloor\bigl(c_{\mathrm{msb}+2},(2^j - \mathrm{lsb})
- c_{\mathrm{msb}+3},\mathrm{lsb}\bigr) / 2^{2j}\bigr\rfloor. \f]
That is Equation (8): the Lemma 6 weights \f$(2^j - \mathrm{lsb}_j)\f$ and \f$\mathrm{lsb}_j\f$, in integer arithmetic. Two multiplications and a shift, \f$\Theta(1)\f$.
The paper's online protocols look these tables up under a DPF. Haar is
paired there with a deterministic Pika truncation; bior(5,3) is paired
with segment parity. Those protocols are not a key type here. The value
they open is table(raw).
\code{cpp} auto samples = grotto::sample_dwt_signal(6, 4, [](double x) { return 1.0 / (1.0 + std::exp(-(x - 2.0))); }); auto haar = grotto::make_haar_dwt_lut(samples, 4, 2); auto bior = grotto::make_bior53_dwt_lut(samples, 4, 2); auto h = haar(std::uint64_t{32}); auto b = bior(std::uint64_t{33}); \endcode
Code samples\n
- dwt_lut.cpp \include{cpp} grotto/dwt_lut.cpp
Defined in\n @ref grotto/dwt_lut.hpp
Closed form
eval_closed(closed::atanh, fractional_bits, raw) composes
eval_reduced and eval_window. The closed names are the inverse
hyperbolics and inverse trig functions, selu, elu, celu,
softsign, tanhshrink, the logistic / exponential / laplace /
cauchy quantiles, sinc, and the extra powers cbrt, qtrt,
icbrt, iqtrt, pow_m01, pow_p15, pow_m3. Precision is one of
8, 12, …, 32. Each call runs a constant number of eval_reduced or
eval_window evaluations.
\code{cpp} auto y = grotto::eval_closed(grotto::closed::atan, 16, raw); \endcode
Defined in\n @ref grotto/closed_form.hpp
Exact steps
Counts and booleans come back as fixed-point integers,
n << fractional_bits.
\code{cpp} auto floor_x = grotto::eval_dec_floor(raw, 16); auto digits = grotto::eval_dec_width(raw, 16); auto bits = grotto::eval_bit_widthstd::int32_t(raw, 16); \endcode
Decimal digit counts divide in a loop, so the time follows the number
of digits. eval_bit_width, eval_bit_floor, eval_bit_ceil,
eval_countl_one, and eval_has_single_bit scan the raw width,
Θ(width) bit operations and at most 64 shifts. eval_logstar is five
magnitude comparisons, Θ(1). eval_deg2rad and eval_rad2deg are one
scale each.
Also eval_dec_ceil, eval_oct_width, eval_b64_width,
eval_value_length (base 8, 10, or 64), eval_has_single_digit,
eval_bit_floor, eval_bit_ceil, eval_countl_one, eval_has_single_bit,
eval_deg2rad, and eval_rad2deg. make_exact_step_lut builds the
matching easy_lut.
Defined in\n @ref grotto/exact_steps.hpp
Gadget functors
grotto/gadgets.hpp still includes the decimal and exponential reference
headers. The functors that those headers used to provide are deprecated:
call eval_reduced, eval_window, make_*_lut, or exact_constant
instead. gadget_hints<T> holds the old domain, degree, and pole notes
for a functor type.
Defined in\n @ref grotto/gadgets.hpp, @ref grotto/gadget_hints.hpp
\htmlonly