def test_floats_with_step(self): interval = FloatInterval((0, 0.5), step=0.5) assert interval.lower == 0 assert interval.upper == 0.5 assert not interval.lower_inc assert not interval.upper_inc assert interval.step
def test_string_as_constructor_param(self): with raises(TypeError) as e: FloatInterval('(0.2, 0.5)') assert str(e.value) == ( 'First argument should be a list or tuple. If you wish to ' 'initialize an interval from string, use from_string factory ' 'method.' )
def __init__(self, _type, const): super(Constnode, self).__init__(_type) self.const = const val = float(const) if val == int(val) and not const.endswith('.0'): self.I = IntInterval([int(val), int(val)]) #TODO: float else: self.I = FloatInterval([val, val]) #TODO: float
def __init__(self, var_type, interval=None): super(Var, self).__init__() self.var_type = var_type self.interval = {} if var_type == 'int': if interval == None: self.interval['int'] = IntInterval.all() else: self.interval['int'] = IntInterval(interval) elif var_type == 'float': if interval == None: self.interval['float'] = FloatInterval.all() else: self.interval['float'] = FloatInterval(interval)
class TestComparisonOperators(object): @mark.parametrize(('comparison', 'result'), ( (IntInterval([1, 3]) == IntInterval([1, 3]), True), (IntInterval([1, 3]) == IntInterval([1, 4]), False), (IntInterval([inf, inf]) == inf, True), (IntInterval([3, 3]) == 3, True), (IntInterval([3, 3]) == 5, False), (IntInterval('(,)') == None, False) )) def test_eq_operator(self, comparison, result): assert comparison is result @mark.parametrize(('comparison', 'result'), ( (IntInterval([1, 3]) != IntInterval([1, 3]), False), (IntInterval([1, 3]) != IntInterval([1, 4]), True), (IntInterval([inf, inf]) != inf, False), (IntInterval([3, 3]) != 3, False), (IntInterval([3, 3]) != 5, True), (IntInterval('(,)') != None, True) )) def test_ne_operator(self, comparison, result): assert comparison is result @mark.parametrize(('comparison', 'result'), ( (IntInterval([1, 3]) > IntInterval([0, 2]), True), (IntInterval((1, 4)) > 1, False), (IntInterval((1, 6)) > [1, 6], False), (IntInterval((1, 6)) > 0, True) )) def test_gt_operator(self, comparison, result): assert comparison is result @mark.parametrize(('comparison', 'result'), ( (IntInterval([1, 3]) >= IntInterval([0, 2]), True), (IntInterval((1, 4)) >= 1, False), (IntInterval((1, 6)) >= [1, 6], False), (IntInterval((1, 6)) >= 0, True) )) def test_ge_operator(self, comparison, result): assert comparison is result @mark.parametrize(('comparison', 'result'), ( (IntInterval([0, 2]) < IntInterval([1, 3]), True), (IntInterval([2, 3]) < IntInterval([2, 3]), False), (IntInterval([2, 5]) < 6, True), (IntInterval([2, 5]) < 5, False), (IntInterval([2, 5]) < inf, True) )) def test_lt_operator(self, comparison, result): assert comparison is result @mark.parametrize(('comparison', 'result'), ( (IntInterval([0, 2]) <= IntInterval([1, 3]), True), (IntInterval([1, 3]) <= IntInterval([1, 3]), True), (IntInterval([1, 7]) <= 8, True), (IntInterval([1, 6]) <= 5, False), (IntInterval([1, 5]) <= inf, True) )) def test_le_operator(self, comparison, result): assert comparison is result def test_integer_comparison(self): assert IntInterval([2, 2]) <= 3 assert IntInterval([1, 3]) >= 0 assert IntInterval([2, 2]) == 2 assert IntInterval([2, 2]) != 3 @mark.parametrize('value', ( IntInterval([0, 2]), 1, (-1, 1), )) def test_contains_operator_for_inclusive_interval(self, value): assert value in IntInterval([-1, 2]) @mark.parametrize('value', ( IntInterval([0, 2]), 2, '[-1, 1]', )) def test_contains_operator_for_non_inclusive_interval(self, value): assert value not in IntInterval((-1, 2)) @mark.parametrize(('interval1', 'interval2', 'expected'), ( (IntInterval((0, 2)), IntInterval((0, 2)), True), (IntInterval([0, 2]), IntInterval([0, 2]), True), (IntInterval('[0, 2)'), IntInterval('[0, 2)'), True), (IntInterval('(0, 2]'), IntInterval('(0, 2]'), True), (IntInterval((0, 2)), IntInterval((1, 2)), False), (IntInterval((0, 2)), IntInterval((0, 1)), False), (IntInterval((0, 2)), IntInterval([0, 1]), False), (IntInterval((0, 2)), FloatInterval((0, 1)), False), )) def test_hash_operator_with_interval_attributes(self, interval1, interval2, expected): actual = (interval1.__hash__() == interval2.__hash__()) assert actual == expected @mark.parametrize(('contains_check', 'expected'), ( (IntInterval([0, 2]) in {IntInterval([0, 2]): ''}, True), (IntInterval([0, 2]) in {IntInterval((0, 2)): ''}, False), (IntInterval([0, 2]) in set([IntInterval([0, 2])]), True), )) def test_hash_operator_with_collections(self, contains_check, expected): assert contains_check is expected
class TestIntervalProperties(object): @mark.parametrize(('number_range', 'length'), ( ([1, 4], 3), ([-1, 1], 2), ((-inf, inf), inf), ((1, inf), inf), )) def test_length(self, number_range, length): assert IntInterval(number_range).length == length @mark.parametrize(('number_range', 'radius'), ( ([1, 4], 1.5), ([-1, 1], 1.0), ([-4, -1], 1.5), ((-inf, inf), inf), ((1, inf), inf), )) def test_radius(self, number_range, radius): assert IntInterval(number_range).radius == radius @mark.parametrize(('number_range', 'centre'), ( ([1, 4], 2.5), ([-1, 1], 0), ([-4, -1], -2.5), ((1, inf), inf), )) def test_centre(self, number_range, centre): assert IntInterval(number_range).centre == centre @mark.parametrize(('number_range', 'is_open'), (((2, 3), True), ('(2, 5)', True), ('[3, 4)', False), ('(4, 5]', False), ('3 - 4', False), ([4, 5], False), ('[4, 5]', False))) def test_open(self, number_range, is_open): assert IntInterval(number_range).open == is_open @mark.parametrize( ('number_range', 'is_closed'), (((2, 3), False), ('(2, 5)', False), ('[3, 4)', False), ('(4, 5]', False), ('3 - 4', True), ([4, 5], True), ('[4, 5]', True))) def test_closed(self, number_range, is_closed): assert IntInterval(number_range).closed == is_closed @mark.parametrize(('number_range', 'empty'), ( ((2, 3), True), ([2, 3], False), ([2, 2], False), ((2, 2), True), ('[2, 2)', True), ('(2, 2]', True), ('[2, 3)', False), ((2, 10), False), )) def test_empty(self, number_range, empty): assert IntInterval(number_range).empty == empty @mark.parametrize(('number_range', 'degenerate'), ( ((2, 4), False), ('(2, 2)', True), ('[0, 0)', True), )) def test_degenerate(self, number_range, degenerate): assert IntInterval(number_range).degenerate == degenerate @mark.parametrize( ('interval', 'discrete'), ((IntInterval((2, 3)), True), (IntInterval(5), True), (FloatInterval(3.5), False), (DecimalInterval(Decimal('2.4')), False), (DateTimeInterval(datetime(2002, 1, 1)), False), (DateInterval(date(2002, 1, 1)), True))) def test_discrete(self, interval, discrete): assert interval.discrete == discrete
def test_floats(self): interval = FloatInterval((0.2, 0.5)) assert interval.lower == 0.2 assert interval.upper == 0.5 assert not interval.lower_inc assert not interval.upper_inc
def test_invalid_argument(self): with raises(IllegalArgument) as e: FloatInterval((0, 0)) assert ( 'The bounds may be equal only if at least one of the bounds is ' 'closed.') in str(e)
def test_step_rounding(self): interval = FloatInterval((0.2, 0.8), step=0.5) assert interval.lower == 0 assert interval.upper == 1
def get_evaluate(self, op, arg1, arg2): #需要判断死分支,所以必须是类内函数 if type(op) in [Rangenode, Ftrangenode]: I1 = arg1.I I2 = op.I #Rangenode有I、且I一定不是None #而Ftrangenode一开始的I没有初始化,要等到做完Future Resolution以后才有 elif type(op) == Opnode: #op,可能是一元或二元的 I1 = arg1.I if arg2 != None: I2 = arg2.I else: I2 = None if type(arg1) == Varnode\ and (arg1.var.endswith('_t') or arg1.var.endswith('_f'))\ and arg1.Belong != op.Belong: # 如果arg1要参与其他分量节点的区间运算(即出循环)、且它是_t/_f,则要让它的区间收敛 # print arg1.var, I1 if I1 != None: #有可能它是空集;这说明这是一个不可能分支 pass # TODO:应该在这里让区间收敛。但是要解决问题太多了 # 问题1、怎么确定收敛结果?因为在分支语句中可能是跟变量比较,收敛的结果可能是一个区间而不是一个点! # 问题2、就算能让它正确收敛,因为区间变小了,它也无法再影响传递出去的e了。 # 问题3、就算一路上的节点成功更新了,还有一个问题:无法区分PHI语句的from是循环初值还是if分支 # 把循环初值误判成了if分支还造成了其他的问题:不可能分支的误判 if arg2 != None and type(arg2) == Varnode\ and (arg2.var.endswith('_t') or arg2.var.endswith('_f'))\ and arg2.Belong != op.Belong: # 如果arg2要参与其他分量节点的区间运算(即出循环)、且它是_t/_f,则要让它的区间收敛 if I2 != None: #有可能它是空集;这说明这是一个不可能分支 pass # TODO:同上 # else: # pass if type(op) in [Rangenode, Ftrangenode]: #求交集 if I1 == None: e = None #这里I1为None代表它是空集,而不是未初始化 elif I2 == None: e = copy.deepcopy(I1) else: try: e = I1 & I2 except Exception as ex: e = 'Empty' #没有交集,则取空集 else: pass finally: pass # 以下都是opnode。先是一元运算 elif op.op == '(float)': e = FloatInterval([I1.lower, I1.upper]) e.upper_inc = I1.upper_inc e.lower_inc = I1.lower_inc elif op.op == '(int)': if math.isinf(I1.lower): lower = -inf else: val = round(I1.lower) if abs(val - int(val)) <= 1e-6: lower = int(val) #浮点误差 else: lower = int(I1.lower) if math.isinf(I1.upper): upper = inf else: val = round(I1.upper) if abs(val - int(val)) <= 1e-6: upper = int(val) #浮点误差 else: upper = int(I1.upper) e = IntInterval([lower, upper]) e.upper_inc = I1.upper_inc e.lower_inc = I1.lower_inc # TODO:整数化以后开闭可能变化! elif op.op == '=': e = copy.deepcopy(I1) # 再是二元运算 elif op.op == 'PHI': # 如果一个arg是同一个分量的,而另一个不是:这是在一个循环内部 if (arg1.Belong == op.Belong and arg2.Belong != op.Belong)\ or (arg2.Belong == op.Belong and arg1.Belong != op.Belong): # if arg1.Belong == op.Belong or arg2.Belong == op.Belong: if arg1.Belong == op.Belong: inner = arg1 outer = arg2 else: inner = arg2 outer = arg1 # 第一次进循环,内部还是None,直接取外部;以后内部不是None了,就取内部 if inner.I == None: e = copy.deepcopy(outer.I) else: e = copy.deepcopy(inner.I) else: #两个都不是另外一个分量的:这是一个if分支,应该取并集 # 注意:还可能两者都是Inner的,见样例t9那个很复杂的分量。我认为这也算一个if分支? # 注意,只对if分支先排除不可能分支,因为对于循环存在误判的可能: # 循环一开始范围只有初值,此时因为交集为空而被误判为不可能分支 # 但是循环几次以后范围扩大了,此时又变成可能分支了 if op.phi_from_block[arg1.var] in self.last_Unreached: e = copy.deepcopy(arg2.I) elif op.phi_from_block[arg2.var] in self.last_Unreached: e = copy.deepcopy(arg1.I) # 然后取并集 elif arg1.I == None: e = copy.deepcopy(arg2.I) elif arg2.I == None: e = copy.deepcopy(arg1.I) else: inner = arg1 #还是用inner和outer来标记吧 outer = arg2 e = copy.deepcopy(inner.I) if (outer.I.lower<e.lower) or\ (outer.I.lower == e.lower and outer.I.lower_inc==False): #就算一个是(5一个是[6,或者一个是(6一个是[6,改一下也不会错 e.lower = outer.I.lower e.lower_inc = outer.I.lower_inc # 开闭也要修改 if (outer.I.upper>e.upper) or\ (outer.I.upper == e.upper and outer.I.upper_inc==True): #就算一个是(5一个是[6,或者一个是(6一个是[6,改一下也不会错 e.upper = outer.I.upper e.upper_inc = outer.I.upper_inc # 开闭也要修改 else: #op:None参与时直接返回None if I1 == None or I2 == None: e = None elif op.op == '+': e = I1 + I2 e.lower_inc = I1.lower_inc and I2.lower_inc #只要有一个开,结果就是开 e.upper_inc = I1.upper_inc and I2.upper_inc #只要有一个开,结果就是开 #TODO,对于整数,(1,3)+(3,5)=(4,8)是错的!不过允许有一点误差 elif op.op == '-': # e = I1 - I2 这样写可能会出错 e = copy.deepcopy(I1) e.lower += -I2.upper e.upper += -I2.lower #- -inf不等于+ inf,所以必须这么写…… e.lower_inc = I1.lower_inc and I2.upper_inc #只要有一个开,结果就是开 e.upper_inc = I1.upper_inc and I2.lower_inc #只要有一个开,结果就是开 #TODO,对于整数,(1,3)+(3,5)=(4,8)是错的!不过允许有一点误差 elif op.op == '*': #TODO: 开闭的问题!!!现在默认是闭! L = [ mul(I1.lower, I2.lower), mul(I1.lower, I2.upper), mul(I1.upper, I2.lower), mul(I1.upper, I2.upper) ] if arg1._type == 'int': e = IntInterval([min(L), max(L)]) else: e = FloatInterval([min(L), max(L)]) elif op.op == '/': # TODO 开闭的问题 L = [div(I1.lower,I2.lower), div(I1.lower,I2.upper),\ div(I1.upper,I2.lower), div(I1.upper,I2.upper)] if arg1._type == 'int': e = IntInterval([min(L), max(L)]) else: e = FloatInterval([min(L), max(L)]) return e