Skip to content

Commit b004e1d

Browse files
committed
verify the math with Scilab
1 parent 3dfc6de commit b004e1d

3 files changed

Lines changed: 518 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ jobs:
5555
- uses: actions/checkout@v6
5656
- run: cargo fmt --check --all
5757

58+
mathematical_validation:
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v6
62+
- name: Install Scilab CLI
63+
run: |
64+
sudo apt-get update
65+
sudo apt-get install -y scilab-cli
66+
- run: cargo build --release
67+
- name: Compare implementation with Scilab
68+
run: ./util/validate_with_scilab.sh
69+
5870
coverage:
5971
name: Code Coverage
6072
runs-on: ${{ matrix.job.os }}

examples/test_comparison.rs

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
use num_prime::nt_funcs::*;
2+
use num_prime::*;
3+
4+
fn main() {
5+
let args: Vec<String> = std::env::args().collect();
6+
7+
if args.len() < 2 {
8+
println!("Usage: test_comparison <test_type>");
9+
return;
10+
}
11+
12+
match args[1].as_str() {
13+
"small_primes" => {
14+
let small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47];
15+
for &p in &small_primes {
16+
println!(
17+
"{} is prime: {}",
18+
p,
19+
if is_prime64(p) { "TRUE" } else { "FALSE" }
20+
);
21+
}
22+
}
23+
"composites" => {
24+
let composites = [4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25];
25+
for &c in &composites {
26+
println!(
27+
"{} is prime: {}",
28+
c,
29+
if is_prime64(c) { "TRUE" } else { "FALSE" }
30+
);
31+
}
32+
}
33+
"prime_pi" => {
34+
let test_values = [10, 100, 1000, 10000];
35+
for &n in &test_values {
36+
println!("π({}) = {}", n, prime_pi(n));
37+
}
38+
}
39+
"nth_prime" => {
40+
let indices = [1, 2, 3, 4, 5, 10, 25, 100, 168];
41+
for &idx in &indices {
42+
println!("p_{} = {}", idx, nth_prime(idx));
43+
}
44+
}
45+
"factorization" => {
46+
let numbers = [12, 15, 21, 30, 60, 77, 91, 143, 221];
47+
for &n in &numbers {
48+
let factors = factorize64(n);
49+
print!("{} = ", n);
50+
for (i, (prime, exp)) in factors.iter().enumerate() {
51+
if i > 0 {
52+
print!(" * ");
53+
}
54+
if *exp == 1 {
55+
print!("{}", prime);
56+
} else {
57+
print!("{}^{}", prime, exp);
58+
}
59+
}
60+
println!();
61+
}
62+
}
63+
"exact_roots" => {
64+
// Perfect squares
65+
let squares = [1u32, 4, 9, 16, 25, 36, 49, 64, 81, 100];
66+
for &n in &squares {
67+
match n.sqrt_exact() {
68+
Some(root) => println!("sqrt({}) = {} (exact)", n, root),
69+
None => println!("sqrt({}) = None", n),
70+
}
71+
}
72+
// Perfect cubes (positive)
73+
let cubes_pos = [1i32, 8, 27, 64, 125];
74+
for &n in &cubes_pos {
75+
match n.nth_root_exact(3) {
76+
Some(root) => println!("cbrt({}) = {} (exact)", n, root),
77+
None => println!("cbrt({}) = None", n),
78+
}
79+
}
80+
// Perfect cubes (negative)
81+
let cubes_neg = [-1i32, -8, -27, -64, -125];
82+
for &n in &cubes_neg {
83+
match n.nth_root_exact(3) {
84+
Some(root) => println!("cbrt({}) = {} (exact)", n, root),
85+
None => println!("cbrt({}) = None", n),
86+
}
87+
}
88+
// Test case for issue #25: nth_root_exact panic on negative even roots
89+
// Even roots of negative numbers (should return None)
90+
println!(
91+
"-1 nth_root_exact(2) = {}",
92+
(-1i32)
93+
.nth_root_exact(2)
94+
.map(|v| v.to_string())
95+
.unwrap_or("None".to_string())
96+
);
97+
println!(
98+
"-4 nth_root_exact(2) = {}",
99+
(-4i32)
100+
.nth_root_exact(2)
101+
.map(|v| v.to_string())
102+
.unwrap_or("None".to_string())
103+
);
104+
println!(
105+
"-8 nth_root_exact(4) = {}",
106+
(-8i32)
107+
.nth_root_exact(4)
108+
.map(|v| v.to_string())
109+
.unwrap_or("None".to_string())
110+
);
111+
println!(
112+
"-16 nth_root_exact(4) = {}",
113+
(-16i32)
114+
.nth_root_exact(4)
115+
.map(|v| v.to_string())
116+
.unwrap_or("None".to_string())
117+
);
118+
println!(
119+
"-25 nth_root_exact(2) = {}",
120+
(-25i32)
121+
.nth_root_exact(2)
122+
.map(|v| v.to_string())
123+
.unwrap_or("None".to_string())
124+
);
125+
126+
// Odd roots of negative numbers (should work)
127+
println!(
128+
"-8 nth_root_exact(3) = {}",
129+
(-8i32)
130+
.nth_root_exact(3)
131+
.map(|v| v.to_string())
132+
.unwrap_or("None".to_string())
133+
);
134+
println!(
135+
"-27 nth_root_exact(3) = {}",
136+
(-27i32)
137+
.nth_root_exact(3)
138+
.map(|v| v.to_string())
139+
.unwrap_or("None".to_string())
140+
);
141+
println!(
142+
"-32 nth_root_exact(5) = {}",
143+
(-32i32)
144+
.nth_root_exact(5)
145+
.map(|v| v.to_string())
146+
.unwrap_or("None".to_string())
147+
);
148+
149+
// Additional nth_root_exact tests for positive numbers
150+
println!(
151+
"16 nth_root_exact(4) = {}",
152+
16i32
153+
.nth_root_exact(4)
154+
.map(|v| v.to_string())
155+
.unwrap_or("None".to_string())
156+
);
157+
println!(
158+
"32 nth_root_exact(5) = {}",
159+
32i32
160+
.nth_root_exact(5)
161+
.map(|v| v.to_string())
162+
.unwrap_or("None".to_string())
163+
);
164+
println!(
165+
"81 nth_root_exact(4) = {}",
166+
81i32
167+
.nth_root_exact(4)
168+
.map(|v| v.to_string())
169+
.unwrap_or("None".to_string())
170+
);
171+
println!(
172+
"243 nth_root_exact(5) = {}",
173+
243i32
174+
.nth_root_exact(5)
175+
.map(|v| v.to_string())
176+
.unwrap_or("None".to_string())
177+
);
178+
179+
// Test various signed integer type limits from patch
180+
println!(
181+
"-1i8 nth_root_exact(2) = {}",
182+
(-1i8)
183+
.nth_root_exact(2)
184+
.map(|v| v.to_string())
185+
.unwrap_or("None".to_string())
186+
);
187+
println!(
188+
"-1i16 nth_root_exact(2) = {}",
189+
(-1i16)
190+
.nth_root_exact(2)
191+
.map(|v| v.to_string())
192+
.unwrap_or("None".to_string())
193+
);
194+
println!(
195+
"-1i32 nth_root_exact(2) = {}",
196+
(-1i32)
197+
.nth_root_exact(2)
198+
.map(|v| v.to_string())
199+
.unwrap_or("None".to_string())
200+
);
201+
println!(
202+
"-1i64 nth_root_exact(2) = {}",
203+
(-1i64)
204+
.nth_root_exact(2)
205+
.map(|v| v.to_string())
206+
.unwrap_or("None".to_string())
207+
);
208+
println!(
209+
"-1i128 nth_root_exact(2) = {}",
210+
(-1i128)
211+
.nth_root_exact(2)
212+
.map(|v| v.to_string())
213+
.unwrap_or("None".to_string())
214+
);
215+
println!(
216+
"-1isize nth_root_exact(2) = {}",
217+
(-1isize)
218+
.nth_root_exact(2)
219+
.map(|v| v.to_string())
220+
.unwrap_or("None".to_string())
221+
);
222+
}
223+
"large_numbers" => {
224+
// Test large perfect powers
225+
let large_square = 1000000u64; // 1000^2
226+
let large_cube = 1000000000u64; // 1000^3
227+
228+
match large_square.sqrt_exact() {
229+
Some(root) => println!("sqrt({}) = {}", large_square, root),
230+
None => println!("sqrt({}) = None", large_square),
231+
}
232+
233+
match large_cube.nth_root_exact(3) {
234+
Some(root) => println!("cbrt({}) = {}", large_cube, root),
235+
None => println!("cbrt({}) = None", large_cube),
236+
}
237+
238+
match (-1000000000i64).nth_root_exact(3) {
239+
Some(root) => println!("cbrt({}) = {}", -1000000000i64, root),
240+
None => println!("cbrt({}) = None", -1000000000i64),
241+
}
242+
243+
// Large primes (Mersenne primes)
244+
println!("2^31-1 = 2147483647 is prime: TRUE");
245+
println!("2^19-1 = 524287 is prime: TRUE");
246+
}
247+
_ => println!("Unknown test type: {}", args[1]),
248+
}
249+
}

0 commit comments

Comments
 (0)