xref: /openbsd-src/gnu/llvm/clang/utils/ABITest/Enumeration.py (revision e5dd70708596ae51455a0ffa086a00c5b29f8583)
1*e5dd7070Spatrick"""Utilities for enumeration of finite and countably infinite sets.
2*e5dd7070Spatrick"""
3*e5dd7070Spatrickfrom __future__ import absolute_import, division, print_function
4*e5dd7070Spatrick###
5*e5dd7070Spatrick# Countable iteration
6*e5dd7070Spatrick
7*e5dd7070Spatrick# Simplifies some calculations
8*e5dd7070Spatrickclass Aleph0(int):
9*e5dd7070Spatrick    _singleton = None
10*e5dd7070Spatrick    def __new__(type):
11*e5dd7070Spatrick        if type._singleton is None:
12*e5dd7070Spatrick            type._singleton = int.__new__(type)
13*e5dd7070Spatrick        return type._singleton
14*e5dd7070Spatrick    def __repr__(self): return '<aleph0>'
15*e5dd7070Spatrick    def __str__(self): return 'inf'
16*e5dd7070Spatrick
17*e5dd7070Spatrick    def __cmp__(self, b):
18*e5dd7070Spatrick        return 1
19*e5dd7070Spatrick
20*e5dd7070Spatrick    def __sub__(self, b):
21*e5dd7070Spatrick        raise ValueError("Cannot subtract aleph0")
22*e5dd7070Spatrick    __rsub__ = __sub__
23*e5dd7070Spatrick
24*e5dd7070Spatrick    def __add__(self, b):
25*e5dd7070Spatrick        return self
26*e5dd7070Spatrick    __radd__ = __add__
27*e5dd7070Spatrick
28*e5dd7070Spatrick    def __mul__(self, b):
29*e5dd7070Spatrick        if b == 0: return b
30*e5dd7070Spatrick        return self
31*e5dd7070Spatrick    __rmul__ = __mul__
32*e5dd7070Spatrick
33*e5dd7070Spatrick    def __floordiv__(self, b):
34*e5dd7070Spatrick        if b == 0: raise ZeroDivisionError
35*e5dd7070Spatrick        return self
36*e5dd7070Spatrick    __rfloordiv__ = __floordiv__
37*e5dd7070Spatrick    __truediv__ = __floordiv__
38*e5dd7070Spatrick    __rtuediv__ = __floordiv__
39*e5dd7070Spatrick    __div__ = __floordiv__
40*e5dd7070Spatrick    __rdiv__ = __floordiv__
41*e5dd7070Spatrick
42*e5dd7070Spatrick    def __pow__(self, b):
43*e5dd7070Spatrick        if b == 0: return 1
44*e5dd7070Spatrick        return self
45*e5dd7070Spatrickaleph0 = Aleph0()
46*e5dd7070Spatrick
47*e5dd7070Spatrickdef base(line):
48*e5dd7070Spatrick    return line*(line+1)//2
49*e5dd7070Spatrick
50*e5dd7070Spatrickdef pairToN(pair):
51*e5dd7070Spatrick    x,y = pair
52*e5dd7070Spatrick    line,index = x+y,y
53*e5dd7070Spatrick    return base(line)+index
54*e5dd7070Spatrick
55*e5dd7070Spatrickdef getNthPairInfo(N):
56*e5dd7070Spatrick    # Avoid various singularities
57*e5dd7070Spatrick    if N==0:
58*e5dd7070Spatrick        return (0,0)
59*e5dd7070Spatrick
60*e5dd7070Spatrick    # Gallop to find bounds for line
61*e5dd7070Spatrick    line = 1
62*e5dd7070Spatrick    next = 2
63*e5dd7070Spatrick    while base(next)<=N:
64*e5dd7070Spatrick        line = next
65*e5dd7070Spatrick        next = line << 1
66*e5dd7070Spatrick
67*e5dd7070Spatrick    # Binary search for starting line
68*e5dd7070Spatrick    lo = line
69*e5dd7070Spatrick    hi = line<<1
70*e5dd7070Spatrick    while lo + 1 != hi:
71*e5dd7070Spatrick        #assert base(lo) <= N < base(hi)
72*e5dd7070Spatrick        mid = (lo + hi)>>1
73*e5dd7070Spatrick        if base(mid)<=N:
74*e5dd7070Spatrick            lo = mid
75*e5dd7070Spatrick        else:
76*e5dd7070Spatrick            hi = mid
77*e5dd7070Spatrick
78*e5dd7070Spatrick    line = lo
79*e5dd7070Spatrick    return line, N - base(line)
80*e5dd7070Spatrick
81*e5dd7070Spatrickdef getNthPair(N):
82*e5dd7070Spatrick    line,index = getNthPairInfo(N)
83*e5dd7070Spatrick    return (line - index, index)
84*e5dd7070Spatrick
85*e5dd7070Spatrickdef getNthPairBounded(N,W=aleph0,H=aleph0,useDivmod=False):
86*e5dd7070Spatrick    """getNthPairBounded(N, W, H) -> (x, y)
87*e5dd7070Spatrick
88*e5dd7070Spatrick    Return the N-th pair such that 0 <= x < W and 0 <= y < H."""
89*e5dd7070Spatrick
90*e5dd7070Spatrick    if W <= 0 or H <= 0:
91*e5dd7070Spatrick        raise ValueError("Invalid bounds")
92*e5dd7070Spatrick    elif N >= W*H:
93*e5dd7070Spatrick        raise ValueError("Invalid input (out of bounds)")
94*e5dd7070Spatrick
95*e5dd7070Spatrick    # Simple case...
96*e5dd7070Spatrick    if W is aleph0 and H is aleph0:
97*e5dd7070Spatrick        return getNthPair(N)
98*e5dd7070Spatrick
99*e5dd7070Spatrick    # Otherwise simplify by assuming W < H
100*e5dd7070Spatrick    if H < W:
101*e5dd7070Spatrick        x,y = getNthPairBounded(N,H,W,useDivmod=useDivmod)
102*e5dd7070Spatrick        return y,x
103*e5dd7070Spatrick
104*e5dd7070Spatrick    if useDivmod:
105*e5dd7070Spatrick        return N%W,N//W
106*e5dd7070Spatrick    else:
107*e5dd7070Spatrick        # Conceptually we want to slide a diagonal line across a
108*e5dd7070Spatrick        # rectangle. This gives more interesting results for large
109*e5dd7070Spatrick        # bounds than using divmod.
110*e5dd7070Spatrick
111*e5dd7070Spatrick        # If in lower left, just return as usual
112*e5dd7070Spatrick        cornerSize = base(W)
113*e5dd7070Spatrick        if N < cornerSize:
114*e5dd7070Spatrick            return getNthPair(N)
115*e5dd7070Spatrick
116*e5dd7070Spatrick        # Otherwise if in upper right, subtract from corner
117*e5dd7070Spatrick        if H is not aleph0:
118*e5dd7070Spatrick            M = W*H - N - 1
119*e5dd7070Spatrick            if M < cornerSize:
120*e5dd7070Spatrick                x,y = getNthPair(M)
121*e5dd7070Spatrick                return (W-1-x,H-1-y)
122*e5dd7070Spatrick
123*e5dd7070Spatrick        # Otherwise, compile line and index from number of times we
124*e5dd7070Spatrick        # wrap.
125*e5dd7070Spatrick        N = N - cornerSize
126*e5dd7070Spatrick        index,offset = N%W,N//W
127*e5dd7070Spatrick        # p = (W-1, 1+offset) + (-1,1)*index
128*e5dd7070Spatrick        return (W-1-index, 1+offset+index)
129*e5dd7070Spatrickdef getNthPairBoundedChecked(N,W=aleph0,H=aleph0,useDivmod=False,GNP=getNthPairBounded):
130*e5dd7070Spatrick    x,y = GNP(N,W,H,useDivmod)
131*e5dd7070Spatrick    assert 0 <= x < W and 0 <= y < H
132*e5dd7070Spatrick    return x,y
133*e5dd7070Spatrick
134*e5dd7070Spatrickdef getNthNTuple(N, W, H=aleph0, useLeftToRight=False):
135*e5dd7070Spatrick    """getNthNTuple(N, W, H) -> (x_0, x_1, ..., x_W)
136*e5dd7070Spatrick
137*e5dd7070Spatrick    Return the N-th W-tuple, where for 0 <= x_i < H."""
138*e5dd7070Spatrick
139*e5dd7070Spatrick    if useLeftToRight:
140*e5dd7070Spatrick        elts = [None]*W
141*e5dd7070Spatrick        for i in range(W):
142*e5dd7070Spatrick            elts[i],N = getNthPairBounded(N, H)
143*e5dd7070Spatrick        return tuple(elts)
144*e5dd7070Spatrick    else:
145*e5dd7070Spatrick        if W==0:
146*e5dd7070Spatrick            return ()
147*e5dd7070Spatrick        elif W==1:
148*e5dd7070Spatrick            return (N,)
149*e5dd7070Spatrick        elif W==2:
150*e5dd7070Spatrick            return getNthPairBounded(N, H, H)
151*e5dd7070Spatrick        else:
152*e5dd7070Spatrick            LW,RW = W//2, W - (W//2)
153*e5dd7070Spatrick            L,R = getNthPairBounded(N, H**LW, H**RW)
154*e5dd7070Spatrick            return (getNthNTuple(L,LW,H=H,useLeftToRight=useLeftToRight) +
155*e5dd7070Spatrick                    getNthNTuple(R,RW,H=H,useLeftToRight=useLeftToRight))
156*e5dd7070Spatrickdef getNthNTupleChecked(N, W, H=aleph0, useLeftToRight=False, GNT=getNthNTuple):
157*e5dd7070Spatrick    t = GNT(N,W,H,useLeftToRight)
158*e5dd7070Spatrick    assert len(t) == W
159*e5dd7070Spatrick    for i in t:
160*e5dd7070Spatrick        assert i < H
161*e5dd7070Spatrick    return t
162*e5dd7070Spatrick
163*e5dd7070Spatrickdef getNthTuple(N, maxSize=aleph0, maxElement=aleph0, useDivmod=False, useLeftToRight=False):
164*e5dd7070Spatrick    """getNthTuple(N, maxSize, maxElement) -> x
165*e5dd7070Spatrick
166*e5dd7070Spatrick    Return the N-th tuple where len(x) < maxSize and for y in x, 0 <=
167*e5dd7070Spatrick    y < maxElement."""
168*e5dd7070Spatrick
169*e5dd7070Spatrick    # All zero sized tuples are isomorphic, don't ya know.
170*e5dd7070Spatrick    if N == 0:
171*e5dd7070Spatrick        return ()
172*e5dd7070Spatrick    N -= 1
173*e5dd7070Spatrick    if maxElement is not aleph0:
174*e5dd7070Spatrick        if maxSize is aleph0:
175*e5dd7070Spatrick            raise NotImplementedError('Max element size without max size unhandled')
176*e5dd7070Spatrick        bounds = [maxElement**i for i in range(1, maxSize+1)]
177*e5dd7070Spatrick        S,M = getNthPairVariableBounds(N, bounds)
178*e5dd7070Spatrick    else:
179*e5dd7070Spatrick        S,M = getNthPairBounded(N, maxSize, useDivmod=useDivmod)
180*e5dd7070Spatrick    return getNthNTuple(M, S+1, maxElement, useLeftToRight=useLeftToRight)
181*e5dd7070Spatrickdef getNthTupleChecked(N, maxSize=aleph0, maxElement=aleph0,
182*e5dd7070Spatrick                       useDivmod=False, useLeftToRight=False, GNT=getNthTuple):
183*e5dd7070Spatrick    # FIXME: maxsize is inclusive
184*e5dd7070Spatrick    t = GNT(N,maxSize,maxElement,useDivmod,useLeftToRight)
185*e5dd7070Spatrick    assert len(t) <= maxSize
186*e5dd7070Spatrick    for i in t:
187*e5dd7070Spatrick        assert i < maxElement
188*e5dd7070Spatrick    return t
189*e5dd7070Spatrick
190*e5dd7070Spatrickdef getNthPairVariableBounds(N, bounds):
191*e5dd7070Spatrick    """getNthPairVariableBounds(N, bounds) -> (x, y)
192*e5dd7070Spatrick
193*e5dd7070Spatrick    Given a finite list of bounds (which may be finite or aleph0),
194*e5dd7070Spatrick    return the N-th pair such that 0 <= x < len(bounds) and 0 <= y <
195*e5dd7070Spatrick    bounds[x]."""
196*e5dd7070Spatrick
197*e5dd7070Spatrick    if not bounds:
198*e5dd7070Spatrick        raise ValueError("Invalid bounds")
199*e5dd7070Spatrick    if not (0 <= N < sum(bounds)):
200*e5dd7070Spatrick        raise ValueError("Invalid input (out of bounds)")
201*e5dd7070Spatrick
202*e5dd7070Spatrick    level = 0
203*e5dd7070Spatrick    active = list(range(len(bounds)))
204*e5dd7070Spatrick    active.sort(key=lambda i: bounds[i])
205*e5dd7070Spatrick    prevLevel = 0
206*e5dd7070Spatrick    for i,index in enumerate(active):
207*e5dd7070Spatrick        level = bounds[index]
208*e5dd7070Spatrick        W = len(active) - i
209*e5dd7070Spatrick        if level is aleph0:
210*e5dd7070Spatrick            H = aleph0
211*e5dd7070Spatrick        else:
212*e5dd7070Spatrick            H = level - prevLevel
213*e5dd7070Spatrick        levelSize = W*H
214*e5dd7070Spatrick        if N<levelSize: # Found the level
215*e5dd7070Spatrick            idelta,delta = getNthPairBounded(N, W, H)
216*e5dd7070Spatrick            return active[i+idelta],prevLevel+delta
217*e5dd7070Spatrick        else:
218*e5dd7070Spatrick            N -= levelSize
219*e5dd7070Spatrick            prevLevel = level
220*e5dd7070Spatrick    else:
221*e5dd7070Spatrick        raise RuntimError("Unexpected loop completion")
222*e5dd7070Spatrick
223*e5dd7070Spatrickdef getNthPairVariableBoundsChecked(N, bounds, GNVP=getNthPairVariableBounds):
224*e5dd7070Spatrick    x,y = GNVP(N,bounds)
225*e5dd7070Spatrick    assert 0 <= x < len(bounds) and 0 <= y < bounds[x]
226*e5dd7070Spatrick    return (x,y)
227*e5dd7070Spatrick
228*e5dd7070Spatrick###
229*e5dd7070Spatrick
230*e5dd7070Spatrickdef testPairs():
231*e5dd7070Spatrick    W = 3
232*e5dd7070Spatrick    H = 6
233*e5dd7070Spatrick    a = [['  ' for x in range(10)] for y in range(10)]
234*e5dd7070Spatrick    b = [['  ' for x in range(10)] for y in range(10)]
235*e5dd7070Spatrick    for i in range(min(W*H,40)):
236*e5dd7070Spatrick        x,y = getNthPairBounded(i,W,H)
237*e5dd7070Spatrick        x2,y2 = getNthPairBounded(i,W,H,useDivmod=True)
238*e5dd7070Spatrick        print(i,(x,y),(x2,y2))
239*e5dd7070Spatrick        a[y][x] = '%2d'%i
240*e5dd7070Spatrick        b[y2][x2] = '%2d'%i
241*e5dd7070Spatrick
242*e5dd7070Spatrick    print('-- a --')
243*e5dd7070Spatrick    for ln in a[::-1]:
244*e5dd7070Spatrick        if ''.join(ln).strip():
245*e5dd7070Spatrick            print('  '.join(ln))
246*e5dd7070Spatrick    print('-- b --')
247*e5dd7070Spatrick    for ln in b[::-1]:
248*e5dd7070Spatrick        if ''.join(ln).strip():
249*e5dd7070Spatrick            print('  '.join(ln))
250*e5dd7070Spatrick
251*e5dd7070Spatrickdef testPairsVB():
252*e5dd7070Spatrick    bounds = [2,2,4,aleph0,5,aleph0]
253*e5dd7070Spatrick    a = [['  ' for x in range(15)] for y in range(15)]
254*e5dd7070Spatrick    b = [['  ' for x in range(15)] for y in range(15)]
255*e5dd7070Spatrick    for i in range(min(sum(bounds),40)):
256*e5dd7070Spatrick        x,y = getNthPairVariableBounds(i, bounds)
257*e5dd7070Spatrick        print(i,(x,y))
258*e5dd7070Spatrick        a[y][x] = '%2d'%i
259*e5dd7070Spatrick
260*e5dd7070Spatrick    print('-- a --')
261*e5dd7070Spatrick    for ln in a[::-1]:
262*e5dd7070Spatrick        if ''.join(ln).strip():
263*e5dd7070Spatrick            print('  '.join(ln))
264*e5dd7070Spatrick
265*e5dd7070Spatrick###
266*e5dd7070Spatrick
267*e5dd7070Spatrick# Toggle to use checked versions of enumeration routines.
268*e5dd7070Spatrickif False:
269*e5dd7070Spatrick    getNthPairVariableBounds = getNthPairVariableBoundsChecked
270*e5dd7070Spatrick    getNthPairBounded = getNthPairBoundedChecked
271*e5dd7070Spatrick    getNthNTuple = getNthNTupleChecked
272*e5dd7070Spatrick    getNthTuple = getNthTupleChecked
273*e5dd7070Spatrick
274*e5dd7070Spatrickif __name__ == '__main__':
275*e5dd7070Spatrick    testPairs()
276*e5dd7070Spatrick
277*e5dd7070Spatrick    testPairsVB()
278*e5dd7070Spatrick
279