class TestCase::Operator::LogicalOperator {
  use TestCase::Minimal;
  use Point;
  use TestCase::Simple;

  static method combination : int () {
    {
      my $ret = (1 || 0) && (0 || 1);
      unless ($ret == 1) {
        return 0;
      }
    }

    {
      my $ret = (1 || 0) && !(0 || 1);
      unless ($ret == 0) {
        return 0;
      }
    }
    
    {
      my $ret = 0 || 0;
      unless ($ret == 0) {
        return 0;
      }
    }
    
    {
      my $ret = 0 || 1;
      unless ($ret == 1) {
        return 0;
      }
    }
    
    # This return -1. This is not wrong. If the type of conditional operand is int, return the last evaluated value.
    {
      my $ret = 0 || -1;
      unless ($ret == -1) {
        return 0;
      }
    }
    
    {
      my $ret = !(0 || -1);
      unless ($ret == 0) {
        return 0;
      }
    }

    {
      my $ret = 0 || -1.0;
      unless ($ret == 1) {
        return 0;
      }
    }

    {
      my $ret = 0 || -1L;
      unless ($ret == 1) {
        return 0;
      }
    }

    {
      my $ret = 1 && 0;
      unless ($ret == 0) {
        return 0;
      }
    }
    
    {
      my $ret = 1 && 1;
      unless ($ret == 1) {
        return 0;
      }
    }

    {
      my $ret = 1 && -1;
      unless ($ret == -1) {
        return 0;
      }
    }
    
    {
      my $ret = !(1 && -1);
      unless ($ret == 0) {
        return 0;
      }
    }

    {
      my $ret = 1 && -1.0;
      unless ($ret == 1) {
        return 0;
      }
    }

    {
      my $ret = 1 && -1L;
      unless ($ret == 1) {
        return 0;
      }
    }

    {
      my $ret = 0 || -1 || 0;
      unless ($ret == -1) {
        return 0;
      }
    }

    {
      my $ret = -1 || 0;
      unless ($ret == -1) {
        return 0;
      }
    }

    {
      my $ret = 0 || -1;
      unless ($ret == -1) {
        return 0;
      }
    }

    {
      my $ret = -1 || 1 && 1;
      unless ($ret == -1) {
        return 0;
      }
    }

    {
      my $ret = 1 && -1 || 0;
      unless ($ret == -1) {
        return 0;
      }
    }

    {
      my $ret = -1 && 1 || 0;
      unless ($ret == 1) {
        return 0;
      }
    }
    
    {
      {
        my $simple = TestCase::Simple->new;
        my $minimal = TestCase::Minimal->new;
        $minimal->{x} = 1;
        
        $simple->{minimal} = $minimal;
        
        {
          my $ret = 0 % 60 == 0 || !$simple->{minimal}->x;
          
          unless ($ret == 1) {
            return 0;
          }
        }
        
        {
          my $ret = 1 % 60 == 0 || !$simple->{minimal}->x;
          
          unless ($ret == 0) {
            return 0;
          }
        }
      }
      
      {
        my $simple = TestCase::Simple->new;
        my $minimal = TestCase::Minimal->new;
        $minimal->{x} = 0;
        
        $simple->{minimal} = $minimal;
        
        {
          my $ret = 0 % 60 == 0 || !$simple->{minimal}->x;
          
          unless ($ret == 1) {
            return 0;
          }
        }
        
        {
          my $ret = 1 % 60 == 0 || !$simple->{minimal}->x;
          
          unless ($ret == 1) {
            return 0;
          }
        }
      }
    }
    
    return 1;
  }
  
  static method logical_not_operator : int () {
    
    # Reverse true value "1"
    {
      my $true = 1;
      my $false = !$true;
      unless ($false == 0) {
        return 0;
      }
    }
    
    # Reverse true value "-5"
    {
      my $true = -5;
      my $false = !$true;
      unless ($false == 0) {
        return 0;
      }
    }

    # Reverse true value object
    {
      my $true = Point->new;
      my $false = !$true;
      unless ($false == 0) {
        return 0;
      }
    }

    # Reverse false value "0"
    {
      my $true = 0;
      my $false = !$true;
      unless ($false == 1) {
        return 0;
      }
    }

    # Reverse false value "undef"
    {
      my $true = 0;
      my $false = !$true;
      unless ($false == 1) {
        return 0;
      }
    }
    
    # !!0
    {
      my $value = 0;
      my $result = !!$value;
      unless ($result == 0) {
        return 0;
      }
    }
    
    # !!1
    {
      my $value = 1;
      my $result = !!$value;
      unless ($result == 1) {
        return 0;
      }
    }
    return 1;
  }

