entropyfs 0.7.17

Entropy-native Linux filesystem: persist irreducible state, materialize structure, preserve exact bytes.
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
//! Phase-10B: the foreground representation policy — how much search CPU
//! a write-path chunk deserves RIGHT NOW.
//!
//! `OptimizeOptions` describes which representations EXIST (the ablation
//! semantics, unchanged). `ForegroundPolicy` decides which families are
//! worth evaluating for an incoming chunk in the write path, where
//! latency is the product. The two are deliberately separate: ablations
//! construct `OptimizeOptions` and run with `ForegroundPolicy::full()`;
//! the mounted filesystem carries a policy chosen by the court.
//!
//! The 10A millisecond map measured the motivation: on incompressible
//! data the full foreground search spends ~440 µs/chunk in the LZ/entropy
//! families before RAW wins (sequence_rans 481 ms + sequence_dict 120 ms
//! over the court workload; direct-store 64 MiB random 37.7 MiB/s full
//! vs 592.7 MiB/s raw-only). A cheap probe classifies each chunk first:
//!
//! - obvious ZERO/FILL: the zero/fill candidates decide immediately (they
//!   are already evaluated first and are ~free);
//! - HIGH entropy (incompressible): dedup (CAS) + ZERO/FILL + RAW — the
//!   rANS/LZ/configurational families are skipped, because they cannot
//!   beat RAW on data the probe already knows is random;
//! - LOW/uncertain: the full foreground search runs as before.
//!
//! False negatives are harmless: RAW is exact, and the background
//! optimizer (full search) can revisit any extent later — the
//! foreground-state/settled-state distinction is exactly what makes this
//! asymmetry safe.
//!
//! PURPOSE
//!     Decide, per incoming write-path chunk, how much search CPU the
//!     representation search deserves right now — the foreground half of
//!     the foreground/settled division of labor.
//!
//! BOUNDARY
//!     Decides CPU budget only. Which families EXIST is `OptimizeOptions`
//!     (`optimizer::policy`, the ablation authority); this module never
//!     defines correctness, never touches the store, and never commits.
//!     The background optimizer (`optimizer::background`) is the other
//!     half: it may revisit any extent later, which is what makes the
//!     aggressive skips here safe.
//!
//! MODEL
//!     Two gates compose per chunk: the policy (this module) decides
//!     whether the full candidate search may run, and the options decide
//!     which families exist. Cheap mode classifies each chunk with a
//!     deterministic entropy probe before spending the expensive
//!     LZ/entropy searches (see the three classes above).
//!
//! PERSISTENT AUTHORITY
//!     None. A skip here changes only which candidate is chosen in
//!     memory; RAW is exact, so no on-disk representation depends on the
//!     policy.
//!
//! CORRECTNESS INVARIANTS
//!     - the probe is deterministic (fixed stride), so the classification
//!       is reproducible across runs;
//!     - false negatives are harmless: a chunk misclassified LOW costs
//!       CPU, never bytes; a chunk misclassified HIGH falls back to RAW,
//!       which is exact, and the background optimizer revisits it later;
//!     - anti-aliasing: the probe takes the MINIMUM entropy over three
//!       consecutive strides, so periodic data never looks random (a
//!       period p > 1 cannot divide three consecutive integers);
//!     - chunks smaller than 256 bytes always run the full search (the
//!       probe is unreliable and the families are cheap).
//!
//! CONCURRENCY
//!     Per-chunk and single-threaded; no locks. The probe reads only the
//!     chunk buffer handed to it.
//!
//! DURABILITY
//!     None: nothing here persists.
//!
//! RESOURCE BOUNDS
//!     The probe reads at most `probe_bytes` (default 4096) bytes of the
//!     chunk over three strides, so classification cost is
//!     O(probe_bytes) regardless of chunk size. The families themselves
//!     are bounded by the policy mode plus `OptimizeOptions`.
//!
//! PERFORMANCE
//!     The 10A millisecond map measured the motivation (above). The
//!     sealed 10B court pair (evidence `8062f2d` / `d38f73f`) measured
//!     the outcome: mounted random 64 MiB writes 66.5 → 229.3 MiB/s
//!     (3.4×), compressed.tgz 42.0 → 66.3 MiB/s, daemon CPU 0.41× →
//!     0.26× (−37%), and — the decisive number — settled density
//!     UNCHANGED at 1.994×, because the background optimizer recovers
//!     everything the cheap foreground defers. Direct-store random
//!     writes 39.8 → 852 MiB/s (21×).
//!
//! FAILURE MODES
//!     No hard failures: the only failure is a misclassification, which
//!     is bounded on both sides (wasted CPU, or a densification deferred
//!     to the background pass). The min-over-strides probe is the
//!     conservative direction — families are skipped only when EVERY
//!     stride looks high-entropy.
//!
//! HISTORY / EVIDENCE
//!     Phase-10B introduced `ForegroundMode::Cheap` (evidence `8062f2d` /
//!     `d38f73f`); the anti-aliasing min-over-strides was found by the
//!     periodic fixture in `entropy_classification_is_deterministic_and_sane`;
//!     `ForegroundMode::RawOnly` is the raw-only control arm.
//!     Phase 12C-1 introduced `ForegroundMode::Focused` — the adaptive
//!     foreground budget (evidence `evidence/performance/adaptive-budget-probe-*/`,
//!     CHANGELOG v0.7.14). The 12C-1-0 frontier measured the adoption-wedge
//!     search CPU composition (rANS sweep ~67% of the `search` row) and
//!     proved the foreground search is density-OPTIONAL on the wedge
//!     corpora (the background optimizer recovers the full footprint to
//!     0.00–0.62% regression). Focused therefore defers the rANS families
//!     to the background when the semantic class prior says they rarely
//!     win, and keeps them when the class says they win (self-calibrating:
//!     the gate engages only for classes with enough observations whose
//!     winner distribution distrusts rANS).
//!     Phase 12C-1-2 added the PRESSURE dimension to `Focused` (evidence
//!     `evidence/performance/pressure-deferral-probe-*/`, CHANGELOG
//!     v0.7.16): the class prior answers "is rANS valuable for this
//!     class?"; the pressure gate answers "is NOW the right time to pay
//!     for it?". When the store's worker pool is saturated
//!     (`pressure_enter` reached, hysteresis by `pressure_leave`), the
//!     rANS sweep is DEFERRED to the background optimizer even for
//!     rANS-valuable classes, and the deferral is accounted as explicit
//!     optimization debt (bounded by `pressure_max_deferred_bytes` — the
//!     starvation invariant). Valuable + idle runs rANS now; valuable +
//!     pressured persists the cheap exact representation + enqueues debt;
//!     the background pays the debt.

