Skip to main content

p3_field/extension/
quintic_extension.rs

1//! Degree-5 extension field using the trinomial `X^5 + X^2 - 1`.
2//!
3//! This extension requires that `X^5 + X^2 - 1` is irreducible over the base field.
4//! Currently used for KoalaBear where irreducibility has been verified.
5//!
6//! Reduction identity: `X^5 = 1 - X^2`
7
8use alloc::format;
9use alloc::string::ToString;
10use alloc::vec::Vec;
11use core::array;
12use core::fmt::{self, Display, Formatter};
13use core::iter::{Product, Sum};
14use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
15
16use itertools::Itertools;
17use num_bigint::BigUint;
18use p3_util::{as_base_slice, as_base_slice_mut, reconstitute_from_base};
19
20use super::packed_quintic_extension::PackedQuinticTrinomialExtensionField;
21use super::{ExtField, HasFrobenius, HasTwoAdicQuinticExtension};
22use crate::extension::{ExtensionAlgebra, QuinticTrinomial, QuinticTrinomialExtendable};
23use crate::field::Field;
24use crate::{
25    Algebra, ExtensionField, PackedFieldExtension, PrimeCharacteristicRing, RawDataSerializable,
26    TwoAdicField, field_to_array,
27};
28
29/// A degree-5 extension field using the trinomial `X^5 + X^2 - 1`.
30///
31/// Elements are represented as `a_0 + a_1*X + a_2*X^2 + a_3*X^3 + a_4*X^4`.
32///
33/// Type alias for the unified [`ExtField`] with `Shape = QuinticTrinomial`.
34pub type QuinticTrinomialExtensionField<F, A = F> = ExtField<F, 5, QuinticTrinomial, A>;
35
36impl<F: Copy> QuinticTrinomialExtensionField<F, F> {
37    /// Convert a `[[F; 5]; N]` array to an array of extension field elements.
38    ///
39    /// # Panics
40    /// Panics if `N == 0`.
41    #[inline]
42    pub const fn new_array<const N: usize>(input: [[F; 5]; N]) -> [Self; N] {
43        const { assert!(N > 0) }
44        let mut output = [Self::new(input[0]); N];
45        let mut i = 1;
46        while i < N {
47            output[i] = Self::new(input[i]);
48            i += 1;
49        }
50        output
51    }
52}
53
54impl<F: QuinticTrinomialExtendable> ExtensionField<F> for QuinticTrinomialExtensionField<F>
55where
56    PackedQuinticTrinomialExtensionField<F, F::Packing>: PackedFieldExtension<F, Self>,
57{
58    type ExtensionPacking = PackedQuinticTrinomialExtensionField<F, F::Packing>;
59
60    #[inline]
61    fn is_in_basefield(&self) -> bool {
62        self.value[1..].iter().all(F::is_zero)
63    }
64
65    #[inline]
66    fn as_base(&self) -> Option<F> {
67        <Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
68    }
69}
70
71impl<F: QuinticTrinomialExtendable> HasFrobenius<F> for QuinticTrinomialExtensionField<F> {
72    #[inline]
73    fn frobenius(&self) -> Self {
74        let a = &self.value;
75        let fc = &F::FROBENIUS_COEFFS;
76
77        // φ(a) = a_0 + Σ_{k=1}^{4} a_k * X^{k*p} where X^{k*p} = fc[k-1]
78        let a_tail = &[a[1], a[2], a[3], a[4]];
79        let c0 = a[0] + F::dot_product::<4>(a_tail, &[fc[0][0], fc[1][0], fc[2][0], fc[3][0]]);
80        let c1 = F::dot_product::<4>(a_tail, &[fc[0][1], fc[1][1], fc[2][1], fc[3][1]]);
81        let c2 = F::dot_product::<4>(a_tail, &[fc[0][2], fc[1][2], fc[2][2], fc[3][2]]);
82        let c3 = F::dot_product::<4>(a_tail, &[fc[0][3], fc[1][3], fc[2][3], fc[3][3]]);
83        let c4 = F::dot_product::<4>(a_tail, &[fc[0][4], fc[1][4], fc[2][4], fc[3][4]]);
84
85        Self::new([c0, c1, c2, c3, c4])
86    }
87
88    /// Apply Frobenius `count` times: `x → x^{p^count}`.
89    #[inline]
90    fn repeated_frobenius(&self, count: usize) -> Self {
91        match count % 5 {
92            0 => *self,
93            _ => {
94                let mut result = *self;
95                for _ in 0..(count % 5) {
96                    result = result.frobenius();
97                }
98                result
99            }
100        }
101    }
102
103    /// Compute pseudo-inverse using Frobenius automorphism.
104    ///
105    /// Returns `0` if `self == 0`, and `1/self` otherwise.
106    ///
107    /// Uses the identity: `a^{-1} = ProdConj(a) * Norm(a)^{-1}` where
108    /// - `ProdConj(a) = a^{p^4 + p^3 + p^2 + p}`,
109    /// - `Norm(a) = a * ProdConj(a)` is in the base field.
110    #[inline]
111    fn pseudo_inv(&self) -> Self {
112        if self.is_zero() {
113            return Self::ZERO;
114        }
115
116        // Compute ProdConj(a) = a^{p^4 + p^3 + p^2 + p} efficiently:
117        // a^{p + p^2} = (a * a^p)^p, then a^{p + p^2 + p^3 + p^4} = (a^{p+p^2}) * (a^{p+p^2})^{p^2}
118        let a_exp_p = self.frobenius();
119        let a_exp_p_plus_p2 = (*self * a_exp_p).frobenius();
120        let prod_conj = a_exp_p_plus_p2 * a_exp_p_plus_p2.repeated_frobenius(2);
121
122        // Norm(a) = a * ProdConj(a) lies in the base field.
123        // Compute only the constant coefficient.
124        let norm = self.compute_norm_with_prod_conj(&prod_conj);
125        debug_assert_eq!(Self::from(norm), *self * prod_conj);
126
127        prod_conj * norm.inverse()
128    }
129}
130
131impl<F: QuinticTrinomialExtendable> QuinticTrinomialExtensionField<F> {
132    /// Compute the norm given pre-computed product of conjugates.
133    ///
134    /// The norm `Norm(a) = a * prod_conj` lies in the base field.
135    /// This computes only the constant coefficient for efficiency.
136    #[inline]
137    fn compute_norm_with_prod_conj(&self, prod_conj: &Self) -> F {
138        let a = &self.value;
139        let b = &prod_conj.value;
140
141        // For trinomial X^5 + X^2 - 1, the constant term of a*b is:
142        // c_0 + c_5 - c_8 where c_k = Σ_{i+j=k} a_i*b_j
143        let c0 = a[0] * b[0];
144        let c5 = F::dot_product::<4>(&[a[1], a[2], a[3], a[4]], &[b[4], b[3], b[2], b[1]]);
145        let c8 = a[4] * b[4];
146
147        c0 + c5 - c8
148    }
149}
150
151impl<F, A> PrimeCharacteristicRing for QuinticTrinomialExtensionField<F, A>
152where
153    F: QuinticTrinomialExtendable,
154    A: ExtensionAlgebra<F, 5, QuinticTrinomial> + Copy,
155{
156    type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
157
158    const ZERO: Self = Self::new([A::ZERO; 5]);
159    const ONE: Self = Self::new(field_to_array(A::ONE));
160    const TWO: Self = Self::new(field_to_array(A::TWO));
161    const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
162
163    #[inline]
164    fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
165        <A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
166    }
167
168    #[inline]
169    fn halve(&self) -> Self {
170        Self::new(array::from_fn(|i| self.value[i].halve()))
171    }
172
173    #[inline(always)]
174    fn square(&self) -> Self {
175        let mut res = Self::default();
176        <A as ExtensionAlgebra<F, 5, QuinticTrinomial>>::ext_square(&self.value, &mut res.value);
177        res
178    }
179
180    #[inline]
181    fn mul_2exp_u64(&self, exp: u64) -> Self {
182        Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
183    }
184
185    #[inline]
186    fn div_2exp_u64(&self, exp: u64) -> Self {
187        Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
188    }
189
190    #[inline]
191    fn zero_vec(len: usize) -> Vec<Self> {
192        // SAFETY: `Self` is `repr(transparent)` over `[A; 5]`.
193        unsafe { reconstitute_from_base(A::zero_vec(len * 5)) }
194    }
195}
196
197impl<F: QuinticTrinomialExtendable> Algebra<F> for QuinticTrinomialExtensionField<F> {}
198
199impl<F: QuinticTrinomialExtendable> RawDataSerializable for QuinticTrinomialExtensionField<F> {
200    const NUM_BYTES: usize = F::NUM_BYTES * 5;
201
202    #[inline]
203    fn into_bytes(self) -> impl IntoIterator<Item = u8> {
204        self.value.into_iter().flat_map(|x| x.into_bytes())
205    }
206
207    #[inline]
208    fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
209        F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
210    }
211
212    #[inline]
213    fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
214        F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
215    }
216
217    #[inline]
218    fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
219        F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
220    }
221
222    #[inline]
223    fn into_parallel_byte_streams<const N: usize>(
224        input: impl IntoIterator<Item = [Self; N]>,
225    ) -> impl IntoIterator<Item = [u8; N]> {
226        F::into_parallel_byte_streams(
227            input
228                .into_iter()
229                .flat_map(|x| (0..5).map(move |i| array::from_fn(|j| x[j].value[i]))),
230        )
231    }
232
233    #[inline]
234    fn into_parallel_u32_streams<const N: usize>(
235        input: impl IntoIterator<Item = [Self; N]>,
236    ) -> impl IntoIterator<Item = [u32; N]> {
237        F::into_parallel_u32_streams(
238            input
239                .into_iter()
240                .flat_map(|x| (0..5).map(move |i| array::from_fn(|j| x[j].value[i]))),
241        )
242    }
243
244    #[inline]
245    fn into_parallel_u64_streams<const N: usize>(
246        input: impl IntoIterator<Item = [Self; N]>,
247    ) -> impl IntoIterator<Item = [u64; N]> {
248        F::into_parallel_u64_streams(
249            input
250                .into_iter()
251                .flat_map(|x| (0..5).map(move |i| array::from_fn(|j| x[j].value[i]))),
252        )
253    }
254}
255
256impl<F: QuinticTrinomialExtendable> Field for QuinticTrinomialExtensionField<F> {
257    type Packing = Self;
258
259    const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
260
261    fn try_inverse(&self) -> Option<Self> {
262        if self.is_zero() {
263            return None;
264        }
265        Some(self.pseudo_inv())
266    }
267
268    #[inline]
269    fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
270        // SAFETY: `Self` is `repr(transparent)` over `[F; 5]`.
271        // Addition is F-linear, so we can operate on base field slices.
272        unsafe {
273            let base_slice_1 = as_base_slice_mut(slice_1);
274            let base_slice_2 = as_base_slice(slice_2);
275            F::add_slices(base_slice_1, base_slice_2);
276        }
277    }
278
279    #[inline]
280    fn order() -> BigUint {
281        F::order().pow(5)
282    }
283}
284
285impl<F: QuinticTrinomialExtendable> Display for QuinticTrinomialExtensionField<F> {
286    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
287        if self.is_zero() {
288            write!(f, "0")
289        } else {
290            let str = self
291                .value
292                .iter()
293                .enumerate()
294                .filter(|(_, x)| !x.is_zero())
295                .map(|(i, x)| match (i, x.is_one()) {
296                    (0, _) => format!("{x}"),
297                    (1, true) => "X".to_string(),
298                    (1, false) => format!("{x} X"),
299                    (_, true) => format!("X^{i}"),
300                    (_, false) => format!("{x} X^{i}"),
301                })
302                .join(" + ");
303            write!(f, "{str}")
304        }
305    }
306}
307
308impl<F, A> Neg for QuinticTrinomialExtensionField<F, A>
309where
310    F: QuinticTrinomialExtendable,
311    A: Algebra<F>,
312{
313    type Output = Self;
314
315    #[inline]
316    fn neg(self) -> Self {
317        Self::new(self.value.map(A::neg))
318    }
319}
320
321impl<F, A> Add for QuinticTrinomialExtensionField<F, A>
322where
323    F: QuinticTrinomialExtendable,
324    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
325{
326    type Output = Self;
327
328    #[inline]
329    fn add(self, rhs: Self) -> Self {
330        Self::new(<A as ExtensionAlgebra<F, 5, QuinticTrinomial>>::ext_add(
331            &self.value,
332            &rhs.value,
333        ))
334    }
335}
336
337impl<F, A> Add<A> for QuinticTrinomialExtensionField<F, A>
338where
339    F: QuinticTrinomialExtendable,
340    A: Algebra<F>,
341{
342    type Output = Self;
343
344    #[inline]
345    fn add(mut self, rhs: A) -> Self {
346        self.value[0] += rhs;
347        self
348    }
349}
350
351impl<F, A> AddAssign for QuinticTrinomialExtensionField<F, A>
352where
353    F: QuinticTrinomialExtendable,
354    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
355{
356    #[inline]
357    fn add_assign(&mut self, rhs: Self) {
358        self.value =
359            <A as ExtensionAlgebra<F, 5, QuinticTrinomial>>::ext_add(&self.value, &rhs.value);
360    }
361}
362
363impl<F, A> AddAssign<A> for QuinticTrinomialExtensionField<F, A>
364where
365    F: QuinticTrinomialExtendable,
366    A: Algebra<F>,
367{
368    #[inline]
369    fn add_assign(&mut self, rhs: A) {
370        self.value[0] += rhs;
371    }
372}
373
374impl<F, A> Sum for QuinticTrinomialExtensionField<F, A>
375where
376    F: QuinticTrinomialExtendable,
377    A: ExtensionAlgebra<F, 5, QuinticTrinomial> + Copy,
378{
379    #[inline]
380    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
381        iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
382    }
383}
384
385impl<F, A> Sub for QuinticTrinomialExtensionField<F, A>
386where
387    F: QuinticTrinomialExtendable,
388    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
389{
390    type Output = Self;
391
392    #[inline]
393    fn sub(self, rhs: Self) -> Self {
394        Self::new(<A as ExtensionAlgebra<F, 5, QuinticTrinomial>>::ext_sub(
395            &self.value,
396            &rhs.value,
397        ))
398    }
399}
400
401impl<F, A> Sub<A> for QuinticTrinomialExtensionField<F, A>
402where
403    F: QuinticTrinomialExtendable,
404    A: Algebra<F>,
405{
406    type Output = Self;
407
408    #[inline]
409    fn sub(self, rhs: A) -> Self {
410        let mut res = self.value;
411        res[0] -= rhs;
412        Self::new(res)
413    }
414}
415
416impl<F, A> SubAssign for QuinticTrinomialExtensionField<F, A>
417where
418    F: QuinticTrinomialExtendable,
419    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
420{
421    #[inline]
422    fn sub_assign(&mut self, rhs: Self) {
423        self.value =
424            <A as ExtensionAlgebra<F, 5, QuinticTrinomial>>::ext_sub(&self.value, &rhs.value);
425    }
426}
427
428impl<F, A> SubAssign<A> for QuinticTrinomialExtensionField<F, A>
429where
430    F: QuinticTrinomialExtendable,
431    A: Algebra<F>,
432{
433    #[inline]
434    fn sub_assign(&mut self, rhs: A) {
435        self.value[0] -= rhs;
436    }
437}
438
439impl<F, A> Mul for QuinticTrinomialExtensionField<F, A>
440where
441    F: QuinticTrinomialExtendable,
442    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
443{
444    type Output = Self;
445
446    #[inline]
447    fn mul(self, rhs: Self) -> Self {
448        let mut res = Self::default();
449        <A as ExtensionAlgebra<F, 5, QuinticTrinomial>>::ext_mul(
450            &self.value,
451            &rhs.value,
452            &mut res.value,
453        );
454        res
455    }
456}
457
458impl<F, A> Mul<A> for QuinticTrinomialExtensionField<F, A>
459where
460    F: QuinticTrinomialExtendable,
461    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
462{
463    type Output = Self;
464
465    #[inline]
466    fn mul(self, rhs: A) -> Self {
467        Self::new(<A as ExtensionAlgebra<F, 5, QuinticTrinomial>>::ext_base_mul(self.value, rhs))
468    }
469}
470
471impl<F, A> MulAssign for QuinticTrinomialExtensionField<F, A>
472where
473    F: QuinticTrinomialExtendable,
474    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
475{
476    #[inline]
477    fn mul_assign(&mut self, rhs: Self) {
478        *self = self.clone() * rhs;
479    }
480}
481
482impl<F, A> MulAssign<A> for QuinticTrinomialExtensionField<F, A>
483where
484    F: QuinticTrinomialExtendable,
485    A: ExtensionAlgebra<F, 5, QuinticTrinomial>,
486{
487    #[inline]
488    fn mul_assign(&mut self, rhs: A) {
489        *self = self.clone() * rhs;
490    }
491}
492
493impl<F, A> Product for QuinticTrinomialExtensionField<F, A>
494where
495    F: QuinticTrinomialExtendable,
496    A: ExtensionAlgebra<F, 5, QuinticTrinomial> + Copy,
497{
498    #[inline]
499    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
500        iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
501    }
502}
503
504impl<F> Div for QuinticTrinomialExtensionField<F>
505where
506    F: QuinticTrinomialExtendable,
507{
508    type Output = Self;
509
510    #[allow(clippy::suspicious_arithmetic_impl)]
511    #[inline]
512    fn div(self, rhs: Self) -> Self::Output {
513        self * rhs.inverse()
514    }
515}
516
517impl<F> DivAssign for QuinticTrinomialExtensionField<F>
518where
519    F: QuinticTrinomialExtendable,
520{
521    #[inline]
522    fn div_assign(&mut self, rhs: Self) {
523        *self = *self / rhs;
524    }
525}
526
527impl<F: QuinticTrinomialExtendable + HasTwoAdicQuinticExtension> TwoAdicField
528    for QuinticTrinomialExtensionField<F>
529{
530    const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
531
532    #[inline]
533    fn two_adic_generator(bits: usize) -> Self {
534        Self::new(F::ext_two_adic_generator(bits))
535    }
536}
537
538/// Multiply two elements in the quintic trinomial extension field (X^5 + X^2 - 1).
539#[inline]
540pub fn trinomial_quintic_mul<R: PrimeCharacteristicRing>(a: &[R; 5], b: &[R; 5], res: &mut [R; 5]) {
541    // Compute convolution coefficients c_k = Σ_{i+j=k} a_i * b_j using dot products
542    let c0 = a[0].dup() * b[0].dup();
543    let c1 = R::dot_product::<2>(&[a[0].dup(), a[1].dup()], &[b[1].dup(), b[0].dup()]);
544    let c2 = R::dot_product::<3>(
545        &[a[0].dup(), a[1].dup(), a[2].dup()],
546        &[b[2].dup(), b[1].dup(), b[0].dup()],
547    );
548    let c3 = R::dot_product::<4>(
549        &[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup()],
550        &[b[3].dup(), b[2].dup(), b[1].dup(), b[0].dup()],
551    );
552    let c4 = R::dot_product::<5>(
553        a,
554        &[b[4].dup(), b[3].dup(), b[2].dup(), b[1].dup(), b[0].dup()],
555    );
556
557    // High-degree coefficients
558    let c5 = R::dot_product::<4>(
559        &[a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
560        &[b[4].dup(), b[3].dup(), b[2].dup(), b[1].dup()],
561    );
562    let c6 = R::dot_product::<3>(
563        &[a[2].dup(), a[3].dup(), a[4].dup()],
564        &[b[4].dup(), b[3].dup(), b[2].dup()],
565    );
566    let c7 = R::dot_product::<2>(&[a[3].dup(), a[4].dup()], &[b[4].dup(), b[3].dup()]);
567    let c8 = a[4].dup() * b[4].dup();
568
569    // Pre-compute c5 - c8 to save an operation
570    let c5_minus_c8 = c5 - c8.dup();
571
572    // Apply reduction: X^5 = 1 - X^2, X^6 = X - X^3, X^7 = X^2 - X^4, X^8 = X^3 + X^2 - 1
573    res[0] = c0 + c5_minus_c8.dup();
574    res[1] = c1 + c6.dup();
575    res[2] = c2 - c5_minus_c8 + c7.dup();
576    res[3] = c3 - c6 + c8;
577    res[4] = c4 - c7;
578}
579
580/// Square an element in the quintic extension field.
581#[inline]
582pub fn quintic_square<R: PrimeCharacteristicRing>(a: &[R; 5], res: &mut [R; 5]) {
583    // Precompute doubled coefficients for cross terms
584    let a0_2 = a[0].double();
585    let a1_2 = a[1].double();
586    let a2_2 = a[2].double();
587    let a3_2 = a[3].double();
588
589    // Compute convolution coefficients using dot products
590    // c0 = a0^2
591    let c0 = a[0].square();
592    // c1 = 2*a0*a1
593    let c1 = a0_2.dup() * a[1].dup();
594    // c2 = 2*a0*a2 + a1^2
595    let c2 = R::dot_product::<2>(&[a0_2.dup(), a[1].dup()], &[a[2].dup(), a[1].dup()]);
596    // c3 = 2*a0*a3 + 2*a1*a2
597    let c3 = R::dot_product::<2>(&[a0_2.dup(), a1_2.dup()], &[a[3].dup(), a[2].dup()]);
598    // c4 = 2*a0*a4 + 2*a1*a3 + a2^2
599    let c4 = R::dot_product::<3>(
600        &[a0_2, a1_2.dup(), a[2].dup()],
601        &[a[4].dup(), a[3].dup(), a[2].dup()],
602    );
603    // c5 = 2*a1*a4 + 2*a2*a3
604    let c5 = R::dot_product::<2>(&[a1_2, a2_2.dup()], &[a[4].dup(), a[3].dup()]);
605    // c6 = 2*a2*a4 + a3^2
606    let c6 = R::dot_product::<2>(&[a2_2, a[3].dup()], &[a[4].dup(), a[3].dup()]);
607    // c7 = 2*a3*a4
608    let c7 = a3_2 * a[4].dup();
609    // c8 = a4^2
610    let c8 = a[4].square();
611
612    // Pre-compute c5 - c8 to save an operation
613    let c5_minus_c8 = c5 - c8.dup();
614
615    // Apply reduction: X^5 = 1 - X^2, X^6 = X - X^3, X^7 = X^2 - X^4, X^8 = X^3 + X^2 - 1
616    res[0] = c0 + c5_minus_c8.dup();
617    res[1] = c1 + c6.dup();
618    res[2] = c2 - c5_minus_c8 + c7.dup();
619    res[3] = c3 - c6 + c8;
620    res[4] = c4 - c7;
621}