29 #ifndef PIRANHA_KRONECKER_ARRAY_HPP 30 #define PIRANHA_KRONECKER_ARRAY_HPP 40 #include <type_traits> 44 #include <piranha/config.hpp> 46 #include <piranha/mp_integer.hpp> 47 #include <piranha/safe_cast.hpp> 58 using ka_type_reqs = std::integral_constant<bool, std::is_integral<T>::value && std::is_signed<T>::value>;
86 template <
typename SignedInteger>
94 #if !defined(PIRANHA_DOXYGEN_INVOKED) 95 static_assert(detail::ka_type_reqs<int_type>::value,
"This class can be used only with signed integers.");
103 typedef std::vector<limit_type> limits_type;
120 static const limits_type m_limits;
126 static limit_type determine_limit(
const size_type &m)
128 piranha_assert(m >= 1u);
129 std::mt19937 engine(static_cast<std::mt19937::result_type>(m));
130 std::uniform_int_distribution<int> dist(-5, 5);
132 auto perturb = [&engine, &dist](
integer &arg) {
133 arg += (dist(engine) * (arg)) / 100;
137 std::vector<integer> m_vec, M_vec, c_vec, prev_c_vec, prev_m_vec, prev_M_vec;
144 c_vec.push_back(c_vec.back() *
integer(3));
147 auto dot_prod = [](
const std::vector<integer> &v1,
const std::vector<integer> &v2) ->
integer {
148 piranha_assert(v1.size() && v1.size() == v2.size());
149 return std::inner_product(v1.begin(), v1.end(), v2.begin(),
integer(0));
153 integer h_min = dot_prod(c_vec, m_vec);
154 integer h_max = dot_prod(c_vec, M_vec);
156 piranha_assert(diff >= 0);
159 (void)static_cast<int_type>(h_min);
160 (void)static_cast<int_type>(h_max);
165 (void)static_cast<int_type>(diff + 1);
168 }
catch (
const std::overflow_error &) {
169 std::vector<int_type> tmp;
171 if (prev_c_vec.size()) {
172 h_min = dot_prod(prev_c_vec, prev_m_vec);
173 h_max = dot_prod(prev_c_vec, prev_M_vec);
174 std::transform(prev_M_vec.begin(), prev_M_vec.end(), std::back_inserter(tmp),
176 return std::make_tuple(tmp, static_cast<int_type>(h_min), static_cast<int_type>(h_max),
177 static_cast<int_type>(h_max - h_min));
189 auto it = c_vec.begin() + 1, prev_it = prev_c_vec.begin();
190 for (; it != c_vec.end(); ++it, ++prev_it) {
200 it = c_vec.begin() + 1;
201 piranha_assert(M_vec.size() && M_vec.size() == m_vec.size());
202 for (
size_type i = 0u; i < M_vec.size() - 1u; ++i, ++it) {
203 M_vec[i] = ((*it) / *(it - 1) - 1) / 2;
204 m_vec[i] = -M_vec[i];
209 M_vec.back() = (4 * M_vec.back() + 1) / 2;
210 perturb(M_vec.back());
211 m_vec.back() = -M_vec.back();
214 static limits_type determine_limits()
219 auto tmp = determine_limit(i);
220 if (std::get<0u>(tmp).empty()) {
223 retval.push_back(std::move(tmp));
275 template <
typename Vector>
278 const auto size = v.size();
281 if (unlikely(size >= m_limits.size())) {
282 piranha_throw(std::invalid_argument,
"size of vector to be encoded is too large");
284 if (unlikely(!size)) {
288 const auto &limit = m_limits[size];
289 const auto &minmax_vec = std::get<0u>(limit);
292 for (
min_int<decltype(v.size()), decltype(minmax_vec.size())> i = 0u; i < size; ++i) {
293 if (unlikely(safe_cast<int_type>(v[i]) < -minmax_vec[i] ||
safe_cast<
int_type>(v[i]) > minmax_vec[i])) {
294 piranha_throw(std::invalid_argument,
"a component of the vector to be encoded is out of bounds");
297 piranha_assert(minmax_vec[0u] > 0);
299 cur_c =
static_cast<int_type>(2 * minmax_vec[0u] + 1);
300 piranha_assert(retval >= 0);
301 for (decltype(v.size()) i = 1u; i < size; ++i) {
303 piranha_assert(minmax_vec[i] > 0);
304 cur_c =
static_cast<int_type>(cur_c * (2 * minmax_vec[i] + 1));
306 return static_cast<int_type>(retval + std::get<1u>(limit));
329 template <
typename Vector>
332 typedef typename Vector::value_type v_type;
333 const auto m = retval.size();
334 if (unlikely(m >= m_limits.size())) {
335 piranha_throw(std::invalid_argument,
"size of vector to be decoded is too large");
338 if (unlikely(n != 0)) {
339 piranha_throw(std::invalid_argument,
"a vector of size 0 must always be encoded as 0");
344 const auto &limit = m_limits[m];
345 const auto &minmax_vec = std::get<0u>(limit);
346 const auto hmin = std::get<1u>(limit), hmax = std::get<2u>(limit);
347 if (unlikely(n < hmin || n > hmax)) {
348 piranha_throw(std::invalid_argument,
"the integer to be decoded is out of bounds");
354 piranha_assert(code >= 0);
355 piranha_assert(minmax_vec[0u] > 0);
358 retval[0u] =
safe_cast<v_type>((code % mod_arg) - minmax_vec[0u]);
359 for (
min_int<
typename Vector::size_type, decltype(minmax_vec.size())> i = 1u; i < m; ++i) {
360 piranha_assert(minmax_vec[i] > 0);
361 retval[i] =
safe_cast<v_type>((code % (mod_arg * (2 * minmax_vec[i] + 1))) / mod_arg - minmax_vec[i]);
362 mod_arg =
static_cast<int_type>(mod_arg * (2 * minmax_vec[i] + 1));
368 template <
typename SignedInteger>
369 const typename kronecker_array<SignedInteger>::limits_type kronecker_array<SignedInteger>::m_limits
370 = kronecker_array<SignedInteger>::determine_limits();
static const limits_type & get_limits()
Get the limits of the Kronecker codification.
Multiprecision integer class.
mp_integer< 1 > integer
Alias for piranha::mp_integer with 1 limb of static storage.
std::size_t size_type
Size type.
typename detail::min_int_impl< T, Args... >::type min_int
Detect narrowest integer type.
#define piranha_throw(exception_type,...)
Exception-throwing macro.
static int_type encode(const Vector &v)
Encode vector.
static void decode(Vector &retval, const int_type &n)
Decode into vector.
SignedInteger int_type
Signed integer type used for encoding.
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.