{"id":112376,"date":"2026-06-02T07:00:00","date_gmt":"2026-06-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112376"},"modified":"2026-06-02T20:22:28","modified_gmt":"2026-06-03T03:22:28","slug":"20260602-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260602-00\/?p=112376","title":{"rendered":"Rotation revisited: Another unidirectional algorithm"},"content":{"rendered":"<p>Some time ago, we looked at the problem of <a title=\"Swapping two blocks of memory that reside inside a larger block, in constant memory\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260101-00\/?p=111955\"> swapping two blocks of memory that reside inside a larger block, in constant memory<\/a>, and along the way, we learned about <code>std::<wbr \/>rotate<\/code> which swaps two <i>adjacent<\/i> blocks of memory (not necessarily the same size).<\/p>\n<p>I noted in a postscript that <a href=\"https:\/\/github.com\/llvm\/llvm-project\/blob\/682c8e22e61f50ce2d9a0c42475a3aa6d578a1ad\/libcxx\/include\/__algorithm\/rotate.h\"> clang&#8217;s libcxx<\/a> and <a href=\"https:\/\/github.com\/gcc-mirror\/gcc\/blob\/master\/libstdc%2B%2B-v3\/include\/bits\/stl_algo.h\"> gcc&#8217;s libstdc++<\/a> contain specializations of <code>std::<wbr \/>rotate<\/code> for random-access iterators that view the operation as a permutation and decomposes the permutation into cycles.<\/p>\n<p>I was mistaken.<\/p>\n<p>The implementation in gcc&#8217;s libstdc++ has special cases for single-element rotations, but in the general case, it uses a different algorithm.<\/p>\n<p>Let&#8217;s call the blocks of memory to be exchanged A and B, where A is made up of elements A1, A2, A3, and so on; and block B has elements B1, B2, B3, and so on. Without loss of generality, suppose the A block is smaller. (If not, we can just mirror the algorithm.) And for concreteness let&#8217;s say that the elements are A1, A2, A3, B1, B2, B3, B4, B5.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"See text above\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B5<\/td>\n<\/tr>\n<tr>\n<td>\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<\/tr>\n<tr>\n<td>first<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>mid<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>last<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Exchange elements at <code>first<\/code> and <code>mid<\/code>, then move both iterators forward. After the first step, we have this:<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"B1 A2 A3 A1 B2 B3 B4 B5, with first pointing at A2 and mid pionting at B2\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B5<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>first<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>mid<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>last<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>After three steps, we have moved all of the A&#8217;s out and replaced them with an equal number of B&#8217;s.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"B1 B2 B3 A1 A2 A3 B4 B5, with first pointing at A1 and mid pointing at B4\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B5<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>first<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>mid<\/td>\n<td>&nbsp;<\/td>\n<td>last<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>But don&#8217;t stop. Keep on going until <code>mid<\/code> reaches <code>last<\/code>.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"B1 B2 B3 B4 B5 A3 A1 A2, with first pointing at A1 and mid pointing one past the final element\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B5<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A2<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>first<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>mid<br \/>\nlast<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>All of the B&#8217;s have been swapped to their final positions, but the A&#8217;s are jumbled.<\/p>\n<p>But you can predict the exact nature of the jumbling. The A block is in two chunks. If we let <var>n<\/var> be the total number of elements |A| + |B| and <var>a<\/var> be the number of elements in A, then the first chunk has the final <var>n<\/var> % <var>a<\/var> elements, and the second chunk has the initial <var>a<\/var> \u2212 (<var>n<\/var> % <var>a<\/var>) elements.<\/p>\n<p>Therefore, we can recursively rotate the two pieces of the A block to finish the job. Move <code>mid<\/code> to <code>first<\/code> + (<var>n<\/var> % <var>a<\/var>) and restart the algorithm.<\/p>\n<p>This algorithm performs <var>n<\/var> \u2212 1 swaps. You can calculate this inductively by observing that we perform |B| swaps, and then recursively rotate |A|. Or you can calculate this directly by observing that each swap moves one element to its final position, except that the final swap moves two elements to their final position.<\/p>\n<p>The locality of this algorithm fairly good. The <code>first<\/code> iterator moves steadily forward, and the <code>mid<\/code> iterator moves forward most of the time, with at most <var>O<\/var>(log (min(|A|, |B|)) backward resets.<\/p>\n<p>Next time, we&#8217;ll make a shocking discovery about this algorithm.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Moving in a straight line, in a different way.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112376","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Moving in a straight line, in a different way.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112376","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112376"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112376\/revisions"}],"predecessor-version":[{"id":112377,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112376\/revisions\/112377"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112376"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112376"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112376"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}