pkg/math/bits: complete operations

- add shift operations
- rewritten and simplified several functions
- remove reverse operations as not sure what they mean

Closes #156

Change-Id: I0769c771e1a1e310d672d39a72b4c888e464be21
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3965
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 41f8861..3752689 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -510,6 +510,18 @@
 		test("struct", `struct.MaxFields(2) & {a: 1}`),
 		`{a: 1}`,
 	}, {
+		test("math/bits", `bits.Lsh(0x8, 4)`), `128`,
+	}, {
+		test("math/bits", `bits.Rsh(0x100, 4)`), `16`,
+	}, {
+		test("math/bits", `bits.At(0x100, 8)`), `1`,
+	}, {
+		test("math/bits", `bits.At(0x100, 9)`), `0`,
+	}, {
+		test("math/bits", `bits.Set(0x100, 7, 1)`), `384`,
+	}, {
+		test("math/bits", `bits.Set(0x100, 8, 0)`), `0`,
+	}, {
 		test("math/bits", `bits.And(0x10000000000000F0E, 0xF0F7)`), `6`,
 	}, {
 		test("math/bits", `bits.Or(0x100000000000000F0, 0x0F)`),
diff --git a/cue/builtins.go b/cue/builtins.go
index 46d0971..b1c215e 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -1570,25 +1570,64 @@
 	},
 	"math/bits": &builtinPkg{
 		native: []*builtin{{
+			Name:   "Lsh",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, n := c.bigInt(0), c.uint(1)
+				c.ret = func() interface{} {
+					var z big.Int
+					z.Lsh(x, n)
+					return &z
+				}()
+			},
+		}, {
+			Name:   "Rsh",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, n := c.bigInt(0), c.uint(1)
+				c.ret = func() interface{} {
+					var z big.Int
+					z.Rsh(x, n)
+					return &z
+				}()
+			},
+		}, {
+			Name:   "At",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, i := c.bigInt(0), c.uint(1)
+				c.ret, c.err = func() (interface{}, error) {
+					if i > 1<<62 {
+						return 0, fmt.Errorf("bit index too large")
+					}
+					return x.Bit(int(i)), nil
+				}()
+			},
+		}, {
+			Name:   "Set",
+			Params: []kind{intKind, intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, i, bit := c.bigInt(0), c.int(1), c.uint(2)
+				c.ret = func() interface{} {
+					var z big.Int
+					z.SetBit(x, i, bit)
+					return &z
+				}()
+			},
+		}, {
 			Name:   "And",
 			Params: []kind{intKind, intKind},
 			Result: intKind,
 			Func: func(c *callCtxt) {
 				a, b := c.bigInt(0), c.bigInt(1)
 				c.ret = func() interface{} {
-					wa := a.Bits()
-					wb := b.Bits()
-					n := len(wa)
-					if len(wb) < n {
-						n = len(wb)
-					}
-					w := make([]big.Word, n)
-					for i := range w {
-						w[i] = wa[i] & wb[i]
-					}
-					i := &big.Int{}
-					i.SetBits(w)
-					return i
+					var z big.Int
+					z.And(a, b)
+					return &z
 				}()
 			},
 		}, {
@@ -1598,22 +1637,9 @@
 			Func: func(c *callCtxt) {
 				a, b := c.bigInt(0), c.bigInt(1)
 				c.ret = func() interface{} {
-					wa := a.Bits()
-					wb := b.Bits()
-					var w []big.Word
-					n := len(wa)
-					if len(wa) > len(wb) {
-						w = append(w, wa...)
-						n = len(wb)
-					} else {
-						w = append(w, wb...)
-					}
-					for i := 0; i < n; i++ {
-						w[i] = wa[i] | wb[i]
-					}
-					i := &big.Int{}
-					i.SetBits(w)
-					return i
+					var z big.Int
+					z.Or(a, b)
+					return &z
 				}()
 			},
 		}, {
@@ -1623,22 +1649,9 @@
 			Func: func(c *callCtxt) {
 				a, b := c.bigInt(0), c.bigInt(1)
 				c.ret = func() interface{} {
-					wa := a.Bits()
-					wb := b.Bits()
-					var w []big.Word
-					n := len(wa)
-					if len(wa) > len(wb) {
-						w = append(w, wa...)
-						n = len(wb)
-					} else {
-						w = append(w, wb...)
-					}
-					for i := 0; i < n; i++ {
-						w[i] = wa[i] ^ wb[i]
-					}
-					i := &big.Int{}
-					i.SetBits(w)
-					return i
+					var z big.Int
+					z.Xor(a, b)
+					return &z
 				}()
 			},
 		}, {
@@ -1648,18 +1661,9 @@
 			Func: func(c *callCtxt) {
 				a, b := c.bigInt(0), c.bigInt(1)
 				c.ret = func() interface{} {
-					wa := a.Bits()
-					wb := b.Bits()
-					w := append([]big.Word(nil), wa...)
-					for i, m := range wb {
-						if i >= len(w) {
-							break
-						}
-						w[i] = wa[i] &^ m
-					}
-					i := &big.Int{}
-					i.SetBits(w)
-					return i
+					var z big.Int
+					z.AndNot(a, b)
+					return &z
 				}()
 			},
 		}, {
@@ -1667,29 +1671,13 @@
 			Params: []kind{intKind},
 			Result: intKind,
 			Func: func(c *callCtxt) {
-				x := c.uint64(0)
+				x := c.bigInt(0)
 				c.ret = func() interface{} {
-					return bits.OnesCount64(x)
-				}()
-			},
-		}, {
-			Name:   "Reverse",
-			Params: []kind{intKind},
-			Result: intKind,
-			Func: func(c *callCtxt) {
-				x := c.uint64(0)
-				c.ret = func() interface{} {
-					return bits.Reverse64(x)
-				}()
-			},
-		}, {
-			Name:   "ReverseBytes",
-			Params: []kind{intKind},
-			Result: intKind,
-			Func: func(c *callCtxt) {
-				x := c.uint64(0)
-				c.ret = func() interface{} {
-					return bits.ReverseBytes64(x)
+					var count int
+					for _, w := range x.Bits() {
+						count += bits.OnesCount64(uint64(w))
+					}
+					return count
 				}()
 			},
 		}, {
@@ -1697,9 +1685,9 @@
 			Params: []kind{intKind},
 			Result: intKind,
 			Func: func(c *callCtxt) {
-				x := c.uint64(0)
+				x := c.bigInt(0)
 				c.ret = func() interface{} {
-					return bits.Len64(x)
+					return x.BitLen()
 				}()
 			},
 		}},
@@ -3042,7 +3030,7 @@
 		short?: string
 		long?:  string
 		tasks: {
-			[name=_]: Task
+			[name=string]: Task
 		}
 	}
 	Task: {
@@ -3067,7 +3055,7 @@
 		cmd:      string | [string, ...string]
 		install?: string | [string, ...string]
 		env: {
-			[Key=_]: string
+			[Key=string]: string
 		}
 		stdout:  *null | string | bytes
 		stderr:  *null | string | bytes
@@ -3115,10 +3103,10 @@
 		response: {
 			body: *bytes | string
 			header: {
-				[Name=_]: string | [...string]
+				[Name=string]: string | [...string]
 			}
 			trailer: {
-				[Name=_]: string | [...string]
+				[Name=string]: string | [...string]
 			}
 			status:     string
 			statusCode: int
@@ -3127,10 +3115,10 @@
 		request: {
 			body: *bytes | string
 			header: {
-				[Name=_]: string | [...string]
+				[Name=string]: string | [...string]
 			}
 			trailer: {
-				[Name=_]: string | [...string]
+				[Name=string]: string | [...string]
 			}
 		}
 	}
diff --git a/pkg/math/bits/manual.go b/pkg/math/bits/manual.go
index 64ea5bb..506d4f1 100644
--- a/pkg/math/bits/manual.go
+++ b/pkg/math/bits/manual.go
@@ -19,105 +19,94 @@
 package bits
 
 import (
+	"fmt"
 	"math/big"
 	"math/bits"
 )
 
-// And returns the bitwise and of a and b (a & b in Go).
-//
+// Lsh returns x shifted left by n bits.
+func Lsh(x *big.Int, n uint) *big.Int {
+	var z big.Int
+	z.Lsh(x, n)
+	return &z
+}
+
+// Rsh returns x shifted right by n bits.
+func Rsh(x *big.Int, n uint) *big.Int {
+	var z big.Int
+	z.Rsh(x, n)
+	return &z
+}
+
+// At returns the value of the i'th bit of x.
+func At(x *big.Int, i uint) (uint, error) {
+	if i > 1<<62 {
+		return 0, fmt.Errorf("bit index too large")
+	}
+	return x.Bit(int(i)), nil
+}
+
+// SetBit returns x with x's i'th bit set to b (0 or 1). That is, if b is 1
+// SetBit returns x with its i'th bit set; if b is 0 SetBit returns x with
+// its i'th bit cleared.
+func Set(x *big.Int, i int, bit uint) *big.Int {
+	var z big.Int
+	z.SetBit(x, i, bit)
+	return &z
+}
+
+// And returns the bitwise and of a and b.
 func And(a, b *big.Int) *big.Int {
-	wa := a.Bits()
-	wb := b.Bits()
-	n := len(wa)
-	if len(wb) < n {
-		n = len(wb)
-	}
-	w := make([]big.Word, n)
-	for i := range w {
-		w[i] = wa[i] & wb[i]
-	}
-	i := &big.Int{}
-	i.SetBits(w)
-	return i
+	var z big.Int
+	z.And(a, b)
+	return &z
 }
 
 // Or returns the bitwise or of a and b (a | b in Go).
-//
 func Or(a, b *big.Int) *big.Int {
-	wa := a.Bits()
-	wb := b.Bits()
-	var w []big.Word
-	n := len(wa)
-	if len(wa) > len(wb) {
-		w = append(w, wa...)
-		n = len(wb)
-	} else {
-		w = append(w, wb...)
-	}
-	for i := 0; i < n; i++ {
-		w[i] = wa[i] | wb[i]
-	}
-	i := &big.Int{}
-	i.SetBits(w)
-	return i
+	var z big.Int
+	z.Or(a, b)
+	return &z
 }
 
 // Xor returns the bitwise xor of a and b (a ^ b in Go).
-//
 func Xor(a, b *big.Int) *big.Int {
-	wa := a.Bits()
-	wb := b.Bits()
-	var w []big.Word
-	n := len(wa)
-	if len(wa) > len(wb) {
-		w = append(w, wa...)
-		n = len(wb)
-	} else {
-		w = append(w, wb...)
-	}
-	for i := 0; i < n; i++ {
-		w[i] = wa[i] ^ wb[i]
-	}
-	i := &big.Int{}
-	i.SetBits(w)
-	return i
+	var z big.Int
+	z.Xor(a, b)
+	return &z
 }
 
 // Clear returns the bitwise and not of a and b (a &^ b in Go).
 //
 func Clear(a, b *big.Int) *big.Int {
-	wa := a.Bits()
-	wb := b.Bits()
-	w := append([]big.Word(nil), wa...)
-	for i, m := range wb {
-		if i >= len(w) {
-			break
-		}
-		w[i] = wa[i] &^ m
-	}
-	i := &big.Int{}
-	i.SetBits(w)
-	return i
+	var z big.Int
+	z.AndNot(a, b)
+	return &z
 }
 
-// TODO: ShiftLeft, maybe trailing and leading zeros
-
 // OnesCount returns the number of one bits ("population count") in x.
-func OnesCount(x uint64) int {
-	return bits.OnesCount64(x)
+func OnesCount(x *big.Int) int {
+	var count int
+	for _, w := range x.Bits() {
+		count += bits.OnesCount64(uint64(w))
+	}
+	return count
 }
 
+// TODO: Reverse, ReverseBytes?
+// Not entirely sure what that means for infinite precision.
 // Reverse returns the value of x with its bits in reversed order.
-func Reverse(x uint64) uint64 {
-	return bits.Reverse64(x)
-}
+// func Reverse(x uint64) uint64 {
+// 	return bits.Reverse64(x)
+// }
 
-// ReverseBytes returns the value of x with its bytes in reversed order.
-func ReverseBytes(x uint64) uint64 {
-	return bits.ReverseBytes64(x)
-}
+// // ReverseBytes returns the value of x with its bytes in reversed order.
+// func ReverseBytes(x uint64) uint64 {
+// 	return bits.ReverseBytes64(x)
+// }
 
-// Len returns the minimum number of bits required to represent x; the result is 0 for x == 0.
-func Len(x uint64) int {
-	return bits.Len64(x)
+// Len returns the length of the absolute value of x in bits. The bit length
+// of 0 is 0.
+func Len(x *big.Int) int {
+	return x.BitLen()
 }