#![forbid(unsafe_code)]

use crate::optimizer::policy::OptimizeOptions;

/// How much CPU the foreground search may spend on one chunk.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ForegroundMode {
    /// Evaluate every family the configuration admits (the pre-10B
    /// behavior; the ablation default).
    Full,
    /// Probe first: high-entropy chunks skip the LZ/entropy families and
    /// go dedup + ZERO/FILL + RAW; structured/uncertain chunks get the
    /// full foreground search.
    Cheap,
    /// Phase 12C-1: the adaptive foreground budget — the entropy probe
    /// PLUS the semantic class prior. High-entropy chunks skip the
    /// LZ/entropy families (the Cheap rule); additionally, when the
    /// chunk's semantic class has enough observations and its winner
    /// distribution says the rANS families rarely win
    /// (`P(Rans) < focused_rans_skip_share`), the rANS sweep is deferred
    /// to the background optimizer — the 12C-1-0 frontier proved the
    /// background recovers the full footprint (0.00–0.62% regression on
    /// the adoption corpora), so the deferral is density-safe in the
    /// settled state. A class that wins with rANS keeps the full sweep
    /// (the gate is self-calibrating: it never starves a family that
    /// genuinely wins). The gate engages only after
    /// `focused_min_observations` observations, so cold classes always
    /// get the full search.
    Focused,
    /// Hash → CAS → ZERO/FILL → RAW only (the raw-only control; the
    /// background optimizer still densifies later).
    RawOnly,
}

