Tiling solver - Combinatorics

Skip to content
Back to top
View this page
Edit this page

# Tiling solver

Tiling a \(n\)-dimensional polyomino with n-dimensional polyominoes.

This module defines two classes:

- `sage.combinat.tiling.Polyomino` class, to represent polyominoes in arbitrary dimension. The goal of this class is to return all the rotated, reflected and/or translated copies of a polyomino that are contained in a certain box.

- `sage.combinat.tiling.TilingSolver` class, to solve the problem of tiling a \(n\)-dimensional polyomino with a set of \(n\)-dimensional polyominoes. One can specify if rotations and reflections are allowed or not and if pieces can be reused or not. This class convert the tiling data into rows of a matrix that are passed to the DLX solver. It also allows to compute the number of solutions.

This uses dancing links code which is in Sage. Dancing links were originally introduced by Donald Knuth in 2000 [Knuth1]. Knuth used dancing links to solve tilings of a region by 2d pentaminoes. Here we extend the method to any dimension.

In particular, the `sage.games.quantumino` module is based on the Tiling Solver and allows to solve the 3d Quantumino puzzle.

AUTHOR:

- Sébastien Labbé, June 2011, initial version

- Sébastien Labbé, July 2015, count solutions up to rotations

- Sébastien Labbé, April 2017, tiling a polyomino, not only a rectangular box

EXAMPLES:

## 2d Easy Example

Here is a 2d example. Let us try to fill the \(3 \times 2\) rectangle with a \(1 \times 2\) rectangle and a \(2 \times 2\) square. Obviously, there are two solutions:

Sage

    sage: from sage.combinat.tiling import TilingSolver, Polyomino
    sage: p = Polyomino([(0,0), (0,1)])
    sage: q = Polyomino([(0,0), (0,1), (1,0), (1,1)])
    sage: T = TilingSolver([p,q], box=[3,2])
    sage: it = T.solve()
    sage: next(it)
    [Polyomino: [(0, 0), (0, 1), (1, 0), (1, 1)], Color: gray,
     Polyomino: [(2, 0), (2, 1)], Color: gray]
    sage: next(it)
    [Polyomino: [(1, 0), (1, 1), (2, 0), (2, 1)], Color: gray,
     Polyomino: [(0, 0), (0, 1)], Color: gray]
    sage: next(it)
    Traceback (most recent call last):
    ...
    StopIteration
    sage: T.number_of_solutions()
    2

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import TilingSolver, Polyomino
    >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1))])
    >>> q = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1)), (Integer(1),Integer(0)), (Integer(1),Integer(1))])
    >>> T = TilingSolver([p,q], box=[Integer(3),Integer(2)])
    >>> it = T.solve()
    >>> next(it)
    [Polyomino: [(0, 0), (0, 1), (1, 0), (1, 1)], Color: gray,
     Polyomino: [(2, 0), (2, 1)], Color: gray]
    >>> next(it)
    [Polyomino: [(1, 0), (1, 1), (2, 0), (2, 1)], Color: gray,
     Polyomino: [(0, 0), (0, 1)], Color: gray]
    >>> next(it)
    Traceback (most recent call last):
    ...
    StopIteration
    >>> T.number_of_solutions()
    2

## Scott’s pentamino problem

As mentioned in the introduction of [Knuth1], Scott’s pentamino problem consists in tiling a chessboard leaving the center four squares vacant with the 12 distinct pentaminoes.

The 12 pentaminoes:

Sage

    sage: from sage.combinat.tiling import Polyomino
    sage: I = Polyomino([(0,0),(1,0),(2,0),(3,0),(4,0)], color='brown')
    sage: N = Polyomino([(1,0),(1,1),(1,2),(0,2),(0,3)], color='yellow')
    sage: L = Polyomino([(0,0),(1,0),(0,1),(0,2),(0,3)], color='magenta')
    sage: U = Polyomino([(0,0),(1,0),(0,1),(0,2),(1,2)], color='violet')
    sage: X = Polyomino([(1,0),(0,1),(1,1),(1,2),(2,1)], color='pink')
    sage: W = Polyomino([(2,0),(2,1),(1,1),(1,2),(0,2)], color='green')
    sage: P = Polyomino([(1,0),(2,0),(0,1),(1,1),(2,1)], color='orange')
    sage: F = Polyomino([(1,0),(1,1),(0,1),(2,1),(2,2)], color='gray')
    sage: Z = Polyomino([(0,0),(1,0),(1,1),(1,2),(2,2)], color='yellow')
    sage: T = Polyomino([(0,0),(0,1),(1,1),(2,1),(0,2)], color='red')
    sage: Y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='green')
    sage: V = Polyomino([(0,0),(0,1),(0,2),(1,0),(2,0)], color='blue')

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import Polyomino
    >>> I = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(2),Integer(0)),(Integer(3),Integer(0)),(Integer(4),Integer(0))], color='brown')
    >>> N = Polyomino([(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2)),(Integer(0),Integer(2)),(Integer(0),Integer(3))], color='yellow')
    >>> L = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3))], color='magenta')
    >>> U = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(1),Integer(2))], color='violet')
    >>> X = Polyomino([(Integer(1),Integer(0)),(Integer(0),Integer(1)),(Integer(1),Integer(1)),(Integer(1),Integer(2)),(Integer(2),Integer(1))], color='pink')
    >>> W = Polyomino([(Integer(2),Integer(0)),(Integer(2),Integer(1)),(Integer(1),Integer(1)),(Integer(1),Integer(2)),(Integer(0),Integer(2))], color='green')
    >>> P = Polyomino([(Integer(1),Integer(0)),(Integer(2),Integer(0)),(Integer(0),Integer(1)),(Integer(1),Integer(1)),(Integer(2),Integer(1))], color='orange')
    >>> F = Polyomino([(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(0),Integer(1)),(Integer(2),Integer(1)),(Integer(2),Integer(2))], color='gray')
    >>> Z = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2)),(Integer(2),Integer(2))], color='yellow')
    >>> T = Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(1),Integer(1)),(Integer(2),Integer(1)),(Integer(0),Integer(2))], color='red')
    >>> Y = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(2),Integer(0)),(Integer(3),Integer(0)),(Integer(2),Integer(1))], color='green')
    >>> V = Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(1),Integer(0)),(Integer(2),Integer(0))], color='blue')

A \(8 \times 8\) chessboard leaving the center four squares vacant:

Sage

    sage: import itertools
    sage: s = set(itertools.product(range(8), repeat=2))
    sage: s.difference_update([(3,3), (3,4), (4,3), (4,4)])
    sage: chessboard = Polyomino(s)
    sage: len(chessboard)
    60

[Python]

    >>> from sage.all import *
    >>> import itertools
    >>> s = set(itertools.product(range(Integer(8)), repeat=Integer(2)))
    >>> s.difference_update([(Integer(3),Integer(3)), (Integer(3),Integer(4)), (Integer(4),Integer(3)), (Integer(4),Integer(4))])
    >>> chessboard = Polyomino(s)
    >>> len(chessboard)
    60

This problem is represented by a matrix made of 1568 rows and 72 columns. It has 65 different solutions up to isometries:

Sage

    sage: from sage.combinat.tiling import TilingSolver
    sage: T = TilingSolver([I,N,L,U,X,W,P,F,Z,T,Y,V], box=chessboard, reflection=True)
    sage: T
    Tiling solver of 12 pieces into a box of size 60
    Rotation allowed: True
    Reflection allowed: True
    Reusing pieces allowed: False
    sage: len(T.rows())                # long time
    1568
    sage: T.number_of_solutions()      # long time
    520
    sage: 520 / 8
    65

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import TilingSolver
    >>> T = TilingSolver([I,N,L,U,X,W,P,F,Z,T,Y,V], box=chessboard, reflection=True)
    >>> T
    Tiling solver of 12 pieces into a box of size 60
    Rotation allowed: True
    Reflection allowed: True
    Reusing pieces allowed: False
    >>> len(T.rows())                # long time
    1568
    >>> T.number_of_solutions()      # long time
    520
    >>> Integer(520) / Integer(8)
    65

Showing one solution:

Sage

    sage: solution = next(T.solve())                                    # long time
    sage: G = sum([piece.show2d() for piece in solution], Graphics())   # long time, needs sage.plot
    sage: G.show(aspect_ratio=1, axes=False)                            # long time, needs sage.plot

[Python]

    >>> from sage.all import *
    >>> solution = next(T.solve())                                    # long time
    >>> G = sum([piece.show2d() for piece in solution], Graphics())   # long time, needs sage.plot
    >>> G.show(aspect_ratio=Integer(1), axes=False)                            # long time, needs sage.plot

## 1d Easy Example

Here is an easy one dimensional example where we try to tile a stick of length 6 with three sticks of length 1, 2 and 3. There are six solutions:

Sage

    sage: p = Polyomino([[0]])
    sage: q = Polyomino([[0],[1]])
    sage: r = Polyomino([[0],[1],[2]])
    sage: T = TilingSolver([p,q,r], box=[6])
    sage: len(T.rows())
    15
    sage: it = T.solve()
    sage: next(it)
    [Polyomino: [(0)], Color: gray,
     Polyomino: [(1), (2)], Color: gray,
     Polyomino: [(3), (4), (5)], Color: gray]
    sage: next(it)
    [Polyomino: [(0)], Color: gray,
     Polyomino: [(1), (2), (3)], Color: gray,
     Polyomino: [(4), (5)], Color: gray]
    sage: T.number_of_solutions()
    6

Python

    >>> from sage.all import *
    >>> p = Polyomino([[Integer(0)]])
    >>> q = Polyomino([[Integer(0)],[Integer(1)]])
    >>> r = Polyomino([[Integer(0)],[Integer(1)],[Integer(2)]])
    >>> T = TilingSolver([p,q,r], box=[Integer(6)])
    >>> len(T.rows())
    15
    >>> it = T.solve()
    >>> next(it)
    [Polyomino: [(0)], Color: gray,
     Polyomino: [(1), (2)], Color: gray,
     Polyomino: [(3), (4), (5)], Color: gray]
    >>> next(it)
    [Polyomino: [(0)], Color: gray,
     Polyomino: [(1), (2), (3)], Color: gray,
     Polyomino: [(4), (5)], Color: gray]
    >>> T.number_of_solutions()
    6

## 2d Puzzle allowing reflections

The following is a puzzle owned by Florent Hivert:

