use std::{fmt, hash::Hash};
use serde::*;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
#[serde(from = "(usize, usize)", into = "(usize, usize)")]
pub struct Signature {
args: u16,
outputs: u16,
under_args: u16,
under_outputs: u16,
}
impl From<(usize, usize)> for Signature {
fn from((args, outputs): (usize, usize)) -> Self {
Self::new(args, outputs)
}
}
impl From<Signature> for (usize, usize) {
fn from(sig: Signature) -> Self {
(sig.args(), sig.outputs())
}
}
impl Signature {
pub const fn new(args: usize, outputs: usize) -> Self {
Self {
args: args as u16,
outputs: outputs as u16,
under_args: 0,
under_outputs: 0,
}
}
pub fn with_under(self, under_args: usize, under_outputs: usize) -> Self {
Self {
args: self.args,
outputs: self.outputs,
under_args: under_args as u16,
under_outputs: under_outputs as u16,
}
}
#[inline(always)]
pub const fn args(&self) -> usize {
self.args as usize
}
#[inline(always)]
pub const fn outputs(&self) -> usize {
self.outputs as usize
}
pub const fn under_args(&self) -> usize {
self.under_args as usize
}
pub const fn under_outputs(&self) -> usize {
self.under_outputs as usize
}
pub const fn set_args(&mut self, args: usize) {
self.args = args as u16;
}
pub const fn set_outputs(&mut self, outputs: usize) {
self.outputs = outputs as u16;
}
pub fn update_args(&mut self, f: impl FnOnce(usize) -> usize) {
self.args = f(self.args()) as u16;
}
pub fn update_outputs(&mut self, f: impl FnOnce(usize) -> usize) {
self.outputs = f(self.outputs()) as u16;
}
pub fn update_under_args(&mut self, f: impl FnOnce(usize) -> usize) {
self.under_args = f(self.under_args()) as u16;
}
pub fn update_under_outputs(&mut self, f: impl FnOnce(usize) -> usize) {
self.under_outputs = f(self.under_outputs()) as u16;
}
pub fn update_args_outputs(&mut self, f: impl FnOnce(usize, usize) -> (usize, usize)) {
let (args, outputs) = f(self.args(), self.outputs());
self.args = args as u16;
self.outputs = outputs as u16;
}
pub fn is_compatible_with(self, other: Self) -> bool {
self.args as isize - self.outputs as isize == other.args as isize - other.outputs as isize
}
pub fn is_superset_of(self, other: Self) -> bool {
self.is_compatible_with(other) && self.args >= other.args
}
pub fn is_subset_of(self, other: Self) -> bool {
self.is_compatible_with(other) && self.args <= other.args
}
pub fn max_with(self, other: Self) -> Self {
Self::new(
self.args().max(other.args()),
self.outputs().max(other.outputs()),
)
.with_under(
self.under_args().max(other.under_args()),
self.under_outputs().max(other.under_outputs()),
)
}
pub fn compose(self, other: Self) -> Self {
let args = other.args() + self.args().saturating_sub(other.outputs());
let outputs = self.outputs() + other.outputs().saturating_sub(self.args());
let under_args =
other.under_args() + self.under_args().saturating_sub(other.under_outputs());
let under_outputs =
self.under_outputs() + other.under_outputs().saturating_sub(self.under_args());
Self::new(args, outputs).with_under(under_args, under_outputs)
}
pub fn inverse(self) -> Self {
Self::new(self.outputs(), self.args())
}
pub fn anti(self) -> Option<Self> {
if self.args == 0 {
return None;
}
Some(Signature::new(self.outputs() + 1, self.args() - 1))
}
pub fn under(self) -> Signature {
Signature::new(self.under_args(), self.under_outputs())
}
}
impl PartialEq<(usize, usize)> for Signature {
fn eq(&self, other: &(usize, usize)) -> bool {
self.args() == other.0 && self.outputs() == other.1
}
}
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "|{}.{}", self.args, self.outputs)
}
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "|{}.{}", self.args, self.outputs)
}
}