# Methods defined here are primitives because they either:
# * can't be expressed in Crystal (need to be expressed in LLVM). For example unary
#   and binary math operators fall into this category.
# * should always be inlined with an LLVM instruction for performance reasons, even
#   in non-release builds. An example of this is `Char#ord`, which could be implemented
#   in Crystal by assigning `self` to a variable and casting a pointer to it to `Int32`,
#   and then reading back the value.

class Object
  # Returns the **runtime** `Class` of an object.
  #
  # ```
  # 1.class       # => Int32
  # "hello".class # => String
  # ```
  #
  # Compare it with `typeof`, which returns the **compile-time** type of an object:
  #
  # ```
  # random_value = rand # => 0.627423
  # value = random_value < 0.5 ? 1 : "hello"
  # value         # => "hello"
  # value.class   # => String
  # typeof(value) # => Int32 | String
  # ```
  @[Primitive(:class)]
  def class
  end

  # :nodoc:
  @[Primitive(:object_crystal_type_id)]
  def crystal_type_id : Int32
  end
end

class Reference
  # Returns a `UInt64` that uniquely identifies this object.
  #
  # The returned value is the memory address of this object.
  #
  # ```
  # string = "hello"
  # string.object_id # => 4460249568
  #
  # pointer = Pointer(String).new(string.object_id)
  # string2 = pointer.as(String)
  # string2.object_id == string.object_id # => true
  # ```
  @[Primitive(:object_id)]
  def object_id : UInt64
  end

  # Performs basic initialization so that the given *address* is ready for use
  # as an object's instance data. Returns *address* cast to `self`'s type.
  #
  # More specifically, this is the part of object initialization that occurs
  # after memory allocation and before calling one of the `#initialize`
  # overloads. It zeroes the memory, sets up the type ID (necessary for dynamic
  # dispatch), and then runs all inline instance variable initializers.
  #
  # *address* must point to a suitably aligned buffer of at least
  # `instance_sizeof(self)` bytes.
  #
  # WARNING: This method is unsafe, as it assumes the caller is responsible for
  # managing the memory at the given *address* manually.
  #
  # ```
  # class Foo
  #   getter i : Int64
  #   getter str = "abc"
  #
  #   def initialize(@i)
  #   end
  #
  #   def self.alloc_with_libc(i : Int64)
  #     foo_buffer = LibC.malloc(instance_sizeof(Foo))
  #     foo = Foo.pre_initialize(foo_buffer)
  #     foo.i                  # => 0
  #     foo.str                # => "abc"
  #     (foo || "").is_a?(Foo) # => true
  #
  #     foo.initialize(i) # okay
  #     foo
  #   end
  # end
  #
  # foo = Foo.alloc_with_libc(123_i64)
  # foo.i # => 123
  # ```
  #
  # See also: `Reference.unsafe_construct`.
  @[Experimental("This API is still under development. Join the discussion about custom reference allocation at [#13481](https://github.com/crystal-lang/crystal/issues/13481).")]
  @[Primitive(:pre_initialize)]
  {% if compare_versions(Crystal::VERSION, "1.2.0") >= 0 %}
    def self.pre_initialize(address : Pointer)
      # This ensures `.pre_initialize` is instantiated for every subclass,
      # otherwise all calls will refer to the sole instantiation in
      # `Reference.class`. This is necessary when the receiver is a virtual
      # metaclass type. Apparently this works even for primitives
      \{% @type %}
    end
  {% else %}
    # Primitives cannot have a body until 1.2.0 (#11147)
    def self.pre_initialize(address : Pointer)
    end
  {% end %}
end

class Class
  # :nodoc:
  @[Primitive(:class_crystal_instance_type_id)]
  def crystal_instance_type_id : Int32
  end
end

struct Bool
  # Returns `true` if `self` is equal to *other*.
  @[Primitive(:binary)]
  def ==(other : Bool) : Bool
  end

  # Returns `true` if `self` is not equal to *other*.
  @[Primitive(:binary)]
  def !=(other : Bool) : Bool
  end
end

