#####################################################################################
#
#  Copyright (c) Microsoft Corporation. All rights reserved.
#
# This source code is subject to terms and conditions of the Apache License, Version 2.0. A
# copy of the license can be found in the License.html file at the root of this distribution. If
# you cannot locate the  Apache License, Version 2.0, please send an email to
# ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
# by the terms of the Apache License, Version 2.0.
#
# You must not remove this notice, or any other, from this software.
#
#
#####################################################################################

'''
This module is in place to test the interoperatbility between CPython and CLR numerical
types.

TODO:
- at the moment the test cases in place are simple sanity checks to ensure the
  appropriate operator overloads have been implemented.  This needs to be extended
  quite a bit (e.g., see what happens with OverFlow cases).
- a few special cases aren't covered yet - comlex numbers, unary ops, System.Char,

'''

from iptest.assert_util import *
skiptest("win32")
   
import System
import clr
      
#Test Python/CLR number interop.
clr_integer_types = [ "System.Byte",
                      "System.SByte",
                      "System.Int16",
                      "System.UInt16",
                      "System.Int32",
                      "System.UInt32",
                      "System.Int64",
                      "System.UInt64"]

clr_float_types = [ "System.Single",
                    "System.Double",
                    "System.Decimal",
                  ]

#TODO - char???
clr_types = clr_integer_types + clr_float_types


py_integer_types = ["int", "long"]
py_float_types   = ["float"]

#TODO - special case complex???
py_types = py_integer_types + py_float_types

bug_operands = []
unsupported_operands = [
                         #System.Decimal +: System.Single, System.Double, long, float
                         "System.Decimal+long",
                         "long+System.Decimal",
                         
                         #System.Decimal -: System.Single, System.Double, long, float
                         "System.Decimal-long",
                         "long-System.Decimal",
                         
                         #System.Decimal *: System.Single, System.Double, long, float
                         "System.Decimal*long",
                         "long*System.Decimal",
                         
                         #System.Decimal /:System.Single, System.Double, long, float
                         "System.Decimal/long",
                         "long/System.Decimal",
                         
                         #System.Decimal //:System.Byte System.SByte
                         "System.Decimal//System.Byte",
                         "System.Decimal//System.SByte",
                         "System.Decimal//System.Int16",
                         "System.Decimal//System.UInt16",
                         "System.Decimal//System.Int32",
                         "System.Decimal//System.UInt32",
                         "System.Decimal//System.Int64",
                         "System.Decimal//System.UInt64",
                         "System.Decimal//System.Decimal",
                         "System.Decimal//int",
                         "System.Decimal//long",
                         "System.Byte//System.Decimal",
                         "System.SByte//System.Decimal",
                         "System.Int16//System.Decimal",
                         "System.UInt16//System.Decimal",
                         "System.Int32//System.Decimal",
                         "System.UInt32//System.Decimal",
                         "System.Int64//System.Decimal",
                         "System.UInt64//System.Decimal",
                         "System.Decimal//System.Decimal",
                         "int//System.Decimal",
                         "long//System.Decimal",
                         
                         "System.Decimal**System.Byte",
                         "System.Decimal**System.SByte",
                         "System.Decimal**System.Int16",
                         "System.Decimal**System.UInt16",
                         "System.Decimal**System.Int32",
                         "System.Decimal**System.UInt32",
                         "System.Decimal**System.Int64",
                         "System.Decimal**System.UInt64",
                         "System.Decimal**System.Decimal",
                         "System.Decimal**int",
                         "System.Decimal**long",
                         "System.Byte**System.Decimal",
                         "System.SByte**System.Decimal",
                         "System.Int16**System.Decimal",
                         "System.UInt16**System.Decimal",
                         "System.Int32**System.Decimal",
                         "System.UInt32**System.Decimal",
                         "System.Int64**System.Decimal",
                         "System.UInt64**System.Decimal",
                         "System.Decimal**System.Decimal",
                         "int**System.Decimal",
                         "long**System.Decimal",
                         
                         "System.Decimal%long",
                         "long%System.Decimal",
                            ] + bug_operands


known_bugs = []

#------------------------------------------------------------------------------
def num_ok_for_type(number, proposed_type):
    '''
    Helper function returns true if the number param is within the range of
    valid values for the proposed type
    '''
    #handle special cases first
    if proposed_type=="long":
        #arbitrary precision
        return True
    if proposed_type=="float":
        #arbitrary precision
        return True
    
    
    if number >= eval(proposed_type + ".MinValue") and number <= eval(proposed_type + ".MaxValue"):
        return True
    #must give it another shot...maybe the operator is broken
    if eval(proposed_type + ".MinValue") <= number and eval(proposed_type + ".MaxValue") >= number:
        return True
        
    return False
            

