| 1930 | # a core building block for some CPU-intensive generator applications. |
| 1931 | |
| 1932 | def conjoin(gs): |
| 1933 | |
| 1934 | n = len(gs) |
| 1935 | values = [None] * n |
| 1936 | |
| 1937 | # Do one loop nest at time recursively, until the # of loop nests |
| 1938 | # remaining is divisible by 3. |
| 1939 | |
| 1940 | def gen(i): |
| 1941 | if i >= n: |
| 1942 | yield values |
| 1943 | |
| 1944 | elif (n-i) % 3: |
| 1945 | ip1 = i+1 |
| 1946 | for values[i] in gs[i](): |
| 1947 | for x in gen(ip1): |
| 1948 | yield x |
| 1949 | |
| 1950 | else: |
| 1951 | for x in _gen3(i): |
| 1952 | yield x |
| 1953 | |
| 1954 | # Do three loop nests at a time, recursing only if at least three more |
| 1955 | # remain. Don't call directly: this is an internal optimization for |
| 1956 | # gen's use. |
| 1957 | |
| 1958 | def _gen3(i): |
| 1959 | assert i < n and (n-i) % 3 == 0 |
| 1960 | ip1, ip2, ip3 = i+1, i+2, i+3 |
| 1961 | g, g1, g2 = gs[i : ip3] |
| 1962 | |
| 1963 | if ip3 >= n: |
| 1964 | # These are the last three, so we can yield values directly. |
| 1965 | for values[i] in g(): |
| 1966 | for values[ip1] in g1(): |
| 1967 | for values[ip2] in g2(): |
| 1968 | yield values |
| 1969 | |
| 1970 | else: |
| 1971 | # At least 6 loop nests remain; peel off 3 and recurse for the |
| 1972 | # rest. |
| 1973 | for values[i] in g(): |
| 1974 | for values[ip1] in g1(): |
| 1975 | for values[ip2] in g2(): |
| 1976 | for x in _gen3(ip3): |
| 1977 | yield x |
| 1978 | |
| 1979 | for x in gen(0): |
| 1980 | yield x |
| 1981 | |
| 1982 | # And one more approach: For backtracking apps like the Knight's Tour |
| 1983 | # solver below, the number of backtracking levels can be enormous (one |