/// The foreground representation policy.
///
/// Role: the CPU-budget authority for one write-path chunk. It composes
/// with `OptimizeOptions` (the family authority): the policy says whether
/// the full search may run, the options say which families exist.
///
/// Invariants: `mode` is one of the sealed modes (the 10B / 12C-1
/// comparison arms); `high_entropy_bits` is Shannon entropy in bits per
/// byte (8.0 = uniform byte alphabet; compressed data sits near it;
/// source/text is typically 4–6); `probe_bytes` is the probe sample size
/// in bytes. The two `focused_*` fields are the 12C-1 adaptive gate's
/// parameters (dormant in the other modes). A `Copy` value with no
/// interior state, safe to share across threads.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ForegroundPolicy {
    /// Search mode.
    pub mode: ForegroundMode,
    /// Entropy (bits per byte) at or above which a chunk is classified
    /// high-entropy and the LZ/entropy families are skipped (Cheap/Focused
    /// modes). Shannon entropy of a uniform byte alphabet is 8.0;
    /// compressed data sits near it. Source/text is typically 4–6.
    pub high_entropy_bits: f64,
    /// Probe sample size (bytes, deterministic stride over the chunk).
    pub probe_bytes: usize,
    /// Phase 12C-1 (Focused only): the class's rANS-winner share below
    /// which the rANS families are deferred to the background (0..1;
    /// 0.10 default — a class must win with rANS fewer than 10% of the
    /// time before its rANS sweep is judged waste). Dormant in the other
    /// modes.
    pub focused_rans_skip_share: f64,
    /// Phase 12C-1 (Focused only): minimum class observations before the
    /// rANS deferral may engage. A cold class (fewer observations) always
    /// gets the full search — the winner distribution is unreliable until
    /// the class has earned its confidence. 16 default.
    pub focused_min_observations: u64,
    /// Phase 12C-1-2 (Focused only): the pressure level at which the
    /// foreground enters the pressured state (idle → pressured; the
    /// hysteresis ENTER threshold). `2.0` disables the pressure gate
    /// (the plain 12C-1 `focused()` policy; the 12C-1-2 evidence picks
    /// the production threshold).
    pub pressure_enter: f64,
    /// Phase 12C-1-2 (Focused only): the pressure level below which the
    /// foreground leaves the pressured state (pressured → idle; the
    /// hysteresis LEAVE threshold). With `pressure_enter ==
    /// pressure_leave` the gate has no hysteresis band (the plain
    /// Pressure-25/50/75 matrix arms); a band (`enter 0.80 / leave
    /// 0.60`, the brief's example) prevents search/skip flapping when the
    /// pressure oscillates around the threshold.
    pub pressure_leave: f64,
    /// Phase 12C-1-2 (Focused only): the starvation bound — the
    /// cumulative deferred logical bytes at which the pressure gate stops
    /// deferring (the foreground pays the search again, bounding the
    /// optimization debt). `u64::MAX` = unbounded (the matrix arms; the
    /// starvation lane sweeps the cap). The brief's "continuous
    /// foreground pressure must not prevent deferred optimization
    /// forever" invariant: debt cannot grow past the cap, and the
    /// background optimizer pays whatever was deferred.
    pub pressure_max_deferred_bytes: u64,
    /// Phase 12C-1-2 (Focused only): whether the pressure gate ALSO
    /// defers the configurational families (SPARSE/PALETTE/PERIODIC/
    /// SPARSE_BLOCK64) alongside rANS. False = rANS-only deferral (the
    /// brief's named mechanism; ~67% of the search row — the 12C-1
    /// frontier's composition). True = the full "expensive
    /// representation-search work" deferral (rANS + configurational,
    /// ~80% of the search row), leaving the CHEAP exact families
    /// (dedup, ZERO/FILL, dictionaries, bases, RAW) in the foreground.
    /// The 12C-1-2 matrix measures both; the evidence picks the default.
    pub pressure_defer_configurational: bool,
}

impl Default for ForegroundPolicy {
    fn default() -> Self {
        Self {
            mode: ForegroundMode::Full,
            high_entropy_bits: 7.2,
            probe_bytes: 4096,
            focused_rans_skip_share: 0.10,
            focused_min_observations: 16,
            pressure_enter: 2.0,
            pressure_leave: 2.0,
            pressure_max_deferred_bytes: u64::MAX,
            pressure_defer_configurational: false,
        }
    }
}

impl ForegroundPolicy {
    /// The pre-10B policy: every family, always (ablation semantics).
    pub const fn full() -> Self {
        Self {
            mode: ForegroundMode::Full,
            high_entropy_bits: 7.2,
            probe_bytes: 4096,
            focused_rans_skip_share: 0.10,
            focused_min_observations: 16,
            pressure_enter: 2.0,
            pressure_leave: 2.0,
            pressure_max_deferred_bytes: u64::MAX,
            pressure_defer_configurational: false,
        }
    }

