rival3 0.1.0

Real Computation via Interval Arithmetic
Documentation
//! Low-level instruction representation for the register machine

use rug::{Float, Rational};
use std::hash::{Hash, Hasher};

// Re-export operation enums generated by ops registry.
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);
    }
}

/// Register machine instruction with output register index and payload data
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Instruction {
    pub out: usize,
    pub data: InstructionData,
}

/// Payload for an instruction covering literal, constant, and operator variants
#[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 {
    /// Visit each input register index referenced by this instruction
    pub fn for_each_input(&self, f: impl FnMut(usize)) {
        self.data.for_each_input(f);
    }
}

impl InstructionData {
    /// Create a literal instruction from a Float value
    pub fn literal(value: Float) -> Self {
        InstructionData::Literal {
            value: HashFloat(value),
        }
    }

    /// Create a rational instruction from a Rational value
    pub fn rational(val: Rational) -> Self {
        InstructionData::Rational {
            val: HashRational(val),
        }
    }

    /// Create a constant instruction for the given operation
    pub fn constant(op: ConstantOp) -> Self {
        InstructionData::Constant { op }
    }

    /// Create a unary instruction with one input register
    pub fn unary(op: UnaryOp, arg: usize) -> Self {
        InstructionData::Unary { op, arg }
    }

    /// Create a unary parameterized instruction with a parameter and one input register
    pub fn unary_param(op: UnaryParamOp, param: u64, arg: usize) -> Self {
        InstructionData::UnaryParam { op, param, arg }
    }

    /// Create a binary instruction with two input registers
    pub fn binary(op: BinaryOp, lhs: usize, rhs: usize) -> Self {
        InstructionData::Binary { op, lhs, rhs }
    }

    /// Create a ternary instruction with three input registers
    pub fn ternary(op: TernaryOp, arg1: usize, arg2: usize, arg3: usize) -> Self {
        InstructionData::Ternary {
            op,
            arg1,
            arg2,
            arg3,
        }
    }

    /// Visit each input register index for this payload variant
    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);
            }
        }
    }

    /// Return the input register index at the requested position if it exists
    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,
        }
    }

    /// Return an operation name for profiling/debugging
    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),
        }
    }
}