Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions PrimeOdin/solution_2/BitSieve.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package prime_drag_race

import "base:intrinsics"
import "core:container/bit_array"
import "core:math"
import "core:fmt"
import "core:time"
import "core:mem"

BitSieve :: struct {
size : int,
bits : ^bit_array.Bit_Array
}

RunBitSieve :: proc( sieveSize : int) -> (result :BitSieve)
{
factor := 3

bitSieve := bit_array.create( sieveSize)
// q := int( math.sqrt_f64( f64(sieveSize)))

for factor*factor <= sieveSize { // faster than "factor <= q"
// find the first "confirmed" prime in the Sieve
#no_bounds_check {
for num := factor; num < sieveSize; num += 2 {
if !bit_array.unsafe_get( bitSieve, num) {
factor = num
break;
}
}

// then "nullify" the subsequent multiples of that prime.
step := factor * 2
for num := factor * factor; num < sieveSize; num += step {
bit_array.unsafe_set( bitSieve, num)
}

factor += 2
}
}
result.size = sieveSize
result.bits = bitSieve

return
}

GoBitSieve :: proc ( sieveSize : int)
{
fiveSecs :: time.Duration(5_000_000_000) // nano seconds

passCount := 0

timer : time.Stopwatch
time.stopwatch_start( &timer)
defer time.stopwatch_stop( &timer)

for {
bitSieve := RunBitSieve( sieveSize)
passCount += 1

duration := time.stopwatch_duration( timer)
if duration > fiveSecs {
fmt.printfln( "arenol;%d;%.5f;1;algorithm=base,faithful=yes,bits=1", passCount, f64(duration) * 1.0e-9)
bit_array.destroy( bitSieve.bits)
break
}
bit_array.destroy( bitSieve.bits)
}

}
114 changes: 114 additions & 0 deletions PrimeOdin/solution_2/ByteSieve.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package prime_drag_race

import "core:fmt"
import "core:time"
import "core:mem"


ByteSieve :: struct {
size : int,
bits : []bool,
}

RunByteSieve :: proc( size :int) -> (result :ByteSieve)
{
upper := size / 2;
theSieve : []bool = make( []bool, upper)

for factor := 3; factor*factor < size; factor +=2 {
#no_bounds_check { // speeds up the algorithm with a factor of 2.5
// find the first "confirmed" prime in the Sieve
for num := factor/2; num < upper; num += 1 {
if !theSieve[num] {
factor = num * 2 + 1
break;
}
}

// then "nullify" the subsequent multiples of that prime.
for num := factor * factor / 2; num < upper; num += factor {
theSieve[num] = true
}

}
}
result.size = size
result.bits = theSieve
return
}

PrintPrimes :: proc( calculatedSieve : []bool )
{
fmt.print( "2")

for b, i in calculatedSieve[1:] {
if !b {
fmt.print( ",", i*2+3)
}
}

fmt.println( "")
}

CountPrimes :: proc( calculatedSieve :[]bool) -> int
{
n := len( calculatedSieve)
count := sieveSize >= 2 ? 1 : 0
for b in calculatedSieve[1:] {
if !b {
count += 1
}
}
return count
}

ExpectedPrimeCount :: proc( siveSize :int) -> int{
switch sieveSize {
case 10: return 4
case 100: return 25
case 1_000: return 168
case 10_000: return 1_229
case 100_000: return 9_592
case 1_000_000: return 78_498
case 10_000_000: return 664_579
case 100_000_000: return 5_761_455
}
panic("Invalid sieve size")
}


GoByteSieve :: proc( sieveSize : int)
{

fiveSecs :: time.Duration(5_000_000_000) // nano seconds

passCount := 0

/*
note that the default value of bool in odin is false.
rather than initialize the sieve with trues and make them
false, as we go, we reverse the logic.
*/


timer : time.Stopwatch
time.stopwatch_start( &timer)
defer time.stopwatch_stop( &timer)

for {
theSieve := RunByteSieve( sieveSize)
passCount += 1

duration := time.stopwatch_duration( timer)
if duration > fiveSecs {
assert( CountPrimes( theSieve.bits[:]) == ExpectedPrimeCount( theSieve.size))
fmt.printfln( "arenol;%d,%.5f;1;algorithm=base,faithful=yes,bits=8", passCount, f64(duration) * 1.0e-9)
// PrintPrimes( theSieve.bits[:])
free( (^rawptr)(&theSieve.bits)^)
break
}
free( (^rawptr)(&theSieve.bits)^)
}


}
27 changes: 27 additions & 0 deletions PrimeOdin/solution_2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dockerfile to build an environment for running Odin code on Ubuntu Linux.

FROM ubuntu:latest

Check failure on line 3 in PrimeOdin/solution_2/Dockerfile

View workflow job for this annotation

GitHub Actions / build (PrimeOdin/solution_2)

Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag

RUN apt-get update -qq && \

Check notice on line 5 in PrimeOdin/solution_2/Dockerfile

View workflow job for this annotation

GitHub Actions / build (PrimeOdin/solution_2)

Delete the apt-get lists after installing something
apt-get install -y llvm clang git build-essential

WORKDIR /odin-build

RUN git clone https://github.com/odin-lang/Odin.git /odin-build && \
git checkout dev-2025-08 && \
make

RUN mkdir /opt/odin && \
cp -R ./base ./core ./shared ./vendor ./odin /opt/odin

WORKDIR /
RUN rm -rf /odin-build

ENV PATH="/opt/odin:${PATH}"

WORKDIR /app
COPY *.odin /app

RUN odin build . -o:speed -out:odinprimes

CMD ./odinprimes

Check failure on line 27 in PrimeOdin/solution_2/Dockerfile

View workflow job for this annotation

GitHub Actions / build (PrimeOdin/solution_2)

Use arguments JSON notation for CMD and ENTRYPOINT arguments
26 changes: 26 additions & 0 deletions PrimeOdin/solution_2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Odin solution by Agnar Renolen
Just implemented the original C++ implementation in Odin.

Could not compile the multithreaded and bit versions of Ginger Bill and Kelimion's solution with my current odin installation (version dev-2025-08) on my laptop. But I got a significant improvement on the single-threaded byte version.

The bit version was just an experiment to figure out how the *core:collection/byte_array* package would perform. Surprisingly well, I'd say.

## Tested variations
Rather than allocate and deallocate the sieve for each iteration, I tried to create one sieve, and reset it by "memcopy-ing" from a blank sieve between each iteration. I was surprised to see that this was slightly slower.

## Run instructions
This solution was developed under odin *dev-2025-8*. It should work on later versions, and may also work on some earlier versions, though the *bit_array* package implementation used in *dev-2025-8* is quite new.

You should run the program using **odin run . -o:speed**.
I also tried the *aggressive* optimization without any improvement.

## Output
On my laptop:

arenol;8377,5.0000;1;algorithm=base,faithful=yes,bits=8
arenol;7731;5.0006;1;algorithm=base,faithful=yes,bits=1

Compared to Ginger Bill and Kelimion's solution with the same laptop and compiler:

odin_byte_moe;7461;5.0005955;1;algorithm=base,faithful=yes,bits=8

14 changes: 14 additions & 0 deletions PrimeOdin/solution_2/main.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

package prime_drag_race

import "core:math"

sieveSize :: 1_000_000

// calculate q once and for all

main :: proc()
{
GoByteSieve( sieveSize)
GoBitSieve( sieveSize)
}
Loading