atagen

macltok

public
Star0
Watch0
dc73ad5eb906122a9266ab9bd189a11bc706c31f
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: src/space_order.rs

9bd34e25bb86atagen2026-10-24 07:23 UTC1
use std::cmp::Ordering;
9bd34e25bb86atagen2026-10-24 07:23 UTC2
9bd34e25bb86atagen2026-10-24 07:23 UTC3
use ruma::OwnedRoomId;
9bd34e25bb86atagen2026-10-24 07:23 UTC4
9bd34e25bb86atagen2026-10-24 07:23 UTC5
const FIRST: u8 = 0x20;
9bd34e25bb86atagen2026-10-24 07:23 UTC6
const LAST: u8 = 0x7e;
9bd34e25bb86atagen2026-10-24 07:23 UTC7
const BASE: u64 = (LAST - FIRST + 1) as u64;
9bd34e25bb86atagen2026-10-24 07:23 UTC8
const MAX_LEN: usize = 50;
9bd34e25bb86atagen2026-10-24 07:23 UTC9
9bd34e25bb86atagen2026-10-24 07:23 UTC10
pub(crate) const EVENT_TYPE: &str = "org.matrix.msc3230.space_order";
9bd34e25bb86atagen2026-10-24 07:23 UTC11
9bd34e25bb86atagen2026-10-24 07:23 UTC12
pub(crate) fn valid(order: &str) -> bool {
9bd34e25bb86atagen2026-10-24 07:23 UTC13
    (1..=MAX_LEN).contains(&order.len()) && order.bytes().all(|b| (FIRST..=LAST).contains(&b))
9bd34e25bb86atagen2026-10-24 07:23 UTC14
}
9bd34e25bb86atagen2026-10-24 07:23 UTC15
9bd34e25bb86atagen2026-10-24 07:23 UTC16
/// how msc3230 clients sort spaces: ordered ones by (order, room id), then the rest by room id
9bd34e25bb86atagen2026-10-24 07:23 UTC17
pub(crate) fn compare(
9bd34e25bb86atagen2026-10-24 07:23 UTC18
    (a_id, a_order): (&OwnedRoomId, Option<&str>),
9bd34e25bb86atagen2026-10-24 07:23 UTC19
    (b_id, b_order): (&OwnedRoomId, Option<&str>),
9bd34e25bb86atagen2026-10-24 07:23 UTC20
) -> Ordering {
9bd34e25bb86atagen2026-10-24 07:23 UTC21
    match (a_order, b_order) {
9bd34e25bb86atagen2026-10-24 07:23 UTC22
        (Some(a), Some(b)) => a.cmp(b).then_with(|| a_id.cmp(b_id)),
9bd34e25bb86atagen2026-10-24 07:23 UTC23
        (Some(_), None) => Ordering::Less,
9bd34e25bb86atagen2026-10-24 07:23 UTC24
        (None, Some(_)) => Ordering::Greater,
9bd34e25bb86atagen2026-10-24 07:23 UTC25
        (None, None) => a_id.cmp(b_id),
9bd34e25bb86atagen2026-10-24 07:23 UTC26
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC27
}
9bd34e25bb86atagen2026-10-24 07:23 UTC28
9bd34e25bb86atagen2026-10-24 07:23 UTC29
/// the orders to write so `spaces`, given with their current valid orders, sort in
9bd34e25bb86atagen2026-10-24 07:23 UTC30
/// exactly this sequence. keeps the longest run of orders that already sorts right
9bd34e25bb86atagen2026-10-24 07:23 UTC31
pub(crate) fn plan(spaces: &[(OwnedRoomId, Option<String>)]) -> Vec<(OwnedRoomId, String)> {
9bd34e25bb86atagen2026-10-24 07:23 UTC32
    let key = |index: usize| (&spaces[index].0, spaces[index].1.as_deref());
9bd34e25bb86atagen2026-10-24 07:23 UTC33
    let n = spaces.len();
9bd34e25bb86atagen2026-10-24 07:23 UTC34
9bd34e25bb86atagen2026-10-24 07:23 UTC35
    let mut unordered_tail = n;
9bd34e25bb86atagen2026-10-24 07:23 UTC36
    while unordered_tail > 0
9bd34e25bb86atagen2026-10-24 07:23 UTC37
        && spaces[unordered_tail - 1].1.is_none()
9bd34e25bb86atagen2026-10-24 07:23 UTC38
        && (unordered_tail == n
9bd34e25bb86atagen2026-10-24 07:23 UTC39
            || compare(key(unordered_tail - 1), key(unordered_tail)) == Ordering::Less)
9bd34e25bb86atagen2026-10-24 07:23 UTC40
    {
9bd34e25bb86atagen2026-10-24 07:23 UTC41
        unordered_tail -= 1;
9bd34e25bb86atagen2026-10-24 07:23 UTC42
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC43
9bd34e25bb86atagen2026-10-24 07:23 UTC44
    let mut chain = vec![0usize; n];
9bd34e25bb86atagen2026-10-24 07:23 UTC45
    let mut previous = vec![None; n];
9bd34e25bb86atagen2026-10-24 07:23 UTC46
    for i in (0..n).filter(|&i| spaces[i].1.is_some()) {
9bd34e25bb86atagen2026-10-24 07:23 UTC47
        chain[i] = 1;
9bd34e25bb86atagen2026-10-24 07:23 UTC48
        for j in 0..i {
9bd34e25bb86atagen2026-10-24 07:23 UTC49
            if chain[j] > 0 && chain[j] + 1 > chain[i] && compare(key(j), key(i)) == Ordering::Less
9bd34e25bb86atagen2026-10-24 07:23 UTC50
            {
9bd34e25bb86atagen2026-10-24 07:23 UTC51
                chain[i] = chain[j] + 1;
9bd34e25bb86atagen2026-10-24 07:23 UTC52
                previous[i] = Some(j);
9bd34e25bb86atagen2026-10-24 07:23 UTC53
            }
9bd34e25bb86atagen2026-10-24 07:23 UTC54
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC55
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC56
9bd34e25bb86atagen2026-10-24 07:23 UTC57
    let mut longest: Option<usize> = None;
9bd34e25bb86atagen2026-10-24 07:23 UTC58
    let mut best: Option<(usize, usize, Option<usize>)> = None;
9bd34e25bb86atagen2026-10-24 07:23 UTC59
    for split in 0..=n {
9bd34e25bb86atagen2026-10-24 07:23 UTC60
        if split > 0 && chain[split - 1] > longest.map_or(0, |end| chain[end]) {
9bd34e25bb86atagen2026-10-24 07:23 UTC61
            longest = Some(split - 1);
9bd34e25bb86atagen2026-10-24 07:23 UTC62
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC63
        if split < unordered_tail {
9bd34e25bb86atagen2026-10-24 07:23 UTC64
            continue;
9bd34e25bb86atagen2026-10-24 07:23 UTC65
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC66
        let cost = split - longest.map_or(0, |end| chain[end]);
9bd34e25bb86atagen2026-10-24 07:23 UTC67
        if best.is_none_or(|(best_cost, _, _)| cost < best_cost) {
9bd34e25bb86atagen2026-10-24 07:23 UTC68
            best = Some((cost, split, longest));
9bd34e25bb86atagen2026-10-24 07:23 UTC69
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC70
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC71
    let Some((_, split, end)) = best else {
9bd34e25bb86atagen2026-10-24 07:23 UTC72
        return Vec::new();
9bd34e25bb86atagen2026-10-24 07:23 UTC73
    };
9bd34e25bb86atagen2026-10-24 07:23 UTC74
9bd34e25bb86atagen2026-10-24 07:23 UTC75
    let mut kept = vec![false; n];
9bd34e25bb86atagen2026-10-24 07:23 UTC76
    let mut cursor = end;
9bd34e25bb86atagen2026-10-24 07:23 UTC77
    while let Some(index) = cursor {
9bd34e25bb86atagen2026-10-24 07:23 UTC78
        kept[index] = true;
9bd34e25bb86atagen2026-10-24 07:23 UTC79
        cursor = previous[index];
9bd34e25bb86atagen2026-10-24 07:23 UTC80
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC81
9bd34e25bb86atagen2026-10-24 07:23 UTC82
    let mut writes = Vec::new();
9bd34e25bb86atagen2026-10-24 07:23 UTC83
    let mut run_start = 0;
9bd34e25bb86atagen2026-10-24 07:23 UTC84
    for index in 0..=split {
9bd34e25bb86atagen2026-10-24 07:23 UTC85
        if index < split && !kept[index] {
9bd34e25bb86atagen2026-10-24 07:23 UTC86
            continue;
9bd34e25bb86atagen2026-10-24 07:23 UTC87
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC88
        if index > run_start {
9bd34e25bb86atagen2026-10-24 07:23 UTC89
            let lo = run_start
9bd34e25bb86atagen2026-10-24 07:23 UTC90
                .checked_sub(1)
9bd34e25bb86atagen2026-10-24 07:23 UTC91
                .and_then(|i| spaces[i].1.as_deref());
9bd34e25bb86atagen2026-10-24 07:23 UTC92
            let hi = (index < split)
9bd34e25bb86atagen2026-10-24 07:23 UTC93
                .then(|| spaces[index].1.as_deref())
9bd34e25bb86atagen2026-10-24 07:23 UTC94
                .flatten();
9bd34e25bb86atagen2026-10-24 07:23 UTC95
            let Some(orders) = between(lo, hi, index - run_start) else {
9bd34e25bb86atagen2026-10-24 07:23 UTC96
                return rebalance(&spaces[..split]);
9bd34e25bb86atagen2026-10-24 07:23 UTC97
            };
9bd34e25bb86atagen2026-10-24 07:23 UTC98
            writes.extend(
9bd34e25bb86atagen2026-10-24 07:23 UTC99
                spaces[run_start..index]
9bd34e25bb86atagen2026-10-24 07:23 UTC100
                    .iter()
9bd34e25bb86atagen2026-10-24 07:23 UTC101
                    .map(|(id, _)| id.clone())
9bd34e25bb86atagen2026-10-24 07:23 UTC102
                    .zip(orders),
9bd34e25bb86atagen2026-10-24 07:23 UTC103
            );
9bd34e25bb86atagen2026-10-24 07:23 UTC104
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC105
        run_start = index + 1;
9bd34e25bb86atagen2026-10-24 07:23 UTC106
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC107
    writes
9bd34e25bb86atagen2026-10-24 07:23 UTC108
}
9bd34e25bb86atagen2026-10-24 07:23 UTC109
9bd34e25bb86atagen2026-10-24 07:23 UTC110
fn rebalance(spaces: &[(OwnedRoomId, Option<String>)]) -> Vec<(OwnedRoomId, String)> {
9bd34e25bb86atagen2026-10-24 07:23 UTC111
    let orders = between(None, None, spaces.len()).unwrap_or_default();
9bd34e25bb86atagen2026-10-24 07:23 UTC112
    spaces
9bd34e25bb86atagen2026-10-24 07:23 UTC113
        .iter()
9bd34e25bb86atagen2026-10-24 07:23 UTC114
        .zip(orders)
9bd34e25bb86atagen2026-10-24 07:23 UTC115
        .filter(|((_, current), order)| current.as_ref() != Some(order))
9bd34e25bb86atagen2026-10-24 07:23 UTC116
        .map(|((id, _), order)| (id.clone(), order))
9bd34e25bb86atagen2026-10-24 07:23 UTC117
        .collect()
9bd34e25bb86atagen2026-10-24 07:23 UTC118
}
9bd34e25bb86atagen2026-10-24 07:23 UTC119
9bd34e25bb86atagen2026-10-24 07:23 UTC120
/// `count` evenly spread orders strictly between `lo` and `hi`, as short as they can be
9bd34e25bb86atagen2026-10-24 07:23 UTC121
fn between(lo: Option<&str>, hi: Option<&str>, count: usize) -> Option<Vec<String>> {
9bd34e25bb86atagen2026-10-24 07:23 UTC122
    let lo = lo.unwrap_or("").as_bytes();
9bd34e25bb86atagen2026-10-24 07:23 UTC123
    (lo.len().max(1)..=MAX_LEN).find_map(|len| {
9bd34e25bb86atagen2026-10-24 07:23 UTC124
        let lo = digits(lo, len);
9bd34e25bb86atagen2026-10-24 07:23 UTC125
        let hi = match hi {
9bd34e25bb86atagen2026-10-24 07:23 UTC126
            Some(hi) => digits(&hi.as_bytes()[..hi.len().min(len)], len),
9bd34e25bb86atagen2026-10-24 07:23 UTC127
            None => {
9bd34e25bb86atagen2026-10-24 07:23 UTC128
                let mut one = vec![0; len + 1];
9bd34e25bb86atagen2026-10-24 07:23 UTC129
                one[0] = 1;
9bd34e25bb86atagen2026-10-24 07:23 UTC130
                one
9bd34e25bb86atagen2026-10-24 07:23 UTC131
            }
9bd34e25bb86atagen2026-10-24 07:23 UTC132
        };
9bd34e25bb86atagen2026-10-24 07:23 UTC133
        if hi <= lo {
9bd34e25bb86atagen2026-10-24 07:23 UTC134
            return None;
9bd34e25bb86atagen2026-10-24 07:23 UTC135
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC136
        let step = divide(&subtract(&hi, &lo), count as u64 + 1);
9bd34e25bb86atagen2026-10-24 07:23 UTC137
        if step.iter().all(|&digit| digit == 0) {
9bd34e25bb86atagen2026-10-24 07:23 UTC138
            return None;
9bd34e25bb86atagen2026-10-24 07:23 UTC139
        }
9bd34e25bb86atagen2026-10-24 07:23 UTC140
        let mut point = lo;
9bd34e25bb86atagen2026-10-24 07:23 UTC141
        Some(
9bd34e25bb86atagen2026-10-24 07:23 UTC142
            (0..count)
9bd34e25bb86atagen2026-10-24 07:23 UTC143
                .map(|_| {
9bd34e25bb86atagen2026-10-24 07:23 UTC144
                    point = add(&point, &step);
9bd34e25bb86atagen2026-10-24 07:23 UTC145
                    let mut order: Vec<u8> = point[1..].iter().map(|&d| FIRST + d as u8).collect();
9bd34e25bb86atagen2026-10-24 07:23 UTC146
                    while order.last() == Some(&FIRST) {
9bd34e25bb86atagen2026-10-24 07:23 UTC147
                        order.pop();
9bd34e25bb86atagen2026-10-24 07:23 UTC148
                    }
9bd34e25bb86atagen2026-10-24 07:23 UTC149
                    String::from_utf8(order).expect("orders are ascii")
9bd34e25bb86atagen2026-10-24 07:23 UTC150
                })
9bd34e25bb86atagen2026-10-24 07:23 UTC151
                .collect(),
9bd34e25bb86atagen2026-10-24 07:23 UTC152
        )
9bd34e25bb86atagen2026-10-24 07:23 UTC153
    })
9bd34e25bb86atagen2026-10-24 07:23 UTC154
}
9bd34e25bb86atagen2026-10-24 07:23 UTC155
9bd34e25bb86atagen2026-10-24 07:23 UTC156
/// big-endian base-95 digits with one leading digit of headroom, zero padded to `len`
9bd34e25bb86atagen2026-10-24 07:23 UTC157
fn digits(order: &[u8], len: usize) -> Vec<u64> {
9bd34e25bb86atagen2026-10-24 07:23 UTC158
    let mut out = vec![0; len + 1];
9bd34e25bb86atagen2026-10-24 07:23 UTC159
    for (slot, byte) in out[1..].iter_mut().zip(order) {
9bd34e25bb86atagen2026-10-24 07:23 UTC160
        *slot = u64::from(byte - FIRST);
9bd34e25bb86atagen2026-10-24 07:23 UTC161
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC162
    out
9bd34e25bb86atagen2026-10-24 07:23 UTC163
}
9bd34e25bb86atagen2026-10-24 07:23 UTC164
9bd34e25bb86atagen2026-10-24 07:23 UTC165
fn subtract(a: &[u64], b: &[u64]) -> Vec<u64> {
9bd34e25bb86atagen2026-10-24 07:23 UTC166
    let mut out = vec![0; a.len()];
9bd34e25bb86atagen2026-10-24 07:23 UTC167
    let mut borrow = 0;
9bd34e25bb86atagen2026-10-24 07:23 UTC168
    for i in (0..a.len()).rev() {
9bd34e25bb86atagen2026-10-24 07:23 UTC169
        let (value, under) = (a[i] + BASE - b[i] - borrow, a[i] < b[i] + borrow);
9bd34e25bb86atagen2026-10-24 07:23 UTC170
        out[i] = value % BASE;
9bd34e25bb86atagen2026-10-24 07:23 UTC171
        borrow = u64::from(under);
9bd34e25bb86atagen2026-10-24 07:23 UTC172
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC173
    out
9bd34e25bb86atagen2026-10-24 07:23 UTC174
}
9bd34e25bb86atagen2026-10-24 07:23 UTC175
9bd34e25bb86atagen2026-10-24 07:23 UTC176
fn add(a: &[u64], b: &[u64]) -> Vec<u64> {
9bd34e25bb86atagen2026-10-24 07:23 UTC177
    let mut out = vec![0; a.len()];
9bd34e25bb86atagen2026-10-24 07:23 UTC178
    let mut carry = 0;
9bd34e25bb86atagen2026-10-24 07:23 UTC179
    for i in (0..a.len()).rev() {
9bd34e25bb86atagen2026-10-24 07:23 UTC180
        let sum = a[i] + b[i] + carry;
9bd34e25bb86atagen2026-10-24 07:23 UTC181
        out[i] = sum % BASE;
9bd34e25bb86atagen2026-10-24 07:23 UTC182
        carry = sum / BASE;
9bd34e25bb86atagen2026-10-24 07:23 UTC183
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC184
    out
9bd34e25bb86atagen2026-10-24 07:23 UTC185
}
9bd34e25bb86atagen2026-10-24 07:23 UTC186
9bd34e25bb86atagen2026-10-24 07:23 UTC187
fn divide(a: &[u64], divisor: u64) -> Vec<u64> {
9bd34e25bb86atagen2026-10-24 07:23 UTC188
    let mut remainder = 0;
9bd34e25bb86atagen2026-10-24 07:23 UTC189
    a.iter()
9bd34e25bb86atagen2026-10-24 07:23 UTC190
        .map(|&digit| {
9bd34e25bb86atagen2026-10-24 07:23 UTC191
            let current = remainder * BASE + digit;
9bd34e25bb86atagen2026-10-24 07:23 UTC192
            remainder = current % divisor;
9bd34e25bb86atagen2026-10-24 07:23 UTC193
            current / divisor
9bd34e25bb86atagen2026-10-24 07:23 UTC194
        })
9bd34e25bb86atagen2026-10-24 07:23 UTC195
        .collect()
9bd34e25bb86atagen2026-10-24 07:23 UTC196
}
9bd34e25bb86atagen2026-10-24 07:23 UTC197
9bd34e25bb86atagen2026-10-24 07:23 UTC198
#[cfg(test)]
9bd34e25bb86atagen2026-10-24 07:23 UTC199
mod tests {
9bd34e25bb86atagen2026-10-24 07:23 UTC200
    use ruma::owned_room_id;
9bd34e25bb86atagen2026-10-24 07:23 UTC201
9bd34e25bb86atagen2026-10-24 07:23 UTC202
    use super::*;
9bd34e25bb86atagen2026-10-24 07:23 UTC203
9bd34e25bb86atagen2026-10-24 07:23 UTC204
    fn apply(spaces: &[(OwnedRoomId, Option<String>)]) -> (Vec<OwnedRoomId>, usize) {
9bd34e25bb86atagen2026-10-24 07:23 UTC205
        let writes = plan(spaces);
9bd34e25bb86atagen2026-10-24 07:23 UTC206
        let mut after: Vec<_> = spaces
9bd34e25bb86atagen2026-10-24 07:23 UTC207
            .iter()
9bd34e25bb86atagen2026-10-24 07:23 UTC208
            .map(|(id, order)| {
9bd34e25bb86atagen2026-10-24 07:23 UTC209
                let written = writes.iter().find(|(w, _)| w == id).map(|(_, o)| o.clone());
9bd34e25bb86atagen2026-10-24 07:23 UTC210
                (id.clone(), written.or_else(|| order.clone()))
9bd34e25bb86atagen2026-10-24 07:23 UTC211
            })
9bd34e25bb86atagen2026-10-24 07:23 UTC212
            .collect();
9bd34e25bb86atagen2026-10-24 07:23 UTC213
        assert!(after.iter().all(|(_, o)| o.as_deref().is_none_or(valid)));
9bd34e25bb86atagen2026-10-24 07:23 UTC214
        after.sort_by(|a, b| compare((&a.0, a.1.as_deref()), (&b.0, b.1.as_deref())));
9bd34e25bb86atagen2026-10-24 07:23 UTC215
        (after.into_iter().map(|(id, _)| id).collect(), writes.len())
9bd34e25bb86atagen2026-10-24 07:23 UTC216
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC217
9bd34e25bb86atagen2026-10-24 07:23 UTC218
    fn ids(spaces: &[(OwnedRoomId, Option<String>)]) -> Vec<OwnedRoomId> {
9bd34e25bb86atagen2026-10-24 07:23 UTC219
        spaces.iter().map(|(id, _)| id.clone()).collect()
9bd34e25bb86atagen2026-10-24 07:23 UTC220
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC221
9bd34e25bb86atagen2026-10-24 07:23 UTC222
    #[test]
9bd34e25bb86atagen2026-10-24 07:23 UTC223
    fn already_sorted_spaces_need_no_writes() {
9bd34e25bb86atagen2026-10-24 07:23 UTC224
        let spaces = vec![
9bd34e25bb86atagen2026-10-24 07:23 UTC225
            (owned_room_id!("!z:x"), Some("a".to_owned())),
9bd34e25bb86atagen2026-10-24 07:23 UTC226
            (owned_room_id!("!a:x"), None),
9bd34e25bb86atagen2026-10-24 07:23 UTC227
            (owned_room_id!("!b:x"), None),
9bd34e25bb86atagen2026-10-24 07:23 UTC228
        ];
9bd34e25bb86atagen2026-10-24 07:23 UTC229
        assert_eq!(apply(&spaces), (ids(&spaces), 0));
9bd34e25bb86atagen2026-10-24 07:23 UTC230
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC231
9bd34e25bb86atagen2026-10-24 07:23 UTC232
    #[test]
9bd34e25bb86atagen2026-10-24 07:23 UTC233
    fn moving_one_space_writes_one_order() {
9bd34e25bb86atagen2026-10-24 07:23 UTC234
        let spaces = vec![
9bd34e25bb86atagen2026-10-24 07:23 UTC235
            (owned_room_id!("!c:x"), Some("c".to_owned())),
9bd34e25bb86atagen2026-10-24 07:23 UTC236
            (owned_room_id!("!a:x"), Some("a".to_owned())),
9bd34e25bb86atagen2026-10-24 07:23 UTC237
            (owned_room_id!("!b:x"), Some("b".to_owned())),
9bd34e25bb86atagen2026-10-24 07:23 UTC238
            (owned_room_id!("!d:x"), Some("d".to_owned())),
9bd34e25bb86atagen2026-10-24 07:23 UTC239
        ];
9bd34e25bb86atagen2026-10-24 07:23 UTC240
        assert_eq!(apply(&spaces), (ids(&spaces), 1));
9bd34e25bb86atagen2026-10-24 07:23 UTC241
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC242
9bd34e25bb86atagen2026-10-24 07:23 UTC243
    #[test]
9bd34e25bb86atagen2026-10-24 07:23 UTC244
    fn unordered_spaces_out_of_id_order_get_orders() {
9bd34e25bb86atagen2026-10-24 07:23 UTC245
        let spaces = vec![
9bd34e25bb86atagen2026-10-24 07:23 UTC246
            (owned_room_id!("!c:x"), None),
9bd34e25bb86atagen2026-10-24 07:23 UTC247
            (owned_room_id!("!a:x"), None),
9bd34e25bb86atagen2026-10-24 07:23 UTC248
            (owned_room_id!("!b:x"), None),
9bd34e25bb86atagen2026-10-24 07:23 UTC249
        ];
9bd34e25bb86atagen2026-10-24 07:23 UTC250
        assert_eq!(apply(&spaces), (ids(&spaces), 1));
9bd34e25bb86atagen2026-10-24 07:23 UTC251
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC252
9bd34e25bb86atagen2026-10-24 07:23 UTC253
    #[test]
9bd34e25bb86atagen2026-10-24 07:23 UTC254
    fn orders_with_no_room_between_them_rebalance() {
9bd34e25bb86atagen2026-10-24 07:23 UTC255
        let spaces = vec![
9bd34e25bb86atagen2026-10-24 07:23 UTC256
            (owned_room_id!("!a:x"), Some("a".to_owned())),
9bd34e25bb86atagen2026-10-24 07:23 UTC257
            (owned_room_id!("!c:x"), None),
9bd34e25bb86atagen2026-10-24 07:23 UTC258
            (owned_room_id!("!b:x"), Some("a ".to_owned())),
9bd34e25bb86atagen2026-10-24 07:23 UTC259
        ];
9bd34e25bb86atagen2026-10-24 07:23 UTC260
        assert_eq!(apply(&spaces).0, ids(&spaces));
9bd34e25bb86atagen2026-10-24 07:23 UTC261
        let prefix = "~".repeat(MAX_LEN - 1);
9bd34e25bb86atagen2026-10-24 07:23 UTC262
        let spaces = vec![
9bd34e25bb86atagen2026-10-24 07:23 UTC263
            (owned_room_id!("!b:x"), Some(format!("{prefix}!"))),
9bd34e25bb86atagen2026-10-24 07:23 UTC264
            (owned_room_id!("!a:x"), None),
9bd34e25bb86atagen2026-10-24 07:23 UTC265
            (owned_room_id!("!c:x"), Some(format!("{prefix}\""))),
9bd34e25bb86atagen2026-10-24 07:23 UTC266
        ];
9bd34e25bb86atagen2026-10-24 07:23 UTC267
        assert_eq!(apply(&spaces).0, ids(&spaces));
9bd34e25bb86atagen2026-10-24 07:23 UTC268
    }
9bd34e25bb86atagen2026-10-24 07:23 UTC269
}