255 lines
5.5 KiB
Go
255 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
func calculateProgress(start, end, step, index int64) 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 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, loadList bool, numRoutines int, useTensors bool) []int64 {
|
|
var primes []int64
|
|
|
|
bootTime := time.Now()
|
|
|
|
if loadList {
|
|
primes = loadPrimes()
|
|
} else {
|
|
primes = []int64{2, 3, 5}
|
|
}
|
|
|
|
fmt.Printf("Startup time: %v\n", time.Now().Sub(bootTime))
|
|
|
|
progress := make(chan float32)
|
|
go printProgress(progress, "Generating primes")
|
|
|
|
startTime := time.Now()
|
|
|
|
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)
|
|
|
|
for i := (primes[len(primes) - 1] + 2); i <= upperLimit; i += 2 {
|
|
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
|
|
}
|
|
|
|
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), 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
|
|
dontLoad bool
|
|
numRoutines int
|
|
useTensors 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.IntVar(&numRoutines, "r", 0, "How many routines to use for calculation. 0 = number of available CPU cores")
|
|
flag.BoolVar(&useTensors, "t", false, "Use tensorflow")
|
|
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.ParseInt(numStr, 10, 64)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if primeList {
|
|
onlyGenerate(number, dontLoad, numRoutines, useTensors)
|
|
} else {
|
|
calculate(number, dontLoad, numRoutines, useTensors)
|
|
}
|
|
}
|
|
|
|
func onlyGenerate(number int64, dontLoad bool, numRoutines int, useTensors bool) {
|
|
primes := generatePrimes(number, !dontLoad, numRoutines, useTensors)
|
|
|
|
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 int64, dontLoad bool, numRoutines int, useTensors bool) {
|
|
primes := generatePrimes(number, !dontLoad, numRoutines, useTensors)
|
|
|
|
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)
|
|
}
|
|
|
|
func loadPrimes() []int64 {
|
|
file, err := os.Open("prime.txt")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var primes []int64
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
nextPrime, err := strconv.ParseInt(scanner.Text(), 10, 64)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
primes = append(primes, nextPrime)
|
|
}
|
|
|
|
file.Close()
|
|
|
|
return primes
|
|
}
|