★ Related data type definition
It is necessary to define a variety of data types before doing business, and to avoid causing confusion in future coding.
Uintx x-bit unsigned shaping, such as UInt32 for 32-bit unsigned shaping
IntX x-bit signed shaping, such as int32 for 32-bit signed shaping
Basic Data type definitions:
#ifdef _msc_ver
typedef __int8 INT8;
typedef __int16 INT16;
typedef __int32 Int32;
typedef __int64 Int64;
typedef unsigned __INT8 uint8;
typedef unsigned __INT16 uint16;
typedef unsigned __INT32 UInt32;
typedef unsigned __INT64 UInt64;
#else
typedef signed CHAR int8;
typedef signed short Int16;
typedef signed INT int32;
typedef signed long long Int64;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned long long UInt64;
#endif
the type definitions here may not all be used, because they are used directly from my other projects.
Boolean type:
typedef UINT8 Boolean;
#define TRUE 1
#define FALSE 0
★ Definition of large integer type
The longest type of C language is 64 bits, and how should I save more than 64 bits of unsigned shaping? It is simple to store with multiple single-precision types.
For example, 1234 can be expressed as 1x1000+2x100+3x10+4x1, so it is easy to think of using decimal, each bit of the array is represented by 0 to 9, and then the number array according to written calculation principle of writing subtraction function, but this will be very inefficient, Because the large number of 1024-bit in the decimal also has more than 300, for any kind of operation, need to be in two has hundreds of elements of the array space repeated multiple cycles, but also need a lot of extra space to store the calculation of the advance and exit signs and intermediate results, the speed is conceivable, and for some shift operations, Using decimal can become cumbersome and slow. For procedural apes, binary thinking is important and is reflected here. For large integer calculations, you can use 2^n to simplify the calculation process (the 2^n is actually a binary reduction representation), and for a 32-bit system, n can take 32, so that the digits used to represent large integers are greatly reduced. For example, the decimal 4294967296 can be expressed as 1, 0 (2^32), 4294967297 can be expressed as 1, 1 (2^32), and processing shift calculation will be very convenient.
first define the single and double types:
typedef uint32 BN_DIGIT; //defines a large integer digit type, usually the length of the operating system, equivalent to a single precision type.
typedef UInt64 BN_UDBL; //define double-word unsigned shaping, which is 64 bits under a 32-bit system, equivalent to a double-precision type.
The reason for this definition is to facilitate porting, for 64-bit operating systems Bn_digit is 64-bit unsigned (in this case using 2^64), at which time Bn_udbl is undefined.
bignum structure Definition:
typedef struct
{
int sign; Symbol ID, 1 for non-negative integer, 1 for negative integer.
size_t Alloc; Represents the number of available digits before an array DP increases size, and the number of alloc must be a non-negative integer.
size_t used; Indicates how many bits are used by the array DP to represent a specified integer, and the number of used must be a non-negative integer.
Bn_digit *DP; The pointer DP points to the dynamically assigned array that represents the specified multi-precision integer, the alloc-used is all 0, and the data in the array is stored in the LSB order (low address save lower)
} bignum;
effective Bignum structure:
Given the efficiency and robustness of the code, several rules are specified for the state of the bignum structure (in some cases exceptions):
1. The value of the alloc is a nonnegative integer, that is, the DP either points to an array with pre-allocated space, or null.
2. The value of the used is a non-negative integer, and Used<=alloc.
The value of 3.used implies that the number of used-1 in the array DP cannot be 0, which means that the 0-headed high must be truncated, and the position of used and above in the array DP must be set to 0.
4. If used is 0, sign = 1, at which point the value of Bignum is 0 or DP simply initializes but does not allocate memory.
★ Parameter Transfer
during the development of any library, the Convention of function parameter passing should be determined as early as possible, so as to avoid difficulties in future development with the increase of library and complexity. In my large integer library, it is expressed in the form of an assignment. For example, the calculation of two large integers, a and a, and the result is placed in C, the function prototype is: int bn_add_bn (bignum *c, const bignum *a, const bignum *b); That is, C = a + B, the function returns an integer that indicates whether the calculation encountered an error, such as a memory allocation failure, and returns 0 indicating that the function call is normal.
★ Error Handling
In the large integer algorithm, the most likely problem is the dynamic memory allocation error, of course, there are some other problems, later in the discussion slowly. For memory errors, as soon as one appears, the subsequent calculations cannot be made, and you should exit immediately and return the error value. In my large number library, the following errors are defined:
Error Value
#define BN_MEMORY_ALLOCATE_FAIL-0X0001//Dynamic memory allocation error
#define BN_EXCEED_MAX_LIMB-0X0002 //out of maximum digits
#define BN_EXCEED_MAX_BITS-0X0003 //exceeds the maximum bit bit
#define BN_NEGATIVE_VALUE_ERROR-0X0004 //Negative number error
#define BN_INVALID_INPUT-0X0005 //Invalid input
#define BN_DIVISION_BY_ZERO-0X0006 //divided by 0 error
#define BN_BUFFER_TOO_SMALL-0X0007 //buffer is too small
#define BN_READ_FILE_ERROR-0X0008 //Read file error
#define BN_WRITE_FILE_ERROR-0X0009 //write file Errors
#define BN_NO_MODULAR_INVERSE-0X000A //modulo inverse not present
Error checking macros:
#define BN_CHECK (x) if (ret = x)! = 0) {goto clean;}
This macro is used to check the execution of each step of the function, once an error is detected (not a function return value of 0), that is, the return value of the function is set to the error value X, and then jump to the end of the function to perform cleanup operations (usually temporary variable dynamic memory release). This macro definition uses a goto statement, which usually avoids the use of goto statements, which is necessary in most cases, but it makes sense to use a few lines of code to handle errors when all functions are likely to fail.
The usual form of a function that is computed for a large integer is this:
int bn_function (bignum *b, const bignum *a)
{
int ret; If the Bn_check macro is used, the RET must be defined
Bignum C;
Bn_init (&C); Initialization of temporary variables
/**
* Do something here
*/
Bn_check (Bn_function2 (b, A, c)); Bn_check Check the bn_function2 of the call situation, if there is an error, jump directly to clean after the cleanup operation.
Clean
Bn_free (&C); Temporary variable memory release
return ret; Returns the error value if there is no error, ret = 0.
}
★ Summary
This article simply introduces the definition of a large integer, the next one is about to start talking about the most basic maintenance algorithm, mainly the initialization of large integers, increased accuracy, memory release and so on.
Large integer algorithm [01] representation and correlation definition of large integers