-
Notifications
You must be signed in to change notification settings - Fork 14
/
opt.go
101 lines (85 loc) · 2.19 KB
/
opt.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
// Package opt implements generic optimizations that remove redundancy from addition chains.
package opt
import (
"fmt"
"math/big"
"github.com/mmcloughlin/addchain"
"github.com/mmcloughlin/addchain/alg"
)
// Algorithm applies chain optimization to the result of a wrapped algorithm.
type Algorithm struct {
Algorithm alg.ChainAlgorithm
}
func (a Algorithm) String() string {
return fmt.Sprintf("opt(%s)", a.Algorithm)
}
// FindChain delegates to the wrapped algorithm, then runs Optimize on the result.
func (a Algorithm) FindChain(n *big.Int) (addchain.Chain, error) {
c, err := a.Algorithm.FindChain(n)
if err != nil {
return nil, err
}
opt, err := Optimize(c)
if err != nil {
return nil, err
}
return opt, nil
}
// Optimize aims to remove redundancy from an addition chain.
func Optimize(c addchain.Chain) (addchain.Chain, error) {
// Build program for c with all possible options at each step.
ops := make([][]addchain.Op, len(c))
for k := 1; k < len(c); k++ {
ops[k] = c.Ops(k)
}
// Count how many times each index is used where it is the only available Op.
counts := make([]int, len(c))
for k := 1; k < len(c); k++ {
if len(ops[k]) != 1 {
continue
}
for _, i := range ops[k][0].Operands() {
counts[i]++
}
}
// Now, try to remove the positions which are never the only available op.
remove := []int{}
for k := 1; k < len(c)-1; k++ {
if counts[k] > 0 {
continue
}
// Prune places k is used.
for l := k + 1; l < len(c); l++ {
ops[l] = pruneuses(ops[l], k)
// If this list now only has one element, the operands in it are now
// indispensable.
if len(ops[l]) == 1 {
for _, i := range ops[l][0].Operands() {
counts[i]++
}
}
}
// Mark k for deletion.
remove = append(remove, k)
}
// Perform removals.
pruned := addchain.Chain{}
for i, x := range c {
if len(remove) > 0 && remove[0] == i {
remove = remove[1:]
continue
}
pruned = append(pruned, x)
}
return pruned, nil
}
// pruneuses removes any uses of i from the list of operations.
func pruneuses(ops []addchain.Op, i int) []addchain.Op {
filtered := ops[:0]
for _, op := range ops {
if !op.Uses(i) {
filtered = append(filtered, op)
}
}
return filtered
}