Rewrite SpecifiedValue as a read-only structure

This commit is contained in:
Aaron Fenyes 2025-03-03 23:10:28 -08:00
parent 8b4a72c60c
commit 84bfdefccb
4 changed files with 41 additions and 44 deletions

View file

@ -13,6 +13,7 @@ itertools = "0.13.0"
js-sys = "0.3.70"
lazy_static = "1.5.0"
nalgebra = "0.33.0"
readonly = "0.2.12"
rustc-hash = "2.0.0"
slab = "0.4.9"
sycamore = "0.9.0-beta.3"

View file

@ -7,7 +7,7 @@ use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */
use crate::{
engine::{Q, local_unif_to_std, realize_gram, ConfigSubspace, PartialMatrix},
specified::{SpecifiedValue, SpecifiedValue::{Absent, Present}}
specified::SpecifiedValue
};
// the types of the keys we use to access an assembly's elements and regulators
@ -239,7 +239,7 @@ impl Assembly {
reps.0.dot(&(&*Q * reps.1))
}
);
let set_point = create_signal(Absent);
let set_point = create_signal(SpecifiedValue::from_empty_spec());
self.insert_regulator(Regulator {
subjects: subjects,
measurement: measurement,
@ -256,9 +256,9 @@ impl Assembly {
&JsValue::from(reg.subjects.0),
&JsValue::from(reg.subjects.1),
&JsValue::from(":"),
&JsValue::from(reg.set_point.with_untracked(
|set_pt| set_pt.spec()
))
&reg.set_point.with_untracked(
|set_pt| JsValue::from(set_pt.spec.as_str())
)
);
}
});
@ -269,7 +269,7 @@ impl Assembly {
console::log_1(&JsValue::from(
format!("Updated constraint with subjects ({}, {})", subjects.0, subjects.1)
));
if set_point.with(|set_pt| matches!(set_pt, Present { .. })) {
if set_point.with(|set_pt| set_pt.is_present()) {
self.realize();
}
});
@ -292,11 +292,11 @@ impl Assembly {
self.regulators.with_untracked(|regs| {
for (_, reg) in regs {
reg.set_point.with_untracked(|set_pt| {
if let Present { value, .. } = set_pt {
if let Some(val) = set_pt.value {
let subjects = reg.subjects;
let row = elts[subjects.0].column_index.unwrap();
let col = elts[subjects.1].column_index.unwrap();
gram_to_be.push_sym(row, col, *value);
gram_to_be.push_sym(row, col, val);
}
});
}

View file

@ -14,7 +14,7 @@ use crate::{
Regulator,
RegulatorKey
},
specified::{SpecifiedValue, SpecifiedValue::{Absent, Present}}
specified::SpecifiedValue
};
// an editable view of a regulator
@ -22,7 +22,7 @@ use crate::{
fn RegulatorInput(regulator: Regulator) -> View {
let valid = create_signal(true);
let value = create_signal(
regulator.set_point.with_untracked(|set_pt| set_pt.spec())
regulator.set_point.with_untracked(|set_pt| set_pt.spec.clone())
);
// this closure resets the input value to the regulator's set point
@ -30,7 +30,7 @@ fn RegulatorInput(regulator: Regulator) -> View {
let reset_value = move || {
batch(|| {
valid.set(true);
value.set(regulator.set_point.with(|set_pt| set_pt.spec()));
value.set(regulator.set_point.with(|set_pt| set_pt.spec.clone()));
})
};
@ -44,9 +44,10 @@ fn RegulatorInput(regulator: Regulator) -> View {
class=move || {
if valid.get() {
regulator.set_point.with(|set_pt| {
match set_pt {
Absent => "regulator-input",
Present { .. } => "regulator-input constraint"
if set_pt.is_present() {
"regulator-input constraint"
} else {
"regulator-input"
}
})
} else {

View file

@ -1,48 +1,43 @@
use std::num::ParseFloatError;
// to construct a `SpecifiedValue` that might be `Present`, use the associated
// function `try_from`. this ensures that `spec` is always a valid specification
// of `value` according to the format discussed at the implementation of
// `TryFrom<String>`
pub enum SpecifiedValue {
Absent,
Present {
spec: String,
value: f64
}
// a real number described by a specification string. since the structure is
// read-only, we can guarantee that `spec` always specifies `value` in the
// following format
// ┌──────────────────────────────────────────────────────┬───────────┐
// │ `spec` │ `value` │
// ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━┥
// │ a string that parses to the floating-point value `x` │ `Some(x)` │
// ├──────────────────────────────────────────────────────┼───────────┤
// │ the empty string │ `None` │
// └──────────────────────────────────────────────────────┴───────────┘
#[readonly::make]
pub struct SpecifiedValue {
pub spec: String,
pub value: Option<f64>
}
use SpecifiedValue::{Absent, Present};
impl SpecifiedValue {
// get the specification for this value. the associated function `try_from`
// is essentially a left inverse of this method:
//
// SpecifiedValue::try_from(x.spec()) == Ok(x)
//
pub fn spec(&self) -> String {
match self {
Absent => String::new(),
Present { spec, .. } => spec.clone()
pub fn from_empty_spec() -> SpecifiedValue {
SpecifiedValue { spec: String::new(), value: None }
}
pub fn is_present(&self) -> bool {
matches!(self.value, Some(_))
}
}
// we can try to turn a specification string into a `SpecifiedValue`. if the
// specification is empty, the `SpecifiedValue` is `Absent`. if the
// specification parses to a floating-point value `x`, the `SpecifiedValue` is
// `Present`, with a `value` of `x`, and the specification is stored in `spec`.
// these are currently the only valid specifications; any other produces an
// error
// a `SpecifiedValue` can be constructed from a specification string, formatted
// as described in the comment on the structure definition. the result is `Ok`
// if the specification is properly formatted, and `Error` if not
impl TryFrom<String> for SpecifiedValue {
type Error = ParseFloatError;
fn try_from(spec: String) -> Result<Self, Self::Error> {
if spec.is_empty() {
Ok(Absent)
Ok(SpecifiedValue { spec: spec, value: None })
} else {
spec.parse::<f64>().map(
|value| Present { spec: spec, value: value }
|value| SpecifiedValue { spec: spec, value: Some(value) }
)
}
}