struct Char
  # Returns the codepoint of this char.
  #
  # The codepoint is the integer representation.
  # The Universal Coded Character Set (UCS) standard, commonly known as Unicode,
  # assigns names and meanings to numbers, these numbers are called codepoints.
  #
  # For values below and including 127 this matches the ASCII codes
  # and thus its byte representation.
  #
  # ```
  # 'a'.ord      # => 97
  # '\0'.ord     # => 0
  # '\u007f'.ord # => 127
  # '☃'.ord      # => 9731
  # ```
  @[Primitive(:convert)]
  def ord : Int32
  end

  {% for op, desc in {
                       "==" => "equal to",
                       "!=" => "not equal to",
                       "<"  => "less than",
                       "<=" => "less than or equal to",
                       ">"  => "greater than",
                       ">=" => "greater than or equal to",
                     } %}
    # Returns `true` if `self`'s codepoint is {{desc.id}} *other*'s codepoint.
    @[Primitive(:binary)]
    def {{op.id}}(other : Char) : Bool
    end
  {% end %}
end

struct Symbol
  # Returns `true` if `self` is equal to *other*.
  @[Primitive(:binary)]
  def ==(other : Symbol) : Bool
  end

  # Returns `true` if `self` is not equal to *other*.
  @[Primitive(:binary)]
  def !=(other : Symbol) : Bool
  end

  # Returns a unique number for this symbol.
  @[Primitive(:convert)]
  def to_i : Int32
  end

  # Returns the symbol's name as a String.
  #
  # ```
  # :foo.to_s           # => "foo"
  # :"hello world".to_s # => "hello world"
  # ```
  @[Primitive(:symbol_to_s)]
  def to_s : String
  end
end

struct Pointer(T)
  # Allocates `size * sizeof(T)` bytes from the system's heap initialized
  # to zero and returns a pointer to the first byte from that memory.
  # The memory is allocated by the `GC`, so when there are
  # no pointers to this memory, it will be automatically freed.
  #
  # ```
  # # Allocate memory for an Int32: 4 bytes
  # ptr = Pointer(Int32).malloc(1_u64)
  # ptr.value # => 0
  #
  # # Allocate memory for 10 Int32: 40 bytes
  # ptr = Pointer(Int32).malloc(10_u64)
  # ptr[0] # => 0
  # # ...
  # ptr[9] # => 0
  # ```
  #
  # The implementation uses `GC.malloc` if the compiler is aware that the
  # allocated type contains inner address pointers. See
  # `Crystal::Macros::TypeNode#has_inner_pointers?` for details.
  #
  # To override this implicit behaviour, `GC.malloc` and `GC.malloc_atomic`
  # can be used directly instead.
  @[Primitive(:pointer_malloc)]
  def self.malloc(size : UInt64)
  end

  # Returns a pointer that points to the given memory address.
  # This doesn't allocate memory.
  #
  # ```
  # ptr = Pointer(Int32).new(5678_u64)
  # ptr.address # => 5678
  # ```
  @[Primitive(:pointer_new)]
  def self.new(address : UInt64)
  end

  # Gets the value pointed by this pointer.
  #
  # ```
  # ptr = Pointer(Int32).malloc(4)
  # ptr.value = 42
  # ptr.value # => 42
  # ```
  #
  # WARNING: The pointer must be appropriately aligned, i.e. `address` must be
  # a multiple of `alignof(T)`. It is undefined behavior to load from a
  # misaligned pointer. Such reads should instead be done via a cast to
  # `Pointer(UInt8)`, which is guaranteed to have byte alignment:
  #
  # ```
  # # raises SIGSEGV on X86 if `ptr` is misaligned
  # x = ptr.as(UInt128*).value
  #
  # # okay, `ptr` can have any alignment
  # x = uninitialized UInt128
  # ptr.as(UInt8*).copy_to(pointerof(x).as(UInt8*), sizeof(typeof(x)))
  # ```
  @[Primitive(:pointer_get)]
  def value : T
  end

  # Sets the value pointed by this pointer.
  #
  # ```
  # ptr = Pointer(Int32).malloc(4)
  # ptr.value = 42
  # ptr.value # => 42
  # ```
  #
  # WARNING: The pointer must be appropriately aligned, i.e. `address` must be
  # a multiple of `alignof(T)`. It is undefined behavior to store to a
  # misaligned pointer. Such writes should instead be done via a cast to
  # `Pointer(UInt8)`, which is guaranteed to have byte alignment:
  #
  # ```
  # # raises SIGSEGV on X86 if `ptr` is misaligned
  # x = 123_u128
  # ptr.as(UInt128*).value = x
  #
  # # okay, `ptr` can have any alignment
  # ptr.as(UInt8*).copy_from(pointerof(x).as(UInt8*), sizeof(typeof(x)))
  # ```
  @[Primitive(:pointer_set)]
  def value=(value : T)
  end

  # Returns the address of this pointer.
  #
  # ```
  # ptr = Pointer(Int32).new(1234)
  # ptr.address # => 1234
  # ```
  @[Primitive(:pointer_address)]
  def address : UInt64
  end

  # Tries to change the size of the allocation pointed to by this pointer to *size*,
  # and returns that pointer.
  #
  # Since the space after the end of the block may be in use, realloc may find it
  # necessary to copy the block to a new address where more free space is available.
  # The value of realloc is the new address of the block.
  # If the block needs to be moved, realloc copies the old contents.
  #
  # Remember to always assign the value of realloc.
  #
  # ```
  # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4]
  # ptr = ptr.realloc(8_u8)
  # ptr # [1, 2, 3, 4, 0, 0, 0, 0]
  # ```
  @[Primitive(:pointer_realloc)]
  def realloc(size : UInt64) : self
  end

  # Returns a new pointer whose address is this pointer's address
  # incremented by `offset * sizeof(T)`.
  #
  # ```
  # ptr = Pointer(Int32).new(1234)
  # ptr.address # => 1234
  #
  # # An Int32 occupies four bytes
  # ptr2 = ptr + 1_u64
  # ptr2.address # => 1238
  # ```
  @[Primitive(:pointer_add)]
  def +(offset : Int64) : self
  end

  # Returns how many T elements are there between this pointer and *other*.
  # That is, this is `(self.address - other.address) / sizeof(T)`.
  #
  # ```
  # ptr1 = Pointer(Int32).malloc(4)
  # ptr2 = ptr1 + 2
  # ptr2 - ptr1 # => 2
  # ```
  @[Primitive(:pointer_diff)]
  def -(other : self) : Int64
  end
