{"id":112384,"date":"2026-06-04T07:00:00","date_gmt":"2026-06-04T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112384"},"modified":"2026-06-04T23:27:10","modified_gmt":"2026-06-05T06:27:10","slug":"20260604-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260604-00\/?p=112384","title":{"rendered":"Rotation revisited: Cycle decomposition in clang&#8217;s libcxx"},"content":{"rendered":"<p>We got distracted by the rotation algorithm in gcc&#8217;s libstdc++, <a title=\"Rotation revisited: Another unidirectional algorithm\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260602-00\/?p=112376\"> but let&#8217;s get back to the cycle decomposition algorithm in <\/a><a href=\"https:\/\/github.com\/llvm\/llvm-project\/blob\/682c8e22e61f50ce2d9a0c42475a3aa6d578a1ad\/libcxx\/include\/__algorithm\/rotate.h\"> clang&#8217;s libcxx<\/a><a title=\"Rotation revisited: Another unidirectional algorithm\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260602-00\/?p=112376\">. <\/a><\/p>\n<p>The implementation in clang&#8217;s libcxx performs the minimum number of swaps, roughly <var>n<\/var>\/2, where <var>n<\/var> is the total number of elements. It does so by viewing the rotation as a permutation and walking through each of the cycles.<\/p>\n<p>For notational convenience, let <var>a<\/var> be |A| and <var>n<\/var> be |A| + |B| (the total number of elements). The number of cycles is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Greatest_common_divisor\">gcd<\/a>(<var>a<\/var>, <var>b<\/var>), and the <var>k<\/var>&#8216;th cycle consists of the elements starting at <code>first<\/code> + <var>k<\/var>, and then stepping to the next element by moving forward another <var>a<\/var> elements, with wraparound, until you return back to the starting point.<\/p>\n<p>For example, if you have |A| = 4 and |B| = 6, then the cycle that starts at A1 takes 4 steps forward to continues to B1; takes another 4 steps forward to B5; then takes 2 steps forward, wraps around, and then two more steps forward, landing on A3; then takes 4 steps forward to B3; and then takes 4 steps forward and wraps around to A1, which is the starting point.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"See text\" 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;\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A4<\/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<td style=\"width: 2em; border: solid 1px currentcolor;\">B6<\/td>\n<\/tr>\n<tr>\n<td>\u21b3<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u21b4<\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">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;\">B6<\/td>\n<\/tr>\n<tr>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td>\u21b3<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u21b4<\/td>\n<td><!-- --><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A4<\/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; background: lch(from currentcolor l c h \/ .1);\">B5<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B6<\/td>\n<\/tr>\n<tr>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u21b4<\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td>\u21b3<\/td>\n<td>\u2192<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">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;\">A4<\/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<td style=\"width: 2em; border: solid 1px currentcolor;\">B6<\/td>\n<\/tr>\n<tr>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td>\u21b3<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u21b4<\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A4<\/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; background: lch(from currentcolor l c h \/ .1);\">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;\">B6<\/td>\n<\/tr>\n<tr>\n<td>\u21b4<\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td><!-- --><\/td>\n<td>\u21b3<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<\/tr>\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;\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">A4<\/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<td style=\"width: 2em; border: solid 1px currentcolor;\">B6<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>There&#8217;s another cycle that starts at A2 and continues to B2, B6, A4, B4, then back to A2.<\/p>\n<p>Now, we&#8217;ve been counting swaps, but a single-element rotation is not done as a sequence of swaps, but rather by picking up the first element, sliding all the other elements over, and then putting the original first element at the end. I&#8217;ve been informally calling an assignment &#8220;half of a swap&#8221;, though a swap is really a constructor, two assignments, and a destructor. But let&#8217;s stick with the &#8220;half a swap&#8221; accounting fiction.<\/p>\n<p>The rotation algorithm goes like this:<\/p>\n<pre>auto a = std::distance(first, mid); \/\/ number of \"A\" elements\r\nauto n = std::distance(first, last); \/\/ total elements\r\nauto g = gcd(a, n); \/\/ number of cycles\r\n\r\nfor (auto k = 0; k &lt; g; ++k) {\r\n    \/\/ Rotate the elements in the cycle starting at k\r\n    auto save = std::move(first[k]);\r\n    auto i, next = k;\r\n    while (i = next, next = (i + a) % n, next != k) {\r\n        first[i] = std::move(first[next]);\r\n    }\r\n    first[i] = std::move(save);\r\n}\r\n<\/pre>\n<p>For example, if rotating A1, A2, B1, B2, B3, B4, there are two cycles: A1, B1, B3; and A2, B2, B4. The elements within each cycle rotate one position.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"See text\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\u2ba3<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2ba7<\/td>\n<\/tr>\n<tr>\n<td>\u2ba4<\/td>\n<td style=\"border: solid 1px currentcolor; border-bottom: none;\"><!-- --><\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px currentcolor; border-bottom: none;\"><!-- --><\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px currentcolor; border-bottom: none;\"><!-- --><\/td>\n<td>\u2ba0<\/td>\n<\/tr>\n<tr style=\"height: 1ex;\">\n<td colspan=\"7\"><!-- --><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em;\">\u00a0<\/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;\">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<\/tr>\n<tr style=\"height: 1ex;\">\n<td colspan=\"7\"><!-- --><\/td>\n<\/tr>\n<tr>\n<td>\u2ba6<\/td>\n<td style=\"border: solid 1px currentcolor; border-top: none;\"><!-- --><\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px currentcolor; border-top: none;\"><!-- --><\/td>\n<td>\u2190<\/td>\n<td style=\"border: solid 1px currentcolor; border-top: none;\"><!-- --><\/td>\n<td>\u2ba2<\/td>\n<\/tr>\n<tr>\n<td>\u2ba1<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2192<\/td>\n<td>\u2ba5<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>And when you&#8217;re done with all the cycles, you&#8217;ve rotated the entire A and B blocks.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"See text\" 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; 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<\/tbody>\n<\/table>\n<p>This performs <var>n<\/var>\/2 swaps, which is the fewest swaps of all the algorithms we&#8217;ve looked at so far. However, it has terrible locality because the elements in the cycle are all spread out.<\/p>\n<p>Calculating the greated common divisor of two numbers can be done in <var>O<\/var>(log <var>n<\/var>) steps via Euclid&#8217;s algorithm.<\/p>\n<pre>int gcd(int a, int b)\r\n{\r\n    do {\r\n        auto r = a % b;\r\n        a = b;\r\n        b = r;\r\n    } while (r);\r\n    return a;\r\n}\r\n<\/pre>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260101-00\/?p=111955&amp;commentid=143690#comment-143690\"> Commenter Brent thought that the cycle decomposition algorithm was obvious<\/a>. Of course, the trick is the step they called &#8220;Repeat&#8221;. How many times do you repeat?<\/p>\n<p>The clang libcxx algorithm calculates the number of repeats by taking the gcd. But there&#8217;s a trick so we don&#8217;t have to calculated it at all. We&#8217;ll look at that trick next time.<\/p>\n<p><b>Bonus chatter<\/b>: I think it&#8217;s interesting that of the three major implementations of the C++ standard library, each one uses a different rotation algorithm when given random-access iterators!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rotating in the minimum number of steps by performing cycle decomposition.<\/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-112384","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Rotating in the minimum number of steps by performing cycle decomposition.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112384","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=112384"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112384\/revisions"}],"predecessor-version":[{"id":112387,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112384\/revisions\/112387"}],"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=112384"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112384"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112384"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}