Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
numeric_zigzag.h
1#ifndef SOURCEMETA_CORE_NUMERIC_ZIGZAG_H_
2#define SOURCEMETA_CORE_NUMERIC_ZIGZAG_H_
3
4#include <sourcemeta/core/numeric_decimal.h>
5
6#include <cassert> // assert
7#include <concepts> // std::same_as
8#include <cstdint> // std::uint64_t, std::int64_t
9
10namespace sourcemeta::core {
11
15template <typename T> auto zigzag_encode(const T &value) {
16 if constexpr (std::same_as<T, Decimal>) {
17 assert(value.is_integral());
18 if (value >= Decimal{0}) {
19 return value * Decimal{2};
20 }
21 const Decimal absolute{value.is_signed() ? -value : value};
22 return (absolute * Decimal{2}) - Decimal{1};
23 } else {
24 const auto signed_value{static_cast<std::int64_t>(value)};
25 if (signed_value >= 0) {
26 return static_cast<std::uint64_t>(signed_value) * 2;
27 }
28 // Negate in unsigned to avoid UB for INT64_MIN
29 return (static_cast<std::uint64_t>(0) -
30 static_cast<std::uint64_t>(signed_value)) *
31 2 -
32 1;
33 }
34}
35
38template <typename T> auto zigzag_decode(const T &value) {
39 if constexpr (std::same_as<T, Decimal>) {
40 assert(value.is_integral());
41 assert(value >= Decimal{0});
42 if (value % Decimal{2} == Decimal{0}) {
43 return value.divide_integer(Decimal{2});
44 }
45 return -((value + Decimal{1}).divide_integer(Decimal{2}));
46 } else {
47 const auto unsigned_value{static_cast<std::uint64_t>(value)};
48 if (unsigned_value % 2 == 0) {
49 return static_cast<std::int64_t>(unsigned_value / 2);
50 }
51 // Use bitwise complement to avoid overflow for UINT64_MAX
52 // `~(x / 2)` == `-(x / 2 + 1)` in two's complement
53 return static_cast<std::int64_t>(~(unsigned_value / 2));
54 }
55}
56
57} // namespace sourcemeta::core
58
59#endif
Definition numeric_decimal.h:21
auto zigzag_decode(const T &value)
Definition numeric_zigzag.h:38
auto zigzag_encode(const T &value)
Definition numeric_zigzag.h:15