You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

274 lines
9.2 KiB

/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef KC_MULTIEXP_TCC_
#define KC_MULTIEXP_TCC_
namespace libsnark {
template<typename T1, typename T2, mp_size_t n>
knowledge_commitment<T1,T2> opt_window_wnaf_exp(const knowledge_commitment<T1,T2> &base,
const bigint<n> &scalar, const size_t scalar_bits)
{
return knowledge_commitment<T1,T2>(opt_window_wnaf_exp(base.g, scalar, scalar_bits),
opt_window_wnaf_exp(base.h, scalar, scalar_bits));
}
template<typename T1, typename T2, typename FieldT>
knowledge_commitment<T1, T2> kc_multi_exp_with_mixed_addition(const knowledge_commitment_vector<T1, T2> &vec,
const size_t min_idx,
const size_t max_idx,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end,
const size_t chunks,
const bool use_multiexp)
{
enter_block("Process scalar vector");
auto index_it = std::lower_bound(vec.indices.begin(), vec.indices.end(), min_idx);
const size_t offset = index_it - vec.indices.begin();
auto value_it = vec.values.begin() + offset;
const FieldT zero = FieldT::zero();
const FieldT one = FieldT::one();
std::vector<FieldT> p;
std::vector<knowledge_commitment<T1, T2> > g;
knowledge_commitment<T1, T2> acc = knowledge_commitment<T1, T2>::zero();
size_t num_skip = 0;
size_t num_add = 0;
size_t num_other = 0;
const size_t scalar_length = std::distance(scalar_start, scalar_end);
while (index_it != vec.indices.end() && *index_it < max_idx)
{
const size_t scalar_position = (*index_it) - min_idx;
assert(scalar_position < scalar_length);
const FieldT scalar = *(scalar_start + scalar_position);
if (scalar == zero)
{
// do nothing
++num_skip;
}
else if (scalar == one)
{
#ifdef USE_MIXED_ADDITION
acc.g = acc.g.mixed_add(value_it->g);
acc.h = acc.h.mixed_add(value_it->h);
#else
acc.g = acc.g + value_it->g;
acc.h = acc.h + value_it->h;
#endif
++num_add;
}
else
{
p.emplace_back(scalar);
g.emplace_back(*value_it);
++num_other;
}
++index_it;
++value_it;
}
//print_indent(); printf("* Elements of w skipped: %zu (%0.2f%%)\n", num_skip, 100.*num_skip/(num_skip+num_add+num_other));
//print_indent(); printf("* Elements of w processed with special addition: %zu (%0.2f%%)\n", num_add, 100.*num_add/(num_skip+num_add+num_other));
//print_indent(); printf("* Elements of w remaining: %zu (%0.2f%%)\n", num_other, 100.*num_other/(num_skip+num_add+num_other));
leave_block("Process scalar vector");
return acc + multi_exp<knowledge_commitment<T1, T2>, FieldT>(g.begin(), g.end(), p.begin(), p.end(), chunks, use_multiexp);
}
template<typename T1, typename T2>
void kc_batch_to_special(std::vector<knowledge_commitment<T1, T2> > &vec)
{
enter_block("Batch-convert knowledge-commitments to special form");
std::vector<T1> g_vec;
g_vec.reserve(vec.size());
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].g.is_zero())
{
g_vec.emplace_back(vec[i].g);
}
}
batch_to_special_all_non_zeros<T1>(g_vec);
auto g_it = g_vec.begin();
T1 T1_zero_special = T1::zero();
T1_zero_special.to_special();
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].g.is_zero())
{
vec[i].g = *g_it;
++g_it;
}
else
{
vec[i].g = T1_zero_special;
}
}
g_vec.clear();
std::vector<T2> h_vec;
h_vec.reserve(vec.size());
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].h.is_zero())
{
h_vec.emplace_back(vec[i].h);
}
}
batch_to_special_all_non_zeros<T2>(h_vec);
auto h_it = h_vec.begin();
T2 T2_zero_special = T2::zero();
T2_zero_special.to_special();
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].h.is_zero())
{
vec[i].h = *h_it;
++h_it;
}
else
{
vec[i].h = T2_zero_special;
}
}
g_vec.clear();
leave_block("Batch-convert knowledge-commitments to special form");
}
template<typename T1, typename T2, typename FieldT>
knowledge_commitment_vector<T1, T2> kc_batch_exp_internal(const size_t scalar_size,
const size_t T1_window,
const size_t T2_window,
const window_table<T1> &T1_table,
const window_table<T2> &T2_table,
const FieldT &T1_coeff,
const FieldT &T2_coeff,
const std::vector<FieldT> &v,
const size_t start_pos,
const size_t end_pos,
const size_t expected_size)
{
knowledge_commitment_vector<T1, T2> res;
res.values.reserve(expected_size);
res.indices.reserve(expected_size);
for (size_t pos = start_pos; pos != end_pos; ++pos)
{
if (!v[pos].is_zero())
{
res.values.emplace_back(knowledge_commitment<T1, T2>(windowed_exp(scalar_size, T1_window, T1_table, T1_coeff * v[pos]),
windowed_exp(scalar_size, T2_window, T2_table, T2_coeff * v[pos])));
res.indices.emplace_back(pos);
}
}
return res;
}
template<typename T1, typename T2, typename FieldT>
knowledge_commitment_vector<T1, T2> kc_batch_exp(const size_t scalar_size,
const size_t T1_window,
const size_t T2_window,
const window_table<T1> &T1_table,
const window_table<T2> &T2_table,
const FieldT &T1_coeff,
const FieldT &T2_coeff,
const std::vector<FieldT> &v,
const size_t suggested_num_chunks)
{
knowledge_commitment_vector<T1, T2> res;
res.domain_size_ = v.size();
size_t nonzero = 0;
for (size_t i = 0; i < v.size(); ++i)
{
nonzero += (v[i].is_zero() ? 0 : 1);
}
const size_t num_chunks = std::max((size_t)1, std::min(nonzero, suggested_num_chunks));
if (!inhibit_profiling_info)
{
print_indent(); printf("Non-zero coordinate count: %zu/%zu (%0.2f%%)\n", nonzero, v.size(), 100.*nonzero/v.size());
}
std::vector<knowledge_commitment_vector<T1, T2> > tmp(num_chunks);
std::vector<size_t> chunk_pos(num_chunks+1);
const size_t chunk_size = nonzero / num_chunks;
const size_t last_chunk = nonzero - chunk_size * (num_chunks - 1);
chunk_pos[0] = 0;
size_t cnt = 0;
size_t chunkno = 1;
for (size_t i = 0; i < v.size(); ++i)
{
cnt += (v[i].is_zero() ? 0 : 1);
if (cnt == chunk_size && chunkno < num_chunks)
{
chunk_pos[chunkno] = i;
cnt = 0;
++chunkno;
}
}
chunk_pos[num_chunks] = v.size();
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < num_chunks; ++i)
{
tmp[i] = kc_batch_exp_internal<T1, T2, FieldT>(scalar_size, T1_window, T2_window, T1_table, T2_table, T1_coeff, T2_coeff, v,
chunk_pos[i], chunk_pos[i+1], i == num_chunks - 1 ? last_chunk : chunk_size);
#ifdef USE_MIXED_ADDITION
kc_batch_to_special<T1, T2>(tmp[i].values);
#endif
}
if (num_chunks == 1)
{
tmp[0].domain_size_ = v.size();
return tmp[0];
}
else
{
for (size_t i = 0; i < num_chunks; ++i)
{
res.values.insert(res.values.end(), tmp[i].values.begin(), tmp[i].values.end());
res.indices.insert(res.indices.end(), tmp[i].indices.begin(), tmp[i].indices.end());
}
return res;
}
}
} // libsnark
#endif // KC_MULTIEXP_TCC_