aika 0.2.1

Multi-agent coordination framework in Rust with single and multi-threaded execution engines.
Documentation
//! Core data structures for simulation including messages, events, and scheduling primitives.
//! Contains `Msg` for inter-actor communication, `Event` for actor scheduling, `AntiMsg` for
//! optimistic rollback, and local event/mail systems for efficient time-based scheduling.
use std::{
    cmp::{Ordering, Reverse},
    collections::BinaryHeap,
};

use bytemuck::{Pod, Zeroable};
use mesocarp::comms::buses::Message;
use mesocarp::scheduling::{htw::Clock, Scheduleable};

use crate::AikaError;

/// A `Msg` is a direct message between two entities that shares a piece of data of type T
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct Msg<T: Clone> {
    pub from: (usize, usize),
    pub to: (usize, usize),
    pub sent: u64,
    pub recv: u64,
    pub data: T,
}

impl<T: Clone> Msg<T> {
    /// Create a new `Msg`. If `to: Option<usize>` is set to None, the `Msg` will be broadcasted to all entities.
    pub fn new(data: T, sent: u64, recv: u64, sender_id: usize, target_actor: usize) -> Self {
        Self {
            from: (usize::MAX, sender_id),
            to: (usize::MAX, target_actor),
            sent,
            recv,
            data,
        }
    }
}

impl<T: Clone> Scheduleable for Msg<T> {
    fn time(&self) -> u64 {
        self.recv
    }

    fn commit_time(&self) -> u64 {
        self.sent
    }
}

impl<T: Clone> PartialOrd for Msg<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: Clone> PartialEq for Msg<T> {
    fn eq(&self, other: &Self) -> bool {
        self.from == other.from
            && self.to == other.to
            && self.sent == other.sent
            && self.recv == other.recv
    }
}

impl<T: Clone> Eq for Msg<T> {}

impl<T: Clone> Ord for Msg<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.recv
            .cmp(&other.recv)
            .then_with(|| self.sent.cmp(&other.sent))
            .then_with(|| self.from.cmp(&other.from))
            .then_with(|| self.to.cmp(&other.to))
    }
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
/// An `AntiMsg` allows you to directly cancel messages with the same metadata in an optimistic execution environment
pub(crate) struct AntiMsg {
    pub(crate) sent: u64,
    pub(crate) received: u64,
    pub(crate) from: (usize, usize),
    pub(crate) to: (usize, usize),
}

impl AntiMsg {
    /// Create a new `AntiMsg`. Note that you won't need to manual call this to maintain synchronization, this is just for flexibility.
    pub fn new(sent: u64, received: u64, from: (usize, usize), to: (usize, usize)) -> Self {
        AntiMsg {
            sent,
            received,
            from,
            to,
        }
    }

    /// Annihilate a `Msg<T>` and `AntiMsg` pair.
    pub fn annihilate<T: Clone>(&self, other: &Msg<T>) -> bool {
        self.sent == other.sent
            && self.received == other.recv
            && self.from == other.from
            && self.to == other.to
    }
}

impl PartialEq for AntiMsg {
    fn eq(&self, other: &Self) -> bool {
        self.sent == other.sent && self.received == other.received
    }
}

impl Eq for AntiMsg {}

impl PartialOrd for AntiMsg {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for AntiMsg {
    fn cmp(&self, other: &Self) -> Ordering {
        self.received.partial_cmp(&other.received).unwrap()
    }
}

impl Scheduleable for AntiMsg {
    fn time(&self) -> u64 {
        self.received
    }
    fn commit_time(&self) -> u64 {
        self.sent
    }
}

unsafe impl Pod for AntiMsg {}
unsafe impl Zeroable for AntiMsg {}

/// An object that can be transfered between `Planet` threads during optimistic execution
#[derive(Debug, Clone, Copy)]
pub(crate) enum Transfer<T: Pod + Zeroable + Clone> {
    Msg(Msg<T>),
    AntiMsg(AntiMsg),
}

impl<T: Pod + Zeroable + Clone> Message for Transfer<T> {
    fn from(&self) -> usize {
        match self {
            Transfer::Msg(msg) => msg.from.0,
            Transfer::AntiMsg(anti_msg) => anti_msg.from.0,
        }
    }

    fn to(&self) -> Option<usize> {
        match self {
            Transfer::Msg(msg) => Some(msg.to.0),
            Transfer::AntiMsg(anti_msg) => Some(anti_msg.to.0),
        }
    }
}

impl<T: Pod + Zeroable + Clone> Transfer<T> {
    pub fn actor_from(&self) -> usize {
        match self {
            Transfer::Msg(msg) => msg.from.1,
            Transfer::AntiMsg(anti_msg) => anti_msg.from.1,
        }
    }

