1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use cryptix_field::field::montgomery::MontgomeryOps;

use super::fp12::Fp12Element;

/// This is an element in cyclotomic subfield of Fp12.
/// Some arithmetic operations are significantly simplified in this field.
#[derive(Debug, Clone, Copy)]
pub struct Fp12Clotomic(Fp12Element);

impl From<Fp12Clotomic> for Fp12Element {
    fn from(value: Fp12Clotomic) -> Self {
        value.0
    }
}

impl Fp12Clotomic {
    /// # Safety
    /// 
    /// This function is safe only when the element is indeed an element of the cyclotomic subfield
    pub const unsafe fn new_unchecked(value: Fp12Element) -> Self {
        Self(value)
    }

    pub fn clotomic_inv(self) -> Self {
        let mut tmp = self.0;
        tmp.0.1 = -tmp.0.1;
        tmp.1.0 = -tmp.1.0;
        tmp.2.1 = -tmp.2.1;
        Self(tmp)
    }

    pub fn clotomic_sqr(self) -> Self {
        let a = self.0;
        
        let t1 = a.0.mont_sqr();
        let c0 = t1 + t1 + t1;
        let t1 = a.0.conjugate();
        let c0 = c0 - (t1 + t1);

        let t1 = a.1.conjugate();
        let c1 = t1 + t1;
        let t1 = a.2.mont_sqr().mul_s();
        let t2 = t1 + t1 + t1;
        let c1 = c1 + t2;
        
        let t1 = a.1.mont_sqr();
        let c2 = t1 + t1 + t1;
        let t1 = a.2.conjugate();
        let c2 = c2 - (t1 + t1);

        Self(Fp12Element(c0, c1, c2))
    }

    pub fn mont_mul(self, rhs: Self) -> Self {
        Fp12Clotomic(self.0.mont_mul(rhs.0))
    }

    pub fn mont_form(self) -> Self {
        Fp12Clotomic(self.0.mont_form())
    }

    pub fn map_frob(self) -> Self {
        Self(self.0.map_frob())
    }

    pub fn map2_frob(self) -> Self {
        Self(self.0.map2_frob())
    }

    pub fn exp_const(self) -> Self {
        // t = 0x4080000000000001
        let mut r = self.clotomic_sqr().clotomic_sqr(); // 1 * 2 + 1 = 3
        let a8 = r.clotomic_sqr(); // 3 * 2 + 1 = 7 0b100000000

        for _ in 0..8 {
            r = r.clotomic_sqr()
        }  // r^(1000000000000)
        
        r = r.mont_mul(a8);
        for _ in 0..52 {
            r = r.clotomic_sqr()
        }

        r.mont_mul(self)
    }
}