  static method logical_and : int () {
    # Logical AND 1 && 1
    {
      my $ret = 1 && 1;
      unless ($ret == 1) {
        return 0;
      }
    }

    # Logical AND 1 && 0
    {
      my $ret = 1 && 0;
      unless ($ret == 0) {
        return 0;
      }
    }

    # Logical AND 0 && 1
    {
      my $ret = 0 && 1;
      unless ($ret == 0) {
        return 0;
      }
    }

    # Logical AND 0 && 0
    {
      my $ret = 0 && 0;
      unless ($ret == 0) {
        return 0;
      }
    }

    # Logical AND object && 1
    {
      my $point = Point->new;
      my $ret = $point && 1;
      unless ($ret == 1) {
        return 0;
      }
    }

    # Logical AND undef object && undef
    {
      my $point = (Point)undef;
      my $ret = $point && undef;
      unless ($ret == 0) {
        return 0;
      }
    }
    
    return 1;
  }

  static method logical_or : int () {
    # Logical OR 1 || 1
    {
      my $ret = 1 || 1;
      unless ($ret == 1) {
        return 0;
      }
    }

    # Logical OR 1 || 0
    {
      my $ret = 1 || 0;
      unless ($ret == 1) {
        return 0;
      }
    }

    # Logical OR 0 || 1
    {
      my $ret = 0 || 1;
      unless ($ret == 1) {
        return 0;
      }
    }

    # Logical OR 0 || 0
    {
      my $ret = 0 || 0;
      unless ($ret == 0) {
        return 0;
      }
    }

    # Logical OR object || 1
    {
      my $point = Point->new;
      my $ret = $point || 0;
      unless ($ret == 1) {
        return 0;
      }
    }

    # Logical OR undef object || undef
    {
      my $point = (Point)undef;
      my $ret = $point || undef;
      unless ($ret == 0) {
        return 0;
      }
    }
    
    return 1;
  }

  static method logical_and_nagate : int () {
    if (!(1 && 1)) {
      return 0;
    }
    
    return 1;
  }
  
  static method logical_and_push_mortal_leave_scope : int () {
    my $minimals = new TestCase::Minimal[4];
    $minimals->[0] = TestCase::Minimal->new;
    $minimals->[0]{x} = 1;
    $minimals->[1] = TestCase::Minimal->new;
    $minimals->[1]{x} = 2;
    $minimals->[2] = TestCase::Minimal->new;
    $minimals->[2]{x} = 3;
    $minimals->[3] = TestCase::Minimal->new;
    $minimals->[3]{x} = 4;
    
    
    if ($minimals->[0]{x} == 1 && $minimals->[1]{x} == 2 && $minimals->[2]{x} == 3 && $minimals->[3]{x} == 4) {
      return 1;
    }
    
    return 0;
  }
  
  static method logical_not_false : int () {
    if (!0) {
      return 1;
    }
    
    return 0;
  }

  # logical or
  static method logical_or_both_true : int () {
    unless (1 || 1) {
      return 0;
    }
    
    if (1 || 1) {
      # OK
    }
    else {
      return 0;
    }
    
    if (1 || 0) {
      # OK
    }
    else {
      return 0;
    }
    
    if (0 || 1) {
      # OK
    }
    else {
      return 0;
    }
    
    if (0 || 0) {
      return 0;
    }
    else {
      # OK
    }

    return 1;
  }
  
  static method logical_or_left_true : int () {
    if (1 || 0) {
      return 1;
    }
    
    return 0;
  }

  static method logical_or_right_true : int () {
    if (0 || 1) {
      return 1;
    }
    
    return 0;
  }

  static method logical_or_both_false : int () {
    if (0 || 0) {
      return 0;
    }
    
    return 1;
  }
  
