use std::{hash::Hash, sync::Arc};
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
struct ParameterMetadata {
name: String,
fixed: Option<f64>,
initial: Option<f64>,
bounds: (Option<f64>, Option<f64>),
unit: Option<String>,
latex: Option<String>,
description: Option<String>,
}
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub struct Parameter(Arc<Mutex<ParameterMetadata>>);
impl PartialEq for Parameter {
fn eq(&self, other: &Self) -> bool {
self.0.lock().name == other.0.lock().name
}
}
impl Eq for Parameter {}
impl Hash for Parameter {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.lock().name.hash(state);
}
}
pub trait IntoBound {
fn into_bound(self) -> Option<f64>;
}
impl IntoBound for f64 {
fn into_bound(self) -> Option<f64> {
Some(self)
}
}
impl IntoBound for Option<f64> {
fn into_bound(self) -> Option<f64> {
self
}
}
impl Parameter {
pub fn new(name: impl Into<String>) -> Self {
Self(Arc::new(Mutex::new(ParameterMetadata {
name: name.into(),
..Default::default()
})))
}
pub fn new_fixed(name: impl Into<String>, value: f64) -> Self {
Self(Arc::new(Mutex::new(ParameterMetadata {
name: name.into(),
fixed: Some(value),
..Default::default()
})))
}
pub fn name(&self) -> String {
self.0.lock().name.clone()
}
pub fn fixed(&self) -> Option<f64> {
self.0.lock().fixed
}
pub fn initial(&self) -> Option<f64> {
self.0.lock().initial
}
pub fn bounds(&self) -> (Option<f64>, Option<f64>) {
self.0.lock().bounds
}
pub fn unit(&self) -> Option<String> {
self.0.lock().unit.clone()
}
pub fn latex(&self) -> Option<String> {
self.0.lock().latex.clone()
}
pub fn description(&self) -> Option<String> {
self.0.lock().description.clone()
}
pub(crate) fn set_name(&self, name: impl Into<String>) {
self.0.lock().name = name.into();
}
pub fn set_fixed_value(&self, value: Option<f64>) {
let mut guard = self.0.lock();
if let Some(value) = value {
guard.fixed = Some(value);
guard.initial = Some(value);
} else {
guard.fixed = None;
}
}
pub fn set_initial(&self, value: f64) {
assert!(
self.is_free(),
"cannot manually set `initial` on a fixed parameter"
);
self.0.lock().initial = Some(value);
}
pub fn set_bounds<L, U>(&self, min: L, max: U)
where
L: IntoBound,
U: IntoBound,
{
self.0.lock().bounds = (IntoBound::into_bound(min), IntoBound::into_bound(max));
}
pub fn set_unit(&self, unit: impl Into<String>) {
self.0.lock().unit = Some(unit.into());
}
pub fn set_latex(&self, latex: impl Into<String>) {
self.0.lock().latex = Some(latex.into());
}
pub fn set_description(&self, description: impl Into<String>) {
self.0.lock().description = Some(description.into());
}
pub fn is_free(&self) -> bool {
self.0.lock().fixed.is_none()
}
pub fn is_fixed(&self) -> bool {
self.0.lock().fixed.is_some()
}
}
#[macro_export]
macro_rules! parameter {
($name:expr) => {{
$crate::parameters::Parameter::new($name)
}};
($name:expr, $value:expr) => {{
let p = $crate::parameters::Parameter::new($name);
p.set_fixed_value(Some($value));
p
}};
($name:expr, $($rest:tt)+) => {{
let p = $crate::parameters::Parameter::new($name);
$crate::parameter!(@parse p, [fixed = false, initial = false]; $($rest)+);
p
}};
(@parse $p:ident, [fixed = $f:tt, initial = $i:tt]; ) => {};
(@parse $p:ident, [fixed = false, initial = false]; fixed : $value:expr $(, $($rest:tt)*)?) => {{
$p.set_fixed_value(Some($value));
$crate::parameter!(@parse $p, [fixed = true, initial = false]; $($($rest)*)?);
}};
(@parse $p:ident, [fixed = false, initial = false]; initial : $value:expr $(, $($rest:tt)*)?) => {{
$p.set_initial($value);
$crate::parameter!(@parse $p, [fixed = false, initial = true]; $($($rest)*)?);
}};
(@parse $p:ident, [fixed = true, initial = false]; initial : $value:expr $(, $($rest:tt)*)?) => {
compile_error!("parameter!: cannot specify both `fixed` and `initial`");
};
(@parse $p:ident, [fixed = false, initial = true]; fixed : $value:expr $(, $($rest:tt)*)?) => {
compile_error!("parameter!: cannot specify both `fixed` and `initial`");
};
(@parse $p:ident, [fixed = $f:tt, initial = $i:tt]; bounds : ($min:expr, $max:expr) $(, $($rest:tt)*)?) => {{
$p.set_bounds($min, $max);
$crate::parameter!(@parse $p, [fixed = $f, initial = $i]; $($($rest)*)?);
}};
(@parse $p:ident, [fixed = $f:tt, initial = $i:tt]; unit : $value:expr $(, $($rest:tt)*)?) => {{
$p.set_unit($value);
$crate::parameter!(@parse $p, [fixed = $f, initial = $i]; $($($rest)*)?);
}};
(@parse $p:ident, [fixed = $f:tt, initial = $i:tt]; latex : $value:expr $(, $($rest:tt)*)?) => {{
$p.set_latex($value);
$crate::parameter!(@parse $p, [fixed = $f, initial = $i]; $($($rest)*)?);
}};
(@parse $p:ident, [fixed = $f:tt, initial = $i:tt]; description : $value:expr $(, $($rest:tt)*)?) => {{
$p.set_description($value);
$crate::parameter!(@parse $p, [fixed = $f, initial = $i]; $($($rest)*)?);
}};
}