GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/permutation.h
Date: 2026-09-07 02:36:43
Exec Total Coverage
Lines: 62 64 96.9%
Functions: 6 6 100.0%
Branches: 31 72 43.1%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_BASIC_PERMUTATION
41 #define GEOGRAM_BASIC_PERMUTATION
42
43 #include <geogram/basic/common.h>
44 #include <geogram/basic/numeric.h>
45 #include <geogram/basic/memory.h>
46
47 /**
48 * \file geogram/basic/permutation.h
49 * \brief Functions to manipulate permutations
50 */
51
52 namespace GEO {
53
54 /**
55 * \brief Utilities for manipulating permutations
56 */
57 namespace Permutation {
58
59 /**
60 * \brief Mark used to mark elements for in-place permutation
61 */
62 constexpr index_t MARKED_BIT = index_t(1) << (sizeof(index_t)*8 - 1);
63
64 /**
65 * \brief Maximum size of a permutation that can be applied in-place
66 */
67 constexpr index_t MAX_SIZE = MARKED_BIT;
68
69 /**
70 * \brief Checks whether a vector is a valid permutation.
71 * \details The vector \p permutation is a valid permutation if there
72 * is a bijection between the range [0..N-1] and the range
73 * [permutation[0]..permutation[N-1]] where N is the number of
74 * elements in the vector. An empty vector is considered as a valid
75 * permutation.
76 * \param[in] permutation a vector of integers
77 * \retval true if \p permutation is a valid permutation
78 * \retval false otherwise
79 */
80 66 inline bool is_valid(const vector<index_t>& permutation) {
81
1/2
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
66 std::vector<bool> visited(permutation.size(), false);
82
2/2
✓ Branch 1 taken 52340 times.
✓ Branch 2 taken 66 times.
52406 for(index_t i = 0; i < permutation.size(); i++) {
83
2/4
✓ Branch 1 taken 52340 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 52340 times.
52340 if(permutation[i] >= permutation.size()) {
84 return false;
85 }
86
2/4
✓ Branch 1 taken 52340 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 52340 times.
52340 if(visited[permutation[i]]) {
87 return false;
88 }
89
1/2
✓ Branch 1 taken 52340 times.
✗ Branch 2 not taken.
52340 visited[permutation[i]] = true;
90 }
91 66 return true;
92 66 }
93
94 /**
95 * \brief Marks a permutation element as visited
96 * \details Sets a \e visited mark for element at index \p i.
97 * in \p permutation. The element must \b not have been already marked.
98 * The mark can be checked with is_marked() and removed with unmark().
99 * \param[in,out] permutation a valid permutation
100 * \param[in] i the index of the element in \p permutation
101 * \note Used internally by apply().
102 * Implementation note: marking an element modifies the value in such
103 * a way that the initial value of the element can be restored.
104 */
105 52340 inline void mark(vector<index_t>& permutation, index_t i) {
106
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 52340 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
52340 geo_debug_assert(i < permutation.size());
107 52340 permutation[i] |= MARKED_BIT;
108 52340 }
109
110 /**
111 * \brief Checks if a permutation element has been visited
112 * \param[in] permutation a valid permutation
113 * \param[in] i the index of the element in \p permutation
114 * \note Used internally by apply()
115 */
116 104680 inline bool is_marked(const vector<index_t>& permutation, index_t i) {
117
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 104680 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
104680 geo_debug_assert(i < permutation.size());
118 104680 return ((permutation[i] & MARKED_BIT) != 0);
119 }
120
121 /**
122 * \brief Unmarks a permutation element
123 * \details This restores the initial value of element at index \p i
124 * in \p permutation
125 * \param[in,out] permutation a valid permutation
126 * \param[in] i the index of the element in \p permutation
127 * \note Used internally by apply()
128 */
129 52340 inline void unmark(vector<index_t>& permutation, index_t i) {
130
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 52340 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
52340 geo_debug_assert(is_marked(permutation, i));
131 52340 permutation[i] &= ~MARKED_BIT;
132 52340 }
133
134 /**
135 * \brief Applies a permutation in-place.
136 * Permutes the first \p N elements of size \p elemsize in array \p
137 * data using permutation \p permutation where \p N is the number
138 * of elements in \p permutation. The result of the permutation is
139 * left in \p data.
140 * The array \p data must contain at least \c permutation.size()
141 * elements otherwise memory corruption will happen.
142 *
143 * Applying permutation \p permutation is equivalent to:
144 * \code
145 * for(i=0; i<permutation.size(); i++) {
146 * data2[i] = data[permutation[i]]
147 * }
148 * data = data2 ;
149 * \endcode
150 * \param[in,out] data an array of \c permutation.size() elements to
151 * permute
152 * \param[in] permutation_in the permutation.
153 * It is temporarily changed during execution of the
154 * function, but identical to the input on exit.
155 * \param[in] elemsize size of the vector elements
156 */
157 42 inline void apply(
158 void* data, const vector<index_t>& permutation_in,
159 size_t elemsize
160 ) {
161
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
42 geo_debug_assert(permutation_in.size() <= MAX_SIZE);
162 42 Memory::pointer pdata = (Memory::pointer) (data);
163 42 vector<index_t>& permutation =
164 const_cast<vector<index_t>&>(permutation_in);
165
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
42 geo_debug_assert(is_valid(permutation));
166 42 Memory::byte* temp = static_cast<Memory::byte*>(alloca(elemsize));
167
2/2
✓ Branch 1 taken 37749 times.
✓ Branch 2 taken 42 times.
37791 for(index_t k = 0; k < permutation.size(); k++) {
168
2/2
✓ Branch 1 taken 37444 times.
✓ Branch 2 taken 305 times.
37749 if(is_marked(permutation, k)) {
169 37444 continue;
170 }
171 305 index_t i = k;
172 305 index_t j = permutation[k];
173 305 Memory::copy(temp, pdata + i * elemsize, elemsize);
174 305 mark(permutation, k);
175
2/2
✓ Branch 0 taken 37444 times.
✓ Branch 1 taken 305 times.
37749 while(j != k) {
176 37444 Memory::copy(
177 37444 pdata + i * elemsize, pdata + j * elemsize, elemsize
178 );
179 37444 index_t nj = permutation[j];
180 37444 mark(permutation, j);
181 37444 i = j;
182 37444 j = nj;
183 }
184 305 Memory::copy(pdata + i * elemsize, temp, elemsize);
185 }
186
2/2
✓ Branch 1 taken 37749 times.
✓ Branch 2 taken 42 times.
37791 for(index_t k = 0; k < permutation.size(); k++) {
187 37749 unmark(permutation, k);
188 }
189 42 }
190
191 /**
192 * \brief Applies a permutation in-place.
193 * Permutes the first \p N elements of vector \p data using
194 * permutation \p permutation where \p N is the number of elements in
195 * \p permutation. The result of the permutation is left in \p data.
196 * The array \p data must contain at least \c permutation.size()
197 * elements otherwise the function throws an out_of_range exception.
198 *
199 * Applying permutation \p permutation is equivalent to:
200 * \code
201 * for(i=0; i<permutation.size(); i++) {
202 * data2[i] = data[permutation[i]]
203 * }
204 * data = data2 ;
205 * \endcode
206 * \param[in,out] data the vector to permute
207 * \param[in] permutation_in the permutation.
208 * It is temporarily changed during execution of the
209 * function, but identical to the input on exit.
210 */
211 template <class T>
212 inline void apply(
213 vector<T>& data, const vector<index_t>& permutation_in
214 ) {
215 geo_debug_assert(permutation_in.size() <= MAX_SIZE);
216 vector<index_t>& permutation =
217 const_cast<vector<index_t>&>(permutation_in);
218 geo_debug_assert(is_valid(permutation));
219 T temp;
220 for(index_t k = 0; k < permutation.size(); k++) {
221 if(is_marked(permutation, k)) {
222 continue;
223 }
224 index_t i = k;
225 temp = data[i];
226 index_t j = permutation[k];
227 mark(permutation, k);
228 while(j != k) {
229 data[i] = data[j];
230 index_t nj = permutation[j];
231 mark(permutation, j);
232 i = j;
233 j = nj;
234 }
235 data[i] = temp;
236 }
237 for(index_t k = 0; k < permutation.size(); k++) {
238 unmark(permutation, k);
239 }
240 }
241
242 /**
243 * \brief Inverts a permutation in-place.
244 * \details Inverses the given \p permutation in place, the
245 * result of the inversion is left in \p permutation.
246 *
247 * The inversion is equivalent to:
248 * \code
249 * vector<index_t> inverse;
250 * Permutation::invert(permutation, inverse);
251 * permutation = inverse;
252 * \endcode
253 * \param[in,out] permutation to inverse.
254 */
255 24 inline void invert(vector<index_t>& permutation) {
256
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
24 geo_debug_assert(permutation.size() < MAX_SIZE);
257
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
24 geo_debug_assert(is_valid(permutation));
258
2/2
✓ Branch 1 taken 14591 times.
✓ Branch 2 taken 24 times.
14615 for(index_t k = 0; k < permutation.size(); k++) {
259
2/2
✓ Branch 1 taken 14445 times.
✓ Branch 2 taken 146 times.
14591 if(is_marked(permutation, k)) {
260 14445 continue;
261 }
262 146 index_t i = k;
263 146 index_t j = permutation[i];
264
2/2
✓ Branch 0 taken 14445 times.
✓ Branch 1 taken 146 times.
14591 while(j != k) {
265 14445 index_t temp = permutation[j];
266 14445 permutation[j] = i;
267 14445 mark(permutation, j);
268 14445 i = j;
269 14445 j = temp;
270 }
271 146 permutation[j] = i;
272 146 mark(permutation, j);
273 }
274
2/2
✓ Branch 1 taken 14591 times.
✓ Branch 2 taken 24 times.
14615 for(index_t k = 0; k < permutation.size(); k++) {
275 14591 unmark(permutation, k);
276 }
277 24 }
278
279 /**
280 * \brief Inverts a permutation.
281 * \details Computes the inverse of a given \p permutation and
282 * stores the result in another one.
283 * \param[in] permutation the permutation to invert
284 * \param[out] invert the computed inverse of \p permutation
285 * \note there is also a variant of invert() that computes the
286 * permutation in-place.
287 */
288 inline void invert(
289 const vector<index_t>& permutation, vector<index_t>& invert
290 ) {
291 geo_debug_assert(is_valid(permutation));
292 invert.resize(permutation.size());
293 for(index_t i=0; i<permutation.size(); ++i) {
294 invert[permutation[i]] = i;
295 }
296 }
297 }
298 }
299
300 #endif
301