Skip to content

Commit 8a4221f

Browse files
committed
Implementation of mp_printf
1 parent 0df542c commit 8a4221f

17 files changed

+997
-108
lines changed

CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,34 @@ if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN")
7474
list(APPEND LTM_C_FLAGS -no-undefined)
7575
endif()
7676

77+
#-----------------------------------------------------------------------------
78+
# Check for floating point intrinsics
79+
#-----------------------------------------------------------------------------
80+
81+
# TODO: add checks to mp_[gs]et_double if this works well
82+
include(CheckTypeSize)
83+
CHECK_TYPE_SIZE("float" FLOAT_SIZE)
84+
CHECK_TYPE_SIZE("double" DOUBLE_SIZE)
85+
CHECK_TYPE_SIZE("long double" LONG_DOUBLE_SIZE)
86+
87+
# Do we have anything even vaguely resembling floating points at all?
88+
if(FLOAT_SIZE GREATER 0)
89+
list(APPEND LTM_C_FLAGS -DLTM_HAVE_FLOAT_TYPE)
90+
# float.h may or may not exist, even with floats avaibl
91+
list(APPEND LTM_C_FLAGS -DLTM_FLOAT_SIZE=${FLOAT_SIZE})
92+
endif()
93+
94+
if(DOUBLE_SIZE GREATER 0)
95+
list(APPEND LTM_C_FLAGS -DLTM_HAVE_DOUBLE_TYPE)
96+
list(APPEND LTM_C_FLAGS -DLTM_DOUBLE_SIZE=${DOUBLE_SIZE})
97+
endif()
98+
99+
if(LONG_DOUBLE_SIZE GREATER 0)
100+
list(APPEND LTM_C_FLAGS -DLTM_HAVE_LONG_DOUBLE_TYPE)
101+
list(APPEND LTM_C_FLAGS -DLTM_LONG_DOUBLE_SIZE=${LONG_DOUBLE_SIZE})
102+
endif()
103+
104+
77105
# TODO: coverage (lgcov)
78106

79107
# If the user set the environment variables at generate-time, append them

demo/test.c

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,6 +2426,234 @@ static int test_mp_pack_unpack(void)
24262426
return EXIT_FAILURE;
24272427
}
24282428