end

struct Slice(T)
  # Constructs a read-only `Slice` constant from the given *args*. The slice
  # contents are stored in the program's read-only data section.
  #
  # If `T` is specified, it must be one of the `Number::Primitive` types and
  # cannot be a union. The *args* must all be number literals that fit into
  # `T`'s range, as if they are autocasted into `T`.
  #
  # If `T` is not specified, it is inferred from *args*, which must all be
  # number literals of the same type, and cannot be empty.
  #
  # ```
  # x = Slice(UInt8).literal(0, 1, 4, 9, 16, 25)
  # x            # => Slice[0, 1, 4, 9, 16, 25]
  # x.read_only? # => true
  #
  # Slice.literal(1_u8, 2_u8, 3_u8) # => Bytes[1, 2, 3]
  # ```
  @[Experimental("Slice literals are still under development. Join the discussion at [#2886](https://github.com/crystal-lang/crystal/issues/2886).")]
  @[Primitive(:slice_literal)]
  def self.literal(*args)
  end
end

struct Proc
  # Invokes this `Proc` and returns the result.
  #
  # ```
  # add = ->(x : Int32, y : Int32) { x + y }
  # add.call(1, 2) # => 3
  # ```
  @[Primitive(:proc_call)]
  @[Raises]
  def call(*args : *T) : R
  end
end

# All `Number` methods are defined on concrete structs (for example `Int32`, `UInt8`, etc.),
# never on `Number`, `Int` or `Float` because we don't want to handle a primitive for
# other types that could extend these types (for example `BigInt`): if we do that
# a compiler crash will happen.
#
# A similar logic is applied to method arguments: they are always concrete, to avoid
# unintentionally handling a `BigInt` and have a crash. We also can't have an argument
# be a union, because the codegen primitives always consider primitive types, never
# unions.

