use core::fmt;
use core::hash::{Hash, Hasher};
use puremp::Rational;
use crate::ast::AstId;
use crate::util::symbol::Symbol;
use crate::util::zstring::Zstring;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
pub enum ParameterKind {
Int = 0,
Ast = 1,
Symbol = 2,
Zstring = 3,
Rational = 4,
Double = 5,
External = 6,
}
#[derive(Clone, Debug)]
pub enum Parameter {
Int(i32),
Ast(AstId),
Symbol(Symbol),
Zstring(Zstring),
Rational(Rational),
Double(f64),
External(u32),
}
impl Parameter {
pub const fn kind(&self) -> ParameterKind {
match self {
Parameter::Int(_) => ParameterKind::Int,
Parameter::Ast(_) => ParameterKind::Ast,
Parameter::Symbol(_) => ParameterKind::Symbol,
Parameter::Zstring(_) => ParameterKind::Zstring,
Parameter::Rational(_) => ParameterKind::Rational,
Parameter::Double(_) => ParameterKind::Double,
Parameter::External(_) => ParameterKind::External,
}
}
pub const fn is_int(&self) -> bool {
matches!(self, Parameter::Int(_))
}
pub const fn is_ast(&self) -> bool {
matches!(self, Parameter::Ast(_))
}
pub const fn is_symbol(&self) -> bool {
matches!(self, Parameter::Symbol(_))
}
pub const fn is_zstring(&self) -> bool {
matches!(self, Parameter::Zstring(_))
}
pub const fn is_rational(&self) -> bool {
matches!(self, Parameter::Rational(_))
}
pub const fn is_double(&self) -> bool {
matches!(self, Parameter::Double(_))
}
pub const fn is_external(&self) -> bool {
matches!(self, Parameter::External(_))
}
pub const fn get_int(&self) -> Option<i32> {
match self {
Parameter::Int(i) => Some(*i),
_ => None,
}
}
pub const fn get_ast(&self) -> Option<AstId> {
match self {
Parameter::Ast(a) => Some(*a),
_ => None,
}
}
pub const fn get_symbol(&self) -> Option<Symbol> {
match self {
Parameter::Symbol(s) => Some(*s),
_ => None,
}
}
pub fn get_zstring(&self) -> Option<&Zstring> {
match self {
Parameter::Zstring(s) => Some(s),
_ => None,
}
}
pub fn get_rational(&self) -> Option<&Rational> {
match self {
Parameter::Rational(r) => Some(r),
_ => None,
}
}
pub const fn get_double(&self) -> Option<f64> {
match self {
Parameter::Double(d) => Some(*d),
_ => None,
}
}
pub const fn get_ext_id(&self) -> Option<u32> {
match self {
Parameter::External(id) => Some(*id),
_ => None,
}
}
}
impl From<i32> for Parameter {
fn from(v: i32) -> Parameter {
Parameter::Int(v)
}
}
impl From<AstId> for Parameter {
fn from(a: AstId) -> Parameter {
Parameter::Ast(a)
}
}
impl From<Symbol> for Parameter {
fn from(s: Symbol) -> Parameter {
Parameter::Symbol(s)
}
}
impl From<Rational> for Parameter {
fn from(r: Rational) -> Parameter {
Parameter::Rational(r)
}
}
impl From<f64> for Parameter {
fn from(d: f64) -> Parameter {
Parameter::Double(d)
}
}
impl PartialEq for Parameter {
fn eq(&self, other: &Parameter) -> bool {
match (self, other) {
(Parameter::Int(a), Parameter::Int(b)) => a == b,
(Parameter::Ast(a), Parameter::Ast(b)) => a == b,
(Parameter::Symbol(a), Parameter::Symbol(b)) => a == b,
(Parameter::Zstring(a), Parameter::Zstring(b)) => a == b,
(Parameter::Rational(a), Parameter::Rational(b)) => a == b,
(Parameter::Double(a), Parameter::Double(b)) => a.to_bits() == b.to_bits(),
(Parameter::External(a), Parameter::External(b)) => a == b,
_ => false,
}
}
}
impl Eq for Parameter {}
impl Hash for Parameter {
fn hash<H: Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
Parameter::Int(i) => i.hash(state),
Parameter::Ast(a) => a.hash(state),
Parameter::Symbol(s) => s.hash(state),
Parameter::Zstring(s) => s.hash(state),
Parameter::Rational(r) => r.hash(state),
Parameter::Double(d) => d.to_bits().hash(state),
Parameter::External(id) => id.hash(state),
}
}
}
impl fmt::Display for Parameter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Parameter::Int(i) => write!(f, "{i}"),
Parameter::Ast(a) => write!(f, "#{}", a.0),
Parameter::Symbol(s) => write!(f, "{s}"),
Parameter::Zstring(s) => write!(f, "\"{s}\""),
Parameter::Rational(r) => write!(f, "{r}"),
Parameter::Double(d) => write!(f, "{d}"),
Parameter::External(id) => write!(f, "{id}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
use puremp::Int;
#[test]
fn kinds_and_accessors() {
let p = Parameter::Int(42);
assert_eq!(p.kind(), ParameterKind::Int);
assert_eq!(p.get_int(), Some(42));
assert_eq!(p.get_symbol(), None);
let s = Parameter::from(Symbol::new("bv"));
assert!(s.is_symbol());
assert_eq!(s.get_symbol(), Some(Symbol::new("bv")));
let r = Parameter::from(Rational::new(Int::from(3), Int::from(4)));
assert!(r.is_rational());
assert_eq!(r.get_rational().unwrap().to_string(), "3/4");
}
#[test]
fn equality_and_kind_order() {
assert_eq!(Parameter::Int(1), Parameter::Int(1));
assert_ne!(Parameter::Int(1), Parameter::Int(2));
assert_ne!(Parameter::Int(1), Parameter::External(1));
assert_eq!(ParameterKind::Int as u8, 0);
assert_eq!(ParameterKind::Rational as u8, 4);
assert_eq!(ParameterKind::External as u8, 6);
}
#[test]
fn double_uses_bit_equality() {
assert_eq!(Parameter::Double(1.5), Parameter::Double(1.5));
assert_eq!(Parameter::Double(f64::NAN), Parameter::Double(f64::NAN));
assert_eq!(format!("{}", Parameter::Ast(AstId(7))), "#7");
}
}