package main import ( "context" "flag" "fmt" "log" "runtime" "strconv" "time" "os" ) type Config struct { PrimeList bool DontLoad bool NumRoutines int UseTensors bool UseTxtRead bool UseTxtWrite bool } func calculateProgress(start, end, index int64) float32 { steps := (end - start) 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 int64, offset, stride int, primes *[]int64, resultChannel chan bool, ctx context.Context) { threshold := number / 2 for i := offset; i < len(*primes); i += stride { select { case <-ctx.Done(): resultChannel <- false return default: // no need to continue checking if number is greater than half our number, division is already impossible if (*primes)[i] > threshold { resultChannel <- false return } // 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 int64, config Config) []int64 { var primes []int64 bootTime := time.Now() if config.DontLoad { primes = []int64{2, 3, 5} } else { primes = loadPrimes(config.UseTxtRead) } fmt.Printf("Startup time: %v\n", time.Now().Sub(bootTime)) progress := make(chan float32) go printProgress(progress, "Generating primes") startTime := time.Now() if config.UseTensors { primes = generatePrimesGPU(upperLimit, primes, progress) } else { primes = generatePrimesCPU(upperLimit, primes, config.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) continueFrom := primes[len(primes) - 1] + 2 for i := continueFrom; i <= upperLimit; i += 2 { select { case progress <- calculateProgress(continueFrom, upperLimit, 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 } func calculatePrimeParts(number int64, primes []int64) []int64 { // don't calculate if number is a prime itself if primes[len(primes)-1] == number { return []int64{number} } progress := make(chan float32) go printProgress(progress, "Calculating") var primeParts []int64 for i := int64(len(primes) - 1); i >= 0; i-- { select { case progress <- 1.0 - calculateProgress(0, int64(len(primes) - 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 config Config flag.BoolVar(&config.PrimeList, "p", false, "Only calculate and print prime list") flag.BoolVar(&config.DontLoad, "d", false, "Don't load precalculated primes, calculate from 0") flag.IntVar(&config.NumRoutines, "r", 0, "How many routines to use for calculation. 0 = number of available CPU cores") flag.BoolVar(&config.UseTensors, "t", false, "Use tensorflow") flag.BoolVar(&config.UseTxtRead, "read-legacy", false, "Read legacy prime.txt file instead of prime.msgpack") flag.BoolVar(&config.UseTxtWrite, "write-legacy", false, "Write legacy prime.txt file instead of prime.msgpack") flag.Parse() numStr := flag.Arg(0) if numStr == "" { fmt.Printf("Usage: %s [-p|d] \n", os.Args[0]) flag.PrintDefaults() return } number, err := strconv.ParseInt(numStr, 10, 64) if err != nil { log.Fatal(err) } if config.PrimeList { onlyGenerate(number, config) } else { calculate(number, config) } } func onlyGenerate(number int64, config Config) { primes := generatePrimes(number, config) writePrimes(primes, config.UseTxtWrite) } func calculate(number int64, config Config) { primes := generatePrimes(number, config) primeParts := calculatePrimeParts(number, primes) var sum int64 for i, prime := range primeParts { sum += prime print(prime) // if not last element if i < len(primeParts) - 1 { print(" + ") } } print(" = ") println(sum) }