/*############################################################################
  # Copyright 2016 Intel Corporation
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  ############################################################################*/

/*!
 * \file
 * \brief FfElement unit tests.
 */

#include <cstring>
#include <limits>
#include <algorithm>

#include "gtest/gtest.h"

#include "epid/common-testhelper/errors-testhelper.h"
#include "epid/common-testhelper/prng-testhelper.h"
#include "epid/common-testhelper/octstr-testhelper.h"
#include "epid/common-testhelper/finite_field_wrapper-testhelper.h"
#include "epid/common-testhelper/ffelement_wrapper-testhelper.h"
#include "epid/common-testhelper/bignum_wrapper-testhelper.h"

extern "C" {
#include "epid/common/math/finitefield.h"
}

#include "epid/common/types.h"

/// compares FqElemStr values
bool operator==(FqElemStr const& lhs, FqElemStr const& rhs) {
  return 0 == std::memcmp(&lhs, &rhs, sizeof(lhs));
}
bool operator==(Fq2ElemStr const& lhs, Fq2ElemStr const& rhs) {
  return 0 == std::memcmp(&lhs, &rhs, sizeof(lhs));
}
bool operator==(Fq6ElemStr const& lhs, Fq6ElemStr const& rhs) {
  return 0 == std::memcmp(&lhs, &rhs, sizeof(lhs));
}
/// compares Fq12ElemStr values
bool operator==(Fq12ElemStr const& lhs, Fq12ElemStr const& rhs) {
  return 0 == std::memcmp(&lhs, &rhs, sizeof(lhs));
}
/// compares FpElemStr values
bool operator==(FpElemStr const& lhs, FpElemStr const& rhs) {
  return 0 == std::memcmp(&lhs, &rhs, sizeof(lhs));
}

bool operator==(FfElementObj const& lhs, FfElementObj const& rhs) {
  auto lhs_data = lhs.data();
  auto rhs_data = rhs.data();
  return lhs_data.size() == rhs_data.size() &&
         std::equal(lhs_data.begin(), lhs_data.end(), lhs_data.begin());
}

namespace {

class FfElementTest : public ::testing::Test {
 public:
  virtual void SetUp() {
    FqElemStr beta_str = {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xCD, 0x46,
                           0xE5, 0xF2, 0x5E, 0xEE, 0x71, 0xA4, 0x9F, 0x0C, 0xDC,
                           0x65, 0xFB, 0x12, 0x98, 0x0A, 0x82, 0xD3, 0x29, 0x2D,
                           0xDB, 0xAE, 0xD3, 0x30, 0x12}};
    Fq2ElemStr xi_str = {
        {{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}},
         {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}}}};
    Fq6ElemStr v_str = {
        {{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
           {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
         {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
           {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
         {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
           {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}}};

    fq = FiniteFieldObj(epid20_q);

    // construct Fq^2 finite field
    FfElementObj neg_beta(&fq);
    THROW_ON_EPIDERR(FfNeg(fq, FfElementObj(&fq, beta_str), neg_beta));
    fq2 = FiniteFieldObj(fq, neg_beta, 2);

    // construct Fq^6 finite field
    FfElementObj neg_xi(&fq2);
    THROW_ON_EPIDERR(FfNeg(fq2, FfElementObj(&fq2, xi_str), neg_xi));
    fq6 = FiniteFieldObj(fq2, neg_xi, 3);

    // construct Fq^12 finite field
    FfElementObj neg_v(&fq6);
    THROW_ON_EPIDERR(FfNeg(fq6, FfElementObj(&fq6, v_str), neg_v));
    fq12 = FiniteFieldObj(fq6, neg_v, 2);

    fq_zero = FfElementObj(&fq, &bn_zero_data, sizeof((bn_zero_data)));
    fq_one = FfElementObj(&fq, &bn_one_data, sizeof(bn_one_data));
    fq_a = FfElementObj(&fq, &bn_a_data, sizeof(bn_a_data));
    fq_b = FfElementObj(&fq, &bn_b_data, sizeof(bn_b_data));
    fq_r = FfElementObj(&fq);

    fp = FiniteFieldObj(epid20_p);
    fp_elem = FfElementObj(&fp, fp_p_data);

    exponent_zero = BigNumObj(bn_zero_data);
    exponent_one = BigNumObj(bn_one_data);
    exponent_b = BigNumObj(bn_a_data);

    fq2_a = FfElementObj(&fq2);
    fq2_b = FfElementObj(&fq2);
    fq2_r = FfElementObj(&fq2);

    fq12_a = FfElementObj(&fq12);
    fq12_b = FfElementObj(&fq12);
    fq12_r = FfElementObj(&fq12);
  }

  FiniteFieldObj fq;
  FiniteFieldObj fq2;
  FiniteFieldObj fq6;
  FiniteFieldObj fq12;

  FfElementObj fq_zero;
  FfElementObj fq_one;
  FfElementObj fq_a;
  FfElementObj fq_b;
  FfElementObj fq_r;

  FfElementObj fq2_a;
  FfElementObj fq2_b;
  FfElementObj fq2_r;

  FfElementObj fq12_a;
  FfElementObj fq12_b;
  FfElementObj fq12_r;

  FiniteFieldObj fp;
  FfElementObj fp_elem;

  BigNumObj exponent_zero;
  BigNumObj exponent_one;
  BigNumObj exponent_b;

  /// Intel(R) EPID 2.0 parameter q
  static const BigNumStr epid20_q;
  static const BigNumStr epid20_p;
  static const BigNumStr bn_zero_data;
  static const FqElemStr fq_zero_data;
  static const BigNumStr bn_one_data;
  static const FqElemStr fq_one_data;
  static const BigNumStr bn_two_data;
  static const BigNumStr bn_a_data;
  static const FqElemStr fq_a_data;
  static const BigNumStr bn_b_data;
  static const FqElemStr fq_b_data;
  static const FqElemStr fq_mul_ab_data;
  static const FqElemStr fq_inv_a_data;
  static const FqElemStr fq_neg_a_data;
  static const FqElemStr fq_exp_ab_data;
  static const FqElemStr fq_sum_ab_data;
  static const FpElemStr fp_p_data;
  static const uint8_t sha_msg[3];  /// predefined message for tests "abc"
  static const FqElemStr fq_r_sha256;
  static const FqElemStr fq_r_sha384;
  static const FqElemStr fq_r_sha512;
};

/// Intel(R) EPID 2.0 parameter q
const BigNumStr FfElementTest::epid20_q = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xCD, 0x46, 0xE5, 0xF2,
    0x5E, 0xEE, 0x71, 0xA4, 0x9F, 0x0C, 0xDC, 0x65, 0xFB, 0x12, 0x98,
    0x0A, 0x82, 0xD3, 0x29, 0x2D, 0xDB, 0xAE, 0xD3, 0x30, 0x13};

const BigNumStr FfElementTest::epid20_p = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xCD, 0x46, 0xE5, 0xF2,
    0x5E, 0xEE, 0x71, 0xA4, 0x9E, 0x0C, 0xDC, 0x65, 0xFB, 0x12, 0x99,
    0x92, 0x1A, 0xF6, 0x2D, 0x53, 0x6C, 0xD1, 0x0B, 0x50, 0x0D};

const BigNumStr FfElementTest::bn_zero_data = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const FqElemStr FfElementTest::fq_zero_data = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const BigNumStr FfElementTest::bn_one_data = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};

const BigNumStr FfElementTest::bn_two_data = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02};

const FqElemStr FfElementTest::fq_one_data = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};

const BigNumStr FfElementTest::bn_a_data = {
    0x12, 0xA6, 0x5B, 0xD6, 0x91, 0x8D, 0x50, 0xA7, 0x66, 0xEB, 0x7D,
    0x52, 0xE3, 0x40, 0x17, 0x60, 0x7F, 0xDF, 0x6C, 0xA1, 0x2C, 0x1A,
    0x37, 0xE0, 0x92, 0xC0, 0xF7, 0xB9, 0x76, 0xAB, 0xB1, 0x8A};

const FqElemStr FfElementTest::fq_a_data = {
    0x12, 0xA6, 0x5B, 0xD6, 0x91, 0x8D, 0x50, 0xA7, 0x66, 0xEB, 0x7D,
    0x52, 0xE3, 0x40, 0x17, 0x60, 0x7F, 0xDF, 0x6C, 0xA1, 0x2C, 0x1A,
    0x37, 0xE0, 0x92, 0xC0, 0xF7, 0xB9, 0x76, 0xAB, 0xB1, 0x8A};

const BigNumStr FfElementTest::bn_b_data = {
    0xE6, 0x65, 0x23, 0x9B, 0xD4, 0x07, 0x16, 0x83, 0x38, 0x23, 0xB2,
    0x67, 0x57, 0xEB, 0x0F, 0x23, 0x3A, 0xF4, 0x8E, 0xDA, 0x71, 0x5E,
    0xD9, 0x98, 0x63, 0x98, 0x2B, 0xBC, 0x78, 0xD1, 0x94, 0xF2};

const FqElemStr FfElementTest::fq_b_data = {
    0xE6, 0x65, 0x23, 0x9B, 0xD4, 0x07, 0x16, 0x83, 0x38, 0x23, 0xB2,
    0x67, 0x57, 0xEB, 0x0F, 0x23, 0x3A, 0xF4, 0x8E, 0xDA, 0x71, 0x5E,
    0xD9, 0x98, 0x63, 0x98, 0x2B, 0xBC, 0x78, 0xD1, 0x94, 0xF2};

const FqElemStr FfElementTest::fq_mul_ab_data = {
    0xE4, 0xAB, 0xE3, 0xE4, 0x08, 0xC3, 0x8A, 0x00, 0x78, 0x50, 0x01,
    0xA9, 0x4A, 0xFC, 0x43, 0x15, 0xA2, 0x99, 0x51, 0x20, 0x6B, 0x9C,
    0x00, 0xCF, 0x82, 0x88, 0xBD, 0x39, 0x0B, 0xA2, 0x0B, 0x58};

const FqElemStr FfElementTest::fq_inv_a_data = {
    0x15, 0x17, 0x1A, 0x1E, 0x93, 0x71, 0x1B, 0x39, 0xC5, 0x97, 0xEF,
    0x78, 0xA5, 0x51, 0x34, 0x62, 0x44, 0xAD, 0x8D, 0x51, 0xDF, 0x90,
    0x7C, 0x6F, 0x56, 0xB6, 0xAB, 0x63, 0x5A, 0x68, 0x6D, 0xF6};

const FqElemStr FfElementTest::fq_neg_a_data = {
    0xED, 0x59, 0xA4, 0x29, 0x6E, 0x6F, 0xA0, 0x25, 0xDF, 0xFA, 0x75,
    0x0C, 0x0B, 0x31, 0x8D, 0x3E, 0x8C, 0xFC, 0xF9, 0x59, 0xE6, 0x7D,
    0xD2, 0xA2, 0x40, 0x68, 0x36, 0x22, 0x38, 0x27, 0x7E, 0x89};

const FqElemStr FfElementTest::fq_exp_ab_data = {
    0x29, 0x65, 0x68, 0x0E, 0x56, 0xB9, 0x8F, 0xBA, 0xA6, 0xEA, 0x8F,
    0xE1, 0x13, 0x44, 0x3B, 0x12, 0x5C, 0xB9, 0xF8, 0x76, 0x42, 0x12,
    0xCB, 0xB2, 0xED, 0xC0, 0x23, 0xA3, 0x5C, 0xAD, 0x38, 0xD1};

const FqElemStr FfElementTest::fq_sum_ab_data = {
    0xF9, 0x0B, 0x7F, 0x72, 0x65, 0x94, 0x67, 0x2A, 0x9F, 0x0F, 0x2F,
    0xBA, 0x3B, 0x2B, 0x26, 0x83, 0xBA, 0xD3, 0xFB, 0x7B, 0x9D, 0x79,
    0x11, 0x78, 0xF6, 0x59, 0x23, 0x75, 0xEF, 0x7D, 0x46, 0x7C};

const FpElemStr FfElementTest::fp_p_data = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xCD, 0x46, 0xE5, 0xF2,
    0x5E, 0xEE, 0x71, 0xA4, 0x9E, 0x0C, 0xDC, 0x65, 0xFB, 0x12, 0x99,
    0x92, 0x1A, 0xF6, 0x2D, 0x53, 0x6C, 0xD1, 0x0B, 0x50, 0x00};
const uint8_t FfElementTest::sha_msg[] = {'a', 'b', 'c'};
const FqElemStr FfElementTest::fq_r_sha256 = {
    0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, 0x41, 0x41, 0x40,
    0xDE, 0x5D, 0xAE, 0x22, 0x23, 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17,
    0x7A, 0x9C, 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD,
};
const FqElemStr FfElementTest::fq_r_sha384 = {
    0x10, 0xAC, 0xE4, 0xE8, 0x8F, 0xBB, 0x96, 0xBF, 0x53, 0xB6, 0xAF,
    0xE4, 0x4E, 0x95, 0xDE, 0xA5, 0x98, 0x8F, 0x10, 0xBF, 0x59, 0xC4,
    0x11, 0xFE, 0x1F, 0x7A, 0xA6, 0x1E, 0x57, 0x3E, 0xE3, 0x3C,
};
const FqElemStr FfElementTest::fq_r_sha512 = {
    0x14, 0xB4, 0x28, 0x89, 0xD2, 0x86, 0xA4, 0x79, 0xE3, 0x5B, 0xC4,
    0x50, 0xD1, 0x3B, 0xF2, 0x35, 0xF7, 0x97, 0x91, 0x05, 0x3D, 0x64,
    0xC0, 0x06, 0x45, 0x9F, 0xEC, 0xD8, 0xDB, 0x53, 0x3E, 0xC3,
};

///////////////////////////////////////////////////////////////////////
// NewFfElement

TEST_F(FfElementTest, NewFailsGivenNullPointer) {
  FfElement* ff_elem = nullptr;
  EXPECT_EQ(kEpidBadArgErr, NewFfElement(nullptr, &ff_elem));
  EXPECT_EQ(kEpidBadArgErr, NewFfElement(this->fq, nullptr));
  DeleteFfElement(&ff_elem);
}

TEST_F(FfElementTest, NewSucceedsGiven256BitFiniteField) {
  FfElement* ff_elem = nullptr;
  EXPECT_EQ(kEpidNoErr, NewFfElement(this->fq, &ff_elem));
  DeleteFfElement(&ff_elem);
}

TEST_F(FfElementTest, Default256BitElementIsZero) {
  FfElement* ff_elem = nullptr;
  THROW_ON_EPIDERR(NewFfElement(this->fq, &ff_elem));
  FqElemStr ff_elem_str;
  EpidStatus sts =
      WriteFfElement(this->fq, ff_elem, &ff_elem_str, sizeof(ff_elem_str));
  DeleteFfElement(&ff_elem);
  THROW_ON_EPIDERR(sts);

  FqElemStr fq_zero_str = {0};
  EXPECT_EQ(fq_zero_str, ff_elem_str);
}

///////////////////////////////////////////////////////////////////////
// DeleteFfElement

TEST_F(FfElementTest, DeleteNullsPointer) {
  FfElement* ff_elem = nullptr;
  EpidStatus sts = NewFfElement(this->fq, &ff_elem);
  DeleteFfElement(&ff_elem);
  THROW_ON_EPIDERR(sts);
  EXPECT_EQ(nullptr, ff_elem);
}

TEST_F(FfElementTest, DeleteWorksGivenNullPointer) {
  EXPECT_NO_THROW(DeleteFfElement(nullptr));
  FfElement* ff_elem = nullptr;
  EXPECT_NO_THROW(DeleteFfElement(&ff_elem));
}

///////////////////////////////////////////////////////////////////////
// ReadFfElement

TEST_F(FfElementTest, ReadFailsGivenArgumentsMismatch) {
  uint8_t buf[sizeof(Fq12ElemStr)] = {0};
  EXPECT_EQ(kEpidBadArgErr,
            ReadFfElement(this->fq12, buf, sizeof(Fq12ElemStr), this->fq_a));
  EXPECT_EQ(kEpidBadArgErr,
            ReadFfElement(this->fq, buf, sizeof(FqElemStr), this->fq12_a));
}

TEST_F(FfElementTest, ReadFailsGivenNullPointer) {
  FqElemStr ff_elem_str;
  EXPECT_EQ(kEpidBadArgErr, ReadFfElement(nullptr, &ff_elem_str,
                                          sizeof(ff_elem_str), this->fq_a));
  EXPECT_EQ(kEpidBadArgErr,
            ReadFfElement(this->fq, nullptr, sizeof(ff_elem_str), this->fq_a));
  EXPECT_EQ(kEpidBadArgErr, ReadFfElement(this->fq, &ff_elem_str,
                                          sizeof(ff_elem_str), nullptr));
}

TEST_F(FfElementTest, ReadFailsGivenInvalidBufferSize) {
  FqElemStr ff_elem_str;
  EXPECT_EQ(kEpidBadArgErr,
            ReadFfElement(this->fq, &ff_elem_str, 0, this->fq_a));
  EXPECT_EQ(kEpidBadArgErr,
            ReadFfElement(this->fq, &ff_elem_str,
                          std::numeric_limits<size_t>::max(), this->fq_a));
}

TEST_F(FfElementTest, ReadFailsGivenElementDoesNotBelongToFF) {
  // q does not belong to Fq
  FqElemStr ff_elem_str = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xCD,
                           0x46, 0xE5, 0xF2, 0x5E, 0xEE, 0x71, 0xA4, 0x9F,
                           0x0C, 0xDC, 0x65, 0xFB, 0x12, 0x98, 0x0A, 0x82,
                           0xD3, 0x29, 0x2D, 0xDB, 0xAE, 0xD3, 0x30, 0x13};
  EXPECT_EQ(kEpidBadArgErr, ReadFfElement(this->fq, &ff_elem_str,
                                          sizeof(ff_elem_str), this->fq_a));
}

TEST_F(FfElementTest, Read256BitElementCorrectly) {
  // q-1 is valid element of Fq
  FqElemStr ff_elem_str = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xCD,
                           0x46, 0xE5, 0xF2, 0x5E, 0xEE, 0x71, 0xA4, 0x9F,
                           0x0C, 0xDC, 0x65, 0xFB, 0x12, 0x98, 0x0A, 0x82,
                           0xD3, 0x29, 0x2D, 0xDB, 0xAE, 0xD3, 0x30, 0x12};
  ASSERT_EQ(kEpidNoErr, ReadFfElement(this->fq, &ff_elem_str,
                                      sizeof(ff_elem_str), this->fq_a));

  FqElemStr buf;
  EXPECT_EQ(kEpidNoErr,
            WriteFfElement(this->fq, this->fq_a, &buf, sizeof(buf)));
  EXPECT_EQ(ff_elem_str, buf);
}

///////////////////////////////////////////////////////////////////////
// FfIsEqual

TEST_F(FfElementTest, FfIsEqualFailsGivenArgumentsMismatch) {
  bool result;
  EXPECT_EQ(kEpidBadArgErr,
            FfIsEqual(this->fq12, this->fq_a, this->fq_b, &result));
  EXPECT_EQ(kEpidBadArgErr,
            FfIsEqual(this->fq, this->fq12_a, this->fq_b, &result));
  EXPECT_EQ(kEpidBadArgErr,
            FfIsEqual(this->fq, this->fq_a, this->fq12_b, &result));
}

TEST_F(FfElementTest, FfIsEqualFailsGivenNullPointer) {
  bool result;
  EXPECT_EQ(kEpidBadArgErr,
            FfIsEqual(nullptr, this->fq_a, this->fq_a, &result));
  EXPECT_EQ(kEpidBadArgErr, FfIsEqual(this->fq, nullptr, this->fq_a, &result));
  EXPECT_EQ(kEpidBadArgErr, FfIsEqual(this->fq, this->fq_a, nullptr, &result));
  EXPECT_EQ(kEpidBadArgErr,
            FfIsEqual(this->fq, this->fq_a, this->fq_a, nullptr));
}

TEST_F(FfElementTest, FfIsEqualCanCompareElementWithItself) {
  bool result;
  EXPECT_EQ(kEpidNoErr, FfIsEqual(this->fq, this->fq_a, this->fq_a, &result));
  EXPECT_TRUE(result);
}

TEST_F(FfElementTest, Different256BitFiniteFieldElementsAreNotEqual) {
  bool result;
  EXPECT_EQ(kEpidNoErr, FfIsEqual(this->fq, this->fq_a, this->fq_b, &result));
  EXPECT_FALSE(result);
}

TEST_F(FfElementTest, Same256BitFiniteFieldElementsAreEqual) {
  THROW_ON_EPIDERR(ReadFfElement(this->fq, &(this->bn_a_data),
                                 sizeof(this->bn_a_data), this->fq_b));
  bool result;
  EXPECT_EQ(kEpidNoErr, FfIsEqual(this->fq, this->fq_a, this->fq_b, &result));
  EXPECT_TRUE(result);
}

