Skip to main content

vyre_libs/scan/
mod.rs

1//! Byte and text scan helpers  -  substring search, DFA / Aho–Corasick. Used
2//! as components inside full `vyre::Program` values (decode, graph, heuristics).
3//!
4//! Sub-dialects:
5//! - `substring`  -  brute-force single-string scanner
6//! - `dfa`  -  DFA compiler + Aho-Corasick multi-string scanner
7//!
8//! Flat re-exports preserved for back-compat.
9//!
10//! # API index
11//!
12//! Every public surface in this module is enumerated in `API_INDEX`
13//! as a stable `(name, kind, feature)` triple. Consumers that need to
14//! discover the engine surface programmatically  -  consumer engine listings,
15//! the conformance harness's coverage check, the cargo-doc completeness test
16//! below  -  read this single const instead
17//! of grepping the module tree.
18
19/// Stable index of public exports under `vyre_libs::scan`. Each
20/// entry is a `(symbol, kind, feature_gate)` triple. `feature_gate`
21/// is `None` for unconditional exports and `Some("flag-name")` for
22/// items behind a Cargo feature.
23///
24/// Keep this in sync with the `pub use` lines below. The
25/// `api_index_covers_every_export` test in `tests/api_index.rs`
26/// verifies that every name in `API_INDEX` resolves to a real
27/// import path so a refactor that removes or renames a public symbol
28/// fails CI loudly instead of silently leaving the index stale.
29pub const API_INDEX: &[(&str, ApiKind, Option<&str>)] = &[
30    // Unconditional dispatch primitives.
31    ("byte_scan_dispatch_config", ApiKind::Function, None),
32    ("candidate_start_dispatch_config", ApiKind::Function, None),
33    ("haystack_len_u32", ApiKind::Function, None),
34    ("pack_haystack_u32", ApiKind::Function, None),
35    ("pack_u32_slice", ApiKind::Function, None),
36    ("scan_guard", ApiKind::Function, None),
37    ("u32_words_as_le_bytes", ApiKind::Function, None),
38    ("unpack_match_triples", ApiKind::Function, None),
39    ("DEFAULT_MAX_SCAN_BYTES", ApiKind::Const, None),
40    // Engine traits + helpers.
41    ("MatchScan", ApiKind::Trait, None),
42    ("MatchEngineCache", ApiKind::Trait, None),
43    ("ScanResult", ApiKind::Struct, None),
44    ("cached_load_or_compile", ApiKind::Function, None),
45    ("engine_cache_path", ApiKind::Function, None),
46    // Hit-buffer helpers.
47    ("compact_hits", ApiKind::Function, None),
48    ("compact_hits_with_layout", ApiKind::Function, None),
49    ("emit_hit", ApiKind::Function, None),
50    ("emit_hit_then_compact", ApiKind::Function, None),
51    ("emit_hit_then_compact_with_layout", ApiKind::Function, None),
52    ("emit_hit_with_layout", ApiKind::Function, None),
53    ("HIT_BUFFER_LIVE_LENGTH", ApiKind::Const, None),
54    ("HIT_BUFFER_OVERFLOW_COUNT", ApiKind::Const, None),
55    // Literal-set engine  -  unconditional.
56    ("GpuLiteralSet", ApiKind::Struct, None),
57    ("LiteralMatch", ApiKind::TypeAlias, None),
58    ("LiteralSetPreparedCount", ApiKind::Struct, None),
59    ("LiteralSetPreparedPresenceByRegion", ApiKind::Struct, None),
60    ("LiteralSetPreparedScan", ApiKind::Struct, None),
61    ("LiteralSetScanScratch", ApiKind::Struct, None),
62    ("LiteralSetWireError", ApiKind::Enum, None),
63    ("PendingFusedRegion", ApiKind::Struct, None),
64    ("PendingMatches", ApiKind::Struct, None),
65    ("PendingPresence", ApiKind::Struct, None),
66    ("PendingPresenceByRegion", ApiKind::Struct, None),
67    ("ScanAllTimed", ApiKind::Struct, None),
68    ("ResidentLiteralScan", ApiKind::Struct, None),
69    ("ResidentFusedRegionScan", ApiKind::Struct, None),
70    ("ResidentPresencePipeline", ApiKind::Struct, None),
71    ("scan_paged_fused", ApiKind::Function, None),
72    ("scan_paged_fused_timed", ApiKind::Function, None),
73    ("scan_paged_fused_async", ApiKind::Function, None),
74    ("scan_sharded_fused", ApiKind::Function, None),
75    ("scan_sharded_fused_weighted", ApiKind::Function, None),
76    ("scan_sharded_fused_timed", ApiKind::Function, None),
77    ("scan_pattern_sharded", ApiKind::Function, None),
78    ("PatternShard", ApiKind::Struct, None),
79    ("ShardTiming", ApiKind::Struct, None),
80    ("ShardedScanTiming", ApiKind::Struct, None),
81    ("scan_paths_paged", ApiKind::Function, None),
82    ("scan_paths_paged_prefetched", ApiKind::Function, None),
83    ("PagedScanResult", ApiKind::Struct, None),
84    ("PagedScanTiming", ApiKind::Struct, None),
85    ("GlobalMatch", ApiKind::Struct, None),
86    ("LITERAL_SET_COUNT_RESOURCE_INDEX", ApiKind::Const, None),
87    (
88        "LITERAL_SET_PRESENCE_BY_REGION_OUTPUT_RESOURCE_INDEX",
89        ApiKind::Const,
90        None,
91    ),
92    (
93        "LITERAL_SET_COUNT_RESET_RESOURCE_INDICES",
94        ApiKind::Const,
95        None,
96    ),
97    (
98        "LITERAL_SET_COUNT_SCAN_RESOURCE_INDICES",
99        ApiKind::Const,
100        None,
101    ),
102    (
103        "LITERAL_SET_MATCH_COUNT_RESOURCE_INDEX",
104        ApiKind::Const,
105        None,
106    ),
107    ("LITERAL_SET_MATCHES_RESOURCE_INDEX", ApiKind::Const, None),
108    ("LITERAL_SET_RESET_RESOURCE_INDICES", ApiKind::Const, None),
109    ("LITERAL_SET_SCAN_RESOURCE_INDICES", ApiKind::Const, None),
110    // Cross-program fusion (re-exported from vyre-foundation).
111    ("fuse_programs", ApiKind::Function, None),
112    ("fuse_programs_vec", ApiKind::Function, None),
113    ("FusionError", ApiKind::Enum, None),
114    // matching-substring.
115    (
116        "substring_search",
117        ApiKind::Function,
118        Some("matching-substring"),
119    ),
120    // matching-dfa.
121    ("aho_corasick", ApiKind::Function, Some("matching-dfa")),
122    ("dfa_compile", ApiKind::Function, Some("matching-dfa")),
123    (
124        "dfa_compile_with_budget",
125        ApiKind::Function,
126        Some("matching-dfa"),
127    ),
128    ("CompiledDfa", ApiKind::Struct, Some("matching-dfa")),
129    ("DfaCompileError", ApiKind::Enum, Some("matching-dfa")),
130    (
131        "DEFAULT_DFA_BUDGET_BYTES",
132        ApiKind::Const,
133        Some("matching-dfa"),
134    ),
135    ("DirectGpuScanner", ApiKind::Struct, Some("matching-dfa")),
136    // matching-nfa.
137    (
138        "build_rule_pipeline",
139        ApiKind::Function,
140        Some("matching-nfa"),
141    ),
142    ("PipelineWireError", ApiKind::Enum, Some("matching-nfa")),
143    ("RulePipeline", ApiKind::Struct, Some("matching-nfa")),
144    (
145        "ResidentRulePipeline",
146        ApiKind::Struct,
147        Some("matching-nfa"),
148    ),
149    // matching-regex.
150    (
151        "build_rule_pipeline_from_regex",
152        ApiKind::Function,
153        Some("matching-regex"),
154    ),
155    (
156        "compile_regex_set",
157        ApiKind::Function,
158        Some("matching-regex"),
159    ),
160    ("CompiledRegexSet", ApiKind::Struct, Some("matching-regex")),
161    ("RegexCompileError", ApiKind::Enum, Some("matching-regex")),
162    ("RegexConstruct", ApiKind::Enum, Some("matching-regex")),
163    (
164        "regex_construct_diagnostic_code",
165        ApiKind::Function,
166        Some("matching-regex"),
167    ),
168    ("CaptureMode", ApiKind::Enum, Some("matching-regex")),
169    (
170        "CaptureModeContract",
171        ApiKind::Struct,
172        Some("matching-regex"),
173    ),
174    // regex-set → dense DFA → existing AC kernel composition.
175    // Gated on both matching-regex (for compile_regex_set) and
176    // matching-dfa (for build_ac_bounded_ranges_program). The single
177    // entry is reported under matching-regex so the existing index
178    // tooling that filters by one feature still finds it.
179    (
180        "build_regex_dfa_pipeline",
181        ApiKind::Function,
182        Some("matching-regex"),
183    ),
184    (
185        "build_regex_dfa_unanchored",
186        ApiKind::Function,
187        Some("matching-regex"),
188    ),
189    (
190        "build_regex_dfa_shards",
191        ApiKind::Function,
192        Some("matching-regex"),
193    ),
194    (
195        "build_regex_dfa_shards_unanchored",
196        ApiKind::Function,
197        Some("matching-regex"),
198    ),
199    ("RegexDfaPipeline", ApiKind::Struct, Some("matching-regex")),
200    ("RegexDfaShard", ApiKind::Struct, Some("matching-regex")),
201    ("RegexDfaError", ApiKind::Enum, Some("matching-regex")),
202    (
203        "AnchoredWindowValidator",
204        ApiKind::Struct,
205        Some("matching-regex"),
206    ),
207    (
208        "anchored_window_extract_program",
209        ApiKind::Function,
210        Some("matching-regex"),
211    ),
212    (
213        "ANCHORED_WINDOW_MATCH_COUNT_BINDING",
214        ApiKind::Const,
215        Some("matching-regex"),
216    ),
217    (
218        "ANCHORED_WINDOW_MATCHES_BINDING",
219        ApiKind::Const,
220        Some("matching-regex"),
221    ),
222    (
223        "regex_admission_by_region_program",
224        ApiKind::Function,
225        Some("matching-regex"),
226    ),
227    (
228        "regex_admission_by_region_reference",
229        ApiKind::Function,
230        Some("matching-regex"),
231    ),
232    (
233        "regex_admission_presence_words",
234        ApiKind::Function,
235        Some("matching-regex"),
236    ),
237    ("region_of", ApiKind::Function, Some("matching-regex")),
238    (
239        "fused_region_evidence_program",
240        ApiKind::Function,
241        Some("matching-regex"),
242    ),
243    (
244        "fused_region_evidence_reference",
245        ApiKind::Function,
246        Some("matching-regex"),
247    ),
248    (
249        "FusedRegionEvidence",
250        ApiKind::Struct,
251        Some("matching-regex"),
252    ),
253    (
254        "FUSED_EVIDENCE_PRESENCE_BINDING",
255        ApiKind::Const,
256        Some("matching-regex"),
257    ),
258    (
259        "FUSED_EVIDENCE_MATCHES_BINDING",
260        ApiKind::Const,
261        Some("matching-regex"),
262    ),
263    (
264        "RegionEvidencePipeline",
265        ApiKind::Struct,
266        Some("matching-regex"),
267    ),
268    ("RegionEvidenceError", ApiKind::Enum, Some("matching-regex")),
269];
270
271/// Item-kind tag for entries in `API_INDEX`. Coarse on purpose  -
272/// the goal is "what's the symbol shape?" not full reflection.
273#[derive(Copy, Clone, Debug, PartialEq, Eq)]
274pub enum ApiKind {
275    /// Free function or method exported at module root.
276    Function,
277    /// `pub struct` or unit struct.
278    Struct,
279    /// `pub enum`.
280    Enum,
281    /// `pub trait`.
282    Trait,
283    /// `pub const`.
284    Const,
285    /// `pub type` alias.
286    TypeAlias,
287}
288
289pub mod builders;
290pub mod hit_buffer;
291
292/// Shared GPU dispatch primitives for matching engines.
293///
294/// Centralises haystack-packing, length validation, dispatch geometry,
295/// and match-triple unpacking so every new matcher (literal-set,
296/// regex pipeline, future taint scan) reuses the same byte-level
297/// plumbing instead of re-implementing it.
298pub mod dispatch_io;
299
300/// Common scan + cache traits for every matcher in this crate.
301///
302/// Engines implement `MatchScan` (object-safe) and `MatchEngineCache`
303/// (typed errors). Consumers use `cached_load_or_compile` to wire on-
304/// disk caches generically  -  the per-engine cache wiring scan consumer
305/// previously hand-rolled is now a one-line call.
306pub mod engine;
307pub use dispatch_io::{
308    byte_scan_dispatch_config, candidate_start_dispatch_config, haystack_len_u32,
309    pack_haystack_u32, pack_u32_slice, scan_guard, u32_words_as_le_bytes, unpack_match_triples,
310    DEFAULT_MAX_SCAN_BYTES,
311};
312pub use engine::{
313    cache_path as engine_cache_path, cached_load_or_compile, MatchEngineCache, MatchScan,
314    ScanResult,
315};
316
317#[cfg(feature = "matching-substring")]
318pub mod substring;
319
320#[cfg(feature = "matching-dfa")]
321pub mod dfa;
322
323/// Classic Aho-Corasick with precomputed flat `output_links`.
324/// Scans in O(matches) per position, not O(states × n).
325#[cfg(feature = "matching-dfa")]
326pub mod classic_ac;
327
328/// Subgroup-cooperative NFA scan helper (G1). Composes
329/// `vyre_primitives::nfa::subgroup_nfa::nfa_step` into a multi-byte /
330/// multi-pattern scan. Feature-gated behind `matching-nfa` so consumers
331/// opt in when they need NFAs up to 1024 states with subgroup-shuffle
332/// epsilon closure.
333#[cfg(feature = "matching-nfa")]
334pub mod nfa;
335
336pub mod literal_set;
337
338/// W2-4 paged-corpus scanning: scan a corpus larger than one resident window as a
339/// sequence of resident-window dispatches with stable global region ids and u64
340/// global positions, identical to a single-shot scan of the concatenated corpus.
341/// Public entry [`paged_corpus::scan_paged_fused`]; the window planner is a private
342/// helper.
343pub mod paged_corpus;
344
345/// Match post-processing: dedup, entropy, and confidence in one reference pass.
346pub mod post_process;
347
348/// Generic engine + post-processor pipeline. Pairs any `MatchScan`
349/// implementer with the canonical post-processing contract.
350pub mod pipeline;
351
352/// Canonical literal/regex/haystack fixture corpus shared by every
353/// integration test in this crate. Public when the consumer opts into
354/// `feature = "test-fixtures"`; always available inside the in-tree
355/// test compilation.
356#[cfg(any(test, feature = "test-fixtures"))]
357pub mod test_fixtures;
358
359#[cfg(feature = "matching-dfa")]
360pub mod direct_gpu;
361
362/// Mega-scan integrator (G-stack). The single authoritative entry
363/// point that produces one `RulePipeline` object program-analysis
364/// consumers dispatch. Currently only G1 (subgroup-cooperative NFA
365/// scan) is wired end-to-end; G2-G10 (rule fusion, decode-scan
366/// handoff, speculative commit, persistent-engine work items,
367/// content-hash cache key, adaptive CSR/dense traversal, CHD perfect
368/// hash, differential scan file selection) are planned composition
369/// hooks that land here as they are implemented.
370#[cfg(feature = "matching-nfa")]
371pub mod mega_scan;
372
373/// Resident-buffer dispatch session for [`mega_scan::RulePipeline`]. Uploads the
374/// immutable NFA transition/epsilon tables into backend-resident resources once,
375/// so repeated scans transfer only the haystack instead of re-uploading the
376/// multi-MiB tables on every dispatch (the borrowed `RulePipeline::scan` cost).
377#[cfg(feature = "matching-nfa")]
378pub mod resident;
379
380/// Resident-buffer dispatch session for [`literal_set::GpuLiteralSet`]
381/// region-presence scans. Uploads the immutable DFA + suffix-prefilter tables into
382/// backend-resident resources once, so repeated coalesced-batch presence scans
383/// transfer only the per-file haystack and a presence-prefix reset instead of
384/// re-uploading the multi-MiB tables on every dispatch (the borrowed
385/// `GpuLiteralSet::scan_presence_by_region` cost).
386pub mod resident_presence;
387
388/// Regex AST → NfaPlan frontend. Lowers a regex string into the same
389/// `(NfaPlan, transition_table, epsilon_table)` triple that
390/// [`nfa::compile`] produces for literals, so every downstream component
391/// (`nfa_scan` Program, `mega_scan::build`, `RulePipeline`) runs
392/// unmodified. Behind `matching-regex` so consumers without the regex
393/// frontend skip the `regex-syntax` dep.
394#[cfg(feature = "matching-regex")]
395pub mod regex_compile;
396
397/// Regex set → dense `CompiledDfa` GPU pipeline. Composes
398/// `compile_regex_set` (NFA build) → `nfa_to_dfa` (subset construction,
399/// vyre-primitives) → `build_ac_bounded_ranges_program` (existing AC
400/// kernel) so regex pattern sets dispatch through the same O(1)-per-byte
401/// kernel that literal AC uses. Behind `matching-regex` + `matching-dfa`
402/// because both halves are required.
403#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
404pub mod regex_dfa;
405
406/// Anchored-window regex extraction (plan W2-3, line 179): validate candidate
407/// origins from the positions pass against an anchored [`regex_dfa`] DFA,
408/// turning literal-prefilter *admission* into full-match *extraction*
409/// (confirm + locate). CPU primitive + parity oracle for the GPU kernel.
410#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
411pub mod regex_anchored_window;
412
413/// Regex-DFA per-region admission (plan W2-2, line 153's third evidence family):
414/// a per-region presence bitmap of which regex patterns start a match in each
415/// region, the regex counterpart of literal presence-by-region. Shipped as a
416/// SEPARATE occupancy-cheap pass because the "single fused launch" is
417/// measured-refuted (see the module docs / BACKLOG).
418#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
419pub mod regex_region_admission;
420
421/// Fused single-launch phase-1 evidence (plan W2-2, line 153): ONE dispatch, ONE
422/// anchored DFA walk per byte, producing all three families a coalesced-batch consumer otherwise
423/// assembles from three dispatches, per-region presence, position triples for a
424/// designated subset, and per-region admission bits. A correctness-equivalent
425/// primitive; the fast path stays the separate specialized passes because kernel
426/// fusion is measured-refuted on this substrate (see the module docs / BACKLOG).
427#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
428pub mod fused_region_evidence;
429
430/// Region-evidence pipeline (plan W2-2, line 158): the successor to the vestigial
431/// [`mega_scan::RulePipeline`]. One type, one call, returning the full phase-1
432/// evidence bundle (presence + positions + admission), a fast two-dispatch path
433/// ([`region_evidence_pipeline::RegionEvidencePipeline::scan`]) and the
434/// single-launch capability ([`region_evidence_pipeline::RegionEvidencePipeline::scan_fused`]).
435#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
436pub mod region_evidence_pipeline;
437
438#[cfg(feature = "matching-dfa")]
439pub use dfa::{
440    aho_corasick, dfa_compile, dfa_compile_with_budget, CompiledDfa, DfaCompileError,
441    DEFAULT_DFA_BUDGET_BYTES,
442};
443#[cfg(feature = "matching-dfa")]
444pub use direct_gpu::DirectGpuScanner;
445#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
446pub use fused_region_evidence::{
447    fused_region_evidence_program, fused_region_evidence_reference, FusedRegionEvidence,
448    FUSED_EVIDENCE_ADMISSION_BINDING, FUSED_EVIDENCE_MATCHES_BINDING,
449    FUSED_EVIDENCE_MATCH_COUNT_BINDING, FUSED_EVIDENCE_PRESENCE_BINDING,
450};
451pub use hit_buffer::{
452    compact_hits, compact_hits_with_layout, emit_hit, emit_hit_then_compact,
453    emit_hit_then_compact_with_layout, emit_hit_with_layout, HIT_BUFFER_LIVE_LENGTH,
454    HIT_BUFFER_OVERFLOW_COUNT,
455};
456pub use literal_set::{
457    GpuLiteralSet, LiteralSetPreparedCount, LiteralSetPreparedPresenceByRegion,
458    LiteralSetPreparedScan, LiteralSetScanScratch, LiteralSetWireError, Match as LiteralMatch,
459    PendingFusedRegion, PendingMatches, PendingPresence, PendingPresenceByRegion,
460    ResidentFusedRegionScan, ResidentLiteralScan, ScanAllTimed,
461    LITERAL_SET_COUNT_RESET_RESOURCE_INDICES, LITERAL_SET_COUNT_RESOURCE_INDEX,
462    LITERAL_SET_COUNT_SCAN_RESOURCE_INDICES, LITERAL_SET_MATCHES_RESOURCE_INDEX,
463    LITERAL_SET_MATCH_COUNT_RESOURCE_INDEX, LITERAL_SET_PRESENCE_BY_REGION_OUTPUT_RESOURCE_INDEX,
464    LITERAL_SET_RESET_RESOURCE_INDICES, LITERAL_SET_SCAN_RESOURCE_INDICES,
465};
466#[cfg(feature = "matching-nfa")]
467pub use mega_scan::{build as build_rule_pipeline, PipelineWireError, RulePipeline};
468pub use paged_corpus::{
469    scan_paged_fused, scan_paged_fused_async, scan_paged_fused_timed, scan_paths_paged,
470    scan_paths_paged_prefetched, scan_pattern_sharded, scan_sharded_fused,
471    scan_sharded_fused_timed, scan_sharded_fused_weighted, GlobalMatch, PagedScanResult,
472    PagedScanTiming, PatternShard, ShardTiming, ShardedScanTiming,
473};
474pub use pipeline::{Pipeline, PostProcessFn};
475#[cfg(any(test, feature = "cpu-parity"))]
476pub use post_process::{
477    reference_post_process, shannon_entropy_bits_per_byte, try_reference_post_process,
478    try_reference_post_process_into,
479};
480pub use post_process::{PostProcessError, PostProcessedMatch};
481#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
482pub use regex_anchored_window::{
483    anchored_window_extract_program, AnchoredWindowValidator, ANCHORED_WINDOW_MATCHES_BINDING,
484    ANCHORED_WINDOW_MATCH_COUNT_BINDING,
485};
486#[cfg(feature = "matching-regex")]
487pub use regex_compile::{
488    build_rule_pipeline_from_regex, compile_regex_set, regex_construct_diagnostic_code,
489    CaptureMode, CaptureModeContract, CompiledRegexSet, RegexCompileError, RegexConstruct,
490};
491#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
492pub use regex_dfa::{
493    build_regex_dfa_pipeline, build_regex_dfa_shards, build_regex_dfa_shards_unanchored,
494    build_regex_dfa_unanchored, RegexDfaError, RegexDfaPipeline, RegexDfaShard,
495};
496#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
497pub use regex_region_admission::{
498    regex_admission_by_region_program, regex_admission_by_region_reference,
499    regex_admission_presence_words, region_of,
500};
501#[cfg(all(feature = "matching-regex", feature = "matching-dfa"))]
502pub use region_evidence_pipeline::{RegionEvidenceError, RegionEvidencePipeline};
503#[cfg(feature = "matching-nfa")]
504pub use resident::ResidentRulePipeline;
505pub use resident_presence::ResidentPresencePipeline;
506#[cfg(feature = "matching-substring")]
507pub use substring::{substring_search, SCAN_SUBSTRING_OP_ID};
508// Re-export the cross-program fusion API at the matching layer so consumers
509// don't have to reach into `vyre-foundation` directly.
510pub use vyre_foundation::execution_plan::fusion::{fuse_programs, fuse_programs_vec, FusionError};
511
512#[cfg(feature = "cpu-parity")]
513use vyre_primitives::matching::region::dedup_regions_cpu as primitive_dedup_regions_cpu;
514#[cfg(any(test, feature = "cpu-parity"))]
515pub use vyre_primitives::matching::region::dedup_regions_inplace;
516/// Re-export the region-dedup GPU program builders through the scan layer
517/// so consumers get the canonical span-coalescing helpers without taking a
518/// separate dependency on `vyre-primitives`.
519pub use vyre_primitives::matching::region::{dedup_regions_flag_program, RegionTriple};
520
521/// Reference/parity region deduplication helper.
522///
523/// Production scan APIs avoid CPU-named symbols; this helper is explicitly a
524/// reference contract for tests, examples, and conformance comparisons.
525#[cfg(feature = "cpu-parity")]
526#[must_use]
527pub fn dedup_regions_reference(input: Vec<RegionTriple>) -> Vec<RegionTriple> {
528    primitive_dedup_regions_cpu(input)
529}