浏览代码

Correctly handle bitwise operations for negative BigInt

gingerBill 7 年之前
父节点
当前提交
8504ff920b
共有 2 个文件被更改,包括 227 次插入91 次删除
  1. 224 80
      src/big_int.cpp
  2. 3 11
      src/exact_value.cpp

+ 224 - 80
src/big_int.cpp

@@ -55,6 +55,8 @@ void big_int_from_i64(BigInt *dst, i64 x);
 void big_int_init    (BigInt *dst, BigInt const *src);
 void big_int_from_string(BigInt *dst, String const &s);
 
+BigInt big_int_make(BigInt const *b, bool abs=false);
+BigInt big_int_make_abs(BigInt const *b);
 BigInt big_int_make_u64(u64 x);
 BigInt big_int_make_i64(i64 x);
 
@@ -215,6 +217,22 @@ void big_int_init(BigInt *dst, BigInt const *src) {
 	gb_memmove(dst->d.words, s, gb_size_of(u64)*dst->len);
 }
 
+BigInt big_int_make(BigInt const *b, bool abs) {
+	BigInt i = {};
+	big_int_init(&i, b);
+	if (abs) i.neg = false;
+	return i;
+}
+BigInt big_int_make_abs(BigInt const *b) {
+	return big_int_make(b, true);
+}
+BigInt big_int_alias_abs(BigInt const *b) {
+	BigInt x = *b;
+	x.neg = false;
+	return x;
+}
+
+
 BigInt big_int_make_u64(u64 x) {
 	BigInt i = {};
 	big_int_from_u64(&i, x);
@@ -786,14 +804,13 @@ void bi__divW(BigInt const *x, u64 y, BigInt *q_, u64 *r_) {
 		GB_PANIC("division by zero");
 	} else if (y == 1) {
 		q = *x;
-		goto end;
 	} else if (m == 0) {
-		goto end;
+		// okay
+	} else {
+		big_int_alloc(&q, m, m);
+		bi__divWVW(&q, 0, x, y, &r);
+		big_int_normalize(&q);
 	}
-	big_int_alloc(&q, m, m);
-	bi__divWVW(&q, 0, x, y, &r);
-	big_int_normalize(&q);
-end:
 	if (q_) *q_ = q;
 	if (r_) *r_ = r;
 }
@@ -989,45 +1006,64 @@ void big_int_and(BigInt *dst, BigInt const *x, BigInt const *y) {
 		return;
 	}
 
-	GB_ASSERT(x->neg == y->neg);
+	if (x->neg == y->neg) {
+		if (x->neg) {
+			// (-x) & (-y) == ~(x-1) & ~(x-y) == ~((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1)
+			BigInt x1 = big_int_make_abs(x);
+			BigInt y1 = big_int_make_abs(y);
+			BigInt z1 = {};
+
+			big_int_sub_eq(&x1, &BIG_INT_ONE);
+			big_int_sub_eq(&y1, &BIG_INT_ONE);
+			big_int_or(&z1, &x1, &y1);
+			big_int_add(dst, &z1, &BIG_INT_ONE);
+			dst->neg = true; // NOTE(bill): dst cannot be 0 as x and y are both negative
+			big_int_normalize(dst);
+			return;
+		}
+		u64 const *xd = big_int_ptr(x);
+		u64 const *yd = big_int_ptr(y);
 
-	dst->neg = x->neg;
-	u64 const *xd = big_int_ptr(x);
-	u64 const *yd = big_int_ptr(y);
+		if (x->len == 1 && y->len == 1) {
+			dst->len = 1;
+			dst->d.word = xd[0] & yd[0];
+			return;
+		}
 
-	if (x->len == 1 && y->len == 1) {
-		dst->len = 1;
-		dst->d.word = xd[0] & yd[0];
+		i32 len = gb_max(x->len, y->len);
+		big_int_alloc(dst, len, len);
+		GB_ASSERT(dst->len > 1);
+
+		i32 i = 0;
+		for (; i < x->len && i < y->len; i++) {
+			dst->d.words[i] = xd[i] & yd[i];
+		}
+		for (; i < len; i++) {
+			dst->d.words[i] = 0;
+		}
+		dst->neg = false;
+		big_int_normalize(dst);
 		return;
 	}
+	if (x->neg) {
+		BigInt const *tmp = x;
+		x = y;
+		y = tmp;
+		// NOTE(bill): AND is symmetric
+	}
 
-	i32 len = gb_max(x->len, y->len);
-	big_int_alloc(dst, len, len);
-	GB_ASSERT(dst->len > 1);
 
-	i32 i = 0;
-	for (; i < x->len && i < y->len; i++) {
-		dst->d.words[i] = xd[i] & yd[i];
-	}
-	for (; i < len; i++) {
-		dst->d.words[i] = 0;
-	}
+	// x & (-y) == x &~ (y-1)
+
+	dst->neg = false;
+	BigInt x1 = big_int_make_abs(x);
+	BigInt y1 = big_int_make_abs(y);
+	big_int_sub_eq(&y1, &BIG_INT_ONE);
+	big_int_and_not(dst, &x1, &y1);
 	big_int_normalize(dst);
 }
 