  # logical and
  static method logical_and_both_true : int () {
    unless (1 && 1) {
      return 0;
    }
    
    if (1 && 1) {
      # OK
    }
    else {
      return 0;
    }

    if (1 && 0) {
      return 0;
    }
    else {
      # OK
    }
    
    if (0 && 1) {
      return 0;
    }
    else {
      # OK
    }
    
    if (0 && 0) {
      return 0;
    }
    else {
      # OK
    }
    
    return 1;
  }
  
  static method logical_and_left_true : int () {
    if (1 && 0) {
      return 0;
    }
    
    return 1;
  }

  static method logical_and_right_true : int () {
    if (1 && 0) {
      return 0;
    }
    
    return 1;
  }

  static method logical_and_both_false : int () {
    if (0 && 0) {
      return 0;
    }
    
    return 1;
  }
  
  static method logical_not_assign : int () {
    {
      my $num = 0;
      
      my $bool = !($num = 5);
      
      unless ($bool == 0) {
        return 0;
      }
      
      unless ($num == 5) {
        return 0;
      }
      
    }
    
    {
      my $bool = !(my $num = 5);
      
      unless ($bool == 0) {
        return 0;
      }
      
      unless ($num == 5) {
        return 0;
      }
      
    }
    
    {
      my $bool = 0;
      if (!(my $x = Point->new(0, 2)->x)) {
        $bool = 1;
      }
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method logical_and_assign : int () {
    {
      my $num = 1;
      my $bool = 1 && ($num = 0);
      
      unless ($bool == 0) {
        return 0;
      }
      
      unless ($num == 0) {
        return 0;
      }
      
    }
    
    {
      my $num = 1;
      my $bool = 0 && ($num = 0);
      
      unless ($bool == 0) {
        return 0;
      }
      
      unless ($num == 1) {
        return 0;
      }
      
    }
    
    return 1;
  }
  
  static method logical_or_assign : int () {
    {
      my $num = 1;
      my $bool = 0 || ($num = 0);
      
      unless ($bool == 0) {
        return 0;
      }
      
      unless ($num == 0) {
        return 0;
      }
      
    }
    
    {
      my $num = 1;
      my $bool = 1 || ($num = 0);
      
      unless ($bool == 1) {
        return 0;
      }
      
      unless ($num == 1) {
        return 0;
      }
      
    }
    
    return 1;
  }

  static method logical_and_type : int () {
    
    # byte
    {
      my $bool = 1 && (byte)3;
      
      unless ($bool == 3) {
        return 0;
      }
    }
    
    # byte
    {
      my $bool = 1 && (short)3;
      
      unless ($bool == 3) {
        return 0;
      }
    }
    
    # int
    {
      my $bool = 1 && 3;
      
      unless ($bool == 3) {
        return 0;
      }
    }
    
    # long
    {
      my $bool = 1 && 3L;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    # float
    {
      my $bool = 1 && 3f;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    # double
    {
      my $bool = 1 && 3.0;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    # object
    {
      my $bool = 1 && Point->new;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    return 1;
  }

  static method logical_or_type : int () {
    
    # byte
    {
      my $bool = 0 || (byte)3;
      
      unless ($bool == 3) {
        return 0;
      }
    }
    
    # byte
    {
      my $bool = 0 || (short)3;
      
      unless ($bool == 3) {
        return 0;
      }
    }
    
    # int
    {
      my $bool = 0 || 3;
      
      unless ($bool == 3) {
        return 0;
      }
    }
    
    # long
    {
      my $bool = 0 || 3L;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    # float
    {
      my $bool = 0 || 3f;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    # double
    {
      my $bool = 0 || 3.0;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    # object
    {
      my $bool = 0 || Point->new;
      
      unless ($bool == 1) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method extra : int () {
    
    # https://github.com/yuki-kimoto/SPVM/issues/499
    {
      {
        my $ret = !!1 == !!0;
        
        unless ($ret == 0) {
          return 0;
        }
      }
      
      {
        my $ret = !!0 == !!1;
        
        unless ($ret == 0) {
          return 0;
        }
      }
    }
    
    return 1;
  }
}
