diff --git a/kernel/lcrconn-cli/src/app.rs b/kernel/lcrconn-cli/src/app.rs index 74881d6..bce2cd9 100644 --- a/kernel/lcrconn-cli/src/app.rs +++ b/kernel/lcrconn-cli/src/app.rs @@ -2,7 +2,7 @@ use crate::cli::{AppConfig, AppResolver}; use anyhow::Result; use lcrconn::{ BfsResolver, DeviceKind, LutResolver, Request, Resolver, Response, ResponsePriority, - common::{Circuit, CircuitDeviceScale, JointKind, sanitize_device_value, sanitize_floating_point}, + common::{Circuit, CircuitDeviceScale, JointKind, validate_device_value, validate_floating_point}, dataset::{DatasetCollection, from_human_readable_value, to_human_readable_value}, query::MAX_RESPONSE_CNT, }; @@ -310,7 +310,7 @@ impl App { let value = self.parse_plain_float(pct_str, |x| *x >= 0.0 && *x <= 100.0); value .map(|v| v / 100.0 * target_value) - .map(|v| sanitize_device_value(v)) + .map(|v| validate_device_value(v)) .transpose() .ok() .flatten() @@ -349,7 +349,7 @@ impl App { Ok(value) => value, Err(_) => return None, }; - let value = sanitize_floating_point(value).ok()?; + let value = validate_floating_point(value).ok()?; if checker(&value) { Some(value) } else { None } } diff --git a/kernel/lcrconn/src/common.rs b/kernel/lcrconn/src/common.rs index 38b7649..c3d72bf 100644 --- a/kernel/lcrconn/src/common.rs +++ b/kernel/lcrconn/src/common.rs @@ -1,13 +1,15 @@ use strum_macros::EnumIter; use thiserror::Error as TeError; -// region: Sanitizer +// region: Validator +/// Error occurs when validating floating point value. #[derive(Debug, TeError)] -#[error("given floating value {0} is invalid")] +#[error("given floating point value {0} is invalid")] pub struct FloatingPointError(f64); -pub fn sanitize_floating_point(f: f64) -> Result { +/// Check whether given floating point value is okey for arithmetic operation. +pub fn validate_floating_point(f: f64) -> Result { if f.is_finite() { Ok(f) } else { @@ -15,16 +17,21 @@ pub fn sanitize_floating_point(f: f64) -> Result { } } +/// Error occurs when validating device value. #[derive(Debug, TeError)] pub enum DeviceValueError { - #[error("{0}")] + #[error("given device value is bad floating point: {0}")] BadFloatingPoint(#[from] FloatingPointError), #[error("given device value {0} is out of range")] OutOfRange(f64), } -pub fn sanitize_device_value(f: f64) -> Result { - let f = sanitize_floating_point(f)?; +/// Check whether given value is good for device value. +/// +/// A good device value should be finity floating point, +/// and it should be greater than zero. +pub fn validate_device_value(f: f64) -> Result { + let f = validate_floating_point(f)?; if f > 0f64 { Ok(f) } else { @@ -97,14 +104,21 @@ impl CircuitDeviceScale { // region: Circuit Stuff -/// Error occurs when manipulating [SubCircuit]. +/// Error occurs when manipulating [Circuit] and [SubCircuit]. #[derive(Debug, TeError)] -pub enum SubCircuitError { +pub enum CircuitError { #[error("invalid device value in circuit: {0}")] BadDeviceValue(DeviceValueError), - #[error("bad previous computed circuit value: {0}")] + #[error("third device cannot exist without second device when building circuit")] + InterleavedSubCircuit, + #[error("the joint or device with given index is not presented in circuit")] + NoSuchDevice, + + #[error("invalid target value: {0}")] + BadTargetValue(DeviceValueError), + #[error("bad previous evaluated joint value: {0}")] BadPreviousValue(DeviceValueError), - #[error("arithmetic error: {0}")] + #[error("floating point is invalid after arithmetic operation: {0}")] BadArithmetic(FloatingPointError), } @@ -122,24 +136,24 @@ impl SubCircuit { /// /// The input device value should greater than zero, /// otherwise an error will return. - pub fn new(device_value: f64, joint_kind: JointKind) -> Result { - let device_value = sanitize_device_value(device_value) - .map_err(|err| SubCircuitError::BadDeviceValue(err))?; + pub fn new(device_value: f64, joint_kind: JointKind) -> Result { + let device_value = + validate_device_value(device_value).map_err(|err| CircuitError::BadDeviceValue(err))?; Ok(Self { device_value, joint_kind, }) } - /// Compute the joint value with given previous computed value and device kind. + /// Evaluate the joint value with given previous joint evaluated value and device kind. /// - /// Parameter `value` should be the value computed from previous devices. + /// Parameter `value` should be the value evaluated from previous joint. /// And it should greater than zero. /// `device_kind` is the kind of the device. - pub fn compute(&self, value: f64, device_kind: DeviceKind) -> Result { + pub fn evaluate(&self, value: f64, device_kind: DeviceKind) -> Result { // Check the range of provided value for computing let value = - sanitize_device_value(value).map_err(|err| SubCircuitError::BadPreviousValue(err))?; + validate_device_value(value).map_err(|err| CircuitError::BadPreviousValue(err))?; // We perform series connect for: series resistor, series inductor and parallel capacitor. // We perform parallel connect for: parallel resistor, parallel inductor and series capacitor. @@ -148,11 +162,11 @@ impl SubCircuit { _ => self.joint_kind, }; - sanitize_floating_point(match joint_kind { + validate_floating_point(match joint_kind { JointKind::Series => self.device_value + value, JointKind::Parallel => (self.device_value * value) / (self.device_value + value), }) - .map_err(|err| SubCircuitError::BadArithmetic(err)) + .map_err(|err| CircuitError::BadArithmetic(err)) } /// Get the device value. @@ -166,19 +180,6 @@ impl SubCircuit { } } -/// Error occurs when manipulating [Circuit]. -#[derive(Debug, TeError)] -pub enum CircuitError { - #[error("invalid device value in circuit: {0}")] - BadDeviceValue(DeviceValueError), - #[error("third device cannot exist without second device when building circuit")] - BlankSecondSubCircuit, - #[error("{0}")] - SubCircuit(#[from] SubCircuitError), - #[error("the joint or device with given index is not presented in circuit")] - NoSuchDevice, -} - /// The circuit composed of multiple joints. #[derive(Clone, Debug)] pub struct Circuit { @@ -193,20 +194,20 @@ pub struct Circuit { impl Circuit { /// Initialize the circuit with subcircuit. /// - /// * `first_device_value` - The value of the first device. - /// * `second_device_subckt` - The second device and its joint property. - /// * `third_device_subckt` - The third device and its joint property. + /// - `first_device_value`: The value of the first device. + /// - `second_device_subckt`: The second device and its joint property. + /// - `third_device_subckt`: The third device and its joint property. fn new( first_device_value: f64, second_device_subckt: Option, third_device_subckt: Option, ) -> Result { // Check the value of first device - let first_device_value = sanitize_device_value(first_device_value) + let first_device_value = validate_device_value(first_device_value) .map_err(|err| CircuitError::BadDeviceValue(err))?; // Check impossible form if second_device_subckt.is_none() && third_device_subckt.is_some() { - return Err(CircuitError::BlankSecondSubCircuit); + return Err(CircuitError::InterleavedSubCircuit); } // Everything is okey @@ -250,23 +251,51 @@ impl Circuit { ) } - /// Compute the circuit value with given value and device kind - pub fn compute(&self, device_kind: DeviceKind) -> Result { + /// Evaluate the circuit value with device kind + pub fn evaluate(&self, device_kind: DeviceKind) -> Result { let mut value = self.first_device_value; match &self.second_device_subckt { - Some(subckt) => value = subckt.compute(value, device_kind)?, + Some(subckt) => value = subckt.evaluate(value, device_kind)?, None => return Ok(value), } match &self.third_device_subckt { - Some(subckt) => value = subckt.compute(value, device_kind)?, + Some(subckt) => value = subckt.evaluate(value, device_kind)?, None => return Ok(value), } Ok(value) } + /// Evaluate the circuit value with given target value and device kind + pub fn evaluate_with_target( + &self, + target_value: f64, + device_kind: DeviceKind, + ) -> Result { + let target_value = + validate_device_value(target_value).map_err(|err| CircuitError::BadTargetValue(err))?; + + let value = self.evaluate(device_kind)?; + let difference = validate_floating_point(value - target_value) + .map_err(|err| CircuitError::BadArithmetic(err))?; + let unsigned_difference = validate_floating_point(difference.abs()) + .map_err(|err| CircuitError::BadArithmetic(err))?; + let relative_difference = validate_floating_point(difference / target_value) + .map_err(|err| CircuitError::BadArithmetic(err))?; + let unsigned_relative_difference = validate_floating_point(relative_difference.abs()) + .map_err(|err| CircuitError::BadArithmetic(err))?; + + Ok(CircuitEvaluation { + value, + difference, + unsigned_difference, + relative_difference, + unsigned_relative_difference, + }) + } + /// Get the device scale. /// /// # Returns @@ -320,146 +349,25 @@ impl Circuit { } } -/// Error occurs when manipulating [CircuitCalculator]. -#[derive(Debug, TeError)] -pub enum CircuitCalculatorError { - #[error("invalid target value: {0}")] - BadTargetValue(DeviceValueError), - #[error("{0}")] - Circuit(#[from] CircuitError), - #[error("arithmetic error: {0}")] - BadArithmetic(FloatingPointError), - #[error("bad provided value reducing computation steps: {0}")] - BadReuseValue(FloatingPointError), -} - -/// The helper for circuit value computation. +/// The evaluation result of circuit with target value. #[derive(Debug, Clone)] -pub struct CircuitCalculator { - /// The kind of the device. - device_kind: DeviceKind, - /// The target value. - target_value: f64, -} - -impl CircuitCalculator { - /// Initialize this calculator with given device kind and target value. - pub fn new(device_kind: DeviceKind, target_value: f64) -> Result { - let target_value = sanitize_device_value(target_value) - .map_err(|err| CircuitCalculatorError::BadTargetValue(err))?; - Ok(Self { - device_kind, - target_value, - }) - } - +pub struct CircuitEvaluation { /// The value of this circuit. - pub fn value(&self, circuit: &Circuit) -> Result { - Ok(circuit.compute(self.device_kind)?) - } - + pub value: f64, /// The signed difference between the target value and the value of this circuit. /// /// Positive value indicates that the value of this circuit is greater than the target value. /// Negative value indicates that the value of this circuit is less than the target value. - /// - /// * `circuit` - The circuit for computation. - /// * `value` - The value of the circuit computed by the [`value`](Self::value) method - /// for reducing computation steps, or `None` if you request this method to compute the value. - pub fn difference( - &self, - circuit: &Circuit, - value: Option, - ) -> Result { - let value = match value { - Some(v) => sanitize_floating_point(v) - .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, - None => self.value(circuit)?, - }; - - sanitize_floating_point(value - self.target_value) - .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) - } - + pub difference: f64, /// The unsigned difference between the target value and the value of this circuit. - /// - /// * `circuit` - The circuit for computation. - /// * `value` - The value of the circuit computed by the [`value`](Self::value) method - /// for reducing computation steps, or `None` if you request this method to compute the value. - /// * `difference` - The difference of the circuit computed by the - /// [`difference`](Self::difference) method for reducing computation steps, - /// or `None` if you request this method to compute the difference. - pub fn unsigned_difference( - &self, - circuit: &Circuit, - value: Option, - difference: Option, - ) -> Result { - let diff = match difference { - Some(d) => sanitize_floating_point(d) - .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, - None => self.difference(circuit, value)?, - }; - - sanitize_floating_point(diff.abs()) - .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) - } - + pub unsigned_difference: f64, /// The signed relative difference between the target value and the value of this circuit. /// /// Positive value indicates that the value of this circuit is greater than the target value. /// Negative value indicates that the value of this circuit is less than the target value. - /// - /// * `circuit` - The circuit for computation. - /// * `value` - The value of the circuit computed by the [`value`](Self::value) method - /// for reducing computation steps, or `None` if you request this method to compute the value. - /// * `difference` - The difference of the circuit computed by the - /// [`difference`](Self::difference) method for reducing computation steps, - /// or `None` if you request this method to compute the difference. - pub fn relative_difference( - &self, - circuit: &Circuit, - value: Option, - difference: Option, - ) -> Result { - let diff = match difference { - Some(d) => sanitize_floating_point(d) - .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, - None => self.difference(circuit, value)?, - }; - - sanitize_floating_point(diff / self.target_value) - .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) - } - + pub relative_difference: f64, /// The unsigned relative difference between the target value and the value of this circuit. - /// - /// * `circuit` - The circuit for computation. - /// * `value` - The value of the circuit computed by the [`value`](Self::value) method - /// for reducing computation steps, or `None` if you request this method to compute the value. - /// * `difference` - The difference of the circuit computed by the - /// [`difference`](Self::difference) method for reducing computation steps, - /// or `None` if you request this method to compute the difference. - /// * `relative_difference` - The relative difference of the circuit computed by the - /// [`relative_difference`](Self::relative_difference) method for reducing computation steps, - /// or `None` if you request this method to compute the relative difference. - /// - pub fn unsigned_relative_difference( - &self, - circuit: &Circuit, - value: Option, - difference: Option, - relative_difference: Option, - ) -> Result { - let rel_diff = match relative_difference { - Some(rd) => sanitize_floating_point(rd) - .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, - None => self.relative_difference(circuit, value, difference)?, - }; - - sanitize_floating_point(rel_diff.abs()) - .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) - } + pub unsigned_relative_difference: f64, } // endregion diff --git a/kernel/lcrconn/src/dataset.rs b/kernel/lcrconn/src/dataset.rs index 86e236f..a131c13 100644 --- a/kernel/lcrconn/src/dataset.rs +++ b/kernel/lcrconn/src/dataset.rs @@ -1,5 +1,5 @@ use crate::common::{ - DeviceValueError, FloatingPointError, sanitize_device_value, sanitize_floating_point, + DeviceValueError, FloatingPointError, validate_device_value, validate_floating_point, }; use ordered_float::OrderedFloat; use std::collections::HashSet; @@ -43,7 +43,7 @@ impl DatasetItem { /// Create a new dataset item with validation. fn new(value: f64, str_value: String) -> Result { // Check arguments - let value = sanitize_device_value(value)?; + let value = validate_device_value(value)?; if str_value.is_empty() { return Err(DatasetError::BlankDeviceValue); } @@ -392,7 +392,7 @@ pub fn from_human_readable_value(strl: &str) -> Result()?; - Ok(sanitize_floating_point(num * multiplier)?) + Ok(validate_floating_point(num * multiplier)?) } /// The unit scale for human readable value. diff --git a/kernel/lcrconn/src/query.rs b/kernel/lcrconn/src/query.rs index e9cacf1..3ec313c 100644 --- a/kernel/lcrconn/src/query.rs +++ b/kernel/lcrconn/src/query.rs @@ -1,6 +1,6 @@ use crate::common::{ Circuit, CircuitCalculator, CircuitCalculatorError, DeviceKind, DeviceValueError, - sanitize_device_value, + validate_device_value, }; use ordered_float::OrderedFloat; use thiserror::Error as TeError; @@ -54,9 +54,9 @@ impl Request { ) -> Result { // Check arguments let target_value = - sanitize_device_value(target_value).map_err(|err| RequestError::BadTargetValue(err))?; + validate_device_value(target_value).map_err(|err| RequestError::BadTargetValue(err))?; let tolerance = - sanitize_device_value(tolerance).map_err(|err| RequestError::BadTolerance(err))?; + validate_device_value(tolerance).map_err(|err| RequestError::BadTolerance(err))?; if count_limit == 0 || count_limit > MAX_RESPONSE_CNT { return Err(RequestError::BadCountLimit(count_limit)); } diff --git a/kernel/lcrconn/src/resolver/lut.rs b/kernel/lcrconn/src/resolver/lut.rs index fc50f31..87aeaed 100644 --- a/kernel/lcrconn/src/resolver/lut.rs +++ b/kernel/lcrconn/src/resolver/lut.rs @@ -28,7 +28,7 @@ pub struct LutItem { impl LutItem { /// Create a new LUT item by computing the circuit value. pub fn new(circuit: Circuit, device_kind: DeviceKind) -> Result { - let value = circuit.compute(device_kind)?; + let value = circuit.evaluate(device_kind)?; Ok(Self { circuit, value: OrderedFloat(value),