1
0
mirror of https://github.com/golang/go synced 2024-11-22 08:04:39 -07:00

big: Add bitwise methods for Int

R=gri
CC=golang-dev
https://golang.org/cl/987041
This commit is contained in:
Evan Shaw 2010-05-03 11:20:52 -07:00 committed by Robert Griesemer
parent e6d0a6c9c1
commit 4d1b1574af
4 changed files with 355 additions and 12 deletions

142
src/pkg/big/int.go Normal file → Executable file
View File

@ -408,3 +408,145 @@ func (z *Int) Rsh(x *Int, n uint) *Int {
z.abs = z.abs.shr(x.abs, n) z.abs = z.abs.shr(x.abs, n)
return z return z
} }
// And sets z = x & y and returns z.
func (z *Int) And(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1)
x1 := nat{}.sub(x.abs, natOne)
y1 := z.abs.sub(y.abs, natOne)
z.neg = true
z.abs = z.abs.add(z.abs.or(x1, y1), natOne)
return z
}
// x & y == x & y
z.neg = false
z.abs = z.abs.and(x.abs, y.abs)
return z
}
// x.neg != y.neg
if x.neg {
x, y = y, x // & is symmetric
}
// x & (-y) == x & ^(y-1) == x &^ (y-1)
y1 := z.abs.sub(y.abs, natOne)
z.neg = false
z.abs = z.abs.andNot(x.abs, y1)
return z
}
// AndNot sets z = x &^ y and returns z.
func (z *Int) AndNot(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1)
x1 := nat{}.sub(x.abs, natOne)
y1 := z.abs.sub(y.abs, natOne)
z.neg = false
z.abs = z.abs.andNot(y1, x1)
return z
}
// x &^ y == x &^ y
z.neg = false
z.abs = z.abs.andNot(x.abs, y.abs)
return z
}
if x.neg {
// (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1)
x1 := z.abs.sub(x.abs, natOne)
z.neg = true
z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne)
return z
}
// x &^ (-y) == x &^ ^(y-1) == x & (y-1)
y1 := z.abs.add(y.abs, natOne)
z.neg = false
z.abs = z.abs.and(x.abs, y1)
return z
}
// Or sets z = x | y and returns z.
func (z *Int) Or(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1)
x1 := nat{}.sub(x.abs, natOne)
y1 := z.abs.sub(y.abs, natOne)
z.neg = true
z.abs = z.abs.add(z.abs.and(x1, y1), natOne)
return z
}
// x | y == x | y
z.neg = false
z.abs = z.abs.or(x.abs, y.abs)
return z
}
// x.neg != y.neg
if x.neg {
x, y = y, x // | is symmetric
}
// x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1)
y1 := z.abs.sub(y.abs, natOne)
z.neg = true
z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne)
return z
}
// Xor sets z = x ^ y and returns z.
func (z *Int) Xor(x, y *Int) *Int {
if x.neg == y.neg {
if x.neg {
// (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1)
x1 := nat{}.sub(x.abs, natOne)
y1 := z.abs.sub(y.abs, natOne)
z.neg = false
z.abs = z.abs.xor(x1, y1)
return z
}
// x ^ y == x ^ y
z.neg = false
z.abs = z.abs.xor(x.abs, y.abs)
return z
}
// x.neg != y.neg
if x.neg {
x, y = y, x // | is symmetric
}
// x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1)
y1 := z.abs.sub(y.abs, natOne)
z.neg = true
z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne)
return z
}
// Not sets z = ^x and returns z.
func (z *Int) Not(x *Int) *Int {
if x.neg {
// ^(-x) == ^(^(x-1)) == x-1
z.neg = false
z.abs = z.abs.sub(x.abs, natOne)
return z
}
// ^x == -x-1 == -(x+1)
z.neg = true
z.abs = z.abs.add(x.abs, natOne)
return z
}

126
src/pkg/big/int_test.go Normal file → Executable file
View File

@ -709,3 +709,129 @@ func TestInt64(t *testing.T) {
} }
} }
} }
type bitwiseTest struct {
x, y string
and, or, xor, andNot string
}
var bitwiseTests = []bitwiseTest{
bitwiseTest{"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"},
bitwiseTest{"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"},
bitwiseTest{"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"},
bitwiseTest{"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"},
bitwiseTest{"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"},
bitwiseTest{"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"},
bitwiseTest{"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"},
bitwiseTest{"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"},
bitwiseTest{"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"},
bitwiseTest{"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"},
bitwiseTest{"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"},
bitwiseTest{"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"},
bitwiseTest{
"0x1000009dc6e3d9822cba04129bcbe3401",
"0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd",
"0x1000001186210100001000009048c2001",
"0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd",
"0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc",
"0x8c40c2d8822caa04120b8321400",
},
bitwiseTest{
"0x1000009dc6e3d9822cba04129bcbe3401",
"-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd",
"0x8c40c2d8822caa04120b8321401",
"-0xb9bd7d543685789d57ca918e82229142459020483cd2014001fd",
"-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe",
"0x1000001186210100001000009048c2000",
},
bitwiseTest{
"-0x1000009dc6e3d9822cba04129bcbe3401",
"-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd",
"-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd",
"-0x1000001186210100001000009048c2001",
"0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc",
"0xb9bd7d543685789d57ca918e82229142459020483cd2014001fc",
},
}
type bitFun func(z, x, y *Int) *Int
func testBitFun(t *testing.T, msg string, f bitFun, x, y *Int, exp string) {
expected := new(Int)
expected.SetString(exp, 16)
out := f(new(Int), x, y)
if out.Cmp(expected) != 0 {
println("Test failed")
t.Errorf("%s: got %s want %s", msg, out, expected)
}
}
func testBitFunSelf(t *testing.T, msg string, f bitFun, x, y *Int, exp string) {
expected := new(Int)
expected.SetString(exp, 16)
x = f(x, x, y)
if x.Cmp(expected) != 0 {
println("Test failed")
t.Errorf("%s: got %s want %s", msg, x, expected)
}
}
func TestBitwise(t *testing.T) {
x := new(Int)
y := new(Int)
for _, test := range bitwiseTests {
x.SetString(test.x, 16)
y.SetString(test.y, 16)
testBitFun(t, "and", (*Int).And, x, y, test.and)
testBitFunSelf(t, "and", (*Int).And, x, y, test.and)
testBitFun(t, "andNot", (*Int).AndNot, x, y, test.andNot)
testBitFunSelf(t, "andNot", (*Int).AndNot, x, y, test.andNot)
testBitFun(t, "or", (*Int).Or, x, y, test.or)
testBitFunSelf(t, "or", (*Int).Or, x, y, test.or)
testBitFun(t, "xor", (*Int).Xor, x, y, test.xor)
testBitFunSelf(t, "xor", (*Int).Xor, x, y, test.xor)
}
}
type notTest struct {
in string
out string
}
var notTests = []notTest{
notTest{"0", "-1"},
notTest{"1", "-2"},
notTest{"7", "-8"},
notTest{"0", "-1"},
notTest{"-81910", "81909"},
notTest{
"298472983472983471903246121093472394872319615612417471234712061",
"-298472983472983471903246121093472394872319615612417471234712062",
},
}
func TestNot(t *testing.T) {
in := new(Int)
out := new(Int)
expected := new(Int)
for i, test := range notTests {
in.SetString(test.in, 10)
expected.SetString(test.out, 10)
out = out.Not(in)
if out.Cmp(expected) != 0 {
t.Errorf("#%d: got %s want %s", i, out, expected)
}
out = out.Not(out)
if out.Cmp(in) != 0 {
t.Errorf("#%d: got %s want %s", i, out, in)
}
}
}

77
src/pkg/big/nat.go Normal file → Executable file
View File

@ -629,7 +629,7 @@ func hexValue(ch byte) int {
} }
// scanN returns the natural number corresponding to the // scan returns the natural number corresponding to the
// longest possible prefix of s representing a natural number in a // longest possible prefix of s representing a natural number in a
// given conversion base, the actual conversion base used, and the // given conversion base, the actual conversion base used, and the
// prefix length. The syntax of natural numbers follows the syntax // prefix length. The syntax of natural numbers follows the syntax
@ -833,6 +833,81 @@ func (z nat) shiftRightDeprecated(x nat, n uint) nat {
} }
func (z nat) and(x, y nat) nat {
m := len(x)
n := len(y)
if m > n {
m = n
}
// m <= n
z = z.make(m)
for i := 0; i < m; i++ {
z[i] = x[i] & y[i]
}
return z.norm()
}
func (z nat) andNot(x, y nat) nat {
m := len(x)
n := len(y)
if n > m {
n = m
}
// m >= n
z = z.make(m)
for i := 0; i < n; i++ {
z[i] = x[i] &^ y[i]
}
copy(z[n:m], x[n:m])
return z.norm()
}
func (z nat) or(x, y nat) nat {
m := len(x)
n := len(y)
s := x
if m < n {
n, m = m, n
s = y
}
// n >= m
z = z.make(n)
for i := 0; i < m; i++ {
z[i] = x[i] | y[i]
}
copy(z[m:n], s[m:n])
return z.norm()
}
func (z nat) xor(x, y nat) nat {
m := len(x)
n := len(y)
s := x
if n < m {
n, m = m, n
s = y
}
// n >= m
z = z.make(n)
for i := 0; i < m; i++ {
z[i] = x[i] ^ y[i]
}
copy(z[m:n], s[m:n])
return z.norm()
}
// greaterThan returns true iff (x1<<_W + x2) > (y1<<_W + y2) // greaterThan returns true iff (x1<<_W + x2) > (y1<<_W + y2)
func greaterThan(x1, x2, y1, y2 Word) bool { return x1 > y1 || x1 == y1 && x2 > y2 } func greaterThan(x1, x2, y1, y2 Word) bool { return x1 > y1 || x1 == y1 && x2 > y2 }

22
src/pkg/big/nat_test.go Normal file → Executable file
View File

@ -173,37 +173,37 @@ func BenchmarkMul(b *testing.B) {
} }
type strN struct { type str struct {
x nat x nat
b int b int
s string s string
} }
var tabN = []strN{ var tab = []str{
strN{nil, 10, "0"}, str{nil, 10, "0"},
strN{nat{1}, 10, "1"}, str{nat{1}, 10, "1"},
strN{nat{10}, 10, "10"}, str{nat{10}, 10, "10"},
strN{nat{1234567890}, 10, "1234567890"}, str{nat{1234567890}, 10, "1234567890"},
} }
func TestString(t *testing.T) { func TestString(t *testing.T) {
for _, a := range tabN { for _, a := range tab {
s := a.x.string(a.b) s := a.x.string(a.b)
if s != a.s { if s != a.s {
t.Errorf("stringN%+v\n\tgot s = %s; want %s", a, s, a.s) t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
} }
x, b, n := nat(nil).scan(a.s, a.b) x, b, n := nat(nil).scan(a.s, a.b)
if x.cmp(a.x) != 0 { if x.cmp(a.x) != 0 {
t.Errorf("scanN%+v\n\tgot z = %v; want %v", a, x, a.x) t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
} }
if b != a.b { if b != a.b {
t.Errorf("scanN%+v\n\tgot b = %d; want %d", a, b, a.b) t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.b)
} }
if n != len(a.s) { if n != len(a.s) {
t.Errorf("scanN%+v\n\tgot n = %d; want %d", a, n, len(a.s)) t.Errorf("scan%+v\n\tgot n = %d; want %d", a, n, len(a.s))
} }
} }
} }