forked from yvv/decision
Lot T3b : les tests d'abord, rouges sur le texte de T3 (2ce2028) — la phrase de G de N1.1 vraie pour tout W et tout quorum, sur la grille de « comme d'habitude » (as_usual_grid, partagée avec the_share_of_a_level_as_usual_is_asked_beyond_the_quorum) ; « autant de « pour » » en français ; le test des nombres honnête sur « 100 votants » (un paramètre, 2 × voteFloor ; la participation de référence ne vaut 100 votants qu'entre 200 et 10 000 membres, un test à part)
Vus rouges sur le texte de T3 : every_number_of_the_quorum_texts_is_the_engine_s (les cinq parts de la phrase de G manquent), g_gives_the_share_of_the_level_beyond_the_quorum_when_the_group_votes_as_usual, the_texts_of_lot_t3_change_these_phrases_only, under_the_quorum_it_takes_as_many_for_as_if_the_quorum_had_voted. the_reference_turnout_is_a_parameter_the_bounds_bring_back décrit le moteur : vert d'emblée, et vu tomber quand on lui fait dire « de 100 à 10 000 membres » (W 100). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,12 @@
|
||||
//! only (N1.1, F1.2). A third guarded amendment, which follows lot T2's, writes them on a base
|
||||
//! made as production is after M6c; the texts of T2 are kept in
|
||||
//! `tests/fixtures/seed-quorum-t2.json`. F1.1 needs none: it is not touched.
|
||||
//!
|
||||
//! After the review of lot T3 (2ce2028, lot T3b, before it went online: the same amendment, its
|
||||
//! fingerprint `to` changed): N1.1 says G gives the share of the level beyond the quorum, when the
|
||||
//! group votes as usual — « à la participation habituelle » was false under the quorum; « autant de
|
||||
//! « pour » » in French; and « 100 votants » is checked as the parameter it is, not as a turnout
|
||||
//! that would hold on every W.
|
||||
|
||||
use ld_engine::inertia::{InertiaLevel, Participation, Scale, gradient, quorum_bounds};
|
||||
use ld_engine::threshold::{FormulaParams, Tally, judged_voters, passes, required_ratio, wot_threshold, yes_needed};
|
||||
@@ -122,14 +128,18 @@ fn seed_dir(tag: &str, seed: &Value) -> PathBuf {
|
||||
|
||||
/// Every figure the texts may hold: parameters of the seed and constants of the engine and
|
||||
/// the server. Since lot T3 (Yvv, 03/11: « pas de chiffre datés dans un texte voté »), none
|
||||
/// depends on the web of trust W or on a turnout: no participation goes in, and the same
|
||||
/// figures come out on every W of Monnaie libre (`figures`).
|
||||
/// is computed from the web of trust W or from a turnout: no participation goes in. The one
|
||||
/// the engine computes from W, the lowest quorum an author may set, comes out the same on
|
||||
/// every W of Monnaie libre (`figures`); the others no W enters.
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Figures {
|
||||
/// Q_k of Monnaie libre (the seed).
|
||||
quorums: [u32; 5],
|
||||
/// Twice the reference quorum of the document, in voters (the engine's p0 · W, before
|
||||
/// the bounds).
|
||||
/// « deux fois le quorum de référence du document : 100 votants » — a parameter, 2 ×
|
||||
/// voteFloor, written before the bounds; not a turnout that holds on every W. The engine's
|
||||
/// p0 = 2 × voteFloor / W is that many voters only on a web of 200 to 10 000 members;
|
||||
/// beyond, the bounds N1.1 says after it, in the same paragraph, bring it back
|
||||
/// (`the_reference_turnout_is_a_parameter_the_bounds_bring_back`).
|
||||
reference: u32,
|
||||
/// The lowest quorum an author may set in Monnaie libre.
|
||||
lowest_quorum: u32,
|
||||
@@ -155,8 +165,8 @@ fn figures_at(seed: &Value, w: u32) -> Figures {
|
||||
let ml =
|
||||
seed["instances"].as_array().expect("instances").iter().find(|i| i["group"] == "monnaie-libre").expect("ml");
|
||||
let quorums: [u32; 5] = serde_json::from_value(ml["quorums"].clone()).expect("quorums");
|
||||
let vote_floor = ml["voteFloor"].as_f64().expect("voteFloor");
|
||||
let reference = Participation::reference_ratio(w, vote_floor) * f64::from(w);
|
||||
// Twice the reference quorum: a parameter, the same whatever W (no W enters it).
|
||||
let reference = 2.0 * ml["voteFloor"].as_f64().expect("voteFloor");
|
||||
assert!((reference - reference.round()).abs() < 1e-6, "a whole number of voters: {reference}");
|
||||
let levels: Vec<u8> = seed["units"]
|
||||
.as_array()
|
||||
@@ -185,7 +195,9 @@ fn figures_at(seed: &Value, w: u32) -> Figures {
|
||||
}
|
||||
}
|
||||
|
||||
/// The figures of the texts: the same on every W Monnaie libre may have — none is dated.
|
||||
/// The figures of the texts, on every W Monnaie libre may have: none is dated. Only the lowest
|
||||
/// quorum is computed from W here; the reference turnout is a parameter, and where it is a
|
||||
/// turnout is `the_reference_turnout_is_a_parameter_the_bounds_bring_back`.
|
||||
fn figures(seed: &Value) -> Figures {
|
||||
let mut all = ELECTORATES.iter().map(|w| (*w, figures_at(seed, *w)));
|
||||
let (_, first) = all.next().expect("an electorate");
|
||||
@@ -397,21 +409,28 @@ fn claims(code: &str, lang: &str, f: &Figures, seed: &Value) -> Vec<String> {
|
||||
.collect();
|
||||
let (b0, b1) = (pct(lang, f.bounds.0), pct(lang, f.bounds.1));
|
||||
let (ms, r) = (f.min_sample, f.reference);
|
||||
// s of each level, as the sentence of G names them: the shares of the list, level 1 to 5.
|
||||
let (top, lower) = InertiaLevel::ALL.split_last().expect("five levels");
|
||||
let head = lower.iter().map(|k| dec(lang, k.share_pct())).collect::<Vec<_>>().join(", ");
|
||||
let last = pct(lang, top.share_pct());
|
||||
out.extend(match lang {
|
||||
"fr" => [
|
||||
"propositions du groupe votées depuis un an".to_owned(),
|
||||
format!("Tant que le groupe n'a pas voté sur {ms} propositions, elle vaut deux fois le quorum de référence du document : {r} votants dans Monnaie libre"),
|
||||
format!("Elle reste entre {b0} et {b1} de l'électorat, et se mesure une fois par jour"),
|
||||
format!("où s est cette part du cran ({head} ou {last})"),
|
||||
],
|
||||
"en" => [
|
||||
"the group's proposals voted on over the past year".to_owned(),
|
||||
format!("Until the group has voted on {ms} proposals, it is twice the document's reference quorum: {r} voters in Monnaie libre"),
|
||||
format!("It stays between {b0} and {b1} of the electorate, and is measured once a day"),
|
||||
format!("where s is that share of the level ({head} or {last})"),
|
||||
],
|
||||
_ => [
|
||||
"propuestas del grupo votadas en el último año".to_owned(),
|
||||
format!("Hasta que el grupo haya votado {ms} propuestas, vale dos veces el quórum de referencia del documento: {r} votantes en Monnaie libre"),
|
||||
format!("Se mantiene entre el {b0} y el {b1} del electorado, y se mide una vez al día"),
|
||||
format!("donde s es esa proporción del nivel ({head} o {last})"),
|
||||
],
|
||||
});
|
||||
out
|
||||
@@ -600,9 +619,10 @@ fn the_three_phrases_of_the_review_of_m6b_are_written_true() {
|
||||
// Lot T3: no dated figure, every sentence true for every W and every quorum
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// (article, language, the words of T2, the words of today): the only change of lot T3. F1.1
|
||||
/// has none: it holds no dated figure and every sentence of it is true (`b_to_the_power_w_…`).
|
||||
const PHRASES_T3: [(&str, &str, &str, &str); 12] = [
|
||||
/// (article, language, the words of T2, the words of today): the only change of lot T3, with its
|
||||
/// review (2ce2028: the sentence of G; « autant de « pour » » in French). F1.1 has none: it holds
|
||||
/// no dated figure and every sentence of it is true (`b_to_the_power_w_…`).
|
||||
const PHRASES_T3: [(&str, &str, &str, &str); 15] = [
|
||||
(
|
||||
"F1.2",
|
||||
"fr",
|
||||
@@ -625,7 +645,7 @@ const PHRASES_T3: [(&str, &str, &str, &str); 12] = [
|
||||
"N1.1",
|
||||
"fr",
|
||||
"Chaque cran a aussi son quorum, qui n'est pas un nombre minimal de votants : sous le quorum, les votants qui manquent comptent comme « non ».",
|
||||
"Chaque cran a aussi son quorum : sous le quorum, les votants qui manquent comptent comme « non », et il faut autant de voix pour que si le quorum avait voté.",
|
||||
"Chaque cran a aussi son quorum : sous le quorum, les votants qui manquent comptent comme « non », et il faut autant de « pour » que si le quorum avait voté.",
|
||||
),
|
||||
(
|
||||
"N1.1",
|
||||
@@ -675,6 +695,24 @@ const PHRASES_T3: [(&str, &str, &str, &str); 12] = [
|
||||
"\n\nEjemplo: con 7224 miembros y una participación habitual de 120 votantes, cuando votan 120 personas, hacen falta 72, 81, 90, 100 o 108 votos a favor, del nivel 1 al nivel 5; en el nivel 3, el 75 % de los 120 votantes, es decir 90. Por debajo del quórum, los que faltan cuentan como «no»: en el nivel 2 (quórum 12), 9 votos a favor pasan con 3 en contra como mucho; en el nivel 3 (quórum 50), hacen falta 39 votos a favor, con 11 en contra como mucho.",
|
||||
"",
|
||||
),
|
||||
(
|
||||
"N1.1",
|
||||
"fr",
|
||||
"**G** se calcule pour que la part exigée, à la participation habituelle, soit exactement celle du cran : `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, où s est la part exigée comme d'habitude et p̃ la participation habituelle divisée par W.",
|
||||
"**G** se calcule pour que la part exigée, au-delà du quorum, soit exactement celle du cran quand le groupe vote comme d'habitude : `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, où s est cette part du cran (60, 67, 75, 83 ou 90 %) et p̃ la participation habituelle divisée par W.",
|
||||
),
|
||||
(
|
||||
"N1.1",
|
||||
"en",
|
||||
"**G** is computed so that the share asked, at the usual turnout, is exactly that of the level: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, where s is the share asked as usual and p̃ the usual turnout divided by W.",
|
||||
"**G** is computed so that the share asked, beyond the quorum, is exactly that of the level when the group votes as usual: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, where s is that share of the level (60, 67, 75, 83 or 90%) and p̃ the usual turnout divided by W.",
|
||||
),
|
||||
(
|
||||
"N1.1",
|
||||
"es",
|
||||
"**G** se calcula para que la proporción exigida, con la participación habitual, sea exactamente la del nivel: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, donde s es la proporción exigida como de costumbre y p̃ la participación habitual dividida por W.",
|
||||
"**G** se calcula para que la proporción exigida, más allá del quórum, sea exactamente la del nivel cuando el grupo vota como de costumbre: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, donde s es esa proporción del nivel (60, 67, 75, 83 o 90 %) y p̃ la participación habitual dividida por W.",
|
||||
),
|
||||
];
|
||||
|
||||
/// The texts of T2 with the phrases of lot T3 written in: what the seed gives today.
|
||||
@@ -717,7 +755,8 @@ fn participations(w: u32, vote_floor: f64) -> [f64; 4] {
|
||||
}
|
||||
|
||||
/// N1.1, the phrase of lot T3: « sous le quorum, les votants qui manquent comptent comme « non »,
|
||||
/// et il faut autant de voix pour que si le quorum avait voté ». True for every quorum an author
|
||||
/// et il faut autant de « pour » que si le quorum avait voté » (after the review of 2ce2028: « de
|
||||
/// voix pour que » read first as « pour que », a conjunction). True for every quorum an author
|
||||
/// may set (2 to W in Monnaie libre, 1 to W in the test group), every level, every W, every
|
||||
/// participation within the bounds: the « pour » that pass with nobody against, fewer voters
|
||||
/// than the quorum, are the threshold of the quorum; and under it, a tally passes exactly when
|
||||
@@ -727,7 +766,7 @@ fn participations(w: u32, vote_floor: f64) -> [f64; 4] {
|
||||
fn under_the_quorum_it_takes_as_many_for_as_if_the_quorum_had_voted() {
|
||||
let today = today_seed();
|
||||
let said = [
|
||||
("fr", "et il faut autant de voix pour que si le quorum avait voté."),
|
||||
("fr", "et il faut autant de « pour » que si le quorum avait voté."),
|
||||
("en", "and it takes as many votes in favour as if the quorum had voted."),
|
||||
("es", "y hacen falta tantos votos a favor como si hubiera votado el quórum."),
|
||||
];
|
||||
@@ -791,6 +830,49 @@ fn under_the_quorum_it_takes_as_many_for_as_if_the_quorum_had_voted() {
|
||||
assert!(minimum.iter().any(|m| m.0 && m.3 == 1));
|
||||
}
|
||||
|
||||
/// One case of the grid of « comme d'habitude »: a web, the usual turnout of its group (in voters,
|
||||
/// and as p̃), a level and a quorum.
|
||||
struct AsUsual {
|
||||
w: u32,
|
||||
usual: u32,
|
||||
p: f64,
|
||||
k: InertiaLevel,
|
||||
q: u32,
|
||||
}
|
||||
|
||||
impl AsUsual {
|
||||
fn at(&self) -> String {
|
||||
format!("W {} usual {} k {} Q {}", self.w, self.usual, self.k.get(), self.q)
|
||||
}
|
||||
}
|
||||
|
||||
/// The grid of « comme d'habitude » (lot T3 and its review): every W of the grid, usual turnouts
|
||||
/// within the bounds (a median out of them is brought back, and is then not the group's usual
|
||||
/// turnout), every level, quorums from 2 to W, among them the usual turnout and one more.
|
||||
fn as_usual_grid() -> Vec<AsUsual> {
|
||||
let mut grid = Vec::new();
|
||||
for w in ELECTORATES {
|
||||
for usual in [w / 100 + 1, 12, 40, 50, 100, 120, 1000, w / 2] {
|
||||
let part = Participation::of(w, 50.0, Some(f64::from(usual)), 9);
|
||||
if part.bounded {
|
||||
continue;
|
||||
}
|
||||
for k in InertiaLevel::ALL {
|
||||
for q in [2, 12, 50, 100, 1000, usual, usual + 1, w].into_iter().filter(|q| (2..=w).contains(q)) {
|
||||
grid.push(AsUsual { w, usual, p: part.ratio, k, q });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
grid
|
||||
}
|
||||
|
||||
/// The letters of the formula for a case of the grid (the quorums of the scale do not enter: the
|
||||
/// quorum is the case's).
|
||||
fn as_usual_params(c: &AsUsual) -> FormulaParams {
|
||||
Scale { electorate: c.w, participation: c.p, quorums: [12, 12, 50, 50, 50] }.params(c.k, c.q)
|
||||
}
|
||||
|
||||
/// N1.1 and F1.2, lot T3: the share of a level « comme d'habitude » is asked beyond the quorum
|
||||
/// only. When the usual turnout reaches the quorum, the group voting as usual is asked s_k; when
|
||||
/// it does not (a web of fewer than 5 000 members and a median under 50 at a quorum of 50, an
|
||||
@@ -799,31 +881,19 @@ fn under_the_quorum_it_takes_as_many_for_as_if_the_quorum_had_voted() {
|
||||
#[test]
|
||||
fn the_share_of_a_level_as_usual_is_asked_beyond_the_quorum() {
|
||||
let mut under = 0;
|
||||
for w in ELECTORATES {
|
||||
let medians = [w / 100 + 1, 12, 40, 50, 100, 120, 1000, w / 2];
|
||||
for usual in medians {
|
||||
let part = Participation::of(w, 50.0, Some(f64::from(usual)), 9);
|
||||
if part.bounded {
|
||||
continue;
|
||||
}
|
||||
let scale = Scale { electorate: w, participation: part.ratio, quorums: [12, 12, 50, 50, 50] };
|
||||
for k in InertiaLevel::ALL {
|
||||
let (s, m) = (k.share_pct() / 100.0, k.majority_pct() / 100.0);
|
||||
for q in [2, 12, 50, 100, 1000, usual, usual + 1, w].into_iter().filter(|q| (2..=w).contains(q)) {
|
||||
let params = scale.params(k, q);
|
||||
let at = format!("W {w} usual {usual} k {} Q {q}", k.get());
|
||||
let asked = required_ratio(w, judged_voters(usual, q), ¶ms);
|
||||
if usual >= q {
|
||||
assert!((asked - s).abs() < 1e-9, "{at}: {asked}, not s");
|
||||
} else {
|
||||
assert!(asked < s - 1e-9, "{at}: {asked}");
|
||||
under += 1;
|
||||
}
|
||||
// Everyone votes: M, at every quorum.
|
||||
assert!((required_ratio(w, judged_voters(w, q), ¶ms) - m).abs() < 1e-12, "{at}: M");
|
||||
}
|
||||
}
|
||||
for c in as_usual_grid() {
|
||||
let (s, m) = (c.k.share_pct() / 100.0, c.k.majority_pct() / 100.0);
|
||||
let params = as_usual_params(&c);
|
||||
let at = c.at();
|
||||
let asked = required_ratio(c.w, judged_voters(c.usual, c.q), ¶ms);
|
||||
if c.usual >= c.q {
|
||||
assert!((asked - s).abs() < 1e-9, "{at}: {asked}, not s");
|
||||
} else {
|
||||
assert!(asked < s - 1e-9, "{at}: {asked}");
|
||||
under += 1;
|
||||
}
|
||||
// Everyone votes: M, at every quorum.
|
||||
assert!((required_ratio(c.w, judged_voters(c.w, c.q), ¶ms) - m).abs() < 1e-12, "{at}: M");
|
||||
}
|
||||
assert!(under > 0);
|
||||
// The case of F1.2, its default level and quorum: W 3 000, a usual turnout of 40 voters
|
||||
@@ -842,6 +912,106 @@ fn the_share_of_a_level_as_usual_is_asked_beyond_the_quorum() {
|
||||
assert_eq!(wot_threshold(w, usual, &p), Ok(38), "but 40 voters are judged as 50");
|
||||
}
|
||||
|
||||
/// N1.1, the sentence of G, after the review of 2ce2028. Lot T3 left « G se calcule pour que la
|
||||
/// part exigée, à la participation habituelle, soit exactement celle du cran […], où s est la part
|
||||
/// exigée comme d'habitude »: false where the usual turnout is under the quorum, the group voting
|
||||
/// as usual being judged on the quorum (T' = Q), lower on the curve than s, and asked of more
|
||||
/// voters than came. Written true for every W and every quorum: G is computed so that the share
|
||||
/// asked, beyond the quorum, is exactly that of the level when the group votes as usual; s is that
|
||||
/// share of the level, the one of the list (60, 67, 75, 83 or 90 %) — never « la part exigée »,
|
||||
/// which the formula above it names and which moves with T'. On the grid of « comme d'habitude »,
|
||||
/// with G as the text writes it.
|
||||
#[test]
|
||||
fn g_gives_the_share_of_the_level_beyond_the_quorum_when_the_group_votes_as_usual() {
|
||||
let today = today_seed();
|
||||
// The sentence of G closes N1.1, in each language; nothing of the sentence of T3 is left.
|
||||
for lang in LANGS {
|
||||
let (_, body) = text(&today, "N1.1", lang);
|
||||
let (_, _, before, now) = PHRASES_T3
|
||||
.iter()
|
||||
.find(|(c, l, _, new)| (*c, *l) == ("N1.1", lang) && new.starts_with("**G**"))
|
||||
.expect("the sentence of G");
|
||||
assert!(body.ends_with(now), "N1.1 {lang}: the sentence of G\n{body}");
|
||||
let (said_before, _) = before.split_once(" = ").expect("the formula");
|
||||
assert!(!body.contains(said_before), "N1.1 {lang}: the sentence of T3 is left\n{body}");
|
||||
let beyond = named(lang, "au-delà du quorum", "beyond the quorum", "más allá del quórum");
|
||||
let (said_now, _) = now.split_once(" = ").expect("the formula");
|
||||
assert!(said_now.contains(beyond), "N1.1 {lang}: « {beyond} »");
|
||||
}
|
||||
// By the engine, with G as the text writes it (s, the share of the level; M; p̃). On the curve,
|
||||
// the share at the usual turnout is s, whatever the quorum: that is what G is computed for.
|
||||
// The share asked of the group voting as usual is s beyond the quorum; under it, the share of
|
||||
// the quorum, lower — the sentence of T3 said s there.
|
||||
let grad = code_spans(text(&today, "N1.1", "fr").1)[1];
|
||||
let (mut beyond, mut under) = (0, 0);
|
||||
for c in as_usual_grid() {
|
||||
let (s, m) = (c.k.share_pct() / 100.0, c.k.majority_pct() / 100.0);
|
||||
let env: HashMap<String, f64> = HashMap::from([("s".into(), s), ("M".into(), m), ("p̃".into(), c.p)]);
|
||||
let engine = as_usual_params(&c);
|
||||
let params = FormulaParams { gradient: eval(grad, "G", &env), ..engine };
|
||||
let at = c.at();
|
||||
assert!(close(params.gradient, engine.gradient), "{at}: G");
|
||||
assert!((required_ratio(c.w, c.usual, ¶ms) - s).abs() < 1e-9, "{at}: s on the curve");
|
||||
let judged = judged_voters(c.usual, c.q);
|
||||
let asked = required_ratio(c.w, judged, ¶ms);
|
||||
if c.usual >= c.q {
|
||||
assert_eq!(judged, c.usual, "{at}");
|
||||
assert!((asked - s).abs() < 1e-9, "{at}: {asked}, not s");
|
||||
beyond += 1;
|
||||
} else {
|
||||
assert_eq!(judged, c.q, "{at}");
|
||||
assert!(asked < s - 1e-9, "{at}: {asked}");
|
||||
under += 1;
|
||||
}
|
||||
}
|
||||
assert!(beyond > 0 && under > 0, "beyond {beyond}, under {under}");
|
||||
// The case of the review: W 3 000, a usual turnout of 40 voters, level 3, quorum 50. On the
|
||||
// curve, 75 % at 40 voters; the share asked is that of 50 voters, 74,23 %; 38 « pour » of the
|
||||
// 40 who came, 95 %.
|
||||
let (w, usual) = (3000, 40);
|
||||
let c = AsUsual { w, usual, p: Participation::of(w, 50.0, Some(f64::from(usual)), 9).ratio, k: level(3), q: 50 };
|
||||
let p = as_usual_params(&c);
|
||||
assert!((required_ratio(w, usual, &p) - 0.75).abs() < 1e-12);
|
||||
let asked = required_ratio(w, judged_voters(usual, c.q), &p);
|
||||
assert_eq!((asked * 10_000.0).round() / 100.0, 74.23);
|
||||
let needed = wot_threshold(w, usual, &p).expect("valid");
|
||||
assert_eq!((needed, f64::from(needed) / f64::from(usual)), (38, 0.95));
|
||||
}
|
||||
|
||||
/// N1.1, « Tant que le groupe n'a pas voté sur 5 propositions, elle vaut deux fois le quorum de
|
||||
/// référence du document : 100 votants dans Monnaie libre. […] Elle reste entre 1 % et 50 % de
|
||||
/// l'électorat » (review of 2ce2028). 100 is a parameter, 2 × voteFloor, which the number test
|
||||
/// checks as such (`figures`); it is not a turnout that holds on every W: the engine's p0 is 100
|
||||
/// voters on a web of 200 to 10 000 members only (1 % and 50 % of W); beyond, the bounds the
|
||||
/// paragraph says after it bring it back. The voted text is not changed for this.
|
||||
#[test]
|
||||
fn the_reference_turnout_is_a_parameter_the_bounds_bring_back() {
|
||||
let seed = today_seed();
|
||||
let f = figures(&seed);
|
||||
let vote_floor = f64::from(f.reference) / 2.0;
|
||||
for w in ELECTORATES.into_iter().chain([199, 200, 10_000, 10_001]) {
|
||||
let p0 = Participation::of(w, vote_floor, None, 0);
|
||||
let within = (200..=10_000).contains(&w);
|
||||
assert_eq!(p0.bounded, !within, "W {w}");
|
||||
if within {
|
||||
assert!((p0.voters(w) - f64::from(f.reference)).abs() < 1e-9, "W {w}: {} voters", p0.voters(w));
|
||||
} else {
|
||||
let bound = if w < 200 { Participation::HIGHEST } else { Participation::LOWEST };
|
||||
assert_eq!(p0.ratio, bound, "W {w}");
|
||||
}
|
||||
}
|
||||
// The text says the parameter first, then the bounds, in each language.
|
||||
for (lang, reference, bounds) in [
|
||||
("fr", format!("{} votants dans Monnaie libre.", f.reference), "Elle reste entre"),
|
||||
("en", format!("{} voters in Monnaie libre.", f.reference), "It stays between"),
|
||||
("es", format!("{} votantes en Monnaie libre.", f.reference), "Se mantiene entre"),
|
||||
] {
|
||||
let (_, body) = text(&seed, "N1.1", lang);
|
||||
let at = |s: &str| body.find(s).unwrap_or_else(|| panic!("N1.1 {lang}: « {s} »"));
|
||||
assert!(at(&reference) < at(bounds), "N1.1 {lang}: the bounds after the reference");
|
||||
}
|
||||
}
|
||||
|
||||
/// F1.1, « B : la base ; à la puissance W, elle est si petite qu'elle ne compte pas », and F1.2,
|
||||
/// « dans la toile de confiance, `B^W` est si petit qu'il ne compte pas »: true on every W of the
|
||||
/// web of trust (`wot::PLAUSIBLE`, 100 to 10 000 000) — the engine's threshold is the one
|
||||
|
||||
Reference in New Issue
Block a user