/// This file implements algorithms for enumerating all monoid presentations /// up to a given length. func nextSymbol(_ s: inout Symbol, alphabet: Int) -> Bool { precondition(alphabet > 0) if s == alphabet - 1 { s = 0 return true } s += 1 return false } func nextWord(_ word: inout Word, alphabet: Int) -> Bool { var carry = true for i in word.indices.reversed() { carry = nextSymbol(&word[i], alphabet: alphabet) if !carry { break } } return carry } func nextRule(_ rule: inout Rule, alphabet: Int) -> Bool { if nextWord(&rule.rhs, alphabet: alphabet) { rule.rhs = Word(repeating: 0, count: rule.rhs.count) if nextWord(&rule.lhs, alphabet: alphabet) { rule.lhs = Word(repeating: 0, count: rule.lhs.count) return true } } return false } func nextPresentation(_ p: inout Presentation, alphabet: Int) -> Bool { precondition(!p.rules.isEmpty) var carry = true for i in p.rules.indices.reversed() { carry = nextRule(&p.rules[i], alphabet: alphabet) if !carry { break } } return carry } struct RuleShape { var lhs: Int var rhs: Int var rule: Rule { return Rule(lhs: Word(repeating: 0, count: lhs), rhs: Word(repeating: 0, count: rhs)) } } struct PresentationShape { var rules: [RuleShape] func presentation(alphabet: Int) -> Presentation { return Presentation(alphabet: alphabet, rules: rules.map { $0.rule }) } } func enumerateAll(alphabet: Int, shapes: [PresentationShape], output: Bool) -> [Presentation] { var filteredLHS = 0 var filteredRHS = 0 var filteredSymmetry = 0 var total = 0 var instances: [Presentation] = [] var unique: Set = [] let perms = allPermutations(alphabet) for shape in shapes { var p = shape.presentation(alphabet: alphabet) var done = false loop: while !done { defer { done = nextPresentation(&p, alphabet: alphabet) } total += 1 for n in 0 ..< p.rules.count - 1 { if compare(p.rules[n].lhs, p.rules[n + 1].lhs, order: .shortlex) != .lessThan { filteredLHS += 1 continue loop } } for rule in p.rules { precondition(rule.rhs.count <= rule.lhs.count) if compare(rule.rhs, rule.lhs, order: .shortlex) != .lessThan { filteredRHS += 1 continue loop } } if unique.contains(p) { filteredSymmetry += 1 continue } for perm in perms { let permuted = p.permuted(perm) unique.insert(permuted.sorted(order: .shortlex)) } instances.append(p) } } if output { print("# Total \(total)") print("# Discarded lhs:\(filteredLHS),rhs:\(filteredRHS)," + "symmetry:\(filteredSymmetry)") } return instances } func ruleShapes(_ n: Int) -> [RuleShape] { precondition(n > 0) var result: [RuleShape] = [] for i in 0 ..< n { let j = n - i // Don't consider rules with a shorter left-hand side. if j < i { continue } // Don't consider rules of the form x=1 or x=y where x, y are generators. if j == 1 && i <= 1 { continue } result.append(RuleShape(lhs: j, rhs: i)) } return result } func presentationShapes(rules: Int, ofLength n: Int) -> [PresentationShape] { precondition(n > 0) precondition(rules > 0) if rules == 1 { return ruleShapes(n).map { PresentationShape(rules: [$0]) } } var result: [PresentationShape] = [] for i in 1 ..< n { let next = presentationShapes(rules: rules - 1, ofLength: i) for x in ruleShapes(n - i) { for y in next { if x.lhs <= y.rules.first!.lhs { result.append(PresentationShape(rules: [x] + y.rules)) } } } } return result } func presentationShapes(rules: Int, upToLength n: Int) -> [PresentationShape] { var shapes: [PresentationShape] = [] for i in 1 ... n { shapes.append(contentsOf: presentationShapes(rules: rules, ofLength: i)) } return shapes }