/src/brunsli/c/common/lehmer_code.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) Google LLC 2019 |
2 | | // |
3 | | // Use of this source code is governed by an MIT-style |
4 | | // license that can be found in the LICENSE file or at |
5 | | // https://opensource.org/licenses/MIT. |
6 | | |
7 | | // Library to compute the Lehmer code of a permutation and to reconstruct the |
8 | | // permutation from its Lehmer code. For more details on Lehmer codes, see |
9 | | // http://en.wikipedia.org/wiki/Lehmer_code |
10 | | |
11 | | #ifndef BRUNSLI_COMMON_LEHMER_CODE_H_ |
12 | | #define BRUNSLI_COMMON_LEHMER_CODE_H_ |
13 | | |
14 | | #include <algorithm> |
15 | | #include <utility> |
16 | | #include <vector> |
17 | | |
18 | | #include "./platform.h" |
19 | | #include <brunsli/types.h> |
20 | | |
21 | | namespace brunsli { |
22 | | |
23 | | // Computes the Lehmer code of the permutation sigma[0..len) and puts the |
24 | | // result into code[0..len). |
25 | | void ComputeLehmerCode(const uint32_t* sigma, size_t len, uint32_t* code); |
26 | | |
27 | | // Decodes the Lehmer code in code[0..len) and puts the resulting permutation |
28 | | // into sigma[0..len). |
29 | | bool DecodeLehmerCode(const uint32_t* code, size_t len, uint32_t* sigma); |
30 | | |
31 | | // This class is an optimized Lehmer-like coder that takes the remaining |
32 | | // number of possible values into account to reduce the bit usage. |
33 | | // TODO(eustas): in worst case (always removing the first element), O(N^2) |
34 | | // elements are moved; "Fenwick tree" is simple to implement and could reduce |
35 | | // the complexity to O(N * log(N)). |
36 | | class PermutationCoder { |
37 | | public: |
38 | 42.2k | PermutationCoder() {} |
39 | | |
40 | 19.5k | void Init(std::vector<uint8_t> values) { |
41 | 19.5k | values_ = std::move(values); |
42 | 19.5k | } |
43 | | |
44 | 21.6k | void Clear() { |
45 | 21.6k | std::vector<uint8_t>().swap(values_); |
46 | 21.6k | } |
47 | | |
48 | | // number of bits needed to represent the next code. |
49 | 142k | int num_bits() const { |
50 | 142k | uint32_t num_values = static_cast<uint32_t>(values_.size()); |
51 | 142k | BRUNSLI_DCHECK(num_values > 0); |
52 | 142k | if (num_values <= 1) return 0; |
53 | 142k | return static_cast<int>(Log2FloorNonZero(num_values - 1) + 1); |
54 | 142k | } |
55 | | |
56 | | // Copy value at position 'code' and remove it. Returns false in |
57 | | // case of error (invalid slot). |
58 | 128k | bool Remove(size_t code, uint8_t* value) { |
59 | 128k | if (code >= values_.size()) { |
60 | 9 | return false; |
61 | 9 | } |
62 | 128k | *value = values_[code]; |
63 | 128k | values_.erase(values_.begin() + code); |
64 | 128k | return true; |
65 | 128k | } |
66 | | |
67 | | // Removes 'value' from the list and assign a code + number-of-bits |
68 | | // for it. Returns false if value could not be encoded. |
69 | 0 | bool RemoveValue(uint8_t value, int* code, int* nbits) { |
70 | 0 | std::vector<uint8_t>::iterator it = |
71 | 0 | std::find(values_.begin(), values_.end(), value); |
72 | 0 | if (it == values_.end()) { |
73 | 0 | return false; // invalid/non-existing value was passed. |
74 | 0 | } |
75 | 0 | *code = static_cast<int>(it - values_.begin()); |
76 | 0 | *nbits = num_bits(); |
77 | 0 | values_.erase(it); |
78 | 0 | return true; |
79 | 0 | } |
80 | | |
81 | | private: |
82 | | std::vector<uint8_t> values_; |
83 | | }; |
84 | | |
85 | | } // namespace brunsli |
86 | | |
87 | | #endif // BRUNSLI_COMMON_LEHMER_CODE_H_ |