use std::collections::HashSet;

const P_1: [usize; 32] = [
    9, 17, 23, 31, 13, 28, 2, 18, 24, 16, 30, 6, 26, 20, 10, 1, 8, 14, 25, 3, 4, 29, 11, 19, 32,
    12, 22, 7, 5, 27, 15, 21,
];

const P: [usize; 32] = [
    16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19,
    13, 30, 6, 22, 11, 4, 25,
];

const E: [usize; 48] = [
    32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18,
    19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1,
];

fn permutate<T: Copy + Default>(i: &[T], b: &[usize]) -> Vec<T> {
    let mut res: Vec<_> = (0..b.len()).map(|_| T::default()).collect();
    for (i, p) in i.iter().zip(b.iter()) {
        res[*p - 1] = *i;
    }
    res
}

trait Split {
    fn split(&self) -> (u32, u32);
}

impl Split for u64 {
    fn split(&self) -> (u32, u32) {
        ((*self >> 32) as u32, *self as u32)
    }
}

fn xor(a: &[bool], b: &[bool]) -> Vec<bool> {
    assert!(a.len() == b.len());
    a.iter().zip(b.iter()).map(|(a, b)| a ^ b).collect()
}

fn to_bin<T: std::fmt::Binary>(i: T) -> Vec<bool> {
    format!("{:b}", i)
        .chars()
        .map(|c| if c == '1' { true } else { false })
        .collect()
}

fn show(b: &[bool], split: u8) {
    let mut i = 1;
    b.iter().map(|c| if *c { '1' } else { '0' }).for_each(|c| {
        print!("{}", c);
        if i % split == 0 {
            print!(" ");
        }
        i += 1;
    });
    println!();
}

fn reverse_des(i: u64, o: u64) {
    let (l0, r0) = i.split();
    let (l1, r1) = o.split();
    let e_out = permutate(&to_bin(r0), &E);
    println!("e_out: ");
    show(&e_out, 100);
    let f_out = r1 ^ l0;
    let s_out = permutate(&to_bin(f_out), &P_1);
    assert!(&to_bin(f_out) == &permutate(&s_out, &P));
    println!("s_out: ");
    show(&s_out, 4);
    println!();
}

fn do_it(s: &[[[bool; 6]; 4]], x: &[bool]) -> HashSet<Vec<bool>> {
    let mut set1 = HashSet::new();

    for a in s[0].iter() {
        for b in s[1].iter() {
            for c in s[2].iter() {
                for d in s[3].iter() {
                    for e in s[4].iter() {
                        for f in s[5].iter() {
                            for g in s[6].iter() {
                                for h in s[7].iter() {
                                    let mut v = Vec::with_capacity(48);
                                    v.extend(a);
                                    v.extend(b);
                                    v.extend(c);
                                    v.extend(d);
                                    v.extend(e);
                                    v.extend(f);
                                    v.extend(g);
                                    v.extend(h);
                                    let r = xor(&v, x);
                                    set1.insert(r);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    set1
}

fn main() {
    let x1: u64 = 0xF57DDDB12EAA799D;
    let y1: u64 = 0x2EAA799D332924D8;
    let x2: u64 = 0x15837977D5202437;
    let y2: u64 = 0xD5202437B5D8FC31;

    let s_in_1 = [
        [
            [false, true, true, true, true, false],
            [false, false, false, true, false, true],
            [true, true, false, true, true, false],
            [true, false, true, true, true, true],
        ],
        [
            [false, true, true, false, true, false],
            [false, false, false, false, true, true],
            [true, false, true, true, false, false],
            [true, false, false, false, false, true],
        ],
        [
            [false, false, false, true, false, false],
            [false, false, false, true, true, true],
            [true, false, false, true, true, false],
            [true, false, true, false, true, true],
        ],
        [
            [false, true, false, true, true, false],
            [false, false, false, true, true, true],
            [true, true, true, false, false, false],
            [true, true, false, true, false, true],
        ],
        [
            [false, false, true, true, false, false],
            [false, false, false, false, true, true],
            [true, false, false, true, true, false],
            [true, false, false, false, false, true],
        ],
        [
            [false, false, false, false, false, false],
            [false, false, true, false, true, true],
            [true, false, true, true, false, false],
            [true, false, false, true, true, true],
        ],
        [
            [false, true, false, true, false, false],
            [false, false, true, false, true, true],
            [true, true, true, true, false, false],
            [true, true, false, false, false, true],
        ],
        [
            [false, true, true, false, true, false],
            [false, true, true, false, false, true],
            [true, true, false, false, false, false],
            [true, true, false, true, true, true],
        ],
    ];
    let e_out_1 = [
        false, true, true, true, false, true, false, true, false, false, true, true, false, false,
        true, false, true, true, true, false, true, false, false, false, false, false, false,
        false, false, false, false, true, false, false, false, false, false, false, false, false,
        false, false, false, false, false, false, false, false,
    ];

    let s_in_2 = [
        [
            [false, false, true, true, true, false],
            [false, true, true, true, true, true],
            [true, false, false, true, true, false],
            [true, false, false, true, false, true],
        ],
        [
            [false, true, true, true, false, false],
            [false, true, true, true, true, true],
            [true, true, false, false, false, false],
            [true, true, true, false, true, true],
        ],
        [
            [false, true, false, false, true, false],
            [false, false, false, false, false, true],
            [true, false, false, false, false, false],
            [true, false, false, true, false, true],
        ],
        [
            [false, false, false, true, true, false],
            [false, false, true, true, true, true],
            [true, true, false, true, false, false],
            [true, false, false, false, false, true],
        ],
        [
            [false, false, false, false, false, false],
            [false, false, false, true, false, true],
            [true, false, false, false, true, false],
            [true, false, true, true, false, true],
        ],
        [
            [false, false, true, false, true, false],
            [false, false, false, true, true, true],
            [true, false, true, false, false, false],
            [true, false, false, true, false, true],
        ],
        [
            [false, true, true, true, false, false],
            [false, true, true, true, true, true],
            [true, true, false, true, false, false],
            [true, false, false, false, false, true],
        ],
        [
            [false, true, false, false, false, false],
            [false, false, false, true, true, true],
            [true, true, true, true, true, false],
            [true, false, true, true, false, true],
        ],
    ];
    let e_out_2 = [
        true, false, true, false, true, false, false, false, false, false, false, true, false,
        false, true, false, false, true, true, true, true, false, false, false, false, false,
        false, false, false, false, false, true, false, false, false, false, false, false, false,
        false, false, false, false, false, false, false, false, false,
    ];

    let set1 = do_it(&s_in_1, &e_out_1);
    let set2 = do_it(&s_in_2, &e_out_2);

    let intersection: HashSet<_> = set1.intersection(&set2).collect();
    println!("{}", intersection.len());

    // reverse_des(x1, y1);
    // reverse_des(x2, y2);
}
