use rustc_hash::FxHashMap as HashMap;
use std::cell::{Cell, RefCell};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum Reading {
RefitDetection,
OldTracking,
RefitCandidacy,
CarrierResidual,
}
const CAPACITY: usize = 1 << 16;
thread_local! {
static MEMO: RefCell<HashMap<(Reading, [u64; 2]), Option<f64>>> = RefCell::new(HashMap::default());
static SIGN_TERMS: RefCell<HashMap<[u64; 2], Option<[f64; 4]>>> = RefCell::new(HashMap::default());
static HITS: Cell<u64> = const { Cell::new(0) };
static MISSES: Cell<u64> = const { Cell::new(0) };
}
thread_local! {
static HATCH_OFF: std::cell::OnceCell<bool> = const { std::cell::OnceCell::new() };
}
pub(crate) fn memo_on() -> bool {
!HATCH_OFF.with(|cell| *cell.get_or_init(|| std::env::var("BREP_READING_MEMO").as_deref() == Ok("0")))
}
pub(crate) fn lookup(reading: Reading, key: [u64; 2]) -> Option<Option<f64>> {
if !memo_on() { return None; }
let value = MEMO.with(|memo| memo.borrow().get(&(reading, key)).copied());
if value.is_some() { note(true); }
value
}
pub(crate) struct SignFoldScope(bool);
thread_local! {
static FOLD_TERMS: RefCell<Option<HashMap<[u64; 2], [f64; 4]>>> = const { RefCell::new(None) };
static SIGN_NEW: Cell<u64> = const { Cell::new(0) };
}
impl SignFoldScope {
pub(crate) fn begin() -> Self {
Self(FOLD_TERMS.with(|memo| {
let mut memo = memo.borrow_mut();
if memo.is_some() { false } else { *memo = Some(HashMap::default()); true }
}))
}
}
impl Drop for SignFoldScope {
fn drop(&mut self) {
if self.0 { FOLD_TERMS.with(|memo| *memo.borrow_mut() = None); }
}
}
pub(crate) fn sign_new() -> u64 { SIGN_NEW.with(Cell::get) }
fn retain_sign(key: [u64; 2], value: Option<[f64; 4]>) {
if let Some(value) = value {
FOLD_TERMS.with(|memo| { if let Some(memo) = memo.borrow_mut().as_mut() { memo.insert(key, value); } });
}
}
pub(crate) fn memoized(reading: Reading, key: [u64; 2], read: impl FnOnce() -> Option<f64>) -> Option<f64> {
if !memo_on() {
return read();
}
if let Some(value) = MEMO.with(|memo| memo.borrow().get(&(reading, key)).copied()) {
HITS.with(|hits| hits.set(hits.get() + 1));
return value;
}
MISSES.with(|misses| misses.set(misses.get() + 1));
let value = read();
MEMO.with(|memo| {
let mut memo = memo.borrow_mut();
if memo.len() >= CAPACITY {
memo.clear();
}
memo.insert((reading, key), value);
});
value
}
pub(crate) fn memoized_sign_terms(key: [u64; 2], read: impl FnOnce() -> Option<[f64; 4]>) -> Option<[f64; 4]> {
if !memo_on() {
return read();
}
if let Some(value) = FOLD_TERMS.with(|memo| memo.borrow().as_ref().and_then(|m| m.get(&key).copied())) {
note(true);
return Some(value);
}
if let Some(value) = SIGN_TERMS.with(|memo| memo.borrow().get(&key).copied()) {
note(true);
retain_sign(key, value);
return value;
}
note(false);
SIGN_NEW.with(|n| n.set(n.get() + 1));
let value = read();
retain_sign(key, value);
SIGN_TERMS.with(|memo| {
let mut memo = memo.borrow_mut();
if memo.len() >= CAPACITY {
memo.clear();
}
memo.insert(key, value);
});
value
}
pub(crate) fn note(hit: bool) {
if hit {
HITS.with(|hits| hits.set(hits.get() + 1));
} else {
MISSES.with(|misses| misses.set(misses.get() + 1));
}
}
pub(crate) const fn capacity() -> usize {
CAPACITY
}
pub(crate) fn clear() {
MEMO.with(|memo| memo.borrow_mut().clear());
SIGN_TERMS.with(|memo| memo.borrow_mut().clear());
FOLD_TERMS.with(|memo| { if let Some(memo) = memo.borrow_mut().as_mut() { memo.clear(); } });
crate::heal::clear_claim_memo();
crate::boolean::clear_candidate_memo();
crate::imprint::clear_window_memo();
crate::imprint::clear_touch_memo();
}
pub(crate) fn counts() -> (u64, u64) {
(HITS.with(|hits| hits.get()), MISSES.with(|misses| misses.get()))
}