    /// The cheap policy (10B): probe + skip hopeless families.
    ///
    /// Evidence (Phase-10B, sealed `8062f2d` / `d38f73f`): the
    /// high-entropy probe skips the LZ/entropy families for incompressible
    /// chunks, and the background optimizer recovers everything the cheap
    /// foreground defers — direct-store random writes 39.8 → 852 MiB/s
    /// (21×), mounted random 64 MiB writes 66.5 → 229.3 MiB/s (3.4×),
    /// daemon CPU −37%, settled density unchanged at 1.994×.
    pub const fn cheap() -> Self {
        Self {
            mode: ForegroundMode::Cheap,
            high_entropy_bits: 7.2,
            probe_bytes: 4096,
            focused_rans_skip_share: 0.10,
            focused_min_observations: 16,
            pressure_enter: 2.0,
            pressure_leave: 2.0,
            pressure_max_deferred_bytes: u64::MAX,
            pressure_defer_configurational: false,
        }
    }

    /// Phase 12C-1: the adaptive foreground budget — the entropy probe
    /// plus the semantic class-prior rANS deferral (see
    /// [`ForegroundMode::Focused`] for the full semantics and the sealed
    /// 12C-1 evidence). The 12C-1-2 pressure gate is DISABLED here
    /// (`pressure_enter` 2.0 — never reached); the 12C-1-2 evidence picks
    /// the production pressure threshold, and the probe constructs the
    /// pressure variants directly (the fields are public policy
    /// parameters, not hidden state).
    pub const fn focused() -> Self {
        Self {
            mode: ForegroundMode::Focused,
            high_entropy_bits: 7.2,
            probe_bytes: 4096,
            focused_rans_skip_share: 0.10,
            focused_min_observations: 16,
            pressure_enter: 2.0,
            pressure_leave: 2.0,
            pressure_max_deferred_bytes: u64::MAX,
            pressure_defer_configurational: false,
        }
    }

    /// The raw-only control policy.
    pub const fn raw_only() -> Self {
        Self {
            mode: ForegroundMode::RawOnly,
            high_entropy_bits: 7.2,
            probe_bytes: 4096,
            focused_rans_skip_share: 0.10,
            focused_min_observations: 16,
            pressure_enter: 2.0,
            pressure_leave: 2.0,
            pressure_max_deferred_bytes: u64::MAX,
            pressure_defer_configurational: false,
        }
    }

    /// Whether the full candidate search may run for this chunk under
    /// this policy. The probe is deterministic (fixed stride), so the
    /// classification is reproducible across runs.
    pub fn allow_full_search(&self, chunk: &[u8]) -> bool {
        match self.mode {
            ForegroundMode::Full => true,
            ForegroundMode::RawOnly => false,
            ForegroundMode::Cheap | ForegroundMode::Focused => !high_entropy(chunk, self),
        }
    }

    /// Whether a family may even be evaluated (mode-level gate, kept
    /// separate so `OptimizeOptions` remains the family-authority).
    pub fn family_evaluations_allowed(&self) -> bool {
        self.mode != ForegroundMode::RawOnly
    }

    /// Phase 12C-1: whether the `Focused` gate defers the rANS families
    /// for a class with `observations` observations and a measured
    /// rANS-winner share of `rans_share`.
    ///
    /// Engages only when ALL of: the mode is `Focused`; the class has
    /// earned at least `focused_min_observations` observations (a cold
    /// class gets the full search — its winner distribution is not yet
    /// reliable); and the class wins with rANS less than
    /// `focused_rans_skip_share` of the time (the sweep is judged waste
    /// for this class; the background optimizer recovers any density the
    /// deferral costs — the 12C-1-0 frontier measured recovery to
    /// 0.00–0.62% on the adoption corpora).
    pub fn focused_skips_rans(&self, observations: u64, rans_share: f64) -> bool {
        self.mode == ForegroundMode::Focused
            && observations >= self.focused_min_observations
            && rans_share < self.focused_rans_skip_share
    }

    /// Phase 12C-1-2: the pressure-state transition (idle ↔ pressured
    /// with the hysteresis band). Pure — the store owns the state, this
    /// owns the policy's thresholds.
    ///
    /// ```text
    /// idle      -> pressured  when  P >= pressure_enter
    /// pressured -> idle       when  P <= pressure_leave
    /// ```
    ///
    /// With `enter == leave` the band is empty (the plain Pressure-25/50/75
    /// matrix arms: a single threshold). With a band (`enter 0.80 / leave
    /// 0.60` — the brief's example) a pressure oscillating around the
    /// threshold does not flap the gate across adjacent requests: the
    /// state enters once and stays until the pressure genuinely clears.
    pub fn pressure_transition(&self, pressured: bool, p: f64) -> bool {
        if pressured {
            p > self.pressure_leave
        } else {
            p >= self.pressure_enter
        }
    }
}