{% begin %}
  {% ints = %w(Int8 Int16 Int32 Int64 Int128 UInt8 UInt16 UInt32 UInt64 UInt128) %}
  {% floats = %w(Float32 Float64) %}
  {% nums = %w(Int8 Int16 Int32 Int64 Int128 UInt8 UInt16 UInt32 UInt64 UInt128 Float32 Float64) %}
  {% binaries = {"+" => "adding", "-" => "subtracting", "*" => "multiplying"} %}

  {% for num in nums %}
    struct {{num.id}}
      {% for name, type in {
                             to_i: Int32, to_u: UInt32, to_f: Float64,
                             to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,
                             to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,
                             to_f32: Float32, to_f64: Float64,
                           } %}
        # Returns `self` converted to `{{type}}`.
        # Raises `OverflowError` in case of overflow.
        @[::Primitive(:convert)]
        @[Raises]
        def {{name.id}} : {{type}}
        end

        # Returns `self` converted to `{{type}}`.
        # In case of overflow
        # {% if ints.includes?(num) %} a wrapping is performed.
        # {% elsif type < Int %} the result is undefined.
        # {% else %} infinity is returned.
        # {% end %}
        @[::Primitive(:unchecked_convert)]
        def {{name.id}}! : {{type}}
        end
      {% end %}

      {% for num2 in nums %}
        {% for op, desc in {
                             "==" => "equal to",
                             "!=" => "not equal to",
                             "<"  => "less than",
                             "<=" => "less than or equal to",
                             ">"  => "greater than",
                             ">=" => "greater than or equal to",
                           } %}
          # Returns `true` if `self` is {{desc.id}} *other*{% if op == "!=" && (!ints.includes?(num) || !ints.includes?(num2)) %}
          # or if `self` and *other* are unordered{% end %}.
          @[::Primitive(:binary)]
          def {{op.id}}(other : {{num2.id}}) : Bool
          end
        {% end %}
      {% end %}
    end
  {% end %}

  {% for int in ints %}
    struct {{int.id}}
      # Returns a `Char` that has the unicode codepoint of `self`,
      # without checking if this integer is in the range valid for
      # chars (`0..0xd7ff` and `0xe000..0x10ffff`). In case of overflow
      # a wrapping is performed.
      #
      # You should never use this method unless `chr` turns out to
      # be a bottleneck.
      #
      # ```
      # 97.unsafe_chr # => 'a'
      # ```
      @[::Primitive(:unchecked_convert)]
      def unsafe_chr : Char
      end

      {% for int2 in ints %}
        {% for op, desc in binaries %}
          # Returns the result of {{desc.id}} `self` and *other*.
          # Raises `OverflowError` in case of overflow.
          @[::Primitive(:binary)]
          @[Raises]
          def {{op.id}}(other : {{int2.id}}) : self
          end

          # Returns the result of {{desc.id}} `self` and *other*.
          # In case of overflow a wrapping is performed.
          @[::Primitive(:binary)]
          def &{{op.id}}(other : {{int2.id}}) : self
          end
        {% end %}

        # Returns the result of performing a bitwise OR of `self`'s and *other*'s bits.
        @[::Primitive(:binary)]
        def |(other : {{int2.id}}) : self
        end

        # Returns the result of performing a bitwise AND of `self`'s and *other*'s bits.
        @[::Primitive(:binary)]
        def &(other : {{int2.id}}) : self
        end

        # Returns the result of performing a bitwise XOR of `self`'s and *other*'s bits.
        @[::Primitive(:binary)]
        def ^(other : {{int2.id}}) : self
        end

        # :nodoc:
        @[::Primitive(:binary)]
        def unsafe_shl(other : {{int2.id}}) : self
        end

        # :nodoc:
        @[::Primitive(:binary)]
        def unsafe_shr(other : {{int2.id}}) : self
        end

        # :nodoc:
        @[::Primitive(:binary)]
        def unsafe_div(other : {{int2.id}}) : self
        end

        # :nodoc:
        @[::Primitive(:binary)]
        def unsafe_mod(other : {{int2.id}}) : self
        end
      {% end %}

      {% for float in floats %}
        {% for op, desc in binaries %}
          # Returns the result of {{desc.id}} `self` and *other*.
          @[::Primitive(:binary)]
          def {{op.id}}(other : {{float.id}}) : {{float.id}}
          end
        {% end %}
      {% end %}
    end
  {% end %}

  {% for float in floats %}
    struct {{float.id}}
      {% for num in nums %}
        {% for op, desc in binaries %}
          # Returns the result of {{desc.id}} `self` and *other*.
          @[::Primitive(:binary)]
          def {{op.id}}(other : {{num.id}}) : self
          end
        {% end %}

        # Returns the float division of `self` and *other*.
        @[::Primitive(:binary)]
        def fdiv(other : {{num.id}}) : self
        end
      {% end %}

      # Returns the result of division `self` and *other*.
      @[::Primitive(:binary)]
      def /(other : {{float.id}}) : {{float.id}}
      end
    end
  {% end %}
{% end %}
