A centrifuge is a laboratory device that uses centrifugal force1 to separate various components of a fluid. This is achieved by spinning the fluid at high speed, thereby separating fluids of different densities (e.g. cream from milk) or liquids from solids. The fluid is contained in a test tube that is placed in one of the holes of the centrifuge. This is an example of a 20-hole centrifuge, where 8 holes have been filled with test tubes:

centrifuge
A 20-hole centrifuge, where 8 holes have been filled with test tubes.

Assignment

For a centrifuge with $$n$$ holes that are distributed equidistant around a circle, we introduce a coordinate system whose X-axis and Y-axis intersect at the center of the circle. There is always a hole whose center is on the positive X-axis. The holes are numbered counterclockwise from 0, starting at the hole whose center is on the positive X-axis.

centrifuge
The six holes of this centrifuge are numbered counterclockwise from zero, starting at the hole whose center is on the positive X axis. The four orange holes (0, 1, 3 and 4) are filled with test tubes and the two gray holes (2 and 5) are empty.

We represent a configuration describing which holes of a centrifuge are filled with test tubes as a collection (list, tuple or set) with the numbers (int) of the filled holes. The order in which the numbers of the filled holes are listed does not matter. If the four orange holes in the above figure are filled with test tubes and the two dark gray holes are empty, we can for example represent that configuration by the set {4, 1, 3, 0}.

A centrifuge can rotate clockwise or counterclockwise. In one rotation step, all holes (and test tubes) advance one number clockwise or counterclockwise, so that after the rotational step we again have one hole whose center is on the positive X-axis. This is illustrated in the figure below, where we have filled all holes of the centrifuge with test tubes, each having its own unique color.

rotation step
A centrifuge can rotate clockwise or counterclockwise. In one rotation step, all holes (and test tubes) advance one number clockwise or counterclockwise, so that after the rotation step we again have one hole whose center is on the positive X-axis. This is illustrated in this figure, where we have filled all holes of the centrifuge with test tubes, each having its own unique color.

When a centrifuge is mirrored, its configuration of test tubes is mirrored across the X-axis. If we mirror a mirrored centrifuge again, the configuration of the centrifuge is restored in its original state.

mirror
When a centrifuge is mirrored, its configuration of test tubes is mirrored across the X-axis. If we mirror a mirrored centrifuge again, the configuration of the centrifuge is restored in its original state.

Define a class Centrifuge to represent centrifuges with some holes filled with test tubes. Two arguments must be passed when creating a new centrifuge (Centrifuge): i) the number of holes $$n$$ and ii) a configuration that describes which holes of the centrifuge are filled with test tubes.

If a centrifuge (Centrifuge) is passed to the built-in function repr, a string representation (str) must be returned that reads as a Python expression to create a new centrifuge (Centrifuge) with the same number of holes and the same configuration of filled holes as the centrifuge passed to the function repr, and the configuration represented as an increasingly ordered list (list) of numbers (int).

In addition, a centrifuge $$c$$ (Centrifuge) must support at least the following methods:

Make sure the operator == can be used (c == d) to check if two centrifuges $$c$$ and $$d$$ (Centrifuge) are equal. This is the case if both centrifuges have the same number of holes and if one of the centrifuges can be rotated over a number of steps so that both centrifuges have the same filled holes, possibly after mirroring one of the centrifuges. The operator == may not change the stage of centrifuges $$c$$ and $$d$$.

Make sure the operator += can be used (c += d) to fill all holes in centrifuge $$c$$ (Centrifuge) that are filled in centrifuge $$d$$ (Centrifuge). This may not change the state of centrifuge $$d$$. If centrifuges $$c$$ and $$d$$ do not have the same number of holes or if there is a filled hole in centrifuge $$d$$ that is also filled in centrifuge $$c$$, the state of centrifuge $$c$$ may not change and an AssertionError must be raised with the message could not fill holes.

Make sure the operator -= can be used (c -= d) to empty all holes in centrifuge $$c$$ (Centrifuge) that are filled in centrifuge $$d$$ (Centrifuge). This may not change the state of centrifuge $$d$$. If centrifuges $$c$$ and $$d$$ do not have the same number of holes or if there is a filled hole in centrifuge $$d$$ that is empty in centrifuge $$c$$, the state of centrifuge $$c$$ may not change and an AssertionError must be raised with the message could not empty holes.

Tip

Use the special method __iadd__2 to overload the operator += and the special method __isub__3 to overload the operator -=. Both methods need to return a reference to the object being modified, because c += d is equivalent to c = c.__iadd__(d) and c -= d is equivalent to c = c.__iasub__(d).

Example

>>> centrifuge = Centrifuge(6, {4, 1, 3, 0})
>>> centrifuge
Centrifuge(6, [0, 1, 3, 4])
>>> centrifuge.rotate()
Centrifuge(6, [1, 2, 4, 5])
>>> centrifuge.rotate(clockwise=True)
Centrifuge(6, [0, 1, 3, 4])
>>> centrifuge.mirror()
Centrifuge(6, [0, 2, 3, 5])
>>> centrifuge.mirror()
Centrifuge(6, [0, 1, 3, 4])

>>> centrifuge = Centrifuge(6, [2, 0, 5])
>>> centrifuge == Centrifuge(6, [2, 4, 5])
True
>>> centrifuge == Centrifuge(6, [3, 4, 5])
False
>>> centrifuge
Centrifuge(6, [0, 2, 5])
>>> centrifuge.rotate().rotate().mirror()
Centrifuge(6, [2, 4, 5])
>>> centrifuge.mirror().rotate(True).rotate(True)
Centrifuge(6, [0, 2, 5])
>>> centrifuge += Centrifuge(6, {1, 4})
>>> centrifuge
Centrifuge(6, [0, 1, 2, 4, 5])
>>> centrifuge += Centrifuge(6, [1, 4, 2])
Traceback (most recent call last):
AssertionError: could not fill holes
>>> centrifuge -= Centrifuge(6, {1, 4})
>>> centrifuge
Centrifuge(6, [0, 2, 5])
>>> centrifuge -= Centrifuge(6, (1, 5))
Traceback (most recent call last):
AssertionError: could not empty holes