Initial data commit
This commit is contained in:
parent
72d218235f
commit
f23f22d71c
199087 changed files with 3378941 additions and 0 deletions
|
|
@ -0,0 +1,152 @@
|
|||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
const heap = std.heap;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
|
||||
fn assertInt(comptime T: type) builtin.TypeInfo.Int {
|
||||
if (@typeInfo(T) != .Int)
|
||||
@compileError("data type must be an integer.");
|
||||
const int = @typeInfo(T).Int;
|
||||
if (int.is_signed == true or int.bits % 2 == 1 or int.bits < 4)
|
||||
@compileError("type must be an unsigned integer with even bit size (of at least 4 bits).");
|
||||
return int;
|
||||
}
|
||||
|
||||
fn sqrtType(comptime T: type) type {
|
||||
const t = assertInt(T);
|
||||
return meta.Int(.unsigned, t.bits / 2);
|
||||
}
|
||||
|
||||
// given an upper bound, max, return the most restrictive sieving data type.
|
||||
pub fn autoSieveType(comptime max: u64) type {
|
||||
if (max == 0)
|
||||
@compileError("The maximum sieving size must be non-zero.");
|
||||
var bit_len = 64 - @clz(u64, max);
|
||||
if (max & (max - 1) == 0) // power of two
|
||||
bit_len -= 1;
|
||||
if (bit_len % 2 == 1)
|
||||
bit_len += 1;
|
||||
if (bit_len < 4)
|
||||
bit_len = 4;
|
||||
return meta.Int(.unsigned, bit_len);
|
||||
}
|
||||
|
||||
test "type meta functions" {
|
||||
const expect = std.testing.expect;
|
||||
expect(sqrtType(u20) == u10);
|
||||
expect(autoSieveType(8000) == u14);
|
||||
expect(autoSieveType(9000) == u14);
|
||||
expect(autoSieveType(16384) == u14);
|
||||
expect(autoSieveType(16385) == u16);
|
||||
expect(autoSieveType(32768) == u16);
|
||||
expect(autoSieveType(1000) == u10);
|
||||
expect(autoSieveType(10) == u4);
|
||||
expect(autoSieveType(4) == u4);
|
||||
expect(autoSieveType(std.math.maxInt(u32)) == u32);
|
||||
}
|
||||
|
||||
const wheel2357 = [48]u8{
|
||||
10, 2, 4, 2, 4, 6, 2, 6,
|
||||
4, 2, 4, 6, 6, 2, 6, 4,
|
||||
2, 6, 4, 6, 8, 4, 2, 4,
|
||||
2, 4, 8, 6, 4, 6, 2, 4,
|
||||
6, 2, 6, 6, 4, 2, 4, 6,
|
||||
2, 6, 4, 2, 4, 2, 10, 2,
|
||||
};
|
||||
|
||||
fn Wheel2357Multiple(comptime Int: type) type {
|
||||
_ = assertInt(Int);
|
||||
return struct {
|
||||
multiple: Int,
|
||||
base_prime: Int,
|
||||
offset: u6,
|
||||
|
||||
fn less(self: Wheel2357Multiple(Int), other: Wheel2357Multiple(Int)) bool {
|
||||
return self.multiple < other.multiple;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn PrimeGen(comptime Int: type) type {
|
||||
_ = assertInt(Int);
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
initial_primes: u16,
|
||||
offset: u6,
|
||||
candidate: Int,
|
||||
multiples: std.PriorityQueue(Wheel2357Multiple(Int)),
|
||||
allocator: *mem.Allocator,
|
||||
count: u32,
|
||||
|
||||
pub fn init(alloc: *mem.Allocator) Self {
|
||||
return Self{
|
||||
.initial_primes = 0xAC, // primes 2, 3, 5, 7 in a bitmask
|
||||
.offset = 0,
|
||||
.candidate = 1,
|
||||
.count = 0,
|
||||
.allocator = alloc,
|
||||
.multiples = std.PriorityQueue(Wheel2357Multiple(Int)).init(alloc, Wheel2357Multiple(Int).less),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *PrimeGen(Int)) void {
|
||||
self.multiples.deinit();
|
||||
}
|
||||
|
||||
pub fn next(self: *PrimeGen(Int)) !?Int {
|
||||
if (self.initial_primes != 0) { // use the bitmask up first
|
||||
const p = @as(Int, @ctz(u16, self.initial_primes));
|
||||
self.initial_primes &= self.initial_primes - 1;
|
||||
self.count += 1;
|
||||
return p;
|
||||
} else {
|
||||
while (true) {
|
||||
// advance to the next prime candidate.
|
||||
if (@addWithOverflow(Int, self.candidate, wheel2357[self.offset], &self.candidate))
|
||||
return null;
|
||||
self.offset = (self.offset + 1) % @as(u6, wheel2357.len);
|
||||
|
||||
// See if the composite number on top of the heap matches
|
||||
// the candidate.
|
||||
//
|
||||
var top = self.multiples.peek();
|
||||
if (top == null or self.candidate < top.?.multiple) {
|
||||
// prime found, add the square and it's position on the wheel
|
||||
// to the heap.
|
||||
//
|
||||
if (self.candidate <= std.math.maxInt(sqrtType(Int)))
|
||||
try self.multiples.add(Wheel2357Multiple(Int){
|
||||
.multiple = self.candidate * self.candidate,
|
||||
.base_prime = self.candidate,
|
||||
.offset = self.offset,
|
||||
});
|
||||
self.count += 1;
|
||||
return self.candidate;
|
||||
} else {
|
||||
while (true) {
|
||||
// advance the top of heap to the next prime multiple
|
||||
// that is not a multiple of 2, 3, 5, 7.
|
||||
//
|
||||
var mult = self.multiples.remove();
|
||||
// If the multiple becomes too big (greater than the the maximum
|
||||
// sieve size), then there's no reason to add it back to the queue.
|
||||
//
|
||||
var tmp: Int = undefined;
|
||||
if (!@mulWithOverflow(Int, mult.base_prime, wheel2357[mult.offset], &tmp) and
|
||||
!@addWithOverflow(Int, tmp, mult.multiple, &mult.multiple))
|
||||
{
|
||||
mult.offset = (mult.offset + 1) % @as(u6, wheel2357.len);
|
||||
try self.multiples.add(mult);
|
||||
}
|
||||
top = self.multiples.peek();
|
||||
if (top == null or self.candidate != top.?.multiple)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
const std = @import("std");
|
||||
const sieve = @import("sieve.zig");
|
||||
const PrimeGen = sieve.PrimeGen;
|
||||
const heap = std.heap;
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
pub fn main() !void {
|
||||
try part1();
|
||||
try part2();
|
||||
try part3();
|
||||
}
|
||||
|
||||
// exercize 1: Print small primes
|
||||
fn part1() !void {
|
||||
var arena = heap.ArenaAllocator.init(heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
var primes = PrimeGen(u8).init(&arena.allocator);
|
||||
defer primes.deinit();
|
||||
|
||||
try stdout.print("The first 20 primes:", .{});
|
||||
while (try primes.next()) |p| {
|
||||
try stdout.print(" {}", .{p});
|
||||
if (primes.count == 20)
|
||||
break;
|
||||
}
|
||||
try stdout.print("\nThe primes between 100 and 150:", .{});
|
||||
while (try primes.next()) |p| if (p >= 100 and p <= 150)
|
||||
try stdout.print(" {}", .{p});
|
||||
try stdout.print("\n", .{});
|
||||
}
|
||||
|
||||
// exercize 2: count medium primes
|
||||
fn part2() !void {
|
||||
var arena = heap.ArenaAllocator.init(heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
var primes = PrimeGen(sieve.autoSieveType(8000)).init(&arena.allocator);
|
||||
defer primes.deinit();
|
||||
|
||||
var count: i32 = 0;
|
||||
while (try primes.next()) |p| {
|
||||
if (p > 8000)
|
||||
break;
|
||||
if (p > 7700)
|
||||
count += 1;
|
||||
}
|
||||
try stdout.print("There are {} primes between 7700 and 8000.\n", .{count});
|
||||
}
|
||||
|
||||
// exercize 3: find big primes
|
||||
fn part3() !void {
|
||||
var arena = heap.ArenaAllocator.init(heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
var primes = PrimeGen(u32).init(&arena.allocator);
|
||||
defer primes.deinit();
|
||||
|
||||
var c: u32 = 10;
|
||||
while (try primes.next()) |p| {
|
||||
if (primes.count == c) {
|
||||
try stdout.print("The {}th prime is {}\n", .{ c, p });
|
||||
if (c == 100_000_000)
|
||||
break;
|
||||
c *= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
// Since in the common case (primes < 2^32 - 1), the stack only needs to be 8 16-bit words long
|
||||
// (only twice the size of a pointer) the required stacks are stored in each cell, rather
|
||||
// than using an indirection (e.g. linked list of integer cells)
|
||||
//
|
||||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
const meta = std.meta;
|
||||
const mem = std.mem;
|
||||
|
||||
fn assertInt(comptime T: type) builtin.TypeInfo.Int {
|
||||
const Signedness = builtin.Signedness;
|
||||
if (@typeInfo(T) != .Int)
|
||||
@compileError("data type must be an integer.");
|
||||
const int = @typeInfo(T).Int;
|
||||
if (int.signedness == Signedness.signed or int.bits % 2 == 1 or int.bits < 4 or int.bits > 64)
|
||||
@compileError("type must be an unsigned integer with even bit size (of at least 4 bits).");
|
||||
return int;
|
||||
}
|
||||
|
||||
// given a type, return the maximum stack size required by the algorthm.
|
||||
fn listSize(comptime T: type) usize {
|
||||
_ = assertInt(T);
|
||||
const primes = [_]u6{
|
||||
2, 3, 5, 7, 11, 13, 17, 19,
|
||||
23, 29, 31, 37, 41, 43, 47, 53,
|
||||
};
|
||||
// Find the first primorial that will overflow type T.
|
||||
// the size of the list is the primorial index minus one,
|
||||
// since the sieve doesn't include 2.
|
||||
//
|
||||
var i: usize = 0;
|
||||
var pi: T = 1;
|
||||
while (!@mulWithOverflow(T, pi, primes[i], &pi))
|
||||
i += 1;
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
fn sqrtType(comptime T: type) type {
|
||||
const t = assertInt(T);
|
||||
return meta.Int(.unsigned, t.bits / 2);
|
||||
}
|
||||
|
||||
// stack type (actually just an array list)
|
||||
fn arrayList(comptime Int: type) type {
|
||||
return [listSize(Int)]sqrtType(Int);
|
||||
}
|
||||
|
||||
// given an upper bound, max, return the most restrictive sieving data type.
|
||||
pub fn autoSieveType(comptime max: u64) type {
|
||||
if (max == 0)
|
||||
@compileError("The maximum sieving size must be non-zero.");
|
||||
var bit_len = 64 - @clz(u64, max);
|
||||
if (max & (max - 1) == 0) // power of two
|
||||
bit_len -= 1;
|
||||
if (bit_len % 2 == 1)
|
||||
bit_len += 1;
|
||||
if (bit_len < 4)
|
||||
bit_len = 4;
|
||||
return meta.Int(.unsigned, bit_len);
|
||||
}
|
||||
|
||||
test "type meta functions" {
|
||||
const expect = std.testing.expect;
|
||||
try expect(sqrtType(u20) == u10);
|
||||
try expect(autoSieveType(8000) == u14);
|
||||
try expect(autoSieveType(9000) == u14);
|
||||
try expect(autoSieveType(16384) == u14);
|
||||
try expect(autoSieveType(16385) == u16);
|
||||
try expect(autoSieveType(32768) == u16);
|
||||
try expect(autoSieveType(1000) == u10);
|
||||
try expect(autoSieveType(10) == u4);
|
||||
try expect(autoSieveType(4) == u4);
|
||||
try expect(autoSieveType(std.math.maxInt(u32)) == u32);
|
||||
try expect(listSize(u64) == 14);
|
||||
try expect(listSize(u32) == 8);
|
||||
try expect(@sizeOf(arrayList(u32)) == 16);
|
||||
try expect(@sizeOf(arrayList(u36)) == 36);
|
||||
try expect(@sizeOf(arrayList(u64)) == 56);
|
||||
}
|
||||
|
||||
pub fn PrimeGen(comptime Int: type) type {
|
||||
_ = assertInt(Int);
|
||||
return struct {
|
||||
const Self = @This();
|
||||
const Sieve = std.ArrayList(arrayList(Int));
|
||||
|
||||
sieve: Sieve,
|
||||
count: usize,
|
||||
candidate: Int,
|
||||
rt: sqrtType(Int),
|
||||
sq: Int,
|
||||
pos: usize,
|
||||
|
||||
// grow the sieve by a comptime fixed amount
|
||||
fn growBy(self: *Self, comptime n: usize) !void {
|
||||
var chunk: [n]arrayList(Int) = undefined;
|
||||
for (chunk) |*a|
|
||||
mem.set(sqrtType(Int), a, 0);
|
||||
try self.sieve.appendSlice(&chunk);
|
||||
}
|
||||
|
||||
// add a known prime number to the sieve at postion k
|
||||
fn add(self: *Self, p: sqrtType(Int), k: usize) void {
|
||||
for (self.sieve.items[k]) |*x|
|
||||
if (x.* == 0) {
|
||||
x.* = p;
|
||||
return;
|
||||
};
|
||||
// each bucket is precalculated for the max size.
|
||||
// If we get here, there's been a mistake somewhere.
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn init(alloc: *mem.Allocator) Self {
|
||||
return Self{
|
||||
.count = 0,
|
||||
.sieve = Sieve.init(alloc),
|
||||
.candidate = 3,
|
||||
.rt = 3,
|
||||
.sq = 9,
|
||||
.pos = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.sieve.deinit();
|
||||
}
|
||||
|
||||
pub fn next(self: *Self) !?Int {
|
||||
self.count += 1;
|
||||
if (self.count == 1) {
|
||||
try self.growBy(1); // prepare sieve
|
||||
return 2;
|
||||
} else {
|
||||
var is_prime = false;
|
||||
while (!is_prime) {
|
||||
is_prime = true;
|
||||
// Step 1: check the list at self.pos; if there are divisors then
|
||||
// the candidate is not prime. Move each divisor to its next multiple
|
||||
// in the sieve.
|
||||
//
|
||||
if (self.sieve.items[self.pos][0] != 0) {
|
||||
is_prime = false;
|
||||
for (self.sieve.items[self.pos]) |*x| {
|
||||
const p = x.*;
|
||||
x.* = 0;
|
||||
if (p == 0)
|
||||
break;
|
||||
self.add(p, (p + self.pos) % self.sieve.items.len);
|
||||
}
|
||||
}
|
||||
// Step 2: If we've hit the next perfect square, and we thought the number
|
||||
// was prime from step 1, note that it wasn't prime but rather was a non p-smooth
|
||||
// number. Add the square root to the sieve. In any case, look ahead to the next
|
||||
// square number.
|
||||
//
|
||||
if (self.candidate == self.sq) {
|
||||
if (is_prime) {
|
||||
is_prime = false;
|
||||
self.add(self.rt, (self.pos + self.rt) % self.sieve.items.len);
|
||||
}
|
||||
// advance to the next root; if doing so would cause overflow then just ignore it,
|
||||
// since we'll never see the next square.
|
||||
//
|
||||
var rt: sqrtType(Int) = undefined;
|
||||
if (!@addWithOverflow(sqrtType(Int), self.rt, 2, &rt)) {
|
||||
self.rt = rt;
|
||||
self.sq = @as(Int, rt) * rt;
|
||||
}
|
||||
}
|
||||
// advance the iterator; Note if we overflow, the candidate cannot be prime
|
||||
// since the bit count must be even and all integers of the form 2^n - 1 with
|
||||
// even n (except 2) are composite.
|
||||
//
|
||||
if (@addWithOverflow(Int, self.candidate, 2, &self.candidate)) {
|
||||
std.debug.assert(!is_prime);
|
||||
return null;
|
||||
}
|
||||
self.pos += 1;
|
||||
if (self.pos == self.sieve.items.len) {
|
||||
// expand the array by 2 to maintain the invariant: sieve.items.len > √candidate
|
||||
try self.growBy(2);
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
return self.candidate - 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue