Initial commit

This commit is contained in:
datalore 2024-09-30 00:34:45 +02:00
commit 17642366d2
5 changed files with 4118310 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.tags
prime-div

18
README.md Normal file
View File

@ -0,0 +1,18 @@
# prime-div
This tool was originally written to display any natural number as a sum of prime numbers, but it is also a efficient tool to calculate prime numbers.
## Prime number calculation
The known list of prime numbers are saved in `prime.txt` in string representation, one prime per line. The program loads this file, begins at the next larger integer, and checks if it is divisible by any of the known primes. If not it is added to the list. Repeat with the next larger integer, and so on.
The check is performed in parallel using multiple striding goroutines.
## Usage
`prime-div [flags] <number>`
Flags:
- `-p`: Calculate and save prime list up to the given number. If not given, the given number will be split into primes. If not all necessary prime numbers are in the list, the necessary primes will be calculated before, but they will not be saved.
- `-d`: Don't load `prime.txt`. This results in calculation starting from zero.

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module datalore/prime-div
go 1.23.0

223
main.go Normal file
View File

@ -0,0 +1,223 @@
package main
import (
"bufio"
"context"
"flag"
"fmt"
"log"
"os"
"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
if loadList {
primes = loadPrimes()
} else {
primes = []int{2, 3, 5}
}
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)
numRoutines := 12
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
}

4118064
prime.txt Normal file

File diff suppressed because it is too large Load Diff