Merge pull request #1122 from Kelimion/bigint

big: Add `internal_random_prime`.
This commit is contained in:
Jeroen van Rijn
2021-09-05 10:41:50 +02:00
committed by GitHub
6 changed files with 180 additions and 17 deletions
+14 -3
View File
@@ -88,6 +88,17 @@ MATH_BIG_USE_FROBENIUS_TEST :: !MATH_BIG_USE_LUCAS_SELFRIDGE_TEST;
*/ */
USE_MILLER_RABIN_ONLY := false; USE_MILLER_RABIN_ONLY := false;
/*
How many times we'll call `internal_int_random` during random prime generation before we bail out.
Set to 0 or less to try indefinitely.
*/
MAX_ITERATIONS_RANDOM_PRIME := 1_000_000;
/*
How many iterations we used for the last random prime.
*/
@thread_local RANDOM_PRIME_ITERATIONS_USED: int;
/* /*
We don't allow these to be switched at runtime for two reasons: We don't allow these to be switched at runtime for two reasons:
@@ -175,9 +186,9 @@ Error_String :: #partial [Error]string{
}; };
Primality_Flag :: enum u8 { Primality_Flag :: enum u8 {
Blum_Blum_Shub = 0, /* BBS style prime */ Blum_Blum_Shub = 0, // Make prime congruent to 3 mod 4
Safe = 1, /* Safe prime (p-1)/2 == prime */ Safe = 1, // Make sure (p-1)/2 is prime as well (implies .Blum_Blum_Shub)
Second_MSB_On = 3, /* force 2nd MSB to 1 */ Second_MSB_On = 3, // Make the 2nd highest bit one
}; };
Primality_Flags :: bit_set[Primality_Flag; u8]; Primality_Flags :: bit_set[Primality_Flag; u8];
+18 -7
View File
@@ -37,6 +37,7 @@ Runtime tunable:
FACTORIAL_BINARY_SPLIT_CUTOFF %v FACTORIAL_BINARY_SPLIT_CUTOFF %v
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS %v FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS %v
USE_MILLER_RABIN_ONLY %v USE_MILLER_RABIN_ONLY %v
MAX_ITERATIONS_RANDOM_PRIME %v
`, _DIGIT_BITS, `, _DIGIT_BITS,
_LOW_MEMORY, _LOW_MEMORY,
@@ -58,6 +59,7 @@ FACTORIAL_MAX_N,
FACTORIAL_BINARY_SPLIT_CUTOFF, FACTORIAL_BINARY_SPLIT_CUTOFF,
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS, FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS,
USE_MILLER_RABIN_ONLY, USE_MILLER_RABIN_ONLY,
MAX_ITERATIONS_RANDOM_PRIME,
); );
} }
@@ -84,18 +86,27 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline
} }
} }
// printf :: fmt.printf; printf :: fmt.printf;
demo :: proc() { demo :: proc() {
a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
defer destroy(a, b, c, d, e, f, res); defer destroy(a, b, c, d, e, f, res);
set(a, _private_prime_table[_PRIME_TAB_SIZE - 1]); bits := 111;
print("a: ", a); trials := -1;
trials := number_of_rabin_miller_trials(internal_count_bits(a));
err := internal_int_prime_next_prime(a, trials, false); flags := Primality_Flags{};
print("a->next: ", a); fmt.printf("Trying to generate a %v bit prime using %v Miller-Rabin trials and options %v.\n", bits, trials, flags);
fmt.printf("Trials: %v, Error: %v\n", trials, err);
err: Error;
{
SCOPED_TIMING(.random_prime);
err = internal_random_prime(a, bits, trials, flags);
}
print("a(10): ", a, 10, true, true, true);
fmt.printf("err: %v\n", err);
fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED);
} }
main :: proc() { main :: proc() {
+3 -3
View File
@@ -362,15 +362,15 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
return 0; // We shouldn't get here. return 0; // We shouldn't get here.
} }
int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
/* /*
Check that `a` is usable. Check that `a` is usable.
*/ */
assert_if_nil(dest); assert_if_nil(dest);
return #force_inline internal_int_rand(dest, bits, r, allocator); return #force_inline internal_int_random(dest, bits, r, allocator);
} }
rand :: proc { int_rand, }; random :: proc { int_random, };
/* /*
Internal helpers. Internal helpers.
+31 -2
View File
@@ -2045,6 +2045,7 @@ internal_invmod :: proc{ internal_int_inverse_modulo, };
/* /*
Helpers to extract values from the `Int`. Helpers to extract values from the `Int`.
Offset is zero indexed.
*/ */
internal_int_bitfield_extract_bool :: proc(a: ^Int, offset: int) -> (val: bool, err: Error) { internal_int_bitfield_extract_bool :: proc(a: ^Int, offset: int) -> (val: bool, err: Error) {
limb := offset / _DIGIT_BITS; limb := offset / _DIGIT_BITS;
@@ -2115,6 +2116,34 @@ internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WOR
return res, nil; return res, nil;
} }
/*
Helpers to (un)set a bit in an Int.
Offset is zero indexed.
*/
internal_int_bitfield_set_single :: proc(a: ^Int, offset: int) -> (err: Error) {
limb := offset / _DIGIT_BITS;
if limb < 0 || limb >= a.used { return .Invalid_Argument; }
i := DIGIT(1 << uint((offset % _DIGIT_BITS)));
a.digit[limb] |= i;
return;
}
internal_int_bitfield_unset_single :: proc(a: ^Int, offset: int) -> (err: Error) {
limb := offset / _DIGIT_BITS;
if limb < 0 || limb >= a.used { return .Invalid_Argument; }
i := DIGIT(1 << uint((offset % _DIGIT_BITS)));
a.digit[limb] &= _MASK - i;
return;
}
internal_int_bitfield_toggle_single :: proc(a: ^Int, offset: int) -> (err: Error) {
limb := offset / _DIGIT_BITS;
if limb < 0 || limb >= a.used { return .Invalid_Argument; }
i := DIGIT(1 << uint((offset % _DIGIT_BITS)));
a.digit[limb] ~= i;
return;
}
/* /*
Resize backing store. Resize backing store.
We don't need to pass the allocator, because the storage itself stores it. We don't need to pass the allocator, because the storage itself stores it.
@@ -2817,7 +2846,7 @@ internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
return 0; // We shouldn't get here. return 0; // We shouldn't get here.
} }
internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator; context.allocator = allocator;
bits := bits; bits := bits;
@@ -2842,7 +2871,7 @@ internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator :
dest.used = digits; dest.used = digits;
return nil; return nil;
} }
internal_rand :: proc { internal_int_rand, }; internal_random :: proc { internal_int_random, };
/* /*
Internal helpers. Internal helpers.
+113 -2
View File
@@ -456,7 +456,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
for ix := 0; ix < miller_rabin_trials; ix += 1 { for ix := 0; ix < miller_rabin_trials; ix += 1 {
// rand() guarantees the first digit to be non-zero // rand() guarantees the first digit to be non-zero
internal_rand(b, _DIGIT_TYPE_BITS, r) or_return; internal_random(b, _DIGIT_TYPE_BITS, r) or_return;
// Reduce digit before casting because DIGIT might be bigger than // Reduce digit before casting because DIGIT might be bigger than
// an unsigned int and "mask" on the other side is most probably not. // an unsigned int and "mask" on the other side is most probably not.
@@ -474,7 +474,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
ix -= 1; ix -= 1;
continue; continue;
} }
internal_rand(b, l) or_return; internal_random(b, l) or_return;
// That number might got too big and the witness has to be smaller than "a" // That number might got too big and the witness has to be smaller than "a"
l = internal_count_bits(b); l = internal_count_bits(b);
@@ -1168,6 +1168,117 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all
return; return;
} }
/*
Makes a truly random prime of a given size (bits),
Flags are as follows:
Blum_Blum_Shub - Make prime congruent to 3 mod 4
Safe - Make sure (p-1)/2 is prime as well (implies .Blum_Blum_Shub)
Second_MSB_On - Make the 2nd highest bit one
This is possibly the mother of all prime generation functions, muahahahahaha!
*/
internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags := Primality_Flags{}, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
flags := flags;
trials := trials;
t := &Int{};
defer internal_destroy(t);
/*
Sanity check the input.
*/
if size_in_bits <= 1 || trials < -1 { return .Invalid_Argument; }
/*
`.Safe` implies `.Blum_Blum_Shub`.
*/
if .Safe in flags {
if size_in_bits < 3 {
/*
The smallest safe prime is 5, which takes 3 bits.
We early out now, else we'd be locked in an infinite loop trying to generate a 2-bit Safe Prime.
*/
return .Invalid_Argument;
}
flags += { .Blum_Blum_Shub, };
}
/*
Automatically choose the number of Rabin-Miller trials?
*/
if trials == -1 {
trials = number_of_rabin_miller_trials(size_in_bits);
}
res: bool;
RANDOM_PRIME_ITERATIONS_USED = 0;
for {
if MAX_ITERATIONS_RANDOM_PRIME > 0 {
RANDOM_PRIME_ITERATIONS_USED += 1;
if RANDOM_PRIME_ITERATIONS_USED > MAX_ITERATIONS_RANDOM_PRIME {
return .Max_Iterations_Reached;
}
}
internal_int_random(a, size_in_bits) or_return;
/*
Make sure it's odd.
*/
if size_in_bits > 2 {
a.digit[0] |= 1;
} else {
/*
A 2-bit prime can be either 2 (0b10) or 3 (0b11).
So, let's force the top bit to 1 and return early.
*/
a.digit[0] |= 2;
return nil;
}
if .Blum_Blum_Shub in flags {
a.digit[0] |= 3;
}
if .Second_MSB_On in flags {
internal_int_bitfield_set_single(a, size_in_bits - 2) or_return;
}
/*
Is it prime?
*/
res = internal_int_is_prime(a, trials) or_return;
if (!res) {
continue;
}
if .Safe in flags {
/*
See if (a-1)/2 is prime.
*/
internal_sub(a, a, 1) or_return;
internal_int_shr1(a, a) or_return;
/*
Is it prime?
*/
res = internal_int_is_prime(a, trials) or_return;
}
if res { break; }
}
if .Safe in flags {
/*
Restore a to the original value.
*/
internal_int_shl1(a, a) or_return;
internal_add(a, a, 1) or_return;
}
return;
}
/* /*
Returns the number of Rabin-Miller trials needed for a given bit size. Returns the number of Rabin-Miller trials needed for a given bit size.
+1
View File
@@ -24,6 +24,7 @@ Category :: enum {
bitfield_extract, bitfield_extract,
rm_trials, rm_trials,
is_prime, is_prime,
random_prime,
}; };
Event :: struct { Event :: struct {