# Path: do_mpc/tools/indexedproperty.py
# class IndexedProperty(object):
#     """
#     Based on the python implementation of the regular property() decorator.
#     See for example: https://docs.python.org/3/howto/descriptor.html
# 
#     The main tweak is __get__, where the above mentionned implementation
#     directly calls the fget function. We instead return the class instance itself,
#     where the parent class is now added to the class dict.
#     Since the call is followed by brackets, immediatly the __getitem__ or __setitem__
#     methods are invoked. These methods are lacking the parent class but it now
#     exists in the scope of the property instance.
#     We can therefore call fget or fset with the parent class.
#     """
# 
#     def __init__(self, fget=None, fset=None, doc=None):
#         self.fget = fget
#         self.fset = fset
#         if doc is None and fget is not None:
#             doc = fget.__doc__
#         self.__doc__ = doc
# 
#     def __get__(self, obj, objtype=None):
#         self.obj = obj
#         return self
# 
#     def __getitem__(self,ind):
#         return self.fget(self.obj,ind)
# 
#     def __setitem__(self,ind, val):
#         return self.fset(self.obj,ind,val)
# 
#     def getter(self, fget):
#         @wraps(fset)
#         def wrapper(obj,*args, **kwargs):
#             return fget(obj, *args, **kwargs)
#         self.fget = wrapper
#         return type(self)(wrapper, self.fset, self.__doc__)
# 
#     def setter(self, fset):
#         def wrapper(obj, *args, **kwargs):
#             return fset(obj, *args, **kwargs)
#         self.fset = wrapper
#         return type(self)(self.fget, wrapper, self.__doc__)


import numpy as np
import pdb
import itertools
import time
import warnings
from casadi import *
from casadi.tools import *
from do_mpc.tools.indexedproperty import IndexedProperty
    def bounds(self, ind, val):
        """See Docstring for bounds getter method"""

        assert isinstance(ind, tuple), 'Power index must include bound_type, var_type, var_name (as a tuple).'
        assert len(ind)>=3, 'Power index must include bound_type, var_type, var_name (as a tuple).'
        bound_type = ind[0]
        var_type   = ind[1]
        var_name   = ind[2:]

        err_msg = 'Invalid power index {} for bound_type. Must be from (lower, upper).'
        assert bound_type in ('lower', 'upper'), err_msg.format(bound_type)
        err_msg = 'Invalid power index {} for var_type. Must be from (_x, _u, _z, _p_est).'
        assert var_type in ('_x', '_u', '_z', '_p_est'), err_msg.format(var_type)

        if bound_type == 'lower':
            query = '{var_type}_{bound_type}'.format(var_type=var_type, bound_type='lb')
        elif bound_type == 'upper':
            query = '{var_type}_{bound_type}'.format(var_type=var_type, bound_type='ub')
        # query results string e.g. _x_lb, _x_ub, _u_lb, u_ub ....

        # Get the desired struct:
        var_struct = getattr(self, query)

        err_msg = 'Calling .bounds with {} is not valid. Possible keys are {}.'
        assert (var_name[0] if isinstance(var_name, tuple) else var_name) in var_struct.keys(), msg.format(ind, var_struct.keys())

        # Set value on struct:
        var_struct[var_name] = val



# Path: do_mpc/tools/indexedproperty.py
# class IndexedProperty(object):
#     """
#     Based on the python implementation of the regular property() decorator.
#     See for example: https://docs.python.org/3/howto/descriptor.html
# 
#     The main tweak is __get__, where the above mentionned implementation
#     directly calls the fget function. We instead return the class instance itself,
#     where the parent class is now added to the class dict.
#     Since the call is followed by brackets, immediatly the __getitem__ or __setitem__
#     methods are invoked. These methods are lacking the parent class but it now
#     exists in the scope of the property instance.
#     We can therefore call fget or fset with the parent class.
#     """
# 
#     def __init__(self, fget=None, fset=None, doc=None):
#         self.fget = fget
#         self.fset = fset
#         if doc is None and fget is not None:
#             doc = fget.__doc__
#         self.__doc__ = doc
# 
#     def __get__(self, obj, objtype=None):
#         self.obj = obj
#         return self
# 
#     def __getitem__(self,ind):
#         return self.fget(self.obj,ind)
# 
#     def __setitem__(self,ind, val):
#         return self.fset(self.obj,ind,val)
# 
#     def getter(self, fget):
#         @wraps(fset)
#         def wrapper(obj,*args, **kwargs):
#             return fget(obj, *args, **kwargs)
#         self.fget = wrapper
#         return type(self)(wrapper, self.fset, self.__doc__)
# 
#     def setter(self, fset):
#         def wrapper(obj, *args, **kwargs):
#             return fset(obj, *args, **kwargs)
#         self.fset = wrapper
#         return type(self)(self.fget, wrapper, self.__doc__)


# Path: do_mpc/optimizer.py
import numpy as np
import pdb
import itertools
import time
import warnings
from casadi import *
from casadi.tools import *
from do_mpc.tools.indexedproperty import IndexedProperty

    def bounds(self, ind, val):
        """See Docstring for bounds getter method"""

        assert isinstance(ind, tuple), 'Power index must include bound_type, var_type, var_name (as a tuple).'
        assert len(ind)>=3, 'Power index must include bound_type, var_type, var_name (as a tuple).'
        bound_type = ind[0]
        var_type   = ind[1]
        var_name   = ind[2:]

        err_msg = 'Invalid power index {} for bound_type. Must be from (lower, upper).'
        assert bound_type in ('lower', 'upper'), err_msg.format(bound_type)
        err_msg = 'Invalid power index {} for var_type. Must be from (_x, _u, _z, _p_est).'
        assert var_type in ('_x', '_u', '_z', '_p_est'), err_msg.format(var_type)

        if bound_type == 'lower':
            query = '{var_type}_{bound_type}'.format(var_type=var_type, bound_type='lb')
        elif bound_type == 'upper':
            query = '{var_type}_{bound_type}'.format(var_type=var_type, bound_type='ub')
        # query results string e.g. _x_lb, _x_ub, _u_lb, u_ub ....

        # Get the desired struct:
        var_struct = getattr(self, query)

        err_msg = 'Calling .bounds with {} is not valid. Possible keys are {}.'
        assert (var_name[0] if isinstance(var_name, tuple) else var_name) in var_struct.keys(), msg.format(ind, var_struct.keys())

        # Set value on struct:
        var_struct[var_name] = val



    @IndexedProperty