1#![allow(clippy::multiple_inherent_impl)]
4use crate::{
5 Ctx, Group,
6 ast::{
7 self, BuilderScope, GroupComments, GroupFn, ParsingBuilder, fmt_comment_liberty,
8 },
9 common::f64_into_hash_ord_fn,
10 expression::logic,
11 table::{
12 DisplayTableLookUp, DisplayValues, OcvSigmaTable, OcvSigmaTableBuilder, SigmaType,
13 TableLookUp2D, TableLookUp2DBuilder,
14 },
15};
16use core::iter::zip;
17use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Not as _, Sub};
18use itertools::izip;
19use strum::{Display, EnumString};
20#[derive(
68 Debug, Clone, Copy, PartialEq, Display, EnumString, Default, Hash, Eq, PartialOrd, Ord
69)]
70#[derive(serde::Serialize, serde::Deserialize)]
71pub enum TimingSenseType {
72 #[strum(serialize = "positive_unate")]
78 PositiveUnate,
79 #[strum(serialize = "negative_unate")]
85 NegativeUnate,
86 #[strum(serialize = "non_unate")]
94 #[default]
95 NonUnate,
96}
97
98impl TimingSenseType {
99 #[must_use]
100 #[inline]
101 pub fn compute_edge(&self, pin_edge: &logic::Edge) -> Option<logic::Edge> {
102 match self {
103 Self::PositiveUnate => Some(*pin_edge),
104 Self::NegativeUnate => Some(pin_edge.not()),
105 Self::NonUnate => None,
106 }
107 }
108}
109crate::ast::impl_self_builder!(TimingSenseType);
110crate::ast::impl_simple!(TimingSenseType);
111
112#[derive(Debug, Clone)]
122#[derive(liberty_macros::Group)]
123#[mut_set::derive::item]
124#[derive(serde::Serialize, serde::Deserialize)]
125#[serde(bound = "C::Other: serde::Serialize + serde::de::DeserializeOwned")]
126pub struct CellDegradation<C: 'static + Ctx> {
127 #[liberty(name)]
129 #[id(borrow = str)]
130 pub name: String,
131 #[liberty(comments)]
133 comments: GroupComments,
134 #[liberty(extra_ctx)]
135 pub extra_ctx: C::Other,
136 #[liberty(attributes)]
138 pub attributes: ast::Attributes,
139 #[liberty(complex)]
144 pub index_1: Vec<f64>,
145 #[liberty(complex)]
150 pub values: Vec<f64>,
151}
152impl<C: 'static + Ctx> GroupFn<C> for CellDegradation<C> {}
153
154#[derive(Debug, Clone, Default)]
155#[derive(serde::Serialize, serde::Deserialize)]
156pub struct TimingTableLookUp<C: 'static + Ctx> {
157 pub extra_ctx: C::Table,
158 pub name: String,
159 pub comments: String,
160 pub index_1: Vec<f64>,
161 pub index_2: Vec<f64>,
162 pub values: Vec<f64>,
163 pub lvf_moments_values: Vec<LVFMoments>,
164 pub lvf_early_late_values: Vec<LVFEarlyLate>,
165}
166#[expect(clippy::indexing_slicing, clippy::arithmetic_side_effects)]
167impl<C: 'static + Ctx> TimingTableLookUp<C> {
168 #[inline]
169 pub(crate) fn use_common_template(
170 table: &mut Option<Self>,
171 scope: &mut BuilderScope<C>,
172 ) {
173 if let Some(t) = table {
174 #[cfg(feature = "lut_template")]
175 crate::table::TableCtx::set_lut_template(
176 &mut t.extra_ctx,
177 scope.lu_table_template.get(&t.name),
178 );
179 }
180 }
181 #[inline]
182 const fn find_pos(len: usize, pos: usize) -> Option<(usize, usize)> {
183 if len <= 1 {
184 None
185 } else {
186 Some(if pos == 0 {
187 (0, 1)
188 } else if pos == len {
189 (len - 2, len - 1)
190 } else {
191 (pos - 1, pos)
192 })
193 }
194 }
195 #[inline]
196 fn get_value(&self, ix: usize, iy: usize) -> Option<f64> {
197 self.values.get(ix * self.index_2.len() + iy).copied()
198 }
199 #[inline]
200 fn get_lvf_moments_value(&self, ix: usize, iy: usize) -> Option<LVFMoments> {
201 self.lvf_moments_values.get(ix * self.index_2.len() + iy).copied()
202 }
203 #[must_use]
205 #[inline]
206 #[expect(clippy::float_arithmetic)]
207 pub fn lookup(&self, idx1: &f64, idx2: &f64) -> Option<f64> {
208 let idx1_ = f64_into_hash_ord_fn(idx1);
209 let idx2_ = f64_into_hash_ord_fn(idx2);
210 match self.index_1.binary_search_by(|v| f64_into_hash_ord_fn(v).cmp(&idx1_)) {
211 Ok(i1_) => {
212 match self.index_2.binary_search_by(|v| f64_into_hash_ord_fn(v).cmp(&idx2_)) {
213 Ok(i_1) => self.get_value(i1_, i_1),
214 Err(pos2) => {
215 let (i_1, i_2) = Self::find_pos(self.index_2.len(), pos2)?;
216 let q_1 = self.get_value(i1_, i_1)?;
217 let q_2 = self.get_value(i1_, i_2)?;
218 let x_1 = self.index_2[i_1];
219 let x_2 = self.index_2[i_2];
220 Some((q_2 - q_1).mul_add((idx2 - x_1) / (x_2 - x_1), q_1))
222 }
223 }
224 }
225 Err(pos1) => {
226 let (i1_, i2_) = Self::find_pos(self.index_1.len(), pos1)?;
227 let x1_ = self.index_1[i1_];
228 let x2_ = self.index_1[i2_];
229 match self.index_2.binary_search_by(|v| f64_into_hash_ord_fn(v).cmp(&idx2_)) {
230 Ok(i_1) => {
231 let q1_ = self.get_value(i1_, i_1)?;
232 let q2_ = self.get_value(i2_, i_1)?;
233 Some((q2_ - q1_).mul_add((idx1 - x1_) / (x2_ - x1_), q1_))
235 }
236 Err(pos2) => {
237 let (i_1, i_2) = Self::find_pos(self.index_2.len(), pos2)?;
238 let q11 = self.get_value(i1_, i_1)?;
239 let q12 = self.get_value(i1_, i_2)?;
240 let q21 = self.get_value(i2_, i_1)?;
241 let q22 = self.get_value(i2_, i_2)?;
242 let x_1 = self.index_2[i_1];
243 let x_2 = self.index_2[i_2];
244 let q1_ = (q12 - q11).mul_add((idx2 - x_1) / (x_2 - x_1), q11);
246 let q2_ = (q22 - q21).mul_add((idx2 - x_1) / (x_2 - x_1), q21);
248 Some((q2_ - q1_).mul_add((idx1 - x1_) / (x2_ - x1_), q1_))
250 }
251 }
252 }
253 }
254 }
255 #[must_use]
256 #[inline]
257 #[expect(clippy::float_arithmetic)]
258 pub fn lookup_lvf_moments(&self, idx1: &f64, idx2: &f64) -> Option<LVFMoments> {
259 let idx1_ = f64_into_hash_ord_fn(idx1);
260 let idx2_ = f64_into_hash_ord_fn(idx2);
261 match self.index_1.binary_search_by(|v| f64_into_hash_ord_fn(v).cmp(&idx1_)) {
262 Ok(i1_) => {
263 match self.index_2.binary_search_by(|v| f64_into_hash_ord_fn(v).cmp(&idx2_)) {
264 Ok(i_1) => self.get_lvf_moments_value(i1_, i_1),
265 Err(pos2) => {
266 let (i_1, i_2) = Self::find_pos(self.index_2.len(), pos2)?;
267 let q_1 = self.get_lvf_moments_value(i1_, i_1)?;
268 let q_2 = self.get_lvf_moments_value(i1_, i_2)?;
269 let x_1 = self.index_2[i_1];
270 let x_2 = self.index_2[i_2];
271 Some((q_2 - q_1).mul_add((idx2 - x_1) / (x_2 - x_1), q_1))
273 }
274 }
275 }
276 Err(pos1) => {
277 let (i1_, i2_) = Self::find_pos(self.index_1.len(), pos1)?;
278 let x1_ = self.index_1[i1_];
279 let x2_ = self.index_1[i2_];
280 match self.index_2.binary_search_by(|v| f64_into_hash_ord_fn(v).cmp(&idx2_)) {
281 Ok(i_1) => {
282 let q1_ = self.get_lvf_moments_value(i1_, i_1)?;
283 let q2_ = self.get_lvf_moments_value(i2_, i_1)?;
284 Some((q2_ - q1_).mul_add((idx1 - x1_) / (x2_ - x1_), q1_))
286 }
287 Err(pos2) => {
288 let (i_1, i_2) = Self::find_pos(self.index_2.len(), pos2)?;
289 let q11 = self.get_lvf_moments_value(i1_, i_1)?;
290 let q12 = self.get_lvf_moments_value(i1_, i_2)?;
291 let q21 = self.get_lvf_moments_value(i2_, i_1)?;
292 let q22 = self.get_lvf_moments_value(i2_, i_2)?;
293 let x_1 = self.index_2[i_1];
294 let x_2 = self.index_2[i_2];
295 let q1_ = (q12 - q11).mul_add((idx2 - x_1) / (x_2 - x_1), q11);
297 let q2_ = (q22 - q21).mul_add((idx2 - x_1) / (x_2 - x_1), q21);
299 Some((q2_ - q1_).mul_add((idx1 - x1_) / (x2_ - x1_), q1_))
301 }
302 }
303 }
304 }
305 }
306}
307
308#[derive(serde::Serialize, serde::Deserialize)]
309#[derive(Debug, Default, Clone, Copy)]
310pub struct LVFMoments {
311 pub mean: f64,
313 pub std_dev: f64,
314 pub skewness: f64,
315}
316#[derive(serde::Serialize, serde::Deserialize)]
317#[derive(Debug, Default, Clone, Copy)]
318pub struct LVFEarlyLate {
319 pub early_sigma: f64,
320 pub late_sigma: f64,
321}
322#[expect(clippy::float_arithmetic)]
323impl LVFMoments {
324 const Z: f64 = 3.0;
327 const TMP1: f64 = (Self::Z * Self::Z - 1.0) / 6.0;
328 const TMP2: f64 = (2.0 * Self::Z * Self::Z * Self::Z - 5.0 * Self::Z) / 36.0;
329 #[inline]
330 #[must_use]
331 #[deprecated = "TODO"]
332 pub const fn to_early_late(&self, nominal: f64) -> Option<LVFEarlyLate> {
336 if !(self.std_dev >= 0.0
337 && nominal.is_finite()
338 && self.mean.is_finite()
339 && self.std_dev.is_finite()
340 && self.skewness.is_finite())
341 {
342 return None;
343 }
344 let quantile_plus_3sigma = self.mean
345 + self.std_dev
346 * (self.skewness * Self::TMP1 + Self::Z
347 - self.skewness * self.skewness * Self::TMP2);
348 let quantile_minus_3sigma = self.mean
349 + self.std_dev
350 * (self.skewness * Self::TMP1 - Self::Z
351 + self.skewness * self.skewness * Self::TMP2);
352 Some(LVFEarlyLate {
353 early_sigma: (nominal - quantile_minus_3sigma) / Self::Z,
354 late_sigma: (quantile_plus_3sigma - nominal) / Self::Z,
355 })
356 }
357}
358
359#[expect(clippy::float_arithmetic)]
360impl LVFEarlyLate {
361 #[inline]
369 #[must_use]
370 #[expect(clippy::suboptimal_flops)]
371 #[deprecated = "TODO"]
372 pub fn to_moments(&self, nominal: f64, mean: f64) -> Option<LVFMoments> {
373 if !(self.early_sigma >= 0.0
374 && self.late_sigma >= 0.0
375 && nominal.is_finite()
376 && mean.is_finite()
377 && self.early_sigma.is_finite()
378 && self.late_sigma.is_finite())
379 {
380 return None;
381 }
382
383 let delta = LVFMoments::Z * (self.early_sigma + self.late_sigma) - 2.0 * nominal;
384 let a = LVFMoments::Z * (self.late_sigma - self.early_sigma) / 2.0;
385 let a_minus_mean = a - mean;
386
387 let k = 2.0 * LVFMoments::TMP2 * a_minus_mean * a_minus_mean
389 / (LVFMoments::TMP1 * LVFMoments::TMP1);
390
391 let disc = delta * delta + 8.0 * LVFMoments::Z * k;
393 if !disc.is_finite() {
394 return None;
395 }
396 let std_dev = (delta + disc.sqrt()) / (4.0 * LVFMoments::Z);
397 if !(std_dev.is_finite() && std_dev >= 0.0) {
398 return None;
399 } let skewness =
401 if std_dev > 0.0 { a_minus_mean / (std_dev * LVFMoments::TMP1) } else { 0.0 };
402
403 Some(LVFMoments { mean, std_dev, skewness })
404 }
405}
406
407impl LVFMoments {
408 #[inline]
411 #[expect(
412 clippy::as_conversions,
413 clippy::float_arithmetic,
414 clippy::cast_precision_loss,
415 clippy::arithmetic_side_effects
416 )]
417 pub fn estimate<E, I: Iterator<Item = Result<f64, E>>>(
418 data: I,
419 ) -> Result<Option<Self>, E> {
420 let mut n: usize = 0;
421 let mut mean = 0.0;
422 let mut m2 = 0.0; let mut m3 = 0.0; for x in data {
426 n += 1;
427 let n_f = n as f64;
428
429 let delta = x? - mean;
431 let delta_n = delta / n_f;
432 let term1 = delta * delta_n * (n_f - 1.0);
433
434 m3 += (term1 * delta_n).mul_add(n_f - 2.0, -(3.0 * delta_n * m2));
435 m2 += term1;
437 mean += delta_n;
438 }
439
440 if n == 0 {
441 return Ok(None);
442 }
443
444 let n_f = n as f64;
445 let std_dev = (m2 / n_f).sqrt();
446 let skewness = if n >= 2 && std_dev > 0.0 {
447 m3 / ((n_f - 1.0) * std_dev * std_dev * std_dev)
448 } else {
449 0.0
450 };
451
452 Ok(Some(Self { mean, std_dev, skewness }))
453 }
454 #[inline]
455 #[must_use]
456 pub fn mul_add(self, a: f64, b: Self) -> Self {
458 Self {
459 mean: self.mean.mul_add(a, b.mean),
460 std_dev: self.std_dev.mul_add(a, b.std_dev),
461 skewness: self.skewness.mul_add(a, b.skewness),
462 }
463 }
464}
465impl PartialEq for LVFMoments {
466 #[inline]
467 fn eq(&self, other: &Self) -> bool {
468 f64_into_hash_ord_fn(&self.mean) == f64_into_hash_ord_fn(&other.mean)
469 && f64_into_hash_ord_fn(&self.std_dev) == f64_into_hash_ord_fn(&other.std_dev)
470 && f64_into_hash_ord_fn(&self.skewness) == f64_into_hash_ord_fn(&other.skewness)
471 }
472}
473#[expect(clippy::float_arithmetic)]
474impl Add for LVFMoments {
475 type Output = Self;
476 #[inline]
477 fn add(self, rhs: Self) -> Self::Output {
478 Self {
479 mean: self.mean + rhs.mean,
480 std_dev: self.std_dev + rhs.std_dev,
481 skewness: self.skewness + rhs.skewness,
482 }
483 }
484}
485#[expect(clippy::float_arithmetic)]
486impl AddAssign for LVFMoments {
487 #[inline]
488 fn add_assign(&mut self, rhs: Self) {
489 self.mean += rhs.mean;
490 self.std_dev += rhs.std_dev;
491 self.skewness += rhs.skewness;
492 }
493}
494#[expect(clippy::float_arithmetic)]
495impl AddAssign for LVFEarlyLate {
496 #[inline]
497 fn add_assign(&mut self, rhs: Self) {
498 self.early_sigma += rhs.early_sigma;
499 self.late_sigma += rhs.late_sigma;
500 }
501}
502#[expect(clippy::float_arithmetic)]
503impl Sub for LVFMoments {
504 type Output = Self;
505 #[inline]
506 fn sub(self, rhs: Self) -> Self::Output {
508 Self {
509 mean: self.mean - rhs.mean,
510 std_dev: self.std_dev - rhs.std_dev,
512 skewness: self.skewness - rhs.skewness,
513 }
514 }
515}
516#[expect(clippy::float_arithmetic)]
517impl Mul<f64> for LVFMoments {
518 type Output = Self;
519 #[inline]
520 fn mul(self, rhs: f64) -> Self::Output {
521 Self {
522 mean: self.mean * rhs,
523 std_dev: self.std_dev * rhs,
524 skewness: self.skewness * rhs,
525 }
526 }
527}
528#[expect(clippy::float_arithmetic)]
529impl MulAssign<f64> for LVFMoments {
530 #[inline]
531 fn mul_assign(&mut self, rhs: f64) {
532 self.mean *= rhs;
533 self.std_dev *= rhs;
534 self.skewness *= rhs;
535 }
536}
537#[expect(clippy::float_arithmetic)]
538impl Div<f64> for LVFMoments {
539 type Output = Self;
540 #[inline]
541 fn div(self, rhs: f64) -> Self::Output {
542 Self {
543 mean: self.mean / rhs,
544 std_dev: self.std_dev / rhs,
545 skewness: self.skewness / rhs,
546 }
547 }
548}
549#[expect(clippy::float_arithmetic)]
550impl DivAssign<f64> for LVFMoments {
551 #[inline]
552 fn div_assign(&mut self, rhs: f64) {
553 self.mean /= rhs;
554 self.std_dev /= rhs;
555 self.skewness /= rhs;
556 }
557}
558#[expect(clippy::float_arithmetic)]
559impl DivAssign<f64> for LVFEarlyLate {
560 #[inline]
561 fn div_assign(&mut self, rhs: f64) {
562 self.early_sigma /= rhs;
563 self.late_sigma /= rhs;
564 }
565}
566
567impl<C: 'static + Ctx> ParsingBuilder<C> for Option<TimingTableLookUp<C>> {
568 type Builder = (
570 Option<<TableLookUp2D<C> as ParsingBuilder<C>>::Builder>,
572 Option<<TableLookUp2D<C> as ParsingBuilder<C>>::Builder>,
574 Option<<TableLookUp2D<C> as ParsingBuilder<C>>::Builder>,
576 Option<<TableLookUp2D<C> as ParsingBuilder<C>>::Builder>,
578 Vec<<OcvSigmaTable<C> as ParsingBuilder<C>>::Builder>,
580 );
581 #[inline]
582 #[expect(clippy::float_arithmetic, clippy::too_many_lines)]
583 fn build(builder: Self::Builder, _scope: &mut BuilderScope<C>) -> Self {
584 #[inline]
585 fn eq_index<C: 'static + Ctx>(
586 lhs: &<TableLookUp2D<C> as ParsingBuilder<C>>::Builder,
587 rhs: &<TableLookUp2D<C> as ParsingBuilder<C>>::Builder,
588 ) -> bool {
589 lhs.index_1 == rhs.index_1
590 && lhs.index_2 == rhs.index_2
591 && lhs.values.inner.len() == rhs.values.inner.len()
592 }
593 #[inline]
594 fn ocv_eq_index<C: 'static + Ctx>(
595 lhs: &<TableLookUp2D<C> as ParsingBuilder<C>>::Builder,
596 rhs: &<OcvSigmaTable<C> as ParsingBuilder<C>>::Builder,
597 ) -> bool {
598 lhs.index_1 == rhs.index_1
599 && lhs.index_2 == rhs.index_2
600 && lhs.values.inner.len() == rhs.values.inner.len()
601 }
602 fn obtain_ocv_sigma<C: 'static + Ctx>(
603 value: &TableLookUp2DBuilder<C>,
604 comments: &mut String,
605 ocv_sigma: Vec<OcvSigmaTableBuilder<C>>,
606 ) -> Vec<LVFEarlyLate> {
607 let mut early = None;
608 let mut late = None;
609 let mut early_late = None;
610 for table in ocv_sigma {
611 match table.sigma_type {
612 SigmaType::Early => early = Some(table),
613 SigmaType::Late => late = Some(table),
614 SigmaType::EarlyAndLate => early_late = Some(table),
615 }
616 }
617 if let (Some(early_table), Some(late_table)) = (early, late) {
618 if ocv_eq_index(value, &early_table) && ocv_eq_index(value, &late_table) {
619 zip(early_table.values.inner, late_table.values.inner)
620 .map(|(early_sigma, late_sigma)| LVFEarlyLate { early_sigma, late_sigma })
621 .collect()
622 } else {
623 crate::error!("LVF early_late LUTs' index mismatch");
624 comments.push_str("LVF early_late LUTs' index mismatch");
625 Vec::new()
626 }
627 } else if let Some(early_late_table) = early_late {
628 if ocv_eq_index(value, &early_late_table) {
629 early_late_table
630 .values
631 .inner
632 .into_iter()
633 .map(|sigma| LVFEarlyLate { early_sigma: sigma, late_sigma: sigma })
634 .collect()
635 } else {
636 crate::error!("LVF early_late LUTs' index mismatch");
637 comments.push_str("LVF early_late LUTs' index mismatch");
638 Vec::new()
639 }
640 } else {
641 Vec::new()
642 }
643 }
644 let mut comments = String::new();
645 match builder {
646 (Some(_value), Some(_mean_shift), Some(_std_dev), Some(_skewness), ocv_sigma) => {
647 let lvf_moments_values = if eq_index(&_value, &_mean_shift)
648 && eq_index(&_mean_shift, &_std_dev)
649 && eq_index(&_std_dev, &_skewness)
650 {
651 izip!(
652 _value.values.inner.iter(),
653 _mean_shift.values.inner,
654 _std_dev.values.inner,
655 _skewness.values.inner
656 )
657 .map(|(value, mean_shift, std_dev, skewness)| {
658 let mean = value + mean_shift;
659 LVFMoments { mean, std_dev, skewness }
660 })
661 .collect()
662 } else {
663 crate::error!("LVF moments LUTs' index mismatch");
664 comments.push_str("LVF moments LUTs' index mismatch");
665 Vec::new()
666 };
667 let lvf_early_late_values = obtain_ocv_sigma(&_value, &mut comments, ocv_sigma);
668 Some(TimingTableLookUp {
669 extra_ctx: C::Table::default(),
670 name: _value.name,
671 comments,
672 index_1: _value.index_1,
673 index_2: _value.index_2,
674 values: _value.values.inner,
675 lvf_moments_values,
676 lvf_early_late_values,
677 })
678 }
679 (Some(_value), None, None, None, ocv_sigma) => {
680 let lvf_early_late_values = obtain_ocv_sigma(&_value, &mut comments, ocv_sigma);
681 Some(TimingTableLookUp {
682 extra_ctx: C::Table::default(),
683 name: _value.name,
684 comments,
685 index_1: _value.index_1,
686 index_2: _value.index_2,
687 values: _value.values.inner,
688 lvf_moments_values: Vec::new(),
689 lvf_early_late_values,
690 })
691 }
692 _ => None,
693 }
694 }
695}
696impl<C: 'static + Ctx> ParsingBuilder<C> for TimingTableLookUp<C> {
697 type Builder = ();
698 fn build(_: Self::Builder, _: &mut BuilderScope<C>) -> Self {
699 unreachable!()
700 }
701}
702impl<C: 'static + Ctx> ast::GroupAttri<C> for TimingTableLookUp<C> {
703 #[inline]
704 #[expect(clippy::float_arithmetic)]
705 fn fmt_liberty<T: core::fmt::Write, I: ast::Indentation, K: core::fmt::Display>(
706 &self,
707 key: K,
708 f: &mut ast::CodeFormatter<'_, T, I>,
709 ) -> core::fmt::Result {
710 let chunk_size =
711 if self.index_2.is_empty() { self.values.len() } else { self.index_2.len() };
712 let len = self.values.len();
713 fmt_comment_liberty(Some(&self.comments), f)?;
714 DisplayTableLookUp {
715 name: &self.name,
716 index_1: &self.index_1,
717 index_2: &self.index_2,
718 sigma_type: None,
719 values: DisplayValues {
720 len,
721 chunk_size,
722 inner: self.values.iter().copied(),
723 },
724 }
725 .fmt_self::<_, _, C, _, _>("", &key, f)?;
726 if !self.lvf_moments_values.is_empty() {
727 DisplayTableLookUp {
728 name: &self.name,
729 index_1: &self.index_1,
730 index_2: &self.index_2,
731 sigma_type: None,
732 values: DisplayValues {
733 len,
734 chunk_size,
735 inner: izip!(self.values.iter(), self.lvf_moments_values.iter())
736 .map(|(value, lvf)| lvf.mean - value),
737 },
738 }
739 .fmt_self::<_, _, C, _, _>("ocv_mean_shift_", &key, f)?;
740 DisplayTableLookUp {
741 name: &self.name,
742 index_1: &self.index_1,
743 index_2: &self.index_2,
744 sigma_type: None,
745 values: DisplayValues {
746 len,
747 chunk_size,
748 inner: self.lvf_moments_values.iter().map(|lvf| lvf.std_dev),
749 },
750 }
751 .fmt_self::<_, _, C, _, _>("ocv_std_dev_", &key, f)?;
752 DisplayTableLookUp {
753 name: &self.name,
754 index_1: &self.index_1,
755 index_2: &self.index_2,
756 sigma_type: None,
757 values: DisplayValues {
758 len,
759 chunk_size,
760 inner: self.lvf_moments_values.iter().map(|lvf| lvf.skewness),
761 },
762 }
763 .fmt_self::<_, _, C, _, _>("ocv_skewness_", &key, f)?;
764 }
765 if !self.lvf_early_late_values.is_empty() {
766 DisplayTableLookUp {
767 name: &self.name,
768 index_1: &self.index_1,
769 index_2: &self.index_2,
770 sigma_type: Some(SigmaType::Early),
771 values: DisplayValues {
772 len,
773 chunk_size,
774 inner: self.lvf_early_late_values.iter().map(|lvf| lvf.early_sigma),
775 },
776 }
777 .fmt_self::<_, _, C, _, _>("ocv_sigma_", &key, f)?;
778 DisplayTableLookUp {
779 name: &self.name,
780 index_1: &self.index_1,
781 index_2: &self.index_2,
782 sigma_type: Some(SigmaType::Late),
783 values: DisplayValues {
784 len,
785 chunk_size,
786 inner: self.lvf_early_late_values.iter().map(|lvf| lvf.late_sigma),
787 },
788 }
789 .fmt_self::<_, _, C, _, _>("ocv_sigma_", &key, f)?;
790 }
791 Ok(())
792 }
793 fn nom_parse<'a, const IS_INCLUDED: bool>(
794 _: &mut Self::Builder,
795 _: &'a str,
796 _: &str,
797 _: &mut ast::ParseScope<'_>,
798 ) -> nom::IResult<&'a str, Result<(), ast::IdError>, nom::error::Error<&'a str>> {
799 unreachable!()
800 }
801}
802
803impl<C: 'static + Ctx> Group<C> for TimingTableLookUp<C> {}
804
805#[cfg(test)]
806mod test {
807 use super::{LVFEarlyLate, LVFMoments};
808
809 #[test]
810 #[expect(deprecated)]
811 fn lvf_convert() {
812 let nominal = 0.5065;
813 let mean_shift = 0.005352;
814 let std_dev = 0.04952;
815 let skewness = 0.03483;
816 let early_sigma = 0.0422;
817 let late_sigma = 0.0624;
818 let mean = nominal + mean_shift;
819 let moments = LVFMoments { mean, std_dev, skewness };
820 let early_late = LVFEarlyLate { early_sigma, late_sigma };
821 moments.to_early_late(nominal);
822 moments.to_early_late(nominal).unwrap().to_moments(nominal, mean);
823 early_late.to_moments(nominal, mean);
824 }
825}