# Path: craft_ai/errors.py
# class CraftAiError(Exception):
#     """Base class for exceptions in the craft ai client."""
# 
#     def __init__(self, message=None, metadata=None):
#         self.message = message
#         self.metadata = metadata
#         super(CraftAiError, self).__init__(message, metadata)
# 
#     def __str__(self):
#         return repr(self.message)
# 
# Path: craft_ai/formatters.py
# def format_decision_rules(rules):
#     return " and ".join([_format_decision_rule(rule) for rule in rules])
# 
# Path: craft_ai/operators.py
# OPERATORS = OPERATORS_V2


from functools import reduce as ft_reduce
from .errors import CraftAiError
from .formatters import format_decision_rules
from .operators import OPERATORS
        )

    if op_2 >= op_1_from and op_2 < op_1_to:
        # op_2 belongs to op_1
        #    |    op_1    |
        #           |op_2
        return {
            "property": rule_1["property"],
            "operator": OPERATORS["IN_INTERVAL"],
            "operand": [op_2, op_1_to],
        }

    # op_2 before op_1
    #    |    op_1    |
    #   |op_2
    return rule_1


def _in_lt_reducer(rule_1, rule_2):
    op_1_from = rule_1["operand"][0]
    op_1_to = rule_1["operand"][1]
    op_2 = rule_2["operand"]

    op_1_is_cyclic = op_1_from > op_1_to

    if op_1_is_cyclic:
        # Cyclics makes no sense with single bound limits
        raise CraftAiError(
            """Unable to reduce decision rules '{}' and '{}': """
            """the resulting rule is not fulfillable.""".format(

# Path: craft_ai/errors.py
# class CraftAiError(Exception):
#     """Base class for exceptions in the craft ai client."""
# 
#     def __init__(self, message=None, metadata=None):
#         self.message = message
#         self.metadata = metadata
#         super(CraftAiError, self).__init__(message, metadata)
# 
#     def __str__(self):
#         return repr(self.message)
# 
# Path: craft_ai/formatters.py
# def format_decision_rules(rules):
#     return " and ".join([_format_decision_rule(rule) for rule in rules])
# 
# Path: craft_ai/operators.py
# OPERATORS = OPERATORS_V2


# Path: craft_ai/reducer.py
from functools import reduce as ft_reduce
from .errors import CraftAiError
from .formatters import format_decision_rules
from .operators import OPERATORS

        )

    if op_2 >= op_1_from and op_2 < op_1_to:
        # op_2 belongs to op_1
        #    |    op_1    |
        #           |op_2
        return {
            "property": rule_1["property"],
            "operator": OPERATORS["IN_INTERVAL"],
            "operand": [op_2, op_1_to],
        }

    # op_2 before op_1
    #    |    op_1    |
    #   |op_2
    return rule_1


def _in_lt_reducer(rule_1, rule_2):
    op_1_from = rule_1["operand"][0]
    op_1_to = rule_1["operand"][1]
    op_2 = rule_2["operand"]

    op_1_is_cyclic = op_1_from > op_1_to

    if op_1_is_cyclic:
        # Cyclics makes no sense with single bound limits
        raise CraftAiError(
            """Unable to reduce decision rules '{}' and '{}': """
            """the resulting rule is not fulfillable.""".format(

                format_decision_rules([rule_1]), format_decision_rules([rule_2])