use crate::games::partizan::Game;
use crate::linalg::integer::reduce_integer_vector;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GameRelation {
pub coeffs: Vec<i128>,
}
impl GameRelation {
pub fn new(coeffs: Vec<i128>) -> Self {
assert!(
coeffs.iter().any(|&c| c != 0),
"game relation must be nonzero"
);
GameRelation { coeffs }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GameRelationCertificate {
pub coeffs: Vec<i128>,
pub value_key: String,
pub independent: bool,
}
impl GameRelationCertificate {
pub fn display(&self) -> String {
self.to_string()
}
}
impl fmt::Display for GameRelationCertificate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"GameRelationCertificate(coeffs={:?}, value_key={:?}, independent={})",
self.coeffs, self.value_key, self.independent
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RelationSearchCertificate {
pub bound: i128,
pub exhaustive: bool,
pub candidate_count: Option<usize>,
pub relations: Vec<GameRelationCertificate>,
}
impl RelationSearchCertificate {
pub fn display(&self) -> String {
self.to_string()
}
}
impl fmt::Display for RelationSearchCertificate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let n = self.relations.len();
match (self.exhaustive, self.candidate_count) {
(true, Some(c)) => write!(
f,
"RelationSearchCertificate(exhaustive, bound={}, {c} candidate(s) searched, \
{n} relation(s) accepted)",
self.bound
),
(true, None) => write!(
f,
"RelationSearchCertificate(exhaustive, bound={}, {n} relation(s) accepted)",
self.bound
),
(false, Some(c)) => write!(
f,
"RelationSearchCertificate(INCOMPLETE — singleton-only search, bound={}, \
{c} candidate(s) unexplored, {n} relation(s) accepted)",
self.bound
),
(false, None) => write!(
f,
"RelationSearchCertificate(INCOMPLETE — box too large to count, bound={}, \
{n} relation(s) accepted)",
self.bound
),
}
}
}
pub(super) fn relation_search_certificate(
gens: &[Game],
bound: i128,
exhaustive: bool,
candidate_count: Option<usize>,
relations: &[GameRelation],
independent: bool,
) -> RelationSearchCertificate {
RelationSearchCertificate {
bound,
exhaustive,
candidate_count,
relations: relation_certificates(gens, relations, independent),
}
}
pub(super) fn relation_certificates(
gens: &[Game],
relations: &[GameRelation],
trust_independent: bool,
) -> Vec<GameRelationCertificate> {
let mut previous = Vec::new();
relations
.iter()
.map(|rel| {
let mut reduced = rel.coeffs.clone();
reduce_integer_vector(&mut reduced, previous.clone());
let independent = trust_independent || reduced.iter().any(|&c| c != 0);
previous.push(rel.coeffs.clone());
GameRelationCertificate {
coeffs: rel.coeffs.clone(),
value_key: eval_relation(gens, &rel.coeffs).canonical_string(),
independent,
}
})
.collect()
}
pub(super) fn eval_relation(gens: &[Game], coeffs: &[i128]) -> Game {
let mut acc = Game::zero();
for (g, &c) in gens.iter().zip(coeffs) {
acc = acc.add(&g.times_int(c));
}
acc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn game_relation_certificate_render_byte_matches_py_repr() {
let cert = GameRelationCertificate {
coeffs: vec![1, -1],
value_key: "0".to_string(),
independent: true,
};
assert_eq!(
cert.to_string(),
"GameRelationCertificate(coeffs=[1, -1], value_key=\"0\", independent=true)"
);
assert_eq!(cert.display(), cert.to_string());
}
#[test]
fn relation_search_certificate_incomplete_render_is_visibly_incomplete() {
let cert = RelationSearchCertificate {
bound: 3,
exhaustive: false,
candidate_count: Some(342),
relations: vec![],
};
assert_eq!(
cert.to_string(),
"RelationSearchCertificate(INCOMPLETE — singleton-only search, bound=3, \
342 candidate(s) unexplored, 0 relation(s) accepted)"
);
assert!(cert.to_string().contains("INCOMPLETE"));
assert_eq!(cert.display(), cert.to_string());
}
#[test]
fn relation_search_certificate_exhaustive_render_says_exhaustive() {
let rel = GameRelationCertificate {
coeffs: vec![1, 1, -1],
value_key: "0".to_string(),
independent: true,
};
let cert = RelationSearchCertificate {
bound: 1,
exhaustive: true,
candidate_count: Some(8),
relations: vec![rel],
};
assert_eq!(
cert.to_string(),
"RelationSearchCertificate(exhaustive, bound=1, 8 candidate(s) searched, \
1 relation(s) accepted)"
);
}
}