Source code for algebraixlib.mathobjects.couplet
"""Provide the class :class:`~.Couplet`; it represents a :term:`couplet`."""
# Copyright Algebraix Data Corporation 2015 - 2017
#
# This file is part of algebraixlib <http://github.com/AlgebraixData/algebraixlib>.
#
# algebraixlib is free software: you can redistribute it and/or modify it under the terms of version
# 3 of the GNU Lesser General Public License as published by the Free Software Foundation.
#
# algebraixlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with algebraixlib.
# If not, see <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------------------------------------
import functools as _functools
import algebraixlib.structure as _structure
import algebraixlib.undef as _undef
import algebraixlib.util.miscellaneous as _misc
from algebraixlib.tmp_sqlda_op import tmp_sqlda_op
from .atom import auto_convert
from .mathobject import MathObject
from ..cache_status import CacheStatus
from ._flags import Flags as _Flags
# --------------------------------------------------------------------------------------------------
def _init_cache() -> int:
"""Initialization function for `Couplet._INIT_CACHE`."""
flags = _Flags()
# Known to be true:
flags.f.couplet = CacheStatus.IS
# Known to be false:
flags.f.atom = CacheStatus.IS_NOT
flags.f.set = CacheStatus.IS_NOT
flags.f.relation = CacheStatus.IS_NOT
flags.f.clan = CacheStatus.IS_NOT
flags.f.multiset = CacheStatus.IS_NOT
flags.f.multiclan = CacheStatus.IS_NOT
# Known to be undefined/not apply:
flags.f.functional = CacheStatus.N_A
flags.f.right_functional = CacheStatus.N_A
flags.f.regular = CacheStatus.N_A
flags.f.symmetric = CacheStatus.N_A
flags.f.transitive = CacheStatus.N_A
return flags.asint
@tmp_sqlda_op(True)
[docs]def make_couplet(*args):
"""Factory wrapper to create a :class:`~.Couplet`."""
for arg in args:
if arg is _undef.Undef():
return _undef.make_or_raise_undef(2)
return Couplet(*args)
@tmp_sqlda_op(True)
[docs]def make_couplet_unchecked(*args):
"""Factory wrapper to create a :class:`~.Couplet` (unchecked version)."""
for arg in args:
if arg is _undef.Undef():
return _undef.make_or_raise_undef(2)
return Couplet(*args, direct_load=True)
@_functools.total_ordering
[docs]class Couplet(MathObject):
"""A :term:`couplet` containing a :term:`left component` and a :term:`right component`."""
_INIT_CACHE = _init_cache()
def __init__(self, left, right=None, direct_load=False):
"""
:param left: The :term:`left component` of the couplet, and the default value for the
:term:`right component` (see ``right``). If this argument is not a `MathObject`, it is
converted into an :class:`~.Atom`.
:param right: (Optional) The :term:`right component` of the couplet. If this argument is
not a `MathObject`, it is converted into an :class:`~.Atom`. If this argument is
missing, the value of ``left`` is used and a :term:`reflexive` couplet where
:term:`left` and :term:`right` are the same is created.
:param direct_load: (Optional) Set to ``True`` if you know that both ``left`` and ``right``
are instances of `MathObject`.
"""
super().__init__(self._INIT_CACHE)
if direct_load:
assert isinstance(left, MathObject)
self._left = left
if right is None:
self._right = self._left
else:
assert isinstance(right, MathObject)
self._right = right
else:
self._left = auto_convert(left)
if right is None:
self._right = self._left
else:
self._right = auto_convert(right)
self._hash = 0
# ----------------------------------------------------------------------------------------------
# Characteristics of the instance.
@property
def left(self) -> '( M )':
"""Read-only property; return the :term:`left component` of this instance."""
return self._left
@property
def right(self) -> '( M )':
"""Read-only property; return the :term:`right component` of this instance."""
return self._right
[docs] def get_ground_set(self) -> _structure.Structure:
"""Return the :term:`ground set` of the lowest-level algebra of ``self``.
"""
return _structure.CartesianProduct(
self.left.get_ground_set(), self.right.get_ground_set())
# ----------------------------------------------------------------------------------------------
# (Python-)Special functions.
[docs] def __eq__(self, other):
"""A value-based comparison for equality. Return ``True`` if type and both members match."""
return isinstance(other, Couplet) \
and (self.left == other.left) and (self.right == other.right)
[docs] def __ne__(self, other):
"""A value-based comparison for inequality. Return ``True`` if type or members don't match.
"""
return not isinstance(other, Couplet) \
or (self.left != other.left) or (self.right != other.right)
def __lt__(self, other):
"""A value-based comparison for less than. Return ``True`` if ``self < other``.
This implementation must be aligned with `__eq__`; an object must not be equal to and less
than another object at the same time.
:return Normally a `bool` (`True` if ``self`` is less than ``other``), or `NotImplemented`
if the types can't be compared.
"""
if not isinstance(other, MathObject):
return NotImplemented
if other.is_couplet:
return repr(self) < repr(other)
else:
return super()._less_than(other)
[docs] def __hash__(self):
"""Return a hash based on the value that is calculated on demand and cached."""
if not self._hash:
self._hash = _misc.get_hash(
'algebraixlib.mathobjects.couplet.Couplet', self.left, self.right)
return self._hash
[docs] def __repr__(self):
"""Return the instance's code representation."""
return 'Couplet(left={left}, right={right})'.format(
left=repr(self.left), right=repr(self.right))
[docs] def __str__(self):
"""Return the instance's string representation."""
return '({left}->{right})'.format(left=str(self.left), right=str(self.right))