//! Inner compression: GF(2^4) matrix multiplication + LFSR diffusion
//! Corresponds to inner_compress_gf2x4 @ 0x80007084
//!
//! Processes 128-bit message blocks, updates 256-bit state

use crate::util::partial_bitrev_shr1;

/// GF(2^4) nibble-parallel multiplication.
/// Decompiled (State 0):
///   a1 = a & 0x11111111; a2 = a & 0x22222222; a4 = a & 0x44444444; a8 = a & 0x88888888;
///   b1 = b & 0x11111111; b2 = b & 0x22222222; b4 = b & 0x44444444; b8 = b & 0x88888888;
///   r8 = (a1*b8 ^ b4*a2 ^ a8*b1 ^ a4*b2) & 0x88888888
///   r4 = (b8*a8 ^ a1*b4 ^ a4*b1 ^ a2*b2) & 0x44444444
///   r1 = (b8*a2 ^ a4*b4 ^ b1*a1 ^ b2*a8) & 0x11111111
///   r2 = (a4*b8 ^ b4*a8 ^ b1*a2 ^ b2*a1) & 0x22222222
fn gf2x4_mul(a: u32, b: u32) -> u32 {
    let a1 = a & 0x11111111;
    let a2 = a & 0x22222222;
    let a4 = a & 0x44444444;
    let a8 = a & 0x88888888;
    let b1 = b & 0x11111111;
    let b2 = b & 0x22222222;
    let b4 = b & 0x44444444;
    let b8 = b & 0x88888888;

    let r8 = (a1.wrapping_mul(b8) ^ b4.wrapping_mul(a2) ^ a8.wrapping_mul(b1) ^ a4.wrapping_mul(b2)) & 0x88888888;
    let r4 = (b8.wrapping_mul(a8) ^ a1.wrapping_mul(b4) ^ a4.wrapping_mul(b1) ^ a2.wrapping_mul(b2)) & 0x44444444;
    let r1 = (b8.wrapping_mul(a2) ^ a4.wrapping_mul(b4) ^ b1.wrapping_mul(a1) ^ b2.wrapping_mul(a8)) & 0x11111111;
    let r2 = (a4.wrapping_mul(b8) ^ b4.wrapping_mul(a8) ^ b1.wrapping_mul(a2) ^ b2.wrapping_mul(a1)) & 0x22222222;

    r8 | r4 | r1 | r2
}

/// LFSR forward feedback: x << 31 ^ x << 30 ^ x << 25
#[inline]
fn lfsr_fwd(x: u32) -> u32 {
    (x << 31) ^ (x << 30) ^ (x << 25)
}

/// LFSR backward feedback: x >> 2 ^ x >> 1 ^ x >> 7
#[inline]
fn lfsr_bwd(x: u32) -> u32 {
    (x >> 2) ^ (x >> 1) ^ (x >> 7)
}

/// Inner compression function.
/// state: 256-bit (8 u32), uses Feistel structure on upper/lower halves
/// block: 128-bit message block (4 u32)
pub fn inner_compress(state: &mut [u32; 8], block: &[u32; 4]) {
    // 1. XOR message into state upper half (note: reverse order)
    state[7] ^= block[3];
    state[6] ^= block[2];
    state[5] ^= block[1];
    state[4] ^= block[0];

    // 2. Prepare working values
    let (s0, s1, s2, s3) = (state[4], state[5], state[6], state[7]);
    let (m0, m1, m2, m3) = (block[0], block[1], block[2], block[3]);

    // Precompute state-side values
    let s01 = s0 ^ s1;
    let s23 = s2 ^ s3;
    let s02 = s0 ^ s2;
    let s13 = s1 ^ s3;
    let s0123 = s01 ^ s23;

    // Precompute message-side values
    let m01 = m0 ^ m1;
    let m23 = m2 ^ m3;
    let m02 = m0 ^ m2;
    let m13 = m1 ^ m3;
    let m0123 = m01 ^ m23;

    // Bit-reversed versions
    let rs0 = partial_bitrev_shr1(s0);
    let rs1 = partial_bitrev_shr1(s1);
    let rs2 = partial_bitrev_shr1(s2);
    let rs3 = partial_bitrev_shr1(s3);
    let rs01 = partial_bitrev_shr1(s01);
    let rs23 = partial_bitrev_shr1(s23);
    let rs02 = partial_bitrev_shr1(s02);
    let rs13 = partial_bitrev_shr1(s13);
    let rs0123 = partial_bitrev_shr1(s0123);

    // 3. GF(2^4) multiplications (18 products)
    let p0 = gf2x4_mul(s0, m0);
    let p1 = gf2x4_mul(s1, m1);
    let p2 = gf2x4_mul(s2, m2);
    let p3 = gf2x4_mul(s3, m3);
    let p01 = gf2x4_mul(s01, m01);
    let p23 = gf2x4_mul(s23, m23);
    let p02 = gf2x4_mul(s02, m02);
    let p13 = gf2x4_mul(s13, m13);
    let p0123 = gf2x4_mul(s0123, m0123);

    let rp0 = gf2x4_mul(rs0, m0);
    let rp1 = gf2x4_mul(rs1, m1);
    let rp2 = gf2x4_mul(rs2, m2);
    let rp3 = gf2x4_mul(rs3, m3);
    let rp01 = gf2x4_mul(rs01, m01);
    let rp23 = gf2x4_mul(rs23, m23);
    let rp02 = gf2x4_mul(rs02, m02);
    let rp13 = gf2x4_mul(rs13, m13);
    let rp0123 = gf2x4_mul(rs0123, m0123);

    // 4. Karatsuba-style recombination
    // Row 0: p0
    // Row 1: p01 ^ p0 ^ p1
    // Row 2: p02 ^ p0 ^ p2
    // Row 3: p0123 ^ p01 ^ p02 ^ p13 ^ p23 ^ p0 ^ p3
    let k0 = p0;
    let k1 = p01 ^ p0 ^ p1;
    let k2 = p02 ^ p0 ^ p2;
    let k3 = p0123 ^ p01 ^ p02 ^ p13 ^ p23 ^ p0 ^ p3;

    let rk0 = rp0;
    let rk1 = rp01 ^ rp0 ^ rp1;
    let rk2 = rp02 ^ rp0 ^ rp2;
    let rk3 = rp0123 ^ rp01 ^ rp02 ^ rp13 ^ rp23 ^ rp0 ^ rp3;

    // 5. LFSR diffusion + mixing
    let out0 = k0 ^ lfsr_bwd(rk0) ^ lfsr_fwd(k0);
    let out1 = k1 ^ lfsr_bwd(rk1) ^ lfsr_fwd(k1);
    let out2 = k2 ^ lfsr_bwd(rk2) ^ lfsr_fwd(k2);
    let out3 = k3 ^ lfsr_bwd(rk3) ^ lfsr_fwd(k3);

    // 6. Feistel update: shift right half
    state[4] = state[0];
    state[5] = state[1];
    state[6] = state[2];
    state[7] = state[3];

    // Write new left half
    state[0] = out0;
    state[1] = out1;
    state[2] = out2;
    state[3] = out3;
}