    pub fn actor_to(&self) -> usize {
        match self {
            Transfer::Msg(msg) => msg.to.1,
            Transfer::AntiMsg(anti_msg) => anti_msg.to.1,
        }
    }
}

impl<T: Pod + Zeroable + Clone> Scheduleable for Transfer<T> {
    fn time(&self) -> u64 {
        match self {
            Transfer::Msg(msg) => msg.time(),
            Transfer::AntiMsg(anti_msg) => anti_msg.time(),
        }
    }

    fn commit_time(&self) -> u64 {
        match self {
            Transfer::Msg(msg) => msg.commit_time(),
            Transfer::AntiMsg(anti_msg) => anti_msg.commit_time(),
        }
    }
}

impl<T: Pod + Zeroable + Clone> PartialOrd for Transfer<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: Pod + Zeroable + Clone> PartialEq for Transfer<T> {
    fn eq(&self, other: &Self) -> bool {
        self.from() == other.from()
            && self.to() == other.to()
            && self.commit_time() == other.commit_time()
            && self.time() == other.time()
    }
}

impl<T: Pod + Zeroable + Clone> Eq for Transfer<T> {}

impl<T: Pod + Zeroable + Clone> Ord for Transfer<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.time().cmp(&other.time())
    }
}

unsafe impl<T: Pod + Zeroable + Clone> Send for Transfer<T> {}
unsafe impl<T: Pod + Zeroable + Clone> Sync for Transfer<T> {}

/// A SchedulingTask that an `Actor` or `ConnectedActor` can take.
#[derive(Copy, Clone, Debug)]
pub enum SchedulingTask {
    Timeout(u64),
    Schedule(u64),
    Trigger { time: u64, idx: usize },
    Wait,
    Break,
}

/// An event that can be scheduled in a simulation. This is used to trigger an actor, or schedule another event.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct Event {
    pub time: u64,
    pub commit_time: u64,
    pub actor: usize,
    pub task: SchedulingTask,
}

impl Event {
    pub fn new(commit_time: u64, time: u64, actor: usize, task: SchedulingTask) -> Self {
        Self {
            commit_time,
            time,
            actor,
            task,
        }
    }

    pub fn time(&self) -> u64 {
        self.time
    }
}

impl PartialEq for Event {
    fn eq(&self, other: &Self) -> bool {
        self.time == other.time
    }
}
impl Eq for Event {}

impl PartialOrd for Event {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for Event {
    fn cmp(&self, other: &Self) -> Ordering {
        self.time.partial_cmp(&other.time).unwrap()
    }
}

impl Scheduleable for Event {
    fn time(&self) -> u64 {
        self.time
    }
    fn commit_time(&self) -> u64 {
        self.commit_time
    }
}

unsafe impl Zeroable for Event {}
unsafe impl Pod for Event {}

unsafe impl Send for Event {}
unsafe impl Sync for Event {}

#[derive(Debug)]
/// Thread-local scheduler of a single object type in simulation time.
pub struct LocalScheduler<const CLOCK_BW: usize, const CLOCK_SCALES: usize, T: Scheduleable> {
    pub(crate) overflow: BinaryHeap<Reverse<T>>,
    pub(crate) clock: Clock<T, CLOCK_BW, CLOCK_SCALES>,
}

impl<const CLOCK_BW: usize, const CLOCK_SCALES: usize, T: Scheduleable>
    LocalScheduler<CLOCK_BW, CLOCK_SCALES, T>
{
    pub fn new() -> Result<Self, AikaError> {
        let overflow = BinaryHeap::new();
        let clock = Clock::new()?;
        Ok(Self { overflow, clock })
    }

    pub fn insert(&mut self, object: T) {
        let possible_overflow = self.clock.insert(object);
        if possible_overflow.is_err() {
            let object = possible_overflow.err().unwrap();
            self.overflow.push(Reverse(object));
        }
    }

    pub fn now(&self) -> u64 {
        self.clock.time
    }

    pub fn increment(&mut self) {
        self.clock.increment(&mut self.overflow)
    }

    pub fn tick(&mut self) -> Result<Vec<T>, AikaError> {
        Ok(self.clock.tick()?)
    }

    pub fn rollback(&mut self, time: u64) {
        self.clock.rollback(&mut self.overflow, time)
    }
}

unsafe impl<const CLOCK_SLOTS: usize, const CLOCK_HEIGHT: usize, T: Scheduleable> Send
    for LocalScheduler<CLOCK_SLOTS, CLOCK_HEIGHT, T>
{
}
unsafe impl<const CLOCK_SLOTS: usize, const CLOCK_HEIGHT: usize, T: Scheduleable> Sync
    for LocalScheduler<CLOCK_SLOTS, CLOCK_HEIGHT, T>
{
}