The numerals were now being converted automatically from base 2 to base 10. . . 881, 883, 887, 907 . . . each one confirmed as a prime number.
We begin this chapter with assignment, the simplest and also the most important function. To be able to assign to a CLINT
object a_l
the value of another CLINT
object b_l
, we require a function that copies the digits of b_l
to the reserved storage space for a_l
, an event that we shall call elementwise assignment. It will not suffice merely to copy the address of the object b_l
into the variable a_l
, since then both objects would refer to the same location in memory, namely that of b_l
, and any change in a_l
would be reflected in a change in the object b_l
, and conversely. Furthermore, access to the area of memory addressed by a_l
could become lost.
We shall return to the problems of elementwise assignment in the second part of this book when we concern ourselves with the implementation of the assignment operator "=" in C++ (see Section 14.3).
The assignment of the value of a CLINT
object to another CLINT
is effected with the function cpy_l()
.
Function: | copy a |
Syntax: |
|
Input: |
|
Output: |
|
void cpy_l (CLINT dest_l, CLINT src_l) { clint *lastsrc_l = MSDPTR_L (src_l); *dest_l = *src_l;
In the next step leading zeros are found and then ignored. At the same time, the number of digits of the target object is adjusted.
while ((*lastsrc_l == 0) && (*dest_l > 0)) { --lastsrc_l; --*dest_l; }
Now the relevant digits of the source object are copied into the goal object. Then the function is terminated.
while (src_l < lastsrc_l) { *++dest_l = *++src_l; } }
The exchange of the values of two CLINT
objects can be accomplished with the help of the macro SWAP_L
, the FLINT/C variant of the macro SWAP
, which manages in an interesting way to accomplish the exchange of two variables using XOR
operations without the requirement of intermediate storage in a temporary variable:
#define SWAP(a, b) ((a)^=(b), (b)^=(a), (a)^=(b)) #define SWAP_L(a_l, b_l) (xor_l((a_l), (b_l), (a_l)), xor_l((b_l), (a_l), (b_l)), xor_l((a_l), (b_l), (a_l)))
Function: | swap the values of two |
Syntax: | |
Input: |
|
Output: |
|
The functions in the FLINT/C library for the input and output of numbers in a form readable by human beings are not among the most exciting functions in this library, yet for many applications they are unavoidable. For practical reasons a form was selected to allow for the input and output by means of character strings, as vectors of type char
. For this the two essentially complementary functions str2clint_l()
and xclint2str_l()
were developed: The first transforms a character string with digits into a CLINT
object, and the second, conversely, transforms a CLINT
object into a character string. The base of the character string's representation is specified, with representations to bases in the range from 2 to 16 allowed.
The conversion to be carried out by the function str2clint_l()
of a representation of type CLINT
into a representation in the base specified is accomplished by means of a sequence of multiplications and additions to base B (cf. [Knut], Section 4.4). The function registers any overflow that occurs, the use of invalid bases, and the passing of the null pointer and returns the corresponding error code. Any prefixes indicating the number's representation, "0X
," "0x
," "0B
," or "0b
," are ignored.
Function: | conversion of a character string into a |
Syntax: |
|
Input: |
(base of the numerical representation of the character string, 2 ≤ |
Output: |
|
Return: |
|
int str2clint_l (CLINT n_l, char *str, USHORT base) { USHORT n; int error = E_CLINT_OK; if (str == NULL) { return E_CLINT_NPT; } if (2 > base || base > 16) { return E_CLINT_BOR; /* error: invalid base */ } SETZERO_L (n_l); if (*str == '0') { if ((tolower_l(*(str+1)) == 'x') || (tolower_l(*(str+1)) == 'b')) /* ignore any prefix */ { ++str; ++str; } } while (isxdigit ((int)*str) || isspace ((int)*str)) { if (!isspace ((int)*str)) { n = (USHORT)tolower_l (*str);
Many implementations of tolower()
from non-ANSI-conforming C libraries return undefined resultsif a character is not uppercase. The FLINT/C functiontolower_l()
calls tolower()
only for uppercase A–Z and otherwise returns thecharacter unchanged.
switch (n) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': n -= (USHORT)('a' -- 10); break; default: n -= (USHORT)'0'; } if (n >= base) { error = E_CLINT_BOR; break; } if ((error = umul_l (n_l, base, n_l)) != E_CLINT_OK) { break; } if ((error = uadd_l (n_l, n, n_l)) != E_CLINT_OK) { break; } } ++str; } return error; }
The function xclint2str_l()
, complementary to str2clint_l()
, returns a pointer to an internal buffer of storage class static
(cf. [Harb], Section 4.3), which contains the calculated numerical representation and its value until xclint2str_l()
is called again or the program is ended.
The function xclint2str_l()
carries the required conversion of the CLINT
representation into the representation to the specified base by means of a sequence of divisions with remainder to the base B.
Function: | Conversion of a |
Syntax: |
|
Input: |
|
Return: | pointer to the calculated character string if all ok
|
static char ntable[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; char * xclint2str_l (CLINT n_l, USHORT base, int showbase) { CLINTD u_l, r_l; int i = 0; static char N[CLINTMAXBIT + 3]; if (2U > base || base > 16U) { return (char *)NULL; /* error: invalid base */ } cpy_l (u_l, n_l); do { (void) udiv_l (u_l, base, u_l, r_l); if (EQZ_L (r_l)) { N[i++] = '0'; } else
{ N[i++] = (char) ntable[*LSDPTR_L (r_l) & 0xff]; } } while (GTZ_L (u_l)); if (showbase) { switch (base) { case 2: N[i++] = 'b'; N[i++] = '0'; break; case 8: N[i++] = '0'; break; case 16: N[i++] = 'x'; N[i++] = '0'; break; } } N[i] = '0'; return strrev_l (N); }
For reasons of compatibility with the function clint2str_l()
in the first edition of this book, clint2str_l(n_l, base)
was defined as a macro that calls the function xclint2str(n_l, base, 0)
.
Furthermore, macros HEXSTR_L()
, DECSTR_L()
, OCTSTR_L()
, and BINSTR_L()
were created, which create, from a passed CLINT
object as argument, a character string without prefix with the numerical representation specified by the macro name and thus eliminate the base of the representation as an argument (see Appendix C).
As standard form for the output of CLINT
values we have available the macroDISP_L()
, which processes a pointer to a character string and a CLINT
object as arguments. The character string contains, according to the purpose to which it will be put, information about the following CLINT
value to be output, such as "The product of a_l and b_l has the value
. . .." The output of the CLINT
value is in hexadecimal, that is, to base 16. Additionally, DISP_L()
outputs in a new line the number of significant binary digits (that is, without leading zeros) of the indicated CLINT
object (cf. Appendix C).
If there are to be conversions between byte vectors and CLINT
objects, then the pair of functions byte2clint_l()
and clint2byte_l()
can be employed (cf. [IEEE], 5.5.1).
It is assumed that the byte vectors embody a numerical representation to base 256 with values increasing from right to left. For the implementation of these functions the reader is referred to the file flint.c
. We give here only the function headers.
Function: | conversion of a byte vector into a |
Syntax: |
|
Input: |
|
Output: |
|
Return: |
|
Function: | conversion of a |
Syntax: |
|
Input: |
|
Output: |
|
Return: | pointer to the calculated byte vector
|
Finally, for the transformation of unsigned
values into the CLINT
numerical format the two functions u2clint_l()
and ul2clint_l()
can be used. The function u2clint_l()
converts USHORT
arguments, and the function ul2clint_l()
converts ULONG
arguments, into the CLINT
numerical format. The function ul2clint_l()
will be described in the following as an example.
Function: | conversion of a value of type |
Syntax: |
|
Input: |
|
Output: |
|
void ul2clint_l (CLINT num_l, ULONG ul) { *LSDPTR_L (num_l) = (USHORT)(ul & 0xffff); *(LSDPTR_L (num_l) + 1) = (USHORT)((ul >> 16) & 0xffff); SETDIGITS_L (num_l, 2); RMLDZRS_L (num_l); }
To end this chapter we shall discuss a function that carries out a validity check of a memory object for the CLINT
number format. Control functions of this type are called as needed whenever "foreign" values are imported into a system for further processing into a subsystem. Such a subsystem can be, for example, a cryptographic module that before every processing of input data must check whether it is dealing with valid values or arguments. Checking at run time whether the assumptions about the input values of a function have been met is good programming practice, one that helps to avoid undefined situations and that can contribute decisively to the stability of an application. For testing and debugging this usually takes place with assertions, with the help of which run-time conditions can be tested. Assertions are inserted as macros and can be decommissioned for the actual running of the program, usually during compilation via #define NDEBUG
. In addition to the assert
macro of the C standard library (see [Pla1], Chapter 1) there are a number of further implementations of similar mechanisms that take various actions when the test conditions are violated, such as listing recognized exceptional conditions in a log file, with or without program termination in the event of an error. For extensive information in this area the reader is referred to [Magu], Chapters 2 and 3, as well as [Murp], Chapter 4.
The protection of the functions of a program library like the FLINT/C package against being passed values that lie outside of the domain of definition of the respective parameters can occur within the invoked functions themselves or within the calling functions, where in the latter case the responsibility lies with the programmer who employs the library. For performance considerations, in the development of the FLINT/C functions we did not test every passed CLINT
argument for a valid address and possible overflow. The thought of carrying out multiply redundant checks of the numerical format in thousands of modular multiplications of an exponentiation moved the author to offload this control task to the programs that use the FLINT/C functions. An exception is the passing of divisors with the value zero, which is checked as a matter of principle and if it occurs is acknowledged with a suitable error notification, even in all the functions for residue class arithmetic. The code of all the functions was particularly carefully tested to make sure that the FLINT/C library generates only valid formats (cf. Chapter 12).
The function vcheck_l()
was created for the analysis of CLINT
arguments with regard to the validity of their format. It should help to protect the FLINT/C functions from being passed invalid parameters as CLINT
values.
Function: | test for a valid |
Syntax: |
|
Input: |
|
Return: |
errors and warnings according to Table 8-1 |
Table 8-1. Diagnostic values of the function vcheck_1()
|
|
|
---|---|---|
| Format is ok | Info: The number has a valid representation and a value with the range of definition of a |
| leading zeros | Warning: The number has leading zeros, but otherwise a valid definition within the range of definition. |
| memory error | Error: NULL Pointer was passed. |
| genuine overflow | Error: The passed number is too large; it cannot be represented as a CLINT object. |
int vcheck_l (CLINT n_l) { unsigned int error = E_VCHECK_OK;
Check for the null pointer: the ugliest error of them all.
if (n_l == NULL) { error = E_VCHECK_MEM; } else {
Check for overflow: Does the number have too many digits?
if (((unsigned int) DIGITS_L (n_l)) > CLINTMAXDIGIT) { error = E_VCHECK_OFL; } else {
Check for leading zeros: These we can live with ;-)
if ((DIGITS_L (n_l) > 0) && (n_l[DIGITS_L (n_l)] == 0)) { error = E_VCHECK_LDZ; } } } return error; }
The return values of the function are defined as macros in the file flint.h
. An explanation of these values is provided in Table 8-1.
The numeric values of the error codes are smaller than zero, so that a simple comparison with zero suffices to distinguish between errors on the one hand and warnings or the valid case on the other.
3.142.200.109