-
Notifications
You must be signed in to change notification settings - Fork 14
/
program.go
133 lines (116 loc) · 3.07 KB
/
program.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package addchain
import (
"fmt"
"math/big"
"github.com/mmcloughlin/addchain/internal/bigint"
)
// Op is an instruction to add positions I and J in a chain.
type Op struct{ I, J int }
// IsDouble returns whether this operation is a doubling.
func (o Op) IsDouble() bool { return o.I == o.J }
// Operands returns the indicies used in this operation. This will contain one
// or two entries depending on whether this is a doubling.
func (o Op) Operands() []int {
if o.IsDouble() {
return []int{o.I}
}
return []int{o.I, o.J}
}
// Uses reports whether the given index is one of the operands.
func (o Op) Uses(i int) bool {
return o.I == i || o.J == i
}
// Program is a sequence of operations.
type Program []Op
// Shift appends a sequence of operations that bitwise shift index i left by s,
// equivalent to s double operations. Returns the index of the result.
func (p *Program) Shift(i int, s uint) (int, error) {
for ; s > 0; s-- {
next, err := p.Double(i)
if err != nil {
return 0, err
}
i = next
}
return i, nil
}
// Double appends an operation that doubles index i. Returns the index of the
// result.
func (p *Program) Double(i int) (int, error) {
return p.Add(i, i)
}
// Add appends an operation that adds indices i and j. Returns the index of the
// result.
func (p *Program) Add(i, j int) (int, error) {
if err := p.boundscheck(i); err != nil {
return 0, err
}
if err := p.boundscheck(j); err != nil {
return 0, err
}
*p = append(*p, Op{i, j})
return len(*p), nil
}
// boundscheck returns an error if i is out of bounds.
func (p Program) boundscheck(i int) error {
// Note the corresponding chain is one longer than the program.
n := len(p)
switch {
case i < 0:
return fmt.Errorf("negative index %d", i)
case i > n:
return fmt.Errorf("index %d out of bounds", i)
}
return nil
}
// Doubles returns the number of doubles in the program.
func (p Program) Doubles() int {
doubles, _ := p.Count()
return doubles
}
// Adds returns the number of adds in the program.
func (p Program) Adds() int {
_, adds := p.Count()
return adds
}
// Count returns the number of doubles and adds in the program.
func (p Program) Count() (doubles, adds int) {
for _, op := range p {
if op.IsDouble() {
doubles++
} else {
adds++
}
}
return
}
// Evaluate executes the program and returns the resulting chain.
func (p Program) Evaluate() Chain {
c := New()
for _, op := range p {
sum := new(big.Int).Add(c[op.I], c[op.J])
c = append(c, sum)
}
return c
}
// ReadCounts returns how many times each index is read in the program.
func (p Program) ReadCounts() []int {
reads := make([]int, len(p)+1)
for _, op := range p {
for _, i := range op.Operands() {
reads[i]++
}
}
return reads
}
// Dependencies returns an array of bitsets where each bitset contains the set
// of indicies that contributed to that position.
func (p Program) Dependencies() []*big.Int {
bitsets := []*big.Int{bigint.One()}
for i, op := range p {
bitset := new(big.Int).Or(bitsets[op.I], bitsets[op.J])
bitset.SetBit(bitset, i+1, 1)
bitsets = append(bitsets, bitset)
}
return bitsets
}