////////////////////////////////
// NOTE(allen): Float Constant Functions

function F32
inf_F32(void){
  U32 u = 0x7f800000;
  F32 *p = (F32*)&u;
  return(*p);
}

function F32
neg_inf_F32(void){
  U32 u = 0xff800000;
  F32 *p = (F32*)&u;
  return(*p);
}

function B32
is_inf_or_nan_F32(F32 x){
  U32 u = *(U32*)&x;
  B32 result = ((u&0x7f800000) == 0x7f800000);
  return(result);
}

function F64
inf_F64(void){
  U64 u = 0x7ff0000000000000;
  F64 *p = (F64*)&u;
  return(*p);
}

function F64
neg_inf_F64(void){
  U64 u = 0xfff0000000000000;
  F64 *p = (F64*)&u;
  return(*p);
}

function B64
is_inf_or_nan_F64(F64 x){
  U64 u = *(U64*)&x;
  B64 result = ((u&0x7ff0000000000000) == 0x7ff0000000000000);
  return(result);
}

////////////////////////////////
// NOTE(allen): Math Functions

function F32
abs_F32(F32 x){
  union{ F32 f; U32 u; } r;
  r.f = x;
  r.u &= 0x7fffffff;
  return(r.f);
}

function F64
abs_F64(F64 x){
  union{ F64 f; U64 u; } r;
  r.f = x;
  r.u &= 0x7fffffffffffffff;
  return(r.f);
}

function F32
sign_F32(F32 x){
  union{ F32 f; U32 u; } r;
  r.f = x;
  F32 result = (r.u&0x80000000)?-1.f:1.f;
  return(result);
}

function F64
sign_F64(F64 x){
  union{ F64 f; U32 u; } r;
  r.f = x;
  F64 result = (r.u&0x8000000000000000)?-1.:1.;
  return(result);
}

#include <math.h>

function F32
sqrt_F32(F32 x){
  return(sqrtf(x));
}

function F32
sin_F32(F32 x){
  return(sinf(x));
}

function F32
cos_F32(F32 x){
  return(cosf(x));
}

function F32
tan_F32(F32 x){
  return(tanf(x));
}

function F32
atan_F32(F32 x){
  return(atanf(x));
}

function F32
atan2_F32(F32 x, F32 y){
  return(atan2f(y, x));
}

function F32
ln_F32(F32 x){
  return(logf(x));
}

function F32
pow_F32(F32 base, F32 x){
  return(powf(base, x));
}

function F64
sqrt_F64(F64 x){
  return(sqrt(x));
}

function F64
sin_F64(F64 x){
  return(sin(x));
}

function F64
cos_F64(F64 x){
  return(cos(x));
}

function F64
tan_F64(F64 x){
  return(tan(x));
}

function F64
atan_F64(F64 x){
  return(atanf(x));
}

function F64
ln_F64(F64 x){
  return(log(x));
}

function F64
pow_F64(F64 base, F64 x){
  return(powf(base, x));
}

function F32
lerp(F32 a, F32 t, F32 b){
  F32 x = a + (b - a)*t;
  return(x);
}

function F32
unlerp(F32 a, F32 x, F32 b){
  F32 t = 0.f;
  if (a != b){
    t = (x - a)/(b - a);
  }
  return(t);
}

function F32
lerp_range(RangeF32 r, F32 t){
  F32 x = r.min + (r.max - r.min)*t;
  return(x);
}

function F32
trunc_F32(F32 x){
  // TODO(allen): does this always work?
  //              can we do better?
  return((F32)(S32)x);
}

function F32
floor_F32(F32 x){
  F32 r = (F32)((S32)x);
  if (r > x){
    r -= 1.f;
  }
  return(r);
}

function F32
ceil_F32(F32 x){
  F32 r = (F32)((S32)x);
  if (r < x){
    r += 1.f;
  }
  return(r);
}

function F32
mod_F32(F32 x, F32 m){
  Assert(m > 0.f);
  //                               +   |         -
  F32 q = (x/m);       //        (x/m) |       (x/m)
  S32 q_s32 = (S32)q;  //   floor(x/m) |   ceil(x/m)
  F32 r = m*(F32)q_s32;// m*floor(x/m) | m*ceil(x/m)
  //                        <= x       |   >= x
  F32 d = x - r;
  if (d < 0){
    d += m;
  }
  return(d);
}

////////////////////////////////
// NOTE(allen): Compound Type Functions

function V2S32
v2s32(S32 x, S32 y){
  V2S32 r = {x, y};
  return(r);
}

function V2F32
v2f32(F32 x, F32 y){
  V2F32 r = {x, y};
  return(r);
}

function V3F32
v3f32(F32 x, F32 y, F32 z){
  V3F32 r = {x, y, z};
  return(r);
}

function V4F32
v4f32(F32 x, F32 y, F32 z, F32 w){
  V4F32 r = {x, y, z, w};
  return(r);
}

function RangeF32
rangef32(F32 min, F32 max){
  RangeF32 r = {min, max};
  if (max < min){
    r.min = max;
    r.max = min;
  }
  return(r);
}

