use super::{CRCBuilder, CRC};
use crate::checksum::CheckReverserError;
use crate::endian::{bytes_to_int, int_to_bytes, wordspec_combos, Endian, WordSpec};
use crate::utils::{cart_prod, unresult_iter};
use delsum_poly::*;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
use std::convert::TryInto;
use std::pin::Pin;
pub fn reverse_crc<'a>(
spec: &CRCBuilder<u128>,
chk_bytes: &'a [(&[u8], Vec<u8>)],
verbosity: u64,
extended_search: bool,
) -> impl Iterator<Item = Result<CRC<u128>, CheckReverserError>> + 'a {
let spec = spec.clone();
let mut files = chk_bytes.to_owned();
files.sort_unstable_by(|a, b| a.0.len().cmp(&b.0.len()).reverse());
let ref_combinations: Vec<_> = discrete_combos(&spec, extended_search);
ref_combinations
.into_iter()
.map(move |((refin, refout), wordspec)| {
unresult_iter(reverse(
&spec,
files.clone(),
verbosity,
refin,
refout,
wordspec,
))
})
.flatten()
.filter_map(|x| {
match x {
Ok(a) => Some(Ok(a)),
Err(Some(e)) => Some(Err(e)),
Err(None) => None,
}
})
}
#[cfg(feature = "parallel")]
pub fn reverse_crc_para<'a>(
spec: &CRCBuilder<u128>,
chk_bytes: &'a [(&[u8], Vec<u8>)],
verbosity: u64,
extended_search: bool,
) -> impl ParallelIterator<Item = Result<CRC<u128>, CheckReverserError>> + 'a {
let spec = spec.clone();
let mut files = chk_bytes.to_owned();
files.sort_unstable_by(|a, b| a.0.len().cmp(&b.0.len()).reverse());
let ref_combinations: Vec<_> = discrete_combos(&spec, extended_search);
ref_combinations
.into_par_iter()
.map(move |((refin, refout), wordspec)| {
unresult_iter(reverse(
&spec,
files.clone(),
verbosity,
refin,
refout,
wordspec,
))
.par_bridge()
})
.flatten()
.filter_map(|x| match x {
Ok(a) => Some(Ok(a)),
Err(Some(e)) => Some(Err(e)),
Err(None) => None,
})
}
fn discrete_combos(
spec: &CRCBuilder<u128>,
extended_search: bool,
) -> Vec<((bool, bool), WordSpec)> {
let width = spec.width.expect("Missing width argument");
let refins = spec
.refin
.map(|x| vec![x])
.unwrap_or_else(|| vec![false, true]);
let mut ret = Vec::new();
for refin in refins {
let refouts = spec.refout.map(|x| vec![x]).unwrap_or_else(|| {
if extended_search {
vec![false, true]
} else {
vec![refin]
}
});
let input_endian = spec.input_endian.or_else(|| {
Some(match refin {
true => Endian::Big,
false => Endian::Little,
})
});
let refs = cart_prod(&[refin], &refouts);
let wordspecs = wordspec_combos(
spec.wordsize,
input_endian,
spec.output_endian,
width,
extended_search,
);
ret.append(&mut cart_prod(&refs, &wordspecs));
}
ret
}
fn reverse<'a>(
spec: &CRCBuilder<u128>,
chk_bytes: Vec<(&'a [u8], Vec<u8>)>,
verbosity: u64,
refin: bool,
refout: bool,
wordspec: WordSpec,
) -> Result<impl Iterator<Item = CRC<u128>> + 'a, Option<CheckReverserError>> {
let width = match spec.width {
Some(x) => x,
None => return Err(Some(CheckReverserError::MissingParameter("width"))),
};
if 3 > chk_bytes.len()
+ spec.init.is_some() as usize
+ spec.xorout.is_some() as usize
+ spec.poly.is_some() as usize
{
return Err(Some(CheckReverserError::MissingParameter(
"at least 3 parameters/files",
)));
}
if spec.init.is_none()
&& chk_bytes.iter().map(|x| x.0.len()).max() == chk_bytes.iter().map(|x| x.0.len()).min()
{
return Err(Some(CheckReverserError::UnsuitableFiles(
"need at least one file with different length",
)));
}
let maybe_polys: Option<Vec<_>> = chk_bytes
.iter()
.map(|(b, c)| {
if b.len() * 8 % wordspec.wordsize != 0 {
None
} else {
bytes_to_poly(b, c, width as u8, refin, refout, wordspec).map(|p| (p, b.len()))
}
})
.collect();
let mut polys = match maybe_polys {
Some(x) => x,
None => return Err(None),
};
polys.sort_by(|(fa, la), (fb, lb)| la.cmp(&lb).then(deg(fa).cmp(°(fb)).reverse()));
let revinfo = RevInfo::from_builder(spec, refin, refout, wordspec);
rev_from_polys(&revinfo, &polys, verbosity).map(|x| x.iter())
}
struct RevInfo {
width: usize,
init: Option<PolyPtr>,
xorout: Option<PolyPtr>,
poly: Option<PolyPtr>,
refin: bool,
refout: bool,
wordspec: WordSpec,
}
impl RevInfo {
fn from_builder(
spec: &CRCBuilder<u128>,
refin: bool,
refout: bool,
wordspec: WordSpec,
) -> Self {
let width = spec.width.unwrap();
let init = spec.init.map(|i| new_poly(&i.to_le_bytes()));
let poly = spec.poly.map(|p| {
let mut p = new_poly(&p.to_le_bytes());
p.pin_mut().add_to(&new_poly_shifted(&[1], width as i64));
p
});
let xorout = spec
.xorout
.map(|x| new_poly(&cond_reverse(width as u8, x, refout).to_le_bytes()));
RevInfo {
width,
init,
xorout,
poly,
refin,
refout,
wordspec,
}
}
}
struct RevResult {
polys: Vec<PolyPtr>,
inits: PrefactorMod,
xorout: InitPoly,
width: usize,
refin: bool,
refout: bool,
wordspec: WordSpec,
}
impl RevResult {
fn iter(self) -> impl Iterator<Item = CRC<u128>> {
let RevResult {
polys,
inits,
xorout,
width,
refin,
refout,
wordspec,
} = self;
polys
.into_iter()
.map(move |pol| {
inits
.iter_inits(&pol, &xorout)
.map(move |(poly_p, init_p, xorout_p)| {
let poly =
poly_to_u128(&add(&poly_p, &new_poly_shifted(&[1], width as i64)));
let init = poly_to_u128(&init_p);
let xorout = cond_reverse(width as u8, poly_to_u128(&xorout_p), refout);
CRC::<u128>::with_options()
.width(width)
.poly(poly)
.init(init)
.xorout(xorout)
.refin(refin)
.refout(refout)
.inendian(wordspec.input_endian)
.outendian(wordspec.output_endian)
.wordsize(wordspec.wordsize)
.build()
.unwrap()
})
})
.flatten()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum InitPlace {
None,
Single(usize),
Pair(usize, usize),
}
type InitPoly = (PolyPtr, InitPlace);
fn rev_from_polys(
spec: &RevInfo,
arg_polys: &[(PolyPtr, usize)],
verbosity: u64,
) -> Result<RevResult, Option<CheckReverserError>> {
let log = |s| {
if verbosity > 0 {
eprintln!(
"<crc, refin = {}, refout = {}> {}",
spec.refin, spec.refout, s
);
}
};
let mut polys: Vec<_> = arg_polys
.iter()
.rev()
.map(|(p, l)| (copy_poly(p), InitPlace::Single(*l)))
.collect();
if let Some(init) = &spec.init {
log("removing inits");
remove_inits(&init, &mut polys);
}
log("removing xorouts");
let (polys, mut xorout) = remove_xorouts(&spec.xorout, polys);
log("finding poly");
let (polys, mut hull) = find_polyhull(spec, polys, verbosity)?;
log("finding init and refining poly");
let init = find_init(&spec.init, hull.pin_mut(), polys);
let polyhull_factors: Vec<_>;
if deg(&hull) > 0 {
xorout.0.pin_mut().rem_to(&hull);
log("factoring poly");
polyhull_factors = factor(&hull, if verbosity > 1 { 1 } else { 0 })
.into_iter()
.map(|PolyI64Pair { poly, l }| (copy_poly(poly), *l))
.collect();
} else {
log("could not find any fitting factors for poly");
xorout.0 = new_zero();
polyhull_factors = vec![];
}
log("finding all factor combinations for poly and finishing");
Ok(RevResult {
polys: find_prod_comb(spec.width, &polyhull_factors),
inits: init,
xorout,
width: spec.width,
refin: spec.refin,
refout: spec.refout,
wordspec: spec.wordspec,
})
}
fn remove_inits(init: &Poly, polys: &mut [InitPoly]) {
for (p, l) in polys {
match l {
InitPlace::Single(d) => {
p.pin_mut().add_to(&shift(init, 8 * *d as i64));
*l = InitPlace::None;
}
InitPlace::None => (),
InitPlace::Pair(_, _) => {
panic!("Internal Error: remove_inits should not receive Pair Inits")
}
}
}
}
fn remove_xorouts(
maybe_xorout: &Option<PolyPtr>,
mut polys: Vec<InitPoly>,
) -> (Vec<InitPoly>, InitPoly) {
let mut ret_vec = Vec::new();
let mut prev = polys
.pop()
.expect("Internal Error: Zero-length vector given to remove_xorouts");
let xor_ret = match maybe_xorout {
Some(xorout) => {
ret_vec.push((add(&prev.0, xorout), prev.1));
(copy_poly(&xorout), InitPlace::None)
}
None => (copy_poly(&prev.0), prev.1),
};
for (p, l) in polys.into_iter().rev() {
let appendix = match (maybe_xorout, l != InitPlace::None && l == prev.1) {
(None, _) | (_, true) => {
let poly_diff = add(&p, &prev.0);
let new_init_place = match (prev.1, l) {
(InitPlace::None, other) | (other, InitPlace::None) => other,
(InitPlace::Single(l1), InitPlace::Single(l2)) => {
if l1 == l2 {
InitPlace::None
} else {
InitPlace::Pair(l1, l2)
}
}
(InitPlace::Pair(_, _), _) | (_, InitPlace::Pair(_, _)) => {
panic!("Internal Error: init pair in the input array of remove_xorouts")
}
};
(poly_diff, new_init_place)
}
(Some(xorout), false) => {
let poly_no_xorout = add(&p, xorout);
(poly_no_xorout, l)
}
};
ret_vec.push(appendix);
prev = (p, l);
}
(ret_vec, xor_ret)
}
fn find_polyhull(
spec: &RevInfo,
polys: Vec<InitPoly>,
verbosity: u64,
) -> Result<(Vec<InitPoly>, PolyPtr), Option<CheckReverserError>> {
let log = |s| {
if verbosity > 1 {
eprintln!(
"<crc poly, refin = {}, refout = {}> {}",
spec.refin, spec.refout, s
);
}
};
let mut contain_init_vec = Vec::new();
let mut hull = spec
.poly
.as_ref()
.map(|x| copy_poly(x))
.unwrap_or_else(new_zero);
log("gcd'ing same length files together");
for (p, l) in polys {
match l {
InitPlace::None => {
hull.pin_mut().gcd_to(&p);
}
_ => {
contain_init_vec.push((p, l));
}
}
if deg(&hull) == 0 {
return Ok((contain_init_vec, hull));
}
}
log("gcd'ing different length files together");
for ((p, l), (q, m)) in contain_init_vec.iter().zip(contain_init_vec.iter().skip(1)) {
let power_8n = |n: usize| new_poly_shifted(&[1], 8 * n as i64);
let (mut p_fac, mut q_fac) = match (l, m) {
(InitPlace::None, _) | (_, InitPlace::None) => unreachable!(),
(InitPlace::Single(d), InitPlace::Single(e)) => {
let min = d.min(e);
(power_8n(d - min), power_8n(e - min))
}
(InitPlace::Single(d), InitPlace::Pair(e1, e2)) => {
let min = d.min(e1).min(e2);
let p_fac = power_8n(d - min);
let mut q_fac = power_8n(e2 - min);
q_fac += &power_8n(e1 - min);
(p_fac, q_fac)
}
(InitPlace::Pair(d1, d2), InitPlace::Single(e)) => {
let min = d1.min(d1).min(e);
let mut p_fac = power_8n(d2 - min);
p_fac += &power_8n(d1 - min);
let q_fac = power_8n(e - min);
(p_fac, q_fac)
}
(InitPlace::Pair(d1, d2), InitPlace::Pair(e1, e2)) => {
let min = d1.min(d2).min(e1).min(e2);
let mut p_fac = power_8n(d2 - min);
p_fac += &power_8n(d1 - min);
let mut q_fac = power_8n(e2 - min);
q_fac += &power_8n(e1 - min);
(p_fac, q_fac)
}
};
p_fac *= q;
q_fac *= p;
q_fac += &p_fac;
hull.pin_mut().gcd_to(&q_fac);
if deg(&hull) == 0 {
return Ok((contain_init_vec, hull));
}
}
if hull.is_zero() {
return Err(Some(CheckReverserError::UnsuitableFiles(
"Duplicate files or unluckiest person alive",
)));
}
log("removing factors with degree*multiplicity > width");
let mut cumulative_prod = new_polyrem(&new_poly(&[1]), &hull);
let x = new_polyrem(&new_poly(&[1 << 1]), &hull);
let mut x_to_2_to_n = copy_polyrem(&x);
for i in 0..spec.width {
if verbosity > 1 {
eprintln!(
"<crc poly, refin = {}, refout = {}> step {} of {}",
spec.refin, spec.refout, i, spec.width
)
}
x_to_2_to_n.pin_mut().sqr();
let mut fac = copy_polyrem(&x_to_2_to_n);
fac += &x;
cumulative_prod *= &fac;
}
drop(x_to_2_to_n);
let reduced_prod = cumulative_prod.rep();
drop(cumulative_prod);
log("doing final gcd");
hull.pin_mut().gcd_to(&reduced_prod);
log("removing trailing zeros");
for i in 0..=spec.width {
if hull.coeff(i as i64) {
hull = shift(&hull, -(i as i64));
break;
}
}
Ok((contain_init_vec, hull))
}
struct MemoPower {
prev_power: usize,
prev_ppoly: PolyRemPtr,
init_fac: PolyPtr,
hull: PolyPtr,
}
impl MemoPower {
fn new(hull: &Poly) -> Self {
MemoPower {
prev_power: 0,
prev_ppoly: new_polyrem(&new_poly(&[1]), &hull),
init_fac: new_zero(),
hull: copy_poly(hull),
}
}
fn update_init_fac(&mut self, place: &InitPlace) -> &Poly {
let mut update_power = |&new_level: &usize| {
if new_level < self.prev_power {
panic!("Internal Error: Polynomials non-ascending");
}
let x = new_polyrem(&new_poly(&[1 << 1]), &self.hull);
let power_diff = powermod(&x, (new_level - self.prev_power) as i64 * 8);
self.prev_power = new_level;
self.prev_ppoly *= &power_diff;
self.prev_ppoly.rep()
};
self.init_fac = match place {
InitPlace::None => new_zero(),
InitPlace::Single(d) => update_power(d),
InitPlace::Pair(d1, d2) => {
let mut current_power = update_power(d1);
current_power += &update_power(d2);
current_power
}
};
&self.init_fac
}
fn get_init_fac(&self) -> &Poly {
&self.init_fac
}
fn update_hull(&mut self, hull: &Poly) {
self.hull = copy_poly(hull);
self.prev_ppoly = new_polyrem(&self.prev_ppoly.rep(), &hull)
}
}
struct PrefactorMod {
unknown: PolyPtr,
possible: PolyPtr,
hull: PolyPtr,
}
impl PrefactorMod {
fn empty() -> Self {
PrefactorMod {
unknown: new_poly(&[1]),
possible: new_zero(),
hull: new_poly(&[1]),
}
}
fn new_init(maybe_init: &Option<PolyPtr>, hull: &Poly) -> Self {
let (unknown, possible) = match maybe_init {
None => (copy_poly(hull), new_zero()),
Some(init) => (new_poly(&[1]), copy_poly(init)),
};
PrefactorMod {
unknown,
possible,
hull: copy_poly(&hull),
}
}
fn new_file(
mut file: PolyPtr,
power: &mut MemoPower,
mut hull: Pin<&mut Poly>,
) -> Option<Self> {
file.pin_mut().rem_to(&hull);
let file_float = gcd(&file, &hull);
let power_float = gcd(power.get_init_fac(), &hull);
let common_float = gcd(&power_float, &file_float);
let discrepancy = div(&power_float, &common_float);
if !discrepancy.eq(&new_poly(&[1])) {
let hull_part = highest_power_gcd(&hull, &discrepancy);
let file_part = gcd(&file_float, &hull_part);
hull.as_mut().div_to(&hull_part);
hull.as_mut().mul_to(&file_part);
if deg(&hull) <= 0 {
return None;
}
power.update_hull(&hull);
}
drop(discrepancy);
drop(power_float);
drop(file_float);
let possible = inverse_fixed(file, power.get_init_fac(), &common_float, &hull);
Some(PrefactorMod {
unknown: common_float,
possible,
hull: copy_poly(&hull),
})
}
fn update_hull(&mut self, hull: &Poly) {
if self.hull.eq(hull) {
return;
}
self.hull = copy_poly(hull);
self.unknown.pin_mut().gcd_to(hull);
self.possible %= &self.valid();
}
fn merge(mut self, mut other: Self, mut hull: Pin<&mut Poly>) -> Option<Self> {
self.update_hull(&hull);
other.update_hull(&hull);
self.adjust_compability(&mut other, hull.as_mut());
if deg(&hull) <= 0 {
return None;
}
let mut self_fac = new_zero();
let mut other_fac = new_zero();
let self_valid = self.valid();
let other_valid = other.valid();
let common_valid = xgcd(
self_fac.pin_mut(),
other_fac.pin_mut(),
&self_valid,
&other_valid,
);
self_fac *= &self_valid;
self_fac *= &other.possible;
other_fac *= &other_valid;
other_fac *= &self.possible;
self_fac += &other_fac;
self_fac /= &common_valid;
self.possible = self_fac;
self.unknown = gcd(&self.unknown, &other.unknown);
Some(self)
}
fn adjust_compability(&mut self, other: &mut Self, mut hull: Pin<&mut Poly>) {
let common_valid = gcd(&self.valid(), &other.valid());
let actual_valid = gcd(&add(&self.possible, &other.possible), &common_valid);
hull.as_mut().div_to(&common_valid);
hull.as_mut().mul_to(&actual_valid);
if deg(&hull) <= 0 {
return;
}
self.update_hull(&hull);
other.update_hull(&hull);
}
fn valid(&self) -> PolyPtr {
div(&self.hull, &self.unknown)
}
fn iter_inits(
&self,
red_poly: &Poly,
xorout: &InitPoly,
) -> impl Iterator<Item = (PolyPtr, PolyPtr, PolyPtr)> {
let red_unknown = gcd(&self.unknown, red_poly);
let red_valid = div(red_poly, &red_unknown);
let red_init = rem(&self.possible, &red_valid);
let mod_valid = new_polyrem(&red_valid, red_poly);
let mod_init = new_polyrem(&red_init, red_poly);
let mod_xorout = new_polyrem(&xorout.0, red_poly);
let x = new_polyrem(&new_poly(&[&1 << 1]), red_poly);
let mod_power = match xorout.1 {
InitPlace::None => new_polyrem(&new_zero(), red_poly),
InitPlace::Single(l) => powermod(&x, 8 * l as i64),
_ => panic!("Internal Error: Double"),
};
let poly_copy = copy_poly(red_poly);
let max = 1u128
.checked_shl(deg(&red_unknown) as u32)
.unwrap_or(0)
.wrapping_sub(1);
(0u128..=max).map(move |p| {
let mut current_init = new_polyrem(&new_poly(&p.to_le_bytes()), &poly_copy);
current_init *= &mod_valid;
current_init += &mod_init;
let mut current_xorout = copy_polyrem(&mod_power);
current_xorout *= ¤t_init;
current_xorout += &mod_xorout;
(
copy_poly(&poly_copy),
current_init.rep(),
current_xorout.rep(),
)
})
}
}
fn find_init(
maybe_init: &Option<PolyPtr>,
mut hull: Pin<&mut Poly>,
polys: Vec<InitPoly>,
) -> PrefactorMod {
if deg(&hull) <= 0 {
return PrefactorMod::empty();
}
let mut ret = PrefactorMod::new_init(maybe_init, &hull);
let mut power = MemoPower::new(&hull);
for (p, l) in polys {
power.update_init_fac(&l);
let file_solutions = PrefactorMod::new_file(p, &mut power, hull.as_mut());
ret = match file_solutions
.map(|f| ret.merge(f, hull.as_mut()))
.flatten()
{
Some(valid) => valid,
None => return PrefactorMod::empty(),
}
}
ret
}
fn highest_power_gcd(a: &Poly, b: &Poly) -> PolyPtr {
let mut prev = new_poly(&[1]);
let mut cur = b % a;
while !cur.eq(&prev) {
prev = copy_poly(&cur);
cur.pin_mut().sqr();
cur.pin_mut().gcd_to(a);
}
cur
}
fn inverse_fixed(mut a: PolyPtr, b: &Poly, common: &Poly, hull: &Poly) -> PolyPtr {
a /= common;
let mut b = copy_poly(b);
b /= common;
let module = div(hull, common);
if module.eq(&new_poly(&[1])) {
return new_zero();
}
let mut ma = new_polyrem(&a, &module);
let mb = new_polyrem(&b, &module);
ma /= &mb;
ma.rep()
}
fn find_prod_comb(
width: usize,
gens: &[(PolyPtr, i64)],
) -> Vec<PolyPtr> {
let mut ret: Vec<Vec<PolyPtr>> = (0..=width).map(|_| Vec::new()).collect();
for (p, l) in gens.iter() {
let retcopy: Vec<Vec<_>> = ret
.iter()
.map(|v| v.iter().map(|q| copy_poly(q)).collect())
.collect();
let mut q = copy_poly(p);
for _ in 1..=*l {
let inc_deg = deg(&q) as usize;
if inc_deg > width {
break;
}
ret[inc_deg].push(copy_poly(&q));
for (j, el) in retcopy[0..=width as usize - inc_deg].iter().enumerate() {
for m in el {
ret[j + inc_deg].push(mul(&q, m));
}
}
q *= &p;
}
}
ret.pop().unwrap()
}
fn bytes_to_poly(
bytes: &[u8],
checksum: &[u8],
width: u8,
refin: bool,
refout: bool,
wordspec: WordSpec,
) -> Option<PolyPtr> {
let new_bytes = reorder_poly_bytes(bytes, refin, wordspec);
let mut poly = new_poly_shifted(&new_bytes, width as i64);
let sum = bytes_to_int(&checksum, wordspec.output_endian);
let check_mask = 1u128.checked_shl(width as u32).unwrap_or(0).wrapping_sub(1);
if (!check_mask & sum) != 0 {
return None;
}
let check = cond_reverse(width, sum, refout);
poly += &new_poly(&check.to_le_bytes());
Some(poly)
}
fn reorder_poly_bytes(bytes: &[u8], refin: bool, wordspec: WordSpec) -> Vec<u8> {
wordspec
.iter_words(bytes)
.rev()
.map(|n| {
let n_ref = if refin {
n.reverse_bits() >> (64 - wordspec.wordsize)
} else {
n
};
int_to_bytes(n_ref, Endian::Little, wordspec.wordsize)
})
.flatten()
.collect()
}
fn cond_reverse(width: u8, value: u128, refout: bool) -> u128 {
if refout {
value.reverse_bits() >> (128 - width)
} else {
value
}
}
fn poly_to_u128(poly: &Poly) -> u128 {
u128::from_be_bytes(
poly.to_bytes(16)
.as_ref()
.unwrap()
.as_slice()
.try_into()
.unwrap(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::checksum::tests::ReverseFileSet;
use crate::crc::{CRCBuilder, CRC};
use quickcheck::{Arbitrary, Gen, TestResult};
impl Arbitrary for CRCBuilder<u128> {
fn arbitrary(g: &mut Gen) -> Self {
let width = (u8::arbitrary(g) % 128) + 1;
let poly;
let init;
let xorout;
if width != 128 {
poly = (u128::arbitrary(g) % (1 << width)) | 1;
init = u128::arbitrary(g) % (1 << width);
xorout = u128::arbitrary(g) % (1 << width);
} else {
poly = u128::arbitrary(g) | 1;
init = u128::arbitrary(g);
xorout = u128::arbitrary(g);
};
let mut builder = CRC::<u128>::with_options();
builder
.width(usize::from(width))
.poly(poly)
.init(init)
.xorout(xorout);
let combos = discrete_combos(&builder, false);
let ((refin, refout), wordspec) = *g.choose(&combos).unwrap();
builder
.refout(refout)
.refin(refin)
.wordsize(wordspec.wordsize)
.outendian(wordspec.output_endian)
.inendian(wordspec.input_endian);
builder
}
}
#[quickcheck]
fn qc_crc_rev(
files: ReverseFileSet,
crc_build: CRCBuilder<u128>,
known: (bool, bool, bool, bool, bool),
wordspec_known: (bool, bool),
) -> TestResult {
let crc = crc_build.build().unwrap();
let mut naive = CRC::<u128>::with_options();
naive.width(crc_build.width.unwrap());
if known.0 {
naive.poly(crc_build.poly.unwrap());
}
if known.1 {
naive.init(crc_build.init.unwrap());
}
if known.2 {
naive.xorout(crc_build.xorout.unwrap());
}
if known.3 {
naive.refin(crc_build.refin.unwrap());
}
if known.4 {
naive.refout(crc_build.refout.unwrap());
}
if wordspec_known.0 {
naive.wordsize(crc_build.wordsize.unwrap());
}
if wordspec_known.1 {
naive.outendian(crc_build.output_endian.unwrap());
}
let chk_files = files.with_checksums(&crc);
let reverser = reverse_crc(&naive, &chk_files, 0, false);
files.check_matching(&crc, reverser)
}
#[test]
fn test_crc32() {
let crc = CRC::<u128>::with_options()
.poly(0x04c11db7)
.width(32)
.init(0xffffffff)
.xorout(0xffffffff)
.refout(true)
.refin(true)
.build()
.unwrap();
let files = ReverseFileSet(vec![
vec![0x12u8, 0x34u8, 0x56u8],
vec![0x67u8, 0x41u8, 0xffu8],
vec![0x15u8, 0x56u8, 0x76u8, 0x1fu8],
vec![0x14u8, 0x62u8, 0x51u8, 0xa4u8, 0xd3u8],
]);
let chk_files: Vec<_> = files.with_checksums(&crc);
let mut crc_naive = CRC::<u128>::with_options();
crc_naive.width(32).refin(true).refout(true);
let reverser = reverse_crc(&crc_naive, &chk_files, 0, false);
assert!(!files.check_matching(&crc, reverser).is_failure())
}
#[test]
fn test_crc16() {
let crc = CRC::<u128>::with_options()
.poly(0x8005)
.width(16)
.refin(true)
.refout(true)
.build()
.unwrap();
let files = ReverseFileSet(vec![
vec![0x12u8, 0x34u8, 0x56u8],
vec![0x67u8, 0x41u8, 0xffu8],
vec![0x15u8, 0x56u8, 0x76u8, 0x1fu8],
vec![0x14u8, 0x62u8, 0x51u8, 0xa4u8, 0xd3u8],
]);
let chk_files = files.with_checksums(&crc);
let mut crc_naive = CRC::<u128>::with_options();
crc_naive.width(16).refin(true).refout(true);
let reverser = reverse_crc(&crc_naive, &chk_files, 0, false);
assert!(!files.check_matching(&crc, reverser).is_failure())
}
#[test]
fn error1() {
let crc = CRC::with_options()
.width(32)
.poly(0x04c11db7)
.init(0xffffffff)
.xorout(0xffffffff)
.build()
.unwrap();
let files = ReverseFileSet(vec![
vec![63, 6, 30, 42, 34, 48, 87, 50, 69, 26, 23, 98, 49, 3, 99, 86],
vec![13, 13, 41, 51, 13, 62, 99, 11],
vec![],
]);
let chk_files = files.with_checksums(&crc);
let mut crc_naive = CRC::<u128>::with_options();
crc_naive.width(32);
let reverser = reverse_crc(&crc_naive, &chk_files, 0, false);
assert!(!files.check_matching(&crc, reverser).is_failure())
}
#[test]
fn error2() {
let crc = CRC::with_options()
.width(50)
.poly(0x2f)
.init(0x5)
.xorout(0x58)
.refout(true)
.build()
.unwrap();
let files = ReverseFileSet(vec![
vec![
8, 86, 64, 28, 64, 25, 99, 40, 15, 92, 15, 66, 42, 12, 66, 80,
],
vec![73, 68, 27, 35, 16, 69, 9, 24],
vec![51, 37, 13, 18, 50, 23, 49, 9],
vec![49, 81, 64, 26, 24, 45, 36, 62],
vec![18, 42, 24, 61, 15, 22, 41, 28],
vec![16, 53, 20, 62, 50, 48, 25, 35],
vec![4, 73, 5, 16, 37, 83, 40, 91],
]);
let chk_files = files.with_checksums(&crc);
let mut crc_naive = CRC::<u128>::with_options();
crc_naive.width(50).xorout(0x58);
let reverser = reverse_crc(&crc_naive, &chk_files, 0, true);
assert!(!files.check_matching(&crc, reverser).is_failure())
}
#[test]
fn error3() {
let crc = CRC::with_options()
.width(17)
.poly(0x1)
.init(0xb)
.xorout(0x56)
.refout(true)
.build()
.unwrap();
let files = ReverseFileSet(vec![
vec![
70, 49, 21, 1, 7, 18, 4, 38, 11, 47, 30, 90, 49, 69, 17, 26, 48, 51, 91, 81, 31,
61, 35, 3, 8, 94, 90, 10, 28, 30, 22, 31, 42, 87, 67, 28, 69, 49, 85, 95, 84, 81,
30, 40, 81, 99, 92, 53, 95, 87, 17, 2, 56, 43, 33, 2, 5, 99, 32, 74, 68, 59, 47,
88, 70, 42, 34, 68, 49, 41, 13, 84, 67, 5, 45, 31, 94, 80, 92, 36, 73, 4, 29, 36,
14, 95, 79, 71, 93, 14, 39, 64, 15, 65, 56, 72, 0, 93, 35, 27, 66, 94, 21, 84, 72,
69, 87, 44, 28, 75, 76, 25, 61, 41, 50, 44, 7, 42, 56, 63, 11, 20, 48, 55, 44, 44,
45, 58, 77, 1, 83, 87, 30, 65, 27, 53, 42, 9, 39, 79, 47, 29, 78, 96, 9, 65, 98,
55, 95, 85, 0, 55, 69, 33, 63, 5, 88, 13, 61, 21, 47, 90, 16, 65, 73, 38, 95, 45,
95, 15, 54, 67, 11, 98, 50, 80, 95, 69, 88, 33, 24, 1, 8, 73, 39, 74, 59, 51, 7,
64, 51, 58, 80, 99, 1, 68, 70, 45, 77, 5, 6, 4, 32, 33, 93, 95, 13, 98, 76, 87, 96,
99, 74, 18, 23, 23, 4, 51, 47, 76, 14, 62, 91, 18, 68, 64, 38, 71, 15, 35, 51, 46,
18, 53, 38, 88, 76, 42, 28, 25, 86, 17, 10, 73, 74, 45, 98, 20, 72, 12, 64, 24, 15,
15, 87, 75, 12, 29, 16, 11, 16, 11, 16, 94, 41, 29, 97, 32, 88, 27, 96, 78, 70, 54,
49, 82, 73, 0, 19, 15, 19, 93, 36, 74, 90, 33, 94, 70, 72, 6, 73, 54, 50, 4, 9, 0,
81, 44, 72, 76, 29, 94, 85, 10, 43, 88, 15, 53, 17, 31, 53, 0, 3, 81, 84, 72, 29,
74, 66, 71, 97, 66, 44, 81, 26, 57, 8, 49, 24, 18, 9, 89, 57, 37, 91, 86, 62, 16,
41, 0, 88, 92, 54, 22, 8, 12, 12, 73, 38, 25, 67, 73, 87, 82, 48, 98, 43, 10, 65,
66, 88, 76, 70, 3, 62, 22, 89, 99, 78, 79, 68, 23, 60, 60, 58, 14, 94, 52, 62, 75,
10, 79, 89, 81, 81, 65, 71, 3, 74, 43, 14, 25, 21, 49, 1, 33, 64, 28, 72, 88, 56,
46, 95, 93, 71, 24, 85, 2, 52, 29, 81, 0, 71, 25, 94, 91, 5, 77, 20, 81, 37, 85,
74, 57, 96, 82, 17, 27, 81, 46, 96, 1, 19, 0, 51, 47, 63, 49, 65, 97, 12, 93, 78,
84, 26, 26, 4, 73, 16, 43, 60, 95, 69, 29, 94, 97, 52, 83, 2, 10, 83, 80, 30, 91,
3, 33, 72, 5, 44, 43, 44, 52, 34, 66, 99, 2, 15, 65, 52, 27, 39, 32, 58, 76, 5, 54,
92, 27, 32, 52, 1, 10, 14, 70, 52, 74, 36, 12, 94, 34, 38, 46, 15, 91, 62, 84, 86,
43, 83, 3, 56, 51, 96, 93, 14, 0, 34, 46, 79, 64, 50, 62, 25, 43, 47, 32, 36, 33,
29, 25, 76, 39, 56, 27, 66, 17, 72, 84, 68, 56, 72, 10, 55, 37, 79, 54, 85, 48, 5,
64, 14, 28, 40, 94, 30, 52, 43, 77, 47, 89, 30, 14, 43, 35, 87, 24, 14, 35,
],
vec![
2, 12, 30, 87, 4, 35, 39, 66, 11, 22, 93, 85, 87, 23, 75, 27, 91, 65, 26, 6, 29,
56, 53, 56, 76, 30, 26, 68, 28, 60, 39, 35, 35, 31, 61, 72, 27, 44, 91, 77, 13, 47,
85, 69, 91, 30, 8, 57, 59, 91, 41, 97, 34, 46, 57, 5, 94, 79, 80, 6, 97, 23, 53,
64, 6, 6, 39, 35, 38, 98, 71, 30, 59, 15, 71, 69, 96, 8, 97, 41, 26, 76, 44, 61,
79, 11, 57, 19, 83, 47, 15, 51, 14, 49, 99, 30, 3, 85, 45, 74, 85, 34, 68, 86, 90,
86, 59, 29, 8, 8, 31, 13, 15, 40, 49, 2, 42, 71, 33, 87, 55, 63, 85, 74, 68, 71,
18, 79, 86, 15, 13, 98, 89, 64, 94, 62, 3, 59, 34, 6, 42, 65, 25, 76, 22, 43, 69,
82, 17, 48, 46, 87, 68, 43, 46, 0, 35, 61, 52, 22, 79, 35, 6, 66, 68, 4, 97, 16,
76, 26, 49, 36, 90, 2, 85, 5, 46, 86, 28, 71, 4, 22, 97, 67, 7, 10, 77, 89, 5, 8,
26, 17, 25, 6, 12, 3, 22, 50, 93, 35, 80, 61, 43, 62, 45, 19, 27, 44, 42, 64, 71,
24, 90, 98, 63, 31, 96, 97, 45, 25, 74, 37, 64, 25, 38, 32, 6, 3, 91, 67, 31, 92,
49, 41, 99, 29, 32, 78, 49, 26, 16, 81, 15, 27, 93, 4, 94, 1, 8, 9, 2, 10, 6, 55,
11, 77, 51, 10, 53, 47, 91, 14, 62, 9, 87, 57, 83, 92, 3, 13, 54, 23, 77, 58, 73,
2, 37, 88, 33, 88, 76, 0, 56, 1, 96, 74, 8, 75, 21, 84, 45, 19, 69, 60, 80, 16, 3,
68, 46, 95, 38, 18, 18, 6, 18, 49, 84, 7, 64, 12, 90, 62, 90, 15, 16, 15, 20, 45,
54, 13, 54, 13, 74, 88, 22, 28, 99, 47, 13, 52, 61, 36, 66, 93, 43, 17, 33, 78, 38,
56, 87, 54, 88, 0, 58, 98, 35, 28, 19, 33, 93, 19, 68, 5, 65, 87, 30, 2, 34, 48,
15, 42, 99, 48, 29, 13, 28, 46, 83, 96, 74, 39, 14, 83, 96, 83, 93, 26, 5, 2, 51,
44, 10, 72, 24, 37, 7, 84, 74, 83, 50, 31, 41, 76, 73, 82, 9, 33, 39, 85, 11, 92,
33, 36, 72, 75, 16, 22, 22, 54, 8, 57, 59, 60, 61, 66, 76, 77, 7, 66, 17, 30, 60,
41, 87, 59, 50, 69, 3, 15, 90, 62,
],
vec![
98, 13, 33, 21, 95, 33, 29, 93, 66, 85, 9, 11, 64, 78, 41, 33, 96, 90, 94, 97, 7,
36, 36, 67, 26, 48, 96, 96, 55, 24, 27, 45, 84, 44, 86, 69, 38, 80, 95, 18, 41, 72,
56, 93, 19, 7, 66, 17, 20, 49, 85, 27, 13, 68, 49, 67, 64, 35, 56, 45, 77, 93, 52,
31, 93, 35, 91, 11, 45, 44, 90, 35, 90, 94, 90, 86, 17, 51, 67, 11, 69, 21, 83, 19,
17, 62, 0, 63, 1, 7, 97, 24, 88, 33, 12, 4, 16, 70, 69, 37, 77, 32, 66, 48, 8, 21,
47, 14, 83, 57, 27, 86, 6, 75, 65, 26, 96, 1, 94, 72, 51, 15, 40, 86, 89, 32, 36,
52, 58, 52, 58, 52, 39, 10, 18, 81, 56, 80, 49, 4, 59, 66, 53, 20, 71, 87, 25, 87,
34, 2, 37, 11, 67, 80, 96, 2, 13, 80, 75, 17, 9, 48, 70, 19, 24, 61, 23, 31, 7, 6,
42, 12, 63, 71, 21, 40,
],
]);
let chk_files = files.with_checksums(&crc);
let mut crc_naive = CRC::<u128>::with_options();
crc_naive.width(17);
let reverser = reverse_crc(&crc_naive, &chk_files, 0, true);
assert!(!files.check_matching(&crc, reverser).is_failure())
}
#[test]
fn error4() {
let crc = CRC::with_options()
.width(128)
.poly(0x67e8938640a23cb377b3bedbd54f723bu128)
.init(0xefb5b2562c0f368325e2cc4c0d0b0473)
.xorout(0xeaf1086973fee2615b0c54a58ae4cc89)
.build()
.unwrap();
let files = ReverseFileSet(vec![
vec![114, 119, 88, 38, 237, 8, 1, 0],
vec![30, 200, 192, 21, 109, 88, 89, 147],
vec![],
]);
let chk_files = files.with_checksums(&crc);
let mut crc_naive = CRC::<u128>::with_options();
crc_naive.width(128);
let reverser = reverse_crc(&crc_naive, &chk_files, 0, false);
assert!(!files.check_matching(&crc, reverser).is_failure())
}
#[test]
fn error5() {
let crc = CRC::with_options()
.width(128)
.poly(0x1u128)
.build()
.unwrap();
let files = ReverseFileSet(vec![
vec![
214, 114, 192, 142, 1, 255, 141, 1, 61, 53, 127, 104, 32, 63, 55, 137, 122, 10,
246, 127, 47, 94, 103, 3, 13, 241, 88, 247, 40, 225, 250, 14, 1, 46, 199, 43, 221,
62, 110, 14, 214, 227, 86, 75, 1, 139, 238, 224, 1, 184, 217, 16, 228, 16, 131,
168,
],
vec![
255, 229, 147, 16, 1, 111, 81, 226, 160, 153, 132, 210, 65, 168, 25, 170, 68, 63,
72, 238, 101, 46, 189, 233, 198, 71, 50, 55, 107, 82, 146, 90, 183, 154, 180, 47,
217, 211, 59, 204,
],
vec![
142, 89, 218, 180, 221, 10, 176, 190, 119, 147, 154, 191, 235, 51, 165, 255, 186,
134, 6, 104, 146, 67, 255, 34, 1, 238, 58, 246, 188, 132, 224, 0, 106, 173, 64, 1,
245, 102, 74, 76,
],
]);
let chk_files = files.with_checksums(&crc);
let mut crc_naive = CRC::<u128>::with_options();
crc_naive.width(128).poly(1);
let reverser = reverse_crc(&crc_naive, &chk_files, 0, false);
assert!(!files.check_matching(&crc, reverser).is_failure())
}
}