github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/go-weave/amb/det.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package amb 6 7 import "fmt" 8 9 // StrategyDFS explores the ambiguous value space in depth-first order 10 // up to MaxDepth. It is deterministic and (given enough time) will 11 // explore the entire space. 12 type StrategyDFS struct { 13 // MaxDepth specifies the maximum depth of the tree. If this 14 // is 0, it defaults to DefaultMaxDepth. 15 MaxDepth int 16 17 branchWidths []int 18 curPath []int 19 step int 20 } 21 22 func (s *StrategyDFS) Reset() { 23 s.branchWidths = nil 24 s.curPath = nil 25 s.step = 0 26 } 27 28 func (s *StrategyDFS) maxDepth() int { 29 if s.MaxDepth == 0 { 30 return DefaultMaxDepth 31 } 32 return s.MaxDepth 33 } 34 35 func (s *StrategyDFS) Amb(n int) (int, bool) { 36 if s.step < len(s.curPath) { 37 // We're in replay mode. 38 if n != s.branchWidths[s.step] { 39 panic(&ErrNondeterminism{fmt.Sprintf("Amb(%d) during replay, but previous call was Amb(%d)", n, s.branchWidths[s.step])}) 40 } 41 res := s.curPath[s.step] 42 s.step++ 43 return res, true 44 } 45 46 if len(s.curPath) == s.maxDepth() { 47 return 0, false 48 } 49 50 // We're in exploration mode. 51 s.branchWidths = append(s.branchWidths, n) 52 s.curPath = append(s.curPath, 0) 53 s.step++ 54 return 0, true 55 } 56 57 func (s *StrategyDFS) Next() bool { 58 s.step = 0 59 60 // Construct the next path prefix to explore. 61 for i := len(s.curPath) - 1; i >= 0; i-- { 62 s.curPath[i]++ 63 if s.curPath[i] < s.branchWidths[i] { 64 break 65 } 66 s.curPath = s.curPath[:len(s.curPath)-1] 67 } 68 s.branchWidths = s.branchWidths[:len(s.curPath)] 69 if len(s.branchWidths) == 0 { 70 // We're out of paths. 71 return false 72 } 73 return true 74 } 75 76 // ErrNondeterminism is the error used by deterministic strategies to 77 // indicate that the strategy detected that the application behaved 78 // non-deterministically. 79 type ErrNondeterminism struct { 80 Detail string 81 } 82 83 func (e *ErrNondeterminism) Error() string { 84 return "non-determinism detected: " + e.Detail 85 }