2024-09-30 00:34:45 +02:00
package main
import (
"bufio"
"context"
"flag"
"fmt"
"log"
"os"
2024-09-30 18:28:51 +02:00
"runtime"
2024-09-30 00:34:45 +02:00
"strconv"
"time"
)
2024-10-03 00:32:59 +02:00
func calculateProgress ( start , end , step , index int64 ) float32 {
2024-09-30 00:34:45 +02:00
steps := ( end - start ) / step
current := index - start
if current == 0 {
return 0
}
return float32 ( current ) / float32 ( steps )
}
func printProgress ( progress <- chan float32 , message string ) {
for {
time . Sleep ( time . Second )
p , channelOpen := <- progress
if ! channelOpen {
break
}
fmt . Printf ( "%s %.2f%%\n" , message , p * 100 )
}
}
2024-10-03 00:32:59 +02:00
func checkIsDivisibleByPrime ( number int64 , offset , stride int , primes * [ ] int64 , resultChannel chan bool , ctx context . Context ) {
2024-10-03 01:13:34 +02:00
threshold := number / 2
2024-09-30 00:34:45 +02:00
for i := offset ; i < len ( * primes ) ; i += stride {
select {
case <- ctx . Done ( ) :
resultChannel <- false
return
default :
2024-10-03 01:13:34 +02:00
// no need to continue checking if number is greater than half our number, division is already impossible
if ( * primes ) [ i ] > threshold {
resultChannel <- false
return
}
2024-09-30 00:34:45 +02:00
// only have to check primes, because all other numbers are multiple of primes.
// if a number is divisible by a multiple of a prime, it is by definition also divisible by the prime itself.
if number % ( * primes ) [ i ] == 0 {
resultChannel <- true
return
}
}
}
resultChannel <- false
}
2024-10-03 00:32:59 +02:00
func generatePrimes ( upperLimit int64 , loadList bool , numRoutines int , useTensors bool ) [ ] int64 {
var primes [ ] int64
2024-09-30 00:34:45 +02:00
2024-09-30 18:28:51 +02:00
bootTime := time . Now ( )
2024-09-30 00:34:45 +02:00
if loadList {
primes = loadPrimes ( )
} else {
2024-10-03 00:32:59 +02:00
primes = [ ] int64 { 2 , 3 , 5 }
2024-09-30 00:34:45 +02:00
}
2024-10-03 00:32:59 +02:00
fmt . Printf ( "Startup time: %v\n" , time . Now ( ) . Sub ( bootTime ) )
2024-09-30 18:28:51 +02:00
2024-09-30 00:34:45 +02:00
progress := make ( chan float32 )
go printProgress ( progress , "Generating primes" )
startTime := time . Now ( )
2024-10-03 00:32:59 +02:00
if useTensors {
primes = generatePrimesGPU ( upperLimit , primes , progress )
} else {
primes = generatePrimesCPU ( upperLimit , primes , numRoutines , progress )
}
close ( progress )
endTime := time . Now ( )
fmt . Printf ( "Prime generation took %v\nLargest prime found: %d\nTotal prime number count: %d\n" , endTime . Sub ( startTime ) , primes [ len ( primes ) - 1 ] , len ( primes ) )
return primes
}
func generatePrimesCPU ( upperLimit int64 , primes [ ] int64 , numRoutines int , progress chan float32 ) [ ] int64 {
if numRoutines == 0 {
numRoutines = runtime . NumCPU ( )
}
fmt . Printf ( "Calculating with %d routines\n\n" , numRoutines )
2024-10-03 00:51:21 +02:00
for i := ( primes [ len ( primes ) - 1 ] + 2 ) ; i <= upperLimit ; i += 2 {
2024-09-30 00:34:45 +02:00
select {
case progress <- calculateProgress ( 6 , upperLimit , 1 , i ) :
default :
}
isPrime := true
checkCtx , checkCancel := context . WithCancel ( context . Background ( ) )
resultChannel := make ( chan bool )
for r := 0 ; r < numRoutines ; r ++ {
go checkIsDivisibleByPrime ( i , r , numRoutines , & primes , resultChannel , checkCtx )
}
for r := 0 ; r < numRoutines ; r ++ {
if <- resultChannel {
isPrime = false
checkCancel ( )
// can't break here, because all channel writes have to be read to avoid deadlocks
}
}
if isPrime {
primes = append ( primes , i )
}
}
return primes
}
2024-10-03 00:32:59 +02:00
func calculatePrimeParts ( number int64 , primes [ ] int64 ) [ ] int64 {
2024-09-30 00:34:45 +02:00
// don't calculate if number is a prime itself
if primes [ len ( primes ) - 1 ] == number {
2024-10-03 00:32:59 +02:00
return [ ] int64 { number }
2024-09-30 00:34:45 +02:00
}
progress := make ( chan float32 )
go printProgress ( progress , "Calculating" )
2024-10-03 00:32:59 +02:00
var primeParts [ ] int64
2024-09-30 00:34:45 +02:00
2024-10-03 00:32:59 +02:00
for i := int64 ( len ( primes ) - 1 ) ; i >= 0 ; i -- {
2024-09-30 00:34:45 +02:00
select {
2024-10-03 00:32:59 +02:00
case progress <- 1.0 - calculateProgress ( 0 , int64 ( len ( primes ) - 1 ) , 1 , i ) :
2024-09-30 00:34:45 +02:00
default :
}
flooredDiv := number / primes [ i ]
if flooredDiv == 0 {
continue
}
primeParts = append ( primeParts , primes [ i ] )
number = number - ( flooredDiv * primes [ i ] )
if number == 0 {
break
}
}
close ( progress )
if number != 0 {
primeParts = append ( primeParts , 1 )
}
return primeParts
}
func main ( ) {
2024-10-03 00:32:59 +02:00
var (
primeList bool
dontLoad bool
numRoutines int
useTensors bool
)
2024-09-30 00:34:45 +02:00
flag . BoolVar ( & primeList , "p" , false , "Only calculate and print prime list" )
flag . BoolVar ( & dontLoad , "d" , false , "Don't load precalculated primes, calculate from 0" )
2024-09-30 19:43:46 +02:00
flag . IntVar ( & numRoutines , "r" , 0 , "How many routines to use for calculation. 0 = number of available CPU cores" )
2024-10-03 00:32:59 +02:00
flag . BoolVar ( & useTensors , "t" , false , "Use tensorflow" )
2024-09-30 00:34:45 +02:00
flag . Parse ( )
numStr := flag . Arg ( 0 )
if numStr == "" {
fmt . Printf ( "Usage: %s [-p|d] <number>\n" , os . Args [ 0 ] )
flag . PrintDefaults ( )
return
}
2024-10-03 00:32:59 +02:00
number , err := strconv . ParseInt ( numStr , 10 , 64 )
2024-09-30 00:34:45 +02:00
if err != nil {
log . Fatal ( err )
}
if primeList {
2024-10-03 00:32:59 +02:00
onlyGenerate ( number , dontLoad , numRoutines , useTensors )
2024-09-30 00:34:45 +02:00
} else {
2024-10-03 00:32:59 +02:00
calculate ( number , dontLoad , numRoutines , useTensors )
2024-09-30 00:34:45 +02:00
}
}
2024-10-03 00:32:59 +02:00
func onlyGenerate ( number int64 , dontLoad bool , numRoutines int , useTensors bool ) {
primes := generatePrimes ( number , ! dontLoad , numRoutines , useTensors )
2024-09-30 00:34:45 +02:00
file , err := os . Create ( "prime.txt" )
if err != nil {
log . Fatal ( err )
}
writer := bufio . NewWriter ( file )
for _ , prime := range primes {
writer . WriteString ( fmt . Sprintf ( "%d\n" , prime ) )
}
writer . Flush ( )
file . Close ( )
}
2024-10-03 00:32:59 +02:00
func calculate ( number int64 , dontLoad bool , numRoutines int , useTensors bool ) {
primes := generatePrimes ( number , ! dontLoad , numRoutines , useTensors )
2024-09-30 00:34:45 +02:00
primeParts := calculatePrimeParts ( number , primes )
2024-10-03 00:32:59 +02:00
var sum int64
2024-09-30 00:34:45 +02:00
for i , prime := range primeParts {
sum += prime
print ( prime )
// if not last element
if i < len ( primeParts ) - 1 {
print ( " + " )
}
}
print ( " = " )
println ( sum )
}
2024-10-03 00:32:59 +02:00
func loadPrimes ( ) [ ] int64 {
2024-09-30 00:34:45 +02:00
file , err := os . Open ( "prime.txt" )
if err != nil {
log . Fatal ( err )
}
2024-10-03 00:32:59 +02:00
var primes [ ] int64
2024-09-30 00:34:45 +02:00
scanner := bufio . NewScanner ( file )
for scanner . Scan ( ) {
2024-10-03 00:32:59 +02:00
nextPrime , err := strconv . ParseInt ( scanner . Text ( ) , 10 , 64 )
2024-09-30 00:34:45 +02:00
if err != nil {
log . Fatal ( err )
}
primes = append ( primes , nextPrime )
}
file . Close ( )
return primes
}