/// Deterministic sampled Shannon entropy (bits per byte) of a chunk.
/// Samples `probe_bytes` bytes on a fixed stride across the whole chunk
/// so both small files and 64 KiB chunks are classified from their full
/// extent, not just their head.
///
/// Anti-aliasing (Phase-10B, found by test): a fixed stride can alias
/// with the data's periodicity and misestimate entropy — a 256-period
/// pattern at stride 16 samples one residue class and looks uniformly
/// random. The probe therefore takes the MINIMUM entropy over three
/// consecutive strides: a period `p > 1` cannot divide three consecutive
/// integers, so at least one stride breaks the alias. The minimum is
/// also the conservative direction (only skip the families when EVERY
/// stride looks high-entropy; a false low-entropy verdict just costs CPU,
/// never correctness). Pinned by the periodic fixture in
/// `entropy_classification_is_deterministic_and_sane`: a 256-period
/// uniform pattern must stay below the high-entropy threshold so the
/// configurational (periodic) family still gets evaluated.
pub fn sampled_entropy(chunk: &[u8], probe_bytes: usize) -> f64 {
    if chunk.is_empty() {
        return 0.0;
    }
    let n = probe_bytes.min(chunk.len());
    let base_step = (chunk.len() / n.max(1)).max(1);
    let mut best = f64::INFINITY;
    for shift in 0..3usize {
        let step = base_step.saturating_add(shift).max(1);
        let mut hist = [0u32; 256];
        let mut counted = 0usize;
        let mut i = 0usize;
        while i < chunk.len() && counted < n {
            hist[chunk[i] as usize] += 1;
            counted += 1;
            i += step;
        }
        if counted == 0 {
            continue;
        }
        let mut entropy = 0.0f64;
        for &c in &hist {
            if c == 0 {
                continue;
            }
            let p = c as f64 / counted as f64;
            entropy -= p * p.log2();
        }
        best = best.min(entropy);
    }
    if best.is_infinite() { 0.0 } else { best }
}

/// The 10B classification: high entropy (incompressible) — the LZ and
/// entropy families cannot beat RAW on such data.
///
/// Threshold: Shannon entropy (bits per byte) at or above
/// `policy.high_entropy_bits` (7.2 default). The 256-byte floor exists
/// because on tiny chunks the probe is unreliable and the families are
/// cheap — always run the full search (pinned by `tiny_chunks_never_skip`).
pub fn high_entropy(chunk: &[u8], policy: &ForegroundPolicy) -> bool {
    if chunk.len() < 256 {
        // Tiny chunks: the probe is unreliable and the families are
        // cheap; always run the full search.
        return false;
    }
    sampled_entropy(chunk, policy.probe_bytes) >= policy.high_entropy_bits
}

/// True when a chunk is obviously degenerate (single symbol): the
/// ZERO/FILL candidates decide it without any further search.
pub fn is_degenerate(chunk: &[u8]) -> bool {
    let Some(&first) = chunk.first() else {
        return true;
    };
    chunk.iter().all(|&b| b == first)
}

/// The families the foreground search may evaluate for a chunk, given
/// the policy and the configuration (the two gates compose: the policy
/// decides CPU budget, the options decide what exists).
pub fn foreground_allows(
    options: &OptimizeOptions,
    policy: &ForegroundPolicy,
    chunk: &[u8],
) -> ForegroundFamilySet {
    if !policy.allow_full_search(chunk) {
        // High-entropy / raw-only: dedup + ZERO/FILL + RAW only. The
        // families are skipped entirely; RAW is exact and the background
        // optimizer revisits later (foreground vs settled state).
        return ForegroundFamilySet {
            dedup: true,
            zero_fill: true,
            configurational: false,
            byte_rans: false,
            sequence_rans: false,
            sequence_deep: false,
            sequence_dict: false,
            shared_dict: false,
            bases: false,
            universe: false,
        };
    }
    ForegroundFamilySet {
        dedup: true,
        zero_fill: true,
        configurational: options.allow_configurational,
        byte_rans: options.allow_byte_rans,
        sequence_rans: options.allow_sequence_rans,
        sequence_deep: options.allow_sequence_rans_deep,
        sequence_dict: options.allow_sequence_dict,
        shared_dict: options.allow_shared_dict,
        bases: options.allow_bases,
        universe: options.allow_universe,
    }
}