function RectF32
rectf32(F32 x0, F32 y0, F32 x1, F32 y1){
  RectF32 r = {x0, y0, x1, y1};
  if (x1 < x0){
    r.x0 = x1;
    r.x1 = x0;
  }
  if (y1 < y0){
    r.y0 = y1;
    r.y1 = y0;
  }
  return(r);
}

function RectF32
rectf32_vec(V2F32 min, V2F32 max){
  RectF32 r = rectf32(min.x, min.y, max.x, max.y);
  return(r);
}

function RectF32
rectf32_range(RangeF32 x, RangeF32 y){
  RectF32 r = rectf32(x.min, y.min, x.max, y.max);
  return(r);
}

function RectF32
rectf32_from_rects32(RectS32 *r){
  RectF32 result = {(F32)r->x0, (F32)r->y0, (F32)r->x1, (F32)r->y1};
  return(result);
}

function V2F32
v2f32_add(V2F32 a, V2F32 b){
  V2F32 result = {a.x + b.x, a.y + b.y};
  return(result);
}

function V2F32
v2f32_sub(V2F32 a, V2F32 b){
  V2F32 result = {a.x - b.x, a.y - b.y};
  return(result);
}

function V2F32
v2f32_scalar_mul(F32 s, V2F32 v){
  V2F32 result = {s*v.x, s*v.y};
  return(result);
}

function V2F32
v2f32_polar(F32 theta, F32 radius){
  V2F32 result = {radius*cos_F32(theta), radius*sin_F32(theta)};
  return(result);
}

function F32
angle_from_v2f32(V2F32 v){
  F32 result = atan2_F32(v.x, v.y);
  return(result);
}

function V2S32
dim_from_rects32(RectS32 r){
  V2S32 result = {r.x1 - r.x0, r.y1 - r.y0};
  return(result);
}

////////////////////////////////
// NOTE(allen): Character Functions

function B32
str8_char_is_slash(U8 c){
  return(c == '/' || c == '\\');
}

function U8
str8_char_uppercase(U8 c){
  if ('a' <= c && c <= 'z'){
    c += 'A' - 'a';
  }
  return(c);
}

function U8
str8_char_lowercase(U8 c){
  if ('A' <= c && c <= 'Z'){
    c += 'a' - 'A';
  }
  return(c);
}

////////////////////////////////
// NOTE(allen): String Constructor Functions

function String8
str8(U8 *str, U64 size){
  String8 result = {str, size};
  return(result);
}

function String8
str8_range(U8 *first, U8 *opl){
  String8 result = {first, (U64)(opl - first)};
  return(result);
}

function String8
str8_cstring(U8 *cstr){
  U8 *ptr = cstr;
  for (;*ptr != 0; ptr += 1);
  String8 result = str8_range(cstr, ptr);
  return(result);
}

function String8
str8_prefix(String8 str, U64 size){
  U64 size_clamped = ClampTop(size, str.size);
  String8 result = {str.str, size_clamped};
  return(result);
}

function String8
str8_chop(String8 str, U64 amount){
  U64 amount_clamped = ClampTop(amount, str.size);
  U64 remaining_size = str.size - amount_clamped;
  String8 result = {str.str, remaining_size};
  return(result);
}

function String8
str8_postfix(String8 str, U64 size){
  U64 size_clamped = ClampTop(size, str.size);
  U64 skip_to = str.size - size_clamped;
  String8 result = {str.str + skip_to, size_clamped};
  return(result);
}

function String8
str8_skip(String8 str, U64 amount){
  U64 amount_clamped = ClampTop(amount, str.size);
  U64 remaining_size = str.size - amount_clamped;
  String8 result = {str.str + amount_clamped, remaining_size};
  return(result);
}

function String8
str8_substr_opl(String8 str, U64 first, U64 opl){
  U64 opl_clamped = ClampTop(opl, str.size);
  U64 first_clamped = ClampTop(first, opl_clamped);
  String8 result = {str.str + first_clamped, opl_clamped - first_clamped};
  return(result);
}

function String8
str8_substr_size(String8 str, U64 first, U64 size){
  String8 result = str8_substr_opl(str, first, first + size);
  return(result);
}

function String16
str16(U16 *str, U64 size){
  String16 result = {str, size};
  return(result);
}

function String16
str16_cstring(U16 *cstr){
  U16 *ptr = cstr;
  for (;*ptr != 0; ptr += 1);
  String16 result = {cstr, (U64)(ptr - cstr)};
  return(result);
}

////////////////////////////////
// NOTE(allen): String Path Helpers

function String8
str8_chop_last_slash(String8 string){
  String8 result = string;
  if (string.size > 0){
    // pos one past last slash
    U64 pos = string.size;
    for (S64 i = string.size - 1; i >= 0; i -= 1){
      if (str8_char_is_slash(string.str[i])){
        pos = i;
        break;
      }
    }
    
    // chop result string
    result.size = pos;
  }
  return(result);
}

////////////////////////////////
// NOTE(allen): Helpers for Serializer/Deserializer Code

function B32
str8_read(String8 data, U64 off, void *dst, U64 size){
  B32 result = 0;
  if (off + size <= data.size){
    result = 1;
    MemoryCopy(dst, data.str + off, size);
  }
  return(result);
}
