Skip to content

Commit 9457772

Browse files
authored
perf(core): optimize isPrime with 6k±1 algorithm and add benchmarks (#5)
Optimize isPrime function using the 6k±1 optimization, which reduces iterations by ~33% since all primes > 3 are of the form 6k±1. Changes: - Use 6k±1 pattern for trial division instead of checking all odd numbers - Add early exit for divisibility by 3 - Add comprehensive benchmark covering edge cases, project primes, and worst-case scenarios All cases maintain zero allocations.
1 parent b21cdd0 commit 9457772

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

core/params.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,22 @@ func ValidateParams(params kmosaic.MOSAICParams) error {
9696
return nil
9797
}
9898

99-
// isPrime checks if a number is prime using a simple trial division.
99+
// isPrime checks if a number is prime using optimized trial division.
100+
// Uses the 6k±1 optimization since all primes > 3 are of the form 6k±1.
100101
// This is used for validating parameters, not for generating large primes.
101102
func isPrime(n int) bool {
102103
if n < 2 {
103104
return false
104105
}
105-
if n == 2 {
106+
if n == 2 || n == 3 {
106107
return true
107108
}
108-
if n%2 == 0 {
109+
if n%2 == 0 || n%3 == 0 {
109110
return false
110111
}
111-
for i := 3; i*i <= n; i += 2 {
112-
if n%i == 0 {
112+
// Check divisibility by numbers of the form 6k±1
113+
for i := 5; i*i <= n; i += 6 {
114+
if n%i == 0 || n%(i+2) == 0 {
113115
return false
114116
}
115117
}

core/params_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,52 @@ func TestIsPrime(t *testing.T) {
9191
}
9292
}
9393
}
94+
95+
func BenchmarkIsPrime(b *testing.B) {
96+
testCases := []struct {
97+
name string
98+
n int
99+
}{
100+
// Edge cases
101+
{"Edge_Zero", 0},
102+
{"Edge_One", 1},
103+
{"Edge_Two", 2},
104+
{"Edge_Three", 3},
105+
106+
// Small numbers
107+
{"Small_Prime", 17},
108+
{"Small_Composite_Even", 18},
109+
{"Small_Composite_Odd", 21}, // 3*7
110+
{"Small_Composite_ByThree", 27}, // 3^3
111+
112+
// Actual project primes (from MOS128/256 params)
113+
{"Project_EGRW128_Prime", 1021},
114+
{"Project_EGRW256_Prime", 2039},
115+
{"Project_TDD_Prime", 7681},
116+
{"Project_SLSS_Prime", 12289},
117+
118+
// Composites near project primes (harder cases)
119+
{"Near_1021_Composite", 1023}, // 3*11*31
120+
{"Near_2039_Composite", 2041}, // 13*157
121+
{"Near_7681_Composite", 7685}, // 5*29*53
122+
{"Near_12289_Composite", 12291}, // 3*4097
123+
124+
// Large primes (worst case - need many checks)
125+
{"Large_Prime_Near_Square", 104729}, // sqrt ≈ 323
126+
{"Large_Prime_100k", 104743},
127+
{"Very_Large_Prime", 1000003},
128+
129+
// Large composites (various patterns)
130+
{"Large_Composite_Even", 104730},
131+
{"Large_Composite_Odd", 104731}, // 181*579
132+
{"Large_Composite_Product", 1000001}, // 101*9901
133+
}
134+
135+
for _, tc := range testCases {
136+
b.Run(tc.name, func(b *testing.B) {
137+
for i := 0; i < b.N; i++ {
138+
isPrime(tc.n)
139+
}
140+
})
141+
}
142+
}

0 commit comments

Comments
 (0)