1 """Utilities for enumeration of finite and countably infinite sets.
3 from __future__
import absolute_import
, division
, print_function
8 # Simplifies some calculations
13 if type._singleton
is None:
14 type._singleton
= int.__new
__(type)
15 return type._singleton
27 raise ValueError("Cannot subtract aleph0")
43 def __floordiv__(self
, b
):
45 raise ZeroDivisionError
48 __rfloordiv__
= __floordiv__
49 __truediv__
= __floordiv__
50 __rtuediv__
= __floordiv__
51 __div__
= __floordiv__
52 __rdiv__
= __floordiv__
64 return line
* (line
+ 1) // 2
69 line
, index
= x
+ y
, y
70 return base(line
) + index
73 def getNthPairInfo(N
):
74 # Avoid various singularities
78 # Gallop to find bounds for line
81 while base(next
) <= N
:
85 # Binary search for starting line
89 # assert base(lo) <= N < base(hi)
97 return line
, N
- base(line
)
101 line
, index
= getNthPairInfo(N
)
102 return (line
- index
, index
)
105 def getNthPairBounded(N
, W
=aleph0
, H
=aleph0
, useDivmod
=False):
106 """getNthPairBounded(N, W, H) -> (x, y)
108 Return the N-th pair such that 0 <= x < W and 0 <= y < H."""
111 raise ValueError("Invalid bounds")
113 raise ValueError("Invalid input (out of bounds)")
116 if W
is aleph0
and H
is aleph0
:
119 # Otherwise simplify by assuming W < H
121 x
, y
= getNthPairBounded(N
, H
, W
, useDivmod
=useDivmod
)
127 # Conceptually we want to slide a diagonal line across a
128 # rectangle. This gives more interesting results for large
129 # bounds than using divmod.
131 # If in lower left, just return as usual
136 # Otherwise if in upper right, subtract from corner
141 return (W
- 1 - x
, H
- 1 - y
)
143 # Otherwise, compile line and index from number of times we
146 index
, offset
= N
% W
, N
// W
147 # p = (W-1, 1+offset) + (-1,1)*index
148 return (W
- 1 - index
, 1 + offset
+ index
)
151 def getNthPairBoundedChecked(
152 N
, W
=aleph0
, H
=aleph0
, useDivmod
=False, GNP
=getNthPairBounded
154 x
, y
= GNP(N
, W
, H
, useDivmod
)
155 assert 0 <= x
< W
and 0 <= y
< H
159 def getNthNTuple(N
, W
, H
=aleph0
, useLeftToRight
=False):
160 """getNthNTuple(N, W, H) -> (x_0, x_1, ..., x_W)
162 Return the N-th W-tuple, where for 0 <= x_i < H."""
167 elts
[i
], N
= getNthPairBounded(N
, H
)
175 return getNthPairBounded(N
, H
, H
)
177 LW
, RW
= W
// 2, W
- (W
// 2)
178 L
, R
= getNthPairBounded(N
, H
**LW
, H
**RW
)
180 L
, LW
, H
=H
, useLeftToRight
=useLeftToRight
181 ) + getNthNTuple(R
, RW
, H
=H
, useLeftToRight
=useLeftToRight
)
184 def getNthNTupleChecked(N
, W
, H
=aleph0
, useLeftToRight
=False, GNT
=getNthNTuple
):
185 t
= GNT(N
, W
, H
, useLeftToRight
)
193 N
, maxSize
=aleph0
, maxElement
=aleph0
, useDivmod
=False, useLeftToRight
=False
195 """getNthTuple(N, maxSize, maxElement) -> x
197 Return the N-th tuple where len(x) < maxSize and for y in x, 0 <=
200 # All zero sized tuples are isomorphic, don't ya know.
204 if maxElement
is not aleph0
:
205 if maxSize
is aleph0
:
206 raise NotImplementedError("Max element size without max size unhandled")
207 bounds
= [maxElement
**i
for i
in range(1, maxSize
+ 1)]
208 S
, M
= getNthPairVariableBounds(N
, bounds
)
210 S
, M
= getNthPairBounded(N
, maxSize
, useDivmod
=useDivmod
)
211 return getNthNTuple(M
, S
+ 1, maxElement
, useLeftToRight
=useLeftToRight
)
214 def getNthTupleChecked(
219 useLeftToRight
=False,
222 # FIXME: maxsize is inclusive
223 t
= GNT(N
, maxSize
, maxElement
, useDivmod
, useLeftToRight
)
224 assert len(t
) <= maxSize
226 assert i
< maxElement
230 def getNthPairVariableBounds(N
, bounds
):
231 """getNthPairVariableBounds(N, bounds) -> (x, y)
233 Given a finite list of bounds (which may be finite or aleph0),
234 return the N-th pair such that 0 <= x < len(bounds) and 0 <= y <
238 raise ValueError("Invalid bounds")
239 if not (0 <= N
< sum(bounds
)):
240 raise ValueError("Invalid input (out of bounds)")
243 active
= list(range(len(bounds
)))
244 active
.sort(key
=lambda i
: bounds
[i
])
246 for i
, index
in enumerate(active
):
247 level
= bounds
[index
]
252 H
= level
- prevLevel
254 if N
< levelSize
: # Found the level
255 idelta
, delta
= getNthPairBounded(N
, W
, H
)
256 return active
[i
+ idelta
], prevLevel
+ delta
261 raise RuntimError("Unexpected loop completion")
264 def getNthPairVariableBoundsChecked(N
, bounds
, GNVP
=getNthPairVariableBounds
):
265 x
, y
= GNVP(N
, bounds
)
266 assert 0 <= x
< len(bounds
) and 0 <= y
< bounds
[x
]
276 a
= [[" " for x
in range(10)] for y
in range(10)]
277 b
= [[" " for x
in range(10)] for y
in range(10)]
278 for i
in range(min(W
* H
, 40)):
279 x
, y
= getNthPairBounded(i
, W
, H
)
280 x2
, y2
= getNthPairBounded(i
, W
, H
, useDivmod
=True)
281 print(i
, (x
, y
), (x2
, y2
))
283 b
[y2
][x2
] = "%2d" % i
287 if "".join(ln
).strip():
291 if "".join(ln
).strip():
296 bounds
= [2, 2, 4, aleph0
, 5, aleph0
]
297 a
= [[" " for x
in range(15)] for y
in range(15)]
298 b
= [[" " for x
in range(15)] for y
in range(15)]
299 for i
in range(min(sum(bounds
), 40)):
300 x
, y
= getNthPairVariableBounds(i
, bounds
)
306 if "".join(ln
).strip():
312 # Toggle to use checked versions of enumeration routines.
314 getNthPairVariableBounds
= getNthPairVariableBoundsChecked
315 getNthPairBounded
= getNthPairBoundedChecked
316 getNthNTuple
= getNthNTupleChecked
317 getNthTuple
= getNthTupleChecked
319 if __name__
== "__main__":