Sage

    sage: from sage.combinat.tiling import Polyomino, TilingSolver
    sage: L = []
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(0,3),(1,0),(1,1),(1,2),(1,3)], 'yellow'))
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(0,3),(1,0),(1,1),(1,2)], "black"))
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(0,3),(1,0),(1,1),(1,3)], "gray"))
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(0,3),(1,0),(1,3)],"cyan"))
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(0,3),(1,0),(1,1)],"red"))
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(0,3),(1,1),(1,2)],"blue"))
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(0,3),(1,1),(1,3)],"green"))
    sage: L.append(Polyomino([(0,1),(0,2),(0,3),(1,0),(1,1),(1,3)],"magenta"))
    sage: L.append(Polyomino([(0,1),(0,2),(0,3),(1,0),(1,1),(1,2)],"orange"))
    sage: L.append(Polyomino([(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)],"pink"))

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import Polyomino, TilingSolver
    >>> L = []
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2)),(Integer(1),Integer(3))], 'yellow'))
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2))], "black"))
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(3))], "gray"))
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(0)),(Integer(1),Integer(3))],"cyan"))
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(0)),(Integer(1),Integer(1))],"red"))
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(1)),(Integer(1),Integer(2))],"blue"))
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(1)),(Integer(1),Integer(3))],"green"))
    >>> L.append(Polyomino([(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(3))],"magenta"))
    >>> L.append(Polyomino([(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(3)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2))],"orange"))
    >>> L.append(Polyomino([(Integer(0),Integer(0)),(Integer(0),Integer(1)),(Integer(0),Integer(2)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2))],"pink"))

By default, rotations are allowed and reflections are not. In this case, there are no solutions for tiling a \(8 \times 8\) rectangular box:

Sage

    sage: T = TilingSolver(L, box=(8,8))
    sage: T.number_of_solutions()                                       # long time (2.5s)
    0

[Python]

    >>> from sage.all import *
    >>> T = TilingSolver(L, box=(Integer(8),Integer(8)))
    >>> T.number_of_solutions()                                       # long time (2.5s)
    0

If reflections are allowed, there are solutions. Solve the puzzle and show one solution:

Sage

    sage: T = TilingSolver(L, box=(8,8), reflection=True)
    sage: solution = next(T.solve())                                    # long time (7s)
    sage: G = sum([piece.show2d() for piece in solution], Graphics())   # long time (<1s), needs sage.plot
    sage: G.show(aspect_ratio=1, axes=False)                            # long time (2s), needs sage.plot

Python

    >>> from sage.all import *
    >>> T = TilingSolver(L, box=(Integer(8),Integer(8)), reflection=True)
    >>> solution = next(T.solve())                                    # long time (7s)
    >>> G = sum([piece.show2d() for piece in solution], Graphics())   # long time (<1s), needs sage.plot
    >>> G.show(aspect_ratio=Integer(1), axes=False)                            # long time (2s), needs sage.plot

Compute the number of solutions:

Sage

    sage: T.number_of_solutions()                                       # long time (2.6s)
    328

[Python]

    >>> from sage.all import *
    >>> T.number_of_solutions()                                       # long time (2.6s)
    328

Create a animation of all the solutions:

Sage

    sage: a = T.animate()                            # not tested
    sage: a                                          # not tested
    Animation with 328 frames

Python

    >>> from sage.all import *
    >>> a = T.animate()                            # not tested
    >>> a                                          # not tested
    Animation with 328 frames

## 3d Puzzle

The same thing done in 3d without allowing reflections this time:

Sage

    sage: from sage.combinat.tiling import Polyomino, TilingSolver
    sage: L = []
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,2,0),(1,3,0)]))
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,2,0)]))
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,3,0)]))
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,3,0)]))
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0)]))
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,1,0),(1,2,0)]))
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,1,0),(1,3,0)]))
    sage: L.append(Polyomino([(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,3,0)]))
    sage: L.append(Polyomino([(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,2,0)]))
    sage: L.append(Polyomino([(0,0,0),(0,1,0),(0,2,0),(1,0,0),(1,1,0),(1,2,0)]))

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import Polyomino, TilingSolver
    >>> L = []
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(2),Integer(0)),(Integer(1),Integer(3),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(2),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(3),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(3),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(2),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(3),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(3),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(0),Integer(3),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(2),Integer(0))]))
    >>> L.append(Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(1),Integer(0)),(Integer(0),Integer(2),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(2),Integer(0))]))

Solve the puzzle and show one solution:

Sage

    sage: T = TilingSolver(L, box=(8,8,1))
    sage: solution = next(T.solve())                                    # long time (8s)
    sage: G = sum([p.show3d(size=0.85) for p in solution], Graphics())  # long time (<1s), needs sage.plot
    sage: G.show(aspect_ratio=1, viewer='tachyon')                      # long time (2s), needs sage.plot

[Python]

    >>> from sage.all import *
    >>> T = TilingSolver(L, box=(Integer(8),Integer(8),Integer(1)))
    >>> solution = next(T.solve())                                    # long time (8s)
    >>> G = sum([p.show3d(size=RealNumber('0.85')) for p in solution], Graphics())  # long time (<1s), needs sage.plot
    >>> G.show(aspect_ratio=Integer(1), viewer='tachyon')                      # long time (2s), needs sage.plot

Let us compute the number of solutions:

Sage

    sage: T.number_of_solutions()                                       # long time (3s)
    328

Python

    >>> from sage.all import *
    >>> T.number_of_solutions()                                       # long time (3s)
    328

## Donald Knuth example : the Y pentamino

Donald Knuth [Knuth1] considered the problem of packing 45 Y pentaminoes into a \(15 \times 15\) square:

Sage

    sage: from sage.combinat.tiling import Polyomino, TilingSolver
    sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)])
    sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True)
    sage: T.number_of_solutions()
    10
    sage: solution = next(T.solve())
    sage: G = sum([p.show2d() for p in solution], Graphics())                           # needs sage.plot
    sage: G.show(aspect_ratio=1)                                        # long time (2s), needs sage.plot

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import Polyomino, TilingSolver
    >>> y = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(2),Integer(0)),(Integer(3),Integer(0)),(Integer(2),Integer(1))])
    >>> T = TilingSolver([y], box=(Integer(5),Integer(10)), reusable=True, reflection=True)
    >>> T.number_of_solutions()
    10
    >>> solution = next(T.solve())
    >>> G = sum([p.show2d() for p in solution], Graphics())                           # needs sage.plot
    >>> G.show(aspect_ratio=Integer(1))                                        # long time (2s), needs sage.plot

Sage

    sage: T = TilingSolver([y], box=(15,15), reusable=True, reflection=True)
    sage: T.number_of_solutions()                      # not tested
    1696

[Python]

    >>> from sage.all import *
    >>> T = TilingSolver([y], box=(Integer(15),Integer(15)), reusable=True, reflection=True)
    >>> T.number_of_solutions()                      # not tested
    1696

Up to the symmetries of the square, there are 212 distinct solutions:

Sage

    sage: 1696 // 8
    212

Python

    >>> from sage.all import *
    >>> Integer(1696) // Integer(8)
    212

## Animation of Donald Knuth’s dancing links

Animation of the solutions:

Sage

    sage: from sage.combinat.tiling import Polyomino, TilingSolver
    sage: Y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='yellow')
    sage: T = TilingSolver([Y], box=(15,15), reusable=True, reflection=True)
    sage: a = T.animate(stop=40); a                     # long time, optional - imagemagick, needs sage.plot
    Animation with 40 frames

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import Polyomino, TilingSolver
    >>> Y = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(2),Integer(0)),(Integer(3),Integer(0)),(Integer(2),Integer(1))], color='yellow')
    >>> T = TilingSolver([Y], box=(Integer(15),Integer(15)), reusable=True, reflection=True)
    >>> a = T.animate(stop=Integer(40)); a                     # long time, optional - imagemagick, needs sage.plot
    Animation with 40 frames

Incremental animation of the solutions (one piece is removed/added at a time):

Sage

    sage: a = T.animate('incremental', stop=40); a      # long time, optional - imagemagick, needs sage.plot
    Animation with 40 frames
    sage: a.show(delay=50, iterations=1)                # long time, optional - imagemagick, needs sage.plot

[Python]

    >>> from sage.all import *
    >>> a = T.animate('incremental', stop=Integer(40)); a      # long time, optional - imagemagick, needs sage.plot
    Animation with 40 frames
    >>> a.show(delay=Integer(50), iterations=Integer(1))                # long time, optional - imagemagick, needs sage.plot

## 5d Easy Example

Here is a 5d example. Let us try to fill the \(2 \times 2 \times 2 \times 2 \times 2\) rectangle with reusable \(1 \times 1 \times 1 \times 1 \times 1\) rectangles. Obviously, there is one solution:

Sage

    sage: from sage.combinat.tiling import Polyomino, TilingSolver
    sage: p = Polyomino([(0,0,0,0,0)])
    sage: T = TilingSolver([p], box=(2,2,2,2,2), reusable=True)
    sage: rows = T.rows()                               # long time (3s)
    sage: rows                                          # long time (fast)
    [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12],
     [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24],
     [25], [26], [27], [28], [29], [30], [31]]
    sage: T.number_of_solutions()                       # long time (fast)
    1

Python

    >>> from sage.all import *
    >>> from sage.combinat.tiling import Polyomino, TilingSolver
    >>> p = Polyomino([(Integer(0),Integer(0),Integer(0),Integer(0),Integer(0))])
    >>> T = TilingSolver([p], box=(Integer(2),Integer(2),Integer(2),Integer(2),Integer(2)), reusable=True)
    >>> rows = T.rows()                               # long time (3s)
    >>> rows                                          # long time (fast)
    [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12],
     [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24],
     [25], [26], [27], [28], [29], [30], [31]]
    >>> T.number_of_solutions()                       # long time (fast)
    1

