tempo: rustfmt the new files (the rest of the repo is not rustfmt-clean at its base, so no other file is reformatted)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
751923d326
commit
c8749c5657
2 changed files with 58 additions and 12 deletions
|
|
@ -50,15 +50,25 @@ pub struct Standing {
|
||||||
/// The urgency in force for a Mind, in thousandths: the strongest of what she
|
/// The urgency in force for a Mind, in thousandths: the strongest of what she
|
||||||
/// declared, what her topics say and a member addressing her (which is 1000).
|
/// declared, what her topics say and a member addressing her (which is 1000).
|
||||||
pub fn urgency_permille(standing: &Standing) -> u16 {
|
pub fn urgency_permille(standing: &Standing) -> u16 {
|
||||||
let addressed = if standing.addressed_by_member { 1000 } else { 0 };
|
let addressed = if standing.addressed_by_member {
|
||||||
standing.self_permille.max(standing.topic_permille).max(addressed).min(1000)
|
1000
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
standing
|
||||||
|
.self_permille
|
||||||
|
.max(standing.topic_permille)
|
||||||
|
.max(addressed)
|
||||||
|
.min(1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `(waited / beat) x 10^(LOG_BOUND_ORDERS x urgency / 1000)`. A Mind that has
|
/// `(waited / beat) x 10^(LOG_BOUND_ORDERS x urgency / 1000)`. A Mind that has
|
||||||
/// never had a turn has waited forever: infinite, so she is first, and the tie
|
/// never had a turn has waited forever: infinite, so she is first, and the tie
|
||||||
/// rule orders the never-acted among themselves.
|
/// rule orders the never-acted among themselves.
|
||||||
pub fn score(standing: &Standing, beat_s: u32) -> f64 {
|
pub fn score(standing: &Standing, beat_s: u32) -> f64 {
|
||||||
let waited = standing.waited_s.map_or(f64::INFINITY, |waited| waited as f64);
|
let waited = standing
|
||||||
|
.waited_s
|
||||||
|
.map_or(f64::INFINITY, |waited| waited as f64);
|
||||||
let exponent = LOG_BOUND_ORDERS * f64::from(urgency_permille(standing)) / 1000.0;
|
let exponent = LOG_BOUND_ORDERS * f64::from(urgency_permille(standing)) / 1000.0;
|
||||||
waited / f64::from(beat_s.max(1)) * 10f64.powf(exponent)
|
waited / f64::from(beat_s.max(1)) * 10f64.powf(exponent)
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +83,11 @@ pub fn pick(rows: &[Standing], beat_s: u32) -> Option<usize> {
|
||||||
.then_with(|| a.1.last_turn_ms.cmp(&b.1.last_turn_ms))
|
.then_with(|| a.1.last_turn_ms.cmp(&b.1.last_turn_ms))
|
||||||
.then_with(|| a.1.slug.cmp(&b.1.slug))
|
.then_with(|| a.1.slug.cmp(&b.1.slug))
|
||||||
};
|
};
|
||||||
rows.iter().enumerate().filter(|(_, row)| row.eligible).min_by(order).map(|(index, _)| index)
|
rows.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, row)| row.eligible)
|
||||||
|
.min_by(order)
|
||||||
|
.map(|(index, _)| index)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,22 @@ fn the_tally_does_not_depend_on_the_order_the_votes_came_in() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn waiting_alone_round_robins() {
|
fn waiting_alone_round_robins() {
|
||||||
let rows = [mind("ann", Some(100)), mind("bob", Some(400)), mind("cy", Some(250))];
|
let rows = [
|
||||||
assert_eq!(pick(&rows, 100), Some(1), "the longest waiter takes the beat");
|
mind("ann", Some(100)),
|
||||||
|
mind("bob", Some(400)),
|
||||||
|
mind("cy", Some(250)),
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
pick(&rows, 100),
|
||||||
|
Some(1),
|
||||||
|
"the longest waiter takes the beat"
|
||||||
|
);
|
||||||
// After bob's turn he has waited least; the next longest waiter goes.
|
// After bob's turn he has waited least; the next longest waiter goes.
|
||||||
let after = [mind("ann", Some(200)), mind("bob", Some(0)), mind("cy", Some(350))];
|
let after = [
|
||||||
|
mind("ann", Some(200)),
|
||||||
|
mind("bob", Some(0)),
|
||||||
|
mind("cy", Some(350)),
|
||||||
|
];
|
||||||
assert_eq!(pick(&after, 100), Some(2));
|
assert_eq!(pick(&after, 100), Some(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +69,11 @@ fn a_full_urgency_equals_ten_times_the_wait_at_one_order() {
|
||||||
let steady = mind("ann", Some(100));
|
let steady = mind("ann", Some(100));
|
||||||
let mut urgent = mind("bob", Some(11));
|
let mut urgent = mind("bob", Some(11));
|
||||||
urgent.self_permille = 1000;
|
urgent.self_permille = 1000;
|
||||||
assert_eq!(pick(&[steady.clone(), urgent.clone()], 100), Some(1), "eleven seconds at full urgency beat a hundred waiting");
|
assert_eq!(
|
||||||
|
pick(&[steady.clone(), urgent.clone()], 100),
|
||||||
|
Some(1),
|
||||||
|
"eleven seconds at full urgency beat a hundred waiting"
|
||||||
|
);
|
||||||
urgent.waited_s = Some(9);
|
urgent.waited_s = Some(9);
|
||||||
urgent.last_turn_ms = Some(1_000_000 - 9000);
|
urgent.last_turn_ms = Some(1_000_000 - 9000);
|
||||||
assert_eq!(pick(&[steady, urgent], 100), Some(0), "nine do not");
|
assert_eq!(pick(&[steady, urgent], 100), Some(0), "nine do not");
|
||||||
|
|
@ -84,7 +100,11 @@ fn urgency_is_the_strongest_of_self_topic_and_a_member_addressing_her() {
|
||||||
row.addressed_by_member = false;
|
row.addressed_by_member = false;
|
||||||
row.topic_permille = 0;
|
row.topic_permille = 0;
|
||||||
row.self_permille = 5000;
|
row.self_permille = 5000;
|
||||||
assert_eq!(urgency_permille(&row), 1000, "an urgency past the scale is the scale");
|
assert_eq!(
|
||||||
|
urgency_permille(&row),
|
||||||
|
1000,
|
||||||
|
"an urgency past the scale is the scale"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -93,14 +113,26 @@ fn a_tie_goes_to_the_oldest_last_turn_then_to_the_slug() {
|
||||||
old.last_turn_ms = Some(10);
|
old.last_turn_ms = Some(10);
|
||||||
let mut new = mind("ann", Some(100));
|
let mut new = mind("ann", Some(100));
|
||||||
new.last_turn_ms = Some(20);
|
new.last_turn_ms = Some(20);
|
||||||
assert_eq!(pick(&[new.clone(), old.clone()], 100), Some(1), "the older last turn wins the tie, whatever the slug");
|
assert_eq!(
|
||||||
|
pick(&[new.clone(), old.clone()], 100),
|
||||||
|
Some(1),
|
||||||
|
"the older last turn wins the tie, whatever the slug"
|
||||||
|
);
|
||||||
new.last_turn_ms = Some(10);
|
new.last_turn_ms = Some(10);
|
||||||
assert_eq!(pick(&[old, new], 100), Some(1), "equal turns: the lower slug");
|
assert_eq!(
|
||||||
|
pick(&[old, new], 100),
|
||||||
|
Some(1),
|
||||||
|
"equal turns: the lower slug"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_mind_that_never_acted_goes_first_and_the_never_acted_tie_by_slug() {
|
fn a_mind_that_never_acted_goes_first_and_the_never_acted_tie_by_slug() {
|
||||||
let rows = [mind("ann", Some(86_400)), mind("cy", None), mind("bob", None)];
|
let rows = [
|
||||||
|
mind("ann", Some(86_400)),
|
||||||
|
mind("cy", None),
|
||||||
|
mind("bob", None),
|
||||||
|
];
|
||||||
assert_eq!(pick(&rows, 900), Some(2));
|
assert_eq!(pick(&rows, 900), Some(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue