aboutsummaryrefslogtreecommitdiff
path: root/src/bigint.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bigint.c')
-rw-r--r--src/bigint.c149
1 files changed, 124 insertions, 25 deletions
diff --git a/src/bigint.c b/src/bigint.c
index 240dbb2..c78503d 100644
--- a/src/bigint.c
+++ b/src/bigint.c
@@ -67,7 +67,7 @@ static size_t bh_bigint_type_log2(BH_BIGINT_TYPE x)
tmp = (x >> (BH_BIGINT_BITS - 7)) & 0xFF;
if (tmp)
return ((BH_BIGINT_BITS + 1) - 8 * (i + 1)) + lookup[tmp];
-
+
x <<= 8;
}
@@ -405,7 +405,7 @@ static int bh_bigint_add_base(bh_bigint_t *to,
to->size = most->size + 1;
else
to->size = most->size;
-
+
return BH_OK;
}
@@ -415,7 +415,7 @@ static int bh_bigint_sub_base(bh_bigint_t *to,
{
BH_BIGINT_TYPE carry, tmp;
size_t size, i;
-
+
/* Clear up result error flag */
to->error = 0;
@@ -496,9 +496,14 @@ int bh_bigint_add(bh_bigint_t *to,
else
code = bh_bigint_sub_base(to, most, least);
- /* Set result sign */
if (!code)
- to->type = type;
+ {
+ /* Set result sign or zero */
+ if (to->size)
+ to->type = type;
+ else
+ to->type = 0;
+ }
/* Return result code */
return code;
@@ -538,9 +543,14 @@ int bh_bigint_sub(bh_bigint_t *to,
else
code = bh_bigint_add_base(to, most, least);
- /* Set result sign */
if (!code)
- to->type = type;
+ {
+ /* Set result sign or zero */
+ if (to->size)
+ to->type = type;
+ else
+ to->type = 0;
+ }
/* Return result code */
return code;
@@ -639,6 +649,73 @@ int bh_bigint_mul(bh_bigint_t *to,
return BH_OK;
}
+int bh_bigint_mul_digit(bh_bigint_t *to,
+ bh_bigint_t *left,
+ bh_int16_t right)
+{
+ BH_BIGINT_TYPE carry;
+ BH_BIGINT_TMP tmp;
+ size_t i, size;
+
+ /* Shortcut - one of the arguments has error flag set */
+ if (left->error)
+ {
+ to->error = 1;
+ return BH_ERROR;
+ }
+
+ /* Clear up result error flag */
+ to->error = 0;
+
+ /* Shortcut - multiplication by zero */
+ if (left->type == 0 || right == 0)
+ {
+ to->type = 0;
+ to->size = 0;
+ return BH_OK;
+ }
+
+ /* Reserve space for result */
+ if (to->capacity < (left->size + 1))
+ {
+ if (bh_bigint_reserve(to, left->size + 1))
+ {
+ to->error = 1;
+ return BH_OOM;
+ }
+ }
+
+ /* Determine sign */
+ if ((left->type > 0 && right < 0) || (left->type < 0 && right > 0))
+ to->type = -1;
+ else
+ to->type = 1;
+
+ if (right < 0)
+ right = -right;
+
+ /* Prepare variables */
+ carry = 0;
+
+ /* Multiplication loop */
+ for (i = 0; i < left->size; i++)
+ {
+ tmp = (BH_BIGINT_TMP)left->data[i] * (BH_BIGINT_TMP)right + carry;
+ carry = tmp >> BH_BIGINT_BITS;
+ to->data[i] = tmp & BH_BIGINT_MASK;
+ }
+ to->data[i] = carry;
+
+ /* Truncate multiplication result size */
+ size = left->size + 1;
+ while (size && !to->data[size - 1]) size--;
+
+ /* Update size */
+ to->size = size;
+
+ return BH_OK;
+}
+
int bh_bigint_pow(bh_bigint_t *to,
bh_bigint_t *from,
bh_uint32_t power)
@@ -732,7 +809,7 @@ int bh_bigint_powm(bh_bigint_t *to,
code = BH_OK;
/* Shortcut - one of the arguments has error flag set */
- if (left->error || right->error)
+ if (left->error || right->error || mod->error)
{
to->error = 1;
return BH_ERROR;
@@ -740,7 +817,7 @@ int bh_bigint_powm(bh_bigint_t *to,
/* Clear up result error flag */
to->error = 0;
-
+
/* Shortcut - power is equal to 0 */
if (bh_bigint_is_zero(right))
return bh_bigint_set_int(to, 1);
@@ -752,7 +829,7 @@ int bh_bigint_powm(bh_bigint_t *to,
/* Shortcut - power is negative */
if (bh_bigint_is_negative(right))
return bh_bigint_clear(to);
-
+
/* Figure out result sign */
if (left->type < 0 && right->data[0] & 0x1)
type = -1;
@@ -816,7 +893,7 @@ int bh_bigint_powm(bh_bigint_t *to,
bh_bigint_mul(base, base, base);
bh_bigint_mod(base, base, mod);
bh_bigint_rsh(exponent, exponent, 1);
-
+
/* Stop if error occured */
if (base->error || result->error)
{
@@ -848,14 +925,14 @@ static BH_BIGINT_TYPE bh_bigint_div_guess(bh_bigint_t *left,
bh_bigint_t *right)
{
BH_BIGINT_TMP tmp;
-
+
/* Left is bigger then right */
if (bh_bigint_equal_mod(left, right) < 0)
return 0;
/* Make a guess */
tmp = left->data[left->size - 1];
-
+
if (left->size != right->size)
tmp = (tmp << BH_BIGINT_BITS) + left->data[left->size - 2];
@@ -959,7 +1036,7 @@ static int bh_bigint_div_base(bh_bigint_t *quotient,
}
nleft->size--;
}
-
+
while (1)
{
/* Make a guess at what first digit of quotient should be */
@@ -993,7 +1070,8 @@ static int bh_bigint_div_base(bh_bigint_t *quotient,
}
/* Calculate remainder */
- bh_bigint_sub(remainder, remainder, tmp);
+ /* Prevent using addition logic and use subtraction */
+ bh_bigint_sub_base(remainder, remainder, tmp);
if (!nleft->size)
break;
@@ -1020,6 +1098,9 @@ finish:
return code;
}
+/**
+ * This follows C99 model (a/b)*b + a%b
+ */
int bh_bigint_divmod(bh_bigint_t *quotient,
bh_bigint_t *remainder,
bh_bigint_t *left,
@@ -1111,10 +1192,13 @@ int bh_bigint_divmod(bh_bigint_t *quotient,
code = bh_bigint_div_base(quotient, tmp, left, right, &shift);
if (!code)
{
+ /* Remainder should use sign of the left operand */
+ tmp->type = left->type;
+
/* If remainder required - normilize it */
if (tmp == remainder)
bh_bigint_rsh(remainder, remainder, shift);
-
+
/* Fix the sign */
if (quotient)
quotient->type = type;
@@ -1145,7 +1229,7 @@ int bh_bigint_lsh(bh_bigint_t *to,
bh_uint32_t shift)
{
size_t i, bits, blocks;
- BH_BIGINT_TYPE tmp;
+ BH_BIGINT_TYPE tmp;
/* Shortcut - one of the arguments has error flag set */
if (left->error)
@@ -1255,7 +1339,7 @@ int bh_bigint_rsh(bh_bigint_t *to,
/* Calculate distance in blocks and bits */
blocks = shift / BH_BIGINT_BITS;
bits = shift % BH_BIGINT_BITS;
-
+
/* Shortcut - shift is bigger than value or value is 0 */
if (left->type == 0 || left->size < blocks)
{
@@ -1302,7 +1386,7 @@ int bh_bigint_rsh(bh_bigint_t *to,
return BH_OOM;
}
}
-
+
/* Main loop for aligned shift */
for (i = 0; i < left->size - blocks; i++)
to->data[i] = left->data[i + blocks];
@@ -1312,7 +1396,7 @@ int bh_bigint_rsh(bh_bigint_t *to,
i = left->size - blocks;
while (i && !to->data[i - 1]) i--;
- /* Update fields */
+ /* Update fields */
to->size = i;
to->type = left->type;
@@ -1358,10 +1442,16 @@ int bh_bigint_or(bh_bigint_t *to,
to->data[i] = tmp;
}
+ /* Shrink size */
+ while (size && !to->data[size - 1]) size--;
+
/* Update fields */
to->type = 1;
to->size = size;
+ if (!size)
+ to->type = 0;
+
return BH_OK;
}
@@ -1401,10 +1491,16 @@ int bh_bigint_and(bh_bigint_t *to,
to->data[i] = tmp;
}
+ /* Shrink size */
+ while (size && !to->data[size - 1]) size--;
+
/* Update fields */
to->type = 1;
to->size = size;
+ if (!size)
+ to->type = 0;
+
return BH_OK;
}
@@ -1444,10 +1540,16 @@ int bh_bigint_xor(bh_bigint_t *to,
to->data[i] = tmp;
}
+ /* Shrink size */
+ while (size && !to->data[size - 1]) size--;
+
/* Update fields */
to->type = 1;
to->size = size;
+ if (!size)
+ to->type = 0;
+
return BH_OK;
}
@@ -1472,10 +1574,7 @@ int bh_bigint_negate(bh_bigint_t *to,
}
/* Change the sign */
- if (to->type < 0)
- to->type = 1;
- else if (to->type > 0)
- to->type = -1;
+ to->type = 0 - to->type;
return BH_OK;
}
@@ -1555,4 +1654,4 @@ size_t bh_bigint_log2(bh_bigint_t *bigint)
result = bh_bigint_type_log2(bigint->data[bigint->size - 1]);
return result + (bigint->size - 1) * BH_BIGINT_BITS;
-} \ No newline at end of file
+}