struct Lfsr {
    reg: Vec<bool>,
    p: Vec<bool>,
}

impl Lfsr {
    fn new(iv: &[bool], p: &[bool]) -> Self {
        assert_eq!(iv.len(), p.len());
        Self {
            reg: iv.iter().cloned().collect(),
            p: p.iter().cloned().collect(),
        }
    }

    fn next(&mut self) -> bool {
        // let res = self.reg[self.len - 1];
        let next = self
            .reg
            .iter()
            .zip(self.p.iter())
            .map(|(n, m)| n & m)
            .fold(false, |acc, v| acc ^ v);
        self.reg.insert(0, next);
        let ret = self.reg.pop().unwrap();
        assert_eq!(self.reg.len(), self.p.len());
        ret
    }

    fn state(&self) -> &[bool] {
        &self.reg
    }
}

fn convert_state(s: &[bool]) -> String {
    s.iter()
        .map(|x| if *x { "1".to_string() } else { "0".to_string() })
        .collect::<Vec<String>>()
        .join(" & ")
}

/// helper for 3c)
fn recover_keystream(plain: &[bool], cipher: &[bool]) -> Vec<bool> {
    assert_eq!(plain.len(), cipher.len());
    plain
        .iter()
        .zip(cipher.iter())
        .map(|(a, b)| a ^ b)
        .collect()
}

fn decrypt(l: &mut Lfsr, c: &[bool]) -> u8 {
    let s: String = c
        .into_iter()
        .map(|c| c ^ l.next())
        .map(|c| if c { "1" } else { "0" })
        .collect();
    u8::from_str_radix(&s, 2).unwrap()
}

/// main for 3d)
fn main() {
    let p = &[false, true, true, false, false, false, true, true];
    // let s = &[true, false, true, true, true, false, true, true];
    // let s = &[true, false, true, false, false, false, false, true];
    // let s = &[true, false, false, false, false, true, false, true];
    let s = &[true, true, false, true, true, true, false, true];
    let mut l = Lfsr::new(s, p);
    // for _ in 0..17 {
    //     println!("{}", if lfsr.next() { "1" } else { "0" });
    // }
    // 00010011
    // 11010100
    let c = &[true, true, false, true, false, true, false, false];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 00010011 = 0x13
    let c = &[false, false, false, true, false, false, true, true];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 00010000 = 0x10
    let c = &[false, false, false, true, false, false, false, false];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 01110101 = 0x75
    let c = &[false, true, true, true, false, true, false, true];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 01100000 = 0x60
    let c = &[false, true, true, false, false, false, false, false];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 00000101 = 0x05
    let c = &[false, false, false, false, false, true, false, true];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 01111100 = 0x7c
    let c = &[false, true, true, true, true, true, false, false];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 01010000 = 0x50
    let c = &[false, true, false, true, false, false, false, false];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 11011011 = 0xdb
    let c = &[true, true, false, true, true, false, true, true];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);

    // 01110011 = 0x73
    let c = &[false, true, true, true, false, false, true, true];
    let d = decrypt(&mut l, c);
    println!("{}: {}", d, d as char);
}

// fn main() {
//     let plain = &[
//         false, true, false, false, true, true, false, true, false, true, true, false, true, true,
//         true, true,
//     ];
//     let cipher = &[
//         true, true, true, false, true, true, false, false, true, true, false, true, false, true,
//         false, false,
//     ];
//     let key_stream = recover_keystream(plain, cipher);
//     println!(
//         "{}",
//         key_stream
//             .iter()
//             .map(|k| if *k { "1, " } else { "0, " })
//             .collect::<String>()
//     );
// }

/// main for exercise 1
fn main1() {
    // 1a)
    // let mut lfsr = Lfsr::new(
    //     &[true, true, false, false, false, false],
    //     // &[true, true, true, true, true, true],
    //     // &[true, false, false, false, false, false],
    //     &[false, true, false, true, true, true],
    // );

    // 1b)
    // let mut lfsr = Lfsr::new(
    //     // &[true, true, true, true, true, true],
    //     &[true, false, false, false, false, false],
    //     &[false, false, false, false, true, true],
    // );

    // 1c)
    let mut lfsr = Lfsr::new(
        // &[true, true, true, false, false, false],
        // &[true, true, false, false, false, false],
        &[true, false, true, true, false, true],
        // &[true, true, true, true, true, true],
        // &[true, false, false, false, false, false],
        &[false, false, true, true, true, true],
    );
    let mut seen = Vec::new();
    let mut i = 0;
    while !seen.contains(&lfsr.state().iter().cloned().collect::<Vec<_>>()) {
        seen.push(lfsr.state().iter().cloned().collect::<Vec<_>>());
        print!("{}", convert_state(lfsr.state()));
        println!(" & {} \\\\", if lfsr.next() { 1 } else { 0 });
        i += 1;
    }
    eprintln!("Repetition after {} iterations", i);
}
