├── LICENSE
├── README.md
├── images
├── auxiliary.gif
├── bridge.gif
├── bridge_helix_trinity_reversal.png
├── contrev_piston_griesmills_juggler.png
├── grail.gif
├── griesmills.gif
├── helix.gif
├── juggling.gif
├── piston.gif
├── reversal.gif
└── trinity.gif
└── src
├── bench.c
└── rotate.h
/LICENSE:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2014-2023 Igor van den Hoven ivdhoven@gmail.com
3 | */
4 |
5 | /*
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the
8 | "Software"), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | The most commonly used rotation algorithms (aka block swaps) were documented around 1981 and haven't notably changed since.
2 |
3 | Below I'll describe the known variants as well as three novel rotation algorithms introduced in 2021, notably the [trinity rotation](https://github.com/scandum/rotate#Trinity-Rotation), followed by some [benchmarks](https://github.com/scandum/rotate#Benchmarks).
4 |
5 | [C source code](https://github.com/scandum/rotate/tree/main/src) is available for all rotations described.
6 |
7 | Introduction to rotating
8 | ------------------------
9 | A rotation is to swap the left side of an array with the right side.
10 | ```c
11 | ┌──────────────────────────┬─────────────────┐
12 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
13 | └──────────────────────────┴─────────────────┘
14 | ```
15 | After the rotation the data is as following.
16 | ```c
17 | ┌─────────────────┬──────────────────────────┐
18 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
19 | └─────────────────┴──────────────────────────┘
20 | ```
21 |
22 | Utility of rotating
23 | -------------------
24 | According to Sean Parent rotations are a very common operation: https://www.youtube.com/watch?v=UZmeDQL4LaE
25 |
26 | Auxiliary Rotation
27 | ------------------
28 | This is an easy and fast way to rotate, but since it requires auxiliary memory it is of little interest to in-place algorithms. It's a good strategy for array sizes of 1000 elements or less.
29 |
30 | Typically the smaller half is copied to swap memory, the larger half is moved, and the swap memory is copied back to the main array.
31 | ```c
32 | ┌──────────────────────────┬─────────────────┐
33 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
34 | └──────────────────────────┴─────────────────┘
35 | └──┴──┴──┴──┴──┴────┬──┬──┬──┬──┬──┐
36 | ┌──────────────────────────┬─────────────────┐ ┌─────────────────┐
37 | │ 1 2 3 4 5 6 7 8 9│ │ │10 11 12 13 14 15│
38 | └──────────────────────────┴─────────────────┘ └─────────────────┘
39 | └──┴──┴──┴──┴──┴──┼──┼──┼──┬──┬──┬──┬──┬──┐
40 | ┌─────────────────┬──────────────────────────┐ ┌─────────────────┐
41 | │ │ 1 2 3 4 5 6 7 8 9│ │10 11 12 13 14 15│
42 | └─────────────────┴──────────────────────────┘ └─────────────────┘
43 | ┌──┬──┬──┬──┬──┬───────────────────────────────┴──┴──┴──┴──┴──┘
44 | ┌─────────────────┬──────────────────────────┐
45 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
46 | └─────────────────┴──────────────────────────┘
47 | ```
48 |
49 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=0s)
50 |
51 | Bridge Rotation
52 | ---------------
53 | This is a slightly more complex auxiliary rotation that reduces the maximum auxiliary memory requirement from 50% to 33%. If the overlap between the two halves is smaller than the halves themselves it copies the overlap to swap memory instead. Its first known publication was in 2021 by Igor van den Hoven.[^1]
54 | ```c
55 | ┌──────────────────────────┬─────────────────┐
56 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
57 | └──────────────────────────┴─────────────────┘
58 | └──┴──┴──────────────────────┬──┬──┐
59 | ┌─────────────────┬────────┬─────────────────┐ ┌────────┐
60 | │ 1 2 3 4 5 6│ │10 11 12 13 14 15│ │ 7 8 9│
61 | └─────────────────┴────────┴─────────────────┘ └────────┘
62 | ├─────────────────┬────────┘
63 | ┌─────────────────┬────────┬─────────────────┐ ┌────────┐
64 | │10 2 3 4 5 6│ 1 │ 11 12 13 14 15│ │ 7 8 9│
65 | └─────────────────┴────────┴─────────────────┘ └────────┘
66 | ├─────────────────┬────────┘
67 | ┌─────────────────┬────────┬─────────────────┐ ┌────────┐
68 | │10 11 3 4 5 6│ 1 2 │ 12 13 14 15│ │ 7 8 9│
69 | └─────────────────┴────────┴─────────────────┘ └────────┘
70 | ├─────────────────┬────────┘
71 | ┌─────────────────┬────────┬─────────────────┐ ┌────────┐
72 | │10 11 12 4 5 6│ 1 2 3│ 13 14 15│ │ 7 8 9│
73 | └─────────────────┴────────┴─────────────────┘ └────────┘
74 | ├─────────────────┬────────┘
75 | ┌─────────────────┬────────┬─────────────────┐ ┌────────┐
76 | │10 11 12 13 5 6│ 1 2 3│ 4 14 15│ │ 7 8 9│
77 | └─────────────────┴────────┴─────────────────┘ └────────┘
78 | ├─────────────────┬────────┘
79 | ┌─────────────────┬────────┬─────────────────┐ ┌────────┐
80 | │10 11 12 13 14 6│ 1 2 3│ 4 5 15│ │ 7 8 9│
81 | └─────────────────┴────────┴─────────────────┘ └────────┘
82 | ├─────────────────┬────────┘
83 | ┌─────────────────┬────────┬─────────────────┐ ┌────────┐
84 | │10 11 12 13 14 15│ 1 2 3│ 4 5 6 │ │ 7 8 9│
85 | └─────────────────┴────────┴─────────────────┘ └────────┘
86 | ┌──┬──┬────┴──┴──┘
87 | ┌─────────────────┬──────────────────────────┐
88 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
89 | └─────────────────┴──────────────────────────┘
90 | ```
91 |
92 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=15s)
93 |
94 | Juggling Rotation
95 | -----------------
96 | Also known as the dolphin algorithm. This is a relatively complex and inefficient way to rotate in-place, though it does so in the minimal number of moves. Its first known publication was in 1966.[^2]
97 |
98 | It computes the greatest common divisor and uses a loop to create a chain of consecutive swaps.
99 |
100 | ```c
101 | ┌──────────────────────────┬─────────────────┐
102 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
103 | └──────────────────────────┴─────────────────┘
104 | ↓ ↓ ↓ ↓ ↓
105 | ┌──────────────────────────┬─────────────────┐
106 | │10 2 3 13 5 6 1 8 9│ 4 11 12 7 14 15│
107 | └──────────────────────────┴─────────────────┘
108 | ↓ ↓ ↓ ↓ ↓
109 | ┌──────────────────────────┬─────────────────┐
110 | │10 11 3 13 14 6 1 2 9│ 4 5 12 7 8 15│
111 | └──────────────────────────┴─────────────────┘
112 | ↓ ↓ ↓ ↓ ↓
113 | ┌─────────────────┬──────────────────────────┐
114 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
115 | └─────────────────┴──────────────────────────┘
116 | ```
117 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=119s)
118 |
119 | Triple Reversal Rotation
120 | ------------------------
121 | This is an easy and reliable way to rotate in-place. You reverse the left side, next you reverse the right side, next you reverse the entire array. Upon completion the left and right block will be swapped. There's no known first publication, but it was prior to 1981.[^3]
122 | ```c
123 | ┌──────────────────────────┬─────────────────┐
124 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
125 | └──────────────────────────┴─────────────────┘
126 | ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
127 | ┌──────────────────────────┬─────────────────┐
128 | │ 9 8 7 6 5 4 3 2 1│10 11 12 13 14 15│
129 | └──────────────────────────┴─────────────────┘
130 | ↓ ↓ ↓ ↓ ↓ ↓
131 | ┌──────────────────────────┬─────────────────┐
132 | │ 9 8 7 6 5 4 3 2 1│15 14 13 12 11 10│
133 | └──────────────────────────┴─────────────────┘
134 | ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
135 | ┌─────────────────┬──────────────────────────┐
136 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
137 | └─────────────────┴──────────────────────────┘
138 | ```
139 |
140 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=23s)
141 |
142 | Gries-Mills Rotation
143 | --------------------
144 | In some cases this rotation outperforms the classic triple reversal rotation while making fewer moves. You swap the smallest array linearly towards its proper location, since the blocks behind it are in the proper location you can forget about them. What remains of the larger array is now the smallest array, which you rotate in a similar manner, until the smallest side shrinks to 0 elements. Its first known publication was in 1981 by David Gries and Harlan Mills.[^3]
145 | ```c
146 | ┌──────────────────────────┬─────────────────┐
147 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
148 | └──────────────────────────┴─────────────────┘
149 | ┌──┬──┬──┬──┬──┬──┴──┴──┴──┴──┴──┘
150 | ┌────────┬─────────────────┬─────────────────┐
151 | │ 1 2 3│10 11 12 13 14 15│ 4 5 6 7 8 9│
152 | └────────┴─────────────────┴─────────────────┘
153 | └──┴──┴──┬──┬──┐
154 | ┌────────┬────────┬──────────────────────────┐
155 | │10 11 12│ 1 2 3│13 14 15 4 5 6 7 8 9│
156 | └────────┴────────┴──────────────────────────┘
157 | └──┴──┴──┬──┬──┐
158 | ┌─────────────────┬──────────────────────────┐
159 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
160 | └─────────────────┴──────────────────────────┘
161 | ```
162 |
163 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=47s)
164 |
165 | Successive Rotation
166 | -------------------
167 | First described by Gries and Mills in 1981, this rotation is very similar to the Gries-Mills rotation but performs non-linear swaps.[^3] It is implemented as the Piston Rotation in the benchmark, named after a loop optimization that removes up to `log n` branch mispredictions by performing both a left and rightward rotation in each loop.
168 |
169 | ```c
170 | ┌──────────────────────────┬─────────────────┐
171 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
172 | └──────────────────────────┴─────────────────┘
173 | ┌──┬──┬──┬──┬──┬───────────┴──┴──┴──┴──┴──┘
174 | ┌─────────────────┬────────┬─────────────────┐
175 | │10 11 12 13 14 15│ 7 8 9│ 1 2 3 4 5 6│
176 | └─────────────────┴────────┴─────────────────┘
177 | └──┴──┴───────────┬──┬──┐
178 | ┌─────────────────┬────────┬────────┬────────┐
179 | │10 11 12 13 14 15│ 4 5 6│ 1 2 3│ 7 8 9│
180 | └─────────────────┴────────┴────────┴────────┘
181 | ┌──┬──┬──┴──┴──┘
182 | ┌─────────────────┬──────────────────────────┐
183 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
184 | └─────────────────┴──────────────────────────┘
185 | ```
186 |
187 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=87s)
188 |
189 | Grail Rotation
190 | --------------
191 | The grail rotation from the Holy Grail Sort Project is Gries-Mills derived and tries to improve locality by shifting memory either left or right depending on which side it's swapped from.[^4] In addition it performs an auxiliary rotation on stack memory when the smallest side reaches a size of 1 element, which is the worst case for the Gries-Mills rotation. The flow diagram is identical to that of Gries-Mills, but due to memory being shifted from the right the visualization differs.
192 |
193 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=67s)
194 |
195 | Helix Rotation
196 | ---------------
197 | The helix rotation has similarities with the Gries-Mills rotation but has a distinct sequential movement pattern. It is an improvement upon the Grail rotation by merging the two inner loops into a single loop, significantly improving performance when the relative size difference between the two halves is large. In addition it doesn't stop when the smallest block no longer fits, but continues and recalculates the left or right side. The utilization of the merged loops is counter-intuitive and is likely novel. Its first known publication was in 2021 by Control from the Holy Grail Sort Project.[^4]
198 | ```c
199 | ┌──────────────────────────┬─────────────────┐
200 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
201 | └──────────────────────────┴─────────────────┘
202 | ┌──┬──┬──┬──┬──┬──┴──┴──┴──┴──┴──┘
203 | ┌────────┬─────────────────┬─────────────────┐
204 | │ 1 2 3│10 11 12 13 14 15│ 4 5 6 7 8 9│
205 | └────────┴─────────────────┴─────────────────┘
206 | ┌──┬──┬───────────┴──┴──┘
207 | ┌─────────────────┬──────────────────────────┐
208 | │13 14 15 10 11 12│ 1 2 3 4 5 6 7 8 9│
209 | └─────────────────┴──────────────────────────┘
210 | └──┴──┴──┬──┬──┐
211 | ┌─────────────────┬──────────────────────────┐
212 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
213 | └─────────────────┴──────────────────────────┘
214 | ```
215 |
216 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=108s)
217 |
218 | Drill Rotation
219 | --------------
220 | The drill rotation is a grail variant that utilizes a piston main loop and a helix inner loop. Performance is similar to the helix rotation. The flow diagram and visualization are identical to the grail rotation.
221 |
222 | Trinity Rotation
223 | ----------------
224 | The trinity rotation (aka conjoined triple reversal) is derived from the triple reversal rotation. Rather than three separate reversals it conjoins the three reversals, improving locality and reducing the number of moves. Optionally, if the overlap is smaller than 8 elements, it skips the trinity rotation and performs an auxiliary or bridge rotation on stack memory. Its first known publication was in 2021 by Igor van den Hoven.[^1]
225 | ```c
226 | ┌──────────────────────────┬─────────────────┐
227 | │ 1 2 3 4 5 6 7 8 9│10 11 12 13 14 15│
228 | └──────────────────────────┴─────────────────┘
229 | ↓ ↓ ↓ ↓
230 | ┌──────────────────────────┬─────────────────┐
231 | │10 2 3 4 5 6 7 8 1│15 11 12 13 14 9│
232 | └──────────────────────────┴─────────────────┘
233 | ↓ ↓ ↓ ↓
234 | ┌──────────────────────────┬─────────────────┐
235 | │10 11 3 4 5 6 7 2 1│15 14 12 13 8 9│
236 | └──────────────────────────┴─────────────────┘
237 | ↓ ↓ ↓ ↓
238 | ┌──────────────────────────┬─────────────────┐
239 | │10 11 12 4 5 6 3 2 1│15 14 13 7 8 9│
240 | └──────────────────────────┴─────────────────┘
241 | ↓ ↓ ↓
242 | ┌──────────────────────────┬─────────────────┐
243 | │10 11 12 13 5 4 3 2 1│15 14 6 7 8 9│
244 | └──────────────────────────┴─────────────────┘
245 | ↓ ↓
246 | ┌──────────────────────────┬─────────────────┐
247 | │10 11 12 13 14 4 3 2 1│15 5 6 7 8 9│
248 | └──────────────────────────┴─────────────────┘
249 | ↓ ↓
250 | ┌──────────────────────────┬─────────────────┐
251 | │10 11 12 13 14 15 3 2 1│ 4 5 6 7 8 9│
252 | └──────────────────────────┴─────────────────┘
253 | ↓ ↓
254 | ┌─────────────────┬──────────────────────────┐
255 | │10 11 12 13 14 15│ 1 2 3 4 5 6 7 8 9│
256 | └─────────────────┴──────────────────────────┘
257 | ```
258 |
259 | [](https://www.youtube.com/watch?v=rHubUT40FDc&t=35s)
260 |
261 | Benchmarks
262 | ----------
263 | Since the auxiliary/bridge rotations are fairly similar I've omitted the auxiliary rotation from the benchmark graph. Similarly the grail rotation has been omitted since it's fundamentally slower than the helix rotation. The contrev rotation is the trinity rotation without auxiliary memory.
264 |
265 | While performance may vary depending on the specific implemention and array size, from worst to best the order is:
266 |
267 | * Juggling Rotation (juggler)
268 | * Auxiliary Rotation
269 | * Gries-Mills Rotation (griesmills)
270 | * Successive Rotation (piston)
271 | * Grail Rotation
272 | * Bridge Rotation (bridge)
273 | * Triple Reversal Rotation (reversal)
274 | * Helix Rotation (helix)
275 | * Conjoined Triple Reversal Rotation (contrev)
276 | * Trinity Rotation (trinity)
277 |
278 | It should be noted that the auxiliary Rotation performs better for smaller arrays and when the relative size difference between the two halves is large.
279 |
280 | The following benchmark was on WSL 2 gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04). The source code was compiled using `gcc -O3 bench.c`. Each test was ran 1,000 times with the time (in seconds) reported of the best and average run.
281 |
282 | 
283 |
284 | data table
285 |
286 | | Name | Items | Type | Best | Average | Loops | Samples | Distribution |
287 | | --------- | -------- | ---- | -------- | -------- | --------- | ------- | ---------------- |
288 | | bridge | 1000000 | 32 | 0.000389 | 0.000510 | 1 | 200 | 1/999999 |
289 | | helix | 1000000 | 32 | 0.000383 | 0.000457 | 1 | 200 | 1/999999 |
290 | | trinity | 1000000 | 32 | 0.000395 | 0.000482 | 1 | 200 | 1/999999 |
291 | | reversal | 1000000 | 32 | 0.000517 | 0.000617 | 1 | 200 | 1/999999 |
292 | | | | | | | | | |
293 | | bridge | 1000000 | 32 | 0.000385 | 0.000445 | 1 | 200 | 1000/999000 |
294 | | helix | 1000000 | 32 | 0.000487 | 0.000559 | 1 | 200 | 1000/999000 |
295 | | trinity | 1000000 | 32 | 0.000412 | 0.000469 | 1 | 200 | 1000/999000 |
296 | | reversal | 1000000 | 32 | 0.000507 | 0.000581 | 1 | 200 | 1000/999000 |
297 | | | | | | | | | |
298 | | bridge | 1000000 | 32 | 0.000456 | 0.000616 | 1 | 200 | 99999/900001 |
299 | | helix | 1000000 | 32 | 0.000543 | 0.000619 | 1 | 200 | 99999/900001 |
300 | | trinity | 1000000 | 32 | 0.000446 | 0.000551 | 1 | 200 | 99999/900001 |
301 | | reversal | 1000000 | 32 | 0.000519 | 0.000598 | 1 | 200 | 99999/900001 |
302 | | | | | | | | | |
303 | | bridge | 1000000 | 32 | 0.000495 | 0.000595 | 1 | 200 | 199998/800002 |
304 | | helix | 1000000 | 32 | 0.000572 | 0.000656 | 1 | 200 | 199998/800002 |
305 | | trinity | 1000000 | 32 | 0.000447 | 0.000513 | 1 | 200 | 199998/800002 |
306 | | reversal | 1000000 | 32 | 0.000519 | 0.000636 | 1 | 200 | 199998/800002 |
307 | | | | | | | | | |
308 | | bridge | 1000000 | 32 | 0.000557 | 0.000659 | 1 | 200 | 299997/700003 |
309 | | helix | 1000000 | 32 | 0.000519 | 0.000694 | 1 | 200 | 299997/700003 |
310 | | trinity | 1000000 | 32 | 0.000441 | 0.000544 | 1 | 200 | 299997/700003 |
311 | | reversal | 1000000 | 32 | 0.000515 | 0.000701 | 1 | 200 | 299997/700003 |
312 | | | | | | | | | |
313 | | bridge | 1000000 | 32 | 0.000527 | 0.000612 | 1 | 200 | 399996/600004 |
314 | | helix | 1000000 | 32 | 0.000481 | 0.000553 | 1 | 200 | 399996/600004 |
315 | | trinity | 1000000 | 32 | 0.000437 | 0.000506 | 1 | 200 | 399996/600004 |
316 | | reversal | 1000000 | 32 | 0.000506 | 0.000602 | 1 | 200 | 399996/600004 |
317 | | | | | | | | | |
318 | | bridge | 1000000 | 32 | 0.000394 | 0.000488 | 1 | 200 | 499995/500005 |
319 | | helix | 1000000 | 32 | 0.000669 | 0.000746 | 1 | 200 | 499995/500005 |
320 | | trinity | 1000000 | 32 | 0.000398 | 0.000498 | 1 | 200 | 499995/500005 |
321 | | reversal | 1000000 | 32 | 0.000511 | 0.000590 | 1 | 200 | 499995/500005 |
322 |
323 |
324 |
325 | 
326 |
327 | data table
328 |
329 | | Name | Items | Type | Best | Average | Loops | Samples | Distribution |
330 | | --------- | -------- | ---- | -------- | -------- | --------- | ------- | ---------------- |
331 | | contrev | 1000000 | 32 | 0.000717 | 0.000840 | 1 | 200 | 1/999999 |
332 | | piston | 1000000 | 32 | 0.002091 | 0.002398 | 1 | 200 | 1/999999 |
333 | |griesmills | 1000000 | 32 | 0.002436 | 0.002565 | 1 | 200 | 1/999999 |
334 | | juggler | 1000000 | 32 | 0.000612 | 0.000701 | 1 | 200 | 1/999999 |
335 | | | | | | | | | |
336 | | contrev | 1000000 | 32 | 0.000416 | 0.000487 | 1 | 200 | 1000/999000 |
337 | | piston | 1000000 | 32 | 0.000474 | 0.000549 | 1 | 200 | 1000/999000 |
338 | |griesmills | 1000000 | 32 | 0.000490 | 0.000581 | 1 | 200 | 1000/999000 |
339 | | juggler | 1000000 | 32 | 0.001234 | 0.001339 | 1 | 200 | 1000/999000 |
340 | | | | | | | | | |
341 | | contrev | 1000000 | 32 | 0.000448 | 0.000512 | 1 | 200 | 99999/900001 |
342 | | piston | 1000000 | 32 | 0.000534 | 0.000682 | 1 | 200 | 99999/900001 |
343 | |griesmills | 1000000 | 32 | 0.000550 | 0.000619 | 1 | 200 | 99999/900001 |
344 | | juggler | 1000000 | 32 | 0.001279 | 0.001570 | 1 | 200 | 99999/900001 |
345 | | | | | | | | | |
346 | | contrev | 1000000 | 32 | 0.000446 | 0.000528 | 1 | 200 | 199998/800002 |
347 | | piston | 1000000 | 32 | 0.000564 | 0.000630 | 1 | 200 | 199998/800002 |
348 | |griesmills | 1000000 | 32 | 0.000578 | 0.000687 | 1 | 200 | 199998/800002 |
349 | | juggler | 1000000 | 32 | 0.001282 | 0.001783 | 1 | 200 | 199998/800002 |
350 | | | | | | | | | |
351 | | contrev | 1000000 | 32 | 0.000444 | 0.000517 | 1 | 200 | 299997/700003 |
352 | | piston | 1000000 | 32 | 0.000517 | 0.000626 | 1 | 200 | 299997/700003 |
353 | |griesmills | 1000000 | 32 | 0.000527 | 0.000612 | 1 | 200 | 299997/700003 |
354 | | juggler | 1000000 | 32 | 0.002259 | 0.002476 | 1 | 200 | 299997/700003 |
355 | | | | | | | | | |
356 | | contrev | 1000000 | 32 | 0.000431 | 0.000502 | 1 | 200 | 399996/600004 |
357 | | piston | 1000000 | 32 | 0.000505 | 0.000610 | 1 | 200 | 399996/600004 |
358 | |griesmills | 1000000 | 32 | 0.000509 | 0.000588 | 1 | 200 | 399996/600004 |
359 | | juggler | 1000000 | 32 | 0.001983 | 0.002230 | 1 | 200 | 399996/600004 |
360 | | | | | | | | | |
361 | | contrev | 1000000 | 32 | 0.000443 | 0.000543 | 1 | 200 | 499995/500005 |
362 | | piston | 1000000 | 32 | 0.000667 | 0.000755 | 1 | 200 | 499995/500005 |
363 | |griesmills | 1000000 | 32 | 0.000681 | 0.000776 | 1 | 200 | 499995/500005 |
364 | | juggler | 1000000 | 32 | 0.001304 | 0.001475 | 1 | 200 | 499995/500005 |
365 |
366 |
367 |
368 | Footnotes
369 | ---------
370 |
371 | [^1]: [A collection of array rotation algorithms, Igor van den Hoven, May 2021](https://raw.githubusercontent.com/scandum/rotate/ae113459f2f2ad236666f841f230ec13446e242e/src/rotate.h)
372 | [^2]: [Algorithm 284: Interchange of two blocks of data, William Fletcher and Roland Silver, May 1966](https://dl.acm.org/doi/10.1145/355592.365609)
373 | [^3]: [SWAPPING SECTIONS, David Gries and Harlan Mills, January 1981](https://ecommons.cornell.edu/bitstream/handle/1813/6292/81-452.pdf)
374 | [^4]: [The Holy Grail Sort Project](https://github.com/HolyGrailSortProject)
375 |
--------------------------------------------------------------------------------
/images/auxiliary.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/auxiliary.gif
--------------------------------------------------------------------------------
/images/bridge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/bridge.gif
--------------------------------------------------------------------------------
/images/bridge_helix_trinity_reversal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/bridge_helix_trinity_reversal.png
--------------------------------------------------------------------------------
/images/contrev_piston_griesmills_juggler.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/contrev_piston_griesmills_juggler.png
--------------------------------------------------------------------------------
/images/grail.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/grail.gif
--------------------------------------------------------------------------------
/images/griesmills.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/griesmills.gif
--------------------------------------------------------------------------------
/images/helix.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/helix.gif
--------------------------------------------------------------------------------
/images/juggling.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/juggling.gif
--------------------------------------------------------------------------------
/images/piston.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/piston.gif
--------------------------------------------------------------------------------
/images/reversal.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/reversal.gif
--------------------------------------------------------------------------------
/images/trinity.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scandum/rotate/6d0d2d56d10454def027e35921890200b45fe82c/images/trinity.gif
--------------------------------------------------------------------------------
/src/bench.c:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2014-2021 Igor van den Hoven ivdhoven@gmail.com
3 | */
4 |
5 | /*
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the
8 | "Software"), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 | /*
27 | To compile use:
28 |
29 | gcc -O3 bench.c
30 |
31 | or
32 |
33 | g++ -O3 -w -fpermissive bench.c
34 | */
35 |
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 |
44 | #include "rotate.h"
45 |
46 | long long utime()
47 | {
48 | struct timeval now_time;
49 |
50 | gettimeofday(&now_time, NULL);
51 |
52 | return now_time.tv_sec * 1000000LL + now_time.tv_usec;
53 | }
54 |
55 | typedef void ROTATEFUNC(int *array, size_t left, size_t right);
56 |
57 | void test_sort(void *array, void *unsorted, void *valid, int max, int samples, int repetitions, int left, int right, const char *name, char *desc, size_t size)
58 | {
59 | ROTATEFUNC *rotate;
60 | long long start, end, total, best, average;
61 | size_t rep, sam;
62 | int *pta = (int *) array, *ptv = (int *) valid, cnt;
63 | static int legend;
64 |
65 | if (*name == '*')
66 | {
67 | if (legend == 0)
68 | {
69 | legend = 1;
70 |
71 | printf("%s\n", "| Name | Items | Type | Best | Average | Loops | Samples | Distribution |");
72 | printf("%s\n", "| --------- | -------- | ---- | -------- | -------- | --------- | ------- | ---------------- |");
73 | }
74 | else
75 | {
76 | printf("%s\n", "| | | | | | | | |");
77 | }
78 | return;
79 | }
80 |
81 | switch (name[0] + name[1] * 128 + name[2] * 16384)
82 | {
83 | case 'a' + 'u' * 128 + 'x' * 16384:
84 | rotate = auxiliary_rotation;
85 | break;
86 |
87 | case 'b' + 'r' * 128 + 'i' * 16384:
88 | rotate = bridge_rotation;
89 | break;
90 |
91 | case 'c' + 'o' * 128 + 'n' * 16384:
92 | rotate = contrev_rotation;
93 | break;
94 |
95 | case 'd' + 'r' * 128 + 'i' * 16384:
96 | rotate = drill_rotation;
97 | break;
98 |
99 | case 'g' + 'r' * 128 + 'a' * 16384:
100 | rotate = grail_rotation;
101 | break;
102 |
103 | case 'g' + 'r' * 128 + 'i' * 16384:
104 | rotate = griesmills_rotation;
105 | break;
106 |
107 | case 'h' + 'e' * 128 + 'l' * 16384:
108 | rotate = helix_rotation;
109 | break;
110 |
111 | case 'j' + 'u' * 128 + 'g' * 16384:
112 | rotate = juggling_rotation;
113 | break;
114 |
115 | case 'p' + 'i' * 128 + 's' * 16384:
116 | rotate = piston_rotation;
117 | break;
118 |
119 | case 'r' + 'e' * 128 + 'v' * 16384:
120 | rotate = reversal_rotation;
121 | break;
122 |
123 | case 't' + 'r' * 128 + 'i' * 16384:
124 | rotate = trinity_rotation;
125 | break;
126 |
127 | default:
128 | printf("Unknown rotation: (%s). Valid rotations are: auxiliary, bridge, contrev, drill, grail, griesmills, helix, juggling, piston, reversal, trinity\n", name);
129 | return;
130 | }
131 |
132 | best = average = 0;
133 |
134 | for (sam = 0 ; sam < samples ; sam++)
135 | {
136 | total = 0;
137 |
138 | start = utime();
139 |
140 | for (rep = 0 ; rep < repetitions ; rep++)
141 | {
142 | memcpy(array, unsorted, max * size);
143 |
144 | rotate(array, left, right);
145 | }
146 | end = utime();
147 |
148 | total = end - start;
149 |
150 | if (!best || total < best)
151 | {
152 | best = total;
153 | }
154 | average += total;
155 | }
156 |
157 | average /= samples;
158 |
159 | printf("|%10s | %8d | %4d | %f | %f | %9d | %7d | %16s |\n", name, max, (int) size * 8, best / 1000000.0, average / 1000000.0, repetitions, samples, desc);
160 |
161 | for (cnt = 0 ; cnt < max ; cnt++)
162 | {
163 | if (pta[cnt] != ptv[cnt])
164 | {
165 | printf(" validate: array[%d] != valid[%d]. (%d vs %d\n", cnt, cnt, pta[cnt], ptv[cnt]);
166 | break;
167 | }
168 | }
169 | }
170 |
171 | int main(int argc, char **argv)
172 | {
173 | int max = 1000000;
174 | int samples = 200;
175 | int repetitions = 1;
176 | int seed = 0;
177 | int cnt, left, right, index;
178 | int *a_array, *r_array, *v_array;
179 | char dist[40], *sorts[] = { "*", "bridge", "helix", "trinity", "reversal" };
180 | // char dist[40], *sorts[] = { "*", "contrev", "piston", "griesmills", "juggler" };
181 | // char dist[40], *sorts[] = { "*", "auxiliary", "bridge", "contrev", "drill", "grail", "griesmills", "helix", "juggling", "piston", "reversal", "trinity" };
182 |
183 | if (argc >= 1 && argv[1] && *argv[1])
184 | {
185 | max = atoi(argv[1]);
186 | }
187 |
188 | if (argc >= 2 && argv[2] && *argv[2])
189 | {
190 | samples = atoi(argv[2]);
191 | }
192 |
193 | if (argc >= 3 && argv[3] && *argv[3])
194 | {
195 | repetitions = atoi(argv[3]);
196 | }
197 |
198 | if (argc >= 4 && argv[4] && *argv[4])
199 | {
200 | seed = atoi(argv[4]);
201 | }
202 |
203 | printf("Benchmark: array size: %d, samples: %d, repetitions: %d, seed: %d\n\n", max, samples, repetitions, seed);
204 |
205 | // 32 bit
206 |
207 | a_array = (int *) malloc(max * sizeof(int));
208 | r_array = (int *) malloc(max * sizeof(int));
209 | v_array = (int *) malloc(max * sizeof(int));
210 |
211 | for (cnt = 0 ; cnt < max ; cnt++)
212 | {
213 | r_array[cnt] = cnt;
214 | }
215 |
216 | int values[] = { 1, 1000, 99999, 199998, 299997, 399996, 499995, 0};
217 |
218 | for (index = 0 ; values[index] != 0 ; index++)
219 | {
220 | left = values[index];
221 |
222 | right = max - left;
223 |
224 | memcpy(v_array, r_array, max * sizeof(int));
225 |
226 | auxiliary_rotation(v_array, left, right);
227 |
228 | sprintf(dist, "%d/%d", left, right);
229 |
230 | for (cnt = 0 ; cnt < sizeof(sorts) / sizeof(char *) ; cnt++)
231 | {
232 | test_sort(a_array, r_array, v_array, max, samples, repetitions, left, right, sorts[cnt], dist, sizeof(int));
233 | }
234 | }
235 |
236 | for (left = 1 ; left <= 9 ; left ++)
237 | {
238 | right = max - left;
239 |
240 | memcpy(v_array, r_array, max * sizeof(int));
241 |
242 | auxiliary_rotation(v_array, left, right);
243 |
244 | sprintf(dist, "%d/%d", left, right);
245 |
246 | for (cnt = 0 ; cnt < sizeof(sorts) / sizeof(char *) ; cnt++)
247 | {
248 | test_sort(a_array, r_array, v_array, max, samples, repetitions, left, right, sorts[cnt], dist, sizeof(int));
249 | }
250 | }
251 |
252 | for (left = max / 3 ; left < max / 3 + 5 ; left++)
253 | {
254 | right = max - left;
255 |
256 | memcpy(v_array, r_array, max * sizeof(int));
257 |
258 | auxiliary_rotation(v_array, left, right);
259 |
260 | sprintf(dist, "%d/%d", left, right);
261 |
262 | for (cnt = 0 ; cnt < sizeof(sorts) / sizeof(char *) ; cnt++)
263 | {
264 | test_sort(a_array, r_array, v_array, max, samples, repetitions, left, right, sorts[cnt], dist, sizeof(int));
265 | }
266 | }
267 |
268 | for (left = max / 2 ; left < max / 2 + 9 ; left++)
269 | {
270 | right = max - left;
271 |
272 | memcpy(v_array, r_array, max * sizeof(int));
273 |
274 | auxiliary_rotation(v_array, left, right);
275 |
276 | sprintf(dist, "%d/%d", left, right);
277 |
278 | for (cnt = 0 ; cnt < sizeof(sorts) / sizeof(char *) ; cnt++)
279 | {
280 | test_sort(a_array, r_array, v_array, max, samples, repetitions, left, right, sorts[cnt], dist, sizeof(int));
281 | }
282 | }
283 |
284 | for (left = max * 10 / 100 - 1 ; left < max ; left += max * 10 / 100 - 1)
285 | {
286 | right = max - left;
287 |
288 | memcpy(v_array, r_array, max * sizeof(int));
289 |
290 | auxiliary_rotation(v_array, left, right);
291 |
292 | sprintf(dist, "%d/%d", left, right);
293 |
294 | for (cnt = 0 ; cnt < sizeof(sorts) / sizeof(char *) ; cnt++)
295 | {
296 | test_sort(a_array, r_array, v_array, max, samples, repetitions, left, right, sorts[cnt], dist, sizeof(int));
297 | }
298 | }
299 |
300 | free(a_array);
301 | free(r_array);
302 | free(v_array);
303 |
304 | return 0;
305 | }
306 |
--------------------------------------------------------------------------------
/src/rotate.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2021 Igor van den Hoven ivdhoven@gmail.coms
3 | */
4 |
5 | /*
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the
8 | "Software"), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 | /*
27 | rotate 1.2
28 | */
29 |
30 | #ifndef ROTATE_H
31 | #define ROTATE_H
32 |
33 | void outsidein_reversal(int *array, size_t block_size)
34 | {
35 | int *pta, *ptb, swap;
36 |
37 | pta = array;
38 | ptb = array + block_size;
39 |
40 | block_size /= 2;
41 |
42 | while (block_size--)
43 | {
44 | swap = *pta; *pta++ = *--ptb; *ptb = swap;
45 | }
46 | }
47 |
48 | void insideout_reversal(int *array, size_t block_size)
49 | {
50 | int *pta, *ptb, swap;
51 |
52 | ptb = array + block_size;
53 |
54 | block_size /= 2;
55 |
56 | pta = array + block_size;
57 | ptb -= block_size;
58 |
59 | while (block_size--)
60 | {
61 | swap = *--pta; *pta = *ptb; *ptb++ = swap;
62 | }
63 | }
64 |
65 | void forward_block_swap(int *array, const size_t start1, const size_t start2, size_t block_size)
66 | {
67 | int *pta, *ptb, swap;
68 |
69 | pta = array + start1;
70 | ptb = array + start2;
71 |
72 | while (block_size--)
73 | {
74 | swap = *pta; *pta++ = *ptb; *ptb++ = swap;
75 | }
76 | }
77 |
78 | void backward_block_swap(int *array, const size_t start1, const size_t start2, size_t block_size)
79 | {
80 | int *pta, *ptb, swap;
81 |
82 | pta = array + start1 + block_size;
83 | ptb = array + start2 + block_size;
84 |
85 | while (block_size--)
86 | {
87 | swap = *--pta; *pta = *--ptb; *ptb = swap;
88 | }
89 | }
90 |
91 | void auxiliary_rotation(int *array, size_t left, size_t right)
92 | {
93 | int *pta, *ptb, *ptc, *swap;
94 |
95 | pta = array;
96 | ptb = array + left;
97 | ptc = array + right;
98 |
99 | if (left < right)
100 | {
101 | swap = malloc(left * sizeof(int));
102 |
103 | memcpy(swap, pta, left * sizeof(int));
104 |
105 | memmove(pta, ptb, right * sizeof(int));
106 |
107 | memcpy(ptc, swap, left * sizeof(int));
108 | }
109 | else
110 | {
111 | swap = malloc(right * sizeof(int));
112 |
113 | memcpy(swap, ptb, right * sizeof(int));
114 |
115 | memmove(ptc, pta, left * sizeof(int));
116 |
117 | memcpy(pta, swap, right * sizeof(int));
118 | }
119 | free(swap);
120 | }
121 |
122 | void stack_rotation(int *array, size_t left, size_t right)
123 | {
124 | int *pta, *ptb, *ptc, swap[8];
125 |
126 | pta = array;
127 | ptb = array + left;
128 | ptc = array + right;
129 |
130 | if (left < right)
131 | {
132 | memcpy(swap, pta, left * sizeof(int));
133 |
134 | memmove(pta, ptb, right * sizeof(int));
135 |
136 | memcpy(ptc, swap, left * sizeof(int));
137 | }
138 | else
139 | {
140 | memcpy(swap, ptb, right * sizeof(int));
141 |
142 | memmove(ptc, pta, left * sizeof(int));
143 |
144 | memcpy(pta, swap, right * sizeof(int));
145 | }
146 | }
147 |
148 | // 3 reversal - Origin unknown, but prior to 1981
149 |
150 | void reversal_rotation(int *array, size_t left, size_t right)
151 | {
152 | outsidein_reversal(array, left);
153 | outsidein_reversal(array + left, right);
154 | outsidein_reversal(array, left + right);
155 | }
156 |
157 | // 2021 - Bridge rotation by Igor van den Hoven
158 |
159 | void bridge_rotation(int *array, size_t left, size_t right)
160 | {
161 | int *pta, *ptb, *ptc, *ptd, *swap;
162 |
163 | pta = array;
164 | ptb = pta + left;
165 | ptc = pta + right;
166 | ptd = ptc + left;
167 |
168 | if (left < right)
169 | {
170 | size_t bridge = right - left;
171 |
172 | if (bridge < left)
173 | {
174 | swap = malloc(bridge * sizeof(int));
175 |
176 | memcpy(swap, ptb, bridge * sizeof(int));
177 |
178 | while (left--)
179 | {
180 | *--ptc = *--ptd; *ptd = *--ptb;
181 | }
182 | memcpy(pta, swap, bridge * sizeof(int));
183 | }
184 | else
185 | {
186 | swap = malloc(left * sizeof(int));
187 | memcpy(swap, pta, left * sizeof(int));
188 | memmove(pta, ptb, right * sizeof(int));
189 | memcpy(ptc, swap, left * sizeof(int));
190 | }
191 | }
192 | else if (right < left)
193 | {
194 | size_t bridge = left - right;
195 |
196 | if (bridge < right)
197 | {
198 | swap = malloc(bridge * sizeof(int));
199 |
200 | memcpy(swap, ptc, bridge * sizeof(int));
201 |
202 | while (right--)
203 | {
204 | *ptc++ = *pta; *pta++ = *ptb++;
205 | }
206 | memcpy(ptd - bridge, swap, bridge * sizeof(int));
207 | }
208 | else
209 | {
210 | swap = malloc(right * sizeof(int));
211 | memcpy(swap, ptb, right * sizeof(int));
212 | memmove(ptc, pta, left * sizeof(int));
213 | memcpy(pta, swap, right * sizeof(int));
214 | }
215 | }
216 | else
217 | {
218 | swap = malloc(1 * sizeof(int));
219 |
220 | while (left--)
221 | {
222 | *swap = *pta; *pta++ = *ptb; *ptb++ = *swap;
223 | }
224 | }
225 | free(swap);
226 | }
227 |
228 | // 2021 - Conjoined Triple Reversal rotation by Igor van den Hoven
229 |
230 | void contrev_rotation(int *array, size_t left, size_t right)
231 | {
232 | int *pta, *ptb, *ptc, *ptd, swap;
233 | size_t loop;
234 |
235 | pta = array;
236 | ptb = array + left;
237 | ptc = array + left;
238 | ptd = array + left + right;
239 |
240 | if (left > right)
241 | {
242 | loop = right / 2;
243 |
244 | while (loop--)
245 | {
246 | swap = *--ptb; *ptb = *pta; *pta++ = *ptc; *ptc++ = *--ptd; *ptd = swap;
247 | }
248 |
249 | loop = (ptb - pta) / 2;
250 |
251 | while (loop--)
252 | {
253 | swap = *--ptb; *ptb = *pta; *pta++ = *--ptd; *ptd = swap;
254 | }
255 | loop = (ptd - pta) / 2;
256 |
257 | while (loop--)
258 | {
259 | swap = *pta; *pta++ = *--ptd; *ptd = swap;
260 | }
261 | }
262 | else if (left < right)
263 | {
264 | loop = left / 2;
265 |
266 | while (loop--)
267 | {
268 | swap = *--ptb; *ptb = *pta; *pta++ = *ptc; *ptc++ = *--ptd; *ptd = swap;
269 | }
270 |
271 | loop = (ptd - ptc) / 2;
272 |
273 | while (loop--)
274 | {
275 | swap = *ptc; *ptc++ = *--ptd; *ptd = *pta; *pta++ = swap;
276 | }
277 | loop = (ptd - pta) / 2;
278 |
279 | while (loop--)
280 | {
281 | swap = *pta; *pta++ = *--ptd; *ptd = swap;
282 | }
283 | }
284 | else
285 | {
286 | loop = left;
287 |
288 | while (loop--)
289 | {
290 | swap = *pta; *pta++ = *ptb; *ptb++ = swap;
291 | }
292 | }
293 | }
294 |
295 | // 2021 - Trinity rotation by Igor van den Hoven (Conjoined Triple Reversal + Bridge rotation)
296 |
297 | #define MAX_AUX 8
298 |
299 | void trinity_rotation(int *array, size_t left, size_t right)
300 | {
301 | int *pta, *ptb, *ptc, *ptd, swap[MAX_AUX];
302 | size_t loop;
303 |
304 | if (left < right)
305 | {
306 | if (left <= MAX_AUX)
307 | {
308 | memcpy(swap, array, left * sizeof(int));
309 | memmove(array, array + left, right * sizeof(int));
310 | memcpy(array + right, swap, left * sizeof(int));
311 | }
312 | else
313 | {
314 | pta = array;
315 | ptb = pta + left;
316 |
317 | loop = right - left;
318 |
319 | if (loop <= MAX_AUX && loop > 3)
320 | {
321 | ptc = pta + right;
322 | ptd = ptc + left;
323 |
324 | memcpy(swap, ptb, loop * sizeof(int));
325 |
326 | while (left--)
327 | {
328 | *--ptc = *--ptd; *ptd = *--ptb;
329 | }
330 | memcpy(pta, swap, loop * sizeof(int));
331 | }
332 | else
333 | {
334 | ptc = ptb;
335 | ptd = ptc + right;
336 |
337 | loop = left / 2;
338 |
339 | while (loop--)
340 | {
341 | *swap = *--ptb; *ptb = *pta; *pta++ = *ptc; *ptc++ = *--ptd; *ptd = *swap;
342 | }
343 |
344 | loop = (ptd - ptc) / 2;
345 |
346 | while (loop--)
347 | {
348 | *swap = *ptc; *ptc++ = *--ptd; *ptd = *pta; *pta++ = *swap;
349 | }
350 |
351 | loop = (ptd - pta) / 2;
352 |
353 | while (loop--)
354 | {
355 | *swap = *pta; *pta++ = *--ptd; *ptd = *swap;
356 | }
357 | }
358 | }
359 | }
360 | else if (right < left)
361 | {
362 | if (right <= MAX_AUX)
363 | {
364 | memcpy(swap, array + left, right * sizeof(int));
365 | memmove(array + right, array, left * sizeof(int));
366 | memcpy(array, swap, right * sizeof(int));
367 | }
368 | else
369 | {
370 | pta = array;
371 | ptb = pta + left;
372 |
373 | loop = left - right;
374 |
375 | if (loop <= MAX_AUX && loop > 3)
376 | {
377 | ptc = pta + right;
378 | ptd = ptc + left;
379 |
380 | memcpy(swap, ptc, loop * sizeof(int));
381 |
382 | while (right--)
383 | {
384 | *ptc++ = *pta; *pta++ = *ptb++;
385 | }
386 | memcpy(ptd - loop, swap, loop * sizeof(int));
387 | }
388 | else
389 | {
390 | ptc = ptb;
391 | ptd = ptc + right;
392 |
393 | loop = right / 2;
394 |
395 | while (loop--)
396 | {
397 | *swap = *--ptb; *ptb = *pta; *pta++ = *ptc; *ptc++ = *--ptd; *ptd = *swap;
398 | }
399 |
400 | loop = (ptb - pta) / 2;
401 |
402 | while (loop--)
403 | {
404 | *swap = *--ptb; *ptb = *pta; *pta++ = *--ptd; *ptd = *swap;
405 | }
406 |
407 | loop = (ptd - pta) / 2;
408 |
409 | while (loop--)
410 | {
411 | *swap = *pta; *pta++ = *--ptd; *ptd = *swap;
412 | }
413 | }
414 | }
415 | }
416 | else
417 | {
418 | pta = array;
419 | ptb = pta + left;
420 |
421 | while (left--)
422 | {
423 | *swap = *pta; *pta++ = *ptb; *ptb++ = *swap;
424 | }
425 | }
426 | }
427 |
428 | #undef MAX_AUX
429 |
430 | // 1981 - Gries-Mills rotation by David Gries and Harlan Mills
431 |
432 | void griesmills_rotation(int *array, size_t left, size_t right)
433 | {
434 | size_t start = 0;
435 |
436 | while (left && right)
437 | {
438 | if (left <= right)
439 | {
440 | do
441 | {
442 | forward_block_swap(array, start, start + left, left);
443 |
444 | start += left;
445 | right -= left;
446 | }
447 | while (left <= right);
448 | }
449 | else
450 | {
451 | do
452 | {
453 | forward_block_swap(array, start + left - right, start + left, right);
454 |
455 | left -= right;
456 | }
457 | while (right <= left);
458 | }
459 | }
460 | }
461 |
462 | // 2020 - Grail rotation by the Holy Grail Sort project (Gries-Mills derived)
463 |
464 | void grail_rotation(int *array, size_t left, size_t right)
465 | {
466 | size_t min = left <= right ? left : right;
467 | size_t start = 0;
468 |
469 | while (min > 1)
470 | {
471 | if (left <= right)
472 | {
473 | do
474 | {
475 | forward_block_swap(array, start, start + left, left);
476 |
477 | start += left;
478 | right -= left;
479 | }
480 | while (left <= right);
481 |
482 | min = right;
483 | }
484 | else
485 | {
486 | do
487 | {
488 | backward_block_swap(array, start + left - right, start + left, right);
489 |
490 | left -= right;
491 | }
492 | while (right <= left);
493 |
494 | min = left;
495 | }
496 | }
497 |
498 | if (min)
499 | {
500 | stack_rotation(array + start, left, right);
501 | }
502 | }
503 |
504 | // 2021 - Piston rotation by Igor van den Hoven. Based on the successive swap described by Gries and Mills in 1981.
505 |
506 | void piston_rotation(int *array, size_t left, size_t right)
507 | {
508 | size_t start = 0;
509 |
510 | while (left > 0)
511 | {
512 | while (left <= right)
513 | {
514 | forward_block_swap(array, start, start + right, left);
515 | right -= left;
516 | }
517 | if (right <= 0)
518 | {
519 | break;
520 | }
521 | do
522 | {
523 | forward_block_swap(array, start, start + left, right);
524 | left -= right;
525 | start += right;
526 | }
527 | while (right <= left);
528 | }
529 |
530 | /* if (left && right)
531 | {
532 | stack_rotation(array + start, left, right);
533 | }*/
534 | }
535 |
536 | // 2021 - Helix rotation by Control (grail derived)
537 |
538 | void helix_rotation(int *array, size_t left, size_t right)
539 | {
540 | int swap;
541 | size_t start = 0;
542 | size_t end = left + right;
543 | size_t mid = left;
544 |
545 | while (1)
546 | {
547 | if (left > right)
548 | {
549 | if (right <= 1)
550 | {
551 | break;
552 | }
553 |
554 | while (mid > start)
555 | {
556 | swap = array[--mid]; array[mid] = array[--end]; array[end] = swap;
557 | }
558 | mid += (left %= right);
559 | right = end - mid;
560 | }
561 | else
562 | {
563 | if (left <= 1)
564 | {
565 | break;
566 | }
567 |
568 | while (mid < end)
569 | {
570 | swap = array[mid]; array[mid++] = array[start]; array[start++] = swap;
571 | }
572 | mid -= (right %= left);
573 | left = mid - start;
574 | }
575 | }
576 |
577 | if (left && right)
578 | {
579 | stack_rotation(array + start, left, right);
580 | }
581 | }
582 |
583 | // 2021 - Drill rotation by Igor van den Hoven (grail derived with piston and helix loops)
584 |
585 | void drill_rotation(int *array, size_t left, size_t right)
586 | {
587 | int swap;
588 | size_t start = 0;
589 | size_t end = left + right;
590 | size_t mid = left;
591 | size_t loop;
592 |
593 | while (left > 1)
594 | {
595 | if (left <= right)
596 | {
597 | loop = end - mid - (right %= left);
598 |
599 | do
600 | {
601 | swap = array[mid]; array[mid++] = array[start]; array[start++] = swap;
602 | }
603 | while (--loop);
604 | }
605 |
606 | if (right <= 1)
607 | {
608 | break;
609 | }
610 |
611 | loop = mid - start - (left %= right);
612 |
613 | do
614 | {
615 | swap = array[--mid]; array[mid] = array[--end]; array[end] = swap;
616 | }
617 | while (--loop);
618 | }
619 |
620 | if (left && right)
621 | {
622 | stack_rotation(array + start, left, right);
623 | }
624 | }
625 |
626 | // 1965 - Juggling aka Dolphin rotation
627 |
628 | int gcd(int a, int b)
629 | {
630 | int r;
631 |
632 | while (b)
633 | {
634 | r = a % b;
635 | a = b;
636 | b = r;
637 | }
638 | return a;
639 | }
640 |
641 | void juggling_rotation(int *array, size_t left, size_t right)
642 | {
643 | int *pta, *ptb, *ptc, *ptd, swap;
644 | const size_t nmemb = left + right;
645 |
646 | if (left == 0)
647 | {
648 | return;
649 | }
650 |
651 | ptd = array + gcd(left, nmemb);
652 |
653 | for (ptc = array ; ptc < ptd ; ptc++)
654 | {
655 | swap = *ptc;
656 | pta = ptc;
657 |
658 | while (1)
659 | {
660 | ptb = pta + left;
661 |
662 | if (ptb >= array + nmemb)
663 | {
664 | ptb -= nmemb;
665 |
666 | if (ptb == ptc)
667 | {
668 | break;
669 | }
670 | }
671 | *pta = *ptb;
672 | pta = ptb;
673 | }
674 | *pta = swap;
675 | }
676 | }
677 |
678 | #endif
679 |
--------------------------------------------------------------------------------