1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use crate::cmp;
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
use crate::ptr;

/// 旋转范围 `[mid-left, mid+right)`,以使 `mid` 处的元素成为第一个元素。等效地,将范围 `left` 元素向左旋转或将 `right` 元素向右旋转。
///
/// # Safety
///
/// 指定的范围必须对读写有效。
///
/// # Algorithm
///
/// 算法 1 用于较小的 `left + right` 或较大的 `T`。
/// 元素从 `mid - left` 开始一次移动到它们的最终位置,并以 `left + right` 为模以 `right` 步长前进,因此只需要一个临时位置。
/// 最终,我们回到 `mid - left`。
/// 但是,如果 `gcd(left + right, right)` 不为 1,则上述步骤将跳过元素。
/// 例如:
///
/// ```text
/// left = 10, right = 6
/// the `^` indicates an element in its final place
/// 6 7 8 9 10 11 12 13 14 15 . 0 1 2 3 4 5
/// after using one step of the above algorithm (The X will be overwritten at the end of the round,
/// and 12 is stored in a temporary):
/// X 7 8 9 10 11 6 13 14 15 . 0 1 2 3 4 5
///               ^
/// after using another step (now 2 is in the temporary):
/// X 7 8 9 10 11 6 13 14 15 . 0 1 12 3 4 5
///               ^                 ^
/// after the third step (the steps wrap around, and 8 is in the temporary):
/// X 7 2 9 10 11 6 13 14 15 . 0 1 12 3 4 5
///     ^         ^                 ^
/// after 7 more steps, the round ends with the temporary 0 getting put in the X:
/// 0 7 2 9 4 11 6 13 8 15 . 10 1 12 3 14 5
/// ^   ^   ^    ^    ^       ^    ^    ^
/// ```
///
/// 幸运的是,最终元素之间被跳过的元素的数量始终相等,因此我们可以偏移起始位置并进行更多的回合 (总回合数为 `gcd(left + right, right)` 值)。
///
/// 最终结果是所有元素只能一次完成。
///
/// 如果 `left + right` 大但 `min(left, right)` 小到足以适合栈缓冲区,则使用算法 2。
/// 将 `min(left, right)` 元素复制到缓冲区,将 `memmove` 应用于其他元素,然后将缓冲区中的元素移回它们起源的另一侧的 hole 中。
///
/// 一旦 `left + right` 变得足够大,可以向量化的算法就会胜过上面的算法。
/// 可以通过分块并一次执行多个回合来对算法 1 进行矢量化,但是在 `left + right` 变得巨大之前,平均回合数量太少,而且单回合的最坏情况始终存在。
/// 相反,算法 3 利用 `min(left, right)` 元素的重复交换,直到剩下较小的旋转问题为止。
///
/// ```text
/// left = 11, right = 4
/// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3]
///                  ^  ^  ^  ^   ^ ^ ^ ^ swapping the right most elements with elements to the left
/// [4 5 6 7 8 9 10 . 0 1 2 3] 11 12 13 14
///        ^ ^ ^  ^   ^ ^ ^ ^ swapping these
/// [4 5 6 . 0 1 2 3] 7 8 9 10 11 12 13 14
/// we cannot swap any more, but a smaller rotation problem is left to solve
/// ```
/// `left < right` 时,交换发生在左侧。
///
///
///
///
///
pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize) {
    type BufType = [usize; 32];
    if T::IS_ZST {
        return;
    }
    loop {
        // 注意,如果不检查这些情况,以下算法可能会失败
        if (right == 0) || (left == 0) {
            return;
        }
        if (left + right < 24) || (mem::size_of::<T>() > mem::size_of::<[usize; 4]>()) {
            // 算法 1 的微基准测试表明,直到 `left + right == 32` 左右,随机移位的平均性能一直都比较好,但是最坏的情况甚至会达到 16。
            // 24 被选为中间立场。
            // 如果 `T` 的大小大于 4 个 `usize`,则该算法也将优于其他算法。
            // SAFETY: 调用者必须确保 `mid - left` 对读写有效。
            //
            //
            let x = unsafe { mid.sub(left) };
            // 第一轮开始
            // SAFETY: 参见上一个注释。
            let mut tmp: T = unsafe { x.read() };
            let mut i = right;
            // `gcd` 可以通过计算 `gcd(left + right, right)` 预先找到,但执行一个循环会更快,将 gcd 作为副作用计算,然后执行块的其余部分
            //
            //
            let mut gcd = right;
            // 基准测试表明,与一遍交换临时文件相比,一次读取一个临时文件,向后复制,然后在最后写入该临时文件要快得多。
            // 这可能是由于以下事实:交换或替换临时节点在循环中仅使用一个内存地址,而不需要管理两个。
            //
            //
            loop {
                // [long-safety-expl]
                // SAFETY: 调用者必须确保 `[left, left+mid+right)` 对读取和写入都是有效的。
                //
                // - `i` 以 `right` 开头所以 `mid-left <= x+i = x+right = mid-left+right < mid+right`
                // - `i <= left+right-1` 始终为 true
                //   - 如果 `i < left`,`right` 被添加,所以 `i < left+right` 和在下一次迭代中 `left` 从 `i` 中删除,所以它不会更进一步
                //
                //   - 如果 `i >= left`,`left` 立即被删除,所以它不会更进一步。
                // - `i` 不会发生溢出,因为函数的安全保证要求 `mid+right-1 = x+left+right` 对写入有效
                // - 不能发生下溢,因为 `i` 必须大于或等于 `left`,才能发生 `left` 的减法。
                //
                // 因此,如果调用者遵守契约,则 `x+i` 对读和写是有效的
                //
                //
                //
                tmp = unsafe { x.add(i).replace(tmp) };
                // 而不是递增 `i`,然后检查它是否在范围之内,我们检查 `i` 是否在下一次递增时越界。
                // 这样可以防止指针或 `usize` 的任何换行。
                //
                if i >= left {
                    i -= left;
                    if i == 0 {
                        // 第一轮结束
                        // SAFETY: tmp 已从有效来源读取,并且 x 可根据调用者的情况进行写入。
                        //
                        unsafe { x.write(tmp) };
                        break;
                    }
                    // 如果 `left + right >= 15`,则此条件必须在此处
                    if i < gcd {
                        gcd = i;
                    }
                } else {
                    i += right;
                }
            }
            // 用更多的回合完成大块
            for start in 1..gcd {
                // SAFETY: `gcd` 至多等于 `right`,因此 `1..gcd` 中的所有值都可以根据函数的安全保证进行读写,参见上面的 [long-safety-expl]
                //
                //
                tmp = unsafe { x.add(start).read() };
                // [safety-expl-addition]
                //
                // 这里 `start < gcd` so `start < right` so `i < right+right`: `right` 是 `(left+right, right)` 的最大公约数意味着 `left = right` so `i < left+right` so `x+i = mid-left+i` 根据函数的安全保证对于读写总是有效的。
                //
                //
                //
                i = start + right;
                loop {
                    // SAFETY: 请参见 [long-safety-expl] 和 [safety-expl-addition]
                    tmp = unsafe { x.add(i).replace(tmp) };
                    if i >= left {
                        i -= left;
                        if i == start {
                            // SAFETY: 请参见 [long-safety-expl] 和 [safety-expl-addition]
                            unsafe { x.add(start).write(tmp) };
                            break;
                        }
                    } else {
                        i += right;
                    }
                }
            }
            return;
        // `T` 不是零大小类型,所以除以它的大小是可以的。
        } else if cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>() {
            // 算法 2 这里的 `[T; 0]` 是为了确保它与 T 正确对齐
            //
            let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit();
            let buf = rawarray.as_mut_ptr() as *mut T;
            // SAFETY: `mid-left <= mid-left+right < mid+right`
            let dim = unsafe { mid.sub(left).add(right) };
            if left <= right {
                // SAFETY:
                //
                // 1) 关于大小的 `else if` 条件确保 `[mid-left; left]` 将适合 `buf` 而不会溢出,而 `buf` 是在上面创建的,因此不能与 `[mid-left; left]` 的任何值重叠
                //
                // 2) [mid-left, mid+right) 都适用于读取和写入,我们不关心这里的重叠。
                // 3) 关于 `left <= right` 的 `if` 条件确保将 `left` 元素写入 `dim = mid-left+right` 是有效的,因为:
                //    - `buf` 有效,`left` 元素写入其中 1)
                //    - `dim+left = mid-left+right+left = mid+right` 我们写 `[dim, dim+left)`
                //
                //
                //
                unsafe {
                    // 1)
                    ptr::copy_nonoverlapping(mid.sub(left), buf, left);
                    // 2)
                    ptr::copy(mid, mid.sub(left), right);
                    // 3)
                    ptr::copy_nonoverlapping(buf, dim, left);
                }
            } else {
                // SAFETY: 与上述相同的推理,但 `left` 和 `right` 颠倒了
                unsafe {
                    ptr::copy_nonoverlapping(mid, buf, right);
                    ptr::copy(mid.sub(left), dim, left);
                    ptr::copy_nonoverlapping(buf, mid.sub(left), right);
                }
            }
            return;
        } else if left >= right {
            // 算法 3 有另一种交换方式,该方式涉及找到该算法的最后交换位置,然后使用该最后一个块进行交换,而不是像该算法那样交换相邻的块,但是这种方式仍然更快。
            //
            //
            //
            loop {
                // SAFETY:
                // `left >= right` 所以 `[mid-right, mid+right)` 对读写有效从 `mid` 中减去 `right` 每圈被加法抵消并在它之后检查。
                //
                //
                unsafe {
                    ptr::swap_nonoverlapping(mid.sub(right), mid, right);
                    mid = mid.sub(right);
                }
                left -= right;
                if left < right {
                    break;
                }
            }
        } else {
            // 算法 3,`left < right`
            loop {
                // SAFETY: `[mid-left, mid+left)` 对读写有效,因为 `left < right` 所以 `mid+left < mid+right`。
                //
                // 将 `left` 添加到 `mid` 每一圈都会被减法及其后的检查抵消。
                //
                unsafe {
                    ptr::swap_nonoverlapping(mid.sub(left), mid, left);
                    mid = mid.add(left);
                }
                right -= left;
                if right < left {
                    break;
                }
            }
        }
    }
}