pub fn final_exponentiation_naive(
    f: &FieldElement<Degree12ExtensionField>,
) -> FieldElement<Degree12ExtensionField> {
    // Easy part:
    // Computes f ^ {(p^6 - 1) * (p^2 + 1)}
    let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate().
    let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}).

    // Hard part:
    // Computes f_easy ^ ((p^4 - p^2 + 1) / r)
    // See https://hackmd.io/@Wimet/ry7z1Xj-2#The-Hard-Part, where f_easy is called m.
    // We define different exponentiation of f_easy that we will use later.
    let mx = f_easy.pow(X);
    let mx2 = mx.pow(X);
    let mx3 = mx2.pow(X);
    let mp = frobenius(&f_easy);
    let mp2 = frobenius_square(&f_easy);
    let mp3 = frobenius_cube(&f_easy);
    let mxp = frobenius(&mx); // (m^x)^p
    let mx2p = frobenius(&mx2); // (m^{x^2})^p
    let mx3p = frobenius(&mx3); // (m^{x^3})^p
    let mx2p2 = frobenius_square(&mx2); // (m^{x^2})^p^2

    let y0 = mp * mp2 * mp3;
    let y1 = f_easy.conjugate();
    let y2 = mx2p2;
    let y3 = mxp.conjugate();
    let y4 = (mx * mx2p).conjugate();
    let y5 = mx2.conjugate();
    let y6 = (mx3 * mx3p).conjugate();

    y0 * y1.square()
        * y2.pow(6usize)
        * y3.pow(12usize)
        * y4.pow(18usize)
        * y5.pow(30usize)
        * y6.pow(36usize)
}
Computes f ^ {(p^12 - 1) / r}
using that (p^12 - 1)/r = (p^6 - 1) * (p^2 + 1) * (p^4 - p^2 + 1)/r.
Algorithm taken from https://hackmd.io/@Wimet/ry7z1Xj-2#Final-Exponentiation.
crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/pairing.rs