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"
)
func calculateProgress ( start , end , step , index int ) float32 {
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 )
}
}
func checkIsDivisibleByPrime ( number , offset , stride int , primes * [ ] int , resultChannel chan bool , ctx context . Context ) {
for i := offset ; i < len ( * primes ) ; i += stride {
select {
case <- ctx . Done ( ) :
resultChannel <- false
return
default :
// 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
}
func generatePrimes ( upperLimit int , loadList bool ) [ ] int {
var primes [ ] int
2024-09-30 18:28:51 +02:00
bootTime := time . Now ( )
2024-09-30 00:34:45 +02:00
if loadList {
primes = loadPrimes ( )
} else {
primes = [ ] int { 2 , 3 , 5 }
}
2024-09-30 18:28:51 +02:00
numRoutines := runtime . NumCPU ( )
fmt . Printf ( "Startup time: %v\nCalculating with %d routines\n\n" , time . Now ( ) . Sub ( bootTime ) , numRoutines )
2024-09-30 00:34:45 +02:00
progress := make ( chan float32 )
go printProgress ( progress , "Generating primes" )
startTime := time . Now ( )
for i := ( primes [ len ( primes ) - 1 ] + 1 ) ; i <= upperLimit ; i ++ {
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 )
}
}
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 calculatePrimeParts ( number int , primes [ ] int ) [ ] int {
// don't calculate if number is a prime itself
if primes [ len ( primes ) - 1 ] == number {
return [ ] int { number }
}
progress := make ( chan float32 )
go printProgress ( progress , "Calculating" )
var primeParts [ ] int
for i := len ( primes ) - 1 ; i >= 0 ; i -- {
select {
case progress <- 1.0 - calculateProgress ( 0 , len ( primes ) - 1 , 1 , i ) :
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 ( ) {
var primeList bool
var dontLoad bool
flag . BoolVar ( & primeList , "p" , false , "Only calculate and print prime list" )
flag . BoolVar ( & dontLoad , "d" , false , "Don't load precalculated primes, calculate from 0" )
flag . Parse ( )
numStr := flag . Arg ( 0 )
if numStr == "" {
fmt . Printf ( "Usage: %s [-p|d] <number>\n" , os . Args [ 0 ] )
flag . PrintDefaults ( )
return
}
number , err := strconv . Atoi ( numStr )
if err != nil {
log . Fatal ( err )
}
if primeList {
onlyGenerate ( number , dontLoad )
} else {
calculate ( number , dontLoad )
}
}
func onlyGenerate ( number int , dontLoad bool ) {
primes := generatePrimes ( number , ! dontLoad )
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 ( )
}
func calculate ( number int , dontLoad bool ) {
primes := generatePrimes ( number , ! dontLoad )
primeParts := calculatePrimeParts ( number , primes )
sum := 0
for i , prime := range primeParts {
sum += prime
print ( prime )
// if not last element
if i < len ( primeParts ) - 1 {
print ( " + " )
}
}
print ( " = " )
println ( sum )
}
func loadPrimes ( ) [ ] int {
file , err := os . Open ( "prime.txt" )
if err != nil {
log . Fatal ( err )
}
var primes [ ] int
scanner := bufio . NewScanner ( file )
for scanner . Scan ( ) {
nextPrime , err := strconv . Atoi ( scanner . Text ( ) )
if err != nil {
log . Fatal ( err )
}
primes = append ( primes , nextPrime )
}
file . Close ( )
return primes
}