Conflict Resolution using Maximum or Minimum Values
===================================================

The `zope.minmax.AbstractValue` class provides a super class which can
be subclassed to store arbitrary *homogeneous* values in a persistent
storage and apply different conflict resolution policies.

The subclasses defined here are resolving the conflicts using always
either the maximum or the minimum of the conflicting values.

Maximum
-------

The `zope.minmax.Maximum` class always resolves conflicts favoring the
maximum value.  Let's instantiate one object and verify that it
satisfies the interface.

    >>> import zope.minmax
    >>> import zope.interface.verify
    >>> max_favored = zope.minmax.Maximum()
    >>> zope.interface.verify.verifyObject(
    ...     zope.minmax.interfaces.IAbstractValue, max_favored)
    True

We can confirm that the initial value is zero.

    >>> bool(max_favored)
    False
    >>> print(max_favored.value)
    None

Now, we can store a new value in the object.

    >>> max_favored.value = 11
    >>> print(max_favored.value)
    11
    >>> bool(max_favored)
    True

Or we can use the methods.

    >>> max_favored.__setstate__(4532)
    >>> max_favored.__getstate__()
    4532
    >>> print(max_favored.value)
    4532
    >>> bool(max_favored)
    True

Do notice that using a direct assignment to the value attribute is a
more natural use.

Minimum
-------

The `zope.minmax.Minimum` class always resolves conflicts favoring the
minimum value.  Again, we instantiate an object and verify that it
satisfies the interface.

    >>> min_favored = zope.minmax.Minimum()
    >>> zope.interface.verify.verifyObject(
    ...     zope.minmax.interfaces.IAbstractValue, min_favored)
    True

We need a confirmation that the initial value is zero.

    >>> bool(min_favored)
    False
    >>> print(min_favored.value)
    None

Let's populate this one too.

    >>> min_favored.value = 22
    >>> print(min_favored.value)
    22
    >>> bool(min_favored)
    True

Or we can use the methods, again.

    >>> min_favored.__setstate__(8796)
    >>> min_favored.__getstate__()
    8796
    >>> print(min_favored.value)
    8796
    >>> bool(min_favored)
    True

Please, notice, again, that using a direct assignment to the value
attribute is a more natural use.

Conflict Resolution
-------------------

Now, we need to exercise the conflict resolution interface.
First for the `zope.minmax.Maximum`:

Let's try differing values larger than the old value.

    >>> max_favored._p_resolveConflict(max_favored.value, 4536, 4535)
    4536
    >>> max_favored._p_resolveConflict(max_favored.value, 4573, 4574)
    4574

What happens when all the values are equal, including the old.

    >>> max_favored._p_resolveConflict(max_favored.value, 4532, 4532)
    4532

Notice that when the old value is larger than both the committed and
new, it is still disregarded.

    >>> max_favored._p_resolveConflict(max_favored.value, 4531, 4530)
    4531

Now, the `zope.minmax.Minimum`:

Let's try differing values smaller than the old value.

    >>> min_favored._p_resolveConflict(min_favored.value, 8792, 8791)
    8791
    >>> min_favored._p_resolveConflict(min_favored.value, 8785, 8786)
    8785

What happens when all the values are equal, including the old.

    >>> min_favored._p_resolveConflict(min_favored.value, 8796, 8796)
    8796

Notice that when the old value is smaller than both the committed and
new, it is still disregarded.

    >>> min_favored._p_resolveConflict(min_favored.value, 8798, 8799)
    8798

How about an example that is not numerical?

    >>> max_word = zope.minmax.Maximum('joy')
    >>> print(max_word.value)
    joy
    >>> bool(max_word)
    True
    >>> max_word._p_resolveConflict(max_word.value, 'happiness', 'exuberance')
    'happiness'
    >>> max_word._p_resolveConflict(max_word.value, 'exuberance', 'happiness')
    'happiness'
    >>> min_word = zope.minmax.Minimum(max_word.value)
    >>> print(min_word.value)
    joy
    >>> bool(min_word)
    True
    >>> min_word._p_resolveConflict(min_word.value, 'happiness', 'exuberance')
    'exuberance'
    >>> min_word._p_resolveConflict(min_word.value, 'exuberance', 'happiness')
    'exuberance'

As indicated, we don't need to have numbers, just *homegeneous* items.
The homogeneous values are not really inherently required.  However, it
makes no sense to apply min() or max() on, say, one number and one
string.  Simply, the ordering relations do not work at all on
heterogeneous values.