#------------------------------------------------------------------------------
def _test_interop_set(clr_types, py_types, test_cases):
    '''
    Helper function which permutes Python/CLR types onto test cases
    '''
    global unsupported_operands
    global known_bugs
    
    #each test case
    for leftop, op, rightop, expected_value in test_cases:
        
        #get the left operand as a Python type
        py_left = eval(leftop)
        
        #------------------------------------------------------------------
        #create a list of values where each element is the lefthand operand
        #converted to a CLR type
        leftop_clr_types = [x for x in clr_types if num_ok_for_type(py_left, x)]
        leftop_clr_values = [eval(x + "(" + leftop + ")") for x in leftop_clr_types]
                    
        #------------------------------------------------------------------
        #create a list of values where each element is the lefthand operand
        #converted to a Python type
        leftop_py_types = [x for x in py_types if num_ok_for_type(py_left, x)]
        leftop_py_values = [eval(x + "(" + leftop + ")") for x in leftop_py_types]
        
        #------------------------------------------------------------------
        #get the right operand as a Python type
        py_right = eval(rightop)
        rightop_clr_types = [x for x in clr_types if num_ok_for_type(py_right, x)]
        rightop_clr_values = [eval(x + "(" + rightop + ")") for x in rightop_clr_types]
                    
        #------------------------------------------------------------------
        #create a list of values where each element is the righthand operand
        #converted to a Python type
        rightop_py_types = [x for x in py_types if num_ok_for_type(py_right, x)]
        rightop_py_values = [eval(x + "(" + rightop + ")") for x in rightop_py_types]
                
        #------------------------------------------------------------------
        #Comparisons between CPython/CLR types
        def assertionHelper(left_type, left_op, op, right_type, right_op, expected):
            '''
            Helper function used to figure out which test cases fail
            without blowing up the rest of the test.
            '''
            expression_str = left_type + "("+ left_op +") " + str(op) + " " + right_type + "("+ right_op +")"
            
            #if it's supposedly unsupported...make sure
            if unsupported_operands.count(left_type + op + right_type)>0:
                AssertError(TypeError, eval, expression_str)
                return
                
            try:
                expression = eval(expression_str)
            except TypeError as e:
                print("TYPE BUG:", expression_str)
                raise
            
            try:
                AreEqual(expression, expected)
                if known_bugs.count(left_type + op + right_type)>0:
                    raise "NO BUG FOR: " + expression_str
            except:
                if known_bugs.count(left_type + op + right_type)>0:
                    return
                print(expression_str)
                raise
            
            
        #CLR-CLR
        for x in leftop_clr_types:
            for y in rightop_clr_types:
                assertionHelper(x, leftop, op, y, rightop, expected_value)
                
        #CLR-PY
        for x in leftop_clr_types:
            for y in rightop_py_types:
                assertionHelper(x, leftop, op, y, rightop, expected_value)
                             
        #PY-CLR
        for x in leftop_py_types:
            for y in rightop_clr_types:
                assertionHelper(x, leftop, op, y, rightop, expected_value)
            
        #PY-PY
        for x in leftop_py_types:
            for y in rightop_py_types:
                assertionHelper(x, leftop, op, y, rightop, expected_value)

#------------------------------------------------------------------------------
#--BOOLEAN
bool_test_cases = [
                    #x==x
                    ("0","==","0", True),
                    ("0.0","==","0", True),
                    ("1","==","1", True),
                    ("3","==","3", True),
                    ("-1","==","-1", True),
                    
                    #! x==x
                    ("10","==","0", False),
                    ("10.0","==","0", False),
                    ("11","==","1", False),
                    ("31","==","3", False),
                    ("-11","==","-1", False),
                    
                    #x!=x
                    ("10","!=","0", True),
                    ("10.0","!=","0", True),
                    ("11","!=","1", True),
                    ("31","!=","3", True),
                    ("-11","!=","-1", True),
                    
                    #! x!=x
                    ("0","!=","0", False),
                    ("0.0","!=","0", False),
                    ("1","!=","1", False),
                    ("3","!=","3", False),
                    ("-1","!=","-1", False),
                    
                    #x<=x
                    ("0","<=","0", True),
                    ("0.0","<=","0", True),
                    ("1","<=","1", True),
                    ("3","<=","3", True),
                    ("-1","<=","-1", True),
                        
                    #! x<=x
                    ("10","<=","0", False),
                    ("10.0","<=","0", False),
                    ("11","<=","1", False),
                    ("13","<=","3", False),
                    ("10","<=","-1", False),
                    
                    #x>=x
                    ("0",">=","0", True),
                    ("0.0",">=","0", True),
                    ("1",">=","1", True),
                    ("3",">=","3", True),
                    ("-1",">=","-1", True),
                   
                    #! x>=x
                    ("0",">=","10", False),
                    ("0.0",">=","10", False),