# Path: pyknow/pattern.py
# class Bindable:
#     def __rlshift__(self, other):
#         if not isinstance(other, str):
#             raise TypeError("%s can only be binded to a string" % self)
#         elif self.__bind__ is not None:
#             raise RuntimeError("%s can only be binded once" % repr(self))
#         else:
#             self.__bind__ = other
#             return self
# 
#     def __hash__(self):
#         return hash((self.__bind__, ) + tuple(self))
# 
#     def __eq__(self, other):
#         return (self.__class__ == other.__class__
#                 and self.__bind__ == other.__bind__
#                 and super().__eq__(other))
# 
# Path: pyknow/utils.py
# @singledispatch
# def freeze(obj):
#     if isinstance(obj, collections.abc.Hashable):
#         return obj
#     else:
#         raise TypeError(
#             ("type(%r) => %s is not hashable, "
#              "see `pyknow.utils.freeze` docs to register your "
#              "own freeze method") % (obj, type(obj)))
# 
# @singledispatch
# def unfreeze(obj):
#     return obj
# 
# Path: pyknow/conditionalelement.py
# class OperableCE:
#     def __and__(self, other):
#         if isinstance(self, AND) and isinstance(other, AND):
#             return AND(*[x for x in chain(self, other)])
#         elif isinstance(self, AND):
#             return AND(*[x for x in self]+[other])
#         elif isinstance(other, AND):
#             return AND(*[self]+[x for x in other])
#         else:
#             return AND(self, other)
# 
#     def __or__(self, other):
#         if isinstance(self, OR) and isinstance(other, OR):
#             return OR(*[x for x in chain(self, other)])
#         elif isinstance(self, OR):
#             return OR(*[x for x in self]+[other])
#         elif isinstance(other, OR):
#             return OR(*[self]+[x for x in other])
#         else:
#             return OR(self, other)
# 
#     def __invert__(self):
#         return NOT(self)
# 
# Path: pyknow/conditionalelement.py
# class ConditionalElement(tuple):
#     """Base Conditional Element"""
# 
#     def __new__(cls, *args):
#         return super(ConditionalElement, cls).__new__(cls, args)
# 
#     def __repr__(self):  # pragma: no cover
#         return "%s%s" % (self.__class__.__name__, super().__repr__())


from itertools import chain
from functools import lru_cache
from schema import Schema
from pyknow.pattern import Bindable
from pyknow.utils import freeze, unfreeze
from pyknow.conditionalelement import OperableCE
from pyknow.conditionalelement import ConditionalElement
import abc
import collections
                return self.__defaults.setdefault(key, default)

    def __setitem__(self, key, value):
        if self.__factid__ is None:
            super().__setitem__(key, freeze(value))
        else:
            raise RuntimeError("A fact can't be modified after declaration.")

    def validate(self):
        for name, field in self.__fields__.items():
            if name in self:
                try:
                    field.validate(self[name])
                except Exception as exc:
                    raise ValueError(
                        "Invalid value on field %r for fact %r"
                        % (name, self))
            elif field.mandatory:
                raise ValueError(
                    "Mandatory field %r is not defined for fact %r"
                    % (name, self))
            else:
                pass

    def update(self, mapping):
        for k, v in mapping.items():
            self[k] = v

    def as_dict(self):
        """Return a dictionary containing this `Fact` data."""

# Path: pyknow/pattern.py
# class Bindable:
#     def __rlshift__(self, other):
#         if not isinstance(other, str):
#             raise TypeError("%s can only be binded to a string" % self)
#         elif self.__bind__ is not None:
#             raise RuntimeError("%s can only be binded once" % repr(self))
#         else:
#             self.__bind__ = other
#             return self
# 
#     def __hash__(self):
#         return hash((self.__bind__, ) + tuple(self))
# 
#     def __eq__(self, other):
#         return (self.__class__ == other.__class__
#                 and self.__bind__ == other.__bind__
#                 and super().__eq__(other))
# 
# Path: pyknow/utils.py
# @singledispatch
# def freeze(obj):
#     if isinstance(obj, collections.abc.Hashable):
#         return obj
#     else:
#         raise TypeError(
#             ("type(%r) => %s is not hashable, "
#              "see `pyknow.utils.freeze` docs to register your "
#              "own freeze method") % (obj, type(obj)))
# 
# @singledispatch
# def unfreeze(obj):
#     return obj
# 
# Path: pyknow/conditionalelement.py
# class OperableCE:
#     def __and__(self, other):
#         if isinstance(self, AND) and isinstance(other, AND):
#             return AND(*[x for x in chain(self, other)])
#         elif isinstance(self, AND):
#             return AND(*[x for x in self]+[other])
#         elif isinstance(other, AND):
#             return AND(*[self]+[x for x in other])
#         else:
#             return AND(self, other)
# 
#     def __or__(self, other):
#         if isinstance(self, OR) and isinstance(other, OR):
#             return OR(*[x for x in chain(self, other)])
#         elif isinstance(self, OR):
#             return OR(*[x for x in self]+[other])
#         elif isinstance(other, OR):
#             return OR(*[self]+[x for x in other])
#         else:
#             return OR(self, other)
# 
#     def __invert__(self):
#         return NOT(self)
# 
# Path: pyknow/conditionalelement.py
# class ConditionalElement(tuple):
#     """Base Conditional Element"""
# 
#     def __new__(cls, *args):
#         return super(ConditionalElement, cls).__new__(cls, args)
# 
#     def __repr__(self):  # pragma: no cover
#         return "%s%s" % (self.__class__.__name__, super().__repr__())


# Path: pyknow/fact.py
from itertools import chain
from functools import lru_cache
from schema import Schema
from pyknow.pattern import Bindable
from pyknow.utils import freeze, unfreeze
from pyknow.conditionalelement import OperableCE
from pyknow.conditionalelement import ConditionalElement
import abc
import collections

                return self.__defaults.setdefault(key, default)

    def __setitem__(self, key, value):
        if self.__factid__ is None:
            super().__setitem__(key, freeze(value))
        else:
            raise RuntimeError("A fact can't be modified after declaration.")

    def validate(self):
        for name, field in self.__fields__.items():
            if name in self:
                try:
                    field.validate(self[name])
                except Exception as exc:
                    raise ValueError(
                        "Invalid value on field %r for fact %r"
                        % (name, self))
            elif field.mandatory:
                raise ValueError(
                    "Mandatory field %r is not defined for fact %r"
                    % (name, self))
            else:
                pass

    def update(self, mapping):
        for k, v in mapping.items():
            self[k] = v

    def as_dict(self):
        """Return a dictionary containing this `Fact` data."""

        return {k: unfreeze(v)