class sage.combinat.tiling.Polyomino(coords, color='gray', dimension=None)

  Bases: `SageObject`

  A polyomino in \(\ZZ^d\).

  The polyomino is the union of the unit square (or cube, or n-cube) centered at those coordinates. Such an object should be connected, but the code does not make this assumption.

  INPUT:

  - `coords` – iterable of integer coordinates in \(\ZZ^d\)

  - `color` – string (default: `'gray'`); color for display

  - `dimension` – integer (default: `None`); dimension of the space, if `None`, it is guessed from the `coords` if `coords` is non empty

  EXAMPLES:

  Sage

      sage: from sage.combinat.tiling import Polyomino
      sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue')
      Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue

  Python

      >>> from sage.all import *
      >>> from sage.combinat.tiling import Polyomino
      >>> Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(1))], color='blue')
      Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue

  boundary()

    Return the boundary of a 2d polyomino.

    INPUT:

    - `self` – a 2d polyomino

    OUTPUT:

    - list of edges (an edge is a pair of adjacent 2d coordinates)

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0), (1,0), (0,1), (1,1)])
        sage: sorted(p.boundary())
        [((-0.5, -0.5), (-0.5, 0.5)), ((-0.5, -0.5), (0.5, -0.5)),
         ((-0.5, 0.5), (-0.5, 1.5)), ((-0.5, 1.5), (0.5, 1.5)),
         ((0.5, -0.5), (1.5, -0.5)), ((0.5, 1.5), (1.5, 1.5)),
         ((1.5, -0.5), (1.5, 0.5)), ((1.5, 0.5), (1.5, 1.5))]
        sage: len(_)
        8
        sage: p = Polyomino([(5,5)])
        sage: sorted(p.boundary())
        [((4.5, 4.5), (4.5, 5.5)), ((4.5, 4.5), (5.5, 4.5)),
         ((4.5, 5.5), (5.5, 5.5)), ((5.5, 4.5), (5.5, 5.5))]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(0),Integer(1)), (Integer(1),Integer(1))])
        >>> sorted(p.boundary())
        [((-0.5, -0.5), (-0.5, 0.5)), ((-0.5, -0.5), (0.5, -0.5)),
         ((-0.5, 0.5), (-0.5, 1.5)), ((-0.5, 1.5), (0.5, 1.5)),
         ((0.5, -0.5), (1.5, -0.5)), ((0.5, 1.5), (1.5, 1.5)),
         ((1.5, -0.5), (1.5, 0.5)), ((1.5, 0.5), (1.5, 1.5))]
        >>> len(_)
        8
        >>> p = Polyomino([(Integer(5),Integer(5))])
        >>> sorted(p.boundary())
        [((4.5, 4.5), (4.5, 5.5)), ((4.5, 4.5), (5.5, 4.5)),
         ((4.5, 5.5), (5.5, 5.5)), ((5.5, 4.5), (5.5, 5.5))]

  bounding_box()

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink')
        sage: p.bounding_box()
        [[0, 0, 0], [1, 2, 1]]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(1),Integer(1)),(Integer(1),Integer(2),Integer(0))], color='deeppink')
        >>> p.bounding_box()
        [[0, 0, 0], [1, 2, 1]]

  canonical()

    Return the translated copy of `self` having minimal and nonnegative coordinates

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink')
        sage: p
        Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink
        sage: p.canonical()
        Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(1),Integer(1)),(Integer(1),Integer(2),Integer(0))], color='deeppink')
        >>> p
        Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink
        >>> p.canonical()
        Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink

  canonical_isometric_copies(orientation_preserving=True, mod_box_isometries=False)

    Return the list of image of `self` under isometries of the \(n\)-cube where the coordinates are all nonnegative and minimal.

    INPUT:

    - `orientation_preserving` – boolean (default: `True`); if `True`, the group of isometries of the \(n\)-cube is restricted to those that preserve the orientation, i.e. of determinant 1.

    - `mod_box_isometries` – boolean (default: `False`); whether to quotient the group of isometries of the \(n\)-cube by the subgroup of isometries of the \(a_1\times a_2\cdots \times a_n\) rectangular box where are the \(a_i\) are assumed to be distinct.

    OUTPUT: set of Polyomino

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue')
        sage: s = p.canonical_isometric_copies()
        sage: len(s)
        12

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(1))], color='blue')
        >>> s = p.canonical_isometric_copies()
        >>> len(s)
        12

    With the non orientation-preserving:

    Sage

        sage: s = p.canonical_isometric_copies(orientation_preserving=False)
        sage: len(s)
        24

    [Python]

        >>> from sage.all import *
        >>> s = p.canonical_isometric_copies(orientation_preserving=False)
        >>> len(s)
        24

    Modulo rotation by angle 180 degrees:

    Sage

        sage: s = p.canonical_isometric_copies(mod_box_isometries=True)
        sage: len(s)
        3

    Python

        >>> from sage.all import *
        >>> s = p.canonical_isometric_copies(mod_box_isometries=True)
        >>> len(s)
        3

  center()

    Return the center of the polyomino.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0),(0,0,1)])
        sage: p.center()
        (0, 0, 1/2)

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(0),Integer(1))])
        >>> p.center()
        (0, 0, 1/2)

    In 3d:

    Sage

        sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink')
        sage: p.center()
        (4/5, 4/5, 1/5)

    [Python]

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(1),Integer(1)),(Integer(1),Integer(2),Integer(0))], color='deeppink')
        >>> p.center()
        (4/5, 4/5, 1/5)

    In 2d:

    Sage

        sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)])
        sage: p.center()
        (3/4, 3/4)

    Python

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2))])
        >>> p.center()
        (3/4, 3/4)

  color(color=None)

    Return or change the color of the polyomino.

    INPUT:

    - `color` – string, RBG tuple or `None` (default: `None`), if `None`, it returns the current color

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue')
        sage: p.color()
        'blue'

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(1))], color='blue')
        >>> p.color()
        'blue'

  frozenset()

    Return the elements of \(\ZZ^d\) in the polyomino as a frozenset.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red')
        sage: p.frozenset()
        frozenset({(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)})

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(1))], color='red')
        >>> p.frozenset()
        frozenset({(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)})

  intersection(other)

    Return the intersection of `self` and `other`.

    INPUT:

    - `other` – a polyomino

    OUTPUT: polyomino

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: a = Polyomino([(0,0)])
        sage: b = Polyomino([(0,0), (0,1), (1,1), (2,1)])
        sage: a.intersection(b)
        Polyomino: [(0, 0)], Color: gray
        sage: a.intersection(b+(1,1))
        Polyomino: [], Color: gray

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> a = Polyomino([(Integer(0),Integer(0))])
        >>> b = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1)), (Integer(1),Integer(1)), (Integer(2),Integer(1))])
        >>> a.intersection(b)
        Polyomino: [(0, 0)], Color: gray
        >>> a.intersection(b+(Integer(1),Integer(1)))
        Polyomino: [], Color: gray

  isometric_copies(box, orientation_preserving=True, mod_box_isometries=False)

    Return the translated and isometric images of `self` that lies in the box.

    INPUT:

    - `box` – polyomino or tuple of integers (size of a box)

    - `orientation_preserving` – boolean (default: `True`); if `True`, the group of isometries of the \(n\)-cube is restricted to those that preserve the orientation, i.e. of determinant 1.

    - `mod_box_isometries` – boolean (default: `False`); whether to quotient the group of isometries of the \(n\)-cube by the subgroup of isometries of the \(a_1\times a_2\cdots \times a_n\) rectangular box where are the \(a_i\) are assumed to be distinct.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink')
        sage: L = list(p.isometric_copies(box=(5,8,2)))
        sage: len(L)
        360

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(1),Integer(1)),(Integer(1),Integer(2),Integer(0))], color='deeppink')
        >>> L = list(p.isometric_copies(box=(Integer(5),Integer(8),Integer(2))))
        >>> len(L)
        360

    Sage

        sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,2,0),(1,2,1)], color='orange')
        sage: L = list(p.isometric_copies(box=(5,8,2)))
        sage: len(L)
        180
        sage: L = list(p.isometric_copies((5,8,2), False))
        sage: len(L)
        360
        sage: L = list(p.isometric_copies((5,8,2), mod_box_isometries=True))
        sage: len(L)
        45

    [Python]

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(2),Integer(0)),(Integer(1),Integer(2),Integer(1))], color='orange')
        >>> L = list(p.isometric_copies(box=(Integer(5),Integer(8),Integer(2))))
        >>> len(L)
        180
        >>> L = list(p.isometric_copies((Integer(5),Integer(8),Integer(2)), False))
        >>> len(L)
        360
        >>> L = list(p.isometric_copies((Integer(5),Integer(8),Integer(2)), mod_box_isometries=True))
        >>> len(L)
        45

    Sage

        sage: p = Polyomino([(0,0), (1,0), (0,1)])
        sage: b = Polyomino([(0,0), (1,0), (2,0), (0,1), (1,1), (0,2)])
        sage: sorted(p.isometric_copies(b), key=lambda p: p.sorted_list())
        [Polyomino: [(0, 0), (0, 1), (1, 0)], Color: gray,
         Polyomino: [(0, 0), (0, 1), (1, 1)], Color: gray,
         Polyomino: [(0, 0), (1, 0), (1, 1)], Color: gray,
         Polyomino: [(0, 1), (0, 2), (1, 1)], Color: gray,
         Polyomino: [(0, 1), (1, 0), (1, 1)], Color: gray,
         Polyomino: [(1, 0), (1, 1), (2, 0)], Color: gray]

    Python

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(0),Integer(1))])
        >>> b = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(2),Integer(0)), (Integer(0),Integer(1)), (Integer(1),Integer(1)), (Integer(0),Integer(2))])
        >>> sorted(p.isometric_copies(b), key=lambda p: p.sorted_list())
        [Polyomino: [(0, 0), (0, 1), (1, 0)], Color: gray,
         Polyomino: [(0, 0), (0, 1), (1, 1)], Color: gray,
         Polyomino: [(0, 0), (1, 0), (1, 1)], Color: gray,
         Polyomino: [(0, 1), (0, 2), (1, 1)], Color: gray,
         Polyomino: [(0, 1), (1, 0), (1, 1)], Color: gray,
         Polyomino: [(1, 0), (1, 1), (2, 0)], Color: gray]

  isometric_copies_intersection(box, orientation_preserving=True)

    Return the set of non empty intersections of isometric images of `self` with a polyomino.

    INPUT:

    - `box` – polyomino or tuple of integers (size of a box)

    - `orientation_preserving` – boolean (default: `True`); if `True`, the group of isometries of the \(n\)-cube is restricted to those that preserve the orientation, i.e. of determinant 1.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0),(1,0)], color='deeppink')
        sage: sorted(sorted(a.frozenset()) for a in p.isometric_copies_intersection(box=(2,3)))
        [[(0, 0)],
         [(0, 0), (0, 1)],
         [(0, 0), (1, 0)],
         [(0, 1)],
         [(0, 1), (0, 2)],
         [(0, 1), (1, 1)],
         [(0, 2)],
         [(0, 2), (1, 2)],
         [(1, 0)],
         [(1, 0), (1, 1)],
         [(1, 1)],
         [(1, 1), (1, 2)],
         [(1, 2)]]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0))], color='deeppink')
        >>> sorted(sorted(a.frozenset()) for a in p.isometric_copies_intersection(box=(Integer(2),Integer(3))))
        [[(0, 0)],
         [(0, 0), (0, 1)],
         [(0, 0), (1, 0)],
         [(0, 1)],
         [(0, 1), (0, 2)],
         [(0, 1), (1, 1)],
         [(0, 2)],
         [(0, 2), (1, 2)],
         [(1, 0)],
         [(1, 0), (1, 1)],
         [(1, 1)],
         [(1, 1), (1, 2)],
         [(1, 2)]]

  neighbor_edges()

    Return an iterator over the pairs of neighbor coordinates inside of the polyomino.

    Two points \(P\) and \(Q\) in the polyomino are neighbor if \(P - Q\) has one coordinate equal to \(+1\) or \(-1\) and zero everywhere else.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0),(0,0,1)])
        sage: [sorted(edge) for edge in p.neighbor_edges()]
        [[(0, 0, 0), (0, 0, 1)]]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(0),Integer(0),Integer(1))])
        >>> [sorted(edge) for edge in p.neighbor_edges()]
        [[(0, 0, 0), (0, 0, 1)]]

    In 3d:

    Sage

        sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink')
        sage: L = sorted(sorted(edge) for edge in p.neighbor_edges())
        sage: for a in L: a
        [(0, 0, 0), (1, 0, 0)]
        [(1, 0, 0), (1, 1, 0)]
        [(1, 1, 0), (1, 1, 1)]
        [(1, 1, 0), (1, 2, 0)]

    [Python]

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(1),Integer(1)),(Integer(1),Integer(2),Integer(0))], color='deeppink')
        >>> L = sorted(sorted(edge) for edge in p.neighbor_edges())
        >>> for a in L: a
        [(0, 0, 0), (1, 0, 0)]
        [(1, 0, 0), (1, 1, 0)]
        [(1, 1, 0), (1, 1, 1)]
        [(1, 1, 0), (1, 2, 0)]

    In 2d:

    Sage

        sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)])
        sage: L = sorted(sorted(edge) for edge in p.neighbor_edges())
        sage: for a in L: a
        [(0, 0), (1, 0)]
        [(1, 0), (1, 1)]
        [(1, 1), (1, 2)]

    Python

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2))])
        >>> L = sorted(sorted(edge) for edge in p.neighbor_edges())
        >>> for a in L: a
        [(0, 0), (1, 0)]
        [(1, 0), (1, 1)]
        [(1, 1), (1, 2)]

  self_surrounding(radius, remove_incomplete_copies=True, ncpus=None)

    Return a list of isometric copies of `self` surrounding it with an annulus of given radius.

    INPUT:

    - `self` – a polyomino of dimension 2

    - `radius` – integer

    - `remove_incomplete_copies` – boolean (default: `True`); whether to keep only complete copies of `self` in the output

    - `ncpus` – integer (default: `None`); maximal number of subprocesses to use at the same time. If `None`, it detects the number of effective CPUs in the system using `sage.parallel.ncpus.ncpus()`. If `ncpus=1`, the first solution is searched serially.

    OUTPUT: list of polyominoes

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: H = Polyomino([(-1, 1), (-1, 4), (-1, 7), (0, 0), (0, 1), (0, 2),
        ....: (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (1, 1), (1, 2),
        ....: (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (2, 0), (2, 2),
        ....: (2, 3), (2, 5), (2, 6), (2, 8)])
        sage: solution = H.self_surrounding(8)
        sage: G = sum([p.show2d() for p in solution], Graphics())                   # needs sage.plot

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> H = Polyomino([(-Integer(1), Integer(1)), (-Integer(1), Integer(4)), (-Integer(1), Integer(7)), (Integer(0), Integer(0)), (Integer(0), Integer(1)), (Integer(0), Integer(2)),
        ... (Integer(0), Integer(3)), (Integer(0), Integer(4)), (Integer(0), Integer(5)), (Integer(0), Integer(6)), (Integer(0), Integer(7)), (Integer(0), Integer(8)), (Integer(1), Integer(1)), (Integer(1), Integer(2)),
        ... (Integer(1), Integer(3)), (Integer(1), Integer(4)), (Integer(1), Integer(5)), (Integer(1), Integer(6)), (Integer(1), Integer(7)), (Integer(1), Integer(8)), (Integer(2), Integer(0)), (Integer(2), Integer(2)),
        ... (Integer(2), Integer(3)), (Integer(2), Integer(5)), (Integer(2), Integer(6)), (Integer(2), Integer(8))])
        >>> solution = H.self_surrounding(Integer(8))
        >>> G = sum([p.show2d() for p in solution], Graphics())                   # needs sage.plot

    Sage

        sage: # long time
        sage: solution = H.self_surrounding(8, remove_incomplete_copies=False)
        sage: G = sum([p.show2d() for p in solution], Graphics())                   # needs sage.plot

    [Python]

        >>> from sage.all import *
        >>> # long time
        >>> solution = H.self_surrounding(Integer(8), remove_incomplete_copies=False)
        >>> G = sum([p.show2d() for p in solution], Graphics())                   # needs sage.plot

  show2d(size=0.7, color='black', thickness=1)

    Return a 2d Graphic object representing the polyomino.

    INPUT:

    - `self` – a polyomino of dimension 2

    - `size` – number (default: `0.7`); the size of each square

    - `color` – color (default: `'black'`); color of the boundary line

    - `thickness` – number (default: `1`); how thick the boundary line is

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)], color='deeppink')
        sage: p.show2d()                    # long time (0.5s)                      # needs sage.plot
        Graphics object consisting of 17 graphics primitives

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(1),Integer(1)),(Integer(1),Integer(2))], color='deeppink')
        >>> p.show2d()                    # long time (0.5s)                      # needs sage.plot
        Graphics object consisting of 17 graphics primitives

  show3d(size=1)

    Return a 3d Graphic object representing the polyomino.

    INPUT:

    - `self` – a polyomino of dimension 3

    - `size` – number (default: `1`); the size of each `1 \times 1 \times 1` cube. This does a homothety with respect to the center of the polyomino.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue')
        sage: p.show3d()                    # long time (2s)                        # needs sage.plot
        Graphics3d Object

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(1))], color='blue')
        >>> p.show3d()                    # long time (2s)                        # needs sage.plot
        Graphics3d Object

  sorted_list()

    Return the color of the polyomino.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue')
        sage: p.sorted_list()
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(0)), (Integer(1),Integer(1),Integer(1))], color='blue')
        >>> p.sorted_list()
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)]

  translated_copies(box)

    Return an iterator over the translated images of `self` inside a polyomino.

    INPUT:

    - `box` – polyomino or tuple of integers (size of a box)

    OUTPUT: iterator of 3d polyominoes

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink')
        sage: for t in p.translated_copies(box=(5,8,2)): t
        Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink
        Polyomino: [(0, 1, 0), (1, 1, 0), (1, 2, 0), (1, 2, 1), (1, 3, 0)], Color: deeppink
        Polyomino: [(0, 2, 0), (1, 2, 0), (1, 3, 0), (1, 3, 1), (1, 4, 0)], Color: deeppink
        Polyomino: [(0, 3, 0), (1, 3, 0), (1, 4, 0), (1, 4, 1), (1, 5, 0)], Color: deeppink
        Polyomino: [(0, 4, 0), (1, 4, 0), (1, 5, 0), (1, 5, 1), (1, 6, 0)], Color: deeppink
        Polyomino: [(0, 5, 0), (1, 5, 0), (1, 6, 0), (1, 6, 1), (1, 7, 0)], Color: deeppink
        Polyomino: [(1, 0, 0), (2, 0, 0), (2, 1, 0), (2, 1, 1), (2, 2, 0)], Color: deeppink
        Polyomino: [(1, 1, 0), (2, 1, 0), (2, 2, 0), (2, 2, 1), (2, 3, 0)], Color: deeppink
        Polyomino: [(1, 2, 0), (2, 2, 0), (2, 3, 0), (2, 3, 1), (2, 4, 0)], Color: deeppink
        Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (2, 5, 0)], Color: deeppink
        Polyomino: [(1, 4, 0), (2, 4, 0), (2, 5, 0), (2, 5, 1), (2, 6, 0)], Color: deeppink
        Polyomino: [(1, 5, 0), (2, 5, 0), (2, 6, 0), (2, 6, 1), (2, 7, 0)], Color: deeppink
        Polyomino: [(2, 0, 0), (3, 0, 0), (3, 1, 0), (3, 1, 1), (3, 2, 0)], Color: deeppink
        Polyomino: [(2, 1, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (3, 3, 0)], Color: deeppink
        Polyomino: [(2, 2, 0), (3, 2, 0), (3, 3, 0), (3, 3, 1), (3, 4, 0)], Color: deeppink
        Polyomino: [(2, 3, 0), (3, 3, 0), (3, 4, 0), (3, 4, 1), (3, 5, 0)], Color: deeppink
        Polyomino: [(2, 4, 0), (3, 4, 0), (3, 5, 0), (3, 5, 1), (3, 6, 0)], Color: deeppink
        Polyomino: [(2, 5, 0), (3, 5, 0), (3, 6, 0), (3, 6, 1), (3, 7, 0)], Color: deeppink
        Polyomino: [(3, 0, 0), (4, 0, 0), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: deeppink
        Polyomino: [(3, 1, 0), (4, 1, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0)], Color: deeppink
        Polyomino: [(3, 2, 0), (4, 2, 0), (4, 3, 0), (4, 3, 1), (4, 4, 0)], Color: deeppink
        Polyomino: [(3, 3, 0), (4, 3, 0), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: deeppink
        Polyomino: [(3, 4, 0), (4, 4, 0), (4, 5, 0), (4, 5, 1), (4, 6, 0)], Color: deeppink
        Polyomino: [(3, 5, 0), (4, 5, 0), (4, 6, 0), (4, 6, 1), (4, 7, 0)], Color: deeppink

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0)),(Integer(1),Integer(0),Integer(0)),(Integer(1),Integer(1),Integer(0)),(Integer(1),Integer(1),Integer(1)),(Integer(1),Integer(2),Integer(0))], color='deeppink')
        >>> for t in p.translated_copies(box=(Integer(5),Integer(8),Integer(2))): t
        Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink
        Polyomino: [(0, 1, 0), (1, 1, 0), (1, 2, 0), (1, 2, 1), (1, 3, 0)], Color: deeppink
        Polyomino: [(0, 2, 0), (1, 2, 0), (1, 3, 0), (1, 3, 1), (1, 4, 0)], Color: deeppink
        Polyomino: [(0, 3, 0), (1, 3, 0), (1, 4, 0), (1, 4, 1), (1, 5, 0)], Color: deeppink
        Polyomino: [(0, 4, 0), (1, 4, 0), (1, 5, 0), (1, 5, 1), (1, 6, 0)], Color: deeppink
        Polyomino: [(0, 5, 0), (1, 5, 0), (1, 6, 0), (1, 6, 1), (1, 7, 0)], Color: deeppink
        Polyomino: [(1, 0, 0), (2, 0, 0), (2, 1, 0), (2, 1, 1), (2, 2, 0)], Color: deeppink
        Polyomino: [(1, 1, 0), (2, 1, 0), (2, 2, 0), (2, 2, 1), (2, 3, 0)], Color: deeppink
        Polyomino: [(1, 2, 0), (2, 2, 0), (2, 3, 0), (2, 3, 1), (2, 4, 0)], Color: deeppink
        Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (2, 5, 0)], Color: deeppink
        Polyomino: [(1, 4, 0), (2, 4, 0), (2, 5, 0), (2, 5, 1), (2, 6, 0)], Color: deeppink
        Polyomino: [(1, 5, 0), (2, 5, 0), (2, 6, 0), (2, 6, 1), (2, 7, 0)], Color: deeppink
        Polyomino: [(2, 0, 0), (3, 0, 0), (3, 1, 0), (3, 1, 1), (3, 2, 0)], Color: deeppink
        Polyomino: [(2, 1, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (3, 3, 0)], Color: deeppink
        Polyomino: [(2, 2, 0), (3, 2, 0), (3, 3, 0), (3, 3, 1), (3, 4, 0)], Color: deeppink
        Polyomino: [(2, 3, 0), (3, 3, 0), (3, 4, 0), (3, 4, 1), (3, 5, 0)], Color: deeppink
        Polyomino: [(2, 4, 0), (3, 4, 0), (3, 5, 0), (3, 5, 1), (3, 6, 0)], Color: deeppink
        Polyomino: [(2, 5, 0), (3, 5, 0), (3, 6, 0), (3, 6, 1), (3, 7, 0)], Color: deeppink
        Polyomino: [(3, 0, 0), (4, 0, 0), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: deeppink
        Polyomino: [(3, 1, 0), (4, 1, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0)], Color: deeppink
        Polyomino: [(3, 2, 0), (4, 2, 0), (4, 3, 0), (4, 3, 1), (4, 4, 0)], Color: deeppink
        Polyomino: [(3, 3, 0), (4, 3, 0), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: deeppink
        Polyomino: [(3, 4, 0), (4, 4, 0), (4, 5, 0), (4, 5, 1), (4, 6, 0)], Color: deeppink
        Polyomino: [(3, 5, 0), (4, 5, 0), (4, 6, 0), (4, 6, 1), (4, 7, 0)], Color: deeppink

    This method is independent of the translation of the polyomino:

    Sage

        sage: q = Polyomino([(0,0,0), (1,0,0)])
        sage: list(q.translated_copies((2,2,1)))
        [Polyomino: [(0, 0, 0), (1, 0, 0)], Color: gray,
         Polyomino: [(0, 1, 0), (1, 1, 0)], Color: gray]
        sage: q = Polyomino([(34,7,-9), (35,7,-9)])
        sage: list(q.translated_copies((2,2,1)))
        [Polyomino: [(0, 0, 0), (1, 0, 0)], Color: gray,
         Polyomino: [(0, 1, 0), (1, 1, 0)], Color: gray]

    [Python]

        >>> from sage.all import *
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(1),Integer(0),Integer(0))])
        >>> list(q.translated_copies((Integer(2),Integer(2),Integer(1))))
        [Polyomino: [(0, 0, 0), (1, 0, 0)], Color: gray,
         Polyomino: [(0, 1, 0), (1, 1, 0)], Color: gray]
        >>> q = Polyomino([(Integer(34),Integer(7),-Integer(9)), (Integer(35),Integer(7),-Integer(9))])
        >>> list(q.translated_copies((Integer(2),Integer(2),Integer(1))))
        [Polyomino: [(0, 0, 0), (1, 0, 0)], Color: gray,
         Polyomino: [(0, 1, 0), (1, 1, 0)], Color: gray]

    Inside smaller boxes:

    Sage

        sage: list(p.translated_copies(box=(2,2,3)))
        []
        sage: list(p.translated_copies(box=(2,3,2)))
        [Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink]
        sage: list(p.translated_copies(box=(3,2,2)))
        []
        sage: list(p.translated_copies(box=(1,1,1)))
        []

    Python

        >>> from sage.all import *
        >>> list(p.translated_copies(box=(Integer(2),Integer(2),Integer(3))))
        []
        >>> list(p.translated_copies(box=(Integer(2),Integer(3),Integer(2))))
        [Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink]
        >>> list(p.translated_copies(box=(Integer(3),Integer(2),Integer(2))))
        []
        >>> list(p.translated_copies(box=(Integer(1),Integer(1),Integer(1))))
        []

    Using a Polyomino as input:

    Sage

        sage: b = Polyomino([(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)])
        sage: p = Polyomino([(0,0)])
        sage: list(p.translated_copies(b))
        [Polyomino: [(0, 0)], Color: gray,
         Polyomino: [(0, 1)], Color: gray,
         Polyomino: [(0, 2)], Color: gray,
         Polyomino: [(1, 0)], Color: gray,
         Polyomino: [(1, 1)], Color: gray,
         Polyomino: [(1, 2)], Color: gray]

    [Python]

        >>> from sage.all import *
        >>> b = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1)), (Integer(0),Integer(2)), (Integer(1),Integer(0)), (Integer(1),Integer(1)), (Integer(1),Integer(2))])
        >>> p = Polyomino([(Integer(0),Integer(0))])
        >>> list(p.translated_copies(b))
        [Polyomino: [(0, 0)], Color: gray,
         Polyomino: [(0, 1)], Color: gray,
         Polyomino: [(0, 2)], Color: gray,
         Polyomino: [(1, 0)], Color: gray,
         Polyomino: [(1, 1)], Color: gray,
         Polyomino: [(1, 2)], Color: gray]

    Sage

        sage: p = Polyomino([(0,0), (1,0), (0,1)])
        sage: b = Polyomino([(0,0), (1,0), (2,0), (0,1), (1,1), (0,2)])
        sage: list(p.translated_copies(b))
        [Polyomino: [(0, 0), (0, 1), (1, 0)], Color: gray,
         Polyomino: [(0, 1), (0, 2), (1, 1)], Color: gray,
         Polyomino: [(1, 0), (1, 1), (2, 0)], Color: gray]

    Python

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(0),Integer(1))])
        >>> b = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(2),Integer(0)), (Integer(0),Integer(1)), (Integer(1),Integer(1)), (Integer(0),Integer(2))])
        >>> list(p.translated_copies(b))
        [Polyomino: [(0, 0), (0, 1), (1, 0)], Color: gray,
         Polyomino: [(0, 1), (0, 2), (1, 1)], Color: gray,
         Polyomino: [(1, 0), (1, 1), (2, 0)], Color: gray]

  translated_copies_intersection(box)

    Return the set of non empty intersections of translated images of `self` with a polyomino.

    INPUT:

    - `box` – polyomino or tuple of integers (size of a box)

    OUTPUT: set of 3d polyominoes

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino
        sage: p = Polyomino([(0,0),(1,0)], color='deeppink')
        sage: sorted(sorted(a.frozenset())
        ....:        for a in p.translated_copies_intersection(box=(2,3)))
        [[(0, 0)],
         [(0, 0), (1, 0)],
         [(0, 1)],
         [(0, 1), (1, 1)],
         [(0, 2)],
         [(0, 2), (1, 2)],
         [(1, 0)],
         [(1, 1)],
         [(1, 2)]]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0))], color='deeppink')
        >>> sorted(sorted(a.frozenset())
        ...        for a in p.translated_copies_intersection(box=(Integer(2),Integer(3))))
        [[(0, 0)],
         [(0, 0), (1, 0)],
         [(0, 1)],
         [(0, 1), (1, 1)],
         [(0, 2)],
         [(0, 2), (1, 2)],
         [(1, 0)],
         [(1, 1)],
         [(1, 2)]]

    Using a Polyomino as input:

    Sage

        sage: b = Polyomino([(0,0), (0,1), (0,2), (1,0), (2,0)])
        sage: p = Polyomino([(0,0), (1,0)])
        sage: sorted(sorted(a.frozenset())
        ....:        for a in p.translated_copies_intersection(b))
        [[(0, 0)], [(0, 0), (1, 0)], [(0, 1)], [(0, 2)], [(1, 0), (2, 0)], [(2, 0)]]

    [Python]

        >>> from sage.all import *
        >>> b = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1)), (Integer(0),Integer(2)), (Integer(1),Integer(0)), (Integer(2),Integer(0))])
        >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0))])
        >>> sorted(sorted(a.frozenset())
        ...        for a in p.translated_copies_intersection(b))
        [[(0, 0)], [(0, 0), (1, 0)], [(0, 1)], [(0, 2)], [(1, 0), (2, 0)], [(2, 0)]]

