libreda-logic 0.0.3

Logic library for LibrEDA.
Documentation
// SPDX-FileCopyrightText: 2023 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Trait for logic values and implementations thereof.

use std::hash::Hash;

use crate::traits::LogicValue;

impl LogicValue for bool {
    fn num_values() -> usize {
        2
    }

    fn value(idx: usize) -> Self {
        match idx {
            0 => false,
            1 => true,
            _ => panic!("index out of bounds"),
        }
    }

    fn zero() -> Self {
        false
    }

    fn one() -> Self {
        true
    }
}

/// Custom implementation of a boolean value.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum Bool {
    /// Low, zero or 'false'.
    L,
    /// High, one or 'true.
    H,
}

impl LogicValue for Bool {
    fn num_values() -> usize {
        2
    }

    fn value(idx: usize) -> Self {
        match idx {
            0 => Self::L,
            1 => Self::H,
            _ => panic!("index out of bounds"),
        }
    }

    fn zero() -> Self {
        Self::L
    }

    fn one() -> Self {
        Self::H
    }
}

/// Three-valued logic.
/// In contrast to bivalent logic values such as `bool`
/// this logic type also encodes the unknown value `X`.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub enum Logic3 {
    /// Low, zero or 'false'.
    L,
    /// High, one or 'true.
    H,
    /// Value for representing unknown/undecidable/both/irrelevant.
    #[default]
    X,
}

impl From<bool> for Logic3 {
    fn from(value: bool) -> Self {
        match value {
            true => Self::H,
            false => Self::L,
        }
    }
}

impl From<Option<bool>> for Logic3 {
    fn from(value: Option<bool>) -> Self {
        match value {
            None => Self::X,
            Some(true) => Self::H,
            Some(false) => Self::L,
        }
    }
}

impl TryInto<bool> for Logic3 {
    type Error = ();

    fn try_into(self) -> Result<bool, Self::Error> {
        match self {
            Logic3::L => Ok(false),
            Logic3::H => Ok(true),
            Logic3::X => Err(()),
        }
    }
}

impl LogicValue for Logic3 {
    fn value(idx: usize) -> Self {
        match idx {
            0 => Self::L,
            1 => Self::H,
            2 => Self::X,
            _ => panic!("index out of bounds"),
        }
    }

    fn zero() -> Self {
        Self::L
    }

    fn one() -> Self {
        Self::H
    }

    fn num_values() -> usize {
        3
    }
}

impl std::ops::Not for Logic3 {
    type Output = Self;

    fn not(self) -> Self::Output {
        use Logic3::*;
        match self {
            L => H,
            H => L,
            X => X,
        }
    }
}

impl std::ops::BitAnd for Logic3 {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        use Logic3::*;
        match (self, rhs) {
            (L, _) | (_, L) => L,
            (H, H) => H,
            _ => X,
        }
    }
}

impl std::ops::BitOr for Logic3 {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        use Logic3::*;
        match (self, rhs) {
            (H, _) | (_, H) => H,
            (L, L) => L,
            _ => X,
        }
    }
}

impl std::ops::BitXor for Logic3 {
    type Output = Self;

    fn bitxor(self, rhs: Self) -> Self::Output {
        use Logic3::*;
        match (self, rhs) {
            (H, H) | (L, L) => L,
            (H, L) | (L, H) => H,
            (X, _) | (_, X) => X,
        }
    }
}

#[test]
fn test_logic3_ops() {
    use Logic3::*;

    assert_eq!(H & H, H);
    assert_eq!(L & X, L);
    assert_eq!(X & L, L);
    assert_eq!(H & X, X);

    assert_eq!(L | L, L);
    assert_eq!(H | X, H);
    assert_eq!(X | H, H);
    assert_eq!(X | L, X);
}