From 0c6a1120663fd13d675e9edcc721821d6dc27b25 Mon Sep 17 00:00:00 2001 From: Lamdonn Date: Fri, 27 Sep 2024 02:18:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=A7=E5=9E=8B=E6=95=B4?= =?UTF-8?q?=E6=95=B0=E8=AE=A1=E7=AE=97=E6=A8=A1=E5=9D=97intl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 1 + README.md | 1 + doc/intl.md | 229 ++++++++ source/01_general/intl.c | 975 +++++++++++++++++++++++++++++++++++ source/01_general/intl.h | 82 +++ source/01_general/intl_cfg.h | 224 ++++++++ test/test.mk | 3 +- test/test_intl.c | 105 ++++ 8 files changed, 1619 insertions(+), 1 deletion(-) create mode 100644 doc/intl.md create mode 100644 source/01_general/intl.c create mode 100644 source/01_general/intl.h create mode 100644 source/01_general/intl_cfg.h create mode 100644 test/test_intl.c diff --git a/README.en.md b/README.en.md index 3fb2c37..34d4fd3 100644 --- a/README.en.md +++ b/README.en.md @@ -29,6 +29,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with | tool | 01.00.00 | [link](/doc/tool.md) | ./source/01_general | General tools code | valloc | 01.00.00 | [link](/doc/valloc.md) | ./source/01_general | Dynamic memory usage testing tool | vlog | 01.01.00 | [link](/doc/vlog.md) | ./source/01_general | Log output module +| intl | 01.00.00 | [link](/doc/intl.md) | ./source/01_general | Large integer arithmetic module | vctype | 01.00.00 | [link](/doc/vctype.md) | ./source/02_vstd | Similar to the C standard library ctype | vmath | 01.00.00 | [link](/doc/vmath.md) | ./source/02_vstd | Similar to the C standard library math | vmem | 01.00.00 | [link](/doc/vmem.md) | ./source/02_vstd | Simple implementation of memory pool diff --git a/README.md b/README.md index b050d96..6aabafc 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用 | tool | 01.00.00 | [link](/doc/tool.md) | ./source/01_general | 通用工具代码 | valloc | 01.00.00 | [link](/doc/valloc.md) | ./source/01_general | 动态内存使用测试工具 | vlog | 01.01.00 | [link](/doc/vlog.md) | ./source/01_general | 日志输出模块 +| intl | 01.00.00 | [link](/doc/intl.md) | ./source/01_general | 大型整数运算模块 | vctype | 01.00.00 | [link](/doc/vctype.md) | ./source/02_vstd | 类似于C标准库ctype | vmath | 01.00.00 | [link](/doc/vmath.md) | ./source/02_vstd | 类似于C标准库math | vmem | 01.00.00 | [link](/doc/vmem.md) | ./source/02_vstd | 内存池的简单实现 diff --git a/doc/intl.md b/doc/intl.md new file mode 100644 index 0000000..43da11f --- /dev/null +++ b/doc/intl.md @@ -0,0 +1,229 @@ +## 介绍 + +`intl`为一个大型整数模块,该模块允许对超过 C 语言标准数据类型限制的大整数进行操作。它提供了一个灵活的长整型表示,使用 16 位和 32 位段来存储。 + +现在主流的计算机系统中一般支持的最大整形数为 32bits 或者 64bits 的,其能满足在一般情况下的计算及存储,但是面对一些更大的数时候就会显得有心无力。 +而`intl`模块作为额外的整型数扩充,采用了一致的存储机制,而且在使用起来也非常方便。提供以下的计算: + +- 支持基本算术操作:加法、减法、乘法、除法和取模。 +- 位运算:与、或、异或、非。 +- 移位操作:左移和右移。 +- 将字符串和标准整数转换为大整数的功能。 +- 提供以十进制、十六进制和二进制格式输出大整数的函数。 + +`intl` 可以按需配置为 128bits、256bits、512bits、1024bits、2048bits、4096bits、8192bits,如果都还不满足,还提供了生成更大bits的函数来生成更大的配置。 + + +## 接口 + +## 结构定义 + +### `intl` + +一个结构体,用于表示长整型整数,使用联合体以两种不同方式存储整数: + +- 一个 `uint16_t` 类型的数组(16 位段)。 +- 一个 `uint32_t` 类型的数组(32 位段)。 + +```c +typedef struct { + union { + uint16_t u16[INTL_U16_PARTS]; + uint32_t u32[INTL_U32_PARTS]; + }; +} intl; +``` + +### 定义函数 + +```c +intl intl_from(const char *str); // +intl intl_from2(int value); // +``` + +`intl`提供了两种定义函数。 + +`intl_from`为从数字字符串中解析生成一个`intl`,支持解析十进制、二进制、八进制、十六进制字符串。 +`intl_from2`为从`int`型中创建一个`intl`,此函数封装成更简短的宏定义方法`intl()`更贴近使用习惯,此方法执行效率更高。 + +两者的使用,当值在`int`的取值范围内时,推荐使用`intl()`方法,而当数值超越`int`的取值范围时或者需要不同进制进行定义时,就可以使用`intl_from()`方法。 + +同时,也可以使用常用值的宏定义`INTL_MAX`、`INTL_MIN`、`INTL_ZERO`。 + +例子: +```c +static void test_define(void) +{ + intl a = intl(0); + intl b = intl(10); + intl c = intl(-10); + intl d = intl(0xFF); + + intl e = intl_from("0"); + intl f = intl_from("100"); + intl g = intl_from("-100"); + intl h = intl_from("123456789123456789123456789"); + intl i = intl_from("0b1110000001111100101010100"); + intl j = intl_from("0o1236541236763210233642166"); + intl k = intl_from("0xF125E3F6D743648EEFFF12356"); + + intl max = INTL_MAX; + intl min = INTL_MIN; + intl n0 = INTL_ZERO; +} +``` + +### 转换函数 + +```c +const char* intl_sdec(intl a, char buffer[INTL_MAX_DEC]); +const char* intl_shex(intl a, char buffer[INTL_MAX_HEX]); +const char* intl_sbin(intl a, char buffer[INTL_MAX_BIN]); +``` + +此三个函数使用方法上是一致的,把`a`的值转换成十进制、十六进制、二进制到传入的`buffer`中,并返回有效位的`buffer`地址。 + +```c +#define INTL_P_DEC 0x01 +#define INTL_P_BIN 0x02 +#define INTL_P_HEX 0x04 + +static void print_intl(intl a, int type) +{ + char buffer[INTL_MAX_BIN]; + if (type & INTL_P_DEC) printf("intl (decimal): %s\n", intl_sdec(a, buffer)); + if (type & INTL_P_HEX) printf("intl (hex): %s\n", intl_shex(a, buffer)); + if (type & INTL_P_BIN) printf("intl (bin): %s\n", intl_sbin(a, buffer)); +} + +static void test_print(void) +{ + intl a = intl_from("123456789123456789123456789"); + + print_intl(a, INTL_P_DEC); + print_intl(a, INTL_P_BIN); + print_intl(a, INTL_P_HEX); +} +``` + +结果: +``` +intl (decimal): 123456789123456789123456789 +intl (bin): 110011000011110111111011111001011100011101100011001111101111100000001000101111100010101 +intl (hex): 661EFDF2E3B19F7C045F15 +``` + +### 运算函数 + +```c +intl intl_add(intl a, intl b); // a+b +intl intl_sub(intl a, intl b); // a-b +intl intl_mul(intl a, intl b); // a*b +intl intl_div(intl a, intl b); // a/b +intl intl_mod(intl a, intl b); // a%b +intl intl_and(intl a, intl b); // a&b +intl intl_xor(intl a, intl b); // a^b +intl intl_or(intl a, intl b); // a|b +intl intl_shl(intl a, uint32_t amount); // a<>amount +intl intl_not(intl a); // ~a +intl intl_abs(intl a); // |a| +intl intl_inc(intl a); // a++ +intl intl_dec(intl a); // a-- +intl intl_neg(intl a); // -a +int intl_cmp(intl a, intl b); // a>b a=b a<=b +int intl_sign(intl a); // a>0 a<0 a==0 +``` + +`intl`运算函数与类似符号运算一致。 + +使用例子: +```c +static void test_calculate(void) +{ + intl a = intl_from("123456789123456789123456789"); + intl b = intl_from("987654321987654321987654321"); + + printf("a: \r\n"); + print_intl(a, INTL_P_DEC); + printf("b: \r\n"); + print_intl(b, INTL_P_DEC); + + printf("a + b: \r\n"); + print_intl(intl_add(a, b), INTL_P_DEC); + + printf("a - b: \r\n"); + print_intl(intl_sub(a, b), INTL_P_DEC); + + printf("a * b: \r\n"); + print_intl(intl_mul(a, b), INTL_P_DEC); + + printf("b / a: \r\n"); + print_intl(intl_div(b, a), INTL_P_DEC); + + printf("b %% a: \r\n"); + print_intl(intl_mod(b, a), INTL_P_DEC); + + printf("a & b: \r\n"); + print_intl(intl_and(a, b), INTL_P_DEC); + + printf("a | b: \r\n"); + print_intl(intl_or(a, b), INTL_P_DEC); + + printf("a ^ b: \r\n"); + print_intl(intl_xor(a, b), INTL_P_DEC); + + printf("~a: \r\n"); + print_intl(intl_not(a), INTL_P_DEC); + + printf("a << 1: \r\n"); + print_intl(intl_shl(a, 1), INTL_P_DEC); + + printf("b >> 1: \r\n"); + print_intl(intl_shr(b, 1), INTL_P_DEC); + + printf("compare: %d\r\n", intl_cmp(a, b)); + + printf("a incremented: \r\n"); + print_intl(intl_inc(a), INTL_P_DEC); + + printf("b decremented: \r\n"); + print_intl(intl_dec(b), INTL_P_DEC); +} +``` + +结果: +``` +a: +intl (decimal): 123456789123456789123456789 +b: +intl (decimal): 987654321987654321987654321 +a + b: +intl (decimal): 1111111111111111111111111110 +a - b: +intl (decimal): -864197532864197532864197532 +a * b: +intl (decimal): 121932631356500531591068431581771069347203169112635269 +b / a: +intl (decimal): 8 +b % a: +intl (decimal): 9000000009000000009 +a & b: +intl (decimal): 38793946662829560778723857 +a | b: +intl (decimal): 1072317164448281550332387253 +a ^ b: +intl (decimal): 1033523217785451989553663396 +~a: +intl (decimal): -123456789123456789123456790 +a << 1: +intl (decimal): 246913578246913578246913578 +b >> 1: +intl (decimal): 493827160993827160993827160 +compare: -1 +a incremented: +intl (decimal): 123456789123456789123456790 +b decremented: +intl (decimal): 987654321987654321987654320 +``` + diff --git a/source/01_general/intl.c b/source/01_general/intl.c new file mode 100644 index 0000000..6eff846 --- /dev/null +++ b/source/01_general/intl.c @@ -0,0 +1,975 @@ +/********************************************************************************************************* + * ------------------------------------------------------------------------------------------------------ + * file description + * ------------------------------------------------------------------------------------------------------ + * \file intl.c + * \unit intl + * \brief This is a simple large inter number calculate module for C language + * \author Lamdonn + * \version v1.0.0 + * \license GPL-2.0 + * \copyright Copyright (C) 2023 Lamdonn. + ********************************************************************************************************/ +#include "intl.h" + +/* Internal static function declarations */ + +static int intl_ucmp(intl a, intl b); +static intl intl_umul(intl a, intl b); +static intl intl_udiv(intl a, intl b, intl *mod); +static intl intl_umod(intl a, intl b); + +/** + * \brief Adds two intl numbers. + * + * This function computes the sum of two 128-bit integers + * (intl). It processes each 16-bit part of the input integers, + * handling carry bits as necessary. The result is stored in a + * new intl number. This function ensures that overflow is + * correctly managed across all parts. + * + * \param[in] a: The first operand (intl number). + * \param[in] b: The second operand (intl number). + * \return The sum of a and b as an intl (128-bit integer). + */ +intl intl_add(intl a, intl b) +{ + intl result; // Initialize the result variable + uint16_t carry = 0; /** Carry bit */ + + // Perform addition for each 16-bit part + for (int i = 0; i < INTL_U16_PARTS; i++) + { + // Calculate the sum of corresponding parts and carry + uint32_t sum = (uint32_t)a.u16[i] + (uint32_t)b.u16[i] + carry; + result.u16[i] = (uint16_t)(sum & 0xFFFF); /** Lower 16 bits */ + carry = (sum >> 16) & 0xFFFF; /** Upper 16 bits as carry */ + } + + return result; // Return the resulting intl number +} + +/** + * \brief Subtracts one intl number from another. + * + * This function computes the difference of two 128-bit integers + * (intl). It processes each 16-bit part of the minuend and + * subtrahend, handling borrow bits as necessary. The result is + * stored in a new intl number. This function ensures that + * borrowing is correctly managed across all parts. + * + * \param[in] a: The minuend (the number from which another is to be subtracted). + * \param[in] b: The subtrahend (the number to be subtracted). + * \return The result of a - b as an intl (128-bit integer). + */ +intl intl_sub(intl a, intl b) +{ + intl result; // Initialize the result variable + + // Perform subtraction for each 16-bit part + for (int i = 0; i < INTL_U16_PARTS; i++) + { + uint32_t diff = (uint32_t)a.u16[i] - (uint32_t)b.u16[i]; + + // Check if a borrow occurred + if (diff & 0xFFFF0000) /** Borrow occurred */ + { + // Adjust the higher parts to account for the borrow + for (int j = i + 1; j < INTL_U16_PARTS; j++) + { + a.u16[j] -= 1; // Borrow from the next part + if (a.u16[j] != 0xFFFF) break; // Stop if no further borrow needed + } + } + + // Store the result of the subtraction + result.u16[i] = (uint16_t)(diff & 0xFFFF); + } + + return result; // Return the resulting intl number +} + +/** + * \brief Increments the intl number by one. + * + * This function increments a 128-bit integer (intl) by one. + * It processes each 32-bit part of the input integer and + * handles carry bits as necessary. The function continues + * to increment the subsequent parts until there is no overflow. + * + * \param[in] a: The intl number to increment. + * \return The incremented intl number as an intl (128-bit integer). + */ +intl intl_inc(intl a) +{ + // Increment each 32-bit part of the intl number + for (int i = 0; i < INTL_U32_PARTS; i++) + { + a.u32[i]++; // Increment the current part + // Check if the current part overflowed + if (a.u32[i] != 0) + { + break; /** Return immediately if no overflow */ + } + } + return a; /** Return the incremented result */ +} + +/** + * \brief Decrements the intl number by one. + * + * This function decrements a 128-bit integer (intl) by one. + * It processes each 32-bit part of the input integer, handling + * borrowing as necessary. If the current part is zero, it sets + * that part to its maximum value (0xFFFFFFFF) and continues + * to the next part to borrow from it. + * + * \param[in] a: The intl number to decrement. + * \return The decremented intl number as an intl (128-bit integer). + */ +intl intl_dec(intl a) +{ + // Decrement each 32-bit part of the intl number + for (int i = 0; i < INTL_U32_PARTS; i++) + { + // Check if the current part can be decremented + if (a.u32[i] != 0) + { + a.u32[i]--; // Decrement the current part + break; /** Return immediately if current part can be decremented */ + } + // If current part is zero, set it to max value and continue borrowing + a.u32[i] = 0xFFFFFFFF; + } + return a; /** Return the decremented result */ +} + +/** + * \brief Multiplies two intl unsigned numbers. + * + * This function performs multiplication of two 128-bit unsigned integers + * (intl) by using a method similar to the schoolbook algorithm. The + * multiplication is carried out by breaking the numbers into their + * 16-bit components and accumulating the results. The function handles + * carry-over during the multiplication and addition stages to ensure + * the final product is accurately represented. + * + * \param[in] a: The first operand to multiply. + * \param[in] b: The second operand to multiply. + * \return The product of a and b as an intl (128-bit unsigned integer). + */ +static intl intl_umul(intl a, intl b) +{ + intl result = {0}; /** Initialize the result to 0 */ + intl temp[INTL_U16_PARTS] = {{0}}; // Temporary storage for intermediate results + uint16_t carry = 0; // Variable to hold carry-over during multiplication + + // Perform multiplication + for (int i = 0; i < INTL_U16_PARTS; i++) + { + carry = 0; // Reset carry for the current row + for (int j = 0; j < INTL_U16_PARTS; j++) + { + if (i + j < INTL_U16_PARTS) + { + // Multiply the 16-bit segments and add carry + uint32_t mul = (uint32_t)a.u16[i] * (uint32_t)b.u16[j] + carry; + temp[i].u16[i + j] = (mul & 0xFFFF); // Store the lower 16 bits + carry = ((mul >> 16) & 0xFFFF); // Update carry for the next addition + } + } + } + + carry = 0; // Reset carry for the addition phase + // Combine results from the temporary storage + for (int i = 0; i < INTL_U16_PARTS; i++) + { + uint32_t add = 0; // Variable to hold the sum of the current column + for (int j = 0; j < INTL_U16_PARTS; j++) + { + add += temp[j].u16[i]; // Accumulate results from temp + } + add += carry; // Add any carry from the previous column + result.u16[i] = (add & 0xFFFF); // Store the lower 16 bits in result + carry = ((add >> 16) & 0xFFFF); // Update carry for the next column + } + + return result; // Return the final product +} + +/** + * \brief Multiplies two intl numbers. + * + * This function multiplies two 128-bit integers (intl) and returns + * the product as another intl number. It handles signed multiplication + * by checking the sign of the operands. If either operand is negative, + * it negates the operand and adjusts the sign of the result accordingly. + * The actual multiplication is performed using the `intl_umul` + * function, which handles the absolute values of the integers. + * + * \param[in] a: The first operand to multiply. + * \param[in] b: The second operand to multiply. + * \return The product of a and b as an intl (128-bit integer). + */ +intl intl_mul(intl a, intl b) +{ + intl result = {0}; // Initialize the result to 0 + int sign = 1; // Variable to track the sign of the result + + // Check and handle the sign of the first operand + if (a.u32[INTL_U32_PARTS - 1] & 0x80000000) + { + sign = -sign; // Negate the sign for the result + a = intl_neg(a); // Negate the first operand + } + + // Check and handle the sign of the second operand + if (b.u32[INTL_U32_PARTS - 1] & 0x80000000) + { + sign = -sign; // Negate the sign for the result + b = intl_neg(b); // Negate the second operand + } + + // Perform unsigned multiplication + result = intl_umul(a, b); + + // If the result should be negative, negate it + if (sign < 0) + result = intl_neg(result); + + return result; // Return the final product +} + +/** + * \brief Divides one intl unsigned number by another. + * + * This function performs division of one 128-bit unsigned integer (intl) + * by another. It calculates the quotient using a bitwise approach, + * handling division by zero gracefully. The result is built bit by bit + * from the most significant bit to the least significant bit. If the + * divisor is zero, it prints an error message and returns zero. + * + * \param[in] a: The dividend (number to be divided). + * \param[in] b: The divisor (number to divide by). + * \return The quotient of a divided by b as an intl (128-bit unsigned integer). + */ +static intl intl_udiv(intl a, intl b, intl *mod) +{ + // Check for division by zero + if (intl_sign(b) == 0) + { + printf("Division by zero!\n"); + return (intl){0}; /** Handle division by zero */ + } + + intl result = {0}; // Initialize the result to zero + intl remainder = {0}; // Initialize the remainder to zero + + /** Calculate bit by bit from the highest bit */ + for (int i = INTL_BIT_PARTS - 1; i >= 0; i--) + { + /** Left shift remainder and add current bit */ + remainder = intl_shl(remainder, 1); // Shift remainder left by 1 + remainder.u32[0] |= (a.u32[i / 32] >> (i % 32)) & 1; // Add current bit from dividend + + /** If remainder is greater than or equal to b, subtract b */ + if (intl_ucmp(remainder, b) >= 0) + { + remainder = intl_sub(remainder, b); // Subtract b from remainder + result.u32[i / 32] |= (1 << (i % 32)); // Set corresponding bit in result + } + } + + if (mod) *mod = remainder; + + return result; // Return the final quotient +} + +/** + * \brief Divides one intl number by another. + * + * This function performs division of two 128-bit integers (intl) + * and returns the quotient as another intl number. It handles signed + * division by checking the sign of the operands. If either operand + * is negative, it negates the operand and adjusts the sign of the + * result accordingly. The actual division is performed using the + * `intl_udiv` function, which handles the absolute values of + * the integers. + * + * \param[in] a: The dividend (number to be divided). + * \param[in] b: The divisor (number to divide by). + * \return The quotient of a divided by b as an intl (128-bit integer). + */ +intl intl_div(intl a, intl b) +{ + intl result = {0}; // Initialize the result to 0 + int sign = 1; // Variable to track the sign of the result + + // Check and handle the sign of the dividend + if (a.u32[INTL_U32_PARTS - 1] & 0x80000000) + { + sign = -sign; // Negate the sign for the result + a = intl_neg(a); // Negate the dividend + } + + // Check and handle the sign of the divisor + if (b.u32[INTL_U32_PARTS - 1] & 0x80000000) + { + sign = -sign; // Negate the sign for the result + b = intl_neg(b); // Negate the divisor + } + + // Perform unsigned division + result = intl_udiv(a, b, NULL); + + // If the result should be negative, negate it + if (sign < 0) + result = intl_neg(result); + + return result; // Return the final quotient +} + +/** + * \brief Computes the remainder of the division of two unsigned intl numbers. + * + * This function calculates the remainder of the division of two + * 128-bit unsigned integers (intl). It uses a bitwise approach to + * compute the remainder by processing each bit from the most significant + * to the least significant. If the divisor is zero, it handles the + * error gracefully by printing a message and returning zero. + * + * \param[in] a: The dividend (number to be divided). + * \param[in] b: The divisor (number to divide by). + * \return The remainder of a divided by b as an intl (128-bit unsigned integer). + */ +static intl intl_umod(intl a, intl b) +{ + intl mod = {0}; + intl_udiv(a, b, &mod); + return mod; +} + +/** + * \brief Computes the remainder of the division of two intl numbers. + * + * This function calculates the remainder of the division of two + * 128-bit integers (intl). It handles signed integers by checking + * the sign of the dividend. If the dividend is negative, it negates + * the dividend before performing the unsigned modulus operation. + * The sign of the result is adjusted based on the sign of the + * dividend. The actual remainder calculation is performed using the + * `intl_umod` function, which handles the absolute values of + * the integers. + * + * \param[in] a: The dividend (number to be divided). + * \param[in] b: The divisor (number to divide by). + * \return The remainder of a divided by b as an intl (128-bit integer). + */ +intl intl_mod(intl a, intl b) +{ + intl result = {0}; // Initialize result to zero + int sign = 1; // Variable to track the sign of the result + + // Check and handle the sign of the dividend + if (a.u32[INTL_U32_PARTS - 1] & 0x80000000) + { + sign = -sign; // Negate the sign for the result + a = intl_neg(a); // Negate the dividend + } + + // Perform unsigned modulus with the absolute value of the divisor + result = intl_umod(a, intl_abs(b)); + + // If the result should be negative, negate it + if (sign < 0) + result = intl_neg(result); + + return result; // Return the final remainder +} + +/** + * \brief Left shifts an intl number by a specified number of bits. + * + * This function performs a left bitwise shift on a 128-bit integer + * (intl). The shift amount can be greater than 32 bits, in which case + * the function calculates how many whole 32-bit parts to shift and + * how many bits to shift within the remaining part. It constructs + * the result based on the input number after applying the shift. + * + * \param[in] a: The intl number to shift. + * \param[in] amount: The number of bits to shift to the left. + * \return The left-shifted intl number. + */ +intl intl_shl(intl a, uint32_t amount) +{ + intl result = {0}; // Initialize the result to zero + int u32bias = amount / 32; // Number of whole 32-bit parts to shift + int bitsbias = amount % 32; // Remaining bits to shift + + // Perform the shift for each 32-bit part of the intl number + for (int i = 0; i < INTL_U32_PARTS; i++) + { + if (i < u32bias) + { + result.u32[i] = 0; // Set shifted-out parts to zero + } + else + { + // Shift the current part and add bits from the previous part if needed + result.u32[i] = (a.u32[i - u32bias] << bitsbias) | + (((i - u32bias - 1) >= 0 && bitsbias > 0) ? + (a.u32[i - u32bias - 1] >> (32 - bitsbias)) : 0); + } + } + return result; // Return the left-shifted result +} + +/** + * \brief Right shifts an intl number by a specified number of bits. + * + * This function performs a right bitwise shift on a 128-bit integer + * (intl). The shift amount can be greater than 32 bits, in which case + * the function calculates how many whole 32-bit parts to shift and + * how many bits to shift within the remaining part. It constructs + * the result based on the input number after applying the shift. + * The sign bit is preserved for signed shifts. + * + * \param[in] a: The intl number to shift. + * \param[in] amount: The number of bits to shift to the right. + * \return The right-shifted intl number. + */ +intl intl_shr(intl a, uint32_t amount) +{ + intl result = {0}; // Initialize the result to zero + int u32bias = amount / 32; // Number of whole 32-bit parts to shift + int bitsbias = amount % 32; // Remaining bits to shift + + // Perform the shift for each 32-bit part of the intl number + for (int i = 0; i < INTL_U32_PARTS; i++) + { + // Check if the current index is beyond the range for valid shifts + if (i > INTL_U32_PARTS - u32bias - 1 && INTL_U32_PARTS - u32bias - 1 > 0) + { + result.u32[i] = 0; // Set shifted-out parts to zero + } + else + { + // Shift the current part and add bits from the next part if needed + result.u32[i] = (a.u32[i + u32bias] >> bitsbias) | + (((i + u32bias + 1) < INTL_U32_PARTS && bitsbias > 0) ? + (a.u32[i + u32bias + 1] << (32 - bitsbias)) : + ((a.u32[INTL_U32_PARTS - 1] & 0x80000000) ? 0xFFFFFFFF : 0)); + } + } + + return result; // Return the right-shifted result +} + +/** + * \brief Performs bitwise AND operation on two intl numbers. + * + * This function computes the bitwise AND of two 128-bit integers + * (intl). It processes each 32-bit part of the input integers and + * performs the AND operation on corresponding parts, storing the + * result in a new intl number. This operation yields a number that + * has bits set only where both operands have bits set. + * + * \param[in] a: The first operand (intl number). + * \param[in] b: The second operand (intl number). + * \return The result of a AND b as an intl (128-bit integer). + */ +intl intl_and(intl a, intl b) +{ + intl result; // Initialize the result variable + // Perform the bitwise AND operation for each 32-bit part + for (int i = 0; i < INTL_U32_PARTS; i++) + { + result.u32[i] = a.u32[i] & b.u32[i]; // Compute AND for each part + } + return result; // Return the resulting intl number +} + +/** + * \brief Performs bitwise OR operation on two intl numbers. + * + * This function computes the bitwise OR of two 128-bit integers + * (intl). It processes each 32-bit part of the input integers and + * performs the OR operation on corresponding parts, storing the + * result in a new intl number. This operation yields a number that + * has bits set where at least one of the operands has bits set. + * + * \param[in] a: The first operand (intl number). + * \param[in] b: The second operand (intl number). + * \return The result of a OR b as an intl (128-bit integer). + */ +intl intl_or(intl a, intl b) +{ + intl result; // Initialize the result variable + // Perform the bitwise OR operation for each 32-bit part + for (int i = 0; i < INTL_U32_PARTS; i++) + { + result.u32[i] = a.u32[i] | b.u32[i]; // Compute OR for each part + } + return result; // Return the resulting intl number +} + +/** + * \brief Performs bitwise XOR operation on two intl numbers. + * + * This function computes the bitwise XOR of two 128-bit integers + * (intl). It processes each 32-bit part of the input integers and + * performs the XOR operation on corresponding parts, storing the + * result in a new intl number. This operation yields a number that + * has bits set where only one of the operands has bits set. + * + * \param[in] a: The first operand (intl number). + * \param[in] b: The second operand (intl number). + * \return The result of a XOR b as an intl (128-bit integer). + */ +intl intl_xor(intl a, intl b) +{ + intl result; // Initialize the result variable + // Perform the bitwise XOR operation for each 32-bit part + for (int i = 0; i < INTL_U32_PARTS; i++) + { + result.u32[i] = a.u32[i] ^ b.u32[i]; // Compute XOR for each part + } + return result; // Return the resulting intl number +} + +/** + * \brief Performs bitwise NOT operation on an intl number. + * + * This function computes the bitwise NOT (negation) of a 128-bit + * integer (intl). It processes each 32-bit part of the input integer + * and applies the NOT operation, storing the result in a new intl + * number. This operation inverts all bits of the input number. + * + * \param[in] a: The intl number to negate. + * \return The bitwise negation of a as an intl (128-bit integer). + */ +intl intl_not(intl a) +{ + intl result; // Initialize the result variable + // Perform the bitwise NOT operation for each 32-bit part + for (int i = 0; i < INTL_U32_PARTS; i++) + { + result.u32[i] = ~a.u32[i]; // Compute NOT for each part + } + return result; // Return the resulting intl number +} + +/** + * \brief Computes the absolute value of an intl number. + * + * This function checks if the given 128-bit unsigned integer (intl) + * represents a negative value in two's complement representation. + * If the most significant bit (sign bit) of the highest 32-bit segment + * is set, it indicates a negative number, and the function calls + * intl_neg to return its positive equivalent. If the number is + * already non-negative, it simply returns the original number. + * + * \param[in] a: The intl number for which to compute the absolute value. + * \return The absolute value of the intl number a. + */ +intl intl_abs(intl a) +{ + // Check if the sign bit of the highest 32-bit part is set + if (a.u32[INTL_U32_PARTS - 1] & 0x80000000) + return intl_neg(a); // Return negated value if negative + return a; // Return the original value if non-negative +} + +/** + * \brief Converts a string to an intl number. + * + * This function converts a string representation of a number + * in various bases (decimal, binary, octal, hexadecimal) + * into a 128-bit integer (intl). It handles optional signs + * and base prefixes, and processes the string from the end to + * the start for efficiency in base conversions. + * + * \param[in] str: The string to convert. + * \return The converted intl number. Returns zero if the + * string is invalid or represents zero. + */ +intl intl_from(const char *str) +{ + const uint8_t ttable[4] = {10, 2, 8, 16}; // Table of digit limits for each base + const uint8_t btable[4] = {0, 1, 3, 4}; // Table of bit shifts for each base + uint32_t type = 0; // 0 - decimal, 1 - binary, 2 - octal, 3 - hexadecimal + intl result = {0}; // Resulting intl number + intl base = intl(1); /** Base initialized to 1 */ + int sign = 1; // Sign of the number (1 for positive, -1 for negative) + const char *p = str; // Pointer to traverse the input string + + // Determine the number type based on the string prefix + switch (*p) + { + case '0': { + if (p[1] == 0) { return (intl){0}; } // Handle case of "0" + else if (p[1] == 'x' || p[1] == 'X') type = 3; // Hexadecimal + else if (p[1] == 'o' || p[1] == 'O') type = 2; // Octal + else if (p[1] == 'b' || p[1] == 'B') type = 1; // Binary + else if (p[1] < '0' || p[1] >= '0' + ttable[type]) return (intl){0}; + p += 2; // Move past the prefix + } break; + case '-': { sign = -1; } // Handle negative sign + case '+': { p++; } break; // Handle positive sign + default: + break; // No sign or prefix + } + + uint32_t len = strlen(p); // Length of the number string + const char *s = &p[len - 1]; // Pointer to the last character of the number string + + // Process decimal numbers + if (type == 0) + { + while (s >= p) // Traverse the string backwards + { + char c = *s; + if (c >= '0' && c <= '9') + { + uint32_t num = c - '0'; /** Convert character to number */ + + /** Process current digit */ + intl addend = intl_umul(base, (intl){num}); // Multiply base by the digit + result = intl_add(result, addend); // Add to result + + /** Multiply base by 10 for the next digit */ + base = intl_umul(base, (intl){10}); + } + else + { + printf("Invalid character in input string: %c\n", *s); // Error handling + return (intl){0}; // Return zero for invalid input + } + s--; // Move to the previous character + } + + if (sign == -1) // Apply sign if negative + { + result = intl_neg(result); + } + } + else + { + // Process non-decimal bases (binary, octal, hexadecimal) + uint8_t bit = 0; // Bit position within the current u32 part + int index = 0; // Current index in the result array + while (s >= p && index < INTL_U32_PARTS) + { + char c = *s; + // Adjust character for hex representation + if (c >= 'A' && c <= 'F') c -= 7; // A-F to 10-15 + else if (c >= 'a' && c <= 'f') c -= 39; // a-f to 10-15 + + // Validate and process the character + if (c >= '0' && c < ('0' + ttable[type])) + { + result.u32[index] |= ((c - '0') << bit); // Set the value in the corresponding bit + bit += btable[type]; // Update the bit position + if (bit >= 32) // If bit exceeds 32, move to the next part + { + bit -= 32; // Reset bit position + index++; // Move to the next u32 part + } + } + else + { + printf("Invalid character in input string: %c\n", *s); // Error handling + return (intl){0}; // Return zero for invalid input + } + s--; // Move to the previous character + } + } + + return result; // Return the resulting intl number +} + +/** + * \brief Converts a 32-bit unsigned integer to an intl number. + * + * This function initializes the first 32-bit segment of the intl structure + * with the provided 32-bit unsigned integer value, while the other segments + * are set to zero. This allows for easy conversion from a standard integer type + * to the custom 128-bit representation. + * + * \param[in] value: The 32-bit unsigned integer to convert. + * \return The corresponding intl number initialized with the given value. + */ +intl intl_from2(int value) +{ + intl result = {0}; + memcpy(&result, &value, sizeof(value)); + if (value < 0) + { + memset(((char *)(&result)) + sizeof(value), -1, sizeof(result) - sizeof(value)); + } + return result; +} + +/** + * \brief Determines the sign of an intl number. + * + * This function checks the sign of the given 128-bit unsigned integer + * (intl) based on its representation. It first examines the most significant + * bit of the highest 32-bit segment to determine if the number is negative. + * If this bit is set, the function returns -1, indicating a negative value. + * If all segments are zero, it returns 0, indicating that the number is zero. + * If the number is positive, it returns 1. + * + * \param[in] a: The intl number to evaluate for its sign. + * \return -1 if the number is negative, 0 if the number is zero, and + * 1 if the number is positive. + */ +int intl_sign(intl a) +{ + // Check if the sign bit of the highest 32-bit part is set + if (a.u32[INTL_U32_PARTS - 1] & 0x80000000) return -1; + + // Check if the number is zero + for (int i = INTL_U32_PARTS - 1; i >= 0; i--) + { + if (a.u32[i] != 0) return 1; // Return 1 if any part is non-zero + } + + return 0; // Return 0 if all parts are zero +} + +/** + * \brief Converts an intl number to a decimal string. + * + * This function converts a 128-bit integer (intl) into its + * decimal string representation. The resulting string is + * constructed by repeatedly dividing the number by 10 and + * storing the remainders. The function handles both positive + * and negative integers, including the special case where the + * number is zero. + * + * \param[in] a: The intl number to convert. + * \param[out] buffer: The buffer to store the resulting decimal string. + * + * \return A pointer to the resulting decimal string. + */ +const char* intl_sdec(intl a, char buffer[INTL_MAX_DEC]) +{ + intl ten = intl(10); /** Base 10 for conversion */ + intl remainder; // To hold the remainder during division + intl temp = a; // Temporary variable for manipulation + char *p = buffer; // Pointer for writing to the buffer + + // Check if the number is negative + if (a.u32[INTL_U32_PARTS - 1] & 0x80000000) // Check sign bit + { + temp = intl_neg(a); // Negate the number for conversion + } + + /** Calculate decimal string of intl */ + while (intl_ucmp(temp, (intl){0}) > 0) // While the number is positive + { + remainder = intl_umod(temp, ten); // Get remainder when divided by 10 + char digit = '0' + remainder.u32[0]; // Convert remainder to character + *p++ = digit; // Store character in buffer + temp = intl_udiv(temp, ten, NULL); // Update temp for integer division by 10 + } + + // Handle special case for 0 + if (p == buffer) + { + *p++ = '0'; // If no digits were added, it's zero + *p = 0; // Null-terminate the string + } + else + { + // If the original number was negative, add '-' sign + if (a.u32[INTL_U32_PARTS - 1] & 0x80000000) + { + *p++ = '-'; // Append negative sign + } + *p-- = 0; // Null-terminate the string + + // Reverse the string to correct the order of digits + char *s = buffer; // Pointer to the start of the buffer + while (s < p) // Swap characters + { + char t = *s; // Temporary variable for swapping + *s = *p; // Swap start and end + *p = t; + s++; // Move pointers towards the center + p--; + } + } + + return buffer; // Return the resulting decimal string +} + +/** + * \brief Converts an intl number to a hexadecimal string. + * + * This function converts a 128-bit integer (intl) into a + * hexadecimal string representation. The resulting string is + * formatted as a sequence of hexadecimal digits, with each + * part of the intl number contributing 8 digits to the final + * string. The string is stored in the provided buffer. + * + * \param[in] a: The intl number to convert. + * \param[out] buffer: The buffer to store the resulting hexadecimal string. + * + * \return A pointer to the resulting hexadecimal string. + */ +const char* intl_shex(intl a, char buffer[INTL_MAX_HEX]) +{ + char temp[9]; /** Temporary buffer for each part (8 hex digits + null terminator) */ + char skip = 1; /** Skip invalid 0 */ + char *p = buffer; + + buffer[0] = '\0'; /** Initialize as empty string */ + + // Convert each part of the intl number to hexadecimal, starting from the least significant part + for (int i = INTL_U32_PARTS - 1; i >= 0; i--) + { + if (skip == 1) + { + if (a.u32[i] != 0) skip = 0; + else continue; /** Prevent leading zero */ + } + + snprintf(temp, sizeof(temp), "%08X", a.u32[i]); /** Convert to hex and store in temp */ + strcat(p, temp); /** Append converted hex to the result string */ + p += 8; + } + + /** Prevent leading zero */ + while (*buffer == '0') buffer++; // Uncomment this line to remove leading zeros + + /** a == 0, print 0 only */ + if (p == buffer) + { + buffer[0] = '0'; + buffer[1] = '\0'; + } + + return buffer; /** Return the resulting hexadecimal string */ +} + +/** + * \brief Converts an intl number to a binary string. + * + * This function converts a given 128-bit unsigned integer (intl) + * into a binary string representation. It extracts each bit of the + * integer and stores the corresponding '0' or '1' character in the + * provided buffer. The resulting string is left-aligned, with leading + * zeros trimmed. + * + * \param[in] a: The intl number to convert. + * \param[out] buffer: The buffer to store the resulting binary string. + * It should be large enough to hold the binary + * representation (at least INTL_MAX_BIN characters). + * \return A pointer to the first non-zero character in the binary string, + * or a pointer to the buffer if the number is zero. + */ +const char* intl_sbin(intl a, char buffer[INTL_MAX_BIN]) +{ + int i, j; + uint8_t *base = (uint8_t *)&a; // Cast intl to a byte array + int width = sizeof(a); // Get the size of intl + char *p = buffer; // Pointer to the current position in buffer + + // Convert each byte to binary + for (i = width - 1; i >= 0; i--) + { + for (j = 7; j >= 0; j--) + { + *p++ = (((base[i]) >> (j) & 1) ? '1' : '0'); // Extract bits + } + } + *p = 0; // Null-terminate the string + + // Trim leading zeros and find the first '1' + while (buffer < p - 1) + { + if (*buffer == '1') break; // Stop at the first '1' + buffer++; // Skip leading zeros + } + + return buffer; // Return pointer to the trimmed string +} + +/** + * \brief Compares two intl unsigned numbers. + * + * This function compares two 128-bit unsigned integers (intl) + * by examining each 32-bit segment from the most significant to + * the least significant. It returns 1 if the first number is + * greater than the second, -1 if it is less, and 0 if they are + * equal. The comparison is done in a way that respects the + * unsigned nature of the integers. + * + * \param[in] a: The first number to compare. + * \param[in] b: The second number to compare. + * \return 1 if a > b, -1 if a < b, and 0 if a == b. + */ +static int intl_ucmp(intl a, intl b) +{ + // Compare each 32-bit part from the most significant to the least significant + for (int i = INTL_U32_PARTS - 1; i >= 0; i--) + { + if (a.u32[i] > b.u32[i]) return 1; // a is greater + if (a.u32[i] < b.u32[i]) return -1; // a is less + } + return 0; // a and b are equal +} + +/** + * \brief Compares two intl numbers. + * + * This function compares two 128-bit integers (intl) and + * determines their relative order. The comparison is performed + * starting from the most significant part (highest order) to + * the least significant part (lowest order). + * + * \param[in] a: The first number to compare. + * \param[in] b: The second number to compare. + * + * \return 1 if a > b, -1 if a < b, 0 if a == b. + */ +int intl_cmp(intl a, intl b) +{ + // Compare each 32-bit part from the most significant to the least significant + for (int i = INTL_U32_PARTS - 1; i >= 0; i--) + { + // Compare the current parts as signed integers + if ((int32_t)a.u32[i] > (int32_t)b.u32[i]) return 1; // a is greater + if ((int32_t)a.u32[i] < (int32_t)b.u32[i]) return -1; // a is less + } + return 0; // a is equal to b +} + +/** + * \brief Computes the two's complement (negation) of an intl number. + * + * This function calculates the negative representation of a given 128-bit + * unsigned integer (intl) using two's complement. It first inverts all bits + * of the input number and then adds one to the result. This effectively + * represents the negative value of the original number in a signed + * integer format. + * + * \param[in] a: The intl number to negate. + * \return The negated intl number (two's complement of a). + */ +intl intl_neg(intl a) +{ + intl result = {0}; + + // First, bitwise NOT (invert) the input number + for (int i = 0; i < INTL_U32_PARTS; i++) + { + result.u32[i] = ~a.u32[i]; + } + + // Add one to complete the two's complement operation + return intl_inc(result); +} + diff --git a/source/01_general/intl.h b/source/01_general/intl.h new file mode 100644 index 0000000..514f33e --- /dev/null +++ b/source/01_general/intl.h @@ -0,0 +1,82 @@ + +/********************************************************************************************************* + * ------------------------------------------------------------------------------------------------------ + * file description + * ------------------------------------------------------------------------------------------------------ + * \file intl.h + * \unit intl + * \brief This is a simple large inter number calculate module for C language + * \author Lamdonn + * \version v1.0.0 + * \license GPL-2.0 + * \copyright Copyright (C) 2023 Lamdonn. + ********************************************************************************************************/ +#ifndef __intl_H +#define __intl_H + +#include +#include +#include +#include +#include +#include "intl_cfg.h" + +/* Version infomation */ + +#define INTL_V_MAJOR 1 +#define INTL_V_MINOR 0 +#define INTL_V_PATCH 0 + +/** + * \brief A structure to represent a long bits integer. + * + * This structure uses a union to store the long bits integer in two different ways: + * - An array of `INTL_U16_PARTS` uint16_t values, allowing for operations on individual 16-bit segments. + * - An array of `INTL_U32_PARTS` uint32_t values, providing a way to work with 32-bit segments. + * + * The union allows for flexibility in how the data is accessed and manipulated, + * depending on the needs of the operations being performed. + */ +typedef struct { + union { + uint16_t u16[INTL_U16_PARTS]; ///< Array of uint16_t values representing the long bit integer in 16-bit segments. + uint32_t u32[INTL_U32_PARTS]; ///< Array of uint32_t values representing the long bit integer in 32-bit segments. + }; +} intl; + +/** + * \brief intl large integer api declaration, support for basic addition, subtraction, multiplication, division, etc. + */ + +intl intl_add(intl a, intl b); +intl intl_sub(intl a, intl b); +intl intl_mul(intl a, intl b); +intl intl_div(intl a, intl b); +intl intl_mod(intl a, intl b); +intl intl_and(intl a, intl b); +intl intl_xor(intl a, intl b); +intl intl_or(intl a, intl b); +intl intl_shl(intl a, uint32_t amount); +intl intl_shr(intl a, uint32_t amount); +intl intl_not(intl a); +intl intl_abs(intl a); +intl intl_inc(intl a); +intl intl_dec(intl a); +intl intl_neg(intl a); +intl intl_from(const char *str); +intl intl_from2(int value); +const char* intl_sdec(intl a, char buffer[INTL_MAX_DEC]); +const char* intl_shex(intl a, char buffer[INTL_MAX_HEX]); +const char* intl_sbin(intl a, char buffer[INTL_MAX_BIN]); +int intl_cmp(intl a, intl b); +int intl_sign(intl a); + +/** + * \brief Converts a integer to an intl number. + * \param[in] value: The integer to convert. + * \return The corresponding intl number initialized with the given value. + */ +#define intl(value) intl_from2(value) + +#endif + diff --git a/source/01_general/intl_cfg.h b/source/01_general/intl_cfg.h new file mode 100644 index 0000000..4c527fb --- /dev/null +++ b/source/01_general/intl_cfg.h @@ -0,0 +1,224 @@ + +/********************************************************************************************************* + * ------------------------------------------------------------------------------------------------------ + * file description + * ------------------------------------------------------------------------------------------------------ + * \file intl_cfg.h + * \unit intl + * \brief This is a simple large int number calculate module config header file for C language + * \author Lamdonn + * \version v1.0.0 + * \license GPL-2.0 + * \copyright Copyright (C) 2023 Lamdonn. + ********************************************************************************************************/ +#ifndef __intl_cfg_H +#define __intl_cfg_H + +/* + * With this definition, you can configure the footprint size of an intl. + * This is a flexible definition that can be extended to larger numbers in addition to the given few size definitions + */ + +/*****************************************************************/ +/* Config start */ +/*****************************************************************/ +// #define INTL_USE_128BITS +// #define INTL_USE_256BITS +#define INTL_USE_512BITS +// #define INTL_USE_1024BITS +// #define INTL_USE_2048BITS +// #define INTL_USE_4096BITS +// #define INTL_USE_8192BITS + +#if defined(INTL_USE_128BITS) +#define INTL_BIT_PARTS 128 +#define INTL_U32_PARTS 4 +#define INTL_U16_PARTS 8 +#define INTL_MAX_BIN 129 +#define INTL_MAX_DEC 40 +#define INTL_MAX_HEX 33 +#define INTL_MAX (intl){.u32={-1,-1,-1,0x7FFFFFFF,}} +#define INTL_MIN (intl){.u32={0,0,0,0x80000000,}} +#define INTL_ZERO (intl){.u32={0,0,0,0,}} +#elif defined(INTL_USE_256BITS) +#define INTL_BIT_PARTS 256 +#define INTL_U32_PARTS 8 +#define INTL_U16_PARTS 16 +#define INTL_MAX_BIN 257 +#define INTL_MAX_DEC 78 +#define INTL_MAX_HEX 65 +#define INTL_MAX (intl){.u32={-1,-1,-1,-1,-1,-1,-1,0x7FFFFFFF,}} +#define INTL_MIN (intl){.u32={0,0,0,0,0,0,0,0x80000000,}} +#define INTL_ZERO (intl){.u32={0,0,0,0,0,0,0,0,}} +#elif defined(INTL_USE_512BITS) +#define INTL_BIT_PARTS 512 +#define INTL_U32_PARTS 16 +#define INTL_U16_PARTS 32 +#define INTL_MAX_BIN 513 +#define INTL_MAX_DEC 155 +#define INTL_MAX_HEX 129 +#define INTL_MAX (intl){.u32={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x7FFFFFFF,}} +#define INTL_MIN (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000000,}} +#define INTL_ZERO (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}} +#elif defined(INTL_USE_1024BITS) +#define INTL_BIT_PARTS 1024 +#define INTL_U32_PARTS 32 +#define INTL_U16_PARTS 64 +#define INTL_MAX_BIN 1025 +#define INTL_MAX_DEC 309 +#define INTL_MAX_HEX 257 +#define INTL_MAX (intl){.u32={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x7FFFFFFF,}} +#define INTL_MIN (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000000,}} +#define INTL_ZERO (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}} +#elif defined(INTL_USE_2048BITS) +#define INTL_BIT_PARTS 2048 +#define INTL_U32_PARTS 64 +#define INTL_U16_PARTS 128 +#define INTL_MAX_BIN 2049 +#define INTL_MAX_DEC 618 +#define INTL_MAX_HEX 513 +#define INTL_MAX (intl){.u32={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x7FFFFFFF,}} +#define INTL_MIN (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000000,}} +#define INTL_ZERO (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}} +#elif defined(INTL_USE_4096BITS) +#define INTL_BIT_PARTS 4096 +#define INTL_U32_PARTS 128 +#define INTL_U16_PARTS 256 +#define INTL_MAX_BIN 4097 +#define INTL_MAX_DEC 1234 +#define INTL_MAX_HEX 1025 +#define INTL_MAX (intl){.u32={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x7FFFFFFF,}} +#define INTL_MIN (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000000,}} +#define INTL_ZERO (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}} +#elif defined(INTL_USE_8192BITS) +#define INTL_BIT_PARTS 8192 +#define INTL_U32_PARTS 256 +#define INTL_U16_PARTS 512 +#define INTL_MAX_BIN 8193 +#define INTL_MAX_DEC 2468 +#define INTL_MAX_HEX 2049 +#define INTL_MAX (intl){.u32={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x7FFFFFFF,}} +#define INTL_MIN (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000000,}} +#define INTL_ZERO (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}} +#endif +/*****************************************************************/ +/* Config end */ +/*****************************************************************/ + +#if 0 +void intl_gen_cfg(uint32_t bits) +{ + if ((bits & (bits - 1)) != 0) + { + printf("[ERROR] `bits` not a power of 2\r\n"); + return; + } + + if (bits < 128) + { + printf("[ERROR] `bits` too small\r\b"); + return; + } + + printf("/*****************************************************************/\r\n"); + printf("/* Config start */\r\n"); + printf("/*****************************************************************/\r\n"); + + for (uint32_t tb = 128; tb <= bits; tb <<= 1) + { + if (tb == bits) + { + printf("#define INTL_USE_%uBITS\r\n", tb); + } + else + { + printf("// #define INTL_USE_%uBITS\r\n", tb); + } + } + printf("\r\n"); + + intl temp; + + for (uint32_t tb = 128; tb <= bits; tb <<= 1) + { + if (tb == 128) + { + printf("#if defined(INTL_USE_%uBITS)\r\n", tb); + } + + else + { + printf("#elif defined(INTL_USE_%uBITS)\r\n", tb); + } + + uint32_t BIT_PARTS ; /* bits */ + uint32_t U32_PARTS ; /* INTL_BIT_PARTS / 32 */ + uint32_t U16_PARTS ; /* INTL_BIT_PARTS / 16 */ + uint32_t MAX_BIN ; /* INTL_BIT_PARTS + 1 */ + uint32_t MAX_DEC ; /* strlen(intl_shl(intl(1), INTL_BIT_PARTS - 1)) + 1 */ + uint32_t MAX_HEX ; /* INTL_BIT_PARTS / 4 + 1 */ + + BIT_PARTS = tb; + U32_PARTS = BIT_PARTS >> (5); + U16_PARTS = BIT_PARTS >> (4); + MAX_BIN = BIT_PARTS + 1; + MAX_DEC = MAX_BIN; + MAX_HEX = (BIT_PARTS >> (2)) + 1; + + if (sizeof(temp) == (bits >> 3)) + { + char buffer[INTL_MAX_BIN]; + MAX_DEC = strlen(intl_sdec(intl_shl(intl(1), BIT_PARTS - 1), buffer)) + 1; + } + + printf("#define INTL_BIT_PARTS %u\r\n", BIT_PARTS); + printf("#define INTL_U32_PARTS %u\r\n", U32_PARTS); + printf("#define INTL_U16_PARTS %u\r\n", U16_PARTS); + printf("#define INTL_MAX_BIN %u\r\n", MAX_BIN); + printf("#define INTL_MAX_DEC %u\r\n", MAX_DEC); + printf("#define INTL_MAX_HEX %u\r\n", MAX_HEX); + intl max = (intl){.u32={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0x7FFFFFFF,}}; + intl min = (intl){.u32={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000000,}}; + printf("#define INTL_MAX (intl){.u32={"); + for (uint32_t i = 0; i < U32_PARTS; i++) + { + printf("%s,", (i == U32_PARTS-1) ? "0x7FFFFFFF" : "-1"); + } + printf("}}\r\n"); + + printf("#define INTL_MIN (intl){.u32={"); + for (uint32_t i = 0; i < U32_PARTS; i++) + { + printf("%s,", (i == U32_PARTS-1) ? "0x80000000" : "0"); + } + printf("}}\r\n"); + + printf("#define INTL_ZERO (intl){.u32={"); + for (uint32_t i = 0; i < U32_PARTS; i++) + { + printf("0,"); + } + printf("}}\r\n"); + } + + printf("#endif\r\n"); + + printf("/*****************************************************************/\r\n"); + printf("/* Config end */\r\n"); + printf("/*****************************************************************/\r\n"); + + printf("\r\n\r\n-------------------------------------------------------------\r\n\r\n"); + + if (sizeof(temp) != (bits >> 3)) + { + printf("[TODO] Apply the current configuration and run it again to get the new `INTL_MAX_DEC`\r\n"); + } + else + { + printf("[INFO] The configuration has been generated, copy it to the `intl_cfg.h` range specified\r\n"); + } +} +#endif + +#endif + diff --git a/test/test.mk b/test/test.mk index 1e3600a..ff21f6a 100644 --- a/test/test.mk +++ b/test/test.mk @@ -27,7 +27,7 @@ TEST_INC = # TEST_SRC += $(TESTSPACE)/test_map.c # TEST_SRC += $(TESTSPACE)/test_heap.c # TEST_SRC += $(TESTSPACE)/test_tree.c -TEST_SRC += $(TESTSPACE)/test_graph.c +# TEST_SRC += $(TESTSPACE)/test_graph.c # TEST_SRC += $(TESTSPACE)/test_rbtree.c # TEST_SRC += $(TESTSPACE)/test_pthread.c # TEST_SRC += $(TESTSPACE)/test_encrypt.c @@ -45,5 +45,6 @@ TEST_SRC += $(TESTSPACE)/test_graph.c # TEST_SRC += $(TESTSPACE)/test_sList.c # TEST_SRC += $(TESTSPACE)/test_dList.c # TEST_SRC += $(TESTSPACE)/test_cQueue.c +TEST_SRC += $(TESTSPACE)/test_intl.c export TEST_SRC TEST_INC diff --git a/test/test_intl.c b/test/test_intl.c new file mode 100644 index 0000000..e190420 --- /dev/null +++ b/test/test_intl.c @@ -0,0 +1,105 @@ +#include +#include +#include "init.h" +#include "intl.h" + +#define INTL_P_DEC 0x01 +#define INTL_P_BIN 0x02 +#define INTL_P_HEX 0x04 + +static void print_intl(intl a, uint8_t type) +{ + char buffer[INTL_MAX_BIN]; + if (type & INTL_P_DEC) printf("intl (decimal): %s\n", intl_sdec(a, buffer)); + if (type & INTL_P_HEX) printf("intl (hex): %s\n", intl_shex(a, buffer)); + if (type & INTL_P_BIN) printf("intl (bin): %s\n", intl_sbin(a, buffer)); +} + +static void test_define(void) +{ + intl a = intl(0); + intl b = intl(10); + intl c = intl(-10); + intl d = intl(0xFF); + + intl e = intl_from("0"); + intl f = intl_from("100"); + intl g = intl_from("-100"); + intl h = intl_from("123456789123456789123456789"); + intl i = intl_from("0b1110000001111100101010100"); + intl j = intl_from("0o1236541236763210233642166"); + intl k = intl_from("0xF125E3F6D743648EEFFF12356"); + + intl max = INTL_MAX; + intl min = INTL_MIN; + intl n0 = INTL_ZERO; +} + +static void test_print(void) +{ + intl a = intl_from("123456789123456789123456789"); + + print_intl(a, INTL_P_DEC); + print_intl(a, INTL_P_BIN); + print_intl(a, INTL_P_HEX); +} + +static void test_calculate(void) +{ + intl a = intl_from("123456789123456789123456789"); + intl b = intl_from("987654321987654321987654321"); + + printf("a: \r\n"); + print_intl(a, INTL_P_DEC); + printf("b: \r\n"); + print_intl(b, INTL_P_DEC); + + printf("a + b: \r\n"); + print_intl(intl_add(a, b), INTL_P_DEC); + + printf("a - b: \r\n"); + print_intl(intl_sub(a, b), INTL_P_DEC); + + printf("a * b: \r\n"); + print_intl(intl_mul(a, b), INTL_P_DEC); + + printf("b / a: \r\n"); + print_intl(intl_div(b, a), INTL_P_DEC); + + printf("b %% a: \r\n"); + print_intl(intl_mod(b, a), INTL_P_DEC); + + printf("a & b: \r\n"); + print_intl(intl_and(a, b), INTL_P_DEC); + + printf("a | b: \r\n"); + print_intl(intl_or(a, b), INTL_P_DEC); + + printf("a ^ b: \r\n"); + print_intl(intl_xor(a, b), INTL_P_DEC); + + printf("~a: \r\n"); + print_intl(intl_not(a), INTL_P_DEC); + + printf("a << 1: \r\n"); + print_intl(intl_shl(a, 1), INTL_P_DEC); + + printf("b >> 1: \r\n"); + print_intl(intl_shr(b, 1), INTL_P_DEC); + + printf("compare: %d\r\n", intl_cmp(a, b)); + + printf("a incremented: \r\n"); + print_intl(intl_inc(a), INTL_P_DEC); + + printf("b decremented: \r\n"); + print_intl(intl_dec(b), INTL_P_DEC); +} + +static void test(void) +{ + // test_define(); + // test_print(); + test_calculate(); +} +init_export_app(test);