#  _________________________________________________________________________
#
#  Coopr: A COmmon Optimization Python Repository
#  Copyright (c) 2008 Sandia Corporation.
#  This software is distributed under the BSD License.
#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
#  the U.S. Government retains certain rights in this software.
#  For more information, see the Coopr README.txt file.
#  _________________________________________________________________________

__all__ = ['_VirtualSet', '_AnySet', 'RealSet', 'IntegerSet', 'BooleanSet', 'Any', 'Reals', 'PositiveReals', 'NonPositiveReals', 'NegativeReals', 'NonNegativeReals', 'PercentFraction', 'Integers', 'PositiveIntegers', 'NonPositiveIntegers', 'NegativeIntegers', 'NonNegativeIntegers', 'Boolean', 'Binary']

import pyutilib.plugin.core
import sets
from coopr.pyomo.base.plugin import *


_virtual_sets = []

class _VirtualSet(sets._SetContainer, pyutilib.plugin.core.Plugin):
    """
    A set that does not contain elements, but instead overrides the
       __contains__ method to define set membership.
    """

    pyutilib.plugin.core.implements(IPyomoSet)

    def __init__(self,*args,**kwds):
        if "name" in kwds:
            pyutilib.plugin.core.Plugin.__init__(self,name=kwds["name"])
        else:
            pyutilib.plugin.core.Plugin.__init__(self)
        self._class_override=False
        sets._SetContainer.__init__(self,*args,**kwds)
        self.virtual=True
        #
        global _virtual_sets
        _virtual_sets.append(self)


class _AnySet(_VirtualSet):
    """A virutal set that allows any value"""

    def __init__(self,*args,**kwds):
        """Constructor"""
        _VirtualSet.__init__(self,*args,**kwds)

    def __contains__(self, element):
        if element is None:
            return False
        return True


class RealSet(_VirtualSet):
    """A virutal set that represents real values"""

    def __init__(self,*args,**kwds):
        """Constructor"""
        if not 'bounds' in kwds:
            kwds['bounds'] = (None,None)
        _VirtualSet.__init__(self,*args,**kwds)

    
    def __contains__(self, element):
        """Report whether an element is an 'int', 'long' or 'float' value.

        (Called in response to the expression 'element in self'.)
        """
        return _VirtualSet.__contains__(self,element) and (type(element) in (float,int,long))


class IntegerSet(_VirtualSet):
    """A set that represents integer values"""

    def __init__(self,*args,**kwds):
        """Constructor"""
        if not 'bounds' in kwds:
            kwds['bounds'] = (None,None)
        _VirtualSet.__init__(self,*args,**kwds)

    def __contains__(self, element):
        """Report whether an element is an 'int'.

        (Called in response to the expression 'element in self'.)
        """
        return _VirtualSet.__contains__(self,element) and (type(element) in (int,))


class BooleanSet(_VirtualSet):
    """A set that represents boolean values"""

    def __init__(self,*args,**kwds):
        """Construct the set of booleans, which contains no explicit values"""
        kwds['bounds'] = (0,1)
        _VirtualSet.__init__(self,*args,**kwds)

    def __contains__(self, element):
        """Report whether an element is a boolean.

        (Called in response to the expression 'element in self'.)
        """
        return _VirtualSet.__contains__(self,element) and (type(element) in (bool,int)) and (element in (0,1,True,False))


#
# Concrete instances of the standard sets
#
Any=_AnySet(name="Any", doc="A set of any data")

Reals=RealSet(name="Reals", doc="A set of real values")
def validate_PositiveValues(x,model):
    return x>0
PositiveReals=RealSet(name="PositiveReals",validate=validate_PositiveValues, doc="A set of positive real values", bounds=(0,None))
def validate_NonPositiveValues(x,model):
    return x<=0
NonPositiveReals=RealSet(name="NonPositiveReals",validate=validate_NonPositiveValues, doc="A set of non-positive real values", bounds=(None,0))
def validate_NegativeValues(x,model):
    return x<0
NegativeReals=RealSet(name="NegativeReals",validate=validate_NegativeValues, doc="A set of negative real values", bounds=(None,0))
def validate_NonNegativeValues(x,model):
    return x>=0
NonNegativeReals=RealSet(name="NonNegativeReals",validate=validate_NonNegativeValues, doc="A set of non-negative real values", bounds=(0,None))

def validate_PercentFraction(x,model):
    return x>=0 and x<=1.0
PercentFraction=RealSet(name="PercentFraction",validate=validate_PercentFraction, doc="A set of real values in the interval [0,1]", bounds=(0.0,1.0))

Integers=IntegerSet(name="Integers", doc="A set of integer values")
PositiveIntegers=IntegerSet(name="PositiveIntegers",validate=validate_PositiveValues, doc="A set of positive integer values", bounds=(1,None))
NonPositiveIntegers=IntegerSet(name="NonPositiveIntegers",validate=validate_NonPositiveValues, doc="A set of non-positive integer values", bounds=(None,0))
NegativeIntegers=IntegerSet(name="NegativeIntegers",validate=validate_NegativeValues, doc="A set of negative integer values", bounds=(None,-1))
NonNegativeIntegers=IntegerSet(name="NonNegativeIntegers",validate=validate_NonNegativeValues, doc="A set of non-negative integer values", bounds=(0,None))

Boolean=BooleanSet(name="Boolean", doc="A set of boolean values")
Binary=BooleanSet(name="Binary", doc="A set of boolean values")

