use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
use serde_json::Value;
pub type MemoCache<T> = Rc<RefCell<HashMap<usize, HashMap<String, (Vec<Value>, T)>>>>;
pub fn internal_memof1_call_by<F, T>(caches: MemoCache<T>, address: usize, key: String, args: Vec<Value>, f: F) -> Result<T, String>
where
T: Debug + Clone,
F: Fn() -> Result<T, String>,
{
let mut cache = caches.borrow_mut();
let f_dict = cache.entry(address).or_default();
let value = f_dict.get(&key);
if let Some((old_args, v)) = value {
if old_args == &args {
Ok(v.to_owned())
} else {
let v = f()?;
f_dict.insert(key, (args, v.to_owned()));
Ok(v)
}
} else {
let v = f()?;
f_dict.insert(key, (args, v.to_owned()));
Ok(v)
}
}
#[macro_export]
macro_rules! memo1_call_by {
($f:ident, $cache:expr, $key:expr, $arg1:expr) => {
$crate::internal_memof1_call_by(
$cache.to_owned(),
$f as usize,
$key.to_owned(),
vec![$crate::util::cast_into_json($arg1)],
move || $f($cache.to_owned(), $arg1),
)
};
($f:ident, $cache:expr, $key:expr, $arg1:expr,) => {
$crate::memo1_call_by!($f, $cache, $key, $arg1);
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr) => {
$crate::internal_memof1_call_by(
$cache.to_owned(),
$f as usize,
$key.to_owned(),
vec![$crate::util::cast_into_json($arg1), $crate::util::cast_into_json($arg2)],
move || $f($cache.to_owned(), $arg1, $arg2),
)
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr,) => {
$crate::memo1_call_by!($f, $cache, $key, $arg1, $arg2);
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr, $arg3:expr) => {
$crate::internal_memof1_call_by(
$cache.to_owned(),
$f as usize,
$key.to_owned(),
vec![
$crate::util::cast_into_json($arg1),
$crate::util::cast_into_json($arg2),
$crate::util::cast_into_json($arg3),
],
move || $f($cache.to_owned(), $arg1, $arg2, $arg3),
)
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr, $arg3:expr,) => {
$crate::memo1_call_by!($f, $cache, $key, $arg1, $arg2, $arg3);
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr) => {
$crate::internal_memof1_call_by(
$cache.to_owned(),
$f as usize,
$key.to_owned(),
vec![
$crate::util::cast_into_json($arg1),
$crate::util::cast_into_json($arg2),
$crate::util::cast_into_json($arg3),
$crate::util::cast_into_json($arg4),
],
move || $f($cache.to_owned(), $arg1, $arg2, $arg3, $arg4),
)
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr,) => {
$crate::memo1_call_by!($f, $cache, $key, $arg1, $arg2, $arg3, $arg4);
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => {
$crate::internal_memof1_call_by(
$cache.to_owned(),
$f as usize,
$key.to_owned(),
vec![
$crate::util::cast_into_json($arg1),
$crate::util::cast_into_json($arg2),
$crate::util::cast_into_json($arg3),
$crate::util::cast_into_json($arg4),
$crate::util::cast_into_json($arg5),
],
move || $f($cache.to_owned(), $arg1, $arg2, $arg3, $arg4, $arg5),
)
};
($f:ident, $cache:expr, $key:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => {
$crate::memo1_call_by!($f, $cache, $key, $arg1, $arg2, $arg3, $arg4, $arg5);
};
}
pub use memo1_call_by;