import numpy
import pytest
import sys
import unittest

from pythran.tests import TestEnv
from pythran.typing import *

class TestBase(TestEnv):
    def test_pass(self):
        self.run_test("def pass_(a):pass", 1, pass_=[int])

    def test_empty_return(self):
        self.run_test("def empty_return(a,b,c):return", 1,1.,True, empty_return=[int,float,bool])

    def test_identity(self):
        self.run_test("def identity(a): return a", 1.5, identity=[float])

    def test_compare(self):
        self.run_test("def compare(a,b,c):\n if a < b < c: return a\n else: return b != c", 1,2,3, compare=[int, int, int])

    def test_arithmetic(self):
        self.run_test("def arithmetic(a,b,c): return a+b*c", 1,2,3.3, arithmetic=[int,int, float])

    def test_boolop(self):
        self.run_test("def boolop(a,b,c): return a and b or c", True, True, False, boolop=[bool,bool, bool])

    def test_operator(self):
        self.run_test("def operator_(a,b,c): return (a+b-b*a//(a%b)**(a<<a>>b|b^a&a//b))/c",1,2,3., operator_=[int,int, float])

    def test_unaryop(self):
        self.run_test("def unaryop(a): return not(~(+(-a)))", 1, unaryop=[int])

    def test_expression(self):
        self.run_test("def expression(a,b,c): a+b*c", 1,2,3.3, expression=[int,int, float])

    def test_recursion1(self):
        code="""
def fibo(n): return n if n <2 else fibo(n-1) + fibo(n-2)
def fibo2(n): return fibo2(n-1) + fibo2(n-2) if n > 1 else n
"""
        self.run_test(code, 4, fibo=[int])

    def test_recursion2(self):
        code="""
def fibo(n): return n if n <2 else fibo(n-1) + fibo(n-2)
def fibo2(n): return fibo2(n-1) + fibo2(n-2) if n > 1 else n
"""
        self.run_test(code, 4., fibo2=[float])

    def test_manual_list_comprehension(self):
        self.run_test("def f(l):\n ll=list()\n for k in l:\n  ll+=[k]\n return ll\ndef manual_list_comprehension(l): return f(l)", [1,2,3], manual_list_comprehension=[List[int]])

    def test_list_comprehension(self):
        self.run_test("def list_comprehension(l): return [ x*x for x in l ]", [1,2,3], list_comprehension=[List[int]])

    def test_dict_comprehension(self):
        self.run_test("def dict_comprehension(l): return { i: 1 for i in l if len(i)>1 }", ["1","12","123"], dict_comprehension=[List[str]])

    def test_filtered_list_comprehension(self):
        self.run_test("def filtered_list_comprehension(l): return [ x*x for x in l if x > 1 if x <10]", [1,2,3], filtered_list_comprehension=[List[int]])

    def test_multilist_comprehension(self):
        self.run_test("def multilist_comprehension(l): return [ x*y for x in l for y in l]", [1,2,3], multilist_comprehension=[List[int]])

    def test_zipped_list_comprehension(self):
        self.run_test("def zipped_list_comprehension(l): return [ x*y for x,y in zip(l,l) ]", [1,2,3], zipped_list_comprehension=[List[int]])

    def test_zip(self):
        self.run_test("def zip_(l0,l1): return list(zip(l0,l1))", [1,2,3],["one", "two", "three"], zip_=[List[int], List[str]])

    def test_multizip(self):
        self.run_test("def multizip(l0,l1): return list(zip(l0,zip(l0,l1)))", [1,2,3],["one", "two", "three"], multizip=[List[int], List[str]])

    def test_reduce(self):
        self.run_test("def reduce_(l): from functools import reduce; return reduce(lambda x,y:x+y, l)", [0.,1.1,2.2,3.3], reduce_=[List[float]])

    def test_another_reduce(self):
        code = '''
        def another_reduce(l0,l1):
            from functools import reduce
            return reduce(lambda x,y:x+y[0]+y[1], zip(l0, l1),0)
        '''
        self.run_test(code, [0.4,1.4,2.4,3.4], [0.,1.1,2.2,3.3], another_reduce=[List[float], List[float]])

    def test_sum(self):
        self.run_test("def sum_(l): return sum(l)", [0.,1.1,2.2,3.3], sum_=[List[float]])

    def test_multisum(self):
        self.run_test("def multisum(l0, l1): return sum(l0) + sum(l1)", [0.,1.1,2.2,3.3],[1,2,3], multisum=[List[float], List[int]])

    def test_max(self):
        self.run_test("def max_(l):return max(l)", [ 1.1, 2.2 ], max_=[List[float]])

    def test_multimax(self):
        self.run_test("def multimax(l,v):return max(v,max(l))", [ 1.1, 2.2 ], 1, multimax=[List[float],int])

    def test_min(self):
        self.run_test("def min_(l):return min(l)", [ 1.1, 2.2 ], min_=[List[float]])

    def test_multimin(self):
        self.run_test("def multimin(l,v):return min(v,min(l))", [ 1.1, 2.2 ], 3, multimin=[List[float],int])

    def test_map(self):
        self.run_test("def map_(l0, l1, v): return list(map(lambda x,y:x*v+y, l0, l1))", [0,1,2], [0.,1.1,2.2], 2, map_=[List[int], List[float], int])

    def test_multimap(self):
        self.run_test("def multimap(l0, l1, v): return list(map(lambda x,y:x*v+y, l0, map(lambda z:z+1,l1)))", [0,1,2], [0.,1.1,2.2], 2, multimap=[List[int], List[float], int])

    def test_intrinsic_map(self):
        self.run_test("def intrinsic_map(l): return list(map(max,l))",[[0,1,2],[2,0,1]], intrinsic_map=[List[List[int]]])

    def test_range1(self):
        self.run_test("def range1_(e): return list(range(e))", 3, range1_=[int])

    def test_range2(self):
        self.run_test("def range2_(b,e): return list(range(b,e))", 1, 3, range2_=[int,int])

    def test_range3(self):
        self.run_test("def range3_(b,e,s): return list(range(b,e,s))", 8,3,-2, range3_=[int,int,int])

    def test_range4(self):
        self.run_test("def range4_(b,e,s): return list(range(b,e,s))", 8,2,-2, range4_=[int,int,int])

    def test_range5(self):
        self.run_test("def range5_(b,e,s): return list(range(b,e,s))", 3,8,1, range5_=[int,int,int])

    def test_range6(self):
        self.run_test("def range6_(b,e,s): return list(range(b,e,s))", 3,8,3, range6_=[int,int,int])

    def test_range7(self):
        self.run_test("def range7_(b,e,s): return list(range(b,e,s))", 3,9,3, range7_=[int,int,int])

    def test_rrange1(self):
        self.run_test("def rrange1_(e): return list(reversed(range(e)))", 3, rrange1_=[int])

    def test_rrange2(self):
        self.run_test("def rrange2_(b,e): return set(reversed(range(b,e)))", 1, 3, rrange2_=[int,int])

    def test_rrange3(self):
        self.run_test("def rrange3_(b,e,s): return list(reversed(range(b,e,s)))", 8,3,-2, rrange3_=[int,int,int])

    def test_rrange4(self):
        self.run_test("def rrange4_(b,e,s): return set(reversed(range(b,e,s)))", 8,2,-2, rrange4_=[int,int,int])

    def test_rrange5(self):
        self.run_test("def rrange5_(b,e,s): return list(reversed(range(b,e,s)))", 3,8,1, rrange5_=[int,int,int])

    def test_rrange6(self):
        self.run_test("def rrange6_(b,e,s): return set(reversed(range(b,e,s)))", 3,8,3, rrange6_=[int,int,int])

    def test_rrange7(self):
        self.run_test("def rrange7_(b,e,s): return list(reversed(range(b,e,s)))", 3,9,3, rrange7_=[int,int,int])

    def test_multirange(self):
        self.run_test("def multirange(i): return list(map(lambda x,y:y*x//2, range(1,i), range(i,1,-1)))", 3, multirange=[int])

    def test_xrange1(self):
        self.run_test("def xrange1_(e): return list(range(e))", 3, xrange1_=[int])

    def test_xrange2(self):
        self.run_test("def xrange2_(b,e): return list(range(b,e))", 1, 3, xrange2_=[int,int])

    def test_xrange3(self):
        self.run_test("def xrange3_(b,e,s): return list(range(b,e,s))", 8,3,-2, xrange3_=[int,int,int])

    def test_xrange4(self):
        self.run_test("def xrange4_(b,e,s): return list(range(b,e,s))", 3,8,1, xrange4_=[int,int,int])

    def test_xrange5(self):
        self.run_test("def xrange5_(e): return max(range(e))", 3, xrange5_=[int])

    def test_multixrange(self):
        self.run_test("def multixrange(i): return list(map(lambda x,y:y*x//2, range(1,i), range(i,1,-1)))", 3, multixrange=[int])

    def test_print(self):
        self.run_test("def print_(a,b,c,d): print(a,b,c,d,'e',1.5)", [1.,2.,3.1],3,True, "d", print_=[List[float], int, bool, str])

    def test_print_tuple(self):
        self.run_test("def print_tuple(a,b,c,d): t = (a,b,c,d,'e',1.5,); print(t)", [1.,2.,3.1],3,True, "d", print_tuple=[List[float], int, bool, str])

    def test_fstring_noarg(self):
        self.run_test("def fstring_noarg(): return f'hello'", fstring_noarg=[])

    def test_fstring_1arg(self):
        self.run_test("def fstring_1arg(a): return f'{a:s}'", "a", fstring_1arg=[str])

    def test_fstring(self):
        self.run_test("def fstring(a,b,c): return f'a: {a: 4d}; b: {b:.2f}; c: {c:s}'", 2, 6.28, "c", fstring=[int, float, str])

    def test_assign(self):
        self.run_test("def assign(a): b=2*a ; return b", 1, assign=[int])

    def test_multiassign(self):
        self.run_test("def multiassign(a):\n c=b=a\n return c", [1], multiassign=[List[int]])

    def test_divmax(self):
        self.run_test("def divmax_(a): b=4.*a;c=b/2;return max(c,b)", 1, divmax_=[int])

    @unittest.skip("impossible to handle max(int, float) without conversion")
    def test_divmax_float(self):
        self.run_test("def divmax_float_(a): b=2*a;c=b/2;return max(c,b)", 1, divmax_float_=[int])

    def test_if(self):
        self.run_test("def if_(a,b):\n if a>b: return a\n else: return b", 1, 1.1, if_=[int, float])

    def test_while(self):
        self.run_test("def while_(a):\n while(a>0): a-=1\n return a", 8, while_=[int])

    def test_for(self):
        self.run_test("def for_(l):\n s=0\n for i in l:\n  s+=i\n return s", [0,1,2], for_=[List[int]])

    def test_declarations(self):
        code = """
def declarations():
    if True:
        a=0
        while a <3:
            b = 1
            a = b + a
    else:
        a=1
    return a + b
"""
        self.run_test(code, declarations=[])

    def test_lambda(self):
        code = """
def lambda_():
    l=lambda x,y: x+y
    return l(1,2) + l(1.2,2)
"""
        self.run_test(code, lambda_=[])

    def test_multidef1(self):
        self.run_test("def def10(): pass\ndef def11(): def10()", def11=[])

    def test_multidef2(self):
        self.run_test("def def21(): def20()\ndef def20(): pass", def21=[])

    def test_multidef3(self):
        self.run_test("def def31(): return 1\ndef def30(): return def31()", def31=[])

    def test_multidef4(self):
       self.run_test("def def41(): return def40()\ndef def40(): return 1", def41=[])

    def test_tuple(self):
        self.run_test("def tuple_(t): return t[0]+t[1]", (0,1), tuple_=[Tuple[int, int]])

    def test_nested_list_comprehension(self):
        self.run_test("def nested_list_comprehension(): return [ [ x+y for x in range(10) ] for y in range(20) ]", nested_list_comprehension=[])

    def test_delete(self):
        self.run_test("def delete_(v): del v", 1, delete_=[int])

    def test_continue(self):
        self.run_test("def continue_():\n for i in range(3):continue\n return i", continue_=[])

    def test_break(self):
        self.run_test("def break_():\n for i in range(3):break\n return i", break_=[])

    def test_assert(self):
        with self.assertRaises(AssertionError):
            self.run_test("def assert_(i): assert i > 0", -1, assert_=[int])

    def test_assert_with_msg(self):
        self.run_test("def assert_with_msg(i): assert i > 0, 'hell yeah'", 1, assert_with_msg=[int])

    def test_import_from(self):
        self.run_test("def import_from(): from math import cos ; return cos(1.)", import_from=[])

    def test_len(self):
        self.run_test("def len_(i,j,k): return len(i)+len(j)+len(k)", "youpi", [1,2],[], len_=[str,List[int], List[float]])

    def test_in_string(self):
        self.run_test("def in_string(i,j): return i in j", "yo", "youpi", in_string=[str,str])

    def test_not_in_string(self):
        self.run_test("def not_in_string(i,j): return i not in j", "yo", "youpi", not_in_string=[str,str])

    def test_in_list(self):
        self.run_test("def in_list(i,j): return i in j", 1, [1,2,3], in_list=[int,List[int]])

    def test_not_in_list(self):
        self.run_test("def not_in_list(i,j): return i not in j", False, [True, True, True], not_in_list=[bool,List[bool]])

    def test_subscript(self):
        self.run_test("def subscript(l,i): l[0]=l[0]+l[i]", [1], 0, subscript=[List[int], int])

    def test_nested_lists(self):
        self.run_test("def nested_lists(l,i): return l[0][i]", [[1]], 0, nested_lists=[List[List[int]],int])

    def test_nested_tuples(self):
        self.run_test("def nested_tuples(l,i): return l[i][1]", [(0.1,1,)], 0, nested_tuples=[List[Tuple[float,int]],int])

    def test_return_empty_list(self):
        self.run_test("def return_empty_list(): return list()", return_empty_list=[])

    def test_empty_list(self):
        self.run_test("def empty_list(): a=[]", empty_list=[])

    def test_multi_list(self):
        self.run_test("def multi_list(): return [[[2.0],[1,2,3]],[[2.0],[1,2,3]]]", multi_list=[])

    def test_empty_tuple(self):
        self.run_test("def empty_tuple(): a=()", empty_tuple=[])

    def test_multi_tuple(self):
        self.run_test("def multi_tuple(): return (1,('e',2.0),[1,2,3])", multi_tuple=[])

    def test_augmented_assign0(self):
        self.run_test("def augmented_assign0(a):\n a+=1.5\n return a", 12, augmented_assign0=[int])

    def test_augmented_assign1(self):
        self.run_test("def augmented_assign1(a):\n a-=1.5\n return a", 12, augmented_assign1=[int])

    def test_augmented_assign2(self):
        self.run_test("def augmented_assign2(a):\n a*=1.5\n return a", 12, augmented_assign2=[int])

    def test_augmented_assign3(self):
        self.run_test("def augmented_assign3(a):\n a/=1.5\n return a", 12, augmented_assign3=[int])

    def test_augmented_assign4(self):
        self.run_test("def augmented_assign4(a):\n a %= 5\n return a", 12, augmented_assign4=[int])

    def test_augmented_assign5(self):
        self.run_test("def augmented_assign5(a):\n a//=2\n return a", 12, augmented_assign5=[int])

    def test_augmented_assign6(self):
        self.run_test("def augmented_assign6(a):\n a**=5\n return a", 12, augmented_assign6=[int])

    def test_augmented_assign7(self):
        self.run_test("def augmented_assign7(a):\n a<<=1\n return a", 12, augmented_assign7=[int])

    def test_augmented_assign8(self):
        self.run_test("def augmented_assign8(a):\n a>>=1\n return a", 12, augmented_assign8=[int])

    def test_augmented_assign9(self):
        self.run_test("def augmented_assign9(a):\n a^=1\n return a", 12, augmented_assign9=[int])

    def test_augmented_assignA(self):
        self.run_test("def augmented_assignA(a):\n a|=1\n return a", 12, augmented_assignA=[int])

    def test_augmented_assignB(self):
        self.run_test("def augmented_assignB(a):\n a&=1\n return a", 12, augmented_assignB=[int])

    def test_augmented_list_assign(self):
        self.run_test("def augmented_list_assign(l):\n a=list()\n a+=l\n return a", [1,2], augmented_list_assign=[List[int]])

    def test_initialization_list(self):
        self.run_test("def initialization_list(): return [1, 2.3]", initialization_list=[])

    def test_multiple_assign(self):
        self.run_test("def multiple_assign():\n a=0 ; b = a\n a=1.5\n return a, b", multiple_assign=[])

    def test_multiple_return1(self):
        self.run_test("def multiple_return1(a):\n if True:return 1\n else:\n  return a", 2,  multiple_return1=[int])

    def test_multiple_return2(self):
        self.run_test("def multiple_return2(a):\n if True:return 1\n else:\n  b=a\n  return b", 2,  multiple_return2=[int])

    def test_multiple_return3(self):
        self.run_test("def multiple_return3(a):\n if True:return 1\n else:\n  b=a\n  return a+b", 2,  multiple_return3=[int])

    def test_id(self):
        self.run_test("def id_(a):\n c=a\n return id(a)==id(c)", [1,2,3], id_=[List[int]])

    def test_delayed_max(self):
        self.run_test("def delayed_max(a,b,c):\n m=max\n return m(a,b) + m(b,c)", 1, 2, 3.5, delayed_max=[int, int, float])

    def test_slicing(self):
        self.run_test("def slicing(l): return l[0:1] + l[:-1]",[1,2,3,4], slicing=[List[int]])

    def test_not_so_deep_recursive_calls(self):
        code="""
def a(i): return b(i)
def b(i): return b(a(i-1)) if i else i
def not_so_deep_recursive_calls(i):return b(i)"""
        self.run_test(code,3, not_so_deep_recursive_calls=[int])

    def test_deep_recursive_calls(self):
        code="""
def a(i): return a(i-1) + b(i) if i else i
def b(i): return b(i-1)+a(i-1) if i else c(i-1) if i+1 else i
def c(i): return c(i-1) if i>0 else 1
def deep_recursive_calls(i):a(i)+b(i) +c(i)"""
        self.run_test(code,3, deep_recursive_calls=[int])

    def test_dummy_nested_def(self):
        code="""
def dummy_nested_def(a):
    def the_dummy_nested_def(b):return b
    return the_dummy_nested_def(a)"""
        self.run_test(code,3, dummy_nested_def=[int])

    def test_nested_def(self):
        code="""
def nested_def(a):
    def the_nested_def(b):return a+b
    return the_nested_def(3)"""
        self.run_test(code,3, nested_def=[int])

    def test_none(self):
        self.run_test("def none_(l):\n if len(l)==0: return\n else: return l", [], none_=[List[int]])

    def test_import(self):
        self.run_test("import math\ndef import_(): return math.cos(1)", import_=[])

    def test_local_import(self):
        self.run_test("def local_import_(): import math;return math.cos(1)", local_import_=[])

    def test_abs(self):
        """ Check builtins.abs behavior with float. """
        self.run_test("""
        def abs_(a):
            return abs(a)""",
                      -1.3, abs_=[float])

    def test_npabs(self):
        """ Check builtins.abs behavior with numpy.array. """
        self.run_test("""
        def npabs_(a):
            return abs(a)""",
                      numpy.array([-1.3, 2.3, -4]),
                      npabs_=[NDArray[float, :]])

    def test_all(self):
        self.run_test("def all_(a): return all(a)", [True, False, True], all_=[List[bool]])

    def test_any(self):
        self.run_test("def any_(a): return any(a)", [0, 1, 2], any_=[List[int]])

    def test_bin(self):
        self.run_test("def bin_(a): return bin(a)", 54321, bin_=[int])

    def test_bin2(self):
        self.run_test("def bin2_(a): return bin(a)", -543, bin2_=[int])

    @pytest.mark.skipif(sys.platform == "win32",
                        reason="Windows uses long of 32-bit, this test assumes they are 64-bit")
    def test_bin3(self):
        self.run_test("def bin3_(a): return bin(a)", -sys.maxsize - 1, bin3_=[int])

    def test_bin4(self):
        self.run_test("def bin4_(a): return bin(a)", -1, bin4_=[int])

    def test_chr(self):
        self.run_test("def chr_(a): return chr(a)", 42, chr_=[int])

    def test_complex(self):
        self.run_test("def complex_(a): return complex(a)", 1, complex_=[int])

    def test_divmod(self):
        self.run_test("def divmod_(a,b): return divmod(a,b)", 5, 2, divmod_=[int,int])

    def test_enumerate(self):
        self.run_test("def enumerate_(l): return [ x for x in enumerate(l) ]", ["a","b","c"], enumerate_=[List[str]])

    def test_enumerat2(self):
        self.run_test("def enumerate2_(l): return [ x for x in enumerate(l, 3) ]", ["a","b","c"], enumerate2_=[List[str]])

    def test_filter(self):
        self.run_test("def filter_(l): return list(filter(lambda x:x%2, l))", [1,2,3], filter_=[List[int]])

    def test_hex(self):
        self.run_test("def hex_(a): return hex(a)", 18, hex_=[int])

    def test_oct(self):
        self.run_test("def oct_(a): return oct(a)", 18, oct_=[int])

    def test_pow(self):
        self.run_test("def pow_(a): return pow(a,5)", 18, pow_=[int])

    def test_powf_op0(self):
        self.run_test("def powf_op0_(a): return a ** 4.", 8., powf_op0_=[float])

    def test_powf_op1(self):
        self.run_test("def powf_op1_(a): return a ** 4.3", 8., powf_op1_=[float])

    def test_pow_op0(self):
        self.run_test("def pow_op0(a): return a ** 0, a ** 1, a **2, a ** 3, a ** 4, a ** 5, a ** 6, a** 7",
                      18, pow_op0=[int])

    def test_pow_op1(self):
        self.run_test("def pow_op1(a): return a ** -0, a ** -1, a **-2, a ** -3, a ** -4, a ** -5, a ** -6, a** -7",
                      18, pow_op1=[int])

    def test_pow_op2(self):
        self.run_test("def pow_op2(a): return int(a ** a), a ** -a",
                      7, pow_op2=[int])

    def test_reversed(self):
        self.run_test("def reversed_(l): return [x for x in reversed(l)]", [1,2,3], reversed_=[List[int]])

    def test_round(self):
        self.run_test("def round_(v): return round(v) + round(v,2)", 0.1234, round_=[float])

    def test_sorted0(self):
        self.run_test("def sorted0(l): return [x for x in sorted(l)]", [4, 1,2,3], sorted0=[List[int]])

    def test_sorted1(self):
        self.run_test("def sorted1(l): return [x for x in sorted(l, reverse=True)]", [4, 1,2,3], sorted1=[List[int]])

    def test_sorted2(self):
        self.run_test("def sorted2(l): return [x for x in sorted(l, key=lambda x:-x)]", [4, 1,2,3], sorted2=[List[int]])

    def test_sorted3(self):
        self.run_test("def sorted3(l): return [x for x in sorted(l,reverse=True,key=lambda x:-x)]", [4, 1,2,3], sorted3=[List[int]])

    def test_str(self):
        self.run_test("def str_(l): return str(l)", [1,2,3], str_=[List[int]])

    def test_append(self):
        self.run_test("def append(): l=[] ; l.append(1) ; return l", append=[])

    def test_append_in_call(self):
        self.run_test("def call(l):l.append(1.)\ndef append_in_call(): l=[] ; call(l) ; l.append(1) ; return l", append_in_call=[])

    def test_complex_append_in_call(self):
        code="""
def foo(a,b):
	i = 3*b
	if not i in a:
		a.append(i)
def complex_append_in_call(l1,l2):
	b = []
	for x in l1:
		if not x in l2:
			foo(b,x)"""
        self.run_test(code, [1,2,3],[2],complex_append_in_call=[List[int],List[int]])

    def test_complex_number(self):
        code="""
def complex_number():
    c=complex(0,1)
    return c.real + c.imag"""
        self.run_test(code, complex_number=[])

    def test_raise(self):
        self.run_test("def raise_():\n raise RuntimeError('pof')", raise_=[], check_exception=True)

    def test_complex_number_serialization(self):
        self.run_test("def complex_number_serialization(l): return [x+y for x in l for y in l]", [complex(1,0), complex(1,0)], complex_number_serialization=[List[complex]])

    def test_complex_conj(self):
        self.run_test("def complex_conjugate(c): return c.conjugate()", complex(0,1), complex_conjugate=[complex])

    def test_cast(self):
        self.run_test("def cast(i,f): return float(i)+int(f)", 1,1.5, cast=[int, float])

    def test_subscript_assignment(self):
        code="""
def foo(A):
    A[0]=1.5
def subscript_assignment ():
    a=list(range(1))
    foo(a)
    return a[0]"""
        self.run_test(code,subscript_assignment=[])

    def test_conflicting_keywords(self):
        code="""
def export(template):
    return [ new*new for new in template ]"""
        self.run_test(code, [1], export=[List[int]])

    def test_forelse(self):
        code="""
def forelse():
    l=0
    for i in range(10):
        if i > 3:break
        for j in range(10):
            if j > 5:break
            l+=1
        else:
            l*=2
    else:
        l*=3
    return l"""
        self.run_test(code, forelse=[])

    def test_tuples(self):
        self.run_test("def tuples(n): return ((1,2.,'e') , [ x for x in tuple([1,2,n])] )", 1, tuples=[int])

    def test_reversed_slice(self):
        self.run_test("def reversed_slice(l): return l[::-2]", [0,1,2,3,4], reversed_slice=[List[int]])

    def test_shadow_parameters(self):
        code="""
def shadow_parameters(l):
    if False:l=None
    return l"""
        self.run_test(code, [1], shadow_parameters=[List[int]])

    def test_yielder(self):
        code="""
def iyielder(i):
    for k in range(i+18):
        yield k
    return

def yielder():
    f=iyielder(1)
    b=next(f)
    return [i*i for i in f]"""
        self.run_test(code, yielder=[])

    def test_yield_with_default_param(self):
        code="""
def foo(a=1000):
    for i in range(10):
        yield a

def yield_param():
    it = foo()
    return [i for i in it]"""
        self.run_test(code, yield_param=[])

    def test_set(self):
        code="""
def set_(a,b):
    S=set()
    S.add(a)
    S.add(b)
    return len(S)"""
        self.run_test(code, 1,2,set_=[int, int])
    def test_in_set(self):
        code="""
def in_set(a):
    S=set()
    S.add(a)
    return a in S"""
        self.run_test(code, 1.5, in_set=[float])

    def test_return_set(self):
        self.run_test("def return_set(l): return set(l)", [1,2,3,3], return_set=[List[int]])

    def test_import_set(self):
        self.run_test("def import_set(l): l.add(1) ; return l", {0,2}, import_set=[Set[int]])

    def test_raw_set(self):
        self.run_test("def raw_set(): return { 1, 1., 2 }", raw_set=[])

    def test_iter_set(self):
        self.run_test("def iter_set(s):\n l=0\n for k in s: l+=k\n return l", { 1, 2, 3 } , iter_set=[Set[int]])

    def test_set_comprehension(self):
        self.run_test("def set_comprehension(l): return { i*i for i in l }", [1 , 2, 1, 3], set_comprehension=[List[int]])

    def test_slicer(self):
        code="""
def slicer(l):
    l[2:5]=[1,2]
    return l"""
        self.run_test(code,[1,2,3,4,5,6,7,8,9], slicer=[List[int]])

    def test_generator_expression(self):
        code="""
def generator_expression(l):
    return sum(x for x in l if x == 1)"""
        self.run_test(code,[1,1,1,2], generator_expression=[List[int]])

    def test_default_parameters(self):
        code="""
def dp(b,a=1.2):
    return a

def default_parameters():
    a=1
    c=dp(a)
    d=dp(5,"yeah")
    return str(c)+d"""
        self.run_test(code, default_parameters=[])

    def test_import_as(self):
        code="""
from math import cos as COS
def import_as():
    x=.42
    import math as MATH
    return MATH.sin(x)**2 + COS(x)**2"""
        self.run_test(code, import_as=[])

    def test_tuple_unpacking(self):
        self.run_test("def tuple_unpacking(t): a,b = t ; return a, b", (1,"e"), tuple_unpacking=[Tuple[int, str]])

    def test_list_unpacking(self):
        self.run_test("def list_unpacking(t): [a,b] = t ; return a, b", (1,2), list_unpacking=[Tuple[int, int]])

    def test_recursive_attr(self):
        self.run_test("def recursive_attr(): return {1,2,3}.union({1,2}).union({5})", recursive_attr=[])

    def test_range_negative_step(self):
        self.run_test("""def range_negative_step(n):
        o=[]
        for i in range(n, 0, -1): o.append(i)
        return o""", 10, range_negative_step=[int])

    def test_reversed_range_negative_step(self):
        self.run_test("""def reversed_range_negative_step(n):
        o=[]
        for i in reversed(range(n, 0, -1)): o.append(i)
        return o""", 10, reversed_range_negative_step=[int])

    def test_update_empty_list(self):
        self.run_test('''
def update_empty_list(l):
    p = list()
    return p + l[:1]''', list(range(5)), update_empty_list=[List[int]])

    def test_update_list_with_slice(self):
        self.run_test('''
def update_list_with_slice(l):
    p = list()
    for i in range(10):
        p += l[:1]
    return p,i''', list(range(5)), update_list_with_slice=[List[int]])

    def test_add_slice_to_list(self):
        self.run_test('''
def add_slice_to_list(l):
    p = list()
    for i in range(10):
        p = p + l[:1]
    return p,i''', list(range(5)), add_slice_to_list=[List[int]])

    def test_bool_(self):
        self.run_test("def _bool(d): return bool(d)", 3, _bool=[int])

    def test_complex_add(self):
        self.run_test("def complex_add(): a = 1j ; b = 2 ; return a + b", complex_add=[])

    def test_complex_sub(self):
        self.run_test("def complex_sub(): a = 1j ; b = 2 ; return a - b", complex_sub=[])

    def test_complex_mul(self):
        self.run_test("def complex_mul(): a = 1j ; b = 2 ; return a * b", complex_mul=[])

    def test_complex_div(self):
        self.run_test("def complex_div(): a = 1j ; b = 2 ; return a / b", complex_div=[])

    def test_modulo_int0(self):
        self.run_test("def modulo_int0(n): return n%3, (-n)%3",
                      5,
                      modulo_int0=[int])

    def test_modulo_int1(self):
        self.run_test("def modulo_int1(n): return n%3, (-n)%3",
                      3,
                      modulo_int1=[int])

    def test_modulo_float0(self):
        self.run_test("def modulo_float0(n): return n%3, (-n)%3",
                      5.4,
                      modulo_float0=[float])

    def test_modulo_float1(self):
        self.run_test("def modulo_float1(n): return n%3, (-n)%3",
                      3.5,
                      modulo_float1=[float])

    def test_floordiv_int0(self):
        self.run_test("def floordiv_int0(n): return n%3, (-n)%3",
                      5,
                      floordiv_int0=[int])

    def test_floordiv_int1(self):
        self.run_test("def floordiv_int1(n): return n//2, (-n)//2",
                      3,
                      floordiv_int1=[int])

    def test_floordiv_float0(self):
        self.run_test("def floordiv_float0(n): return n//2, (-n)//2",
                      5.4,
                      floordiv_float0=[float])

    def test_floordiv_float1(self):
        self.run_test("def floordiv_float1(n): return n//2, (-n)//2",
                      3.5,
                      floordiv_float1=[float])

    def test_int_base0(self):
        self.run_test("def int_base(x, y): return [int(x0, y0) for x0, y0 in zip(x, y)]",
                      ["11", "11", "16", "FF"],
                      [2, 4, 8, 16],
                      int_base=[List[str], List[int]])

    def test_int_base1(self):
        self.run_test("def int_base_lit(x, y): return int(x, 8), int('A', y)",
                      "14", 16,
                      int_base_lit=[str, int])

    def test_slice0(self):
        self.run_test("""
        def slice0(a, x):
            return a[x], x, slice(1,1,1)""",
                      numpy.array([-1.3, 2.3, -4]),
                      slice(0,2,1),
                      slice0=[NDArray[float, :], slice])
