use rug::{Float, Rational};
use std::hash::{Hash, Hasher};
pub use super::ops::{
BinaryOp, ConstantOp, TernaryOp, UnaryOp, UnaryParamOp, name_of_binary, name_of_constant,
name_of_ternary, name_of_unary, name_of_unary_param,
};
#[derive(Clone, Debug)]
pub struct HashFloat(pub Float);
impl PartialEq for HashFloat {
fn eq(&self, other: &Self) -> bool {
if self.0.prec() != other.0.prec() {
return false;
}
if self.0.is_nan() && other.0.is_nan() {
return true;
}
self.0 == other.0
}
}
impl Eq for HashFloat {}
impl Hash for HashFloat {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.prec().hash(state);
self.0.to_string_radix(16, None).hash(state);
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HashRational(pub Rational);
impl Hash for HashRational {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.numer().hash(state);
self.0.denom().hash(state);
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Instruction {
pub out: usize,
pub data: InstructionData,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum InstructionData {
Literal {
value: HashFloat,
},
Rational {
val: HashRational,
},
Constant {
op: ConstantOp,
},
Unary {
op: UnaryOp,
arg: usize,
},
UnaryParam {
op: UnaryParamOp,
param: u64,
arg: usize,
},
Binary {
op: BinaryOp,
lhs: usize,
rhs: usize,
},
Ternary {
op: TernaryOp,
arg1: usize,
arg2: usize,
arg3: usize,
},
}
impl Instruction {
pub fn for_each_input(&self, f: impl FnMut(usize)) {
self.data.for_each_input(f);
}
}
impl InstructionData {
pub fn literal(value: Float) -> Self {
InstructionData::Literal {
value: HashFloat(value),
}
}
pub fn rational(val: Rational) -> Self {
InstructionData::Rational {
val: HashRational(val),
}
}
pub fn constant(op: ConstantOp) -> Self {
InstructionData::Constant { op }
}
pub fn unary(op: UnaryOp, arg: usize) -> Self {
InstructionData::Unary { op, arg }
}
pub fn unary_param(op: UnaryParamOp, param: u64, arg: usize) -> Self {
InstructionData::UnaryParam { op, param, arg }
}
pub fn binary(op: BinaryOp, lhs: usize, rhs: usize) -> Self {
InstructionData::Binary { op, lhs, rhs }
}
pub fn ternary(op: TernaryOp, arg1: usize, arg2: usize, arg3: usize) -> Self {
InstructionData::Ternary {
op,
arg1,
arg2,
arg3,
}
}
pub fn for_each_input(&self, mut f: impl FnMut(usize)) {
match self {
InstructionData::Literal { .. }
| InstructionData::Rational { .. }
| InstructionData::Constant { .. } => {}
InstructionData::Unary { arg, .. } => f(*arg),
InstructionData::UnaryParam { arg, .. } => f(*arg),
InstructionData::Binary { lhs, rhs, .. } => {
f(*lhs);
f(*rhs);
}
InstructionData::Ternary {
arg1, arg2, arg3, ..
} => {
f(*arg1);
f(*arg2);
f(*arg3);
}
}
}
pub fn input_at(&self, index: usize) -> Option<usize> {
match (self, index) {
(InstructionData::Unary { arg, .. }, 0) => Some(*arg),
(InstructionData::UnaryParam { arg, .. }, 0) => Some(*arg),
(InstructionData::Binary { lhs, .. }, 0) => Some(*lhs),
(InstructionData::Binary { rhs, .. }, 1) => Some(*rhs),
(InstructionData::Ternary { arg1, .. }, 0) => Some(*arg1),
(InstructionData::Ternary { arg2, .. }, 1) => Some(*arg2),
(InstructionData::Ternary { arg3, .. }, 2) => Some(*arg3),
_ => None,
}
}
pub fn name_static(&self) -> &'static str {
match self {
InstructionData::Literal { .. } => "literal",
InstructionData::Rational { .. } => "rational",
InstructionData::Constant { op } => name_of_constant(*op),
InstructionData::Unary { op, .. } => name_of_unary(*op),
InstructionData::UnaryParam { op, .. } => name_of_unary_param(*op),
InstructionData::Binary { op, .. } => name_of_binary(*op),
InstructionData::Ternary { op, .. } => name_of_ternary(*op),
}
}
}