class sage.combinat.tiling.TilingSolver(pieces, box, rotation=True, reflection=False, reusable=False, outside=False)

  Bases: `SageObject`

  Tiling solver.

  Solve the problem of tiling a polyomino with a certain number of polyominoes.

  INPUT:

  - `pieces` – iterable of Polyominoes

  - `box` – polyomino or tuple of integers (size of a box)

  - `rotation` – boolean (default: `True`); whether to allow rotations

  - `reflection` – boolean (default: `False`); whether to allow reflections

  - `reusable` – boolean (default: `False`); whether to allow the pieces to be reused

  - `outside` – boolean (default: `False`); whether to allow pieces to partially go outside of the box (all non-empty intersection of the pieces with the box are considered)

  EXAMPLES:

  By default, rotations are allowed and reflections are not allowed:

  Sage

      sage: from sage.combinat.tiling import TilingSolver, Polyomino
      sage: p = Polyomino([(0,0,0)])
      sage: q = Polyomino([(0,0,0), (0,0,1)])
      sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
      sage: T = TilingSolver([p,q,r], box=(1,1,6))
      sage: T
      Tiling solver of 3 pieces into a box of size 6
      Rotation allowed: True
      Reflection allowed: False
      Reusing pieces allowed: False

  Python

      >>> from sage.all import *
      >>> from sage.combinat.tiling import TilingSolver, Polyomino
      >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
      >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
      >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
      >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
      >>> T
      Tiling solver of 3 pieces into a box of size 6
      Rotation allowed: True
      Reflection allowed: False
      Reusing pieces allowed: False

  Solutions are given by an iterator:

  Sage

      sage: it = T.solve()
      sage: for p in next(it): p
      Polyomino: [(0, 0, 0)], Color: gray
      Polyomino: [(0, 0, 1), (0, 0, 2)], Color: gray
      Polyomino: [(0, 0, 3), (0, 0, 4), (0, 0, 5)], Color: gray

  [Python]

      >>> from sage.all import *
      >>> it = T.solve()
      >>> for p in next(it): p
      Polyomino: [(0, 0, 0)], Color: gray
      Polyomino: [(0, 0, 1), (0, 0, 2)], Color: gray
      Polyomino: [(0, 0, 3), (0, 0, 4), (0, 0, 5)], Color: gray

  Another solution:

  Sage

      sage: for p in next(it): p
      Polyomino: [(0, 0, 0)], Color: gray
      Polyomino: [(0, 0, 1), (0, 0, 2), (0, 0, 3)], Color: gray
      Polyomino: [(0, 0, 4), (0, 0, 5)], Color: gray

  Python

      >>> from sage.all import *
      >>> for p in next(it): p
      Polyomino: [(0, 0, 0)], Color: gray
      Polyomino: [(0, 0, 1), (0, 0, 2), (0, 0, 3)], Color: gray
      Polyomino: [(0, 0, 4), (0, 0, 5)], Color: gray

  Tiling of a polyomino by polyominoes:

  Sage

      sage: b = Polyomino([(0,0), (1,0), (1,1), (2,1), (1,2), (2,2), (0,3), (1,3)])
      sage: p = Polyomino([(0,0), (1,0)])
      sage: T = TilingSolver([p], box=b, reusable=True)
      sage: T.number_of_solutions()
      2

  [Python]

      >>> from sage.all import *
      >>> b = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0)), (Integer(1),Integer(1)), (Integer(2),Integer(1)), (Integer(1),Integer(2)), (Integer(2),Integer(2)), (Integer(0),Integer(3)), (Integer(1),Integer(3))])
      >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(1),Integer(0))])
      >>> T = TilingSolver([p], box=b, reusable=True)
      >>> T.number_of_solutions()
      2

  animate(partial=None, stop=None, size=0.75, axes=False)

    Return an animation of evolving solutions.

    INPUT:

    - `partial` – string (default: `None`); whether to include partial (incomplete) solutions. It can be one of the following:

      - `None` – include only complete solutions

      - `'common_prefix'` – common prefix between two consecutive solutions

      - `'incremental'` – one piece change at a time

    - `stop` – integer (default: `None`); number of frames

    - `size` – number (default: `0.75`); the size of each `1 \times 1` square. This does a homothety with respect to the center of each polyomino.

    - `axes` – boolean (default: `False`); whether the x and y axes are shown

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import Polyomino, TilingSolver
        sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='cyan')
        sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True)
        sage: a = T.animate()                                                       # needs sage.plot
        sage: a                             # long time, optional - imagemagick, needs sage.plot
        Animation with 10 frames

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino, TilingSolver
        >>> y = Polyomino([(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(2),Integer(0)),(Integer(3),Integer(0)),(Integer(2),Integer(1))], color='cyan')
        >>> T = TilingSolver([y], box=(Integer(5),Integer(10)), reusable=True, reflection=True)
        >>> a = T.animate()                                                       # needs sage.plot
        >>> a                             # long time, optional - imagemagick, needs sage.plot
        Animation with 10 frames

    Include partial solutions (common prefix between two consecutive solutions):

    Sage

        sage: a = T.animate('common_prefix')                                        # needs sage.plot
        sage: a                             # long time, optional - imagemagick, needs sage.plot
        Animation with 19 frames

    [Python]

        >>> from sage.all import *
        >>> a = T.animate('common_prefix')                                        # needs sage.plot
        >>> a                             # long time, optional - imagemagick, needs sage.plot
        Animation with 19 frames

    Incremental solutions (one piece removed or added at a time):

    Sage

        sage: a = T.animate('incremental')  # long time (2s)                        # needs sage.plot
        sage: a                             # long time (2s), optional - imagemagick, needs sage.plot
        Animation with 123 frames

    Python

        >>> from sage.all import *
        >>> a = T.animate('incremental')  # long time (2s)                        # needs sage.plot
        >>> a                             # long time (2s), optional - imagemagick, needs sage.plot
        Animation with 123 frames

    Sage

        sage: a.show()                      # long time, optional - imagemagick, needs sage.plot

    [Python]

        >>> from sage.all import *
        >>> a.show()                      # long time, optional - imagemagick, needs sage.plot

    The `show` function takes arguments to specify the delay between frames (measured in hundredths of a second, default value 20) and the number of iterations (default: 0, which means to iterate forever). To iterate 4 times with half a second between each frame:

    Sage

        sage: a.show(delay=50, iterations=4)        # long time, optional - imagemagick, needs sage.plot

    Python

        >>> from sage.all import *
        >>> a.show(delay=Integer(50), iterations=Integer(4))        # long time, optional - imagemagick, needs sage.plot

    Limit the number of frames:

    Sage

        sage: a = T.animate('incremental', stop=13); a      # not tested            # needs sage.plot
        Animation with 13 frames

    [Python]

        >>> from sage.all import *
        >>> a = T.animate('incremental', stop=Integer(13)); a      # not tested            # needs sage.plot
        Animation with 13 frames

  coord_to_int_dict()

    Return a dictionary mapping coordinates to integers.

    OUTPUT: dictionary

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: A = T.coord_to_int_dict()
        sage: sorted(A.items())
        [((0, 0, 0), 3), ((0, 0, 1), 4), ((0, 0, 2), 5),
         ((0, 0, 3), 6), ((0, 0, 4), 7), ((0, 0, 5), 8)]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> A = T.coord_to_int_dict()
        >>> sorted(A.items())
        [((0, 0, 0), 3), ((0, 0, 1), 4), ((0, 0, 2), 5),
         ((0, 0, 3), 6), ((0, 0, 4), 7), ((0, 0, 5), 8)]

    Reusable pieces:

    Sage

        sage: p = Polyomino([(0,0), (0,1)])
        sage: q = Polyomino([(0,0), (0,1), (1,0), (1,1)])
        sage: T = TilingSolver([p,q], box=[3,2], reusable=True)
        sage: B = T.coord_to_int_dict()
        sage: sorted(B.items())
        [((0, 0), 0), ((0, 1), 1), ((1, 0), 2), ((1, 1), 3),
         ((2, 0), 4), ((2, 1), 5)]

    [Python]

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1))])
        >>> q = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1)), (Integer(1),Integer(0)), (Integer(1),Integer(1))])
        >>> T = TilingSolver([p,q], box=[Integer(3),Integer(2)], reusable=True)
        >>> B = T.coord_to_int_dict()
        >>> sorted(B.items())
        [((0, 0), 0), ((0, 1), 1), ((1, 0), 2), ((1, 1), 3),
         ((2, 0), 4), ((2, 1), 5)]

  dlx_solver()

    Return the sage DLX solver of that tiling problem.

    OUTPUT: dLX Solver

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: T.dlx_solver()
        Dancing links solver for 9 columns and 15 rows

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> T.dlx_solver()
        Dancing links solver for 9 columns and 15 rows

  int_to_coord_dict()

    Return a dictionary mapping integers to coordinates.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: B = T.int_to_coord_dict()
        sage: sorted(B.items())
        [(3, (0, 0, 0)), (4, (0, 0, 1)), (5, (0, 0, 2)),
         (6, (0, 0, 3)), (7, (0, 0, 4)), (8, (0, 0, 5))]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> B = T.int_to_coord_dict()
        >>> sorted(B.items())
        [(3, (0, 0, 0)), (4, (0, 0, 1)), (5, (0, 0, 2)),
         (6, (0, 0, 3)), (7, (0, 0, 4)), (8, (0, 0, 5))]

    Reusable pieces:

    Sage

        sage: from sage.combinat.tiling import Polyomino, TilingSolver
        sage: p = Polyomino([(0,0), (0,1)])
        sage: q = Polyomino([(0,0), (0,1), (1,0), (1,1)])
        sage: T = TilingSolver([p,q], box=[3,2], reusable=True)
        sage: B = T.int_to_coord_dict()
        sage: sorted(B.items())
        [(0, (0, 0)), (1, (0, 1)), (2, (1, 0)),
         (3, (1, 1)), (4, (2, 0)), (5, (2, 1))]

    [Python]

        >>> from sage.all import *
        >>> from sage.combinat.tiling import Polyomino, TilingSolver
        >>> p = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1))])
        >>> q = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1)), (Integer(1),Integer(0)), (Integer(1),Integer(1))])
        >>> T = TilingSolver([p,q], box=[Integer(3),Integer(2)], reusable=True)
        >>> B = T.int_to_coord_dict()
        >>> sorted(B.items())
        [(0, (0, 0)), (1, (0, 1)), (2, (1, 0)),
         (3, (1, 1)), (4, (2, 0)), (5, (2, 1))]

  is_suitable()

    Return whether the volume of the box is equal to sum of the volume of the polyominoes and the number of rows sent to the DLX solver is larger than zero.

    If these conditions are not verified, then the problem is not suitable in the sense that there are no solution.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: T.is_suitable()
        True
        sage: T = TilingSolver([p,q,r], box=(1,1,7))
        sage: T.is_suitable()
        False

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> T.is_suitable()
        True
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(7)))
        >>> T.is_suitable()
        False

  nrows_per_piece()

    Return the number of rows necessary by each piece.

    OUTPUT: list

    EXAMPLES:

    Sage

        sage: from sage.games.quantumino import QuantuminoSolver
        sage: q = QuantuminoSolver(0)
        sage: T = q.tiling_solver()
        sage: T.nrows_per_piece()             # long time (10s)
        [360, 360, 360, 360, 360, 180, 180, 672, 672, 360, 360, 180, 180, 360, 360, 180]

    Python

        >>> from sage.all import *
        >>> from sage.games.quantumino import QuantuminoSolver
        >>> q = QuantuminoSolver(Integer(0))
        >>> T = q.tiling_solver()
        >>> T.nrows_per_piece()             # long time (10s)
        [360, 360, 360, 360, 360, 180, 180, 672, 672, 360, 360, 180, 180, 360, 360, 180]

  number_of_solutions()

    Return the number of distinct solutions.

    OUTPUT: integer

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0)])
        sage: q = Polyomino([(0,0), (0,1)])
        sage: r = Polyomino([(0,0), (0,1), (0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,6))
        sage: T.number_of_solutions()
        6

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0)), (Integer(0),Integer(1)), (Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(6)))
        >>> T.number_of_solutions()
        6

    Sage

        sage: T = TilingSolver([p,q,r], box=(1,7))
        sage: T.number_of_solutions()
        0

    [Python]

        >>> from sage.all import *
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(7)))
        >>> T.number_of_solutions()
        0

  pieces()

    Return the list of pieces.

    OUTPUT: list of 3d polyominoes

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: for p in T._pieces: p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 0), (0, 0, 1)], Color: gray
        Polyomino: [(0, 0, 0), (0, 0, 1), (0, 0, 2)], Color: gray

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> for p in T._pieces: p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 0), (0, 0, 1)], Color: gray
        Polyomino: [(0, 0, 0), (0, 0, 1), (0, 0, 2)], Color: gray

  row_to_polyomino(row_number)

    Return a polyomino associated to a row.

    INPUT:

    - `row_number` – integer; the \(i\)-th row

    OUTPUT: polyomino

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: a = Polyomino([(0,0,0), (0,0,1), (1,0,0)], color='blue')
        sage: b = Polyomino([(0,0,0), (1,0,0), (0,1,0)], color='red')
        sage: T = TilingSolver([a,b], box=(2,1,3))
        sage: len(T.rows())
        16

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> a = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(1),Integer(0),Integer(0))], color='blue')
        >>> b = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0))], color='red')
        >>> T = TilingSolver([a,b], box=(Integer(2),Integer(1),Integer(3)))
        >>> len(T.rows())
        16

    Sage

        sage: T.row_to_polyomino(7)
        Polyomino: [(0, 0, 2), (1, 0, 1), (1, 0, 2)], Color: blue

    [Python]

        >>> from sage.all import *
        >>> T.row_to_polyomino(Integer(7))
        Polyomino: [(0, 0, 2), (1, 0, 1), (1, 0, 2)], Color: blue

    Sage

        sage: T.row_to_polyomino(13)
        Polyomino: [(0, 0, 1), (1, 0, 1), (1, 0, 2)], Color: red

    Python

        >>> from sage.all import *
        >>> T.row_to_polyomino(Integer(13))
        Polyomino: [(0, 0, 1), (1, 0, 1), (1, 0, 2)], Color: red

  rows()

    Creation of the rows.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: rows = T.rows()
        sage: for row in rows: row
        [0, 3]
        [0, 4]
        [0, 5]
        [0, 6]
        [0, 7]
        [0, 8]
        [1, 3, 4]
        [1, 4, 5]
        [1, 5, 6]
        [1, 6, 7]
        [1, 7, 8]
        [2, 3, 4, 5]
        [2, 4, 5, 6]
        [2, 5, 6, 7]
        [2, 6, 7, 8]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> rows = T.rows()
        >>> for row in rows: row
        [0, 3]
        [0, 4]
        [0, 5]
        [0, 6]
        [0, 7]
        [0, 8]
        [1, 3, 4]
        [1, 4, 5]
        [1, 5, 6]
        [1, 6, 7]
        [1, 7, 8]
        [2, 3, 4, 5]
        [2, 4, 5, 6]
        [2, 5, 6, 7]
        [2, 6, 7, 8]

  rows_for_piece(i, mod_box_isometries=False)

    Return the rows for the \(i\)-th piece.

    INPUT:

    - `i` – integer; the \(i\)-th piece

    - `mod_box_isometries` – boolean (default: `False`); whether to consider only rows for positions up to the action of the quotient the group of isometries of the \(n\)-cube by the subgroup of isometries of the \(a_1\times a_2\cdots \times a_n\) rectangular box where are the \(a_i\) are assumed to be distinct.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: T.rows_for_piece(0)
        [[0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8]]
        sage: T.rows_for_piece(1)
        [[1, 3, 4], [1, 4, 5], [1, 5, 6], [1, 6, 7], [1, 7, 8]]
        sage: T.rows_for_piece(2)
        [[2, 3, 4, 5], [2, 4, 5, 6], [2, 5, 6, 7], [2, 6, 7, 8]]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> T.rows_for_piece(Integer(0))
        [[0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8]]
        >>> T.rows_for_piece(Integer(1))
        [[1, 3, 4], [1, 4, 5], [1, 5, 6], [1, 6, 7], [1, 7, 8]]
        >>> T.rows_for_piece(Integer(2))
        [[2, 3, 4, 5], [2, 4, 5, 6], [2, 5, 6, 7], [2, 6, 7, 8]]

    Less rows when using `mod_box_isometries=True`:

    Sage

        sage: a = Polyomino([(0,0,0), (0,0,1), (1,0,0)])
        sage: b = Polyomino([(0,0,0), (1,0,0), (0,1,0)])
        sage: T = TilingSolver([a,b], box=(2,1,3))
        sage: T.rows_for_piece(0)
        [[0, 2, 3, 5],
         [0, 3, 4, 6],
         [0, 2, 3, 6],
         [0, 3, 4, 7],
         [0, 2, 5, 6],
         [0, 3, 6, 7],
         [0, 3, 5, 6],
         [0, 4, 6, 7]]
        sage: T.rows_for_piece(0, mod_box_isometries=True)
        [[0, 2, 3, 5], [0, 3, 4, 6]]
        sage: T.rows_for_piece(1, mod_box_isometries=True)
        [[1, 2, 3, 5], [1, 3, 4, 6]]

    [Python]

        >>> from sage.all import *
        >>> a = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(1),Integer(0),Integer(0))])
        >>> b = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0))])
        >>> T = TilingSolver([a,b], box=(Integer(2),Integer(1),Integer(3)))
        >>> T.rows_for_piece(Integer(0))
        [[0, 2, 3, 5],
         [0, 3, 4, 6],
         [0, 2, 3, 6],
         [0, 3, 4, 7],
         [0, 2, 5, 6],
         [0, 3, 6, 7],
         [0, 3, 5, 6],
         [0, 4, 6, 7]]
        >>> T.rows_for_piece(Integer(0), mod_box_isometries=True)
        [[0, 2, 3, 5], [0, 3, 4, 6]]
        >>> T.rows_for_piece(Integer(1), mod_box_isometries=True)
        [[1, 2, 3, 5], [1, 3, 4, 6]]

  solve(partial=None)

    Return an iterator of list of polyominoes that are an exact cover of the box.

    INPUT:

    - `partial` – string (default: `None`); whether to include partial (incomplete) solutions. It can be one of the following:

      - `None` – include only complete solution

      - `'common_prefix'` – common prefix between two consecutive solutions

      - `'incremental'` – one piece change at a time

    OUTPUT: iterator of list of polyominoes

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: it = T.solve()
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2)], Color: gray
        Polyomino: [(0, 0, 3), (0, 0, 4), (0, 0, 5)], Color: gray
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2), (0, 0, 3)], Color: gray
        Polyomino: [(0, 0, 4), (0, 0, 5)], Color: gray
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0), (0, 0, 1)], Color: gray
        Polyomino: [(0, 0, 2), (0, 0, 3), (0, 0, 4)], Color: gray
        Polyomino: [(0, 0, 5)], Color: gray

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> it = T.solve()
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2)], Color: gray
        Polyomino: [(0, 0, 3), (0, 0, 4), (0, 0, 5)], Color: gray
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2), (0, 0, 3)], Color: gray
        Polyomino: [(0, 0, 4), (0, 0, 5)], Color: gray
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0), (0, 0, 1)], Color: gray
        Polyomino: [(0, 0, 2), (0, 0, 3), (0, 0, 4)], Color: gray
        Polyomino: [(0, 0, 5)], Color: gray

    Including the partial solutions:

    Sage

        sage: it = T.solve(partial='common_prefix')
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2)], Color: gray
        Polyomino: [(0, 0, 3), (0, 0, 4), (0, 0, 5)], Color: gray
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2), (0, 0, 3)], Color: gray
        Polyomino: [(0, 0, 4), (0, 0, 5)], Color: gray
        sage: for p in next(it): p
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0), (0, 0, 1)], Color: gray
        Polyomino: [(0, 0, 2), (0, 0, 3), (0, 0, 4)], Color: gray
        Polyomino: [(0, 0, 5)], Color: gray

    [Python]

        >>> from sage.all import *
        >>> it = T.solve(partial='common_prefix')
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2)], Color: gray
        Polyomino: [(0, 0, 3), (0, 0, 4), (0, 0, 5)], Color: gray
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: gray
        Polyomino: [(0, 0, 1), (0, 0, 2), (0, 0, 3)], Color: gray
        Polyomino: [(0, 0, 4), (0, 0, 5)], Color: gray
        >>> for p in next(it): p
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0), (0, 0, 1)], Color: gray
        Polyomino: [(0, 0, 2), (0, 0, 3), (0, 0, 4)], Color: gray
        Polyomino: [(0, 0, 5)], Color: gray

    Colors are preserved when the polyomino can be reused:

    Sage

        sage: p = Polyomino([(0,0,0)], color='yellow')
        sage: q = Polyomino([(0,0,0), (0,0,1)], color='yellow')
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)], color='yellow')
        sage: T = TilingSolver([p,q,r], box=(1,1,6), reusable=True)
        sage: it = T.solve()
        sage: for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: yellow
        Polyomino: [(0, 0, 1)], Color: yellow
        Polyomino: [(0, 0, 2)], Color: yellow
        Polyomino: [(0, 0, 3)], Color: yellow
        Polyomino: [(0, 0, 4)], Color: yellow
        Polyomino: [(0, 0, 5)], Color: yellow

    Python

        >>> from sage.all import *
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))], color='yellow')
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))], color='yellow')
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))], color='yellow')
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)), reusable=True)
        >>> it = T.solve()
        >>> for p in next(it): p
        Polyomino: [(0, 0, 0)], Color: yellow
        Polyomino: [(0, 0, 1)], Color: yellow
        Polyomino: [(0, 0, 2)], Color: yellow
        Polyomino: [(0, 0, 3)], Color: yellow
        Polyomino: [(0, 0, 4)], Color: yellow
        Polyomino: [(0, 0, 5)], Color: yellow

  space()

    Return an iterator over all the nonnegative integer coordinates contained in the space to tile.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: list(T.space())
        [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (0, 0, 5)]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> list(T.space())
        [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (0, 0, 5)]

  starting_rows()

    Return the starting rows for each piece.

    EXAMPLES:

    Sage

        sage: from sage.combinat.tiling import TilingSolver, Polyomino
        sage: p = Polyomino([(0,0,0)])
        sage: q = Polyomino([(0,0,0), (0,0,1)])
        sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)])
        sage: T = TilingSolver([p,q,r], box=(1,1,6))
        sage: T.starting_rows()
        [0, 6, 11, 15]

    Python

        >>> from sage.all import *
        >>> from sage.combinat.tiling import TilingSolver, Polyomino
        >>> p = Polyomino([(Integer(0),Integer(0),Integer(0))])
        >>> q = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1))])
        >>> r = Polyomino([(Integer(0),Integer(0),Integer(0)), (Integer(0),Integer(0),Integer(1)), (Integer(0),Integer(0),Integer(2))])
        >>> T = TilingSolver([p,q,r], box=(Integer(1),Integer(1),Integer(6)))
        >>> T.starting_rows()
        [0, 6, 11, 15]

