| 1 | |
|---|
| 2 | class XorRestriction(base): |
|---|
| 3 | """Boolean XOR grouping of restrictions.""" |
|---|
| 4 | __slots__ = () |
|---|
| 5 | |
|---|
| 6 | def __init__(self, *a, **kw): |
|---|
| 7 | raise NotImplementedError("kindly don't use xor yet") |
|---|
| 8 | |
|---|
| 9 | def match(self, vals): |
|---|
| 10 | if not self.restrictions: |
|---|
| 11 | return not self.negate |
|---|
| 12 | |
|---|
| 13 | if self.negate: |
|---|
| 14 | # 1|1 == 0|0 == 1, 0|1 == 1|0 == 0 |
|---|
| 15 | armed = self.restrictions[0].match(*vals) |
|---|
| 16 | for rest in islice(self.restrictions, 1, len(self.restrictions)): |
|---|
| 17 | if armed != rest.match(vals): |
|---|
| 18 | return False |
|---|
| 19 | return True |
|---|
| 20 | # 0|1 == 1|0 == 1, 0|0 == 1|1 == 0 |
|---|
| 21 | armed = False |
|---|
| 22 | for rest in self.restrictions: |
|---|
| 23 | if armed == rest.match(vals): |
|---|
| 24 | if armed: |
|---|
| 25 | return False |
|---|
| 26 | else: |
|---|
| 27 | if not armed: |
|---|
| 28 | armed = True |
|---|
| 29 | if armed: |
|---|
| 30 | return True |
|---|
| 31 | return False |
|---|
| 32 | |
|---|
| 33 | def force_True(self, pkg, *vals): |
|---|
| 34 | pvals = [pkg] |
|---|
| 35 | pvals.extend(vals) |
|---|
| 36 | entry_point = pkg.changes_count() |
|---|
| 37 | truths = [r.match(*pvals) for r in self.restrictions] |
|---|
| 38 | count = truths.count(True) |
|---|
| 39 | # get the simple one out of the way first. |
|---|
| 40 | l = len(truths) |
|---|
| 41 | if self.negate: |
|---|
| 42 | f = lambda r: r.force_False(*pvals) |
|---|
| 43 | t = lambda r: r.force_True(*pvals) |
|---|
| 44 | if count > l/2: order = ((t, count, True), (f, l - count, False)) |
|---|
| 45 | else: order = ((f, l - count, False), (t, count, True)) |
|---|
| 46 | for action, current, desired in order: |
|---|
| 47 | if current == l: |
|---|
| 48 | return True |
|---|
| 49 | for x, r in enumerate(self.restrictions): |
|---|
| 50 | if truths[x] != desired: |
|---|
| 51 | if action(r): |
|---|
| 52 | current += 1 |
|---|
| 53 | else: |
|---|
| 54 | break |
|---|
| 55 | if current == l: |
|---|
| 56 | return True |
|---|
| 57 | pkg.rollback(entry_point) |
|---|
| 58 | return False |
|---|
| 59 | |
|---|
| 60 | stack = [] |
|---|
| 61 | for x, val in enumerate(truths): |
|---|
| 62 | falses = filter(None, val) |
|---|
| 63 | if truths[x]: |
|---|
| 64 | falses.remove(x) |
|---|
| 65 | stack.append((falses, None)) |
|---|
| 66 | else: |
|---|
| 67 | stack.append((falses, x)) |
|---|
| 68 | |
|---|
| 69 | if count == 1: |
|---|
| 70 | return True |
|---|
| 71 | del stack[truths.index(True)] |
|---|
| 72 | |
|---|
| 73 | for falses, truths in stack: |
|---|
| 74 | failed = False |
|---|
| 75 | for x in falses: |
|---|
| 76 | if not self.restrictions[x].force_False(*pvals): |
|---|
| 77 | failed = True |
|---|
| 78 | break |
|---|
| 79 | if not failed: |
|---|
| 80 | if trues is not None: |
|---|
| 81 | if self.restrictions[x].force_True(*pvals): |
|---|
| 82 | return True |
|---|
| 83 | else: |
|---|
| 84 | return True |
|---|
| 85 | pkg.rollback(entry_point) |
|---|
| 86 | return False |
|---|
| 87 | |
|---|
| 88 | def force_False(self, pkg, *vals): |
|---|
| 89 | pvals = [pkg] |
|---|
| 90 | pvals.extend(vals) |
|---|
| 91 | entry_point = pkg.changes_count() |
|---|
| 92 | truths = [r.match(*pvals) for r in self.restrictions] |
|---|
| 93 | count = truths.count(True) |
|---|
| 94 | # get the simple one out of the way first. |
|---|
| 95 | l = len(truths) |
|---|
| 96 | if not self.negate: |
|---|
| 97 | f = lambda r: r.force_False(*pvals) |
|---|
| 98 | t = lambda r: r.force_True(*pvals) |
|---|
| 99 | if count > l/2: order = ((t, count, True), (f, l - count, False)) |
|---|
| 100 | else: order = ((f, l - count, False), (t, count, True)) |
|---|
| 101 | for action, current, desired in order: |
|---|
| 102 | if current == l: |
|---|
| 103 | return True |
|---|
| 104 | |
|---|
| 105 | for x, r in enumerate(self.restrictions): |
|---|
| 106 | if truths[x] != desired: |
|---|
| 107 | if action(r): |
|---|
| 108 | current += 1 |
|---|
| 109 | else: |
|---|
| 110 | break |
|---|
| 111 | if current == l: |
|---|
| 112 | return True |
|---|
| 113 | pkg.rollback(entry_point) |
|---|
| 114 | return False |
|---|
| 115 | # the fun one. |
|---|
| 116 | stack = [] |
|---|
| 117 | for x, val in enumerate(truths): |
|---|
| 118 | falses = filter(None, val) |
|---|
| 119 | if truths[x]: |
|---|
| 120 | falses.remove(x) |
|---|
| 121 | stack.append((falses, None)) |
|---|
| 122 | else: |
|---|
| 123 | stack.append((falses, x)) |
|---|
| 124 | |
|---|
| 125 | if count == 1: |
|---|
| 126 | return True |
|---|
| 127 | |
|---|
| 128 | for falses, truths in stack: |
|---|
| 129 | failed = False |
|---|
| 130 | for x in falses: |
|---|
| 131 | if not self.restrictions[x].force_False(*pvals): |
|---|
| 132 | failed = True |
|---|
| 133 | break |
|---|
| 134 | if not failed: |
|---|
| 135 | if trues is not None: |
|---|
| 136 | if self.restrictions[x].force_True(*pvals): |
|---|
| 137 | return True |
|---|
| 138 | else: |
|---|
| 139 | return True |
|---|
| 140 | pkg.rollback(entry_point) |
|---|
| 141 | return False |
|---|
| 142 | |
|---|
| 143 | def __str__(self): |
|---|
| 144 | if self.negate: |
|---|
| 145 | return "not ( %s )" % " ^^ ".join(str(x) for x in self.restrictions) |
|---|
| 146 | return "( %s )" % " ^^ ".join(str(x) for x in self.restrictions) |
|---|