class TestCase::Operator::Assign {
  use TestCase::Minimal;
  use TestCase::Simple;
  use Complex_2f;
  use Complex_2d;
  use Fn;
  use Array;
  use TestCase::Point_3b;
  use TestCase::Point_3d;
  use TestCase::Point_3f;
  use TestCase::Point_3i;
  use TestCase::Point_3l;
  use TestCase::Point_3s;
  
  static method assign_ref : int () {
    my $num = 1;
    my $ref_num1 = \$num;
    my $ref_num2 = $ref_num1;
    
    if ($$ref_num2 == 1) {
      return 1;
    }
    
    return 0;
  }
  
  static method assign_return_value_multi_numeric_type : int () {
    my $z1 : Complex_2d;
    $z1->{re} = 1;
    $z1->{im} = 2;
    
    my $z2 : Complex_2d;
    my $z3 : Complex_2d;
    
    $z3 = $z2 = $z1;
    
    if ($z3->{re} == 1 && $z3->{im} == 2 && $z2->{re} == 1 && $z2->{im} == 2) {
      return 1;
    }
    
    return 0;
  }
  
  static method assign_return_value_value_field : int () {
    my $z : Complex_2d;
    $z->{im} = 1;
    my $im1 : double;
    my $im2 : double;
    
    $im1 = $im2 = $z->{im};
    
    if ($im1 == 1 && $im2 == 1) {
      return 1;
    }
    
    return 0;
  }

  static method assign_return_value_ref : int () {
    my $num = 1;
    my $num_ref1 : int*;
    my $num_ref2 : int*;
    
    $num_ref1 = $num_ref2 = \$num;
    
    if ($$num_ref1 == 1 && $$num_ref2 == 1) {
      return 1;
    }
    
    return 0;
  }

  static method assign_return_value_my_var : int () {
    
    if (my $var = 1) {
      return 1;
    }
    
    return 0;
  }

