pub use crate::games::piecewise::Pl;
use crate::games::piecewise::{combine, rdiv, sub_pl};
use crate::games::Game;
use crate::scalar::{Rational, Scalar};
use std::cmp::Ordering;
pub(crate) fn meeting_temperature(e: &Pl) -> Rational {
let two = Rational::from_int(2);
let d_at = |t: &Rational| e.value_at(t).sub(&two.mul(t));
let t0 = Rational::zero();
if d_at(&t0).sign() != Ordering::Greater {
return t0; }
let ts: Vec<Rational> = e.pts.iter().map(|(t, _)| t.clone()).collect();
for w in ts.windows(2) {
let (a, b) = (&w[0], &w[1]);
let (da, db) = (d_at(a), d_at(b));
if da.sign() == Ordering::Equal {
return a.clone();
}
if da.sign() == Ordering::Greater && db.sign() != Ordering::Greater {
let denom = da.sub(&db);
return a.add(&b.sub(a).mul(&rdiv(&da, &denom)));
}
}
let last = e.pts.last().unwrap().1.clone();
rdiv(&last, &two)
}
pub(crate) fn freeze(raw: &Pl, tau: &Rational, mast: &Rational, left: bool) -> Pl {
let mut pts = Vec::new();
for (t, v) in &raw.pts {
if t.cmp(tau) == Ordering::Less {
let shifted = if left { v.sub(t) } else { v.add(t) };
pts.push((t.clone(), shifted));
}
}
pts.push((tau.clone(), mast.clone()));
Pl { pts }.cleaned()
}
#[derive(Clone, Debug)]
pub struct Thermograph {
pub mast: Rational,
pub temperature: Rational,
pub left_wall: Pl,
pub right_wall: Pl,
}
impl Thermograph {
pub fn mean(&self) -> Rational {
self.mast.clone()
}
pub fn left_stop(&self) -> Rational {
self.left_wall.value_at(&Rational::zero())
}
pub fn right_stop(&self) -> Rational {
self.right_wall.value_at(&Rational::zero())
}
pub fn cooled_stops(&self, t: &Rational) -> (Rational, Rational) {
(self.left_wall.value_at(t), self.right_wall.value_at(t))
}
}
pub(crate) fn walls_with<F>(g: &Game, fold: F) -> Option<(Pl, Pl, Rational, Rational)>
where
F: Fn(&Pl, &Pl, bool) -> Pl + Copy,
{
let g = g.canonical();
if g.is_number() {
let v = g.number_value()?.as_rational()?; let c = Pl::constant(v.clone());
return Some((c.clone(), c, v, Rational::from_int(-1)));
}
if g.left().is_empty() || g.right().is_empty() {
return None;
}
let mut left_raw: Option<Pl> = None;
for l in g.left() {
let rw = walls_with(l, fold)?.1;
left_raw = Some(match left_raw {
None => rw,
Some(acc) => fold(&acc, &rw, true),
});
}
let mut right_raw: Option<Pl> = None;
for r in g.right() {
let lw = walls_with(r, fold)?.0;
right_raw = Some(match right_raw {
None => lw,
Some(acc) => fold(&acc, &lw, false),
});
}
let (left_raw, right_raw) = (left_raw.unwrap(), right_raw.unwrap());
let e = sub_pl(&left_raw, &right_raw);
let tau = meeting_temperature(&e);
let mast = left_raw.value_at(&tau).sub(&tau);
let left_wall = freeze(&left_raw, &tau, &mast, true);
let right_wall = freeze(&right_raw, &tau, &mast, false);
Some((left_wall, right_wall, mast, tau))
}
fn walls(g: &Game) -> Option<(Pl, Pl, Rational, Rational)> {
walls_with(g, combine)
}
pub fn thermograph(g: &Game) -> Option<Thermograph> {
let (left_wall, right_wall, mast, temperature) = walls(g)?;
Some(Thermograph {
mast,
temperature,
left_wall,
right_wall,
})
}
pub fn temperature(g: &Game) -> Option<Rational> {
thermograph(g).map(|t| t.temperature)
}
pub fn mean_value(g: &Game) -> Option<Rational> {
thermograph(g).map(|t| t.mast)
}
pub fn left_stop(g: &Game) -> Option<Rational> {
thermograph(g).map(|t| t.left_stop())
}
pub fn right_stop(g: &Game) -> Option<Rational> {
thermograph(g).map(|t| t.right_stop())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::games::piecewise::req;
fn rat(n: i128, d: i128) -> Rational {
Rational::new(n, d)
}
fn int(n: i128) -> Rational {
Rational::from_int(n)
}
#[test]
fn numbers_are_cold() {
for n in [-3i128, 0, 5] {
let th = thermograph(&Game::integer(n)).unwrap();
assert!(req(&th.mast, &int(n)));
assert!(req(&th.temperature, &int(-1)));
assert!(req(&th.left_stop(), &int(n)) && req(&th.right_stop(), &int(n)));
}
let half = Game::new(vec![Game::integer(0)], vec![Game::integer(1)]);
let th = thermograph(&half).unwrap();
assert!(req(&th.mast, &rat(1, 2)));
assert!(req(&th.temperature, &int(-1)));
}
#[test]
fn switches_have_classic_thermographs() {
for (a, b) in [(1i128, -1i128), (2, -2), (3, -1), (0, -4), (5, 1)] {
let g = Game::switch(a, b);
let th = thermograph(&g).unwrap();
assert!(req(&th.temperature, &rat(a - b, 2)), "temp {{{a}|{b}}}");
assert!(req(&th.mast, &rat(a + b, 2)), "mean {{{a}|{b}}}");
assert!(req(&th.left_stop(), &int(a)), "LS {{{a}|{b}}}");
assert!(req(&th.right_stop(), &int(b)), "RS {{{a}|{b}}}");
}
}
#[test]
fn infinitesimals_have_temperature_zero() {
for g in [Game::star(), Game::up(), Game::up().neg()] {
let th = thermograph(&g).unwrap();
assert!(req(&th.temperature, &int(0)), "temp of {}", g.display());
assert!(req(&th.mast, &int(0)), "mean of {}", g.display());
}
let star2 = Game::new(
vec![Game::integer(0), Game::star()],
vec![Game::integer(0), Game::star()],
);
let th = thermograph(&star2).unwrap();
assert!(req(&th.temperature, &int(0)));
assert!(req(&th.mast, &int(0)));
}
#[test]
fn nested_hot_game() {
let g = Game::new(vec![Game::integer(3)], vec![Game::switch(1, -1)]);
let th = thermograph(&g).unwrap();
assert!(req(&th.mast, &rat(3, 2)), "mast {:?}", th.mast);
assert!(
req(&th.temperature, &rat(3, 2)),
"temp {:?}",
th.temperature
);
assert!(req(&th.left_stop(), &int(3)));
assert!(req(&th.right_stop(), &int(1))); }
#[test]
fn mean_value_is_additive() {
let g = Game::switch(1, -1); let h = Game::switch(2, 0); let mg = mean_value(&g).unwrap();
let mh = mean_value(&h).unwrap();
let mgh = mean_value(&g.add(&h)).unwrap();
assert!(req(&mgh, &mg.add(&mh)), "mean(G+H) = {:?}", mgh);
let a = Game::switch(4, -4); let b = Game::integer(3); assert!(req(&mean_value(&a.add(&b)).unwrap(), &int(3)));
}
#[test]
fn cooled_stops_freeze_at_temperature() {
let g = Game::switch(3, -1); let th = thermograph(&g).unwrap();
let (l1, r1) = th.cooled_stops(&int(1));
assert!(l1.cmp(&r1) == Ordering::Greater);
let (l2, r2) = th.cooled_stops(&int(2));
assert!(req(&l2, &th.mast) && req(&r2, &th.mast));
let (l3, r3) = th.cooled_stops(&int(5));
assert!(req(&l3, &th.mast) && req(&r3, &th.mast));
}
}