TEST_F(FfElementTest, DifferentFq12ElementsAreNotEqual) {
  const Fq12ElemStr a_str = {
      0xba, 0x10, 0x1f, 0xf6, 0x46, 0x8b, 0xe9, 0x32, 0x4f, 0xc0, 0xa5, 0x01,
      0xad, 0x5e, 0xe2, 0x31, 0x16, 0x29, 0x96, 0xed, 0xa7, 0xde, 0x4c, 0xe1,
      0xd2, 0x8d, 0x33, 0xca, 0x50, 0xab, 0x7b, 0xc6, 0x15, 0xeb, 0x79, 0xf4,
      0xeb, 0xde, 0x30, 0xb6, 0xc4, 0x07, 0x7c, 0x42, 0xcb, 0x04, 0x54, 0xf2,
      0x1f, 0x4d, 0x1f, 0xc0, 0xdf, 0xa2, 0x2b, 0x9e, 0x34, 0xc4, 0x4c, 0x84,
      0x14, 0xd3, 0x62, 0x07, 0xf1, 0x8b, 0x84, 0xd1, 0x46, 0x57, 0xb6, 0xe7,
      0x80, 0xe1, 0x46, 0x49, 0x1c, 0x0d, 0xef, 0x81, 0x31, 0xb0, 0xbe, 0x8c,
      0xb9, 0x08, 0xd0, 0xd3, 0xc4, 0x56, 0xca, 0xad, 0xf9, 0x1d, 0x75, 0x19,
      0x3f, 0xee, 0x7c, 0x43, 0xc1, 0xfa, 0x4e, 0x50, 0xb7, 0x19, 0x01, 0x00,
      0x6f, 0xd5, 0x16, 0xb6, 0xf4, 0x85, 0xe0, 0xeb, 0x2e, 0x5f, 0x0a, 0x7e,
      0xf8, 0xac, 0xbc, 0x05, 0xec, 0x73, 0xb5, 0x57, 0xe3, 0xb3, 0x18, 0x29,
      0xbb, 0xef, 0x86, 0x50, 0x87, 0xcf, 0x70, 0xba, 0x13, 0x8b, 0xb1, 0xb6,
      0x2d, 0x6f, 0x65, 0x3d, 0xa1, 0x0b, 0xe3, 0x92, 0xc5, 0x72, 0x86, 0x6a,
      0xb3, 0xeb, 0xe0, 0xe5, 0xda, 0x0e, 0x57, 0x87, 0xd5, 0xa9, 0x61, 0xa5,
      0x1e, 0xcb, 0x04, 0x86, 0xcd, 0xc3, 0x18, 0x2a, 0x36, 0xa0, 0x81, 0x73,
      0xe7, 0x13, 0x87, 0x80, 0x8d, 0x1a, 0xfe, 0x6e, 0x4b, 0xa3, 0x13, 0x03,
      0x66, 0x9e, 0x80, 0x4d, 0x8a, 0xaa, 0x00, 0x95, 0x72, 0xce, 0xbb, 0x51,
      0xe8, 0x01, 0x09, 0x41, 0xd3, 0x63, 0x28, 0x05, 0xa4, 0xbe, 0xd6, 0x41,
      0xa6, 0x2f, 0x5f, 0xbf, 0x0b, 0x13, 0xb4, 0x54, 0x5b, 0x50, 0x65, 0xdc,
      0x6f, 0x29, 0xd6, 0xda, 0xbf, 0xc2, 0x06, 0xea, 0x3b, 0xb2, 0xf1, 0xd4,
      0x26, 0x5c, 0x92, 0x6b, 0x95, 0x6d, 0x88, 0xab, 0x8f, 0xc6, 0x9d, 0x31,
      0xe4, 0x9b, 0x71, 0x49, 0xe0, 0xce, 0x97, 0x8f, 0xc9, 0x9f, 0xbc, 0xa8,
      0x4a, 0xc6, 0xaa, 0x4a, 0xc8, 0x0d, 0x2a, 0x60, 0x1a, 0x43, 0x40, 0x03,
      0xb3, 0x53, 0x30, 0x98, 0x1f, 0x3f, 0xdf, 0x5c, 0x0f, 0xf0, 0x84, 0x8e,
      0x5a, 0x5d, 0x41, 0xd2, 0x47, 0x78, 0x6d, 0x9f, 0x89, 0xce, 0xf5, 0x8e,
      0xb6, 0x54, 0xa2, 0x26, 0xe5, 0x40, 0x39, 0x5c, 0x59, 0x08, 0xb3, 0xda,
      0xf5, 0xf8, 0xa0, 0x18, 0x33, 0x57, 0xd1, 0x72, 0xbb, 0xba, 0x6c, 0xed,
      0xe8, 0xa0, 0x5e, 0xc8, 0x81, 0xc5, 0xac, 0x15, 0x1b, 0xd0, 0xe6, 0xc8,
      0x92, 0xf9, 0x43, 0x03, 0x5a, 0x00, 0x42, 0xe3, 0x49, 0xa5, 0xf7, 0x19,
      0x78, 0x8a, 0x39, 0x89, 0x32, 0xae, 0xbf, 0x4d, 0x4b, 0xb3, 0x33, 0x76,
      0x16, 0xfd, 0x0b, 0xfe, 0x42, 0x1e, 0x17, 0x37, 0x2a, 0x04, 0xea, 0x26,
      0xba, 0x6e, 0x2c, 0x36, 0xaf, 0x35, 0x1b, 0x75, 0x6d, 0x17, 0xdc, 0x8e,
  };
  const Fq12ElemStr b_str = {
      0x41, 0xfb, 0xbe, 0xd2, 0x20, 0x95, 0xe5, 0xba, 0x87, 0x54, 0x23, 0xcc,
      0x04, 0x97, 0x38, 0x4b, 0x69, 0x28, 0x18, 0x6d, 0xae, 0x19, 0xe3, 0x3d,
      0xfe, 0x39, 0xe2, 0x1c, 0xc2, 0x53, 0x17, 0xf6, 0xea, 0xa3, 0x0f, 0x62,
      0x6e, 0xbf, 0x6f, 0x8f, 0xbc, 0xfa, 0x6e, 0x07, 0xd3, 0xd3, 0xae, 0x5c,
      0xaa, 0xb5, 0x9c, 0xd7, 0xb8, 0x5b, 0x1b, 0x69, 0x52, 0x93, 0xbf, 0xdb,
      0x87, 0x5c, 0x19, 0x07, 0x1b, 0x03, 0xe1, 0x79, 0x3c, 0x10, 0x0b, 0x65,
      0xbf, 0xd7, 0xf3, 0x45, 0x1a, 0xd4, 0xdd, 0xb8, 0x67, 0x1a, 0x8f, 0x41,
      0x1a, 0xd4, 0x04, 0x3c, 0xd2, 0xa4, 0x3b, 0x47, 0xfe, 0xbd, 0xea, 0x87,
      0xdf, 0x7f, 0x94, 0x9c, 0xb8, 0x65, 0x54, 0x8d, 0xd7, 0x0b, 0x33, 0x26,
      0x25, 0x89, 0xd4, 0x60, 0x0c, 0x89, 0x5f, 0x2c, 0x00, 0x7f, 0xaf, 0x89,
      0x35, 0xe5, 0xb9, 0x23, 0x7f, 0x79, 0x1a, 0x86, 0xce, 0x01, 0x4f, 0x24,
      0x3c, 0x3a, 0xc3, 0x61, 0x5b, 0xd9, 0x2d, 0x87, 0x94, 0xa3, 0xd2, 0x8b,
      0x57, 0xc4, 0x07, 0xf8, 0x29, 0x5e, 0x09, 0x23, 0xca, 0xe9, 0x85, 0xa1,
      0xdb, 0xf2, 0x0c, 0x8f, 0xc2, 0xfb, 0x9a, 0xcc, 0xd9, 0x82, 0x63, 0x28,
      0x83, 0xb7, 0x4f, 0x99, 0x3a, 0x19, 0x11, 0xb0, 0xb4, 0xe7, 0x73, 0x94,
      0x35, 0x19, 0x6b, 0xc2, 0x5d, 0x2e, 0x8c, 0x33, 0xff, 0x13, 0xcd, 0xbe,
      0x29, 0x43, 0xf6, 0xa0, 0xe9, 0xdc, 0x27, 0x27, 0x9b, 0x0a, 0x33, 0xbc,
      0x85, 0x6a, 0xbf, 0x8b, 0xc5, 0x89, 0x10, 0x92, 0xae, 0x73, 0xc9, 0x32,
      0x46, 0x8d, 0x24, 0x1f, 0x32, 0x96, 0xed, 0x0b, 0x44, 0x8c, 0x6d, 0xf6,
      0x1a, 0x2f, 0xca, 0x18, 0x8e, 0x84, 0xff, 0x5f, 0x5f, 0xaf, 0xc0, 0x52,
      0x35, 0xa1, 0xb3, 0xa0, 0xde, 0xee, 0x97, 0xbf, 0xb8, 0xfa, 0x8b, 0x9e,
      0x00, 0x13, 0xe7, 0x26, 0xfb, 0x78, 0xa1, 0x6d, 0x41, 0x92, 0xa6, 0xc6,
      0x7f, 0xad, 0xb7, 0xfc, 0x4f, 0x5f, 0x48, 0x94, 0x7d, 0xb7, 0xf5, 0xcc,
      0xb5, 0x7e, 0x08, 0xcd, 0xbf, 0x58, 0xc9, 0x30, 0xeb, 0xe4, 0xa0, 0xbf,
      0x0c, 0x6a, 0x89, 0x18, 0x97, 0x37, 0x54, 0x76, 0x79, 0x3e, 0x8e, 0xa6,
      0x5b, 0x77, 0xce, 0x4a, 0xcb, 0xd9, 0x8b, 0x7b, 0x32, 0x1a, 0x30, 0x40,
      0x49, 0xff, 0x84, 0xe7, 0xbe, 0xd8, 0xcf, 0xfd, 0x2d, 0x23, 0x54, 0xb5,
      0x2e, 0x29, 0xe9, 0xc7, 0x62, 0x1b, 0x03, 0x34, 0x3a, 0xeb, 0xc9, 0x69,
      0x38, 0x9f, 0xd1, 0x2b, 0xda, 0x8b, 0x07, 0x58, 0x30, 0xf8, 0x38, 0x08,
      0x09, 0x07, 0xca, 0x5b, 0x3a, 0x13, 0xed, 0xf4, 0x5d, 0xc9, 0x23, 0x66,
      0x85, 0x8c, 0x6a, 0x7d, 0xe9, 0x11, 0x17, 0xe6, 0x43, 0x2f, 0x12, 0xc5,
      0x07, 0x63, 0x84, 0x98, 0xa0, 0x1c, 0xdc, 0xf3, 0x91, 0x91, 0x11, 0x55,
  };

  THROW_ON_EPIDERR(
      ReadFfElement(this->fq12, &(a_str), sizeof(a_str), this->fq12_a));
  THROW_ON_EPIDERR(
      ReadFfElement(this->fq12, &(b_str), sizeof(b_str), this->fq12_b));
  bool result;
  EXPECT_EQ(kEpidNoErr,
            FfIsEqual(this->fq12, this->fq12_a, this->fq12_b, &result));
  EXPECT_FALSE(result);
}

TEST_F(FfElementTest, SameFq12ElementsAreEqual) {
  const Fq12ElemStr a_str = {
      0xba, 0x10, 0x1f, 0xf6, 0x46, 0x8b, 0xe9, 0x32, 0x4f, 0xc0, 0xa5, 0x01,
      0xad, 0x5e, 0xe2, 0x31, 0x16, 0x29, 0x96, 0xed, 0xa7, 0xde, 0x4c, 0xe1,
      0xd2, 0x8d, 0x33, 0xca, 0x50, 0xab, 0x7b, 0xc6, 0x15, 0xeb, 0x79, 0xf4,
      0xeb, 0xde, 0x30, 0xb6, 0xc4, 0x07, 0x7c, 0x42, 0xcb, 0x04, 0x54, 0xf2,
      0x1f, 0x4d, 0x1f, 0xc0, 0xdf, 0xa2, 0x2b, 0x9e, 0x34, 0xc4, 0x4c, 0x84,
      0x14, 0xd3, 0x62, 0x07, 0xf1, 0x8b, 0x84, 0xd1, 0x46, 0x57, 0xb6, 0xe7,
      0x80, 0xe1, 0x46, 0x49, 0x1c, 0x0d, 0xef, 0x81, 0x31, 0xb0, 0xbe, 0x8c,
      0xb9, 0x08, 0xd0, 0xd3, 0xc4, 0x56, 0xca, 0xad, 0xf9, 0x1d, 0x75, 0x19,
      0x3f, 0xee, 0x7c, 0x43, 0xc1, 0xfa, 0x4e, 0x50, 0xb7, 0x19, 0x01, 0x00,
      0x6f, 0xd5, 0x16, 0xb6, 0xf4, 0x85, 0xe0, 0xeb, 0x2e, 0x5f, 0x0a, 0x7e,
      0xf8, 0xac, 0xbc, 0x05, 0xec, 0x73, 0xb5, 0x57, 0xe3, 0xb3, 0x18, 0x29,
      0xbb, 0xef, 0x86, 0x50, 0x87, 0xcf, 0x70, 0xba, 0x13, 0x8b, 0xb1, 0xb6,
      0x2d, 0x6f, 0x65, 0x3d, 0xa1, 0x0b, 0xe3, 0x92, 0xc5, 0x72, 0x86, 0x6a,
      0xb3, 0xeb, 0xe0, 0xe5, 0xda, 0x0e, 0x57, 0x87, 0xd5, 0xa9, 0x61, 0xa5,
      0x1e, 0xcb, 0x04, 0x86, 0xcd, 0xc3, 0x18, 0x2a, 0x36, 0xa0, 0x81, 0x73,
      0xe7, 0x13, 0x87, 0x80, 0x8d, 0x1a, 0xfe, 0x6e, 0x4b, 0xa3, 0x13, 0x03,
      0x66, 0x9e, 0x80, 0x4d, 0x8a, 0xaa, 0x00, 0x95, 0x72, 0xce, 0xbb, 0x51,
      0xe8, 0x01, 0x09, 0x41, 0xd3, 0x63, 0x28, 0x05, 0xa4, 0xbe, 0xd6, 0x41,
      0xa6, 0x2f, 0x5f, 0xbf, 0x0b, 0x13, 0xb4, 0x54, 0x5b, 0x50, 0x65, 0xdc,
      0x6f, 0x29, 0xd6, 0xda, 0xbf, 0xc2, 0x06, 0xea, 0x3b, 0xb2, 0xf1, 0xd4,
      0x26, 0x5c, 0x92, 0x6b, 0x95, 0x6d, 0x88, 0xab, 0x8f, 0xc6, 0x9d, 0x31,
      0xe4, 0x9b, 0x71, 0x49, 0xe0, 0xce, 0x97, 0x8f, 0xc9, 0x9f, 0xbc, 0xa8,
      0x4a, 0xc6, 0xaa, 0x4a, 0xc8, 0x0d, 0x2a, 0x60, 0x1a, 0x43, 0x40, 0x03,
      0xb3, 0x53, 0x30, 0x98, 0x1f, 0x3f, 0xdf, 0x5c, 0x0f, 0xf0, 0x84, 0x8e,
      0x5a, 0x5d, 0x41, 0xd2, 0x47, 0x78, 0x6d, 0x9f, 0x89, 0xce, 0xf5, 0x8e,
      0xb6, 0x54, 0xa2, 0x26, 0xe5, 0x40, 0x39, 0x5c, 0x59, 0x08, 0xb3, 0xda,
      0xf5, 0xf8, 0xa0, 0x18, 0x33, 0x57, 0xd1, 0x72, 0xbb, 0xba, 0x6c, 0xed,
      0xe8, 0xa0, 0x5e, 0xc8, 0x81, 0xc5, 0xac, 0x15, 0x1b, 0xd0, 0xe6, 0xc8,
      0x92, 0xf9, 0x43, 0x03, 0x5a, 0x00, 0x42, 0xe3, 0x49, 0xa5, 0xf7, 0x19,
      0x78, 0x8a, 0x39, 0x89, 0x32, 0xae, 0xbf, 0x4d, 0x4b, 0xb3, 0x33, 0x76,
      0x16, 0xfd, 0x0b, 0xfe, 0x42, 0x1e, 0x17, 0x37, 0x2a, 0x04, 0xea, 0x26,
      0xba, 0x6e, 0x2c, 0x36, 0xaf, 0x35, 0x1b, 0x75, 0x6d, 0x17, 0xdc, 0x8e,
  };
  const Fq12ElemStr b_str = {
      0xba, 0x10, 0x1f, 0xf6, 0x46, 0x8b, 0xe9, 0x32, 0x4f, 0xc0, 0xa5, 0x01,
      0xad, 0x5e, 0xe2, 0x31, 0x16, 0x29, 0x96, 0xed, 0xa7, 0xde, 0x4c, 0xe1,
      0xd2, 0x8d, 0x33, 0xca, 0x50, 0xab, 0x7b, 0xc6, 0x15, 0xeb, 0x79, 0xf4,
      0xeb, 0xde, 0x30, 0xb6, 0xc4, 0x07, 0x7c, 0x42, 0xcb, 0x04, 0x54, 0xf2,
      0x1f, 0x4d, 0x1f, 0xc0, 0xdf, 0xa2, 0x2b, 0x9e, 0x34, 0xc4, 0x4c, 0x84,
      0x14, 0xd3, 0x62, 0x07, 0xf1, 0x8b, 0x84, 0xd1, 0x46, 0x57, 0xb6, 0xe7,
      0x80, 0xe1, 0x46, 0x49, 0x1c, 0x0d, 0xef, 0x81, 0x31, 0xb0, 0xbe, 0x8c,
      0xb9, 0x08, 0xd0, 0xd3, 0xc4, 0x56, 0xca, 0xad, 0xf9, 0x1d, 0x75, 0x19,
      0x3f, 0xee, 0x7c, 0x43, 0xc1, 0xfa, 0x4e, 0x50, 0xb7, 0x19, 0x01, 0x00,
      0x6f, 0xd5, 0x16, 0xb6, 0xf4, 0x85, 0xe0, 0xeb, 0x2e, 0x5f, 0x0a, 0x7e,
      0xf8, 0xac, 0xbc, 0x05, 0xec, 0x73, 0xb5, 0x57, 0xe3, 0xb3, 0x18, 0x29,
      0xbb, 0xef, 0x86, 0x50, 0x87, 0xcf, 0x70, 0xba, 0x13, 0x8b, 0xb1, 0xb6,
      0x2d, 0x6f, 0x65, 0x3d, 0xa1, 0x0b, 0xe3, 0x92, 0xc5, 0x72, 0x86, 0x6a,
      0xb3, 0xeb, 0xe0, 0xe5, 0xda, 0x0e, 0x57, 0x87, 0xd5, 0xa9, 0x61, 0xa5,
      0x1e, 0xcb, 0x04, 0x86, 0xcd, 0xc3, 0x18, 0x2a, 0x36, 0xa0, 0x81, 0x73,
      0xe7, 0x13, 0x87, 0x80, 0x8d, 0x1a, 0xfe, 0x6e, 0x4b, 0xa3, 0x13, 0x03,
      0x66, 0x9e, 0x80, 0x4d, 0x8a, 0xaa, 0x00, 0x95, 0x72, 0xce, 0xbb, 0x51,
      0xe8, 0x01, 0x09, 0x41, 0xd3, 0x63, 0x28, 0x05, 0xa4, 0xbe, 0xd6, 0x41,
      0xa6, 0x2f, 0x5f, 0xbf, 0x0b, 0x13, 0xb4, 0x54, 0x5b, 0x50, 0x65, 0xdc,
      0x6f, 0x29, 0xd6, 0xda, 0xbf, 0xc2, 0x06, 0xea, 0x3b, 0xb2, 0xf1, 0xd4,
      0x26, 0x5c, 0x92, 0x6b, 0x95, 0x6d, 0x88, 0xab, 0x8f, 0xc6, 0x9d, 0x31,
      0xe4, 0x9b, 0x71, 0x49, 0xe0, 0xce, 0x97, 0x8f, 0xc9, 0x9f, 0xbc, 0xa8,
      0x4a, 0xc6, 0xaa, 0x4a, 0xc8, 0x0d, 0x2a, 0x60, 0x1a, 0x43, 0x40, 0x03,
      0xb3, 0x53, 0x30, 0x98, 0x1f, 0x3f, 0xdf, 0x5c, 0x0f, 0xf0, 0x84, 0x8e,
      0x5a, 0x5d, 0x41, 0xd2, 0x47, 0x78, 0x6d, 0x9f, 0x89, 0xce, 0xf5, 0x8e,
      0xb6, 0x54, 0xa2, 0x26, 0xe5, 0x40, 0x39, 0x5c, 0x59, 0x08, 0xb3, 0xda,
      0xf5, 0xf8, 0xa0, 0x18, 0x33, 0x57, 0xd1, 0x72, 0xbb, 0xba, 0x6c, 0xed,
      0xe8, 0xa0, 0x5e, 0xc8, 0x81, 0xc5, 0xac, 0x15, 0x1b, 0xd0, 0xe6, 0xc8,
      0x92, 0xf9, 0x43, 0x03, 0x5a, 0x00, 0x42, 0xe3, 0x49, 0xa5, 0xf7, 0x19,
      0x78, 0x8a, 0x39, 0x89, 0x32, 0xae, 0xbf, 0x4d, 0x4b, 0xb3, 0x33, 0x76,
      0x16, 0xfd, 0x0b, 0xfe, 0x42, 0x1e, 0x17, 0x37, 0x2a, 0x04, 0xea, 0x26,
      0xba, 0x6e, 0x2c, 0x36, 0xaf, 0x35, 0x1b, 0x75, 0x6d, 0x17, 0xdc, 0x8e,
  };

  THROW_ON_EPIDERR(
      ReadFfElement(this->fq12, &(a_str), sizeof(a_str), this->fq12_a));
  THROW_ON_EPIDERR(
      ReadFfElement(this->fq12, &(b_str), sizeof(b_str), this->fq12_b));
  bool result;
  EXPECT_EQ(kEpidNoErr,
            FfIsEqual(this->fq12, this->fq12_a, this->fq12_b, &result));
  EXPECT_TRUE(result);
}

///////////////////////////////////////////////////////////////////////
// WriteFfElement

TEST_F(FfElementTest, WriteFailsGivenArgumentsMismatch) {
  uint8_t buf[sizeof(Fq12ElemStr)] = {0};
  EXPECT_EQ(kEpidBadArgErr,
            WriteFfElement(this->fq12, this->fq_a, buf, sizeof(Fq12ElemStr)));
  EXPECT_EQ(kEpidBadArgErr,
            WriteFfElement(this->fq, this->fq12_a, buf, sizeof(Fq12ElemStr)));
}

TEST_F(FfElementTest, WriteFailsGivenNullPointer) {
  FqElemStr ff_elem_str;
  EXPECT_EQ(kEpidBadArgErr, WriteFfElement(nullptr, this->fq_a, &ff_elem_str,
                                           sizeof(ff_elem_str)));
  EXPECT_EQ(kEpidBadArgErr, WriteFfElement(this->fq, nullptr, &ff_elem_str,
                                           sizeof(ff_elem_str)));
  EXPECT_EQ(kEpidBadArgErr,
            WriteFfElement(this->fq, this->fq_a, nullptr, sizeof(ff_elem_str)));
}

TEST_F(FfElementTest, WriteFailsGivenInvalidBufferSize) {
  FqElemStr ff_elem_str;
  EXPECT_EQ(kEpidBadArgErr,
            WriteFfElement(this->fq, this->fq_a, &ff_elem_str, 0));
  EXPECT_EQ(kEpidBadArgErr, WriteFfElement(this->fq, this->fq_a, &ff_elem_str,
                                           sizeof(ff_elem_str) - 1));
  EXPECT_EQ(kEpidBadArgErr, WriteFfElement(this->fq, this->fq_a, &ff_elem_str,
                                           std::numeric_limits<size_t>::max()));
}

TEST_F(FfElementTest, WriteWorksGiven256BitElement) {
  FqElemStr ff_elem_str;
  EXPECT_EQ(kEpidNoErr, WriteFfElement(this->fq, this->fq_a, &ff_elem_str,
                                       sizeof(ff_elem_str)));
  EXPECT_EQ(this->fq_a_data, ff_elem_str);
}

///////////////////////////////////////////////////////////////////////
// FfNeg

