ellalgo_rs/
ell_calc.rs

1// mod lib;
2use crate::cutting_plane::CutStatus;
3
4/// The `EllCalcCore` struct represents the parameters for calculating the new Ellipsoid Search Space.
5///
6/// Properties:
7///
8/// * `n_f`: The `n_f` property represents the number of variables in the ellipsoid search space.
9/// * `n_plus_1`: The `n_plus_1` property represents the value of `n + 1`, where `n` is the dimension of
10/// the search space. It is used in calculations related to the ellipsoid search space.
11/// * `half_n`: The `half_n` property represents half of the dimension of the ellipsoid search space. It
12/// is used in the calculation of the parameters for the ellipsoid search space.
13/// * `cst1`: The `cst1` property is a constant used in the calculation of the parameters for the new
14/// Ellipsoid Search Space. Its specific purpose and value are not provided in the code snippet.
15/// * `cst2`: The `cst2` property is a constant used in the calculation of the parameters for the new
16/// Ellipsoid Search Space. It is not specified what exactly it represents or how it is used in the
17/// calculation.
18/// * `cst3`: The `cst3` property is a constant value used in the calculation of the parameters for the
19/// new Ellipsoid Search Space. It is not specified what exactly this constant represents or how it is
20/// used in the calculations.
21#[derive(Debug, Clone)]
22pub struct EllCalcCore {
23    pub n_f: f64,
24    pub n_plus_1: f64,
25    pub half_n: f64,
26    cst1: f64,
27    cst2: f64,
28}
29
30impl EllCalcCore {
31    /// The `new` function constructs a new [`EllCalcCore`] object with the given parameter `n_f` and
32    /// initializes its internal variables.
33    ///
34    /// Arguments:
35    ///
36    /// * `n_f`: The parameter `n_f` represents the value of `n` in the calculations. It is a floating-point
37    /// number.
38    ///
39    /// Returns:
40    ///
41    /// The `new` function returns an instance of the `EllCalcCore` struct.
42    ///
43    /// # Examples:
44    ///
45    /// ```rust
46    /// use approx_eq::assert_approx_eq;
47    /// use ellalgo_rs::ell_calc::EllCalcCore;
48    ///
49    /// let ell_calc_core = EllCalcCore::new(4.0);
50    ///
51    /// assert_approx_eq!(ell_calc_core.n_f, 4.0);
52    /// assert_approx_eq!(ell_calc_core.half_n, 2.0);
53    /// assert_approx_eq!(ell_calc_core.n_plus_1, 5.0);
54    /// ```
55    pub fn new(n_f: f64) -> EllCalcCore {
56        let n_plus_1 = n_f + 1.0;
57        let half_n = n_f / 2.0;
58        let n_sq = n_f * n_f;
59        let cst0 = 1.0 / (n_f + 1.0);
60        let cst1 = n_sq / (n_sq - 1.0);
61        let cst2 = 2.0 * cst0;
62
63        EllCalcCore {
64            n_f,
65            n_plus_1,
66            half_n,
67            cst1,
68            cst2,
69        }
70    }
71
72    #[doc = svgbobdoc::transform!(
73    /// The function calculates the core values for updating an ellipsoid with either a parallel-cut or
74    /// a deep-cut.
75    ///
76    /// Arguments:
77    ///
78    /// * `beta0`: The parameter `beta0` represents the semi-minor axis of the ellipsoid before the cut. It is
79    /// a floating-point number.
80    /// * `beta1`: The parameter `beta1` represents the length of the semi-minor axis of the ellipsoid.
81    /// * `tsq`: tsq is a reference to a f64 value, which represents the square of the semi-major axis
82    /// of the ellipsoid.
83    ///
84    /// ```svgbob
85    ///      _.-'''''''-._
86    ///    ,'     |       `.
87    ///   /  |    |         \
88    ///  .   |    |          .
89    ///  |   |    |          |
90    ///  |   |    |.         |
91    ///  |   |    |          |
92    ///  :\  |    |         /:
93    ///  | `._    |      _.' |
94    ///  |   |'-.......-'    |
95    ///  |   |    |          |
96    /// "-τ" "-β" "-β"      +τ
97    ///        1    0
98    /// ```
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use approx_eq::assert_approx_eq;
104    /// use ellalgo_rs::ell_calc::EllCalcCore;
105    ///
106    /// let ell_calc_core = EllCalcCore::new(4.0);
107    /// let (rho, sigma, delta) = ell_calc_core.calc_parallel_bias_cut(1.0, 2.0, &4.0);
108    /// assert_approx_eq!(rho, 1.2);
109    /// assert_approx_eq!(sigma, 0.8);
110    /// assert_approx_eq!(delta, 0.8);
111    /// ```
112    )]
113    #[inline]
114    pub fn calc_parallel_bias_cut(&self, beta0: f64, beta1: f64, tsq: &f64) -> (f64, f64, f64) {
115        let b0b1 = beta0 * beta1;
116        let eta = tsq + self.n_f * b0b1;
117        self.calc_parallel_bias_cut_fast(beta0, beta1, tsq, &b0b1, &eta)
118    }
119
120    pub fn calc_parallel_bias_cut_fast(
121        &self,
122        beta0: f64,
123        beta1: f64,
124        tsq: &f64,
125        b0b1: &f64,
126        eta: &f64,
127    ) -> (f64, f64, f64) {
128        let bsum = beta0 + beta1;
129        let bsumsq = bsum * bsum;
130        let h = tsq + b0b1 + self.half_n * bsumsq;
131        let root = h + (h * h - eta * self.n_plus_1 * bsumsq).sqrt();
132        let inv_mu_plus_2 = eta / root;
133        let inv_mu = eta / (root - 2.0 * eta);
134        let rho = bsum * inv_mu_plus_2;
135        let sigma = 2.0 * inv_mu_plus_2;
136        let delta = 1.0 + (-2.0 * b0b1 + bsumsq * inv_mu_plus_2) * inv_mu / tsq;
137
138        (rho, sigma, delta)
139    }
140
141    #[doc = svgbobdoc::transform!(
142    /// The function calculates the core values for updating an ellipsoid with the parallel-cut method.
143    ///
144    /// Arguments:
145    ///
146    /// * `beta1`: The parameter `beta1` represents the semi-minor axis of the ellipsoid. It is a floating-point
147    /// number.
148    /// * `tsq`: The parameter `tsq` represents the square of the gamma semi-axis length of the ellipsoid.
149    ///
150    /// ```svgbob
151    ///      _.-'''''''-._
152    ///    ,'      |      `.
153    ///   /  |     |        \
154    ///  .   |     |         .
155    ///  |   |               |
156    ///  |   |     .         |
157    ///  |   |               |
158    ///  :\  |     |        /:
159    ///  | `._     |     _.' |
160    ///  |   |'-.......-'    |
161    ///  |   |     |         |
162    /// "-τ" "-β"  0        +τ
163    ///        1
164    ///
165    ///   2    2    2
166    ///  α  = β  / τ
167    ///
168    ///      n    2
169    ///  h = ─ ⋅ α
170    ///      2
171    ///             _____________
172    ///            ╱ 2          2
173    ///  r = h + ╲╱ h  + 1.0 - α
174    ///
175    ///        β
176    ///  ϱ = ─────
177    ///      r + 1
178    ///
179    ///        2
180    ///  σ = ─────
181    ///      r + 1
182    ///
183    ///        n ⋅ r
184    ///  δ = ─────────
185    ///      n ⋅ r - 1
186    /// ```
187    ///
188    /// # Example
189    ///
190    /// ```
191    /// use approx_eq::assert_approx_eq;
192    /// use ellalgo_rs::ell_calc::EllCalcCore;
193    ///
194    /// let ell_calc_core = EllCalcCore::new(4.0);
195    /// let (rho, sigma, delta) = ell_calc_core.calc_parallel_central_cut(1.0, &4.0);
196    /// assert_approx_eq!(rho, 0.4);
197    /// assert_approx_eq!(sigma, 0.8);
198    /// assert_approx_eq!(delta, 1.2);
199    /// ```
200    )]
201    pub fn calc_parallel_central_cut(&self, beta1: f64, tsq: &f64) -> (f64, f64, f64) {
202        let b1sq = beta1 * beta1;
203        let a1sq = b1sq / tsq;
204        let temp = self.half_n * a1sq;
205        let mu_plus_1 = temp + (1.0 - a1sq + temp * temp).sqrt();
206        let mu_plus_2 = mu_plus_1 + 1.0;
207        let rho = beta1 / mu_plus_2;
208        let sigma = 2.0 / mu_plus_2;
209        let temp2 = self.n_f * mu_plus_1;
210        let delta = temp2 / (temp2 - 1.0);
211
212        (rho, sigma, delta)
213    }
214
215    #[doc = svgbobdoc::transform!(
216    /// The function calculates the core values needed for updating an ellipsoid with the deep-cut method.
217    ///
218    /// Arguments:
219    ///
220    /// * `beta`: The `beta` parameter represents a value used in the calculation of the core of updating
221    /// the ellipsoid with the deep-cut. It is of type `f64`, which means it is a 64-bit floating-point
222    /// number.
223    /// * `tau`: The parameter `tau` represents the time constant of the system. It is a measure of how
224    /// quickly the system responds to changes.
225    /// * `eta`: The parameter `eta` represents the deep-cut factor. It is a measure of how much the
226    /// ellipsoid is being updated or modified.
227    ///
228    /// ```svgbob
229    ///       _.-'''''''-._
230    ///     ,'    |        `.
231    ///    /      |          \
232    ///   .       |           .
233    ///   |       |           |
234    ///   |       | .         |
235    ///   |       |           |
236    ///   :\      |          /:
237    ///   | `._   |       _.' |
238    ///   |    '-.......-'    |
239    ///   |       |           |
240    ///  "-τ"     "-β"       +τ
241    ///
242    ///   η = τ + n ⋅ β
243    ///   
244    ///         η
245    ///   ϱ = ─────
246    ///       n + 1
247    ///
248    ///       2 ⋅ ϱ
249    ///   σ = ─────
250    ///       "τ + β"
251    ///
252    ///          2       2    2
253    ///         n       τ  - β
254    ///   δ = ────── ⋅  ───────
255    ///        2           2
256    ///       n  - 1      τ
257    /// ```
258    ///
259    /// # Example
260    ///
261    /// ```
262    /// use approx_eq::assert_approx_eq;
263    /// use ellalgo_rs::ell_calc::EllCalcCore;
264    ///
265    /// let ell_calc_core = EllCalcCore::new(4.0);
266    /// let (rho, sigma, delta) = ell_calc_core.calc_bias_cut(&1.0, &2.0);
267    /// assert_approx_eq!(rho, 1.2);
268    /// assert_approx_eq!(sigma, 0.8);
269    /// assert_approx_eq!(delta, 0.8);
270    /// ```
271    )]
272    #[inline]
273    pub fn calc_bias_cut(&self, beta: &f64, tau: &f64) -> (f64, f64, f64) {
274        let eta = tau + self.n_f * beta;
275        self.calc_bias_cut_fast(beta, tau, &eta)
276    }
277
278    #[doc = svgbobdoc::transform!(
279    /// The function calculates the core values needed for updating an ellipsoid with the deep-cut method.
280    ///
281    /// Arguments:
282    ///
283    /// * `beta`: The `beta` parameter represents a value used in the calculation of the core of updating
284    /// the ellipsoid with the deep-cut. It is of type `f64`, which means it is a 64-bit floating-point
285    /// number.
286    /// * `tau`: The parameter `tau` represents the time constant of the system. It is a measure of how
287    /// quickly the system responds to changes.
288    /// * `eta`: The parameter `eta` represents the deep-cut factor. It is a measure of how much the
289    /// ellipsoid is being updated or modified.
290    ///
291    /// ```svgbob
292    ///       _.-'''''''-._
293    ///     ,'    |        `.
294    ///    /      |          \
295    ///   .       |           .
296    ///   |       |           |
297    ///   |       | .         |
298    ///   |       |           |
299    ///   :\      |          /:
300    ///   | `._   |       _.' |
301    ///   |    '-.......-'    |
302    ///   |       |           |
303    ///  "-τ"     "-β"       +τ
304    ///
305    ///         η
306    ///   ϱ = ─────
307    ///       n + 1
308    ///
309    ///       2 ⋅ ϱ
310    ///   σ = ─────
311    ///       "τ + β"
312    ///
313    ///          2       2    2
314    ///         n       τ  - β
315    ///   δ = ────── ⋅  ───────
316    ///        2           2
317    ///       n  - 1      τ
318    /// ```
319    ///
320    /// # Example
321    ///
322    /// ```
323    /// use approx_eq::assert_approx_eq;
324    /// use ellalgo_rs::ell_calc::EllCalcCore;
325    ///
326    /// let ell_calc_core = EllCalcCore::new(4.0);
327    /// let (rho, sigma, delta) = ell_calc_core.calc_bias_cut_fast(&1.0, &2.0, &6.0);
328    /// assert_approx_eq!(rho, 1.2);
329    /// assert_approx_eq!(sigma, 0.8);
330    /// assert_approx_eq!(delta, 0.8);
331    /// ```
332    )]
333    pub fn calc_bias_cut_fast(&self, beta: &f64, tau: &f64, eta: &f64) -> (f64, f64, f64) {
334        let rho = eta / self.n_plus_1;
335        let sigma = 2.0 * rho / (tau + beta);
336        let alpha = beta / tau;
337        let delta = self.cst1 * (1.0 - alpha * alpha);
338        (rho, sigma, delta)
339    }
340
341    #[doc = svgbobdoc::transform!(
342    /// The `calc_central_cut_core` function calculates the core values needed for updating an ellipsoid with the
343    /// central-cut.
344    ///
345    /// Arguments:
346    ///
347    /// * `tsq`: The parameter `tsq` represents the square of the time taken to update the ellipsoid with
348    /// the central-cut.
349    ///
350    /// ```svgbob
351    ///       _.-'''''''-._
352    ///     ,'      |      `.
353    ///    /        |        \
354    ///   .         |         .
355    ///   |                   |
356    ///   |         .         |
357    ///   |                   |
358    ///   :\        |        /:
359    ///   | `._     |     _.' |
360    ///   |    '-.......-'    |
361    ///   |         |         |
362    ///  "-τ"       0        +τ
363    ///
364    ///         2
365    ///   σ = ─────
366    ///       n + 1
367    ///
368    ///         τ
369    ///   ϱ = ─────
370    ///       n + 1
371    ///
372    ///          2
373    ///         n
374    ///   δ = ──────
375    ///        2
376    ///       n  - 1
377    /// ```
378    ///
379    /// # Example
380    ///
381    /// ```rust
382    /// use approx_eq::assert_approx_eq;
383    /// use ellalgo_rs::ell_calc::EllCalcCore;
384    /// let ell_calc_core = EllCalcCore::new(4.0);
385    /// let (rho, sigma, delta) = ell_calc_core.calc_central_cut(&4.0);
386    /// assert_approx_eq!(rho, 0.4);
387    /// assert_approx_eq!(sigma, 0.4);
388    /// assert_approx_eq!(delta, 16.0/15.0);
389    /// ```
390    )]
391    pub fn calc_central_cut(&self, tsq: &f64) -> (f64, f64, f64) {
392        // self.mu = self.half_n_minus_1;
393        let sigma = self.cst2;
394        let rho = tsq.sqrt() / self.n_plus_1;
395        let delta = self.cst1;
396        (rho, sigma, delta)
397    }
398}
399
400/// The `EllCalc` struct represents an ellipsoid search space in Rust.
401///
402///  EllCalc = {x | (x - xc)^T mq^-1 (x - xc) \le \kappa}
403///
404/// Properties:
405///
406/// * `n_f`: The `n_f` property is a floating-point number that represents the dimensionality of the
407/// search space. It indicates the number of variables or dimensions in the ellipsoid search space.
408/// * `helper`: The `helper` property is of type `EllCalcCore` and is used to perform
409/// calculations related to the ellipsoid search space. It is a separate struct that contains the
410/// necessary methods and data for these calculations.
411/// * `use_parallel_cut`: A boolean flag indicating whether to use parallel cut or not.
412#[derive(Debug, Clone)]
413pub struct EllCalc {
414    n_f: f64,
415    pub helper: EllCalcCore,
416    pub use_parallel_cut: bool,
417}
418
419impl EllCalc {
420    /// The `new` function constructs a new [`EllCalc`] object with a given value for `n_f` and sets the
421    /// `use_parallel_cut` flag to `true`.
422    ///
423    /// Arguments:
424    ///
425    /// * `n_f`: The parameter `n_f` is a floating-point number that is used to initialize the `EllCalc`
426    /// struct.
427    ///
428    /// Returns:
429    ///
430    /// The `new` function is returning an instance of the `EllCalc` struct.
431    ///
432    /// # Examples:
433    ///
434    /// ```rust
435    /// use ellalgo_rs::ell_calc::EllCalc;
436    /// let ell_calc = EllCalc::new(4.0);
437    /// assert!(ell_calc.use_parallel_cut);
438    /// ```
439    pub fn new(n_f: f64) -> EllCalc {
440        let helper = EllCalcCore::new(n_f);
441
442        EllCalc {
443            n_f,
444            helper,
445            use_parallel_cut: true,
446        }
447    }
448
449    // pub fn update_cut(&mut self, beta: f64) -> CutStatus { self.calc_deep_cut(beta) }
450
451    /// The function calculates the updating of an ellipsoid with a single or parallel-cut.
452    ///
453    /// Arguments:
454    ///
455    /// * `beta`: The `beta` parameter is a tuple containing two values: `beta0` and `beta1_opt`. `beta0` is of type
456    /// `f64` and `beta1_opt` is an optional value of type `Option<f64>`.
457    /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
458    pub fn calc_single_or_parallel_deep_cut(
459        &self,
460        beta: &(f64, Option<f64>),
461        tsq: &f64,
462    ) -> (CutStatus, (f64, f64, f64)) {
463        let (beta0, beta1_opt) = *beta;
464        if let Some(beta1) = beta1_opt {
465            self.calc_parallel_deep_cut(beta0, beta1, tsq)
466        } else {
467            self.calc_deep_cut(&beta0, tsq)
468        }
469    }
470
471    /// The function calculates the updating of an ellipsoid with a single or parallel-cut (one of them is central-cut).
472    ///
473    /// Arguments:
474    ///
475    /// * `beta`: The `beta` parameter is a tuple containing two values: `f64` and `Option<f64>`.
476    /// The first value, denoted as `_b0`, is of type `f64`. The second value, `beta1_opt`, is of type `Option<f64>`.
477    /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
478    pub fn calc_single_or_parallel_central_cut(
479        &self,
480        beta: &(f64, Option<f64>),
481        tsq: &f64,
482    ) -> (CutStatus, (f64, f64, f64)) {
483        let (_b0, beta1_opt) = *beta;
484        if let Some(beta1) = beta1_opt {
485            self.calc_parallel_central_cut(beta1, tsq)
486        } else {
487            self.calc_central_cut(tsq)
488        }
489    }
490
491    /// The function calculates the updating of an ellipsoid with a single or parallel-cut (discrete version).
492    ///
493    /// Arguments:
494    ///
495    /// * `beta`: The `beta` parameter is a tuple containing two values: `beta0` and `beta1_opt`. `beta0` is of type
496    /// `f64` and `beta1_opt` is an optional value of type `Option<f64>`.
497    /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
498    pub fn calc_single_or_parallel_q(
499        &self,
500        beta: &(f64, Option<f64>),
501        tsq: &f64,
502    ) -> (CutStatus, (f64, f64, f64)) {
503        let (beta0, beta1_opt) = *beta;
504        if let Some(beta1) = beta1_opt {
505            self.calc_parallel_q(beta0, beta1, tsq)
506        } else {
507            self.calc_bias_cut_q(&beta0, tsq)
508        }
509    }
510
511    /// Parallel Deep Cut
512    ///
513    /// # Examples:
514    ///
515    /// ```rust
516    /// use approx_eq::assert_approx_eq;
517    /// use ellalgo_rs::ell_calc::EllCalc;
518    /// use ellalgo_rs::cutting_plane::CutStatus;
519    ///
520    /// let ell_calc = EllCalc::new(4.0);
521    /// let (status, _result) = ell_calc.calc_parallel_deep_cut(0.07, 0.03, &0.01);
522    /// assert_eq!(status, CutStatus::NoSoln);
523    ///
524    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.0, 0.05, &0.01);
525    /// assert_eq!(status, CutStatus::Success);
526    /// assert_approx_eq!(sigma, 0.8);
527    /// assert_approx_eq!(rho, 0.02);
528    /// assert_approx_eq!(delta, 1.2);
529    ///
530    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.05, 0.11, &0.01);
531    /// assert_eq!(status, CutStatus::Success);
532    /// assert_approx_eq!(sigma, 0.8);
533    /// assert_approx_eq!(rho, 0.06);
534    /// assert_approx_eq!(delta, 0.8);
535    ///
536    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.01, 0.04, &0.01);
537    /// assert_eq!(status, CutStatus::Success);
538    /// assert_approx_eq!(sigma, 0.928);
539    /// assert_approx_eq!(rho, 0.0232);
540    /// assert_approx_eq!(delta, 1.232);
541    /// ```
542    pub fn calc_parallel_deep_cut(
543        &self,
544        beta0: f64,
545        beta1: f64,
546        tsq: &f64,
547    ) -> (CutStatus, (f64, f64, f64)) {
548        if beta1 < beta0 {
549            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
550        }
551
552        let b1sqn = beta1 * (beta1 / tsq);
553        let t1n = 1.0 - b1sqn;
554        if t1n < 0.0 || !self.use_parallel_cut {
555            return self.calc_deep_cut(&beta0, tsq);
556        }
557
558        (
559            CutStatus::Success,
560            self.helper.calc_parallel_bias_cut(beta0, beta1, tsq),
561        )
562    }
563
564    /// Discrete Parallel Deep Cut
565    ///
566    /// # Examples:
567    ///
568    /// ```rust
569    /// use approx_eq::assert_approx_eq;
570    /// use ellalgo_rs::ell_calc::EllCalc;
571    /// use ellalgo_rs::cutting_plane::CutStatus;
572    ///
573    /// let ell_calc = EllCalc::new(4.0);
574    /// let (status, _result) = ell_calc.calc_parallel_q(-0.07, 0.07, &0.01);
575    /// assert_eq!(status, CutStatus::NoEffect);
576    ///
577    /// let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, &0.01);
578    /// assert_eq!(status, CutStatus::NoEffect);
579    /// ```
580    pub fn calc_parallel_q(
581        &self,
582        beta0: f64,
583        beta1: f64,
584        tsq: &f64,
585    ) -> (CutStatus, (f64, f64, f64)) {
586        if beta1 < beta0 {
587            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
588        }
589
590        if (beta1 > 0.0) && &(beta1 * beta1) >= tsq || !self.use_parallel_cut {
591            return self.calc_bias_cut_q(&beta0, tsq);
592        }
593
594        let b0b1 = beta0 * beta1;
595        let eta = tsq + self.n_f * b0b1;
596        if eta <= 0.0 {
597            return (CutStatus::NoEffect, (0.0, 0.0, 1.0)); // no effect
598        }
599
600        (
601            CutStatus::Success,
602            self.helper
603                .calc_parallel_bias_cut_fast(beta0, beta1, tsq, &b0b1, &eta),
604        )
605    }
606
607    /// Parallel Central Cut
608    ///
609    /// # Examples:
610    ///
611    /// ```rust
612    /// use approx_eq::assert_approx_eq;
613    /// use ellalgo_rs::ell_calc::EllCalc;
614    /// use ellalgo_rs::cutting_plane::CutStatus;
615    ///
616    /// let ell_calc = EllCalc::new(4.0);
617    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.11, &0.01);
618    /// assert_eq!(status, CutStatus::Success);
619    /// assert_approx_eq!(sigma, 0.4);
620    /// assert_approx_eq!(rho, 0.02);
621    /// assert_approx_eq!(delta, 16.0 / 15.0);
622    ///
623    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.05, &0.01);
624    /// assert_eq!(status, CutStatus::Success);
625    /// assert_approx_eq!(sigma, 0.8);
626    /// assert_approx_eq!(rho, 0.02);
627    /// assert_approx_eq!(delta, 1.2);
628    /// ```
629    pub fn calc_parallel_central_cut(&self, beta1: f64, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
630        if beta1 < 0.0 {
631            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no solution
632        }
633        let b1sqn = beta1 * (beta1 / tsq);
634        let t1n = 1.0 - b1sqn;
635        if t1n < 0.0 || !self.use_parallel_cut {
636            return self.calc_central_cut(tsq);
637        }
638        (
639            CutStatus::Success,
640            self.helper.calc_parallel_central_cut(beta1, tsq),
641        )
642    }
643
644    /// Deep Cut
645    ///
646    /// # Examples:
647    ///
648    /// ```rust
649    /// use approx_eq::assert_approx_eq;
650    /// use ellalgo_rs::ell_calc::EllCalc;
651    /// use ellalgo_rs::cutting_plane::CutStatus;
652    ///
653    /// let ell_calc = EllCalc::new(4.0);
654    /// let (status, _result) = ell_calc.calc_deep_cut(&0.11, &0.01);
655    /// assert_eq!(status, CutStatus::NoSoln);
656    /// let (status, _result) = ell_calc.calc_deep_cut(&0.0, &0.01);
657    /// assert_eq!(status, CutStatus::Success);
658    ///
659    /// let (status, (rho, sigma, delta)) = ell_calc.calc_deep_cut(&0.05, &0.01);
660    /// assert_eq!(status, CutStatus::Success);
661    /// assert_approx_eq!(sigma, 0.8);
662    /// assert_approx_eq!(rho, 0.06);
663    /// assert_approx_eq!(delta, 0.8);
664    /// ```
665    pub fn calc_deep_cut(&self, beta: &f64, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
666        if *tsq < beta * beta {
667            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
668        }
669
670        let tau = tsq.sqrt();
671        (CutStatus::Success, self.helper.calc_bias_cut(beta, &tau))
672    }
673
674    /// Discrete Deep Cut
675    ///
676    /// # Examples:
677    ///
678    /// ```rust
679    /// use approx_eq::assert_approx_eq;
680    /// use ellalgo_rs::ell_calc::EllCalc;
681    /// use ellalgo_rs::cutting_plane::CutStatus;
682    ///
683    /// let ell_calc = EllCalc::new(4.0);
684    /// let (status, _result) = ell_calc.calc_bias_cut_q(&-0.05, &0.01);
685    /// assert_eq!(status, CutStatus::NoEffect);
686    /// ```
687    pub fn calc_bias_cut_q(&self, beta: &f64, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
688        let tau = tsq.sqrt();
689
690        if tau < *beta {
691            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
692        }
693
694        let eta = tau + self.n_f * beta;
695        if eta < 0.0 {
696            return (CutStatus::NoEffect, (0.0, 0.0, 1.0)); // no effect
697        }
698
699        (
700            CutStatus::Success,
701            self.helper.calc_bias_cut_fast(beta, &tau, &eta),
702        )
703    }
704
705    /// Central Cut
706    ///
707    /// # Examples:
708    ///
709    /// ```rust
710    /// use approx_eq::assert_approx_eq;
711    /// use ellalgo_rs::ell_calc::EllCalc;
712    /// use ellalgo_rs::cutting_plane::CutStatus;
713    ///
714    /// let ell_calc = EllCalc::new(4.0);
715    /// let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(&0.01);
716    ///
717    /// assert_eq!(status, CutStatus::Success);
718    /// assert_approx_eq!(sigma, 0.4);
719    /// assert_approx_eq!(rho, 0.02);
720    /// assert_approx_eq!(delta, 16.0 / 15.0);
721    /// ```
722    #[inline]
723    pub fn calc_central_cut(&self, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
724        // self.mu = self.half_n_minus_1;
725        (CutStatus::Success, self.helper.calc_central_cut(tsq))
726    }
727}
728
729// pub trait UpdateByCutChoices {
730//     fn update_by(self, ell: &mut EllCalc) -> CutStatus;
731// }
732#[cfg(test)]
733mod tests {
734    use super::*;
735    use approx_eq::assert_approx_eq;
736
737    #[test]
738    pub fn test_construct() {
739        let helper = EllCalcCore::new(4.0);
740        assert_eq!(helper.n_f, 4.0);
741        assert_eq!(helper.half_n, 2.0);
742        assert_approx_eq!(helper.cst1, 16.0 / 15.0);
743        assert_approx_eq!(helper.cst2, 0.4);
744    }
745
746    #[test]
747    pub fn test_calc_central_cut() {
748        let ell_calc = EllCalc::new(4.0);
749        // ell_calc.tsq = 0.01;
750        let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(&0.01);
751        assert_eq!(status, CutStatus::Success);
752        assert_approx_eq!(sigma, 0.4);
753        assert_approx_eq!(rho, 0.02);
754        assert_approx_eq!(delta, 16.0 / 15.0);
755    }
756
757    #[test]
758    pub fn test_calc_deep_cut() {
759        let ell_calc = EllCalc::new(4.0);
760        // ell_calc.tsq = 0.01;
761        let (status, _result) = ell_calc.calc_deep_cut(&0.11, &0.01);
762        assert_eq!(status, CutStatus::NoSoln);
763        let (status, _result) = ell_calc.calc_deep_cut(&0.0, &0.01);
764        assert_eq!(status, CutStatus::Success);
765        let (status, _result) = ell_calc.calc_bias_cut_q(&-0.05, &0.01);
766        assert_eq!(status, CutStatus::NoEffect);
767
768        // ell_calc.tsq = 0.01;
769        let (status, (rho, sigma, delta)) = ell_calc.calc_deep_cut(&0.05, &0.01);
770        assert_eq!(status, CutStatus::Success);
771        assert_approx_eq!(sigma, 0.8);
772        assert_approx_eq!(rho, 0.06);
773        assert_approx_eq!(delta, 0.8);
774    }
775
776    #[test]
777    pub fn test_calc_parallel_central_cut() {
778        let ell_calc = EllCalc::new(4.0);
779        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.11, &0.01);
780        assert_eq!(status, CutStatus::Success);
781        assert_approx_eq!(sigma, 0.4);
782        assert_approx_eq!(rho, 0.02);
783        assert_approx_eq!(delta, 16.0 / 15.0);
784
785        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.05, &0.01);
786        assert_eq!(status, CutStatus::Success);
787        assert_approx_eq!(sigma, 0.8);
788        assert_approx_eq!(rho, 0.02);
789        assert_approx_eq!(delta, 1.2);
790    }
791
792    #[test]
793    pub fn test_calc_parallel() {
794        let ell_calc = EllCalc::new(4.0);
795        // ell_calc.tsq = 0.01;
796        let (status, _result) = ell_calc.calc_parallel_deep_cut(0.07, 0.03, &0.01);
797        assert_eq!(status, CutStatus::NoSoln);
798
799        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.0, 0.05, &0.01);
800        assert_eq!(status, CutStatus::Success);
801        assert_approx_eq!(sigma, 0.8);
802        assert_approx_eq!(rho, 0.02);
803        assert_approx_eq!(delta, 1.2);
804
805        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.05, 0.11, &0.01);
806        assert_eq!(status, CutStatus::Success);
807        assert_approx_eq!(sigma, 0.8);
808        assert_approx_eq!(rho, 0.06);
809        assert_approx_eq!(delta, 0.8);
810
811        let (status, _result) = ell_calc.calc_parallel_q(-0.07, 0.07, &0.01);
812        assert_eq!(status, CutStatus::NoEffect);
813
814        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.01, 0.04, &0.01);
815        assert_eq!(status, CutStatus::Success);
816        assert_approx_eq!(sigma, 0.928);
817        assert_approx_eq!(rho, 0.0232);
818        assert_approx_eq!(delta, 1.232);
819
820        let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, &0.01);
821        assert_eq!(status, CutStatus::NoEffect);
822    }
823}