expect_error_fixed = function(...) testthat::expect_error(..., fixed = TRUE)
test_that("undefined result biproportional", {
seats = c(10, 20, 1, 1)
set.seed(1284)
vm = matrix(runif(4*10), ncol = 4) * matrix(rep(seats, 10), byrow = TRUE, ncol = 4) * 1000
vm <- round(vm)
vm[vm < 200] <- 0
expect_identical(upper_apportionment(vm, seats, use_list_votes = FALSE)$party,
proporz(rowSums(vm), sum(seats), "round"))
expect_error_fixed(upper_apportionment(vm, seats),
"Result is undefined, equal quotient for parties: 4, 6")
expect_error_fixed(biproporz(uri2020$votes_matrix, uri2020$seats_vector, quorum_any(any_district = 0.7)),
"Result is undefined, equal quotient for parties: 'CVP', 'SPGB', 'FDP', 'SVP'")
vm5 = matrix(c(10, 10, 10, 10), 2, 2)
expect_error_fixed(biproporz(vm5, c(3,1)),
"Result is undefined, tied votes and multiple possible seat assignments")
vm0.5 = matrix(c(1500, 4500), 2)
expect_identical(c(biproporz(vm0.5, 5, method = "round")), c(1L, 4L))
expect_identical(c(biproporz(vm0.5, 7, method = "round")), c(2L, 5L))
# manual fix (actual implementation depends on rules)
vm4 <- vm6 <- vm
vm4[4,1] <- vm4[4,1]+1
vm6[6,4] <- vm6[6,4]+1
ua4 = upper_apportionment(vm4, seats)
ua6 = upper_apportionment(vm6, seats)
expect_identical(ua4$party[4], ua6$party[6])
# fully tied
vdf = data.frame(
party = rep(c("A", "B", "C", "D", "E"), 5),
district = rep(c("d1", "d2", "d3", "d4", "d5"), each = 5L),
votes = rep(c(0.2, 0.5, 0.2, 0.5, 0.2, 0.5, 0.2, 0.5, 0.2),
c(2L, 3L, 2L, 5L, 3L, 2L, 3L, 2L, 3L)))
vdf_seats = data.frame(
district = c("d1", "d2", "d3", "d4", "d5"),
seats = rep(1L, 5L))
expect_error(pukelsheim(vdf, vdf_seats),
"Result is undefined, cannot assign all seats in lower apportionment")
expect_error(
biproporz(matrix(c(50,40,30,20,25,20,15,10), nrow = 4), c(10, 10)),
"Result is undefined, cannot assign all seats in lower apportionment")
expect_is(
biproporz(matrix(c(10,15,10,15,10,10,10,10,15), 3), c(1,1,1)),
"proporz_matrix")
})
test_that("flow criterion check for almost empty matrix", {
vm1 = matrix(c(10,0,0,0, 0,0,10,0, 0,0,0,20, 0,0,0,20), nrow = 4)
expect_identical(biproporz(vm1, 4), biproporz(vm1, rep(1,4)))
vm2 = matrix(0, nrow = 4, ncol = 4)
vm2[1,2] <- 10
expect_identical(sum(biproporz(vm2, 2)), 2L)
expect_error_fixed(biproporz(vm2, c(1,1,0,0)),
"No votes in a district with at least one seat")
expect_error_fixed(
lower_apportionment(
matrix(c(0,0,0,1,0,0,0,0,0),3), c(1,1,1), c(1,0,2)),
"Not enough non-zero votes matrix entries to assign seats in districts: 1, 3")
expect_error_fixed(
lower_apportionment(
matrix(c(1,0,0,1,0,0,0,0,0),3), c(1,1,0), c(1,1,0)),
"Not enough non-zero votes matrix entries to assign seats to party: 2")
expect_error_fixed(
lower_apportionment(
matrix(0, nrow = 4), c(1,1,1,1), c(1,0,1,2),
"Not enough non-zero votes matrix entries to assign seats in districts: 1, 3, 4"))
expect_error_fixed(
biproporz(matrix(c(0, 1, 0, 0, 4, 3, 0, 0, 20), 3), c(4,1,3)),
"Not enough seats for party 3 in districts 2, 3\n(6 seats necessary, 4 available)")
expect_error_fixed(
lower_apportionment(matrix(c(5,0,0,0,15,16), nrow = 3), c(3,1), c(2,1,1)), # almost impossible to trigger with biproporz
"Not enough seats for parties 2, 3 in district 2\n(2 seats necessary, 1 available)")
expect_error_fixed(
lower_apportionment(matrix(c(0, 10, 15, 0, 0, 20, 10, 0, 10, 0, 0, 20), 4),
c(3,1,1), c(2,1,1,1)),
"Not enough seats for parties 1, 4 in district 3\n(3 seats necessary, 1 available)")
expect_error_fixed(
biproporz(matrix(c(1000,10,0,1), 2), c(1,1)),
"Not enough seats for party 1 in district 1\n(2 seats necessary, 1 available)")
vm3a = matrix(c(4,3,0,20,1,0), nrow = 2)
expect_error_fixed(
biproporz(vm3a, c(1,3,4)),
"Not enough seats for party 2 in districts 1, 2\n(6 seats necessary, 4 available)")
vm3b = vm3a[,c(1,3,2)]
rownames(vm3b) <- c("ONE", "TWO")
expect_error_fixed(
biproporz(vm3b, c(1,4,3)),
"Not enough seats for party 'TWO' in districts 1, 3\n(6 seats necessary, 4 available)")
colnames(vm3b) <- c("A", "B", "C")
expect_error_fixed(
biproporz(vm3b, c(A=1,B=4,C=3)),
"Not enough seats for party 'TWO' in districts 'A', 'C'\n(6 seats necessary, 4 available)")
# check in multiple districts
expect_error_fixed(
check_flow_criterion(matrix(c(1,1,1,0,0,0, 1,0,0,1,1,0, 0,0,0,0,0,1), nrow = 6),
c(2,2,2), c(1,1,1,1,1,1)),
"Not enough seats for parties 1, 2, 3, 4, 5 in districts 1, 2\n(5 seats necessary, 4 available)")
vm_blocks1 = matrix(c(12L, 10L, 0L, 0L, 5L, 5L, 0L, 0L, 0L, 0L, 6L, 10L, 0L, 0L, 10L, 5L),
nrow = 4L, ncol = 4L,
dimnames = list(
party = c("A", "B", "C", "D"),
district = c("District 1", "District 2", "District 3", "District 4")))
seats_blocks1 = c(`District 1` = 5L, `District 2` = 5L, `District 3` = 5L, `District 4` = 6L)
expect_error_fixed(biproporz(vm_blocks1, seats_blocks1),
"Not enough seats for parties 'A', 'B' in districts 'District 1', 'District 2'\n(11 seats necessary, 10 available")
vm_blocks2 = vm_blocks1[c(4,3,1,2),c(1,3,2,4)]
dimnames(vm_blocks2) <- list(LETTERS[1:4], as.character(1:4))
seats_blocks2 = setNames(seats_blocks1[c(1,3,2,4)], colnames(vm_blocks2))
expect_error_fixed(biproporz(vm_blocks2, seats_blocks2),
"Not enough seats for parties 'C', 'D' in districts '1', '3'\n(11 seats necessary, 10 available)")
vm_blocks3 = uri2020$votes_matrix
vm_blocks3[1,c(1,2,4)] <- vm_blocks3[4,c(1,2,4)] <- 0
expect_error(biproporz(vm_blocks3, uri2020$seats_vector),
"Not enough seats for parties 'CVP', 'SVP' in district 'Erstfeld'")
# no submatrix error for matrix with diag = 0
vm_diag = matrix(c(0, 20, 100, 20, 0, 20, 100, 100, 0), nrow = 3)
expect_is(biproporz(vm_diag, c(50, 30, 90)), "proporz_matrix")
})
test_that("error messages", {
set.seed(2)
vm = matrix(round(runif(12, 50, 1500)), nrow = 3)
seats = c(10,20,10,9)
# pukelsheim
vdf = pivot_to_df(vm)
colnames(vdf) <- c("party", "district", "votes")
seats_df = data.frame(col = 1:4, seats = seats)
seats_df124 = seats_df[-3,]
vdf134 = vdf[vdf[["district"]] != 2,]
expect_gt(nrow(vdf134), 0)
# unique party ids
vdf_dupl = rbind(vdf, vdf[9:12,])
expect_error_fixed(pukelsheim(vdf_dupl, seats_df),
"There are duplicate party-district pairs in `vdf_dupl`.")
# unique district ids
expect_error_fixed(pukelsheim(vdf_dupl, rbind(seats_df, seats_df)),
"District ids in `rbind(seats_df, seats_df)` are not unique")
# more helpful message if columns are switched
expect_error_fixed(pukelsheim(vdf[,c(2,1,3)], seats_df),
paste0("District ids not found in second column of `vdf[, c(2, 1, 3)]`. ",
"Are columns in the correct order (party, district, votes)?"))
# districts not in vote_districts
expect_error_fixed(pukelsheim(vdf134, seats_df),
paste0("Not all district ids in `seats_df`s first column exist ",
"in `vdf134`s second column"))
# vote_districts not in districts
expect_error_fixed(pukelsheim(vdf, seats_df124),
"Not all district ids in `vdf`s second column exist in `seats_df124`s first column")
# mismatch on both ends
expect_error_fixed(pukelsheim(vdf134, seats_df124),
"Not all district ids in `seats_df124`s first column exist in `vdf134`s second column")
# seats_df is not a data.frame
seats_vec124 = setNames(seats_df124$seats, seats_df124$col)
expect_error_fixed(pukelsheim(vdf134, seats_vec124), "`seats_vec124` must be a data.frame")
# non-numeric
vdf_non_num = vdf
vdf_non_num$votes <- as.character(vdf_non_num$votes)
expect_error_fixed(pukelsheim(vdf_non_num, seats_df),
"Vote values in `vdf_non_num`s third column must be numbers >= 0")
# negative votes
vdf_neg = vdf
vdf_neg$votes <- vdf_neg$votes-500
expect_error_fixed(pukelsheim(vdf_neg, seats_df),
"Vote values in `vdf_neg`s third column must be numbers >= 0")
vm_neg = vm
vm_neg[2:3,2] <- -vm_neg[2:3,2]
expect_error_fixed(biproporz(vm_neg, c(2,3,2,1)), "Votes in `vm_neg` must be numbers >= 0")
vm_char = matrix(as.character(vm), nrow = nrow(vm))
expect_error_fixed(biproporz(vm_char, c(2,3,2,1)), "Votes in `vm_char` must be numbers >= 0")
# biproportional
expect_error_fixed(biproporz(vm, NA), "`NA` must be a numeric vector, data.frame or a single number")
expect_error_fixed(biproporz(vdf, c(1,2,3)), "`vdf` must be a matrix")
expect_error_fixed(biproporz(vm, c(1,2,3)), "`vm` needs to have districts as columns and parties as rows")
expect_error_fixed(biproporz(vm, seats, method = "largest_remainder_method"),
'Cannot use "largest_remainder_method", only divisor methods are possible in biproportional apportionment')
expect_s3_class(biproporz(vm+0.1, seats), "proporz_matrix")
expect_s3_class(biproporz(vm*0.1, seats), "proporz_matrix")
expect_identical(as.matrix(biproporz(vm*0.01, seats)), as.matrix(biproporz(vm*20, seats)))
expect_error_fixed(biproporz(vm, seats+0.1), "`seats + 0.1` must be integers")
expect_error_fixed(biproporz(vm, seats, method = c("round", "floor", "ceiling")),
"Only one or two methods allowed")
expect_error_fixed(biproporz(vm, seats, method = round),
"Method must be a character or a list")
expect_error_fixed(biproporz(vm, vm), "`vm` must be a numeric vector, data.frame or a single number")
# upper/lower_apportionment
ua = upper_apportionment(vm+0.1, seats)
expect_true(is.matrix(lower_apportionment(vm+0.1, ua$district, ua$party)))
expect_error_fixed(lower_apportionment(vm+0.1, seats, 1:3), "sum(seats_cols) == sum(seats_rows")
# votes_matrix
vm_names = matrix(1, 3, 2)
rownames(vm_names) <- c("A", "A", "B")
expect_error_fixed(prep_votes_matrix(vm_names, "x"), "rownames in `x` must be unique")
colnames(vm_names) <- c("I", "I")
expect_error_fixed(prep_votes_matrix(vm_names, "x"), "rownames in `x` must be unique")
rownames(vm_names) <- c("A", "C", "B")
expect_error_fixed(prep_votes_matrix(vm_names, "x"), "colnames in `x` must be unique")
# max iterations
options(proporz_max_iterations = 2)
expect_error_fixed(biproporz(vm, seats), "Result is undefined, exceeded maximum number of iterations (2)")
options(proporz_max_iterations = NULL)
# custom function
expect_error(
lower_apportionment(matrix(c(21,11,33,21), 2), c(2,2), c(2,2), method = function(x) x),
"Rounding function does not return integers")
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.