use crate::cnf::{Reduced, ShowSet, VarId, Weights, parse_weight};
use crate::preprocess::simplify::OriginalFate;
use crate::preprocess::{OriginalMap, OriginalTarget, VarMap};
type Map = VarMap<Reduced, Reduced>;
#[test]
fn identity_names_every_variable_as_itself() {
assert_eq!(
Map::identity(3),
Map::from_entries(vec![Some(1), Some(2), Some(3)])
);
assert!(Map::identity(0).is_empty());
}
#[test]
fn injectivity_rejects_aliasing_and_out_of_range_entries() {
let m = Map::from_entries(vec![Some(2), None, Some(-1)]);
assert!(m.is_injective(2));
assert!(!Map::from_entries(vec![Some(1), Some(-1)]).is_injective(2));
assert!(!m.is_injective(1));
}
#[test]
fn inversion_preserves_polarity_and_leaves_introduced_variables_unnamed() {
let m = Map::from_entries(vec![Some(2), None, Some(-1)]);
assert_eq!(
m.invert(3),
Map::from_entries(vec![Some(-3), Some(1), None])
);
}
#[test]
fn inversion_composes_an_earlier_stages_naming() {
let m = Map::from_entries(vec![Some(2), Some(-1)]);
let composed = m.invert_composed(2, |source_var| source_var as i32 + 10);
assert_eq!(composed, Map::from_entries(vec![Some(-11), Some(10)]));
assert_eq!(m.invert(2).invert(2), m);
}
#[test]
fn a_map_serializes_as_the_bare_array_of_entries() {
let m = Map::from_entries(vec![Some(2), None, Some(-1)]);
assert_eq!(serde_json::to_string(&m).unwrap(), "[2,null,-1]");
}
#[test]
fn carry_show_is_ascending_and_drops_introduced_variables() {
let map = Map::from_entries(vec![Some(3), None, Some(1), Some(2)]);
let target_show = ShowSet::<Reduced>::from_zero_based([0, 2]);
assert_eq!(map.carry_show(&target_show).to_dimacs(), vec![1, 3],);
let introduced = Map::from_entries(vec![None, None]);
assert!(introduced.carry_show(&target_show).is_empty());
assert!(
Map::from_entries(vec![Some(2)])
.carry_show(&target_show)
.is_empty()
);
}
#[test]
fn carry_weights_swaps_the_pair_of_a_negated_entry() {
let w = |s: &str| parse_weight(s).expect("an exact rational");
let map = Map::from_entries(vec![Some(2), Some(-1), None]);
let target = Weights::<Reduced>::from_dimacs_pairs(
&[
(1, w("1/3")),
(-1, w("2/5")),
(2, w("4/7")),
(-2, w("6/11")),
],
2,
);
assert_eq!(
map.carry_weights(&target).as_pairs(),
[
(w("6/11"), w("4/7")),
(w("1/3"), w("2/5")),
(w("1/1"), w("1/1")),
],
);
}
#[test]
fn injectivity_rejects_an_entry_naming_no_variable() {
assert!(!Map::from_entries(vec![Some(1), Some(0)]).is_injective(2));
}
#[test]
fn the_readers_of_an_entry_naming_no_variable_drop_it() {
let map = Map::from_entries(vec![Some(1), Some(0)]);
assert_eq!(
map.invert_composed(2, |source_var| source_var as i32 + 10),
Map::from_entries(vec![Some(10), None]),
);
let target_show = ShowSet::<Reduced>::from_zero_based([0, 1]);
assert_eq!(map.carry_show(&target_show).to_dimacs(), vec![1],);
let w = |s: &str| parse_weight(s).expect("an exact rational");
let target = Weights::<Reduced>::from_dimacs_pairs(&[(1, w("1/3")), (-1, w("2/5"))], 2);
assert_eq!(
map.carry_weights(&target).as_pairs(),
[(w("2/5"), w("1/3")), (w("1/1"), w("1/1"))],
);
}
#[test]
fn each_original_fate_becomes_its_own_map_entry_kind() {
let map = OriginalMap::from_fates(&[
OriginalFate::Variable {
index: 2,
same_polarity: true,
},
OriginalFate::Variable {
index: 0,
same_polarity: false,
},
OriginalFate::Forced(true),
OriginalFate::Forced(false),
OriginalFate::Unconstrained,
]);
assert_eq!(
map,
OriginalMap::from_entries(vec![
OriginalTarget::Literal(3),
OriginalTarget::Literal(-1),
OriginalTarget::Constant(true),
OriginalTarget::Constant(false),
OriginalTarget::Free,
]),
);
}
#[test]
fn the_identity_original_map_names_every_variable_as_its_own_reduced_one() {
let map = OriginalMap::identity(3);
assert_eq!(map.len(), 3, "one entry per original variable");
assert_eq!(map.get(VarId(0)), Some(OriginalTarget::Literal(1)));
assert_eq!(map.get(VarId(2)), Some(OriginalTarget::Literal(3)));
assert_eq!(
map.get(VarId(3)),
None,
"an id the original formula never had has no entry",
);
assert!(OriginalMap::identity(0).is_empty());
}