Return an expression where the expression or all subterms equal to a key
        expression are substituted with the corresponding value expression using
        a mapping of: {expr->expr to substitute.}

        Return this expression unmodified if nothing could be substituted.

        Note that this can be used to tested for expression containment.

def subs(self, substitutions, default=None, simplify=False):
        """
        Return an expression where the expression or all subterms equal to a key
        expression are substituted with the corresponding value expression using
        a mapping of: {expr->expr to substitute.}

        Return this expression unmodified if nothing could be substituted.

        Note that this can be used to tested for expression containment.
        """
        # shortcut: check if we have our whole expression as a possible
        # subsitution source
        for expr, substitution in substitutions.items():
            if expr == self:
                return substitution

        # otherwise, do a proper substitution of sub expressions
        expr = self._subs(substitutions, default, simplify)
        return self if expr is None else expr