diff --git a/kernel/lcrconn/src/common.rs b/kernel/lcrconn/src/common.rs index 795b237..38b7649 100644 --- a/kernel/lcrconn/src/common.rs +++ b/kernel/lcrconn/src/common.rs @@ -1,6 +1,41 @@ use strum_macros::EnumIter; use thiserror::Error as TeError; +// region: Sanitizer + +#[derive(Debug, TeError)] +#[error("given floating value {0} is invalid")] +pub struct FloatingPointError(f64); + +pub fn sanitize_floating_point(f: f64) -> Result { + if f.is_finite() { + Ok(f) + } else { + Err(FloatingPointError(f)) + } +} + +#[derive(Debug, TeError)] +pub enum DeviceValueError { + #[error("{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)?; + if f > 0f64 { + Ok(f) + } else { + Err(DeviceValueError::OutOfRange(f)) + } +} + +// endregion + +// region: Circuit Utilities + /// The kind of device. #[derive(Debug, Clone, Copy)] pub enum DeviceKind { @@ -35,82 +70,6 @@ impl JointKind { } } -/// Error occurs when manipulating [SubCircuit]. -#[derive(Debug, TeError)] -pub enum SubCircuitError { - #[error("given circuit device value {0} should greater than zero")] - BadDeviceValue(f64), - #[error("the previous computed circuit value {0} should greater than zero")] - BadPreviousValue(f64), - #[error("bad float point arithmetic")] - BadArithmetic, -} - -/// The part of circuit composed of two devices and the joint kind. -#[derive(Debug, Clone)] -pub struct SubCircuit { - /// The value of the device. - device_value: f64, - /// The joint kind between this device and the next device. - joint_kind: JointKind, -} - -impl SubCircuit { - /// Initialize subcircuit with given device value and joint kind. - /// - /// The input device value should greater than zero, - /// otherwise an error will return. - pub fn new(device_value: f64, joint_kind: JointKind) -> Result { - if device_value > 0f64 { - Ok(Self { - device_value, - joint_kind, - }) - } else { - Err(SubCircuitError::BadDeviceValue(device_value)) - } - } - - /// Compute the joint value with given previous computed value and device kind. - /// - /// Parameter `value` should be the value computed from previous devices. - /// And it should greater than zero. - /// `device_kind` is the kind of the device. - pub fn compute(&self, value: f64, device_kind: DeviceKind) -> Result { - // Check the range of provided value for computing - if !(value > 0f64) { - return Err(SubCircuitError::BadPreviousValue(value)); - } - - // We perform series connect for: series resistor, series inductor and parallel capacitor. - // We perform parallel connect for: parallel resistor, parallel inductor and series capacitor. - let joint_kind = match device_kind { - DeviceKind::Capacitor => self.joint_kind.flip(), - _ => self.joint_kind, - }; - - let rv = match joint_kind { - JointKind::Series => self.device_value + value, - JointKind::Parallel => (self.device_value * value) / (self.device_value + value), - }; - if rv.is_finite() { - Ok(rv) - } else { - Err(SubCircuitError::BadArithmetic) - } - } - - /// Get the device value. - pub fn device_value(&self) -> f64 { - self.device_value - } - - /// Get the joint kind. - pub fn joint_kind(&self) -> JointKind { - self.joint_kind - } -} - /// The scale of devices in the circuit. #[derive(Debug, Clone, Copy)] pub enum CircuitDeviceScale { @@ -134,11 +93,84 @@ impl CircuitDeviceScale { } } +// endregion + +// region: Circuit Stuff + +/// Error occurs when manipulating [SubCircuit]. +#[derive(Debug, TeError)] +pub enum SubCircuitError { + #[error("invalid device value in circuit: {0}")] + BadDeviceValue(DeviceValueError), + #[error("bad previous computed circuit value: {0}")] + BadPreviousValue(DeviceValueError), + #[error("arithmetic error: {0}")] + BadArithmetic(FloatingPointError), +} + +/// The part of circuit composed of two devices and the joint kind. +#[derive(Debug, Clone)] +pub struct SubCircuit { + /// The value of the device. + device_value: f64, + /// The joint kind between this device and the next device. + joint_kind: JointKind, +} + +impl SubCircuit { + /// Initialize subcircuit with given device value and joint kind. + /// + /// 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))?; + Ok(Self { + device_value, + joint_kind, + }) + } + + /// Compute the joint value with given previous computed value and device kind. + /// + /// Parameter `value` should be the value computed from previous devices. + /// And it should greater than zero. + /// `device_kind` is the kind of the device. + pub fn compute(&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))?; + + // We perform series connect for: series resistor, series inductor and parallel capacitor. + // We perform parallel connect for: parallel resistor, parallel inductor and series capacitor. + let joint_kind = match device_kind { + DeviceKind::Capacitor => self.joint_kind.flip(), + _ => self.joint_kind, + }; + + sanitize_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)) + } + + /// Get the device value. + pub fn device_value(&self) -> f64 { + self.device_value + } + + /// Get the joint kind. + pub fn joint_kind(&self) -> JointKind { + self.joint_kind + } +} + /// Error occurs when manipulating [Circuit]. #[derive(Debug, TeError)] pub enum CircuitError { - #[error("given circuit device value {0} should greater than zero")] - BadDeviceValue(f64), + #[error("invalid device value in circuit: {0}")] + BadDeviceValue(DeviceValueError), #[error("third device cannot exist without second device when building circuit")] BlankSecondSubCircuit, #[error("{0}")] @@ -170,9 +202,8 @@ impl Circuit { third_device_subckt: Option, ) -> Result { // Check the value of first device - if !(first_device_value > 0f64) { - return Err(CircuitError::BadDeviceValue(first_device_value)); - } + let first_device_value = sanitize_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); @@ -292,14 +323,14 @@ impl Circuit { /// Error occurs when manipulating [CircuitCalculator]. #[derive(Debug, TeError)] pub enum CircuitCalculatorError { - #[error("given target value {0} should be greater than zero")] - BadTargetValue(f64), + #[error("invalid target value: {0}")] + BadTargetValue(DeviceValueError), #[error("{0}")] Circuit(#[from] CircuitError), - #[error("bad float point arithmetic")] - BadArithmetic, - #[error("provided value {0} reducing computation steps is invalid")] - BadReuseValue(f64), + #[error("arithmetic error: {0}")] + BadArithmetic(FloatingPointError), + #[error("bad provided value reducing computation steps: {0}")] + BadReuseValue(FloatingPointError), } /// The helper for circuit value computation. @@ -314,14 +345,12 @@ pub struct CircuitCalculator { impl CircuitCalculator { /// Initialize this calculator with given device kind and target value. pub fn new(device_kind: DeviceKind, target_value: f64) -> Result { - if target_value > 0f64 { - Ok(Self { - device_kind, - target_value, - }) - } else { - Err(CircuitCalculatorError::BadTargetValue(target_value)) - } + let target_value = sanitize_device_value(target_value) + .map_err(|err| CircuitCalculatorError::BadTargetValue(err))?; + Ok(Self { + device_kind, + target_value, + }) } /// The value of this circuit. @@ -343,22 +372,13 @@ impl CircuitCalculator { value: Option, ) -> Result { let value = match value { - Some(v) => { - if v.is_finite() { - v - } else { - return Err(CircuitCalculatorError::BadReuseValue(v)); - } - } + Some(v) => sanitize_floating_point(v) + .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, None => self.value(circuit)?, }; - let rv = value - self.target_value; - if rv.is_finite() { - Ok(rv) - } else { - Err(CircuitCalculatorError::BadArithmetic) - } + sanitize_floating_point(value - self.target_value) + .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) } /// The unsigned difference between the target value and the value of this circuit. @@ -376,22 +396,13 @@ impl CircuitCalculator { difference: Option, ) -> Result { let diff = match difference { - Some(d) => { - if d.is_finite() { - d - } else { - return Err(CircuitCalculatorError::BadReuseValue(d)); - } - } + Some(d) => sanitize_floating_point(d) + .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, None => self.difference(circuit, value)?, }; - let rv = diff.abs(); - if rv.is_finite() { - Ok(rv) - } else { - Err(CircuitCalculatorError::BadArithmetic) - } + sanitize_floating_point(diff.abs()) + .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) } /// The signed relative difference between the target value and the value of this circuit. @@ -412,22 +423,13 @@ impl CircuitCalculator { difference: Option, ) -> Result { let diff = match difference { - Some(d) => { - if d.is_finite() { - d - } else { - return Err(CircuitCalculatorError::BadReuseValue(d)); - } - } + Some(d) => sanitize_floating_point(d) + .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, None => self.difference(circuit, value)?, }; - let rv = diff / self.target_value; - if rv.is_finite() { - Ok(rv) - } else { - Err(CircuitCalculatorError::BadArithmetic) - } + sanitize_floating_point(diff / self.target_value) + .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) } /// The unsigned relative difference between the target value and the value of this circuit. @@ -450,21 +452,14 @@ impl CircuitCalculator { relative_difference: Option, ) -> Result { let rel_diff = match relative_difference { - Some(rd) => { - if rd.is_finite() { - rd - } else { - return Err(CircuitCalculatorError::BadReuseValue(rd)); - } - } + Some(rd) => sanitize_floating_point(rd) + .map_err(|err| CircuitCalculatorError::BadReuseValue(err))?, None => self.relative_difference(circuit, value, difference)?, }; - let rv = rel_diff.abs(); - if rv.is_finite() { - Ok(rv) - } else { - Err(CircuitCalculatorError::BadArithmetic) - } + sanitize_floating_point(rel_diff.abs()) + .map_err(|err| CircuitCalculatorError::BadArithmetic(err)) } } + +// endregion diff --git a/kernel/lcrconn/src/dataset.rs b/kernel/lcrconn/src/dataset.rs index 0dd7c5e..fefd42d 100644 --- a/kernel/lcrconn/src/dataset.rs +++ b/kernel/lcrconn/src/dataset.rs @@ -1,7 +1,12 @@ +use thiserror::Error as TeError; use std::collections::HashSet; use std::path::Path; -use crate::common::LcrConnError; +/// Error occurs when building dataset. +#[derive(Debug, TeError)] +pub enum DatasetError { + +} /// An item in the dataset. #[derive(Clone, Debug)] @@ -17,14 +22,14 @@ impl DatasetItem { /// /// # Errors /// - /// Returns [`LcrConnError::InvalidDatasetValue`] if the value is not greater than 0. - /// Returns [`LcrConnError::EmptyDatasetItem`] if the string value is empty. - pub fn new(value: f64, str_value: String) -> Result { + /// Returns [`DatasetError::InvalidDatasetValue`] if the value is not greater than 0. + /// Returns [`DatasetError::EmptyDatasetItem`] if the string value is empty. + pub fn new(value: f64, str_value: String) -> Result { if value <= 0.0 { - return Err(LcrConnError::InvalidDatasetValue(value)); + return Err(DatasetError::InvalidDatasetValue(value)); } if str_value.is_empty() { - return Err(LcrConnError::EmptyDatasetItem); + return Err(DatasetError::EmptyDatasetItem); } Ok(Self { value, str_value }) } @@ -46,10 +51,10 @@ impl Dataset { /// /// # Errors /// - /// Returns [`LcrConnError::DuplicateDatasetItem`] if duplicate values are found. - /// Returns [`LcrConnError::EmptyDataset`] if the iterable produces no items. - /// Returns [`LcrConnError::InvalidHumanReadableValue`] if a value cannot be parsed. - pub fn from_iterable(str_values: I) -> Result + /// Returns [`DatasetError::DuplicateDatasetItem`] if duplicate values are found. + /// Returns [`DatasetError::EmptyDataset`] if the iterable produces no items. + /// Returns [`DatasetError::InvalidHumanReadableValue`] if a value cannot be parsed. + pub fn from_iterable(str_values: I) -> Result where I: IntoIterator, S: Into, @@ -64,7 +69,7 @@ impl Dataset { let value = from_human_readable_value(&str_value)?; // Check and update set if !seen.insert(value) { - return Err(LcrConnError::DuplicateDatasetItem(str_value)); + return Err(DatasetError::DuplicateDatasetItem(str_value)); } // Add into result items.push(DatasetItem::new(value, str_value)?); @@ -72,7 +77,7 @@ impl Dataset { // Check empty case if items.is_empty() { - return Err(LcrConnError::EmptyDataset); + return Err(DatasetError::EmptyDataset); } // Ok, assign it @@ -86,7 +91,7 @@ impl Dataset { /// # Errors /// /// See [`Dataset::from_iterable`]. - pub fn from_text(text: &str) -> Result { + pub fn from_text(text: &str) -> Result { let lines: Vec = text .lines() .map(|line| line.trim().to_string()) @@ -99,9 +104,9 @@ impl Dataset { /// /// # Errors /// - /// Returns [`LcrConnError::Io`] if the file cannot be read. + /// Returns [`DatasetError::Io`] if the file cannot be read. /// See [`Dataset::from_iterable`] for other errors. - pub fn from_file(path: impl AsRef) -> Result { + pub fn from_file(path: impl AsRef) -> Result { let text = std::fs::read_to_string(path)?; Self::from_text(&text) } @@ -111,7 +116,7 @@ impl Dataset { /// # Errors /// /// See [`Dataset::from_iterable`]. - pub fn resistor_preset() -> Result { + pub fn resistor_preset() -> Result { Self::from_iterable([ "100", "220", "270", "390", "470", "680", "1k", "1.2k", "1.5k", "2.2k", "3.3k", "4.7k", "6.8k", "10k", "47k", "100k", "1M", @@ -123,7 +128,7 @@ impl Dataset { /// # Errors /// /// See [`Dataset::from_iterable`]. - pub fn capacitor_preset() -> Result { + pub fn capacitor_preset() -> Result { Self::from_iterable([ "10p", "22p", "33p", "47p", "68p", "100p", "150p", "220p", "330p", "470p", "560p", "1u", "2.2u", "3.3u", "4.7u", "10u", "22u", "47u", "100u", "220u", "470u", @@ -135,7 +140,7 @@ impl Dataset { /// # Errors /// /// See [`Dataset::from_iterable`]. - pub fn inductor_preset() -> Result { + pub fn inductor_preset() -> Result { Self::from_iterable([ "0.1u", "0.15u", "0.47u", "0.68u", "1u", "1.5u", "2.2u", "3.3u", "4.7u", "6.8u", "8.2u", "10u", "15u", "22u", "33u", "47u", "68u", "100u", @@ -155,8 +160,8 @@ impl Dataset { /// /// # Errors /// - /// Returns [`LcrConnError::Io`] if the file cannot be written. - pub fn save_file(&self, path: impl AsRef) -> Result<(), LcrConnError> { + /// Returns [`DatasetError::Io`] if the file cannot be written. + pub fn save_file(&self, path: impl AsRef) -> Result<(), DatasetError> { std::fs::write(path, self.save_text())?; Ok(()) } @@ -206,7 +211,7 @@ impl DatasetCollection { resistor: I1, capacitor: I2, inductor: I3, - ) -> Result + ) -> Result where I1: IntoIterator, S1: Into, @@ -233,7 +238,7 @@ impl DatasetCollection { /// # Errors /// /// See [`Dataset::from_text`]. - pub fn from_text(resistor: &str, capacitor: &str, inductor: &str) -> Result { + pub fn from_text(resistor: &str, capacitor: &str, inductor: &str) -> Result { Ok(Self { resistor: Dataset::from_text(resistor)?, capacitor: Dataset::from_text(capacitor)?, @@ -256,7 +261,7 @@ impl DatasetCollection { resistor: impl AsRef, capacitor: impl AsRef, inductor: impl AsRef, - ) -> Result { + ) -> Result { Ok(Self { resistor: Dataset::from_file(resistor)?, capacitor: Dataset::from_file(capacitor)?, @@ -269,7 +274,7 @@ impl DatasetCollection { /// # Errors /// /// See [`Dataset::from_iterable`]. - pub fn devices_preset() -> Result { + pub fn devices_preset() -> Result { Ok(Self { resistor: Dataset::resistor_preset()?, capacitor: Dataset::capacitor_preset()?, @@ -300,13 +305,13 @@ impl DatasetCollection { /// /// # Errors /// - /// Returns [`LcrConnError::Io`] if any file cannot be written. + /// Returns [`DatasetError::Io`] if any file cannot be written. pub fn save_file( &self, resistor: impl AsRef, capacitor: impl AsRef, inductor: impl AsRef, - ) -> Result<(), LcrConnError> { + ) -> Result<(), DatasetError> { self.resistor.save_file(resistor)?; self.capacitor.save_file(capacitor)?; self.inductor.save_file(inductor)?; @@ -329,6 +334,11 @@ impl DatasetCollection { } } +#[derive(Debug, TeError)] +pub enum ParseHumanReadableValueError { + +} + /// Convert human readable value to float. /// /// # Arguments @@ -341,8 +351,8 @@ impl DatasetCollection { /// /// # Errors /// -/// Returns [`LcrConnError::InvalidHumanReadableValue`] if the input string is not a valid number. -pub fn from_human_readable_value(strl: &str) -> Result { +/// Returns [`DatasetError::InvalidHumanReadableValue`] if the input string is not a valid number. +pub fn from_human_readable_value(strl: &str) -> Result { let strl = strl.trim(); let (num_part, multiplier) = if let Some(stripped) = strl.strip_suffix('n') { @@ -366,7 +376,7 @@ pub fn from_human_readable_value(strl: &str) -> Result { num_part .parse::() .map(|v| v * multiplier) - .map_err(|_| LcrConnError::InvalidHumanReadableValue(strl.to_string())) + .map_err(|_| DatasetError::InvalidHumanReadableValue(strl.to_string())) } /// The unit scale for human readable value.