use parking_lot::Mutex;
use std::sync::LazyLock;
static THREAD_POOL: LazyLock<Mutex<()>> = LazyLock::new(|| {
unsafe { crate::SetMaxThreads(0) };
Mutex::new(())
});
#[allow(clippy::large_types_passed_by_value)]
fn check(
deal: crate::DdTableDeal,
solution: crate::DdTableResults,
pars: [crate::ParResultsMaster; 2],
) {
#[allow(clippy::cast_possible_wrap)]
const SUCCESS: i32 = crate::RETURN_NO_FAULT as i32;
let mut tricks = crate::DdTableResults::default();
let status = unsafe {
let _guard = THREAD_POOL.lock();
crate::CalcDDtable(deal, &raw mut tricks)
};
assert_eq!(status, SUCCESS);
assert_eq!(tricks, solution);
let mut result = [crate::ParResultsMaster::default(); 2];
let status = unsafe { crate::SidesParBin(&raw mut tricks, &raw mut result[0], 0) };
assert_eq!(status, SUCCESS);
assert_eq!(result, pars);
}
const NO_CONTRACT: crate::ContractType = crate::ContractType {
level: 0,
denom: 0,
seats: 0,
under_tricks: 0,
over_tricks: 0,
};
#[test]
fn solve_four_13_card_straight_flushes() {
const MASK: core::ffi::c_uint = ((1 << 13) - 1) << 2;
const DEAL: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[0, 0, 0, MASK], [0, 0, MASK, 0], [0, MASK, 0, 0], [MASK, 0, 0, 0], ],
};
const SOLUTION: crate::DdTableResults = crate::DdTableResults {
res_table: [
[0, 13, 0, 13], [13, 0, 13, 0], [0, 13, 0, 13], [13, 0, 13, 0], [0, 0, 0, 0], ],
};
const CONTRACTS: [crate::ContractType; 10] = [
crate::ContractType {
level: 7,
denom: 1, seats: 5,
under_tricks: 0,
over_tricks: 0,
},
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
];
const NS: crate::ParResultsMaster = crate::ParResultsMaster {
score: -1510,
number: 1,
contracts: CONTRACTS,
};
const EW: crate::ParResultsMaster = crate::ParResultsMaster {
score: 1510,
number: 1,
contracts: CONTRACTS,
};
check(DEAL, SOLUTION, [NS, EW]);
}
#[test]
fn solve_par_5_tricks() {
const AKQJ: core::ffi::c_uint = 0xF << 11;
const T987: core::ffi::c_uint = 0xF << 7;
const XXXX: core::ffi::c_uint = 0xF << 3;
const X: core::ffi::c_uint = 1 << 2;
const DEAL: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[AKQJ, X, XXXX, T987], [XXXX, T987, AKQJ, X], [X, AKQJ, T987, XXXX], [T987, XXXX, X, AKQJ], ],
};
const SOLUTION: crate::DdTableResults = crate::DdTableResults {
res_table: [[5; 4]; 5],
};
const PAR: crate::ParResultsMaster = crate::ParResultsMaster {
score: 0,
number: 1,
contracts: [NO_CONTRACT; 10],
};
check(DEAL, SOLUTION, [PAR; 2]);
}
#[test]
fn solver_context_solve_dd_table() {
const MASK: core::ffi::c_uint = ((1 << 13) - 1) << 2;
const DEAL: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[0, 0, 0, MASK],
[0, 0, MASK, 0],
[0, MASK, 0, 0],
[MASK, 0, 0, 0],
],
};
const SOLUTION: crate::DdTableResults = crate::DdTableResults {
res_table: [
[0, 13, 0, 13],
[13, 0, 13, 0],
[0, 13, 0, 13],
[13, 0, 13, 0],
[0, 0, 0, 0],
],
};
#[allow(clippy::cast_possible_wrap)]
const SUCCESS: i32 = crate::RETURN_NO_FAULT as i32;
let cfg = crate::DdsSolverConfig {
tt_kind: crate::DDS_TT_KIND_LARGE.try_into().unwrap(),
tt_mem_default_mb: 0,
tt_mem_maximum_mb: 0,
};
let deal = DEAL;
let mut tricks = crate::DdTableResults::default();
let status = unsafe {
let _guard = THREAD_POOL.lock();
let ctx = crate::dds_solver_context_new(&raw const cfg);
assert!(!ctx.is_null());
let s = crate::dds_calc_dd_table(ctx, &raw const deal, &raw mut tricks);
crate::dds_solver_context_free(ctx);
s
};
assert_eq!(status, SUCCESS);
assert_eq!(tricks, SOLUTION);
}
#[test]
fn solver_context_batched_matches_serial() {
const MASK: core::ffi::c_uint = ((1 << 13) - 1) << 2;
const FLUSH: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[0, 0, 0, MASK],
[0, 0, MASK, 0],
[0, MASK, 0, 0],
[MASK, 0, 0, 0],
],
};
const FLUSH_ROT: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[0, 0, MASK, 0],
[0, MASK, 0, 0],
[MASK, 0, 0, 0],
[0, 0, 0, MASK],
],
};
const A54: core::ffi::c_uint = 0b10000_0000_1100_00;
const QJ32: core::ffi::c_uint = 0b00110_0000_0011_00;
const K976: core::ffi::c_uint = 0b01000_1011_0000_00;
const T8: core::ffi::c_uint = 0b00001_0100_0000_00;
const SYMMETRIC_1NT: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[A54, QJ32, K976, T8],
[T8, A54, QJ32, K976],
[K976, T8, A54, QJ32],
[QJ32, K976, T8, A54],
],
};
const AKQJ: core::ffi::c_uint = 0xF << 11;
const T987: core::ffi::c_uint = 0xF << 7;
const XXXX: core::ffi::c_uint = 0xF << 3;
const X: core::ffi::c_uint = 1 << 2;
const PAR_ZERO: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[AKQJ, X, XXXX, T987],
[XXXX, T987, AKQJ, X],
[X, AKQJ, T987, XXXX],
[T987, XXXX, X, AKQJ],
],
};
let deals = [FLUSH, FLUSH_ROT, SYMMETRIC_1NT, PAR_ZERO];
let cfg = crate::DdsSolverConfig {
tt_kind: crate::DDS_TT_KIND_LARGE.try_into().unwrap(),
tt_mem_default_mb: 0,
tt_mem_maximum_mb: 0,
};
let mut expected: Vec<crate::DdTableResults> =
vec![crate::DdTableResults::default(); deals.len()];
unsafe {
let _guard = THREAD_POOL.lock();
let ctx = crate::dds_solver_context_new(&raw const cfg);
assert!(!ctx.is_null());
for (i, d) in deals.iter().enumerate() {
let s = crate::dds_calc_dd_table(ctx, &raw const *d, &raw mut expected[i]);
assert_eq!(s, crate::RETURN_NO_FAULT as i32);
}
crate::dds_solver_context_free(ctx);
}
let mut got: Vec<crate::DdTableResults> = vec![crate::DdTableResults::default(); deals.len()];
let status = unsafe {
let _guard = THREAD_POOL.lock();
crate::dds_calc_dd_tables_batched(
i32::try_from(deals.len()).unwrap(),
deals.as_ptr(),
got.as_mut_ptr(),
4,
&raw const cfg,
)
};
assert_eq!(status, crate::RETURN_NO_FAULT as i32);
assert_eq!(got, expected);
}
#[cfg(not(target_os = "windows"))]
#[test]
#[allow(clippy::unusual_byte_groupings)]
fn solver_context_batched_stress_pool_reuse() {
const A54: core::ffi::c_uint = 0b10000_0000_1100_00;
const QJ32: core::ffi::c_uint = 0b00110_0000_0011_00;
const K976: core::ffi::c_uint = 0b01000_1011_0000_00;
const T8: core::ffi::c_uint = 0b00001_0100_0000_00;
const SYMMETRIC_1NT: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[A54, QJ32, K976, T8],
[T8, A54, QJ32, K976],
[K976, T8, A54, QJ32],
[QJ32, K976, T8, A54],
],
};
let cfg = crate::DdsSolverConfig {
tt_kind: crate::DDS_TT_KIND_LARGE.try_into().unwrap(),
tt_mem_default_mb: 0,
tt_mem_maximum_mb: 0,
};
let deal = SYMMETRIC_1NT;
let mut expected = crate::DdTableResults::default();
unsafe {
let _guard = THREAD_POOL.lock();
let ctx = crate::dds_solver_context_new(&raw const cfg);
assert!(!ctx.is_null());
let s = crate::dds_calc_dd_table(ctx, &raw const deal, &raw mut expected);
assert_eq!(s, crate::RETURN_NO_FAULT as i32);
crate::dds_solver_context_free(ctx);
}
let par = std::thread::available_parallelism().map_or(8, |p| p.get());
let n = par * 2;
let deals = vec![deal; n];
for _ in 0..3 {
let mut got = vec![crate::DdTableResults::default(); n];
let status = unsafe {
let _guard = THREAD_POOL.lock();
crate::dds_calc_dd_tables_batched(
i32::try_from(n).unwrap(),
deals.as_ptr(),
got.as_mut_ptr(),
0, &raw const cfg,
)
};
assert_eq!(status, crate::RETURN_NO_FAULT as i32);
for r in &got {
assert_eq!(*r, expected);
}
}
}
#[test]
#[allow(clippy::unusual_byte_groupings)]
fn solve_everyone_makes_1nt() {
const A54: core::ffi::c_uint = 0b10000_0000_1100_00;
const QJ32: core::ffi::c_uint = 0b00110_0000_0011_00;
const K976: core::ffi::c_uint = 0b01000_1011_0000_00;
const T8: core::ffi::c_uint = 0b00001_0100_0000_00;
const DEAL: crate::DdTableDeal = crate::DdTableDeal {
cards: [
[A54, QJ32, K976, T8], [T8, A54, QJ32, K976], [K976, T8, A54, QJ32], [QJ32, K976, T8, A54], ],
};
const SOLUTION: crate::DdTableResults = crate::DdTableResults {
res_table: [[6; 4], [6; 4], [6; 4], [6; 4], [7; 4]],
};
const NS: crate::ParResultsMaster = crate::ParResultsMaster {
score: 90,
number: 1,
contracts: [
crate::ContractType {
level: 1,
denom: 0, seats: 4,
under_tricks: 0,
over_tricks: 0,
},
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
],
};
const EW: crate::ParResultsMaster = crate::ParResultsMaster {
score: 90,
number: 1,
contracts: [
crate::ContractType {
level: 1,
denom: 0, seats: 5,
under_tricks: 0,
over_tricks: 0,
},
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
NO_CONTRACT,
],
};
check(DEAL, SOLUTION, [NS, EW]);
}