Merge pull request #1057 from odin-lang/new-big-int-library-integration

New Big Int Library Integration
This commit is contained in:
gingerBill
2021-07-11 17:20:57 +01:00
committed by GitHub
173 changed files with 10898 additions and 1931 deletions

View File

@@ -1,5 +1,5 @@
GIT_SHA=$(shell git rev-parse --short HEAD)
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-unused-value
LDFLAGS=-pthread -ldl -lm -lstdc++
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
@@ -8,31 +8,31 @@ CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif
ifeq ($(OS), Linux)
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
all: debug demo
@@ -41,13 +41,16 @@ demo:
./odin run examples/demo/demo.odin
debug:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
release:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
release_native:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
nightly:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin

View File

@@ -46,7 +46,7 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4189 ^
-wd4100 -wd4101 -wd4127 -wd4146 -wd4189 ^
-wd4201 -wd4204 ^
-wd4456 -wd4457 -wd4480 ^
-wd4512
@@ -70,7 +70,7 @@ set linker_settings=%libs% %linker_flags%
del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name%
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
if %errorlevel% neq 0 goto end_of_build
if %release_mode% EQU 0 odin run examples/demo/demo.odin

File diff suppressed because it is too large Load Diff

View File

@@ -745,7 +745,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (op.value.value_integer.neg) {
if (big_int_is_neg(&op.value.value_integer)) {
error(op.expr, "Negative 'swizzle' index");
return false;
}
@@ -795,10 +795,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (x.mode == Addressing_Constant &&
y.mode == Addressing_Constant) {
if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) {
x.value = exact_value_to_float(x.value);
y.value = exact_value_to_float(y.value);
if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) {
x.type = t_untyped_float;
}
if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) {
if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) {
y.type = t_untyped_float;
}
}
@@ -882,16 +884,20 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
y.mode == Addressing_Constant &&
z.mode == Addressing_Constant &&
w.mode == Addressing_Constant) {
if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) {
x.value = exact_value_to_float(x.value);
y.value = exact_value_to_float(y.value);
z.value = exact_value_to_float(z.value);
w.value = exact_value_to_float(w.value);
if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) {
x.type = t_untyped_float;
}
if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) {
if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) {
y.type = t_untyped_float;
}
if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) {
if (is_type_numeric(z.type) && z.value.kind == ExactValue_Float) {
z.type = t_untyped_float;
}
if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) {
if (is_type_numeric(w.type) && w.value.kind == ExactValue_Float) {
w.type = t_untyped_float;
}
}
@@ -1484,7 +1490,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (operand->mode == Addressing_Constant) {
switch (operand->value.kind) {
case ExactValue_Integer:
operand->value.value_integer.neg = false;
mp_abs(&operand->value.value_integer, &operand->value.value_integer);
break;
case ExactValue_Float:
operand->value.value_float = gb_abs(operand->value.value_float);
@@ -1837,7 +1843,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_invalid;
return false;
}
if (x.value.value_integer.neg) {
if (big_int_is_neg(&x.value.value_integer)) {
error(call, "Negative vector element length");
operand->mode = Addressing_Type;
operand->type = t_invalid;
@@ -1877,7 +1883,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_invalid;
return false;
}
if (x.value.value_integer.neg) {
if (big_int_is_neg(&x.value.value_integer)) {
error(call, "Negative array element length");
operand->mode = Addressing_Type;
operand->type = t_invalid;

View File

@@ -1503,13 +1503,13 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
big_int_from_i64(&bi127, 127);
big_int_shl_eq(&umax, &bi128);
big_int_sub_eq(&umax, &BIG_INT_ONE);
mp_decr(&umax);
big_int_shl_eq(&imin, &bi127);
big_int_neg(&imin, &imin);
big_int_shl_eq(&imax, &bi127);
big_int_sub_eq(&imax, &BIG_INT_ONE);
mp_decr(&imax);
}
switch (type->Basic.kind) {
@@ -1555,7 +1555,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
{
// return 0ull <= i && i <= umax;
int b = big_int_cmp(&i, &umax);
return !i.neg && (b <= 0);
return !i.sign && (b <= 0);
}
case Basic_UntypedInteger:
@@ -1758,12 +1758,6 @@ void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s'", a, b);
} else {
#if 0
gb_printf_err("AddressingMode, %d\n", o->mode);
gb_printf_err("ExactValueKind, %d\n", o->value.kind);
bool ok = check_representable_as_constant(ctx, o->value, type, &out_value);
gb_printf_err("ok, %d\n", ok);
#endif
error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s", a, b, c);
check_assignment_error_suggestion(ctx, o, type);
}
@@ -2206,7 +2200,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
}
BigInt max_shift = {};
big_int_from_u64(&max_shift, 128);
big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) {
gbString err_str = expr_to_string(y->expr);
@@ -2248,7 +2242,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
}
}
if (y->mode == Addressing_Constant && y->value.value_integer.neg) {
if (y->mode == Addressing_Constant && big_int_is_neg(&y->value.value_integer)) {
gbString err_str = expr_to_string(y->expr);
error(node, "Shift amount cannot be negative: '%s'", err_str);
gb_string_free(err_str);
@@ -3320,7 +3314,7 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
if (operand.mode == Addressing_Constant &&
(c->state_flags & StateFlag_no_bounds_check) == 0) {
BigInt i = exact_value_to_integer(operand.value).value_integer;
if (i.neg && !is_type_enum(index_type)) {
if (i.sign && !is_type_enum(index_type)) {
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr, "Index '%s' cannot be a negative value", expr_str);
gb_string_free(expr_str);
@@ -3366,7 +3360,7 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
} else { // NOTE(bill): Do array bound checking
i64 v = -1;
if (i.len <= 1) {
if (i.used <= 1) {
v = big_int_to_i64(&i);
}
if (value) *value = v;

View File

@@ -207,7 +207,7 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
if (is_type_untyped(type) || is_type_integer(type)) {
if (o.value.kind == ExactValue_Integer) {
BigInt v = o.value.value_integer;
if (v.len > 1) {
if (v.used > 1) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &v);
error(node, "#align too large, %.*s", LIT(str));
@@ -1998,16 +1998,16 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
if (is_type_untyped(type) || is_type_integer(type)) {
if (o->value.kind == ExactValue_Integer) {
BigInt count = o->value.value_integer;
if (o->value.value_integer.neg) {
if (big_int_is_neg(&o->value.value_integer)) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &count);
error(e, "Invalid negative array count, %.*s", LIT(str));
gb_free(a, str.text);
return 0;
}
switch (count.len) {
switch (count.used) {
case 0: return 0;
case 1: return count.d.word;
case 1: return big_int_to_u64(&count);
}
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &count);

View File

@@ -75,8 +75,8 @@ HashKey hash_exact_value(ExactValue v) {
}
case ExactValue_Integer:
{
HashKey key = hashing_proc(big_int_ptr(&v.value_integer), v.value_integer.len * gb_size_of(u64));
u8 last = (u8)v.value_integer.neg;
HashKey key = hashing_proc(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
u8 last = (u8)v.value_integer.sign;
key.key = (key.key ^ last) * 0x100000001b3ll;
return key;
}
@@ -719,7 +719,6 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case Token_Shr: big_int_shr(&c, a, b); break;
default: goto error;
}
big_int_normalize(&c);
ExactValue res = {ExactValue_Integer};
res.value_integer = c;
return res;

View File

@@ -1,744 +0,0 @@
#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
#define MSVC_AMD64_INTRINSICS
#include <intrin.h>
#pragma intrinsic(_mul128)
#endif
#define BIT128_U64_HIGHBIT 0x8000000000000000ull
#define BIT128_U64_BITS62 0x7fffffffffffffffull
#define BIT128_U64_ALLBITS 0xffffffffffffffffull
typedef struct u128 { u64 lo; u64 hi; } u128;
typedef struct i128 { u64 lo; i64 hi; } i128;
static u128 const U128_ZERO = {0, 0};
static u128 const U128_ONE = {1, 0};
static i128 const I128_ZERO = {0, 0};
static i128 const I128_ONE = {1, 0};
static u128 const U128_NEG_ONE = {BIT128_U64_ALLBITS, BIT128_U64_ALLBITS};
static i128 const I128_NEG_ONE = {BIT128_U64_ALLBITS, cast(i64)BIT128_U64_ALLBITS};
u128 u128_lo_hi (u64 lo, u64 hi);
u128 u128_from_u32 (u32 u);
u128 u128_from_u64 (u64 u);
u128 u128_from_i64 (i64 u);
u128 u128_from_f32 (f32 f);
u128 u128_from_f64 (f64 f);
u128 u128_from_string(String string);
i128 i128_lo_hi (u64 lo, i64 hi);
i128 i128_from_u32 (u32 u);
i128 i128_from_u64 (u64 u);
i128 i128_from_i64 (i64 u);
i128 i128_from_f32 (f32 f);
i128 i128_from_f64 (f64 f);
i128 i128_from_string(String string);
u64 u128_to_u64(u128 a);
i64 u128_to_i64(u128 a);
f64 u128_to_f64(u128 a);
i128 u128_to_i128(u128 a);
u64 i128_to_u64(i128 a);
i64 i128_to_i64(i128 a);
f64 i128_to_f64(i128 a);
u128 i128_to_u128(i128 a);
String u128_to_string(u128 a, char *buf, isize len);
String i128_to_string(i128 a, char *buf, isize len);
i32 u128_cmp (u128 a, u128 b);
bool u128_eq (u128 a, u128 b);
bool u128_ne (u128 a, u128 b);
bool u128_lt (u128 a, u128 b);
bool u128_gt (u128 a, u128 b);
bool u128_le (u128 a, u128 b);
bool u128_ge (u128 a, u128 b);
u128 u128_add (u128 a, u128 b);
u128 u128_not (u128 a);
u128 u128_neg (u128 a);
u128 u128_sub (u128 a, u128 b);
u128 u128_and (u128 a, u128 b);
u128 u128_or (u128 a, u128 b);
u128 u128_xor (u128 a, u128 b);
u128 u128_and_not(u128 a, u128 b);
u128 u128_shl (u128 a, u32 n);
u128 u128_shr (u128 a, u32 n);
u128 u128_mul (u128 a, u128 b);
void u128_divide (u128 num, u128 den, u128 *quo, u128 *rem);
u128 u128_quo (u128 a, u128 b);
u128 u128_mod (u128 a, u128 b);
i128 i128_abs (i128 a);
i32 i128_cmp (i128 a, i128 b);
bool i128_eq (i128 a, i128 b);
bool i128_ne (i128 a, i128 b);
bool i128_lt (i128 a, i128 b);
bool i128_gt (i128 a, i128 b);
bool i128_le (i128 a, i128 b);
bool i128_ge (i128 a, i128 b);
i128 i128_add (i128 a, i128 b);
i128 i128_not (i128 a);
i128 i128_neg (i128 a);
i128 i128_sub (i128 a, i128 b);
i128 i128_and (i128 a, i128 b);
i128 i128_or (i128 a, i128 b);
i128 i128_xor (i128 a, i128 b);
i128 i128_and_not(i128 a, i128 b);
i128 i128_shl (i128 a, u32 n);
i128 i128_shr (i128 a, u32 n);
i128 i128_mul (i128 a, i128 b);
void i128_divide (i128 num, i128 den, i128 *quo, i128 *rem);
i128 i128_quo (i128 a, i128 b);
i128 i128_mod (i128 a, i128 b);
bool operator==(u128 const &a, u128 const &b) { return u128_eq(a, b); }
bool operator!=(u128 const &a, u128 const &b) { return u128_ne(a, b); }
bool operator< (u128 const &a, u128 const &b) { return u128_lt(a, b); }
bool operator> (u128 const &a, u128 const &b) { return u128_gt(a, b); }
bool operator<=(u128 const &a, u128 const &b) { return u128_le(a, b); }
bool operator>=(u128 const &a, u128 const &b) { return u128_ge(a, b); }
u128 operator+ (u128 const &a, u128 const &b) { return u128_add(a, b); }
u128 operator- (u128 const &a, u128 const &b) { return u128_sub(a, b); }
u128 operator* (u128 const &a, u128 const &b) { return u128_mul(a, b); }
u128 operator/ (u128 const &a, u128 const &b) { return u128_quo(a, b); }
u128 operator% (u128 const &a, u128 const &b) { return u128_mod(a, b); }
u128 operator& (u128 const &a, u128 const &b) { return u128_and(a, b); }
u128 operator| (u128 const &a, u128 const &b) { return u128_or (a, b); }
u128 operator^ (u128 const &a, u128 const &b) { return u128_xor(a, b); }
u128 operator~ (u128 const &a) { return u128_not(a); }
u128 operator+ (u128 const &a) { return a; }
u128 operator- (u128 const &a) { return u128_neg(a); }
u128 operator<<(u128 const &a, u32 const &b) { return u128_shl(a, b); }
u128 operator>>(u128 const &a, u32 const &b) { return u128_shr(a, b); }
bool operator==(i128 const &a, i128 const &b) { return i128_eq(a, b); }
bool operator!=(i128 const &a, i128 const &b) { return i128_ne(a, b); }
bool operator< (i128 const &a, i128 const &b) { return i128_lt(a, b); }
bool operator> (i128 const &a, i128 const &b) { return i128_gt(a, b); }
bool operator<=(i128 const &a, i128 const &b) { return i128_le(a, b); }
bool operator>=(i128 const &a, i128 const &b) { return i128_ge(a, b); }
i128 operator+ (i128 const &a, i128 const &b) { return i128_add(a, b); }
i128 operator- (i128 const &a, i128 const &b) { return i128_sub(a, b); }
i128 operator* (i128 const &a, i128 const &b) { return i128_mul(a, b); }
i128 operator/ (i128 const &a, i128 const &b) { return i128_quo(a, b); }
i128 operator% (i128 const &a, i128 const &b) { return i128_mod(a, b); }
i128 operator& (i128 const &a, i128 const &b) { return i128_and(a, b); }
i128 operator| (i128 const &a, i128 const &b) { return i128_or (a, b); }
i128 operator^ (i128 const &a, i128 const &b) { return i128_xor(a, b); }
i128 operator~ (i128 const &a) { return i128_not(a); }
i128 operator+ (i128 const &a) { return a; }
i128 operator- (i128 const &a) { return i128_neg(a); }
i128 operator<<(i128 const &a, u32 b) { return i128_shl(a, b); }
i128 operator>>(i128 const &a, u32 b) { return i128_shr(a, b); }
////////////////////////////////////////////////////////////////
u64 bit128__digit_value(Rune r) {
if ('0' <= r && r <= '9') {
return r - '0';
} else if ('a' <= r && r <= 'f') {
return r - 'a' + 10;
} else if ('A' <= r && r <= 'F') {
return r - 'A' + 10;
}
return 16; // NOTE(bill): Larger than highest possible
}
u128 u128_lo_hi(u64 lo, u64 hi) {
u128 r = {};
r.lo = lo;
r.hi = hi;
return r;
}
u128 u128_from_u32(u32 u) { return u128_lo_hi(cast(u64)u, 0); }
u128 u128_from_u64(u64 u) { return u128_lo_hi(cast(u64)u, 0); }
u128 u128_from_i64(i64 u) { return u128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
u128 u128_from_f32(f32 f) { return u128_lo_hi(cast(u64)f, 0); }
u128 u128_from_f64(f64 f) { return u128_lo_hi(cast(u64)f, 0); }
u128 u128_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
u64 base = 10;
bool has_prefix = false;
if (string.len > 2 && string[0] == '0') {
switch (string[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
u128 base_ = u128_from_u64(base);
u128 result = {0};
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
u64 v = bit128__digit_value(r);
if (v >= base) {
break;
}
result = u128_mul(result, base_);
result = u128_add(result, u128_from_u64(v));
}
return result;
}
i128 i128_lo_hi(u64 lo, i64 hi) {
i128 i;
i.lo = lo;
i.hi = hi;
return i;
}
i128 i128_from_u32(u32 u) { return i128_lo_hi(cast(u64)u, 0); }
i128 i128_from_u64(u64 u) { return i128_lo_hi(cast(u64)u, 0); }
i128 i128_from_i64(i64 u) { return i128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
i128 i128_from_f32(f32 f) { return i128_lo_hi(cast(u64)f, 0); }
i128 i128_from_f64(f64 f) { return i128_lo_hi(cast(u64)f, 0); }
i128 i128_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
u64 base = 10;
bool has_prefix = false;
if (string.len > 2 && string[0] == '0') {
switch (string[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
i128 base_ = i128_from_u64(base);
i128 result = {0};
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
u64 v = bit128__digit_value(r);
if (v >= base) {
break;
}
result = i128_mul(result, base_);
result = i128_add(result, i128_from_u64(v));
}
return result;
}
u64 u128_to_u64(u128 a) {
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
}
i64 u128_to_i64(u128 a) {
return a.lo;
}
f64 u128_to_f64(u128 a) {
if (a.hi >= 0) {
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
}
i64 h = cast(i64)a.hi;
u64 l = a.lo;
h = ~h;
l = ~l;
l += 1;
if (l == 0) {
h += 1;
}
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
}
i128 u128_to_i128(u128 a) {
return bit_cast<i128>(a);
}
u64 i128_to_u64(i128 a) {
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
}
i64 i128_to_i64(i128 a) {
return cast(i64)a.lo;
}
f64 i128_to_f64(i128 a) {
if (a.hi >= 0) {
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
}
i64 h = a.hi;
u64 l = a.lo;
h = ~h;
l = ~l;
l += 1;
if (l == 0) {
h += 1;
}
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
}
u128 i128_to_u128(i128 a) {
return bit_cast<u128>(a);
}
String u128_to_string(u128 v, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
isize i = gb_size_of(buf);
u128 b = u128_from_u64(10);;
while (u128_ge(v, b)) {
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
v = u128_quo(v, b);
}
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
gb_memmove(out_buf, &buf[i], len);
return make_string(cast(u8 *)out_buf, len);
}
String i128_to_string(i128 a, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
isize i = gb_size_of(buf);
bool negative = false;
if (i128_lt(a, I128_ZERO)) {
negative = true;
a = i128_neg(a);
}
u128 v = bit_cast<u128>(a);
u128 b = u128_from_u64(10);;
while (u128_ge(v, b)) {
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
v = u128_quo(v, b);
}
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
if (negative) {
buf[--i] = '-';
}
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
gb_memmove(out_buf, &buf[i], len);
return make_string(cast(u8 *)out_buf, len);
}
////////////////////////////////////////////////////////////////
i32 u128_cmp(u128 a, u128 b) {
if (a.hi == b.hi && b.lo == b.lo) {
return 0;
}
if (a.hi == b.hi) {
return a.lo < b.lo ? -1 : +1;
}
return a.hi < b.hi ? -1 : +1;
}
bool u128_eq(u128 a, u128 b) { return a.hi == b.hi && a.lo == b.lo; }
bool u128_ne(u128 a, u128 b) { return !u128_eq(a, b); }
bool u128_lt(u128 a, u128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
bool u128_gt(u128 a, u128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
bool u128_le(u128 a, u128 b) { return !u128_gt(a, b); }
bool u128_ge(u128 a, u128 b) { return !u128_lt(a, b); }
u128 u128_add(u128 a, u128 b) {
u128 old_a = a;
a.lo += b.lo;
a.hi += b.hi;
if (a.lo < old_a.lo) {
a.hi += 1;
}
return a;
}
u128 u128_not(u128 a) { return u128_lo_hi(~a.lo, ~a.hi); }
u128 u128_neg(u128 a) {
return u128_add(u128_not(a), u128_from_u64(1));
}
u128 u128_sub(u128 a, u128 b) {
return u128_add(a, u128_neg(b));
}
u128 u128_and(u128 a, u128 b) { return u128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
u128 u128_or (u128 a, u128 b) { return u128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
u128 u128_xor(u128 a, u128 b) { return u128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
u128 u128_and_not(u128 a, u128 b) { return u128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
u128 u128_shl(u128 a, u32 n) {
if (n >= 128) {
return u128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.hi = __shiftleft128(a.lo, a.hi, n);
a.lo = a.lo << n;
return a;
#else
if (n >= 64) {
n -= 64;
a.hi = a.lo;
a.lo = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS >> n);
a.hi <<= n;
a.hi |= (a.lo&mask) >> (64 - n);
a.lo <<= n;
}
return a;
#endif
}
u128 u128_shr(u128 a, u32 n) {
if (n >= 128) {
return u128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.lo = __shiftright128(a.lo, a.hi, n);
a.hi = a.hi >> n;
return a;
#else
if (n >= 64) {
n -= 64;
a.lo = a.hi;
a.hi = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS << n);
a.lo >>= n;
a.lo |= (a.hi&mask) << (64 - n);
a.hi >>= n;
}
return a;
#endif
}
u128 u128_mul(u128 a, u128 b) {
if (a.lo == 0 && a.hi == 0) {
return u128_from_u64(0);
} else if (b.lo == 0 && b.hi == 0) {
return u128_from_u64(0);
}
if (u128_eq(a, U128_ONE)) {
return b;
}
if (u128_eq(b, U128_ONE)) {
return a;
}
#if defined(MSVC_AMD64_INTRINSICS)
if (a.hi == 0 && b.hi == 0) {
a.lo = _umul128(a.lo, b.lo, &a.hi);
return a;
}
#endif
u128 res = {0};
u128 t = b;
for (u32 i = 0; i < 128; i++) {
if ((t.lo&1) != 0) {
res = u128_add(res, u128_shl(a, i));
}
t = u128_shr(t, 1);
}
return res;
}
bool u128_hibit(u128 const &d) { return (d.hi & BIT128_U64_HIGHBIT) != 0; }
bool i128_hibit(i128 const &d) { return d.hi < 0; }
void u128_divide(u128 a, u128 b, u128 *quo, u128 *rem) {
if (u128_eq(b, U128_ZERO)) {
if (quo) *quo = u128_from_u64(a.lo/b.lo);
if (rem) *rem = U128_ZERO;
return;
}
if (a.hi == 0 && b.hi == 0) {
if (quo) *quo = u128_from_u64(a.lo/b.lo);
if (rem) *rem = u128_from_u64(a.lo%b.lo);
return;
}
u128 r = a;
u128 d = b;
u128 x = U128_ONE;
u128 q = U128_ZERO;
while (u128_ge(r, d) && !u128_hibit(d)) {
x = u128_shl(x, 1);
d = u128_shl(d, 1);
}
while (u128_ne(x, U128_ZERO)) {
if (u128_ge(r, d)) {
r = u128_sub(r, d);
q = u128_or(q, x);
}
x = u128_shr(x, 1);
d = u128_shr(d, 1);
}
if (quo) *quo = q;
if (rem) *rem = r;
}
u128 u128_quo(u128 a, u128 b) {
if (a.hi == 0 && b.hi == 0) {
return u128_from_u64(a.lo/b.lo);
}
u128 res = {0};
u128_divide(a, b, &res, nullptr);
return res;
}
u128 u128_mod(u128 a, u128 b) {
if (a.hi == 0 && b.hi == 0) {
return u128_from_u64(a.lo%b.lo);
}
u128 res = {0};
u128_divide(a, b, nullptr, &res);
return res;
}
////////////////////////////////////////////////////////////////
i128 i128_abs(i128 a) {
if ((a.hi&BIT128_U64_HIGHBIT) != 0) {
return i128_neg(a);
}
return a;
}
i32 i128_cmp(i128 a, i128 b) {
if (a.hi == b.hi && b.lo == b.lo) {
return 0;
}
if (a.hi == b.hi) {
return a.lo < b.lo ? -1 : +1;
}
return a.hi < b.hi ? -1 : +1;
}
bool i128_eq(i128 a, i128 b) { return a.hi == b.hi && a.lo == b.lo; }
bool i128_ne(i128 a, i128 b) { return !i128_eq(a, b); }
bool i128_lt(i128 a, i128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
bool i128_gt(i128 a, i128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
bool i128_le(i128 a, i128 b) { return a.hi == b.hi ? a.lo <= b.lo : a.hi <= b.hi; }
bool i128_ge(i128 a, i128 b) { return a.hi == b.hi ? a.lo >= b.lo : a.hi >= b.hi; }
i128 i128_add(i128 a, i128 b) {
i128 old_a = a;
a.lo += b.lo;
a.hi += b.hi;
if (a.lo < old_a.lo) {
a.hi += 1;
}
return a;
}
i128 i128_not(i128 a) { return i128_lo_hi(~a.lo, ~a.hi); }
i128 i128_neg(i128 a) {
return i128_add(i128_not(a), i128_from_u64(1));
}
i128 i128_sub(i128 a, i128 b) {
return i128_add(a, i128_neg(b));
}
i128 i128_and(i128 a, i128 b) { return i128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
i128 i128_or (i128 a, i128 b) { return i128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
i128 i128_xor(i128 a, i128 b) { return i128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
i128 i128_and_not(i128 a, i128 b) { return i128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
i128 i128_shl(i128 a, u32 n) {
if (n >= 128) {
return i128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.hi = __shiftleft128(a.lo, a.hi, n);
a.lo = a.lo << n;
return a;
#else
if (n >= 64) {
n -= 64;
a.hi = a.lo;
a.lo = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS >> n);
a.hi <<= n;
a.hi |= (a.lo&mask) >> (64 - n);
a.lo <<= n;
}
return a;
#endif
}
i128 i128_shr(i128 a, u32 n) {
if (n >= 128) {
return i128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.lo = __shiftright128(a.lo, a.hi, n);
a.hi = a.hi >> n;
return a;
#else
if (n >= 64) {
n -= 64;
a.lo = a.hi;
a.hi = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS << n);
a.lo >>= n;
a.lo |= (a.hi&mask) << (64 - n);
a.hi >>= n;
}
return a;
#endif
}
i128 i128_mul(i128 a, i128 b) {
if (a.lo == 0 && a.hi == 0) {
return i128_from_u64(0);
} else if (b.lo == 0 && b.hi == 0) {
return i128_from_u64(0);
}
if (i128_eq(a, I128_ONE)) {
return b;
}
if (i128_eq(b, I128_ONE)) {
return a;
}
#if defined(MSVC_AMD64_INTRINSICS)
if (a.hi == 0 && b.hi == 0) {
a.lo = _mul128(a.lo, b.lo, &a.hi);
return a;
}
#endif
i128 res = {0};
i128 t = b;
for (u32 i = 0; i < 128; i++) {
if ((t.lo&1) != 0) {
res = i128_add(res, i128_shl(a, i));
}
t = i128_shr(t, 1);
}
return res;
}
void i128_divide(i128 a, i128 b, i128 *quo_, i128 *rem_) {
// IMPORTANT TODO(bill): Optimize this i128 division calculation
i128 iquo = {0};
i128 irem = {0};
if (a.hi == 0 && b.hi == 0) {
u64 q = a.lo / b.lo;
u64 r = a.lo % b.lo;
iquo = i128_from_u64(q);
irem = i128_from_u64(r);
} else if ((~a.hi) == 0 && (~b.hi) == 0) {
i64 x = i128_to_i64(a);
i64 y = i128_to_i64(b);
i64 q = x / y;
i64 r = x % y;
iquo = i128_from_i64(q);
irem = i128_from_i64(r);
} else if (a.hi > 0 || b.hi > 0) {
u128 q, r = {0};
u128_divide(bit_cast<u128>(a), bit_cast<u128>(b), &q, &r);
iquo = bit_cast<i128>(q);
irem = bit_cast<i128>(r);
} else if (i128_eq(b, I128_ZERO)) {
iquo = i128_from_u64(a.lo/b.lo);
} else {
i32 rem_sign = 1;
i32 quo_sign = 1;
if (i128_lt(a, I128_ZERO)) {
a = i128_neg(a);
rem_sign = -1;
}
if (i128_lt(b, I128_ZERO)) {
b = i128_neg(b);
quo_sign = -1;
}
quo_sign *= rem_sign;
iquo = a;
for (isize i = 0; i < 128; i++) {
irem = i128_shl(irem, 1);
if (i128_lt(iquo, I128_ZERO)) {
irem.lo |= 1;
}
iquo = i128_shl(iquo, 1);
if (i128_ge(irem, b)) {
irem = i128_sub(irem, b);
iquo = i128_add(iquo, I128_ONE);
}
}
if (quo_sign < 0) iquo = i128_neg(iquo);
if (rem_sign < 0) irem = i128_neg(irem);
}
if (quo_) *quo_ = iquo;
if (rem_) *rem_ = irem;
}
i128 i128_quo(i128 a, i128 b) {
i128 res = {0};
i128_divide(a, b, &res, nullptr);
return res;
}
i128 i128_mod(i128 a, i128 b) {
i128 res = {0};
i128_divide(a, b, nullptr, &res);
return res;
}

154
src/libtommath.cpp Normal file
View File

@@ -0,0 +1,154 @@
#include "libtommath/mp_2expt.c"
#include "libtommath/mp_abs.c"
#include "libtommath/mp_add.c"
#include "libtommath/mp_add_d.c"
#include "libtommath/mp_addmod.c"
#include "libtommath/mp_and.c"
#include "libtommath/mp_clamp.c"
#include "libtommath/mp_clear.c"
#include "libtommath/mp_clear_multi.c"
#include "libtommath/mp_cmp.c"
#include "libtommath/mp_cmp_d.c"
#include "libtommath/mp_cmp_mag.c"
#include "libtommath/mp_cnt_lsb.c"
#include "libtommath/mp_complement.c"
#include "libtommath/mp_copy.c"
#include "libtommath/mp_count_bits.c"
#include "libtommath/mp_cutoffs.c"
#include "libtommath/mp_div.c"
#include "libtommath/mp_div_2.c"
#include "libtommath/mp_div_2d.c"
#include "libtommath/mp_div_d.c"
#include "libtommath/mp_dr_is_modulus.c"
#include "libtommath/mp_dr_reduce.c"
#include "libtommath/mp_dr_setup.c"
#include "libtommath/mp_error_to_string.c"
#include "libtommath/mp_exch.c"
#include "libtommath/mp_expt_n.c"
#include "libtommath/mp_exptmod.c"
#include "libtommath/mp_exteuclid.c"
#include "libtommath/mp_fread.c"
#include "libtommath/mp_from_sbin.c"
#include "libtommath/mp_from_ubin.c"
#include "libtommath/mp_fwrite.c"
#include "libtommath/mp_gcd.c"
#include "libtommath/mp_get_double.c"
#include "libtommath/mp_get_i32.c"
#include "libtommath/mp_get_i64.c"
#include "libtommath/mp_get_l.c"
#include "libtommath/mp_get_mag_u32.c"
#include "libtommath/mp_get_mag_u64.c"
#include "libtommath/mp_get_mag_ul.c"
#include "libtommath/mp_grow.c"
#include "libtommath/mp_init.c"
#include "libtommath/mp_init_copy.c"
#include "libtommath/mp_init_i32.c"
#include "libtommath/mp_init_i64.c"
#include "libtommath/mp_init_l.c"
#include "libtommath/mp_init_multi.c"
#include "libtommath/mp_init_set.c"
#include "libtommath/mp_init_size.c"
#include "libtommath/mp_init_u32.c"
#include "libtommath/mp_init_u64.c"
#include "libtommath/mp_init_ul.c"
#include "libtommath/mp_invmod.c"
#include "libtommath/mp_is_square.c"
#include "libtommath/mp_kronecker.c"
#include "libtommath/mp_lcm.c"
#include "libtommath/mp_log_n.c"
#include "libtommath/mp_lshd.c"
#include "libtommath/mp_mod.c"
#include "libtommath/mp_mod_2d.c"
#include "libtommath/mp_montgomery_calc_normalization.c"
#include "libtommath/mp_montgomery_reduce.c"
#include "libtommath/mp_montgomery_setup.c"
#include "libtommath/mp_mul.c"
#include "libtommath/mp_mul_2.c"
#include "libtommath/mp_mul_2d.c"
#include "libtommath/mp_mul_d.c"
#include "libtommath/mp_mulmod.c"
#include "libtommath/mp_neg.c"
#include "libtommath/mp_or.c"
#include "libtommath/mp_pack.c"
#include "libtommath/mp_pack_count.c"
// #include "libtommath/mp_prime_fermat.c"
// #include "libtommath/mp_prime_frobenius_underwood.c"
// #include "libtommath/mp_prime_is_prime.c"
// #include "libtommath/mp_prime_miller_rabin.c"
// #include "libtommath/mp_prime_next_prime.c"
// #include "libtommath/mp_prime_rabin_miller_trials.c"
// #include "libtommath/mp_prime_rand.c"
// #include "libtommath/mp_prime_strong_lucas_selfridge.c"
#include "libtommath/mp_radix_size.c"
#include "libtommath/mp_radix_size_overestimate.c"
// #include "libtommath/mp_rand.c"
// #include "libtommath/mp_rand_source.c"
#include "libtommath/mp_read_radix.c"
#include "libtommath/mp_reduce.c"
#include "libtommath/mp_reduce_2k.c"
#include "libtommath/mp_reduce_2k_l.c"
#include "libtommath/mp_reduce_2k_setup.c"
#include "libtommath/mp_reduce_2k_setup_l.c"
#include "libtommath/mp_reduce_is_2k.c"
#include "libtommath/mp_reduce_is_2k_l.c"
#include "libtommath/mp_reduce_setup.c"
#include "libtommath/mp_root_n.c"
#include "libtommath/mp_rshd.c"
#include "libtommath/mp_sbin_size.c"
#include "libtommath/mp_set.c"
#include "libtommath/mp_set_double.c"
#include "libtommath/mp_set_i32.c"
#include "libtommath/mp_set_i64.c"
#include "libtommath/mp_set_l.c"
#include "libtommath/mp_set_u32.c"
#include "libtommath/mp_set_u64.c"
#include "libtommath/mp_set_ul.c"
#include "libtommath/mp_shrink.c"
#include "libtommath/mp_signed_rsh.c"
#include "libtommath/mp_sqrmod.c"
#include "libtommath/mp_sqrt.c"
#include "libtommath/mp_sqrtmod_prime.c"
#include "libtommath/mp_sub.c"
#include "libtommath/mp_sub_d.c"
#include "libtommath/mp_submod.c"
#include "libtommath/mp_to_radix.c"
#include "libtommath/mp_to_sbin.c"
#include "libtommath/mp_to_ubin.c"
#include "libtommath/mp_ubin_size.c"
#include "libtommath/mp_unpack.c"
#include "libtommath/mp_xor.c"
#include "libtommath/mp_zero.c"
#include "libtommath/s_mp_add.c"
#include "libtommath/s_mp_copy_digs.c"
#include "libtommath/s_mp_div_3.c"
#include "libtommath/s_mp_div_recursive.c"
#include "libtommath/s_mp_div_school.c"
#include "libtommath/s_mp_div_small.c"
#include "libtommath/s_mp_exptmod.c"
#include "libtommath/s_mp_exptmod_fast.c"
#include "libtommath/s_mp_get_bit.c"
#include "libtommath/s_mp_invmod.c"
#include "libtommath/s_mp_invmod_odd.c"
#include "libtommath/s_mp_log.c"
#include "libtommath/s_mp_log_2expt.c"
#include "libtommath/s_mp_log_d.c"
#include "libtommath/s_mp_montgomery_reduce_comba.c"
#include "libtommath/s_mp_mul.c"
#include "libtommath/s_mp_mul_balance.c"
#include "libtommath/s_mp_mul_comba.c"
#include "libtommath/s_mp_mul_high.c"
#include "libtommath/s_mp_mul_high_comba.c"
#include "libtommath/s_mp_mul_karatsuba.c"
#include "libtommath/s_mp_mul_toom.c"
#include "libtommath/s_mp_prime_is_divisible.c"
#include "libtommath/s_mp_prime_tab.c"
#include "libtommath/s_mp_radix_map.c"
#include "libtommath/s_mp_radix_size_overestimate.c"
// #include "libtommath/s_mp_rand_platform.c"
#include "libtommath/s_mp_sqr.c"
#include "libtommath/s_mp_sqr_comba.c"
#include "libtommath/s_mp_sqr_karatsuba.c"
#include "libtommath/s_mp_sqr_toom.c"
#include "libtommath/s_mp_sub.c"
#include "libtommath/s_mp_zero_buf.c"
#include "libtommath/s_mp_zero_digs.c"

26
src/libtommath/LICENSE Normal file
View File

@@ -0,0 +1,26 @@
The LibTom license
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

44
src/libtommath/README.md Normal file
View File

@@ -0,0 +1,44 @@
# libtommath
This is the git repository for [LibTomMath](http://www.libtom.net/LibTomMath/), a free open source portable number theoretic multiple-precision integer (MPI) library written entirely in C.
## Build Status
### Travis CI
master: [![Build Status](https://api.travis-ci.org/libtom/libtommath.png?branch=master)](https://travis-ci.org/libtom/libtommath)
develop: [![Build Status](https://api.travis-ci.org/libtom/libtommath.png?branch=develop)](https://travis-ci.org/libtom/libtommath)
### AppVeyor
master: [![Build status](https://ci.appveyor.com/api/projects/status/b80lpolw3i8m6hsh/branch/master?svg=true)](https://ci.appveyor.com/project/libtom/libtommath/branch/master)
develop: [![Build status](https://ci.appveyor.com/api/projects/status/b80lpolw3i8m6hsh/branch/develop?svg=true)](https://ci.appveyor.com/project/libtom/libtommath/branch/develop)
### ABI Laboratory
API/ABI changes: [check here](https://abi-laboratory.pro/tracker/timeline/libtommath/)
## Summary
The `develop` branch contains the in-development version. Stable releases are tagged.
Documentation is built from the LaTeX file `bn.tex`. There is also limited documentation in `tommath.h`.
There is also a document, `tommath.pdf`, which describes the goals of the project and many of the algorithms used.
The project can be build by using `make`. Along with the usual `make`, `make clean` and `make install`,
there are several other build targets, see the makefile for details.
There are also makefiles for certain specific platforms.
## Testing
Tests are located in `demo/` and can be built in two flavors.
* `make test` creates a stand-alone test binary that executes several test routines.
* `make mtest_opponent` creates a test binary that is intended to be run against `mtest`.
`mtest` can be built with `make mtest` and test execution is done like `./mtest/mtest | ./mtest_opponent`.
`mtest` is creating test vectors using an alternative MPI library and `test` is consuming these vectors to verify correct behavior of ltm
## Building and Installing
Building is straightforward for GNU Linux only, the section "Building LibTomMath" in the documentation in `doc/bn.pdf` has the details.

31
src/libtommath/mp_2expt.c Normal file
View File

@@ -0,0 +1,31 @@
#include "tommath_private.h"
#ifdef MP_2EXPT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* computes a = 2**b
*
* Simple algorithm which zeroes the int, grows it then just sets one bit
* as required.
*/
mp_err mp_2expt(mp_int *a, int b)
{
mp_err err;
/* zero a as per default */
mp_zero(a);
/* grow a to accomodate the single bit */
if ((err = mp_grow(a, (b / MP_DIGIT_BIT) + 1)) != MP_OKAY) {
return err;
}
/* set the used count of where the bit will go */
a->used = (b / MP_DIGIT_BIT) + 1;
/* put the single bit in its place */
a->dp[b / MP_DIGIT_BIT] = (mp_digit)1 << (mp_digit)(b % MP_DIGIT_BIT);
return MP_OKAY;
}
#endif

24
src/libtommath/mp_abs.c Normal file
View File

@@ -0,0 +1,24 @@
#include "tommath_private.h"
#ifdef MP_ABS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = |a|
*
* Simple function copies the input and fixes the sign to positive
*/
mp_err mp_abs(const mp_int *a, mp_int *b)
{
mp_err err;
/* copy a to b */
if ((err = mp_copy(a, b)) != MP_OKAY) {
return err;
}
/* force the sign of b to positive */
b->sign = MP_ZPOS;
return MP_OKAY;
}
#endif

29
src/libtommath/mp_add.c Normal file
View File

@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_ADD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* high level addition (handles signs) */
mp_err mp_add(const mp_int *a, const mp_int *b, mp_int *c)
{
/* handle two cases, not four */
if (a->sign == b->sign) {
/* both positive or both negative */
/* add their magnitudes, copy the sign */
c->sign = a->sign;
return s_mp_add(a, b, c);
}
/* one positive, the other negative */
/* subtract the one with the greater magnitude from */
/* the one of the lesser magnitude. The result gets */
/* the sign of the one with the greater magnitude. */
if (mp_cmp_mag(a, b) == MP_LT) {
MP_EXCH(const mp_int *, a, b);
}
c->sign = a->sign;
return s_mp_sub(a, b, c);
}
#endif

86
src/libtommath/mp_add_d.c Normal file
View File

@@ -0,0 +1,86 @@
#include "tommath_private.h"
#ifdef MP_ADD_D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* single digit addition */
mp_err mp_add_d(const mp_int *a, mp_digit b, mp_int *c)
{
mp_err err;
int oldused;
/* fast path for a == c */
if (a == c) {
if (!mp_isneg(c) &&
!mp_iszero(c) &&
((c->dp[0] + b) < MP_DIGIT_MAX)) {
c->dp[0] += b;
return MP_OKAY;
}
if (mp_isneg(c) &&
(c->dp[0] > b)) {
c->dp[0] -= b;
return MP_OKAY;
}
}
/* grow c as required */
if ((err = mp_grow(c, a->used + 1)) != MP_OKAY) {
return err;
}
/* if a is negative and |a| >= b, call c = |a| - b */
if (mp_isneg(a) && ((a->used > 1) || (a->dp[0] >= b))) {
mp_int a_ = *a;
/* temporarily fix sign of a */
a_.sign = MP_ZPOS;
/* c = |a| - b */
err = mp_sub_d(&a_, b, c);
/* fix sign */
c->sign = MP_NEG;
/* clamp */
mp_clamp(c);
return err;
}
/* old number of used digits in c */
oldused = c->used;
/* if a is positive */
if (!mp_isneg(a)) {
/* add digits, mu is carry */
int i;
mp_digit mu = b;
for (i = 0; i < a->used; i++) {
c->dp[i] = a->dp[i] + mu;
mu = c->dp[i] >> MP_DIGIT_BIT;
c->dp[i] &= MP_MASK;
}
/* set final carry */
c->dp[i] = mu;
/* setup size */
c->used = a->used + 1;
} else {
/* a was negative and |a| < b */
c->used = 1;
/* the result is a single digit */
c->dp[0] = (a->used == 1) ? b - a->dp[0] : b;
}
/* sign always positive */
c->sign = MP_ZPOS;
/* now zero to oldused */
s_mp_zero_digs(c->dp + c->used, oldused - c->used);
mp_clamp(c);
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,15 @@
#include "tommath_private.h"
#ifdef MP_ADDMOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* d = a + b (mod c) */
mp_err mp_addmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d)
{
mp_err err;
if ((err = mp_add(a, b, d)) != MP_OKAY) {
return err;
}
return mp_mod(d, c, d);
}
#endif

54
src/libtommath/mp_and.c Normal file
View File

@@ -0,0 +1,54 @@
#include "tommath_private.h"
#ifdef MP_AND_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* two complement and */
mp_err mp_and(const mp_int *a, const mp_int *b, mp_int *c)
{
int used = MP_MAX(a->used, b->used) + 1, i;
mp_err err;
mp_digit ac = 1, bc = 1, cc = 1;
bool neg = (mp_isneg(a) && mp_isneg(b));
if ((err = mp_grow(c, used)) != MP_OKAY) {
return err;
}
for (i = 0; i < used; i++) {
mp_digit x, y;
/* convert to two complement if negative */
if (mp_isneg(a)) {
ac += (i >= a->used) ? MP_MASK : (~a->dp[i] & MP_MASK);
x = ac & MP_MASK;
ac >>= MP_DIGIT_BIT;
} else {
x = (i >= a->used) ? 0uL : a->dp[i];
}
/* convert to two complement if negative */
if (mp_isneg(b)) {
bc += (i >= b->used) ? MP_MASK : (~b->dp[i] & MP_MASK);
y = bc & MP_MASK;
bc >>= MP_DIGIT_BIT;
} else {
y = (i >= b->used) ? 0uL : b->dp[i];
}
c->dp[i] = x & y;
/* convert to to sign-magnitude if negative */
if (neg) {
cc += ~c->dp[i] & MP_MASK;
c->dp[i] = cc & MP_MASK;
cc >>= MP_DIGIT_BIT;
}
}
c->used = used;
c->sign = (neg ? MP_NEG : MP_ZPOS);
mp_clamp(c);
return MP_OKAY;
}
#endif

27
src/libtommath/mp_clamp.c Normal file
View File

@@ -0,0 +1,27 @@
#include "tommath_private.h"
#ifdef MP_CLAMP_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* trim unused digits
*
* This is used to ensure that leading zero digits are
* trimed and the leading "used" digit will be non-zero
* Typically very fast. Also fixes the sign if there
* are no more leading digits
*/
void mp_clamp(mp_int *a)
{
/* decrease used while the most significant digit is
* zero.
*/
while ((a->used > 0) && (a->dp[a->used - 1] == 0u)) {
--(a->used);
}
/* reset the sign flag if zero */
if (mp_iszero(a)) {
a->sign = MP_ZPOS;
}
}
#endif

20
src/libtommath/mp_clear.c Normal file
View File

@@ -0,0 +1,20 @@
#include "tommath_private.h"
#ifdef MP_CLEAR_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* clear one (frees) */
void mp_clear(mp_int *a)
{
/* only do anything if a hasn't been freed previously */
if (a->dp != NULL) {
/* free ram */
MP_FREE_DIGS(a->dp, a->alloc);
/* reset members to make debugging easier */
a->dp = NULL;
a->alloc = a->used = 0;
a->sign = MP_ZPOS;
}
}
#endif

View File

@@ -0,0 +1,18 @@
#include "tommath_private.h"
#ifdef MP_CLEAR_MULTI_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include <stdarg.h>
void mp_clear_multi(mp_int *mp, ...)
{
va_list args;
va_start(args, mp);
while (mp != NULL) {
mp_clear(mp);
mp = va_arg(args, mp_int *);
}
va_end(args);
}
#endif

21
src/libtommath/mp_cmp.c Normal file
View File

@@ -0,0 +1,21 @@
#include "tommath_private.h"
#ifdef MP_CMP_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* compare two ints (signed)*/
mp_ord mp_cmp(const mp_int *a, const mp_int *b)
{
/* compare based on sign */
if (a->sign != b->sign) {
return mp_isneg(a) ? MP_LT : MP_GT;
}
/* if negative compare opposite direction */
if (mp_isneg(a)) {
MP_EXCH(const mp_int *, a, b);
}
return mp_cmp_mag(a, b);
}
#endif

26
src/libtommath/mp_cmp_d.c Normal file
View File

@@ -0,0 +1,26 @@
#include "tommath_private.h"
#ifdef MP_CMP_D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* compare a digit */
mp_ord mp_cmp_d(const mp_int *a, mp_digit b)
{
/* compare based on sign */
if (mp_isneg(a)) {
return MP_LT;
}
/* compare based on magnitude */
if (a->used > 1) {
return MP_GT;
}
/* compare the only digit of a to b */
if (a->dp[0] != b) {
return a->dp[0] > b ? MP_GT : MP_LT;
}
return MP_EQ;
}
#endif

View File

@@ -0,0 +1,25 @@
#include "tommath_private.h"
#ifdef MP_CMP_MAG_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* compare maginitude of two ints (unsigned) */
mp_ord mp_cmp_mag(const mp_int *a, const mp_int *b)
{
int n;
/* compare based on # of non-zero digits */
if (a->used != b->used) {
return a->used > b->used ? MP_GT : MP_LT;
}
/* compare based on digits */
for (n = a->used; n --> 0;) {
if (a->dp[n] != b->dp[n]) {
return a->dp[n] > b->dp[n] ? MP_GT : MP_LT;
}
}
return MP_EQ;
}
#endif

View File

@@ -0,0 +1,38 @@
#include "tommath_private.h"
#ifdef MP_CNT_LSB_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
static const char lnz[16] = {
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
/* Counts the number of lsbs which are zero before the first zero bit */
int mp_cnt_lsb(const mp_int *a)
{
int x;
mp_digit q;
/* easy out */
if (mp_iszero(a)) {
return 0;
}
/* scan lower digits until non-zero */
for (x = 0; (x < a->used) && (a->dp[x] == 0u); x++) {}
q = a->dp[x];
x *= MP_DIGIT_BIT;
/* now scan this digit until a 1 is found */
if ((q & 1u) == 0u) {
mp_digit p;
do {
p = q & 15u;
x += lnz[p];
q >>= 4;
} while (p == 0u);
}
return x;
}
#endif

View File

@@ -0,0 +1,13 @@
#include "tommath_private.h"
#ifdef MP_COMPLEMENT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = ~a */
mp_err mp_complement(const mp_int *a, mp_int *b)
{
mp_int a_ = *a;
a_.sign = ((a_.sign == MP_ZPOS) && !mp_iszero(a)) ? MP_NEG : MP_ZPOS;
return mp_sub_d(&a_, 1uL, b);
}
#endif

29
src/libtommath/mp_copy.c Normal file
View File

@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_COPY_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* copy, b = a */
mp_err mp_copy(const mp_int *a, mp_int *b)
{
mp_err err;
/* if dst == src do nothing */
if (a == b) {
return MP_OKAY;
}
/* grow dest */
if ((err = mp_grow(b, a->used)) != MP_OKAY) {
return err;
}
/* copy everything over and zero high digits */
s_mp_copy_digs(b->dp, a->dp, a->used);
s_mp_zero_digs(b->dp + a->used, b->used - a->used);
b->used = a->used;
b->sign = a->sign;
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,28 @@
#include "tommath_private.h"
#ifdef MP_COUNT_BITS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* returns the number of bits in an int */
int mp_count_bits(const mp_int *a)
{
int r;
mp_digit q;
/* shortcut */
if (mp_iszero(a)) {
return 0;
}
/* get number of digits and add that */
r = (a->used - 1) * MP_DIGIT_BIT;
/* take the last digit and count the bits in it */
q = a->dp[a->used - 1];
while (q > 0u) {
++r;
q >>= 1u;
}
return r;
}
#endif

View File

@@ -0,0 +1,14 @@
#include "tommath_private.h"
#ifdef MP_CUTOFFS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#ifndef MP_FIXED_CUTOFFS
#include "tommath_cutoffs.h"
int MP_MUL_KARATSUBA_CUTOFF = MP_DEFAULT_MUL_KARATSUBA_CUTOFF,
MP_SQR_KARATSUBA_CUTOFF = MP_DEFAULT_SQR_KARATSUBA_CUTOFF,
MP_MUL_TOOM_CUTOFF = MP_DEFAULT_MUL_TOOM_CUTOFF,
MP_SQR_TOOM_CUTOFF = MP_DEFAULT_SQR_TOOM_CUTOFF;
#endif
#endif

42
src/libtommath/mp_div.c Normal file
View File

@@ -0,0 +1,42 @@
#include "tommath_private.h"
#ifdef MP_DIV_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
mp_err mp_div(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d)
{
mp_err err;
/* is divisor zero ? */
if (mp_iszero(b)) {
return MP_VAL;
}
/* if a < b then q = 0, r = a */
if (mp_cmp_mag(a, b) == MP_LT) {
if (d != NULL) {
if ((err = mp_copy(a, d)) != MP_OKAY) {
return err;
}
}
if (c != NULL) {
mp_zero(c);
}
return MP_OKAY;
}
if (MP_HAS(S_MP_DIV_RECURSIVE)
&& (b->used > (2 * MP_MUL_KARATSUBA_CUTOFF))
&& (b->used <= ((a->used/3)*2))) {
err = s_mp_div_recursive(a, b, c, d);
} else if (MP_HAS(S_MP_DIV_SCHOOL)) {
err = s_mp_div_school(a, b, c, d);
} else if (MP_HAS(S_MP_DIV_SMALL)) {
err = s_mp_div_small(a, b, c, d);
} else {
err = MP_VAL;
}
return err;
}
#endif

40
src/libtommath/mp_div_2.c Normal file
View File

@@ -0,0 +1,40 @@
#include "tommath_private.h"
#ifdef MP_DIV_2_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = a/2 */
mp_err mp_div_2(const mp_int *a, mp_int *b)
{
mp_err err;
int x, oldused;
mp_digit r;
if ((err = mp_grow(b, a->used)) != MP_OKAY) {
return err;
}
oldused = b->used;
b->used = a->used;
/* carry */
r = 0;
for (x = b->used; x --> 0;) {
/* get the carry for the next iteration */
mp_digit rr = a->dp[x] & 1u;
/* shift the current digit, add in carry and store */
b->dp[x] = (a->dp[x] >> 1) | (r << (MP_DIGIT_BIT - 1));
/* forward carry to next iteration */
r = rr;
}
/* zero excess digits */
s_mp_zero_digs(b->dp + b->used, oldused - b->used);
b->sign = a->sign;
mp_clamp(b);
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,61 @@
#include "tommath_private.h"
#ifdef MP_DIV_2D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
mp_err mp_div_2d(const mp_int *a, int b, mp_int *c, mp_int *d)
{
mp_err err;
if (b < 0) {
return MP_VAL;
}
if ((err = mp_copy(a, c)) != MP_OKAY) {
return err;
}
/* 'a' should not be used after here - it might be the same as d */
/* get the remainder */
if (d != NULL) {
if ((err = mp_mod_2d(a, b, d)) != MP_OKAY) {
return err;
}
}
/* shift by as many digits in the bit count */
if (b >= MP_DIGIT_BIT) {
mp_rshd(c, b / MP_DIGIT_BIT);
}
/* shift any bit count < MP_DIGIT_BIT */
b %= MP_DIGIT_BIT;
if (b != 0u) {
int x;
mp_digit r, mask, shift;
/* mask */
mask = ((mp_digit)1 << b) - 1uL;
/* shift for lsb */
shift = (mp_digit)(MP_DIGIT_BIT - b);
/* carry */
r = 0;
for (x = c->used; x --> 0;) {
/* get the lower bits of this word in a temp */
mp_digit rr = c->dp[x] & mask;
/* shift the current word and mix in the carry bits from the previous word */
c->dp[x] = (c->dp[x] >> b) | (r << shift);
/* set the carry to the carry bits of the current word found above */
r = rr;
}
}
mp_clamp(c);
return MP_OKAY;
}
#endif

84
src/libtommath/mp_div_d.c Normal file
View File

@@ -0,0 +1,84 @@
#include "tommath_private.h"
#ifdef MP_DIV_D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* single digit division (based on routine from MPI) */
mp_err mp_div_d(const mp_int *a, mp_digit b, mp_int *c, mp_digit *d)
{
mp_int q;
mp_word w;
mp_err err;
int ix;
/* cannot divide by zero */
if (b == 0u) {
return MP_VAL;
}
/* quick outs */
if ((b == 1u) || mp_iszero(a)) {
if (d != NULL) {
*d = 0;
}
if (c != NULL) {
return mp_copy(a, c);
}
return MP_OKAY;
}
/* power of two ? */
if (MP_HAS(MP_DIV_2) && (b == 2u)) {
if (d != NULL) {
*d = mp_isodd(a) ? 1u : 0u;
}
return (c == NULL) ? MP_OKAY : mp_div_2(a, c);
}
if (MP_HAS(MP_DIV_2D) && MP_IS_2EXPT(b)) {
ix = 1;
while ((ix < MP_DIGIT_BIT) && (b != (((mp_digit)1)<<ix))) {
ix++;
}
if (d != NULL) {
*d = a->dp[0] & (((mp_digit)1<<(mp_digit)ix) - 1uL);
}
return (c == NULL) ? MP_OKAY : mp_div_2d(a, ix, c, NULL);
}
/* three? */
if (MP_HAS(S_MP_DIV_3) && (b == 3u)) {
return s_mp_div_3(a, c, d);
}
/* no easy answer [c'est la vie]. Just division */
if ((err = mp_init_size(&q, a->used)) != MP_OKAY) {
return err;
}
q.used = a->used;
q.sign = a->sign;
w = 0;
for (ix = a->used; ix --> 0;) {
mp_digit t = 0;
w = (w << (mp_word)MP_DIGIT_BIT) | (mp_word)a->dp[ix];
if (w >= b) {
t = (mp_digit)(w / b);
w -= (mp_word)t * (mp_word)b;
}
q.dp[ix] = t;
}
if (d != NULL) {
*d = (mp_digit)w;
}
if (c != NULL) {
mp_clamp(&q);
mp_exch(&q, c);
}
mp_clear(&q);
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,27 @@
#include "tommath_private.h"
#ifdef MP_DR_IS_MODULUS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* determines if a number is a valid DR modulus */
bool mp_dr_is_modulus(const mp_int *a)
{
int ix;
/* must be at least two digits */
if (a->used < 2) {
return false;
}
/* must be of the form b**k - a [a <= b] so all
* but the first digit must be equal to -1 (mod b).
*/
for (ix = 1; ix < a->used; ix++) {
if (a->dp[ix] != MP_MASK) {
return false;
}
}
return true;
}
#endif

View File

@@ -0,0 +1,68 @@
#include "tommath_private.h"
#ifdef MP_DR_REDUCE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* reduce "x" in place modulo "n" using the Diminished Radix algorithm.
*
* Based on algorithm from the paper
*
* "Generating Efficient Primes for Discrete Log Cryptosystems"
* Chae Hoon Lim, Pil Joong Lee,
* POSTECH Information Research Laboratories
*
* The modulus must be of a special format [see manual]
*
* Has been modified to use algorithm 7.10 from the LTM book instead
*
* Input x must be in the range 0 <= x <= (n-1)**2
*/
mp_err mp_dr_reduce(mp_int *x, const mp_int *n, mp_digit k)
{
mp_err err;
/* m = digits in modulus */
int m = n->used;
/* ensure that "x" has at least 2m digits */
if ((err = mp_grow(x, m + m)) != MP_OKAY) {
return err;
}
/* top of loop, this is where the code resumes if
* another reduction pass is required.
*/
for (;;) {
int i;
mp_digit mu = 0;
/* compute (x mod B**m) + k * [x/B**m] inline and inplace */
for (i = 0; i < m; i++) {
mp_word r = ((mp_word)x->dp[i + m] * (mp_word)k) + x->dp[i] + mu;
x->dp[i] = (mp_digit)(r & MP_MASK);
mu = (mp_digit)(r >> ((mp_word)MP_DIGIT_BIT));
}
/* set final carry */
x->dp[i] = mu;
/* zero words above m */
s_mp_zero_digs(x->dp + m + 1, (x->used - m) - 1);
/* clamp, sub and return */
mp_clamp(x);
/* if x >= n then subtract and reduce again
* Each successive "recursion" makes the input smaller and smaller.
*/
if (mp_cmp_mag(x, n) == MP_LT) {
break;
}
if ((err = s_mp_sub(x, n, x)) != MP_OKAY) {
return err;
}
}
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,15 @@
#include "tommath_private.h"
#ifdef MP_DR_SETUP_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* determines the setup value */
void mp_dr_setup(const mp_int *a, mp_digit *d)
{
/* the casts are required if MP_DIGIT_BIT is one less than
* the number of bits in a mp_digit [e.g. MP_DIGIT_BIT==31]
*/
*d = (mp_digit)(((mp_word)1 << (mp_word)MP_DIGIT_BIT) - (mp_word)a->dp[0]);
}
#endif

View File

@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_ERROR_TO_STRING_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* return a char * string for a given code */
const char *mp_error_to_string(mp_err code)
{
switch (code) {
case MP_OKAY:
return "Successful";
case MP_ERR:
return "Unknown error";
case MP_MEM:
return "Out of heap";
case MP_VAL:
return "Value out of range";
case MP_ITER:
return "Max. iterations reached";
case MP_BUF:
return "Buffer overflow";
case MP_OVF:
return "Integer overflow";
default:
return "Invalid error code";
}
}
#endif

13
src/libtommath/mp_exch.c Normal file
View File

@@ -0,0 +1,13 @@
#include "tommath_private.h"
#ifdef MP_EXCH_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* swap the elements of two integers, for cases where you can't simply swap the
* mp_int pointers around
*/
void mp_exch(mp_int *a, mp_int *b)
{
MP_EXCH(mp_int, *a, *b);
}
#endif

View File

@@ -0,0 +1,43 @@
#include "tommath_private.h"
#ifdef MP_EXPT_N_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* calculate c = a**b using a square-multiply algorithm */
mp_err mp_expt_n(const mp_int *a, int b, mp_int *c)
{
mp_err err;
mp_int g;
if ((err = mp_init_copy(&g, a)) != MP_OKAY) {
return err;
}
/* set initial result */
mp_set(c, 1uL);
while (b > 0) {
/* if the bit is set multiply */
if ((b & 1) != 0) {
if ((err = mp_mul(c, &g, c)) != MP_OKAY) {
goto LBL_ERR;
}
}
/* square */
if (b > 1) {
if ((err = mp_sqr(&g, &g)) != MP_OKAY) {
goto LBL_ERR;
}
}
/* shift to next bit */
b >>= 1;
}
LBL_ERR:
mp_clear(&g);
return err;
}
#endif

View File

@@ -0,0 +1,78 @@
#include "tommath_private.h"
#ifdef MP_EXPTMOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* this is a shell function that calls either the normal or Montgomery
* exptmod functions. Originally the call to the montgomery code was
* embedded in the normal function but that wasted alot of stack space
* for nothing (since 99% of the time the Montgomery code would be called)
*/
mp_err mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y)
{
int dr;
/* modulus P must be positive */
if (mp_isneg(P)) {
return MP_VAL;
}
/* if exponent X is negative we have to recurse */
if (mp_isneg(X)) {
mp_int tmpG, tmpX;
mp_err err;
if (!MP_HAS(MP_INVMOD)) {
return MP_VAL;
}
if ((err = mp_init_multi(&tmpG, &tmpX, NULL)) != MP_OKAY) {
return err;
}
/* first compute 1/G mod P */
if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
goto LBL_ERR;
}
/* now get |X| */
if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
goto LBL_ERR;
}
/* and now compute (1/G)**|X| instead of G**X [X < 0] */
err = mp_exptmod(&tmpG, &tmpX, P, Y);
LBL_ERR:
mp_clear_multi(&tmpG, &tmpX, NULL);
return err;
}
/* modified diminished radix reduction */
if (MP_HAS(MP_REDUCE_IS_2K_L) && MP_HAS(MP_REDUCE_2K_L) && MP_HAS(S_MP_EXPTMOD) &&
mp_reduce_is_2k_l(P)) {
return s_mp_exptmod(G, X, P, Y, 1);
}
/* is it a DR modulus? default to no */
dr = (MP_HAS(MP_DR_IS_MODULUS) && mp_dr_is_modulus(P)) ? 1 : 0;
/* if not, is it a unrestricted DR modulus? */
if (MP_HAS(MP_REDUCE_IS_2K) && (dr == 0)) {
dr = (mp_reduce_is_2k(P)) ? 2 : 0;
}
/* if the modulus is odd or dr != 0 use the montgomery method */
if (MP_HAS(S_MP_EXPTMOD_FAST) && (mp_isodd(P) || (dr != 0))) {
return s_mp_exptmod_fast(G, X, P, Y, dr);
}
/* otherwise use the generic Barrett reduction technique */
if (MP_HAS(S_MP_EXPTMOD)) {
return s_mp_exptmod(G, X, P, Y, 0);
}
/* no exptmod for evens */
return MP_VAL;
}
#endif

View File

@@ -0,0 +1,72 @@
#include "tommath_private.h"
#ifdef MP_EXTEUCLID_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* Extended euclidean algorithm of (a, b) produces
a*u1 + b*u2 = u3
*/
mp_err mp_exteuclid(const mp_int *a, const mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3)
{
mp_int u1, u2, u3, v1, v2, v3, t1, t2, t3, q, tmp;
mp_err err;
if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) {
return err;
}
/* initialize, (u1,u2,u3) = (1,0,a) */
mp_set(&u1, 1uL);
if ((err = mp_copy(a, &u3)) != MP_OKAY) goto LBL_ERR;
/* initialize, (v1,v2,v3) = (0,1,b) */
mp_set(&v2, 1uL);
if ((err = mp_copy(b, &v3)) != MP_OKAY) goto LBL_ERR;
/* loop while v3 != 0 */
while (!mp_iszero(&v3)) {
/* q = u3/v3 */
if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) goto LBL_ERR;
/* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */
if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) goto LBL_ERR;
/* (u1,u2,u3) = (v1,v2,v3) */
if ((err = mp_copy(&v1, &u1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&v2, &u2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&v3, &u3)) != MP_OKAY) goto LBL_ERR;
/* (v1,v2,v3) = (t1,t2,t3) */
if ((err = mp_copy(&t1, &v1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&t2, &v2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&t3, &v3)) != MP_OKAY) goto LBL_ERR;
}
/* make sure U3 >= 0 */
if (mp_isneg(&u3)) {
if ((err = mp_neg(&u1, &u1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_neg(&u2, &u2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_neg(&u3, &u3)) != MP_OKAY) goto LBL_ERR;
}
/* copy result out */
if (U1 != NULL) {
mp_exch(U1, &u1);
}
if (U2 != NULL) {
mp_exch(U2, &u2);
}
if (U3 != NULL) {
mp_exch(U3, &u3);
}
LBL_ERR:
mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL);
return err;
}
#endif

66
src/libtommath/mp_fread.c Normal file
View File

@@ -0,0 +1,66 @@
#include "tommath_private.h"
#ifdef MP_FREAD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#ifndef MP_NO_FILE
/* read a bigint from a file stream in ASCII */
mp_err mp_fread(mp_int *a, int radix, FILE *stream)
{
mp_err err;
mp_sign sign = MP_ZPOS;
int ch;
/* make sure the radix is ok */
if ((radix < 2) || (radix > 64)) {
return MP_VAL;
}
/* if first digit is - then set negative */
ch = fgetc(stream);
if (ch == (int)'-') {
sign = MP_NEG;
ch = fgetc(stream);
}
/* no digits, return error */
if (ch == EOF) {
return MP_ERR;
}
/* clear a */
mp_zero(a);
do {
uint8_t y;
unsigned pos;
ch = (radix <= 36) ? MP_TOUPPER(ch) : ch;
pos = (unsigned)(ch - (int)'+');
if (MP_RADIX_MAP_REVERSE_SIZE <= pos) {
break;
}
y = s_mp_radix_map_reverse[pos];
if (y >= radix) {
break;
}
/* shift up and add */
if ((err = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) {
return err;
}
if ((err = mp_add_d(a, y, a)) != MP_OKAY) {
return err;
}
} while ((ch = fgetc(stream)) != EOF);
if (!mp_iszero(a)) {
a->sign = sign;
}
return MP_OKAY;
}
#endif
#endif

View File

@@ -0,0 +1,21 @@
#include "tommath_private.h"
#ifdef MP_FROM_SBIN_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* read signed bin, big endian, first byte is 0==positive or 1==negative */
mp_err mp_from_sbin(mp_int *a, const uint8_t *buf, size_t size)
{
mp_err err;
/* read magnitude */
if ((err = mp_from_ubin(a, buf + 1, size - 1u)) != MP_OKAY) {
return err;
}
/* first byte is 0 for positive, non-zero for negative */
a->sign = (buf[0] != (uint8_t)0) ? MP_NEG : MP_ZPOS;
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,30 @@
#include "tommath_private.h"
#ifdef MP_FROM_UBIN_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* reads a uint8_t array, assumes the msb is stored first [big endian] */
mp_err mp_from_ubin(mp_int *a, const uint8_t *buf, size_t size)
{
mp_err err;
/* make sure there are at least two digits */
if ((err = mp_grow(a, 2)) != MP_OKAY) {
return err;
}
/* zero the int */
mp_zero(a);
/* read the bytes in */
while (size-- > 0u) {
if ((err = mp_mul_2d(a, 8, a)) != MP_OKAY) {
return err;
}
a->dp[0] |= *buf++;
a->used += 1;
}
mp_clamp(a);
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,33 @@
#include "tommath_private.h"
#ifdef MP_FWRITE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#ifndef MP_NO_FILE
mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream)
{
char *buf;
mp_err err;
size_t size, written;
if ((err = mp_radix_size_overestimate(a, radix, &size)) != MP_OKAY) {
return err;
}
buf = (char *) MP_MALLOC(size);
if (buf == NULL) {
return MP_MEM;
}
if ((err = mp_to_radix(a, buf, size, &written, radix)) == MP_OKAY) {
if (fwrite(buf, written, 1uL, stream) != 1uL) {
err = MP_ERR;
}
}
MP_FREE_BUF(buf, size);
return err;
}
#endif
#endif

92
src/libtommath/mp_gcd.c Normal file
View File

@@ -0,0 +1,92 @@
#include "tommath_private.h"
#ifdef MP_GCD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* Greatest Common Divisor using the binary method */
mp_err mp_gcd(const mp_int *a, const mp_int *b, mp_int *c)
{
mp_int u, v;
int k, u_lsb, v_lsb;
mp_err err;
/* either zero than gcd is the largest */
if (mp_iszero(a)) {
return mp_abs(b, c);
}
if (mp_iszero(b)) {
return mp_abs(a, c);
}
/* get copies of a and b we can modify */
if ((err = mp_init_copy(&u, a)) != MP_OKAY) {
return err;
}
if ((err = mp_init_copy(&v, b)) != MP_OKAY) {
goto LBL_U;
}
/* must be positive for the remainder of the algorithm */
u.sign = v.sign = MP_ZPOS;
/* B1. Find the common power of two for u and v */
u_lsb = mp_cnt_lsb(&u);
v_lsb = mp_cnt_lsb(&v);
k = MP_MIN(u_lsb, v_lsb);
if (k > 0) {
/* divide the power of two out */
if ((err = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) {
goto LBL_V;
}
if ((err = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
/* divide any remaining factors of two out */
if (u_lsb != k) {
if ((err = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
if (v_lsb != k) {
if ((err = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
while (!mp_iszero(&v)) {
/* make sure v is the largest */
if (mp_cmp_mag(&u, &v) == MP_GT) {
/* swap u and v to make sure v is >= u */
mp_exch(&u, &v);
}
/* subtract smallest from largest */
if ((err = s_mp_sub(&v, &u, &v)) != MP_OKAY) {
goto LBL_V;
}
/* Divide out all factors of two */
if ((err = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
/* multiply by 2**k which we divided out at the beginning */
if ((err = mp_mul_2d(&u, k, c)) != MP_OKAY) {
goto LBL_V;
}
c->sign = MP_ZPOS;
err = MP_OKAY;
LBL_V:
mp_clear(&u);
LBL_U:
mp_clear(&v);
return err;
}
#endif

View File

@@ -0,0 +1,18 @@
#include "tommath_private.h"
#ifdef MP_GET_DOUBLE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
double mp_get_double(const mp_int *a)
{
int i;
double d = 0.0, fac = 1.0;
for (i = 0; i < MP_DIGIT_BIT; ++i) {
fac *= 2.0;
}
for (i = a->used; i --> 0;) {
d = (d * fac) + (double)a->dp[i];
}
return mp_isneg(a) ? -d : d;
}
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_I32_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_SIGNED(mp_get_i32, mp_get_mag_u32, int32_t, uint32_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_I64_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_SIGNED(mp_get_i64, mp_get_mag_u64, int64_t, uint64_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_L_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_SIGNED(mp_get_l, mp_get_mag_ul, long, unsigned long)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_MAG_U32_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_MAG(mp_get_mag_u32, uint32_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_MAG_U64_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_MAG(mp_get_mag_u64, uint64_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_MAG_UL_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_MAG(mp_get_mag_ul, unsigned long)
#endif

40
src/libtommath/mp_grow.c Normal file
View File

@@ -0,0 +1,40 @@
#include "tommath_private.h"
#ifdef MP_GROW_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* grow as required */
mp_err mp_grow(mp_int *a, int size)
{
/* if the alloc size is smaller alloc more ram */
if (a->alloc < size) {
mp_digit *dp;
if (size > MP_MAX_DIGIT_COUNT) {
return MP_OVF;
}
/* reallocate the array a->dp
*
* We store the return in a temporary variable
* in case the operation failed we don't want
* to overwrite the dp member of a.
*/
dp = (mp_digit *) MP_REALLOC(a->dp,
(size_t)a->alloc * sizeof(mp_digit),
(size_t)size * sizeof(mp_digit));
if (dp == NULL) {
/* reallocation failed but "a" is still valid [can be freed] */
return MP_MEM;
}
/* reallocation succeeded so set a->dp */
a->dp = dp;
/* zero excess digits */
s_mp_zero_digs(a->dp + a->alloc, size - a->alloc);
a->alloc = size;
}
return MP_OKAY;
}
#endif

23
src/libtommath/mp_init.c Normal file
View File

@@ -0,0 +1,23 @@
#include "tommath_private.h"
#ifdef MP_INIT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* init a new mp_int */
mp_err mp_init(mp_int *a)
{
/* allocate memory required and clear it */
a->dp = (mp_digit *) MP_CALLOC((size_t)MP_DEFAULT_DIGIT_COUNT, sizeof(mp_digit));
if (a->dp == NULL) {
return MP_MEM;
}
/* set the used to zero, allocated digits to the default precision
* and sign to positive */
a->used = 0;
a->alloc = MP_DEFAULT_DIGIT_COUNT;
a->sign = MP_ZPOS;
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,21 @@
#include "tommath_private.h"
#ifdef MP_INIT_COPY_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* creates "a" then copies b into it */
mp_err mp_init_copy(mp_int *a, const mp_int *b)
{
mp_err err;
if ((err = mp_init_size(a, b->used)) != MP_OKAY) {
return err;
}
if ((err = mp_copy(b, a)) != MP_OKAY) {
mp_clear(a);
}
return err;
}
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_I32_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_i32, mp_set_i32, int32_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_I64_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_i64, mp_set_i64, int64_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_L_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_l, mp_set_l, long)
#endif

View File

@@ -0,0 +1,41 @@
#include "tommath_private.h"
#ifdef MP_INIT_MULTI_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include <stdarg.h>
mp_err mp_init_multi(mp_int *mp, ...)
{
mp_err err = MP_OKAY;
int n = 0; /* Number of ok inits */
mp_int *cur_arg = mp;
va_list args;
va_start(args, mp); /* init args to next argument from caller */
while (cur_arg != NULL) {
err = mp_init(cur_arg);
if (err != MP_OKAY) {
/* Oops - error! Back-track and mp_clear what we already
succeeded in init-ing, then return error.
*/
va_list clean_args;
/* now start cleaning up */
cur_arg = mp;
va_start(clean_args, mp);
while (n-- != 0) {
mp_clear(cur_arg);
cur_arg = va_arg(clean_args, mp_int *);
}
va_end(clean_args);
break;
}
n++;
cur_arg = va_arg(args, mp_int *);
}
va_end(args);
return err;
}
#endif

View File

@@ -0,0 +1,16 @@
#include "tommath_private.h"
#ifdef MP_INIT_SET_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* initialize and set a digit */
mp_err mp_init_set(mp_int *a, mp_digit b)
{
mp_err err;
if ((err = mp_init(a)) != MP_OKAY) {
return err;
}
mp_set(a, b);
return err;
}
#endif

View File

@@ -0,0 +1,28 @@
#include "tommath_private.h"
#ifdef MP_INIT_SIZE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* init an mp_init for a given size */
mp_err mp_init_size(mp_int *a, int size)
{
size = MP_MAX(MP_MIN_DIGIT_COUNT, size);
if (size > MP_MAX_DIGIT_COUNT) {
return MP_OVF;
}
/* alloc mem */
a->dp = (mp_digit *) MP_CALLOC((size_t)size, sizeof(mp_digit));
if (a->dp == NULL) {
return MP_MEM;
}
/* set the members */
a->used = 0;
a->alloc = size;
a->sign = MP_ZPOS;
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_U32_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_u32, mp_set_u32, uint32_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_U64_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_u64, mp_set_u64, uint64_t)
#endif

View File

@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_UL_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_ul, mp_set_ul, unsigned long)
#endif

View File

@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_INVMOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* hac 14.61, pp608 */
mp_err mp_invmod(const mp_int *a, const mp_int *b, mp_int *c)
{
/* for all n in N and n > 0, n = 0 mod 1 */
if (!mp_isneg(a) && mp_cmp_d(b, 1uL) == MP_EQ) {
mp_zero(c);
return MP_OKAY;
}
/* b cannot be negative and has to be >1 */
if (mp_isneg(b) || (mp_cmp_d(b, 1uL) != MP_GT)) {
return MP_VAL;
}
/* if the modulus is odd we can use a faster routine instead */
if (MP_HAS(S_MP_INVMOD_ODD) && mp_isodd(b)) {
return s_mp_invmod_odd(a, b, c);
}
return MP_HAS(S_MP_INVMOD)
? s_mp_invmod(a, b, c)
: MP_VAL;
}
#endif

View File

@@ -0,0 +1,93 @@
#include "tommath_private.h"
#ifdef MP_IS_SQUARE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* Check if remainders are possible squares - fast exclude non-squares */
static const char rem_128[128] = {
0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1
};
static const char rem_105[105] = {
0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1,
1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1
};
/* Store non-zero to ret if arg is square, and zero if not */
mp_err mp_is_square(const mp_int *arg, bool *ret)
{
mp_err err;
mp_digit c;
mp_int t;
uint32_t r;
/* Default to Non-square :) */
*ret = false;
if (mp_isneg(arg)) {
return MP_VAL;
}
if (mp_iszero(arg)) {
return MP_OKAY;
}
/* First check mod 128 (suppose that MP_DIGIT_BIT is at least 7) */
if (rem_128[127u & arg->dp[0]] == (char)1) {
return MP_OKAY;
}
/* Next check mod 105 (3*5*7) */
if ((err = mp_mod_d(arg, 105uL, &c)) != MP_OKAY) {
return err;
}
if (rem_105[c] == (char)1) {
return MP_OKAY;
}
if ((err = mp_init_u32(&t, 11u*13u*17u*19u*23u*29u*31u)) != MP_OKAY) {
return err;
}
if ((err = mp_mod(arg, &t, &t)) != MP_OKAY) {
goto LBL_ERR;
}
r = mp_get_u32(&t);
/* Check for other prime modules, note it's not an ERROR but we must
* free "t" so the easiest way is to goto LBL_ERR. We know that err
* is already equal to MP_OKAY from the mp_mod call
*/
if (((1uL<<(r%11uL)) & 0x5C4uL) != 0uL) goto LBL_ERR;
if (((1uL<<(r%13uL)) & 0x9E4uL) != 0uL) goto LBL_ERR;
if (((1uL<<(r%17uL)) & 0x5CE8uL) != 0uL) goto LBL_ERR;
if (((1uL<<(r%19uL)) & 0x4F50CuL) != 0uL) goto LBL_ERR;
if (((1uL<<(r%23uL)) & 0x7ACCA0uL) != 0uL) goto LBL_ERR;
if (((1uL<<(r%29uL)) & 0xC2EDD0CuL) != 0uL) goto LBL_ERR;
if (((1uL<<(r%31uL)) & 0x6DE2B848uL) != 0uL) goto LBL_ERR;
/* Final check - is sqr(sqrt(arg)) == arg ? */
if ((err = mp_sqrt(arg, &t)) != MP_OKAY) {
goto LBL_ERR;
}
if ((err = mp_sqr(&t, &t)) != MP_OKAY) {
goto LBL_ERR;
}
*ret = (mp_cmp_mag(&t, arg) == MP_EQ);
LBL_ERR:
mp_clear(&t);
return err;
}
#endif

View File

@@ -0,0 +1,129 @@
#include "tommath_private.h"
#ifdef MP_KRONECKER_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/*
Kronecker symbol (a|p)
Straightforward implementation of algorithm 1.4.10 in
Henri Cohen: "A Course in Computational Algebraic Number Theory"
@book{cohen2013course,
title={A course in computational algebraic number theory},
author={Cohen, Henri},
volume={138},
year={2013},
publisher={Springer Science \& Business Media}
}
*/
mp_err mp_kronecker(const mp_int *a, const mp_int *p, int *c)
{
mp_int a1, p1, r;
mp_err err;
int v, k;
static const char table[] = {0, 1, 0, -1, 0, -1, 0, 1};
if (mp_iszero(p)) {
if ((a->used == 1) && (a->dp[0] == 1u)) {
*c = 1;
} else {
*c = 0;
}
return MP_OKAY;
}
if (mp_iseven(a) && mp_iseven(p)) {
*c = 0;
return MP_OKAY;
}
if ((err = mp_init_copy(&a1, a)) != MP_OKAY) {
return err;
}
if ((err = mp_init_copy(&p1, p)) != MP_OKAY) {
goto LBL_KRON_0;
}
v = mp_cnt_lsb(&p1);
if ((err = mp_div_2d(&p1, v, &p1, NULL)) != MP_OKAY) {
goto LBL_KRON_1;
}
if ((v & 1) == 0) {
k = 1;
} else {
k = table[a->dp[0] & 7u];
}
if (mp_isneg(&p1)) {
p1.sign = MP_ZPOS;
if (mp_isneg(&a1)) {
k = -k;
}
}
if ((err = mp_init(&r)) != MP_OKAY) {
goto LBL_KRON_1;
}
for (;;) {
if (mp_iszero(&a1)) {
if (mp_cmp_d(&p1, 1uL) == MP_EQ) {
*c = k;
goto LBL_KRON;
} else {
*c = 0;
goto LBL_KRON;
}
}
v = mp_cnt_lsb(&a1);
if ((err = mp_div_2d(&a1, v, &a1, NULL)) != MP_OKAY) {
goto LBL_KRON;
}
if ((v & 1) == 1) {
k = k * table[p1.dp[0] & 7u];
}
if (mp_isneg(&a1)) {
/*
* Compute k = (-1)^((a1)*(p1-1)/4) * k
* a1.dp[0] + 1 cannot overflow because the MSB
* of the type mp_digit is not set by definition
*/
if (((a1.dp[0] + 1u) & p1.dp[0] & 2u) != 0u) {
k = -k;
}
} else {
/* compute k = (-1)^((a1-1)*(p1-1)/4) * k */
if ((a1.dp[0] & p1.dp[0] & 2u) != 0u) {
k = -k;
}
}
if ((err = mp_copy(&a1, &r)) != MP_OKAY) {
goto LBL_KRON;
}
r.sign = MP_ZPOS;
if ((err = mp_mod(&p1, &r, &a1)) != MP_OKAY) {
goto LBL_KRON;
}
if ((err = mp_copy(&r, &p1)) != MP_OKAY) {
goto LBL_KRON;
}
}
LBL_KRON:
mp_clear(&r);
LBL_KRON_1:
mp_clear(&p1);
LBL_KRON_0:
mp_clear(&a1);
return err;
}
#endif

44
src/libtommath/mp_lcm.c Normal file
View File

@@ -0,0 +1,44 @@
#include "tommath_private.h"
#ifdef MP_LCM_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* computes least common multiple as |a*b|/(a, b) */
mp_err mp_lcm(const mp_int *a, const mp_int *b, mp_int *c)
{
mp_err err;
mp_int t1, t2;
if ((err = mp_init_multi(&t1, &t2, NULL)) != MP_OKAY) {
return err;
}
/* t1 = get the GCD of the two inputs */
if ((err = mp_gcd(a, b, &t1)) != MP_OKAY) {
goto LBL_T;
}
/* divide the smallest by the GCD */
if (mp_cmp_mag(a, b) == MP_LT) {
/* store quotient in t2 such that t2 * b is the LCM */
if ((err = mp_div(a, &t1, &t2, NULL)) != MP_OKAY) {
goto LBL_T;
}
err = mp_mul(b, &t2, c);
} else {
/* store quotient in t2 such that t2 * a is the LCM */
if ((err = mp_div(b, &t1, &t2, NULL)) != MP_OKAY) {
goto LBL_T;
}
err = mp_mul(a, &t2, c);
}
/* fix the sign to positive */
c->sign = MP_ZPOS;
LBL_T:
mp_clear_multi(&t1, &t2, NULL);
return err;
}
#endif

29
src/libtommath/mp_log_n.c Normal file
View File

@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_LOG_N_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
mp_err mp_log_n(const mp_int *a, int base, int *c)
{
if (mp_isneg(a) || mp_iszero(a) || (base < 2) || (unsigned)base > (unsigned)MP_DIGIT_MAX) {
return MP_VAL;
}
if (MP_HAS(S_MP_LOG_2EXPT) && MP_IS_2EXPT((mp_digit)base)) {
*c = s_mp_log_2expt(a, (mp_digit)base);
return MP_OKAY;
}
if (MP_HAS(S_MP_LOG_D) && (a->used == 1)) {
*c = s_mp_log_d((mp_digit)base, a->dp[0]);
return MP_OKAY;
}
if (MP_HAS(S_MP_LOG)) {
return s_mp_log(a, (mp_digit)base, c);
}
return MP_VAL;
}
#endif

42
src/libtommath/mp_lshd.c Normal file
View File

@@ -0,0 +1,42 @@
#include "tommath_private.h"
#ifdef MP_LSHD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* shift left a certain amount of digits */
mp_err mp_lshd(mp_int *a, int b)
{
mp_err err;
int x;
/* if its less than zero return */
if (b <= 0) {
return MP_OKAY;
}
/* no need to shift 0 around */
if (mp_iszero(a)) {
return MP_OKAY;
}
/* grow to fit the new digits */
if ((err = mp_grow(a, a->used + b)) != MP_OKAY) {
return err;
}
/* increment the used by the shift amount then copy upwards */
a->used += b;
/* much like mp_rshd this is implemented using a sliding window
* except the window goes the otherway around. Copying from
* the bottom to the top. see mp_rshd.c for more info.
*/
for (x = a->used; x --> b;) {
a->dp[x] = a->dp[x - b];
}
/* zero the lower digits */
s_mp_zero_digs(a->dp, b);
return MP_OKAY;
}
#endif

15
src/libtommath/mp_mod.c Normal file
View File

@@ -0,0 +1,15 @@
#include "tommath_private.h"
#ifdef MP_MOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */
mp_err mp_mod(const mp_int *a, const mp_int *b, mp_int *c)
{
mp_err err;
if ((err = mp_div(a, b, NULL, c)) != MP_OKAY) {
return err;
}
return mp_iszero(c) || (c->sign == b->sign) ? MP_OKAY : mp_add(b, c, c);
}
#endif

View File

@@ -0,0 +1,40 @@
#include "tommath_private.h"
#ifdef MP_MOD_2D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* calc a value mod 2**b */
mp_err mp_mod_2d(const mp_int *a, int b, mp_int *c)
{
int x;
mp_err err;
if (b < 0) {
return MP_VAL;
}
if (b == 0) {
mp_zero(c);
return MP_OKAY;
}
/* if the modulus is larger than the value than return */
if (b >= (a->used * MP_DIGIT_BIT)) {
return mp_copy(a, c);
}
if ((err = mp_copy(a, c)) != MP_OKAY) {
return err;
}
/* zero digits above the last digit of the modulus */
x = (b / MP_DIGIT_BIT) + (((b % MP_DIGIT_BIT) == 0) ? 0 : 1);
s_mp_zero_digs(c->dp + x, c->used - x);
/* clear the digit that is not completely outside/inside the modulus */
c->dp[b / MP_DIGIT_BIT] &=
((mp_digit)1 << (mp_digit)(b % MP_DIGIT_BIT)) - (mp_digit)1;
mp_clamp(c);
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,43 @@
#include "tommath_private.h"
#ifdef MP_MONTGOMERY_CALC_NORMALIZATION_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/*
* shifts with subtractions when the result is greater than b.
*
* The method is slightly modified to shift B unconditionally upto just under
* the leading bit of b. This saves alot of multiple precision shifting.
*/
mp_err mp_montgomery_calc_normalization(mp_int *a, const mp_int *b)
{
int x, bits;
mp_err err;
/* how many bits of last digit does b use */
bits = mp_count_bits(b) % MP_DIGIT_BIT;
if (b->used > 1) {
if ((err = mp_2expt(a, ((b->used - 1) * MP_DIGIT_BIT) + bits - 1)) != MP_OKAY) {
return err;
}
} else {
mp_set(a, 1uL);
bits = 1;
}
/* now compute C = A * B mod b */
for (x = bits - 1; x < (int)MP_DIGIT_BIT; x++) {
if ((err = mp_mul_2(a, a)) != MP_OKAY) {
return err;
}
if (mp_cmp_mag(a, b) != MP_LT) {
if ((err = s_mp_sub(a, b, a)) != MP_OKAY) {
return err;
}
}
}
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,89 @@
#include "tommath_private.h"
#ifdef MP_MONTGOMERY_REDUCE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* computes xR**-1 == x (mod N) via Montgomery Reduction */
mp_err mp_montgomery_reduce(mp_int *x, const mp_int *n, mp_digit rho)
{
mp_err err;
int ix, digs;
/* can the fast reduction [comba] method be used?
*
* Note that unlike in mul you're safely allowed *less*
* than the available columns [255 per default] since carries
* are fixed up in the inner loop.
*/
digs = (n->used * 2) + 1;
if ((digs < MP_WARRAY) &&
(x->used <= MP_WARRAY) &&
(n->used < MP_MAX_COMBA)) {
return s_mp_montgomery_reduce_comba(x, n, rho);
}
/* grow the input as required */
if ((err = mp_grow(x, digs)) != MP_OKAY) {
return err;
}
x->used = digs;
for (ix = 0; ix < n->used; ix++) {
int iy;
mp_digit u, mu;
/* mu = ai * rho mod b
*
* The value of rho must be precalculated via
* montgomery_setup() such that
* it equals -1/n0 mod b this allows the
* following inner loop to reduce the
* input one digit at a time
*/
mu = (mp_digit)(((mp_word)x->dp[ix] * (mp_word)rho) & MP_MASK);
/* a = a + mu * m * b**i */
/* Multiply and add in place */
u = 0;
for (iy = 0; iy < n->used; iy++) {
/* compute product and sum */
mp_word r = ((mp_word)mu * (mp_word)n->dp[iy]) +
(mp_word)u + (mp_word)x->dp[ix + iy];
/* get carry */
u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT);
/* fix digit */
x->dp[ix + iy] = (mp_digit)(r & (mp_word)MP_MASK);
}
/* At this point the ix'th digit of x should be zero */
/* propagate carries upwards as required*/
while (u != 0u) {
x->dp[ix + iy] += u;
u = x->dp[ix + iy] >> MP_DIGIT_BIT;
x->dp[ix + iy] &= MP_MASK;
++iy;
}
}
/* at this point the n.used'th least
* significant digits of x are all zero
* which means we can shift x to the
* right by n.used digits and the
* residue is unchanged.
*/
/* x = x/b**n.used */
mp_clamp(x);
mp_rshd(x, n->used);
/* if x >= n then x = x - n */
if (mp_cmp_mag(x, n) != MP_LT) {
return s_mp_sub(x, n, x);
}
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,40 @@
#include "tommath_private.h"
#ifdef MP_MONTGOMERY_SETUP_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* setups the montgomery reduction stuff */
mp_err mp_montgomery_setup(const mp_int *n, mp_digit *rho)
{
mp_digit x, b;
/* fast inversion mod 2**k
*
* Based on the fact that
*
* XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n)
* => 2*X*A - X*X*A*A = 1
* => 2*(1) - (1) = 1
*/
b = n->dp[0];
if ((b & 1u) == 0u) {
return MP_VAL;
}
x = (((b + 2u) & 4u) << 1) + b; /* here x*a==1 mod 2**4 */
x *= 2u - (b * x); /* here x*a==1 mod 2**8 */
x *= 2u - (b * x); /* here x*a==1 mod 2**16 */
#if defined(MP_64BIT) || !(defined(MP_16BIT))
x *= 2u - (b * x); /* here x*a==1 mod 2**32 */
#endif
#ifdef MP_64BIT
x *= 2u - (b * x); /* here x*a==1 mod 2**64 */
#endif
/* rho = -1/m mod b */
*rho = (mp_digit)(((mp_word)1 << (mp_word)MP_DIGIT_BIT) - x) & MP_MASK;
return MP_OKAY;
}
#endif

68
src/libtommath/mp_mul.c Normal file
View File

@@ -0,0 +1,68 @@
#include "tommath_private.h"
#ifdef MP_MUL_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* high level multiplication (handles sign) */
mp_err mp_mul(const mp_int *a, const mp_int *b, mp_int *c)
{
mp_err err;
int min = MP_MIN(a->used, b->used),
max = MP_MAX(a->used, b->used),
digs = a->used + b->used + 1;
bool neg = (a->sign != b->sign);
if ((a == b) &&
MP_HAS(S_MP_SQR_TOOM) && /* use Toom-Cook? */
(a->used >= MP_SQR_TOOM_CUTOFF)) {
err = s_mp_sqr_toom(a, c);
} else if ((a == b) &&
MP_HAS(S_MP_SQR_KARATSUBA) && /* Karatsuba? */
(a->used >= MP_SQR_KARATSUBA_CUTOFF)) {
err = s_mp_sqr_karatsuba(a, c);
} else if ((a == b) &&
MP_HAS(S_MP_SQR_COMBA) && /* can we use the fast comba multiplier? */
(((a->used * 2) + 1) < MP_WARRAY) &&
(a->used < (MP_MAX_COMBA / 2))) {
err = s_mp_sqr_comba(a, c);
} else if ((a == b) &&
MP_HAS(S_MP_SQR)) {
err = s_mp_sqr(a, c);
} else if (MP_HAS(S_MP_MUL_BALANCE) &&
/* Check sizes. The smaller one needs to be larger than the Karatsuba cut-off.
* The bigger one needs to be at least about one MP_MUL_KARATSUBA_CUTOFF bigger
* to make some sense, but it depends on architecture, OS, position of the
* stars... so YMMV.
* Using it to cut the input into slices small enough for s_mp_mul_comba
* was actually slower on the author's machine, but YMMV.
*/
(min >= MP_MUL_KARATSUBA_CUTOFF) &&
((max / 2) >= MP_MUL_KARATSUBA_CUTOFF) &&
/* Not much effect was observed below a ratio of 1:2, but again: YMMV. */
(max >= (2 * min))) {
err = s_mp_mul_balance(a,b,c);
} else if (MP_HAS(S_MP_MUL_TOOM) &&
(min >= MP_MUL_TOOM_CUTOFF)) {
err = s_mp_mul_toom(a, b, c);
} else if (MP_HAS(S_MP_MUL_KARATSUBA) &&
(min >= MP_MUL_KARATSUBA_CUTOFF)) {
err = s_mp_mul_karatsuba(a, b, c);
} else if (MP_HAS(S_MP_MUL_COMBA) &&
/* can we use the fast multiplier?
*
* The fast multiplier can be used if the output will
* have less than MP_WARRAY digits and the number of
* digits won't affect carry propagation
*/
(digs < MP_WARRAY) &&
(min <= MP_MAX_COMBA)) {
err = s_mp_mul_comba(a, b, c, digs);
} else if (MP_HAS(S_MP_MUL)) {
err = s_mp_mul(a, b, c, digs);
} else {
err = MP_VAL;
}
c->sign = ((c->used > 0) && neg) ? MP_NEG : MP_ZPOS;
return err;
}
#endif

53
src/libtommath/mp_mul_2.c Normal file
View File

@@ -0,0 +1,53 @@
#include "tommath_private.h"
#ifdef MP_MUL_2_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = a*2 */
mp_err mp_mul_2(const mp_int *a, mp_int *b)
{
mp_err err;
int x, oldused;
mp_digit r;
/* grow to accomodate result */
if ((err = mp_grow(b, a->used + 1)) != MP_OKAY) {
return err;
}
oldused = b->used;
b->used = a->used;
/* carry */
r = 0;
for (x = 0; x < a->used; x++) {
/* get what will be the *next* carry bit from the
* MSB of the current digit
*/
mp_digit rr = a->dp[x] >> (mp_digit)(MP_DIGIT_BIT - 1);
/* now shift up this digit, add in the carry [from the previous] */
b->dp[x] = ((a->dp[x] << 1uL) | r) & MP_MASK;
/* copy the carry that would be from the source
* digit into the next iteration
*/
r = rr;
}
/* new leading digit? */
if (r != 0u) {
/* add a MSB which is always 1 at this point */
b->dp[b->used++] = 1;
}
/* now zero any excess digits on the destination
* that we didn't write to
*/
s_mp_zero_digs(b->dp + b->used, oldused - b->used);
b->sign = a->sign;
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,63 @@
#include "tommath_private.h"
#ifdef MP_MUL_2D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* shift left by a certain bit count */
mp_err mp_mul_2d(const mp_int *a, int b, mp_int *c)
{
mp_err err;
if (b < 0) {
return MP_VAL;
}
if ((err = mp_copy(a, c)) != MP_OKAY) {
return err;
}
if ((err = mp_grow(c, c->used + (b / MP_DIGIT_BIT) + 1)) != MP_OKAY) {
return err;
}
/* shift by as many digits in the bit count */
if (b >= MP_DIGIT_BIT) {
if ((err = mp_lshd(c, b / MP_DIGIT_BIT)) != MP_OKAY) {
return err;
}
}
/* shift any bit count < MP_DIGIT_BIT */
b %= MP_DIGIT_BIT;
if (b != 0u) {
mp_digit shift, mask, r;
int x;
/* bitmask for carries */
mask = ((mp_digit)1 << b) - (mp_digit)1;
/* shift for msbs */
shift = (mp_digit)(MP_DIGIT_BIT - b);
/* carry */
r = 0;
for (x = 0; x < c->used; x++) {
/* get the higher bits of the current word */
mp_digit rr = (c->dp[x] >> shift) & mask;
/* shift the current word and OR in the carry */
c->dp[x] = ((c->dp[x] << b) | r) & MP_MASK;
/* set the carry to the carry bits of the current word */
r = rr;
}
/* set final carry */
if (r != 0u) {
c->dp[(c->used)++] = r;
}
}
mp_clamp(c);
return MP_OKAY;
}
#endif

68
src/libtommath/mp_mul_d.c Normal file
View File

@@ -0,0 +1,68 @@
#include "tommath_private.h"
#ifdef MP_MUL_D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* multiply by a digit */
mp_err mp_mul_d(const mp_int *a, mp_digit b, mp_int *c)
{
mp_digit u;
mp_err err;
int ix, oldused;
if (b == 1u) {
return mp_copy(a, c);
}
/* power of two ? */
if (MP_HAS(MP_MUL_2) && (b == 2u)) {
return mp_mul_2(a, c);
}
if (MP_HAS(MP_MUL_2D) && MP_IS_2EXPT(b)) {
ix = 1;
while ((ix < MP_DIGIT_BIT) && (b != (((mp_digit)1)<<ix))) {
ix++;
}
return mp_mul_2d(a, ix, c);
}
/* make sure c is big enough to hold a*b */
if ((err = mp_grow(c, a->used + 1)) != MP_OKAY) {
return err;
}
/* get the original destinations used count */
oldused = c->used;
/* set the sign */
c->sign = a->sign;
/* zero carry */
u = 0;
/* compute columns */
for (ix = 0; ix < a->used; ix++) {
/* compute product and carry sum for this term */
mp_word r = (mp_word)u + ((mp_word)a->dp[ix] * (mp_word)b);
/* mask off higher bits to get a single digit */
c->dp[ix] = (mp_digit)(r & (mp_word)MP_MASK);
/* send carry into next iteration */
u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT);
}
/* store final carry [if any] and increment ix offset */
c->dp[ix] = u;
/* set used count */
c->used = a->used + 1;
/* now zero digits above the top */
s_mp_zero_digs(c->dp + c->used, oldused - c->used);
mp_clamp(c);
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,15 @@
#include "tommath_private.h"
#ifdef MP_MULMOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* d = a * b (mod c) */
mp_err mp_mulmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d)
{
mp_err err;
if ((err = mp_mul(a, b, d)) != MP_OKAY) {
return err;
}
return mp_mod(d, c, d);
}
#endif

18
src/libtommath/mp_neg.c Normal file
View File

@@ -0,0 +1,18 @@
#include "tommath_private.h"
#ifdef MP_NEG_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = -a */
mp_err mp_neg(const mp_int *a, mp_int *b)
{
mp_err err;
if ((err = mp_copy(a, b)) != MP_OKAY) {
return err;
}
b->sign = ((!mp_iszero(b) && !mp_isneg(b)) ? MP_NEG : MP_ZPOS);
return MP_OKAY;
}
#endif

54
src/libtommath/mp_or.c Normal file
View File

@@ -0,0 +1,54 @@
#include "tommath_private.h"
#ifdef MP_OR_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* two complement or */
mp_err mp_or(const mp_int *a, const mp_int *b, mp_int *c)
{
int used = MP_MAX(a->used, b->used) + 1, i;
mp_err err;
mp_digit ac = 1, bc = 1, cc = 1;
bool neg = (mp_isneg(a) || mp_isneg(b));
if ((err = mp_grow(c, used)) != MP_OKAY) {
return err;
}
for (i = 0; i < used; i++) {
mp_digit x, y;
/* convert to two complement if negative */
if (mp_isneg(a)) {
ac += (i >= a->used) ? MP_MASK : (~a->dp[i] & MP_MASK);
x = ac & MP_MASK;
ac >>= MP_DIGIT_BIT;
} else {
x = (i >= a->used) ? 0uL : a->dp[i];
}
/* convert to two complement if negative */
if (mp_isneg(b)) {
bc += (i >= b->used) ? MP_MASK : (~b->dp[i] & MP_MASK);
y = bc & MP_MASK;
bc >>= MP_DIGIT_BIT;
} else {
y = (i >= b->used) ? 0uL : b->dp[i];
}
c->dp[i] = x | y;
/* convert to to sign-magnitude if negative */
if (neg) {
cc += ~c->dp[i] & MP_MASK;
c->dp[i] = cc & MP_MASK;
cc >>= MP_DIGIT_BIT;
}
}
c->used = used;
c->sign = (neg ? MP_NEG : MP_ZPOS);
mp_clamp(c);
return MP_OKAY;
}
#endif

69
src/libtommath/mp_pack.c Normal file
View File

@@ -0,0 +1,69 @@
#include "tommath_private.h"
#ifdef MP_PACK_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* based on gmp's mpz_export.
* see http://gmplib.org/manual/Integer-Import-and-Export.html
*/
mp_err mp_pack(void *rop, size_t maxcount, size_t *written, mp_order order, size_t size,
mp_endian endian, size_t nails, const mp_int *op)
{
mp_err err;
size_t odd_nails, nail_bytes, i, j, count;
uint8_t odd_nail_mask;
mp_int t;
count = mp_pack_count(op, nails, size);
if (count > maxcount) {
return MP_BUF;
}
if ((err = mp_init_copy(&t, op)) != MP_OKAY) {
return err;
}
if (endian == MP_NATIVE_ENDIAN) {
MP_GET_ENDIANNESS(endian);
}
odd_nails = (nails % 8u);
odd_nail_mask = 0xff;
for (i = 0u; i < odd_nails; ++i) {
odd_nail_mask ^= (uint8_t)(1u << (7u - i));
}
nail_bytes = nails / 8u;
for (i = 0u; i < count; ++i) {
for (j = 0u; j < size; ++j) {
uint8_t *byte = (uint8_t *)rop +
(((order == MP_LSB_FIRST) ? i : ((count - 1u) - i)) * size) +
((endian == MP_LITTLE_ENDIAN) ? j : ((size - 1u) - j));
if (j >= (size - nail_bytes)) {
*byte = 0;
continue;
}
*byte = (uint8_t)((j == ((size - nail_bytes) - 1u)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFFuL));
if ((err = mp_div_2d(&t, (j == ((size - nail_bytes) - 1u)) ? (int)(8u - odd_nails) : 8, &t, NULL)) != MP_OKAY) {
goto LBL_ERR;
}
}
}
if (written != NULL) {
*written = count;
}
err = MP_OKAY;
LBL_ERR:
mp_clear(&t);
return err;
}
#endif

View File

@@ -0,0 +1,12 @@
#include "tommath_private.h"
#ifdef MP_PACK_COUNT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
size_t mp_pack_count(const mp_int *a, size_t nails, size_t size)
{
size_t bits = (size_t)mp_count_bits(a);
return ((bits / ((size * 8u) - nails)) + (((bits % ((size * 8u) - nails)) != 0u) ? 1u : 0u));
}
#endif

View File

@@ -0,0 +1,41 @@
#include "tommath_private.h"
#ifdef MP_PRIME_FERMAT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* performs one Fermat test.
*
* If "a" were prime then b**a == b (mod a) since the order of
* the multiplicative sub-group would be phi(a) = a-1. That means
* it would be the same as b**(a mod (a-1)) == b**1 == b (mod a).
*
* Sets result to 1 if the congruence holds, or zero otherwise.
*/
mp_err mp_prime_fermat(const mp_int *a, const mp_int *b, bool *result)
{
mp_int t;
mp_err err;
/* ensure b > 1 */
if (mp_cmp_d(b, 1uL) != MP_GT) {
return MP_VAL;
}
/* init t */
if ((err = mp_init(&t)) != MP_OKAY) {
return err;
}
/* compute t = b**a mod a */
if ((err = mp_exptmod(b, a, a, &t)) != MP_OKAY) {
goto LBL_ERR;
}
/* is it equal to b? */
*result = mp_cmp(&t, b) == MP_EQ;
LBL_ERR:
mp_clear(&t);
return err;
}
#endif

View File

@@ -0,0 +1,127 @@
#include "tommath_private.h"
#ifdef MP_PRIME_FROBENIUS_UNDERWOOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/*
* See file mp_prime_is_prime.c or the documentation in doc/bn.tex for the details
*/
#ifndef LTM_USE_ONLY_MR
/*
* floor of positive solution of
* (2^16)-1 = (a+4)*(2*a+5)
* TODO: Both values are smaller than N^(1/4), would have to use a bigint
* for a instead but any a biger than about 120 are already so rare that
* it is possible to ignore them and still get enough pseudoprimes.
* But it is still a restriction of the set of available pseudoprimes
* which makes this implementation less secure if used stand-alone.
*/
#define LTM_FROBENIUS_UNDERWOOD_A 32764
mp_err mp_prime_frobenius_underwood(const mp_int *N, bool *result)
{
mp_int T1z, T2z, Np1z, sz, tz;
int a, ap2, i;
mp_err err;
if ((err = mp_init_multi(&T1z, &T2z, &Np1z, &sz, &tz, NULL)) != MP_OKAY) {
return err;
}
for (a = 0; a < LTM_FROBENIUS_UNDERWOOD_A; a++) {
int j;
/* TODO: That's ugly! No, really, it is! */
if ((a==2) || (a==4) || (a==7) || (a==8) || (a==10) ||
(a==14) || (a==18) || (a==23) || (a==26) || (a==28)) {
continue;
}
mp_set_i32(&T1z, (int32_t)((a * a) - 4));
if ((err = mp_kronecker(&T1z, N, &j)) != MP_OKAY) goto LBL_END;
if (j == -1) {
break;
}
if (j == 0) {
/* composite */
*result = false;
goto LBL_END;
}
}
/* Tell it a composite and set return value accordingly */
if (a >= LTM_FROBENIUS_UNDERWOOD_A) {
err = MP_ITER;
goto LBL_END;
}
/* Composite if N and (a+4)*(2*a+5) are not coprime */
mp_set_u32(&T1z, (uint32_t)((a+4)*((2*a)+5)));
if ((err = mp_gcd(N, &T1z, &T1z)) != MP_OKAY) goto LBL_END;
if (!((T1z.used == 1) && (T1z.dp[0] == 1u))) {
/* composite */
*result = false;
goto LBL_END;
}
ap2 = a + 2;
if ((err = mp_add_d(N, 1uL, &Np1z)) != MP_OKAY) goto LBL_END;
mp_set(&sz, 1uL);
mp_set(&tz, 2uL);
for (i = mp_count_bits(&Np1z) - 2; i >= 0; i--) {
/*
* temp = (sz*(a*sz+2*tz))%N;
* tz = ((tz-sz)*(tz+sz))%N;
* sz = temp;
*/
if ((err = mp_mul_2(&tz, &T2z)) != MP_OKAY) goto LBL_END;
/* a = 0 at about 50% of the cases (non-square and odd input) */
if (a != 0) {
if ((err = mp_mul_d(&sz, (mp_digit)a, &T1z)) != MP_OKAY) goto LBL_END;
if ((err = mp_add(&T1z, &T2z, &T2z)) != MP_OKAY) goto LBL_END;
}
if ((err = mp_mul(&T2z, &sz, &T1z)) != MP_OKAY) goto LBL_END;
if ((err = mp_sub(&tz, &sz, &T2z)) != MP_OKAY) goto LBL_END;
if ((err = mp_add(&sz, &tz, &sz)) != MP_OKAY) goto LBL_END;
if ((err = mp_mul(&sz, &T2z, &tz)) != MP_OKAY) goto LBL_END;
if ((err = mp_mod(&tz, N, &tz)) != MP_OKAY) goto LBL_END;
if ((err = mp_mod(&T1z, N, &sz)) != MP_OKAY) goto LBL_END;
if (s_mp_get_bit(&Np1z, i)) {
/*
* temp = (a+2) * sz + tz
* tz = 2 * tz - sz
* sz = temp
*/
if (a == 0) {
if ((err = mp_mul_2(&sz, &T1z)) != MP_OKAY) goto LBL_END;
} else {
if ((err = mp_mul_d(&sz, (mp_digit)ap2, &T1z)) != MP_OKAY) goto LBL_END;
}
if ((err = mp_add(&T1z, &tz, &T1z)) != MP_OKAY) goto LBL_END;
if ((err = mp_mul_2(&tz, &T2z)) != MP_OKAY) goto LBL_END;
if ((err = mp_sub(&T2z, &sz, &tz)) != MP_OKAY) goto LBL_END;
mp_exch(&sz, &T1z);
}
}
mp_set_u32(&T1z, (uint32_t)((2 * a) + 5));
if ((err = mp_mod(&T1z, N, &T1z)) != MP_OKAY) goto LBL_END;
*result = mp_iszero(&sz) && (mp_cmp(&tz, &T1z) == MP_EQ);
LBL_END:
mp_clear_multi(&tz, &sz, &Np1z, &T2z, &T1z, NULL);
return err;
}
#endif
#endif

View File

@@ -0,0 +1,282 @@
#include "tommath_private.h"
#ifdef MP_PRIME_IS_PRIME_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* portable integer log of two with small footprint */
static unsigned int s_floor_ilog2(int value)
{
unsigned int r = 0;
while ((value >>= 1) != 0) {
r++;
}
return r;
}
mp_err mp_prime_is_prime(const mp_int *a, int t, bool *result)
{
mp_int b;
int ix;
bool res;
mp_err err;
/* default to no */
*result = false;
/* Some shortcuts */
/* N > 3 */
if (a->used == 1) {
if ((a->dp[0] == 0u) || (a->dp[0] == 1u)) {
*result = false;
return MP_OKAY;
}
if (a->dp[0] == 2u) {
*result = true;
return MP_OKAY;
}
}
/* N must be odd */
if (mp_iseven(a)) {
return MP_OKAY;
}
/* N is not a perfect square: floor(sqrt(N))^2 != N */
if ((err = mp_is_square(a, &res)) != MP_OKAY) {
return err;
}
if (res) {
return MP_OKAY;
}
/* is the input equal to one of the primes in the table? */
for (ix = 0; ix < MP_PRIME_TAB_SIZE; ix++) {
if (mp_cmp_d(a, s_mp_prime_tab[ix]) == MP_EQ) {
*result = true;
return MP_OKAY;
}
}
/* first perform trial division */
if ((err = s_mp_prime_is_divisible(a, &res)) != MP_OKAY) {
return err;
}
/* return if it was trivially divisible */
if (res) {
return MP_OKAY;
}
/*
Run the Miller-Rabin test with base 2 for the BPSW test.
*/
if ((err = mp_init_set(&b, 2uL)) != MP_OKAY) {
return err;
}
if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
goto LBL_B;
}
if (!res) {
goto LBL_B;
}
/*
Rumours have it that Mathematica does a second M-R test with base 3.
Other rumours have it that their strong L-S test is slightly different.
It does not hurt, though, beside a bit of extra runtime.
*/
b.dp[0]++;
if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
goto LBL_B;
}
if (!res) {
goto LBL_B;
}
/*
* Both, the Frobenius-Underwood test and the the Lucas-Selfridge test are quite
* slow so if speed is an issue, define LTM_USE_ONLY_MR to use M-R tests with
* bases 2, 3 and t random bases.
*/
#ifndef LTM_USE_ONLY_MR
if (t >= 0) {
#ifdef LTM_USE_FROBENIUS_TEST
err = mp_prime_frobenius_underwood(a, &res);
if ((err != MP_OKAY) && (err != MP_ITER)) {
goto LBL_B;
}
if (!res) {
goto LBL_B;
}
#else
if ((err = mp_prime_strong_lucas_selfridge(a, &res)) != MP_OKAY) {
goto LBL_B;
}
if (!res) {
goto LBL_B;
}
#endif
}
#endif
/* run at least one Miller-Rabin test with a random base */
if (t == 0) {
t = 1;
}
/*
Only recommended if the input range is known to be < 3317044064679887385961981
It uses the bases necessary for a deterministic M-R test if the input is
smaller than 3317044064679887385961981
The caller has to check the size.
TODO: can be made a bit finer grained but comparing is not free.
*/
if (t < 0) {
int p_max = 0;
/*
Sorenson, Jonathan; Webster, Jonathan (2015).
"Strong Pseudoprimes to Twelve Prime Bases".
*/
/* 0x437ae92817f9fc85b7e5 = 318665857834031151167461 */
if ((err = mp_read_radix(&b, "437ae92817f9fc85b7e5", 16)) != MP_OKAY) {
goto LBL_B;
}
if (mp_cmp(a, &b) == MP_LT) {
p_max = 12;
} else {
/* 0x2be6951adc5b22410a5fd = 3317044064679887385961981 */
if ((err = mp_read_radix(&b, "2be6951adc5b22410a5fd", 16)) != MP_OKAY) {
goto LBL_B;
}
if (mp_cmp(a, &b) == MP_LT) {
p_max = 13;
} else {
err = MP_VAL;
goto LBL_B;
}
}
/* we did bases 2 and 3 already, skip them */
for (ix = 2; ix < p_max; ix++) {
mp_set(&b, s_mp_prime_tab[ix]);
if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
goto LBL_B;
}
if (!res) {
goto LBL_B;
}
}
}
/*
Do "t" M-R tests with random bases between 3 and "a".
See Fips 186.4 p. 126ff
*/
else if (t > 0) {
unsigned int mask;
int size_a;
/*
* The mp_digit's have a defined bit-size but the size of the
* array a.dp is a simple 'int' and this library can not assume full
* compliance to the current C-standard (ISO/IEC 9899:2011) because
* it gets used for small embeded processors, too. Some of those MCUs
* have compilers that one cannot call standard compliant by any means.
* Hence the ugly type-fiddling in the following code.
*/
size_a = mp_count_bits(a);
mask = (1u << s_floor_ilog2(size_a)) - 1u;
/*
Assuming the General Rieman hypothesis (never thought to write that in a
comment) the upper bound can be lowered to 2*(log a)^2.
E. Bach, "Explicit bounds for primality testing and related problems,"
Math. Comp. 55 (1990), 355-380.
size_a = (size_a/10) * 7;
len = 2 * (size_a * size_a);
E.g.: a number of size 2^2048 would be reduced to the upper limit
floor(2048/10)*7 = 1428
2 * 1428^2 = 4078368
(would have been ~4030331.9962 with floats and natural log instead)
That number is smaller than 2^28, the default bit-size of mp_digit.
*/
/*
How many tests, you might ask? Dana Jacobsen of Math::Prime::Util fame
does exactly 1. In words: one. Look at the end of _GMP_is_prime() in
Math-Prime-Util-GMP-0.50/primality.c if you do not believe it.
The function mp_rand() goes to some length to use a cryptographically
good PRNG. That also means that the chance to always get the same base
in the loop is non-zero, although very low.
If the BPSW test and/or the addtional Frobenious test have been
performed instead of just the Miller-Rabin test with the bases 2 and 3,
a single extra test should suffice, so such a very unlikely event
will not do much harm.
To preemptivly answer the dangling question: no, a witness does not
need to be prime.
*/
for (ix = 0; ix < t; ix++) {
unsigned int fips_rand;
int len;
/* mp_rand() guarantees the first digit to be non-zero */
if ((err = mp_rand(&b, 1)) != MP_OKAY) {
goto LBL_B;
}
/*
* Reduce digit before casting because mp_digit might be bigger than
* an unsigned int and "mask" on the other side is most probably not.
*/
fips_rand = (unsigned int)(b.dp[0] & (mp_digit) mask);
if (fips_rand > (unsigned int)(INT_MAX - MP_DIGIT_BIT)) {
len = INT_MAX / MP_DIGIT_BIT;
} else {
len = (((int)fips_rand + MP_DIGIT_BIT) / MP_DIGIT_BIT);
}
/* Unlikely. */
if (len < 0) {
ix--;
continue;
}
if ((err = mp_rand(&b, len)) != MP_OKAY) {
goto LBL_B;
}
/*
* That number might got too big and the witness has to be
* smaller than "a"
*/
len = mp_count_bits(&b);
if (len >= size_a) {
len = (len - size_a) + 1;
if ((err = mp_div_2d(&b, len, &b, NULL)) != MP_OKAY) {
goto LBL_B;
}
}
/* Although the chance for b <= 3 is miniscule, try again. */
if (mp_cmp_d(&b, 3uL) != MP_GT) {
ix--;
continue;
}
if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
goto LBL_B;
}
if (!res) {
goto LBL_B;
}
}
}
/* passed the test */
*result = true;
LBL_B:
mp_clear(&b);
return err;
}
#endif

View File

@@ -0,0 +1,91 @@
#include "tommath_private.h"
#ifdef MP_PRIME_MILLER_RABIN_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* Miller-Rabin test of "a" to the base of "b" as described in
* HAC pp. 139 Algorithm 4.24
*
* Sets result to 0 if definitely composite or 1 if probably prime.
* Randomly the chance of error is no more than 1/4 and often
* very much lower.
*/
mp_err mp_prime_miller_rabin(const mp_int *a, const mp_int *b, bool *result)
{
mp_int n1, y, r;
mp_err err;
int s, j;
/* ensure b > 1 */
if (mp_cmp_d(b, 1uL) != MP_GT) {
return MP_VAL;
}
/* get n1 = a - 1 */
if ((err = mp_init_copy(&n1, a)) != MP_OKAY) {
return err;
}
if ((err = mp_sub_d(&n1, 1uL, &n1)) != MP_OKAY) {
goto LBL_ERR1;
}
/* set 2**s * r = n1 */
if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) {
goto LBL_ERR1;
}
/* count the number of least significant bits
* which are zero
*/
s = mp_cnt_lsb(&r);
/* now divide n - 1 by 2**s */
if ((err = mp_div_2d(&r, s, &r, NULL)) != MP_OKAY) {
goto LBL_ERR2;
}
/* compute y = b**r mod a */
if ((err = mp_init(&y)) != MP_OKAY) {
goto LBL_ERR2;
}
if ((err = mp_exptmod(b, &r, a, &y)) != MP_OKAY) {
goto LBL_END;
}
/* if y != 1 and y != n1 do */
if ((mp_cmp_d(&y, 1uL) != MP_EQ) && (mp_cmp(&y, &n1) != MP_EQ)) {
j = 1;
/* while j <= s-1 and y != n1 */
while ((j <= (s - 1)) && (mp_cmp(&y, &n1) != MP_EQ)) {
if ((err = mp_sqrmod(&y, a, &y)) != MP_OKAY) {
goto LBL_END;
}
/* if y == 1 then composite */
if (mp_cmp_d(&y, 1uL) == MP_EQ) {
*result = false;
goto LBL_END;
}
++j;
}
/* if y != n1 then composite */
if (mp_cmp(&y, &n1) != MP_EQ) {
*result = false;
goto LBL_END;
}
}
/* probably prime now */
*result = true;
LBL_END:
mp_clear(&y);
LBL_ERR2:
mp_clear(&r);
LBL_ERR1:
mp_clear(&n1);
return err;
}
#endif

View File

@@ -0,0 +1,127 @@
#include "tommath_private.h"
#ifdef MP_PRIME_NEXT_PRIME_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* finds the next prime after the number "a" using "t" trials
* of Miller-Rabin.
*
* bbs_style = true means the prime must be congruent to 3 mod 4
*/
mp_err mp_prime_next_prime(mp_int *a, int t, bool bbs_style)
{
int x;
mp_err err;
bool res = false;
mp_digit res_tab[MP_PRIME_TAB_SIZE], kstep;
mp_int b;
/* force positive */
a->sign = MP_ZPOS;
/* simple algo if a is less than the largest prime in the table */
if (mp_cmp_d(a, s_mp_prime_tab[MP_PRIME_TAB_SIZE-1]) == MP_LT) {
/* find which prime it is bigger than "a" */
for (x = 0; x < MP_PRIME_TAB_SIZE; x++) {
mp_ord cmp = mp_cmp_d(a, s_mp_prime_tab[x]);
if (cmp == MP_EQ) {
continue;
}
if (cmp != MP_GT) {
if ((bbs_style) && ((s_mp_prime_tab[x] & 3u) != 3u)) {
/* try again until we get a prime congruent to 3 mod 4 */
continue;
} else {
mp_set(a, s_mp_prime_tab[x]);
return MP_OKAY;
}
}
}
/* fall through to the sieve */
}
/* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */
kstep = bbs_style ? 4 : 2;
/* at this point we will use a combination of a sieve and Miller-Rabin */
if (bbs_style) {
/* if a mod 4 != 3 subtract the correct value to make it so */
if ((a->dp[0] & 3u) != 3u) {
if ((err = mp_sub_d(a, (a->dp[0] & 3u) + 1u, a)) != MP_OKAY) {
return err;
}
}
} else {
if (mp_iseven(a)) {
/* force odd */
if ((err = mp_sub_d(a, 1uL, a)) != MP_OKAY) {
return err;
}
}
}
/* generate the restable */
for (x = 1; x < MP_PRIME_TAB_SIZE; x++) {
if ((err = mp_mod_d(a, s_mp_prime_tab[x], res_tab + x)) != MP_OKAY) {
return err;
}
}
/* init temp used for Miller-Rabin Testing */
if ((err = mp_init(&b)) != MP_OKAY) {
return err;
}
for (;;) {
mp_digit step = 0;
bool y;
/* skip to the next non-trivially divisible candidate */
do {
/* y == true if any residue was zero [e.g. cannot be prime] */
y = false;
/* increase step to next candidate */
step += kstep;
/* compute the new residue without using division */
for (x = 1; x < MP_PRIME_TAB_SIZE; x++) {
/* add the step to each residue */
res_tab[x] += kstep;
/* subtract the modulus [instead of using division] */
if (res_tab[x] >= s_mp_prime_tab[x]) {
res_tab[x] -= s_mp_prime_tab[x];
}
/* set flag if zero */
if (res_tab[x] == 0u) {
y = true;
}
}
} while (y && (step < (((mp_digit)1 << MP_DIGIT_BIT) - kstep)));
/* add the step */
if ((err = mp_add_d(a, step, a)) != MP_OKAY) {
goto LBL_ERR;
}
/* if didn't pass sieve and step == MP_MAX then skip test */
if (y && (step >= (((mp_digit)1 << MP_DIGIT_BIT) - kstep))) {
continue;
}
if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
goto LBL_ERR;
}
if (res) {
break;
}
}
LBL_ERR:
mp_clear(&b);
return err;
}
#endif

View File

@@ -0,0 +1,48 @@
#include "tommath_private.h"
#ifdef MP_PRIME_RABIN_MILLER_TRIALS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
static const struct {
int k, t;
} sizes[] = {
{ 80, -1 }, /* Use deterministic algorithm for size <= 80 bits */
{ 81, 37 }, /* max. error = 2^(-96)*/
{ 96, 32 }, /* max. error = 2^(-96)*/
{ 128, 40 }, /* max. error = 2^(-112)*/
{ 160, 35 }, /* max. error = 2^(-112)*/
{ 256, 27 }, /* max. error = 2^(-128)*/
{ 384, 16 }, /* max. error = 2^(-128)*/
{ 512, 18 }, /* max. error = 2^(-160)*/
{ 768, 11 }, /* max. error = 2^(-160)*/
{ 896, 10 }, /* max. error = 2^(-160)*/
{ 1024, 12 }, /* max. error = 2^(-192)*/
{ 1536, 8 }, /* max. error = 2^(-192)*/
{ 2048, 6 }, /* max. error = 2^(-192)*/
{ 3072, 4 }, /* max. error = 2^(-192)*/
{ 4096, 5 }, /* max. error = 2^(-256)*/
{ 5120, 4 }, /* max. error = 2^(-256)*/
{ 6144, 4 }, /* max. error = 2^(-256)*/
{ 8192, 3 }, /* max. error = 2^(-256)*/
{ 9216, 3 }, /* max. error = 2^(-256)*/
{ 10240, 2 } /* For bigger keysizes use always at least 2 Rounds */
};
/* returns # of RM trials required for a given bit size */
int mp_prime_rabin_miller_trials(int size)
{
int x;
for (x = 0; x < (int)(sizeof(sizes)/(sizeof(sizes[0]))); x++) {
if (sizes[x].k == size) {
return sizes[x].t;
}
if (sizes[x].k > size) {
return (x == 0) ? sizes[0].t : sizes[x - 1].t;
}
}
return sizes[x-1].t;
}
#endif

View File

@@ -0,0 +1,123 @@
#include "tommath_private.h"
#ifdef MP_PRIME_RAND_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* makes a truly random prime of a given size (bits),
*
* Flags are as follows:
*
* MP_PRIME_BBS - make prime congruent to 3 mod 4
* MP_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies MP_PRIME_BBS)
* MP_PRIME_2MSB_ON - make the 2nd highest bit one
*
* You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can
* have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself
* so it can be NULL
*
*/
/* This is possibly the mother of all prime generation functions, muahahahahaha! */
mp_err mp_prime_rand(mp_int *a, int t, int size, int flags)
{
uint8_t *tmp, maskAND, maskOR_msb, maskOR_lsb;
int bsize, maskOR_msb_offset;
bool res;
mp_err err;
/* sanity check the input */
if ((size <= 1) || (t <= 0)) {
return MP_VAL;
}
/* MP_PRIME_SAFE implies MP_PRIME_BBS */
if ((flags & MP_PRIME_SAFE) != 0) {
flags |= MP_PRIME_BBS;
}
/* calc the byte size */
bsize = (size>>3) + ((size&7)?1:0);
/* we need a buffer of bsize bytes */
tmp = (uint8_t *) MP_MALLOC((size_t)bsize);
if (tmp == NULL) {
return MP_MEM;
}
/* calc the maskAND value for the MSbyte*/
maskAND = ((size&7) == 0) ? 0xFFu : (uint8_t)(0xFFu >> (8 - (size & 7)));
/* calc the maskOR_msb */
maskOR_msb = 0;
maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0;
if ((flags & MP_PRIME_2MSB_ON) != 0) {
maskOR_msb |= (uint8_t)(0x80 >> ((9 - size) & 7));
}
/* get the maskOR_lsb */
maskOR_lsb = 1u;
if ((flags & MP_PRIME_BBS) != 0) {
maskOR_lsb |= 3u;
}
do {
/* read the bytes */
if ((err = s_mp_rand_source(tmp, (size_t)bsize)) != MP_OKAY) {
goto LBL_ERR;
}
/* work over the MSbyte */
tmp[0] &= maskAND;
tmp[0] |= (uint8_t)(1 << ((size - 1) & 7));
/* mix in the maskORs */
tmp[maskOR_msb_offset] |= maskOR_msb;
tmp[bsize-1] |= maskOR_lsb;
/* read it in */
/* TODO: casting only for now until all lengths have been changed to the type "size_t"*/
if ((err = mp_from_ubin(a, tmp, (size_t)bsize)) != MP_OKAY) {
goto LBL_ERR;
}
/* is it prime? */
if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
goto LBL_ERR;
}
if (!res) {
continue;
}
if ((flags & MP_PRIME_SAFE) != 0) {
/* see if (a-1)/2 is prime */
if ((err = mp_sub_d(a, 1uL, a)) != MP_OKAY) {
goto LBL_ERR;
}
if ((err = mp_div_2(a, a)) != MP_OKAY) {
goto LBL_ERR;
}
/* is it prime? */
if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) {
goto LBL_ERR;
}
}
} while (!res);
if ((flags & MP_PRIME_SAFE) != 0) {
/* restore a to the original value */
if ((err = mp_mul_2(a, a)) != MP_OKAY) {
goto LBL_ERR;
}
if ((err = mp_add_d(a, 1uL, a)) != MP_OKAY) {
goto LBL_ERR;
}
}
err = MP_OKAY;
LBL_ERR:
MP_FREE_BUF(tmp, (size_t)bsize);
return err;
}
#endif

View File

@@ -0,0 +1,281 @@
#include "tommath_private.h"
#ifdef MP_PRIME_STRONG_LUCAS_SELFRIDGE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/*
* See file mp_prime_is_prime.c or the documentation in doc/bn.tex for the details
*/
#ifndef LTM_USE_ONLY_MR
/*
* multiply bigint a with int d and put the result in c
* Like mp_mul_d() but with a signed long as the small input
*/
static mp_err s_mul_si(const mp_int *a, int32_t d, mp_int *c)
{
mp_int t;
mp_err err;
if ((err = mp_init(&t)) != MP_OKAY) {
return err;
}
/*
* mp_digit might be smaller than a long, which excludes
* the use of mp_mul_d() here.
*/
mp_set_i32(&t, d);
err = mp_mul(a, &t, c);
mp_clear(&t);
return err;
}
/*
Strong Lucas-Selfridge test.
returns true if it is a strong L-S prime, false if it is composite
Code ported from Thomas Ray Nicely's implementation of the BPSW test
at http://www.trnicely.net/misc/bpsw.html
Freeware copyright (C) 2016 Thomas R. Nicely <http://www.trnicely.net>.
Released into the public domain by the author, who disclaims any legal
liability arising from its use
The multi-line comments are made by Thomas R. Nicely and are copied verbatim.
Additional comments marked "CZ" (without the quotes) are by the code-portist.
(If that name sounds familiar, he is the guy who found the fdiv bug in the
Pentium (P5x, I think) Intel processor)
*/
mp_err mp_prime_strong_lucas_selfridge(const mp_int *a, bool *result)
{
/* CZ TODO: choose better variable names! */
mp_int Dz, gcd, Np1, Uz, Vz, U2mz, V2mz, Qmz, Q2mz, Qkdz, T1z, T2z, T3z, T4z, Q2kdz;
int32_t D, Ds, J, sign, P, Q, r, s, u, Nbits;
mp_err err;
bool oddness;
*result = false;
/*
Find the first element D in the sequence {5, -7, 9, -11, 13, ...}
such that Jacobi(D,N) = -1 (Selfridge's algorithm). Theory
indicates that, if N is not a perfect square, D will "nearly
always" be "small." Just in case, an overflow trap for D is
included.
*/
if ((err = mp_init_multi(&Dz, &gcd, &Np1, &Uz, &Vz, &U2mz, &V2mz, &Qmz, &Q2mz, &Qkdz, &T1z, &T2z, &T3z, &T4z, &Q2kdz,
NULL)) != MP_OKAY) {
return err;
}
D = 5;
sign = 1;
for (;;) {
Ds = sign * D;
sign = -sign;
mp_set_u32(&Dz, (uint32_t)D);
if ((err = mp_gcd(a, &Dz, &gcd)) != MP_OKAY) goto LBL_LS_ERR;
/* if 1 < GCD < N then N is composite with factor "D", and
Jacobi(D,N) is technically undefined (but often returned
as zero). */
if ((mp_cmp_d(&gcd, 1uL) == MP_GT) && (mp_cmp(&gcd, a) == MP_LT)) {
goto LBL_LS_ERR;
}
if (Ds < 0) {
Dz.sign = MP_NEG;
}
if ((err = mp_kronecker(&Dz, a, &J)) != MP_OKAY) goto LBL_LS_ERR;
if (J == -1) {
break;
}
D += 2;
if (D > (INT_MAX - 2)) {
err = MP_VAL;
goto LBL_LS_ERR;
}
}
P = 1; /* Selfridge's choice */
Q = (1 - Ds) / 4; /* Required so D = P*P - 4*Q */
/* NOTE: The conditions (a) N does not divide Q, and
(b) D is square-free or not a perfect square, are included by
some authors; e.g., "Prime numbers and computer methods for
factorization," Hans Riesel (2nd ed., 1994, Birkhauser, Boston),
p. 130. For this particular application of Lucas sequences,
these conditions were found to be immaterial. */
/* Now calculate N - Jacobi(D,N) = N + 1 (even), and calculate the
odd positive integer d and positive integer s for which
N + 1 = 2^s*d (similar to the step for N - 1 in Miller's test).
The strong Lucas-Selfridge test then returns N as a strong
Lucas probable prime (slprp) if any of the following
conditions is met: U_d=0, V_d=0, V_2d=0, V_4d=0, V_8d=0,
V_16d=0, ..., etc., ending with V_{2^(s-1)*d}=V_{(N+1)/2}=0
(all equalities mod N). Thus d is the highest index of U that
must be computed (since V_2m is independent of U), compared
to U_{N+1} for the standard Lucas-Selfridge test; and no
index of V beyond (N+1)/2 is required, just as in the
standard Lucas-Selfridge test. However, the quantity Q^d must
be computed for use (if necessary) in the latter stages of
the test. The result is that the strong Lucas-Selfridge test
has a running time only slightly greater (order of 10 %) than
that of the standard Lucas-Selfridge test, while producing
only (roughly) 30 % as many pseudoprimes (and every strong
Lucas pseudoprime is also a standard Lucas pseudoprime). Thus
the evidence indicates that the strong Lucas-Selfridge test is
more effective than the standard Lucas-Selfridge test, and a
Baillie-PSW test based on the strong Lucas-Selfridge test
should be more reliable. */
if ((err = mp_add_d(a, 1uL, &Np1)) != MP_OKAY) goto LBL_LS_ERR;
s = mp_cnt_lsb(&Np1);
/* CZ
* This should round towards zero because
* Thomas R. Nicely used GMP's mpz_tdiv_q_2exp()
* and mp_div_2d() is equivalent. Additionally:
* dividing an even number by two does not produce
* any leftovers.
*/
if ((err = mp_div_2d(&Np1, s, &Dz, NULL)) != MP_OKAY) goto LBL_LS_ERR;
/* We must now compute U_d and V_d. Since d is odd, the accumulated
values U and V are initialized to U_1 and V_1 (if the target
index were even, U and V would be initialized instead to U_0=0
and V_0=2). The values of U_2m and V_2m are also initialized to
U_1 and V_1; the FOR loop calculates in succession U_2 and V_2,
U_4 and V_4, U_8 and V_8, etc. If the corresponding bits
(1, 2, 3, ...) of t are on (the zero bit having been accounted
for in the initialization of U and V), these values are then
combined with the previous totals for U and V, using the
composition formulas for addition of indices. */
mp_set(&Uz, 1uL); /* U=U_1 */
mp_set(&Vz, (mp_digit)P); /* V=V_1 */
mp_set(&U2mz, 1uL); /* U_1 */
mp_set(&V2mz, (mp_digit)P); /* V_1 */
mp_set_i32(&Qmz, Q);
if ((err = mp_mul_2(&Qmz, &Q2mz)) != MP_OKAY) goto LBL_LS_ERR;
/* Initializes calculation of Q^d */
mp_set_i32(&Qkdz, Q);
Nbits = mp_count_bits(&Dz);
for (u = 1; u < Nbits; u++) { /* zero bit off, already accounted for */
/* Formulas for doubling of indices (carried out mod N). Note that
* the indices denoted as "2m" are actually powers of 2, specifically
* 2^(ul-1) beginning each loop and 2^ul ending each loop.
*
* U_2m = U_m*V_m
* V_2m = V_m*V_m - 2*Q^m
*/
if ((err = mp_mul(&U2mz, &V2mz, &U2mz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mod(&U2mz, a, &U2mz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_sqr(&V2mz, &V2mz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_sub(&V2mz, &Q2mz, &V2mz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mod(&V2mz, a, &V2mz)) != MP_OKAY) goto LBL_LS_ERR;
/* Must calculate powers of Q for use in V_2m, also for Q^d later */
if ((err = mp_sqr(&Qmz, &Qmz)) != MP_OKAY) goto LBL_LS_ERR;
/* prevents overflow */ /* CZ still necessary without a fixed prealloc'd mem.? */
if ((err = mp_mod(&Qmz, a, &Qmz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mul_2(&Qmz, &Q2mz)) != MP_OKAY) goto LBL_LS_ERR;
if (s_mp_get_bit(&Dz, u)) {
/* Formulas for addition of indices (carried out mod N);
*
* U_(m+n) = (U_m*V_n + U_n*V_m)/2
* V_(m+n) = (V_m*V_n + D*U_m*U_n)/2
*
* Be careful with division by 2 (mod N)!
*/
if ((err = mp_mul(&U2mz, &Vz, &T1z)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mul(&Uz, &V2mz, &T2z)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mul(&V2mz, &Vz, &T3z)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mul(&U2mz, &Uz, &T4z)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = s_mul_si(&T4z, Ds, &T4z)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_add(&T1z, &T2z, &Uz)) != MP_OKAY) goto LBL_LS_ERR;
if (mp_isodd(&Uz)) {
if ((err = mp_add(&Uz, a, &Uz)) != MP_OKAY) goto LBL_LS_ERR;
}
/* CZ
* This should round towards negative infinity because
* Thomas R. Nicely used GMP's mpz_fdiv_q_2exp().
* But mp_div_2() does not do so, it is truncating instead.
*/
oddness = mp_isodd(&Uz);
if ((err = mp_div_2(&Uz, &Uz)) != MP_OKAY) goto LBL_LS_ERR;
if (mp_isneg(&Uz) && oddness) {
if ((err = mp_sub_d(&Uz, 1uL, &Uz)) != MP_OKAY) goto LBL_LS_ERR;
}
if ((err = mp_add(&T3z, &T4z, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
if (mp_isodd(&Vz)) {
if ((err = mp_add(&Vz, a, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
}
oddness = mp_isodd(&Vz);
if ((err = mp_div_2(&Vz, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
if (mp_isneg(&Vz) && oddness) {
if ((err = mp_sub_d(&Vz, 1uL, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
}
if ((err = mp_mod(&Uz, a, &Uz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mod(&Vz, a, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
/* Calculating Q^d for later use */
if ((err = mp_mul(&Qkdz, &Qmz, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mod(&Qkdz, a, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR;
}
}
/* If U_d or V_d is congruent to 0 mod N, then N is a prime or a
strong Lucas pseudoprime. */
if (mp_iszero(&Uz) || mp_iszero(&Vz)) {
*result = true;
goto LBL_LS_ERR;
}
/* NOTE: Ribenboim ("The new book of prime number records," 3rd ed.,
1995/6) omits the condition V0 on p.142, but includes it on
p. 130. The condition is NECESSARY; otherwise the test will
return false negatives---e.g., the primes 29 and 2000029 will be
returned as composite. */
/* Otherwise, we must compute V_2d, V_4d, V_8d, ..., V_{2^(s-1)*d}
by repeated use of the formula V_2m = V_m*V_m - 2*Q^m. If any of
these are congruent to 0 mod N, then N is a prime or a strong
Lucas pseudoprime. */
/* Initialize 2*Q^(d*2^r) for V_2m */
if ((err = mp_mul_2(&Qkdz, &Q2kdz)) != MP_OKAY) goto LBL_LS_ERR;
for (r = 1; r < s; r++) {
if ((err = mp_sqr(&Vz, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_sub(&Vz, &Q2kdz, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mod(&Vz, a, &Vz)) != MP_OKAY) goto LBL_LS_ERR;
if (mp_iszero(&Vz)) {
*result = true;
goto LBL_LS_ERR;
}
/* Calculate Q^{d*2^r} for next r (final iteration irrelevant). */
if (r < (s - 1)) {
if ((err = mp_sqr(&Qkdz, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mod(&Qkdz, a, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR;
if ((err = mp_mul_2(&Qkdz, &Q2kdz)) != MP_OKAY) goto LBL_LS_ERR;
}
}
LBL_LS_ERR:
mp_clear_multi(&Q2kdz, &T4z, &T3z, &T2z, &T1z, &Qkdz, &Q2mz, &Qmz, &V2mz, &U2mz, &Vz, &Uz, &Np1, &gcd, &Dz, NULL);
return err;
}
#endif
#endif

View File

@@ -0,0 +1,34 @@
#include "tommath_private.h"
#ifdef MP_RADIX_SIZE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* returns size of ASCII representation */
mp_err mp_radix_size(const mp_int *a, int radix, size_t *size)
{
mp_err err;
mp_int a_;
int b;
/* make sure the radix is in range */
if ((radix < 2) || (radix > 64)) {
return MP_VAL;
}
if (mp_iszero(a)) {
*size = 2;
return MP_OKAY;
}
a_ = *a;
a_.sign = MP_ZPOS;
if ((err = mp_log_n(&a_, radix, &b)) != MP_OKAY) {
return err;
}
/* mp_ilogb truncates to zero, hence we need one extra put on top and one for `\0`. */
*size = (size_t)b + 2U + (mp_isneg(a) ? 1U : 0U);
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,17 @@
#include "tommath_private.h"
#ifdef MP_RADIX_SIZE_OVERESTIMATE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
mp_err mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size)
{
if (MP_HAS(S_MP_RADIX_SIZE_OVERESTIMATE)) {
return s_mp_radix_size_overestimate(a, radix, size);
}
if (MP_HAS(MP_RADIX_SIZE)) {
return mp_radix_size(a, radix, size);
}
return MP_ERR;
}
#endif

39
src/libtommath/mp_rand.c Normal file
View File

@@ -0,0 +1,39 @@
#include "tommath_private.h"
#ifdef MP_RAND_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
mp_err mp_rand(mp_int *a, int digits)
{
int i;
mp_err err;
mp_zero(a);
if (digits <= 0) {
return MP_OKAY;
}
if ((err = mp_grow(a, digits)) != MP_OKAY) {
return err;
}
if ((err = s_mp_rand_source(a->dp, (size_t)digits * sizeof(mp_digit))) != MP_OKAY) {
return err;
}
/* TODO: We ensure that the highest digit is nonzero. Should this be removed? */
while ((a->dp[digits - 1] & MP_MASK) == 0u) {
if ((err = s_mp_rand_source(a->dp + digits - 1, sizeof(mp_digit))) != MP_OKAY) {
return err;
}
}
a->used = digits;
for (i = 0; i < digits; ++i) {
a->dp[i] &= MP_MASK;
}
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,12 @@
#include "tommath_private.h"
#ifdef MP_RAND_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
mp_err(*s_mp_rand_source)(void *out, size_t size) = s_mp_rand_platform;
void mp_rand_source(mp_err(*source)(void *out, size_t size))
{
s_mp_rand_source = (source == NULL) ? s_mp_rand_platform : source;
}
#endif

View File

@@ -0,0 +1,69 @@
#include "tommath_private.h"
#ifdef MP_READ_RADIX_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* read a string [ASCII] in a given radix */
mp_err mp_read_radix(mp_int *a, const char *str, int radix)
{
mp_err err;
mp_sign sign = MP_ZPOS;
/* make sure the radix is ok */
if ((radix < 2) || (radix > 64)) {
return MP_VAL;
}
/* if the leading digit is a
* minus set the sign to negative.
*/
if (*str == '-') {
++str;
sign = MP_NEG;
}
/* set the integer to the default of zero */
mp_zero(a);
/* process each digit of the string */
while (*str != '\0') {
/* if the radix <= 36 the conversion is case insensitive
* this allows numbers like 1AB and 1ab to represent the same value
* [e.g. in hex]
*/
uint8_t y;
char ch = (radix <= 36) ? (char)MP_TOUPPER((int)*str) : *str;
unsigned pos = (unsigned)(ch - '+');
if (MP_RADIX_MAP_REVERSE_SIZE <= pos) {
break;
}
y = s_mp_radix_map_reverse[pos];
/* if the char was found in the map
* and is less than the given radix add it
* to the number, otherwise exit the loop.
*/
if (y >= radix) {
break;
}
if ((err = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) {
return err;
}
if ((err = mp_add_d(a, y, a)) != MP_OKAY) {
return err;
}
++str;
}
/* if an illegal character was found, fail. */
if ((*str != '\0') && (*str != '\r') && (*str != '\n')) {
return MP_VAL;
}
/* set the sign only if a != 0 */
if (!mp_iszero(a)) {
a->sign = sign;
}
return MP_OKAY;
}
#endif

View File

@@ -0,0 +1,83 @@
#include "tommath_private.h"
#ifdef MP_REDUCE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* reduces x mod m, assumes 0 < x < m**2, mu is
* precomputed via mp_reduce_setup.
* From HAC pp.604 Algorithm 14.42
*/
mp_err mp_reduce(mp_int *x, const mp_int *m, const mp_int *mu)
{
mp_int q;
mp_err err;
int um = m->used;
/* q = x */
if ((err = mp_init_copy(&q, x)) != MP_OKAY) {
return err;
}
/* q1 = x / b**(k-1) */
mp_rshd(&q, um - 1);
/* according to HAC this optimization is ok */
if ((mp_digit)um > ((mp_digit)1 << (MP_DIGIT_BIT - 1))) {
if ((err = mp_mul(&q, mu, &q)) != MP_OKAY) {
goto LBL_ERR;
}
} else if (MP_HAS(S_MP_MUL_HIGH)) {
if ((err = s_mp_mul_high(&q, mu, &q, um)) != MP_OKAY) {
goto LBL_ERR;
}
} else if (MP_HAS(S_MP_MUL_HIGH_COMBA)) {
if ((err = s_mp_mul_high_comba(&q, mu, &q, um)) != MP_OKAY) {
goto LBL_ERR;
}
} else {
err = MP_VAL;
goto LBL_ERR;
}
/* q3 = q2 / b**(k+1) */
mp_rshd(&q, um + 1);
/* x = x mod b**(k+1), quick (no division) */
if ((err = mp_mod_2d(x, MP_DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
goto LBL_ERR;
}
/* q = q * m mod b**(k+1), quick (no division) */
if ((err = s_mp_mul(&q, m, &q, um + 1)) != MP_OKAY) {
goto LBL_ERR;
}
/* x = x - q */
if ((err = mp_sub(x, &q, x)) != MP_OKAY) {
goto LBL_ERR;
}
/* If x < 0, add b**(k+1) to it */
if (mp_cmp_d(x, 0uL) == MP_LT) {
mp_set(&q, 1uL);
if ((err = mp_lshd(&q, um + 1)) != MP_OKAY) {
goto LBL_ERR;
}
if ((err = mp_add(x, &q, x)) != MP_OKAY) {
goto LBL_ERR;
}
}
/* Back off if it's too big */
while (mp_cmp(x, m) != MP_LT) {
if ((err = s_mp_sub(x, m, x)) != MP_OKAY) {
goto LBL_ERR;
}
}
LBL_ERR:
mp_clear(&q);
return err;
}
#endif

View File

@@ -0,0 +1,49 @@
#include "tommath_private.h"
#ifdef MP_REDUCE_2K_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* reduces a modulo n where n is of the form 2**p - d */
mp_err mp_reduce_2k(mp_int *a, const mp_int *n, mp_digit d)
{
mp_int q;
mp_err err;
int p;
if ((err = mp_init(&q)) != MP_OKAY) {
return err;
}
p = mp_count_bits(n);
for (;;) {
/* q = a/2**p, a = a mod 2**p */
if ((err = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
goto LBL_ERR;
}
if (d != 1u) {
/* q = q * d */
if ((err = mp_mul_d(&q, d, &q)) != MP_OKAY) {
goto LBL_ERR;
}
}
/* a = a + q */
if ((err = s_mp_add(a, &q, a)) != MP_OKAY) {
goto LBL_ERR;
}
if (mp_cmp_mag(a, n) == MP_LT) {
break;
}
if ((err = s_mp_sub(a, n, a)) != MP_OKAY) {
goto LBL_ERR;
}
}
LBL_ERR:
mp_clear(&q);
return err;
}
#endif

View File

@@ -0,0 +1,52 @@
#include "tommath_private.h"
#ifdef MP_REDUCE_2K_L_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* reduces a modulo n where n is of the form 2**p - d
This differs from reduce_2k since "d" can be larger
than a single digit.
*/
mp_err mp_reduce_2k_l(mp_int *a, const mp_int *n, const mp_int *d)
{
mp_int q;
mp_err err;
int p;
if ((err = mp_init(&q)) != MP_OKAY) {
return err;
}
p = mp_count_bits(n);
for (;;) {
/* q = a/2**p, a = a mod 2**p */
if ((err = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
goto LBL_ERR;
}
/* q = q * d */
if ((err = mp_mul(&q, d, &q)) != MP_OKAY) {
goto LBL_ERR;
}
/* a = a + q */
if ((err = s_mp_add(a, &q, a)) != MP_OKAY) {
goto LBL_ERR;
}
if (mp_cmp_mag(a, n) == MP_LT) {
break;
}
if ((err = s_mp_sub(a, n, a)) != MP_OKAY) {
goto LBL_ERR;
}
}
LBL_ERR:
mp_clear(&q);
return err;
}
#endif

Some files were not shown because too many files have changed in this diff Show More