# imports

from src.topupopt.problems.esipp.resource import ResourcePrice
from src.topupopt.problems.esipp.resource import are_prices_time_invariant

# *****************************************************************************
# *****************************************************************************

class TestResourcePrice:
        
    # *************************************************************************
    # *************************************************************************
    
    def test_resources_time_invariant(self):
        
        # single entry
        
        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
            }
        
        assert are_prices_time_invariant(resource_prices)
        
        # *********************************************************************
            
        # single assessment, two periods, same prices for both periods
        
        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
            (0, 0, 1): ResourcePrice(prices=1, volumes=None),
            (0, 0, 2): ResourcePrice(prices=1, volumes=None),
            (0, 1, 0): ResourcePrice(prices=1, volumes=None),
            (0, 1, 1): ResourcePrice(prices=1, volumes=None),
            (0, 1, 2): ResourcePrice(prices=1, volumes=None),
            }
        
        assert are_prices_time_invariant(resource_prices)
        
        # *********************************************************************
                
        # single assessment, two periods, same prices per period
        
        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
            (0, 0, 1): ResourcePrice(prices=1, volumes=None),
            (0, 0, 2): ResourcePrice(prices=1, volumes=None),
            (0, 1, 0): ResourcePrice(prices=2, volumes=None),
            (0, 1, 1): ResourcePrice(prices=2, volumes=None),
            (0, 1, 2): ResourcePrice(prices=2, volumes=None),
            }
        
        assert are_prices_time_invariant(resource_prices)
        
        # *********************************************************************
                
        # single assessment, two periods, different prices in a given period
        
        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
            (0, 0, 1): ResourcePrice(prices=1, volumes=None),
            (0, 0, 2): ResourcePrice(prices=1, volumes=None),
            (0, 1, 0): ResourcePrice(prices=2, volumes=None),
            (0, 1, 1): ResourcePrice(prices=2.5, volumes=None),
            (0, 1, 2): ResourcePrice(prices=2, volumes=None),
            }
        
        assert not are_prices_time_invariant(resource_prices)
        
        # *********************************************************************
        
    # *************************************************************************
    # *************************************************************************

    def test_resource_prices_reals(self):
        
        # 1) single segment, no volume limit, real input
        
        prices = 3
                
        volumes = None
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=False) == 1 
        
        assert res_p.number_segments(redo=True) == 1 
        
        assert not res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
            
        # 2) single segment, volume limit, real input
        
        prices = 3
                
        volumes = 1.5
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=False) == 1 
        
        assert res_p.number_segments(redo=True) == 1 
        
        assert res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
            
    # *************************************************************************
    # *************************************************************************

    def test_equivalence_single(self):
        
        # *********************************************************************
        # *********************************************************************
        
        # single segment
    
        # *********************************************************************
        # *********************************************************************
        
        # no volume limit
        
        # single segment, no volume limit, different formats
        # prices and volumes match =  True
        
        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[volumes])
        assert res_p1.is_equivalent(res_p2)
        assert res_p2.is_equivalent(res_p1)
    
        # *********************************************************************
        
        # single segment, no volume limit, different formats
        # prices do not match = False
        
        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices+1], volumes=[volumes])
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        
        # single segment, no volume limit, same format
        # prices and volumes match =  True
        
        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1.is_equivalent(res_p2)
        assert res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        
        # single segment, no volume limit, same format
        # prices do not match = False
        
        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices+1, volumes=volumes)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        # *********************************************************************
        
        # with volume limits
        
        # single segment, volume limit, different formats
        # prices and volumes match =  True
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[volumes])
        assert res_p1.is_equivalent(res_p2)
        assert res_p2.is_equivalent(res_p1)
    
        # *********************************************************************
        
        # single segment, volume limit, different formats: False
        # prices do not match = False
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices+1], volumes=[volumes])
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        
        # single segment, volume limit, same format
        # prices and volumes match =  True
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1.is_equivalent(res_p2)
        assert res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        
        # single segment, volume limit, same format: False
        # prices do not match = False
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices+1, volumes=volumes)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
    
        # *********************************************************************
        
        # single segment, volume limit, different formats
        # volumes do not match = False
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[volumes+1])
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        
        # single segment, volume limit, same format
        # volumes do not match = False
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes+1)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
    
        # *********************************************************************
        
        # single segment, volume limit, different formats
        # volumes do not match = False
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[None])
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        
        # single segment, volume limit, same format
        # volumes do not match = False
        
        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=None)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        # *********************************************************************
        
    # *************************************************************************
    # *************************************************************************

    def test_equivalence_multiple_segments(self):
        
        # *********************************************************************
        # *********************************************************************
        
        # multiple segments
        
        # *********************************************************************
        # *********************************************************************
        
        # no volume limit
        
        # two segments, no volume limit, same format
        # prices and volumes match =  True
        
        prices = [1,3]
        volumes = [1,None]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1.is_equivalent(res_p2)
        assert res_p2.is_equivalent(res_p1)
        
        # two segments, no volume limit, same format
        # prices do not match = False
        
        prices = [1,3]
        volumes = [1,None]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [2,3]
        volumes = [1,None]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        
        # with volume limits
        
        # two segments segment, volume limit, same format
        # prices and volumes match =  True
        
        prices = [1,3]
        volumes = [1,3]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1.is_equivalent(res_p2)
        assert res_p2.is_equivalent(res_p1)
        
        # two segments, volume limit, same format: False
        # prices do not match = False
        
        prices = [1,3]
        volumes = [1,4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1,4]
        volumes = [1,4]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
    
        # *********************************************************************
        
        # volumes do not match
        
        # single segment, volume limit, same format
        # volumes do not match = False
        
        prices = [1,3]
        volumes = [1,4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1,3]
        volumes = [1,5]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # single segment, volume limit, same format
        # volumes do not match = False
        
        prices = [1,3]
        volumes = [1,4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1,3]
        volumes = [1,None]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        # *********************************************************************  
        
        # different number of segments
        
        prices = [1,3]
        volumes = [1,4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1,3,5]
        volumes = [1,4,None]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1.is_equivalent(res_p2)
        assert not res_p2.is_equivalent(res_p1)
        
        # *********************************************************************
        # *********************************************************************        
        
    # *************************************************************************
    # *************************************************************************

    def test_resource_prices_lists(self):
        
        # *********************************************************************
        
        # aspects that were tested:
        # i) number of segments (1, multiple or none)
        # ii) price variations (increasing, decreasing and stable)
        # iii) volume limits
        
        # *********************************************************************
        
        # 1) multiple segments, prices increase, volume limits
        # 2) multiple segments, prices decrease, volume limits
        # 3) multiple segments, prices are stable, volume limits
        # 4) multiple segments, prices increase, no volume limit
        # 5) multiple segments, prices decrease, no volume limit
        # 6) multiple segments, prices are stable, no volume limit
        # 7) one segment, prices are stable, volume limits
        # 8) one segment, prices are stable, no volume limit
        
        # *********************************************************************
        
        # 1) multiple segments, prices increase, volume limits
        
        prices = [1,2,3]
                
        volumes = [2,1,3]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=True) == 3
        
        assert res_p.number_segments(redo=False) == 3
        
        assert res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert not res_p.price_monotonically_decreasing_with_volume()
        
        assert not res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # 2) multiple segments, prices decrease, volume limits
        
        prices = [3,2,1]
                
        volumes = [2,1,3]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=False) == 3
        
        assert res_p.is_volume_capped()
        
        assert not res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert not res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # 3) multiple segments, prices are stable, volume limits
        
        prices = [2,2,2]
                
        volumes = [2,1,3]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=True) == 3
        
        assert res_p.number_segments(redo=False) == 3
        
        assert res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # 4) multiple segments, prices increase, no volume limit
        
        prices = [1,2,3]
                
        volumes = [2,1,None]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=True) == 3
        
        assert res_p.number_segments(redo=False) == 3
        
        assert not res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert not res_p.price_monotonically_decreasing_with_volume()
        
        assert not res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # 5) multiple segments, prices decrease, no volume limit
        
        prices = [2,2,2]
                
        volumes = [2,1,None]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=True) == 3
        
        assert res_p.number_segments(redo=False) == 3
        
        assert not res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # 6) multiple segments, prices are stable, no volume limit
        
        prices = [2,2,2]
                
        volumes = [2,1,None]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=True) == 3
        
        assert res_p.number_segments(redo=False) == 3
        
        assert not res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # 7) one segment, prices are stable, volume limits
        
        prices = [2]
                
        volumes = [2]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=True) == 1
        
        assert res_p.number_segments(redo=False) == 1
        
        assert res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # 8) one segment, prices are stable, no volume limit
        
        prices = [3]
                
        volumes = [None]
        
        res_p = ResourcePrice(prices=prices, volumes=volumes)
        
        assert res_p.number_segments(redo=True) == 1
        
        assert res_p.number_segments(redo=False) == 1
        
        assert not res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
        
        res_p = ResourcePrice(prices=prices[0], volumes=volumes[0])
        
        assert res_p.number_segments(redo=True) == 1
        
        assert res_p.number_segments(redo=False) == 1
        
        assert not res_p.is_volume_capped()
        
        assert res_p.price_monotonically_increasing_with_volume()
        
        assert res_p.price_monotonically_decreasing_with_volume()
        
        assert res_p.is_volume_invariant()
        
        # *********************************************************************
        
        # errors
        
        # *********************************************************************
        
        # create object without prices
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=None,
                              volumes=volumes)
        except TypeError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with negative prices in lists
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,-3,2],
                              volumes=[3,4,5])
        except ValueError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object where an intermediate segment has no volume limit
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,4,2],
                              volumes=[3,None,5])
        except ValueError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with negative volumes in lists
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,3,2],
                              volumes=[4,-1,2])
        except ValueError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with non-numeric prices in lists
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,'4',2],
                              volumes=[3,4,5])
        except TypeError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with non-numeric volumes in lists
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,3,2],
                              volumes=[4,'3',2])
        except TypeError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with mismatched price and volume lists
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,3,2],
                              volumes=[5,7])
        except ValueError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with a price list as an input and an unsupported type
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,3,2],
                              volumes='hello')
        except TypeError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with negative prices in lists (no volumes are provided)
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,3,-2],
                              volumes=None)
        except TypeError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with non-numeric prices in lists (no volumes are provided)
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=[7,3,'a'],
                              volumes=None)
        except TypeError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with non-numeric prices in lists (no volumes are provided)
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=5,
                              volumes=[7,3,4])
        except TypeError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
        # create object with negative prices
    
        error_triggered = False
        
        try:
            _ = ResourcePrice(prices=-3,
                              volumes=None)
        except ValueError:
            error_triggered = True
        assert error_triggered
        
        # *********************************************************************
        
# *****************************************************************************
# *****************************************************************************