use crate::context::Context;
use crate::export::{Export, Result};
use crate::path::Path;
use crate::sensor_id::SensorId;
use crate::slot::Slot;
use crate::vm::vm_status::VMStatus;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct RoundVM {
context: Context,
status: VMStatus,
export_stack: Vec<Export>,
isolated: bool,
}
impl RoundVM {
pub fn new(context: Context) -> Self {
Self {
context,
status: VMStatus::new(),
export_stack: vec![],
isolated: false,
}
}
pub fn export_data(&mut self) -> &mut Export {
self.export_stack.first_mut().unwrap()
}
pub fn self_id(&self) -> &i32 {
self.context.self_id()
}
pub fn register_root<A: 'static + Clone>(&mut self, v: A) {
self.export_data().put(Path::new(), v.clone());
}
pub fn neighbor(&self) -> &Option<i32> {
self.status.neighbour()
}
pub fn index(&self) -> i32 {
self.status.index()
}
pub fn previous_round_val<A: 'static + Clone + FromStr>(&self) -> Result<A> {
self.context
.read_export_value::<A>(self.self_id(), self.status.path())
}
pub fn neighbor_val<A: 'static + Clone + FromStr>(&self) -> Result<A> {
let n: Result<i32> = self.neighbor().ok_or("Isolated".into());
self.context.read_export_value::<A>(&n?, self.status.path())
}
pub fn local_sense<A: 'static>(&self, sensor_id: &SensorId) -> Option<&A> {
self.context.local_sense::<A>(sensor_id)
}
pub fn nbr_sense<A: 'static>(&self, sensor_id: &SensorId) -> Option<&A> {
self.neighbor()
.map(|id| self.context.nbr_sense::<A>(sensor_id, &id))
.flatten()
}
pub fn locally<A: Clone + 'static + FromStr, F>(&mut self, expr: F) -> A
where
F: Fn(&mut RoundVM) -> A,
{
let current_neighbour = *self.neighbor();
self.status.fold_out();
let result = expr(self);
self.status.fold_into(current_neighbour);
result
}
pub fn folded_eval<A: Clone + 'static, F>(&mut self, expr: F, id: i32) -> Option<A>
where
F: Fn(&mut RoundVM) -> A,
{
self.status.push();
self.status.fold_into(Some(id));
let result = expr(self);
self.status.pop();
Some(result)
}
pub fn nest<A: Clone + 'static + FromStr, F>(
&mut self,
slot: Slot,
write: bool,
inc: bool,
expr: F,
) -> A
where
F: Fn(&mut RoundVM) -> A,
{
self.status.push();
self.status.nest(slot);
let val = expr(self);
let res = if write {
let cloned_path = self.status.path().clone();
self.export_data()
.get::<A>(&cloned_path)
.unwrap_or(
self.export_data()
.put_lazy_and_return(cloned_path, || val.clone()),
)
.clone()
} else {
val
};
if inc {
self.status.pop();
self.status.inc_index();
} else {
self.status.pop();
}
res
}
pub fn aligned_neighbours<A: 'static + FromStr + Clone>(&self) -> Vec<i32> {
let mut tmp: Vec<i32> = Vec::new();
if !self.isolated {
tmp = self
.context
.exports()
.clone()
.into_iter()
.filter(|(id, _)| id != self.self_id())
.filter(|(_, export)| {
self.status.path().is_root() || export.get::<A>(self.status.path()).is_ok()
})
.map(|(id, _)| id)
.collect();
tmp.insert(0, *self.self_id());
}
tmp
}
pub fn isolate<A, F>(&mut self, expr: F) -> A
where
F: Fn(&mut RoundVM) -> A,
{
let was_isolated = self.isolated;
self.isolated = true;
let result = expr(self);
self.isolated = was_isolated;
result
}
pub fn unless_folding_on_others(&self) -> bool {
match self.neighbor() {
Some(neighbor) => neighbor == self.self_id(),
None => true,
}
}
pub fn only_when_folding_on_self(&self) -> bool {
match self.neighbor() {
Some(neighbor) => neighbor == self.self_id(),
_ => false,
}
}
pub fn context(&self) -> &Context {
&self.context
}
pub fn new_export_stack(&mut self) {
self.export_stack.push(Export::new())
}
}
#[cfg(test)]
mod tests {
use crate::context::Context;
use crate::export;
use crate::export::Export;
use crate::path;
use crate::path::Path;
use crate::sensor_id::{sensor, SensorId};
use crate::slot::Slot::{Nbr, Rep};
use crate::vm::round_vm::RoundVM;
use crate::vm::vm_status::VMStatus;
use std::any::Any;
use std::collections::HashMap;
use std::rc::Rc;
fn round_vm_builder() -> RoundVM {
let local_sensor =
HashMap::from([(sensor("sensor1"), Rc::new(Box::new(10) as Box<dyn Any>))]);
let nbr_sensor = HashMap::from([(
sensor("sensor1"),
HashMap::from([(0, Rc::new(Box::new(4) as Box<dyn Any>))]),
)]);
let exports = HashMap::from([
(
7,
export!((path!(Nbr(0), Rep(0)), 10)),
),
(
0,
export!((path!(Nbr(0), Rep(0)), 2)),
),
]);
let context = Context::new(7, local_sensor, nbr_sensor, exports);
let mut vm = RoundVM::new(context);
vm.export_stack.push(export!((Path::new(), 0)));
let status = VMStatus::new();
vm.status.fold_into(Some(0));
vm
}
fn expr(vm: &mut RoundVM) -> i32 {
5 * 3
}
#[test]
fn test_export_data() {
let mut vm = round_vm_builder();
assert_eq!(vm.export_data().root::<i32>(), 0)
}
#[test]
fn test_register_root() {
let mut vm = round_vm_builder();
vm.register_root(5 * 3);
assert_eq!(vm.export_data().root::<i32>(), 15)
}
#[test]
fn test_folded_eval() {
let mut vm = round_vm_builder();
let result = vm.folded_eval(expr, 7);
assert_eq!(round_vm_builder().status, vm.status);
assert_eq!(result.unwrap(), 15)
}
#[test]
fn test_previous_round_val() {
let mut vm = round_vm_builder();
vm.status.nest(Rep(0));
vm.status.nest(Nbr(0));
assert_eq!(vm.previous_round_val::<i32>().unwrap(), 10)
}
#[test]
fn test_neighbor_val() {
let mut vm = round_vm_builder();
vm.status.nest(Rep(0));
vm.status.nest(Nbr(0));
assert_eq!(vm.neighbor_val::<i32>().unwrap(), 2)
}
#[test]
fn test_local_sense() {
let vm = round_vm_builder();
assert_eq!(
vm.local_sense::<i32>(&SensorId::new("sensor1".to_string()))
.unwrap(),
&10
)
}
#[test]
fn test_nbr_sense() {
let vm = round_vm_builder();
assert_eq!(
vm.nbr_sense::<i32>(&SensorId::new("sensor1".to_string()))
.unwrap(),
&4
)
}
#[test]
fn test_aligned_neighbours() {
let vm = round_vm_builder();
assert_eq!(vm.aligned_neighbours::<i32>(), vec![7, 0])
}
#[test]
fn test_isolate() {
let mut vm = round_vm_builder();
let was_isolated = vm.isolated.clone();
let result = vm.isolate(|vm| 5 * 3);
assert_eq!(vm.isolated, was_isolated);
assert_eq!(result, 15)
}
#[test]
fn test_unless_folding_on_others() {
let mut vm = round_vm_builder();
assert!(!vm.unless_folding_on_others());
vm.status.fold_into(None);
assert!(vm.unless_folding_on_others());
vm.status.fold_into(Some(7));
assert!(vm.unless_folding_on_others());
}
#[test]
fn test_only_when_folding_on_self() {
let mut vm = round_vm_builder();
assert!(!vm.only_when_folding_on_self());
vm.status.fold_into(None);
assert!(!vm.only_when_folding_on_self());
vm.status.fold_into(Some(7));
assert!(vm.only_when_folding_on_self());
}
}