TEST_F(FfElementTest, FfNegFailsGivenArgumentsMismatch) {
  EXPECT_EQ(kEpidBadArgErr, FfNeg(this->fq12, this->fq_a, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfNeg(this->fq, this->fq12_a, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfNeg(this->fq, this->fq_a, this->fq12_r));
}

TEST_F(FfElementTest, FfNegFailsGivenNullPointer) {
  EXPECT_EQ(kEpidBadArgErr, FfNeg(nullptr, this->fq_a, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfNeg(this->fq, nullptr, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfNeg(this->fq, this->fq_a, nullptr));
}

TEST_F(FfElementTest, FfNegSucceedsGivenElementZero) {
  FqElemStr fq_r_data;
  EXPECT_EQ(kEpidNoErr, FfNeg(this->fq, this->fq_zero, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_zero_data, fq_r_data)
      << "FfNeg: Negate element does not match to reference neg zero value";
}

TEST_F(FfElementTest, FfNegSucceedsGivenNonZeroElement) {
  FqElemStr fq_r_data;
  EXPECT_EQ(kEpidNoErr, FfNeg(this->fq, this->fq_a, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_neg_a_data, fq_r_data)
      << "FfNeg: Negate element does not match to reference neg value";
}

///////////////////////////////////////////////////////////////////////
// FfIsZero

TEST_F(FfElementTest, FfIsZeroFailsGivenNullPointer) {
  bool result = false;
  EXPECT_EQ(kEpidBadArgErr, FfIsZero(nullptr, this->fq_zero, &result));
  EXPECT_EQ(kEpidBadArgErr, FfIsZero(this->fq, nullptr, &result));
  EXPECT_EQ(kEpidBadArgErr, FfIsZero(this->fq, this->fq_zero, nullptr));
}

TEST_F(FfElementTest, FfIsZeroFailsGivenArgumentsMismatch) {
  bool result;
  EXPECT_EQ(kEpidBadArgErr, FfIsZero(this->fq12, this->fq_a, &result));
  EXPECT_EQ(kEpidBadArgErr, FfIsZero(this->fq, this->fq12_a, &result));
}

TEST_F(FfElementTest, FfIsZeroSucceedsGivenZeroElement) {
  bool result = false;
  EXPECT_EQ(kEpidNoErr, FfIsZero(this->fq, this->fq_zero, &result));
  EXPECT_TRUE(result);
}

TEST_F(FfElementTest, FfIsZeroSucceedsGivenNonZeroElement) {
  bool result = false;
  EXPECT_EQ(kEpidNoErr, FfIsZero(this->fq, this->fq_one, &result));
  EXPECT_FALSE(result);
}

///////////////////////////////////////////////////////////////////////
// FfMul

TEST_F(FfElementTest, FfMulFailsGivenNullPointer) {
  EXPECT_EQ(kEpidBadArgErr, FfMul(nullptr, this->fq_a, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMul(this->fq, nullptr, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMul(this->fq, this->fq_a, nullptr, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMul(this->fq, this->fq_a, this->fq_b, nullptr));
}

TEST_F(FfElementTest, FfMulFailsGivenArgumentsMismatch) {
  EXPECT_EQ(kEpidBadArgErr,
            FfMul(this->fq12, this->fq_a, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfMul(this->fq, this->fq12_a, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfMul(this->fq, this->fq_a, this->fq12_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfMul(this->fq, this->fq_a, this->fq_b, this->fq12_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfMul(this->fq2, this->fq_a, this->fq2_b, this->fq2_r));
}

TEST_F(FfElementTest, FfMulSucceedsGivenElementZero) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr, FfMul(this->fq, this->fq_zero, this->fq_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_zero_data, fq_r_data);

  EXPECT_EQ(kEpidNoErr, FfMul(this->fq, this->fq_a, this->fq_zero, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_zero_data, fq_r_data);
}

TEST_F(FfElementTest, FfMulSucceedsGivenElementOne) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr, FfMul(this->fq, this->fq_one, this->fq_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_b_data, fq_r_data);

  EXPECT_EQ(kEpidNoErr, FfMul(this->fq, this->fq_a, this->fq_one, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_a_data, fq_r_data);
}

TEST_F(FfElementTest, FfMulSucceedsGivenTwoElements) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr, FfMul(this->fq, this->fq_a, this->fq_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_mul_ab_data, fq_r_data);

  Fq2ElemStr fq2_r_str;

  const Fq2ElemStr fq2_a_str = {
      0x12, 0xA6, 0x5B, 0xD6, 0x91, 0x8D, 0x50, 0xA7, 0x66, 0xEB, 0x7D,
      0x52, 0xE3, 0x40, 0x17, 0x60, 0x7F, 0xDF, 0x6C, 0xA1, 0x2C, 0x1A,
      0x37, 0xE0, 0x92, 0xC0, 0xF7, 0xB9, 0x76, 0xAB, 0xB1, 0x8A, 0xDD,
      0x2B, 0xE9, 0x59, 0x24, 0xA5, 0xB3, 0xFD, 0xEB, 0xE1, 0x3C, 0xC0,
      0x73, 0x4E, 0x99, 0xEE, 0x36, 0xF6, 0xC0, 0x1A, 0x76, 0x01, 0x0A,
      0xF0, 0xCB, 0xB4, 0x71, 0x88, 0x95, 0xCB, 0x35, 0xBA};

  const Fq2ElemStr fq2_mul_ab_str = {
      0xE4, 0xAB, 0xE3, 0xE4, 0x08, 0xC3, 0x8A, 0x00, 0x78, 0x50, 0x01,
      0xA9, 0x4A, 0xFC, 0x43, 0x15, 0xA2, 0x99, 0x51, 0x20, 0x6B, 0x9C,
      0x00, 0xCF, 0x82, 0x88, 0xBD, 0x39, 0x0B, 0xA2, 0x0B, 0x58, 0xB4,
      0xE4, 0x39, 0xEA, 0x70, 0xDE, 0x15, 0xC3, 0xC6, 0x91, 0xFD, 0x7F,
      0xBE, 0x2B, 0xF5, 0xD2, 0xC0, 0xB7, 0x20, 0xFC, 0x1F, 0x02, 0x33,
      0xC9, 0x39, 0x93, 0xB9, 0x5E, 0x55, 0x51, 0xB6, 0x46};

  THROW_ON_EPIDERR(
      ReadFfElement(this->fq2, &(fq2_a_str), sizeof(fq2_a_str), this->fq2_a));
  THROW_ON_EPIDERR(
      ReadFfElement(this->fq, &(fq_b_data), sizeof(fq_b_data), this->fq_b));
  EXPECT_EQ(kEpidNoErr, FfMul(this->fq2, this->fq2_a, this->fq_b, this->fq2_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq2, this->fq2_r, &fq2_r_str, sizeof(fq2_r_str)));
  EXPECT_EQ(fq2_mul_ab_str, fq2_r_str);

  Fq12ElemStr fq12_r_str;
  const Fq12ElemStr fq12_a_str = {
      0x0f, 0xd0, 0x1c, 0x59, 0x98, 0x52, 0x5a, 0x40, 0xff, 0x26, 0x0a, 0xfd,
      0x70, 0xec, 0x23, 0x48, 0x1b, 0x4b, 0x70, 0xa0, 0x15, 0xc5, 0xb1, 0x8b,
      0xa5, 0x75, 0xac, 0xce, 0xf3, 0x6c, 0xe8, 0xba, 0xa7, 0xfb, 0xf9, 0xea,
      0x18, 0x70, 0xd8, 0x9b, 0x3c, 0xc3, 0x66, 0x10, 0x38, 0xac, 0x16, 0x32,
      0xdc, 0x9d, 0xf0, 0xd4, 0x2c, 0xe7, 0xf6, 0x69, 0x47, 0x69, 0x84, 0xee,
      0x39, 0x5f, 0xd8, 0xcc, 0x1b, 0xef, 0x81, 0x13, 0x81, 0xb9, 0x56, 0xae,
      0xd0, 0xd4, 0xae, 0xcb, 0x24, 0x7c, 0x7b, 0x7f, 0xc0, 0x41, 0x42, 0x0a,
      0x9d, 0x90, 0x6b, 0x1e, 0x3a, 0xad, 0x39, 0x7a, 0x1f, 0xef, 0x56, 0x9e,
      0xbd, 0x84, 0x48, 0xaf, 0x65, 0x66, 0x33, 0xe5, 0x67, 0xf1, 0xc4, 0x48,
      0xf0, 0xb6, 0x61, 0x06, 0xc7, 0x78, 0xb3, 0xc1, 0x12, 0xb8, 0x31, 0xd4,
      0x69, 0x2e, 0x0e, 0xd3, 0xd4, 0x91, 0x81, 0x9a, 0x23, 0x62, 0x9b, 0x83,
      0x95, 0xf6, 0x7f, 0xf6, 0xcc, 0x6b, 0xee, 0x84, 0xf0, 0x5b, 0x93, 0xa6,
      0x90, 0x48, 0x1a, 0xa7, 0xa8, 0xe8, 0x10, 0xa5, 0x53, 0x7e, 0x4f, 0xd5,
      0x0d, 0xb0, 0x86, 0xf7, 0x1f, 0xb8, 0xb7, 0xd4, 0x2e, 0x41, 0x83, 0x2c,
      0xfc, 0x62, 0x14, 0xb8, 0xc2, 0xf6, 0xc1, 0x5e, 0xc1, 0xc1, 0x40, 0x8b,
      0x28, 0xb5, 0x1b, 0x85, 0xee, 0x71, 0xc8, 0x14, 0x56, 0xb9, 0x3b, 0x90,
      0x4a, 0xfe, 0x73, 0xfa, 0xcf, 0x20, 0x67, 0x0e, 0x89, 0x1d, 0xcb, 0x05,
      0x38, 0x27, 0x1c, 0xd5, 0x7a, 0x0c, 0xac, 0x92, 0x17, 0x94, 0x7f, 0x10,
      0x3e, 0xff, 0x97, 0x23, 0x66, 0x8d, 0x70, 0xae, 0x80, 0x97, 0xff, 0x88,
      0xf8, 0x01, 0x2b, 0x99, 0x81, 0xeb, 0xf8, 0x19, 0xef, 0xda, 0xe3, 0xae,
      0xd9, 0x9b, 0x4c, 0x13, 0xfc, 0x9e, 0x18, 0xd6, 0xb0, 0xcf, 0x4e, 0x29,
      0xa8, 0xcf, 0x01, 0xfa, 0xda, 0x22, 0x85, 0x18, 0x9a, 0x51, 0x17, 0xac,
      0x26, 0x57, 0xf6, 0x73, 0x59, 0x24, 0xd5, 0xc9, 0x4d, 0xac, 0xb6, 0xe4,
      0x51, 0xf7, 0x5e, 0x30, 0x5b, 0x25, 0x3d, 0x52, 0xa3, 0xe0, 0x4b, 0xa2,
      0xc3, 0x4c, 0x2f, 0x53, 0xd5, 0x92, 0x33, 0x7e, 0x8b, 0x66, 0x5f, 0x48,
      0x48, 0xca, 0xa8, 0x98, 0x0f, 0x73, 0xd3, 0x60, 0xaf, 0x25, 0x02, 0xee,
      0x64, 0xe7, 0x02, 0x89, 0x9a, 0xd2, 0x96, 0x45, 0x04, 0xe1, 0x30, 0x9b,
      0xd8, 0x85, 0xf0, 0x99, 0x7c, 0x01, 0xd5, 0x58, 0x5a, 0x74, 0x0b, 0xd4,
      0xf2, 0xfa, 0xc7, 0x59, 0x36, 0xfd, 0x91, 0x07, 0x60, 0xf7, 0xdc, 0x11,
      0x3e, 0x8a, 0xc6, 0x48, 0x0d, 0x02, 0x30, 0x18, 0xd0, 0xb9, 0x6b, 0xd6,
      0x39, 0x6a, 0x41, 0x82, 0xdd, 0xe3, 0xce, 0xb8, 0xb6, 0xf5, 0x61, 0x80,
      0x61, 0x16, 0xf4, 0x9a, 0xb8, 0x58, 0x1d, 0x3f, 0x4d, 0xc2, 0xca, 0xcf,
  };
  const Fq12ElemStr fq12_b_str = {
      0x3a, 0x30, 0x33, 0xa2, 0x14, 0xdf, 0xdc, 0x70, 0x48, 0xf3, 0xba, 0x3f,
      0xce, 0xfc, 0x69, 0x24, 0xae, 0xa1, 0xf3, 0xcf, 0xd8, 0x77, 0x69, 0x38,
      0x38, 0xf4, 0x8e, 0xfb, 0x51, 0xfb, 0x10, 0x7c, 0x73, 0xb4, 0xee, 0xf9,
      0x76, 0xbb, 0x45, 0xec, 0x07, 0x24, 0x23, 0xa3, 0x38, 0x64, 0x39, 0x4c,
      0x03, 0x94, 0xf5, 0xee, 0x5f, 0xc9, 0x83, 0x4f, 0xd1, 0xa6, 0x4a, 0x05,
      0x25, 0x0c, 0x46, 0x33, 0xd4, 0x40, 0x47, 0xae, 0xec, 0xda, 0x30, 0xf9,
      0xe1, 0x28, 0xfb, 0xb1, 0x4c, 0x62, 0xfb, 0x6c, 0x90, 0x0d, 0xb1, 0xec,
      0xaf, 0x2d, 0x95, 0x64, 0x11, 0x93, 0xda, 0x8e, 0xb8, 0x7d, 0xe4, 0xa3,
      0x8e, 0xa6, 0x9b, 0xaa, 0x58, 0xd5, 0xdc, 0x59, 0x1a, 0x51, 0x53, 0x47,
      0x30, 0x1c, 0x2b, 0xb0, 0xbb, 0x57, 0x57, 0x0a, 0x8c, 0x5f, 0x62, 0x9c,
      0x52, 0x28, 0xc0, 0x25, 0x27, 0xae, 0xcd, 0x36, 0xbf, 0x2d, 0x03, 0xb1,
      0x33, 0xfd, 0x24, 0x81, 0x3c, 0x91, 0x43, 0x90, 0x0c, 0x20, 0xc4, 0xe3,
      0x69, 0x3f, 0xa9, 0xa2, 0x7c, 0xc2, 0x48, 0x28, 0x1b, 0xf0, 0x81, 0x1f,
      0x2b, 0x8f, 0x8d, 0x43, 0x38, 0x18, 0x12, 0xa3, 0x89, 0xf1, 0xd7, 0x60,
      0x89, 0x68, 0x6a, 0xc4, 0xcc, 0x5d, 0xf4, 0xce, 0x43, 0x95, 0x84, 0xcd,
      0x01, 0x55, 0xb8, 0x5d, 0x24, 0x50, 0xcd, 0xe3, 0x68, 0x1c, 0xff, 0x59,
      0x0d, 0xac, 0xcb, 0xe7, 0x9d, 0x68, 0x0f, 0x4a, 0xaf, 0xeb, 0xb5, 0xfb,
      0xf9, 0xb1, 0x58, 0x80, 0xd5, 0x71, 0x53, 0x26, 0x2e, 0x9c, 0xce, 0x10,
      0xac, 0xd2, 0x0a, 0xeb, 0xb4, 0x5c, 0xc8, 0xd7, 0x16, 0x26, 0x66, 0x71,
      0xdd, 0x67, 0xba, 0xdb, 0x35, 0x76, 0x64, 0xc6, 0x0b, 0x0b, 0x07, 0x4b,
      0x65, 0xa2, 0xf7, 0x68, 0x9e, 0xfe, 0xe8, 0xe3, 0xa7, 0x09, 0x79, 0xc5,
      0xdf, 0x5e, 0x9c, 0xec, 0xee, 0x83, 0x59, 0xb5, 0x89, 0x81, 0xa2, 0x87,
      0xd7, 0x75, 0x65, 0x90, 0xde, 0x78, 0x37, 0x59, 0x04, 0x9b, 0x50, 0xc3,
      0xba, 0x90, 0x09, 0x15, 0xab, 0x17, 0xc5, 0xbf, 0x5b, 0xb9, 0xe4, 0x6c,
      0xa9, 0xb1, 0x49, 0x76, 0x25, 0x74, 0x5d, 0x9c, 0x78, 0xc5, 0x09, 0xee,
      0xeb, 0xeb, 0x9d, 0x1c, 0x6c, 0xc0, 0x27, 0x9d, 0x66, 0xe6, 0x7f, 0x31,
      0xcd, 0xb0, 0x8a, 0xe4, 0x9e, 0xbd, 0x70, 0x18, 0x88, 0xa1, 0x08, 0x39,
      0xa2, 0x48, 0xa5, 0x98, 0xb6, 0xad, 0x10, 0x54, 0x07, 0xcd, 0xfc, 0x6d,
      0xb1, 0x02, 0xff, 0xe3, 0x92, 0xd7, 0x9d, 0x48, 0xfb, 0xce, 0x88, 0x46,
      0x92, 0x07, 0x02, 0xba, 0xee, 0xeb, 0xae, 0x88, 0xb1, 0x4e, 0xd0, 0xf2,
      0xce, 0xd7, 0x57, 0x07, 0xd4, 0x39, 0xd2, 0x7d, 0x1a, 0x0c, 0xef, 0xf2,
      0x84, 0x84, 0x22, 0x8a, 0xb1, 0x80, 0x0d, 0xdc, 0x64, 0x86, 0xfd, 0x70,
  };
  const Fq12ElemStr fq12_mul_ab_str = {
      0x4a, 0x0e, 0x83, 0x4f, 0x62, 0xca, 0x56, 0x0b, 0x0f, 0x2e, 0xf3, 0x1a,
      0xc6, 0xfc, 0x76, 0x4f, 0x4b, 0x85, 0x78, 0x6f, 0x9d, 0xb9, 0xbd, 0xc8,
      0xbb, 0x7c, 0x73, 0x12, 0x28, 0xff, 0xbc, 0x8f, 0x18, 0x8e, 0x5e, 0x4b,
      0x72, 0xa5, 0x4a, 0x99, 0xfc, 0xe7, 0x05, 0xe5, 0xd2, 0x2c, 0x06, 0x95,
      0xf5, 0xe4, 0xa5, 0x87, 0xcb, 0x85, 0x25, 0xf8, 0xda, 0x71, 0x7b, 0x9a,
      0xf1, 0x52, 0xf9, 0x33, 0x05, 0x5f, 0x15, 0x72, 0xa3, 0x61, 0xa6, 0x6c,
      0xb8, 0x43, 0xbb, 0xca, 0x33, 0xb2, 0xdb, 0x59, 0xca, 0xab, 0xad, 0xa0,
      0x4d, 0xe3, 0xb8, 0xf5, 0xc9, 0x8c, 0x06, 0x91, 0xe5, 0xe4, 0x5a, 0xad,
      0xe7, 0x66, 0xcc, 0x2b, 0x2c, 0x11, 0x88, 0x29, 0xb1, 0x3c, 0x52, 0xfe,
      0x50, 0xdf, 0x8f, 0x15, 0x6a, 0x6b, 0xb8, 0x32, 0x96, 0x90, 0x85, 0x03,
      0x78, 0x88, 0x97, 0x7b, 0x5b, 0x50, 0x2e, 0xde, 0xe7, 0x53, 0x22, 0x11,
      0xeb, 0xff, 0x1b, 0x19, 0xae, 0x5f, 0x58, 0x3a, 0x4f, 0x6f, 0x34, 0xfb,
      0x2a, 0x49, 0xca, 0x4b, 0x9c, 0x31, 0xfc, 0x74, 0x0f, 0x11, 0xcc, 0x45,
      0xcd, 0xa2, 0x12, 0xca, 0xf4, 0x2f, 0x30, 0x9a, 0x6a, 0xcb, 0xee, 0x88,
      0x15, 0x56, 0x29, 0x2a, 0xbf, 0x33, 0x7b, 0xdb, 0x59, 0x74, 0x4d, 0xc0,
      0x64, 0xf0, 0x0b, 0x9f, 0xf2, 0x65, 0xb7, 0x3a, 0xda, 0xe2, 0xb5, 0x63,
      0x3a, 0x3e, 0x06, 0xef, 0x73, 0xa3, 0xbc, 0xea, 0xcc, 0x68, 0xd9, 0xf5,
      0x6e, 0x94, 0x1c, 0x54, 0x47, 0xf1, 0x99, 0x2c, 0x44, 0x64, 0x45, 0x84,
      0x50, 0x10, 0xfd, 0xc5, 0x16, 0x65, 0x89, 0xec, 0x72, 0xa6, 0x7e, 0x82,
      0x7f, 0xe2, 0xe3, 0xea, 0x82, 0xa3, 0x18, 0xf4, 0x9b, 0x93, 0x4d, 0x79,
      0xa0, 0x2c, 0xbf, 0x90, 0x0e, 0xdb, 0x41, 0x09, 0x27, 0x44, 0x19, 0xd1,
      0xb4, 0xe5, 0xc6, 0x8c, 0xa5, 0xc7, 0x82, 0xbf, 0x00, 0x7b, 0x8d, 0x7a,
      0x05, 0x7b, 0x7e, 0x33, 0x60, 0x28, 0x34, 0x9c, 0x86, 0x5a, 0x6b, 0xce,
      0xa5, 0xed, 0x26, 0x22, 0x63, 0x4a, 0xe9, 0x57, 0xcd, 0x55, 0xd2, 0xd3,
      0x0e, 0x73, 0x14, 0xe4, 0xee, 0xe7, 0x3c, 0x43, 0xfc, 0xae, 0x84, 0x59,
      0x2f, 0xea, 0x35, 0x84, 0xa5, 0x77, 0xce, 0xa8, 0xc5, 0xaa, 0xb8, 0xdc,
      0xb9, 0xd6, 0x71, 0x11, 0xe2, 0x95, 0x00, 0x97, 0xbd, 0x50, 0xda, 0x58,
      0x1b, 0xda, 0xb3, 0x25, 0x19, 0x2f, 0x9d, 0xfa, 0x2c, 0xfb, 0x96, 0x26,
      0xbb, 0x4a, 0x41, 0x82, 0xa9, 0x45, 0x24, 0xee, 0xee, 0xea, 0xd3, 0xc5,
      0x94, 0xfc, 0x2a, 0x37, 0x6a, 0x43, 0xb8, 0x6c, 0xbb, 0x98, 0xb7, 0x17,
      0x03, 0xc0, 0x3d, 0x6d, 0x59, 0x56, 0x3d, 0x23, 0x42, 0x94, 0x25, 0x64,
      0xb9, 0x62, 0xad, 0x4d, 0x84, 0x9b, 0x3c, 0x05, 0x8c, 0xf0, 0xd3, 0xd3,
  };
  THROW_ON_EPIDERR(ReadFfElement(this->fq12, &(fq12_a_str), sizeof(fq12_a_str),
                                 this->fq12_a));
  THROW_ON_EPIDERR(ReadFfElement(this->fq12, &(fq12_b_str), sizeof(fq12_b_str),
                                 this->fq12_b));
  EXPECT_EQ(kEpidNoErr,
            FfMul(this->fq12, this->fq12_a, this->fq12_b, this->fq12_r));
  THROW_ON_EPIDERR(WriteFfElement(this->fq12, this->fq12_r, &fq12_r_str,
                                  sizeof(fq12_r_str)));
  EXPECT_EQ(fq12_mul_ab_str, fq12_r_str);
}

///////////////////////////////////////////////////////////////////////
// FfAdd
TEST_F(FfElementTest, FfAddFailsGivenArgumentsMismatch) {
  EXPECT_EQ(kEpidBadArgErr,
            FfAdd(this->fq12, this->fq_a, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfAdd(this->fq, this->fq12_a, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfAdd(this->fq, this->fq_a, this->fq12_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfAdd(this->fq, this->fq_a, this->fq_b, this->fq12_r));
}

TEST_F(FfElementTest, FfAddFailsGivenNullPtr) {
  EXPECT_EQ(kEpidBadArgErr, FfAdd(nullptr, this->fq_a, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfAdd(this->fq, nullptr, this->fq_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfAdd(this->fq, this->fq_a, nullptr, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfAdd(this->fq, this->fq_a, this->fq_b, nullptr));
}

TEST_F(FfElementTest, FfAddSucceedsGivenElementZero) {
  FqElemStr fq_r_data = {0x0};

  // additive identity
  EXPECT_EQ(kEpidNoErr, FfAdd(this->fq, this->fq_a, this->fq_zero, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_a_data, fq_r_data);

  EXPECT_EQ(kEpidNoErr, FfAdd(this->fq, this->fq_zero, this->fq_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_b_data, fq_r_data);

  EXPECT_EQ(kEpidNoErr,
            FfAdd(this->fq, this->fq_zero, this->fq_zero, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_zero_data, fq_r_data);
}

TEST_F(FfElementTest, FfAddSucceedsGivenTwoElements) {
  FqElemStr fq_r_data = {0x0};

  EXPECT_EQ(kEpidNoErr, FfAdd(this->fq, this->fq_a, this->fq_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_sum_ab_data, fq_r_data);
}

TEST_F(FfElementTest, FfAddSucceedsGivenTwoElementsFq2) {
  const Fq2ElemStr sum_ab_data = {
      0xF9, 0x0B, 0x7F, 0x72, 0x65, 0x94, 0x67, 0x2A, 0x9F, 0x0F, 0x2F,
      0xBA, 0x3B, 0x2B, 0x26, 0x83, 0xBA, 0xD3, 0xFB, 0x7B, 0x9D, 0x79,
      0x11, 0x78, 0xF6, 0x59, 0x23, 0x75, 0xEF, 0x7D, 0x46, 0x7C, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

  FfElementObj a(&this->fq2, &this->bn_a_data, sizeof(this->bn_a_data));
  FfElementObj b(&this->fq2, &this->bn_b_data, sizeof(this->bn_b_data));
  FfElementObj r(&this->fq2);

  EXPECT_EQ(kEpidNoErr, FfAdd(this->fq2, a, b, r));
  EXPECT_EQ(FfElementObj(&this->fq2, sum_ab_data), r);
}
TEST_F(FfElementTest, FfAddSucceedsGivenTwoElementsFq6) {
  const Fq6ElemStr sum_ab_data = {
      0xF9, 0x0B, 0x7F, 0x72, 0x65, 0x94, 0x67, 0x2A, 0x9F, 0x0F, 0x2F, 0xBA,
      0x3B, 0x2B, 0x26, 0x83, 0xBA, 0xD3, 0xFB, 0x7B, 0x9D, 0x79, 0x11, 0x78,
      0xF6, 0x59, 0x23, 0x75, 0xEF, 0x7D, 0x46, 0x7C, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

  FfElementObj a(&this->fq6, &this->bn_a_data, sizeof(this->bn_a_data));
  FfElementObj b(&this->fq6, &this->bn_b_data, sizeof(this->bn_b_data));
  FfElementObj r(&this->fq6);

  EXPECT_EQ(kEpidNoErr, FfAdd(this->fq6, a, b, r));
  EXPECT_EQ(FfElementObj(&this->fq6, sum_ab_data), r);
}
TEST_F(FfElementTest, FfAddSucceedsGivenTwoElementsFq12) {
  const Fq12ElemStr sum_ab_data = {
      0xF9, 0x0B, 0x7F, 0x72, 0x65, 0x94, 0x67, 0x2A, 0x9F, 0x0F, 0x2F, 0xBA,
      0x3B, 0x2B, 0x26, 0x83, 0xBA, 0xD3, 0xFB, 0x7B, 0x9D, 0x79, 0x11, 0x78,
      0xF6, 0x59, 0x23, 0x75, 0xEF, 0x7D, 0x46, 0x7C, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  };
  FfElementObj a(&this->fq12, &this->bn_a_data, sizeof(this->bn_a_data));
  FfElementObj b(&this->fq12, &this->bn_b_data, sizeof(this->bn_b_data));
  FfElementObj r(&this->fq12);

  EXPECT_EQ(kEpidNoErr, FfAdd(this->fq12, a, b, r));
  EXPECT_EQ(FfElementObj(&this->fq12, sum_ab_data), r);
}

///////////////////////////////////////////////////////////////////////
// FfInv

TEST_F(FfElementTest, FfInvFailsGivenNullPointer) {
  EXPECT_EQ(kEpidBadArgErr, FfInv(nullptr, (this->fq_a), this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfInv(this->fq, nullptr, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfInv(this->fq, this->fq_a, nullptr));
}

TEST_F(FfElementTest, FfInvFailsGivenArgumentsMismatch) {
  EXPECT_EQ(kEpidBadArgErr, FfInv(this->fq12, this->fq_a, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfInv(this->fq, this->fq12_a, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfInv(this->fq, this->fq_a, this->fq12_r));
}

TEST_F(FfElementTest, FfInvFailsGivenElementZero) {
  EXPECT_EQ(kEpidDivByZeroErr, FfInv(this->fq, this->fq_zero, this->fq_r));
}

TEST_F(FfElementTest, FfInvSucceedsGivenElementOne) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr, FfInv(this->fq, this->fq_one, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_one_data, fq_r_data);
}

TEST_F(FfElementTest, FfInvSucceedsGivenAnElement) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr, FfInv(this->fq, this->fq_a, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_inv_a_data, fq_r_data);
}

///////////////////////////////////////////////////////////////////////
// FfExp

TEST_F(FfElementTest, FfExpFailsGivenNullPointer) {
  EXPECT_EQ(kEpidBadArgErr,
            FfExp(nullptr, this->fq_a, this->exponent_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfExp(this->fq, nullptr, this->exponent_b, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfExp(this->fq, this->fq_a, nullptr, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfExp(this->fq, this->fq_a, this->exponent_b, nullptr));
}

TEST_F(FfElementTest, FfExpFailsGivenArgumentsMismatch) {
  EXPECT_EQ(kEpidBadArgErr,
            FfExp(this->fq12, this->fq_a, this->exponent_zero, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfExp(this->fq, this->fq12_a, this->exponent_zero, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfExp(this->fq, this->fq_a, this->exponent_zero, this->fq12_r));
}

TEST_F(FfElementTest, FfExpSucceedsGivenElementZero) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr,
            FfExp(this->fq, this->fq_zero, this->exponent_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_zero_data, fq_r_data);
}

TEST_F(FfElementTest, FfExpSucceedsGivenExponentZero) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr,
            FfExp(this->fq, this->fq_a, this->exponent_zero, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_one_data, fq_r_data);
}

TEST_F(FfElementTest, FfExpSucceedsGivenElementOne) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr,
            FfExp(this->fq, this->fq_one, this->exponent_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_one_data, fq_r_data);
}

TEST_F(FfElementTest, FfExpSucceedsGivenExponentOne) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr,
            FfExp(this->fq, this->fq_a, this->exponent_one, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_a_data, fq_r_data);
}

TEST_F(FfElementTest, FfExpSucceedsGivenAnElement) {
  FqElemStr fq_r_data;

  EXPECT_EQ(kEpidNoErr,
            FfExp(this->fq, this->fq_a, this->exponent_b, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_exp_ab_data, fq_r_data);

  Fq12ElemStr fq12_r_str;
  const Fq12ElemStr fq12_a_str = {
      0xe4, 0xca, 0x0b, 0xa8, 0x9c, 0x70, 0xe2, 0x20, 0x24, 0xd9, 0x8f, 0x09,
      0x12, 0x79, 0x55, 0x44, 0x8c, 0x58, 0x42, 0x8f, 0x18, 0x97, 0x0a, 0xc3,
      0x0a, 0x40, 0x68, 0x0e, 0x1e, 0x21, 0x7f, 0xb4, 0x73, 0xcf, 0x8d, 0x54,
      0x68, 0xae, 0x1c, 0xc4, 0x95, 0x01, 0xe3, 0x52, 0x33, 0xfe, 0x69, 0xb5,
      0x23, 0x45, 0xef, 0xff, 0x79, 0x14, 0xef, 0x98, 0xa8, 0x4e, 0xb1, 0x2e,
      0xe1, 0x15, 0x81, 0x0c, 0x9f, 0x65, 0x90, 0x6a, 0x53, 0xd7, 0xf6, 0x73,
      0x08, 0xda, 0x45, 0x11, 0xde, 0xcf, 0xed, 0x4c, 0x8c, 0x21, 0x02, 0xf5,
      0x3f, 0x97, 0xb6, 0xb2, 0xf5, 0xc6, 0x58, 0xfc, 0x9a, 0xa6, 0xbb, 0xf9,
      0x3c, 0xd8, 0xfc, 0x86, 0x09, 0x1c, 0x16, 0xa4, 0x6f, 0x5e, 0x7a, 0x81,
      0x01, 0xf9, 0x8a, 0x6d, 0x38, 0xb5, 0xc9, 0x5f, 0xe9, 0x7d, 0x6f, 0x46,
      0x92, 0x49, 0x5b, 0xb8, 0x61, 0x03, 0x6f, 0x5e, 0xda, 0x53, 0xcb, 0x62,
      0xcd, 0x82, 0x63, 0x96, 0xc7, 0xc0, 0x39, 0xa4, 0x31, 0xee, 0x15, 0x26,
      0x1c, 0xe6, 0x4e, 0xe4, 0xb4, 0x40, 0xfa, 0x65, 0xa3, 0xf4, 0x73, 0x03,
      0x85, 0x84, 0x41, 0xf9, 0x77, 0xf3, 0x90, 0xdc, 0x88, 0x26, 0xbd, 0x27,
      0x9a, 0x9d, 0x37, 0xf0, 0x80, 0xee, 0x02, 0x1d, 0x3e, 0x4f, 0x88, 0xed,
      0x8f, 0x89, 0x90, 0xf1, 0x6d, 0x5d, 0x70, 0x49, 0x99, 0x07, 0x61, 0x22,
      0x43, 0x52, 0xba, 0xee, 0xa8, 0xe9, 0x8e, 0x2e, 0x83, 0x30, 0x41, 0x85,
      0xe3, 0x51, 0xb0, 0x8c, 0x86, 0x31, 0xec, 0x02, 0x76, 0xe8, 0x41, 0x1f,
      0x88, 0xbc, 0x39, 0xa2, 0xe4, 0xc1, 0x34, 0xa7, 0x1d, 0x5f, 0x8c, 0x23,
      0x0f, 0xf0, 0xdc, 0xcd, 0xba, 0x67, 0xd5, 0x78, 0xd6, 0x8d, 0x32, 0x6a,
      0xc1, 0x7f, 0x6f, 0x97, 0xcf, 0x46, 0xf0, 0x04, 0x7b, 0xe3, 0x5a, 0xee,
      0x31, 0xc4, 0xc0, 0x79, 0x96, 0xbc, 0x01, 0x78, 0x9f, 0xbe, 0x8b, 0xba,
      0x59, 0x22, 0xfb, 0x64, 0x1b, 0x37, 0x5c, 0x33, 0x57, 0xaf, 0x83, 0x5a,
      0x96, 0xea, 0x56, 0xaa, 0x6e, 0x9c, 0xfa, 0xe6, 0xf9, 0xdf, 0xd8, 0x44,
      0xbb, 0x4e, 0xbf, 0xa8, 0xec, 0x0f, 0x16, 0xa8, 0x59, 0x1f, 0x49, 0xf4,
      0x8c, 0x80, 0x6c, 0x52, 0x7f, 0x74, 0x94, 0xc2, 0xe3, 0x29, 0x34, 0x9c,
      0x97, 0xb9, 0x5e, 0xed, 0xfd, 0x59, 0x9d, 0xd4, 0x83, 0xfb, 0xc2, 0xad,
      0x72, 0xe8, 0xf3, 0x68, 0x43, 0xd0, 0xd4, 0xdc, 0xb2, 0x36, 0xc1, 0xdb,
      0x03, 0x29, 0xee, 0x8c, 0x1a, 0x10, 0xd7, 0xfb, 0xc2, 0xea, 0xda, 0xba,
      0x42, 0x18, 0xe6, 0xe4, 0x63, 0x41, 0x6e, 0x8c, 0xb3, 0x14, 0x68, 0x9a,
      0x0f, 0x64, 0x87, 0x16, 0x02, 0xa3, 0x59, 0xf4, 0x60, 0x80, 0x04, 0x2b,
      0xa4, 0xe6, 0x10, 0x2d, 0x61, 0x59, 0x14, 0xfe, 0xaf, 0x84, 0xa0, 0x67,
  };
  BigNumStr exponent_b_str = {
      0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf0, 0xcd, 0x46, 0xe5, 0xf2,
      0x5e, 0xee, 0x71, 0xa4, 0x9e, 0x0c, 0xdc, 0x65, 0xfb, 0x12, 0x99,
      0x92, 0x1a, 0xf6, 0x2d, 0x53, 0x6c, 0xd1, 0x0b, 0x50, 0x0c,
  };
  const Fq12ElemStr fq12_exp_ab_str = {
      0xe4, 0xca, 0x0b, 0xa8, 0x9c, 0x70, 0xe2, 0x20, 0x24, 0xd9, 0x8f, 0x09,
      0x12, 0x79, 0x55, 0x44, 0x8c, 0x58, 0x42, 0x8f, 0x18, 0x97, 0x0a, 0xc3,
      0x0a, 0x40, 0x68, 0x0e, 0x1e, 0x21, 0x7f, 0xb4, 0x73, 0xcf, 0x8d, 0x54,
      0x68, 0xae, 0x1c, 0xc4, 0x95, 0x01, 0xe3, 0x52, 0x33, 0xfe, 0x69, 0xb5,
      0x23, 0x45, 0xef, 0xff, 0x79, 0x14, 0xef, 0x98, 0xa8, 0x4e, 0xb1, 0x2e,
      0xe1, 0x15, 0x81, 0x0c, 0x9f, 0x65, 0x90, 0x6a, 0x53, 0xd7, 0xf6, 0x73,
      0x08, 0xda, 0x45, 0x11, 0xde, 0xcf, 0xed, 0x4c, 0x8c, 0x21, 0x02, 0xf5,
      0x3f, 0x97, 0xb6, 0xb2, 0xf5, 0xc6, 0x58, 0xfc, 0x9a, 0xa6, 0xbb, 0xf9,
      0x3c, 0xd8, 0xfc, 0x86, 0x09, 0x1c, 0x16, 0xa4, 0x6f, 0x5e, 0x7a, 0x81,
      0x01, 0xf9, 0x8a, 0x6d, 0x38, 0xb5, 0xc9, 0x5f, 0xe9, 0x7d, 0x6f, 0x46,
      0x92, 0x49, 0x5b, 0xb8, 0x61, 0x03, 0x6f, 0x5e, 0xda, 0x53, 0xcb, 0x62,
      0xcd, 0x82, 0x63, 0x96, 0xc7, 0xc0, 0x39, 0xa4, 0x31, 0xee, 0x15, 0x26,
      0x1c, 0xe6, 0x4e, 0xe4, 0xb4, 0x40, 0xfa, 0x65, 0xa3, 0xf4, 0x73, 0x03,
      0x85, 0x84, 0x41, 0xf9, 0x77, 0xf3, 0x90, 0xdc, 0x88, 0x26, 0xbd, 0x27,
      0x9a, 0x9d, 0x37, 0xf0, 0x80, 0xee, 0x02, 0x1d, 0x3e, 0x4f, 0x88, 0xed,
      0x8f, 0x89, 0x90, 0xf1, 0x6d, 0x5d, 0x70, 0x49, 0x99, 0x07, 0x61, 0x22,
      0xbc, 0xad, 0x45, 0x11, 0x57, 0x13, 0x62, 0x9e, 0xc3, 0xb5, 0xb0, 0xd9,
      0x0b, 0x1f, 0xf4, 0x12, 0x86, 0xaa, 0x79, 0xf8, 0x9b, 0xaf, 0xc9, 0x63,
      0x4a, 0x6c, 0xf4, 0x38, 0xca, 0x11, 0xfb, 0x6c, 0xe2, 0xa0, 0x73, 0xdc,
      0xf0, 0x0c, 0x13, 0xff, 0x8c, 0x7e, 0x1c, 0xe6, 0x17, 0xe4, 0x72, 0x34,
      0x4b, 0x5c, 0xf6, 0x63, 0x43, 0x51, 0x1a, 0x7e, 0x57, 0x45, 0xd2, 0xed,
      0x7d, 0x0e, 0x6f, 0x9a, 0x69, 0x43, 0xfe, 0x87, 0x60, 0x3e, 0x65, 0x12,
      0xed, 0xc2, 0xf6, 0xfa, 0xd3, 0x3a, 0x48, 0x6b, 0xb5, 0x2c, 0xe2, 0xa0,
      0x7b, 0xad, 0xb3, 0xd8, 0x64, 0x8c, 0x32, 0xf4, 0xb4, 0xf3, 0x57, 0xcf,
      0x44, 0xb1, 0x40, 0x57, 0x13, 0xed, 0xda, 0x24, 0xed, 0xc6, 0xa8, 0x6a,
      0x61, 0xf1, 0x38, 0x4c, 0x8d, 0x67, 0xd1, 0x38, 0x2f, 0x6e, 0xd5, 0xe6,
      0x3b, 0x6f, 0xce, 0xed, 0xb1, 0x79, 0x92, 0x3f, 0x7c, 0x04, 0x3d, 0x52,
      0x8d, 0x13, 0xfd, 0x65, 0x03, 0x15, 0x1d, 0x82, 0x3c, 0x3a, 0xe2, 0xc4,
      0x09, 0xb2, 0x77, 0x6e, 0xf8, 0x87, 0x32, 0x87, 0x10, 0x3e, 0x53, 0x21,
      0x6c, 0xba, 0x49, 0x2f, 0x9c, 0xbe, 0x91, 0x73, 0x4c, 0xe8, 0x88, 0x33,
      0x37, 0x81, 0x6b, 0x48, 0xeb, 0xce, 0x4a, 0xaa, 0xac, 0x5c, 0x61, 0xcf,
      0x6d, 0xb1, 0xfa, 0x55, 0x71, 0xd0, 0x18, 0xdc, 0xff, 0x4e, 0x8f, 0xac,
  };

  THROW_ON_EPIDERR(ReadFfElement(this->fq12, &(fq12_a_str), sizeof(fq12_a_str),
                                 this->fq12_a));
  THROW_ON_EPIDERR(
      ReadBigNum(&exponent_b_str, sizeof(exponent_b_str), this->exponent_b));

  EXPECT_EQ(kEpidNoErr,
            FfExp(this->fq12, this->fq12_a, this->exponent_b, this->fq12_r));
  THROW_ON_EPIDERR(WriteFfElement(this->fq12, this->fq12_r, &fq12_r_str,
                                  sizeof(fq12_r_str)));
  EXPECT_EQ(fq12_exp_ab_str, fq12_r_str);
}

///////////////////////////////////////////////////////////////////////
// FfHash

TEST_F(FfElementTest, FfHashFailsGivenArgumentsMismatch) {
  EXPECT_EQ(kEpidBadArgErr, FfHash(this->fq12, this->sha_msg,
                                   sizeof(this->sha_msg), kSha256, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfHash(this->fq, this->sha_msg, sizeof(this->sha_msg), kSha256,
                   this->fq12_r));
}

TEST_F(FfElementTest, FfHashFailsGivenNullPointer) {
  uint8_t const msg[] = {0};
  EXPECT_EQ(kEpidBadArgErr,
            FfHash(nullptr, msg, sizeof(msg), kSha256, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfHash(this->fq, nullptr, sizeof(msg), kSha256, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfHash(this->fq, msg, sizeof(msg), kSha256, nullptr));
}

TEST_F(FfElementTest, FfHashFailsGivenUnsupportedHashAlg) {
  uint8_t const msg[] = {0};
  EXPECT_EQ(kEpidHashAlgorithmNotSupported,
            FfHash(this->fq, msg, sizeof(msg), kSha512_256, this->fq_r));
  EXPECT_EQ(kEpidHashAlgorithmNotSupported,
            FfHash(this->fq, msg, sizeof(msg), kSha3_256, this->fq_r));
  EXPECT_EQ(kEpidHashAlgorithmNotSupported,
            FfHash(this->fq, msg, sizeof(msg), kSha3_384, this->fq_r));
  EXPECT_EQ(kEpidHashAlgorithmNotSupported,
            FfHash(this->fq, msg, sizeof(msg), kSha3_512, this->fq_r));
}

TEST_F(FfElementTest, FfHashFailsGivenIncorrectMsgLen) {
  uint8_t const msg[] = {0};
  size_t len_greater_int_max = (size_t)INT_MAX + 1;
  EXPECT_EQ(kEpidBadArgErr, FfHash(this->fq, msg, 0, kSha256, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfHash(this->fq, msg, std::numeric_limits<size_t>::max(), kSha256,
                   this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfHash(this->fq, msg, len_greater_int_max, kSha256, this->fq_r));
}

TEST_F(FfElementTest, FfHashWorksGivenSHA256HashAlg) {
  FqElemStr fq_r_data;
  EXPECT_EQ(kEpidNoErr,
            FfHash(this->fq, sha_msg, sizeof(sha_msg), kSha256, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_r_sha256, fq_r_data)
      << "FfHash: Hash element does not match to reference value";
}

TEST_F(FfElementTest, FfHashWorksGivenSHA384HashAlg) {
  FqElemStr fq_r_data;
  EXPECT_EQ(kEpidNoErr,
            FfHash(this->fq, sha_msg, sizeof(sha_msg), kSha384, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_r_sha384, fq_r_data)
      << "FfHash: Hash element does not match to reference value";
}

TEST_F(FfElementTest, FfHashWorksGivenSHA512HashAlg) {
  FqElemStr fq_r_data;
  EXPECT_EQ(kEpidNoErr,
            FfHash(this->fq, sha_msg, sizeof(sha_msg), kSha512, this->fq_r));
  THROW_ON_EPIDERR(
      WriteFfElement(this->fq, this->fq_r, &fq_r_data, sizeof(fq_r_data)));
  EXPECT_EQ(this->fq_r_sha512, fq_r_data)
      << "FfHash: Hash element does not match to reference value";
}

///////////////////////////////////////////////////////////////////////
// FfMultiExp

TEST_F(FfElementTest, FfMultiExpFailsGivenArgumentsMismatch) {
  FfElement const* p[] = {this->fq_a, this->fq_b};
  FfElement const* p12[] = {this->fq12_a, this->fq12_b};
  const BigNumStr bnm0 = {0};
  const BigNumStr bnm1 = {0};
  BigNumStr const* b[] = {&bnm0, &bnm1};
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq12, p, b, 2, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, p12, b, 2, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, p, b, 2, this->fq12_r));
}

TEST_F(FfElementTest, FfMultiExpFailsGivenNullPointer) {
  FfElement const* p[] = {this->fq_a, this->fq_b};
  FfElement const* p_withnull[] = {nullptr, this->fq_b};
  const BigNumStr bnm0 = {{0x11, 0xFF, 0xFF, 0xFF, 0x4F, 0x59, 0xB1, 0xD3, 0x6B,
                           0x08, 0xFF, 0xFF, 0x0B, 0xF3, 0xAF, 0x27, 0xFF, 0xB8,
                           0xFF, 0xFF, 0x98, 0xFF, 0xEB, 0xFF, 0xF2, 0x6A, 0xFF,
                           0xFF, 0xEA, 0x31, 0xFF, 0xFF}};
  const BigNumStr bnm1 = {{0xE2, 0xFF, 0x03, 0x1D, 0xFF, 0x19, 0x81, 0xCB, 0xFF,
                           0xFF, 0x6B, 0xD5, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD,
                           0xFF, 0x5A, 0xFF, 0x5C, 0x7C, 0xFF, 0x84, 0xFF, 0xFF,
                           0x8C, 0x03, 0xB2, 0x26, 0xFF}};
  BigNumStr const* b[] = {&bnm0, &bnm1};
  BigNumStr const* b_withnull[] = {nullptr, &bnm1};
  size_t m = 2;
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(nullptr, p, b, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, nullptr, b, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, p, nullptr, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, p, b, m, nullptr));
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, p_withnull, b, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, p, b_withnull, m, this->fq_r));
}

TEST_F(FfElementTest, FfMultiExpFailsGivenIncorrectMLen) {
  FfElement const* p[] = {this->fq_a, this->fq_b};
  const BigNumStr bnm0 = {{0x11, 0xFF, 0xFF, 0xFF, 0x4F, 0x59, 0xB1, 0xD3, 0x6B,
                           0x08, 0xFF, 0xFF, 0x0B, 0xF3, 0xAF, 0x27, 0xFF, 0xB8,
                           0xFF, 0xFF, 0x98, 0xFF, 0xEB, 0xFF, 0xF2, 0x6A, 0xFF,
                           0xFF, 0xEA, 0x31, 0xFF, 0xFF}};
  const BigNumStr bnm1 = {{0xE2, 0xFF, 0x03, 0x1D, 0xFF, 0x19, 0x81, 0xCB, 0xFF,
                           0xFF, 0x6B, 0xD5, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD,
                           0xFF, 0x5A, 0xFF, 0x5C, 0x7C, 0xFF, 0x84, 0xFF, 0xFF,
                           0x8C, 0x03, 0xB2, 0x26, 0xFF}};
  BigNumStr const* b[] = {&bnm0, &bnm1};
  size_t len_greater_int_max = (size_t)INT_MAX + 1;
  EXPECT_EQ(kEpidBadArgErr, FfMultiExp(this->fq, p, b, 0, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfMultiExp(this->fq, p, b, std::numeric_limits<size_t>::max(),
                       this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfMultiExp(this->fq, p, b, len_greater_int_max, this->fq_r));
}

TEST_F(FfElementTest, FfMultiExpWorksGivenOneExponent) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[1];
  FfElement const* p[1];
  BigNumStr const* b[1];
  int m = 0;
  const FqElemStr ffe[] = {{
      0x12, 0xA6, 0x5B, 0xD6, 0x91, 0x8D, 0x50, 0xA7, 0x66, 0xEB, 0x7D, 0x52,
      0xE3, 0x40, 0x17, 0x60, 0x7F, 0xDF, 0x6C, 0xA1, 0x2C, 0x1A, 0x37, 0xE0,
      0x92, 0xC0, 0xF7, 0xB9, 0x76, 0xAB, 0xB1, 0x8A,
  }};
  const BigNumStr bnm[] = {{
      0xFF, 0xFB, 0x3E, 0x5D, 0xFF, 0x9A, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xFF,
      0xF2, 0xE1, 0x85, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81,
      0xFF, 0xFD, 0xFF, 0xEB, 0xFF, 0x29, 0xA7, 0xFF,
  }};
  const FqElemStr res = {
      0x6A, 0x21, 0xEC, 0x89, 0xCC, 0x13, 0x2F, 0x6F, 0x29, 0x1B, 0x3A,
      0x94, 0xF6, 0xE6, 0xA8, 0xBD, 0x98, 0x25, 0x43, 0x7A, 0xDC, 0xC1,
      0x20, 0xBA, 0x30, 0xD8, 0x1C, 0x8E, 0x79, 0xFC, 0xDA, 0x67,
  };
  // prepare data for test
  for (m = 0; m < 1; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfMultiExp(this->fq, p, b, 1, r));
  // verify calculation is correct
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfMultiExp: Finite field element does not match with reference value";
}

TEST_F(FfElementTest, FfMultiExpWorksGivenTwoExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[2];
  FfElement const* p[2];
  BigNumStr const* b[2];
  int m = 0;
  const FqElemStr ffe[] = {
      {0xE6, 0x65, 0x23, 0x9B, 0xD4, 0x07, 0x16, 0x83, 0x38, 0x23, 0xB2, 0x67,
       0x57, 0xEB, 0x0F, 0x23, 0x3A, 0xF4, 0x8E, 0xDA, 0x71, 0x5E, 0xD9, 0x98,
       0x63, 0x98, 0x2B, 0xBC, 0x78, 0xD1, 0x94, 0xF2},
      {0xDD, 0x2B, 0xE9, 0x59, 0x24, 0xA5, 0xB3, 0xFD, 0xEB, 0xE1, 0x3C, 0xC0,
       0x73, 0x4E, 0x99, 0xEE, 0x36, 0xF6, 0xC0, 0x1A, 0x76, 0x01, 0x0A, 0xF0,
       0xCB, 0xB4, 0x71, 0x88, 0x95, 0xCB, 0x35, 0xBA}};
  const BigNumStr bnm[] = {
      {0x11, 0xFF, 0xFF, 0xFF, 0x4F, 0x59, 0xB1, 0xD3, 0x6B, 0x08, 0xFF, 0xFF,
       0x0B, 0xF3, 0xAF, 0x27, 0xFF, 0xB8, 0xFF, 0xFF, 0x98, 0xFF, 0xEB, 0xFF,
       0xF2, 0x6A, 0xFF, 0xFF, 0xEA, 0x31, 0xFF, 0xFF},
      {0xE2, 0xFF, 0x03, 0x1D, 0xFF, 0x19, 0x81, 0xCB, 0xFF, 0xFF, 0x6B, 0xD5,
       0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0xFF, 0x5A, 0xFF, 0x5C, 0x7C, 0xFF,
       0x84, 0xFF, 0xFF, 0x8C, 0x03, 0xB2, 0x26, 0xFF}};
  const FqElemStr res = {
      0x8A, 0xF7, 0x96, 0x53, 0x9C, 0xC3, 0x57, 0x89, 0x50, 0xE3, 0xAB,
      0x12, 0x62, 0xEA, 0x0C, 0xE4, 0x65, 0xA6, 0x91, 0x09, 0x93, 0x81,
      0xFC, 0xBB, 0x0E, 0xFA, 0xC1, 0xC4, 0xDC, 0x3F, 0x0A, 0xD1,
  };
  // prepare data for test
  for (m = 0; m < 2; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfMultiExp(this->fq, p, b, 2, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfMultiExp: Finite field element does not match with reference value";
}

TEST_F(FfElementTest, FfMultiExpWorksGivenThreeExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[3];
  FfElement const* p[3];
  BigNumStr const* b[3];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x24, 0x9D, 0xDD, 0x1A, 0xC0, 0x07, 0x1F, 0x65, 0xDF, 0xBB, 0xC7, 0x39,
       0x99, 0x44, 0x63, 0x8E, 0x7E, 0xB2, 0x85, 0x82, 0xC6, 0x4E, 0x2D, 0x39,
       0xA8, 0x3F, 0x12, 0xB4, 0xE9, 0x9D, 0x67, 0x28},
      {0x6B, 0x51, 0x6B, 0x33, 0x0B, 0x2E, 0x2A, 0x94, 0x34, 0xCC, 0x7E, 0x97,
       0xFA, 0x85, 0x51, 0x44, 0x30, 0xE5, 0xF2, 0x68, 0x23, 0x31, 0x84, 0xFF,
       0xBB, 0x95, 0x9A, 0xB2, 0x80, 0x95, 0xC4, 0x91},
      {0x77, 0xED, 0xA2, 0x53, 0xA3, 0xBF, 0x6C, 0x58, 0x16, 0x7D, 0xEF, 0x8A,
       0x7D, 0x38, 0x65, 0x3F, 0x92, 0xA6, 0x85, 0xD7, 0x9F, 0x80, 0xA4, 0xA1,
       0x70, 0x02, 0xE5, 0x66, 0xF2, 0xD8, 0xD7, 0xD2}};
  const BigNumStr bnm[] = {
      {0xFF, 0xFF, 0xFF, 0xB4, 0x48, 0xFF, 0xFF, 0xFF, 0xA3, 0xFF, 0xE3, 0xFF,
       0x61, 0xFF, 0xA2, 0x97, 0x9A, 0xFF, 0x6F, 0x04, 0xC6, 0xB9, 0xFF, 0x22,
       0xFF, 0x20, 0x9E, 0xFF, 0x43, 0xFF, 0xFF, 0xFF},
      {0xB1, 0x19, 0xBF, 0xFF, 0x74, 0x7F, 0xCC, 0x34, 0x1E, 0x30, 0x7D, 0xC5,
       0xC4, 0xFF, 0xE7, 0xF8, 0xFF, 0x5F, 0xFF, 0x73, 0xFF, 0x58, 0xFF, 0xD6,
       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0xFF, 0xFF},
      {0xFF, 0xFF, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF, 0x26, 0xA8, 0xFF, 0xFF, 0xFF,
       0x3E, 0xFF, 0xFF, 0xFF, 0xE0, 0x4F, 0xFF, 0xE3, 0xFF, 0xFF, 0xEB, 0x04,
       0x4B, 0xFF, 0x5D, 0x55, 0xFF, 0x2D, 0xFF, 0xFF}};
  const FqElemStr res = {
      0x04, 0x22, 0x21, 0xE7, 0x9F, 0xE3, 0x07, 0x2F, 0xA4, 0xBB, 0x56,
      0x84, 0xD5, 0x93, 0x4A, 0xDA, 0x5B, 0x89, 0x07, 0xBE, 0xC5, 0x5F,
      0xE6, 0x5C, 0x18, 0xF0, 0xF8, 0x8E, 0x8E, 0x00, 0xB9, 0x87,
  };
  // prepare data for test
  for (m = 0; m < 3; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfMultiExp(this->fq, p, b, 3, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfMultiExp: Finite field element does not match with reference value";
}

TEST_F(FfElementTest, FfMultiExpWorksGivenFourExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[4];
  FfElement const* p[4];
  BigNumStr const* b[4];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x81, 0xEE, 0x78, 0x31, 0x4C, 0xC0, 0x3C, 0xDD, 0x16, 0x52, 0xC6, 0x7E,
       0x07, 0x4C, 0xFE, 0xC4, 0x50, 0x63, 0x5A, 0x40, 0xC8, 0xDF, 0x94, 0x23,
       0x1A, 0x90, 0xDD, 0x24, 0x61, 0x23, 0xDD, 0x87},
      {0x76, 0xBB, 0x2C, 0x1C, 0xB3, 0x78, 0x93, 0x7E, 0x10, 0x29, 0xC3, 0xEC,
       0xBD, 0xA3, 0x75, 0x3A, 0xF1, 0x0B, 0xE1, 0xCA, 0xEE, 0x02, 0x58, 0x74,
       0xC7, 0xDC, 0xB8, 0x4F, 0x3F, 0x7B, 0xA2, 0xFC},
      {0x3A, 0xD3, 0x04, 0xAA, 0x9D, 0x2A, 0xDE, 0x86, 0x56, 0x84, 0x80, 0x42,
       0xA5, 0x68, 0xD8, 0x6E, 0x65, 0xC4, 0x08, 0x07, 0x87, 0x4B, 0xC1, 0x92,
       0xCB, 0x68, 0x52, 0x6C, 0x5A, 0x4F, 0x7C, 0xFB},
      {0x56, 0xE7, 0xBD, 0x59, 0x54, 0xCC, 0x20, 0x16, 0xAE, 0x36, 0xD1, 0xB0,
       0xE4, 0x82, 0x47, 0x27, 0x32, 0xD1, 0x38, 0x7C, 0x53, 0x7F, 0xDD, 0xF4,
       0xDE, 0xFA, 0xF7, 0x0F, 0xDD, 0xDF, 0x48, 0xA9}};
  const BigNumStr bnm[] = {
      {0xFF, 0xA5, 0xC3, 0xFF, 0xFF, 0x09, 0x18, 0x18, 0x95, 0x40, 0xFF, 0xFF,
       0xDB, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xFF, 0x25, 0xFF, 0xFF, 0x42, 0x07,
       0xB5, 0xFF, 0x26, 0xE0, 0x4F, 0xB5, 0xB4, 0xFF},
      {0x1F, 0xFF, 0xAA, 0xF1, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0x07, 0xFF,
       0xE7, 0xDF, 0xFF, 0xEE, 0xFF, 0x57, 0xFF, 0xFF, 0xFF, 0xB8, 0xB3, 0x9C,
       0xFF, 0x8A, 0x89, 0xFF, 0xFE, 0x32, 0xFF, 0xC1},
      {0x9A, 0xFF, 0xFF, 0x9B, 0xFF, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
       0xFF, 0x36, 0xF0, 0xC5, 0xAE, 0x87, 0x41, 0xFF, 0x5C, 0xDD, 0xFF, 0xFF,
       0xFF, 0xFF, 0x59, 0xC9, 0xFF, 0x44, 0x5C, 0x58},
      {0x00, 0xFF, 0x95, 0xFF, 0x21, 0xFF, 0xFF, 0x1D, 0x1A, 0x06, 0xD3, 0xFF,
       0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xB0, 0x31, 0xFF, 0xFF, 0x1F,
       0x61, 0x48, 0xFF, 0xFF, 0xFF, 0x17, 0xC9, 0x79}};
  const FqElemStr res = {
      0x26, 0x2C, 0x23, 0x55, 0xD5, 0xBD, 0x7B, 0x67, 0x60, 0xFE, 0x06,
      0x7B, 0xCC, 0xB8, 0x6B, 0xC9, 0x00, 0x81, 0xAE, 0xCB, 0xEC, 0x91,
      0x6D, 0x1B, 0x9E, 0x1C, 0xD8, 0xC0, 0x8D, 0x3F, 0x1B, 0x20,
  };
  // prepare data for test
  for (m = 0; m < 4; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfMultiExp(this->fq, p, b, 4, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfMultiExp: Finite field element does not match with reference value";

  const Fq12ElemStr ff12e[] = {
      {
          0x2c, 0x86, 0xee, 0x80, 0x61, 0x45, 0x8f, 0xfd, 0x96, 0xc2, 0x81,
          0x11, 0xe8, 0x96, 0xe2, 0xca, 0x21, 0x78, 0xf7, 0xa3, 0x4b, 0xba,
          0xdc, 0x0d, 0x34, 0x8a, 0x56, 0x12, 0x67, 0x8d, 0x72, 0x37, 0xa8,
          0xeb, 0x5f, 0x1a, 0x1b, 0xf0, 0x5c, 0xc6, 0x6f, 0xc2, 0xc5, 0x03,
          0xc4, 0x19, 0x85, 0xa6, 0xe0, 0x64, 0xfe, 0x71, 0x51, 0x46, 0xdf,
          0xc2, 0xd6, 0xf9, 0xa0, 0xd6, 0x84, 0xc1, 0x4a, 0x11, 0xe0, 0x33,
          0xa3, 0x1c, 0x73, 0x71, 0x62, 0x17, 0x10, 0x4c, 0xd8, 0x3e, 0xee,
          0xa1, 0x38, 0x9f, 0xa5, 0xfb, 0x1d, 0xc4, 0x9d, 0x2f, 0x08, 0x4a,
          0xd3, 0x61, 0x37, 0x79, 0xab, 0xc1, 0x37, 0x7e, 0x90, 0xfd, 0xa3,
          0x9d, 0x0c, 0x81, 0x52, 0xf4, 0x58, 0x24, 0x89, 0xc4, 0xf4, 0xd4,
          0x82, 0x33, 0xca, 0x5d, 0xb4, 0x58, 0xa6, 0x1e, 0xe6, 0x2b, 0x86,
          0xef, 0x56, 0xc4, 0x4f, 0x9b, 0x27, 0x3c, 0x96, 0x54, 0xda, 0xb5,
          0x32, 0x23, 0x8a, 0x93, 0xcc, 0xa5, 0x7d, 0x28, 0x4a, 0x97, 0xa4,
          0x30, 0xa8, 0x11, 0xbc, 0x43, 0x22, 0x7f, 0x02, 0x73, 0x0e, 0x5c,
          0x63, 0x52, 0x2f, 0x4b, 0x5f, 0x7d, 0xc3, 0x1e, 0x30, 0xae, 0xac,
          0x8c, 0xe8, 0x93, 0xd6, 0xce, 0x05, 0x4b, 0x6b, 0x21, 0x01, 0x14,
          0x4a, 0x1d, 0xd9, 0x08, 0x0c, 0xa1, 0x4c, 0x0b, 0x44, 0x28, 0x9c,
          0xb6, 0xd1, 0x96, 0xbd, 0x07, 0x8b, 0x3d, 0x77, 0x4e, 0xbd, 0x7b,
          0x9f, 0x65, 0x20, 0xdb, 0xa0, 0x95, 0x25, 0x44, 0x44, 0x7a, 0xe3,
          0xdc, 0xdd, 0x2f, 0xba, 0x75, 0x16, 0x19, 0xcb, 0x90, 0x31, 0x79,
          0x38, 0xb6, 0x1a, 0x82, 0xbc, 0xb6, 0x85, 0xcd, 0xeb, 0xb9, 0x13,
          0x43, 0xea, 0xc2, 0x78, 0xbe, 0x25, 0x97, 0x1c, 0x6e, 0x18, 0x55,
          0xfd, 0xcb, 0xe6, 0x7e, 0x1e, 0x92, 0xad, 0x48, 0xe7, 0xa7, 0x02,
          0x87, 0x0b, 0x8f, 0x16, 0xd7, 0x8e, 0x95, 0x8e, 0x62, 0x0a, 0xad,
          0x20, 0x2d, 0x6a, 0x2b, 0x4e, 0x6f, 0xc1, 0xc2, 0x63, 0xba, 0x82,
          0xf3, 0x8c, 0xd0, 0xa2, 0x09, 0xd3, 0xe5, 0xba, 0x1f, 0x65, 0x35,
          0xd0, 0x1c, 0x09, 0x98, 0x47, 0x65, 0xf2, 0x90, 0xd4, 0xe2, 0xd0,
          0x6f, 0x9e, 0xdb, 0xa2, 0x14, 0x9b, 0x4b, 0x3d, 0xaa, 0x75, 0x25,
          0x67, 0xf4, 0x7d, 0x77, 0xe9, 0x24, 0xed, 0xee, 0x1b, 0x07, 0x7f,
          0xe0, 0x7a, 0x34, 0xb7, 0x3c, 0x36, 0x0d, 0xce, 0xf9, 0xbf, 0xf9,
          0xe5, 0x09, 0x86, 0xeb, 0xb9, 0xd8, 0xc4, 0x67, 0xc5, 0xcd, 0xd4,
          0xbc, 0xee, 0x45, 0xf7, 0x11, 0x68, 0xd9, 0x23, 0x6d, 0xd6, 0x24,
          0x6e, 0x97, 0x83, 0x13, 0x76, 0x9f, 0x35, 0xea, 0xb8, 0x47, 0x57,
          0x9b, 0x74, 0x47, 0x1f, 0x19, 0xe7, 0x97, 0x74, 0x1b, 0xb1, 0xa0,
          0xf3, 0x92, 0xdc, 0x35, 0xfd, 0x7f, 0x4f, 0x07, 0x22, 0xac,
      },
      {
          0xfd, 0x05, 0x41, 0x27, 0x00, 0x7a, 0xad, 0x17, 0xbc, 0xbf, 0x22,
          0x5e, 0xda, 0xeb, 0x7d, 0xaa, 0xb5, 0x2d, 0xc8, 0x61, 0xfe, 0x2c,
          0xba, 0x9e, 0x54, 0x43, 0x94, 0xdc, 0xfd, 0x0e, 0x6d, 0x6d, 0xa1,
          0x5e, 0x05, 0xa5, 0x05, 0x55, 0xe7, 0x2a, 0xf7, 0x53, 0x37, 0x2c,
          0xef, 0x44, 0x95, 0xca, 0x2c, 0xe8, 0xeb, 0xae, 0x61, 0x05, 0x33,
          0xe7, 0x0f, 0x30, 0x67, 0xbe, 0xcd, 0xb2, 0x93, 0x44, 0x44, 0x31,
          0x96, 0x4d, 0x72, 0x61, 0xcd, 0x23, 0x1f, 0x7b, 0x57, 0x33, 0x39,
          0xac, 0x57, 0x53, 0x3d, 0x70, 0x57, 0xb3, 0x6d, 0xd5, 0x8a, 0x89,
          0x30, 0xbc, 0xed, 0x29, 0x19, 0x39, 0x6e, 0x6a, 0x80, 0x21, 0x1e,
          0xb7, 0x84, 0xb1, 0x0a, 0xa4, 0xd0, 0xff, 0x59, 0x31, 0xe9, 0xba,
          0x39, 0xf7, 0x81, 0xdf, 0x1b, 0x7b, 0xd9, 0xdb, 0x02, 0x17, 0xbe,
          0x97, 0x63, 0xbd, 0x37, 0x14, 0xb8, 0x8b, 0x6c, 0xcc, 0x5b, 0xa3,
          0xee, 0xc8, 0x0b, 0x7b, 0xf5, 0xde, 0xbc, 0xb7, 0xa8, 0x25, 0x35,
          0xae, 0x0b, 0x30, 0x96, 0x33, 0x52, 0x13, 0xa3, 0x89, 0x3a, 0x2e,
          0xa4, 0x7e, 0x5f, 0x4e, 0xb1, 0xf7, 0x87, 0xba, 0x08, 0xb0, 0xfb,
          0xcb, 0x31, 0xa6, 0x2b, 0x3e, 0xe7, 0xc4, 0xcc, 0xa5, 0x14, 0x61,
          0xc9, 0x18, 0xfc, 0x74, 0x59, 0x06, 0xf4, 0xad, 0xfc, 0x2d, 0x0c,
          0x7d, 0xdb, 0x2a, 0xec, 0xd4, 0xe9, 0x31, 0x31, 0x50, 0x25, 0xb7,
          0x0c, 0x48, 0x30, 0xd5, 0x78, 0xb1, 0x10, 0xea, 0x5d, 0xaa, 0xaa,
          0x03, 0xec, 0xe3, 0x0f, 0x36, 0xa6, 0xbc, 0x18, 0x3a, 0x76, 0x87,
          0xa3, 0xe9, 0x33, 0xda, 0x48, 0x78, 0x93, 0x64, 0x72, 0x9b, 0x2a,
          0x1e, 0x0d, 0x50, 0x9f, 0x6f, 0xbd, 0xa5, 0x18, 0xb1, 0xa8, 0x14,
          0x9a, 0x1b, 0xb9, 0xb4, 0x2c, 0x1f, 0x46, 0xb8, 0x9b, 0x7a, 0xc7,
          0x84, 0xae, 0x27, 0xd1, 0xec, 0x1c, 0xec, 0x69, 0xf0, 0x35, 0x40,
          0x77, 0x7e, 0x27, 0x22, 0x36, 0x38, 0xdf, 0x15, 0xc2, 0xb3, 0xb8,
          0x63, 0x4a, 0x6e, 0x68, 0x8c, 0xc1, 0x39, 0x3c, 0x02, 0x4c, 0x2a,
          0x31, 0x18, 0x2e, 0x35, 0xf5, 0xca, 0xa2, 0xe9, 0x24, 0x92, 0xd5,
          0xe5, 0x2e, 0x5d, 0x1c, 0x94, 0xd9, 0xc7, 0xf0, 0x22, 0x16, 0x20,
          0xa8, 0x37, 0xdd, 0x0e, 0xab, 0x1b, 0x5e, 0x8b, 0xee, 0x81, 0xc6,
          0x17, 0xf1, 0x29, 0x5d, 0x1d, 0x6f, 0x53, 0x5a, 0xb2, 0x20, 0x88,
          0x3d, 0xa4, 0x98, 0x81, 0xa4, 0xef, 0x11, 0x1b, 0x64, 0x9e, 0x2c,
          0x95, 0xdd, 0xd7, 0x57, 0xb3, 0x95, 0xca, 0x40, 0xb9, 0x0d, 0x62,
          0xdc, 0x86, 0x8b, 0xe6, 0xd3, 0x7b, 0x33, 0xea, 0x50, 0x25, 0xe2,
          0x77, 0xe3, 0x0b, 0x5a, 0xc8, 0x81, 0xfc, 0xae, 0x74, 0x38, 0x69,
          0x07, 0xf8, 0x4e, 0xd7, 0x85, 0xa6, 0x2f, 0x13, 0xb7, 0x0a,
      },
      {
          0xf4, 0xdc, 0xac, 0x02, 0xc0, 0x95, 0x07, 0xdc, 0x12, 0x4f, 0x3a,
          0x01, 0xa9, 0xee, 0xf3, 0x07, 0x46, 0x94, 0x47, 0x15, 0x63, 0xa6,
          0x8e, 0x0a, 0xc4, 0xc4, 0xe4, 0x23, 0x85, 0x63, 0xe4, 0x4e, 0x9a,
          0x9e, 0x9f, 0xc1, 0xed, 0x4e, 0x94, 0x10, 0xfe, 0x7d, 0x90, 0xef,
          0x82, 0x21, 0xac, 0x67, 0x24, 0x99, 0x86, 0xc6, 0x85, 0x8b, 0x19,
          0x9a, 0x9b, 0x90, 0x0b, 0xa4, 0x90, 0x78, 0x34, 0x53, 0xb1, 0x4c,
          0xba, 0xb8, 0xc6, 0x40, 0x71, 0xc2, 0x17, 0x51, 0x05, 0xc2, 0xc9,
          0xbc, 0x03, 0x10, 0x4e, 0xfe, 0x2f, 0x94, 0xed, 0x13, 0x35, 0x7b,
          0x20, 0xa6, 0x59, 0x44, 0x81, 0xd0, 0xec, 0x12, 0x74, 0xa2, 0x4a,
          0xfb, 0xd7, 0x28, 0x70, 0xad, 0xde, 0x86, 0x2f, 0xef, 0xf9, 0x50,
          0x5d, 0x1e, 0x46, 0x3b, 0x82, 0x1c, 0x6a, 0xc9, 0x3f, 0x32, 0xae,
          0xcd, 0x93, 0x2c, 0xa5, 0x70, 0x45, 0x24, 0x38, 0x19, 0x79, 0xbf,
          0x35, 0xf5, 0xc1, 0x53, 0x06, 0x42, 0xaa, 0x6b, 0xf3, 0xb7, 0x8f,
          0xfb, 0xfc, 0x28, 0x46, 0x9a, 0x65, 0x2a, 0x45, 0x3d, 0x4c, 0x3c,
          0x6b, 0x9c, 0x44, 0xc5, 0x82, 0xb8, 0x80, 0xf9, 0xb3, 0x44, 0x95,
          0x17, 0x39, 0x01, 0x6b, 0xd6, 0x1d, 0x73, 0x22, 0x94, 0xff, 0x6f,
          0x7b, 0x4e, 0xb6, 0xa4, 0xe4, 0x6c, 0xa8, 0xdb, 0x38, 0x74, 0x0b,
          0xdb, 0xfa, 0x14, 0x99, 0x39, 0x28, 0xee, 0xfc, 0x62, 0xe5, 0x33,
          0x9c, 0x63, 0xd5, 0x7f, 0x2c, 0x4c, 0xf4, 0x76, 0x4c, 0xf7, 0x7b,
          0x27, 0x2c, 0xa9, 0xb9, 0x01, 0x3c, 0x58, 0xf7, 0xce, 0xaa, 0x0a,
          0x21, 0xc0, 0xa3, 0xa7, 0xa1, 0x85, 0xbc, 0xfe, 0x55, 0xfc, 0x3b,
          0x9b, 0x3d, 0xef, 0x14, 0x85, 0x8a, 0x4e, 0x5e, 0x1f, 0x4f, 0xc9,
          0x86, 0xb8, 0xf2, 0xb2, 0xe3, 0xe7, 0x7e, 0x87, 0xe2, 0x20, 0xd7,
          0x85, 0x2c, 0xd1, 0x13, 0xcb, 0x46, 0xad, 0xd7, 0xec, 0x66, 0xd5,
          0x2b, 0x6e, 0x87, 0x92, 0x13, 0xb2, 0x7d, 0x18, 0x69, 0x35, 0xcb,
          0xf1, 0xbe, 0xc2, 0x9d, 0xeb, 0xf5, 0x00, 0x0b, 0x73, 0xa3, 0xfd,
          0x51, 0xdc, 0x91, 0x0e, 0xe4, 0x46, 0x41, 0x23, 0xdd, 0x85, 0x53,
          0x0c, 0xae, 0xf9, 0x74, 0xcf, 0x1c, 0x32, 0x22, 0xda, 0x4a, 0x14,
          0x80, 0x71, 0x51, 0x23, 0xc3, 0xab, 0x7e, 0xec, 0x77, 0xcc, 0x59,
          0x12, 0x1c, 0x0f, 0x65, 0x6b, 0xd8, 0x41, 0xe0, 0x1b, 0x51, 0x9c,
          0x89, 0x26, 0xab, 0x88, 0x63, 0x5d, 0xbb, 0x95, 0x92, 0x5b, 0x2c,
          0x1d, 0xde, 0xc1, 0x88, 0xd6, 0xf1, 0x02, 0x48, 0xe0, 0xe2, 0xd1,
          0x40, 0x7f, 0xf1, 0x23, 0x4a, 0xcf, 0x36, 0xd5, 0xcf, 0x76, 0x0d,
          0x00, 0x2a, 0xf7, 0x5f, 0x31, 0xbb, 0xcc, 0x46, 0x84, 0x07, 0x04,
          0x98, 0x9c, 0xb4, 0x31, 0x15, 0x2d, 0x91, 0xf6, 0x29, 0x33,
      },
      {
          0xa8, 0x8e, 0x9a, 0xf9, 0x25, 0x12, 0x98, 0xe2, 0xc3, 0x61, 0x2e,
          0xe8, 0xd6, 0xa6, 0x77, 0x16, 0x49, 0x04, 0x75, 0x69, 0xd1, 0x83,
          0x2d, 0x3f, 0x2a, 0x79, 0xb6, 0x9b, 0xc9, 0x1d, 0x03, 0x90, 0x2a,
          0xd8, 0x11, 0x9f, 0x26, 0x36, 0xe7, 0xe9, 0x3a, 0x05, 0x4c, 0x15,
          0x49, 0x93, 0xda, 0xe9, 0xd0, 0x5a, 0xe4, 0x8d, 0x8a, 0xfa, 0x04,
          0xf1, 0x20, 0x84, 0x56, 0xec, 0x3c, 0x27, 0x19, 0x5c, 0xf1, 0xaf,
          0xbf, 0xf6, 0x0e, 0x58, 0x84, 0x2d, 0x94, 0x11, 0xf4, 0xb5, 0xf4,
          0x14, 0x51, 0xb0, 0x90, 0x46, 0x1a, 0x81, 0xed, 0xcf, 0x91, 0x66,
          0x58, 0xa6, 0x36, 0x3a, 0x52, 0x18, 0x5a, 0xc1, 0x08, 0x4c, 0x99,
          0xd3, 0xdc, 0xce, 0x7f, 0xce, 0x78, 0xe0, 0x38, 0x87, 0x32, 0xf1,
          0x80, 0x3c, 0x7b, 0x67, 0xaa, 0x6f, 0xdd, 0xe0, 0xfc, 0xcb, 0xd0,
          0xb0, 0x3a, 0x59, 0x52, 0x2a, 0x84, 0xe4, 0xf8, 0x4a, 0xff, 0x50,
          0xa0, 0x65, 0xc4, 0xee, 0xf4, 0x9c, 0xaa, 0x34, 0x46, 0xf9, 0xd2,
          0x6c, 0xa1, 0x61, 0x71, 0x49, 0x32, 0x25, 0x84, 0x54, 0x90, 0x44,
          0xbe, 0xa4, 0x0b, 0xf7, 0xfe, 0x26, 0x81, 0x63, 0x73, 0xf7, 0x2f,
          0xf2, 0xfa, 0x24, 0x52, 0xa4, 0xd9, 0x4c, 0xc1, 0xa7, 0xa5, 0xc3,
          0x03, 0x36, 0x13, 0x9b, 0x16, 0x45, 0x16, 0xcb, 0x4b, 0x99, 0x38,
          0xf3, 0x6d, 0xc8, 0x7e, 0xab, 0xb3, 0x53, 0xdf, 0xb6, 0x82, 0x60,
          0x12, 0x11, 0x36, 0x69, 0x0e, 0x05, 0x31, 0x8e, 0xcf, 0xd7, 0x3f,
          0x32, 0xe7, 0x95, 0x84, 0x1d, 0xc8, 0xb5, 0xbe, 0x49, 0x17, 0x9d,
          0xcf, 0xa9, 0x5a, 0x2a, 0xc4, 0x11, 0x86, 0xe8, 0x6c, 0x02, 0x56,
          0xb0, 0x25, 0x2f, 0xa0, 0x06, 0xb3, 0x62, 0xb2, 0x11, 0xaf, 0xbe,
          0xa4, 0xe8, 0x61, 0x64, 0x85, 0xfb, 0xeb, 0x1c, 0xf1, 0xbc, 0x2c,
          0xae, 0x10, 0x51, 0x16, 0xa6, 0xc0, 0xb3, 0x86, 0x8e, 0x6d, 0x79,
          0xb6, 0xbd, 0xde, 0x1e, 0x26, 0x06, 0x46, 0x65, 0x82, 0x84, 0x5a,
          0x97, 0xd3, 0xb7, 0x93, 0x78, 0x6b, 0x9d, 0x14, 0x33, 0x94, 0x43,
          0x34, 0x04, 0x45, 0xd1, 0x47, 0xd4, 0x2f, 0x17, 0xcf, 0xf1, 0xdd,
          0xea, 0x11, 0x52, 0xae, 0x01, 0x88, 0x3a, 0x10, 0xee, 0x5c, 0x16,
          0xcd, 0xb5, 0x48, 0xe9, 0x16, 0x2c, 0x70, 0xb4, 0x1e, 0x19, 0x38,
          0xe0, 0x18, 0xe9, 0xae, 0xc5, 0xda, 0x74, 0x41, 0x2d, 0x70, 0x07,
          0x60, 0x37, 0x27, 0x66, 0xf7, 0x00, 0xbb, 0x79, 0x51, 0xf3, 0x7c,
          0x8a, 0x2b, 0xb5, 0x69, 0x6e, 0x10, 0x1f, 0xe0, 0x0a, 0x5e, 0xbe,
          0xb4, 0x4e, 0x0e, 0x02, 0x59, 0xb5, 0xcb, 0x4a, 0x6a, 0x86, 0x8b,
          0xcc, 0xa2, 0x13, 0xa0, 0xe9, 0xf2, 0x5c, 0xb0, 0x23, 0xb2, 0x15,
          0xf9, 0xbb, 0x43, 0xc1, 0x54, 0xf4, 0xc8, 0xab, 0x16, 0xa6,
      },
  };
  const BigNumStr bnm12[] = {
      {
          0x14, 0x92, 0xd1, 0x1f, 0xac, 0x90, 0x4d, 0xc3, 0x3e, 0xb8, 0xa7,
          0xfe, 0x35, 0x98, 0xda, 0x94, 0xd6, 0xf3, 0x27, 0x29, 0x4b, 0x70,
          0xcf, 0x0f, 0xda, 0x14, 0x97, 0xa1, 0x9b, 0x6b, 0x24, 0x08,
      },
      {
          0x76, 0x8d, 0xe5, 0xc2, 0xab, 0x78, 0xd9, 0x76, 0x98, 0xff, 0x92,
          0xea, 0x95, 0x58, 0xc5, 0x0c, 0x6f, 0x1b, 0x35, 0xc4, 0xbf, 0x4a,
          0x9e, 0xc2, 0x6a, 0xec, 0xe2, 0xf6, 0xaf, 0xeb, 0xac, 0x06,
      },
      {
          0x50, 0x38, 0x60, 0x31, 0xab, 0x05, 0xcc, 0xb1, 0xc2, 0x06, 0xa0,
          0xd7, 0x92, 0x12, 0xbd, 0x17, 0x81, 0xb9, 0x7e, 0x07, 0x9b, 0xd4,
          0x3b, 0x2d, 0x3c, 0x15, 0x3a, 0x5a, 0xf1, 0xc0, 0xc4, 0x75,
      },
      {
          0xa1, 0x3f, 0xea, 0x99, 0x7e, 0xc7, 0x65, 0xf5, 0x41, 0xa6, 0xd6,
          0xd3, 0x77, 0xec, 0x27, 0xf8, 0x6c, 0x18, 0x28, 0x69, 0x97, 0x08,
          0x0e, 0x63, 0x5a, 0xda, 0xfe, 0x9f, 0xd1, 0x4a, 0x61, 0x4f,
      },
  };
  const Fq12ElemStr fq12_expected_result_str = {
      0x3a, 0x30, 0x33, 0xa2, 0x14, 0xdf, 0xdc, 0x70, 0x48, 0xf3, 0xba, 0x3f,
      0xce, 0xfc, 0x69, 0x24, 0xae, 0xa1, 0xf3, 0xcf, 0xd8, 0x77, 0x69, 0x38,
      0x38, 0xf4, 0x8e, 0xfb, 0x51, 0xfb, 0x10, 0x7c, 0x73, 0xb4, 0xee, 0xf9,
      0x76, 0xbb, 0x45, 0xec, 0x07, 0x24, 0x23, 0xa3, 0x38, 0x64, 0x39, 0x4c,
      0x03, 0x94, 0xf5, 0xee, 0x5f, 0xc9, 0x83, 0x4f, 0xd1, 0xa6, 0x4a, 0x05,
      0x25, 0x0c, 0x46, 0x33, 0xd4, 0x40, 0x47, 0xae, 0xec, 0xda, 0x30, 0xf9,
      0xe1, 0x28, 0xfb, 0xb1, 0x4c, 0x62, 0xfb, 0x6c, 0x90, 0x0d, 0xb1, 0xec,
      0xaf, 0x2d, 0x95, 0x64, 0x11, 0x93, 0xda, 0x8e, 0xb8, 0x7d, 0xe4, 0xa3,
      0x8e, 0xa6, 0x9b, 0xaa, 0x58, 0xd5, 0xdc, 0x59, 0x1a, 0x51, 0x53, 0x47,
      0x30, 0x1c, 0x2b, 0xb0, 0xbb, 0x57, 0x57, 0x0a, 0x8c, 0x5f, 0x62, 0x9c,
      0x52, 0x28, 0xc0, 0x25, 0x27, 0xae, 0xcd, 0x36, 0xbf, 0x2d, 0x03, 0xb1,
      0x33, 0xfd, 0x24, 0x81, 0x3c, 0x91, 0x43, 0x90, 0x0c, 0x20, 0xc4, 0xe3,
      0x69, 0x3f, 0xa9, 0xa2, 0x7c, 0xc2, 0x48, 0x28, 0x1b, 0xf0, 0x81, 0x1f,
      0x2b, 0x8f, 0x8d, 0x43, 0x38, 0x18, 0x12, 0xa3, 0x89, 0xf1, 0xd7, 0x60,
      0x89, 0x68, 0x6a, 0xc4, 0xcc, 0x5d, 0xf4, 0xce, 0x43, 0x95, 0x84, 0xcd,
      0x01, 0x55, 0xb8, 0x5d, 0x24, 0x50, 0xcd, 0xe3, 0x68, 0x1c, 0xff, 0x59,
      0x0d, 0xac, 0xcb, 0xe7, 0x9d, 0x68, 0x0f, 0x4a, 0xaf, 0xeb, 0xb5, 0xfb,
      0xf9, 0xb1, 0x58, 0x80, 0xd5, 0x71, 0x53, 0x26, 0x2e, 0x9c, 0xce, 0x10,
      0xac, 0xd2, 0x0a, 0xeb, 0xb4, 0x5c, 0xc8, 0xd7, 0x16, 0x26, 0x66, 0x71,
      0xdd, 0x67, 0xba, 0xdb, 0x35, 0x76, 0x64, 0xc6, 0x0b, 0x0b, 0x07, 0x4b,
      0x65, 0xa2, 0xf7, 0x68, 0x9e, 0xfe, 0xe8, 0xe3, 0xa7, 0x09, 0x79, 0xc5,
      0xdf, 0x5e, 0x9c, 0xec, 0xee, 0x83, 0x59, 0xb5, 0x89, 0x81, 0xa2, 0x87,
      0xd7, 0x75, 0x65, 0x90, 0xde, 0x78, 0x37, 0x59, 0x04, 0x9b, 0x50, 0xc3,
      0xba, 0x90, 0x09, 0x15, 0xab, 0x17, 0xc5, 0xbf, 0x5b, 0xb9, 0xe4, 0x6c,
      0xa9, 0xb1, 0x49, 0x76, 0x25, 0x74, 0x5d, 0x9c, 0x78, 0xc5, 0x09, 0xee,
      0xeb, 0xeb, 0x9d, 0x1c, 0x6c, 0xc0, 0x27, 0x9d, 0x66, 0xe6, 0x7f, 0x31,
      0xcd, 0xb0, 0x8a, 0xe4, 0x9e, 0xbd, 0x70, 0x18, 0x88, 0xa1, 0x08, 0x39,
      0xa2, 0x48, 0xa5, 0x98, 0xb6, 0xad, 0x10, 0x54, 0x07, 0xcd, 0xfc, 0x6d,
      0xb1, 0x02, 0xff, 0xe3, 0x92, 0xd7, 0x9d, 0x48, 0xfb, 0xce, 0x88, 0x46,
      0x92, 0x07, 0x02, 0xba, 0xee, 0xeb, 0xae, 0x88, 0xb1, 0x4e, 0xd0, 0xf2,
      0xce, 0xd7, 0x57, 0x07, 0xd4, 0x39, 0xd2, 0x7d, 0x1a, 0x0c, 0xef, 0xf2,
      0x84, 0x84, 0x22, 0x8a, 0xb1, 0x80, 0x0d, 0xdc, 0x64, 0x86, 0xfd, 0x70,
  };

  FfElementObj r12(&this->fq12);
  FfElementObj fq12_exp[4];

  // prepare data for test
  for (m = 0; m < 4; m++) {
    fq12_exp[m] = FfElementObj(&this->fq12, ff12e[m]);
    // initialize data for test
    p[m] = fq12_exp[m];
    b[m] = &bnm12[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfMultiExp(this->fq12, p, b, 4, r12));
  EXPECT_EQ(FfElementObj(&this->fq12, fq12_expected_result_str), r12)
      << "FfMultiExp: Finite field element does not match with reference value";
}

TEST_F(FfElementTest, FfMultiExpWorksGivenFiveExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[5];
  FfElement const* p[5];
  BigNumStr const* b[5];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x7B, 0x26, 0x96, 0x84, 0x04, 0x0D, 0x6F, 0x10, 0xAA, 0x7F, 0xD0, 0x0C,
       0x41, 0x3A, 0x68, 0x48, 0xBF, 0x3D, 0xB5, 0xCE, 0x3A, 0x9C, 0xA9, 0x10,
       0xA4, 0x67, 0x7C, 0xC0, 0x41, 0x52, 0xCA, 0xE7},
      {0x2E, 0xD9, 0xEF, 0x86, 0xB6, 0x32, 0x72, 0x89, 0x37, 0xCF, 0x16, 0xCE,
       0x27, 0x15, 0x7A, 0x95, 0xCF, 0x94, 0x1B, 0xF3, 0xCE, 0x49, 0x20, 0x29,
       0x9A, 0x61, 0x2E, 0x0A, 0xF5, 0xE1, 0xDC, 0x9F},
      {0xE0, 0x19, 0x37, 0x44, 0xA7, 0x45, 0x49, 0x7E, 0x19, 0x02, 0x09, 0x78,
       0xBE, 0xA4, 0x65, 0x60, 0x7A, 0xF5, 0xC8, 0xAF, 0x97, 0x5E, 0xDA, 0x3A,
       0xB4, 0x16, 0x70, 0x31, 0xE3, 0xB4, 0x18, 0xA8},
      {0xBC, 0x92, 0x29, 0x92, 0x14, 0xD3, 0x3F, 0xB8, 0xB8, 0x8C, 0x41, 0xDF,
       0xC5, 0xA1, 0xCA, 0x77, 0xE5, 0x73, 0xFE, 0xA7, 0xAC, 0x81, 0x66, 0x11,
       0x5A, 0x6D, 0x7D, 0x97, 0x13, 0x92, 0x8D, 0x1D},
      {0xE1, 0xA0, 0x0D, 0xDC, 0x8F, 0x20, 0xF5, 0x2D, 0x1D, 0x1F, 0xBD, 0x24,
       0x8C, 0xC2, 0x26, 0x06, 0x89, 0xE9, 0x46, 0xA9, 0xD2, 0x44, 0x8F, 0x1A,
       0x8D, 0xAF, 0x20, 0x73, 0x60, 0x0B, 0x66, 0x9C}};

  const BigNumStr bnm[] = {
      {0x78, 0xE6, 0x3F, 0x42, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x6E,
       0xE9, 0xFF, 0x4F, 0xFF, 0x23, 0xFF, 0x67, 0xB4, 0xED, 0xEE, 0xD7, 0xC6,
       0xFF, 0x4B, 0x0B, 0x6F, 0x1F, 0xD6, 0xFD, 0x7F},
      {0xA1, 0x87, 0xFF, 0x1B, 0xFF, 0x85, 0x68, 0xFF, 0x1C, 0xFF, 0xD5, 0xFF,
       0x95, 0x73, 0xFF, 0xFF, 0xFF, 0x9E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E,
       0x9E, 0x62, 0xFF, 0xFF, 0x97, 0xFF, 0xFF, 0xF3},
      {0x2E, 0xE8, 0x1E, 0x35, 0xFF, 0x82, 0x0B, 0x99, 0xEC, 0x30, 0x2B, 0xCE,
       0xEC, 0x83, 0x05, 0xFF, 0x9E, 0x3C, 0xFF, 0xFF, 0xA1, 0xFF, 0xFF, 0xFF,
       0x57, 0x26, 0x5B, 0x6C, 0xFF, 0xE6, 0x94, 0xFF},
      {0xFF, 0xFF, 0xFF, 0xFF, 0x94, 0xFF, 0xFF, 0x79, 0xFC, 0xFF, 0x83, 0xFF,
       0xFF, 0xFF, 0xFF, 0xFF, 0xA9, 0xFF, 0x2F, 0x6B, 0xD1, 0xE7, 0xFF, 0xFF,
       0xFF, 0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
      {0x63, 0x53, 0x53, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0x09, 0xFF,
       0xC0, 0xD8, 0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x45,
       0xFF, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x06}};

  const FqElemStr res = {
      0x8F, 0x6D, 0x54, 0xCA, 0x00, 0x85, 0xD8, 0x78, 0xA4, 0x45, 0x36,
      0x7C, 0x23, 0xD0, 0x3C, 0x2D, 0x20, 0x78, 0xD9, 0xD7, 0xF1, 0xC3,
      0x99, 0xD9, 0xD3, 0x0A, 0x0E, 0x86, 0x98, 0xC2, 0xA7, 0x97,
  };
  // prepare data for test
  for (m = 0; m < 5; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfMultiExp(this->fq, p, b, 5, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfMultiExp: Finite field element does not match with reference value";
}

TEST_F(FfElementTest, FfMultiExpWorksGivenSixExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[6];
  FfElement const* p[6];
  BigNumStr const* b[6];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x75, 0x18, 0x6A, 0x3D, 0xCC, 0x76, 0x33, 0x92, 0xD0, 0x57, 0xDA, 0xE0,
       0x37, 0x6F, 0x71, 0xD5, 0x9E, 0x7E, 0x65, 0xED, 0xD7, 0xFD, 0x82, 0xC0,
       0x6C, 0x83, 0x60, 0x30, 0xC1, 0xD3, 0x3D, 0x4D},
      {0xF5, 0x3A, 0x20, 0xAB, 0x22, 0x47, 0x07, 0xAE, 0x71, 0xC1, 0x91, 0x73,
       0xEF, 0x1F, 0x1D, 0x76, 0x2E, 0xEE, 0x0D, 0xDE, 0xD9, 0xF8, 0x5C, 0x85,
       0xC7, 0x5B, 0x93, 0x88, 0xF6, 0xFF, 0x4C, 0xA5},
      {0x21, 0xB3, 0x5E, 0xE8, 0xD2, 0xDA, 0x14, 0x1C, 0xB5, 0x2C, 0xFC, 0x61,
       0x9D, 0xEB, 0x65, 0x87, 0xBB, 0x5B, 0xBC, 0xFD, 0x37, 0x54, 0x6B, 0xDC,
       0xD9, 0xFA, 0x7A, 0xCD, 0x18, 0x7E, 0x8B, 0x89},
      {0x70, 0xB7, 0x19, 0x42, 0xB4, 0x41, 0x2C, 0x1B, 0xBD, 0x7B, 0x25, 0x06,
       0xA2, 0x95, 0xB5, 0xB6, 0x70, 0xDF, 0x58, 0x71, 0xCA, 0x09, 0x12, 0x66,
       0x5A, 0x7B, 0xF0, 0x5F, 0x43, 0x69, 0x32, 0xF6},
      {0x3D, 0x57, 0xE8, 0x3F, 0x1E, 0x39, 0x2B, 0xE5, 0xD0, 0xCF, 0xA9, 0xC8,
       0x72, 0x10, 0x7F, 0xC5, 0x4F, 0xAE, 0xE9, 0x73, 0x28, 0x4C, 0xFC, 0x3F,
       0xD5, 0xFF, 0xE8, 0xCF, 0x2C, 0xEB, 0x33, 0xD3},
      {0x51, 0x80, 0x15, 0x1C, 0xE6, 0x9C, 0x4B, 0x80, 0x40, 0xBA, 0x40, 0x29,
       0x3A, 0x86, 0xA8, 0xB2, 0x66, 0x08, 0x97, 0x8D, 0xEB, 0x8B, 0xA7, 0x93,
       0xE3, 0x49, 0xC6, 0xC0, 0x46, 0x93, 0xC1, 0x75}};
  const BigNumStr bnm[] = {
      {0xFF, 0xFF, 0xFF, 0x49, 0x50, 0xFF, 0x19, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
       0xFF, 0x28, 0x4A, 0x0E, 0xFF, 0x74, 0xFF, 0xC7, 0xFF, 0xD9, 0x81, 0xFF,
       0xFF, 0xFF, 0xD5, 0x2C, 0xFF, 0xFF, 0xFF, 0xAE},
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
       0xFF, 0x0C, 0x03, 0xFF, 0x95, 0xFF, 0x11, 0x9D, 0xFF, 0x9B, 0xFF, 0xFF,
       0x68, 0x44, 0xFF, 0xC9, 0xFF, 0x10, 0xC7, 0xBF},
      {0x64, 0xFF, 0x89, 0x4A, 0xFF, 0x20, 0xFF, 0xE7, 0xFF, 0x36, 0x23, 0xDD,
       0xFF, 0xFF, 0xFC, 0xFF, 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, 0xFF, 0xFF,
       0xFF, 0xFF, 0xFF, 0xFF, 0x94, 0xD3, 0x59, 0xBE},
      {0x0F, 0xFF, 0x76, 0xA5, 0x9E, 0x02, 0x9B, 0xFF, 0xA1, 0xFF, 0xC2, 0xFF,
       0x9D, 0xFF, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, 0x81, 0x2A, 0xFF, 0x98,
       0xFF, 0xFF, 0xB9, 0xFF, 0xB7, 0x78, 0xE6, 0xFF},
      {0xAF, 0xF3, 0xFF, 0x3C, 0xA8, 0xFF, 0xB9, 0x3A, 0xFF, 0xFF, 0xFF, 0x70,
       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x85, 0xFF, 0xFF, 0x52,
       0xFF, 0x41, 0x10, 0x62, 0xFF, 0x51, 0x35, 0xFF},
      {0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0xC3, 0xD9, 0xFF, 0xFF, 0x3A, 0x44, 0x88,
       0x04, 0xFF, 0x0B, 0xFF, 0xFF, 0xFF, 0x64, 0xFF, 0xFF, 0x52, 0xFF, 0x7D,
       0x6B, 0x24, 0x6E, 0xFF, 0xFF, 0xFF, 0xA7, 0x71}};
  const FqElemStr res = {
      0xFB, 0x89, 0x4F, 0xBB, 0x91, 0xEE, 0xD6, 0x57, 0x2D, 0x34, 0xF9,
      0x0B, 0xE3, 0xEB, 0x71, 0x1D, 0x63, 0x74, 0x96, 0x3B, 0xD4, 0x02,
      0x2F, 0x08, 0xDB, 0x95, 0x6A, 0x40, 0x68, 0xD4, 0x55, 0x76,
  };
  // prepare data for test
  for (m = 0; m < 6; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfMultiExp(this->fq, p, b, 6, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfMultiExp: Finite field element does not match with reference value";
}
///////////////////////////////////////////////////////////////////////
// FfSscmMultiExp

TEST_F(FfElementTest, SscmFfMultiExpFailsGivenArgumentsMismatch) {
  FfElement const* p[] = {this->fq_a, this->fq_b};
  FfElement const* p12[] = {this->fq12_a, this->fq12_b};
  const BigNumStr bnm0 = {0};
  const BigNumStr bnm1 = {0};
  BigNumStr const* b[] = {&bnm0, &bnm1};
  EXPECT_EQ(kEpidBadArgErr, FfSscmMultiExp(this->fq12, p, b, 2, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfSscmMultiExp(this->fq, p12, b, 2, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfSscmMultiExp(this->fq, p, b, 2, this->fq12_r));
}

TEST_F(FfElementTest, SscmFfMultiExpFailsGivenNullPointer) {
  FfElement const* p[] = {this->fq_a, this->fq_b};
  FfElement const* p_withnull[] = {nullptr, this->fq_b};
  const BigNumStr bnm0 = {{0x11, 0xFF, 0xFF, 0xFF, 0x4F, 0x59, 0xB1, 0xD3, 0x6B,
                           0x08, 0xFF, 0xFF, 0x0B, 0xF3, 0xAF, 0x27, 0xFF, 0xB8,
                           0xFF, 0xFF, 0x98, 0xFF, 0xEB, 0xFF, 0xF2, 0x6A, 0xFF,
                           0xFF, 0xEA, 0x31, 0xFF, 0xFF}};
  const BigNumStr bnm1 = {{0xE2, 0xFF, 0x03, 0x1D, 0xFF, 0x19, 0x81, 0xCB, 0xFF,
                           0xFF, 0x6B, 0xD5, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD,
                           0xFF, 0x5A, 0xFF, 0x5C, 0x7C, 0xFF, 0x84, 0xFF, 0xFF,
                           0x8C, 0x03, 0xB2, 0x26, 0xFF}};
  BigNumStr const* b[] = {&bnm0, &bnm1};
  BigNumStr const* b_withnull[] = {nullptr, &bnm1};
  size_t m = 2;
  EXPECT_EQ(kEpidBadArgErr, FfSscmMultiExp(nullptr, p, b, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfSscmMultiExp(this->fq, nullptr, b, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfSscmMultiExp(this->fq, p, nullptr, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr, FfSscmMultiExp(this->fq, p, b, m, nullptr));
  EXPECT_EQ(kEpidBadArgErr,
            FfSscmMultiExp(this->fq, p_withnull, b, m, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfSscmMultiExp(this->fq, p, b_withnull, m, this->fq_r));
}

TEST_F(FfElementTest, SscmFfMultiExpFailsGivenIncorrectMLen) {
  FfElement const* p[] = {this->fq_a, this->fq_b};
  const BigNumStr bnm0 = {{0x11, 0xFF, 0xFF, 0xFF, 0x4F, 0x59, 0xB1, 0xD3, 0x6B,
                           0x08, 0xFF, 0xFF, 0x0B, 0xF3, 0xAF, 0x27, 0xFF, 0xB8,
                           0xFF, 0xFF, 0x98, 0xFF, 0xEB, 0xFF, 0xF2, 0x6A, 0xFF,
                           0xFF, 0xEA, 0x31, 0xFF, 0xFF}};
  const BigNumStr bnm1 = {{0xE2, 0xFF, 0x03, 0x1D, 0xFF, 0x19, 0x81, 0xCB, 0xFF,
                           0xFF, 0x6B, 0xD5, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD,
                           0xFF, 0x5A, 0xFF, 0x5C, 0x7C, 0xFF, 0x84, 0xFF, 0xFF,
                           0x8C, 0x03, 0xB2, 0x26, 0xFF}};
  BigNumStr const* b[] = {&bnm0, &bnm1};
  size_t len_greater_int_max = (size_t)INT_MAX + 1;
  EXPECT_EQ(kEpidBadArgErr, FfSscmMultiExp(this->fq, p, b, 0, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfSscmMultiExp(this->fq, p, b, std::numeric_limits<size_t>::max(),
                           this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfSscmMultiExp(this->fq, p, b, len_greater_int_max, this->fq_r));
}

TEST_F(FfElementTest, SscmFfMultiExpWorksGivenOneExponent) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[1];
  FfElement const* p[1];
  BigNumStr const* b[1];
  int m = 0;
  const FqElemStr ffe[] = {{
      0x12, 0xA6, 0x5B, 0xD6, 0x91, 0x8D, 0x50, 0xA7, 0x66, 0xEB, 0x7D, 0x52,
      0xE3, 0x40, 0x17, 0x60, 0x7F, 0xDF, 0x6C, 0xA1, 0x2C, 0x1A, 0x37, 0xE0,
      0x92, 0xC0, 0xF7, 0xB9, 0x76, 0xAB, 0xB1, 0x8A,
  }};
  const BigNumStr bnm[] = {{
      0xFF, 0xFB, 0x3E, 0x5D, 0xFF, 0x9A, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xFF,
      0xF2, 0xE1, 0x85, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81,
      0xFF, 0xFD, 0xFF, 0xEB, 0xFF, 0x29, 0xA7, 0xFF,
  }};
  const FqElemStr res = {
      0x6A, 0x21, 0xEC, 0x89, 0xCC, 0x13, 0x2F, 0x6F, 0x29, 0x1B, 0x3A,
      0x94, 0xF6, 0xE6, 0xA8, 0xBD, 0x98, 0x25, 0x43, 0x7A, 0xDC, 0xC1,
      0x20, 0xBA, 0x30, 0xD8, 0x1C, 0x8E, 0x79, 0xFC, 0xDA, 0x67,
  };
  // prepare data for test
  for (m = 0; m < 1; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfSscmMultiExp(this->fq, p, b, 1, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfSscmMultiExp: Finite field element does not "
         "match with reference value";
}

TEST_F(FfElementTest, SscmFfMultiExpWorksGivenTwoExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[2];
  FfElement const* p[2];
  BigNumStr const* b[2];
  int m = 0;
  const FqElemStr ffe[] = {
      {0xE6, 0x65, 0x23, 0x9B, 0xD4, 0x07, 0x16, 0x83, 0x38, 0x23, 0xB2, 0x67,
       0x57, 0xEB, 0x0F, 0x23, 0x3A, 0xF4, 0x8E, 0xDA, 0x71, 0x5E, 0xD9, 0x98,
       0x63, 0x98, 0x2B, 0xBC, 0x78, 0xD1, 0x94, 0xF2},
      {0xDD, 0x2B, 0xE9, 0x59, 0x24, 0xA5, 0xB3, 0xFD, 0xEB, 0xE1, 0x3C, 0xC0,
       0x73, 0x4E, 0x99, 0xEE, 0x36, 0xF6, 0xC0, 0x1A, 0x76, 0x01, 0x0A, 0xF0,
       0xCB, 0xB4, 0x71, 0x88, 0x95, 0xCB, 0x35, 0xBA}};
  const BigNumStr bnm[] = {
      {0x11, 0xFF, 0xFF, 0xFF, 0x4F, 0x59, 0xB1, 0xD3, 0x6B, 0x08, 0xFF, 0xFF,
       0x0B, 0xF3, 0xAF, 0x27, 0xFF, 0xB8, 0xFF, 0xFF, 0x98, 0xFF, 0xEB, 0xFF,
       0xF2, 0x6A, 0xFF, 0xFF, 0xEA, 0x31, 0xFF, 0xFF},
      {0xE2, 0xFF, 0x03, 0x1D, 0xFF, 0x19, 0x81, 0xCB, 0xFF, 0xFF, 0x6B, 0xD5,
       0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xBD, 0xFF, 0x5A, 0xFF, 0x5C, 0x7C, 0xFF,
       0x84, 0xFF, 0xFF, 0x8C, 0x03, 0xB2, 0x26, 0xFF}};
  const FqElemStr res = {
      0x8A, 0xF7, 0x96, 0x53, 0x9C, 0xC3, 0x57, 0x89, 0x50, 0xE3, 0xAB,
      0x12, 0x62, 0xEA, 0x0C, 0xE4, 0x65, 0xA6, 0x91, 0x09, 0x93, 0x81,
      0xFC, 0xBB, 0x0E, 0xFA, 0xC1, 0xC4, 0xDC, 0x3F, 0x0A, 0xD1,
  };
  // prepare data for test
  for (m = 0; m < 2; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfSscmMultiExp(this->fq, p, b, 2, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfSscmMultiExp: Finite field element does not "
         "match with reference value";
}

TEST_F(FfElementTest, SscmFfMultiExpWorksGivenThreeExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[3];
  FfElement const* p[3];
  BigNumStr const* b[3];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x24, 0x9D, 0xDD, 0x1A, 0xC0, 0x07, 0x1F, 0x65, 0xDF, 0xBB, 0xC7, 0x39,
       0x99, 0x44, 0x63, 0x8E, 0x7E, 0xB2, 0x85, 0x82, 0xC6, 0x4E, 0x2D, 0x39,
       0xA8, 0x3F, 0x12, 0xB4, 0xE9, 0x9D, 0x67, 0x28},
      {0x6B, 0x51, 0x6B, 0x33, 0x0B, 0x2E, 0x2A, 0x94, 0x34, 0xCC, 0x7E, 0x97,
       0xFA, 0x85, 0x51, 0x44, 0x30, 0xE5, 0xF2, 0x68, 0x23, 0x31, 0x84, 0xFF,
       0xBB, 0x95, 0x9A, 0xB2, 0x80, 0x95, 0xC4, 0x91},
      {0x77, 0xED, 0xA2, 0x53, 0xA3, 0xBF, 0x6C, 0x58, 0x16, 0x7D, 0xEF, 0x8A,
       0x7D, 0x38, 0x65, 0x3F, 0x92, 0xA6, 0x85, 0xD7, 0x9F, 0x80, 0xA4, 0xA1,
       0x70, 0x02, 0xE5, 0x66, 0xF2, 0xD8, 0xD7, 0xD2}};
  const BigNumStr bnm[] = {
      {0xFF, 0xFF, 0xFF, 0xB4, 0x48, 0xFF, 0xFF, 0xFF, 0xA3, 0xFF, 0xE3, 0xFF,
       0x61, 0xFF, 0xA2, 0x97, 0x9A, 0xFF, 0x6F, 0x04, 0xC6, 0xB9, 0xFF, 0x22,
       0xFF, 0x20, 0x9E, 0xFF, 0x43, 0xFF, 0xFF, 0xFF},
      {0xB1, 0x19, 0xBF, 0xFF, 0x74, 0x7F, 0xCC, 0x34, 0x1E, 0x30, 0x7D, 0xC5,
       0xC4, 0xFF, 0xE7, 0xF8, 0xFF, 0x5F, 0xFF, 0x73, 0xFF, 0x58, 0xFF, 0xD6,
       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0xFF, 0xFF},
      {0xFF, 0xFF, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF, 0x26, 0xA8, 0xFF, 0xFF, 0xFF,
       0x3E, 0xFF, 0xFF, 0xFF, 0xE0, 0x4F, 0xFF, 0xE3, 0xFF, 0xFF, 0xEB, 0x04,
       0x4B, 0xFF, 0x5D, 0x55, 0xFF, 0x2D, 0xFF, 0xFF}};
  const FqElemStr res = {
      0x04, 0x22, 0x21, 0xE7, 0x9F, 0xE3, 0x07, 0x2F, 0xA4, 0xBB, 0x56,
      0x84, 0xD5, 0x93, 0x4A, 0xDA, 0x5B, 0x89, 0x07, 0xBE, 0xC5, 0x5F,
      0xE6, 0x5C, 0x18, 0xF0, 0xF8, 0x8E, 0x8E, 0x00, 0xB9, 0x87,
  };
  // prepare data for test
  for (m = 0; m < 3; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfSscmMultiExp(this->fq, p, b, 3, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfSscmMultiExp: Finite field element does not "
         "match with reference value";
}

TEST_F(FfElementTest, SscmFfMultiExpWorksGivenFourExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[4];
  FfElement const* p[4];
  BigNumStr const* b[4];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x81, 0xEE, 0x78, 0x31, 0x4C, 0xC0, 0x3C, 0xDD, 0x16, 0x52, 0xC6, 0x7E,
       0x07, 0x4C, 0xFE, 0xC4, 0x50, 0x63, 0x5A, 0x40, 0xC8, 0xDF, 0x94, 0x23,
       0x1A, 0x90, 0xDD, 0x24, 0x61, 0x23, 0xDD, 0x87},
      {0x76, 0xBB, 0x2C, 0x1C, 0xB3, 0x78, 0x93, 0x7E, 0x10, 0x29, 0xC3, 0xEC,
       0xBD, 0xA3, 0x75, 0x3A, 0xF1, 0x0B, 0xE1, 0xCA, 0xEE, 0x02, 0x58, 0x74,
       0xC7, 0xDC, 0xB8, 0x4F, 0x3F, 0x7B, 0xA2, 0xFC},
      {0x3A, 0xD3, 0x04, 0xAA, 0x9D, 0x2A, 0xDE, 0x86, 0x56, 0x84, 0x80, 0x42,
       0xA5, 0x68, 0xD8, 0x6E, 0x65, 0xC4, 0x08, 0x07, 0x87, 0x4B, 0xC1, 0x92,
       0xCB, 0x68, 0x52, 0x6C, 0x5A, 0x4F, 0x7C, 0xFB},
      {0x56, 0xE7, 0xBD, 0x59, 0x54, 0xCC, 0x20, 0x16, 0xAE, 0x36, 0xD1, 0xB0,
       0xE4, 0x82, 0x47, 0x27, 0x32, 0xD1, 0x38, 0x7C, 0x53, 0x7F, 0xDD, 0xF4,
       0xDE, 0xFA, 0xF7, 0x0F, 0xDD, 0xDF, 0x48, 0xA9}};
  const BigNumStr bnm[] = {
      {0xFF, 0xA5, 0xC3, 0xFF, 0xFF, 0x09, 0x18, 0x18, 0x95, 0x40, 0xFF, 0xFF,
       0xDB, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xFF, 0x25, 0xFF, 0xFF, 0x42, 0x07,
       0xB5, 0xFF, 0x26, 0xE0, 0x4F, 0xB5, 0xB4, 0xFF},
      {0x1F, 0xFF, 0xAA, 0xF1, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0x07, 0xFF,
       0xE7, 0xDF, 0xFF, 0xEE, 0xFF, 0x57, 0xFF, 0xFF, 0xFF, 0xB8, 0xB3, 0x9C,
       0xFF, 0x8A, 0x89, 0xFF, 0xFE, 0x32, 0xFF, 0xC1},
      {0x9A, 0xFF, 0xFF, 0x9B, 0xFF, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
       0xFF, 0x36, 0xF0, 0xC5, 0xAE, 0x87, 0x41, 0xFF, 0x5C, 0xDD, 0xFF, 0xFF,
       0xFF, 0xFF, 0x59, 0xC9, 0xFF, 0x44, 0x5C, 0x58},
      {0x00, 0xFF, 0x95, 0xFF, 0x21, 0xFF, 0xFF, 0x1D, 0x1A, 0x06, 0xD3, 0xFF,
       0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xB0, 0x31, 0xFF, 0xFF, 0x1F,
       0x61, 0x48, 0xFF, 0xFF, 0xFF, 0x17, 0xC9, 0x79}};
  const FqElemStr res = {
      0x26, 0x2C, 0x23, 0x55, 0xD5, 0xBD, 0x7B, 0x67, 0x60, 0xFE, 0x06,
      0x7B, 0xCC, 0xB8, 0x6B, 0xC9, 0x00, 0x81, 0xAE, 0xCB, 0xEC, 0x91,
      0x6D, 0x1B, 0x9E, 0x1C, 0xD8, 0xC0, 0x8D, 0x3F, 0x1B, 0x20,
  };
  // prepare data for test
  for (m = 0; m < 4; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfSscmMultiExp(this->fq, p, b, 4, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfSscmMultiExp: Finite field element does not "
         "match with reference value";

  const Fq12ElemStr ff12e[] = {
      {
          0x2c, 0x86, 0xee, 0x80, 0x61, 0x45, 0x8f, 0xfd, 0x96, 0xc2, 0x81,
          0x11, 0xe8, 0x96, 0xe2, 0xca, 0x21, 0x78, 0xf7, 0xa3, 0x4b, 0xba,
          0xdc, 0x0d, 0x34, 0x8a, 0x56, 0x12, 0x67, 0x8d, 0x72, 0x37, 0xa8,
          0xeb, 0x5f, 0x1a, 0x1b, 0xf0, 0x5c, 0xc6, 0x6f, 0xc2, 0xc5, 0x03,
          0xc4, 0x19, 0x85, 0xa6, 0xe0, 0x64, 0xfe, 0x71, 0x51, 0x46, 0xdf,
          0xc2, 0xd6, 0xf9, 0xa0, 0xd6, 0x84, 0xc1, 0x4a, 0x11, 0xe0, 0x33,
          0xa3, 0x1c, 0x73, 0x71, 0x62, 0x17, 0x10, 0x4c, 0xd8, 0x3e, 0xee,
          0xa1, 0x38, 0x9f, 0xa5, 0xfb, 0x1d, 0xc4, 0x9d, 0x2f, 0x08, 0x4a,
          0xd3, 0x61, 0x37, 0x79, 0xab, 0xc1, 0x37, 0x7e, 0x90, 0xfd, 0xa3,
          0x9d, 0x0c, 0x81, 0x52, 0xf4, 0x58, 0x24, 0x89, 0xc4, 0xf4, 0xd4,
          0x82, 0x33, 0xca, 0x5d, 0xb4, 0x58, 0xa6, 0x1e, 0xe6, 0x2b, 0x86,
          0xef, 0x56, 0xc4, 0x4f, 0x9b, 0x27, 0x3c, 0x96, 0x54, 0xda, 0xb5,
          0x32, 0x23, 0x8a, 0x93, 0xcc, 0xa5, 0x7d, 0x28, 0x4a, 0x97, 0xa4,
          0x30, 0xa8, 0x11, 0xbc, 0x43, 0x22, 0x7f, 0x02, 0x73, 0x0e, 0x5c,
          0x63, 0x52, 0x2f, 0x4b, 0x5f, 0x7d, 0xc3, 0x1e, 0x30, 0xae, 0xac,
          0x8c, 0xe8, 0x93, 0xd6, 0xce, 0x05, 0x4b, 0x6b, 0x21, 0x01, 0x14,
          0x4a, 0x1d, 0xd9, 0x08, 0x0c, 0xa1, 0x4c, 0x0b, 0x44, 0x28, 0x9c,
          0xb6, 0xd1, 0x96, 0xbd, 0x07, 0x8b, 0x3d, 0x77, 0x4e, 0xbd, 0x7b,
          0x9f, 0x65, 0x20, 0xdb, 0xa0, 0x95, 0x25, 0x44, 0x44, 0x7a, 0xe3,
          0xdc, 0xdd, 0x2f, 0xba, 0x75, 0x16, 0x19, 0xcb, 0x90, 0x31, 0x79,
          0x38, 0xb6, 0x1a, 0x82, 0xbc, 0xb6, 0x85, 0xcd, 0xeb, 0xb9, 0x13,
          0x43, 0xea, 0xc2, 0x78, 0xbe, 0x25, 0x97, 0x1c, 0x6e, 0x18, 0x55,
          0xfd, 0xcb, 0xe6, 0x7e, 0x1e, 0x92, 0xad, 0x48, 0xe7, 0xa7, 0x02,
          0x87, 0x0b, 0x8f, 0x16, 0xd7, 0x8e, 0x95, 0x8e, 0x62, 0x0a, 0xad,
          0x20, 0x2d, 0x6a, 0x2b, 0x4e, 0x6f, 0xc1, 0xc2, 0x63, 0xba, 0x82,
          0xf3, 0x8c, 0xd0, 0xa2, 0x09, 0xd3, 0xe5, 0xba, 0x1f, 0x65, 0x35,
          0xd0, 0x1c, 0x09, 0x98, 0x47, 0x65, 0xf2, 0x90, 0xd4, 0xe2, 0xd0,
          0x6f, 0x9e, 0xdb, 0xa2, 0x14, 0x9b, 0x4b, 0x3d, 0xaa, 0x75, 0x25,
          0x67, 0xf4, 0x7d, 0x77, 0xe9, 0x24, 0xed, 0xee, 0x1b, 0x07, 0x7f,
          0xe0, 0x7a, 0x34, 0xb7, 0x3c, 0x36, 0x0d, 0xce, 0xf9, 0xbf, 0xf9,
          0xe5, 0x09, 0x86, 0xeb, 0xb9, 0xd8, 0xc4, 0x67, 0xc5, 0xcd, 0xd4,
          0xbc, 0xee, 0x45, 0xf7, 0x11, 0x68, 0xd9, 0x23, 0x6d, 0xd6, 0x24,
          0x6e, 0x97, 0x83, 0x13, 0x76, 0x9f, 0x35, 0xea, 0xb8, 0x47, 0x57,
          0x9b, 0x74, 0x47, 0x1f, 0x19, 0xe7, 0x97, 0x74, 0x1b, 0xb1, 0xa0,
          0xf3, 0x92, 0xdc, 0x35, 0xfd, 0x7f, 0x4f, 0x07, 0x22, 0xac,
      },
      {
          0xfd, 0x05, 0x41, 0x27, 0x00, 0x7a, 0xad, 0x17, 0xbc, 0xbf, 0x22,
          0x5e, 0xda, 0xeb, 0x7d, 0xaa, 0xb5, 0x2d, 0xc8, 0x61, 0xfe, 0x2c,
          0xba, 0x9e, 0x54, 0x43, 0x94, 0xdc, 0xfd, 0x0e, 0x6d, 0x6d, 0xa1,
          0x5e, 0x05, 0xa5, 0x05, 0x55, 0xe7, 0x2a, 0xf7, 0x53, 0x37, 0x2c,
          0xef, 0x44, 0x95, 0xca, 0x2c, 0xe8, 0xeb, 0xae, 0x61, 0x05, 0x33,
          0xe7, 0x0f, 0x30, 0x67, 0xbe, 0xcd, 0xb2, 0x93, 0x44, 0x44, 0x31,
          0x96, 0x4d, 0x72, 0x61, 0xcd, 0x23, 0x1f, 0x7b, 0x57, 0x33, 0x39,
          0xac, 0x57, 0x53, 0x3d, 0x70, 0x57, 0xb3, 0x6d, 0xd5, 0x8a, 0x89,
          0x30, 0xbc, 0xed, 0x29, 0x19, 0x39, 0x6e, 0x6a, 0x80, 0x21, 0x1e,
          0xb7, 0x84, 0xb1, 0x0a, 0xa4, 0xd0, 0xff, 0x59, 0x31, 0xe9, 0xba,
          0x39, 0xf7, 0x81, 0xdf, 0x1b, 0x7b, 0xd9, 0xdb, 0x02, 0x17, 0xbe,
          0x97, 0x63, 0xbd, 0x37, 0x14, 0xb8, 0x8b, 0x6c, 0xcc, 0x5b, 0xa3,
          0xee, 0xc8, 0x0b, 0x7b, 0xf5, 0xde, 0xbc, 0xb7, 0xa8, 0x25, 0x35,
          0xae, 0x0b, 0x30, 0x96, 0x33, 0x52, 0x13, 0xa3, 0x89, 0x3a, 0x2e,
          0xa4, 0x7e, 0x5f, 0x4e, 0xb1, 0xf7, 0x87, 0xba, 0x08, 0xb0, 0xfb,
          0xcb, 0x31, 0xa6, 0x2b, 0x3e, 0xe7, 0xc4, 0xcc, 0xa5, 0x14, 0x61,
          0xc9, 0x18, 0xfc, 0x74, 0x59, 0x06, 0xf4, 0xad, 0xfc, 0x2d, 0x0c,
          0x7d, 0xdb, 0x2a, 0xec, 0xd4, 0xe9, 0x31, 0x31, 0x50, 0x25, 0xb7,
          0x0c, 0x48, 0x30, 0xd5, 0x78, 0xb1, 0x10, 0xea, 0x5d, 0xaa, 0xaa,
          0x03, 0xec, 0xe3, 0x0f, 0x36, 0xa6, 0xbc, 0x18, 0x3a, 0x76, 0x87,
          0xa3, 0xe9, 0x33, 0xda, 0x48, 0x78, 0x93, 0x64, 0x72, 0x9b, 0x2a,
          0x1e, 0x0d, 0x50, 0x9f, 0x6f, 0xbd, 0xa5, 0x18, 0xb1, 0xa8, 0x14,
          0x9a, 0x1b, 0xb9, 0xb4, 0x2c, 0x1f, 0x46, 0xb8, 0x9b, 0x7a, 0xc7,
          0x84, 0xae, 0x27, 0xd1, 0xec, 0x1c, 0xec, 0x69, 0xf0, 0x35, 0x40,
          0x77, 0x7e, 0x27, 0x22, 0x36, 0x38, 0xdf, 0x15, 0xc2, 0xb3, 0xb8,
          0x63, 0x4a, 0x6e, 0x68, 0x8c, 0xc1, 0x39, 0x3c, 0x02, 0x4c, 0x2a,
          0x31, 0x18, 0x2e, 0x35, 0xf5, 0xca, 0xa2, 0xe9, 0x24, 0x92, 0xd5,
          0xe5, 0x2e, 0x5d, 0x1c, 0x94, 0xd9, 0xc7, 0xf0, 0x22, 0x16, 0x20,
          0xa8, 0x37, 0xdd, 0x0e, 0xab, 0x1b, 0x5e, 0x8b, 0xee, 0x81, 0xc6,
          0x17, 0xf1, 0x29, 0x5d, 0x1d, 0x6f, 0x53, 0x5a, 0xb2, 0x20, 0x88,
          0x3d, 0xa4, 0x98, 0x81, 0xa4, 0xef, 0x11, 0x1b, 0x64, 0x9e, 0x2c,
          0x95, 0xdd, 0xd7, 0x57, 0xb3, 0x95, 0xca, 0x40, 0xb9, 0x0d, 0x62,
          0xdc, 0x86, 0x8b, 0xe6, 0xd3, 0x7b, 0x33, 0xea, 0x50, 0x25, 0xe2,
          0x77, 0xe3, 0x0b, 0x5a, 0xc8, 0x81, 0xfc, 0xae, 0x74, 0x38, 0x69,
          0x07, 0xf8, 0x4e, 0xd7, 0x85, 0xa6, 0x2f, 0x13, 0xb7, 0x0a,
      },
      {
          0xf4, 0xdc, 0xac, 0x02, 0xc0, 0x95, 0x07, 0xdc, 0x12, 0x4f, 0x3a,
          0x01, 0xa9, 0xee, 0xf3, 0x07, 0x46, 0x94, 0x47, 0x15, 0x63, 0xa6,
          0x8e, 0x0a, 0xc4, 0xc4, 0xe4, 0x23, 0x85, 0x63, 0xe4, 0x4e, 0x9a,
          0x9e, 0x9f, 0xc1, 0xed, 0x4e, 0x94, 0x10, 0xfe, 0x7d, 0x90, 0xef,
          0x82, 0x21, 0xac, 0x67, 0x24, 0x99, 0x86, 0xc6, 0x85, 0x8b, 0x19,
          0x9a, 0x9b, 0x90, 0x0b, 0xa4, 0x90, 0x78, 0x34, 0x53, 0xb1, 0x4c,
          0xba, 0xb8, 0xc6, 0x40, 0x71, 0xc2, 0x17, 0x51, 0x05, 0xc2, 0xc9,
          0xbc, 0x03, 0x10, 0x4e, 0xfe, 0x2f, 0x94, 0xed, 0x13, 0x35, 0x7b,
          0x20, 0xa6, 0x59, 0x44, 0x81, 0xd0, 0xec, 0x12, 0x74, 0xa2, 0x4a,
          0xfb, 0xd7, 0x28, 0x70, 0xad, 0xde, 0x86, 0x2f, 0xef, 0xf9, 0x50,
          0x5d, 0x1e, 0x46, 0x3b, 0x82, 0x1c, 0x6a, 0xc9, 0x3f, 0x32, 0xae,
          0xcd, 0x93, 0x2c, 0xa5, 0x70, 0x45, 0x24, 0x38, 0x19, 0x79, 0xbf,
          0x35, 0xf5, 0xc1, 0x53, 0x06, 0x42, 0xaa, 0x6b, 0xf3, 0xb7, 0x8f,
          0xfb, 0xfc, 0x28, 0x46, 0x9a, 0x65, 0x2a, 0x45, 0x3d, 0x4c, 0x3c,
          0x6b, 0x9c, 0x44, 0xc5, 0x82, 0xb8, 0x80, 0xf9, 0xb3, 0x44, 0x95,
          0x17, 0x39, 0x01, 0x6b, 0xd6, 0x1d, 0x73, 0x22, 0x94, 0xff, 0x6f,
          0x7b, 0x4e, 0xb6, 0xa4, 0xe4, 0x6c, 0xa8, 0xdb, 0x38, 0x74, 0x0b,
          0xdb, 0xfa, 0x14, 0x99, 0x39, 0x28, 0xee, 0xfc, 0x62, 0xe5, 0x33,
          0x9c, 0x63, 0xd5, 0x7f, 0x2c, 0x4c, 0xf4, 0x76, 0x4c, 0xf7, 0x7b,
          0x27, 0x2c, 0xa9, 0xb9, 0x01, 0x3c, 0x58, 0xf7, 0xce, 0xaa, 0x0a,
          0x21, 0xc0, 0xa3, 0xa7, 0xa1, 0x85, 0xbc, 0xfe, 0x55, 0xfc, 0x3b,
          0x9b, 0x3d, 0xef, 0x14, 0x85, 0x8a, 0x4e, 0x5e, 0x1f, 0x4f, 0xc9,
          0x86, 0xb8, 0xf2, 0xb2, 0xe3, 0xe7, 0x7e, 0x87, 0xe2, 0x20, 0xd7,
          0x85, 0x2c, 0xd1, 0x13, 0xcb, 0x46, 0xad, 0xd7, 0xec, 0x66, 0xd5,
          0x2b, 0x6e, 0x87, 0x92, 0x13, 0xb2, 0x7d, 0x18, 0x69, 0x35, 0xcb,
          0xf1, 0xbe, 0xc2, 0x9d, 0xeb, 0xf5, 0x00, 0x0b, 0x73, 0xa3, 0xfd,
          0x51, 0xdc, 0x91, 0x0e, 0xe4, 0x46, 0x41, 0x23, 0xdd, 0x85, 0x53,
          0x0c, 0xae, 0xf9, 0x74, 0xcf, 0x1c, 0x32, 0x22, 0xda, 0x4a, 0x14,
          0x80, 0x71, 0x51, 0x23, 0xc3, 0xab, 0x7e, 0xec, 0x77, 0xcc, 0x59,
          0x12, 0x1c, 0x0f, 0x65, 0x6b, 0xd8, 0x41, 0xe0, 0x1b, 0x51, 0x9c,
          0x89, 0x26, 0xab, 0x88, 0x63, 0x5d, 0xbb, 0x95, 0x92, 0x5b, 0x2c,
          0x1d, 0xde, 0xc1, 0x88, 0xd6, 0xf1, 0x02, 0x48, 0xe0, 0xe2, 0xd1,
          0x40, 0x7f, 0xf1, 0x23, 0x4a, 0xcf, 0x36, 0xd5, 0xcf, 0x76, 0x0d,
          0x00, 0x2a, 0xf7, 0x5f, 0x31, 0xbb, 0xcc, 0x46, 0x84, 0x07, 0x04,
          0x98, 0x9c, 0xb4, 0x31, 0x15, 0x2d, 0x91, 0xf6, 0x29, 0x33,
      },
      {
          0xa8, 0x8e, 0x9a, 0xf9, 0x25, 0x12, 0x98, 0xe2, 0xc3, 0x61, 0x2e,
          0xe8, 0xd6, 0xa6, 0x77, 0x16, 0x49, 0x04, 0x75, 0x69, 0xd1, 0x83,
          0x2d, 0x3f, 0x2a, 0x79, 0xb6, 0x9b, 0xc9, 0x1d, 0x03, 0x90, 0x2a,
          0xd8, 0x11, 0x9f, 0x26, 0x36, 0xe7, 0xe9, 0x3a, 0x05, 0x4c, 0x15,
          0x49, 0x93, 0xda, 0xe9, 0xd0, 0x5a, 0xe4, 0x8d, 0x8a, 0xfa, 0x04,
          0xf1, 0x20, 0x84, 0x56, 0xec, 0x3c, 0x27, 0x19, 0x5c, 0xf1, 0xaf,
          0xbf, 0xf6, 0x0e, 0x58, 0x84, 0x2d, 0x94, 0x11, 0xf4, 0xb5, 0xf4,
          0x14, 0x51, 0xb0, 0x90, 0x46, 0x1a, 0x81, 0xed, 0xcf, 0x91, 0x66,
          0x58, 0xa6, 0x36, 0x3a, 0x52, 0x18, 0x5a, 0xc1, 0x08, 0x4c, 0x99,
          0xd3, 0xdc, 0xce, 0x7f, 0xce, 0x78, 0xe0, 0x38, 0x87, 0x32, 0xf1,
          0x80, 0x3c, 0x7b, 0x67, 0xaa, 0x6f, 0xdd, 0xe0, 0xfc, 0xcb, 0xd0,
          0xb0, 0x3a, 0x59, 0x52, 0x2a, 0x84, 0xe4, 0xf8, 0x4a, 0xff, 0x50,
          0xa0, 0x65, 0xc4, 0xee, 0xf4, 0x9c, 0xaa, 0x34, 0x46, 0xf9, 0xd2,
          0x6c, 0xa1, 0x61, 0x71, 0x49, 0x32, 0x25, 0x84, 0x54, 0x90, 0x44,
          0xbe, 0xa4, 0x0b, 0xf7, 0xfe, 0x26, 0x81, 0x63, 0x73, 0xf7, 0x2f,
          0xf2, 0xfa, 0x24, 0x52, 0xa4, 0xd9, 0x4c, 0xc1, 0xa7, 0xa5, 0xc3,
          0x03, 0x36, 0x13, 0x9b, 0x16, 0x45, 0x16, 0xcb, 0x4b, 0x99, 0x38,
          0xf3, 0x6d, 0xc8, 0x7e, 0xab, 0xb3, 0x53, 0xdf, 0xb6, 0x82, 0x60,
          0x12, 0x11, 0x36, 0x69, 0x0e, 0x05, 0x31, 0x8e, 0xcf, 0xd7, 0x3f,
          0x32, 0xe7, 0x95, 0x84, 0x1d, 0xc8, 0xb5, 0xbe, 0x49, 0x17, 0x9d,
          0xcf, 0xa9, 0x5a, 0x2a, 0xc4, 0x11, 0x86, 0xe8, 0x6c, 0x02, 0x56,
          0xb0, 0x25, 0x2f, 0xa0, 0x06, 0xb3, 0x62, 0xb2, 0x11, 0xaf, 0xbe,
          0xa4, 0xe8, 0x61, 0x64, 0x85, 0xfb, 0xeb, 0x1c, 0xf1, 0xbc, 0x2c,
          0xae, 0x10, 0x51, 0x16, 0xa6, 0xc0, 0xb3, 0x86, 0x8e, 0x6d, 0x79,
          0xb6, 0xbd, 0xde, 0x1e, 0x26, 0x06, 0x46, 0x65, 0x82, 0x84, 0x5a,
          0x97, 0xd3, 0xb7, 0x93, 0x78, 0x6b, 0x9d, 0x14, 0x33, 0x94, 0x43,
          0x34, 0x04, 0x45, 0xd1, 0x47, 0xd4, 0x2f, 0x17, 0xcf, 0xf1, 0xdd,
          0xea, 0x11, 0x52, 0xae, 0x01, 0x88, 0x3a, 0x10, 0xee, 0x5c, 0x16,
          0xcd, 0xb5, 0x48, 0xe9, 0x16, 0x2c, 0x70, 0xb4, 0x1e, 0x19, 0x38,
          0xe0, 0x18, 0xe9, 0xae, 0xc5, 0xda, 0x74, 0x41, 0x2d, 0x70, 0x07,
          0x60, 0x37, 0x27, 0x66, 0xf7, 0x00, 0xbb, 0x79, 0x51, 0xf3, 0x7c,
          0x8a, 0x2b, 0xb5, 0x69, 0x6e, 0x10, 0x1f, 0xe0, 0x0a, 0x5e, 0xbe,
          0xb4, 0x4e, 0x0e, 0x02, 0x59, 0xb5, 0xcb, 0x4a, 0x6a, 0x86, 0x8b,
          0xcc, 0xa2, 0x13, 0xa0, 0xe9, 0xf2, 0x5c, 0xb0, 0x23, 0xb2, 0x15,
          0xf9, 0xbb, 0x43, 0xc1, 0x54, 0xf4, 0xc8, 0xab, 0x16, 0xa6,
      },
  };
  const BigNumStr bnm12[] = {
      {
          0x14, 0x92, 0xd1, 0x1f, 0xac, 0x90, 0x4d, 0xc3, 0x3e, 0xb8, 0xa7,
          0xfe, 0x35, 0x98, 0xda, 0x94, 0xd6, 0xf3, 0x27, 0x29, 0x4b, 0x70,
          0xcf, 0x0f, 0xda, 0x14, 0x97, 0xa1, 0x9b, 0x6b, 0x24, 0x08,
      },
      {
          0x76, 0x8d, 0xe5, 0xc2, 0xab, 0x78, 0xd9, 0x76, 0x98, 0xff, 0x92,
          0xea, 0x95, 0x58, 0xc5, 0x0c, 0x6f, 0x1b, 0x35, 0xc4, 0xbf, 0x4a,
          0x9e, 0xc2, 0x6a, 0xec, 0xe2, 0xf6, 0xaf, 0xeb, 0xac, 0x06,
      },
      {
          0x50, 0x38, 0x60, 0x31, 0xab, 0x05, 0xcc, 0xb1, 0xc2, 0x06, 0xa0,
          0xd7, 0x92, 0x12, 0xbd, 0x17, 0x81, 0xb9, 0x7e, 0x07, 0x9b, 0xd4,
          0x3b, 0x2d, 0x3c, 0x15, 0x3a, 0x5a, 0xf1, 0xc0, 0xc4, 0x75,
      },
      {
          0xa1, 0x3f, 0xea, 0x99, 0x7e, 0xc7, 0x65, 0xf5, 0x41, 0xa6, 0xd6,
          0xd3, 0x77, 0xec, 0x27, 0xf8, 0x6c, 0x18, 0x28, 0x69, 0x97, 0x08,
          0x0e, 0x63, 0x5a, 0xda, 0xfe, 0x9f, 0xd1, 0x4a, 0x61, 0x4f,
      },
  };
  const Fq12ElemStr fq12_expected_result_str = {
      0x3a, 0x30, 0x33, 0xa2, 0x14, 0xdf, 0xdc, 0x70, 0x48, 0xf3, 0xba, 0x3f,
      0xce, 0xfc, 0x69, 0x24, 0xae, 0xa1, 0xf3, 0xcf, 0xd8, 0x77, 0x69, 0x38,
      0x38, 0xf4, 0x8e, 0xfb, 0x51, 0xfb, 0x10, 0x7c, 0x73, 0xb4, 0xee, 0xf9,
      0x76, 0xbb, 0x45, 0xec, 0x07, 0x24, 0x23, 0xa3, 0x38, 0x64, 0x39, 0x4c,
      0x03, 0x94, 0xf5, 0xee, 0x5f, 0xc9, 0x83, 0x4f, 0xd1, 0xa6, 0x4a, 0x05,
      0x25, 0x0c, 0x46, 0x33, 0xd4, 0x40, 0x47, 0xae, 0xec, 0xda, 0x30, 0xf9,
      0xe1, 0x28, 0xfb, 0xb1, 0x4c, 0x62, 0xfb, 0x6c, 0x90, 0x0d, 0xb1, 0xec,
      0xaf, 0x2d, 0x95, 0x64, 0x11, 0x93, 0xda, 0x8e, 0xb8, 0x7d, 0xe4, 0xa3,
      0x8e, 0xa6, 0x9b, 0xaa, 0x58, 0xd5, 0xdc, 0x59, 0x1a, 0x51, 0x53, 0x47,
      0x30, 0x1c, 0x2b, 0xb0, 0xbb, 0x57, 0x57, 0x0a, 0x8c, 0x5f, 0x62, 0x9c,
      0x52, 0x28, 0xc0, 0x25, 0x27, 0xae, 0xcd, 0x36, 0xbf, 0x2d, 0x03, 0xb1,
      0x33, 0xfd, 0x24, 0x81, 0x3c, 0x91, 0x43, 0x90, 0x0c, 0x20, 0xc4, 0xe3,
      0x69, 0x3f, 0xa9, 0xa2, 0x7c, 0xc2, 0x48, 0x28, 0x1b, 0xf0, 0x81, 0x1f,
      0x2b, 0x8f, 0x8d, 0x43, 0x38, 0x18, 0x12, 0xa3, 0x89, 0xf1, 0xd7, 0x60,
      0x89, 0x68, 0x6a, 0xc4, 0xcc, 0x5d, 0xf4, 0xce, 0x43, 0x95, 0x84, 0xcd,
      0x01, 0x55, 0xb8, 0x5d, 0x24, 0x50, 0xcd, 0xe3, 0x68, 0x1c, 0xff, 0x59,
      0x0d, 0xac, 0xcb, 0xe7, 0x9d, 0x68, 0x0f, 0x4a, 0xaf, 0xeb, 0xb5, 0xfb,
      0xf9, 0xb1, 0x58, 0x80, 0xd5, 0x71, 0x53, 0x26, 0x2e, 0x9c, 0xce, 0x10,
      0xac, 0xd2, 0x0a, 0xeb, 0xb4, 0x5c, 0xc8, 0xd7, 0x16, 0x26, 0x66, 0x71,
      0xdd, 0x67, 0xba, 0xdb, 0x35, 0x76, 0x64, 0xc6, 0x0b, 0x0b, 0x07, 0x4b,
      0x65, 0xa2, 0xf7, 0x68, 0x9e, 0xfe, 0xe8, 0xe3, 0xa7, 0x09, 0x79, 0xc5,
      0xdf, 0x5e, 0x9c, 0xec, 0xee, 0x83, 0x59, 0xb5, 0x89, 0x81, 0xa2, 0x87,
      0xd7, 0x75, 0x65, 0x90, 0xde, 0x78, 0x37, 0x59, 0x04, 0x9b, 0x50, 0xc3,
      0xba, 0x90, 0x09, 0x15, 0xab, 0x17, 0xc5, 0xbf, 0x5b, 0xb9, 0xe4, 0x6c,
      0xa9, 0xb1, 0x49, 0x76, 0x25, 0x74, 0x5d, 0x9c, 0x78, 0xc5, 0x09, 0xee,
      0xeb, 0xeb, 0x9d, 0x1c, 0x6c, 0xc0, 0x27, 0x9d, 0x66, 0xe6, 0x7f, 0x31,
      0xcd, 0xb0, 0x8a, 0xe4, 0x9e, 0xbd, 0x70, 0x18, 0x88, 0xa1, 0x08, 0x39,
      0xa2, 0x48, 0xa5, 0x98, 0xb6, 0xad, 0x10, 0x54, 0x07, 0xcd, 0xfc, 0x6d,
      0xb1, 0x02, 0xff, 0xe3, 0x92, 0xd7, 0x9d, 0x48, 0xfb, 0xce, 0x88, 0x46,
      0x92, 0x07, 0x02, 0xba, 0xee, 0xeb, 0xae, 0x88, 0xb1, 0x4e, 0xd0, 0xf2,
      0xce, 0xd7, 0x57, 0x07, 0xd4, 0x39, 0xd2, 0x7d, 0x1a, 0x0c, 0xef, 0xf2,
      0x84, 0x84, 0x22, 0x8a, 0xb1, 0x80, 0x0d, 0xdc, 0x64, 0x86, 0xfd, 0x70,
  };

  FfElementObj r12(&this->fq12);
  FfElementObj fq12_exp[4];

  // prepare data for test
  for (m = 0; m < 4; m++) {
    fq12_exp[m] = FfElementObj(&this->fq12, ff12e[m]);
    // initialize data for test
    p[m] = fq12_exp[m];
    b[m] = &bnm12[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfSscmMultiExp(this->fq12, p, b, 4, r12));
  EXPECT_EQ(FfElementObj(&this->fq12, fq12_expected_result_str), r12)
      << "FfSscmMultiExp: Finite field element does not match with reference "
         "value";
}

TEST_F(FfElementTest, SscmFfMultiExpWorksGivenFiveExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[5];
  FfElement const* p[5];
  BigNumStr const* b[5];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x7B, 0x26, 0x96, 0x84, 0x04, 0x0D, 0x6F, 0x10, 0xAA, 0x7F, 0xD0, 0x0C,
       0x41, 0x3A, 0x68, 0x48, 0xBF, 0x3D, 0xB5, 0xCE, 0x3A, 0x9C, 0xA9, 0x10,
       0xA4, 0x67, 0x7C, 0xC0, 0x41, 0x52, 0xCA, 0xE7},
      {0x2E, 0xD9, 0xEF, 0x86, 0xB6, 0x32, 0x72, 0x89, 0x37, 0xCF, 0x16, 0xCE,
       0x27, 0x15, 0x7A, 0x95, 0xCF, 0x94, 0x1B, 0xF3, 0xCE, 0x49, 0x20, 0x29,
       0x9A, 0x61, 0x2E, 0x0A, 0xF5, 0xE1, 0xDC, 0x9F},
      {0xE0, 0x19, 0x37, 0x44, 0xA7, 0x45, 0x49, 0x7E, 0x19, 0x02, 0x09, 0x78,
       0xBE, 0xA4, 0x65, 0x60, 0x7A, 0xF5, 0xC8, 0xAF, 0x97, 0x5E, 0xDA, 0x3A,
       0xB4, 0x16, 0x70, 0x31, 0xE3, 0xB4, 0x18, 0xA8},
      {0xBC, 0x92, 0x29, 0x92, 0x14, 0xD3, 0x3F, 0xB8, 0xB8, 0x8C, 0x41, 0xDF,
       0xC5, 0xA1, 0xCA, 0x77, 0xE5, 0x73, 0xFE, 0xA7, 0xAC, 0x81, 0x66, 0x11,
       0x5A, 0x6D, 0x7D, 0x97, 0x13, 0x92, 0x8D, 0x1D},
      {0xE1, 0xA0, 0x0D, 0xDC, 0x8F, 0x20, 0xF5, 0x2D, 0x1D, 0x1F, 0xBD, 0x24,
       0x8C, 0xC2, 0x26, 0x06, 0x89, 0xE9, 0x46, 0xA9, 0xD2, 0x44, 0x8F, 0x1A,
       0x8D, 0xAF, 0x20, 0x73, 0x60, 0x0B, 0x66, 0x9C}};

  const BigNumStr bnm[] = {
      {0x78, 0xE6, 0x3F, 0x42, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x6E,
       0xE9, 0xFF, 0x4F, 0xFF, 0x23, 0xFF, 0x67, 0xB4, 0xED, 0xEE, 0xD7, 0xC6,
       0xFF, 0x4B, 0x0B, 0x6F, 0x1F, 0xD6, 0xFD, 0x7F},
      {0xA1, 0x87, 0xFF, 0x1B, 0xFF, 0x85, 0x68, 0xFF, 0x1C, 0xFF, 0xD5, 0xFF,
       0x95, 0x73, 0xFF, 0xFF, 0xFF, 0x9E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E,
       0x9E, 0x62, 0xFF, 0xFF, 0x97, 0xFF, 0xFF, 0xF3},
      {0x2E, 0xE8, 0x1E, 0x35, 0xFF, 0x82, 0x0B, 0x99, 0xEC, 0x30, 0x2B, 0xCE,
       0xEC, 0x83, 0x05, 0xFF, 0x9E, 0x3C, 0xFF, 0xFF, 0xA1, 0xFF, 0xFF, 0xFF,
       0x57, 0x26, 0x5B, 0x6C, 0xFF, 0xE6, 0x94, 0xFF},
      {0xFF, 0xFF, 0xFF, 0xFF, 0x94, 0xFF, 0xFF, 0x79, 0xFC, 0xFF, 0x83, 0xFF,
       0xFF, 0xFF, 0xFF, 0xFF, 0xA9, 0xFF, 0x2F, 0x6B, 0xD1, 0xE7, 0xFF, 0xFF,
       0xFF, 0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
      {0x63, 0x53, 0x53, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0x09, 0xFF,
       0xC0, 0xD8, 0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x45,
       0xFF, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x06}};

  const FqElemStr res = {
      0x8F, 0x6D, 0x54, 0xCA, 0x00, 0x85, 0xD8, 0x78, 0xA4, 0x45, 0x36,
      0x7C, 0x23, 0xD0, 0x3C, 0x2D, 0x20, 0x78, 0xD9, 0xD7, 0xF1, 0xC3,
      0x99, 0xD9, 0xD3, 0x0A, 0x0E, 0x86, 0x98, 0xC2, 0xA7, 0x97,
  };
  // prepare data for test
  for (m = 0; m < 5; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfSscmMultiExp(this->fq, p, b, 5, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfSscmMultiExp: Finite field element does not "
         "match with reference value";
}

TEST_F(FfElementTest, SscmFfMultiExpWorksGivenSixExponents) {
  FfElementObj r(&this->fq);
  FfElementObj fq_exp[6];
  FfElement const* p[6];
  BigNumStr const* b[6];
  int m = 0;
  const FqElemStr ffe[] = {
      {0x75, 0x18, 0x6A, 0x3D, 0xCC, 0x76, 0x33, 0x92, 0xD0, 0x57, 0xDA, 0xE0,
       0x37, 0x6F, 0x71, 0xD5, 0x9E, 0x7E, 0x65, 0xED, 0xD7, 0xFD, 0x82, 0xC0,
       0x6C, 0x83, 0x60, 0x30, 0xC1, 0xD3, 0x3D, 0x4D},
      {0xF5, 0x3A, 0x20, 0xAB, 0x22, 0x47, 0x07, 0xAE, 0x71, 0xC1, 0x91, 0x73,
       0xEF, 0x1F, 0x1D, 0x76, 0x2E, 0xEE, 0x0D, 0xDE, 0xD9, 0xF8, 0x5C, 0x85,
       0xC7, 0x5B, 0x93, 0x88, 0xF6, 0xFF, 0x4C, 0xA5},
      {0x21, 0xB3, 0x5E, 0xE8, 0xD2, 0xDA, 0x14, 0x1C, 0xB5, 0x2C, 0xFC, 0x61,
       0x9D, 0xEB, 0x65, 0x87, 0xBB, 0x5B, 0xBC, 0xFD, 0x37, 0x54, 0x6B, 0xDC,
       0xD9, 0xFA, 0x7A, 0xCD, 0x18, 0x7E, 0x8B, 0x89},
      {0x70, 0xB7, 0x19, 0x42, 0xB4, 0x41, 0x2C, 0x1B, 0xBD, 0x7B, 0x25, 0x06,
       0xA2, 0x95, 0xB5, 0xB6, 0x70, 0xDF, 0x58, 0x71, 0xCA, 0x09, 0x12, 0x66,
       0x5A, 0x7B, 0xF0, 0x5F, 0x43, 0x69, 0x32, 0xF6},
      {0x3D, 0x57, 0xE8, 0x3F, 0x1E, 0x39, 0x2B, 0xE5, 0xD0, 0xCF, 0xA9, 0xC8,
       0x72, 0x10, 0x7F, 0xC5, 0x4F, 0xAE, 0xE9, 0x73, 0x28, 0x4C, 0xFC, 0x3F,
       0xD5, 0xFF, 0xE8, 0xCF, 0x2C, 0xEB, 0x33, 0xD3},
      {0x51, 0x80, 0x15, 0x1C, 0xE6, 0x9C, 0x4B, 0x80, 0x40, 0xBA, 0x40, 0x29,
       0x3A, 0x86, 0xA8, 0xB2, 0x66, 0x08, 0x97, 0x8D, 0xEB, 0x8B, 0xA7, 0x93,
       0xE3, 0x49, 0xC6, 0xC0, 0x46, 0x93, 0xC1, 0x75}};
  const BigNumStr bnm[] = {
      {0xFF, 0xFF, 0xFF, 0x49, 0x50, 0xFF, 0x19, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
       0xFF, 0x28, 0x4A, 0x0E, 0xFF, 0x74, 0xFF, 0xC7, 0xFF, 0xD9, 0x81, 0xFF,
       0xFF, 0xFF, 0xD5, 0x2C, 0xFF, 0xFF, 0xFF, 0xAE},
      {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
       0xFF, 0x0C, 0x03, 0xFF, 0x95, 0xFF, 0x11, 0x9D, 0xFF, 0x9B, 0xFF, 0xFF,
       0x68, 0x44, 0xFF, 0xC9, 0xFF, 0x10, 0xC7, 0xBF},
      {0x64, 0xFF, 0x89, 0x4A, 0xFF, 0x20, 0xFF, 0xE7, 0xFF, 0x36, 0x23, 0xDD,
       0xFF, 0xFF, 0xFC, 0xFF, 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, 0xFF, 0xFF,
       0xFF, 0xFF, 0xFF, 0xFF, 0x94, 0xD3, 0x59, 0xBE},
      {0x0F, 0xFF, 0x76, 0xA5, 0x9E, 0x02, 0x9B, 0xFF, 0xA1, 0xFF, 0xC2, 0xFF,
       0x9D, 0xFF, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, 0x81, 0x2A, 0xFF, 0x98,
       0xFF, 0xFF, 0xB9, 0xFF, 0xB7, 0x78, 0xE6, 0xFF},
      {0xAF, 0xF3, 0xFF, 0x3C, 0xA8, 0xFF, 0xB9, 0x3A, 0xFF, 0xFF, 0xFF, 0x70,
       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x85, 0xFF, 0xFF, 0x52,
       0xFF, 0x41, 0x10, 0x62, 0xFF, 0x51, 0x35, 0xFF},
      {0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0xC3, 0xD9, 0xFF, 0xFF, 0x3A, 0x44, 0x88,
       0x04, 0xFF, 0x0B, 0xFF, 0xFF, 0xFF, 0x64, 0xFF, 0xFF, 0x52, 0xFF, 0x7D,
       0x6B, 0x24, 0x6E, 0xFF, 0xFF, 0xFF, 0xA7, 0x71}};
  const FqElemStr res = {
      0xFB, 0x89, 0x4F, 0xBB, 0x91, 0xEE, 0xD6, 0x57, 0x2D, 0x34, 0xF9,
      0x0B, 0xE3, 0xEB, 0x71, 0x1D, 0x63, 0x74, 0x96, 0x3B, 0xD4, 0x02,
      0x2F, 0x08, 0xDB, 0x95, 0x6A, 0x40, 0x68, 0xD4, 0x55, 0x76,
  };
  // prepare data for test
  for (m = 0; m < 6; m++) {
    fq_exp[m] = FfElementObj(&this->fq, ffe[m]);
    // initialize data for test
    p[m] = fq_exp[m];
    b[m] = &bnm[m];
  }
  // do test
  EXPECT_EQ(kEpidNoErr, FfSscmMultiExp(this->fq, p, b, 6, r));
  EXPECT_EQ(FfElementObj(&this->fq, res), r)
      << "FfSscmMultiExp: Finite field element does not "
         "match with reference value";
}
///////////////////////////////////////////////////////////////////////
// FfGetRandom

TEST_F(FfElementTest, FfGetRandomFailsGivenArgumentsMismatch) {
  Prng my_prng;
  EXPECT_EQ(kEpidBadArgErr, FfGetRandom(this->fq12, &this->bn_one_data,
                                        &Prng::Generate, &my_prng, this->fq_r));
  EXPECT_EQ(kEpidBadArgErr,
            FfGetRandom(this->fq, &this->bn_one_data, &Prng::Generate, &my_prng,
                        this->fq12_r));
}

TEST_F(FfElementTest, FfGetRandomFailsGivenNullPtr) {
  Prng my_prng;
  FfElementObj r(&this->fq);
  EXPECT_EQ(kEpidBadArgErr, FfGetRandom(nullptr, &this->bn_one_data,
                                        &Prng::Generate, &my_prng, r));
  EXPECT_EQ(kEpidBadArgErr,
            FfGetRandom(this->fq, nullptr, &Prng::Generate, &my_prng, r));
  EXPECT_EQ(kEpidBadArgErr,
            FfGetRandom(this->fq, &this->bn_one_data, nullptr, &my_prng, r));
  EXPECT_EQ(kEpidBadArgErr, FfGetRandom(this->fq, &this->bn_one_data,
                                        &Prng::Generate, &my_prng, nullptr));
}
TEST_F(FfElementTest, FfGetRandomSucceedsGivenFq2) {
  Prng my_prng;
  FfElementObj r(&this->fq2);
  EXPECT_EQ(kEpidNoErr, FfGetRandom(this->fq2, &this->bn_one_data,
                                    &Prng::Generate, &my_prng, r));
}
TEST_F(FfElementTest, FfGetRandomSucceedsGivenFq6) {
  Prng my_prng;
  FfElementObj r(&this->fq6);
  EXPECT_EQ(kEpidNoErr, FfGetRandom(this->fq6, &this->bn_one_data,
                                    &Prng::Generate, &my_prng, r));
}
TEST_F(FfElementTest, FfGetRandomSucceedsGivenFq12) {
  Prng my_prng;
  FfElementObj r(&this->fq12);
  EXPECT_EQ(kEpidNoErr, FfGetRandom(this->fq12, &this->bn_one_data,
                                    &Prng::Generate, &my_prng, r));
}

TEST_F(FfElementTest, FfGetRandomSuccedsGivenLowBound) {
  Prng my_prng;
  FfElementObj r(&this->fq);
  const BigNumStr bn_low_data1 = {
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
  const BigNumStr bn_low_data2 = {
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
  FqElemStr buf;
  unsigned int result;
  EXPECT_EQ(kEpidNoErr, FfGetRandom(this->fq, &this->bn_one_data,
                                    &Prng::Generate, &my_prng, r));
  THROW_ON_EPIDERR(WriteFfElement(this->fq, r, &buf, sizeof(buf)));
  THROW_ON_EPIDERR(Cmp_OctStr256(&this->bn_one_data.data, &buf.data, &result));
  EXPECT_EQ(result, (unsigned int)2);
  EXPECT_EQ(kEpidNoErr, FfGetRandom(this->fq, &this->bn_two_data,
                                    &Prng::Generate, &my_prng, r));
  THROW_ON_EPIDERR(WriteFfElement(this->fq, r, &buf, sizeof(buf)));
  THROW_ON_EPIDERR(Cmp_OctStr256(&this->bn_two_data.data, &buf.data, &result));
  EXPECT_EQ(result, (unsigned int)2);
  EXPECT_EQ(kEpidNoErr,
            FfGetRandom(this->fq, &bn_low_data1, &Prng::Generate, &my_prng, r));
  THROW_ON_EPIDERR(WriteFfElement(this->fq, r, &buf, sizeof(buf)));
  THROW_ON_EPIDERR(Cmp_OctStr256(&bn_low_data1.data, &buf.data, &result));
  EXPECT_EQ(result, (unsigned int)2);
  EXPECT_EQ(kEpidNoErr,
            FfGetRandom(this->fq, &bn_low_data2, &Prng::Generate, &my_prng, r));
  THROW_ON_EPIDERR(WriteFfElement(this->fq, r, &buf, sizeof(buf)));
  THROW_ON_EPIDERR(Cmp_OctStr256(&bn_low_data2.data, &buf.data, &result));
  EXPECT_EQ(result, (unsigned int)2);
}

TEST_F(FfElementTest, FfGetRandomGeneratesDifferentNumbers) {
  Prng my_prng;
  FfElementObj r1(&this->fq);
  FfElementObj r2(&this->fq);
  bool result;
  EXPECT_EQ(kEpidNoErr, FfGetRandom(this->fq, &this->bn_one_data,
                                    &Prng::Generate, &my_prng, r1));
  EXPECT_EQ(kEpidNoErr, FfGetRandom(this->fq, &this->bn_one_data,
                                    &Prng::Generate, &my_prng, r2));
  THROW_ON_EPIDERR(FfIsEqual(this->fq, r1, r2, &result));
  EXPECT_FALSE(result);
}
TEST_F(FfElementTest, FfGetRandomFailsOnMaxIterGivenLargeLowBound) {
  Prng my_prng;
  FfElementObj r(&this->fq);
  // FfGetRandom generates random data between [low_bound, modulus-1]
  // modulus in this case is Intel(R) EPID 2.0 parameter q
  // giving low_bound = modulus - {0x30, 0x13} should get kEpidRandMaxIterErr
  const BigNumStr bn_low_data = {
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xCD, 0x46, 0xE5, 0xF2,
      0x5E, 0xEE, 0x71, 0xA4, 0x9F, 0x0C, 0xDC, 0x65, 0xFB, 0x12, 0x98,
      0x0A, 0x82, 0xD3, 0x29, 0x2D, 0xDB, 0xAE, 0xD3, 0x00, 0x00};
  EXPECT_EQ(kEpidRandMaxIterErr,
            FfGetRandom(this->fq, &bn_low_data, &Prng::Generate, &my_prng, r));
}

}  // namespace
