// Copyright © 2024 Mikhail Hogrefe // // This file is part of Malachite. // // Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU // Lesser General Public License (LGPL) as published by the Free Software Foundation; either version // 3 of the License, or (at your option) any later version. See . use malachite_base::num::arithmetic::kronecker_symbol::{ jacobi_symbol_unsigned_double_fast_2, jacobi_symbol_unsigned_simple, }; use malachite_base::num::arithmetic::traits::{ModPowerOf2, UnsignedAbs}; use malachite_base::num::basic::integers::PrimitiveInt; use malachite_base::num::basic::signeds::PrimitiveSigned; use malachite_base::num::basic::unsigneds::PrimitiveUnsigned; use malachite_base::num::comparison::traits::PartialOrdAbs; use malachite_base::num::conversion::traits::{HasHalf, JoinHalves, WrappingFrom}; use malachite_base::test_util::generators::{ signed_gen, signed_gen_var_13, signed_pair_gen, signed_pair_gen_var_10, signed_pair_gen_var_8, signed_pair_gen_var_9, signed_triple_gen, signed_triple_gen_var_6, signed_triple_gen_var_7, unsigned_gen, unsigned_gen_var_22, unsigned_pair_gen_var_27, unsigned_pair_gen_var_40, unsigned_pair_gen_var_41, unsigned_pair_gen_var_42, unsigned_quadruple_gen_var_12, unsigned_triple_gen_var_19, unsigned_triple_gen_var_22, unsigned_triple_gen_var_23, }; use malachite_base::test_util::num::arithmetic::kronecker_symbol::{ jacobi_symbol_unsigned_double_fast_1, jacobi_symbol_unsigned_double_simple, jacobi_symbol_unsigned_fast_1, jacobi_symbol_unsigned_fast_2_1, jacobi_symbol_unsigned_fast_2_2, jacobi_symbol_unsigned_fast_2_3, jacobi_symbol_unsigned_fast_2_4, }; use std::panic::catch_unwind; #[test] fn test_jacobi_symbol() { fn test_u(a: T, n: T, s: i8) { assert_eq!(a.legendre_symbol(n), s); assert_eq!(a.jacobi_symbol(n), s); assert_eq!(a.kronecker_symbol(n), s); assert_eq!(jacobi_symbol_unsigned_simple(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_1(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_1(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_2(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_3(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_4(a, n), s); } test_u::(0, 1, 1); test_u::(0, 3, 0); test_u::(1, 3, 1); test_u::(2, 3, -1); test_u::(0, 5, 0); test_u::(1, 5, 1); test_u::(2, 5, -1); test_u::(3, 5, -1); test_u::(4, 5, 1); test_u::(0, 7, 0); test_u::(1, 7, 1); test_u::(2, 7, 1); test_u::(3, 7, -1); test_u::(4, 7, 1); test_u::(5, 7, -1); test_u::(6, 7, -1); test_u::(0, 9, 0); test_u::(1, 9, 1); test_u::(2, 9, 1); test_u::(3, 9, 0); test_u::(4, 9, 1); test_u::(5, 9, 1); test_u::(6, 9, 0); test_u::(7, 9, 1); test_u::(8, 9, 1); test_u::(7, 7, 0); test_u::(8, 7, 1); test_u::(9, 7, 1); test_u::(10, 7, -1); test_u::(11, 7, 1); test_u::(12, 7, -1); test_u::(13, 7, -1); test_u::(9, 9, 0); test_u::(10, 9, 1); test_u::(11, 9, 1); test_u::(12, 9, 0); test_u::(13, 9, 1); test_u::(14, 9, 1); test_u::(15, 9, 0); test_u::(16, 9, 1); test_u::(17, 9, 1); test_u::(1001, 9907, -1); test_u::(10908, 9907, -1); fn test_s + PrimitiveSigned>( a: S, n: S, s: i8, ) { assert_eq!(a.legendre_symbol(n), s); assert_eq!(a.jacobi_symbol(n), s); assert_eq!(a.kronecker_symbol(n), s); } test_s::(0, 1, 1); test_s::(0, 3, 0); test_s::(1, 3, 1); test_s::(2, 3, -1); test_s::(0, 5, 0); test_s::(1, 5, 1); test_s::(2, 5, -1); test_s::(3, 5, -1); test_s::(4, 5, 1); test_s::(0, 7, 0); test_s::(1, 7, 1); test_s::(2, 7, 1); test_s::(3, 7, -1); test_s::(4, 7, 1); test_s::(5, 7, -1); test_s::(6, 7, -1); test_s::(0, 9, 0); test_s::(1, 9, 1); test_s::(2, 9, 1); test_s::(3, 9, 0); test_s::(4, 9, 1); test_s::(5, 9, 1); test_s::(6, 9, 0); test_s::(7, 9, 1); test_s::(8, 9, 1); test_s::(7, 7, 0); test_s::(8, 7, 1); test_s::(9, 7, 1); test_s::(10, 7, -1); test_s::(11, 7, 1); test_s::(12, 7, -1); test_s::(13, 7, -1); test_s::(9, 9, 0); test_s::(10, 9, 1); test_s::(11, 9, 1); test_s::(12, 9, 0); test_s::(13, 9, 1); test_s::(14, 9, 1); test_s::(15, 9, 0); test_s::(16, 9, 1); test_s::(17, 9, 1); test_s::(-7, 7, 0); test_s::(-6, 7, 1); test_s::(-5, 7, 1); test_s::(-4, 7, -1); test_s::(-3, 7, 1); test_s::(-2, 7, -1); test_s::(-1, 7, -1); test_s::(-9, 9, 0); test_s::(-8, 9, 1); test_s::(-7, 9, 1); test_s::(-6, 9, 0); test_s::(-5, 9, 1); test_s::(-4, 9, 1); test_s::(-3, 9, 0); test_s::(-2, 9, 1); test_s::(-1, 9, 1); test_s::(1001, 9907, -1); test_s::(10908, 9907, -1); test_s::(-8906, 9907, -1); } fn jacobi_symbol_fail_helper() { assert_panic!(T::ONE.jacobi_symbol(T::TWO)); } fn jacobi_symbol_fail_helper_signed() { assert_panic!(T::ONE.jacobi_symbol(T::NEGATIVE_ONE)); } #[test] fn jacobi_symbol_fail() { apply_fn_to_primitive_ints!(jacobi_symbol_fail_helper); apply_fn_to_signeds!(jacobi_symbol_fail_helper_signed); } #[test] fn test_jacobi_symbol_unsigned_double() { fn test + JoinHalves + PrimitiveUnsigned>( x_1: T, x_0: T, y_1: T, y_0: T, s: i8, ) { assert_eq!( jacobi_symbol_unsigned_double_simple::(x_1, x_0, y_1, y_0), s ); assert_eq!(jacobi_symbol_unsigned_double_fast_1(x_1, x_0, y_1, y_0), s); assert_eq!(jacobi_symbol_unsigned_double_fast_2(x_1, x_0, y_1, y_0), s); } // - fast_1: y_1 == T::ZERO || y_0 == T::ZERO first time // - fast_2: y_1 == T::ZERO && y_0 == T::ONE test::(0, 0, 0, 1, 1); // - fast_1: y_1 != T::ZERO && y_0 != T::ZERO first time // - fast_1: x_1 != T::ZERO && x_0 != T::ZERO // - fast_1: x_0 != T::ZERO first time // - fast_1: c != T::WIDTH first time // - fast_1: diff_0 != T::ZERO && diff_1 != T::ZERO // - fast_1: diff_1.get_highest_bit() // - fast_1: y_1 != T::ZERO && y_0 != T::ZERO second time // - fast_1: x_0 == T::ZERO second time // - fast_1: c != T::WIDTH second time // - fast_1: bit.even() // - fast_2: y_1 != T::ZERO || y_0 != T::ONE // - fast_2: x_0 != T::ZERO first time // - fast_2: x_0.odd() // - fast_2: x_1 == T::ZERO second time // - fast_2: y_1 != T::ZERO // - fast_2: skip_loop // - fast_2: y_0 != T::ONE second time // - fast_2: x_0 == T::ZERO fourth time // - fast_2: x_1 != T::ZERO fourth time // - fast_2: !bit.get_bit(1) test::(0, 3, 2, 3, 1); // - fast_1: x_1 == T::ZERO || x_0 == T::ZERO // - fast_2: x_0 == T::ZERO first time // - fast_2: x_1 == T::ZERO first time test::(0, 0, 0, 3, 0); // - fast_1: t != T::ZERO // - fast_1: c == T::WIDTH third time // - fast_2: y_0 == T::ONE second time test::(0, 1, 1, 1, 1); // - fast_1: c != T::WIDTH third time test::(0, 1, 1, 3, 1); // - fast_1: x_0 == T::ZERO first time // - fast_2: x_1 != T::ZERO first time // - fast_2: y_0 == T::ONE first time test::(1, 0, 0, 3, 1); // - fast_1: bit.odd() // - fast_2: x_1 != T::ZERO second time // - fast_2: !skip_loop // - fast_2: x_0 != T::ZERO fourth time // - fast_2: bit.get_bit(1) test::(1, 1, 0, 3, -1); // - fast_1: t == T::ZERO // - fast_2: x_1 == y_1 first time // - fast_2: x_1 == y_1 second time // - fast_2: x_0 >= y_0 // - fast_2: x_0 == T::ZERO third time test::(1, 1, 1, 1, 0); // - fast_1: y_1 == T::ZERO || y_0 == T::ZERO second time test::(0, 1, 2, 1, 1); // - fast_1: x_0 != T::ZERO second time // - fast_1: c == T::WIDTH second time // - fast_2: x_1 != y_1 first time // - fast_2: x_1 != T::ZERO third time // - fast_2: y_0 == T::ZERO test::(1, 1, 2, 1, 1); // - fast_1: !diff_1.get_highest_bit() test::(2, 1, 0, 3, 0); // - fast_1: diff_0 == T::ZERO || diff_1 == T::ZERO test::(2, 1, 2, 1, 0); // - fast_1: c == T::WIDTH first time // - fast_2: x_0.even() // - fast_2: y_0 != T::ZERO test::(242, 128, 173, 173, -1); // - fast_2: y_1 == T::ZERO test::(0, 1, 0, 3, 1); // - fast_2: x_0 < y_0 // - fast_2: x_0 != T::ZERO third time // - fast_2: x_0 == T::ONE test::(1, 1, 1, 3, 1); // - fast_2: x_0 != T::ONE test::(1, 1, 1, 7, -1); // - fast_2: x_1 != y_1 second time test::(1, 1, 2, 3, 1); // - fast_2: x_0 == T::ZERO second time test::(2, 1, 1, 1, 1); // - fast_2: x_0 != T::ZERO second time // - fast_2: x_1 == T::ZERO third time test::(2, 1, 1, 3, -1); // - fast_2: y_0 != T::ONE first time test::(3, 0, 0, 3, 0); } // Odd n is already tested in test_jacobi_symbol, so here we just test even n #[test] fn test_kronecker_symbol() { fn test_u(a: T, n: T, s: i8) { assert_eq!(a.kronecker_symbol(n), s); } test_u::(0, 2, 0); test_u::(1, 2, 1); test_u::(2, 2, 0); test_u::(3, 2, -1); test_u::(4, 2, 0); test_u::(5, 2, -1); test_u::(6, 2, 0); test_u::(7, 2, 1); test_u::(0, 4, 0); test_u::(1, 4, 1); test_u::(2, 4, 0); test_u::(3, 4, 1); test_u::(0, 6, 0); test_u::(1, 6, 1); test_u::(2, 6, 0); test_u::(3, 6, 0); test_u::(4, 6, 0); test_u::(5, 6, 1); test_u::(6, 6, 0); test_u::(7, 6, 1); test_u::(8, 6, 0); test_u::(9, 6, 0); test_u::(10, 6, 0); test_u::(11, 6, 1); test_u::(12, 6, 0); test_u::(13, 6, -1); test_u::(14, 6, 0); test_u::(15, 6, 0); test_u::(16, 6, 0); test_u::(17, 6, -1); test_u::(18, 6, 0); test_u::(19, 6, -1); test_u::(20, 6, 0); test_u::(21, 6, 0); test_u::(22, 6, 0); test_u::(23, 6, -1); test_u::(1001, 9908, -1); test_u::(10909, 9908, -1); fn test_s + PrimitiveSigned>( a: S, n: S, s: i8, ) { assert_eq!(a.kronecker_symbol(n), s); } test_s::(0, 2, 0); test_s::(1, 2, 1); test_s::(2, 2, 0); test_s::(3, 2, -1); test_s::(4, 2, 0); test_s::(5, 2, -1); test_s::(6, 2, 0); test_s::(7, 2, 1); test_s::(0, 4, 0); test_s::(1, 4, 1); test_s::(2, 4, 0); test_s::(3, 4, 1); test_s::(0, 6, 0); test_s::(1, 6, 1); test_s::(2, 6, 0); test_s::(3, 6, 0); test_s::(4, 6, 0); test_s::(5, 6, 1); test_s::(6, 6, 0); test_s::(7, 6, 1); test_s::(8, 6, 0); test_s::(9, 6, 0); test_s::(10, 6, 0); test_s::(11, 6, 1); test_s::(12, 6, 0); test_s::(13, 6, -1); test_s::(14, 6, 0); test_s::(15, 6, 0); test_s::(16, 6, 0); test_s::(17, 6, -1); test_s::(18, 6, 0); test_s::(19, 6, -1); test_s::(20, 6, 0); test_s::(21, 6, 0); test_s::(22, 6, 0); test_s::(23, 6, -1); test_s::(-1, 2, 1); test_s::(-2, 2, 0); test_s::(-3, 2, -1); test_s::(-4, 2, 0); test_s::(-5, 2, -1); test_s::(-6, 2, 0); test_s::(-7, 2, 1); test_s::(-1, 4, 1); test_s::(-2, 4, 0); test_s::(-3, 4, 1); test_s::(-1, 6, -1); test_s::(-2, 6, 0); test_s::(-3, 6, 0); test_s::(-4, 6, 0); test_s::(-5, 6, -1); test_s::(-6, 6, 0); test_s::(-7, 6, -1); test_s::(-8, 6, 0); test_s::(-9, 6, 0); test_s::(-10, 6, 0); test_s::(-11, 6, -1); test_s::(-12, 6, 0); test_s::(-13, 6, 1); test_s::(-14, 6, 0); test_s::(-15, 6, 0); test_s::(-16, 6, 0); test_s::(-17, 6, 1); test_s::(-18, 6, 0); test_s::(-19, 6, 1); test_s::(-20, 6, 0); test_s::(-21, 6, 0); test_s::(-22, 6, 0); test_s::(-23, 6, 1); test_s::(0, -2, 0); test_s::(1, -2, 1); test_s::(2, -2, 0); test_s::(3, -2, -1); test_s::(4, -2, 0); test_s::(5, -2, -1); test_s::(6, -2, 0); test_s::(7, -2, 1); test_s::(0, -4, 0); test_s::(1, -4, 1); test_s::(2, -4, 0); test_s::(3, -4, 1); test_s::(0, -6, 0); test_s::(1, -6, 1); test_s::(2, -6, 0); test_s::(3, -6, 0); test_s::(4, -6, 0); test_s::(5, -6, 1); test_s::(6, -6, 0); test_s::(7, -6, 1); test_s::(8, -6, 0); test_s::(9, -6, 0); test_s::(10, -6, 0); test_s::(11, -6, 1); test_s::(12, -6, 0); test_s::(13, -6, -1); test_s::(14, -6, 0); test_s::(15, -6, 0); test_s::(16, -6, 0); test_s::(17, -6, -1); test_s::(18, -6, 0); test_s::(19, -6, -1); test_s::(20, -6, 0); test_s::(21, -6, 0); test_s::(22, -6, 0); test_s::(23, -6, -1); test_s::(-1, -2, -1); test_s::(-2, -2, 0); test_s::(-3, -2, 1); test_s::(-4, -2, 0); test_s::(-5, -2, 1); test_s::(-6, -2, 0); test_s::(-7, -2, -1); test_s::(-1, -4, -1); test_s::(-2, -4, 0); test_s::(-3, -4, -1); test_s::(-1, -6, 1); test_s::(-2, -6, 0); test_s::(-3, -6, 0); test_s::(-4, -6, 0); test_s::(-5, -6, 1); test_s::(-6, -6, 0); test_s::(-7, -6, 1); test_s::(-8, -6, 0); test_s::(-9, -6, 0); test_s::(-10, -6, 0); test_s::(-11, -6, 1); test_s::(-12, -6, 0); test_s::(-13, -6, -1); test_s::(-14, -6, 0); test_s::(-15, -6, 0); test_s::(-16, -6, 0); test_s::(-17, -6, -1); test_s::(-18, -6, 0); test_s::(-19, -6, -1); test_s::(-20, -6, 0); test_s::(-21, -6, 0); test_s::(-22, -6, 0); test_s::(-23, -6, -1); test_s::(1001, -9908, -1); test_s::(10909, -9908, -1); test_s::(-8907, -9908, 1); } fn jacobi_symbol_properties_helper_unsigned() { unsigned_pair_gen_var_40::().test_properties(|(a, n)| { let s = a.jacobi_symbol(n); assert_eq!(a.legendre_symbol(n), s); assert_eq!(a.kronecker_symbol(n), s); assert_eq!(jacobi_symbol_unsigned_simple(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_1(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_1(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_2(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_3(a, n), s); assert_eq!(jacobi_symbol_unsigned_fast_2_4(a, n), s); assert!(s.le_abs(&1i8)); if let Some(b) = a.checked_add(n) { assert_eq!(b.jacobi_symbol(n), s); } if let Some(b) = a.checked_sub(n) { assert_eq!(b.jacobi_symbol(n), s); } assert_eq!(s != 0, a.coprime_with(n)); if let Some(b) = a.checked_mul(T::TWO) { let n_mod_8: u8 = n.mod_power_of_2(3).wrapping_into(); assert_eq!( b.jacobi_symbol(n), if n_mod_8 == 1 || n_mod_8 == 7 { s } else { -s } ); } }); unsigned_pair_gen_var_41::().test_properties(|(m, n)| { let n_mod_4: u8 = n.mod_power_of_2(2).wrapping_into(); let m_mod_4: u8 = m.mod_power_of_2(2).wrapping_into(); assert_eq!( m.jacobi_symbol(n) * n.jacobi_symbol(m), if n_mod_4 == 1 || m_mod_4 == 1 { 1 } else { -1 } ); }); unsigned_triple_gen_var_22::().test_properties(|(a, b, n)| { if let Some(c) = a.checked_mul(b) { assert_eq!(c.jacobi_symbol(n), a.jacobi_symbol(n) * b.jacobi_symbol(n)); } }); unsigned_triple_gen_var_23::().test_properties(|(a, m, n)| { if let Some(o) = m.checked_mul(n) { assert_eq!(a.jacobi_symbol(o), a.jacobi_symbol(m) * a.jacobi_symbol(n)); } }); unsigned_gen_var_22::().test_properties(|n| { if n != T::ONE { assert_eq!(T::ZERO.jacobi_symbol(n), 0); assert_eq!(n.jacobi_symbol(n), 0); } assert_eq!(T::ONE.jacobi_symbol(n), 1); assert_eq!(n.jacobi_symbol(T::ONE), 1); let n_mod_4: u8 = n.mod_power_of_2(2).wrapping_into(); assert_eq!( (n - T::ONE).jacobi_symbol(n), if n_mod_4 == 1 { 1 } else { -1 } ); let n_mod_8: u8 = n.mod_power_of_2(3).wrapping_into(); assert_eq!( T::TWO.jacobi_symbol(n), if n_mod_8 == 1 || n_mod_8 == 7 { 1 } else { -1 } ); }); } fn jacobi_symbol_properties_double_helper_unsigned< T: PrimitiveUnsigned, D: HasHalf + JoinHalves + PrimitiveUnsigned, >() { unsigned_quadruple_gen_var_12::().test_properties(|(x_1, x_0, y_1, y_0)| { let s = jacobi_symbol_unsigned_double_simple::(x_1, x_0, y_1, y_0); assert_eq!(jacobi_symbol_unsigned_double_fast_1(x_1, x_0, y_1, y_0), s); assert_eq!(jacobi_symbol_unsigned_double_fast_2(x_1, x_0, y_1, y_0), s); }); } fn jacobi_symbol_properties_helper_signed< U: PrimitiveUnsigned + WrappingFrom, S: ModPowerOf2 + PrimitiveSigned + UnsignedAbs + WrappingFrom, >() { signed_pair_gen_var_8::().test_properties(|(a, n)| { let s = a.jacobi_symbol(n); assert_eq!(a.legendre_symbol(n), s); assert_eq!(a.kronecker_symbol(n), s); assert!(s.le_abs(&1i8)); if let Some(b) = a.checked_add(n) { assert_eq!(b.jacobi_symbol(n), s); } if let Some(b) = a.checked_sub(n) { assert_eq!(b.jacobi_symbol(n), s); } assert_eq!(s != 0, a.unsigned_abs().coprime_with(n.unsigned_abs())); if let Some(b) = a.checked_mul(S::TWO) { let n_mod_8: u8 = n.mod_power_of_2(3).wrapping_into(); assert_eq!( b.jacobi_symbol(n), if n_mod_8 == 1 || n_mod_8 == 7 { s } else { -s } ); } if let Some(b) = a.checked_neg() { let n_mod_4: u8 = n.mod_power_of_2(2).wrapping_into(); assert_eq!(b.jacobi_symbol(n), if n_mod_4 == 1 { s } else { -s }); } }); signed_pair_gen_var_9::().test_properties(|(m, n)| { let n_mod_4: u8 = n.mod_power_of_2(2).wrapping_into(); let m_mod_4: u8 = m.mod_power_of_2(2).wrapping_into(); assert_eq!( m.jacobi_symbol(n) * n.jacobi_symbol(m), if n_mod_4 == 1 || m_mod_4 == 1 { 1 } else { -1 } ); }); signed_triple_gen_var_6::().test_properties(|(a, b, n)| { if let Some(c) = a.checked_mul(b) { assert_eq!(c.jacobi_symbol(n), a.jacobi_symbol(n) * b.jacobi_symbol(n)); } }); signed_triple_gen_var_7::().test_properties(|(a, m, n)| { if let Some(o) = m.checked_mul(n) { assert_eq!(a.jacobi_symbol(o), a.jacobi_symbol(m) * a.jacobi_symbol(n)); } }); signed_gen_var_13::().test_properties(|n| { if n != S::ONE { assert_eq!(S::ZERO.jacobi_symbol(n), 0); assert_eq!(n.jacobi_symbol(n), 0); } assert_eq!(S::ONE.jacobi_symbol(n), 1); assert_eq!(n.jacobi_symbol(S::ONE), 1); let n_mod_4: u8 = n.mod_power_of_2(2).wrapping_into(); assert_eq!( S::NEGATIVE_ONE.jacobi_symbol(n), if n_mod_4 == 1 { 1 } else { -1 } ); let n_mod_8: u8 = n.mod_power_of_2(3).wrapping_into(); assert_eq!( S::TWO.jacobi_symbol(n), if n_mod_8 == 1 || n_mod_8 == 7 { 1 } else { -1 } ); }); } #[test] fn jacobi_symbol_properties() { apply_fn_to_unsigneds!(jacobi_symbol_properties_helper_unsigned); jacobi_symbol_properties_double_helper_unsigned::(); jacobi_symbol_properties_double_helper_unsigned::(); jacobi_symbol_properties_double_helper_unsigned::(); jacobi_symbol_properties_double_helper_unsigned::(); apply_fn_to_unsigned_signed_pairs!(jacobi_symbol_properties_helper_signed); } fn kronecker_symbol_properties_helper_unsigned() { unsigned_pair_gen_var_27::().test_properties(|(a, n)| { let s = a.kronecker_symbol(n); assert!(s.le_abs(&1i8)); assert_eq!(s != 0, a.coprime_with(n)); let n_mod_4: u8 = n.mod_power_of_2(2).wrapping_into(); if n_mod_4 == 2 { if let Some(four_n) = n.checked_mul(T::from(4u8)) { if let Some(b) = a.checked_add(four_n) { assert_eq!(b.kronecker_symbol(n), s); } if let Some(b) = a.checked_sub(four_n) { assert_eq!(b.kronecker_symbol(n), s); } } } else { if let Some(b) = a.checked_add(n) { assert_eq!(b.kronecker_symbol(n), s); } if let Some(b) = a.checked_sub(n) { assert_eq!(b.kronecker_symbol(n), s); } } let a_mod_4: u8 = a.mod_power_of_2(2).wrapping_into(); if a != T::ZERO && a_mod_4 != 3 { if a_mod_4 == 2 { if let Some(four_a) = a.checked_mul(T::from(4u8)) { if let Some(m) = n.checked_add(four_a) { assert_eq!(a.kronecker_symbol(m), s); } if let Some(m) = n.checked_sub(four_a) { assert_eq!(a.kronecker_symbol(m), s); } } } else { if let Some(m) = n.checked_add(a) { assert_eq!(a.kronecker_symbol(m), s); } if let Some(m) = n.checked_sub(a) { assert_eq!(a.kronecker_symbol(m), s); } } } }); unsigned_triple_gen_var_19::().test_properties(|(x, y, z)| { if let Some(p) = x.checked_mul(y) { assert_eq!( p.kronecker_symbol(z), x.kronecker_symbol(z) * y.kronecker_symbol(z) ); } if let Some(p) = y.checked_mul(z) { assert_eq!( x.kronecker_symbol(p), x.kronecker_symbol(y) * x.kronecker_symbol(z) ); } }); unsigned_pair_gen_var_42::().test_properties(|(m, n)| { let n_odd = if n == T::ZERO { T::ONE } else { n >> n.trailing_zeros() }; let m_odd = if m == T::ZERO { T::ONE } else { m >> m.trailing_zeros() }; let n_odd_mod_4: u8 = n_odd.mod_power_of_2(2).wrapping_into(); let m_odd_mod_4: u8 = m_odd.mod_power_of_2(2).wrapping_into(); let p = if n_odd_mod_4 == 1 || m_odd_mod_4 == 1 { 1 } else { -1 }; assert_eq!(m.kronecker_symbol(n) * n.kronecker_symbol(m), p); }); unsigned_gen().test_properties(|n| { if n != T::ONE { assert_eq!(T::ZERO.kronecker_symbol(n), 0); assert_eq!(n.kronecker_symbol(n), 0); } assert_eq!(T::ONE.kronecker_symbol(n), 1); assert_eq!(n.kronecker_symbol(T::ONE), 1); }); } fn kronecker_symbol_properties_helper_signed< U: PrimitiveUnsigned + WrappingFrom, S: ModPowerOf2 + PrimitiveSigned + UnsignedAbs + WrappingFrom, >() { signed_pair_gen::().test_properties(|(a, n)| { let s = a.kronecker_symbol(n); assert!(s.le_abs(&1i8)); assert_eq!(s != 0, a.unsigned_abs().coprime_with(n.unsigned_abs())); let n_mod_4: u8 = n.mod_power_of_2(2).wrapping_into(); if n_mod_4 == 2 { if let Some(four_n) = n.checked_mul(S::from(4i8)) { if let Some(b) = a.checked_add(four_n) { if n > S::ZERO || a.sign() == b.sign() { assert_eq!(b.kronecker_symbol(n), s); } } if let Some(b) = a.checked_sub(four_n) { if n > S::ZERO || a.sign() == b.sign() { assert_eq!(b.kronecker_symbol(n), s); } } } } else { if let Some(b) = a.checked_add(n) { if n > S::ZERO || a.sign() == b.sign() { assert_eq!(b.kronecker_symbol(n), s); } } if let Some(b) = a.checked_sub(n) { if n > S::ZERO || a.sign() == b.sign() { assert_eq!(b.kronecker_symbol(n), s); } } } let a_mod_4: u8 = a.mod_power_of_2(2).wrapping_into(); if a != S::ZERO && a_mod_4 != 3 { if let Some(abs_a) = a.checked_abs() { if a_mod_4 == 2 { if let Some(four_abs_a) = abs_a.checked_mul(S::from(4i8)) { if let Some(m) = n.checked_add(four_abs_a) { assert_eq!(a.kronecker_symbol(m), s); } if let Some(m) = n.checked_sub(four_abs_a) { assert_eq!(a.kronecker_symbol(m), s); } } } else { if let Some(m) = n.checked_add(abs_a) { assert_eq!(a.kronecker_symbol(m), s); } if let Some(m) = n.checked_sub(abs_a) { assert_eq!(a.kronecker_symbol(m), s); } } } } let m = a; if let Some(m_abs) = m.checked_abs() { let m_odd = if m == S::ZERO { S::ONE } else { m >> m.trailing_zeros() }; let m_odd_mod_4: u8 = m_odd.mod_power_of_2(2).wrapping_into(); // -m won't overflow since m.checked_abs() is Some let m_star = if m_odd_mod_4 == 1 { m } else { -m }; assert_eq!(m_star.kronecker_symbol(n), n.kronecker_symbol(m_abs)); } }); signed_triple_gen::().test_properties(|(x, y, z)| { if !(z == S::NEGATIVE_ONE && (x == S::ZERO && y < S::ZERO || x < S::ZERO && y == S::ZERO)) { if let Some(p) = x.checked_mul(y) { assert_eq!( p.kronecker_symbol(z), x.kronecker_symbol(z) * y.kronecker_symbol(z) ); } } let y_odd_mod_4: u8 = if y == S::ZERO { 0 } else { (y >> y.trailing_zeros()).mod_power_of_2(2).wrapping_into() }; let z_odd_mod_4: u8 = if z == S::ZERO { 0 } else { (z >> z.trailing_zeros()).mod_power_of_2(2).wrapping_into() }; if !(x == S::NEGATIVE_ONE && (y == S::ZERO && z_odd_mod_4 == 3 || y_odd_mod_4 == 3 && z == S::ZERO)) { if let Some(p) = y.checked_mul(z) { assert_eq!( x.kronecker_symbol(p), x.kronecker_symbol(y) * x.kronecker_symbol(z) ); } } }); signed_pair_gen_var_10::().test_properties(|(m, n)| { let n_odd = if n == S::ZERO { S::ONE } else { n >> n.trailing_zeros() }; let m_odd = if m == S::ZERO { S::ONE } else { m >> m.trailing_zeros() }; let n_odd_mod_4: u8 = n_odd.mod_power_of_2(2).wrapping_into(); let m_odd_mod_4: u8 = m_odd.mod_power_of_2(2).wrapping_into(); let p = if n_odd_mod_4 == 1 || m_odd_mod_4 == 1 { 1 } else { -1 }; assert_eq!( m.kronecker_symbol(n) * n.kronecker_symbol(m), if m < S::ZERO && n < S::ZERO { -p } else { p } ); if let Some(m_abs) = m.checked_abs() { assert_eq!(m.kronecker_symbol(n) * n.kronecker_symbol(m_abs), p); } }); signed_gen().test_properties(|n| { if n != S::ONE && n != S::NEGATIVE_ONE { assert_eq!(S::ZERO.kronecker_symbol(n), 0); assert_eq!(n.kronecker_symbol(n), 0); } assert_eq!(S::ONE.kronecker_symbol(n), 1); assert_eq!(n.kronecker_symbol(S::ONE), 1); let n_odd = if n == S::ZERO { S::ONE } else { n >> n.trailing_zeros() }; let n_odd_mod_4: u8 = n_odd.mod_power_of_2(2).wrapping_into(); assert_eq!( S::NEGATIVE_ONE.kronecker_symbol(n), if n_odd_mod_4 == 1 { 1 } else { -1 } ); assert_eq!( n.kronecker_symbol(S::NEGATIVE_ONE), if n >= S::ZERO { 1 } else { -1 } ); }); } #[test] fn kronecker_symbol_properties() { apply_fn_to_unsigneds!(kronecker_symbol_properties_helper_unsigned); apply_fn_to_unsigned_signed_pairs!(kronecker_symbol_properties_helper_signed); }