  static method assign_return_value_byte : int () {
    my $v1 : byte;
    my $v2 : byte;
    
    $v2 = $v1 = Fn->INT8_MIN;
    
    if ($v2 == Fn->INT8_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_short : int () {
    my $v1 : short;
    my $v2 : short;
    
    $v2 = $v1 = Fn->INT16_MIN;
    
    if ($v2 == Fn->INT16_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_int : int () {
    my $v1 : int;
    my $v2 : int;
    
    $v2 = $v1 = (int)Fn->INT32_MIN;
    
    if ($v2 == Fn->INT32_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_long : int () {
    my $v1 : long;
    my $v2 : long;
    
    $v2 = $v1 = (long)Fn->INT64_MIN;
    
    if ($v2 == Fn->INT64_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_float : int () {
    my $v1 : float;
    my $v2 : float;
    
    $v2 = $v1 = (float)Fn->FLT_MIN;
    
    if ($v2 == Fn->FLT_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_double : int () {
    my $v1 : double;
    my $v2 : double;
    
    $v2 = $v1 = (double)Fn->DBL_MIN;
    
    if ($v2 == Fn->DBL_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_var : int () {
      my $v1 : int;
      my $v2 : int;
      my $v3 : int;
      
      $v1 = 1;
      $v3 = $v2 = $v1;
      
      if ($v3 == 1 && $v2 == 1) {
        return 1;
      }
      return 0;
  }
  
  static method assign_return_value_constant : int () {
      my $v1 : int;
      my $v2 : int;
      my $v3 : int;
      
      $v3 = $v2 = 1;
      
      if ($v3 == 1) {
        return 1;
      }
      return 0;
  }

  static method assign_return_value_object : int () {
    my $v1 : TestCase::Minimal;
    my $v2 : TestCase::Minimal;
    
    $v2 = $v1 = TestCase::Minimal->new;
    
    if ($v2 isa TestCase::Minimal) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_element_access_right_var : int () {
    my $nums = new int[1];
    
    my $var1 = 1;
    my $var2 = $nums->[0] = $var1;
    
    if ($var2 == 1) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_element_access_right_not_var : int () {
    my $nums = new int[1];
    
    my $var2 = $nums->[0] = 1;
    
    if ($var2 == 1) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_element_access_left_var : int () {
    my $nums = new int[1];
    
    my $var1 : int;
    $nums->[0] = 1;
    my $var2 = $var1 = $nums->[0];
    
    if ($var2 == 1 && $var1 == 1) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_element_access_chain : int () {
    my $nums = new int[2];
    
    my $var2 = $nums->[0] = $nums->[1] = 1;
    
    if ($var2 == 1 && $nums->[0] == 1 && $nums->[1] == 1) {
      return 1;
    }
    return 0;
  }

  static method assign_return_value_field_access : int () {
    my $v2 : int;
    
    my $minimal = TestCase::Minimal->new;
    $v2 = $minimal->{x} = 1;
    
    if ($v2 == 1) {
      return 1;
    }
    return 0;
  }

  static method assign : int () {
    # Constant assignment
    my $success_constant_narrow = 0;
    {
      # narrowing constant byte
      my $value1 : byte = 127;
      my $value2 : byte = -128;
      my $value3 = $value1 + $value2;
      
      # narrowing constant short
      my $value4 : short = 32767;
      my $value5 : short = -32768;
      my $value6 = $value4 + $value5;

      # narrowing constant int
      my $value7 : int = 2147483647L;
      my $value8 : int = -2147483648L;
      my $value9 = $value7 + $value8;
      
      if ($value3 == -1) {
        if ($value6 == -1) {
          if ($value9 == -1) {
            $success_constant_narrow = 1;
          }
        }
      }
    }
    
    # Widning
    my $success_constant_wide = 0;
    {
      my $value1 : short = (byte)127;
      my $value2 : int = (byte)127;
      my $value3 : long = (byte)127;
      my $value4 : float = (byte)127;
      my $value5 : double = (byte)127;
      
      if ($value1 == 127) {
        if ($value2 == 127) {
          if ($value3 == 127) {
            if ($value4 == 127.0f) {
              if ($value5 = 127.0) {
                $success_constant_wide = 1;
              }
            }
          }
        }
      }
    }
    
    # Assign same name variable
    my $success_assign_same_name_variable = 0;
    {
      my $obj = TestCase::Minimal->new;
      $obj = $obj;
      $success_assign_same_name_variable = 1;
    }
    
    if ($success_constant_narrow && $success_constant_wide && $success_assign_same_name_variable) {
      return 1;
    }
    
    return 0;
  }
  
  static method assign_numeric_to_string : int () {
    my $inum : string = 25;
    my $dnum : string = 0.25;
    
    my $inum_str : string = $inum;
    my $dnum_str : string = $dnum;
    
    if ($inum_str eq "25" && $dnum_str eq "0.25") {
      return 1;
    }
    
    return 0;
  }

  static method assign_byte_to_byte_object : int () {
    my $byte : byte = Fn->INT8_MIN;
    my $byte_obj : Byte = $byte;
    
    if ($byte_obj isa Byte) {
      if ($byte_obj->value == Fn->INT8_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_short_to_short_object : int () {
    my $short : short = Fn->INT16_MIN;
    my $short_obj : Short = $short;
    
    if ($short_obj isa Short) {
      if ($short_obj->value == Fn->INT16_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_int_to_int_object : int () {
    my $int : int = Fn->INT32_MIN;
    my $int_obj : Int = $int;
    
    if ($int_obj isa Int) {
      if ($int_obj->value == Fn->INT32_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_long_to_long_object : int () {
    my $long : long = Fn->INT64_MIN;
    my $long_obj : Long = $long;
    
    if ($long_obj isa Long) {
      if ($long_obj->value == Fn->INT64_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_float_to_float_object : int () {
    my $float : float = Fn->FLT_MIN;
    my $float_obj : Float = $float;
    
    if ($float_obj isa Float) {
      if ($float_obj->value == Fn->FLT_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_double_to_double_object : int () {
    my $double : double = Fn->DBL_MIN;
    my $double_obj : Double = $double;
    
    if ($double_obj isa Double) {
      if ($double_obj->value == Fn->DBL_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_byte_to_any_object : int () {
    my $byte : byte = Fn->INT8_MIN;
    my $byte_obj : object = $byte;
    
    if ($byte_obj isa Byte) {
      my $byte_obj2 = (Byte)$byte_obj;
      if ($byte_obj2->value == Fn->INT8_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_short_to_any_object : int () {
    my $short : short = Fn->INT16_MIN;
    my $short_obj : object = $short;
    
    if ($short_obj isa Short) {
      my $short_obj2 = (Short)$short_obj;
      if ($short_obj2->value == Fn->INT16_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_int_to_any_object : int () {
    my $int : int = Fn->INT32_MIN;
    my $int_obj : object = $int;
    
    if ($int_obj isa Int) {
      my $int_obj2 = (Int)$int_obj;
      if ($int_obj2->value == Fn->INT32_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_long_to_any_object : int () {
    my $long : long = Fn->INT64_MIN;
    my $long_obj : object = $long;
    
    if ($long_obj isa Long) {
      my $long_obj2 = (Long)$long_obj;
      if ($long_obj2->value == Fn->INT64_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_float_to_any_object : int () {
    my $float : float = Fn->FLT_MIN;
    my $float_obj : object = $float;
    
    if ($float_obj isa Float) {
      my $float_obj2 = (Float)$float_obj;
      if ($float_obj2->value == Fn->FLT_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_double_to_any_object : int () {
    my $double : double = Fn->DBL_MIN;
    my $double_obj : object = $double;
    
    if ($double_obj isa Double) {
      my $double_obj2 = (Double)$double_obj;
      if ($double_obj2->value == Fn->DBL_MIN) {
        return 1;
      }
    }
    return 0;
  }

  static method assign_byte_object_to_byte : int () {
    my $byte_obj = Byte->new(Fn->INT8_MIN);
    my $byte : byte = $byte_obj;
    
    if ($byte == Fn->INT8_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_short_object_to_short : int () {
    my $short_obj = Short->new(Fn->INT16_MIN);
    my $short : short = $short_obj;
    
    if ($short == Fn->INT16_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_int_object_to_int : int () {
    my $int_obj = Int->new(Fn->INT32_MIN);
    my $int : int = $int_obj;
    
    if ($int == Fn->INT32_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_long_object_to_long : int () {
    my $long_obj = Long->new(Fn->INT64_MIN);
    my $long : long = $long_obj;
    
    if ($long == Fn->INT64_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_float_object_to_float : int () {
    my $float_obj = Float->new(Fn->FLT_MIN);
    my $float : float = $float_obj;
    
    if ($float == Fn->FLT_MIN) {
      return 1;
    }
    return 0;
  }
  static method assign_double_object_to_double : int () {
    my $double_obj = Double->new(Fn->DBL_MIN);
    my $double : double = $double_obj;
    
    if ($double == Fn->DBL_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_any_object_to_byte : int () {
    my $byte_obj = (object)Byte->new(Fn->INT8_MIN);
    my $byte : byte = $byte_obj;
    
    if ($byte == Fn->INT8_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_any_object_to_short : int () {
    my $short_obj = (object)Short->new(Fn->INT16_MIN);
    my $short : short = $short_obj;
    
    if ($short == Fn->INT16_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_any_object_to_int : int () {
    my $int_obj = (object)Int->new(Fn->INT32_MIN);
    my $int : int = $int_obj;
    
    if ($int == Fn->INT32_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_any_object_to_long : int () {
    my $long_obj = (object)Long->new(Fn->INT64_MIN);
    my $long : long = $long_obj;
    
    if ($long == Fn->INT64_MIN) {
      return 1;
    }
    return 0;
  }

  static method assign_any_object_to_float : int () {
    my $float_obj = (object)Float->new(Fn->FLT_MIN);
    my $float : float = $float_obj;
    
    if ($float == Fn->FLT_MIN) {
      return 1;
    }
    return 0;
  }
  static method assign_any_object_to_double : int () {
    my $double_obj = (object)Double->new(Fn->DBL_MIN);
    my $double : double = $double_obj;
    
    if ($double == Fn->DBL_MIN) {
      return 1;
    }
    return 0;
  }
  
  static method assign_undef_to_numeric_object : int () {
    my $int_obj = Int->new(1);
    $int_obj = undef;
    if ($int_obj == undef) {
      return 1;
    }
    return 0;
  }
  
  static method assign_undef_to_ref : int () {
    
    {
      my $ref : int* = undef;
      
      unless ($ref == undef) {
        return 0;
      }
    }
    
    {
      my $ref : Complex_2d* = undef;
      
      unless ($ref == undef) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method evaluate_left_to_right : int () {
    {
      my $i = 2;
      my $j = ($i = 3) * $i;
      
      unless ($j == 9) {
        return 0;
      }
    }
    
    {
      my $j = 10;
      eval {
        TestCase::Operator::Assign->evaluate_left_to_right_excpetion() / ($j = 3);
      };
      $@ = undef;
      
      unless ($j == 10) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method evaluate_left_to_right_excpetion : int () {
    die "Error";
    
    return 6;
  }
  
  static method assign_cannot_assign : int () {
    
    my $value = (object)new Int[0];
    
    eval { my $value2 = (Long[])$value; }
    
    unless (Fn->contains($@, "An assignment failed. Int[] type cannot be assigned to Long[] type.")) {
      return 0;
    }
    
    return 1;
  }
  
  static method to_mulnum : int () {
    
    {
      {
        my $mulnum : TestCase::Point_3b;
        $mulnum->{x} = 1;
        $mulnum->{y} = 2;
        $mulnum->{z} = 3;
        $mulnum = undef;
        
        unless ($mulnum->{x} == 0) {
          diag $mulnum->{x};
          return 0;
        }
        
        unless ($mulnum->{y} == 0) {
          diag;
          return 0;
        }
        
        unless ($mulnum->{z} == 0) {
          diag;
          return 0;
        }
        
      }
      {
        my $mulnum : TestCase::Point_3s;
        $mulnum->{x} = 1;
        $mulnum->{y} = 2;
        $mulnum->{z} = 3;
        $mulnum = undef;
        
        unless ($mulnum->{x} == 0) {
          return 0;
        }
        
        unless ($mulnum->{y} == 0) {
          return 0;
        }
        
        unless ($mulnum->{z} == 0) {
          return 0;
        }
      }
      {
        my $mulnum : TestCase::Point_3i;
        $mulnum->{x} = 1;
        $mulnum->{y} = 2;
        $mulnum->{z} = 3;
        $mulnum = undef;
        
        unless ($mulnum->{x} == 0) {
          return 0;
        }
        
        unless ($mulnum->{y} == 0) {
          return 0;
        }
        
        unless ($mulnum->{z} == 0) {
          return 0;
        }
      }
      {
        my $mulnum : TestCase::Point_3l;
        $mulnum->{x} = 1;
        $mulnum->{y} = 2;
        $mulnum->{z} = 3;
        $mulnum = undef;
        
        unless ($mulnum->{x} == 0) {
          return 0;
        }
        
        unless ($mulnum->{y} == 0) {
          return 0;
        }
        
        unless ($mulnum->{z} == 0) {
          return 0;
        }
      }
      {
        my $mulnum : TestCase::Point_3f;
        $mulnum->{x} = 1;
        $mulnum->{y} = 2;
        $mulnum->{z} = 3;
        $mulnum = undef;
        
        unless ($mulnum->{x} == 0.0) {
          return 0;
        }
        
        unless ($mulnum->{y} == 0.0) {
          return 0;
        }
        
        unless ($mulnum->{z} == 0.0) {
          return 0;
        }
      }
      {
        my $mulnum : TestCase::Point_3d;
        $mulnum->{x} = 1;
        $mulnum->{y} = 2;
        $mulnum->{z} = 3;
        $mulnum = undef;
        
        unless ($mulnum->{x} == 0.0) {
          return 0;
        }
        
        unless ($mulnum->{y} == 0.0) {
          return 0;
        }
        
        unless ($mulnum->{z} == 0.0) {
          return 0;
        }
      }
    }    
    
    return 1;
  }
}
