viterbi 0.2.0

Pure-Rust, no-unsafe convolutional encoder + Viterbi (MLSE) decoder — CCSDS inner FEC: hard & soft-decision (LLR), rate 1/2 & 1/3, puncturing (2/3–7/8), generic constraint length K<=9.
Documentation
// Author: BolivarTech
// Version: 1.0.0
// Date: 2026-07-02
//! Property-based (`proptest`) and determinism tests (R5, R10, R14).

mod common;
use common::codec;

/// Spacing (in coded bits) between injected errors so each is beyond the constraint span
/// (~K = 7 info bits ⇒ ~14 coded bits) and therefore independently correctable.
const SPACED_ERROR_STRIDE: usize = 40;

/// Flip the MSB-first coded bit at index `i` in a packed byte buffer.
fn flip_coded_bit(bytes: &mut [u8], i: usize) {
    bytes[i / 8] ^= 1 << (7 - (i % 8));
}

proptest::proptest! {
    #[test]
    fn decode_of_encode_is_identity(raw in proptest::collection::vec(proptest::num::u8::ANY, 0..300)) {
        let (enc, mut dec) = codec();
        proptest::prop_assert_eq!(dec.decode_block(&enc.encode(&raw).unwrap()).unwrap().bytes, raw);
    }

    /// R4/R14: isolated errors spaced far apart (every ~40th coded bit, well beyond the constraint
    /// span) are each independently correctable, so decoding recovers the exact payload.
    #[test]
    fn exact_recovery_under_spaced_errors(
        raw in proptest::collection::vec(proptest::num::u8::ANY, 1..128)
    ) {
        let (enc, mut dec) = codec();
        let mut coded = enc.encode(&raw).unwrap();
        // Flip one coded bit per SPACED_ERROR_STRIDE-bit window (offset half a stride from the
        // start so the flips sit clear of the block edges); each error is isolated ⇒ correctable.
        let mut i = SPACED_ERROR_STRIDE / 2;
        while i < coded.nbits {
            flip_coded_bit(&mut coded.bytes, i);
            i += SPACED_ERROR_STRIDE;
        }
        proptest::prop_assert_eq!(dec.decode_block(&coded).unwrap().bytes, raw);
    }
}

#[test]
fn deterministic_across_runs() {
    let (enc, mut dec) = codec();
    let coded = enc.encode(b"determinism").unwrap();
    let a = dec.decode_block(&coded).unwrap();
    let b = dec.decode_block(&coded).unwrap();
    assert_eq!(a, b);
}