#ifndef H_B_ENGINE
#define H_B_ENGINE
#include "../common.h"
#include "../curve.h"
#include "../fp.h"
#include "../extension_towers/fp2.h"
#include "../extension_towers/fp6_3.h"
#include "../extension_towers/fp12.h"
template <usize N>
using ThreePoint = std::tuple<Fp2<N>, Fp2<N>, Fp2<N>>;
template <usize N>
class Bengine
{
protected:
std::vector<u64> u;
bool u_is_negative;
TwistType twist_type;
WeierstrassCurve<Fp2<N>> const &curve_twist;
public:
Bengine(std::vector<u64> u,
bool u_is_negative,
TwistType twist_type,
WeierstrassCurve<Fp2<N>> const &curve_twist) : u(u), u_is_negative(u_is_negative), twist_type(twist_type), curve_twist(curve_twist) {}
std::optional<Fp12<N>>
pair(std::vector<std::tuple<CurvePoint<Fp<N>>, CurvePoint<Fp2<N>>>> const &points, FieldExtension2over3over2<N> const &context) const
{
if (points.size() == 0)
{
return {};
}
auto res = miller_loop(points, context);
return final_exponentiation(res);
}
protected:
virtual Fp12<N> miller_loop(std::vector<std::tuple<CurvePoint<Fp<N>>, CurvePoint<Fp2<N>>>> const &points, FieldExtension2over3over2<N> const &context) const = 0;
virtual std::vector<ThreePoint<N>> prepare(CurvePoint<Fp2<N>> const &twist_point, FieldExtension2over3over2<N> const &context) const = 0;
virtual std::optional<Fp12<N>> final_exponentiation(Fp12<N> const &f) const = 0;
ThreePoint<N> doubling_step(
CurvePoint<Fp2<N>> &r,
Fp<N> const &two_inv) const
{
auto a = r.x;
a.mul(r.y);
a.mul_by_fp(two_inv);
auto b = r.y;
b.square();
auto c = r.z;
c.square();
auto e = curve_twist.get_b();
auto t0 = c;
t0.mul2();
t0.add(c);
e.mul(t0);
auto f = e;
f.mul2();
f.add(e);
auto g = b;
g.add(f);
g.mul_by_fp(two_inv);
auto h = r.y;
h.add(r.z);
h.square();
auto t1 = b;
t1.add(c);
h.sub(t1);
auto i = e;
i.sub(b);
auto j = r.x;
j.square();
auto e_square = e;
e_square.square();
r.x = b;
r.x.sub(f);
r.x.mul(a);
auto e_square_by_3 = e_square;
e_square_by_3.mul2();
e_square_by_3.add(e_square);
r.y = g;
r.y.square();
r.y.sub(e_square_by_3);
r.z = b;
r.z.mul(h);
auto j_by_three = j;
j_by_three.mul2();
j_by_three.add(j);
h.negate();
switch (twist_type)
{
case M:
return std::tuple(i, j_by_three, h);
case D: return std::tuple(h, j_by_three, i);
}
unreachable("");
}
ThreePoint<N> addition_step(
CurvePoint<Fp2<N>> &r,
CurvePoint<Fp2<N>> const &q) const
{
assert(q.is_normalized());
auto theta = q.y;
theta.mul(r.z);
theta.negate();
theta.add(r.y);
auto lambda = q.x;
lambda.mul(r.z);
lambda.negate();
lambda.add(r.x);
auto c = theta;
c.square();
auto d = lambda;
d.square();
auto e = lambda;
e.mul(d);
auto f = r.z;
f.mul(c);
auto g = r.x;
g.mul(d);
auto h = g;
h.mul2();
h.negate();
h.add(e);
h.add(f);
r.x = lambda;
r.x.mul(h);
auto t0 = g;
t0.sub(h);
t0.mul(theta);
r.y.mul(e);
r.y.negate();
r.y.add(t0);
r.z.mul(e);
auto t1 = lambda;
t1.mul(q.y);
auto j = theta;
j.mul(q.x);
j.sub(t1);
theta.negate();
switch (twist_type)
{
case M:
return std::tuple(j, theta, lambda);
case D: return std::tuple(lambda, theta, j);
}
unreachable("");
}
void for_ell(Fp12<N> &f, usize n, std::vector<CurvePoint<Fp<N>>> const &g1_references, std::vector<std::vector<ThreePoint<N>>> const &prepared_coeffs, std::vector<usize> &pc_indexes) const
{
for (usize j = 0; j < n; j++)
{
auto const p = g1_references[j];
auto const coeffs = prepared_coeffs[j][pc_indexes[j]];
pc_indexes[j]++;
ell(f, coeffs, p);
}
}
void ell(
Fp12<N> &f,
ThreePoint<N> const &coeffs,
CurvePoint<Fp<N>> const &p) const
{
assert(p.is_normalized());
auto c0 = std::get<0>(coeffs);
auto c1 = std::get<1>(coeffs);
auto c2 = std::get<2>(coeffs);
switch (twist_type)
{
case M:
{
c2.mul_by_fp(p.y);
c1.mul_by_fp(p.x);
f.mul_by_014(c0, c1, c2);
break;
}
case D:
{
c0.mul_by_fp(p.y);
c1.mul_by_fp(p.x);
f.mul_by_034(c0, c1, c2);
break;
}
}
}
void exp_by_x(Fp12<N> &f) const
{
f = f.cyclotomic_exp(this->u);
if (u_is_negative)
{
f.conjugate();
}
}
};
#endif