/// Which families the foreground may evaluate for one chunk.
///
/// Role: the materialized decision `foreground_allows` computes for one
/// chunk — the policy gate applied on top of the options gate. Produced
/// by `foreground_allows` (or `unrestricted()` where CPU is not the
/// product, e.g. the background/guided search), never hand-assembled in
/// the write path.
///
/// Invariant: `dedup` and `zero_fill` are always true in the write path
/// (exact dedup is a store invariant; ZERO/FILL decide immediately); the
/// rest follow `OptimizeOptions` when the policy admits the full search.
#[derive(Debug, Clone, Copy)]
pub struct ForegroundFamilySet {
    /// Exact dedup (P2) — always allowed in the write path.
    pub dedup: bool,
    /// ZERO/FILL structural candidates.
    pub zero_fill: bool,
    /// Sparse / palette / periodic / sparse64 configurational encoders.
    pub configurational: bool,
    /// Byte-level rANS.
    pub byte_rans: bool,
    /// SequenceRans (local-match + rANS floor).
    pub sequence_rans: bool,
    /// SequenceDeep (background-only family; harmless to leave on).
    pub sequence_deep: bool,
    /// SequenceDict (previous same-file chunk dictionary).
    pub sequence_dict: bool,
    /// SequenceSharedDict (cross-file shared dictionary).
    pub shared_dict: bool,
    /// Base+residual channels (P0/P1/P3/P4).
    pub bases: bool,
    /// Entropy-universe negative control (P5).
    pub universe: bool,
}

impl ForegroundFamilySet {
    /// The unrestricted set (all families the options admit).
    pub fn unrestricted() -> Self {
        Self {
            dedup: true,
            zero_fill: true,
            configurational: true,
            byte_rans: true,
            sequence_rans: true,
            sequence_deep: true,
            sequence_dict: true,
            shared_dict: true,
            bases: true,
            universe: true,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn entropy_classification_is_deterministic_and_sane() {
        let zeros = vec![0u8; 65536];
        // Genuine randomness (splitmix64): every stride samples uniform
        // bytes, so the min-over-strides reads ~8 bits and the chunk is
        // classified high-entropy -> the LZ/entropy families are skipped.
        let mut state = 0x9e37_79b9_7f4a_7c15u64;
        let random: Vec<u8> = (0..65536u32)
            .map(|_| {
                state = state
                    .wrapping_mul(6364136223846793005)
                    .wrapping_add(1442695040888963407);
                (state >> 33) as u8
            })
            .collect();
        // A 256-period uniform pattern: the full-chunk byte distribution
        // is uniform (8 bits), but the pattern is PERIODIC and therefore
        // compressible — the anti-aliasing min-over-strides must keep it
        // OUT of the high-entropy skip (the periodic family wins instead).
        let periodic: Vec<u8> = (0..65536u32)
            .map(|i| (i.wrapping_mul(2654435761)) as u8)
            .collect();
        let text: Vec<u8> = (0..65536u32).map(|i| b'a' + (i % 26) as u8).collect();
        let p = ForegroundPolicy::cheap();
        let e0 = sampled_entropy(&zeros, 4096);
        let er = sampled_entropy(&random, 4096);
        let ep = sampled_entropy(&periodic, 4096);
        let et = sampled_entropy(&text, 4096);
        assert_eq!(e0, 0.0, "zeros are zero-entropy");
        assert!(er >= 7.9, "true random is near 8 bits/byte (got {er})");
        assert!(ep < 6.0, "periodic data must not look random (got {ep})");
        assert!(et < 5.0, "text is low-entropy (got {et})");
        assert!(high_entropy(&random, &p));
        assert!(!high_entropy(&text, &p));
        assert!(!high_entropy(&zeros, &p));
        assert!(
            !high_entropy(&periodic, &p),
            "periodic data stays in the full search"
        );
        // Determinism.
        assert_eq!(sampled_entropy(&random, 4096), er);
    }

    #[test]
    fn tiny_chunks_never_skip() {
        let p = ForegroundPolicy::cheap();
        assert!(!high_entropy(&[7u8; 100], &p));
        assert!(p.allow_full_search(&[7u8; 100]));
    }
}