pxfm/
triple_double.rs

1/*
2 * // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
3 * //
4 * // Redistribution and use in source and binary forms, with or without modification,
5 * // are permitted provided that the following conditions are met:
6 * //
7 * // 1.  Redistributions of source code must retain the above copyright notice, this
8 * // list of conditions and the following disclaimer.
9 * //
10 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * // this list of conditions and the following disclaimer in the documentation
12 * // and/or other materials provided with the distribution.
13 * //
14 * // 3.  Neither the name of the copyright holder nor the names of its
15 * // contributors may be used to endorse or promote products derived from
16 * // this software without specific prior written permission.
17 * //
18 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::common::f_fmla;
30use crate::double_double::DoubleDouble;
31use std::ops::Neg;
32
33#[derive(Clone, Copy, Debug)]
34pub(crate) struct TripleDouble {
35    pub(crate) hi: f64,
36    pub(crate) mid: f64,
37    pub(crate) lo: f64,
38}
39
40// impl TripleDouble {
41//     #[inline]
42//     pub(crate) fn mul_dd_add_f64(p0: TripleDouble, p1: DoubleDouble, p2: f64) -> TripleDouble {
43//         let q0 = TripleDouble::quick_mul_dd(p0, p1);
44//         TripleDouble::add_f64(p2, q0)
45//     }
46// }
47
48impl TripleDouble {
49    // #[inline]
50    // pub(crate) fn mul_dd_add(p0: TripleDouble, p1: DoubleDouble, p2: TripleDouble) -> TripleDouble {
51    //     let q0 = TripleDouble::quick_mul_dd(p0, p1);
52    //     TripleDouble::add(q0, p2)
53    // }
54
55    // #[inline]
56    // pub(crate) fn mul_dd_add_dd(
57    //     p0: TripleDouble,
58    //     p1: DoubleDouble,
59    //     p2: DoubleDouble,
60    // ) -> TripleDouble {
61    //     let q0 = TripleDouble::quick_mul_dd(p0, p1);
62    //     TripleDouble::add_dd(p2, q0)
63    // }
64}
65
66impl TripleDouble {
67    #[inline]
68    pub(crate) fn f64_mul_dd_add(p0: f64, p1: DoubleDouble, p2: TripleDouble) -> TripleDouble {
69        let q0 = TripleDouble::from_quick_mult_dd_f64(p1, p0);
70        TripleDouble::add(q0, p2)
71    }
72
73    #[inline]
74    pub(crate) fn f64_mul_add(p0: f64, p1: TripleDouble, p2: TripleDouble) -> TripleDouble {
75        let q0 = TripleDouble::quick_mult_f64(p1, p0);
76        TripleDouble::add(q0, p2)
77    }
78}
79
80impl TripleDouble {
81    #[inline]
82    pub(crate) const fn from_bit_pair(p0: (u64, u64, u64)) -> TripleDouble {
83        TripleDouble {
84            hi: f64::from_bits(p0.2),
85            mid: f64::from_bits(p0.1),
86            lo: f64::from_bits(p0.0),
87        }
88    }
89}
90
91#[inline]
92fn add12(a: f64, b: f64) -> DoubleDouble {
93    DoubleDouble::from_full_exact_add(a, b)
94}
95
96#[inline]
97fn mul12(a: f64, b: f64) -> DoubleDouble {
98    DoubleDouble::from_exact_mult(a, b)
99}
100
101#[inline]
102pub(crate) fn add22(a: DoubleDouble, b: DoubleDouble) -> DoubleDouble {
103    let DoubleDouble { hi: v1, lo: v2 } = DoubleDouble::from_full_exact_add(a.hi, b.hi);
104    let v3 = a.lo + b.lo;
105    let v4 = v2 + v3;
106    DoubleDouble::from_full_exact_add(v1, v4)
107}
108
109impl TripleDouble {
110    // #[inline]
111    // pub(crate) fn from_quick_mult_dd(a: DoubleDouble, b: DoubleDouble) -> TripleDouble {
112    //     /*
113    //     (rh , t1) ← Mul12 (ah , bh )
114    //     (t2, t3) ← Mul12 (ah , bl )
115    //     (t4, t5) ← Mul12 (al , bh )
116    //     t6 ← al ⊗ bl
117    //     (t7, t8) ← Add22 (t2, t3, t4, t5)
118    //     (t9, t10) ← Add12 (t1, t6)
119    //     (rm , rl ) ← Add22 (t7, t8, t9, t10)
120    //      */
121    //     let DoubleDouble { hi: rh, lo: t1 } = mul12(a.hi, b.hi);
122    //     let r0 = mul12(a.hi, b.lo);
123    //     let r1 = mul12(a.lo, b.hi);
124    //     let t6 = a.lo * b.lo;
125    //     let q0 = add22(r0, r1);
126    //     let q1 = add12(t1, t6);
127    //     let DoubleDouble { hi: rm, lo: rl } = add22(q0, q1);
128    //     TripleDouble {
129    //         hi: rh,
130    //         mid: rm,
131    //         lo: rl,
132    //     }
133    // }
134
135    #[inline]
136    pub(crate) fn from_quick_mult_dd_f64(a: DoubleDouble, b: f64) -> TripleDouble {
137        let DoubleDouble { hi: rh, lo: t1 } = mul12(a.hi, b);
138        let DoubleDouble { hi: t2, lo: t3 } = mul12(a.lo, b);
139        let DoubleDouble { hi: t5, lo: t4 } = add12(t1, t2);
140        let t6 = t3 + t4;
141        let DoubleDouble { hi: rm, lo: rl } = add12(t5, t6);
142        TripleDouble::new(rl, rm, rh)
143    }
144
145    #[inline]
146    pub(crate) fn quick_mult_f64(a: TripleDouble, b: f64) -> TripleDouble {
147        let DoubleDouble { hi: rh, lo: t2 } = mul12(a.hi, b);
148        let DoubleDouble { hi: t3, lo: t4 } = mul12(a.mid, b);
149        let DoubleDouble { hi: t9, lo: t7 } = add12(t2, t3);
150        let t8 = f_fmla(a.lo, b, t4);
151        let t10 = t7 + t8;
152        let DoubleDouble { hi: rm, lo: rl } = add12(t9, t10);
153        TripleDouble::new(rl, rm, rh)
154    }
155
156    #[inline]
157    pub(crate) fn quick_mult(b: TripleDouble, a: TripleDouble) -> TripleDouble {
158        /* Mul12((resh),&_t1,(ah),(bh));
159        Mul12(&_t2,&_t3,(ah),(bm));
160        Mul12(&_t4,&_t5,(am),(bh));
161        Mul12(&_t6,&_t7,(am),(bm));
162        _t8 = (ah) * (bl);
163        _t9 = (al) * (bh);
164        _t10 = (am) * (bl);
165        _t11 = (al) * (bm);
166        _t12 = _t8 + _t9;
167        _t13 = _t10 + _t11;
168        Add12Cond(_t14,_t15,_t1,_t6);
169        _t16 = _t7 + _t15;
170        _t17 = _t12 + _t13;
171        _t18 = _t16 + _t17;
172        Add12Cond(_t19,_t20,_t14,_t18);
173        Add22Cond(&_t21,&_t22,_t2,_t3,_t4,_t5);
174        Add22Cond((resm),(resl),_t21,_t22,_t19,_t20);   */
175        let DoubleDouble { hi: rh, lo: t1 } = DoubleDouble::from_exact_mult(a.hi, b.hi);
176        let DoubleDouble { hi: t2, lo: t3 } = DoubleDouble::from_exact_mult(a.hi, b.mid);
177        let DoubleDouble { hi: t4, lo: t5 } = DoubleDouble::from_exact_mult(a.mid, b.hi);
178        let DoubleDouble { hi: t6, lo: t7 } = DoubleDouble::from_exact_mult(a.mid, b.mid);
179        let t12 = f_fmla(a.hi, b.lo, a.lo * b.hi);
180        let t13 = f_fmla(a.mid, b.lo, a.lo * b.mid);
181        let DoubleDouble { hi: t14, lo: t15 } = add12(t1, t6);
182        let t16 = t7 + t15;
183        let t17 = t12 + t13;
184        let t18 = t16 + t17;
185        let DoubleDouble { hi: t19, lo: t20 } = add12(t14, t18);
186        let DoubleDouble { hi: t21, lo: t22 } =
187            add22(DoubleDouble::new(t3, t2), DoubleDouble::new(t4, t5));
188        let DoubleDouble { hi: rm, lo: rl } =
189            add22(DoubleDouble::new(t22, t21), DoubleDouble::new(t20, t19));
190        TripleDouble::new(rl, rm, rh)
191    }
192
193    #[inline]
194    pub(crate) fn quick_mult_dd(b: TripleDouble, a: DoubleDouble) -> TripleDouble {
195        /*
196        (rh , t1) ← Mul12 (ah , bh )
197        (t2, t3) ← Mul12 (ah , bm )
198        (t4, t5) ← Mul12 (ah , bl )
199        (t6, t7) ← Mul12 (al , bh )
200        (t8, t9) ← Mul12 (al , bm )
201        t10 ← al ⊗ bl
202        (t11, t12) ← Add22 (t2, t3, t4, t5)
203        (t13, t14) ← Add22 (t6, t7, t8, t9)
204        (t15, t16) ← Add22 (t11, t12, t13, t14)
205        (t17, t18) ← Add12 (t1, t10)
206        (rm , rl ) ← Add22 (t17, t18, t15, t16)
207        */
208        let DoubleDouble { hi: rh, lo: t1 } = mul12(a.hi, b.hi);
209        let DoubleDouble { hi: t2, lo: t3 } = mul12(a.hi, b.mid);
210        let DoubleDouble { hi: t4, lo: t5 } = mul12(a.hi, b.lo);
211        let DoubleDouble { hi: t6, lo: t7 } = mul12(a.lo, b.hi);
212        let DoubleDouble { hi: t8, lo: t9 } = mul12(a.lo, b.mid);
213        let t10 = a.lo * b.lo;
214        let z0 = add22(DoubleDouble::new(t3, t2), DoubleDouble::new(t4, t5));
215        let z1 = add22(DoubleDouble::new(t6, t7), DoubleDouble::new(t8, t9));
216        let q0 = add22(z0, z1);
217        let q1 = add12(t1, t10);
218        let DoubleDouble { hi: rm, lo: rl } = add22(q1, q0);
219        TripleDouble {
220            hi: rh,
221            mid: rm,
222            lo: rl,
223        }
224    }
225
226    pub(crate) fn add(a: TripleDouble, b: TripleDouble) -> TripleDouble {
227        /*
228        (rh , t1) ← Add12 (ah , bh )
229        (t2, t3) ← Add12 (am , bm )
230        (t7, t4) ← Add12 (t1, t2)
231        t6 ← al ⊕ bl
232        t5 ← t3 ⊕ t4
233        t8 ← t5 ⊕ t6
234        (rm , rl ) ← Add12 (t7, t8)
235                 */
236        let DoubleDouble { hi: rh, lo: t1 } = add12(a.hi, b.hi);
237        let DoubleDouble { hi: t2, lo: t3 } = add12(a.mid, b.mid);
238        let t6 = a.lo + b.lo;
239        let DoubleDouble { hi: t7, lo: t4 } = add12(t1, t2);
240        let t5 = t3 + t4;
241        let t8 = t5 + t6;
242        let DoubleDouble { hi: rm, lo: rl } = add12(t7, t8);
243        TripleDouble {
244            hi: rh,
245            mid: rm,
246            lo: rl,
247        }
248    }
249
250    // #[inline]
251    // pub(crate) fn add_dd(a: DoubleDouble, b: TripleDouble) -> TripleDouble {
252    //     /*
253    //     (rh , t1) ← Add12 (ah , bh )
254    //     (t2, t3) ← Add12 (al , bm )
255    //     (t4, t5) ← Add12 (t1, t2)
256    //     t6 ← t3 ⊕ bl
257    //     t7 ← t6 ⊕ t5
258    //     (rm , rl ) ← Add12 (t4, t7)
259    //      */
260    //     let DoubleDouble { hi: rh, lo: t1 } = add12(a.hi, b.hi);
261    //     let DoubleDouble { hi: t2, lo: t3 } = add12(a.hi, b.mid);
262    //     let DoubleDouble { hi: t4, lo: t5 } = add12(t1, t2);
263    //     let t6 = t3 + b.lo;
264    //     let t7 = t6 + t5;
265    //     let DoubleDouble { hi: rm, lo: rl } = add12(t4, t7);
266    //     TripleDouble {
267    //         hi: rh,
268    //         mid: rm,
269    //         lo: rl,
270    //     }
271    // }
272
273    #[inline]
274    pub(crate) fn add_f64(a: f64, b: TripleDouble) -> TripleDouble {
275        let DoubleDouble { hi: rh, lo: t1 } = add12(a, b.hi);
276        let DoubleDouble { hi: t2, lo: t3 } = add12(t1, b.mid);
277        let t4 = t3 + b.lo;
278        let DoubleDouble { hi: rm, lo: rl } = add12(t2, t4);
279        TripleDouble::new(rl, rm, rh)
280    }
281
282    // #[inline]
283    // pub(crate) fn to_dd(self) -> DoubleDouble {
284    //     let DoubleDouble { hi: t1, lo: t2 } = add12(self.hi, self.mid);
285    //     let t3 = t2 + self.lo;
286    //     DoubleDouble::new(t3, t1)
287    // }
288
289    #[inline]
290    pub(crate) const fn new(lo: f64, mid: f64, hi: f64) -> Self {
291        Self { hi, mid, lo }
292    }
293
294    #[inline]
295    pub(crate) fn to_f64(self) -> f64 {
296        let DoubleDouble { hi: t1, lo: t2 } = add12(self.hi, self.mid);
297        let t3 = t2 + self.lo;
298        t1 + t3
299    }
300
301    #[inline]
302    pub(crate) fn from_full_exact_add(a: f64, b: f64) -> Self {
303        let DoubleDouble { hi: rh, lo: t1 } = add12(a, b);
304        TripleDouble::new(0., t1, rh).renormalize()
305    }
306
307    #[inline]
308    pub(crate) fn renormalize(self) -> Self {
309        /*
310           double _t1h, _t1l, _t2l;
311
312        Add12(_t1h, _t1l, (am), (al));
313        Add12((*(resh)), _t2l, (ah), (_t1h));
314        Add12((*(resm)), (*(resl)), _t2l, _t1l);
315         */
316        let DoubleDouble { hi: t1h, lo: t1l } = DoubleDouble::from_exact_add(self.mid, self.lo);
317        let DoubleDouble { hi: rh, lo: t2l } = DoubleDouble::from_exact_add(self.hi, t1h);
318        let zml = DoubleDouble::from_exact_add(t2l, t1l);
319        TripleDouble::new(zml.lo, zml.hi, rh)
320    }
321
322    #[inline]
323    pub(crate) fn recip(self) -> Self {
324        /*
325              _rec_r1 = 1.0 / (dh);
326        Mul12(&_rec_t1,&_rec_t2,_rec_r1,(dh));
327        _rec_t3 = _rec_t1 - 1.0;
328        Add12Cond(_rec_t4,_rec_t5,_rec_t3,_rec_t2);
329        Mul12(&_rec_t6,&_rec_t7,_rec_r1,(dm));
330        Add12(_rec_t8,_rec_t9,-1.0,_rec_t6);
331        _rec_t10 = _rec_t9 + _rec_t7;
332        Add12(_rec_t11,_rec_t12,_rec_t8,_rec_t10);
333        _rec_r1 = -_rec_r1;
334        Add22Cond(&_rec_t13,&_rec_t14,_rec_t4,_rec_t5,_rec_t11,_rec_t12);
335        Mul122(&_rec_r2h,&_rec_r2l,_rec_r1,_rec_t13,_rec_t14);
336        Mul233(&_rec_t15,&_rec_t16,&_rec_t17,_rec_r2h,_rec_r2l,(dh),(dm),(dl));
337        Renormalize3(&_rec_t18,&_rec_t19,&_rec_t20,_rec_t15,_rec_t16,_rec_t17);
338        _rec_t18 = -1.0;
339        Mul233(&_rec_t21,&_rec_t22,&_rec_t23,_rec_r2h,_rec_r2l,_rec_t18,_rec_t19,_rec_t20);
340        _rec_t21 = -_rec_t21; _rec_t22 = -_rec_t22; _rec_t23 = -_rec_t23;
341        Renormalize3((resh),(resm),(resl),_rec_t21,_rec_t22,_rec_t23);
342             */
343        let mut r1 = 1. / self.hi;
344        let DoubleDouble {
345            hi: rec_t1,
346            lo: rec_t2,
347        } = DoubleDouble::from_exact_mult(r1, self.hi);
348        let rec_t3 = rec_t1 - 1.0;
349        let DoubleDouble {
350            hi: rec_t4,
351            lo: rec_t5,
352        } = add12(rec_t3, rec_t2);
353        let DoubleDouble {
354            hi: rec_t6,
355            lo: rec_t7,
356        } = mul12(r1, self.mid);
357        let DoubleDouble {
358            hi: rec_t8,
359            lo: rec_t9,
360        } = add12(-1.0, rec_t6);
361        let rec_t10 = rec_t9 + rec_t7;
362        r1 = -r1;
363        let DoubleDouble {
364            hi: rec_t11,
365            lo: rec_t12,
366        } = add12(rec_t8, rec_t10);
367        let z0 = add22(
368            DoubleDouble::new(rec_t5, rec_t4),
369            DoubleDouble::new(rec_t12, rec_t11),
370        );
371        let r2hl = DoubleDouble::quick_mult_f64(z0, r1);
372        let rec15_16_17 = TripleDouble::quick_mult_dd(self, r2hl);
373        let rec18_19_20 = rec15_16_17.renormalize();
374        let rec_t18 = -1.0;
375        let rec_21_22_23 = -TripleDouble::quick_mult_dd(
376            TripleDouble::new(rec18_19_20.lo, rec18_19_20.mid, rec_t18),
377            r2hl,
378        );
379        rec_21_22_23.renormalize()
380    }
381}
382
383impl Neg for TripleDouble {
384    type Output = Self;
385    #[inline]
386    fn neg(self) -> Self::Output {
387        TripleDouble::new(-self.lo, -self.mid, -self.hi)
388    }
389}