def intersection(self, other: Domain):
        """
        Return a domain representing the intersection of self and other.
        Result is at least as strict as self and other.

        contains(self, x) and contains(other, x) -> contains(intersection, x)

        TODO:
        - does order relations are checked for intersection matter?
            - almost certainly yes, intersection is not transitive.
            - so what order is best? fewest remaining relations? does it matter?
        """

        newtags = self.tags.union(other.tags)
        if t.contradiction(newtags):
            raise ValueError(
                f"Contradictory {newtags=} for {self.intersection} {other=}"
            )

        newdom = Domain(newtags)
        for orel, odom in *self.relations, *other.relations:
            newdom.add_relation(orel, copy.deepcopy(odom))

        return newdom

        Return a domain representing the intersection of self and other.
        Result is at least as strict as self and other.

        contains(self, x) and contains(other, x) -> contains(intersection, x)

        TODO:
        - does order relations are checked for intersection matter?
            - almost certainly yes, intersection is not transitive.
            - so what order is best? fewest remaining relations? does it matter?
        
infinigen/core/constraints/reasoning/domain.py