use crate::streams::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum LogicState {
ReturnableFalse,
NeitherReturnable,
ReturnableTrue,
}
impl LogicState {
#[inline]
const fn not_returnable_true(&mut self) {
if let Self::ReturnableTrue = self {
*self = Self::NeitherReturnable;
}
}
#[inline]
const fn not_returnable_false(&mut self) {
if let Self::ReturnableFalse = self {
*self = Self::NeitherReturnable;
}
}
#[inline]
const fn from_bool(was: bool) -> Self {
if was {
Self::ReturnableTrue
} else {
Self::ReturnableFalse
}
}
#[inline]
const fn const_eq(&self, other: &Self) -> bool {
match self {
Self::ReturnableFalse => matches!(other, Self::ReturnableFalse),
Self::NeitherReturnable => matches!(other, Self::NeitherReturnable),
Self::ReturnableTrue => matches!(other, Self::ReturnableTrue),
}
}
#[inline]
const fn not_returnable_with_value(&mut self, value: bool) {
if Self::from_bool(value).const_eq(self) {
*self = Self::NeitherReturnable;
}
}
}
pub struct AndStream<const N: usize, G: Getter<bool, E>, E: Clone + Debug> {
inputs: [G; N],
phantom_e: PhantomData<E>,
}
impl<const N: usize, G: Getter<bool, E>, E: Clone + Debug> AndStream<N, G, E> {
pub const fn new(inputs: [G; N]) -> Self {
Self {
inputs,
phantom_e: PhantomData,
}
}
}
impl<const N: usize, G: Getter<bool, E>, E: Clone + Debug> Updatable<E> for AndStream<N, G, E> {
fn update(&mut self) -> NothingOrError<E> {
for getter in &mut self.inputs {
getter.update()?;
}
Ok(())
}
}
impl<const N: usize, G: Getter<bool, E>, E: Clone + Debug> Getter<bool, E> for AndStream<N, G, E> {
fn get(&self) -> Output<bool, E> {
if N == 0 {
return Ok(None);
}
let mut logic_state = LogicState::ReturnableTrue;
let mut time = Time::ZERO;
for getter in &self.inputs {
match getter.get()? {
None => logic_state.not_returnable_true(),
Some(datum) => {
if datum.time > time {
time = datum.time;
}
if !datum.value {
logic_state = LogicState::ReturnableFalse;
}
}
}
}
Ok(match logic_state {
LogicState::ReturnableTrue => Some(Datum::new(time, true)),
LogicState::ReturnableFalse => Some(Datum::new(time, false)),
LogicState::NeitherReturnable => None,
})
}
}
pub struct OrStream<const N: usize, G: Getter<bool, E>, E: Clone + Debug> {
inputs: [G; N],
phantom_e: PhantomData<E>,
}
impl<const N: usize, G: Getter<bool, E>, E: Clone + Debug> OrStream<N, G, E> {
pub const fn new(inputs: [G; N]) -> Self {
Self {
inputs,
phantom_e: PhantomData,
}
}
}
impl<const N: usize, G: Getter<bool, E>, E: Clone + Debug> Updatable<E> for OrStream<N, G, E> {
fn update(&mut self) -> NothingOrError<E> {
for getter in &mut self.inputs {
getter.update()?;
}
Ok(())
}
}
impl<const N: usize, G: Getter<bool, E>, E: Clone + Debug> Getter<bool, E> for OrStream<N, G, E> {
fn get(&self) -> Output<bool, E> {
if N == 0 {
return Ok(None);
}
let mut logic_state = LogicState::ReturnableFalse;
let mut time = Time::ZERO;
for getter in &self.inputs {
match getter.get()? {
None => logic_state.not_returnable_false(),
Some(datum) => {
if datum.time > time {
time = datum.time;
}
if datum.value {
logic_state = LogicState::ReturnableTrue;
}
}
}
}
Ok(match logic_state {
LogicState::ReturnableTrue => Some(Datum::new(time, true)),
LogicState::ReturnableFalse => Some(Datum::new(time, false)),
LogicState::NeitherReturnable => None,
})
}
}
macro_rules! make_gate {
($name: ident, $default: literal, $struct_doc: literal, $constructor_doc: literal) => {
#[doc = $struct_doc]
pub struct $name<G1, G2> {
input1: G1,
input2: G2,
}
impl<G1, G2> $name<G1, G2> {
#[doc = $constructor_doc]
pub const fn new(input1: G1, input2: G2) -> Self {
Self { input1, input2 }
}
}
impl<G1, G2, E> Updatable<E> for $name<G1, G2>
where
G1: Updatable<E>,
G2: Updatable<E>,
E: Clone + Debug,
{
fn update(&mut self) -> NothingOrError<E> {
self.input1.update()?;
self.input2.update()?;
Ok(())
}
}
impl<G1, G2, E> Getter<bool, E> for $name<G1, G2>
where
G1: Getter<bool, E>,
G2: Getter<bool, E>,
E: Clone + Debug,
{
fn get(&self) -> Output<bool, E> {
let mut logic_state = LogicState::from_bool($default);
let mut time = Time::ZERO;
macro_rules! error_handle_input {
($input: ident, $skip_time_check: literal) => {
match self.$input.get()? {
None => logic_state.not_returnable_with_value($default),
Some(datum) => {
if $skip_time_check || datum.time > time {
time = datum.time;
}
if $default ^ datum.value {
logic_state = LogicState::from_bool(!$default);
}
}
}
};
}
error_handle_input!(input1, true);
error_handle_input!(input2, false);
Ok(match logic_state {
LogicState::ReturnableTrue => Some(Datum::new(time, true)),
LogicState::ReturnableFalse => Some(Datum::new(time, false)),
LogicState::NeitherReturnable => None,
})
}
}
};
}
make_gate!(
Or2,
false,
r#"Performs a logical "or" operation on two input getters which can be of different types. More
specifically, follows these rules, starting at the top and proceeding as needed:
1. If an input returns an error, return the error.
2. If neither input returns an error, if an input returns true, return true.
3. If neither input returns true, if an input returns None, return None.
4. If neither input returns None (both returned false), return false.
Returns the later timestamp of the two inputs if they both return Some.
If you need more than two inputs, you may consider using [`OrStream`] instead of a chain of
`Or2`, especially if the inputs are of the same type."#,
"Constructor for `Or2`. Unlike [`OrStream`], its inputs can be of different types."
);
make_gate!(
And2,
true,
r#"Performs a logical "and" operation on two input getters which can be of different types. More
specifically, follows these rules, starting at the top and proceeding as needed:
1. If an input returns an error, return the error.
2. If neither input returns an error, if an input returns false, return false.
3. If neither input returns false, if an input returns None, return None.
4. If neither input returns None (both returned true), return true.
Returns the later timestamp of the two inputs if they both return Some.
If you need more than two inputs, you may consider using [`AndStream`] instead of a chain of
`And2`, especially if the inputs are of the same type."#,
"Constructor for `And2`. Unlike [`AndStream`], its inputs can be of different types."
);
pub struct NotStream<G: Getter<bool, E>, E: Clone + Debug> {
input: G,
phantom_e: PhantomData<E>,
}
impl<G: Getter<bool, E>, E: Clone + Debug> NotStream<G, E> {
pub const fn new(input: G) -> Self {
Self {
input,
phantom_e: PhantomData,
}
}
}
impl<G: Getter<bool, E>, E: Clone + Debug> Getter<bool, E> for NotStream<G, E> {
fn get(&self) -> Output<bool, E> {
Ok(self.input.get()?.map(|datum| !datum))
}
}
impl<G: Getter<bool, E>, E: Clone + Debug> Updatable<E> for NotStream<G, E> {
fn update(&mut self) -> NothingOrError<E> {
self.input.update()?;
Ok(())
}
}
#[test]
fn logic_state_const_eq() {
const LOGIC_STATES: [LogicState; 3] = [
LogicState::ReturnableFalse,
LogicState::NeitherReturnable,
LogicState::ReturnableTrue,
];
for (a, state_a) in LOGIC_STATES.into_iter().enumerate() {
for (b, state_b) in LOGIC_STATES.into_iter().enumerate() {
assert_eq!(a == b, state_a == state_b);
assert_eq!(a == b, state_a.const_eq(&state_b));
}
}
}
#[test]
fn not_returnable_with_value() {
macro_rules! perform_test {
($start: ident, $not_returnable_with: literal, $end: ident) => {
let mut x = LogicState::$start;
x.not_returnable_with_value($not_returnable_with);
assert_eq!(x, LogicState::$end);
};
}
perform_test!(ReturnableFalse, false, NeitherReturnable);
perform_test!(NeitherReturnable, false, NeitherReturnable);
perform_test!(ReturnableTrue, false, ReturnableTrue);
perform_test!(ReturnableFalse, true, ReturnableFalse);
perform_test!(NeitherReturnable, true, NeitherReturnable);
perform_test!(ReturnableTrue, true, NeitherReturnable);
}