2429+
#ifndef MP_NO_FILE
2430+
2431+
#define LTM_TEST_BUFSIZ 4096
2432+
#include <string.h>
2433+
static int test_mp_fprintf(void)
2434+
{
2435+
FILE *test_file = NULL;
2436+
2437+
char line_buffer[LTM_TEST_BUFSIZ] = {0};
2438+
int i;
2439+
bool write_only = false;
2440+
size_t slen = 0;
2441+
int characters_printed = 0;
2442+
char *fgets_return;
2443+
2444+
const char *test_values[2] = {
2445+
"4DDCFDE0D20EF8663B34D19F829FDD",
2446+
"-51D9769BDAE5B38121F2A31D881E5F"
2447+
};
2448+
2449+
const char *test_strings[] = {
2450+
#ifdef LTM_HAVE_FLOAT_TYPE
2451+
"START 0x0000007b 123.123 -424986725583297217766029037085924959 0x4DDCFDE0D20EF8663B34D19F829FDD END\n",
2452+
#endif
2453+
"START -0b10100011101100101110110100110111101101011100101101100111000000100100001111100101010001100011101100010000001111001011111 END\n",
2454+
"START @ -KTbsczhbiu4XygCTY1vV @ END\n",
2455+
#if (MP_DIGIT_BIT == 60)
2456+
"START 0x63b34d19f829fdd,0x4ddcfde0d20ef86 END\n",
2457+
#elif (MP_DIGIT_BIT == 31)
2458+
"START 0x1f829fdd,0x4c7669a3,0x3483be1,0x26ee7ef END\n",
2459+
#elif (MP_DIGIT_BIT == 28)
2460+
"START 0xf829fdd,0x3b34d19,0x20ef866,0xdcfde0d,0x4d END\n",
2461+
#elif (MP_DIGIT_BIT == 15)
2462+
"START 0x1fdd,0x3f05,0x5346,0x31d9,0x6f86,0x1a41,0x3f78,0x26ee END\n",
2463+
#else
2464+
"undefined limb size\n",
2465+
#endif
2466+
2467+
2468+
#if (MP_DIGIT_BIT == 60)
2469+
"START 0x000000000000063b34d19f829fdd END\n"
2470+
#elif (MP_DIGIT_BIT == 31)
2471+
"START 0x000000000000000000001f829fdd END\n"
2472+
#elif (MP_DIGIT_BIT == 28)
2473+
"START 0x000000000000000000000f829fdd END\n"
2474+
#elif (MP_DIGIT_BIT == 15)
2475+
"START 0x0000000000000000000000001fdd END\n"
2476+
#else
2477+
"undefined limb size\n"
2478+
#endif
2479+
};
2480+
2481+
mp_int p, q;
2482+
int n;
2483+
/* Only stream printing available, no mp_sprintf or the like. File needs a path for repeated truncating */
2484+
test_file = fopen("ltm_testing_mp_fprintf_88a43603fcfc2f7e7c6646cd4b89180a", "w+");
2485+
if (test_file == NULL) {
2486+
/* use logfile instead to have at least sth. in case of an error */
2487+
test_file = stdout;
2488+
write_only = true;
2489+
}
2490+
2491+
DOR(mp_init_multi(&p, &q, NULL));
2492+
2493+
DO(mp_read_radix(&p, test_values[0], 16));
2494+
DO(mp_read_radix(&q, test_values[1], 16));
2495+
2496+
/* No loop here, too much hassle */
2497+
i = 0;
2498+
/* Defined by CMake and is not in any of the generic Makefiles */
2499+
#ifdef LTM_HAVE_FLOAT_TYPE
2500+
characters_printed = mp_fprintf(test_file, "START %#010x %12.12g %10Zd %#10Zx END\n",123,123.123,&q,&p,&q);
2501+
slen = strlen(test_strings[i]);
2502+
if ((characters_printed - (int)slen) != 0) {
2503+
fprintf(stderr, "0 test_mp_fprintf: failed to print o:%zu t:%d\n", slen, characters_printed);
2504+
goto LBL_ERR;
2505+
}
2506+
if (!write_only) {
2507+
rewind(test_file);
2508+
fgets_return = fgets(line_buffer, LTM_TEST_BUFSIZ, test_file);
2509+
if (fgets_return == NULL) {
2510+
fprintf(stderr, "1 test_mp_fprintf: failed to read from file\n");
2511+
goto LBL_ERR;
2512+
}
2513+
if (strcmp(line_buffer, test_strings[i]) != 0) {
2514+
fprintf(stderr, "test_mp_fprintf: file content is not equal to test string #%d\n",i);
2515+
goto LBL_ERR;
2516+
}
2517+
}
2518+
i++;
2519+
/* Clear file content */
2520+
test_file = freopen("ltm_testing_mp_fprintf_88a43603fcfc2f7e7c6646cd4b89180a","w+", test_file);
2521+
if (test_file == NULL) {
2522+
/* use logfile instead to have at least sth. in case of an error */
2523+
test_file = stdout;
2524+
write_only = true;
2525+
}
2526+
#endif
2527+
2528+
characters_printed = mp_fprintf(test_file, "START %#Zb END\n",&q);
2529+
slen = strlen(test_strings[i]);
2530+
if ((characters_printed - (int)slen) != 0) {
2531+
fprintf(stderr, "1 test_mp_fprintf: failed to print o:%zu t:%d\n", slen, characters_printed);
2532+
goto LBL_ERR;
2533+
}
2534+
if (!write_only) {
2535+
rewind(test_file);
2536+
fgets_return = fgets(line_buffer, LTM_TEST_BUFSIZ, test_file);
2537+
if (fgets_return == NULL) {
2538+
fprintf(stderr, "test_mp_fprintf: failed to read from file\n");
2539+
goto LBL_ERR;
2540+
}
2541+
if (strcmp(line_buffer, test_strings[i]) != 0) {
2542+
fprintf(stderr, "test_mp_fprintf: file content is not equal to test string #%d\n",i);
2543+
goto LBL_ERR;
2544+
}
2545+
}
2546+
i++;
2547+
/* Clear file content */
2548+
test_file = freopen("ltm_testing_mp_fprintf_88a43603fcfc2f7e7c6646cd4b89180a","w+", test_file);
2549+
if (test_file == NULL) {
2550+
/* use logfile instead to have at least sth. in case of an error */
2551+
test_file = stdout;
2552+
write_only = true;
2553+
}
2554+
2555+
characters_printed = mp_fprintf(test_file, "START @ %#Z@ @ END\n",&q);
2556+
slen = strlen(test_strings[i]);
2557+
if ((characters_printed - (int)slen) != 0) {
2558+
fprintf(stderr, "1 test_mp_fprintf: failed to print o:%zu t:%d\n", slen, characters_printed);
2559+
goto LBL_ERR;
2560+
}
2561+
if (!write_only) {
2562+
rewind(test_file);
2563+
fgets_return = fgets(line_buffer, LTM_TEST_BUFSIZ, test_file);
2564+
if (fgets_return == NULL) {
2565+
fprintf(stderr, "test_mp_fprintf: failed to read from file\n");
2566+
goto LBL_ERR;
2567+
}
2568+
if (strcmp(line_buffer, test_strings[i]) != 0) {
2569+
fprintf(stderr, "test_mp_fprintf: file content is not equal to test string #%d\n",i);
2570+
goto LBL_ERR;
2571+
}
2572+
}
2573+
i++;
2574+
/* Clear file content */
2575+
test_file = freopen("ltm_testing_mp_fprintf_88a43603fcfc2f7e7c6646cd4b89180a","w+", test_file);
2576+
if (test_file == NULL) {
2577+
/* use logfile instead to have at least sth. in case of an error */
2578+
test_file = stdout;
2579+
write_only = true;
2580+
}
2581+
2582+
/* TODO: add entries for the smaller mp_digits */
2583+
characters_printed = mp_fprintf(test_file,"START %#Nx END\n",&p);
2584+
slen = strlen(test_strings[i]);
2585+
if ((characters_printed - (int)slen) != 0) {
2586+
fprintf(stderr, "2 test_mp_fprintf: failed to print o:%zu t:%d\n", slen, characters_printed);
2587+
goto LBL_ERR;
2588+
}
2589+
if (!write_only) {
2590+
rewind(test_file);
2591+
fgets_return = fgets(line_buffer, LTM_TEST_BUFSIZ, test_file);
2592+
if (fgets_return == NULL) {
2593+
fprintf(stderr, "test_mp_fprintf: failed to read from file\n");
2594+
goto LBL_ERR;
2595+
}
2596+
if (strcmp(line_buffer, test_strings[i]) != 0) {
2597+
fprintf(stderr, "test_mp_fprintf: file content is not equal to test string #%d\n",i);
2598+
goto LBL_ERR;
2599+
}
2600+
}
2601+
i++;
2602+
/* Clear file content */
2603+
test_file = freopen("ltm_testing_mp_fprintf_88a43603fcfc2f7e7c6646cd4b89180a","w+", test_file);
2604+
if (test_file == NULL) {
2605+
/* use logfile instead to have at least sth. in case of an error */
2606+
test_file = stdout;
2607+
write_only = true;
2608+
}
2609+
2610+
characters_printed = mp_fprintf(test_file,"START %0#30Mx END\n",p.dp[0]);
2611+
slen = strlen(test_strings[i]);
2612+
if ((characters_printed - (int)slen) != 0) {
2613+
fprintf(stderr, "3 test_mp_fprintf: failed to print o:%zu t:%d\n", slen, characters_printed);
2614+
goto LBL_ERR;
2615+
}
2616+
if (!write_only) {
2617+
rewind(test_file);
2618+
fgets_return = fgets(line_buffer, LTM_TEST_BUFSIZ, test_file);
2619+
if (fgets_return == NULL) {
2620+
fprintf(stderr, "test_mp_fprintf: failed to read from file\n");
2621+
goto LBL_ERR;
2622+
}
2623+
if (strcmp(line_buffer, test_strings[i]) != 0) {
2624+
fprintf(stderr, "test_mp_fprintf: file content is not equal to test string #%d\n",i);
2625+
goto LBL_ERR;
2626+
}
2627+
}
2628+
2629+
/* It's more or less implementation defined but must be the same. */
2630+
characters_printed = mp_fprintf(stdout,"START %p END\n",&p);
2631+
i = fprintf(stdout,"START %p END\n",&p);
2632+
if ((characters_printed - i) != 0) {
2633+
fprintf(stderr, "test_mp_fprintf: failed to print pointer\n");
2634+
goto LBL_ERR;
2635+
}
2636+
2637+
characters_printed = mp_fprintf(stdout,"START %n END\n",&n);
2638+
if (n != 6) {
2639+
fprintf(stderr, "test_mp_fprintf: failed to count 6 characters properly\n");
2640+
goto LBL_ERR;
2641+
}
2642+
2643+
mp_clear_multi(&p, &q, NULL);
2644+
fclose(test_file);
2645+
if (remove("ltm_testing_mp_fprintf_88a43603fcfc2f7e7c6646cd4b89180a") != 0) {
2646+
fprintf(stderr, "Could not delete file ltm_testing_mp_fprintf_88a43603fcfc2f7e7c6646cd4b89180a\n");
2647+
}
2648+
return EXIT_SUCCESS;
2649+
LBL_ERR:
2650+
mp_clear_multi(&p, &q, NULL);
2651+
fclose(test_file);
2652+
/* We don't delete the testfile in case of error, conrtent might be helpful. */
2653+
return EXIT_FAILURE;
2654+
}
2655+
#endif
2656+
24292657
#ifndef LTM_TEST_DYNAMIC
24302658
#define ONLY_PUBLIC_API_C
24312659
#endif
@@ -2453,6 +2681,7 @@ static int unit_tests(int argc, char **argv)
24532681
T1(mp_dr_reduce, MP_DR_REDUCE),
24542682
T2(mp_pack_unpack,MP_PACK, MP_UNPACK),
24552683
T2(mp_fread_fwrite, MP_FREAD, MP_FWRITE),
2684+
T1(mp_fprintf, S_MP_FPRINTF),
24562685
T1(mp_get_u32, MP_GET_I32),
24572686
T1(mp_get_u64, MP_GET_I64),
24582687
T1(mp_get_ul, MP_GET_L),

