package SPVM::CORE {
  use SPVM::Complex_2f;
  use SPVM::Complex_2d;
  
  native sub cadd : SPVM::Complex_2d ($z1 : SPVM::Complex_2d, $z2 : SPVM::Complex_2d);
  native sub csub : SPVM::Complex_2d ($z1 : SPVM::Complex_2d, $z2 : SPVM::Complex_2d);
  native sub cmul : SPVM::Complex_2d ($z1 : SPVM::Complex_2d, $z2 : SPVM::Complex_2d);
  native sub cdiv : SPVM::Complex_2d ($z1 : SPVM::Complex_2d, $z2 : SPVM::Complex_2d);
  native sub caddf : SPVM::Complex_2f ($z1 : SPVM::Complex_2f, $z2 : SPVM::Complex_2f);
  native sub csubf : SPVM::Complex_2f ($z1 : SPVM::Complex_2f, $z2 : SPVM::Complex_2f);
  native sub cmulf : SPVM::Complex_2f ($z1 : SPVM::Complex_2f, $z2 : SPVM::Complex_2f);
  native sub cdivf : SPVM::Complex_2f ($z1 : SPVM::Complex_2f, $z2 : SPVM::Complex_2f);
  
  sub copy_string : string ($string : string) {
    my $bytes = (byte[])$string;
    
    return (string)$bytes;
  }
  
  sub copy_byte_array : byte[] ($nums : byte[]) {
    my $length = @$nums;
    
    my $new_nums = new byte[$length];
    
    for (my $i = 0; $i < $length; $i++) {
      $new_nums->[$i] = $nums->[$i];
    }
    
    return $new_nums;
  }

  sub copy_short_array : short[] ($nums : short[]) {
    my $length = @$nums;
    
    my $new_nums = new short[$length];
    
    for (my $i = 0; $i < $length; $i++) {
      $new_nums->[$i] = $nums->[$i];
    }
    
    return $new_nums;
  }
  
  sub copy_int_array : int[] ($nums : int[]) {
    my $length = @$nums;
    
    my $new_nums = new int[$length];
    
    for (my $i = 0; $i < $length; $i++) {
      $new_nums->[$i] = $nums->[$i];
    }
    
    return $new_nums;
  }

  sub copy_long_array : long[] ($nums : long[]) {
    my $length = @$nums;
    
    my $new_nums = new long[$length];
    
    for (my $i = 0; $i < $length; $i++) {
      $new_nums->[$i] = $nums->[$i];
    }
    
    return $new_nums;
  }

  sub copy_float_array : float[] ($nums : float[]) {
    my $length = @$nums;
    
    my $new_nums = new float[$length];
    
    for (my $i = 0; $i < $length; $i++) {
      $new_nums->[$i] = $nums->[$i];
    }
    
    return $new_nums;
  }

  sub copy_double_array : double[] ($nums : double[]) {
    my $length = @$nums;
    
    my $new_nums = new double[$length];
    
    for (my $i = 0; $i < $length; $i++) {
      $new_nums->[$i] = $nums->[$i];
    }
    
    return $new_nums;
  }

  sub copy_object_array : object[] ($objects : object[]) {
    my $length = @$objects;
    
    my $new_objects = new object[$length];
    
    for (my $i = 0; $i < $length; $i++) {
      $new_objects->[$i] = $objects->[$i];
    }
    
    return $new_objects;
  }

  sub new_fcomplex_array : SPVM::Complex_2f[] ($re_values : float[], $im_values : float[]) {
    if (@$re_values != @$im_values) {
      croak "Real values length is different from imaginary values";
    }
    
    my $z_array = new SPVM::Complex_2f[@$re_values];
    for (my $i = 0; $i < @$re_values; $i++) {
      $z_array->[$i]{re} = $re_values->[$i];
      $z_array->[$i]{im} = $im_values->[$i];
    }
    
    return $z_array;
  }
  
  sub new_dcomplex_array : SPVM::Complex_2d[] ($re_values : double[], $im_values : double[]) {
    if (@$re_values != @$im_values) {
      croak "Real values length is different from imaginary values";
    }
    
    my $z_array = new SPVM::Complex_2d[@$re_values];
    for (my $i = 0; $i < @$re_values; $i++) {
      $z_array->[$i]{re} = $re_values->[$i];
      $z_array->[$i]{im} = $im_values->[$i];
    }
    
    return $z_array;
  }

  native sub new_fcomplex : SPVM::Complex_2f ($re : float, $im : float);
  native sub new_dcomplex : SPVM::Complex_2d ($re : double, $im : double);
  sub join : string ($sep : string, $strings : string[]) {
    my $join = "";
    
    for (my $i = 0; $i < @$strings; $i++) {
      my $string = $strings->[$i];
      $join .= $string;
      if ($i != @$strings - 1) {
        $join .= $sep;
      }
    }
    
    return $join;
  }
  native sub print : void ($value : string);
  native sub warn : void ($value : string);
  native sub time : long ();

  native sub INFINITYF : float ();
  native sub NANF : float ();
  
  native sub isinff : int($x : float);
  native sub isfinitef : int($x : float);
  native sub isnanf : int ($x : float);
  
  native sub INFINITY : double ();
  native sub NAN : double ();
  
  native sub isinf : int ($x : double);
  native sub isfinite : int ($x : double);
  native sub isnan : int ($x : double);

  native sub PI : double ();
  native sub E : double ();
  
  # Trigonometric functions
  native sub acos : double ($x : double);
  native sub asin : double($x : double);
  native sub atan : double ($x : double);
  native sub atan2 : double ($y : double, $x : double);
  native sub cos : double ($x : double);
  native sub sin : double ($x : double);
  native sub tan : double ($x : double);
  
  # Hyperbolic functions
  native sub acosh : double ($x : double);
  native sub asinh : double ($x : double);
  native sub atanh : double ($x : double);
  native sub cosh : double ($x : double);
  native sub sinh : double ($x : double);
  native sub tanh : double ($x : double);
  
  # Exponential functions and logarithmic functions
  native sub exp : double ($x : double);
  native sub exp2 : double ($x : double);
  native sub expm1 : double ($x : double);
  native sub log : double ($x : double);
  native sub log10 : double ($x : double);
  native sub log1p : double ($x : double);
  
  # Exponentiation functions and Absolute value functions
  native sub abs : int ($x : int);
  native sub labs : long ($x : long);
  native sub cbrt : double ($x : double);
  native sub fabs : double ($x : double);
  native sub hypot : double ($x : double, $y : double);
  native sub pow : double ($x : double, $y : double);
  native sub sqrt : double ($x : double);
  
  # Error function and Gamma function
  native sub erf : double ($x : double);
  native sub erfc : double ($x : double);
  native sub lgamma : double ($x : double);
  native sub tgamma : double ($x : double);

  # Nearest integer function
  native sub ceil : double ($x : double);
  native sub floor : double ($x : double);
  native sub nearbyint : double ($x : double);
  native sub round : double ($x : double);
  
  # Surplus functions
  native sub remainder : double ($x1 : double, $x2 : double);

  native sub INT8_MIN : byte ();
  native sub INT8_MAX : byte ();

  native sub INT16_MIN : short ();
  native sub INT16_MAX : short ();

  native sub INT32_MIN : int ();
  native sub INT32_MAX : int ();

  native sub INT64_MIN : long ();
  native sub INT64_MAX : long ();

  native sub FLT_MAX : float ();
  native sub FLT_MIN : float();
  
  native sub DBL_MAX : double ();
  native sub DBL_MIN : double ();
}
