HEBench
tutorial_eltwiseadd_benchmark_palisade.cpp
Go to the documentation of this file.
1 
2 // Copyright (C) 2021 Intel Corporation
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
7 #include <algorithm>
8 #include <array>
9 #include <cassert>
10 #include <cstring>
11 #include <memory>
12 #include <sstream>
13 #include <utility>
14 #include <vector>
15 
19 
20 //----------------------------------------------
21 // class TutorialEltwiseAddBenchmarkDescription
22 //----------------------------------------------
23 
25 TutorialEltwiseAddBenchmarkDescription::TutorialEltwiseAddBenchmarkDescription()
26 {
28  // initialize the descriptor for this benchmark
29  std::memset(&m_descriptor, 0, sizeof(hebench::APIBridge::BenchmarkDescriptor));
31 
33  m_descriptor.workload = hebench::APIBridge::Workload::EltwiseAdd;
35 
37  m_descriptor.data_type = hebench::APIBridge::DataType::Int64;
39 
41  m_descriptor.category = hebench::APIBridge::Category::Offline;
43 
45  m_descriptor.cat_params.offline.data_count[0] = 2; // 2 data samples for op parameter 0
46  m_descriptor.cat_params.offline.data_count[1] = 5; // 5 data samples for op parameter 1
48 
50  m_descriptor.cipher_param_mask = 1 << 1;
52 
53  //
55  m_descriptor.scheme = HEBENCH_HE_SCHEME_BFV;
57 
59  m_descriptor.security = TUTORIAL_HE_SECURITY_128;
61 
63  m_descriptor.other = 0; // no extras needed for our purpose:
64  // Other backends can use this field to differentiate between
65  // benchmarks for which internal parameters, not specified by
66  // other fields of this structure, differ.
68 
70  // specify default arguments for this workload flexible parameters:
71  hebench::cpp::WorkloadParams::EltwiseAdd default_workload_params;
72  default_workload_params.n() = 400;
73  this->addDefaultParameters(default_workload_params);
75 }
77 
78 TutorialEltwiseAddBenchmarkDescription::~TutorialEltwiseAddBenchmarkDescription()
79 {
80  // nothing needed in this example
81 }
82 
84 std::string TutorialEltwiseAddBenchmarkDescription::getBenchmarkDescription(const hebench::APIBridge::WorkloadParams *p_w_params) const
85 {
86  std::stringstream ss;
87  ss << BenchmarkDescription::getBenchmarkDescription(p_w_params);
88  ss << ", Encryption parameters" << std::endl
89  << ", , HE Library, PALISADE v1.11.3" << std::endl
90  << ", , Poly modulus degree, " << PolyModulusDegree << std::endl
91  << ", , Coefficient Moduli, 60, 60";
92  if (p_w_params)
93  {
94  hebench::cpp::WorkloadParams::EltwiseAdd w_params(*p_w_params);
95  ss << std::endl
96  << ", Workload parameters" << std::endl
97  << ", , n, " << w_params.n();
98  } // end if
99  return ss.str();
100 }
102 
105  const hebench::APIBridge::WorkloadParams *p_params)
106 {
107  if (!p_params)
108  throw hebench::cpp::HEBenchError(HEBERROR_MSG_CLASS("Invalid empty workload parameters. This workload requires flexible parameters."),
110 
111  TutorialEngine &ex_engine = dynamic_cast<TutorialEngine &>(engine);
112  return new TutorialEltwiseAddBenchmark(ex_engine, m_descriptor, *p_params);
113 }
115 
117 void TutorialEltwiseAddBenchmarkDescription::destroyBenchmark(hebench::cpp::BaseBenchmark *p_bench)
118 {
119  if (p_bench)
120  delete p_bench;
121 }
123 
124 //---------------------------------------------
125 // class TutorialEltwiseAddBenchmark::Workload
126 //---------------------------------------------
127 
129 TutorialEltwiseAddBenchmark::Workload::PalisadeBFVContext::PalisadeBFVContext(int poly_modulus_degree)
130 {
131  assert(TutorialEltwiseAddBenchmarkDescription::NumCoefficientModuli == 1);
132  std::size_t plaintext_modulus = 65537;
133  lbcrypto::SecurityLevel sec_level = lbcrypto::HEStd_128_classic;
134  double sigma = 3.2;
135  std::size_t num_coeff_moduli = 2;
136  std::size_t max_depth = 2;
137  std::size_t coeff_moduli_bits = 40;
138 
139  lbcrypto::CryptoContext<lbcrypto::DCRTPoly> crypto_context =
140  lbcrypto::CryptoContextFactory<lbcrypto::DCRTPoly>::genCryptoContextBFVrns(
141  plaintext_modulus, sec_level, sigma, 0, num_coeff_moduli,
142  0, OPTIMIZED, max_depth, 0, coeff_moduli_bits, poly_modulus_degree);
143  crypto_context->Enable(ENCRYPTION);
144  crypto_context->Enable(SHE);
145 
146  lbcrypto::LPKeyPair<lbcrypto::DCRTPoly> local_key = crypto_context->KeyGen();
147  lbcrypto::LPKeyPair<lbcrypto::DCRTPoly> *p_key = new lbcrypto::LPKeyPair<lbcrypto::DCRTPoly>(local_key.publicKey, local_key.secretKey);
148  local_key = lbcrypto::LPKeyPair<lbcrypto::DCRTPoly>();
149  m_keys = std::unique_ptr<lbcrypto::LPKeyPair<lbcrypto::DCRTPoly>>(p_key);
150 
151  m_p_palisade_context = std::make_shared<lbcrypto::CryptoContext<lbcrypto::DCRTPoly>>(crypto_context);
152  m_slot_count = poly_modulus_degree;
153 }
154 
156 {
157  if (vector_size <= 0)
158  throw std::invalid_argument("vector_size");
159  m_vector_size = vector_size;
160  m_p_context = std::make_shared<PalisadeBFVContext>(TutorialEltwiseAddBenchmarkDescription::PolyModulusDegree);
161 }
162 
163 std::vector<lbcrypto::Plaintext> TutorialEltwiseAddBenchmark::Workload::encodeVector(const std::vector<std::vector<std::int64_t>> &vec)
164 {
165  std::vector<lbcrypto::Plaintext> retval(vec.size());
166 
167  for (std::size_t i = 0; i < vec.size(); ++i)
168  {
169  assert(vec[i].size() <= context().getSlotCount());
170  retval[i] = context().context()->MakePackedPlaintext(vec[i]);
171  }
172  return retval;
173 }
174 
175 std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> TutorialEltwiseAddBenchmark::Workload::encryptVector(const std::vector<lbcrypto::Plaintext> &encoded_vec)
176 {
177  std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> retval(encoded_vec.size());
178  for (std::size_t i = 0; i < encoded_vec.size(); i++)
179  retval[i] = context().context()->Encrypt(context().publicKey(), encoded_vec[i]);
180  return retval;
181 }
182 
183 std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> TutorialEltwiseAddBenchmark::Workload::eltwiseadd(const std::vector<lbcrypto::Plaintext> &A,
184  const std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> &B)
185 {
186  std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> retval(A.size() * B.size());
187 
188  // This is the main operation function:
189  // for an offline test, it must store the result of the operation on every
190  // set of input sample in the same order as the input set.
191  // See documentation on "Ordering of Results Based on Input Batch Sizes"
192  // for details.
193  for (std::size_t A_i = 0; A_i < A.size(); ++A_i)
194  for (std::size_t B_i = 0; B_i < B.size(); ++B_i)
195  {
196  lbcrypto::Ciphertext<lbcrypto::DCRTPoly> &retval_item = retval[A_i * B.size() + B_i];
197  retval_item = context().context()->EvalAdd(B[B_i], A[A_i]);
198  }
199 
200  return retval;
201 }
202 
203 std::vector<lbcrypto::Plaintext> TutorialEltwiseAddBenchmark::Workload::decryptResult(const std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> &encrypted_result)
204 {
205  std::vector<lbcrypto::Plaintext> retval(encrypted_result.size());
206  for (std::size_t i = 0; i < encrypted_result.size(); i++)
207  retval[i] = context().decrypt(encrypted_result[i]);
208  return retval;
209 }
210 
211 std::vector<std::vector<int64_t>> TutorialEltwiseAddBenchmark::Workload::decodeResult(const std::vector<lbcrypto::Plaintext> &encoded_result)
212 {
213  std::vector<std::vector<int64_t>> retval(encoded_result.size());
214  for (std::size_t i = 0; i < encoded_result.size(); ++i)
215  {
216  retval[i] = encoded_result[i]->GetPackedValue();
217  retval[i].resize(m_vector_size);
218  }
219  return retval;
220 }
222 
223 //-----------------------------------
224 // class TutorialEltwiseAddBenchmark
225 //-----------------------------------
226 
228 TutorialEltwiseAddBenchmark::TutorialEltwiseAddBenchmark(hebench::cpp::BaseEngine &engine,
229  const hebench::APIBridge::BenchmarkDescriptor &bench_desc,
230  const hebench::APIBridge::WorkloadParams &bench_params) :
231  hebench::cpp::BaseBenchmark(engine, bench_desc, bench_params)
232 {
233  // validate workload parameters
234 
235  // number of workload parameters (1 for eltwise add: n)
236  if (bench_params.count < TutorialEltwiseAddBenchmarkDescription::NumWorkloadParams)
237  throw hebench::cpp::HEBenchError(HEBERROR_MSG_CLASS("Invalid workload parameters. This workload requires "
238  + std::to_string(TutorialEltwiseAddBenchmarkDescription::NumWorkloadParams)
239  + "parameters."),
241 
242  // check values of the workload parameters and make sure they are supported by benchmark:
243  hebench::cpp::WorkloadParams::EltwiseAdd w_params(bench_params);
244  if (w_params.n() <= 0
245  || w_params.n() - 1 > TutorialEltwiseAddBenchmarkDescription::PolyModulusDegree / 2)
246  throw hebench::cpp::HEBenchError(HEBERROR_MSG_CLASS("Invalid workload parameters. This workload only supports vectors of size up to "
247  + std::to_string(TutorialEltwiseAddBenchmarkDescription::PolyModulusDegree / 2)),
249 
250  // Do any extra workload-parameter-based initialization here, if needed.
251 
252  // initialize original Workload (this initializes PALISADE BFV context)
253  m_p_workload = std::make_shared<Workload>(w_params.n());
254 }
256 
258 TutorialEltwiseAddBenchmark::~TutorialEltwiseAddBenchmark()
259 {
260  m_p_workload.reset(); // added for clarity
261 }
263 
266 {
267  assert(p_parameters && p_parameters->pack_count > 0 && p_parameters->p_data_packs);
268 
269  assert(p_parameters->pack_count == 1);
270 
272  const hebench::APIBridge::DataPack &param_pack = p_parameters->p_data_packs[0];
273 
274  if (param_pack.buffer_count != this->getDescriptor().cat_params.offline.data_count[param_pack.param_position])
275  {
276  std::stringstream ss;
277  ss << "Unexpected number of input samples for operation parameter " << param_pack.param_position
278  << ". Expected " << this->getDescriptor().cat_params.offline.data_count[param_pack.param_position]
279  << ", but " << std::to_string(param_pack.buffer_count) << " received.";
281  }
283 
285  std::vector<std::vector<std::int64_t>> clear_param(param_pack.buffer_count);
286 
287  for (std::size_t sample_i = 0; sample_i < clear_param.size(); ++sample_i)
288  {
289  const hebench::APIBridge::NativeDataBuffer &native_sample = param_pack.p_buffers[sample_i];
290  const std::int64_t *start_pt = reinterpret_cast<const std::int64_t *>(native_sample.p);
291  const std::int64_t *end_pt = start_pt + native_sample.size / sizeof(std::int64_t);
292  clear_param[sample_i] = std::vector<std::int64_t>(start_pt, end_pt);
293  }
295 
297  std::vector<lbcrypto::Plaintext> encoded = m_p_workload->encodeVector(clear_param);
300  InternalParam<lbcrypto::Plaintext> retval;
301  retval.samples = std::move(encoded);
302  retval.param_position = param_pack.param_position;
303  retval.tag = InternalParamInfo::tagPlaintext;
304 
305  return this->getEngine().template createHandle<decltype(retval)>(
306  sizeof(lbcrypto::Plaintext) * retval.samples.size(), // size (arbitrary for our usage if we need to)
307  retval.tag, // extra tags
308  std::move(retval)); // constructor parameters
310 }
312 
316 {
317  assert(p_native && p_native->p_data_packs && p_native->pack_count > 0);
318 
320  const InternalParam<lbcrypto::Plaintext> &encoded_data =
321  this->getEngine().retrieveFromHandle<InternalParam<lbcrypto::Plaintext>>(h_encoded_data, InternalParamInfo::tagPlaintext);
323 
325  std::vector<std::vector<std::int64_t>> clear_result = m_p_workload->decodeResult(encoded_data.samples);
327 
329  hebench::APIBridge::DataPack &native_datapack = this->findDataPack(*p_native, encoded_data.param_position);
330 
331  std::uint64_t min_sample_count = std::min(native_datapack.buffer_count, clear_result.size());
332  for (std::uint64_t sample_i = 0; sample_i < min_sample_count; ++sample_i)
333  {
334  // alias the samples
335  hebench::APIBridge::NativeDataBuffer &native_sample = native_datapack.p_buffers[sample_i];
336  // copy as much as possible
337  const std::vector<std::int64_t> &decoded = clear_result[sample_i];
338  std::uint64_t min_size = std::min(decoded.size(), native_sample.size / sizeof(std::int64_t));
339  std::copy_n(decoded.begin(), min_size,
340  reinterpret_cast<std::int64_t *>(native_sample.p));
341  }
343 }
345 
348 {
350  const InternalParam<lbcrypto::Plaintext> &encoded_parameter =
351  this->getEngine().template retrieveFromHandle<InternalParam<lbcrypto::Plaintext>>(h_encoded_parameters,
352  InternalParamInfo::tagPlaintext);
354 
356  std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> encrypted = m_p_workload->encryptVector(encoded_parameter.samples);
358 
360  InternalParam<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> retval;
361  retval.samples = std::move(encrypted);
362  retval.param_position = encoded_parameter.param_position;
363  retval.tag = InternalParamInfo::tagCiphertext;
364 
365  return this->getEngine().template createHandle<decltype(retval)>(
366  sizeof(lbcrypto::Ciphertext<lbcrypto::DCRTPoly>) * retval.samples.size(), // size (arbitrary for our usage if we need to)
367  retval.tag, // extra tags
368  std::move(retval)); // constructor parameters
370 }
372 
375 {
377  const InternalParam<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> &encrypted_data =
378  this->getEngine().retrieveFromHandle<InternalParam<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>>>(h_encrypted_data, InternalParamInfo::tagCiphertext);
380 
381  assert(encrypted_data.param_position == 0);
382 
384  std::vector<lbcrypto::Plaintext> encoded_data_samples = m_p_workload->decryptResult(encrypted_data.samples);
386 
388  InternalParam<lbcrypto::Plaintext> retval;
389  retval.samples = std::move(encoded_data_samples);
390  retval.param_position = encrypted_data.param_position;
391  retval.tag = InternalParamInfo::tagPlaintext;
392 
393  return this->getEngine().template createHandle<decltype(retval)>(
394  sizeof(lbcrypto::Plaintext) * retval.samples.size(), // size (arbitrary for our usage if we need to)
395  retval.tag, // extra tags
396  std::move(retval)); // move to avoid copy
398 }
400 
403 {
404  assert(count == TutorialEltwiseAddBenchmarkDescription::ParametersCount);
405 
407  std::pair<std::vector<lbcrypto::Plaintext>, std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>>> params;
409 
411  // We query for the parameter position, and, once found, we create a copy of the data.
412  for (std::size_t handle_i = 0; handle_i < count; ++handle_i)
413  {
414  const InternalParamInfo &param_info =
415  this->getEngine().retrieveFromHandle<InternalParamInfo>(p_local_data[handle_i]);
416  assert(param_info.param_position < TutorialEltwiseAddBenchmarkDescription::ParametersCount);
417 
418  switch (param_info.param_position)
419  {
420  case 0:
421  {
422  if (!params.first.empty())
423  throw hebench::cpp::HEBenchError(HEBERROR_MSG_CLASS("Duplicated operation parameter detected in input handle."),
425  const InternalParam<lbcrypto::Plaintext> &internal_param =
426  this->getEngine().retrieveFromHandle<InternalParam<lbcrypto::Plaintext>>(p_local_data[handle_i],
427  InternalParamInfo::tagPlaintext);
428  // create a deep copy of input
429  params.first = internal_param.samples;
430  break;
431  }
432  case 1:
433  {
434  if (!params.second.empty())
435  throw hebench::cpp::HEBenchError(HEBERROR_MSG_CLASS("Duplicated operation parameter detected in input handle."),
437  // create a deep copy of input
438  const InternalParam<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> &internal_param =
439  this->getEngine().retrieveFromHandle<InternalParam<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>>>(p_local_data[handle_i],
440  InternalParamInfo::tagCiphertext);
441  // create a deep copy of input
442  params.second = internal_param.samples;
443  break;
444  }
445  } // end switch
446  } // end for
448 
450  return this->getEngine().template createHandle<decltype(params)>(
451  sizeof(params), // size (arbitrary for our usage if we need to)
452  InternalParamInfo::tagPlaintext | InternalParamInfo::tagCiphertext, // extra tags
453  std::move(params)); // move to avoid extra copy
455 }
457 
460  hebench::APIBridge::Handle *p_local_data, std::uint64_t count)
461 {
462  if (count > 0)
463  {
465  std::memset(p_local_data, 0, sizeof(hebench::APIBridge::Handle) * count);
467 
469  p_local_data[0] = this->getEngine().duplicateHandle(h_remote_data,
470  InternalParamInfo::tagCiphertext); // validate that we are operating on the correct handle
472  }
473 }
475 
478  const hebench::APIBridge::ParameterIndexer *p_param_indexers)
479 {
481  const std::pair<std::vector<lbcrypto::Plaintext>, std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>>> &params =
482  this->getEngine().retrieveFromHandle<std::pair<std::vector<lbcrypto::Plaintext>, std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>>>>(
483  h_remote_packed, InternalParamInfo::tagCiphertext | InternalParamInfo::tagPlaintext);
484 
485  // Looks familiar?
486  const std::vector<lbcrypto::Plaintext> &A = params.first;
487  const std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> &B = params.second;
489 
491  std::array<std::size_t, TutorialEltwiseAddBenchmarkDescription::ParametersCount> param_size;
492  param_size[0] = A.size();
493  param_size[1] = B.size();
494  std::uint64_t results_count = 1;
495  for (std::size_t param_i = 0; param_i < TutorialEltwiseAddBenchmarkDescription::ParametersCount; ++param_i)
496  {
497  if (p_param_indexers[param_i].value_index >= param_size[param_i])
498  {
499  std::stringstream ss;
500  ss << "Invalid parameter indexer for operation parameter " << param_i << ". Expected index in range [0, "
501  << param_size[param_i] << "), but " << p_param_indexers[param_i].value_index << " received.";
504  }
505  else if (p_param_indexers[param_i].value_index + p_param_indexers[param_i].batch_size > param_size[param_i])
506  {
507  std::stringstream ss;
508  ss << "Invalid parameter indexer for operation parameter " << param_i << ". Expected batch size in range [1, "
509  << param_size[param_i] - p_param_indexers[param_i].value_index << "], but " << p_param_indexers[param_i].batch_size << " received.";
512  }
513  results_count *= p_param_indexers[param_i].batch_size; // count the number of results expected
514  }
516 
518  std::vector<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> result = m_p_workload->eltwiseadd(A, B);
519  assert(result.size() == results_count);
521 
523  // Finally, we wrap the result in our internal representation.
524  InternalParam<lbcrypto::Ciphertext<lbcrypto::DCRTPoly>> retval;
525  retval.samples = std::move(result);
526  retval.param_position = 0; // position of this result component inside the result tuple.
527  retval.tag = InternalParamInfo::tagCiphertext;
528 
529  // Hide our representation inside an opaque handle to cross the boundary of the API Bridge.
530  // This handle will be passed to method `store()` in the default pipeline.
531  return this->getEngine().template createHandle<decltype(retval)>(
532  sizeof(lbcrypto::Ciphertext<lbcrypto::DCRTPoly>) * retval.samples.size(), // size (arbitrary for our usage if we need to)
533  retval.tag, // extra tags
534  std::move(retval)); // move to avoid extra copies
536 }
538 
Top level opaque benchmark class.
Definition: benchmark.hpp:143
Base class that encapsulates common behavior of backend engines.
Definition: engine.hpp:70
std::uint64_t n() const
Number elements in a vector.
#define HEBERROR_MSG_CLASS(message)
@ Int64
64 bits signed integers.
Definition: types.h:304
ErrorCode encrypt(Handle h_benchmark, Handle h_plaintext, Handle *h_ciphertext)
Encrypts a plain text into a cipher text.
ErrorCode operate(Handle h_benchmark, Handle h_remote_packed_params, const ParameterIndexer *p_param_indexers, uint64_t indexers_count, Handle *h_remote_output)
Performs the workload operation of the benchmark.
std::uint64_t count
Number of workload parameters.
Definition: types.h:371
DataPack * p_data_packs
Collection of data packs.
Definition: types.h:625
ErrorCode encode(Handle h_benchmark, const DataPackCollection *p_parameters, Handle *h_plaintext)
Given a pack of parameters in raw, native data format, encodes them into plain text suitable for back...
std::uint64_t value_index
Index of parameter value inside the data pack.
Definition: types.h:651
ErrorCode decrypt(Handle h_benchmark, Handle h_ciphertext, Handle *h_plaintext)
Decrypts a cipher text into corresponding plain text.
ErrorCode store(Handle h_benchmark, Handle h_remote, Handle *h_local_packed_params, std::uint64_t local_count)
Retrieves the specified data from the backend.
std::uint64_t pack_count
Number of data packs in the collection.
Definition: types.h:626
ErrorCode createBenchmark(Handle h_engine, Handle h_bench_desc, const WorkloadParams *p_params, Handle *h_benchmark)
Instantiates a benchmark on the backend.
std::uint64_t batch_size
Number of values to use, starting from index.
Definition: types.h:652
std::uint64_t size
Size of underlying data.
Definition: types.h:564
std::uint64_t param_position
The 0-based position of this parameter in the corresponding function call.
Definition: types.h:614
void * p
Pointer to underlying data.
Definition: types.h:558
ErrorCode load(Handle h_benchmark, const Handle *h_local_packed_params, std::uint64_t local_count, Handle *h_remote)
Loads the specified data from the local host into the remote backend to use as parameter during a cal...
std::uint64_t buffer_count
Number of data buffers in p_buffers.
Definition: types.h:613
ErrorCode decode(Handle h_benchmark, Handle h_plaintext, DataPackCollection *p_native)
Decodes plaintext data into the appropriate raw, native format.
NativeDataBuffer * p_buffers
Array of data buffers for parameter.
Definition: types.h:612
Workload
Defines all possible workloads.
Definition: types.h:83
Defines a benchmark test.
Definition: types.h:527
Defines a data package for an operation.
Definition: types.h:611
Defines a collection of data packs.
Definition: types.h:624
Specifies the parameters for a workload.
Definition: types.h:363
Structure to contain flexible data.
Definition: types.h:552
std::string to_string(const std::string_view &s)
#define HEBENCH_ECODE_INVALID_ARGS
Indicates invalid arguments to function call.
Definition: types.h:42
#define HEBENCH_ECODE_CRITICAL_ERROR
Specifies a critical error.
Definition: types.h:50
#define HEBENCH_HE_SCHEME_BFV
Definition: types.h:69