gameplay: port source ranking sort
Browse files- crates/urt-gameplay/src/bin/urt-lifecycle.rs +31 -0
- crates/urt-gameplay/src/lib.rs +2 -0
- crates/urt-gameplay/src/rank_sort.rs +187 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/README.md +3 -0
- docs/research/RUST_RANK_SORT.md +7 -0
- docs/research/rust-health-impulse.json +1 -1
- docs/research/rust-rank-sort.json +0 -0
- scripts/analyze_rust_rank_sort.py +20 -0
- scripts/test_rust_rank_sort.py +12 -0
- xtask/checks.tsv +15 -0
- xtask/src/lib.rs +1 -0
crates/urt-gameplay/src/bin/urt-lifecycle.rs
CHANGED
|
@@ -1,4 +1,18 @@
|
|
| 1 |
use urt_gameplay::protected::{ProtectedState, update};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
fn main() {
|
| 3 |
if let Err(e) = run() {
|
| 4 |
eprintln!("{e}");
|
|
@@ -314,6 +328,23 @@ fn run() -> Result<(), String> {
|
|
| 314 |
second.cached_ping as u32
|
| 315 |
);
|
| 316 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
"rank-compare" if w.len() == 21 => {
|
| 318 |
let client = |v: &[u32]| urt_gameplay::ranking::Client {
|
| 319 |
spectator_state: v[0] as i32,
|
|
|
|
| 1 |
use urt_gameplay::protected::{ProtectedState, update};
|
| 2 |
+
fn ranking_client(v: &[u32]) -> urt_gameplay::ranking::Client {
|
| 3 |
+
urt_gameplay::ranking::Client {
|
| 4 |
+
spectator_state: v[0] as i32,
|
| 5 |
+
follow_index: v[1] as i32,
|
| 6 |
+
connection: v[2] as i32,
|
| 7 |
+
team: v[3] as i32,
|
| 8 |
+
spectator_time: v[4] as i32,
|
| 9 |
+
time: v[5] as i32,
|
| 10 |
+
class: v[6] as i32,
|
| 11 |
+
score: v[7] as i32,
|
| 12 |
+
deaths: v[8] as i32,
|
| 13 |
+
awards: v[9] as i32,
|
| 14 |
+
}
|
| 15 |
+
}
|
| 16 |
fn main() {
|
| 17 |
if let Err(e) = run() {
|
| 18 |
eprintln!("{e}");
|
|
|
|
| 328 |
second.cached_ping as u32
|
| 329 |
);
|
| 330 |
}
|
| 331 |
+
"rank-sort" if w.len() >= 2 && w[1] <= 64 => {
|
| 332 |
+
let n = w[1] as usize;
|
| 333 |
+
if w.len() != 2 + 11 * n {
|
| 334 |
+
return Err("invalid rank sort words".into());
|
| 335 |
+
}
|
| 336 |
+
let mut ids: Vec<_> = w[2..2 + n].iter().map(|v| *v as usize).collect();
|
| 337 |
+
let clients: Vec<_> = w[2 + n..].chunks_exact(10).map(ranking_client).collect();
|
| 338 |
+
urt_gameplay::rank_sort::sort(w[0] as i32, &clients, &mut ids)
|
| 339 |
+
.map_err(|e| format!("unsupported input: {e:?}"))?;
|
| 340 |
+
println!(
|
| 341 |
+
"{{\"order\":[{}]}}",
|
| 342 |
+
ids.iter()
|
| 343 |
+
.map(|v| v.to_string())
|
| 344 |
+
.collect::<Vec<_>>()
|
| 345 |
+
.join(",")
|
| 346 |
+
);
|
| 347 |
+
}
|
| 348 |
"rank-compare" if w.len() == 21 => {
|
| 349 |
let client = |v: &[u32]| urt_gameplay::ranking::Client {
|
| 350 |
spectator_state: v[0] as i32,
|
crates/urt-gameplay/src/lib.rs
CHANGED
|
@@ -157,3 +157,5 @@ pub mod recipient_wire;
|
|
| 157 |
pub mod record_clear;
|
| 158 |
pub mod score;
|
| 159 |
pub mod stat_totals;
|
|
|
|
|
|
|
|
|
| 157 |
pub mod record_clear;
|
| 158 |
pub mod score;
|
| 159 |
pub mod stat_totals;
|
| 160 |
+
|
| 161 |
+
pub mod rank_sort;
|
crates/urt-gameplay/src/rank_sort.rs
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! QVM35095 aligned four-byte ranking sort. No Rust Ord assumption.
|
| 2 |
+
use crate::ranking::{Client, RankError, compare};
|
| 3 |
+
|
| 4 |
+
fn median(a: &[usize], c: &[Client], mode: i32, x: usize, y: usize, z: usize) -> usize {
|
| 5 |
+
let cmp = |i: usize, j: usize| compare(mode, c[a[i]], c[a[j]]);
|
| 6 |
+
if cmp(x, y) < 0 {
|
| 7 |
+
if cmp(y, z) < 0 {
|
| 8 |
+
y
|
| 9 |
+
} else if cmp(x, z) < 0 {
|
| 10 |
+
z
|
| 11 |
+
} else {
|
| 12 |
+
x
|
| 13 |
+
}
|
| 14 |
+
} else if cmp(y, z) > 0 {
|
| 15 |
+
y
|
| 16 |
+
} else if cmp(x, z) < 0 {
|
| 17 |
+
x
|
| 18 |
+
} else {
|
| 19 |
+
z
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
fn insertion(a: &mut [usize], clients: &[Client], mode: i32) {
|
| 23 |
+
for i in 1..a.len() {
|
| 24 |
+
let mut j = i;
|
| 25 |
+
while j > 0 && compare(mode, clients[a[j - 1]], clients[a[j]]) > 0 {
|
| 26 |
+
a.swap(j - 1, j);
|
| 27 |
+
j -= 1;
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
fn block_swap(a: &mut [usize], x: usize, y: usize, n: usize) {
|
| 32 |
+
for i in 0..n {
|
| 33 |
+
a.swap(x + i, y + i);
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
pub(crate) fn source_sort(mut a: &mut [usize], c: &[Client], mode: i32) {
|
| 37 |
+
loop {
|
| 38 |
+
let n = a.len();
|
| 39 |
+
if n < 7 {
|
| 40 |
+
insertion(a, c, mode);
|
| 41 |
+
return;
|
| 42 |
+
}
|
| 43 |
+
let mut mid = n / 2;
|
| 44 |
+
if n > 7 {
|
| 45 |
+
let (mut first, mut last) = (0, n - 1);
|
| 46 |
+
if n > 40 {
|
| 47 |
+
let d = n / 8;
|
| 48 |
+
first = median(a, c, mode, 0, d, 2 * d);
|
| 49 |
+
mid = median(a, c, mode, mid - d, mid, mid + d);
|
| 50 |
+
last = median(a, c, mode, last - 2 * d, last - d, last);
|
| 51 |
+
}
|
| 52 |
+
mid = median(a, c, mode, first, mid, last);
|
| 53 |
+
}
|
| 54 |
+
a.swap(0, mid);
|
| 55 |
+
let (mut pa, mut pb) = (1usize, 1usize);
|
| 56 |
+
let (mut pc, mut pd) = ((n - 1) as isize, (n - 1) as isize);
|
| 57 |
+
let mut swapped = false;
|
| 58 |
+
loop {
|
| 59 |
+
while (pb as isize) <= pc {
|
| 60 |
+
let r = compare(mode, c[a[pb]], c[a[0]]);
|
| 61 |
+
if r > 0 {
|
| 62 |
+
break;
|
| 63 |
+
}
|
| 64 |
+
if r == 0 {
|
| 65 |
+
a.swap(pa, pb);
|
| 66 |
+
pa += 1;
|
| 67 |
+
swapped = true;
|
| 68 |
+
}
|
| 69 |
+
pb += 1;
|
| 70 |
+
}
|
| 71 |
+
while (pb as isize) <= pc {
|
| 72 |
+
let r = compare(mode, c[a[pc as usize]], c[a[0]]);
|
| 73 |
+
if r < 0 {
|
| 74 |
+
break;
|
| 75 |
+
}
|
| 76 |
+
if r == 0 {
|
| 77 |
+
a.swap(pc as usize, pd as usize);
|
| 78 |
+
pd -= 1;
|
| 79 |
+
swapped = true;
|
| 80 |
+
}
|
| 81 |
+
pc -= 1;
|
| 82 |
+
}
|
| 83 |
+
if (pb as isize) > pc {
|
| 84 |
+
break;
|
| 85 |
+
}
|
| 86 |
+
a.swap(pb, pc as usize);
|
| 87 |
+
pb += 1;
|
| 88 |
+
pc -= 1;
|
| 89 |
+
swapped = true;
|
| 90 |
+
}
|
| 91 |
+
if !swapped {
|
| 92 |
+
insertion(a, c, mode);
|
| 93 |
+
return;
|
| 94 |
+
}
|
| 95 |
+
let r = pa.min(pb - pa);
|
| 96 |
+
block_swap(a, 0, pb - r, r);
|
| 97 |
+
let right = (pd - pc) as usize;
|
| 98 |
+
let r = right.min(n - 1 - pd as usize);
|
| 99 |
+
block_swap(a, pb, n - r, r);
|
| 100 |
+
let left = pb - pa;
|
| 101 |
+
if left > 1 {
|
| 102 |
+
source_sort(&mut a[..left], c, mode);
|
| 103 |
+
}
|
| 104 |
+
if right <= 1 {
|
| 105 |
+
return;
|
| 106 |
+
}
|
| 107 |
+
a = &mut a[n - right..];
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
/// Eligible connected non-spectators only. The order must be a complete
|
| 111 |
+
/// permutation of at most64 clients. Equal-key ordering follows source swaps.
|
| 112 |
+
pub fn sort(mode: i32, clients: &[Client], order: &mut [usize]) -> Result<(), RankError> {
|
| 113 |
+
if clients.len() > 64 || order.len() != clients.len() {
|
| 114 |
+
return Err(RankError::InvalidOrder);
|
| 115 |
+
}
|
| 116 |
+
if clients
|
| 117 |
+
.iter()
|
| 118 |
+
.any(|c| c.connection != 2 || c.team == 3 || c.spectator_state == 3 || c.follow_index < 0)
|
| 119 |
+
{
|
| 120 |
+
return Err(RankError::UnsupportedParticipant);
|
| 121 |
+
}
|
| 122 |
+
let mut seen = vec![false; clients.len()];
|
| 123 |
+
for &id in order.iter() {
|
| 124 |
+
if id >= clients.len() || seen[id] {
|
| 125 |
+
return Err(RankError::InvalidOrder);
|
| 126 |
+
}
|
| 127 |
+
seen[id] = true;
|
| 128 |
+
}
|
| 129 |
+
source_sort(order, clients, mode);
|
| 130 |
+
Ok(())
|
| 131 |
+
}
|
| 132 |
+
#[cfg(test)]
|
| 133 |
+
mod tests {
|
| 134 |
+
use super::*;
|
| 135 |
+
fn client(score: i32) -> Client {
|
| 136 |
+
Client {
|
| 137 |
+
spectator_state: 0,
|
| 138 |
+
follow_index: 0,
|
| 139 |
+
connection: 2,
|
| 140 |
+
team: 1,
|
| 141 |
+
spectator_time: 0,
|
| 142 |
+
time: score,
|
| 143 |
+
class: score,
|
| 144 |
+
score,
|
| 145 |
+
deaths: 0,
|
| 146 |
+
awards: 0,
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
#[test]
|
| 150 |
+
fn signed_extremes_and_partition_sizes() {
|
| 151 |
+
for n in [0, 1, 6, 7, 8, 40, 41, 64] {
|
| 152 |
+
let mut c: Vec<_> = (0..n).map(|i| client(i as i32 % 5)).collect();
|
| 153 |
+
if n > 1 {
|
| 154 |
+
c[0].score = i32::MIN;
|
| 155 |
+
c[1].score = i32::MAX;
|
| 156 |
+
}
|
| 157 |
+
let mut ids: Vec<_> = (0..n).rev().collect();
|
| 158 |
+
sort(0, &c, &mut ids).unwrap();
|
| 159 |
+
assert!(ids.windows(2).all(|w| c[w[0]].score >= c[w[1]].score));
|
| 160 |
+
let mut permutation = ids;
|
| 161 |
+
permutation.sort_unstable();
|
| 162 |
+
assert_eq!(permutation, (0..n).collect::<Vec<_>>());
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
#[test]
|
| 166 |
+
fn equal_keys_keep_source_unstable_pivot_swap() {
|
| 167 |
+
let c = vec![client(0); 7];
|
| 168 |
+
let mut ids: Vec<_> = (0..7).collect();
|
| 169 |
+
sort(0, &c, &mut ids).unwrap();
|
| 170 |
+
assert_eq!(ids, [3, 1, 2, 0, 4, 5, 6]);
|
| 171 |
+
}
|
| 172 |
+
#[test]
|
| 173 |
+
fn rejects_without_mutating_invalid_or_unsupported_input() {
|
| 174 |
+
let c = vec![client(0); 2];
|
| 175 |
+
let mut ids = [0, 0];
|
| 176 |
+
assert_eq!(sort(0, &c, &mut ids), Err(RankError::InvalidOrder));
|
| 177 |
+
assert_eq!(ids, [0, 0]);
|
| 178 |
+
let mut c = c;
|
| 179 |
+
c[0].connection = 1;
|
| 180 |
+
let mut ids = [1, 0];
|
| 181 |
+
assert_eq!(
|
| 182 |
+
sort(0, &c, &mut ids),
|
| 183 |
+
Err(RankError::UnsupportedParticipant)
|
| 184 |
+
);
|
| 185 |
+
assert_eq!(ids, [1, 0]);
|
| 186 |
+
}
|
| 187 |
+
}
|
docs/FINDINGS.md
CHANGED
|
@@ -1379,3 +1379,6 @@ RE-780–789 exposes and bounds legacy resumer timing/byte-state flaws, migrates
|
|
| 1379 |
|
| 1380 |
|
| 1381 |
**RE-793:** Ranking count globals follow connection/team/bot gates, but deferred records can move active clients out of the positional prefix that receives rank words. [Evidence](research/QVM_RANKING_POPULATION.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1379 |
|
| 1380 |
|
| 1381 |
**RE-793:** Ranking count globals follow connection/team/bot gates, but deferred records can move active clients out of the positional prefix that receives rank words. [Evidence](research/QVM_RANKING_POPULATION.md).
|
| 1382 |
+
|
| 1383 |
+
|
| 1384 |
+
**RE-794:** Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. [Evidence](research/RUST_RANK_SORT.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -1383,3 +1383,6 @@ RE-792: 5864 now composes actual35095 sorting and rank writes over80 larger elig
|
|
| 1383 |
|
| 1384 |
|
| 1385 |
RE-793: Ranking count globals follow connection/team/bot gates, but deferred records can move active clients out of the positional prefix that receives rank words. Full runtime completion remains open. [Evidence](research/QVM_RANKING_POPULATION.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1383 |
|
| 1384 |
|
| 1385 |
RE-793: Ranking count globals follow connection/team/bot gates, but deferred records can move active clients out of the positional prefix that receives rank words. Full runtime completion remains open. [Evidence](research/QVM_RANKING_POPULATION.md).
|
| 1386 |
+
|
| 1387 |
+
|
| 1388 |
+
RE-794: Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. Full runtime completion remains open. [Evidence](research/RUST_RANK_SORT.md).
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -1629,3 +1629,8 @@ Both resumers can synchronize new and changed child words into byte lanes withou
|
|
| 1629 |
## 2026-10-26 — RE-793 Mixed ranking populations and positional rank-prefix behavior
|
| 1630 |
|
| 1631 |
Ranking count globals follow connection/team/bot gates, but deferred records can move active clients out of the positional prefix that receives rank words. Focused `cargo xtask test-group ranking-population` passes (13 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1629 |
## 2026-10-26 — RE-793 Mixed ranking populations and positional rank-prefix behavior
|
| 1630 |
|
| 1631 |
Ranking count globals follow connection/team/bot gates, but deferred records can move active clients out of the positional prefix that receives rank words. Focused `cargo xtask test-group ranking-population` passes (13 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
| 1632 |
+
|
| 1633 |
+
|
| 1634 |
+
## 2026-10-26 — RE-794 Rust source-shaped aligned ranking sort with exact tie order
|
| 1635 |
+
|
| 1636 |
+
Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. Focused `cargo xtask test-group rust-rank-sort` passes (9 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -1429,3 +1429,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 1429 |
|
| 1430 |
|
| 1431 |
- [x] RE-793 Mixed ranking populations and positional rank-prefix behavior ([evidence](research/QVM_RANKING_POPULATION.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 1429 |
|
| 1430 |
|
| 1431 |
- [x] RE-793 Mixed ranking populations and positional rank-prefix behavior ([evidence](research/QVM_RANKING_POPULATION.md)).
|
| 1432 |
+
|
| 1433 |
+
|
| 1434 |
+
- [x] RE-794 Rust source-shaped aligned ranking sort with exact tie order ([evidence](research/RUST_RANK_SORT.md)).
|
docs/research/README.md
CHANGED
|
@@ -969,3 +969,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 969 |
|
| 970 |
|
| 971 |
[RE-793: Mixed ranking populations and positional rank-prefix behavior](QVM_RANKING_POPULATION.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 969 |
|
| 970 |
|
| 971 |
[RE-793: Mixed ranking populations and positional rank-prefix behavior](QVM_RANKING_POPULATION.md).
|
| 972 |
+
|
| 973 |
+
|
| 974 |
+
[RE-794: Rust source-shaped aligned ranking sort with exact tie order](RUST_RANK_SORT.md).
|
docs/research/RUST_RANK_SORT.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rust source-shaped aligned ranking sort with exact tie order
|
| 2 |
+
|
| 3 |
+
RE-794. Reproduce with `python3 scripts/analyze_rust_rank_sort.py`; focused validation: `cargo xtask test-group rust-rank-sort`. [Machine report](rust-rank-sort.json).
|
| 4 |
+
|
| 5 |
+
The Rust implementation follows source pivot selection, equal-key partition swaps, insertion fallback, left recursion and right tail iteration.140 original-QVM fixtures check exact resulting order rather than merely equivalent sorted keys. Unit tests cover partition boundaries, signed score extremes, original equal-key pivot permutation and rejection without mutation. Public inputs remain eligible complete permutations with at most64 clients. Mixed raw comparator behavior is reserved for a separate audited composition.
|
| 6 |
+
|
| 7 |
+
Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
|
docs/research/rust-health-impulse.json
CHANGED
|
@@ -2985,7 +2985,7 @@
|
|
| 2985 |
"exit_code": 2
|
| 2986 |
}
|
| 2987 |
],
|
| 2988 |
-
"rust_unit_tests":
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
|
|
|
| 2985 |
"exit_code": 2
|
| 2986 |
}
|
| 2987 |
],
|
| 2988 |
+
"rust_unit_tests": 57,
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
docs/research/rust-rank-sort.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_rust_rank_sort.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import hashlib,json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from analyze_rust_ranking import words
|
| 5 |
+
from rust_lifecycle_support import binary,result,invoke
|
| 6 |
+
from analyze_qvm import write_json
|
| 7 |
+
|
| 8 |
+
def main():
|
| 9 |
+
b=binary();rows=[];sources={}
|
| 10 |
+
for filename in ['qvm-rank-small-sort','qvm-rank-large-sort']:
|
| 11 |
+
path=Path('docs/research/'+filename+'.json');data=path.read_bytes();sources[str(path)]=hashlib.sha256(data).hexdigest()
|
| 12 |
+
for p in json.loads(data)['probes']:
|
| 13 |
+
c=p['clients'];order=p['initial'];r=result(b,'rank-sort',[p['mode'],len(order),*order,*[v for row in c[:len(order)] for v in words(row)]]) if len(c)==len(order) else None
|
| 14 |
+
# Small fixtures keep six seed records even for shorter arrays.
|
| 15 |
+
if r is None:
|
| 16 |
+
used=sorted(set(order));remap={v:j for j,v in enumerate(used)};r=result(b,'rank-sort',[p['mode'],len(used),*[remap[v] for v in order],*[v for j in used for v in words(c[j])]]);r['order']=[used[j] for j in r['order']]
|
| 17 |
+
rows.append({'mode':p['mode'],'initial':order,'source':p['final'],'rust':r,'clients':c})
|
| 18 |
+
bad=invoke(b,'rank-sort',[0,1,0,*words({'468':1})],check=False);assert bad.returncode!=0
|
| 19 |
+
write_json(Path('docs/research/rust-rank-sort.json'),{'source_reports':sources,'probes':rows,'rejections':[bad.returncode],'limits':'Rust source-shaped35095 word-index sort, exact source order including unstable ties across140 fixtures. Eligible complete permutations only; mixed raw prefilters, native ABI and full ranking publication remain outside this API.'})
|
| 20 |
+
if __name__=='__main__':main()
|
scripts/test_rust_rank_sort.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib,json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
class RustRankSortTests(unittest.TestCase):
|
| 4 |
+
@classmethod
|
| 5 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/rust-rank-sort.json').read_text())
|
| 6 |
+
def test_exact_source_order_including_tie_permutations(self):
|
| 7 |
+
self.assertEqual(len(self.r['probes']),140)
|
| 8 |
+
for p in self.r['probes']:self.assertEqual(p['rust']['order'],p['source'],(p['mode'],p['initial']))
|
| 9 |
+
def test_source_identity_and_domain_rejection(self):
|
| 10 |
+
for path,sha in self.r['source_reports'].items():self.assertEqual(hashlib.sha256(Path(path).read_bytes()).hexdigest(),sha)
|
| 11 |
+
self.assertTrue(all(v!=0 for v in self.r['rejections']))
|
| 12 |
+
if __name__=='__main__':unittest.main()
|
xtask/checks.tsv
CHANGED
|
@@ -1792,3 +1792,18 @@ group ranking-population scripts/analyze_qvm_ranking_population.py
|
|
| 1792 |
edge scripts/analyze_qvm_ranking_population.py scripts/analyze_qvm_resumer_audit.py
|
| 1793 |
|
| 1794 |
group ranking-population scripts/analyze_qvm_rank_large_sort.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1792 |
edge scripts/analyze_qvm_ranking_population.py scripts/analyze_qvm_resumer_audit.py
|
| 1793 |
|
| 1794 |
group ranking-population scripts/analyze_qvm_rank_large_sort.py
|
| 1795 |
+
edge Cargo.toml scripts/analyze_rust_rank_sort.py
|
| 1796 |
+
edge Cargo.lock scripts/analyze_rust_rank_sort.py
|
| 1797 |
+
edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_rank_sort.py
|
| 1798 |
+
edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_rank_sort.py
|
| 1799 |
+
edge crates/urt-gameplay/src/bin/urt-lifecycle.rs scripts/analyze_rust_rank_sort.py
|
| 1800 |
+
edge crates/urt-gameplay/src/rank_sort.rs scripts/analyze_rust_rank_sort.py
|
| 1801 |
+
edge crates/urt-gameplay/src/ranking.rs scripts/analyze_rust_rank_sort.py
|
| 1802 |
+
edge crates/urt-gameplay/src/rank_sort.rs scripts/analyze_rust_health_impulse.py
|
| 1803 |
+
group gameplay scripts/analyze_rust_rank_sort.py
|
| 1804 |
+
|
| 1805 |
+
group rust-rank-sort scripts/analyze_rust_health_impulse.py
|
| 1806 |
+
report scripts/analyze_rust_rank_sort.py docs/research/rust-rank-sort.json
|
| 1807 |
+
edge docs/research/rust-rank-sort.json scripts/test_rust_rank_sort.py
|
| 1808 |
+
group rust-rank-sort scripts/analyze_rust_rank_sort.py
|
| 1809 |
+
edge scripts/analyze_rust_rank_sort.py scripts/analyze_qvm_resumer_audit.py
|
xtask/src/lib.rs
CHANGED
|
@@ -1041,6 +1041,7 @@ mod integration_tests {
|
|
| 1041 |
("recent_awards", "recent_awards"),
|
| 1042 |
("recipient_payload", "recipient_payload"),
|
| 1043 |
("ranking", "ranking"),
|
|
|
|
| 1044 |
("leaderboard", "leaderboard"),
|
| 1045 |
("recipient_wire", "recipient_wire"),
|
| 1046 |
] {
|
|
|
|
| 1041 |
("recent_awards", "recent_awards"),
|
| 1042 |
("recipient_payload", "recipient_payload"),
|
| 1043 |
("ranking", "ranking"),
|
| 1044 |
+
("rank_sort", "rank_sort"),
|
| 1045 |
("leaderboard", "leaderboard"),
|
| 1046 |
("recipient_wire", "recipient_wire"),
|
| 1047 |
] {
|