Skip to content

Commit c076123

Browse files
committed
Addition of faster read_radix method
1 parent b1ad931 commit c076123

File tree

7 files changed

+279
-101
lines changed

7 files changed

+279
-101
lines changed

demo/test.c

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,12 +1151,17 @@ static int test_mp_montgomery_reduce(void)
11511151

11521152
}
11531153

1154+
#include <time.h>
11541155
static int test_mp_read_radix(void)
11551156
{
11561157
char buf[4096];
1157-
size_t written;
1158+
size_t written, maxlen;
11581159
int bignum, i;
11591160

1161+
char *buffer, *bcpy;
1162+
1163+
clock_t start, stop, t_slow, t_fast;
1164+
11601165
mp_int a, b;
11611166
DOR(mp_init_multi(&a, &b, NULL));
11621167

@@ -1187,25 +1192,73 @@ static int test_mp_read_radix(void)
11871192
/* Test the fast method with a slightly larger number */
11881193

11891194
/* Must be bigger than the cut-off value, of course */
1190-
bignum = 2* (2 * s_mp_radix_exponent_y[2] * MP_RADIX_BARRETT_START_MULTIPLICATOR);
1191-
printf("Size of bignum_size = %d\n", bignum);
1192-
/* Check if "bignum" is small enough for the result to fit into "buf"
1193-
otherwise lead tester to this function */
1194-
if (bignum >= 4096) {
1195-
fprintf(stderr, "Buffer too small, please check function \"test_mp_read_radix\" in \"test.c\"");
1195+
bignum = (2 * 20 * MP_RADIX_BARRETT_START_MULTIPLICATOR) * 10;
1196+
buffer = (char *)malloc((size_t)(bignum + 2));
1197+
if (buffer == NULL) {
11961198
goto LBL_ERR;
11971199
}
1198-
/* Produce a random number */
1199-
bignum /= MP_DIGIT_BIT;
1200-
DO(mp_rand(&b, bignum));
1201-
/* Check if it makes the round */
1202-
printf("Number of limbs in &b = %d, bit_count of &b = %d\n", bignum, mp_count_bits(&b));
1200+
DO(mp_rand(&a, bignum / MP_DIGIT_BIT));
1201+
printf("\nNumber of limbs in &b = %d, bit_count of &b = %d\n", bignum / MP_DIGIT_BIT, mp_count_bits(&a));
1202+
start = clock();
1203+
for (i = 2; i < 65; i++) {
1204+
/* printf("FAST radix = %d\n",i); */
1205+
DO(mp_to_radix(&a, buffer, (size_t)(bignum + 1), &written, i));
1206+
DO(mp_read_radix(&b, buffer, i));
1207+
EXPECT(mp_cmp(&a, &b) == MP_EQ);
1208+
}
1209+
stop = clock();
1210+
t_fast = stop - start;
1211+
1212+
printf("Same number, slow radix conversions\n");
1213+
start = clock();
12031214
for (i = 2; i < 65; i++) {
1204-
DO(mp_to_radix(&b, buf, sizeof(buf), &written, i));
1205-
DO(mp_read_radix(&a, buf, i));
1215+
/* printf("SLOW radix = %d\n",i); */
1216+
maxlen = (size_t)(bignum + 1);
1217+
bcpy = buffer;
1218+
DO(s_mp_slower_to_radix(&a, &bcpy, &maxlen, &written, i, false));
1219+
DO(s_mp_slower_read_radix(&b, bcpy, 0, strlen(bcpy), i));
12061220
EXPECT(mp_cmp(&a, &b) == MP_EQ);
1207-
/* fprintf(stderr,"radix = %d\n",i); */
12081221
}
1222+
stop = clock();
1223+
t_slow = stop - start;
1224+
1225+
/* It is "long int" in GLibC but can be bigger and/or even a floating point elsewhere */
1226+
printf("SLOW: %.10f, FAST: %.10f\n", (double)t_slow/(double)CLOCKS_PER_SEC, (double)t_fast/(double)CLOCKS_PER_SEC);
1227+
1228+
/* Check if the branching works. */
1229+
if (MP_HAS(S_MP_FASTER_READ_RADIX) && MP_HAS(S_MP_FASTER_TO_RADIX)) {
1230+
if (t_fast > t_slow) {
1231+
fprintf(stderr, "Timing suspicious in test_mp_read_radix. No fast multiplication? Cut-off too low?\n");
1232+
goto LBL_ERR;
1233+
}
1234+
}
1235+
1236+
1237+
free(buffer);
1238+
1239+
#if ((MP_DIGIT_BIT <= 16) && (defined MP_CHECK_RADIX_OVF))
1240+
/* Check a number of size (MP_MAX_DIGIT_COUNT * MP_DIGIT_BIT - 1) at fixed radix "10". */
1241+
/* Will not work if test is run on platforms with larger int's because
1242+
#define MP_MAX_DIGIT_COUNT ((INT_MAX - 2) / MP_DIGIT_BIT)
1243+
So we have to replace the value for INT_MAX with 2^15 - 1 = 32767 to test 16-bit int's. Not
1244+
very elegant but it works.
1245+
*/
1246+
bignum = ((32767 - 2) / MP_DIGIT_BIT);
1247+
bignum = ((bignum - 1) * MP_DIGIT_BIT) + (MP_DIGIT_BIT - 1);
1248+
/* Manual computation because the automatic methods might not have been included in the build */
1249+
buffer = (char *)malloc(((bignum + 2)/1000) * 333);
1250+
if (buffer == NULL) {
1251+
goto LBL_ERR;
1252+
}
1253+
DO(mp_2expt(&a, bignum));
1254+
DO(mp_decr(&a));
1255+
printf("Number of limbs in &b = %d, bit_count of &b = %d\n", bignum / MP_DIGIT_BIT, mp_count_bits(&a));
1256+
DO(mp_to_radix(&a, buffer, ((bignum + 2)/1000) * 333, &written, 10));
1257+
DO(mp_read_radix(&b, buffer, 10));
1258+
EXPECT(mp_cmp(&a, &b) == MP_EQ);
1259+
free(buffer);
1260+
#endif
1261+
12091262

12101263

12111264
while (0) {
@@ -2504,7 +2557,7 @@ static int unit_tests(int argc, char **argv)
25042557
T1(mp_prime_next_prime, MP_PRIME_NEXT_PRIME),
25052558
T1(mp_prime_rand, MP_PRIME_RAND),
25062559
T1(mp_rand, MP_RAND),
2507-
T1(mp_read_radix, MP_READ_RADIX),
2560+
T3(mp_read_radix, ONLY_PUBLIC_API, MP_READ_RADIX, MP_TO_RADIX),
25082561
T1(mp_read_write_ubin, MP_TO_UBIN),
25092562
T1(mp_read_write_sbin, MP_TO_SBIN),
25102563
T1(mp_reduce_2k, MP_REDUCE_2K),

mp_read_radix.c

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,23 @@
33
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
44
/* SPDX-License-Identifier: Unlicense */
55

6+
static size_t s_mp_strlen(const char *s)
7+
{
8+
const char *p;
9+
p = s;
10+
while (*p != '\0') {
11+
p++;
12+
}
13+
return (size_t)(p - s);
14+
}
15+
616
/* read a string [ASCII] in a given radix */
717
mp_err mp_read_radix(mp_int *a, const char *str, int radix)
818
{
19+
920
mp_err err;
1021
mp_sign sign = MP_ZPOS;
22+
size_t slen;
1123

1224
/* make sure the radix is ok */
1325
if ((radix < 2) || (radix > 64)) {
@@ -22,48 +34,24 @@ mp_err mp_read_radix(mp_int *a, const char *str, int radix)
2234
sign = MP_NEG;
2335
}
2436

25-
/* set the integer to the default of zero */
26-
mp_zero(a);
27-
28-
/* process each digit of the string */
29-
while (*str != '\0') {
30-
/* if the radix <= 36 the conversion is case insensitive
31-
* this allows numbers like 1AB and 1ab to represent the same value
32-
* [e.g. in hex]
33-
*/
34-
uint8_t y;
35-
char ch = (radix <= 36) ? (char)MP_TOUPPER((int)*str) : *str;
36-
unsigned pos = (unsigned)(ch - '+');
37-
if (MP_RADIX_MAP_REVERSE_SIZE <= pos) {
38-
break;
39-
}
40-
y = s_mp_radix_map_reverse[pos];
37+
slen = s_mp_strlen(str);
4138

42-
/* if the char was found in the map
43-
* and is less than the given radix add it
44-
* to the number, otherwise exit the loop.
45-
*/
46-
if (y >= radix) {
47-
break;
48-
}
49-
if ((err = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) {
50-
return err;
51-
}
52-
if ((err = mp_add_d(a, y, a)) != MP_OKAY) {
53-
return err;
54-
}
55-
++str;
56-
}
39+
mp_zero(a);
5740

58-
/* if an illegal character was found, fail. */
59-
if ((*str != '\0') && (*str != '\r') && (*str != '\n')) {
60-
return MP_VAL;
41+
/* Try faster version first */
42+
if (MP_HAS(S_MP_FASTER_READ_RADIX)) {
43+
if ((err = s_mp_faster_read_radix(a, str, 0, slen, radix)) != MP_OKAY) goto LTM_ERR;
44+
} else if (MP_HAS(S_MP_SLOWER_READ_RADIX)) {
45+
if ((err = s_mp_slower_read_radix(a, str, 0, slen, radix)) != MP_OKAY) goto LTM_ERR;
6146
}
6247

6348
/* set the sign only if a != 0 */
6449
if (!mp_iszero(a)) {
6550
a->sign = sign;
6651
}
67-
return MP_OKAY;
52+
53+
LTM_ERR:
54+
return err;
6855
}
56+
6957
#endif

s_mp_faster_read_radix.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include "tommath_private.h"
2+
#ifdef S_MP_FASTER_READ_RADIX_C
3+
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
4+
/* SPDX-License-Identifier: Unlicense */
5+
6+
/* TODO: It is tunable */
7+
#define MP_READ_RADIX_CUTOFF_MULTIPLICATOR 3
8+
9+
/* for(n=2,64,t = ceil(log(2^100)/log(n)); printf(t", "); ) */
10+
static const uint8_t s_read_radix_cutoff[65] = { 0, 0, /* 0 .. 1*/
11+
100, 64, 50, 44, 39, 36, 34, 32, /* 2 .. 9 */
12+
31, 29, 28, 28, 27, 26, 25, 25, /* 10 .. 17 */
13+
24, 24, 24, 23, 23, 23, 22, 22, /* 18 .. 25 */
14+
22, 22, 21, 21, 21, 21, 20, 20, /* 26 .. 33 */
15+
20, 20, 20, 20, 20, 19, 19, 19, /* 34 .. 41 */
16+
19, 19, 19, 19, 19, 19, 18, 18, /* 42 .. 49 */
17+
18, 18, 18, 18, 18, 18, 18, 18, /* 51 .. 57 */
18+
18, 17, 17, 17, 17, 17, 17 /* 58 .. 64 */
19+
};
20+
21+
/* This is in mp_prime_is_prime.c and can be reused */
22+
static int s_floor_ilog2(int value)
23+
{
24+
int r = 0;
25+
while ((value >>= 1) != 0) {
26+
r++;
27+
}
28+
return r;
29+
}
30+
31+
mp_err s_mp_faster_read_radix(mp_int *a, const char *str, size_t start, size_t end, int radix)
32+
{
33+
size_t len, mid;
34+
mp_int A, B, m;
35+
mp_err err = MP_OKAY;
36+
37+
len = end - start;
38+
39+
if (len < (size_t)(s_read_radix_cutoff[radix] * MP_READ_RADIX_CUTOFF_MULTIPLICATOR)) {
40+
return s_mp_slower_read_radix(a, str, start, end, radix);
41+
}
42+
43+
mid = len / 2u;
44+
45+
if ((err = mp_init_set(&m, (mp_digit)radix)) != MP_OKAY) {
46+
return err;
47+
}
48+
if ((err = mp_init_multi(&A, &B, NULL)) != MP_OKAY) {
49+
mp_clear(&m);
50+
return err;
51+
}
52+
53+
if ((err = s_mp_slower_read_radix(&A, str, start, start + mid + 1, radix)) != MP_OKAY) goto LTM_ERR;
54+
if ((err = s_mp_slower_read_radix(&B, str, start + mid +1, end, radix)) != MP_OKAY) goto LTM_ERR;
55+
56+
if (MP_IS_2EXPT((unsigned int)radix)) {
57+
if ((err = mp_mul_2d(&A, (int)(((len - mid) - 1u) * (size_t)s_floor_ilog2(radix)), &A)) != MP_OKAY)goto LTM_ERR;
58+
} else {
59+
if ((err = mp_expt_n(&m, (int)((len - mid) - 1u), &m)) != MP_OKAY) goto LTM_ERR;
60+
if ((err = mp_mul(&A, &m, &A)) != MP_OKAY) goto LTM_ERR;
61+
}
62+
if ((err = mp_add(&A, &B, a)) != MP_OKAY) goto LTM_ERR;
63+
64+
LTM_ERR:
65+
mp_clear_multi(&A, &B, &m, NULL);
66+
return err;
67+
}
68+
69+
#endif

0 commit comments

Comments
 (0)