-void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
-	if (x->len == 0) {
-		big_int_init(dst, y);
-		return;
-	}
-	if (y->len == 0) {
-		big_int_init(dst, x);
-		return;
-	}
-
-	GB_ASSERT(x->neg == y->neg);
-
-	dst->neg = x->neg;
+void big_int__and_not_abs(BigInt *dst, BigInt const *x, BigInt const *y) {
 	u64 const *xd = big_int_ptr(x);
 	u64 const *yd = big_int_ptr(y);
 
@@ -1058,18 +1094,58 @@ void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
 	big_int_normalize(dst);
 }
 
-void big_int_xor(BigInt *dst, BigInt const *x, BigInt const *y) {
+void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
 	if (x->len == 0) {
 		big_int_init(dst, y);
 		return;
-	} else if (y->len == 0) {
+	}
+	if (y->len == 0) {
 		big_int_init(dst, x);
 		return;
 	}
 
-	GB_ASSERT(x->neg == y->neg);
+	if (x->neg == y->neg) {
+		if (x->neg) {
+			// (-x) &~ (-y) == ~(x-1) &~ ~(y-1) == ~(x-1) & (y-1) == (y-1) &~ (x-1)
+			BigInt x1 = big_int_make_abs(x);
+			BigInt y1 = big_int_make_abs(y);
+			big_int_sub_eq(&x1, &BIG_INT_ONE);
+			big_int_sub_eq(&y1, &BIG_INT_ONE);
+
+			big_int__and_not_abs(dst, &y1, &x1);
+			dst->neg = false;
+			return;
+		}
+
+		big_int__and_not_abs(dst, x, y);
+		dst->neg = false;
+		return;
+	}
+
+	if (x->neg) {
+		// (-x) &~ y == ~(x-1) &~ y == ~(x-1) & ~y == ~((x-1) | y) == -(((x-1) | y) + 1)
+		BigInt x1 = big_int_make_abs(x);
+		BigInt y1 = big_int_make_abs(y);
+		big_int_sub_eq(&x1, &BIG_INT_ONE);
+
+		BigInt z1 = {};
+		big_int_or(&z1, &x1, &y1);
+		big_int_add(dst, &z1, &BIG_INT_ONE);
+		dst->neg = true;
+		return;
+	}
 
+	// x &~ (-y) == x &~ ~(y-1) == x & (y-1)
+	BigInt x1 = big_int_make_abs(x);
+	BigInt y1 = big_int_make_abs(y);
+	big_int_sub_eq(&y1, &BIG_INT_ONE);
+	big_int_and(dst, &x1, &y1);
 	dst->neg = false;
+	return;
+}
+
+
+void big_int__xor_abs(BigInt *dst, BigInt const *x, BigInt const *y) {
 	u64 const *xd = big_int_ptr(x);
 	u64 const *yd = big_int_ptr(y);
 
@@ -1098,8 +1174,7 @@ void big_int_xor(BigInt *dst, BigInt const *x, BigInt const *y) {
 	big_int_normalize(dst);
 }
 
-
-void big_int_or(BigInt *dst, BigInt const *x, BigInt const *y) {
+void big_int_xor(BigInt *dst, BigInt const *x, BigInt const *y) {
 	if (x->len == 0) {
 		big_int_init(dst, y);
 		return;
@@ -1108,34 +1183,105 @@ void big_int_or(BigInt *dst, BigInt const *x, BigInt const *y) {
 		return;
 	}
 
-	GB_ASSERT(x->neg == y->neg);
+	if (x->neg == y->neg) {
+		if (x->neg) {
+			// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
+			BigInt x1 = big_int_make_abs(x);
+			BigInt y1 = big_int_make_abs(y);
+			big_int_sub_eq(&x1, &BIG_INT_ONE);
+			big_int_sub_eq(&y1, &BIG_INT_ONE);
+			big_int__xor_abs(dst, &x1, &y1);
+			dst->neg = false;
+			return;
+		}
 
-	dst->neg = x->neg;
-	u64 const *xd = big_int_ptr(x);
-	u64 const *yd = big_int_ptr(y);
+		big_int__xor_abs(dst, x, y);
+		dst->neg = false;
+		return;
+	}
 
-	if (x->len == 1 && y->len == 1) {
-		dst->len = 1;
-		dst->d.word = xd[0] | yd[0];
+	// x->neg != y->neg
+	if (x->neg) {
+		BigInt const *tmp = x;
+		x = y;
+		y = tmp;
+	}
+
+	// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
+
+	dst->neg = false;
+	BigInt y1 = big_int_make_abs(y);
+	big_int_sub_eq(&y1, &BIG_INT_ONE);
+	big_int__xor_abs(dst, x, &y1);
+	big_int_add_eq(dst, &BIG_INT_ONE);
+	dst->neg = true;
+	return;
+}
+
+
+void big_int_or(BigInt *dst, BigInt const *x, BigInt const *y) {
+	if (x->len == 0) {
+		big_int_init(dst, y);
+		return;
+	} else if (y->len == 0) {
+		big_int_init(dst, x);
 		return;
 	}
 
-	i32 len = gb_max(x->len, y->len);
-	big_int_alloc(dst, len, len);
-	GB_ASSERT(dst->len > 1);
+	if (x->neg == y->neg) {
+		if (x->neg) {
+			// (-x) || (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1)
+			BigInt x1 = big_int_make_abs(x);
+			BigInt y1 = big_int_make_abs(y);
+			big_int_sub_eq(&x1, &BIG_INT_ONE);
+			big_int_sub_eq(&y1, &BIG_INT_ONE);
+			big_int_and(dst, &x1, &y1);
+			big_int_add_eq(dst, &BIG_INT_ONE);
+			dst->neg = true;
+			return;
+		}
 
-	for (i32 i = 0; i < len; i++) {
-		u64 word = 0;
-		if (i < x->len) {
-			word |= xd[i];
+		dst->neg = x->neg;
+		u64 const *xd = big_int_ptr(x);
+		u64 const *yd = big_int_ptr(y);
+
+		if (x->len == 1 && y->len == 1) {
+			dst->len = 1;
+			dst->d.word = xd[0] | yd[0];
+			return;
 		}
-		if (i < y->len) {
-			word |= yd[i];
+
+		i32 len = gb_max(x->len, y->len);
+		big_int_alloc(dst, len, len);
+		GB_ASSERT(dst->len > 1);
+
+		for (i32 i = 0; i < len; i++) {
+			u64 word = 0;
+			if (i < x->len) {
+				word |= xd[i];
+			}
+			if (i < y->len) {
+				word |= yd[i];
+			}
+
+			dst->d.words[i] = word;
 		}
+		big_int_normalize(dst);
+	}
 
-		dst->d.words[i] = word;
+	if (x->neg) {
+		BigInt const *tmp = x;
+		x = y;
+		y = tmp;
 	}
-	big_int_normalize(dst);
+
+	// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(~((y-1) &~ x) + 1)
+	BigInt y1 = big_int_make_abs(y);
+	big_int_sub_eq(&y1, &BIG_INT_ONE);
+	big_int__and_not_abs(dst, &y1, x);
+	big_int_add_eq(dst, &BIG_INT_ONE);
+	dst->neg = true;
+	return;
 }
 
 
@@ -1145,7 +1291,6 @@ void bit_int_not(BigInt *dst, BigInt const *x, u64 bit_count, bool is_signed) {
 		return;
 	}
 
-
 	dst->neg = false;
 	u64 const *xd = big_int_ptr(x);
 	if (bit_count <= 64) {
@@ -1163,31 +1308,30 @@ void bit_int_not(BigInt *dst, BigInt const *x, u64 bit_count, bool is_signed) {
 				dst->d.word &= mask;
 			}
 		}
-		goto end;
-	}
-
-	dst->len = cast(i32)((bit_count+63ull) / 64ull);
-	GB_ASSERT(dst->len >= x->len);
-	big_int_alloc(dst, dst->len, dst->len);
-	GB_ASSERT(dst->len > 1);
+	} else {
+		dst->len = cast(i32)((bit_count+63ull) / 64ull);
+		GB_ASSERT(dst->len >= x->len);
+		big_int_alloc(dst, dst->len, dst->len);
+		GB_ASSERT(dst->len > 1);
 
-	i32 i = 0;
-	for (; i < x->len; i++) {
-		dst->d.words[i] = ~xd[i];
-	}
-	for (; i < dst->len; i++) {
-		dst->d.words[i] = ~cast(u64)0ull;
-	}
+		i32 i = 0;
+		for (; i < x->len; i++) {
+			dst->d.words[i] = ~xd[i];
+		}
+		for (; i < dst->len; i++) {
+			dst->d.words[i] = ~cast(u64)0ull;
+		}
 
-	i32 word_idx = cast(i32)(cast(u64)dst->len - (bit_count/64ull)-1ull);
-	u32 word_bit_idx = bit_count % 64;
-	if (word_idx < dst->len) {
-		u64 mask = (1ull << word_bit_idx) - 1ull;
-		dst->d.words[word_idx] &= mask;
+		i32 word_idx = cast(i32)(cast(u64)dst->len - (bit_count/64ull)-1ull);
+		u32 word_bit_idx = bit_count % 64;
+		if (word_idx < dst->len) {
+			u64 mask = (1ull << word_bit_idx) - 1ull;
+			dst->d.words[word_idx] &= mask;
+		}
 	}
 
-end:
 	big_int_normalize(dst);
+
 	if (is_signed) {
 		BigInt prec = big_int_make_u64(bit_count-1);
 		BigInt mask = {};

+ 3 - 11
src/exact_value.cpp

@@ -523,21 +523,13 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
 		case Token_Quo:    return exact_value_float(fmod(big_int_to_f64(a), big_int_to_f64(b)));
 		case Token_QuoEq:  big_int_quo(&c, a, b); break; // NOTE(bill): Integer division
 		case Token_Mod:    big_int_rem(&c, a, b); break;
-		case Token_ModMod:
-			big_int_rem(&c, a, b);
-			big_int_add_eq(&c, b);
-			big_int_rem_eq(&c, b);
-			break;
+		case Token_ModMod: big_int_euclidean_mod(&c, a, b); break;
 		case Token_And:    big_int_and(&c, a, b);     break;
 		case Token_Or:     big_int_or(&c, a, b);      break;
 		case Token_Xor:    big_int_xor(&c, a, b);     break;
 		case Token_AndNot: big_int_and_not(&c, a, b); break;
-		case Token_Shl:
-			big_int_shl(&c, a, b);
-			break;
-		case Token_Shr:
-			big_int_shr(&c, a, b);
-			break;
+		case Token_Shl:    big_int_shl(&c, a, b);     break;
+		case Token_Shr:    big_int_shr(&c, a, b);     break;
 		default: goto error;
 		}