diff --git a/PrimeBeef/solution_1/.gitignore b/PrimeBeef/solution_1/.gitignore new file mode 100644 index 000000000..567609b12 --- /dev/null +++ b/PrimeBeef/solution_1/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/PrimeBeef/solution_1/Dockerfile b/PrimeBeef/solution_1/Dockerfile new file mode 100644 index 000000000..cd570e456 --- /dev/null +++ b/PrimeBeef/solution_1/Dockerfile @@ -0,0 +1,8 @@ +FROM rzuckerm/beef:0.43.5a2-4 + +WORKDIR /opt/app +COPY *.sh ./ +COPY Primes/*.toml ./Primes/ +COPY Primes/src/*.bf ./Primes/src/ +RUN BeefBuild -workspace=Primes -config=Release +ENTRYPOINT ["./run_primes.sh"] diff --git a/PrimeBeef/solution_1/Primes/BeefProj.toml b/PrimeBeef/solution_1/Primes/BeefProj.toml new file mode 100644 index 000000000..94f199ed8 --- /dev/null +++ b/PrimeBeef/solution_1/Primes/BeefProj.toml @@ -0,0 +1,5 @@ +FileVersion = 1 + +[Project] +Name = "Primes" +StartupObject = "Primes.Program" diff --git a/PrimeBeef/solution_1/Primes/BeefSpace.toml b/PrimeBeef/solution_1/Primes/BeefSpace.toml new file mode 100644 index 000000000..0cd10a0d2 --- /dev/null +++ b/PrimeBeef/solution_1/Primes/BeefSpace.toml @@ -0,0 +1,5 @@ +FileVersion = 1 +Projects = {Primes = {Path = "."}} + +[Workspace] +StartupProject = "Primes" diff --git a/PrimeBeef/solution_1/Primes/src/Primes.bf b/PrimeBeef/solution_1/Primes/src/Primes.bf new file mode 100644 index 000000000..67ee2ee52 --- /dev/null +++ b/PrimeBeef/solution_1/Primes/src/Primes.bf @@ -0,0 +1,256 @@ +using System; +using System.Diagnostics; + +namespace Primes; + +class PrimeSieve +{ + public readonly uint64 mSieveSize; + public readonly uint64 mNumBits; + private uint32[] mSieveBits ~ delete _; + + public this(uint64 sieveSize) + { + mSieveSize = sieveSize; + mNumBits = (sieveSize - 1) / 2; + int numItems = (int)((mNumBits + 31) / 32); + mSieveBits = new uint32[numItems]; + Internal.MemSet(mSieveBits.CArray(), 0xff, sizeof(uint32) * numItems); + } + + [Optimize] + [DisableChecks] + public void Sieve() + { + uint64 q = (uint64)((-3 + Math.Sqrt(3 + 2 * mNumBits)) / 2); + for (uint64 bit = 0; bit <= q; bit++) + { + if (GetBit(bit)) + { + ClearBits(2 * (bit + 1) * (bit + 2) - 1, 2 * bit + 3); + } + } + } + + [Optimize] + [DisableChecks] + [Inline] + private bool GetBit(uint64 bit) + { + return mSieveBits[(int)(bit >> 5)] & (1 << (bit & 0x1f)) != 0; + } + + [Optimize] + [DisableChecks] + [Inline] + private void ClearBits(uint64 startBit, uint64 inc) + { + int bit = (int)(startBit & 0x1f); + int index = (int)(startBit >> 5); + int bitInc = (int)(inc & 0x1f); + int indexInc = (int)(inc >> 5); + for (uint64 currBit = startBit; currBit < mNumBits; currBit += inc) + { + mSieveBits[index] &= ~(1 << bit); + bit += bitInc; + index += indexInc; + if (bit >= 32) { + bit -= 32; + index++; + } + } + } + + public uint64 CountPrimes(bool showResults) + { + uint64 numPrimes = 0; + if (mSieveSize >= 2) + { + numPrimes++; + if (showResults) + { + Console.Write("2, "); + } + } + + for (uint64 bit = 0; bit < mNumBits; bit++) + { + if (GetBit(bit)) + { + numPrimes++; + if (showResults) + { + Console.Write($"{2 * bit + 3}, "); + } + } + } + + Console.WriteLine(); + + return numPrimes; + } + + public uint64 GetExpectedPrimesCount() + { + switch (mSieveSize) + { + case 10: return 4; + case 100: return 25; + case 1000: return 168; + case 10000: return 1229; + case 100000: return 9592; + case 1000000: return 78498; + case 10000000: return 664579; + case 100000000: return 5761455; + case 1000000000: return 50847534; + case 10000000000: return 455052511; + } + + return 0; + } + + public bool ValidatePrimesCount(uint64 primesCount) + { + return primesCount == GetExpectedPrimesCount(); + } + + public void ShowResults(bool showResults, uint64 passes, int64 elapsedTimeMsec) + { + uint64 primeCount = CountPrimes(showResults); + bool valid = ValidatePrimesCount(primeCount); + Console.WriteLine( + "Passes: {}, Time: {}ms, Avg: {}ms, Limit: {}, Count: {}, Valid: {}", + passes, + elapsedTimeMsec, + (double)elapsedTimeMsec / passes, + mSieveSize, + primeCount, + valid + ); + Console.WriteLine( + "rzuckerm;{};{};1;algorithm=base,faithful=yes,bits=1", + passes, + (double)elapsedTimeMsec / 1000.0 + ); + } +} + +struct PrimeOptions +{ + public uint64 sieveSize = 1'000'000; + public uint32 timeLimit = 5; + public bool showResults = false; +} + +class Program +{ + public static void ShowHelp(StringView errorMessage="") + { + int statusCode = 0; + if (errorMessage.Length > 0) + { + Console.WriteLine(errorMessage); + Console.WriteLine(); + statusCode = 1; + } + + Console.WriteLine("Options:\n"); + Console.WriteLine("--limit= Upper limit for calculating primes"); + Console.WriteLine("--time= Time limit in seconds"); + Console.WriteLine("--show Print found prime numbers"); + Console.WriteLine("--help Show help message"); + Environment.Exit(statusCode); + } + + public static Result ParseInt(StringView str) + where T : IParseable + { + StringView trimmedStr = scope String(str); + trimmedStr.Trim(); + return T.Parse(trimmedStr); + } + + public static T ParseIntOrDie(StringView str, StringView errorMessage) + where T : IParseable + { + T val = default(T); + switch (ParseInt(str)) + { + case .Err: + Console.WriteLine(errorMessage); + Environment.Exit(1); + case .Ok(out val): + } + + return val; + } + + public static PrimeOptions ParseArgs(String[] args) + { + PrimeOptions options = PrimeOptions(); + for (String arg in args) + { + int index = arg.IndexOf('='); + StringView optionName = ""; + StringView optionValue = ""; + if (index < 0) + { + optionName = arg; + } + else + { + optionName = arg.Substring(0, index); + optionValue = arg.Substring(index + 1); + } + + if (optionName == "--limit") + { + options.sieveSize = ParseIntOrDie(optionValue, @"{arg}: Invalid sieve size"); + } + else if (optionName == "--time") + { + options.timeLimit = ParseIntOrDie(optionValue, @"{arg}: Invalid time limit"); + } + else if (arg == "--show") + { + options.showResults = true; + } + else if (arg == "--help") + { + ShowHelp(); + } + else + { + ShowHelp(@"Invalid option '{arg}'"); + } + } + + return options; + } + + public static void TimedRunSieve(PrimeOptions options) + { + uint64 passes = 0; + int64 timeLimitMsec = (int64)options.timeLimit * 1000; + Stopwatch watch = scope Stopwatch(true); + while (true) + { + passes++; + PrimeSieve sieve = scope PrimeSieve(options.sieveSize); + sieve.Sieve(); + if (watch.ElapsedMilliseconds >= timeLimitMsec) + { + watch.Stop(); + sieve.ShowResults(options.showResults, passes, watch.ElapsedMilliseconds); + break; + } + } + } + + public static int Main(String[] args) + { + PrimeOptions options = ParseArgs(args); + TimedRunSieve(options); + return 0; + } +} diff --git a/PrimeBeef/solution_1/README.md b/PrimeBeef/solution_1/README.md new file mode 100644 index 000000000..185f6ff6f --- /dev/null +++ b/PrimeBeef/solution_1/README.md @@ -0,0 +1,44 @@ +# Beef solution by rzuckerm + +![Algorithm](https://img.shields.io/badge/Algorithm-base-green) +![Faithfulness](https://img.shields.io/badge/Faithful-yes-green) +![Parallelism](https://img.shields.io/badge/Parallel-no-green) +![Bit count](https://img.shields.io/badge/Bits-1-green) + +* `Primes/src/Primes.bf` uses 1 bit for each sieve item + +## Caveats + +The docker image only works on an AMD64 architecture. I was not able to get the +[Beef docker image](https://hub.docker.com/r/rzuckerm/beef) to build using ARM64. + +## Run instructions + +Build the docker image with this: + +```bash +./build.sh +``` + +You should only need to do this once. Run the docker image: + +```bash +./run.sh [] +``` + +where `` are optional command-line arguments: + +* `--limit=` - Upper limit for calculating prime. Default value is 1000000 +* `--time=