2026-09-24 20:44:07 -06:00
/// @file dpf/blocked_dcf.hpp
/// @brief Blocked-checkpoint comparison: one ring word per block of levels.
/// @details The seed spine stays dense. Ring words are published only at a
/// public checkpoint schedule. Point eval expands parked siblings
/// up to the next checkpoint; a full-domain memoizer already holds
/// those nodes. `q` tail bits, when the comparison sets the key
/// depth, are a residual table on the node at height `h`.
2026-09-28 05:59:19 -06:00
/// @note Boyle, Chandran, Gilboa, Gupta, Ishai, Kumar, and Rathee (EUROCRYPT 2021, ePrint 2020/1392) publish a value-correction word on every level. This implementation is ahead of that DCF on payload size: one ring word every B levels, about B times fewer value words, with the same one-seed-word spine. Point evaluation expands the siblings between checkpoints.
2026-09-24 20:44:07 -06:00
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
# ifndef LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__
# define LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__
# include <array>
# include <cstddef>
# include <cstdint>
# include <type_traits>
# include <utility>
# include <vector>
# include "hedley/hedley.h"
# include "dpf/dcf.hpp"
# include "dpf/path_memoizer.hpp"
2026-09-24 23:18:10 -06:00
# include "dpf/tree_traits.hpp"
2026-09-24 20:44:07 -06:00
# include "dpf/twiddle.hpp"
# include "dpf/utils.hpp"
2026-09-28 05:59:19 -06:00
# include "dpf/verifiable.hpp"
2026-09-24 20:44:07 -06:00
namespace dpf
{
namespace detail
{
namespace blocked
{
2026-09-28 05:59:19 -06:00
/// @brief High bit set on `fold_node` level so blocked proofs diverge from native.
/// @details Must match the level passed to `make_cs` at blocked keygen. Parked
/// sibling folds reuse this tag with the sibling's tree depth so they
/// share `correction_seeds[depth-1]`.
inline constexpr std : : size_t fold_spine_tag = std : : size_t { 1 } < < 15 ;
template < typename NodeT >
HEDLEY_ALWAYS_INLINE
void fold_spine_node ( proof_token & pi , std : : size_t level ,
psnip_uint64_t x_bits , NodeT seed , const cs_block & cs ) noexcept
{
detail : : vdpf : : fold_node ( pi , fold_spine_tag | level , x_bits , seed , cs ) ;
}
2026-09-24 20:44:07 -06:00
template < std : : size_t H , std : : size_t B >
struct schedule
{
static constexpr std : : size_t count =
( B = = 0 | | H = = 0 ) ? 0 : ( H + B - 1 ) / B ;
static constexpr auto depths = [ ] {
std : : array < std : : size_t , count = = 0 ? 1 : count > cs { } ;
if constexpr ( count = = 0 )
return cs ;
const std : : size_t base = H / count ;
const std : : size_t extra = H % count ;
std : : size_t acc = 0 ;
for ( std : : size_t i = 0 ; i < count ; + + i )
{
acc + = base + ( i < extra ? 1 : 0 ) ;
cs [ i ] = acc ;
}
return cs ;
} ( ) ;
HEDLEY_CONST
HEDLEY_NO_THROW
static constexpr bool contains ( std : : size_t depth ) noexcept
{
for ( std : : size_t i = 0 ; i < count ; + + i )
{
if ( depths [ i ] = = depth )
return true ;
}
return false ;
}
HEDLEY_CONST
HEDLEY_NO_THROW
static constexpr std : : size_t index ( std : : size_t depth ) noexcept
{
for ( std : : size_t i = 0 ; i < count ; + + i )
{
if ( depths [ i ] = = depth )
return i ;
}
return static_cast < std : : size_t > ( - 1 ) ;
}
} ;
template < typename PRG , typename Node >
HEDLEY_NO_THROW
2026-09-24 23:18:10 -06:00
HEDLEY_ALWAYS_INLINE
2026-09-24 20:44:07 -06:00
uint64_t rho_of ( const Node & node , uint64_t mask ) noexcept
{
2026-09-24 23:18:10 -06:00
auto kids = dpf : : tree_traits < PRG > : : expand_value ( node ) ;
2026-09-24 20:44:07 -06:00
return dcf_impl : : convert_node ( kids [ 0 ] , mask ) ;
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr int control_sign ( uint8_t t0 , uint8_t t1 ) noexcept
{
return static_cast < int > ( t0 ) - static_cast < int > ( t1 ) ;
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t mul_sgn ( int sgn , uint64_t v , uint64_t mask ) noexcept
{
if ( sgn > 0 )
return v & mask ;
if ( sgn < 0 )
return dcf_impl : : neg_m ( v , mask ) ;
return 0 ;
}
2026-09-24 23:18:10 -06:00
/// @brief Group element `sgn` (`+1`, `-1`, or `0`) used as an `assign_cmp` coefficient.
/// @param sgn the `sgn`
/// @param mask the bit mask
/// @return Group element `sgn` (`+1`, `-1`, or `0`) used as an `assign_cmp` coefficient
2026-09-24 20:44:07 -06:00
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t sgn_coeff ( int sgn , uint64_t mask ) noexcept
{
if ( sgn > 0 )
return 1ULL & mask ;
if ( sgn < 0 )
return dcf_impl : : neg_m ( 1ULL , mask ) ;
return 0 ;
}
template < typename PRG , typename Node >
HEDLEY_NO_THROW
uint64_t checkpoint_word ( const Node & n0 , const Node & n1 , uint64_t beta ,
uint64_t mask ) noexcept
{
const int sgn = control_sign (
static_cast < uint8_t > ( dpf : : get_lo_bit ( n0 ) ) ,
static_cast < uint8_t > ( dpf : : get_lo_bit ( n1 ) ) ) ;
const uint64_t r0 = rho_of < PRG > ( n0 , mask ) ;
const uint64_t r1 = rho_of < PRG > ( n1 , mask ) ;
const uint64_t inner =
( beta + dcf_impl : : neg_m ( r0 , mask ) + r1 ) & mask ;
return mul_sgn ( sgn , inner , mask ) ;
}
template < typename Node >
2026-09-24 23:18:10 -06:00
HEDLEY_PURE
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
uint64_t checkpoint_coeff ( const Node & n0 , const Node & n1 ,
uint64_t mask ) noexcept
{
return sgn_coeff ( control_sign (
static_cast < uint8_t > ( dpf : : get_lo_bit ( n0 ) ) ,
static_cast < uint8_t > ( dpf : : get_lo_bit ( n1 ) ) ) , mask ) ;
}
template < typename PRG , typename Node >
HEDLEY_NON_NULL ( 4 )
HEDLEY_NO_THROW
void suffix_masks ( const Node & seed , std : : size_t q , uint64_t mask ,
uint64_t * out ) noexcept
{
Node cur [ 4 ] { } ;
Node nxt [ 8 ] { } ;
cur [ 0 ] = seed ;
std : : size_t n = 1 ;
for ( std : : size_t lvl = 0 ; lvl < q ; + + lvl )
{
std : : size_t m = 0 ;
for ( std : : size_t i = 0 ; i < n ; + + i )
{
2026-09-24 23:18:10 -06:00
auto kids = dpf : : tree_traits < PRG > : : expand_value ( cur [ i ] ) ;
2026-09-24 20:44:07 -06:00
nxt [ m + + ] = kids [ 0 ] ;
nxt [ m + + ] = kids [ 1 ] ;
}
for ( std : : size_t i = 0 ; i < m ; + + i )
cur [ i ] = nxt [ i ] ;
n = m ;
}
for ( std : : size_t i = 0 ; i < n ; + + i )
out [ i ] = dcf_impl : : convert_node ( cur [ i ] , mask ) ;
}
template < typename PRG , typename Node >
HEDLEY_NON_NULL ( 8 )
HEDLEY_NO_THROW
void tail_words ( const Node & n0 , const Node & n1 , uint64_t beta , uint64_t mask ,
bool include_eq , uint64_t suffix , std : : size_t q , uint64_t * words ,
uint64_t * coeffs ) noexcept
{
uint64_t u0 [ 4 ] { } ;
uint64_t u1 [ 4 ] { } ;
suffix_masks < PRG > ( n0 , q , mask , u0 ) ;
suffix_masks < PRG > ( n1 , q , mask , u1 ) ;
const int sgn = control_sign (
static_cast < uint8_t > ( dpf : : get_lo_bit ( n0 ) ) ,
static_cast < uint8_t > ( dpf : : get_lo_bit ( n1 ) ) ) ;
const uint64_t coeff = sgn_coeff ( sgn , mask ) ;
const std : : size_t n = std : : size_t { 1 } < < q ;
for ( std : : size_t z = 0 ; z < n ; + + z )
{
const bool pred = include_eq
? ( z < = suffix )
: ( z < suffix ) ;
const uint64_t inner =
( ( pred ? beta : 0ULL ) + dcf_impl : : neg_m ( u0 [ z ] , mask ) + u1 [ z ] ) & mask ;
words [ z ] = mul_sgn ( sgn , inner , mask ) ;
if ( coeffs ! = nullptr )
coeffs [ z ] = pred ? coeff : 0ULL ;
}
}
template < typename KeyT >
HEDLEY_NO_THROW
uint64_t add_membership ( uint64_t acc , const typename KeyT : : interior_node & node ,
uint64_t word , uint64_t mask , int party ) noexcept
{
using prg = typename KeyT : : interior_prg ;
const uint8_t t = static_cast < uint8_t > ( dpf : : get_lo_bit ( node ) ) ;
const uint64_t y =
( rho_of < prg > ( node , mask ) + ( t ? word : 0ULL ) ) & mask ;
return ( acc + ( party ? dcf_impl : : neg_m ( y , mask ) : y ) ) & mask ;
}
template < typename KeyT >
uint64_t add_frontier ( uint64_t acc , const typename KeyT : : interior_node & seed ,
std : : size_t from_depth , std : : size_t to_depth , const KeyT & dpf , uint64_t word ,
uint64_t mask , int party )
{
using node = typename KeyT : : interior_node ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
2026-09-24 20:44:07 -06:00
std : : vector < node > cur ;
std : : vector < node > nxt ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic pop )
2026-09-24 20:44:07 -06:00
cur . push_back ( seed ) ;
for ( std : : size_t lvl = from_depth ; lvl < to_depth ; + + lvl )
{
nxt . clear ( ) ;
nxt . reserve ( cur . size ( ) * 2 ) ;
const node cw0 = dpf . correction_word ( lvl , false ) ;
const node cw1 = dpf . correction_word ( lvl , true ) ;
2026-09-24 23:18:10 -06:00
const bool is_last = KeyT : : tree : : is_last_level ( lvl , KeyT : : depth ) ;
2026-09-24 20:44:07 -06:00
for ( const node & fs : cur )
{
2026-09-24 23:18:10 -06:00
auto kids = KeyT : : traverse_interior01 ( fs , cw0 , cw1 , is_last ) ;
2026-09-24 20:44:07 -06:00
nxt . push_back ( kids [ 0 ] ) ;
nxt . push_back ( kids [ 1 ] ) ;
}
cur . swap ( nxt ) ;
}
for ( const node & fs : cur )
acc = add_membership < KeyT > ( acc , fs , word , mask , party ) ;
return acc ;
}
template < typename KeyT , typename InputT >
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t query_suffix ( InputT tx , std : : size_t nbits , std : : size_t q ) noexcept
{
if ( q = = 0 )
return 0 ;
uint64_t z = 0 ;
auto bit_mask = KeyT : : msb_mask > > ( nbits - q ) ;
for ( std : : size_t j = 0 ; j < q ; + + j , bit_mask > > = 1 )
z = ( z < < 1 ) | static_cast < uint64_t > ( ! ! ( bit_mask & tx ) ) ;
return z ;
}
template < typename KeyT >
HEDLEY_NO_THROW
uint64_t finish_share ( const KeyT & dpf , uint64_t suffix , uint64_t acc ,
const typename KeyT : : interior_node & at_h , int party ) noexcept
{
using namespace dcf_impl ;
using prg = typename KeyT : : interior_prg ;
const auto & ch = dpf . cmp ( ) ;
const uint64_t mask = ch . mask ;
constexpr std : : size_t q = KeyT : : cmp_q ;
constexpr std : : size_t h = KeyT : : cmp_h ;
if constexpr ( q = = 0 )
{
if ( ch . include_eq )
{
constexpr auto wi = schedule < h , KeyT : : cmp_block > : : index ( h ) ;
acc = add_membership < KeyT > ( acc , at_h , dpf . value_cw ( wi ) , mask , party ) ;
}
( void ) suffix ;
}
else
{
const uint64_t z = suffix ;
uint64_t u [ 4 ] { } ;
suffix_masks < prg > ( at_h , q , mask , u ) ;
const uint8_t t = static_cast < uint8_t > ( dpf : : get_lo_bit ( at_h ) ) ;
const uint64_t y = ( u [ z ] + ( t ? dpf . tail_cw ( z ) : 0ULL ) ) & mask ;
acc = ( acc + ( party ? neg_m ( y , mask ) : y ) ) & mask ;
}
if ( ch . eval_as_ge )
acc = neg_m ( acc , mask ) ;
const uint64_t add = [ & ] ( ) - > uint64_t {
if constexpr ( is_party_key_v < KeyT > )
return dpf . cmp_addend ( ) . raw ( ) ;
else
return dpf . cmp_addend ( ) ;
} ( ) ;
return ( acc + add ) & mask ;
}
template < typename KeyT , typename InputT , typename PathMemoizer >
2026-09-28 05:59:19 -06:00
uint64_t eval_share ( const KeyT & dpf , InputT tx , PathMemoizer & path ,
proof_token * pi = nullptr )
2026-09-24 20:44:07 -06:00
{
using node = typename KeyT : : interior_node ;
const auto & ch = dpf . cmp ( ) ;
const uint64_t mask = ch . mask ;
const uint64_t add = [ & ] ( ) - > uint64_t {
if constexpr ( is_party_key_v < KeyT > )
return dpf . cmp_addend ( ) . raw ( ) ;
else
return dpf . cmp_addend ( ) ;
} ( ) ;
if ( ch . trivial = = cmp_trivial : : always_true
| | ch . trivial = = cmp_trivial : : always_false )
return add & mask ;
constexpr std : : size_t h = KeyT : : cmp_h ;
using sched = schedule < h , KeyT : : cmp_block > ;
const std : : size_t nbits = static_cast < std : : size_t > ( ch . nbits ) ;
const int party = dpf : : get_lo_bit ( dpf . root ( ) ) ? 1 : 0 ;
2026-09-28 05:59:19 -06:00
// Expand the seed spine without native path folds; blocked proofs use
// domain-separated tags on newly filled levels only (path-memo safe).
const auto resume = dpf : : detail : : path_resume_for_level ( path , dpf , tx , h ) ;
2026-09-24 20:44:07 -06:00
dpf : : detail : : ensure_level ( dpf , tx , path , h ) ;
2026-09-28 05:59:19 -06:00
if constexpr ( KeyT : : is_verifiable )
{
if ( pi ! = nullptr & & resume < = h )
{
constexpr auto input_bits =
utils : : bitlength_of_v < typename KeyT : : input_type > ;
for ( std : : size_t level = resume ; level < = h ; + + level )
{
const auto x_bits = static_cast < psnip_uint64_t > (
utils : : to_integral_type < typename KeyT : : input_type > { } ( tx )
> > ( input_bits - level ) ) ;
fold_spine_node ( * pi , level - 1 , x_bits , path [ level ] ,
dpf . correction_seeds ( ) [ level - 1 ] ) ;
}
}
}
2026-09-24 20:44:07 -06:00
struct parked
{
node seed ;
std : : size_t depth ;
2026-09-28 05:59:19 -06:00
psnip_uint64_t prefix_bits ;
2026-09-24 20:44:07 -06:00
} ;
parked pend [ 128 ] ;
std : : size_t npend = 0 ;
uint64_t acc = 0 ;
auto bit_mask = KeyT : : msb_mask ;
2026-09-28 05:59:19 -06:00
// Value accumulation always walks from the root. Proof folds for parked
// siblings must not repeat depths already authenticated on a warm path:
// re-folding XORs the same contribution away (path-memo cancel bug).
2026-09-24 20:44:07 -06:00
for ( std : : size_t level = 0 ; level < h ; + + level , bit_mask > > = 1 )
{
const bool xi = ! ! ( bit_mask & tx ) ;
const node & parent = path [ level ] ;
const node right = KeyT : : traverse_interior ( parent ,
2026-09-24 23:18:10 -06:00
dpf . correction_word ( level , true ) , true ,
KeyT : : tree : : is_last_level ( level , KeyT : : depth ) ) ;
2026-09-24 20:44:07 -06:00
if ( ! xi )
{
pend [ npend ] . seed = right ;
pend [ npend ] . depth = level + 1 ;
2026-09-28 05:59:19 -06:00
// Sibling is the right child of `parent`: path bits with low bit 1.
const auto path_bits = static_cast < psnip_uint64_t > (
utils : : to_integral_type < typename KeyT : : input_type > { } ( tx )
> > ( utils : : bitlength_of_v < typename KeyT : : input_type >
- ( level + 1 ) ) ) ;
pend [ npend ] . prefix_bits = ( path_bits & ~ static_cast < psnip_uint64_t > ( 1 ) )
| static_cast < psnip_uint64_t > ( 1 ) ;
2026-09-24 20:44:07 -06:00
+ + npend ;
}
const std : : size_t c = level + 1 ;
if ( sched : : contains ( c ) )
{
const uint64_t word = dpf . value_cw ( sched : : index ( c ) ) ;
for ( std : : size_t p = 0 ; p < npend ; + + p )
{
2026-09-28 05:59:19 -06:00
if constexpr ( KeyT : : is_verifiable )
{
if ( pi ! = nullptr & & pend [ p ] . depth > = resume )
{
// Tree depth (not checkpoint index): must match CS level.
fold_spine_node ( * pi , pend [ p ] . depth - 1 , pend [ p ] . prefix_bits ,
pend [ p ] . seed , dpf . correction_seeds ( ) [ pend [ p ] . depth - 1 ] ) ;
}
}
2026-09-24 20:44:07 -06:00
acc = add_frontier < KeyT > ( acc , pend [ p ] . seed , pend [ p ] . depth , c ,
dpf , word , mask , party ) ;
}
npend = 0 ;
}
}
const uint64_t suffix = query_suffix < KeyT > ( tx , nbits , KeyT : : cmp_q ) ;
return finish_share ( dpf , suffix , acc , path [ h ] , party ) ;
}
template < typename KeyT , typename Integral , typename Memo >
2026-09-24 23:18:10 -06:00
HEDLEY_PURE
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
bool memo_has ( const Memo & memo , Integral prefix , std : : size_t depth ,
Integral from_lane , Integral to_excl ) noexcept
{
const auto shift = KeyT : : cmp_depth - depth ;
const auto from_p = from_lane > > shift ;
if ( prefix < from_p )
return false ;
const auto idx = static_cast < std : : size_t > ( prefix - from_p ) ;
const auto count = memo . get_nodes_at_level ( depth , from_lane , to_excl ) ;
return idx < count ;
}
template < typename KeyT , typename Integral , typename Memo >
const typename KeyT : : interior_node & memo_node ( const Memo & memo , Integral prefix ,
std : : size_t depth , Integral from_lane )
{
const auto shift = KeyT : : cmp_depth - depth ;
const auto from_p = from_lane > > shift ;
const auto idx = static_cast < std : : size_t > ( prefix - from_p ) ;
return memo [ depth ] [ idx ] ;
}
2026-09-28 05:59:19 -06:00
/// @brief Evaluate one lane from an interval memo; optionally fold a VDPF proof.
/// @details When `pi == nullptr` or the key is not verifiable, matches the
/// historical body (no folds). When set, folds path / covered
/// checkpoint / frontier seeds with `fold_spine_node`, skipping depths
/// already covered by `path` (same resume rule as `eval_share`).
/// Interval BFS prove must keep passing a null `pi` here.
template < typename KeyT , typename Integral , typename Memo ,
typename PathMemoizer = : : dpf : : basic_path_memoizer < KeyT > >
2026-09-24 20:44:07 -06:00
uint64_t eval_share_memo ( const KeyT & dpf , Integral lane ,
2026-09-28 05:59:19 -06:00
Integral from_lane , Integral to_excl , const Memo & memo ,
proof_token * pi = nullptr , PathMemoizer * path = nullptr )
2026-09-24 20:44:07 -06:00
{
using node = typename KeyT : : interior_node ;
const auto & ch = dpf . cmp ( ) ;
const uint64_t mask = ch . mask ;
const uint64_t add = [ & ] ( ) - > uint64_t {
if constexpr ( is_party_key_v < KeyT > )
return dpf . cmp_addend ( ) . raw ( ) ;
else
return dpf . cmp_addend ( ) ;
} ( ) ;
if ( ch . trivial = = cmp_trivial : : always_true
| | ch . trivial = = cmp_trivial : : always_false )
return add & mask ;
constexpr std : : size_t h = KeyT : : cmp_h ;
using sched = schedule < h , KeyT : : cmp_block > ;
const int party = dpf : : get_lo_bit ( dpf . root ( ) ) ? 1 : 0 ;
2026-09-28 05:59:19 -06:00
const std : : size_t nbits = static_cast < std : : size_t > ( ch . nbits ) ;
std : : size_t resume = 0 ;
if constexpr ( KeyT : : is_verifiable )
{
if ( pi ! = nullptr & & path ! = nullptr )
{
const auto tx = static_cast < typename KeyT : : input_type > ( lane ) ;
resume = dpf : : detail : : path_resume_for_level ( * path , dpf , tx , h ) ;
dpf : : detail : : path_note_filled_to ( * path , h ) ;
}
}
if constexpr ( KeyT : : is_verifiable )
{
if ( pi ! = nullptr & & resume < = h )
{
Integral pfx = 0 ;
for ( std : : size_t depth = 0 ; depth < = h ; + + depth )
{
if ( depth > = resume & & depth > = 1 )
{
fold_spine_node ( * pi , depth - 1 ,
static_cast < psnip_uint64_t > ( pfx ) ,
memo_node < KeyT > ( memo , pfx , depth , from_lane ) ,
dpf . correction_seeds ( ) [ depth - 1 ] ) ;
}
if ( depth < h )
{
const bool xi =
( ( lane > > ( nbits - 1 - depth ) ) & Integral { 1 } ) ! = 0 ;
pfx = static_cast < Integral > (
( pfx < < 1 ) | ( xi ? Integral { 1 } : Integral { 0 } ) ) ;
}
}
}
}
2026-09-24 20:44:07 -06:00
struct parked
{
node seed ;
Integral prefix ;
std : : size_t depth ;
} ;
parked pend [ 128 ] ;
std : : size_t npend = 0 ;
uint64_t acc = 0 ;
Integral path_pref = 0 ;
for ( std : : size_t level = 0 ; level < h ; + + level )
{
const bool xi = ( ( lane > > ( nbits - 1 - level ) ) & Integral { 1 } ) ! = 0 ;
const node & parent = memo_node < KeyT > ( memo , path_pref , level , from_lane ) ;
const Integral sib = static_cast < Integral > ( ( path_pref < < 1 ) | Integral { 1 } ) ;
if ( ! xi )
{
const node right = KeyT : : traverse_interior ( parent ,
2026-09-24 23:18:10 -06:00
dpf . correction_word ( level , true ) , true ,
KeyT : : tree : : is_last_level ( level , KeyT : : depth ) ) ;
2026-09-24 20:44:07 -06:00
pend [ npend ] . seed = right ;
pend [ npend ] . prefix = sib ;
pend [ npend ] . depth = level + 1 ;
+ + npend ;
}
2026-09-28 05:59:19 -06:00
path_pref = static_cast < Integral > (
( path_pref < < 1 ) | ( xi ? Integral { 1 } : Integral { 0 } ) ) ;
2026-09-24 20:44:07 -06:00
const std : : size_t c = level + 1 ;
if ( ! sched : : contains ( c ) )
continue ;
const uint64_t word = dpf . value_cw ( sched : : index ( c ) ) ;
for ( std : : size_t p = 0 ; p < npend ; + + p )
{
const std : : size_t extra = c - pend [ p ] . depth ;
const Integral leftmost =
static_cast < Integral > ( pend [ p ] . prefix < < extra ) ;
const Integral rightmost = static_cast < Integral > (
leftmost + static_cast < Integral > ( ( Integral { 1 } < < extra ) - 1 ) ) ;
const bool covered =
memo_has < KeyT > ( memo , leftmost , c , from_lane , to_excl )
& & memo_has < KeyT > ( memo , rightmost , c , from_lane , to_excl ) ;
if ( covered & & extra < 16 )
{
const Integral nleaf = static_cast < Integral > ( Integral { 1 } < < extra ) ;
for ( Integral k = 0 ; k < nleaf ; + + k )
{
const auto pref = static_cast < Integral > ( leftmost + k ) ;
2026-09-28 05:59:19 -06:00
if constexpr ( KeyT : : is_verifiable )
{
if ( pi ! = nullptr & & c > = resume & & c > = 1 )
{
fold_spine_node ( * pi , c - 1 ,
static_cast < psnip_uint64_t > ( pref ) ,
memo_node < KeyT > ( memo , pref , c , from_lane ) ,
dpf . correction_seeds ( ) [ c - 1 ] ) ;
}
}
2026-09-24 20:44:07 -06:00
acc = add_membership < KeyT > ( acc ,
memo_node < KeyT > ( memo , pref , c , from_lane ) , word , mask ,
party ) ;
}
}
else
{
2026-09-28 05:59:19 -06:00
if constexpr ( KeyT : : is_verifiable )
{
if ( pi ! = nullptr & & pend [ p ] . depth > = resume
& & pend [ p ] . depth > = 1 )
{
fold_spine_node ( * pi , pend [ p ] . depth - 1 ,
static_cast < psnip_uint64_t > ( pend [ p ] . prefix ) ,
pend [ p ] . seed ,
dpf . correction_seeds ( ) [ pend [ p ] . depth - 1 ] ) ;
}
}
2026-09-24 20:44:07 -06:00
acc = add_frontier < KeyT > ( acc , pend [ p ] . seed , pend [ p ] . depth , c ,
dpf , word , mask , party ) ;
}
}
npend = 0 ;
}
const node & at_h = memo_node < KeyT > ( memo , path_pref , h , from_lane ) ;
const uint64_t suffix = static_cast < uint64_t > ( lane )
& ( KeyT : : cmp_q = = 0 ? 0ULL : ( ( 1ULL < < KeyT : : cmp_q ) - 1ULL ) ) ;
return finish_share ( dpf , suffix , acc , at_h , party ) ;
}
} // namespace blocked
} // namespace detail
} // namespace dpf
# endif // LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__