├── src ├── data.rs ├── primitive.rs ├── arithmetic.rs ├── lib.rs ├── nterror ├── arithmetic │ ├── sign.rs │ ├── sliceops.rs │ ├── conversion.rs │ ├── inlineops.rs │ ├── muldiv.rs │ ├── sixteenbytes.rs │ ├── mpz_arith.rs │ └── mpz_prime.rs ├── sieve.rs ├── data │ ├── hashtable.rs │ └── primes.rs ├── primitive │ ├── sieve.rs │ ├── pointer.rs │ ├── montprim.rs │ ├── factorprim.rs │ ├── promotion.rs │ └── signednt.rs ├── structs.rs ├── result.rs └── montgomery.rs ├── benches ├── legendre.rs ├── rand.rs ├── spanish ├── primality.rs └── titanic_prime.rs ├── Cargo.lock ├── tests ├── simple_gcd.rs ├── simple_root.rs ├── README.md ├── simple_residue.rs ├── strong_arith.rs ├── strong_ent.rs ├── simple_arith.rs ├── simple_ent.rs └── strong_prime.rs ├── LICENSE ├── verification ├── README.md ├── test └── hash ├── Cargo.toml ├── examples ├── prime_proof.rs └── rsa.rs └── README.md /src/data.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod primes; 2 | -------------------------------------------------------------------------------- /benches/legendre.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Legendre function 3 | */ 4 | -------------------------------------------------------------------------------- /src/primitive.rs: -------------------------------------------------------------------------------- 1 | mod fourbytes; 2 | mod eightbytes; 3 | mod sixteenbytes; 4 | mod montprim; 5 | mod signednt; 6 | mod promotion; 7 | pub(crate) mod factorprim; 8 | 9 | mod sieve; 10 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "number-theory" 7 | version = "0.0.9" 8 | -------------------------------------------------------------------------------- /src/arithmetic.rs: -------------------------------------------------------------------------------- 1 | mod conversion; 2 | pub(crate) mod inlineops; 3 | mod muldiv; 4 | mod sliceops; 5 | 6 | pub(crate) mod mpz; 7 | mod mpz_ent; 8 | pub mod sign; 9 | mod mpz_arith; 10 | mod mpz_prime; 11 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod ntrait; 2 | pub mod primitive; 3 | pub mod data; 4 | pub mod result; 5 | pub mod structs; 6 | mod arithmetic; 7 | 8 | pub use ntrait::NumberTheory; 9 | pub use result::NTResult; 10 | pub use crate::arithmetic::{mpz::Mpz,sign::Sign}; 11 | -------------------------------------------------------------------------------- /src/nterror: -------------------------------------------------------------------------------- 1 | mr psp 8,9,13 2 | 3 | mr 6 4 | 5 | jordan totient 10;20 6 | 7 | dedekind psi 7,3 8 | 9 | prime_gen distribution for 64n bit primes 10 | 11 | Carmichael function 12 | zero -> Infinite 13 | one -> 1 14 | 15 | implement mul-inverse for exp_residue in mpz 16 | implement checks for 1 & 0 in all exp_residue functions 17 | 18 | zero errors{ 19 | pi 20 | radical 21 | loiuville 22 | mobius} 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/simple_gcd.rs: -------------------------------------------------------------------------------- 1 | use number_theory::{Mpz,NumberTheory}; 2 | 3 | #[test] 4 | fn gcds() { 5 | assert_eq!(35u8.gcd(63), 7); 6 | assert_eq!(35u16.gcd(63), 7); 7 | assert_eq!(35u32.gcd(63), 7); 8 | assert_eq!(35u64.gcd(63), 7); 9 | assert_eq!(35u128.gcd(63), 7); 10 | assert_eq!( 11 | Mpz::from(35u64) 12 | .gcd(Mpz::from(63u64)), 13 | Mpz::from(7u64) 14 | ); 15 | 16 | let (gcd, bz1, bz2) = Mpz::from(36u64).extended_gcd(Mpz::from(81u64)); 17 | assert_eq!( 18 | bz1.ref_product(&Mpz::from(36u64)) 19 | .ref_addition(&bz2.ref_product(&Mpz::from(81u64))), 20 | gcd 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /tests/simple_root.rs: -------------------------------------------------------------------------------- 1 | use number_theory::NumberTheory; 2 | 3 | #[ignore] 4 | #[test] 5 | fn sq_rt() { 6 | assert_eq!(0u8.sqrt().0, 0); // All n < 256 truncate to u8 7 | assert_eq!(1u8.sqrt().0, 1); // 8 | 9 | for i in 0..16u8 { 10 | let sqr = i * i; 11 | let rt = sqr.sqrt().0; 12 | assert_eq!(rt, i); 13 | assert_eq!(rt * rt, sqr); 14 | } 15 | 16 | for i in 16u16..256u16 { 17 | let sqr = i * i; 18 | let rt = sqr.sqrt().0; 19 | assert_eq!(rt, i); 20 | assert_eq!(rt * rt, sqr); 21 | } 22 | 23 | for i in 256u32..65536u32 { 24 | let sqr = i * i; 25 | let rt = sqr.sqrt().0; 26 | assert_eq!(rt, i); 27 | assert_eq!(rt * rt, sqr); 28 | } 29 | 30 | //assert_eq! 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Rust-CAS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /verification/README.md: -------------------------------------------------------------------------------- 1 | 2 | As ENT prioritizes correctness it follows a strict protocol. 3 | 4 | First we define a battery of tests that verify that the functions behave as described, the arguments may be statistical or proofs by exhaustion. 5 | These tests are described in /verification/test and some are implemented in the libraries tests. However, due to the complexity and strength of the tests 6 | (handling gigabytes of data as in the case of checking that is_prime almost certainly does have a accuracy in excess of 2^-64) most are not. 7 | 8 | Iff (if and only if) the tests are all passed then we compute a pair of hashes for each source code file using Open-SSL's implementation of SHA-512-256 9 | and SHA-3-256 and write it into /verification/hash. This ensures that users can verify if the sourcecode does infact pass the strong tests by computing 10 | the hash themselves and comparing it. Note that if a hash fails it does not necessarily mean that the sourcecode is broken, simply that it is not tested. 11 | 12 | As the tests have not been fully decided on, this protocol is not being strictly followed. Once ENT reaches 0.1.0 however this will be enforced for every 13 | update. 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO 2 | # 3 | # When uploading crates to the registry Cargo will automatically 4 | # "normalize" Cargo.toml files for maximal compatibility 5 | # with all versions of Cargo and also rewrite `path` dependencies 6 | # to registry (e.g., crates.io) dependencies. 7 | # 8 | # If you are reading this file be aware that the original Cargo.toml 9 | # will likely look very different (and much more reasonable). 10 | # See Cargo.toml.orig for the original contents. 11 | 12 | [package] 13 | edition = "2021" 14 | name = "number-theory" 15 | version = "0.0.24" 16 | authors = ["J.A Sory jasory@rust-cas.org"] 17 | description = "Fast primality, factorization and elementary number theory for integer types" 18 | homepage = "https://rust-cas.org" 19 | readme = "README.md" 20 | keywords = [ 21 | "primes", 22 | "factor", 23 | "number-theory", 24 | "bignum", 25 | ] 26 | categories = [ 27 | "algorithms", 28 | "mathematics", 29 | ] 30 | license = "MIT" 31 | repository = "https://github.com/JASory/ENT" 32 | 33 | [profile.test] 34 | opt-level = 2 35 | 36 | [[bench]] 37 | name = "rand" 38 | harness = false 39 | 40 | [[bench]] 41 | name = "primality" 42 | harness = false 43 | 44 | [[bench]] 45 | name = "titanic_prime" 46 | harness = false 47 | 48 | [dependencies] 49 | machine-prime = "1.2.2" 50 | [features] 51 | parallel = [] 52 | -------------------------------------------------------------------------------- /src/arithmetic/sign.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Enum representing the sign 3 | #[derive(PartialEq, Clone,Copy, Debug, Default)] 4 | pub enum Sign { 5 | /// N >= 0 representation 6 | #[default] 7 | Positive, 8 | /// N < 0 representation 9 | Negative, 10 | } 11 | 12 | impl Sign { 13 | pub(crate) fn neg(&self) -> Sign { 14 | match self { 15 | Sign::Positive => Sign::Negative, 16 | Sign::Negative => Sign::Positive, 17 | } 18 | } 19 | 20 | pub(crate) fn mul(&self, other: &Sign) -> Sign { 21 | match (self, other) { 22 | (&Sign::Positive, &Sign::Negative) => Sign::Negative, 23 | (&Sign::Negative, &Sign::Positive) => Sign::Negative, 24 | _ => Sign::Positive, 25 | } 26 | } 27 | 28 | pub(crate) fn _pow(&self, y: u64) -> Sign { 29 | if self == &Sign::Negative && y % 2 == 1 { 30 | Sign::Negative 31 | } else { 32 | Sign::Positive 33 | } 34 | } 35 | 36 | /// To boolean for FFI 37 | pub fn ffi(&self) -> bool{ 38 | match self{ 39 | Sign::Negative => true, 40 | Sign::Positive => false, 41 | } 42 | } 43 | /// From boolean FFI 44 | pub fn from_ffi(x: bool) -> Self{ 45 | match x{ 46 | true => Sign::Negative, 47 | false => Sign::Positive, 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/prime_proof.rs: -------------------------------------------------------------------------------- 1 | use number_theory::NumberTheory; 2 | 3 | /* 4 | 5 | Demonstrating how to verify primes from the prime_proof function 6 | 7 | The prime_proof function returns a claim, a witness and the factorization of N-1 (also known as a Pratt certificate) 8 | 9 | Verification requires that prime_proof's claim (the first boolean value) equals the verifier result 10 | 11 | */ 12 | 13 | // Input : an integer N and a Pratt Primality Certificate 14 | 15 | fn verify(n: u64, p: Vec) -> bool { 16 | let witness = p[0]; 17 | 18 | if witness.exp_residue(&(n - 1), &n) != 1 { 19 | // check a^n-1 mod n 20 | return false; 21 | } 22 | 23 | for i in 1..p.len() { 24 | let test = witness.exp_residue(&((n - 1) / p[i]), &n); // check a^(n-1)/p mod n if it equals 1 then the witness verifies that it is composite 25 | if test == 1 { 26 | return false; 27 | } 28 | } 29 | return true; 30 | } 31 | 32 | fn main() { 33 | let p = u64::rng(); 34 | let (claim, certificate) = p.prime_proof(); 35 | 36 | println!("\n \"{} is prime\" is a {} statement \n", p, claim); 37 | 38 | println!( 39 | "We have a witness {} and the factors of {}-1 are as follows {:?} \n ", 40 | certificate[0], 41 | p, 42 | &certificate[1..] 43 | ); 44 | 45 | println!("By checking the cerificate we can see that the statement \"{} is prime \" is a {} statement and that the certificate is {} \n ", 46 | p, verify(p,certificate.clone()), verify(p,certificate)==claim) 47 | } 48 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | It has been observed that the standard checks that are performed in most software are only sufficient for the most trivial errors, 2 | and in the field of mathematics detecting unknown errors involves much more statistical analysis than provided by quick edge case checks 3 | 4 | As such number-theory (ENT) has two sets of checks trivial ones that are labeled "simple" and more analytic ones labeled "strong" that are 5 | vastly superior at detecting errors than the simple ones. Some of the strong checks may rigourously prove a function is correct, but more likely they give a statistical argument for it. It is the end goal of this library to be able to run checks that rigourously prove all functions to be correct. 6 | 7 | Note that these are not the extent of the checks performed by the author but rather the checks builtin for the user, the author has performed far stronger checks that 8 | are considered to take too long except for the most fastidious user (aka t > 1E+5 s) 9 | 10 | {For the purposes of this library. "proven to be correct", means behaves exactly as described in the documentation} 11 | 12 | List of functions fully proven to be correct via strong tests in this crate 13 | 14 | is_prime Proven to be deterministic under 2^64+2^42 and have an accuracy of atleast 2^-64 via Damgard et.al and heuristic check of generated pseudoprimes 15 | 16 | 17 | 18 | 19 | 20 | 21 | Proven over intervals 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Broken functions 33 | 34 | logarithm(s) Fails to account for negative integers, which require a complex result, resolution can either be to panic or evaluate 35 | 36 | 37 | -------------------------------------------------------------------------------- /verification/test: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | 5 | Battery of tests 6 | 7 | If numbertheory software passes all these tests then we can produce the hashes 8 | 9 | Note that these tests are still in development so the policy of "correct software being verfied by hash" is not applicable 10 | 11 | */ 12 | 13 | 14 | 15 | Claim Tests Automated Argument 16 | ________________________________________________________________________________________________________________________________________________ 17 | 18 | RNG Evenly distributed random number 19 | 20 | is_prime Correct under 2^64 + 2^45, Enumerate 0;2^35 Yes Proof 21 | 2^-64 error rate for greater values Check 2-SPRPs up to 2^64+2^45 Yes Proof 22 | Check (k+1)(nk+1) where n < 65535 23 | and the product is less than 2^67 is filtered with 24 | no greater than 4 counterexamples Yes Statistical 25 | Enumerate 2^64;2^64+2^40 No Proof 26 | 27 | is_sprp Correct for all intervals 28 | 29 | jacobi Correct for all intervals 30 | 31 | legendre Correct for all intervals 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/sieve.rs: -------------------------------------------------------------------------------- 1 | pub(crate) fn erasto_sieve(sup: usize) -> Vec { 2 | let ndxlmt = (sup - 3) / 2 + 1; 3 | let bfsz = ((sup - 3) / 2) / 32 + 1; 4 | 5 | let mut cmpsts = vec![0u32; bfsz]; 6 | let sqrtndxlmt = ((sup as f64).sqrt() as usize - 3) / 2 + 1; 7 | 8 | for ndx in 0..sqrtndxlmt { 9 | if (cmpsts[ndx >> 5] & (1u32 << (ndx & 31))) == 0 { 10 | let p = ndx + ndx + 3; 11 | 12 | let mut cullpos = (p * p - 3) / 2; 13 | while cullpos < ndxlmt { 14 | unsafe { 15 | let cptr = cmpsts.get_unchecked_mut(cullpos >> 5); 16 | *cptr |= 1u32 << (cullpos & 31); 17 | } 18 | cullpos += p; 19 | } 20 | } 21 | } 22 | 23 | cmpsts 24 | } 25 | 26 | pub(crate) fn prime_count(sup: usize, data: &[u32]) -> u32 { 27 | if sup < 2 { 28 | return 0; 29 | } 30 | let mut corrector = 0u32; 31 | if sup & 1 == 1 { 32 | corrector = 1; 33 | } 34 | ((sup as u64 / 64u64) * 32u64 + (sup as u64 % 64) / 2 35 | - data.iter().map(|&x| x.count_ones() as u64).sum::()) as u32 36 | + corrector 37 | } 38 | 39 | pub(crate) fn prime_list_32(inf: usize, sup: usize, data: &[u32]) -> Vec { 40 | if sup < 2 { 41 | return vec![]; 42 | } 43 | let mut primes = vec![2]; 44 | let ndxlmt = (sup - 3) / 2 + 1; 45 | let lo: isize = (inf as isize - 3) / 2 + 1; // lo = -1; 46 | let temp = (lo..ndxlmt as isize) 47 | .filter_map(move |i| { 48 | if i < 0 { 49 | Some(2) 50 | } else if data[i as usize >> 5] & (1u32 << (i & 31)) == 0 { 51 | Some((i + i + 3) as u32) 52 | } else { 53 | None 54 | } 55 | }) 56 | .collect::>(); 57 | primes.extend_from_slice(&temp[..]); 58 | primes 59 | } 60 | -------------------------------------------------------------------------------- /tests/simple_residue.rs: -------------------------------------------------------------------------------- 1 | use number_theory::{NumberTheory,NTResult}; 2 | 3 | #[test] 4 | fn product_residue() { 5 | assert_eq!(11u8.product_residue(19, 0), 209); 6 | for i in 1..256u64 { 7 | assert_eq!(11u64.product_residue(19, i), 11 * 19 % i); 8 | } 9 | 10 | assert_eq!(17u16.product_residue(19, 0), 323); 11 | for i in 1..256u16 { 12 | assert_eq!(17u16.product_residue(19, i), 17 * 19 % i); 13 | } 14 | 15 | assert_eq!(17u32.product_residue(19, 0), 323); 16 | for i in 1..256u32 { 17 | assert_eq!(17u32.product_residue(19, i), 17 * 19 % i); 18 | } 19 | 20 | assert_eq!(17u64.product_residue(19, 0), 323); 21 | for i in 1..256u64 { 22 | assert_eq!(17u64.product_residue(19, i), 17 * 19 % i); 23 | } 24 | } 25 | 26 | #[test] 27 | fn exp_residue() { 28 | assert_eq!(0u64.checked_exp_residue(0, 0).unwrap(), 1); 29 | assert_eq!(5u64.checked_exp_residue(0, 0).unwrap(), 1); 30 | assert_eq!(5u64.checked_exp_residue(1, 0).unwrap(), 5); 31 | assert_eq!(5u64.checked_exp_residue(5, 0).unwrap(), 3125); 32 | // assert_eq!(5u64.checked_exp_residue(5, 5).unwrap(), 0); 33 | assert_eq!(5u64.checked_exp_residue(5, 7).unwrap(), 3); 34 | assert_eq!((-5i64).checked_exp_residue(5, 7).unwrap(), 4); 35 | assert_eq!((5i64).checked_exp_residue(5, 7).unwrap(), 3); 36 | 37 | let add = (5i64).checked_exp_residue(5, 7).unwrap(); 38 | let inv = (-5i64).checked_exp_residue(5, 7).unwrap(); 39 | assert_eq!((add + inv) % 7, 0); 40 | 41 | let mul = (5i64).checked_exp_residue(5, 7).unwrap(); 42 | let inv = (5i64).checked_exp_residue(-5, 7).unwrap(); 43 | 44 | assert_eq!(mul * inv % 7, 1); 45 | // Unable to compute as inverse does not exist 46 | assert_eq!((5i64).checked_exp_residue(-5, 35), NTResult::DNE); 47 | // Unable to compute as the result exceeds the datatype 48 | assert_eq!((5i64).checked_exp_residue(28, 0), NTResult::Overflow); 49 | } 50 | -------------------------------------------------------------------------------- /benches/rand.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Rand generating function 3 | 4 | as most functions are benchmarked using rand inputs it is useful to see how long it takes to generate the data separately from the function 5 | */ 6 | 7 | use number_theory::NumberTheory; 8 | 9 | fn bench_rand(){ 10 | 11 | let mut start = std::time::Instant::now(); 12 | let mut k = 0u32; 13 | for _ in 0..1_000_000{ 14 | k = u32::rng(); 15 | } 16 | let mut stop = start.elapsed(); 17 | println!("1E+6 32-bit {:?}", stop); 18 | 19 | start = std::time::Instant::now(); 20 | let mut k = 0u64; 21 | for _ in 0..1_000_000{ 22 | k = u64::rng(); 23 | } 24 | stop = start.elapsed(); 25 | println!("1E+6 64-bit {:?}", stop); 26 | 27 | 28 | start = std::time::Instant::now(); 29 | let mut k = 0u128; 30 | for _ in 0..1_000_000{ 31 | k = u128::rng(); 32 | } 33 | stop = start.elapsed(); 34 | println!("1E+6 128-bit {:?}", stop); 35 | 36 | 37 | } 38 | 39 | 40 | fn rand_prime(){ 41 | let mut start = std::time::Instant::now(); 42 | let mut k = 0u16; 43 | for _ in 0..1_000_000{ 44 | k = u16::prime_gen(16); 45 | } 46 | let mut stop = start.elapsed(); 47 | println!(" 1E+6 16-bit primes {:?}", stop); 48 | 49 | start = std::time::Instant::now(); 50 | let mut k = 0u32; 51 | for _ in 0..1_000_000{ 52 | k = u32::prime_gen(32); 53 | } 54 | stop = start.elapsed(); 55 | println!(" 1E+6 32-bit primes {:?}", stop); 56 | 57 | start = std::time::Instant::now(); 58 | let mut k = 0u64; 59 | for _ in 0..1_000_000{ 60 | k = u64::prime_gen(64); 61 | } 62 | stop = start.elapsed(); 63 | println!(" 1E+6 64-bit primes {:?}", stop); 64 | 65 | start = std::time::Instant::now(); 66 | let mut k = 0u128; 67 | for _ in 0..1_000_000{ 68 | k = u128::prime_gen(128); 69 | } 70 | stop = start.elapsed(); 71 | println!(" 1E+6 128-bit primes {:?}", stop); 72 | } 73 | 74 | 75 | fn main(){ 76 | bench_rand(); 77 | rand_prime() 78 | } 79 | -------------------------------------------------------------------------------- /src/data/hashtable.rs: -------------------------------------------------------------------------------- 1 | 2 | #[rustfmt::skip] 3 | pub(crate) static BASE_32: [u16; 200] = [ 4 | 5 | 6 | 0x753f , 0x1c7 , 0xc3 , 0x4fe , 0x601 , 0x1def , 0x464e , 0x13d5 , 0xbdb , 0x14a1 , 7 | 0x604 , 0x1e69 , 0x7f3 , 0x8c1 , 0x975a , 0x27e7 , 0x113 , 0x1967 , 0x101a , 0x762 , 8 | 0x238e , 0x340 , 0x1780 , 0x259 , 0x1348 , 0x614 , 0x720 , 0x479 , 0x222 , 0x4d6 , 9 | 0x81 , 0xd14 , 0xd37 , 0xffa , 0xec80 , 0xdf1 , 0x6e9 , 0x4f2 , 0x74c , 0x61d , 10 | 0x643 , 0x418a , 0x83a , 0xbe4 , 0x2cd , 0x7ed , 0x3141 , 0x593 , 0x78a , 0xe63 , 11 | 0x448 , 0x43 , 0x3208 , 0x9a7 , 0xb18 , 0xf34 , 0x4d2 , 0x4b5 , 0x1de4 , 0x37e8 , 12 | 0x3ec0 , 0x11e9 , 0x2761 , 0x191 , 0x52db , 0x136a , 0x2df9 , 0x43a , 0x112 , 0x386 , 13 | 0x3ca9 , 0x418 , 0xcf8 , 0x1f13 , 0x57c2 , 0x93d , 0xbbc , 0x185d , 0x1d6 , 0xea9 , 14 | 0x3d04 , 0xd9d , 0x1ed , 0x1dd4 , 0x24eb , 0x1c8 , 0xb34 , 0x11d , 0xdd55 , 0x4736 , 15 | 0x43c , 0x1260 , 0x28b3 , 0x14c4 , 0x1954 , 0x145a , 0x1e04 , 0x8f , 0x28 , 0x9f9 , 16 | 0x8e8 , 0x15fd , 0x721 , 0x326f , 0x29f , 0x118 , 0x1a7 , 0x4e7 , 0xa9c , 0x879 , 17 | 0x3ac , 0x2e5 , 0x60c , 0xd5d , 0x9a4 , 0x1f5 , 0x35b7 , 0x128 , 0x79d , 0xc89 , 18 | 0x20f5 , 0x34f7 , 0x110a , 0xf7e , 0x1cbd , 0x1514 , 0x3a , 0x81c , 0x1019 , 0x4e0 , 19 | 0x63e , 0x31c , 0xe26 , 0x9fb , 0xa6d , 0x16f0 , 0xb9be , 0x3bd0 , 0x418 , 0x6f6a , 20 | 0x554f , 0x4d25 , 0x834 , 0xdbb , 0xb01 , 0x9a0 , 0x4e3 , 0x335d , 0x3a5 , 0x237 , 21 | 0x236 , 0xac6 , 0x2166 , 0x2816 , 0x12a6 , 0x1e54 , 0x1061 , 0x2f , 0x13c , 0x40fd , 22 | 0x5ca8 , 0xb08 , 0x3e , 0x13f1 , 0x225 , 0x4825 , 0x4333 , 0x73e , 0x950 , 0x73 , 23 | 0x2a95 , 0xb5a , 0x21e8 , 0x820a , 0x8e5 , 0x6f9 , 0x3207 , 0x153 , 0x3ec , 0x6bb , 24 | 0x156 , 0x214 , 0x29e5 , 0x1bd5 , 0x1c7e , 0x85e , 0x803 , 0x29c5 , 0x30a , 0x786 , 25 | 0x1eae , 0x47c , 0x4fc , 0x1135 , 0xc9a , 0x508 , 0xa9d , 0xb4d , 0x9e5 , 0x59d , 26 | ]; 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ENT 2 | 3 | Elementary Number Theory for Integers in Rust 4 | 5 | Algebraic definitions of primality and factorization are used, permitting checks like -127.is_prime() to return true and unique factorizations to be considered unsigned.Published as number-theory on crates.io 6 | 7 | Currently implements these functions 8 | 9 | - Primality 10 | - Factorization 11 | - GCD, Extended GCD 12 | - Carmichael,Euler & Jordan totients 13 | - Dedekind psi 14 | - Liouville, and Mobius function 15 | - Prime-counting function/nth-prime, and prime lists 16 | - Integer sqrt/nth root 17 | - Integer radical 18 | - K-free 19 | - Quadratic and Exponential residues 20 | - Legendre,Jacobi and Kronecker symbols 21 | - Smoothness checks 22 | 23 | Additionally this library has an implementation of the previous NT functions for arbitrary-precision integers, plus some elementary arithmetic. 24 | Multiplication utilizes Karatsuba algorithm, otherwise all other arithmetic can be assumed to be naive. 25 | 26 | - Addition/subtraction 27 | - Multiplication 28 | - Euclidean Division 29 | - Conversion to and from radix-10 string 30 | - Successor function (+1) 31 | - SIRP-factorials {generalization of factorials} 32 | - Conditional Interval Product (CIP factorial) 33 | - Sqrt/nth root 34 | - Exponentiation 35 | - Logarithms 36 | - Probable pseudoprime construction 37 | 38 | Usage is fairly simple 39 | ```rust 40 | // include NT functions 41 | use number_theory::NumberTheory; 42 | // include arbitrary-precision arithmetic 43 | use number_theory::Mpz; 44 | // Sign, generally unnecessary for ENT 45 | //use number_theory:Sign; 46 | let mersenne = Mpz::from_string("-127").unwrap(); 47 | assert_eq!(mersenne.is_prime(), true); 48 | // Or for a more complex example 49 | 50 | // check if x mod 1 === 0, trivial closure 51 | let modulo = |x: &u64| {if x%1==0{return true} return false}; 52 | 53 | //Generate 872 factorial, using the above trivial function 54 | // this can be just as easily reproduced as Mpz::sirp(1,872,1,0); 55 | let mut factorial = Mpz::cip(1, 872,modulo); 56 | 57 | // Successor function, increment by one 58 | factorial.successor(); 59 | 60 | // 872! + 1 is in-fact a factorial prime 61 | assert_eq!(factorial.is_prime(),true) 62 | ``` 63 | -------------------------------------------------------------------------------- /examples/rsa.rs: -------------------------------------------------------------------------------- 1 | use number_theory::Mpz; 2 | use number_theory::NumberTheory; 3 | 4 | /* 5 | 6 | Naive implementation of RSA cryptosystem in k-bits, works for k greater than 256 7 | 8 | This is not a cryptographic library and this implementation is not secure. 9 | 10 | Cryptographic libraries require special consideration for each operations and protections against variable manipulation 11 | 12 | */ 13 | 14 | // Generate safe prime 15 | fn gen_safe(k: u32) -> Mpz { 16 | // Generate safe primes to minimize the factors of p-1 using standard primes are much faster 17 | 18 | loop { 19 | let p = Mpz::prime_gen(k).unwrap(); 20 | if p.congruence_u64(3, 2) { 21 | match p.is_sophie() { 22 | Some(x) => return x, 23 | None => (), 24 | } 25 | } 26 | } 27 | } 28 | 29 | fn generate_keys(k: u32) -> (Mpz, Mpz, Mpz) { 30 | // private, public, exponent 31 | let mut lo = k >> 1; 32 | let hi = lo; 33 | if k & 1 == 0 { 34 | // Ensures delta of atleast 2^(k-1) to prevent Fermat and Euler factorization 35 | lo -= 1; 36 | } 37 | let p = gen_safe(hi); 38 | 39 | let q = gen_safe(lo); 40 | 41 | let phi = p 42 | .ref_subtraction(&Mpz::one()) 43 | .ref_product(&q.ref_subtraction(&Mpz::one())); 44 | let n = p.ref_product(&q); 45 | assert_eq!(n.bit_length(), k as u64); // checks that the key is exactly k-bits 46 | 47 | let e = Mpz::from_u64(u64::prime_gen(64).unwrap()); // generates exponent between 2^63;2^64 48 | let mut inv = e.extended_gcd(&phi).1; 49 | if !inv.is_positive() { 50 | inv = phi.ref_addition(&inv); 51 | } 52 | 53 | return (inv, n, e); 54 | } 55 | 56 | fn main() { 57 | const STRENGTH: u32 = 512; 58 | 59 | let secret = Mpz::from_u64(0x29A822A1AA29229 << 5); 60 | let start = std::time::Instant::now(); 61 | let (private, public, exponent) = generate_keys(STRENGTH); 62 | let stop = start.elapsed(); 63 | let encrypted = secret.exp_residue(&exponent, &public); 64 | 65 | let decrypted = encrypted.exp_residue(&private, &public); 66 | println!("{:?} for a key of {}-bits \n ", stop, STRENGTH); 67 | println!( 68 | "The secret is . . . \n {}", 69 | String::from_utf8(decrypted.to_u64().unwrap().to_be_bytes().to_vec()).unwrap() 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /src/primitive/sieve.rs: -------------------------------------------------------------------------------- 1 | 2 | pub(crate) fn sieve(sup: u64, collect: bool) -> (Vec,u64){ 3 | let isqrt = (sup as f64).sqrt() as u64; 4 | let segment_size = std::cmp::max(isqrt,32768); 5 | let mut count = 0u64; 6 | let mut values = vec![]; 7 | let mut sieve = Vec::::with_capacity(segment_size as usize); 8 | let mut is_prime = vec![true;isqrt as usize+1]; 9 | let mut primes = vec![]; 10 | let mut multiples = vec![]; 11 | let mut low = 0u64; 12 | 13 | let mut i : u64 = 3; 14 | let mut n : u64 = 3; 15 | let mut s : u64 = 3; 16 | 17 | loop { // full loop 18 | 19 | 20 | if low > sup{ 21 | break; 22 | } 23 | 24 | 25 | sieve.truncate(0); 26 | sieve.resize(segment_size as usize,true); // allocate with true values 27 | 28 | let mut high : u64 = low + segment_size -1; 29 | high = std::cmp::min(high,sup); 30 | 31 | let inner_sup = (high as f64).sqrt() as u64; 32 | 33 | loop{ // Generate sieving primes 34 | 35 | if i*i > high{ 36 | break; 37 | } 38 | 39 | if is_prime[i as usize]{ 40 | let mut j = i*i; 41 | loop { 42 | 43 | if j > isqrt{ 44 | break; 45 | } 46 | is_prime[j as usize] = false; 47 | j+=i; 48 | } 49 | 50 | } 51 | i+=2; 52 | } // End prime generation 53 | 54 | loop{// prime initialisation 55 | if s*s > high{ 56 | break; 57 | } 58 | 59 | if is_prime[s as usize]{ 60 | primes.push(s); 61 | multiples.push(s*s -low); 62 | } 63 | s+=2; 64 | }// end prime initialisation 65 | 66 | for i in 0..primes.len(){// sieve current segment 67 | 68 | let mut j = multiples[i as usize]; 69 | 70 | let k = primes[i as usize]*2; 71 | 72 | loop { 73 | if j >= segment_size{ 74 | break; 75 | } 76 | sieve[j as usize] = false; 77 | j+=k; 78 | } 79 | multiples[i as usize] = j -segment_size; 80 | }// end current sieve 81 | 82 | loop{ 83 | if n > high{ 84 | break; 85 | } 86 | if sieve[(n - low) as usize]{ 87 | count+=1; 88 | 89 | if collect{ 90 | values.push(n); 91 | } 92 | } 93 | n+=2; 94 | } 95 | low+=segment_size; 96 | } 97 | (values,count) 98 | } 99 | -------------------------------------------------------------------------------- /tests/strong_arith.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Randomly generated inputs to search for potential errors 4 | 5 | 10-million integers in the interval -2^704-1 ; 2^704-1 are checked in the following operations takes approximately 5E+3 s 6 | 7 | Multiplication/Euclidean Division 8 | Logarithm 9 | Exponentiation/Nth-roots 10 | 11 | */ 12 | use number_theory::{Mpz,NumberTheory}; 13 | 14 | #[ignore] 15 | #[test] 16 | fn randarith() { 17 | const MAX_LOG_ERROR: f64 = 0.000001; // Maximum error allowed to be encurred by the product operation 18 | 19 | const ITERATIONS: usize = 10_000_000; 20 | 21 | const WORD_LENGTH: u64 = 11; 22 | 23 | let rand_gen = || -> Mpz { 24 | let mut z = Mpz::rand(WORD_LENGTH as usize); // Randomly generates integers in the range -2^704-1;2^704-1 25 | if u64::rng() % 2 == 0 { 26 | z.neg(); 27 | } 28 | z 29 | }; 30 | 31 | for _ in 0..ITERATIONS { 32 | let lhs = rand_gen(); // initializes first integer 33 | let rhs = rand_gen(); // initializes second integer 34 | let third = rand_gen(); // initializes third integer 35 | 36 | let lhsln = lhs.abs().ln(); // logarithms only apply to positive numbers 37 | let rhsln = rhs.abs().ln(); 38 | let prodln_inf = lhsln + rhsln - MAX_LOG_ERROR; // Minimum bound allowed for the computed logarithm to take 39 | let prodln_sup = lhsln + rhsln + MAX_LOG_ERROR; // Maximum bound allowed for the computed logarithm to take 40 | 41 | assert_eq!(lhs, Mpz::from_string(&lhs.to_string()).unwrap()); // Checks that to_string and from_string are inverses 42 | assert_eq!(rhs, Mpz::from_string(&rhs.to_string()).unwrap()); 43 | assert_eq!(third, Mpz::from_string(&third.to_string()).unwrap()); 44 | 45 | let mut prod = lhs.ref_product(&rhs); 46 | let prodln = prod.abs().ln(); 47 | 48 | assert!(prodln < prodln_sup && prodln > prodln_inf); // checks that the natural logarithm and the prod function behave roughly as expected 49 | 50 | prod.mut_addition(third); 51 | let (quo, rem) = prod.euclidean_div(rhs.clone()); 52 | assert_eq!(quo.ref_product(&rhs).ref_addition(&rem), prod); 53 | 54 | assert_eq!(lhs.ref_product(&lhs).abs().sqrt().0, lhs.abs()); // checks the sqrt function, only applies to positive integers 55 | 56 | let randexp = (u64::rng() % 10) + 2; 57 | let estpowln = lhsln * (randexp as f64); 58 | let pow = lhs.pow(randexp); 59 | let powln = pow.abs().ln(); 60 | 61 | let pow_inf = estpowln - MAX_LOG_ERROR * (randexp as f64); 62 | let pow_sup = estpowln + MAX_LOG_ERROR * (randexp as f64); 63 | 64 | assert!(powln < pow_sup && powln > pow_inf); 65 | 66 | assert_eq!(lhs.abs(), pow.abs().nth_root(Mpz::from(randexp)).0) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /benches/spanish: -------------------------------------------------------------------------------- 1 | Una biblioteca para la teoría de números elemental. Pretende ser simple, versátil, eficaz y, sobre todo, correcta. 2 | 3 | A diferencia de la mayoría de las otras bibliotecas de teoría de números, la teoría de números está más generalizada operando sobre anillos de cociente (también llamados clases de residuos en los métodos, es decir, exp_residue para la clase de residuos de pow (x, y) calculada en Z / nZ), en lugar de usar definiciones aritméticas modulares, esto permite resultados más generales que en algunos casos es diferente a la típica definición aritmética modular. Proporcionamos la mayoría de ellos a continuación. 4 | 5 | Z/0Z = Z , en consecuencia, la aritmética realizada sobre él es equivalente a la aritmética en Z, por lo tanto, 4.quadratic_residue(0) == 8 6 | Z/-nZ = Z/nZ y por lo tanto 4.residuo_cuadrático(-3) = 4.residuo_cuadrático(3). 7 | Los números negativos se evalúan como inversos aditivos en el anillo Z/nZ por simplicidad (excepto en el caso de Z/0Z), entonces (-4).exp_residue(&3,5) == 1 no -4 aunque son equivalentes 8 | 9 | Características 10 | 11 | 14 funciones teóricas de números no triviales distintas implementadas por todos los tipos de enteros incorporados y enteros de precisión arbitraria 12 | Aritmética de anillo Z/nZ, frecuentemente usando algoritmos optimizados. 13 | Comprobación de primalidad extremadamente rápida y rigurosamente probada para números enteros inferiores a 2^64+2^42. 14 | 15 | Consideraciones de uso 16 | 17 | El tipo de datos más pequeño suele ser el más eficiente, tanto por el procesador como por el hecho de que muchas de las funciones están mejor optimizadas para valores pequeños. 18 | Las funciones que tienen una contraparte "marcada" realizan verificaciones de corrección cero, no se asustan y confían en que el usuario garantice que la entrada será válida. Esto es para fines de eficiencia, si no puede garantizar que una entrada cumpla con las pautas, entonces use la variante "marcada". 19 | La teoría de números no es una biblioteca de criptografía y no sustituye a los algoritmos rigurosos implementados de forma segura que tienen en cuenta los ataques de los adversarios, la predicción de bifurcaciones, etc. 20 | 21 | 22 | Sin embargo, la teoría de números puede superar a otras bibliotecas "criptográficas" en velocidad y corrección. 23 | 24 | Por qué es posible que no quieras usar esto 25 | 26 | La API se romperá sin previo aviso. Number-theory ni siquiera se acerca a una versión estable, simplemente se publica para que esté disponible dado que supera a muchas bibliotecas similares. (De hecho, la teoría de números era originalmente un subarchivo de un proyecto de álgebra informática aún privado y, en consecuencia, su funcionalidad lo refleja fuertemente). 27 | Las funciones pueden romperse silenciosamente. Fuera de is_prime, ninguna función tiene una prueba rigurosa de corrección (aunque pocas otras bibliotecas la tienen). 28 | No utiliza dependencias ni sigue ningún sistema de API externo (es decir, num-traits). Si bien es deliberado mantener la simplicidad y un mayor control del software por parte del desarrollador, los usuarios pueden encontrarlo menos versátil. 29 | Solo funciona en plataformas x86-64 30 | 31 | verified = verificado 32 | -------------------------------------------------------------------------------- /src/structs.rs: -------------------------------------------------------------------------------- 1 | use crate::ntrait::NumberTheory; 2 | 3 | /// A primality certificate, currently a modification of the Pratt Certificate 4 | /// Instead of checking if a^(n-1) mod n we use the stronger form of the Fermat test 5 | /// which permits proving that Carmichael numbers are composite 6 | #[derive(Clone)] 7 | pub struct Certificate{ 8 | pub(crate) n: T, 9 | pub(crate) witness: T, 10 | pub(crate) fctr: Vec, 11 | } 12 | 13 | impl Certificate{ 14 | 15 | pub(crate) fn new(n: T, witness: T, fctr: Vec) -> Self{ 16 | Self{n,witness,fctr} 17 | } 18 | 19 | 20 | /// Check the certificate 21 | pub fn check(&self) -> bool{ 22 | if self.fctr.is_empty(){ 23 | return false; 24 | } 25 | 26 | if !self.n.clone().strong_fermat(self.witness.clone()){ 27 | return false; 28 | } 29 | 30 | for i in self.fctr.iter(){ 31 | if self.witness.exp_residue(i.clone(), self.n.clone()).is_unit() { 32 | return false 33 | } 34 | } 35 | return true; 36 | } 37 | 38 | } 39 | 40 | #[derive(Clone,Default)] 41 | pub struct Factorization{ 42 | pub(crate) base: Vec, 43 | pub(crate) power: Vec, 44 | } 45 | 46 | impl Factorization{ 47 | 48 | pub fn new() -> Self{ 49 | Self{base: vec![], power: vec![]} 50 | } 51 | 52 | pub fn from_components(base: Vec,power: Vec) -> Self{ 53 | Self{base,power} 54 | } 55 | 56 | pub fn factor_iter(&self) -> std::slice::Iter{ 57 | self.base.iter() 58 | } 59 | 60 | pub fn power_iter(&self) -> std::slice::Iter{ 61 | self.power.iter() 62 | } 63 | 64 | pub fn add_factor(&mut self, fctr: T){ 65 | self.base.push(fctr); 66 | } 67 | 68 | pub fn add_power(&mut self, power: u64){ 69 | self.power.push(power); 70 | } 71 | 72 | pub fn pair_iter(&self) -> std::iter::Zip,std::slice::Iter>{ 73 | self.base.iter().zip(self.power.iter()) 74 | } 75 | 76 | pub fn max(&self) -> T{ 77 | self.factor_iter().max().unwrap().clone() 78 | } 79 | 80 | pub fn k_free(&self, k: u64) -> bool{ 81 | for i in self.power_iter(){ 82 | if *i >= k{ 83 | return false; 84 | } 85 | } 86 | return true; 87 | } 88 | 89 | pub fn prime_omega(&self) -> u64{ 90 | self.power_iter().sum::() 91 | } 92 | 93 | } 94 | 95 | impl std::fmt::Display for Factorization{ 96 | 97 | fn fmt(&self,f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>{ 98 | 99 | let mut output = String::new(); 100 | if self.power[0] != 1{ 101 | output+=&(self.base[0].to_string()+"^"+&self.power[0].to_string()); 102 | } 103 | else{ 104 | output+=&(self.base[0].to_string()); 105 | } 106 | if self.base.len() > 1{ 107 | for i in 1..self.base.len(){ 108 | if self.power[i] != 1{ 109 | let pair = self.base[i].to_string()+"^"+&self.power[i].to_string(); 110 | output+=&(" * ".to_owned() + &pair); 111 | } 112 | else{ 113 | let pair = self.base[i].to_string(); 114 | output+= &(" * ".to_owned()+&pair); 115 | } 116 | } 117 | } 118 | write!(f,"{}",output) 119 | } 120 | } 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/arithmetic/sliceops.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::inlineops::*; 2 | use std::cmp::Ordering; 3 | 4 | /* 5 | Returns index of significant digit 6 | */ 7 | 8 | pub(crate) fn sig_pos(x: &[u64]) -> usize { 9 | if x.last() == Some(&0) { 10 | return x.iter().rposition(|&x| x != 0).map_or(0, |i| i + 1); 11 | } 12 | x.len() 13 | } 14 | 15 | /* 16 | Slice operations 17 | */ 18 | 19 | // Compare slices 20 | pub(crate) fn cmp_slice(x: &[u64], y: &[u64]) -> Ordering { 21 | if x.len() > y.len() { 22 | return Ordering::Greater; 23 | } 24 | if x.len() < y.len() { 25 | Ordering::Less 26 | } else { 27 | Iterator::cmp(x.iter().rev(), y.iter().rev()) 28 | } 29 | } 30 | 31 | /* 32 | Bitwise slice operations 33 | */ 34 | 35 | // slice shift-right 36 | pub(crate) fn _shr_slice(x: &mut [u64], shift: u32) -> u64 { 37 | let mut carry = 0u64; 38 | 39 | for i in x.iter_mut().rev() { 40 | carry = carry_shr(carry, *i, shift, i); 41 | } 42 | 43 | carry 44 | } 45 | // slice shift-left 46 | pub(crate) fn shl_slice(x: &mut [u64], shift: u32) -> u64 { 47 | let mut carry = 0u64; 48 | 49 | for i in x.iter_mut() { 50 | carry = carry_shl(carry, *i, shift, i); 51 | } 52 | 53 | carry 54 | } 55 | 56 | /* 57 | 58 | Arithmetic slice operations 59 | 60 | */ 61 | #[inline] 62 | pub(crate) fn add_slice(x: &mut [u64], y: &[u64]) -> u8 { 63 | //first number must be larger 64 | 65 | let mut carry = 0u8; 66 | 67 | let (lo, hi) = x.split_at_mut(y.len()); //split to make lo equal in length to y 68 | 69 | for (i, j) in lo.iter_mut().zip(y.iter()) { 70 | //add equal 71 | carry = adc(carry, *i, *j, i); 72 | } 73 | 74 | if carry > 0u8 { 75 | // if carry is greater than zero, propagate it through the rest of the array until there is no carry left 76 | 77 | for k in hi.iter_mut() { 78 | //add the carry until there is no carry 79 | carry = adc(carry, *k, 0u64, k); 80 | if carry == 0u8 { 81 | break; 82 | } 83 | } 84 | } 85 | carry 86 | } 87 | 88 | /* 89 | Subtraction x-y 90 | */ 91 | 92 | pub(crate) fn sub_slice(x: &mut [u64], y: &[u64]) -> u8 { 93 | //first number must be larger 94 | 95 | let mut carry = 0u8; 96 | 97 | let (lo, hi) = x.split_at_mut(y.len()); //split to make equal 98 | 99 | for (i, j) in lo.iter_mut().zip(y.iter()) { 100 | //add equal 101 | carry = sbb(carry, *i, *j, i); 102 | } 103 | 104 | if carry > 0u8 { 105 | for k in hi.iter_mut() { 106 | //add the carry until there is no carry 107 | carry = sbb(carry, *k, 0u64, k); 108 | if carry == 0u8 { 109 | break; 110 | } 111 | } 112 | } 113 | carry 114 | } 115 | 116 | pub(crate) fn scale_slice(x: &mut [u64], scale: u64) -> u64 { 117 | let mut carry = 0u64; 118 | 119 | for i in x.iter_mut() { 120 | carry = carry_mul(carry, *i, scale, i); 121 | } 122 | carry 123 | } 124 | 125 | // divides inplace and returns remainder 126 | pub(crate) fn div_slice(x: &mut [u64], divisor: u64) -> u64 { 127 | let mut carry = 0u64; 128 | 129 | for i in x.iter_mut().rev() { 130 | carry = carry_div(carry, *i, divisor, i); 131 | } 132 | carry 133 | } 134 | 135 | // divides inplace and returns remainder 136 | pub(crate) fn mod_slice(x: &[u64], divisor: u64) -> u64 { 137 | let mut carry = 0u64; 138 | 139 | for i in x.iter().rev() { 140 | carry = carry_mod(carry, *i, divisor); 141 | } 142 | carry 143 | } 144 | -------------------------------------------------------------------------------- /src/arithmetic/conversion.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | use std::str::FromStr; 3 | 4 | use crate::arithmetic::sign::Sign; 5 | use crate::arithmetic::sliceops::*; 6 | 7 | // converts u64 to string of length 19 8 | fn string_format(x: u64) -> String { 9 | let k = x.to_string(); 10 | let leading = (0..(19 - k.len())).map(|_| "0").collect::(); 11 | leading + &k 12 | } 13 | 14 | // Naive conversion from Radix-2^64 to Radix-10^19 string, very slow 15 | pub(crate) fn to_string(sign: Sign, digits: Vec) -> String { 16 | if digits.is_empty() { 17 | return "".to_string(); 18 | } 19 | 20 | if digits.len() == 1 { 21 | let p = digits[0].to_string(); 22 | if sign == Sign::Negative { 23 | return "-".to_owned() + &p; 24 | } else { 25 | return p; 26 | } 27 | } 28 | 29 | let mut k = vec![]; 30 | let mut x = digits; 31 | x.push(0u64); 32 | loop { 33 | let idx = sig_pos(&x[..]); 34 | 35 | if idx == 0 { 36 | break; 37 | } 38 | 39 | k.push(div_slice(&mut x[..idx], 0x8AC7230489E80000u64)); 40 | } 41 | k.push(x[0] % 0x8AC7230489E80000u64); 42 | 43 | let mut count = 0usize; 44 | for i in k.iter().rev() { 45 | if *i > 0u64 { 46 | break; 47 | } 48 | count += 1; 49 | } 50 | 51 | k.truncate(k.len() - count); 52 | 53 | let len = k.len() - 1; 54 | let interim = k[..len] 55 | .iter() 56 | .rev() 57 | .map(|x| string_format(*x)) 58 | .collect::>(); 59 | let last = k[len].to_string() + &interim.join(""); 60 | 61 | if sign == Sign::Negative { 62 | return "-".to_string() + &last; 63 | } 64 | last 65 | } 66 | 67 | pub(crate) fn to_hex_string(sign: Sign, digits: Vec) -> String{ 68 | if digits.is_empty() { 69 | return "".to_string(); 70 | } 71 | let mut k = "0x".to_string(); 72 | 73 | if sign == Sign::Negative{ 74 | k = "-".to_string() + &k; 75 | } 76 | 77 | if digits.len()==1{ 78 | let p = format!("{:0X}",digits[0]);//digits[0].to_string(); 79 | if sign == Sign::Negative { 80 | return "-".to_owned() + &p; 81 | } else { 82 | return p; 83 | } 84 | } 85 | for i in digits.iter().rev(){ 86 | k = k.to_owned() + &format!("{:0X}",i);// &word_char(*i); 87 | } 88 | k 89 | } 90 | 91 | pub(crate) fn from_string(string: &str) -> Option> { 92 | let b = string.bytes().all(|c| c.is_ascii_digit()); 93 | 94 | if !b { 95 | return None; 96 | } 97 | 98 | let ln = 19usize; 99 | let strln = string.len(); 100 | let strem = strln % ln; 101 | let t = string.as_bytes(); 102 | let start = 0usize; 103 | 104 | let mut k = vec![]; 105 | 106 | for i in 0..strln / ln { 107 | k.push(str::from_utf8(&t[(strln - (i + 1) * ln)..(strln - i * ln)]).unwrap()) 108 | } 109 | if strem != 0 { 110 | // if there is a remaining slice greater than zero 111 | if str::from_utf8(&t[start..strem]).unwrap() != "" { 112 | // if the first value is not empty then it pushes it to the vector 113 | k.push(str::from_utf8(&t[start..strem]).unwrap()); 114 | } 115 | } 116 | let x = k 117 | .iter() 118 | .rev() 119 | .map(|x| u64::from_str(x).unwrap()) 120 | .collect::>(); // string converted to decimal vector 121 | 122 | let mut z = vec![0]; 123 | for i in 0..x.len() { 124 | let mut carry = scale_slice(&mut z[..], 0x8AC7230489E80000); 125 | if carry > 0 { 126 | z.push(carry) 127 | } 128 | carry = add_slice(&mut z[..], &x[i..(i + 1)]) as u64; 129 | if carry > 0 { 130 | z.push(carry) 131 | } 132 | } 133 | Some(z) 134 | } 135 | -------------------------------------------------------------------------------- /benches/primality.rs: -------------------------------------------------------------------------------- 1 | use number_theory::NumberTheory; 2 | use number_theory::Mpz; 3 | 4 | /* 5 | Benchmark competition and show errors 6 | Rivals 7 | num-modular legendre error (Cmpute's libraries are completely (but silently) broken) 8 | num-prime is_prime_error 32k+ counterexamples 9 | num-primes prime error ? (Not a typo, num-primes is an older attempt by a different author at a simple primality checking crate, 10 | it too was broken although it's current status has not been evaluated) 11 | num-integer Compare gcd 12 | num-bigint 13 | malachite The only real comparable library 14 | primal is_prime error (since fixed due to the author) 15 | primetools 16 | red_primality 17 | glass-pumpkin Baille-psw (lucas test) is broken and the miller rabin over small intervals is far too weak (atleast in old versions) 18 | 19 | */ 20 | // 64-bit known composites that deterministically pass some of the libraries, this is not an exhaustive list (some of them have thousands to millions of known counterexamples ) 21 | //const COUNTEREXAMPLES : [u64;] 22 | 23 | /* An oft looked over property is that compositeness tests like miller-rabin and lucas sequence tests, can infact flag primes as composite 24 | especially if they are not correctly implemented. this is harder to detect than passing composites however and is primarily found by code auditing 25 | rather than running against a list of primes*. glass-pumpkin (1.2) BPSW for instance flags primes due to an incorrect lucas test, as will any miller-rabin check that fails to guarantee that a prime is only checked by a coprime base. 26 | *Of course a fast provably correct library like number-theory can also help detect these errors, and even better is that number-theory can also produce a proof (via prime_proof function) even if you don't trust the is_prime function. 27 | 28 | */ 29 | pub fn bench_prime() { 30 | let mut count = 0i64; 31 | println!("Evaluating 1 million integers in the worst interval for each datatype"); 32 | let mut start = std::time::Instant::now(); 33 | for i in 4293967295..u32::MAX{ 34 | if i.is_prime() { 35 | count += 1 36 | } 37 | } 38 | let mut stop = start.elapsed(); 39 | println!( 40 | "{} primes counted in {:?} with an error of {} : u32", 41 | count, 42 | stop, 43 | count - 44872 44 | ); 45 | count = 0; 46 | start = std::time::Instant::now(); 47 | for i in 18446744073708551615..u64::MAX { 48 | if i.is_prime() { 49 | count += 1 50 | } 51 | } 52 | stop = start.elapsed(); 53 | println!( 54 | "{} primes counted in {:?} with an error of {} : u64", 55 | count, 56 | stop, 57 | count - 22475 58 | ); 59 | 60 | count = 0; 61 | start = std::time::Instant::now(); 62 | for i in (u128::MAX - 1_000_000)..u128::MAX { 63 | if i.is_prime() { 64 | count += 1 65 | } 66 | } 67 | stop = start.elapsed(); 68 | println!( 69 | "{} primes counted in {:?} with an error of {} : u128", 70 | count, 71 | stop, 72 | count - 11281 73 | ); 74 | 75 | let mut p = Mpz::one().shl(256).ref_subtraction(&Mpz::from_u64(1_000_000)); 76 | count = 0; 77 | start = std::time::Instant::now(); 78 | for i in 0..1_000_000{ 79 | if p.is_prime(){ 80 | count+=1; 81 | } 82 | p.successor() 83 | } 84 | stop = start.elapsed(); 85 | println!("{} primes counted in {:?} with an error of {} : 256-bit Mpz",count,stop, count - 5539); 86 | 87 | let mut p = Mpz::from_u64(10).pow(999); 88 | 89 | count = 0; 90 | start = std::time::Instant::now(); 91 | for i in 0..10_000{ 92 | if p.is_prime(){ 93 | count+=1; 94 | } 95 | p.successor() 96 | } 97 | let stop = start.elapsed(); 98 | println!("{} titanic primes found in {:?}",count, stop) 99 | } 100 | 101 | fn main() { 102 | bench_prime() 103 | } 104 | -------------------------------------------------------------------------------- /src/arithmetic/inlineops.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | PRNG 4 | 5 | */ 6 | /* 7 | 8 | Precursor functions 9 | 10 | */ 11 | #[inline(always)] 12 | pub(crate) const fn split(x: u128) -> (u64, u64) { 13 | unsafe { std::mem::transmute::(x) } 14 | } 15 | #[inline(always)] 16 | pub(crate) const fn fuse(hi: u64, lo: u64) -> u128 { 17 | unsafe { std::mem::transmute::<(u64, u64), u128>((lo, hi)) } 18 | } 19 | 20 | #[inline] 21 | pub(crate) fn adc(carry: u8, x: u64, y: u64, output: &mut u64) -> u8 { 22 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 23 | { 24 | unsafe { core::arch::x86_64::_addcarry_u64(carry, x, y, output) } 25 | } 26 | #[cfg(not(any(target_arch = "x86",target_arch="x86_64")))] 27 | { 28 | let x128 = x as u128; 29 | let y128 = y as u128; 30 | let sum = x128+y128 + carry as u128; 31 | *output = sum as u64; 32 | (sum>>64) as u8 33 | } 34 | } 35 | 36 | #[inline] 37 | pub(crate) fn sbb(carry: u8, x: u64, y: u64, output: &mut u64) -> u8 { 38 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 39 | { 40 | unsafe { core::arch::x86_64::_subborrow_u64(carry, x, y, output) } 41 | } 42 | #[cfg(not(any(target_arch = "x86",target_arch="x86_64")))] 43 | { 44 | let (interim,flag) = x.overflowing_sub(carry.into()); 45 | let (res,flag2) = interim.overflowing_sub(y); 46 | *output = res; 47 | if flag || flag2{ 48 | return 1u8 49 | } 50 | 0u8 51 | } 52 | } 53 | 54 | #[inline] 55 | pub(crate) fn carry_mul(carry: u64, x: u64, y: u64, output: &mut u64) -> u64 { 56 | let product = (x as u128 * y as u128) + carry as u128; 57 | let (jtmp, car) = split(product); 58 | *output = jtmp; 59 | car 60 | } 61 | 62 | #[inline] 63 | pub(crate) fn mul_acc(carry: u64, x: u64, y: u64, acc: &mut u128) -> u64 { 64 | *acc += carry as u128; 65 | *acc += x as u128 * y as u128; 66 | let lo = *acc as u64; 67 | *acc >>= 64; 68 | lo 69 | } 70 | 71 | #[inline] 72 | pub(crate) fn carry_div(carry: u64, x: u64, y: u64, output: &mut u64) -> u64 { 73 | let num = fuse(carry, x); 74 | let factor = num / y as u128; 75 | *output = factor as u64; 76 | (num % y as u128) as u64 77 | } 78 | 79 | #[inline] 80 | pub(crate) fn carry_mod(carry: u64, x: u64, y: u64) -> u64 { 81 | let num = fuse(carry, x); 82 | (num % y as u128) as u64 83 | } 84 | 85 | #[inline] 86 | pub(crate) fn wide_div(upper: u64, lower: u64, divisor: u64) -> (u64, u64) { 87 | let interim = fuse(upper, lower); 88 | ( 89 | (interim / divisor as u128) as u64, 90 | (interim % divisor as u128) as u64, 91 | ) 92 | } 93 | 94 | pub(crate) fn divide3by2(ahi: u64, amid: u64, alo: u64, bhi: u64, blo: u64) -> u64 { 95 | let (mut q0, mut r) = if ahi < bhi { 96 | let (q0, r) = wide_div(ahi, amid, bhi); 97 | (q0, r as u128) 98 | } else { 99 | (u64::MAX, ahi as u128 + amid as u128) 100 | }; 101 | 102 | while r <= u64::MAX as u128 103 | && unsafe { std::mem::transmute::<(u64, u64), u128>((alo, r as u64)) } 104 | < q0 as u128 * blo as u128 105 | { 106 | q0 -= 1; 107 | r += blo as u128; 108 | } 109 | q0 110 | } 111 | /* 112 | #[inline] 113 | pub(crate) fn carry_shl(carry: u64, x: u64, places: u32, output: &mut u64) -> u64 { 114 | *output = (x.overflowing_shl(places).0) | carry; 115 | unsafe { core::arch::x86_64::_bextr_u64(x, 64 - places, 64) } 116 | } 117 | */ 118 | #[inline] 119 | pub(crate) fn carry_shl(carry: u64, x: u64, places: u32, output: &mut u64) -> u64{ 120 | *output = (x.overflowing_shl(places).0) | carry; 121 | if places == 0{ 122 | return 0 123 | } 124 | x.wrapping_shr(64-places) 125 | } 126 | 127 | /* 128 | #[inline] 129 | pub(crate) fn carry_shr(carry: u64, x: u64, places: u32, output: &mut u64) -> u64 { 130 | *output = (x >> places) | carry; 131 | if places == 0 { 132 | return 0; 133 | } 134 | unsafe { core::arch::x86_64::_bextr_u64(x, 0, places) << (64 - places) } 135 | } 136 | */ 137 | #[inline] 138 | pub(crate) fn carry_shr(carry: u64, x: u64, places: u32, output: &mut u64) -> u64 { 139 | *output = (x >> places) | carry; 140 | if places == 0 { 141 | return 0; 142 | } 143 | x.overflowing_shl(64 - places).0 144 | } 145 | 146 | 147 | 148 | /* 149 | Truncation function 150 | */ 151 | 152 | pub(crate) fn remove_lead_zeros(x: &mut Vec) { 153 | if let Some(&0) = x.last() { 154 | let len = x.iter().rposition(|&d| d != 0).map_or(0, |i| i + 1); 155 | x.truncate(len); 156 | } 157 | if x.len() < x.capacity() / 4 { 158 | x.shrink_to_fit(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/arithmetic/muldiv.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::inlineops::*; 2 | use crate::arithmetic::sign::Sign; 3 | use crate::arithmetic::sliceops::*; 4 | use std::cmp::Ordering; 5 | 6 | pub(crate) fn sub_sign(mut a: &[u64], mut b: &[u64]) -> (Sign, Vec) { 7 | if let Some(&0) = a.last() { 8 | a = &a[..a.iter().rposition(|&x| x != 0).map_or(0, |i| i + 1)]; 9 | } 10 | if let Some(&0) = b.last() { 11 | b = &b[..b.iter().rposition(|&x| x != 0).map_or(0, |i| i + 1)]; 12 | } 13 | 14 | match cmp_slice(a, b) { 15 | Ordering::Greater => { 16 | let mut a = a.to_vec(); 17 | sub_slice(&mut a[..], b); 18 | (Sign::Positive, a) 19 | } 20 | Ordering::Less => { 21 | let mut b = b.to_vec(); 22 | sub_slice(&mut b[..], a); 23 | (Sign::Negative, b) 24 | } 25 | Ordering::Equal => (Sign::Positive, vec![0]), 26 | } 27 | } 28 | 29 | pub(crate) fn mac_scalar(x: &[u64], y: u64, acc: &mut [u64]) { 30 | if y == 0 { 31 | return; 32 | } 33 | 34 | let mut carry = 0; 35 | let (a_lo, a_hi) = acc.split_at_mut(x.len()); 36 | 37 | for (a, &x) in a_lo.iter_mut().zip(x) { 38 | *a = mul_acc(*a, x, y, &mut carry); 39 | } 40 | 41 | let (carry_hi, carry_lo) = split(carry); 42 | 43 | let _final_carry = if carry_hi == 0 { 44 | add_slice(a_hi, &[carry_lo]) 45 | } else { 46 | add_slice(a_hi, &[carry_hi, carry_lo]) 47 | }; 48 | } 49 | // scale and subtract y from x 50 | fn sub_mul(x: &mut [u64], y: &[u64], z: u64) -> u64 { 51 | let mut carry = u64::MAX; 52 | 53 | for (i, j) in x.iter_mut().zip(y) { 54 | let fused = fuse(u64::MAX, *i); 55 | let result = fused - u64::MAX as u128 + carry as u128 - *j as u128 * z as u128; 56 | 57 | let (new_x, new_carry) = split(result); 58 | carry = new_carry; 59 | *i = new_x; 60 | } 61 | u64::MAX - carry 62 | } 63 | 64 | pub(crate) fn mul_slice(mut b: &[u64], mut c: &[u64], mut acc: &mut [u64]) { 65 | if let Some(&0) = b.first() { 66 | if let Some(nz) = b.iter().position(|&d| d != 0) { 67 | b = &b[nz..]; 68 | acc = &mut acc[nz..]; 69 | } else { 70 | return; 71 | } 72 | } 73 | if let Some(&0) = c.first() { 74 | if let Some(nz) = c.iter().position(|&d| d != 0) { 75 | c = &c[nz..]; 76 | acc = &mut acc[nz..]; 77 | } else { 78 | return; 79 | } 80 | } 81 | 82 | let acc = acc; 83 | let (x, y) = if b.len() < c.len() { (b, c) } else { (c, b) }; 84 | 85 | if x.len() <= 32 { 86 | // Classical 87 | for (i, xi) in x.iter().enumerate() { 88 | mac_scalar(y, *xi, &mut acc[i..]); 89 | } 90 | } else { 91 | //Karatsuba 92 | let b = x.len() / 2; 93 | let (x0, x1) = x.split_at(b); 94 | let (y0, y1) = y.split_at(b); 95 | 96 | let len = x1.len() + y1.len() + 1; 97 | let mut p = vec![0; len]; 98 | 99 | mul_slice(x1, y1, &mut p[..]); 100 | 101 | remove_lead_zeros(&mut p); 102 | 103 | add_slice(&mut acc[b..], &p); 104 | add_slice(&mut acc[b * 2..], &p); 105 | 106 | p.truncate(0); 107 | p.resize(len, 0); 108 | 109 | mul_slice(x0, y0, &mut p); 110 | remove_lead_zeros(&mut p); 111 | 112 | add_slice(acc, &p); 113 | add_slice(&mut acc[b..], &p); 114 | 115 | let (j0_sign, j0) = sub_sign(x1, x0); 116 | let (j1_sign, j1) = sub_sign(y1, y0); 117 | 118 | match j0_sign.mul(&j1_sign) { 119 | Sign::Positive => { 120 | p.truncate(0); 121 | p.resize(len, 0); 122 | 123 | mul_slice(&j0[..], &j1[..], &mut p[..]); 124 | remove_lead_zeros(&mut p); 125 | 126 | sub_slice(&mut acc[b..], &p); 127 | } 128 | Sign::Negative => { 129 | mul_slice(&j0, &j1, &mut acc[b..]); 130 | } 131 | } 132 | } 133 | } 134 | 135 | pub(crate) fn euclidean_slice(a: &mut Vec, b: &[u64]) -> (Vec, Vec) { 136 | let mut a0 = 0; 137 | 138 | let b0 = *b.last().unwrap(); 139 | let b1 = b[b.len() - 2]; 140 | 141 | let quo_len = a.len() - b.len() + 1; 142 | let mut quo = vec![0; quo_len]; 143 | 144 | for j in (0..quo_len).rev() { 145 | let a1 = *a.last().unwrap(); 146 | let a2 = a[a.len() - 2]; 147 | 148 | let mut q0 = divide3by2(a0, a1, a2, b0, b1); 149 | 150 | let borrow = sub_mul(&mut a[j..], b, q0); 151 | 152 | if borrow > a0 { 153 | q0 -= 1; 154 | add_slice(&mut a[j..], b); 155 | } 156 | 157 | quo[j] = q0; 158 | 159 | a0 = a.pop().unwrap(); 160 | } 161 | 162 | a.push(a0); 163 | let mut remainder = a.to_vec(); 164 | let mut quotient = quo.to_vec(); 165 | remove_lead_zeros(&mut remainder); 166 | 167 | remove_lead_zeros(&mut quotient); 168 | if remainder.is_empty() { 169 | remainder.push(0u64) 170 | } 171 | 172 | if quotient.is_empty() { 173 | quotient.push(0u64) 174 | } 175 | (quotient, remainder) 176 | } 177 | -------------------------------------------------------------------------------- /tests/strong_ent.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | List of computationally intensive tests that demonstrate the claims made by the author. 4 | 5 | 6 | Full computation of each individual test may take hours so it is imperative that it is run as cargo test --release and during downtime. 7 | 8 | These tests are primarily to ensure confidence in program correctness, especially in light of the errors presented by other number-theory libraries in Rust. 9 | 10 | The assumptions that are made to claim correctness are listed next to the test. If the assumptions are wrong then the proof of correctness is also wrong 11 | 12 | Assumption abbreviations : P = mathematically proven, V = Verified by at least two parties including at least partially by the author, 13 | UV = Computed by the author, but unverified by external parties, 14 | SP = statistically probable, applies to analyzing external software that corroborates well with proven bounds 15 | 16 | */ 17 | 18 | use number_theory::NumberTheory; 19 | 20 | /* 21 | 22 | Description Assumptions Approximate Time 23 | _______________________________________________________________________________________________________________ 24 | Exhaustive proof of Correctness of Erastothenes sieve in computing pi(n) (P) 5E+3 s 25 | deterministic primality Correctness of strong fermat test (P) 26 | for integers under Correctness of Feitsma-Galway pseudoprime table (V) 27 | 2^64 + 2^44 Correctness of J.A Sory's reduction and extension of table (UV) 28 | Correctness of Kim Walisch's implementation of Gourdon's pi(n) (SP) 29 | Correctness of Damgard et .al in pseudoprime density (P) 30 | 31 | 32 | */ 33 | 34 | /* 35 | Description Assumptions Approximate Time 36 | ___________________________________________________________________________________________________________________________ 37 | 38 | Disproof of Polya's Polya's conjecture is false (P) Factorization is currently too slow 39 | conjecture and bound Louiville function implementation is consistent 40 | checking of values of regardless of the size of input (P) 41 | Louiville function Factorization algorithm is correct (P) 42 | 43 | */ 44 | 45 | /* 46 | fn liouville(){ 47 | 48 | let lvsum = let lsum = |x : u32| { 49 | let mut sum = -1i8; 50 | for j in 2..x+1{ 51 | sum+=j.louiville(); 52 | } 53 | return sum 54 | }; 55 | 56 | } 57 | */ 58 | /* 59 | Description Assumptions Approximate Time 60 | ___________________________________________________________________________________________________________________________ 61 | 62 | Proof of GCD correctness GCD implementation is consistent 63 | 64 | 65 | */ 66 | //#[ignore] 67 | #[test] 68 | fn strong_gcd() { 69 | let _euler_totient = 0u64; 70 | /* 71 | for i in 0..u32::MAX { 72 | if i.euclid_gcd(&u32::MAX) == 1 { 73 | euler_totient += 1; 74 | } 75 | } 76 | assert_eq!(2147483648, euler_totient); 77 | */ 78 | for _ in 0..1_000_000_000 { 79 | // One billion iterations 80 | 81 | let au64 = u64::rng(); 82 | let bu64 = u64::rng(); 83 | 84 | let (gcd, a_inv, b_inv) = au64.extended_gcd(bu64); 85 | 86 | if gcd == au64 || gcd == bu64 { 87 | // N extended gcd check 88 | assert_eq!((a_inv, b_inv), (0, 1)) 89 | } else { 90 | assert_eq!(au64.product_residue(a_inv, bu64), gcd); 91 | assert_eq!(bu64.product_residue(b_inv, au64), gcd); 92 | } 93 | 94 | // Z extended gcd check 95 | 96 | let ai64 = i64::rng(); 97 | let bi64 = i64::rng(); 98 | 99 | let (gcd, x, y) = ai64.extended_gcd(bi64); 100 | 101 | assert_eq!(ai64 * x + bi64 * y, gcd) 102 | } 103 | } 104 | /* 105 | 106 | Description Assumptions Approximate Time 107 | ___________________________________________________________________________________________________________________________ 108 | 109 | Proof of Extended Euclidean gcd(a,b) = a*x + b*y (P) 110 | correctness EEA implementation is consistent 111 | 112 | 113 | 114 | Description Assumptions Approximate Time 115 | ___________________________________________________________________________________________________________________________ 116 | */ 117 | 118 | /* 119 | Description Assumptions Approximate Time 120 | ___________________________________________________________________________________________________________________________ 121 | 122 | */ 123 | #[ignore] 124 | #[test] 125 | fn factor_test() { 126 | /* 127 | // extremely slow, factorization must be improved 128 | let factorprod = |x: Vec| -> u64 { 129 | let mut prod = 1; 130 | 131 | for i in 0..x.len() / 2 { 132 | prod *= x[i * 2].pow(x[i * 2 + 1] as u32) 133 | } 134 | return prod; 135 | }; 136 | 137 | for i in 1u64..u64::MAX >> 44 { 138 | assert_eq!(i, factorprod(i.factor())) 139 | } 140 | 141 | for _i in 0..1000 { 142 | // 60s critical to improve 143 | let rand = u64::rng(); 144 | assert_eq!(rand, factorprod(rand.factor())) 145 | } 146 | */ 147 | } 148 | -------------------------------------------------------------------------------- /src/primitive/pointer.rs: -------------------------------------------------------------------------------- 1 | use crate::traits::NumberTheory; 2 | use crate::result::NTResult; 3 | 4 | impl NumberTheory for usize { 5 | fn rng() -> Self { 6 | u64::rng() as usize 7 | } 8 | 9 | fn residue(&self, ring: &Self) -> Self { 10 | (*self as u64).residue(&(*ring as u64)) as usize 11 | } 12 | fn euclidean_div(&self, other: &Self) -> (Self, Self) { 13 | let (quo, rem) = (*self as u64).euclidean_div(&(*other as u64)); 14 | (quo as usize, rem as usize) 15 | } 16 | 17 | fn is_sprp(&self, other: &Self) -> bool { 18 | (*self as u64).is_sprp(&(*other as u64)) 19 | } 20 | 21 | fn is_prime(&self) -> bool { 22 | (*self as u64).is_prime() 23 | } 24 | 25 | fn prime_proof(&self) -> (bool, Vec) { 26 | let (flag, cert) = (*self as u64).prime_proof(); 27 | ( 28 | flag, 29 | cert.iter().map(|x| *x as usize).collect::>(), 30 | ) 31 | } 32 | 33 | fn prime_list(&self, sup: &Self) -> Vec { 34 | let list = (*self as u64).prime_list(&(*sup as u64)); 35 | list.iter().map(|x| *x as usize).collect::>() 36 | } 37 | 38 | fn nth_prime(&self) -> NTResult { 39 | (*self as u64).nth_prime().map(|y| y as usize) 40 | } 41 | 42 | fn max_exp(&self) -> (Self, Self){ 43 | let (base,exp) = (*self as u64).max_exp(); 44 | (base as usize, exp as usize) 45 | } 46 | 47 | fn pi(&self) -> Self { 48 | (*self as u64).pi() as usize 49 | } 50 | 51 | fn prime_gen(x: u32) -> NTResult { 52 | u64::prime_gen(x).map(|z| z as usize) 53 | } 54 | 55 | fn factor(&self) -> Vec { 56 | let list = (*self as u64).factor(); 57 | list.iter().map(|x| *x as usize).collect::>() 58 | } 59 | 60 | fn checked_factor(&self) -> NTResult>{ 61 | if *self == 0{ 62 | return NTResult::InfiniteSet 63 | } 64 | if *self == 1{ 65 | return NTResult::DNE 66 | } 67 | 68 | NTResult::Eval(self.factor()) 69 | } 70 | 71 | 72 | fn sqrt(&self) -> (Self, Self) { 73 | ((*self as u64).sqrt().0 as usize, 0) 74 | } 75 | 76 | fn nth_root(&self, n: &Self) -> (Self, Self) { 77 | ((*self as u64).nth_root(&(*n as u64)).0 as usize, 0) 78 | } 79 | 80 | fn gcd(&self, other: &Self) -> Self { 81 | (*self as u64).gcd(&(*other as u64)) as usize 82 | } 83 | 84 | fn extended_gcd(&self, other: &Self) -> (Self, Self, Self) { 85 | let (gcd, s_inv, o_inv) = (*self as u64).extended_gcd(&(*other as u64)); 86 | (gcd as usize, s_inv as usize, o_inv as usize) 87 | } 88 | 89 | fn lcm(&self, other: &Self) -> Self { 90 | (*self as u64).lcm(&(*other as u64)) as usize 91 | } 92 | 93 | fn checked_lcm(&self, other: &Self) -> NTResult { 94 | (*self as u64) 95 | .checked_lcm(&(*other as u64)) 96 | .map(|y| y as usize) 97 | } 98 | 99 | fn euler_totient(&self) -> Self { 100 | (*self as u64).euler_totient() as usize 101 | } 102 | 103 | fn jordan_totient(&self, k: &Self) -> NTResult { 104 | (*self as u64) 105 | .jordan_totient(&(*k as u64)) 106 | .map(|y| y as usize) 107 | } 108 | 109 | fn carmichael_totient(&self) -> NTResult{ 110 | (*self as u64).carmichael_totient().map(|y| y as usize) 111 | } 112 | 113 | fn dedekind_psi(&self, k: &Self) -> NTResult { 114 | (*self as u64) 115 | .dedekind_psi(&(*k as u64)) 116 | .map(|y| y as usize) 117 | } 118 | 119 | fn product_residue(&self, other: &Self, n: &Self) -> Self { 120 | (*self as u64).product_residue(&(*other as u64), &(*n as u64)) as usize 121 | } 122 | 123 | fn checked_product_residue(&self, other: &Self, n: &Self) -> NTResult { 124 | (*self as u64).checked_product_residue(&(*other as u64), &(*n as u64)).map(|y| y as usize) 125 | } 126 | 127 | fn quadratic_residue(&self, n: &Self) -> Self { 128 | (*self as u64).quadratic_residue(&(*n as u64)) as usize 129 | } 130 | 131 | fn checked_quadratic_residue(&self, n: &Self) -> NTResult { 132 | (*self as u64).checked_quadratic_residue(&(*n as u64)).map(|y| y as usize) 133 | } 134 | 135 | fn exp_residue(&self, pow: &Self, n: &Self) -> Self { 136 | (*self as u64).exp_residue(&(*pow as u64), &(*n as u64)) as usize 137 | } 138 | 139 | fn checked_exp_residue(&self, pow: &Self, n: &Self) -> NTResult { 140 | (*self as u64) 141 | .checked_exp_residue(&(*pow as u64), &(*n as u64)) 142 | .map(|y| y as usize) 143 | } 144 | 145 | fn k_free(&self, k: &Self) -> bool { 146 | (*self as u64).k_free(&(*k as u64)) 147 | } 148 | 149 | 150 | fn radical(&self) -> NTResult{ 151 | (*self as u64).radical().map(|y| y as usize) 152 | } 153 | 154 | fn smooth(&self) -> NTResult { 155 | (*self as u64).smooth().map(|y| y as usize) 156 | } 157 | 158 | fn is_smooth(&self, b: &Self) -> bool { 159 | (*self as u64).is_smooth(&(*b as u64)) 160 | } 161 | 162 | fn legendre(&self, p: &Self) -> i8 { 163 | (*self as u64).legendre(&(*p as u64)) 164 | } 165 | 166 | fn liouville(&self) -> i8 { 167 | (*self as u64).liouville() 168 | } 169 | 170 | fn derivative(&self) -> NTResult{ 171 | (*self as u64).derivative().map(|y| y as usize) 172 | } 173 | 174 | fn checked_legendre(&self, p: &Self) -> NTResult { 175 | (*self as u64).checked_legendre(&(*p as u64)) 176 | } 177 | 178 | fn mangoldt(&self) -> f64 { 179 | (*self as u64).mangoldt() 180 | } 181 | 182 | fn mobius(&self) -> i8 { 183 | (*self as u64).mobius() 184 | } 185 | 186 | 187 | fn jacobi(&self, p: &Self) -> i8 { 188 | (*self as u64).jacobi(&(*p as u64)) 189 | } 190 | 191 | fn checked_jacobi(&self, p: &Self) -> NTResult { 192 | (*self as u64).checked_jacobi(&(*p as u64)) 193 | } 194 | 195 | fn kronecker(&self, k: &Self) -> i8 { 196 | (*self as u64).kronecker(&(*k as u64)) 197 | } 198 | 199 | } 200 | -------------------------------------------------------------------------------- /verification/hash: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hashes for each source code file as computed by OpenSSL version {OpenSSL 1.1.1n 15 Mar 2022} (from Debian 11 stable repository) 4 | 5 | 6 | HASH 7 | 8 | 9 | File Name (SHA-512-256 & SHA-3-256) 10 | ____________________________________________________________________________________________________________________________ 11 | 12 | Cargo.toml 734abb70413dcf16cbeee2822282ecaf6c4b265cdd540efaf1bb35f2828f0cc4 13 | 633bdf5984c24c5f3a88b5b63cc14cb9e90e33d507149dfa587fd3e0337217e0 14 | 15 | /src/arithmetic/conversion.rs fd75a8a7386b8db7ac254e28c91dc60cd27973885cbb980dae28935e8703c53f 16 | aba2f6d5a811b06fb60952d94f0d742dfb2c344118397fbf4f5da4794048146c 17 | 18 | /src/arithmetic/inlineops.rs c7348eaecb2404997e91fc86f60ba5dadc2abada443ad4f7cede19b486ba3d8f 19 | 68e9b216773785786faa440e54499547216cfeab794a782b888b794644444215 20 | 21 | /src/arithmetic/mpz.rs 7e594461211526170f4318630ab1727becd3b8976a5b221360582f7dd817c989 22 | f60a5e494b7975afe51f467c21860709b5d0d2ac2966fed56c7c1e561e7131ab 23 | 24 | /src/arithmetic/mpz_arith.rs 61f25f348c02c33848e283d616b094e8d84899f0dc7a650f11de15486354a59f 25 | 297dfb4836d39d82dc5906851f58835eb3272f0844052527d01cf5091beb489f 26 | 27 | /src/arithmetic/mpz_ent.rs 92b9756d0a124f1260d3ab1841c96f791f3ee960078a20184d6b332ba3fdc2a3 28 | 94b783844413663480470686389f2f8a5c0cd707ac24f541978c5a54ab3d4a08 29 | 30 | /src/arithmetic/mpz_prime.rs 48685d75c514477fc7ff318c1890aea816abe0a5c67e68fa89dc52a233819d5b 31 | 3abaaec4f0b012d8afd0030b32c7a80f21330d338a640bbb569322bf6e86b1b1 32 | 33 | /src/arithmetic/muldiv.rs fe74988e27aa53f4d3e786a2882175c5205870e5db5120879167048cab050533 34 | ade1a3fb1afd5f4c85596af1018d60581a29014e45316ef4630e717e0f9e36d7 35 | 36 | /src/arithmetic/sign.rs 3d6f327878c619c181ddbd9ccb2356d655c278c0d7177530ec7e0ec78d536f41 37 | f3406b90f53ab49f222bb93bd07bf95c87c3d229f4f36dc96c1124aa4bb74d9f 38 | 39 | /src/arithmetic/sliceops.rs 3e7d2cc1a138f0b40a0b9e88e4d1de8c905c56e17d845de0176a488f92bca50a 40 | 340e0390527ec65e2f546974c528fd0aa9e2d46b6affa64ea317741e7dee6d5a 41 | 42 | /src/arithmetic.rs 1beb2ce6932ff3b8a25ac7c70d6016cb92f3502a289e784dddcb9b1c2692c92e 43 | a75c7e05ab7c843f67d9b110dc3919296d4e2b2af77caf4de81c9e4ec7ab897c 44 | 45 | /src/data/hashtable.rs d7fef8dec687835739d522a32f4cc9fb89c05d4d37a22e9f1e7eb6ffb09e0700 46 | 2360d5b129229b603bbc357230246a3ad53afe6e947aafc43973f3c45182656c 47 | 48 | /src/data/nt_data.rs 522c0e592496efaaacf65ef0682113b4eec2f9630b757e5e6d703b11842e5da6 49 | 962fba433e3679487e3e55952a7dedccf9a6162ba530ca57e66d9fa315b433ad 50 | 51 | /src/data/primes.rs ab4e0d9d8bcc2b737a0313325f4b1b6a5d82e4f14ca334e5685f533b4a4ea572 52 | 0cb00f10934db1e49c70c2b9582fec7391e0bd054d2ae6a1746cbc20cdc1d45c 53 | 54 | /src/data.rs e43e2d29c7322a7c2a80d0c6e7cc9b70ee8987950df4728c7ee1f4db44eb89e3 55 | c4fe15ffe119c56bc9e69ccc6341c47298b7160c9eb09414ebe3d63dfb983616 56 | 57 | /src/lib.rs 6b98a9b525a3a1d94591eee2a036b30ce9c2cc281dcb9e24c6bfe79bffd2ad08 58 | 92ad05fabbe841e9750a9a07e7c96c578ce8c878b2ecdcfe2f6cd541d0091ac9 59 | 60 | /src/montgomery.rs be9f81fc2f34335c0be32b129588bfe8c026858bc15b9a072e662ee7930519d1 61 | e7f1b6875a0e2bbf96a728a80a6f446093ebb7da49c6dd8719ba85708eb37f1e 62 | 63 | /src/primitive/byte.rs 5c4e310933badf71fa530995c00a6f6d3b9d70a70ea618b37373ef485fdc02bc 64 | 3c40ca0442776405d4a3e94d4569416ca923cb2362672de15dc51316370592df 65 | 66 | /src/primitive/eightbytes.rs 28548a2dbac2472910549ea161631d495e15c500bf0ac87d3f91bd1e3cf8a917 67 | 93a460da027f1e994065268b60bd05e2b5fcbe951b11ae08876abb82e26d2be2 68 | 69 | /src/primitive/fourbytes.rs c1faf504505d2520d033a5869847bde389580c7d0a1aa60aea203eccc6628c1c 70 | c3eeba4e6a3cbce7ae4799aed4ca14ddd6f4a71a1dd168768a0cb6f2886937ff 71 | 72 | /src/primitive/pointer.rs 19072fe09c4ca8a1f788121148d7899855144d530e70ea0179b94b9b68add1a8 73 | 9b4c1c13a5e609f60ae625a3dc506eab3c5799f8191ce5c56371eed22f5bee06 74 | 75 | /src/primitive/signednt.rs 9423fbcbd24ed1550a358e932b9d4729609800476ee503e90afc8092f0efb667 76 | 6f552f3d22b8107e13c1841be5a2317a1631ca4c00d515d0ab5e3915c47e0af5 77 | 78 | /src/primitive/sixteenbytes.rs 507b4ad93c17021b5a0ede66d64c2f0b1ce27cdfb4395c60906fe8214d307a81 79 | efdbd90782fede4d23d8a3f9002e7a6fe97a591831521cf0c0719a9ac61e7b78 80 | 81 | 82 | /src/primitive/twobytes.rs e944d67969e7452cc996add70937b1ac69fe27791cc3c62faa06d0c380a4c570 83 | 690b6098005ec5729a31e4ac1f9593a0a47a1a1932095160f2791c20c65b3d93 84 | 85 | 86 | /src/primitive.rs 0ae7fdea9de0dbb02b6a1d93df920446e314b294bc07cdc468f97b7675b93ec3 87 | a472c1b3b2cf1c9cb9a0303f0cfe5bde689cf36ab668a9c4684f6b15e55b4a7e 88 | 89 | 90 | /src/sieve.rs 2ab8769e54f8b46282568a7a1b914e5974755894e1151380ed32264877548c98 91 | e6077f5100c2782197fa09695a1ea4d6fedafc141f135dea4a0639305630353d 92 | 93 | /src/traits.rs 1ed79d7dad13278e5d5b126da990216d2f7e74357b09561e91df52e2f643b3af 94 | 4bd506c019ecd50c4b1dab49864c909ea4cb5b39211752fce9696467dd1e88dd 95 | -------------------------------------------------------------------------------- /tests/simple_arith.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Some simple tests to demonstrate apparent correctness 4 | 5 | */ 6 | 7 | use number_theory::Mpz; 8 | use number_theory::NumberTheory; 9 | //#[ignore] 10 | #[test] 11 | 12 | fn arith() { 13 | assert_eq!( 14 | Mpz::zero() 15 | .ref_addition(&Mpz::from_string("222222222222222222222").unwrap()) 16 | .to_string(), 17 | "222222222222222222222" 18 | ); 19 | assert_eq!( 20 | Mpz::one() 21 | .ref_addition(&Mpz::from_string("222222222222222222222").unwrap()) 22 | .to_string(), 23 | "222222222222222222223" 24 | ); 25 | 26 | assert_eq!( 27 | Mpz::from_string("222222222222222222222") 28 | .unwrap() 29 | .ref_addition(&Mpz::from_string("222222222222222222223").unwrap()) 30 | .to_string(), 31 | "444444444444444444445" 32 | ); 33 | assert_eq!( 34 | Mpz::from_string("222222222222222222222") 35 | .unwrap() 36 | .ref_addition(&Mpz::from_string("222222222222222222222222222222222222222222").unwrap()) 37 | .to_string(), 38 | "222222222222222222222444444444444444444444" 39 | ); 40 | 41 | assert_eq!( 42 | Mpz::from_string("222222222222222222222222222222222222222222") 43 | .unwrap() 44 | .ref_addition(&Mpz::from_string("222222222222222222222").unwrap()) 45 | .to_string(), 46 | "222222222222222222222444444444444444444444" 47 | ); 48 | 49 | assert_eq!( 50 | Mpz::from_string("-222222222222222222222222222222222222222222") 51 | .unwrap() 52 | .ref_addition(&Mpz::from_string("222222222222222222222").unwrap()) 53 | .to_string(), 54 | "-222222222222222222222000000000000000000000" 55 | ); 56 | 57 | assert_eq!( 58 | Mpz::from_string("-222222222222222222222222222222222222222222") 59 | .unwrap() 60 | .ref_addition(&Mpz::from_string("-222222222222222222222").unwrap()) 61 | .to_string(), 62 | "-222222222222222222222444444444444444444444" 63 | ); 64 | 65 | assert_eq!( 66 | Mpz::from_string("222222222222222222222222222222222222222222") 67 | .unwrap() 68 | .ref_addition(&Mpz::from_string("-222222222222222222222").unwrap()) 69 | .to_string(), 70 | "222222222222222222222000000000000000000000" 71 | ); 72 | 73 | // Product 74 | assert_eq!( 75 | Mpz::zero().ref_product(&Mpz::u_new(vec![1, 2])), 76 | Mpz::zero() 77 | ); 78 | assert_eq!( 79 | Mpz::one().ref_product(&Mpz::u_new(vec![1, 2])), 80 | Mpz::u_new(vec![1, 2]) 81 | ); 82 | 83 | assert_eq!( 84 | Mpz::one().ref_product(&Mpz::u_new(vec![1, 2])), 85 | Mpz::u_new(vec![1, 2, 0]) 86 | ); 87 | 88 | assert_eq!( 89 | Mpz::from_string("222222222222222222222") 90 | .unwrap() 91 | .ref_product(&Mpz::from_string("222222222222222222222").unwrap()) 92 | .to_string(), 93 | "49382716049382716049283950617283950617284" 94 | ); 95 | 96 | assert_eq!( 97 | Mpz::from_string("-222222222222222222222") 98 | .unwrap() 99 | .ref_product(&Mpz::from_string("-222222222222222222222").unwrap()) 100 | .to_string(), 101 | "49382716049382716049283950617283950617284" 102 | ); 103 | 104 | assert_eq!( 105 | Mpz::from_string("222222222222222222222") 106 | .unwrap() 107 | .ref_product(&Mpz::from_string("-222222222222222222222").unwrap()) 108 | .to_string(), 109 | "-49382716049382716049283950617283950617284" 110 | ); 111 | 112 | assert_eq!( 113 | Mpz::from_string("-222222222222222222222") 114 | .unwrap() 115 | .ref_product(&Mpz::from_string("222222222222222222222").unwrap()) 116 | .to_string(), 117 | "-49382716049382716049283950617283950617284" 118 | ); 119 | // exponentiation 120 | assert_eq!( 121 | Mpz::from(33i64).pow(42).to_string(), 122 | "5992188611668647482797702362673386216879593110712943882639341889" 123 | ); 124 | assert_eq!( 125 | Mpz::from(-33i64).pow(42).to_string(), 126 | "5992188611668647482797702362673386216879593110712943882639341889" 127 | ); 128 | assert_eq!( 129 | Mpz::from(-33i64).pow(43).to_string(), 130 | "-197742224185065366932324177968221745157026572653527148127098282337" 131 | ); 132 | } 133 | 134 | // #[ignore] 135 | #[test] 136 | 137 | fn math() { 138 | assert_eq!( 139 | Mpz::from(33u64).pow(43).sqrt().0.to_string(), 140 | "444682160857690992367099165368543" 141 | ); // sqrt 142 | assert_eq!(Mpz::from(33u64).pow(43).ln(), 150.34982514305864); // natural logarithm 143 | assert_eq!( 144 | Mpz::from(33u64).pow(43).log(2.718281828459045235), 145 | 150.34982514305864 146 | ); // natural logarithm 147 | 148 | //assert_eq!(Mpz::from_u64(2).shl(65536).iter_log(2.0),4); 149 | } 150 | 151 | //#[ignore] 152 | #[test] 153 | fn bitwise() { 154 | // Shift-right ops 155 | assert_eq!(Mpz::from(1u64).shl(0), Mpz::one()); 156 | assert_eq!(Mpz::from(1u64).shl(1), Mpz::two()); 157 | assert_eq!( 158 | Mpz::one().shl(65), 159 | Mpz::from(36893488147419103232u128) 160 | ); 161 | assert_eq!( 162 | Mpz::from_string("2864165568461618616184867") 163 | .unwrap() 164 | .shl(65) 165 | .to_string(), 166 | "105669058452284624487376314655265894269190144" 167 | ); 168 | 169 | assert_eq!(Mpz::from(1u64).shr(0), Mpz::one()); 170 | assert_eq!(Mpz::from(1u64).shr(1), Mpz::zero()); 171 | assert_eq!(Mpz::from(1u64).shr(15), Mpz::zero()); 172 | assert_eq!( 173 | Mpz::from(36893488147419103232u128) 174 | .shr(65), 175 | Mpz::one() 176 | ); 177 | 178 | assert_eq!( 179 | Mpz::from(16841684685u64) 180 | .and(&Mpz::from(1854618418u64)), 181 | Mpz::from(1786982912u64) 182 | ); 183 | 184 | assert_eq!( 185 | Mpz::from(16841684685u64) 186 | .or(&Mpz::from(1854618418u64)), 187 | Mpz::from(16909320191u64) 188 | ); 189 | assert_eq!( 190 | Mpz::from(16841684685u64) 191 | .xor(&Mpz::from(1854618418u64)), 192 | Mpz::from(15122337279u64) 193 | ); 194 | } 195 | -------------------------------------------------------------------------------- /src/result.rs: -------------------------------------------------------------------------------- 1 | /// Enum returned in checked functions returning any errors of evaluation 2 | #[non_exhaustive] 3 | #[derive(Clone,Debug,PartialEq,Eq)] 4 | pub enum NTResult{ 5 | /// Evaluation is knowable and was calculated in the bounds. FFI return 0 6 | Eval(T), // Flag 0 7 | /// Solution Exists but does not fit in datatype. FFI return 1 8 | Overflow, // Flag 1 9 | /// Solution Does Not Exist. FFI return 2 10 | DNE, // Flag 2 11 | /// Solution Exists but is Infinitely large. FFI return 3 12 | Infinite, // Flag 3 13 | /// Solutions exist but there are infinite number of them (all elements of Z). FFI return 4 14 | InfiniteSet, // Flag 4 15 | /// Computation exceeds some practical bound. FFI return 5 16 | CompExceeded, // Flag 5 17 | /// Overflowed during computation result does not necessarily exceed the datatype. FFI return 6 18 | CompOverflow, // Flag 6 19 | /// Function is not defined for the inputs. FFI return 7 20 | Undefined, // Flag 7 21 | } 22 | 23 | 24 | impl NTResult{ 25 | 26 | /// Returns the Evaluated number 27 | pub fn unwrap(&self) -> T{ 28 | match self{ 29 | NTResult::Eval(x) => x.clone(), 30 | NTResult::Overflow=> panic!("Result does not fit in datatype"), 31 | NTResult::DNE => panic!("Does Not Exist"), 32 | NTResult::Infinite => panic!("Infinitely large solution"), 33 | NTResult::InfiniteSet => panic!("Infinite number of solutions"), 34 | NTResult::CompExceeded => panic!("Computation exceeded hardcoded limit"), 35 | NTResult::CompOverflow => panic!("Overflowed during computation"), 36 | NTResult::Undefined => panic!("Undefined solution"), 37 | } 38 | } 39 | 40 | /// Returns the result or panics with the selected message 41 | pub fn expect(&self, signal: &str)-> T{ 42 | match self{ 43 | NTResult::Eval(x) => x.clone(), 44 | _=> panic!("{}",signal) 45 | } 46 | } 47 | /// Returns the result or the selected default 48 | pub fn unwrap_or(&self, res: T) -> T{ 49 | match self{ 50 | NTResult::Eval(x) => x.clone(), 51 | _=> res, 52 | } 53 | } 54 | /// Checks if the result is infinitely large 55 | pub fn is_infinite(&self) -> bool{ 56 | match self{ 57 | NTResult::Infinite => true, 58 | _=> false, 59 | } 60 | } 61 | /// Checks if the result is an infinite set 62 | pub fn is_infiniteset(&self) -> bool{ 63 | match self{ 64 | NTResult::InfiniteSet => true, 65 | _=> false, 66 | } 67 | } 68 | /// Checks if the solution to the function exists 69 | pub fn is_dne(&self) -> bool{ 70 | match self{ 71 | NTResult::DNE => true, 72 | _=> false, 73 | } 74 | } 75 | /// Checks if the result exceeds the datatype max 76 | pub fn is_overflow(&self) -> bool{ 77 | match self{ 78 | NTResult::Overflow => true, 79 | _=> false, 80 | } 81 | } 82 | /// Checks if there was an overflow during computation 83 | pub fn is_comp_overflow(&self) -> bool{ 84 | match self{ 85 | NTResult::CompOverflow => true, 86 | _=> false, 87 | } 88 | } 89 | /// Checks if the Computation exceeded 90 | pub fn is_comp_exceeded(&self) -> bool{ 91 | match self{ 92 | NTResult::CompExceeded => true, 93 | _=> false, 94 | } 95 | } 96 | /// Checks if the result is undefined 97 | pub fn is_undefined(&self) -> bool{ 98 | match self{ 99 | NTResult::Undefined => true, 100 | _=> false, 101 | } 102 | } 103 | /** Converts from Option to NTResult. None values are converted to the selected NTResult variant 104 | ``` 105 | use number_theory::NumberTheory; 106 | use number_theory::NTResult; 107 | 108 | // A None is returned here due to exceeding the i8::MAX 109 | let res = 255u8.checked_add(1); 110 | // The None option is converted to an NTResult::Overflow 111 | 112 | let convert = NTResult::from_option(res,NTResult::Overflow); 113 | 114 | assert_eq!(convert, NTResult::Overflow); 115 | 116 | // An Some result is converted to Eval 117 | 118 | let res = 254u8.checked_add(1); 119 | let convert = NTResult::from_option(res,NTResult::Overflow); 120 | 121 | assert_eq!(convert, NTResult::Eval(255)); 122 | ``` 123 | */ 124 | pub fn from_option( opt: Option, ntvariant: Self) -> Self{ 125 | match opt{ 126 | Some(x) => NTResult::Eval(x), 127 | None => ntvariant, 128 | } 129 | } 130 | 131 | /// Maps within the Enum 132 | pub fn map U>(&self, func : F) -> NTResult{ 133 | match self { 134 | NTResult::Eval(x) => NTResult::Eval((func)(x.clone())), 135 | NTResult::Overflow => NTResult::Overflow, 136 | NTResult::DNE => NTResult::DNE, 137 | NTResult::Infinite => NTResult::Infinite, 138 | NTResult::InfiniteSet => NTResult::InfiniteSet, 139 | NTResult::CompExceeded => NTResult::CompExceeded, 140 | NTResult::CompOverflow => NTResult::CompOverflow, 141 | NTResult::Undefined => NTResult::Undefined, 142 | } 143 | } 144 | /** Return value and flag for FFI bindings 145 | ``` 146 | use number_theory::NumberTheory; 147 | // Attempt to factor 0 148 | let res = 0.factor(); 149 | // Fails and returns a NTResult that can be decomposed to an empty vec 150 | // and 4 for C api binding 151 | 152 | 153 | let res = 1.factor(); 154 | // Likewise an DNE NTResult gets converted to a vec and 2 155 | 156 | 157 | let res = 9.factor(); 158 | // Finally a fully factorable integer gets a vector and the 0 flag 159 | 160 | ``` 161 | 162 | */ 163 | pub fn ffi(&self)-> (T,u8){ 164 | match self { 165 | NTResult::Eval(x) => (x.clone(),0u8), 166 | NTResult::Overflow => (T::default(),1u8), 167 | NTResult::DNE => (T::default(),2u8), 168 | NTResult::Infinite => (T::default(),3u8), 169 | NTResult::InfiniteSet => (T::default(),4u8), 170 | NTResult::CompExceeded => (T::default(),5u8), 171 | NTResult::CompOverflow => (T::default(),6u8), 172 | NTResult::Undefined => (T::default(),7u8), 173 | } 174 | } 175 | 176 | } 177 | 178 | impl std::fmt::Display for NTResult{ 179 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 180 | match self{ 181 | 182 | NTResult::Eval(x) => write!(f,"{}",x), 183 | NTResult::Overflow => write!(f,"Overflow"), 184 | NTResult::DNE => write!(f,"DNE"), 185 | NTResult::Infinite => write!(f,"Infinite"), 186 | NTResult::InfiniteSet => write!(f,"Infinite solutions"), 187 | NTResult::CompExceeded => write!(f,"Exceeded computation bound"), 188 | NTResult::CompOverflow => write!(f,"Overflowed during computation"), 189 | NTResult::Undefined => write!(f,"Undefined"), 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/primitive/montprim.rs: -------------------------------------------------------------------------------- 1 | use crate::ntrait::MontArith; 2 | 3 | impl MontArith for u32{ 4 | 5 | fn to_mont(self, ring: Self) -> Self{ 6 | (((self as u64)<<32) % (ring as u64)) as u32 7 | } 8 | 9 | fn inv_2(self) -> Self{ 10 | let mut est = machine_prime::INV_8[((self>>1) & 0x7F) as usize] as u32; 11 | est = 2u32.wrapping_sub(est.wrapping_mul(self)).wrapping_mul(est); 12 | est = 2u32.wrapping_sub(est.wrapping_mul(self)).wrapping_mul(est); 13 | est 14 | } 15 | 16 | fn n_identity(self) -> Self{ 17 | (u32::MAX%self).wrapping_add(1) 18 | } 19 | 20 | fn mont_sub(self, subtrahend: Self, ring: Self) -> Self{ 21 | if subtrahend > self { 22 | return ring.wrapping_sub(subtrahend.wrapping_sub(self)); 23 | } 24 | self.wrapping_sub(subtrahend) 25 | } 26 | 27 | fn to_z(self,inv: Self, ring: Self) -> Self{ 28 | let lo = self.wrapping_mul(inv); 29 | let lo = ((lo as u64).wrapping_mul(ring as u64)>>32) as u32; 30 | 31 | lo.wrapping_neg().wrapping_add(ring) 32 | 33 | } 34 | 35 | fn mont_add(self, addend: Self, ring: Self) -> Self{ 36 | unimplemented!() 37 | } 38 | 39 | fn mont_prod(self, multiplicand: Self, inv: Self, ring: Self) -> Self{ 40 | let interim = (self as u64).wrapping_mul(multiplicand as u64); 41 | let lo = (interim as u32).wrapping_mul(inv); 42 | let lo = ((lo as u64).wrapping_mul(ring as u64)>>32) as u32; 43 | let hi = (interim>>32) as u32; 44 | 45 | if hi < lo{ 46 | hi.wrapping_sub(lo).wrapping_add(ring) 47 | } 48 | else{ 49 | hi.wrapping_sub(lo) 50 | } 51 | 52 | } 53 | 54 | fn mont_sqr(self, inv: Self, ring: Self) -> Self{ 55 | self.mont_prod(self,inv,ring) 56 | } 57 | 58 | fn mont_pow(self,mut one: Self,mut p: Self, inv: Self, ring: Self) -> Self{ 59 | let mut base = self; 60 | while p > 1 { 61 | if p & 1 == 0 { 62 | base = base.mont_sqr(inv, ring); 63 | p >>= 1; 64 | } else { 65 | one = one.mont_prod(base, inv, ring); 66 | base = base.mont_sqr(inv,ring); 67 | p = (p - 1) >> 1; 68 | } 69 | } 70 | one.mont_prod(base,inv,ring) 71 | } 72 | 73 | fn odd_pow(self, p: Self, n: Self) -> Self{ 74 | 75 | let one = n.n_identity(); 76 | let base = self.to_mont(n); 77 | let npi = n.inv_2(); 78 | base.mont_pow(one,p,npi,n).to_z(npi,n) 79 | } 80 | 81 | fn even_pow(self, p: Self,n: Self) -> Self{ 82 | let mut z = 1; 83 | let mut base = self; 84 | let mut pow = p; 85 | while pow > 1 { 86 | if pow & 1 == 0 { 87 | base = base.wrapping_mul(base)&n; 88 | pow >>= 1; 89 | } else { 90 | z = base.wrapping_mul(z)&n; 91 | base = base.wrapping_mul(base)&n; 92 | pow = (pow - 1) >> 1 93 | } 94 | } 95 | base.wrapping_mul(z)&n 96 | } 97 | 98 | } 99 | 100 | 101 | impl MontArith for u64{ 102 | 103 | fn to_mont(self, ring: Self) -> Self{ 104 | machine_prime::to_mont(self,ring) 105 | } 106 | 107 | fn inv_2(self) -> Self{ 108 | machine_prime::mul_inv2(self) 109 | } 110 | 111 | fn n_identity(self) -> Self{ 112 | machine_prime::one_mont(self) 113 | } 114 | 115 | fn mont_sub(self, subtrahend: Self, ring: Self) -> Self{ 116 | machine_prime::mont_sub(self,subtrahend,ring) 117 | } 118 | 119 | fn mont_add(self, addend: Self, ring: Self) -> Self{ 120 | unimplemented!() 121 | } 122 | 123 | fn to_z(self,inv: Self, ring: Self) -> Self{ 124 | let lo = self.wrapping_mul(inv); 125 | let lo = ((lo as u128).wrapping_mul(ring as u128)>>64) as u64; 126 | 127 | lo.wrapping_neg().wrapping_add(ring) 128 | 129 | } 130 | 131 | fn mont_prod(self, multiplicand: Self, inv: Self, ring: Self) -> Self{ 132 | machine_prime::mont_prod(self,multiplicand,ring,inv) 133 | } 134 | 135 | fn mont_sqr(self, inv: Self, ring: Self) -> Self{ 136 | self.mont_prod(self,inv,ring) 137 | } 138 | 139 | fn mont_pow(self,one: Self,p: Self, inv: Self, ring: Self) -> Self{ 140 | machine_prime::mont_pow(self,one,p,ring,inv) 141 | } 142 | 143 | fn odd_pow(self, p: Self, n: Self) -> Self{ 144 | 145 | let one = n.n_identity(); 146 | let base = self.to_mont(n); 147 | let npi = n.inv_2(); 148 | if base < 2{ 149 | return base; 150 | } 151 | base.mont_pow(one,p,npi,n).to_z(npi,n) 152 | } 153 | 154 | fn even_pow(self, mut pow: Self,n: Self) -> Self{ 155 | let mut z = 1; 156 | let mut base = self; 157 | while pow > 1 { 158 | if pow & 1 == 0 { 159 | base = base.wrapping_mul(base)&n; 160 | pow >>= 1; 161 | } else { 162 | z = base.wrapping_mul(z)&n; 163 | base = base.wrapping_mul(base)&n; 164 | pow = (pow - 1) >> 1 165 | } 166 | } 167 | base.wrapping_mul(z)&n 168 | } 169 | 170 | } 171 | 172 | #[inline(always)] 173 | const fn split_to_u128(x: u128) -> (u128, u128) { 174 | let (lo,hi) = unsafe { std::mem::transmute::(x) }; 175 | (hi as u128, lo as u128) 176 | } 177 | 178 | 179 | 180 | impl MontArith for u128{ 181 | 182 | fn to_mont(self, ring: Self) -> Self{ 183 | machine_prime::to_mont_128(self%ring,ring) 184 | } 185 | 186 | fn inv_2(self) -> Self{ 187 | machine_prime::mul_inv2_128(self) 188 | } 189 | 190 | fn n_identity(self) -> Self{ 191 | machine_prime::one_mont_128(self) 192 | } 193 | 194 | fn to_z(self, inv: Self, ring: Self) -> Self{ 195 | let lo = self.wrapping_mul(inv); 196 | let lo = machine_prime::u256prod_lo(lo,ring); 197 | 198 | lo.wrapping_neg().wrapping_add(ring) 199 | } 200 | 201 | 202 | fn mont_sub(self, subtrahend: Self, ring: Self) -> Self{ 203 | machine_prime::mont_sub_128(self,subtrahend,ring) 204 | } 205 | 206 | fn mont_add(self, addend: Self, ring: Self) -> Self{ 207 | unimplemented!() 208 | } 209 | 210 | fn mont_prod(self, multiplicand: Self, inv: Self, ring: Self) -> Self{ 211 | machine_prime::mont_prod_128(self,multiplicand,ring,inv) 212 | } 213 | 214 | fn mont_sqr(self, inv: Self, ring: Self) -> Self{ 215 | machine_prime::mont_sqr_128(self,ring,inv) 216 | } 217 | 218 | //fn mont_pow(self,one: Self,p: Self, inv: Self, ring: Self) -> Self{ 219 | fn mont_pow(self,mut one: Self,mut p: Self, inv: Self, ring: Self) -> Self{ 220 | machine_prime::mont_pow_128(self,one,p,ring,inv) 221 | } 222 | 223 | fn odd_pow(self, p: Self, n: Self) -> Self{ 224 | 225 | let one = n.n_identity(); 226 | let base = self.to_mont(n); 227 | if base < 2{ 228 | return base; 229 | } 230 | let npi = n.inv_2(); 231 | base.mont_pow(one,p,npi,n).to_z(npi,n) 232 | } 233 | 234 | fn even_pow(self, mut pow: Self,n: Self) -> Self{ 235 | let mut z = 1; 236 | let mut base = self; 237 | while pow > 1 { 238 | if pow & 1 == 0 { 239 | base = base.wrapping_mul(base)&n; 240 | pow >>= 1; 241 | } else { 242 | z = base.wrapping_mul(z)&n; 243 | base = base.wrapping_mul(base)&n; 244 | pow = (pow - 1) >> 1 245 | } 246 | } 247 | base.wrapping_mul(z)&n 248 | } 249 | } 250 | 251 | -------------------------------------------------------------------------------- /tests/simple_ent.rs: -------------------------------------------------------------------------------- 1 | use number_theory::{Mpz,NumberTheory,Sign}; 2 | 3 | #[ignore] 4 | #[test] 5 | fn primality() { 6 | // proves that integers below 2^16 are correctly evaluated 7 | assert_eq!(0u8.is_prime(), false); 8 | assert_eq!(2u8.is_prime(), true); 9 | 10 | assert_eq!(0i8.is_prime(), false); 11 | assert_eq!(2i8.is_prime(), true); 12 | 13 | let mut mersenne = Mpz::from(1u64).shl(82589933); //1290467 44 14 | mersenne.mut_subtraction(Mpz::one()); 15 | 16 | assert_eq!(mersenne.is_prime(), true); 17 | 18 | // prime ranges, for checks beyond this value see det_primality in strong_prime which proves up to 2^64+2^42 19 | 20 | let mut count = 0u32; 21 | 22 | for i in 0..u8::MAX { 23 | if i.is_prime() { 24 | count += 1 25 | } 26 | } 27 | assert_eq!(count, 54u32); 28 | 29 | for i in 255..u16::MAX { 30 | if i.is_prime() { 31 | count += 1 32 | } 33 | } 34 | 35 | assert_eq!(count, 6542u32); 36 | 37 | for i in -65535i32..0i32 { 38 | // Demonstrate that negative primes work correctly as well 39 | if i.is_prime() { 40 | count -= 1; 41 | } 42 | } 43 | assert_eq!(count, 0); 44 | let sevenbit = i8::prime_gen(7).unwrap() as u8; 45 | assert!((sevenbit < 127) && (sevenbit > 64)); 46 | 47 | let thirtybit = u32::prime_gen(30).unwrap(); 48 | assert!((thirtybit < 1073741824) && (thirtybit > 536870912)) 49 | } 50 | 51 | #[test] 52 | fn euclidean_div() { 53 | assert_eq!( 54 | 17i8.euclidean_div(5i8).0 * 5i8 + (17i8.euclidean_div(5i8).1), 55 | 17i8 56 | ); 57 | assert_eq!( 58 | 17i8.euclidean_div(-5i8).0 * -5i8 + (17i8.euclidean_div(-5i8).1), 59 | 17i8 60 | ); 61 | assert_eq!( 62 | -17i8.euclidean_div(-5i8).0 * -5i8 + (-17i8.euclidean_div(-5i8).1), 63 | -17i8 64 | ); 65 | assert_eq!( 66 | -17i8.euclidean_div(5i8).0 * 5i8 + (-17i8.euclidean_div(5i8).1), 67 | -17i8 68 | ); 69 | 70 | assert_eq!( 71 | 17i16.euclidean_div(5i16).0 * 5i16 + (17i16.euclidean_div(5i16).1), 72 | 17i16 73 | ); 74 | assert_eq!( 75 | 17i16.euclidean_div(-5i16).0 * -5i16 + (17i16.euclidean_div(-5i16).1), 76 | 17i16 77 | ); 78 | assert_eq!( 79 | -17i16.euclidean_div(-5i16).0 * -5i16 + (-17i16.euclidean_div(-5i16).1), 80 | -17i16 81 | ); 82 | assert_eq!( 83 | -17i16.euclidean_div(5i16).0 * 5i16 + (-17i16.euclidean_div(5i16).1), 84 | -17i16 85 | ); 86 | 87 | assert_eq!( 88 | 17i32.euclidean_div(5i32).0 * 5i32 + (17i32.euclidean_div(5i32).1), 89 | 17i32 90 | ); 91 | assert_eq!( 92 | 17i32.euclidean_div(-5i32).0 * -5i32 + (17i32.euclidean_div(-5i32).1), 93 | 17i32 94 | ); 95 | assert_eq!( 96 | -17i32.euclidean_div(-5i32).0 * -5i32 + (-17i32.euclidean_div(-5i32).1), 97 | -17i32 98 | ); 99 | assert_eq!( 100 | -17i32.euclidean_div(5i32).0 * 5i32 + (-17i32.euclidean_div(5i32).1), 101 | -17i32 102 | ); 103 | 104 | assert_eq!( 105 | 17i64.euclidean_div(5i64).0 * 5i64 + (17i64.euclidean_div(5i64).1), 106 | 17i64 107 | ); 108 | assert_eq!( 109 | 17i64.euclidean_div(-5i64).0 * -5i64 + (17i64.euclidean_div(-5i64).1), 110 | 17i64 111 | ); 112 | assert_eq!( 113 | -17i64.euclidean_div(-5i64).0 * -5i64 + (-17i64.euclidean_div(-5i64).1), 114 | -17i64 115 | ); 116 | assert_eq!( 117 | -17i64.euclidean_div(5i64).0 * 5i64 + (-17i64.euclidean_div(5i64).1), 118 | -17i64 119 | ); 120 | 121 | assert_eq!( 122 | 17i128.euclidean_div(5i128).0 * 5i128 + (17i128.euclidean_div(5i128).1), 123 | 17i128 124 | ); 125 | assert_eq!( 126 | 17i128.euclidean_div(-5i128).0 * -5i128 + (17i128.euclidean_div(-5i128).1), 127 | 17i128 128 | ); 129 | assert_eq!( 130 | -17i128.euclidean_div(-5i128).0 * -5i128 + (-17i128.euclidean_div(-5i128).1), 131 | -17i128 132 | ); 133 | assert_eq!( 134 | -17i128.euclidean_div(5i128).0 * 5i128 + (-17i128.euclidean_div(5i128).1), 135 | -17i128 136 | ); 137 | 138 | assert_eq!( 139 | 17i16.euclidean_div(5i16).0 * 5i16 + (17i16.euclidean_div(5i16).1), 140 | 17i16 141 | ); 142 | assert_eq!( 143 | 17i16.euclidean_div(-5i16).0 * -5i16 + (17i16.euclidean_div(-5i16).1), 144 | 17i16 145 | ); 146 | assert_eq!( 147 | -17i16.euclidean_div(-5i16).0 * -5i16 + (-17i16.euclidean_div(-5i16).1), 148 | -17i16 149 | ); 150 | assert_eq!( 151 | -17i16.euclidean_div(5i16).0 * 5i16 + (-17i16.euclidean_div(5i16).1), 152 | -17i16 153 | ); 154 | 155 | let (quo, rem) = Mpz::from(17i64).euclidean_div(Mpz::from(5i64)); 156 | assert_eq!( 157 | (quo.ref_product(&Mpz::from(5))).ref_addition(&rem), 158 | Mpz::from(17i64) 159 | ); 160 | 161 | let (quo, rem) = Mpz::from(-17).euclidean_div(Mpz::from(5i64)); 162 | assert_eq!( 163 | (quo.ref_product(&Mpz::from(5i64))).ref_addition(&rem), 164 | Mpz::from(-17i64) 165 | ); 166 | 167 | let (quo, rem) = Mpz::from(-17).euclidean_div(Mpz::from(-5i64)); 168 | assert_eq!( 169 | (quo.ref_product(&Mpz::from(-5i64))).ref_addition(&rem), 170 | Mpz::from(-17i64) 171 | ); 172 | 173 | let (quo, rem) = Mpz::from(17i64).euclidean_div(Mpz::from(-5i64)); 174 | assert_eq!( 175 | (quo.ref_product(&Mpz::from(-5i64))).ref_addition(&rem), 176 | Mpz::from(17i64) 177 | ); 178 | // i128 179 | let (quo, rem) = Mpz::from(17i128).euclidean_div(Mpz::from(5i128)); 180 | assert_eq!( 181 | (quo.ref_product(&Mpz::from(5i128))).ref_addition(&rem), 182 | Mpz::from(17i128) 183 | ); 184 | 185 | let (quo, rem) = 186 | Mpz::from(-170000000000000000000i128).euclidean_div(Mpz::from(50000000i128)); 187 | assert_eq!( 188 | (quo.ref_product(&Mpz::from(50000000i128))).ref_addition(&rem), 189 | Mpz::from(-170000000000000000000i128) 190 | ); 191 | 192 | let (quo, rem) = Mpz::from(-5i128).euclidean_div(Mpz::from(1i128)); 193 | assert_eq!(quo.to_string(), "-5"); 194 | assert_eq!(rem.to_string(), "0"); 195 | 196 | let (_quo, rem) = Mpz::from_string( 197 | "-2500000000000000000000000000000000000000000000000000000000000000000000000000000000", 198 | ) 199 | .unwrap() 200 | .euclidean_div(Mpz::from_string("50000000000000000000000000000000000000000").unwrap()); 201 | assert_eq!(rem.to_string(), "0"); 202 | 203 | let (quo,_rem) = Mpz::from_string("50000000000000000000000000000000000000000").unwrap().euclidean_div(Mpz::from_string("-2500000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()); 204 | println!("{:?}", quo); 205 | assert_eq!(quo, Mpz::unchecked_new(Sign::Positive, vec![0])); 206 | } 207 | 208 | #[rustfmt::skip] 209 | #[ignore] 210 | #[test] 211 | fn factor() { 212 | assert_eq!(0xFFFFFFF600000019u64.factor().to_string(), "4294967291^2".to_string()); 213 | assert_eq!( 214 | u64::MAX.factor().to_string(), 215 | "3 * 5 * 17 * 257 * 641 * 65537 * 6700417".to_string() 216 | ); 217 | assert_eq!( 218 | u128::MAX.factor().to_string(), 219 | "3 * 5 * 17 * 257 * 641 * 65537 * 274177 * 6700417 * 67280421310721".to_string() 220 | ) 221 | } 222 | 223 | #[test] 224 | fn k_free() { 225 | assert_eq!(108u8.k_free(4), true); 226 | assert_eq!(9375u16.k_free(5), false); 227 | 228 | let factorial = Mpz::sirp(1, 1000, 1, 0); 229 | 230 | for i in 2..15 { 231 | // Checks the power of the first 15 prime factors 232 | assert_eq!(factorial.k_free(Mpz::from(i)), false) 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /benches/titanic_prime.rs: -------------------------------------------------------------------------------- 1 | use number_theory::NumberTheory; 2 | use number_theory::Mpz; 3 | 4 | 5 | const EIGHT_WORD : [u64;256] = [ // 2^512 + k 6 | 75, 145, 285, 727, 1105, 1147, 1273, 2743, 3177, 3913, 4597, 4933, 7 | 5527, 5695, 5821, 5857, 5941, 6273, 6301, 6325, 6333, 7111, 7471, 8 | 7693, 7735, 7741, 8721, 9231, 10161,10173, 10431, 10591, 11031, 9 | 11335, 12661, 13105, 13125, 13573, 14025, 14151, 14607, 15127, 15285, 10 | 15441, 16797, 16951, 17553, 18787, 19615, 19833, 20155, 20571, 20845, 11 | 20887, 21033, 21331, 21475, 21627, 21733, 22707, 22801, 22993, 24451, 12 | 24505, 25387, 25557, 25611, 26205, 26215, 26391, 26557, 26721, 27061, 13 | 27075, 27355, 28221, 28351, 28405, 28473, 28743, 29425, 29607, 29827, 14 | 29931, 30001, 30207, 32073, 32227, 32463, 32835, 33223, 33325, 33511, 15 | 33657, 34231, 34423, 34621, 34743, 34851, 35035, 35847, 36145, 36385, 16 | 36415, 36601, 36663, 36883, 37423, 37515, 38163, 39061, 39291, 39313, 17 | 39373, 39481, 39513, 39727, 40455, 40845, 40921, 41217, 41277, 42097, 18 | 42321, 42351, 42517, 42705, 43345, 43363, 43525, 44013, 44137, 44695, 19 | 45375, 45511, 45813, 45927, 46041, 46195, 47041, 47193, 47341, 47601, 20 | 47691, 47731, 48307, 49005, 49167, 50031, 50055, 50517, 50701, 51097, 21 | 51273, 51813, 52897, 53397, 53517, 53883, 54891, 55323, 55671, 55923, 22 | 56145, 56257, 56331, 56425, 57073, 57433, 57481, 57517, 57861, 57877, 23 | 58641, 58963, 59317, 59443, 60225, 60283, 60345, 60543, 60855, 60921, 24 | 61207, 61287, 61591, 61887, 62497, 62511, 63061, 64245, 64351, 64831, 25 | 64917, 65107, 65217, 65421, 65473, 65475, 65671, 66345, 67015, 68511, 26 | 68703, 68983, 69057, 70681, 70993, 71737, 71853, 72223, 72391, 72423, 27 | 72781, 73453, 73515, 73627, 74001, 74041, 74163, 74701, 75297, 76147, 28 | 76221, 76531, 76617, 76851, 76981, 76995, 77061, 77257, 77743, 78045, 29 | 78135, 78187, 78211, 79017, 79293, 79441, 79773, 79843, 79917, 80023, 30 | 80575, 80743, 80757, 80911, 81117, 82105, 82377, 82401, 82941, 82975, 31 | 83125, 83203, 83473]; 32 | 33 | const TEN_WORD : [u64;256] = [ // 2^640 + k 34 | 115, 303, 391, 757, 1287, 1485, 2943, 3627, 3711, 3861, 3931, 4155, 4291, 35 | 4425, 4683, 4903, 5343, 6507, 8133, 8373, 8677, 9055, 9061, 9543, 9873, 36 | 9885, 11883, 11937, 12573, 12795, 13917, 14083, 14913, 15171, 15177, 15837, 37 | 15855, 16647, 17965, 18615, 18823, 19233, 19681, 20143, 20311, 20785, 22863, 38 | 24313, 25441, 25491, 26235, 26635, 27243, 27541, 28161, 29295, 29397, 29541, 39 | 29553, 30823, 31875, 33363, 33561, 33831, 33847, 33961, 34047, 34327, 34975, 40 | 35515, 35647, 36303, 36495, 36661, 37213, 37257, 37353, 37687, 38137, 38415, 41 | 38863, 39013, 39103, 40975, 42115, 42303, 43135, 43633, 44011, 44047, 44295, 42 | 45867, 48453, 48615, 48645, 49437, 49651, 50283, 50395, 51013, 51027, 51615, 43 | 51921, 53407, 53901, 54907, 55003, 55723, 55761, 56637, 56823, 56997, 57061, 44 | 57451, 57495, 57655, 58131, 58317, 58743, 58765, 59137, 60085, 60091, 60151, 45 | 60393, 60777, 62047, 62905, 63973, 64147, 64485, 64743, 65151, 65643, 65895, 46 | 66313, 66937, 67773, 69271, 69385, 69451, 69777, 70023, 70203, 71047, 71481, 47 | 71683, 72547, 73207, 73725, 73731, 73755, 73885, 74397, 74511, 74973, 75897, 48 | 75937, 76297, 76785, 76881, 76887, 78093, 78337, 78507, 78931, 79741, 80065, 49 | 81165, 81217, 81801, 81855, 82357, 82657, 82953, 83581, 83971, 84091, 84093, 50 | 84583, 85053, 85827, 87387, 87693, 88621, 88645, 89103, 89497, 90831, 90853, 51 | 91111, 91165, 92253, 93055, 93481, 93981, 94431, 94497, 94881, 95487, 95805, 52 | 96261, 97017, 97321, 97365, 97755, 97827, 98077, 98553, 98751, 98881, 99357, 53 | 99397, 99753, 100923, 100933, 101227, 101275, 101377, 101641, 101797, 101977, 54 | 102337, 102447, 102873, 103173, 104041, 104817, 105781, 106537, 106561, 106815, 55 | 107647, 108123, 108147, 108163, 108751, 108865, 110047, 110977, 111141, 111493, 56 | 112371, 112695, 113205, 113437, 113667, 113757, 113947, 114807, 115087, 115467, 57 | 116173, 116367, 116571, 116587 58 | ]; 59 | 60 | 61 | const ONE_HUNDRED_WORD : [u64;256] = [// 2^6400 + k 62 | 14931, 17961, 23313, 46333, 51345, 51643, 62533, 63927, 63 | 67245, 70585, 73485, 84477, 89851, 90325, 94285, 101487, 64 | 102841, 109033, 114073, 115207, 118101, 123511, 124141, 65 | 125203, 126127, 127705, 129363, 136173, 145645, 147345, 66 | 152343, 153003, 154737, 155085, 156415, 166263, 171777, 67 | 172735, 173193, 174433, 174933, 175357, 175531, 184047, 68 | 184393, 187413, 192483, 197653, 200215, 201411, 201813, 69 | 204001, 224823, 230121, 241335, 241507, 254587, 261223, 70 | 266907, 267625, 268131, 268375, 268671, 272385, 276307, 71 | 277887, 278943, 283663, 287997, 290653, 292023, 292333, 72 | 292897, 303135, 306075, 310695, 311227, 312705, 317391, 73 | 317793, 318255, 321093, 323007, 324561, 328395, 329043, 74 | 330277, 333193, 334767, 334975, 372351, 381297, 382215, 75 | 382615, 383971, 387765, 392547, 397123, 398043, 400095, 76 | 402583, 406641, 406837, 411037, 415515, 417897, 421921, 77 | 435535, 440035, 441933, 444513, 451455, 459841, 465667, 78 | 466171, 481477, 490813, 493303, 498957, 503401, 503605, 79 | 507343, 509643, 511195, 511857, 514135, 514581, 515307, 80 | 515335, 516037, 520851, 528133, 535033, 535381, 537361, 81 | 537667, 539611, 540135, 545187, 552765, 554151, 556693, 82 | 563797, 571747, 572137, 576055, 578011, 585577, 588253, 83 | 599155, 602451, 602935, 604435, 604501, 612367, 613827, 84 | 616543, 617215, 619185, 625317, 646911, 649825, 663315, 85 | 674685, 675583, 685267, 686433, 691191, 692733, 694597, 86 | 705853, 707491, 711117, 713517, 723055, 730185, 741321, 87 | 749757, 753693, 753883, 757395, 758221, 761365, 771753, 88 | 777973, 778257, 784093, 785065, 790755, 796021, 798273, 89 | 808657, 816613, 817371, 823375, 824085, 826911, 828295, 90 | 830251, 831715, 837525, 839427, 839457, 840081, 840835, 91 | 844021, 846183, 847605, 852777, 858103, 864475, 867687, 92 | 871371, 871875, 872095, 873915, 880525, 882247, 885945, 93 | 888243, 896211, 899635, 903295, 905911, 913215, 928341, 94 | 949921, 953533, 955131, 956577, 958237, 962271, 971217, 95 | 973813, 983065, 987175, 989421, 997867, 1000495, 1010317, 96 | 1011927, 1013853, 1019593, 1024755, 1025373, 1035037, 97 | 1035783, 1036207, 1038577, 1039605, 1053373, 1057123, 98 | 1058005, 1069677, 1074933, 1088961 99 | ]; 100 | 101 | 102 | const ONE_K_WORD : [u64;6] = [ 103 | 51277, 62301, 26235, 27307, 56763, 26235, 104 | ]; 105 | 106 | fn bench_prime(){ 107 | 108 | let mut start = std::time::Instant::now(); 109 | let mut count = 0u32; 110 | let k = Mpz::one().shl(512); 111 | for i in EIGHT_WORD{ 112 | let p = k.ref_addition(&Mpz::from_u64(i)); 113 | if p.is_prime(){ 114 | count+=1; 115 | } 116 | } 117 | let mut stop = start.elapsed(); 118 | assert_eq!(256,count); 119 | count = 0; 120 | println!("256 8-word (154 decimal digit) primes evaluated in {:?} ", stop); 121 | 122 | 123 | let k = Mpz::one().shl(640); 124 | start = std::time::Instant::now(); 125 | for i in TEN_WORD{ 126 | let p = k.ref_addition(&Mpz::from_u64(i)); 127 | if p.is_prime(){ 128 | count+=1; 129 | } 130 | } 131 | stop = start.elapsed(); 132 | assert_eq!(256,count); 133 | count =0; 134 | println!("256 10-word (192 decimal digit) primes evaluated in {:?} ", stop); 135 | 136 | let k = Mpz::one().shl(6400); 137 | start = std::time::Instant::now(); 138 | for i in ONE_HUNDRED_WORD{ 139 | let p = k.ref_addition(&Mpz::from_u64(i)); 140 | if p.is_prime(){ 141 | count+=1; 142 | } 143 | } 144 | stop = start.elapsed(); 145 | assert_eq!(256,count); 146 | count=0; 147 | println!("256 100-word (1926 decimal digit) primes evaluated in {:?} ", stop); 148 | 149 | /* 150 | let k = Mpz::one().shl(64000); 151 | start = std::time::Instant::now(); 152 | for i in ONE_K_WORD{ 153 | let p = k.ref_addition(&Mpz::from_u64(i)); 154 | if p.is_prime(){ 155 | count+=1; 156 | } 157 | } 158 | 159 | stop = start.elapsed(); 160 | assert_eq!(6,count); 161 | count=0; 162 | println!("6 1000-word (19265 decimal digits) primes evaluated in {:?} ", stop); 163 | */ 164 | } 165 | fn main () { 166 | println!("For faster evaluation use \"parallel\" feature"); 167 | bench_prime() 168 | } 169 | -------------------------------------------------------------------------------- /src/primitive/factorprim.rs: -------------------------------------------------------------------------------- 1 | use crate::ntrait::{NumberTheory,MontArith}; 2 | 3 | pub(crate) fn drbg(mut x: u64) -> u64{ 4 | x ^= x.wrapping_shr(12); 5 | x ^= x.wrapping_shl(25); 6 | x ^= x.wrapping_shr(27); 7 | x.wrapping_mul(0x2545F4914F6CDD1D) 8 | } 9 | 10 | fn poly_eval_32(x: u32, subtrahend: u32, n: u32, npi: u32) -> u32{ 11 | x.mont_prod(x,npi,n).mont_sub(subtrahend,n) 12 | } 13 | 14 | pub(crate) fn pollard_brent_32(base: u32,inv:u32,subtrahend: u32, n: u32) -> Option{ 15 | let m = 4; 16 | let mut r = 1; 17 | let mut q = 1; 18 | let mut g = 1; 19 | let mut ys = 1; 20 | let mut y = base; 21 | let mut x = y; 22 | 23 | for _ in 1..19{ 24 | x = y; 25 | for _ in 0..r{ 26 | y = poly_eval_32(y,subtrahend,n,inv); 27 | } 28 | 29 | let mut k = 0; 30 | 31 | loop{ 32 | 33 | for i in 0..m{ 34 | if i >= r-k{ 35 | 36 | break; 37 | } 38 | 39 | y=poly_eval_32(y,subtrahend,n,inv); 40 | q=q.mont_prod(x.abs_diff(y),inv,n);//machine_prime::mont_prod(q,x.abs_diff(y),n,inv); 41 | } // end loop 42 | 43 | ys=y; 44 | g = q.gcd(n); 45 | k+=m; 46 | if k >= r || g !=1{ 47 | break; 48 | } 49 | } 50 | 51 | r<<=1; 52 | if g != 1{ 53 | break; 54 | } 55 | 56 | } 57 | 58 | if g ==n{ 59 | while g==1{ 60 | ys=poly_eval_32(ys,subtrahend,n,inv); 61 | g=x.abs_diff(ys).gcd(n); 62 | 63 | } 64 | } 65 | if g !=1 && g !=n && machine_prime::is_prime_wc(g as u64){ 66 | return Some(g); 67 | } 68 | None 69 | } 70 | 71 | pub(crate) fn poly_factor_32(n: u32) -> u32{ 72 | 73 | // Start with the polynomial x^2 -1 74 | // This works in most cases and is particularly fast to initialise in Montgomery form 75 | 76 | let inv = n.inv_2(); 77 | let one = n.n_identity(); 78 | let mut base = one.wrapping_add(one); 79 | if base > n{ 80 | base = base.wrapping_sub(n); 81 | } 82 | 83 | match pollard_brent_32(base,inv,one,n){ 84 | Some(factor) => return factor, 85 | None => { 86 | // if x^2 -1 failed try x^2+1 87 | // No particular reason except to reuse some values 88 | let coef = (n-1).to_mont(n);// machine_prime::to_mont(n-1,n); 89 | match pollard_brent_32(base,inv,coef,n){ 90 | Some(factor) => return factor, 91 | None => { 92 | // Loop that has a roughly 0.5 probability of factoring each iteration 93 | let mut param = drbg(n.into()); 94 | loop{ 95 | let rand_base= (param as u32)%(n-3)+3; 96 | match pollard_brent_32(rand_base,inv,one,n){ 97 | Some(factor) => return factor, 98 | None => param=drbg(param), 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | 108 | const fn poly_eval(x: u64, subtrahend: u64, n: u64, npi: u64) -> u64{ 109 | machine_prime::mont_sub(machine_prime::mont_prod(x,x,n,npi),subtrahend,n) 110 | } 111 | 112 | fn pollard_brent(base: u64,inv:u64,subtrahend: u64, n: u64) -> Option{ 113 | let m = 128; 114 | let mut r = 1; 115 | let mut q = 1; 116 | let mut g = 1; 117 | let mut ys = 1; 118 | let mut y = base; 119 | let mut x = y; 120 | 121 | for cycle in 1..17{ 122 | x = y; 123 | for _ in 0..r{ 124 | y = poly_eval(y,subtrahend,n,inv); 125 | } 126 | 127 | let mut k = 0; 128 | 129 | loop{ 130 | 131 | for i in 0..(m*cycle){ 132 | if i >= r-k{ 133 | break; 134 | } 135 | 136 | y=poly_eval(y,subtrahend,n,inv); 137 | q=machine_prime::mont_prod(q,x.abs_diff(y),n,inv); 138 | } // end loop 139 | 140 | ys=y; 141 | g = q.gcd(n); 142 | k+=m; 143 | if k >= r || g !=1{ 144 | break; 145 | } 146 | } 147 | 148 | r<<=1; 149 | if g != 1{ 150 | break; 151 | } 152 | 153 | } 154 | 155 | if g ==n{ 156 | while g==1{ 157 | ys=poly_eval(ys,subtrahend,n,inv); 158 | g=x.abs_diff(ys).gcd(n); 159 | 160 | } 161 | } 162 | if g !=1 && g !=n && machine_prime::is_prime_wc(g){ 163 | return Some(g); 164 | } 165 | None 166 | } 167 | 168 | pub(crate) fn poly_factor(n: u64) -> u64{ 169 | 170 | // Start with the polynomial x^2 -1 171 | // This works in most cases and is particularly fast to initialise in Montgomery form 172 | let inv = machine_prime::mul_inv2(n); 173 | let one = machine_prime::one_mont(n); 174 | let base = machine_prime::two_mont(one,n); 175 | 176 | match pollard_brent(base,inv,one,n){ 177 | Some(factor) => return factor, 178 | None => { 179 | // if x^2 -1 failed try x^2+1 180 | // No particular reason except to reuse some values 181 | let coef = machine_prime::to_mont(n-1,n); 182 | match pollard_brent(base,inv,coef,n){ 183 | Some(factor) => return factor, 184 | None => { 185 | // Loop that has a roughly 0.5 probability of factoring each iteration 186 | let mut param = drbg(n); 187 | loop{ 188 | let rand_base= param%(n-3)+3; 189 | match pollard_brent(rand_base,inv,one,n){ 190 | Some(factor) => return factor, 191 | None => param=drbg(param), 192 | } 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } 199 | 200 | 201 | const fn poly_eval_128(x: u128, subtrahend: u128, n: u128, npi: u128) -> u128{ 202 | machine_prime::mont_sub_128(machine_prime::mont_sqr_128(x,n,npi),subtrahend,n) 203 | } 204 | 205 | fn pollard_brent_128(base: u128,inv:u128,subtrahend: u128, n: u128) -> Option{ 206 | let m = 512; 207 | let mut r = 1; 208 | let mut q = 1; 209 | let mut g = 1; 210 | let mut ys = 1; 211 | let mut y = base; 212 | let mut x = y; 213 | 214 | for cycle in 1..34{ 215 | x = y; 216 | for _ in 0..r{ 217 | y = poly_eval_128(y,subtrahend,n,inv); 218 | } 219 | 220 | let mut k = 0; 221 | 222 | loop{ 223 | 224 | for i in 0..(m*cycle){ 225 | if i >= r-k{ 226 | break; 227 | } 228 | 229 | y=poly_eval_128(y,subtrahend,n,inv); 230 | q=machine_prime::mont_prod_128(q,x.abs_diff(y),n,inv); 231 | } // end loop 232 | 233 | ys=y; 234 | g = q.gcd(n); 235 | k+=m; 236 | if k >= r || g !=1{ 237 | break; 238 | } 239 | } 240 | 241 | r<<=1; 242 | if g != 1{ 243 | break; 244 | } 245 | 246 | } 247 | 248 | if g ==n{ 249 | while g==1{ 250 | ys=poly_eval_128(ys,subtrahend,n,inv); 251 | g=x.abs_diff(ys).gcd(n); 252 | 253 | } 254 | } 255 | if g !=1 && g !=n && machine_prime::is_prime_wc_128(g){ 256 | return Some(g); 257 | } 258 | None 259 | } 260 | 261 | 262 | 263 | 264 | pub(crate) fn poly_factor_128(n: u128) -> u128{ 265 | 266 | // Start with the polynomial x^2 -1 267 | // This works in most cases and is particularly fast to initialise in Montgomery form 268 | let inv = machine_prime::mul_inv2_128(n); 269 | let one = machine_prime::one_mont_128(n); 270 | let base = machine_prime::two_mont_128(one,n); 271 | 272 | match pollard_brent_128(base,inv,one,n){ 273 | Some(factor) => return factor, 274 | None => { 275 | // if x^2 -1 failed try x^2+1 276 | // No particular reason except to reuse some values 277 | let coef = machine_prime::to_mont_128(n-1,n); 278 | match pollard_brent_128(base,inv,coef,n){ 279 | Some(factor) => return factor, 280 | None => { 281 | // Loop that has a roughly 0.5 probability of factoring each iteration 282 | let mut param = drbg(n as u64); 283 | loop{ 284 | let rand_base= (param as u128)%(n-3)+3; 285 | match pollard_brent_128(rand_base,inv,one,n){ 286 | Some(factor) => return factor, 287 | None => param=drbg(param), 288 | } 289 | } 290 | } 291 | } 292 | } 293 | } 294 | } 295 | 296 | -------------------------------------------------------------------------------- /src/arithmetic/sixteenbytes.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::primes::PRIMELIST; 3 | use crate::arithmetic::mpz::Mpz; 4 | use crate::arithmetic::inlineops::*; 5 | 6 | use crate::traits::NumberTheory; 7 | 8 | impl NumberTheory for u128 { 9 | 10 | fn rng() -> Self {fuse(rng_64(), rng_64()) } 11 | 12 | fn euclidean_div(&self, other: &Self) -> (Self,Self) { 13 | (*self/ *other, *self%*other) 14 | } 15 | 16 | fn is_prime(&self) -> bool{ 17 | if *self < u64::MAX as u128{ 18 | return (*self as u64).is_prime() 19 | } 20 | for i in PRIMELIST[..54].iter(){ 21 | if *self%*i as u128 == 0{ 22 | return false 23 | } 24 | } 25 | 26 | let k = Mpz::from_u128(*self); 27 | 28 | return k.sprp_check(5) 29 | 30 | } 31 | 32 | fn factor(&self) -> Vec{ 33 | let mut n = self.clone(); 34 | let twofactors = n.trailing_zeros(); 35 | n>>=twofactors; 36 | 37 | let mut factors : Vec = vec![]; 38 | 39 | if twofactors > 0{ 40 | factors.push(2u128); 41 | factors.push(twofactors as u128); 42 | } 43 | 44 | for i in PRIMELIST[1..].iter(){ // strips out small primes 45 | if n% *i as u128==0{ 46 | factors.push(*i as u128); 47 | let mut count = 0u128; 48 | while n% *i as u128==0 { 49 | count +=1; 50 | n/= *i as u128; 51 | } 52 | factors.push(count); 53 | } 54 | } 55 | 56 | if n < u64::MAX as u128{ // 57 | 58 | let large_fact = (n as u64).factor(); 59 | for i in large_fact{ 60 | factors.push(i as u128) 61 | } 62 | return factors 63 | } 64 | 65 | else{ 66 | let large_fact = Mpz::from_u128(n).factor(); 67 | 68 | for i in large_fact{ 69 | factors.push(i.to_u128().unwrap()) 70 | } 71 | return factors 72 | 73 | } 74 | 75 | 76 | } 77 | 78 | fn radical(&self)-> Self{ 79 | self.factor().iter().step_by(2).product::() 80 | } 81 | 82 | fn k_free(&self, k: &Self)->bool{ 83 | let factors = self.factor(); 84 | for i in 0..factors.len(){ 85 | if factors[i] == *k{ 86 | if i == 0{ 87 | (); 88 | } 89 | else{ 90 | return false 91 | } 92 | } 93 | } 94 | return true 95 | } 96 | 97 | 98 | fn euclid_gcd(&self, other: &Self) -> Self{ 99 | let mut a = self.clone(); 100 | let mut b = other.clone(); 101 | if b == 0 102 | { return a; } 103 | 104 | else if a == 0 105 | { return b; } 106 | 107 | let self_two_factor = a.trailing_zeros(); 108 | let other_two_factor = b.trailing_zeros(); 109 | let min_two_factor = std::cmp::min(self_two_factor, other_two_factor); 110 | a >>= self_two_factor; 111 | b >>= other_two_factor; 112 | loop { 113 | 114 | if b > a { 115 | std::mem::swap(&mut b, &mut a); 116 | } 117 | a -= b; 118 | 119 | if a == 0 { 120 | return b << min_two_factor; 121 | } 122 | a >>= a.trailing_zeros(); 123 | } 124 | } 125 | 126 | fn euler_totient(&self) -> Self{ 127 | let factors = self.factor(); 128 | let numerator = factors.iter().step_by(2).map(|x| x -1u128).product::(); 129 | let denominator = factors.iter().step_by(2).product::(); 130 | (self/denominator)*numerator 131 | } 132 | 133 | fn quadratic_residue(&self, n: &Self) -> Self{ 134 | if n == &0 {return 0u128} Mpz::from_u128(*self).u_quadratic_residue(&Mpz::from_u128(*n)).to_u128().unwrap() 135 | } 136 | 137 | fn mul_mod(&self, other: &Self, n: &Self) -> Self{ 138 | if n == &0 {return 0u128} 139 | Mpz::from_u128(*self).u_mul_mod(&Mpz::from_u128(*other), &Mpz::from_u128(*n)).to_u128().unwrap() 140 | //((*self as u128 * *other as u128) % *n as u128) as u64 141 | } 142 | 143 | fn mod_pow(&self, p: &Self, modulus: &Self)-> Self{ 144 | if modulus == &0 {return 0u128} Mpz::from_u128(*self).u_mod_pow(&Mpz::from_u128(*p),&Mpz::from_u128(*modulus) ).to_u128().unwrap() 145 | } 146 | 147 | 148 | fn legendre(&self, p: &Self) -> i8 { 149 | let k = self.mod_pow(&((*p-1)>>1), p); 150 | if k == 1{return 1}; 151 | if k == *p-1 {return -1}; 152 | return 0 153 | } 154 | 155 | fn checked_legendre(&self, p: &Self) -> Option { 156 | if p == &2 || p.is_prime() == false { 157 | return None 158 | } 159 | Some(self.legendre(&p)) 160 | } 161 | 162 | 163 | fn jacobi(&self, k: &Self) -> i8 { 164 | let mut n = *self; 165 | let mut p = *k; 166 | let mut t = 1i8; 167 | n %= p; 168 | 169 | while n != 0 { 170 | let zeros = n.trailing_zeros(); 171 | n>>=zeros; 172 | 173 | if (p % 8 == 3 || p % 8 == 5) && (zeros%2 == 1) { 174 | t = -t 175 | } 176 | 177 | std::mem::swap(&mut n, &mut p); 178 | if n % 4 == 3 && p % 4 == 3 { 179 | t = -t; 180 | } 181 | n %= p; 182 | } 183 | 184 | if p == 1 { 185 | t 186 | } 187 | 188 | else { 189 | 0 190 | } 191 | } 192 | 193 | fn checked_jacobi(&self, k: &Self) -> Option{ 194 | if k > &0 && *k % 2 == 1 { 195 | return Some(self.jacobi(k)) 196 | } 197 | return None 198 | } 199 | 200 | 201 | fn smooth(&self) -> Self{ 202 | let k = self.factor(); 203 | k[k.len()-2] 204 | } 205 | 206 | fn is_smooth(&self, b: &Self) -> bool{ 207 | &self.smooth() <= b 208 | } 209 | 210 | } 211 | 212 | impl NumberTheory for i128{ 213 | 214 | fn rng() -> Self {fuse(rng_64(), rng_64()) as i128} 215 | 216 | fn euclidean_div(&self, other: &Self) -> (Self,Self) { 217 | (*self/ *other, *self%*other) 218 | } 219 | 220 | fn is_prime(&self) -> bool{ 221 | (self.abs() as u128).is_prime() 222 | } 223 | 224 | fn factor(&self) -> Vec{ 225 | (self.abs() as u128).factor().iter().map(|x| *x as i128).collect::>() 226 | } 227 | 228 | fn radical(&self) -> Self{ 229 | (self.abs() as u128).radical() as i128 230 | } 231 | 232 | fn k_free(&self, k: &Self) -> bool{ 233 | (self.abs() as u128).k_free(&(k.abs() as u128)) 234 | } 235 | 236 | fn euclid_gcd(&self, other: &Self) -> Self{ 237 | (self.abs() as u128).euclid_gcd(&(other.abs() as u128)) as i128 238 | } 239 | 240 | fn euler_totient(&self) -> Self{ 241 | (self.abs() as u128).euler_totient() as i128 242 | } 243 | 244 | fn quadratic_residue(&self, n: &Self) -> Self{ 245 | (self.abs() as u128).quadratic_residue(&(n.abs() as u128)) as i128 246 | } 247 | 248 | fn mul_mod(&self, other: &Self, n: &Self) -> Self{ 249 | let mut a = self.clone(); 250 | let mut b = other.clone(); 251 | let mut modulo = n.abs() ; 252 | 253 | if a < 0i128{ 254 | a= modulo + a ; 255 | } 256 | if b < 0i128{ 257 | b = modulo + b; 258 | } 259 | (a as u128).mul_mod(&(b as u128), &(modulo as u128)) as i128 260 | } 261 | 262 | fn mod_pow(&self, pow: &Self, n: &Self) -> Self{ 263 | let mut a = self.clone(); 264 | if a < 0i128{ 265 | a = n.abs() + self 266 | } 267 | (a as u128).mod_pow( &(pow.abs() as u128), &(n.abs() as u128)) as i128 268 | } 269 | 270 | 271 | 272 | fn legendre(&self, p: &Self) -> i8 { 273 | let k = self.mod_pow(&((p.abs()-1)>>1), &p.abs()); 274 | if k == 1{return 1}; 275 | if k == p.abs()-1 {return -1}; 276 | return 0 277 | } 278 | 279 | fn checked_legendre(&self, p: &Self) -> Option { 280 | if p.abs() == 2 || p.is_prime() == false { 281 | return None 282 | } 283 | Some(self.legendre(&p)) 284 | } 285 | 286 | 287 | fn jacobi(&self, k: &Self) -> i8 { 288 | let mut n = *self; 289 | let mut p = *k; 290 | let mut t = 1i8; 291 | n %= p; 292 | 293 | while n != 0 { 294 | let zeros = n.trailing_zeros(); 295 | n>>=zeros; 296 | 297 | if (p % 8 == 3 || p % 8 == 5) && (zeros%2 == 1) { 298 | t = -t 299 | } 300 | 301 | std::mem::swap(&mut n, &mut p); 302 | if n % 4 == 3 && p % 4 == 3 { 303 | t = -t; 304 | } 305 | n %= p; 306 | } 307 | 308 | if p == 1 { 309 | t 310 | } 311 | 312 | else { 313 | 0 314 | } 315 | } 316 | 317 | fn checked_jacobi(&self, k: &Self) -> Option{ 318 | if k > &0 && *k % 2 == 1 { 319 | return Some(self.jacobi(k)) 320 | } 321 | return None 322 | } 323 | 324 | 325 | fn smooth(&self) -> Self{ 326 | let k = self.factor(); 327 | k[k.len()-2] 328 | } 329 | 330 | fn is_smooth(&self, b: &Self) -> bool{ 331 | self.smooth() <= b.abs() 332 | } 333 | 334 | } 335 | 336 | 337 | -------------------------------------------------------------------------------- /src/primitive/promotion.rs: -------------------------------------------------------------------------------- 1 | use crate::{ntrait::NumberTheory,structs::{Certificate,Factorization},result::NTResult}; 2 | 3 | // Handle u8,u16 and usize values 4 | macro_rules! promoter( 5 | ($($t:ty;$s:ty),* $(,)*) => {$( 6 | 7 | impl NumberTheory for $t{ 8 | 9 | fn is_unit(&self) -> bool{ 10 | if *self == 1{ 11 | return true 12 | } 13 | false 14 | } 15 | 16 | fn rng() -> $t{ 17 | <$s>::rng() as $t 18 | } 19 | 20 | fn residue(&self,ring: Self) -> Self{ 21 | (*self as $s).residue(ring as $s) as $t 22 | } 23 | 24 | fn mul_inverse(&self, ring: Self) -> NTResult{ 25 | (*self as $s).mul_inverse(ring as $s).map(|n| n as $t) 26 | } 27 | 28 | fn euclidean_div(&self, other: Self) -> (Self,Self){ 29 | let (quo,rem) = (*self as $s).euclidean_div(other as $s); 30 | (quo as $t,rem as $t) 31 | } 32 | 33 | fn fermat(&self, base: Self) -> bool{ 34 | (*self as $s).fermat(base as $s) 35 | } 36 | 37 | fn strong_fermat(&self, base: Self) -> bool{ 38 | (*self as $s).strong_fermat(base as $s) 39 | } 40 | 41 | fn is_prime(&self) -> bool{ 42 | (*self as $s).is_prime() 43 | } 44 | 45 | fn prime_proof(&self) -> Certificate{ 46 | let tmp = (*self as $s).prime_proof(); 47 | Certificate::new(tmp.n as $t,tmp.witness as $t,tmp.fctr.iter().map(|x| *x as $t).collect()) 48 | } 49 | 50 | fn prime_list(&self,sup: Self) -> Vec{ 51 | (*self as $s).prime_list(sup as $s).iter().map(|x| *x as $t).collect() 52 | } 53 | 54 | fn nth_prime(&self) -> NTResult{ 55 | let res = (*self as $s).nth_prime(); 56 | match res{ 57 | NTResult::Eval(n) => { 58 | if n > <$t>::MAX as $s{ 59 | return NTResult::Overflow; 60 | } 61 | else{ 62 | return NTResult::Eval(n as $t); 63 | } 64 | } 65 | _=> NTResult::Overflow, 66 | } 67 | } 68 | 69 | fn pi(&self) -> Self{ 70 | (*self as $s).pi() as $t 71 | } 72 | 73 | fn prime_gen(x: u32) -> NTResult{ 74 | if x > Self::BITS-1{ 75 | return NTResult::Overflow; 76 | } 77 | <$s>::prime_gen(x).map(|p| p as $t) 78 | } 79 | 80 | fn factor(&self) -> NTResult>{ 81 | (*self as $s).factor().map(|fctrs| { 82 | let basevec = fctrs.factor_iter().map(|x| *x as $t).collect(); 83 | return Factorization::from_components(basevec,fctrs.power); 84 | } 85 | ) 86 | } 87 | 88 | fn sqrt(&self) -> (Self,Self){ 89 | let (p,q) = (*self as $s).sqrt(); 90 | (p as $t, q as $t) 91 | } 92 | 93 | fn nth_root(&self, n: Self) -> (Self,Self){ 94 | let (x,y) = (*self as $s).nth_root(n as $s); 95 | (x as $t, y as $t) 96 | } 97 | 98 | fn max_exp(&self) -> (Self,Self){ 99 | let (x,y) = (*self as $s).max_exp(); 100 | (x as $t,y as $t) 101 | } 102 | 103 | fn radical(&self) -> NTResult{ 104 | let res = (*self as $s).radical(); 105 | match res{ 106 | NTResult::Eval(n) => { 107 | if n > <$t>::MAX as $s{ 108 | return NTResult::Overflow; 109 | } 110 | else{ 111 | return NTResult::Eval(n as $t); 112 | } 113 | } 114 | _=> NTResult::Overflow, 115 | } 116 | } 117 | 118 | 119 | fn k_free(&self, k: Self) -> bool{ 120 | (*self as $s).k_free(k as $s) 121 | } 122 | 123 | fn gcd(&self, other: Self) -> Self{ 124 | (*self as $s).gcd(other as $s) as $t 125 | } 126 | 127 | //fn coprime(&self, other: Self) -> bool{ 128 | // (*self as $s).coprime(other as $s) 129 | // } 130 | 131 | fn extended_gcd(&self, other: Self) -> (Self,Self,Self){ 132 | let (x,y,g) = (*self as $s).extended_gcd(other as $s); 133 | (x as $t,y as $t, g as $t) 134 | } 135 | 136 | fn lcm(&self, other: Self) -> NTResult{ 137 | let res = (*self as $s).lcm(other as $s); 138 | match res{ 139 | NTResult::Eval(n) => { 140 | if n > <$t>::MAX as $s{ 141 | return NTResult::Overflow; 142 | } 143 | else{ 144 | return NTResult::Eval(n as $t); 145 | } 146 | } 147 | _=> NTResult::Overflow, 148 | } 149 | } 150 | 151 | fn euler_totient(&self) -> Self{ 152 | (*self as $s).euler_totient() as $t 153 | } 154 | 155 | fn jordan_totient(&self, k: Self) -> NTResult{ 156 | let res = (*self as $s).jordan_totient(k as $s); 157 | match res{ 158 | NTResult::Eval(n) => { 159 | if n > <$t>::MAX as $s{ 160 | return NTResult::Overflow; 161 | } 162 | else{ 163 | return NTResult::Eval(n as $t); 164 | } 165 | } 166 | _=> NTResult::Overflow, 167 | } 168 | } 169 | 170 | fn exponent(&self) -> NTResult{ 171 | let res = (*self as $s).exponent(); 172 | match res{ 173 | NTResult::Eval(n) => { 174 | if n > <$t>::MAX as $s{ 175 | return NTResult::Overflow; 176 | } 177 | else{ 178 | return NTResult::Eval(n as $t); 179 | } 180 | } 181 | _=> NTResult::Overflow, 182 | } 183 | } 184 | 185 | fn dedekind_psi(&self, k: Self) -> NTResult{ 186 | let res = (*self as $s).dedekind_psi(k as $s); 187 | match res{ 188 | NTResult::Eval(n) => { 189 | if n > <$t>::MAX as $s{ 190 | return NTResult::Overflow; 191 | } 192 | else{ 193 | return NTResult::Eval(n as $t); 194 | } 195 | } 196 | _=> NTResult::Overflow, 197 | } 198 | } 199 | 200 | fn quadratic_residue(&self, n: Self) -> Self{ 201 | (*self as $s).quadratic_residue(n as $s) as $t 202 | } 203 | 204 | fn checked_quadratic_residue(&self, ring: Self) -> NTResult{ 205 | let res = (*self as $s).checked_quadratic_residue(ring as $s); 206 | match res{ 207 | NTResult::Eval(n) => { 208 | if n > <$t>::MAX as $s{ 209 | return NTResult::Overflow; 210 | } 211 | else{ 212 | return NTResult::Eval(n as $t); 213 | } 214 | } 215 | _=> NTResult::Overflow, 216 | } 217 | } 218 | 219 | fn product_residue(&self, other: Self, n: Self) -> Self{ 220 | (*self as $s).product_residue(other as $s,n as $s) as $t 221 | } 222 | 223 | fn checked_product_residue(&self, other: Self, ring: Self) -> NTResult { 224 | let res = (*self as $s).checked_product_residue(other as $s,ring as $s); 225 | 226 | match res{ 227 | NTResult::Eval(n) => { 228 | if n > <$t>::MAX as $s{ 229 | return NTResult::Overflow; 230 | } 231 | else{ 232 | return NTResult::Eval(n as $t); 233 | } 234 | } 235 | _=> NTResult::Overflow, 236 | } 237 | } 238 | 239 | fn exp_residue(&self, pow: Self, ring: Self) -> Self{ 240 | (*self as $s).exp_residue(pow as $s,ring as $s) as $t 241 | } 242 | 243 | fn checked_exp_residue(&self, pow: Self, ring: Self) -> NTResult{ 244 | let res = (*self as $s).checked_exp_residue(pow as $s, ring as $s); 245 | match res{ 246 | NTResult::Eval(n) => { 247 | if n > <$t>::MAX as $s{ 248 | return NTResult::Overflow; 249 | } 250 | else{ 251 | return NTResult::Eval(n as $t); 252 | } 253 | } 254 | NTResult::DNE => NTResult::DNE, 255 | _=> NTResult::Overflow, 256 | } 257 | } 258 | 259 | fn legendre(&self, p: Self) -> i8{ 260 | (*self as $s).legendre(p as $s) 261 | } 262 | 263 | fn checked_legendre(&self, p: Self) -> NTResult{ 264 | (*self as $s).checked_legendre(p as $s) 265 | } 266 | 267 | fn liouville(&self) -> i8{ 268 | (*self as $s).liouville() 269 | } 270 | 271 | fn derivative(&self) -> NTResult{ 272 | let res = (*self as $s).derivative(); 273 | match res{ 274 | NTResult::Eval(n) => { 275 | if n > <$t>::MAX as $s{ 276 | return NTResult::Overflow; 277 | } 278 | else{ 279 | return NTResult::Eval(n as $t); 280 | } 281 | } 282 | _=> NTResult::Overflow, 283 | } 284 | } 285 | 286 | fn mangoldt(&self) -> f64{ 287 | (*self as $s).mangoldt() 288 | } 289 | 290 | fn mobius(&self) -> i8{ 291 | (*self as $s).mobius() 292 | } 293 | 294 | fn jacobi(&self, k: Self) -> i8{ 295 | (*self as $s).jacobi(k as $s) 296 | } 297 | 298 | fn checked_jacobi(&self, k: Self) -> NTResult{ 299 | (*self as $s).checked_jacobi(k as $s) 300 | } 301 | 302 | fn kronecker(&self, k: Self) -> i8{ 303 | (*self as $s).kronecker(k as $s) 304 | } 305 | 306 | fn smooth(&self) -> NTResult{ 307 | let res = (*self as $s).smooth(); 308 | match res{ 309 | NTResult::Eval(n) => { 310 | if n > <$t>::MAX as $s{ 311 | return NTResult::Overflow; 312 | } 313 | else{ 314 | return NTResult::Eval(n as $t); 315 | } 316 | } 317 | _=> NTResult::Overflow, 318 | } 319 | } 320 | 321 | fn is_smooth(&self, k: Self) -> bool{ 322 | (*self as $s).is_smooth(k as $s) 323 | } 324 | 325 | fn ord(&self, ring: Self) -> NTResult{ 326 | let res = (*self as $s).ord(ring as $s); 327 | match res{ 328 | NTResult::Eval(n) => { 329 | if n > <$t>::MAX as $s{ 330 | return NTResult::Overflow; 331 | } 332 | else{ 333 | return NTResult::Eval(n as $t); 334 | } 335 | } 336 | _=> NTResult::Overflow, 337 | } 338 | } 339 | 340 | } 341 | )*} 342 | ); 343 | 344 | promoter!(u8;u32,u16;u32,usize;u64); 345 | -------------------------------------------------------------------------------- /src/arithmetic/mpz_arith.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::inlineops::*; 2 | use crate::arithmetic::muldiv::*; 3 | use crate::arithmetic::sliceops::*; 4 | use crate::Mpz; 5 | use crate::arithmetic::sign::Sign; 6 | use std::cmp::Ordering; 7 | use crate::ntrait::NumberTheory; 8 | 9 | impl std::cmp::PartialOrd for Mpz{ 10 | fn partial_cmp(&self, other: &Self) -> Option { 11 | match (self.sign,other.sign){ 12 | (Sign::Negative,Sign::Positive) => Some(std::cmp::Ordering::Less), 13 | (Sign::Positive,Sign::Negative) => Some(std::cmp::Ordering::Greater), 14 | _=> Some(self.u_cmp(other)), 15 | } 16 | } 17 | } 18 | 19 | impl std::cmp::Eq for Mpz{} 20 | 21 | impl std::cmp::Ord for Mpz{ 22 | fn cmp(&self,other: &Self) -> std::cmp::Ordering{ 23 | match (self.sign,other.sign){ 24 | (Sign::Negative,Sign::Positive) => std::cmp::Ordering::Less, 25 | (Sign::Positive,Sign::Negative) => std::cmp::Ordering::Greater, 26 | _=> self.u_cmp(other), 27 | } 28 | 29 | } 30 | } 31 | 32 | impl Mpz { 33 | /* 34 | Shifting operations 35 | 36 | */ 37 | /// Performs bitwise AND operation between x and y storing the result in x 38 | pub fn mut_and(&mut self, other: &Self) { 39 | for (i, j) in self.limbs.iter_mut().zip(other.limbs.iter()) { 40 | *i &= j 41 | } 42 | self.limbs.truncate(other.len()); 43 | self.normalize(); 44 | } 45 | 46 | /// Performs bitwise OR operation between x and y storing the result in x 47 | pub fn mut_or(&mut self, other: &Self) { 48 | for (i, j) in self.limbs.iter_mut().zip(other.limbs.iter()) { 49 | *i |= j 50 | } 51 | 52 | if self.len() < other.len() { 53 | self.limbs.extend_from_slice(&other.limbs[self.len()..]) 54 | } 55 | } 56 | /// Performs bitwise XOR operation between x and y storing the result in x 57 | pub fn mut_xor(&mut self, other: &Self) { 58 | for (i, j) in self.limbs.iter_mut().zip(other.limbs.iter()) { 59 | *i ^= j 60 | } 61 | 62 | if self.len() < other.len() { 63 | self.limbs.extend_from_slice(&other.limbs[self.len()..]) 64 | } 65 | } 66 | /// Performs bitwise NOT on x in-place 67 | pub fn mut_not(&mut self) { 68 | for i in self.limbs.iter_mut() { 69 | *i = !*i 70 | } 71 | self.normalize(); 72 | } 73 | /// Shift-left k places in-place 74 | pub fn mut_shl(&mut self, shift: usize) { 75 | //let mut k = self.clone(); 76 | let mut trail_zeroes = vec![0; shift / 64usize]; 77 | 78 | let carry = shl_slice(&mut self.limbs[..], (shift % 64usize) as u32); 79 | 80 | trail_zeroes.extend_from_slice(&self.limbs[..]); 81 | 82 | if carry > 0 { 83 | trail_zeroes.push(carry) 84 | } 85 | 86 | self.limbs = trail_zeroes; 87 | } 88 | 89 | /// Shift-left k places in-place 90 | pub fn mut_shr(&mut self, shift: usize) { 91 | let mut carry = 0u64; 92 | 93 | let mut vector: Vec = self 94 | .limbs 95 | .drain((shift / 64usize)..self.limbs.len()) 96 | .collect(); 97 | let sub_shift = shift % 64usize; 98 | 99 | for i in vector.iter_mut().rev() { 100 | carry = carry_shr(carry, *i, sub_shift as u32, i); 101 | } 102 | 103 | self.limbs = vector; 104 | } 105 | 106 | /// Returns x * 2^k 107 | pub fn shl(&self, shift: usize) -> Mpz { 108 | let mut k = self.clone(); 109 | k.mut_shl(shift); 110 | k 111 | } 112 | /// Returns x / 2^k 113 | pub fn shr(&self, shift: usize) -> Mpz { 114 | let mut k = self.clone(); 115 | k.mut_shr(shift); 116 | k 117 | } 118 | /// Returns x AND y 119 | pub fn and(&self, other: &Self) -> Self { 120 | let mut k = self.clone(); 121 | k.mut_and(other); 122 | k 123 | } 124 | /// Returns x OR y 125 | pub fn or(&self, other: &Self) -> Self { 126 | let mut k = self.clone(); 127 | k.mut_or(other); 128 | k 129 | } 130 | /// Returns x XOR y 131 | pub fn xor(&self, other: &Self) -> Self { 132 | let mut k = self.clone(); 133 | k.mut_xor(other); 134 | k 135 | } 136 | 137 | /* 138 | Arithmetic operations 139 | */ 140 | /// x+y stored in x 141 | pub fn mut_addition(&mut self, mut other: Self) { 142 | let carry: u8; 143 | 144 | if self.sign == other.sign { 145 | if self.limbs.len() < other.limbs.len() { 146 | let len = self.len(); 147 | self.limbs.extend_from_slice(&other.limbs[len..]); 148 | carry = add_slice(&mut self.limbs[..], &other.limbs[..len]); 149 | } else { 150 | carry = add_slice(&mut self.limbs[..], &other.limbs[..]); 151 | } 152 | if carry == 1u8 { 153 | self.limbs.push(1u64) 154 | } 155 | } else { 156 | if self.u_cmp(&other) == Ordering::Less { 157 | sub_slice(&mut other.limbs[..], &self.limbs[..]); 158 | *self = other; 159 | self.normalize(); 160 | } else if self.u_cmp(&other) == Ordering::Equal { 161 | self.limbs.truncate(0); 162 | self.limbs.push(0); 163 | self.sign = Sign::Positive; 164 | } else { 165 | sub_slice(&mut self.limbs[..], &other.limbs[..]); 166 | self.normalize(); 167 | } 168 | } 169 | } 170 | /// Returns x+y 171 | pub fn ref_addition(&self, other: &Self) -> Self { 172 | let mut k = self.clone(); 173 | k.mut_addition(other.clone()); 174 | k 175 | } 176 | /// x-y stored in x 177 | pub fn mut_subtraction(&mut self, mut other: Self) { 178 | other.neg(); 179 | self.mut_addition(other) 180 | } 181 | /// Returns x-y 182 | pub fn ref_subtraction(&self, other: &Self) -> Self { 183 | let mut k = self.clone(); 184 | k.mut_subtraction(other.clone()); 185 | k 186 | } 187 | /// Returns x*y 188 | pub fn ref_product(&self, other: &Self) -> Self { 189 | if self == &Mpz::zero() { 190 | return Mpz::zero(); 191 | } 192 | 193 | if other == &Mpz::zero() { 194 | return Mpz::zero(); 195 | } 196 | 197 | if self.is_unit() { 198 | return Mpz::unchecked_new(self.sign.mul(&other.sign), other.limbs.clone()); 199 | } 200 | 201 | if other.is_unit() { 202 | return Mpz::unchecked_new(self.sign.mul(&other.sign), self.limbs.clone()); 203 | } 204 | 205 | let mut t = vec![0u64; self.len() + other.len() + 1]; 206 | 207 | mul_slice(&self.limbs[..], &other.limbs[..], &mut t[..]); 208 | remove_lead_zeros(&mut t); 209 | Mpz::unchecked_new(self.sign.mul(&other.sign), t) 210 | } 211 | 212 | /// x*y stored in x 213 | pub fn mut_product(&mut self, other: Self) { 214 | if self == &Mpz::zero() {} 215 | 216 | if other == Mpz::zero() { 217 | self.limbs.truncate(0); 218 | self.limbs.push(0u64); 219 | self.sign = Sign::Positive; 220 | } 221 | 222 | if self.is_unit() { 223 | self.limbs.truncate(0); 224 | self.limbs.extend_from_slice(&other.limbs[..]); 225 | self.sign = self.sign.mul(&other.sign); 226 | } 227 | 228 | if other.is_unit() { 229 | self.sign = self.sign.mul(&other.sign); 230 | } else { 231 | let mut t = vec![0u64; self.len() + other.len() + 1]; 232 | 233 | mul_slice(&self.limbs[..], &other.limbs[..], &mut t[..]); 234 | remove_lead_zeros(&mut t); 235 | self.limbs = t; 236 | self.sign = self.sign.mul(&other.sign); 237 | } 238 | } 239 | /// Unsigned in-place multiply by x and add y 240 | pub fn mut_scale_add(&mut self, x: u64, y: u64){ 241 | let mut carry = scale_slice(&mut self.limbs[..],x); 242 | carry += add_slice(&mut self.limbs[..],&[y]) as u64; 243 | if carry > 0{ 244 | self.limbs.push(carry); 245 | } 246 | } 247 | 248 | /// Unsigned in-place multiply by x and add y 249 | pub fn scale_add(&self, x: u64, y: u64) -> Self{ 250 | let mut k = self.clone(); 251 | k.mut_scale_add(x,y); 252 | k 253 | } 254 | 255 | 256 | /// x*x squaring 257 | pub fn sqr(&self) -> Self { 258 | self.ref_product(self) 259 | } 260 | 261 | /// x*x squaring in-place 262 | pub fn mut_sqr(&mut self){ 263 | self.mut_product(self.clone()); 264 | } 265 | 266 | /// Returns x/y and x mod y 267 | pub fn ref_euclidean(&self, other: &Self) -> (Self, Self) { 268 | let mut dividend = Mpz::from_slice(Sign::Positive, &self.limbs[..]); 269 | 270 | if dividend == Mpz::zero() { 271 | return (Mpz::zero(), Mpz::zero()); 272 | } 273 | 274 | if dividend.len() == 0usize { 275 | return (Mpz::zero(), Mpz::zero()); 276 | } 277 | 278 | if other.len() == 0usize || other == &Mpz::zero() { 279 | panic!("Division by zero is undefined in Z") 280 | } 281 | 282 | if other.len() == 1 { 283 | if other.limbs == [1] { 284 | return (dividend, Mpz::zero()); 285 | } 286 | 287 | let rem = div_slice(&mut dividend.limbs, other.limbs[0]); 288 | 289 | remove_lead_zeros(&mut dividend.limbs); 290 | return (dividend, Mpz::unchecked_new(Sign::Positive, vec![rem])); 291 | } 292 | 293 | if dividend.u_cmp(other) == Ordering::Equal { 294 | return (Mpz::one(), Mpz::zero()); 295 | } 296 | 297 | if dividend.u_cmp(other) == Ordering::Less { 298 | return (Mpz::zero(), dividend); 299 | } 300 | 301 | let shift = other.limbs.last().unwrap().leading_zeros() as usize; 302 | 303 | if shift == 0 { 304 | let (quo, rem) = euclidean_slice(&mut dividend.limbs, &other.limbs[..]); 305 | ( 306 | Mpz::unchecked_new(Sign::Positive, quo), 307 | Mpz::unchecked_new(Sign::Positive, rem), 308 | ) 309 | } else { 310 | let (q, r) = 311 | euclidean_slice(&mut dividend.shl(shift).limbs, &other.shl(shift).limbs[..]); 312 | 313 | ( 314 | Mpz::unchecked_new(Sign::Positive, q), 315 | Mpz::unchecked_new(Sign::Positive, r).shr(shift), 316 | ) 317 | } 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/arithmetic/mpz_prime.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Primality 4 | 5 | */ 6 | 7 | use crate::data::primes::PRIMELIST; 8 | 9 | use crate::ntrait::NumberTheory; 10 | use crate::Mpz; 11 | use crate::NTResult; 12 | 13 | use crate::arithmetic::sliceops::{mod_slice,sub_slice}; 14 | 15 | 16 | impl Mpz { 17 | /// Strong Fermat test to a selected base 18 | pub(crate) fn sprp(&self, base: Self) -> bool { 19 | let mut p_minus = self.clone(); 20 | let one = Mpz::one(); 21 | 22 | sub_slice(&mut p_minus.limbs[..], &one.limbs[..]); //subtract one this will always succeed 23 | 24 | let zeroes = p_minus.trailing_zeros() as usize; 25 | 26 | let d = p_minus.shr(zeroes); 27 | let mut x = base.u_mod_pow(&d, self); 28 | 29 | if x == Mpz::one() || x == p_minus { 30 | return true; 31 | } 32 | 33 | for _ in 1..zeroes{ 34 | x = x.u_quadratic_residue(self); 35 | 36 | if x == p_minus { 37 | return true; 38 | } 39 | } 40 | false 41 | } 42 | 43 | #[cfg(not(feature = "parallel"))] 44 | /** Performs n random base checks, can be combined with is_prime to strengthen it. As is_prime is equivalent to one random sprp check in the worst case, the function below has an error rate of less than 2^-64 in the worst case. 45 | 46 | use number_theory::NumberTheory; 47 | use number_theory::Mpz; 48 | 49 | fn strong_check(x: &Mpz) -> bool{ 50 | if !x.is_prime(){ 51 | return false 52 | } 53 | x.sprp_check(31) 54 | } 55 | 56 | */ 57 | pub fn sprp_check(&self, n: usize) -> bool { 58 | if self.len() < 2 { 59 | return u64::try_from(self.clone()).unwrap().is_prime(); 60 | } // if fits into u64, reduce to 64-bit check 61 | 62 | if self.is_fermat() { 63 | return false; 64 | } 65 | 66 | for _ in 0..n { 67 | let z = Mpz::rand((self.bit_length()-1) as usize); 68 | 69 | if !self.sprp(z) { 70 | return false; 71 | } // 72 | } 73 | 74 | true 75 | } 76 | 77 | #[cfg(feature = "parallel")] // Fix this,likely does not check the correct amount of tests 78 | /// Checks n bases 79 | pub fn sprp_check(&self, k: usize) -> bool { 80 | if self.len() < 2 { 81 | return self.to_u64().unwrap().is_prime(); 82 | } // if fits into u64, reduce to 64-bit check 83 | 84 | if self.is_fermat() { 85 | return false; 86 | } 87 | 88 | 89 | let single = |x: Mpz, n: usize| { 90 | for _ in 0..n { 91 | let z = Mpz::rand(x.bit_length() as usize-1); 92 | if !x.sprp(z) { 93 | return false; 94 | } 95 | } 96 | return true; 97 | }; 98 | 99 | let threadcount = usize::from(std::thread::available_parallelism().unwrap()) - 2; 100 | let q = self.clone(); 101 | /* if k < threadcount{ 102 | let mut threadvec = vec![]; 103 | let mut xclone = vec![]; 104 | for i in 0..k{ 105 | xclone.push(self.clone()) 106 | } 107 | for i in xclone{ 108 | threadvec.push(std::thread::spawn(move|| {single(i, 1).clone()})); 109 | } 110 | for j in threadvec{ 111 | if !j.join().unwrap(){ 112 | return false 113 | } 114 | } 115 | return true 116 | } 117 | // else{*/ 118 | let mut threadvec = vec![]; 119 | let mut xclone = vec![]; 120 | for _ in 0..threadcount { 121 | xclone.push(self.clone()) 122 | } 123 | 124 | for i in xclone { 125 | threadvec.push(std::thread::spawn(move || single(i, k / threadcount))); 126 | } 127 | let tally = single(q, k / threadcount); //threadvec.push(std::thread::spawn(move || {single(q.clone(), k/threadcount)})); 128 | 129 | for j in threadvec { 130 | if !j.join().unwrap() { 131 | return false; 132 | } 133 | } 134 | return tally; 135 | } 136 | 137 | /** Detects if self is a number of various forms and returns the prime evaluation if it is 138 | One might be tempted to ask why proth numbers are not evaluated, and it's simply the fact that Proth's theorem is actually an extremely 139 | inefficient primality test, in the worst case strong fermat tests are twice as efficient as a Proth test and far stronger in practice. 140 | */ 141 | pub(crate) fn form_check(&self) -> bool { 142 | // Detects if self is of the form r(k^2 + k) + k + 1 143 | 144 | let sqrt = self.sqrt().0; 145 | if sqrt.sqr() == self.clone() { 146 | // detect perfect square 147 | return false; 148 | } 149 | 150 | !detect_pseudo_mpz(self) 151 | } 152 | 153 | pub(crate) fn trial_div(&self) -> bool { 154 | //weighted trial division 155 | 156 | if self.is_even() { 157 | return false; 158 | } 159 | 160 | let rem = mod_slice(&self.limbs[..], 16294579238595022365); 161 | 162 | for i in PRIMELIST[1..16usize].iter() { 163 | if rem % *i as u64 == 0 { 164 | return false; 165 | } 166 | } 167 | 168 | let mut supremum: usize = 2048; 169 | 170 | if self.len() < 40 { 171 | supremum = self.len() * 50 172 | } 173 | for i in PRIMELIST[17..supremum].iter() { 174 | if self.congruence_u64(*i as u64, 0) { 175 | return false; 176 | } 177 | } 178 | 179 | // insert a sieve that optimizes to eliminate composites (this does not appear to be any more efficient than using the hardcoded primes ) 180 | true 181 | } 182 | 183 | pub(crate) fn _trial_list(&self, primes: &[u64]) -> bool{ 184 | for i in primes{ 185 | if self.congruence_u64(*i,0){ 186 | return false 187 | } 188 | } 189 | true 190 | } 191 | 192 | // weighted strong fermat bases starting from 2^128 193 | pub(crate) fn weighted_sprp(&self) -> bool { 194 | const CHECK_LUT: [u8; 5] = [8, 6, 5, 4, 2]; // starting at 2^128 [12, 10, 7, 6, 4, 3, 2, 1]; 195 | let mut check: usize = 1; 196 | 197 | if self.len() < 8 { 198 | check = CHECK_LUT[self.len() - 3] as usize; 199 | } 200 | 201 | self.sprp_check(check) 202 | } 203 | 204 | pub(crate) fn llt(&self, p: u64) -> bool { 205 | // function will never be called in practical implementation as number-theory does not support the scale of computation needed to use it 206 | let mut s = Mpz::from(4u64); 207 | 208 | for _ in 0..(p - 2) { 209 | s = s.ref_product(&s); 210 | s.normalize(); 211 | sub_slice(&mut s.limbs[..], &[2]); 212 | 213 | s = s.ref_euclidean(self).1; 214 | } 215 | s.normalize(); 216 | if s == Mpz::zero() { 217 | return true; 218 | } 219 | false 220 | } 221 | 222 | /** Faster than naive evaluation of sophie prime, returns safe prime if true, otherwise None. As this uses is_prime, 223 | the accuracy matches that function exactly as the verification of the safe prime itself is deterministic and solely reliant on the accuracy 224 | of the verification of the sophie prime. 225 | */ 226 | pub fn is_sophie(&self) -> Option { 227 | if self.is_prime() { 228 | let mut safe = self.shl(1); 229 | let p = safe.clone(); 230 | let two = Mpz::two(); 231 | safe.successor(); 232 | 233 | if two.exp_residue(p, safe.clone()) == Mpz::one() { 234 | return Some(safe); 235 | } 236 | } 237 | None 238 | } 239 | 240 | pub(crate) fn jacobi_check_mpz(&self) -> bool { 241 | // Performs a check of the Jacobi 242 | let mut witness = 3u64; 243 | loop { 244 | if fast_jacobi_mpz(self, witness) == -1 { 245 | break; 246 | } 247 | witness += 1; 248 | } 249 | 250 | let witty = Mpz::from(witness); 251 | self.sprp(witty) 252 | } 253 | 254 | /// Returns an integer in the interval 2^(k-1);2^k that can satisfy the Monier-Rabin bound of passing the Artjuhov-Selfridge test 255 | /// with (1/4) probability 256 | /// 257 | pub fn psp(k: usize) -> NTResult{ 258 | 259 | let corrector = 1 + (k&1)*2 ; 260 | if k < 14{ 261 | return NTResult::DNE 262 | } 263 | if k > (1<<32){ 264 | return NTResult::CompExceeded 265 | } 266 | loop { 267 | let len = (k-corrector)/2; 268 | let mut x = Mpz::rand(len); 269 | if corrector == 3 && !x.check_bit(len-2){ 270 | x.flip_bit(len-2); 271 | } 272 | if corrector == 1{ 273 | if x.check_bit(len-2){ 274 | x.flip_bit(len-2); 275 | } 276 | if x.check_bit(len-3){ 277 | x.flip_bit(len-3); 278 | } 279 | } 280 | 281 | let lhs = x.shl(1).ref_addition(&Mpz::one()); 282 | let rhs = x.shl(2).ref_addition(&Mpz::one()); 283 | if lhs.is_prime() & rhs.is_prime(){ 284 | let product = lhs.ref_product(&rhs); 285 | assert_eq!(product.bit_length(),k as u64); 286 | 287 | return NTResult::Eval(product) 288 | } 289 | } 290 | } 291 | /// A weak fermat test 292 | pub fn fermat(&self, base: &Self) -> bool{ 293 | base.exp_residue(self.ref_subtraction(&Mpz::one()),self.clone()).is_unit() 294 | } 295 | 296 | /// Deterministic primality test, reliant on GRH 297 | pub fn miller(&self) -> bool{ 298 | let sup = (self.ln()*self.ln()*2.0) as u64; 299 | 300 | match self.to_u128(){ 301 | Some(x) => { 302 | for i in 2..sup{ 303 | if !x.strong_fermat(i as u128){ 304 | return false 305 | } 306 | } 307 | } 308 | None => { 309 | for i in 2..sup{ 310 | if !self.sprp(Mpz::from(i)){ 311 | return false 312 | } 313 | } 314 | } 315 | } 316 | true 317 | } 318 | 319 | 320 | } 321 | 322 | pub(crate) fn fast_jacobi_mpz(x: &Mpz, p: u64) -> i8 { 323 | let k = x.word_div(p).1; 324 | k.jacobi(p) 325 | } 326 | 327 | // detects if the number is in a common pseudoprime form 328 | pub(crate) fn detect_pseudo_mpz(x: &Mpz) -> bool { 329 | let mut xminus = x.abs(); 330 | let copy = xminus.clone(); 331 | let eightprod = copy.scale_add(8,1); 332 | let eightsqrt = eightprod.sqrt().0; 333 | 334 | if eightsqrt.sqr() == eightprod{ 335 | return true 336 | } 337 | 338 | let threeprod = copy.scale_add(3,1); 339 | let threesqrt = threeprod.sqrt().0; 340 | if threesqrt.sqr() == threeprod{ 341 | return true 342 | } 343 | xminus.predecessor(); 344 | 345 | 346 | for i in 1..16 { 347 | let sq = xminus.word_div(2 * i + 1).0.sqrt().0; // (k*k + k)*(2*i) + k + 1 == x 348 | let lhs = sq.ref_product(&sq).ref_addition(&sq); 349 | let lhs2 = lhs.ref_product(&Mpz::from(2 * i + 1)); 350 | if lhs.ref_addition(&lhs2).ref_addition(&Mpz::one()) == copy { 351 | return true; 352 | } 353 | } 354 | false 355 | } 356 | -------------------------------------------------------------------------------- /src/primitive/signednt.rs: -------------------------------------------------------------------------------- 1 | use crate::{ntrait::NumberTheory,structs::{Factorization,Certificate},result::NTResult}; 2 | 3 | macro_rules! signednt( 4 | ($($t:ty; $s:ty),* $(,)*) => {$( 5 | 6 | impl NumberTheory for $t{ 7 | 8 | fn is_unit(&self) -> bool{ 9 | if self.abs() == 1{ 10 | return true; 11 | } 12 | false 13 | } 14 | 15 | fn rng() -> $t { 16 | <$s>::rng() as $t 17 | } 18 | 19 | fn residue(&self, ring: Self) -> Self{ 20 | if ring == 0{ 21 | return *self 22 | } 23 | if *self < 0{ 24 | return ring.abs() -(self.abs() % ring.abs()) 25 | } 26 | *self % ring.abs() 27 | } 28 | 29 | fn euclidean_div(&self, other: Self) -> (Self, Self) { 30 | (*self/ other, *self % other) 31 | } 32 | 33 | fn mul_inverse(&self, ring: Self) -> NTResult{ 34 | let unsigned = ring.abs(); 35 | let b = self.residue(unsigned); 36 | (b as $s).mul_inverse(unsigned as $s).map(|res| res as $t) 37 | } 38 | 39 | fn fermat(&self, base: Self) -> bool{ 40 | let unsigned = self.abs(); 41 | let b = base.residue(unsigned); 42 | 43 | (unsigned as $s).fermat(b as $s) 44 | } 45 | 46 | fn strong_fermat(&self, base: Self) -> bool { 47 | if base < 0{ 48 | if base > *self { 49 | return (self.abs() as $s).strong_fermat( (self.abs()+(base % self.abs())) as $s) 50 | } 51 | return (self.abs() as $s).strong_fermat((self.abs() + base) as $s) 52 | } 53 | (self.abs() as $s).strong_fermat(base as $s) 54 | } 55 | 56 | fn is_prime(&self) -> bool { 57 | (self.abs() as $s).is_prime() 58 | } 59 | 60 | fn prime_proof(&self) -> Certificate { 61 | let u_cert = (*self as $s).prime_proof(); 62 | 63 | Certificate::new(u_cert.n as $t,u_cert.witness as $t,u_cert.fctr.iter().map(|x| *x as $t).collect()) 64 | } 65 | 66 | fn prime_list(&self, sup: Self) -> Vec { 67 | 68 | let inf = std::cmp::min(*self, sup); 69 | let mut hi = std::cmp::max(*self,sup); 70 | 71 | hi = hi.saturating_add(1); 72 | 73 | let mut primevector = vec![]; 74 | 75 | for i in inf..hi{ 76 | if i.is_prime(){ 77 | primevector.push(i) 78 | } 79 | } 80 | primevector 81 | } 82 | 83 | fn nth_prime(&self) -> NTResult { 84 | let mut count : Self = 0; 85 | let mut start : Self = 0; 86 | 87 | if *self == 0{ 88 | return NTResult::DNE; 89 | } 90 | 91 | loop { 92 | start += 1; 93 | 94 | if start == Self::MAX { 95 | return NTResult::Overflow; 96 | } 97 | if start.is_prime() { 98 | count += 1; 99 | } 100 | if count == *self { 101 | return NTResult::Eval(start); 102 | } 103 | } 104 | } 105 | 106 | fn pi(&self) -> Self{ 107 | let mut count : Self = 0; 108 | let zero : Self = 0; 109 | for i in zero..*self{ 110 | if i.is_prime(){ 111 | count+=1; 112 | } 113 | } 114 | count 115 | } 116 | 117 | fn prime_gen(x: u32) -> NTResult{ 118 | if x > Self::BITS-1{ 119 | return NTResult::Overflow 120 | } 121 | <$s>::prime_gen(x).map(|q| q as $t) 122 | } 123 | 124 | fn factor(&self) -> NTResult> { 125 | (self.abs() as $s) 126 | .factor().map(|fctrs| { 127 | let basevec = fctrs.factor_iter().map(|x| *x as $t).collect(); 128 | Factorization::from_components(basevec,fctrs.power) 129 | }) 130 | } 131 | 132 | 133 | 134 | fn sqrt(&self) -> (Self, Self) { 135 | if *self < 0 { 136 | return ((self.abs() as $s).sqrt().0 as $t,1) 137 | } 138 | ((*self as $s).sqrt().0 as $t,0) 139 | } 140 | 141 | fn nth_root(&self, n: Self) -> (Self, Self) { 142 | if n < 0{return (0,0)} 143 | 144 | if n == 1{ 145 | return (*self,0) 146 | } 147 | 148 | if *self < 0 && n.abs()%2 == 0{ 149 | return ((self.abs() as $s).nth_root(n.abs() as $s).0 as $t,1) 150 | } 151 | return ((self.abs() as $s).nth_root(n.abs() as $s).0 as $t,0) 152 | 153 | 154 | } 155 | 156 | fn max_exp(&self) -> (Self,Self){ 157 | if *self == <$t>::MIN{ 158 | return (-2,self.trailing_zeros() as $t) 159 | } 160 | if *self < 0{ 161 | let (base,exp) = (self.abs() as $s).max_exp(); 162 | if exp&1 == 1{ 163 | return (-(base as $t), exp as $t) 164 | } 165 | else{ 166 | return (*self, 1) 167 | } 168 | 169 | } 170 | let (base,exp) = (*self as $s).max_exp(); 171 | (base as $t, exp as $t) 172 | 173 | } 174 | 175 | fn radical(&self) -> NTResult{ 176 | (self.abs() as $s).radical().map(|y| y as $t) 177 | } 178 | 179 | fn k_free(&self, k: Self) -> bool { 180 | (self.abs() as $s).k_free(k.abs() as $s) 181 | } 182 | 183 | fn gcd(&self, other: Self) -> Self { 184 | (self.abs() as $s).gcd(other.abs() as $s) as $t 185 | } 186 | 187 | 188 | fn extended_gcd(&self, other: Self)->(Self,Self,Self){ 189 | let mut gcd : Self =*self; 190 | let mut new_r : Self =other; 191 | let mut bezout_1 : Self =1; 192 | let mut new_s : Self =0; 193 | let mut bezout_2 : Self = 0; 194 | let mut new_t: Self = 1; 195 | 196 | while new_r !=0 { 197 | let quotient =gcd/new_r; 198 | let mut temp : Self =new_r; 199 | new_r=gcd-quotient*temp; 200 | gcd=temp; 201 | 202 | temp=new_s; 203 | new_s=bezout_1-quotient*temp; 204 | bezout_1=temp; 205 | 206 | temp=new_t; 207 | new_t=bezout_2-quotient*temp; 208 | bezout_2=temp; 209 | 210 | } 211 | (gcd,bezout_1,bezout_2) 212 | } 213 | 214 | fn lcm(&self, other: Self) -> NTResult{ 215 | (self.abs() as $s).lcm(other.abs() as $s).map(|y| y as $t) 216 | } 217 | 218 | fn euler_totient(&self) -> Self { 219 | (self.abs() as $s).euler_totient() as $t 220 | } 221 | 222 | fn jordan_totient(&self, k: Self) -> NTResult { 223 | (self.abs() as $s).jordan_totient(k.abs() as $s).map(|y| y as $t) 224 | } 225 | 226 | fn exponent(&self) -> NTResult{ 227 | (self.abs() as $s).exponent().map(|y| y as $t) 228 | } 229 | 230 | fn dedekind_psi(&self, k: Self) -> NTResult { 231 | (self.abs() as $s).dedekind_psi(k.abs() as $s).map(|y| y as $t) 232 | } 233 | 234 | fn quadratic_residue(&self, n: Self) -> Self { 235 | (self.abs() as $s).quadratic_residue(n.abs() as $s) as $t 236 | } 237 | 238 | fn checked_quadratic_residue(&self, n: Self) -> NTResult{ 239 | (self.abs() as $s).checked_quadratic_residue(n.abs() as $s).map(|y| y as $t) 240 | } 241 | 242 | fn product_residue(&self, other: Self, n: Self) -> Self { 243 | let mut a = self.clone(); 244 | let mut b = other.clone(); 245 | let modulo = n.abs(); 246 | 247 | if a < 0 { 248 | a += modulo; 249 | } 250 | if b < 0 { 251 | b += modulo; 252 | } 253 | (a as $s).product_residue(b as $s,modulo as $s) as $t 254 | } 255 | 256 | fn checked_product_residue(&self, other: Self, n: Self) -> NTResult { 257 | let mut a = self.clone(); 258 | let mut b = other.clone(); 259 | let modulo = n.abs(); 260 | 261 | if a < 0 { 262 | a += modulo; 263 | } 264 | if b < 0 { 265 | b += modulo; 266 | } 267 | (a as $s).checked_product_residue(b as $s,modulo as $s).map(|y| y as $t) 268 | } 269 | 270 | 271 | fn exp_residue(&self, pow: Self, n: Self) -> Self { 272 | let mut a = self.residue(n); 273 | if pow < 0{ 274 | let inv = (a as $s).extended_gcd(n.abs() as $s).1; 275 | return inv.exp_residue(pow.abs() as $s,n.abs() as $s) as $t 276 | } 277 | (a as $s).exp_residue(pow.abs() as $s,n.abs() as $s) as $t 278 | } 279 | 280 | fn checked_exp_residue(&self, pow: Self, n: Self) -> NTResult { 281 | let mut a = self.residue(n); 282 | 283 | if pow < 0{ 284 | let (gcd, inv,_) = (a as $s).extended_gcd(n.abs() as $s); 285 | if gcd != 1{ 286 | return NTResult::DNE 287 | } 288 | return inv.checked_exp_residue(pow.abs() as $s,n.abs() as $s).map(|y| y as $t) 289 | } 290 | (a as $s).checked_exp_residue(pow.abs() as $s, n.abs() as $s).map(|y| y as $t) 291 | } 292 | 293 | fn legendre(&self, p: Self) -> i8 { 294 | let k = self.exp_residue((p.abs() - 1) >> 1, p.abs()); 295 | if k == 1 { 296 | return 1; 297 | }; 298 | if k == p.abs() - 1 { 299 | return -1; 300 | }; 301 | return 0; 302 | } 303 | 304 | fn checked_legendre(&self, p: Self) -> NTResult { 305 | if p.abs() == 2 || p.is_prime() == false { 306 | return NTResult::Undefined; 307 | } 308 | NTResult::Eval(self.legendre(p)) 309 | } 310 | 311 | fn liouville(&self) -> i8{ 312 | (*self as $s).liouville() 313 | } 314 | 315 | fn derivative(&self) -> NTResult { 316 | (self.abs() as $s).derivative().map(|y| y as $t) 317 | } 318 | 319 | fn mangoldt(&self) -> f64 { 320 | (self.abs() as $s).mangoldt() 321 | } 322 | 323 | fn mobius(&self) -> i8 { 324 | (self.abs() as $s).mobius() 325 | } 326 | 327 | fn jacobi(&self, k: Self) -> i8 { 328 | 329 | let mut n = *self; 330 | let mut p = k; 331 | let mut t = 1i8; 332 | n %= p; 333 | 334 | while n != 0 { 335 | let zeros = n.trailing_zeros(); 336 | n >>= zeros; 337 | 338 | if (p % 8 == 3 || p % 8 == 5) && (zeros % 2 == 1) { 339 | t = -t 340 | } 341 | 342 | std::mem::swap(&mut n, &mut p); 343 | if n % 4 == 3 && p % 4 == 3 { 344 | t = -t; 345 | } 346 | n %= p; 347 | } 348 | 349 | if p == 1 { 350 | t 351 | } else { 352 | 0 353 | } 354 | } 355 | 356 | // Kronecker symbol 357 | fn kronecker(&self, k: Self) -> i8{ 358 | let res = (*self as $s).kronecker(k as $s); 359 | 360 | if k < 0 && *self < 0{ 361 | return res*-1; 362 | } 363 | res 364 | } 365 | 366 | fn checked_jacobi(&self, k: Self) -> NTResult { 367 | if k > 0 && k % 2 == 1 { 368 | return NTResult::Eval(self.jacobi(k)); 369 | } 370 | return NTResult::Undefined; 371 | } 372 | 373 | fn smooth(&self) -> NTResult { 374 | (*self as $s).smooth().map(|x| x as $t) 375 | } 376 | 377 | 378 | 379 | fn is_smooth(&self, b: Self) -> bool { 380 | match self.smooth(){ 381 | NTResult::Infinite => false, 382 | NTResult::Eval(x) => x <= b, 383 | _=> false, 384 | } 385 | } 386 | 387 | fn ord(&self, ring: Self) -> NTResult{ 388 | let u_ring = ring.abs(); 389 | let element = self.residue(u_ring); 390 | (element as $s).ord(u_ring as $s).map(|x| x as $t) 391 | } 392 | 393 | } 394 | )*} 395 | ); 396 | 397 | signednt!(i8;u8, i16;u16, i32;u32, i64;u64, isize;usize, i128;u128); 398 | -------------------------------------------------------------------------------- /src/montgomery.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of Montgomery arithmetic 3 | */ 4 | 5 | use crate::data::primes::INV_8; 6 | use crate::primitive::sixteenbytes::*; 7 | 8 | // use crate::traits::NumberTheory; 9 | 10 | fn mod_inv32(n: u32) -> u32 { 11 | // inverse of odd n in 2^32 12 | let mut est = INV_8[((n >> 1) & 0x7F) as usize] as u32; 13 | est = 2u32.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 14 | est = 2u32.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 15 | est = 2u32.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 16 | est.wrapping_neg() 17 | } 18 | 19 | pub(crate) fn mod_inv64(n: u64) -> u64 { 20 | // inverse of odd n in 2^64 21 | let mut est = INV_8[((n >> 1) & 0x7F) as usize] as u64; 22 | est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 23 | est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 24 | est = 2u64.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 25 | est.wrapping_neg() 26 | } 27 | 28 | fn mod_inv128(n: u128) -> u128 { 29 | // inverse of odd n in 2^128 30 | let mut est = INV_8[((n >> 1) & 0x7F) as usize] as u128; 31 | est = 2u128.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 32 | est = 2u128.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 33 | est = 2u128.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 34 | est = 2u128.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 35 | est = 2u128.wrapping_sub(est.wrapping_mul(n)).wrapping_mul(est); 36 | est.wrapping_neg() 37 | } 38 | 39 | fn x32_modn(x: u32, n: u32) -> u32 { 40 | (((x as u64) << 32) % (n as u64)) as u32 41 | } 42 | 43 | pub fn x64_modn(x: u64, n: u64) -> u64 { 44 | (((x as u128) << 64) % (n as u128)) as u64 45 | } 46 | 47 | pub fn x128_modn(x: u128, n: u128) -> u128 { 48 | u256mod128((x, 0), n) 49 | //n.wrapping_neg() 50 | //(u128::MAX%n)+1 51 | } 52 | 53 | fn montprod_32(x: u32, y: u32, n: u32, npi: u32) -> u32 { 54 | let input = x as u64 * y as u64; 55 | let tm = (input as u32).wrapping_mul(npi); 56 | let (t, overflow) = input.overflowing_add((tm as u64) * (n as u64)); 57 | let t = (t >> 32) as u32; 58 | 59 | if overflow { 60 | t + n.wrapping_neg() 61 | } else if t >= n { 62 | t - n 63 | } else { 64 | t 65 | } 66 | } 67 | 68 | fn montprod_64(x: u64, y: u64, n: u64, npi: u64) -> u64 { 69 | let input = x as u128 * y as u128; 70 | let tm = (input as u64).wrapping_mul(npi); 71 | let (t, overflow) = input.overflowing_add((tm as u128) * (n as u128)); 72 | let t = (t >> 64) as u64; 73 | 74 | if overflow { 75 | t + n.wrapping_neg() 76 | } else if t >= n { 77 | t - n 78 | } else { 79 | t 80 | } 81 | } 82 | 83 | fn montprod_128(x: u128, y: u128, n: u128, npi: u128) -> u128 { 84 | let (phi, plo) = u256prod(x, y); 85 | 86 | let tm = plo.wrapping_mul(npi); 87 | 88 | let (t, overflow) = overflowing_add(u256prod(n, tm), (phi, plo)); 89 | 90 | let t = t.0; 91 | 92 | if overflow { 93 | t + n.wrapping_neg() 94 | } else if t >= n { 95 | t - n 96 | } else { 97 | t 98 | } 99 | } 100 | 101 | fn mpow_32(x: u32, p: u32, n: u32, npi: u32) -> u32 { 102 | let mut z = x32_modn(1u32, n); 103 | let mut base = x32_modn(x, n); 104 | let mut pow = p; 105 | 106 | while pow > 1 { 107 | if pow & 1 == 0 { 108 | base = montprod_32(base, base, n, npi); 109 | pow >>= 1; 110 | } else { 111 | z = montprod_32(base, z, n, npi); 112 | base = montprod_32(base, base, n, npi); 113 | pow = (pow - 1) >> 1 114 | } 115 | } 116 | montprod_32(base, z, n, npi) 117 | } 118 | 119 | fn mpow_64(x: u64, p: u64, n: u64, npi: u64) -> u64 { 120 | let mut z = x64_modn(1u64, n); 121 | let mut base = x64_modn(x, n); 122 | let mut pow = p; 123 | 124 | while pow > 1 { 125 | if pow & 1 == 0 { 126 | base = montprod_64(base, base, n, npi); 127 | pow >>= 1; 128 | } else { 129 | z = montprod_64(base, z, n, npi); 130 | base = montprod_64(base, base, n, npi); 131 | pow = (pow - 1) >> 1 132 | } 133 | } 134 | montprod_64(base, z, n, npi) 135 | } 136 | 137 | fn mpow_128(x: u128, p: u128, n: u128, npi: u128) -> u128 { 138 | let mut z = (u128::MAX % n) + 1; //x128_modn(1u128, n); 139 | let mut base = x128_modn(x, n); 140 | let mut pow = p; 141 | 142 | while pow > 1 { 143 | if pow & 1 == 0 { 144 | base = montprod_128(base, base, n, npi); 145 | pow >>= 1; 146 | } else { 147 | z = montprod_128(base, z, n, npi); 148 | base = montprod_128(base, base, n, npi); 149 | pow = (pow - 1) >> 1 150 | } 151 | } 152 | montprod_128(base, z, n, npi) 153 | } 154 | 155 | pub(crate) fn sprp_128(p: u128, base: u128) -> bool { 156 | let p_minus = p - 1; 157 | let zeroes = p_minus.trailing_zeros(); 158 | let d = p_minus >> zeroes; 159 | 160 | let npi = mod_inv128(p); 161 | let mut x = mpow_128(base, d, p, npi); 162 | let one = (u128::MAX % p) + 1; //x128_modn(1, p); 163 | let oneinv = x128_modn(p_minus, p); 164 | 165 | if x == one || x == oneinv { 166 | return true; 167 | } 168 | for _ in 1..zeroes { 169 | x = montprod_128(x, x, p, npi); 170 | 171 | if x == oneinv { 172 | return true; 173 | } 174 | } 175 | false 176 | } 177 | 178 | pub(crate) fn sprp_32(p: u32, base: u32) -> bool { 179 | let p_minus = p - 1; 180 | let zeroes = p_minus.trailing_zeros(); 181 | let d = p_minus >> zeroes; 182 | 183 | let npi = mod_inv32(p); 184 | let mut x = mpow_32(base, d, p, npi); 185 | let one = x32_modn(1, p); 186 | let oneinv = x32_modn(p_minus, p); 187 | 188 | if x == one || x == oneinv { 189 | return true; 190 | } 191 | for _ in 1..zeroes { 192 | x = montprod_32(x, x, p, npi); 193 | 194 | if x == oneinv { 195 | return true; 196 | } 197 | } 198 | false 199 | } 200 | 201 | pub(crate) fn sprp_64(p: u64, base: u64) -> bool { 202 | let p_minus = p - 1; 203 | let zeroes = p_minus.trailing_zeros(); 204 | let d = p_minus >> zeroes; 205 | 206 | let npi = mod_inv64(p); 207 | let mut x = mpow_64(base, d, p, npi); 208 | let one = x64_modn(1, p); 209 | let oneinv = x64_modn(p_minus, p); 210 | if x == one || x == oneinv { 211 | return true; 212 | } 213 | for _ in 1..zeroes { 214 | x = montprod_64(x, x, p, npi); 215 | 216 | if x == oneinv { 217 | return true; 218 | } 219 | } 220 | false 221 | } 222 | 223 | // Fuses two strong fermat tests to minimize total operations. Reference implementation, not used 224 | #[inline] 225 | pub(crate) fn _sprp_64_double(p: u64, base1: u64, base2: u64) -> bool { 226 | let (mut ef_1, mut ef_2) = (false, false); 227 | let p_minus = p - 1; 228 | let twofactor = p_minus.trailing_zeros(); 229 | let mut d = p_minus >> twofactor; 230 | 231 | let npi = mod_inv64(p); 232 | let one = (u64::MAX % p) + 1; 233 | let mut z1 = one; 234 | let mut z2 = one; 235 | 236 | let mut result1 = (((base1 as u128) << 64) % (p as u128)) as u64; 237 | let mut result2 = (((base2 as u128) << 64) % (p as u128)) as u64; 238 | 239 | let oneinv = (((p_minus as u128) << 64) % (p as u128)) as u64; 240 | 241 | while d > 1 { 242 | if d & 1 == 0 { 243 | result1 = montprod_64(result1, result1, p, npi); 244 | result2 = montprod_64(result2, result2, p, npi); 245 | d >>= 1; 246 | } else { 247 | z1 = montprod_64(z1, result1, p, npi); 248 | result1 = montprod_64(result1, result1, p, npi); 249 | 250 | z2 = montprod_64(z2, result2, p, npi); 251 | result2 = montprod_64(result2, result2, p, npi); 252 | 253 | d = (d - 1) >> 1; 254 | } 255 | } 256 | 257 | result1 = montprod_64(z1, result1, p, npi); 258 | result2 = montprod_64(z2, result2, p, npi); 259 | 260 | if result1 == one || result1 == oneinv { 261 | ef_1 = true; 262 | } 263 | 264 | if result2 == one || result2 == oneinv { 265 | ef_1 = true; 266 | } 267 | 268 | if ef_1 & ef_2 { 269 | return true; 270 | } 271 | 272 | for _ in 1..twofactor { 273 | result1 = montprod_64(result1, result1, p, npi); 274 | result2 = montprod_64(result2, result2, p, npi); 275 | 276 | if result1 == oneinv { 277 | ef_1 = true; 278 | } 279 | if result2 == oneinv { 280 | ef_2 = true; 281 | } 282 | if ef_1 & ef_2 { 283 | return true; 284 | } 285 | } 286 | false 287 | } 288 | 289 | fn odd_pow32(x: u32, p: u32, n: u32) -> u32 { 290 | let npi = mod_inv32(n); 291 | let interim = mpow_32(x, p, n, npi); 292 | montprod_32(1u32, interim, n, npi) 293 | } 294 | 295 | fn odd_pow64(x: u64, p: u64, n: u64) -> u64 { 296 | let npi = mod_inv64(n); 297 | let interim = mpow_64(x, p, n, npi); 298 | montprod_64(1u64, interim, n, npi) 299 | } 300 | 301 | fn odd_pow128(x: u128, p: u128, n: u128) -> u128 { 302 | let npi = mod_inv128(n); 303 | let interim = mpow_128(x, p, n, npi); 304 | montprod_128(1u128, interim, n, npi) 305 | } 306 | 307 | pub(crate) fn pow_32(x: u32, y: u32, n: u32) -> u32 { 308 | if n & 1 == 0 { 309 | let k = n.trailing_zeros() as u64; 310 | let s = n >> k; 311 | 312 | let reducer = (1 << k) - 1; // A shorthand for arithmetic over Z[2k] 313 | 314 | let k_rem = x.wrapping_pow(y) & reducer; 315 | 316 | let s_rem = odd_pow32(x, y, s); 317 | 318 | let mut s_inv = s; 319 | 320 | for _ in 0..10 { 321 | // Multiplicative inverse over Z[2k] 322 | s_inv = 2u32.wrapping_sub(s_inv.wrapping_mul(s)).wrapping_mul(s_inv) & reducer; 323 | } 324 | 325 | let y = k_rem.wrapping_sub(s_rem).wrapping_mul(s_inv) & reducer; 326 | 327 | s_rem + s * y 328 | } else { 329 | odd_pow32(x, y, n) 330 | } 331 | } 332 | 333 | fn even_pow_64(x: u64, y: u64, reducer: u64) -> u64 { 334 | let mut z = 1u64; 335 | let mut base = x; 336 | 337 | let mut pow = y; 338 | if pow == 0 { 339 | return z; 340 | } 341 | 342 | while pow > 1 { 343 | if pow % 2 == 0 { 344 | base = base.wrapping_mul(base); 345 | pow >>= 1; 346 | } else { 347 | z = base.wrapping_mul(z); 348 | base = base.wrapping_mul(base); 349 | pow = (pow - 1) >> 1; 350 | } 351 | } 352 | 353 | base.wrapping_mul(z) & reducer 354 | } 355 | 356 | fn even_pow_128(x: u128, y: u128, reducer: u128) -> u128 { 357 | let _result = x; 358 | let mut z = 1u128; 359 | let mut base = x; 360 | // let n = modulus as u128; 361 | let mut pow = y; 362 | if pow == 0 { 363 | return z; 364 | } 365 | 366 | while pow > 1 { 367 | if pow % 2 == 0 { 368 | base = base.wrapping_mul(base); 369 | pow >>= 1; 370 | } else { 371 | z = base.wrapping_mul(z); 372 | base = base.wrapping_mul(base); 373 | pow = (pow - 1) >> 1; 374 | } 375 | } 376 | 377 | base.wrapping_mul(z) & reducer 378 | } 379 | 380 | pub(crate) fn pow_64(x: u64, y: u64, n: u64) -> u64 { 381 | if n & 1 == 0 { 382 | let k = n.trailing_zeros() as u64; 383 | let s = n >> k; 384 | 385 | let reducer = (1 << k) - 1; // A shorthand for arithmetic over Z[2k] 386 | 387 | let k_rem = even_pow_64(x, y, reducer); //x.wrapping_pow(y as u32)&reducer; 388 | 389 | let s_rem = odd_pow64(x, y, s); 390 | 391 | let mut s_inv = s; 392 | 393 | for _ in 0..10 { 394 | // Multiplicative inverse over Z[2k] 395 | s_inv = 2u64.wrapping_sub(s_inv.wrapping_mul(s)).wrapping_mul(s_inv) & reducer; 396 | } 397 | 398 | let y = k_rem.wrapping_sub(s_rem).wrapping_mul(s_inv) & reducer; 399 | 400 | s_rem + s * y 401 | } else { 402 | odd_pow64(x, y, n) 403 | } 404 | } 405 | 406 | pub(crate) fn pow_128(x: u128, y: u128, n: u128) -> u128 { 407 | if n & 1 == 0 { 408 | let k = n.trailing_zeros() as u128; 409 | let s = n >> k; 410 | 411 | let reducer = (1 << k) - 1; // A shorthand for arithmetic over Z[2k] 412 | 413 | let k_rem = even_pow_128(x, y, reducer); 414 | 415 | let s_rem = odd_pow128(x, y, s); 416 | 417 | let mut s_inv = s; 418 | 419 | for _ in 0..10 { 420 | // Multiplicative inverse over Z[2k] 421 | s_inv = 2u128 422 | .wrapping_sub(s_inv.wrapping_mul(s)) 423 | .wrapping_mul(s_inv) 424 | & reducer; 425 | } 426 | 427 | let y = k_rem.wrapping_sub(s_rem).wrapping_mul(s_inv) & reducer; 428 | 429 | s_rem + s * y 430 | } else { 431 | odd_pow128(x, y, n) 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /tests/strong_prime.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | List of computationally intensive tests that demonstrate the claims made by the author. 4 | 5 | 6 | Full computation of each individual test may take hours so it is imperative that it is run as cargo test --release and during downtime. 7 | 8 | These tests are primarily to ensure confidence in program correctness, especially in light of the errors presented by other number-theory libraries in Rust 3 9 | and elsewhere. 10 | 11 | The assumptions that are made to claim correctness are listed next to the test. If the assumptions are wrong then the proof of correctness is also wrong 12 | 13 | Assumption abbreviations : P = mathematically proven, V = Verified by at least two parties including at least partially by the author, 14 | UV = Computed by the author, but unverified by external parties, 15 | SP = statistically probable, applies to analyzing external software that corroborates well with proven bounds 16 | 17 | */ 18 | 19 | use number_theory::{NumberTheory,Mpz}; 20 | 21 | /* 22 | 23 | Description Assumptions Approximate Time 24 | _______________________________________________________________________________________________________________ 25 | Exhaustive proof of Correctness of Erastothenes sieve in computing pi(n) (P) 5E+3 s 26 | deterministic primality Correctness of strong fermat test (P) 27 | for integers under Correctness of Feitsma-Galway pseudoprime table (V) 28 | 2^64 + 2^44 Correctness of J.A Sory's reduction and extension of table (UV) 29 | Correctness of Kim Walisch's implementation of Gourdon's pi(n) (SP) 30 | Correctness of Damgard et .al in pseudoprime density (P) 31 | 32 | _________________________________________________________________________________________________________________ 33 | 34 | 35 | 36 | */ 37 | 38 | #[ignore] 39 | #[test] 40 | fn det_primality() { 41 | /* Determinism between 2^35;2^64 + 2^45 is easy as we only have to check the base-2 pseudoprimes 42 | and a table of such pseudoprimes has been provided by Jan Feitsma, William Galway. 43 | J.A Sory reduced the table to strong pseudoprimes coprime to the first 64 odd primes and extended it to 2^64+2^45. 44 | We use this variant below due to 4x faster evaluation. 45 | 46 | Contact j_a_sory at rust-cas dot org to obtain a copy as it is not currently publicly available. 47 | 48 | */ 49 | 50 | let location = "/home/jasory/Proving/FGS2SPRP"; 51 | let data = std::fs::read_to_string(location).expect( 52 | "Change your file path: Unable to find the base-2 pseudoprime table to prove correctness", 53 | ); 54 | // no checks performed as all numbers are guaranteed to be valid if you are using Feitsma or Jasory's table 55 | let pseudos = data 56 | .split_whitespace() 57 | .map(|x| x.parse::().unwrap()) 58 | .collect::>(); 59 | 60 | for i in pseudos { 61 | assert_eq!(i.is_prime(), false) 62 | } 63 | 64 | let mut count = 0u32; 65 | 66 | for i in 0..u32::MAX { 67 | if i.is_prime() { 68 | count += 1; 69 | } 70 | } 71 | 72 | // Deterministic under 2^32, this must be computed exhaustively as it utilizes a single test 73 | assert_eq!(count, 203280221); 74 | 75 | // Finish checking up to 2^35 76 | for i in 4294967296u64..34359738368u64 { 77 | if i.is_prime() { 78 | count += 1; 79 | } 80 | } 81 | // Deterministic under 2^35 82 | assert_eq!(count, 1480206279) 83 | } 84 | 85 | /* 86 | Checks that approximately half (60%+) of the base-2 strong pseudoprimes between 2^64 and 2^67 are filtered. This is almost certainly an error rate much 87 | less than 2^-64. 88 | (Probabilistic claim as only about 1 base-2 pseudoprime exists per 2^40 composites in the interval. However fully computing pseudoprimes up to 2^67 is not possible, 89 | so this cannot be proven) 90 | 91 | Utilizes a pre-computed table of composites of the form (k+1)(rk+1) where r > 65535 92 | 93 | Fails at a rate of 5%, returning a 1, this is well-within the acceptable bounds for the advertised accuracy which is 4 per check (2^67/2^64 = 8*60% = 4) 94 | */ 95 | #[ignore] 96 | #[test] 97 | fn heuristic_test() { 98 | // List of approximately 60% of the 2-strong-pseudoprimes between 2^64;2^67 or 31280009 pseudoprimes 99 | let location = "/home/jasory/Proving/H2SPRP-64-67"; 100 | let data = std::fs::read_to_string(location).expect( 101 | "Change your file path: Unable to find the base-2 pseudoprime table to prove correctness", 102 | ); 103 | // no checks performed as all numbers are guaranteed to be valid 104 | let mut failures = 0u64; 105 | let h_pseudos = data 106 | .split_whitespace() 107 | .map(|x| x.parse::().unwrap()) 108 | .collect::>(); 109 | assert_eq!(h_pseudos.len(), 31280009); 110 | for i in h_pseudos { 111 | //assert_eq!(i.is_prime(), false) 112 | if i.is_prime() { 113 | failures += 1; 114 | } 115 | } 116 | assert_eq!(failures, 0) // may fail however as long as the result is less than 4 (never observed) the accuracy bound is valid 117 | } 118 | 119 | /* 120 | 121 | Tested Permitted Error 122 | 123 | 1E+3 iterations of Arnault's Carmichael number 30% (This is actually asymptotic to 1/4 however for smaller values it may exceed it so the permitted bound is 3/10) 124 | 10 512+ bit Carmichael numbers 0% // These Carmichael numbers are a subset of the form (6k+1)(12k+1)(18k+1) 125 | 10 4000+ bit Carmichael numbers 0% 126 | 127 | 128 | IS_Prime is possibly deterministic against Carmichael numbers of the form (6k+1)(12k+1)(18k+1) (This is hard to evaluate as none of ) 129 | */ 130 | 131 | #[ignore] 132 | #[test] 133 | fn carmichael_test() { 134 | let mut failures = 0u64; 135 | /* 136 | let arnault = Mpz::u_new(vec![ 137 | // Arnault's exceptionally strong Carmichael number that is strong pseudoprime to all n < 307, and approximately 1/3 of all bases 138 | 14125993214864411435, 139 | 17627421051211808665, 140 | 8803062915072472141, 141 | 16592776965248599063, 142 | 2400842081300231610, 143 | 15499637576960551660, 144 | 13635884493322354475, 145 | 11162303294968070407, 146 | 16638949889857351534, 147 | 4755447895431218293, 148 | 3008559606663544016, 149 | 6962752268970074012, 150 | 15476370627519396605, 151 | 7818823728409314089, 152 | 7726208862163452462, 153 | 2793947945622957183, 154 | 9898516143788448943, 155 | 2723958373070846137, 156 | 15945921893246334102, 157 | 9257474160255769327, 158 | 138699416178, 159 | ]); 160 | 161 | for _ in 0..1000 { 162 | if arnault.is_prime() { 163 | failures += 1; 164 | } 165 | } 166 | 167 | assert!(failures < 300); // error rate is less than 30%. 168 | */ 169 | let six = Mpz::from(6u64); 170 | let twelve = Mpz::from(12u64); 171 | let eighteen = Mpz::from(18u64); 172 | let one = Mpz::one(); 173 | 174 | let rand_gen = |len: usize| -> (Mpz, Mpz, Mpz) { 175 | // Generates Carmichael numbers of the form (6k+1)(12k+1)(18k+1) 176 | loop { 177 | let rand = Mpz::rand(len*64); 178 | // rand.set_bit(0); 179 | 180 | let lhs = six.ref_product(&rand).ref_addition(&one); 181 | let mid = twelve.ref_product(&rand).ref_addition(&one); 182 | let rhs = eighteen.ref_product(&rand).ref_addition(&one); 183 | if lhs.is_prime() { 184 | if mid.is_prime() { 185 | if rhs.is_prime() { 186 | return (lhs, mid, rhs); 187 | } 188 | } 189 | } 190 | } 191 | }; 192 | 193 | for _ in 0..100{ 194 | // 512 bit test 195 | 196 | let (lhs, mid, rhs) = rand_gen(1); 197 | let carmichael = lhs.ref_product(&mid).ref_product(&rhs); 198 | if carmichael.sprp_check(1){ 199 | failures+=1; 200 | } 201 | //assert_eq!(carmichael.sprp_check(1), false) 202 | } 203 | assert_eq!(0,failures); 204 | /* 205 | for _ in 0..10 { 206 | // 1E+4 Carmichael numbers of the same magnitude as Arnault's 207 | let (lhs, mid, rhs) = rand_gen(21); 208 | let carmichael = lhs.ref_product(&mid).ref_product(&rhs); 209 | assert_eq!(carmichael.is_prime(), false) 210 | }*/ 211 | } 212 | 213 | /* 214 | 215 | Constructs 1E+5 probable semiprimes of the form (k+1)(rk+1) where r < 64. This is approximately half of the 216 | strong pseudoprimes. If this test passes then it is safe to judge the accuracy 217 | 218 | This test takes approximately 160 hrs to execute. 219 | 220 | */ 221 | #[ignore] 222 | #[test] 223 | fn probable_prime() { 224 | let rand_gen = |len: usize, k: u64| -> (Mpz, Mpz) { 225 | loop { 226 | let rng = Mpz::rand(len*64); 227 | let rk_one = Mpz::from(k).ref_product(&rng).ref_addition(&Mpz::one()); 228 | let k_one = rng.ref_addition(&Mpz::one()); 229 | 230 | if k_one.is_prime() & rk_one.is_prime() { 231 | return (k_one, rk_one); 232 | } 233 | } 234 | }; 235 | 236 | //let mut veccy = vec![]; 237 | // generate and collect 10000 semiprimes of the form (k+1)(2k+1) 238 | let mut count = 0u64; 239 | for j in 2..64 { 240 | for _i in 0..100 { 241 | let (k_one, rk_one) = rand_gen(4, j); 242 | if k_one.ref_product(&rk_one).is_prime() { 243 | count += 1 244 | } 245 | } 246 | } 247 | assert_eq!(count, 0) 248 | } 249 | 250 | /* List of primes that are/were passed by prominent libraries as described in Prime & Prejudice: Primality Testing Albrecht et al . 251 | Testing them here is largely irrelevant as the purpose of the paper was to demonstrate that deterministically selected bases fail 252 | against selected primes and all deterministically selected bases in Number-Theory have either been proven to filter all composites or are complemented by random bases. 253 | 254 | As implied this test is simply for user confidence rather than providing any particular insight. 255 | 256 | 257 | 258 | */ 259 | 260 | #[rustfmt::skip] 261 | #[ignore] 262 | #[test] 263 | 264 | fn confidence_test(){ 265 | 266 | let gmp_fear = Mpz::u_new(vec![ // Claimed to pass GMP 6.1.2 267 | 13347957477412617151, 16259308113041253372, 268 | 8892814040333046162, 1221545748070377901, 269 | 13845990824863685200, 11427719796916674983, 270 | 6523989286207306293, 8228562809691301656, 271 | 15942565893161427335, 17798657415511144750, 272 | 9895556227828505149, 1067200624540728504, 6934903806235641717, 273 | 8868460805486687741, 8433825644186292052, 9355494833841550342 274 | ]); 275 | 276 | for _ in 0..10000{// Deterministic due to a radical difference in base selection 277 | assert_eq!(gmp_fear.is_prime(), false) 278 | } 279 | 280 | 281 | // Claimed to deterministically pass Apple, Libtom, CommonCrypto & WolfSSL, see original paper for software versions 282 | // (this may no longer be true); 283 | // Note that for composites of the form p*(x(p-1)+1)(y(p-1)+1), is_prime has at worst around 1/4 284 | let security_flaw = Mpz::u_new(vec![ 285 | 9331929281010904555, 5494805439598348446, 8274855319168077407, 286 | 8989115052798713304, 7922897861797270742, 16684056220225637685, 287 | 11573383658562860865, 10538110707526527084, 11841525957671433530, 288 | 6629998598229579978, 9754395927480723018, 1082083247965744052, 289 | 13178505664201790977, 445352704338639786, 1262458056955046950, 290 | 10952769848018447964, 9675180056116935648, 18095375587926220513, 291 | 14906862786560135370, 7349764202741105504, 14310555118399348948, 292 | 16532416774430072831, 5249926442601408071, 10781540968865591231, 293 | 15478470103784080673, 12574244713382308730, 7937862675332072157, 294 | 8979440646405107343, 7057598508513752002, 330859835934063533, 295 | 15284532345271639087, 2683089657284425004, 12388650678928717386, 296 | 4601150668493820598, 17591554493017240341, 8874420574259649282, 297 | 5916665853078007903, 1744435599167408248, 1375564577028398640, 298 | 11421316388837429873, 16721851356632668685, 3143746952704220612, 299 | 9964885490987307667, 17847312369004334358, 2102167341425916716, 300 | 17402799351753590688, 5031694375487631134, 9769474730685880473, 301 | 9334559868889485670, 5705824887076732981, 10296763285808252259, 302 | 15052706519152474971, 10441944543730280361, 3742633571041123035, 303 | 7671374831153001193, 5651690108603415721, 7360676820152831689, 304 | 3300584806060773119, 13951650231935285153, 14432958795279723086, 305 | 5197204459193853662, 16439386015664755780, 15489184032414823641, 306 | 5445353960443525377, 17365100034401419080, 2507657222167183359, 307 | 7305097783839097981, 3577408235263180558, 17574691796078419858, 308 | 7220852877782605389, 5724975390124524347, 7983361366552541545, 309 | 7183619713474561234, 14313726161713768561, 17635332671748993665, 310 | 10189608807876133269, 15326339027698575730, 16262806750666400113, 311 | 6718975974224483036, 3265280417026005297, 8262935061937412279, 312 | 11470300983668961140, 18234435204737306426, 13868574488010990731, 313 | 15544778790353478078, 1654287122911138553, 12115954827342798448, 314 | 18315805652863710573, 9072519746302664175, 9530505032704430404, 315 | 10950413179334519245, 11500900080207907576, 5289500343761324225, 316 | 5730607270653974271, 12934085552529239836, 1627559471621096679, 317 | 330020620966697801, 15559778274953646736, 11080068881085564969, 318 | 10399807091365952939, 4843032489303663269, 16516455478232626003, 319 | 10601252522603802158, 767100916292225657, 2497006575850938970, 320 | 5676095330871291613, 11532840839212411388, 14210693347085223154, 321 | 13738501162309476745, 110019121374055] 322 | ); 323 | let mut failures = 0u8; 324 | for _ in 0..100{ 325 | if security_flaw.is_prime(){ 326 | failures+=1 327 | } 328 | } 329 | assert!(failures < 30) 330 | } 331 | -------------------------------------------------------------------------------- /src/data/primes.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Prime Data used for primality testing 4 | 5 | 6 | 7 | 8 | "DET_MAX" is the upperbound for deterministic tests, all primes below this number receive a maximum of 2 sprp tests, 9 | a considerable speed up over the minimum of 12 tests that have been previously proven, note that 10 | this is approximately 590 trillion higher than the bound of 2^64, (and over 13 trillion more primes) provided by other tests and 11 | continously increases due to research by J.A Sory 12 | */ 13 | pub(crate) const DET_MAX: u128 = 0x100021C0000000000; // current bound 2^64 + 2^49 + 2^44 + 2^43 + 2^42 14 | 15 | // List of Mersenne prime exponents, shortcuts computation as all Mersenne's not listed below the bound of 57885161 have been proven composite 16 | pub(crate) const MERSENNE_LIST: [u32; 42] = [ 17 | 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, 11213, 19937, 21701, 18 | 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433, 1257787, 1398269, 2976221, 19 | 3021377, 6972593, 13466917, 20996011, 24036583, 25964951, 30402457, 32582657, 37156667, 20 | 42643801, 43112609, 57885161, 74207281, 77232917, 82589933, 21 | ]; 22 | 23 | 24 | // List of 2048 primes, exclusively used for trial division of huge integers, machine-sized words use precomputed inverses, Possibly remove? 25 | pub(crate) const PRIMELIST : [u16;2048] = [ 26 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 27 | 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 28 | 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 29 | 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 30 | 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 31 | 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 32 | 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 33 | 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 34 | 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 35 | 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 36 | 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 37 | 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 38 | 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 39 | 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 40 | 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 41 | 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 42 | 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 43 | 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 44 | 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 45 | 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 46 | 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 47 | 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 48 | 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 49 | 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 50 | 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 51 | 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 52 | 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 53 | 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 54 | 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 55 | 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 56 | 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 57 | 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 58 | 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 59 | 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 60 | 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 61 | 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 62 | 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 63 | 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 64 | 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 65 | 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 66 | 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 67 | 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 68 | 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 69 | 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 70 | 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 71 | 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 72 | 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 73 | 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 74 | 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 75 | 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 76 | 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 77 | 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 78 | 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, 79 | 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, 80 | 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831, 81 | 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, 82 | 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, 83 | 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 84 | 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, 85 | 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 86 | 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 87 | 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 88 | 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, 89 | 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, 90 | 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 91 | 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, 92 | 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 93 | 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 94 | 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443, 95 | 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 96 | 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 97 | 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 98 | 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211, 99 | 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 100 | 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, 101 | 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 102 | 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 103 | 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 104 | 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, 105 | 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 106 | 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, 107 | 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, 108 | 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 109 | 14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, 110 | 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519, 111 | 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 112 | 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, 113 | 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 114 | 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 115 | 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, 116 | 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607, 117 | 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 118 | 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, 119 | 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 120 | 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 121 | 16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, 122 | 16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, 123 | 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 124 | 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, 125 | 17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 126 | 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, 127 | 17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863]; 128 | 129 | #[test] 130 | fn check_sum(){ 131 | let mut sum = 0u64; 132 | 133 | for i in PRIMELIST{ 134 | sum = sum.wrapping_add(i as u64); 135 | } 136 | assert_eq!(sum,17120309) 137 | 138 | } 139 | --------------------------------------------------------------------------------