use num_bigint::BigInt;
use num_traits::One;
use oxiz_math::fast_rational::FastRational;
use oxiz_math::rational::{
divisor_count, divisor_sum, euler_totient, factorize, is_prime, is_square_free, mobius,
};
use oxiz_math::simplex::{BoundType, Row, SimplexResult, SimplexTableau};
fn next_prime_after(start: u64) -> BigInt {
let mut candidate = BigInt::from(start) + BigInt::one();
loop {
if is_prime(&candidate, 30) {
return candidate;
}
candidate += BigInt::one();
}
}
#[test]
fn is_square_free_false_for_square_of_prime_beyond_trial_division_limit() {
let p = next_prime_after(100_000);
let n = &p * &p;
assert!(
!is_square_free(&n),
"p^2 for prime p={p} beyond the old trial-division limit must not be square-free"
);
}
#[test]
fn is_square_free_true_for_product_of_two_distinct_large_primes() {
let p = next_prime_after(100_000);
let q = next_prime_after(200_000);
let n = &p * &q;
assert!(is_square_free(&n));
}
#[test]
fn divisor_count_sum_mobius_correct_for_semiprime_with_both_factors_above_limit() {
let p = next_prime_after(1_000_000);
let q = next_prime_after(2_000_000);
assert_ne!(p, q);
let n = &p * &q;
assert_eq!(divisor_count(&n), BigInt::from(4));
let expected_sum = BigInt::one() + &p + &q + &n;
assert_eq!(divisor_sum(&n), expected_sum);
assert_eq!(mobius(&n), 1);
}
#[test]
fn mobius_zero_for_square_of_large_prime() {
let p = next_prime_after(500_000);
let n = &p * &p;
assert_eq!(mobius(&n), 0);
}
#[test]
fn euler_totient_terminates_and_is_correct_for_a_large_prime() {
let p = next_prime_after(50_000_000);
let phi = euler_totient(&p);
assert_eq!(phi, &p - BigInt::one());
}
#[test]
fn euler_totient_correct_for_semiprime_beyond_limit() {
let p = next_prime_after(100_000);
let q = next_prime_after(300_000);
let n = &p * &q;
let expected = (&p - BigInt::one()) * (&q - BigInt::one());
assert_eq!(euler_totient(&n), expected);
}
#[test]
fn factorize_public_api_reconstructs_n_and_reports_errors_honestly() {
let p = next_prime_after(10_000);
let q = next_prime_after(20_000);
let n = &p * &p * &q;
let factors = factorize(&n).expect("factorize should succeed for an ordinary semiprime-ish n");
let product: BigInt = factors.iter().product();
assert_eq!(product, n);
assert_eq!(factors.len(), 3);
assert_eq!(
factorize(&BigInt::one()).expect("1 factors to the empty list"),
Vec::<BigInt>::new()
);
}
#[test]
fn fast_rational_mul_i64_min_matches_bigint_arithmetic() {
let a = FastRational::from(i64::MIN);
let b = FastRational::from((1i64, 7i64));
let result = a * b;
let expected = num_rational::BigRational::new(BigInt::from(i64::MIN), BigInt::from(7));
assert_eq!(result.to_big_rational(), expected);
}
#[test]
fn simplex_detects_infeasibility_from_non_basic_bound_violation() {
let mut tableau = SimplexTableau::new();
let x = tableau.fresh_var();
let y = tableau.fresh_var();
let mut row = Row::new(y);
row.coeffs.insert(x, num_bigint::BigInt::one().into());
tableau.add_row(row).expect("row should be added");
tableau
.add_bound(x, BoundType::Lower, num_bigint::BigInt::from(5).into(), 0)
.expect("x >= 5 is not itself a conflict");
let add_upper = tableau.add_bound(y, BoundType::Upper, num_bigint::BigInt::from(3).into(), 1);
let is_unsat = match add_upper {
Err(_) => true,
Ok(()) => tableau.check().is_err(),
};
assert!(
is_unsat,
"system (y = x, x >= 5, y <= 3) is infeasible and must never be reported Sat"
);
let mut tableau2 = SimplexTableau::new();
let x2 = tableau2.fresh_var();
let y2 = tableau2.fresh_var();
let mut row2 = Row::new(y2);
row2.coeffs.insert(x2, num_bigint::BigInt::one().into());
tableau2.add_row(row2).expect("row should be added");
tableau2
.add_bound(
y2,
BoundType::Upper,
num_bigint::BigInt::from(100).into(),
0,
)
.expect("test operation should succeed");
tableau2
.add_bound(x2, BoundType::Lower, num_bigint::BigInt::from(5).into(), 1)
.expect("test operation should succeed");
assert_eq!(
tableau2.check().expect("should be feasible"),
SimplexResult::Sat
);
assert!(tableau2.is_feasible());
}