sage.combinat.tiling.ncube_isometry_group(n, orientation_preserving=True)

  Return the isometry group of the \(n\)-cube as a list of matrices.

  INPUT:

  - `n` – positive integer, dimension of the space

  - `orientation_preserving` – boolean (default: `True`); whether the orientation is preserved

  OUTPUT: list of matrices

  EXAMPLES:

  Sage

      sage: from sage.combinat.tiling import ncube_isometry_group
      sage: sorted(ncube_isometry_group(2))
      [
      [-1  0]  [ 0 -1]  [ 0  1]  [1 0]
      [ 0 -1], [ 1  0], [-1  0], [0 1]
      ]
      sage: sorted(ncube_isometry_group(2, orientation_preserving=False))
      [
      [-1  0]  [-1  0]  [ 0 -1]  [ 0 -1]  [ 0  1]  [0 1]  [ 1  0]  [1 0]
      [ 0 -1], [ 0  1], [-1  0], [ 1  0], [-1  0], [1 0], [ 0 -1], [0 1]
      ]

  Python

      >>> from sage.all import *
      >>> from sage.combinat.tiling import ncube_isometry_group
      >>> sorted(ncube_isometry_group(Integer(2)))
      [
      [-1  0]  [ 0 -1]  [ 0  1]  [1 0]
      [ 0 -1], [ 1  0], [-1  0], [0 1]
      ]
      >>> sorted(ncube_isometry_group(Integer(2), orientation_preserving=False))
      [
      [-1  0]  [-1  0]  [ 0 -1]  [ 0 -1]  [ 0  1]  [0 1]  [ 1  0]  [1 0]
      [ 0 -1], [ 0  1], [-1  0], [ 1  0], [-1  0], [1 0], [ 0 -1], [0 1]
      ]

  There are 24 orientation preserving isometries of the 3-cube:

  Sage

      sage: sorted(ncube_isometry_group(3))
      [
      [-1  0  0]  [-1  0  0]  [-1  0  0]  [-1  0  0]  [ 0 -1  0]  [ 0 -1  0]
      [ 0 -1  0]  [ 0  0 -1]  [ 0  0  1]  [ 0  1  0]  [-1  0  0]  [ 0  0 -1]
      [ 0  0  1], [ 0 -1  0], [ 0  1  0], [ 0  0 -1], [ 0  0 -1], [ 1  0  0],

      [ 0 -1  0]  [ 0 -1  0]  [ 0  0 -1]  [ 0  0 -1]  [ 0  0 -1]  [ 0  0 -1]
      [ 0  0  1]  [ 1  0  0]  [-1  0  0]  [ 0 -1  0]  [ 0  1  0]  [ 1  0  0]
      [-1  0  0], [ 0  0  1], [ 0  1  0], [-1  0  0], [ 1  0  0], [ 0 -1  0],

      [ 0  0  1]  [ 0  0  1]  [ 0  0  1]  [0 0 1]  [ 0  1  0]  [ 0  1  0]
      [-1  0  0]  [ 0 -1  0]  [ 0  1  0]  [1 0 0]  [-1  0  0]  [ 0  0 -1]
      [ 0 -1  0], [ 1  0  0], [-1  0  0], [0 1 0], [ 0  0  1], [-1  0  0],

      [0 1 0]  [ 0  1  0]  [ 1  0  0]  [ 1  0  0]  [ 1  0  0]  [1 0 0]
      [0 0 1]  [ 1  0  0]  [ 0 -1  0]  [ 0  0 -1]  [ 0  0  1]  [0 1 0]
      [1 0 0], [ 0  0 -1], [ 0  0 -1], [ 0  1  0], [ 0 -1  0], [0 0 1]
      ]

  [Python]

      >>> from sage.all import *
      >>> sorted(ncube_isometry_group(Integer(3)))
      [
      [-1  0  0]  [-1  0  0]  [-1  0  0]  [-1  0  0]  [ 0 -1  0]  [ 0 -1  0]
      [ 0 -1  0]  [ 0  0 -1]  [ 0  0  1]  [ 0  1  0]  [-1  0  0]  [ 0  0 -1]
      [ 0  0  1], [ 0 -1  0], [ 0  1  0], [ 0  0 -1], [ 0  0 -1], [ 1  0  0],
      <BLANKLINE>
      [ 0 -1  0]  [ 0 -1  0]  [ 0  0 -1]  [ 0  0 -1]  [ 0  0 -1]  [ 0  0 -1]
      [ 0  0  1]  [ 1  0  0]  [-1  0  0]  [ 0 -1  0]  [ 0  1  0]  [ 1  0  0]
      [-1  0  0], [ 0  0  1], [ 0  1  0], [-1  0  0], [ 1  0  0], [ 0 -1  0],
      <BLANKLINE>
      [ 0  0  1]  [ 0  0  1]  [ 0  0  1]  [0 0 1]  [ 0  1  0]  [ 0  1  0]
      [-1  0  0]  [ 0 -1  0]  [ 0  1  0]  [1 0 0]  [-1  0  0]  [ 0  0 -1]
      [ 0 -1  0], [ 1  0  0], [-1  0  0], [0 1 0], [ 0  0  1], [-1  0  0],
      <BLANKLINE>
      [0 1 0]  [ 0  1  0]  [ 1  0  0]  [ 1  0  0]  [ 1  0  0]  [1 0 0]
      [0 0 1]  [ 1  0  0]  [ 0 -1  0]  [ 0  0 -1]  [ 0  0  1]  [0 1 0]
      [1 0 0], [ 0  0 -1], [ 0  0 -1], [ 0  1  0], [ 0 -1  0], [0 0 1]
      ]

