<commit_before>module PyCall
  Py_LT = 0
  Py_LE = 1
  Py_EQ = 2
  Py_NE = 3
  Py_GT = 4
  Py_GE = 5

  RICH_COMPARISON_OPCODES = {
    :<  => Py_LT,
    :<= => Py_LE,
    :== => Py_EQ,
    :!= => Py_NE,
    :>  => Py_GT,
    :>= => Py_GE
  }.freeze

  module PyObjectMethods
    def rich_compare(other, op)
      opcode = RICH_COMPARISON_OPCODES[op]
      raise ArgumentError, "Unknown comparison op: #{op}" unless opcode

      other = Conversions.from_ruby(other) unless other.kind_of?(PyObject)
      return other.null? if self.null?
      return false if other.null?

      value = LibPython.PyObject_RichCompare(self, other, opcode)
      raise "Unable to compare: #{self} #{op} #{other}" if value.null?
      value.to_ruby
    end

    RICH_COMPARISON_OPCODES.keys.each do |op|
      define_method(op) {|other| rich_compare(other, op) }
    end

    def py_none?
      to_ptr == LibPython.Py_None.to_ptr
    end

    def kind_of?(klass)
      case klass
      when PyObject, PyTypeObject
        Types.pyisinstance(self, klass)
      else
        super
      end
    end
  end

  class PyObject < FFI::Struct
    include PyObjectMethods

    def self.null
      new(FFI::Pointer::NULL)
    end

    alias __aref__ []
    alias __aset__ []=

    def [](*indices)
      if indices.length == 1
        indices = indices[0]
      else
        indices = PyCall.tuple(*indices)
      end
      PyCall.getitem(self, indices)
    end

    def []=(*indices_and_value)
      value = indices_and_value.pop
      indices = indices_and_value
      if indices.length == 1
        indices = indices[0]
      else
        indices = PyCall.tuple(*indices)
      end
      PyCall.setitem(self, indices, value)
    end

    def +(other)
      value = LibPython.PyNumber_Add(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def -(other)
      value = LibPython.PyNumber_Subtract(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def *(other)
      value = LibPython.PyNumber_Multiply(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def /(other)
      value = LibPython.PyNumber_TrueDivide(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def coerce(other)
      [Conversions.from_ruby(other), self]
    end

    def call(*args, **kwargs)
      args = PyCall::Tuple[*args]
      kwargs = kwargs.empty? ? PyObject.null : PyCall::Dict.new(kwargs).__pyobj__
      res = LibPython.PyObject_Call(self, args.__pyobj__, kwargs)
      return res.to_ruby if LibPython.PyErr_Occurred().null?
      raise PyError.fetch
    end

    def method_missing(name, *args, **kwargs)
      if PyCall.hasattr?(self, name)
        PyCall.getattr(self, name)<commit_msg>Fix +-*/ operators about operand conversions<commit_after>module PyCall
  Py_LT = 0
  Py_LE = 1
  Py_EQ = 2
  Py_NE = 3
  Py_GT = 4
  Py_GE = 5

  RICH_COMPARISON_OPCODES = {
    :<  => Py_LT,
    :<= => Py_LE,
    :== => Py_EQ,
    :!= => Py_NE,
    :>  => Py_GT,
    :>= => Py_GE
  }.freeze

  module PyObjectMethods
    def rich_compare(other, op)
      opcode = RICH_COMPARISON_OPCODES[op]
      raise ArgumentError, "Unknown comparison op: #{op}" unless opcode

      other = Conversions.from_ruby(other) unless other.kind_of?(PyObject)
      return other.null? if self.null?
      return false if other.null?

      value = LibPython.PyObject_RichCompare(self, other, opcode)
      raise "Unable to compare: #{self} #{op} #{other}" if value.null?
      value.to_ruby
    end

    RICH_COMPARISON_OPCODES.keys.each do |op|
      define_method(op) {|other| rich_compare(other, op) }
    end

    def py_none?
      to_ptr == LibPython.Py_None.to_ptr
    end

    def kind_of?(klass)
      case klass
      when PyObject, PyTypeObject
        Types.pyisinstance(self, klass)
      else
        super
      end
    end
  end

  class PyObject < FFI::Struct
    include PyObjectMethods

    def self.null
      new(FFI::Pointer::NULL)
    end

    alias __aref__ []
    alias __aset__ []=

    def [](*indices)
      if indices.length == 1
        indices = indices[0]
      else
        indices = PyCall.tuple(*indices)
      end
      PyCall.getitem(self, indices)
    end

    def []=(*indices_and_value)
      value = indices_and_value.pop
      indices = indices_and_value
      if indices.length == 1
        indices = indices[0]
      else
        indices = PyCall.tuple(*indices)
      end
      PyCall.setitem(self, indices, value)
    end

    def +(other)
      other = Conversions.from_ruby(other)
      value = LibPython.PyNumber_Add(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def -(other)
      other = Conversions.from_ruby(other)
      value = LibPython.PyNumber_Subtract(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def *(other)
      other = Conversions.from_ruby(other)
      value = LibPython.PyNumber_Multiply(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def /(other)
      other = Conversions.from_ruby(other)
      value = LibPython.PyNumber_TrueDivide(self, other)
      return value.to_ruby unless value.null?
      raise PyError.fetch
    end

    def coerce(other)
      [Conversions.from_ruby(other), self]
    end

    def call(*args, **kwargs)
      args = PyCall::Tuple[*args]
      kwargs = kwargs.empty? ? PyObject.null : PyCall::Dict.new(kwargs).__pyobj__
      res = LibPython.PyObject_Call(self, args.__pyobj__, kwargs)
      return res.to_ruby if LibPython.PyErr_Occurred().null?
      raise PyError.fetch
    end

    def method_missing(name, *args, **kwargs)
      if PyCall.hasattr?(self, name)
        PyCall.getattr(self, name)
{'fraction_non_alphanumeric': 0.10240655401945725, 'fraction_numerical': 0.0034135518006485747, 'mean_word_length': 2.45926800472255, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4474731', 'n_tokens_mistral': 2025, 'n_tokens_neox': 1945, 'n_words': 449}