refactor: finish refactor of kernel

This commit is contained in:
2026-07-22 20:27:34 +08:00
parent 5a41c6266c
commit 782bd86407
7 changed files with 254 additions and 215 deletions
+6 -6
View File
@@ -105,15 +105,15 @@ pub struct App {
impl App { impl App {
/// Create a new app with the given configuration. /// Create a new app with the given configuration.
pub fn new(config: AppConfig) -> Result<Self> { pub fn new(config: AppConfig) -> Result<Self> {
let datasets = SpecCatalog::from_file( let sepcs = SpecCatalog::from_file(
config.get_resistor_dataset(), config.get_resistor_spec(),
config.get_capacitor_dataset(), config.get_capacitor_specs(),
config.get_inductor_dataset(), config.get_inductor_specs(),
)?; )?;
let resolver: Box<dyn Resolver> = match config.get_resolver() { let resolver: Box<dyn Resolver> = match config.get_resolver() {
AppResolver::Lut => Box::new(LutResolver::new(&datasets)?), AppResolver::Lut => Box::new(LutResolver::new(&sepcs)?),
AppResolver::Bfs => Box::new(BfsResolver::new(datasets)), AppResolver::Bfs => Box::new(BfsResolver::new(sepcs)),
}; };
Ok(Self { resolver }) Ok(Self { resolver })
+24 -24
View File
@@ -6,12 +6,12 @@ use clap::{Parser, ValueEnum};
pub struct AppConfig { pub struct AppConfig {
/// The resolver for the app. /// The resolver for the app.
resolver: AppResolver, resolver: AppResolver,
/// The path to the resistor dataset file. /// The path to the resistor specs file.
resistor_dataset: PathBuf, resistor_specs: PathBuf,
/// The path to the capacitor dataset file. /// The path to the capacitor specs file.
capacitor_dataset: PathBuf, capacitor_specs: PathBuf,
/// The path to the inductor dataset file. /// The path to the inductor specs file.
inductor_dataset: PathBuf, inductor_specs: PathBuf,
} }
impl AppConfig { impl AppConfig {
@@ -19,17 +19,17 @@ impl AppConfig {
pub fn get_resolver(&self) -> &AppResolver { pub fn get_resolver(&self) -> &AppResolver {
&self.resolver &self.resolver
} }
/// Get the path to the resistor dataset file. /// Get the path to the resistor specs file.
pub fn get_resistor_dataset(&self) -> &Path { pub fn get_resistor_spec(&self) -> &Path {
&self.resistor_dataset &self.resistor_specs
} }
/// Get the path to the capacitor dataset file. /// Get the path to the capacitor specs file.
pub fn get_capacitor_dataset(&self) -> &Path { pub fn get_capacitor_specs(&self) -> &Path {
&self.capacitor_dataset &self.capacitor_specs
} }
/// Get the path to the inductor dataset file. /// Get the path to the inductor specs file.
pub fn get_inductor_dataset(&self) -> &Path { pub fn get_inductor_specs(&self) -> &Path {
&self.inductor_dataset &self.inductor_specs
} }
} }
@@ -57,41 +57,41 @@ struct Cli {
#[arg(short = 's', long = "resolver", required = true, value_enum)] #[arg(short = 's', long = "resolver", required = true, value_enum)]
resolver: AppResolver, resolver: AppResolver,
/// The path to the resistor dataset file. /// The path to the resistor specs file.
#[arg( #[arg(
short = 'r', short = 'r',
long = "resistor", long = "resistor",
required = true, required = true,
value_name = "RESISTOR.TXT" value_name = "RESISTOR.TXT"
)] )]
resistor_dataset: PathBuf, resistor_specs: PathBuf,
/// The path to the inductor dataset file. /// The path to the inductor specs file.
#[arg( #[arg(
short = 'l', short = 'l',
long = "inductor", long = "inductor",
required = true, required = true,
value_name = "INDUCTOR.TXT" value_name = "INDUCTOR.TXT"
)] )]
inductor_dataset: PathBuf, inductor_specs: PathBuf,
/// The path to the capacitor dataset file. /// The path to the capacitor specs file.
#[arg( #[arg(
short = 'c', short = 'c',
long = "capacitor", long = "capacitor",
required = true, required = true,
value_name = "CAPACITOR.TXT" value_name = "CAPACITOR.TXT"
)] )]
capacitor_dataset: PathBuf, capacitor_specs: PathBuf,
} }
impl From<Cli> for AppConfig { impl From<Cli> for AppConfig {
fn from(args: Cli) -> Self { fn from(args: Cli) -> Self {
Self { Self {
resolver: args.resolver, resolver: args.resolver,
resistor_dataset: args.resistor_dataset, resistor_specs: args.resistor_specs,
capacitor_dataset: args.capacitor_dataset, capacitor_specs: args.capacitor_specs,
inductor_dataset: args.inductor_dataset, inductor_specs: args.inductor_specs,
} }
} }
} }
+47 -28
View File
@@ -116,6 +116,8 @@ pub enum CircuitError {
#[error("invalid target value: {0}")] #[error("invalid target value: {0}")]
BadTargetValue(DeviceValueError), BadTargetValue(DeviceValueError),
#[error("invalid pre-evaluated circuit value: {0}")]
BadCircuitValue(DeviceValueError),
#[error("bad previous evaluated joint value: {0}")] #[error("bad previous evaluated joint value: {0}")]
BadPreviousValue(DeviceValueError), BadPreviousValue(DeviceValueError),
#[error("floating point is invalid after arithmetic operation: {0}")] #[error("floating point is invalid after arithmetic operation: {0}")]
@@ -268,34 +270,6 @@ impl Circuit {
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<CircuitEvaluation, CircuitError> {
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. /// Get the device scale.
/// ///
/// # Returns /// # Returns
@@ -370,4 +344,49 @@ pub struct CircuitEvaluation {
pub unsigned_relative_difference: f64, pub unsigned_relative_difference: f64,
} }
impl CircuitEvaluation {
/// Internal used constructor. Passed circuit `value` must be checked before calling this.
fn new(value: f64, target_value: f64) -> Result<Self, CircuitError> {
// Check target value
let target_value =
validate_device_value(target_value).map_err(|err| CircuitError::BadTargetValue(err))?;
// Start evaluating
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))?;
// Return evaluation result
Ok(CircuitEvaluation {
value,
difference,
unsigned_difference,
relative_difference,
unsigned_relative_difference,
})
}
/// Evaluate circuit with device kind and target value.
pub fn from_circuit(
circuit: &Circuit,
device_kind: DeviceKind,
target_value: f64,
) -> Result<Self, CircuitError> {
// Fetch circuit value and evaluate it.
let value = circuit.evaluate(device_kind)?;
Self::new(value, target_value)
}
/// Evaluate circuit with pre-evaluated circuit value and target value.
pub fn from_circuit_value(value: f64, target_value: f64) -> Result<Self, CircuitError> {
// Check user given circuit value and evaluate it.
let value =
validate_device_value(value).map_err(|err| CircuitError::BadCircuitValue(err))?;
Self::new(value, target_value)
}
}
// endregion // endregion
+7 -3
View File
@@ -124,8 +124,11 @@ impl ResponseItem {
// I can use OnceLock to implement the behavior closing to Python cached_property. // I can use OnceLock to implement the behavior closing to Python cached_property.
// But I didn't do that due to the increased size of this struct, and inviable error handling. // But I didn't do that due to the increased size of this struct, and inviable error handling.
// So I decide to calculate all values in there. // So I decide to calculate all values in there.
let circuit_evaluation = let circuit_evaluation = CircuitEvaluation::from_circuit(
circuit.evaluate_with_target(request.get_target_value(), request.get_device_kind())?; &circuit,
request.get_device_kind(),
request.get_target_value(),
)?;
// Build self and return // Build self and return
Ok(Self { Ok(Self {
circuit, circuit,
@@ -217,7 +220,8 @@ impl Response {
} }
ResponsePriority::MoreAccuracy => { ResponsePriority::MoreAccuracy => {
items.sort_by(|a, b| { items.sort_by(|a, b| {
OrderedFloat(a.unsigned_difference()).cmp(&OrderedFloat(b.unsigned_difference())) OrderedFloat(a.unsigned_difference())
.cmp(&OrderedFloat(b.unsigned_difference()))
}); });
} }
} }
+27 -17
View File
@@ -1,5 +1,5 @@
use super::{Resolver, ResolverError}; use super::{Resolver, ResolverError};
use crate::common::{Circuit, CircuitError, DeviceKind, JointKind}; use crate::common::{Circuit, CircuitError, CircuitEvaluation, DeviceKind, JointKind};
use crate::query::{Request, Response, ResponseError}; use crate::query::{Request, Response, ResponseError};
use crate::spec::{SpecCatalog, SpecGroup}; use crate::spec::{SpecCatalog, SpecGroup};
use itertools::Itertools; use itertools::Itertools;
@@ -26,7 +26,7 @@ pub enum BfsResolverError {
pub struct BfsItem { pub struct BfsItem {
/// The circuit represented by this item. /// The circuit represented by this item.
circuit: Circuit, circuit: Circuit,
/// The computed value of the circuit. /// The evaluated value of the circuit.
value: f64, value: f64,
/// The unsigned difference between the target value and the value of this circuit. /// The unsigned difference between the target value and the value of this circuit.
unsigned_difference: f64, unsigned_difference: f64,
@@ -37,8 +37,11 @@ impl BfsItem {
pub fn new(circuit: Circuit, request: &Request) -> Result<Self, BfsResolverError> { pub fn new(circuit: Circuit, request: &Request) -> Result<Self, BfsResolverError> {
// YYC MARK: // YYC MARK:
// The same reason for replacing cached_property like I done in `ResponseItem`. // The same reason for replacing cached_property like I done in `ResponseItem`.
let eval = let eval = CircuitEvaluation::from_circuit(
circuit.evaluate_with_target(request.get_target_value(), request.get_device_kind())?; &circuit,
request.get_device_kind(),
request.get_target_value(),
)?;
Ok(Self { Ok(Self {
circuit, circuit,
@@ -52,7 +55,7 @@ impl BfsItem {
&self.circuit &self.circuit
} }
/// The computed value of the circuit. /// The evaluated value of the circuit.
pub fn value(&self) -> f64 { pub fn value(&self) -> f64 {
self.value self.value
} }
@@ -74,8 +77,8 @@ impl BfsItem {
/// A resolver that uses breadth first search to find the best matching circuits. /// A resolver that uses breadth first search to find the best matching circuits.
pub struct BfsResolver { pub struct BfsResolver {
/// The datasets for all device kinds. /// The specs for all device kinds.
datasets: SpecCatalog, specs: SpecCatalog,
} }
impl BfsResolver { impl BfsResolver {
@@ -92,7 +95,7 @@ impl BfsResolver {
/// Iterate all possible circuits with one device without repeating equivalent topology. /// Iterate all possible circuits with one device without repeating equivalent topology.
pub fn iter_one_device_circuit(specs: &SpecGroup) -> impl Iterator<Item = Circuit> { pub fn iter_one_device_circuit(specs: &SpecGroup) -> impl Iterator<Item = Circuit> {
// Every single device is unique so we directly output them. // Every single device is unique so we directly output them.
// This feature is insured by dataset itself. // This feature is insured by spec itself.
specs specs
.iter() .iter()
.map(|v1| Circuit::from_one_device(v1).expect("unexpected failure on building circuit")) .map(|v1| Circuit::from_one_device(v1).expect("unexpected failure on building circuit"))
@@ -146,16 +149,16 @@ impl BfsResolver {
} }
impl BfsResolver { impl BfsResolver {
/// Create a new BFS resolver with the given datasets. /// Create a new BFS resolver with the given specs.
pub fn new(datasets: SpecCatalog) -> Self { pub fn new(specs: SpecCatalog) -> Self {
Self { datasets } Self { specs }
} }
fn pick_specs(&self, device_kind: DeviceKind) -> &SpecGroup { fn pick_specs(&self, device_kind: DeviceKind) -> &SpecGroup {
match device_kind { match device_kind {
DeviceKind::Resistor => self.datasets.resistor_specs(), DeviceKind::Resistor => self.specs.resistor_specs(),
DeviceKind::Capacitor => self.datasets.capacitor_specs(), DeviceKind::Capacitor => self.specs.capacitor_specs(),
DeviceKind::Inductor => self.datasets.inductor_specs(), DeviceKind::Inductor => self.specs.inductor_specs(),
} }
} }
@@ -172,7 +175,7 @@ impl BfsResolver {
} }
fn intern_resolve(&self, request: &Request) -> Result<Response, BfsResolverError> { fn intern_resolve(&self, request: &Request) -> Result<Response, BfsResolverError> {
// Pick dataset from collection // Pick specs group from catalog
let specs = self.pick_specs(request.get_device_kind()); let specs = self.pick_specs(request.get_device_kind());
// Create the result bucket. // Create the result bucket.
@@ -205,7 +208,9 @@ impl Resolver for BfsResolver {
} }
} }
// endregion: // endregion
// endregion
// region: Result Bucket Helper // region: Result Bucket Helper
@@ -279,7 +284,7 @@ impl Ord for ResultBucketItem {
/// ///
/// When the bucket is full, inserting a new item only succeeds if its score /// When the bucket is full, inserting a new item only succeeds if its score
/// is less than the current maximum; the maximum is then evicted. /// is less than the current maximum; the maximum is then evicted.
pub struct ResultBucket { struct ResultBucket {
/// Maximum number of items the bucket can hold. /// Maximum number of items the bucket can hold.
n: usize, n: usize,
/// Max-heap of [`ResultBucketItem`]. /// Max-heap of [`ResultBucketItem`].
@@ -304,13 +309,18 @@ impl ResultBucket {
}) })
} }
} }
// YYC MARK:
// I want to preserve these 2 functions so I add `allow(dead_code)` to them.
/// The number of items currently in the bucket. /// The number of items currently in the bucket.
#[allow(dead_code)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.heap.len() self.heap.len()
} }
/// Whether the bucket is empty. /// Whether the bucket is empty.
#[allow(dead_code)]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.heap.is_empty() self.heap.is_empty()
} }
+139 -133
View File
@@ -1,22 +1,24 @@
use super::bfs::BfsResolver; use super::bfs::BfsResolver;
use super::{Resolver, ResolverError}; use super::{Resolver, ResolverError};
use crate::common::{Circuit, CircuitCalculator, CircuitCalculatorError, CircuitError, DeviceKind}; use crate::common::{Circuit, CircuitError, CircuitEvaluation, DeviceKind};
use crate::spec::{SpecGroup, SpecCatalog}; use crate::spec::{SpecGroup, SpecCatalog};
use crate::query::{Request, Response, ResponseError}; use crate::query::{Request, Response, ResponseError};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use thiserror::Error as TeError; use thiserror::Error as TeError;
// region: LUT Resolver Kernel
/// Errors occurs in LUT resolver. /// Errors occurs in LUT resolver.
#[derive(Debug, TeError)] #[derive(Debug, TeError)]
pub enum LutResolverError { pub enum LutResolverError {
#[error("failed to build circuit: {0}")] #[error("failed on evaluating circuit: {0}")]
Circuit(#[from] CircuitError), CircuitCalculator(#[from] CircuitError),
#[error("failed on computing circuit properties: {0}")]
CircuitCalculator(#[from] CircuitCalculatorError),
#[error("fail to build response: {0}")] #[error("fail to build response: {0}")]
Response(#[from] ResponseError), Response(#[from] ResponseError),
} }
// region: LUT Item
/// An item in the lookup table. /// An item in the lookup table.
pub struct LutItem { pub struct LutItem {
/// The circuit represented by this item. /// The circuit represented by this item.
@@ -46,6 +48,137 @@ impl LutItem {
} }
} }
// endregion
// region: LUT Resolver
/// A resolver that uses a lookup table to find the best matching circuit.
pub struct LutResolver {
/// The lookup table for resistors.
resistor_lut: Vec<LutItem>,
/// The lookup table for capacitors.
capacitor_lut: Vec<LutItem>,
/// The lookup table for inductors.
inductor_lut: Vec<LutItem>,
}
impl LutResolver {
/// Create a new LUT resolver by building lookup tables from the given specs.
pub fn new(specs: &SpecCatalog) -> Result<Self, LutResolverError> {
Ok(Self {
resistor_lut: Self::build_lut(specs.resistor_specs(), DeviceKind::Resistor)?,
capacitor_lut: Self::build_lut(specs.capacitor_specs(), DeviceKind::Capacitor)?,
inductor_lut: Self::build_lut(specs.inductor_specs(), DeviceKind::Inductor)?,
})
}
fn build_lut(
specs: &SpecGroup,
device_kind: DeviceKind,
) -> Result<Vec<LutItem>, LutResolverError> {
// Fetch all items
let mut lut = itertools::chain!(
BfsResolver::iter_one_device_circuit(&specs),
BfsResolver::iter_two_devices_circuit(&specs),
BfsResolver::iter_three_devices_circuit(&specs)
)
.map(|circuit| -> Result<LutItem, LutResolverError> { LutItem::new(circuit, device_kind) })
.collect::<Result<Vec<_>, _>>()?;
// Sort them and return
lut.sort_by(|a, b| a.value.cmp(&b.value));
Ok(lut)
}
fn pick_lut(&self, device_kind: DeviceKind) -> &[LutItem] {
match device_kind {
DeviceKind::Resistor => &self.resistor_lut,
DeviceKind::Capacitor => &self.capacitor_lut,
DeviceKind::Inductor => &self.inductor_lut,
}
}
fn intern_resolve(&self, request: &Request) -> Result<Response, LutResolverError> {
let lut = self.pick_lut(request.get_device_kind());
let target_value = request.get_target_value();
let count_limit = request.get_count_limit();
let mut bucket: Vec<Circuit> = Vec::new();
// Locate the insertion point of target in the sorted LUT.
// left/right start at the two nearest neighbours and expand outward.
let lower_bound = 0;
let upper_bound = lut.len() - 1;
let target = OrderedFloat(target_value);
let idx = lut.partition_point(|item| item.value < target);
let mut left = RangedIndex::new(idx, lower_bound, upper_bound);
let mut right = left.clone();
left.dec();
// Expand outward non-symmetrically: at each step compare the two
// candidates on each side and advance the one that is closer to the target.
// This guarantees items are visited in strictly increasing
// difference order, so the first N items within tolerance are exactly
// the N best matches.
loop {
// Check result count
if bucket.len() >= count_limit {
break;
}
let go_left = if left.in_range() {
if right.in_range() {
let left_item = &lut[left.position()];
let left_diff = CircuitEvaluation::from_circuit_value(left_item.value(),target_value)?.unsigned_difference;
let right_item = &lut[right.position()];
let right_diff = CircuitEvaluation::from_circuit_value(right_item.value(), target_value)?.unsigned_difference;
left_diff <= right_diff
} else {
true
}
} else {
if right.in_range() {
false
} else {
break;
}
};
let item = if go_left {
let item = &lut[left.position()];
left.dec();
item
} else {
let item = &lut[right.position()];
right.inc();
item
};
let diff = CircuitEvaluation::from_circuit_value(item.value(), target_value)?.unsigned_difference;
// Since the LUT is sorted, values on each side only move further
// from target as we advance. Once one side exceeds tolerance,
// the rest of that side is guaranteed out of range.
if diff > request.get_tolerance() {
break;
}
bucket.push(item.circuit().clone());
}
Ok(Response::new(request, bucket.into_iter())?)
}
}
impl Resolver for LutResolver {
fn resolve(&self, request: &Request) -> Result<Response, ResolverError> {
Ok(self.intern_resolve(request)?)
}
}
// endregion
// endregion
// region: Ranged Index Helper
/// The ranged index for bisect LUT finding in resolver. /// The ranged index for bisect LUT finding in resolver.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RangedIndex { pub struct RangedIndex {
@@ -115,131 +248,4 @@ impl RangedIndex {
} }
} }
/// A resolver that uses a lookup table to find the best matching circuit. // endregion
pub struct LutResolver {
/// The lookup table for resistors.
resistor_lut: Vec<LutItem>,
/// The lookup table for capacitors.
capacitor_lut: Vec<LutItem>,
/// The lookup table for inductors.
inductor_lut: Vec<LutItem>,
}
impl LutResolver {
/// Create a new LUT resolver by building lookup tables from the given datasets.
pub fn new(datasets: &SpecCatalog) -> Result<Self, LutResolverError> {
Ok(Self {
resistor_lut: Self::build_lut(datasets.resistor_specs(), DeviceKind::Resistor)?,
capacitor_lut: Self::build_lut(datasets.capacitor_specs(), DeviceKind::Capacitor)?,
inductor_lut: Self::build_lut(datasets.inductor_specs(), DeviceKind::Inductor)?,
})
}
fn build_lut(
dataset: &SpecGroup,
device_kind: DeviceKind,
) -> Result<Vec<LutItem>, LutResolverError> {
// Fetch all items
let mut lut = itertools::chain!(
BfsResolver::iter_one_device_circuit(&dataset),
BfsResolver::iter_two_devices_circuit(&dataset),
BfsResolver::iter_three_devices_circuit(&dataset)
)
.map(|circuit| -> Result<LutItem, LutResolverError> { LutItem::new(circuit?, device_kind) })
.collect::<Result<Vec<_>, _>>()?;
// Sort them and return
lut.sort_by(|a, b| a.value.cmp(&b.value));
Ok(lut)
}
fn pick_lut(&self, device_kind: DeviceKind) -> &[LutItem] {
match device_kind {
DeviceKind::Resistor => &self.resistor_lut,
DeviceKind::Capacitor => &self.capacitor_lut,
DeviceKind::Inductor => &self.inductor_lut,
}
}
fn intern_resolve(&self, request: &Request) -> Result<Response, LutResolverError> {
let lut = self.pick_lut(request.get_device_kind());
let target = OrderedFloat(request.get_target_value());
let count_limit = request.get_count_limit();
let mut bucket: Vec<Circuit> = Vec::new();
// Locate the insertion point of target in the sorted LUT.
// left/right start at the two nearest neighbours and expand outward.
let lower_bound = 0;
let upper_bound = lut.len() - 1;
let idx = lut.partition_point(|item| item.value < target);
let mut left = RangedIndex::new(idx, lower_bound, upper_bound);
let mut right = left.clone();
left.dec();
// Expand outward non-symmetrically: at each step compare the two
// candidates on each side and advance the one that is closer to the
// target. This guarantees items are visited in strictly increasing
// difference order, so the first N items within tolerance are exactly
// the N best matches.
let ccalc = CircuitCalculator::new(request.get_device_kind(), target.0)?;
loop {
// Check result count
if bucket.len() >= count_limit {
break;
}
let go_left = if left.in_range() {
if right.in_range() {
let left_item = &lut[left.position()];
let left_diff = ccalc.unsigned_difference(
left_item.circuit(),
Some(left_item.value()),
None,
)?;
let right_item = &lut[right.position()];
let right_diff = ccalc.unsigned_difference(
right_item.circuit(),
Some(right_item.value()),
None,
)?;
left_diff <= right_diff
} else {
true
}
} else {
if right.in_range() {
false
} else {
break;
}
};
let item = if go_left {
let item = &lut[left.position()];
left.dec();
item
} else {
let item = &lut[right.position()];
right.inc();
item
};
let diff = ccalc.unsigned_difference(item.circuit(), Some(item.value()), None)?;
// Since the LUT is sorted, values on each side only move further
// from target as we advance. Once one side exceeds tolerance,
// the rest of that side is guaranteed out of range.
if diff > request.get_tolerance() {
break;
}
bucket.push(item.circuit().clone());
}
Ok(Response::new(request, bucket.into_iter())?)
}
}
impl Resolver for LutResolver {
fn resolve(&self, request: &Request) -> Result<Response, ResolverError> {
Ok(self.intern_resolve(request)?)
}
}
+4 -4
View File
@@ -3,9 +3,9 @@ use lcrconn::spec;
#[test] #[test]
fn test_spec_preset() { fn test_spec_preset() {
// All individual preset and catalog preset should nit panic // All individual preset and catalog preset should nit panic
let specs = spec::SpecGroup::resistor_preset(); let _ = spec::SpecGroup::resistor_preset();
let specs = spec::SpecGroup::capacitor_preset(); let _ = spec::SpecGroup::capacitor_preset();
let specs = spec::SpecGroup::inductor_preset(); let _ = spec::SpecGroup::inductor_preset();
let specs = spec::SpecCatalog::devices_preset(); let _ = spec::SpecCatalog::devices_preset();
} }