sage.combinat.tiling.ncube_isometry_group_cosets(orientation_preserving=True)

  Return the quotient of the isometry group of the \(n\)-cube by the the isometry group of the rectangular parallelepiped.

  INPUT:

  - `n` – positive integer, dimension of the space

  - `orientation_preserving` – boolean (default: `True`); whether the orientation is preserved

  OUTPUT: list of cosets, each coset being a sorted list of matrices

  EXAMPLES:

  Sage

      sage: from sage.combinat.tiling import ncube_isometry_group_cosets
      sage: sorted(ncube_isometry_group_cosets(2))
      [[
      [-1  0]  [1 0]
      [ 0 -1], [0 1]
      ], [
      [ 0 -1]  [ 0  1]
      [ 1  0], [-1  0]
      ]]
      sage: sorted(ncube_isometry_group_cosets(2, False))
      [[
      [-1  0]  [-1  0]  [ 1  0]  [1 0]
      [ 0 -1], [ 0  1], [ 0 -1], [0 1]
      ], [
      [ 0 -1]  [ 0 -1]  [ 0  1]  [0 1]
      [-1  0], [ 1  0], [-1  0], [1 0]
      ]]

  Python

      >>> from sage.all import *
      >>> from sage.combinat.tiling import ncube_isometry_group_cosets
      >>> sorted(ncube_isometry_group_cosets(Integer(2)))
      [[
      [-1  0]  [1 0]
      [ 0 -1], [0 1]
      ], [
      [ 0 -1]  [ 0  1]
      [ 1  0], [-1  0]
      ]]
      >>> sorted(ncube_isometry_group_cosets(Integer(2), False))
      [[
      [-1  0]  [-1  0]  [ 1  0]  [1 0]
      [ 0 -1], [ 0  1], [ 0 -1], [0 1]
      ], [
      [ 0 -1]  [ 0 -1]  [ 0  1]  [0 1]
      [-1  0], [ 1  0], [-1  0], [1 0]
      ]]

  Sage

      sage: sorted(ncube_isometry_group_cosets(3))
      [[
      [-1  0  0]  [-1  0  0]  [ 1  0  0]  [1 0 0]
      [ 0 -1  0]  [ 0  1  0]  [ 0 -1  0]  [0 1 0]
      [ 0  0  1], [ 0  0 -1], [ 0  0 -1], [0 0 1]
      ], [
      [-1  0  0]  [-1  0  0]  [ 1  0  0]  [ 1  0  0]
      [ 0  0 -1]  [ 0  0  1]  [ 0  0 -1]  [ 0  0  1]
      [ 0 -1  0], [ 0  1  0], [ 0  1  0], [ 0 -1  0]
      ], [
      [ 0 -1  0]  [ 0 -1  0]  [ 0  1  0]  [ 0  1  0]
      [-1  0  0]  [ 1  0  0]  [-1  0  0]  [ 1  0  0]
      [ 0  0 -1], [ 0  0  1], [ 0  0  1], [ 0  0 -1]
      ], [
      [ 0 -1  0]  [ 0 -1  0]  [ 0  1  0]  [0 1 0]
      [ 0  0 -1]  [ 0  0  1]  [ 0  0 -1]  [0 0 1]
      [ 1  0  0], [-1  0  0], [-1  0  0], [1 0 0]
      ], [
      [ 0  0 -1]  [ 0  0 -1]  [ 0  0  1]  [0 0 1]
      [-1  0  0]  [ 1  0  0]  [-1  0  0]  [1 0 0]
      [ 0  1  0], [ 0 -1  0], [ 0 -1  0], [0 1 0]
      ], [
      [ 0  0 -1]  [ 0  0 -1]  [ 0  0  1]  [ 0  0  1]
      [ 0 -1  0]  [ 0  1  0]  [ 0 -1  0]  [ 0  1  0]
      [-1  0  0], [ 1  0  0], [ 1  0  0], [-1  0  0]
      ]]

  [Python]

      >>> from sage.all import *
      >>> sorted(ncube_isometry_group_cosets(Integer(3)))
      [[
      [-1  0  0]  [-1  0  0]  [ 1  0  0]  [1 0 0]
      [ 0 -1  0]  [ 0  1  0]  [ 0 -1  0]  [0 1 0]
      [ 0  0  1], [ 0  0 -1], [ 0  0 -1], [0 0 1]
      ], [
      [-1  0  0]  [-1  0  0]  [ 1  0  0]  [ 1  0  0]
      [ 0  0 -1]  [ 0  0  1]  [ 0  0 -1]  [ 0  0  1]
      [ 0 -1  0], [ 0  1  0], [ 0  1  0], [ 0 -1  0]
      ], [
      [ 0 -1  0]  [ 0 -1  0]  [ 0  1  0]  [ 0  1  0]
      [-1  0  0]  [ 1  0  0]  [-1  0  0]  [ 1  0  0]
      [ 0  0 -1], [ 0  0  1], [ 0  0  1], [ 0  0 -1]
      ], [
      [ 0 -1  0]  [ 0 -1  0]  [ 0  1  0]  [0 1 0]
      [ 0  0 -1]  [ 0  0  1]  [ 0  0 -1]  [0 0 1]
      [ 1  0  0], [-1  0  0], [-1  0  0], [1 0 0]
      ], [
      [ 0  0 -1]  [ 0  0 -1]  [ 0  0  1]  [0 0 1]
      [-1  0  0]  [ 1  0  0]  [-1  0  0]  [1 0 0]
      [ 0  1  0], [ 0 -1  0], [ 0 -1  0], [0 1 0]
      ], [
      [ 0  0 -1]  [ 0  0 -1]  [ 0  0  1]  [ 0  0  1]
      [ 0 -1  0]  [ 0  1  0]  [ 0 -1  0]  [ 0  1  0]
      [-1  0  0], [ 1  0  0], [ 1  0  0], [-1  0  0]
      ]]