doc/bn.tex

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2414,12 +2414,52 @@ \subsection{To ASCII}
24142414
The result is \emph{always} either exact or too large but it is \emph{never} too small.
24152415

24162416

2417-
If \texttt{MP\_NO\_FILE} is not defined a function to write to a file is also available.
2417+
If \texttt{MP\_NO\_FILE} is not defined several functions to write to a file are also available.
24182418

24192419
\index{mp\_fwrite}
24202420
\begin{alltt}
24212421
mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream);
24222422
\end{alltt}
2423+
The function \texttt{mp\_fwrite} writes the radix \texttt{radix} representation of \texttt{a}
2424+
to the file stream \texttt{stream}.
2425+
2426+
2427+
\index{mp\_fprintf}
2428+
\begin{alltt}
2429+
int mp_fprintf(FILE *stream, const char *format, ...);
2430+
\end{alltt}
2431+
2432+
\index{mp\_printf}
2433+
\begin{alltt}
2434+
int mp_fprintf(FILE *stream, const char *format, ...);
2435+
\end{alltt}
2436+
2437+
The functions \texttt{mp\_fprintf} and \texttt{mp\_printf} behave like \texttt{(f)printf} with
2438+
the additional type modifiers \texttt{Z} for a big integer, \texttt{M} for a single limb, and
2439+
\texttt{N} for a comma separated list of the raw limb-array of an \texttt{mp\_int} in the order
2440+
it comes in.
2441+
2442+
Most of the functions of \texttt{printf} are implemented except thousands-separators for big
2443+
integers because they are locale dependent and zero-padding for big integers is also missing.
2444+
2445+
If there is no floating point available (the check for type \texttt{float} failed) trying to
2446+
print one fails silently\footnot{The compiler should have complained in the first place, though.}.
2447+
2448+
If there is no type \texttt{long double} available the modifier \texttt{L} will silently
2449+
print a \texttt{double} instead.
2450+
2451+
If there is no type \texttt{double} available either, the modifier \texttt{L} will silently
2452+
print a \texttt{float} instead.
2453+
2454+
If there is no type \texttt{double} available when printing without the modifier \texttt{L}
2455+
this function will silently print a \texttt{float} instead.
2456+
2457+
It is quite useful for debugging to have a binary representation of the big integers and their
2458+
innards, so the type modifier \texttt{b} had been added to print all of \texttt{Z, M, N} with
2459+
zeros and ones only.
2460+
2461+
Printing the big integer in base-64 is done with \texttt{%Z@}.
2462+
24232463
24242464
\subsection{From ASCII}
24252465
\index{mp\_read\_radix}

libtommath_VS2008.vcproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,10 @@
428428
RelativePath="mp_exteuclid.c"
429429
>
430430
</File>
431+
<File
432+
RelativePath="mp_fprintf.c"
433+
>
434+
</File>
431435
<File
432436
RelativePath="mp_fread.c"
433437
>
@@ -644,6 +648,10 @@
644648
RelativePath="mp_prime_strong_lucas_selfridge.c"
645649
>
646650
</File>
651+
<File
652+
RelativePath="mp_printf.c"
653+
>
654+
</File>
647655
<File
648656
RelativePath="mp_radix_size.c"
649657
>
@@ -840,6 +848,10 @@
840848
RelativePath="s_mp_fp_log_d.c"
841849
>
842850
</File>
851+
<File
852+
RelativePath="s_mp_fprintf.c"
853+
>
854+
</File>
843855
<File
844856
RelativePath="s_mp_get_bit.c"
845857
>

0 commit comments

Comments
 (0)