def _mul_CO_CO(t1, t2): """ Returns an iterator of Triples representing the product of t1 (CO) and t2 (CO). >>> list(_mul_CO_CO(T(18, None, 3), T(5, None, 5))) [(90, *, 15)] >>> list(_mul_CO_CO(T(4, None, 3), T(-6, None, 9))) [(*, *, 3, phase=0)] """ gcf1 = (rational.gcf(t1.skip, t1.phase) if t1.phase else t1.skip) gcf2 = (rational.gcf(t2.skip, t2.phase) if t2.phase else t2.skip) newStart = (None if t1.start < 0 or t2.start < 0 else t1.start * t2.start) yield T(newStart, None, gcf1 * gcf2)
def _xorConstant_sub(t, k): """ Subroutine to assist _xorConstant. The Triple t and the constant k must both be positive. ### C(_xorConstant_sub(T(4, None, 5), 6)).asList() [(2, *, 40), (8, *, 40), (15, *, 40), (21, *, 40), (27, *, 40), (30, *, 40), (33, *, 40), (36, *, 40)] ### C(_xorConstant_sub(T(4, 79, 5), 6)).asList() [(30, 54, 6), (55, 73, 6), (2, 14, 6), (15, 39, 6), (70, 82, 6)] """ assert t.start >= 0 and k > 0, "_xorConstant_sub invariant failed!" bl = utilities.binlist(k) pow2 = 2**len(bl) cycle = pow2 // rational.gcf(pow2, t.skip) cs = cycle * t.skip if t.stop is None: for start in range(t.start, t.start + cs, t.skip): yield T(start ^ k, None, cs) else: tLast = t.stop - t.skip for start in range(t.start, t.start + cs, t.skip): xStart = start ^ k j = (tLast - start) // cs if j >= 0: xStop = xStart + (1 + j) * cs yield T(xStart, xStop, cs)
def _mod_OO_CC(t1, t2): """ Returns an iterator over Triples representing all values in t1 (OO) modulus all values in t2 (CC). ### list(_mod_OO_CC(T(None, None, 5, 0), T(35, 105, 7))) [(0, 98, 1)] ### list(_mod_OO_CC(T(None, None, 5, 0), T(35, 42, 7))) [(0, 35, 5)] ### list(_mod_OO_CC(T(None, None, 5, 3), T(35, 56, 7))) [(0, 49, 1)] """ gcf = rational.gcf(t1.skip, t2.skip) if (t2.stop - t2.start) < (t1.skip * t2.skip // gcf): # not enough for a full cycle, so do indiv values s = set() for n in t2: s.update(_modConstant_set(t1, n)) for obj in utilities.tripleIteratorFromIterable(s): yield obj else: # enough for a full cycle tTemp = next(_mod_OO_OO(t1, T(None, None, t2.skip, t2.phase))) newStop = (t2.stop - t2.skip - 1) + tTemp.skip yield T(tTemp.start, newStop, tTemp.skip)
def _mul_OO_CO(t1, t2): """ Returns a list representing the product of t1 (OO) and t2 (CO). Because of the prime fallout problem, the results of this method are usually supersets of the actual answers, which await meta-Triples (see the docstring for the _mul_OO_OO method). >>> list(_mul_OO_CO(T(None, None, 5, 0), T(0, None, 4))) [(*, *, 20, phase=0)] """ a, b, c, d = t1.phase, t1.skip, t2.start, t2.skip if a == 0: if c == 0: # (bi)(dj) case yield T(None, None, b * d, 0) else: gcf = rational.gcf(abs(c), d) cNew = c // gcf dNew = d // gcf if (1 - cNew) % dNew == 0 or (-1 - cNew) % dNew == 0: yield T(None, None, b * gcf, 0) else: yield T(None, None, b, 0) elif c == 0: yield T(None, None, d, 0) else: yield T(None, None, 1, 0)
def addOp(t1, t2): """ Returns an iterator of Triples representing the sum of the input Triples. No particular attempt is made to optimize the return result, since TripleCollections should be used to do the needed optimizations. Doctests are present in the specific helper functions. """ try: kind1 = (t1.start is None, t1.stop is None) kind2 = (t2.start is None, t2.stop is None) gcf = rational.gcf(t1.skip, t2.skip) t = (kind1, kind2) if t == ((False, False), (False, False)): r = _add_CC_CC(t1, t2) elif t in _add_dispatchTable: r = _add_dispatchTable[t](t1, t2, gcf) else: r = _add_dispatchTable[(kind2, kind1)](t2, t1, gcf) except AttributeError: r = iter([_addConstant(t1, t2)]) return r
def _mul_OC_OC(t1, t2): """ Returns a list representing the product of t1 (OC) and t2 (OC). >>> list(_mul_OC_OC(T(None, 0, 5), T(None, 0, 7))) [(35, *, 35)] >>> list(_mul_OC_OC(T(None, 0, 5), T(None, 6, 3))) [(*, *, 15, phase=0)] """ gcf1 = (rational.gcf(t1.skip, t1.phase) if t1.phase else t1.skip) gcf2 = (rational.gcf(t2.skip, t2.phase) if t2.phase else t2.skip) newSkip = gcf1 * gcf2 selfLast = t1.stop - t1.skip otherLast = t2.stop - t2.skip newStart = (None if selfLast > 0 or otherLast > 0 else selfLast * otherLast) yield T(newStart, None, gcf1 * gcf2)
def convert(t, oldBasis, newBasis, round=True): """ Returns an iterator of Triples representing t converted from oldBasis to newBasis. No particular attempt is made to optimize the return result, since TripleCollections should be used to do the needed optimizations. If round is True values will be rounded (0.5 rounds up). If round is False values will be integer-division truncated. >>> T, C = (triple.Triple, collection.Collection) >>> C(convert(T(1, 11, 2), 4, 4, True)).asList() [(1, 11, 2)] >>> C(convert(T(1, 11, 2), 4, 4, False)).asList() [(1, 11, 2)] >>> C(convert(T(1, 11, 2), 4, 16, True)).asList() [(4, 44, 8)] >>> C(convert(T(1, 11, 2), 4, 16, False)).asList() [(4, 44, 8)] >>> C(convert(T(1, 17, 2), 16, 4, True)).asList() [(0, 5, 1)] >>> C(convert(T(1, 17, 2), 16, 4, False)).asList() [(0, 4, 1)] >>> C(convert(T(2, 29, 3), 9, 12, True)).asList() [(3, 39, 4)] >>> C(convert(T(2, 29, 3), 9, 12, False)).asList() [(2, 38, 4)] """ if oldBasis == newBasis: yield t else: gcf = rational.gcf(oldBasis, newBasis) lcm = (oldBasis * newBasis) // gcf if gcf == oldBasis: for tProduct in t.mul(newBasis // oldBasis): yield tProduct elif gcf == newBasis: divisor = oldBasis // newBasis if round: for tDividend in t.add(divisor // 2): for tQuotient in tDividend.div(divisor): yield tQuotient else: for tQuotient in t.div(divisor): yield tQuotient else: for tScaled in t.mul(lcm // oldBasis): for tResult in convert(tScaled, lcm, newBasis, round): yield tResult
def _mul_CO_OC(t1, t2): """ Returns an iterator of Triples representing the product of t1 (CO) and t2 (OC). >>> list(_mul_CO_OC(T(4, None, 3), T(None, 15, 9))) [(*, *, 3, phase=0)] >>> list(_mul_CO_OC(T(21, None, 7), T(None, 0, 4))) [(*, -56, 28)] """ gcf1 = (rational.gcf(t1.skip, t1.phase) if t1.phase else t1.skip) gcf2 = (rational.gcf(t2.skip, t2.phase) if t2.phase else t2.skip) newSkip = gcf1 * gcf2 if t1.start >= 0 and (t2.stop - t2.skip) < 0: newStop = t1.start * (t2.stop - t2.skip) + newSkip else: newStop = None yield T(None, newStop, newSkip)
def _mod_OO_OO(t1, t2): """ Returns an iterator over Triples representing all values in t1 (OO) modulus all values in t2 (OO). ### list(_mod_OO_OO(T(None, None, 5, 0), T(None, None, 7, 0))) [(0, *, 1)] ### list(_mod_OO_OO(T(None, None, 6, 2), T(None, None, 4, 0))) [(0, *, 2)] ### list(_mod_OO_OO(T(None, None, 24, 11), T(None, None, 9, 6))) [(2, *, 3)] """ gcf = rational.gcf(t1.skip, t2.skip) if gcf == 1: yield T(0, None, 1) elif t2.phase == 0: yield T(t1.phase % gcf, None, gcf) else: newSkip = rational.gcf(gcf, t2.phase) yield T(t1.phase % newSkip, None, newSkip)
def _mul_OO_OC(t1, t2): """ Returns a list representing the product of t1 (OO) and t2 (OC). Because of the prime fallout problem, the results of this method are usually supersets of the actual answers, which await meta-Triples (see the docstring for the _mul_OO_OO method). >>> list(_mul_OO_OC(T(None, None, 5, 0), T(None, 0, 4))) [(*, *, 20, phase=0)] >>> list(_mul_OO_OC(T(None, None, 5, 0), T(None, 10, 4))) [(*, *, 10, phase=0)] >>> list(_mul_OO_OC(T(None, None, 5, 2), T(None, 0, 3))) [(*, *, 1, phase=0)] """ a, b, c, d = t1.phase, t1.skip, t2.stop - t2.skip, t2.skip if a == 0: if c == 0: # (bi)(-dj) case, d>0, j>=0, i any integer # this becomes (-bd)(ij), and if j=1, this spans all values yield T(None, None, b * d, 0) else: # (bi)(c-dj) case, d>0, j>=0, i any integer gcf = rational.gcf(abs(c), d) cNew = c // gcf dNew = d // gcf if (1 - cNew) % dNew == 0 or (-1 - cNew) % dNew == 0: yield T(None, None, b * gcf, 0) else: yield T(None, None, b, 0) # fallback case elif c == 0: yield T(None, None, d, 0) # fallback case else: yield T(None, None, 1, 0) # full fallback case
def intersection(self, other): """ Returns a Triple representing the intersection of self and other, or None if they do not intersect. >>> Triple(1, 11, 2).intersection(Triple(3, 6, 1)) (3, 7, 2) >>> Triple(1, 13, 4).intersection(Triple(9, 49, 4)) (9, 13, 4) >>> Triple(4, 34, 3).intersection(Triple(1, 31, 6)) (7, 31, 6) >>> Triple(4, 34, 3).intersection(Triple(3, 33, 6)) is None True >>> Triple(4, 7, 3).intersection(Triple(1, 31, 6)) is None True >>> Triple(1, 10, 1).intersection(Triple(10, 20, 1)) is None True >>> Triple(None, 20, 1).intersection(Triple(10, 30, 1)) (10, 20, 1) >>> Triple(None, 68, 6).intersection(Triple(23, None, 9)) (32, 68, 18) >>> Triple(None, 14, 2).intersection(Triple(None, 28, 7)) (*, 14, 14) >>> Triple(None, None, 2, 1).intersection(Triple(None, None, 2, 1)) (*, *, 2, phase=1) >>> Triple(None, None, 5, 2).intersection(Triple(None, None, 10, 7)) (*, *, 10, phase=7) """ gcf = rational.gcf(self.skip, other.skip) lcm = (self.skip * other.skip) // gcf if self.phase % gcf != other.phase % gcf: return None firsts, lasts = [], [] for x, y in ((self, other), (other, self)): thisStart = x.start if thisStart is not None: while thisStart % y.skip != y.phase: thisStart += x.skip if x.stop is not None and thisStart >= x.stop: return None firsts.append(thisStart) if x.stop is not None: thisLast = x.stop - x.skip while thisLast % y.skip != y.phase: thisLast -= x.skip if x.start is not None and thisLast < x.start: return None lasts.append(thisLast) thisStart = (max(firsts) if firsts else None) thisStop = (min(lasts) + lcm if lasts else None) if thisStart is not None and thisStop is not None and thisStart >= thisStop: return None if thisStart is None and thisStop is None: newPhase = (self.phase if self.skip >= other.skip else other.phase) else: newPhase = None return Triple(thisStart, thisStop, lcm, newPhase)
def _mul_OO_OO(t1, t2): """ Given two doubly-open Triples (None, None, a, b) and (None, None, c, d), the actual product is the union of the following two meta-Triples: (None, None, (bc, None, ac), (bd, None, ad)) (None, None, (ac-bc, None, ac), ((a-b)(c-d), None, ac-ad)) However, there is currently no support for meta-Triples (i.e. Triples whose starts, stops, skips and/or phases are themselves Triples). This limitation could be addressed at some point in the future. For the time being this method usually returns (None, None, 1, 0), which is at least a superset of the actual answer. There are some special cases handled here, such as both Triples having a phase of zero, or one having a phase of zero and certain t2 conditions holding for the t2, where a real answer is returned. >>> list(_mul_OO_OO(T(None, None, 2, 0), T(None, None, 4, 0))) [(*, *, 8, phase=0)] >>> list(_mul_OO_OO(T(None, None, 2, 0), T(None, None, 4, 1))) [(*, *, 2, phase=0)] >>> list(_mul_OO_OO(T(None, None, 4, 2), T(None, None, 2, 0))) [(*, *, 4, phase=0)] >>> list(_mul_OO_OO(T(None, None, 2, 0), T(None, None, 7, 3))) [(*, *, 1, phase=0)] """ finalCase = False if t1.phase == 0: if t2.phase == 0: yield T(None, None, t1.skip * t2.skip, 0) else: b = t1.skip * t2.phase c = t1.skip * t2.skip newSkip = rational.gcf(b, c) b //= newSkip c //= newSkip if (1 - b) % c == 0 or (-1 - b) % c == 0: yield T(None, None, newSkip, 0) else: yield T(None, None, 1, 0) # fallback case elif t2.phase == 0: b = t2.skip * t1.phase c = t2.skip * t1.skip newSkip = rational.gcf(b, c) b //= newSkip c //= newSkip if (1 - b) % c == 0 or (-1 - b) % c == 0: yield T(None, None, newSkip, 0) else: finalCase = True else: finalCase = True if finalCase: # We have (a+bi) times (c+di) # This breaks into ac + f * (xi + yj + zij) a, b, c, d = t1.phase, t1.skip, t2.phase, t2.skip x, y, z = b * c, a * d, b * d f = rational.gcf(rational.gcf(x, y), z) x //= f y //= f z //= f if abs(x) == 1 or abs(y) == 1: yield T(None, None, f, (a * c) % f) elif (1 - y) % z == 0 or (-1 - y) % z == 0 or (1 - x) % z == 0 or ( -1 - x) % z == 0: yield T(None, None, f, (a * c) % f) else: yield T(None, None, 1, 0) # fallback case
def _andConstant(t, k): """ Returns an iterator of Triples with the results of a constant logical-ANDed with t. >>> T = triple.Triple >>> list(_andConstant(T(1, 100, 1), 0)) [(0, 1, 1)] >>> list(_andConstant(T(0, None, 12), 4)) [(0, 8, 4)] >>> list(_andConstant(T(14, 770, 7), 5)) [(1, 7, 3), (0, 10, 5)] >>> list(_andConstant(T(None, None, 7, 2), -3)) [(*, *, 28, phase=0), (*, *, 28, phase=9), (*, *, 28, phase=16), (*, *, 28, phase=21)] >>> list(_andConstant(T(None, 303, 7), -3)) [(*, 308, 28), (*, 317, 28), (*, 324, 28), (*, 301, 28)] >>> list(_andConstant(T(51, None, 7), -3)) [(56, *, 28), (65, *, 28), (72, *, 28), (49, *, 28)] >>> list(_andConstant(T(16, 37, 7), -3)) [(28, 56, 28), (16, 44, 28), (21, 49, 28)] """ Triple = triple.Triple if k == 0: yield Triple(0, 1, 1) elif k == -1: yield t elif k < 0: # because binlist only works with non-negative numbers, create an # equivalent positive value for k which will result in the same output bl = binlist(-k-1) pow2 = 2 ** len(bl) cycle = pow2 // rational.gcf(pow2, t.skip) bigCycle = cycle * t.skip if t.start is None and t.stop is None: for start in range(t.phase, t.phase + bigCycle, t.skip): yield Triple(None, None, bigCycle, start & k) elif t.start is None: last = t.stop - t.skip d = dict((n % bigCycle, n) for n in range(last, last - bigCycle, -t.skip)) for start in range(t.phase, t.phase + bigCycle, t.skip): yield Triple(None, (d[start] & k) + bigCycle, bigCycle, start & k) elif t.stop is None: d = dict((n % bigCycle, n) for n in range(t.start, t.start + bigCycle, t.skip)) for start in range(t.phase, t.phase + bigCycle, t.skip): yield Triple(d[start] & k, None, bigCycle, start & k) else: last = t.stop - t.skip dStarts = {} for n in range(t.start, t.start + bigCycle, t.skip): if n > last: break dStarts[n % bigCycle] = n & k dStops = {} for n in range(last, last - bigCycle, -t.skip): if n < t.start: break dStops[n % bigCycle] = (n & k) + bigCycle for start in range(t.phase, t.phase + bigCycle, t.skip): if start in dStarts: actualStart = dStarts[start] stop = dStops.get(start, actualStart + bigCycle) yield Triple(actualStart, stop, bigCycle) else: bl = binlist(k) pow2 = 2 ** len(bl) cycle = pow2 // rational.gcf(pow2, t.skip) if t.start is None: if t.stop is None: start = t.phase stop = start + cycle * t.skip else: stop = t.stop start = stop - cycle * t.skip elif t.stop is None: start = t.start stop = start + cycle * t.skip else: start = t.start stop = min(t.stop, start + cycle * t.skip) s = set(n & k for n in range(start, stop, t.skip)) for t in utilities.tripleIteratorFromIterable(s): yield t
def _divConstant(t, k): """ Returns an iterator of Triples resulting from the division of the specified Triple by the specified constant. ### C(_divConstant(T(15, None, 4), -1)) Ranges: [(*, -11, 4)] ### C(_divConstant(T(150, 45000, 50), 64)) Ranges: [(2, 703, 1)] ### C(_divConstant(T(None, None, 7, 2), 5)) Ranges: [(*, *, 7, phase=0), (*, *, 7, phase=1), (*, *, 7, phase=3), (*, *, 7, phase=4), (*, *, 7, phase=6)] ### C(_divConstant(T(None, None, 12, 2), 8)) Ranges: [(*, *, 3, phase=0), (*, *, 3, phase=1)] ### C(_divConstant(T(None, 122, 12, 2), 8)) Ranges: [(*, 15, 3), (*, 16, 3)] ### C(_divConstant(T(50, None, 12, 2), 8)) Ranges: [(6, *, 3), (7, *, 3)] ### C(_divConstant(T(50, 110, 12), 8)) Singles: [7, 10], Ranges: [(6, 15, 3)] ### C(_divConstant(T(3, None, 5), 0)) Traceback (most recent call last): ... ZeroDivisionError: _divConstant called with zero constant! """ if k == 0: raise ZeroDivisionError("_divConstant called with zero constant!") if k < 0: t = -t k = -k if k == 1: yield t elif k >= t.skip: newStart = (None if t.start is None else t.start // k) newStop = (None if t.stop is None else 1 + (t.stop - t.skip) // k) yield T(newStart, newStop, 1) else: gcf = rational.gcf(t.skip, k) phaseSkip = t.skip // gcf if t.start is None and t.stop is None: for i in T(t.phase, t.phase + phaseSkip * k, t.skip): yield T(None, None, phaseSkip, i // k) elif t.start is None: for i in T(t.stop - phaseSkip * k, t.stop, t.skip): yield T(None, i // k + phaseSkip, phaseSkip) elif t.stop is None: for i in T(t.start, t.start + phaseSkip * k, t.skip): yield T(i // k, None, phaseSkip) else: start = t.start loopStop = min(t.stop, t.start + phaseSkip * k) while start < loopStop: startDiv = start // k thisLast = t.stop - t.skip thisLastDiv = thisLast // k while thisLastDiv % phaseSkip != startDiv % phaseSkip: thisLast -= t.skip thisLastDiv = thisLast // k yield T(startDiv, thisLastDiv + phaseSkip, phaseSkip) start += t.skip