Skip to main content

ergo_runtime/compute/implementations/safe_divide/
impl.rs

1use std::collections::HashMap;
2
3use crate::common::Value;
4use crate::compute::{ComputeError, ComputePrimitive, ComputePrimitiveManifest, PrimitiveState};
5
6use super::manifest::safe_divide_manifest;
7
8pub struct SafeDivide {
9    manifest: ComputePrimitiveManifest,
10}
11
12impl SafeDivide {
13    pub fn new() -> Self {
14        Self {
15            manifest: safe_divide_manifest(),
16        }
17    }
18}
19
20impl Default for SafeDivide {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl ComputePrimitive for SafeDivide {
27    fn manifest(&self) -> &ComputePrimitiveManifest {
28        &self.manifest
29    }
30
31    /// Safe divide with explicit fallback.
32    ///
33    /// Returns `fallback` when:
34    /// - `b == 0.0` (would be division by zero)
35    /// - Result is non-finite (overflow)
36    ///
37    /// This implementation never errors for zero/non-finite conditions.
38    /// The author explicitly chooses the fallback value, encoding domain policy.
39    ///
40    /// **Note:** `fallback` must be a finite number. If `fallback` is NaN or
41    /// infinity, safe_divide will return it, and the NUM-FINITE-1 runtime guard
42    /// will raise `ExecError::NonFiniteOutput`.
43    ///
44    /// See: B.2 in PHASE_INVARIANTS.md
45    fn compute(
46        &self,
47        inputs: &HashMap<String, Value>,
48        parameters: &HashMap<String, Value>,
49        _state: Option<&mut PrimitiveState>,
50    ) -> Result<HashMap<String, Value>, ComputeError> {
51        let a = inputs
52            .get("a")
53            .and_then(|v| v.as_number())
54            .expect("missing required numeric input 'a'");
55        let b = inputs
56            .get("b")
57            .and_then(|v| v.as_number())
58            .expect("missing required numeric input 'b'");
59        let fallback = parameters
60            .get("fallback")
61            .and_then(|v| v.as_number())
62            .expect("missing required parameter 'fallback'");
63
64        let result = if b == 0.0 {
65            fallback
66        } else {
67            let r = a / b;
68            if r.is_finite() {
69                r
70            } else {
71                fallback
72            }
73        };
74
75        Ok(HashMap::from([(
76            "result".to_string(),
77            Value::Number(result),
78        )]))
79    }
80}