#include "lang/internal.h"
ray_t* ray_add_fn(ray_t* a, ray_t* b) {
if (is_temporal(a) && is_numeric(b) && b->type != -RAY_F64) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return ray_typed_null(a->type);
int64_t v = as_i64(b);
if (a->type == -RAY_DATE) return ray_date(a->i64 + v);
if (a->type == -RAY_TIME) return ray_time(a->i64 + v);
if (a->type == -RAY_TIMESTAMP) return ray_timestamp(a->i64 + v);
}
if (is_numeric(a) && a->type != -RAY_F64 && is_temporal(b)) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return ray_typed_null(b->type);
int64_t v = as_i64(a);
if (b->type == -RAY_DATE) return ray_date(b->i64 + v);
if (b->type == -RAY_TIME) return ray_time(b->i64 + v);
if (b->type == -RAY_TIMESTAMP) return ray_timestamp(b->i64 + v);
}
if ((a->type == -RAY_F64 && is_temporal(b)) || (is_temporal(a) && b->type == -RAY_F64))
return ray_error("type", NULL);
if (is_numeric(a) && RAY_ATOM_IS_NULL(a) && is_temporal(b))
return ray_error("type", NULL);
if (is_temporal(a) && is_numeric(b) && RAY_ATOM_IS_NULL(b))
return ray_error("type", NULL);
if (a->type == -RAY_DATE && b->type == -RAY_TIME) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIMESTAMP);
return ray_timestamp(a->i64 * 86400000000000LL + b->i64 * 1000000LL);
}
if (a->type == -RAY_TIME && b->type == -RAY_DATE) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIMESTAMP);
return ray_timestamp(b->i64 * 86400000000000LL + a->i64 * 1000000LL);
}
if (a->type == -RAY_TIME && b->type == -RAY_TIME) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIME);
return ray_time(a->i64 + b->i64);
}
if (a->type == -RAY_TIME && b->type == -RAY_TIMESTAMP) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIMESTAMP);
return ray_timestamp(b->i64 + a->i64 * 1000000LL);
}
if (a->type == -RAY_TIMESTAMP && b->type == -RAY_TIME) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIMESTAMP);
return ray_timestamp(a->i64 + b->i64 * 1000000LL);
}
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot add %s and %s",
ray_type_name(a->type), ray_type_name(b->type));
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return null_for_promoted(a, b);
if (is_float_op(a, b)) return make_f64(as_f64(a) + as_f64(b));
int8_t rt = promote_int_type(a, b);
return make_typed_int(rt, as_i64(a) + as_i64(b));
}
ray_t* ray_sub_fn(ray_t* a, ray_t* b) {
if (is_temporal(a) && is_numeric(b)) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return ray_typed_null(a->type);
}
if (is_numeric(a) && is_temporal(b)) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return ray_typed_null(b->type);
}
if (a->type == -RAY_DATE && is_numeric(b)) {
return ray_date(a->i64 - as_i64(b));
}
if (a->type == -RAY_DATE && b->type == -RAY_DATE) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_I32);
return ray_i32((int32_t)(a->i64 - b->i64));
}
if (a->type == -RAY_DATE && b->type == -RAY_TIME) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIMESTAMP);
return ray_timestamp(a->i64 * 86400000000000LL - b->i64 * 1000000LL);
}
if (a->type == -RAY_TIME && is_numeric(b)) {
return ray_time(a->i64 - as_i64(b));
}
if (is_numeric(a) && b->type == -RAY_TIME) {
return ray_time(as_i64(a) - b->i64);
}
if (a->type == -RAY_TIME && b->type == -RAY_TIME) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIME);
return ray_time(a->i64 - b->i64);
}
if (a->type == -RAY_TIMESTAMP && is_numeric(b)) {
return ray_timestamp(a->i64 - as_i64(b));
}
if (a->type == -RAY_TIMESTAMP && b->type == -RAY_TIME) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIMESTAMP);
return ray_timestamp(a->i64 - b->i64 * 1000000LL);
}
if (a->type == -RAY_TIMESTAMP && b->type == -RAY_TIMESTAMP) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_I64);
return make_i64(a->i64 - b->i64);
}
if (a->type == -RAY_TIMESTAMP && b->type == -RAY_DATE)
return ray_error("type", NULL);
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot subtract %s and %s",
ray_type_name(a->type), ray_type_name(b->type));
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return null_for_promoted(a, b);
if (is_float_op(a, b)) {
double r = as_f64(a) - as_f64(b);
if (r == 0.0) r = 0.0;
return make_f64(r);
}
int8_t rt = promote_int_type_right(a, b);
return make_typed_int(rt, as_i64(a) - as_i64(b));
}
ray_t* ray_mul_fn(ray_t* a, ray_t* b) {
if (is_numeric(a) && b->type == -RAY_TIME) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIME);
return ray_time(as_i64(a) * b->i64);
}
if (a->type == -RAY_TIME && is_numeric(b)) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return ray_typed_null(-RAY_TIME);
return ray_time(a->i64 * as_i64(b));
}
if (a->type == -RAY_TIME && b->type == -RAY_TIME)
return ray_error("type", NULL);
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot multiply %s and %s",
ray_type_name(a->type), ray_type_name(b->type));
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) return null_for_promoted(a, b);
if (is_float_op(a, b)) return make_f64(as_f64(a) * as_f64(b));
int8_t rt = promote_int_type(a, b);
return make_typed_int(rt, as_i64(a) * as_i64(b));
}
ray_t* ray_div_fn(ray_t* a, ray_t* b) {
if (is_temporal(a) && is_numeric(b)) {
if (RAY_ATOM_IS_NULL(b) || RAY_ATOM_IS_NULL(a))
return ray_typed_null(a->type);
if (is_float_op(a, b)) {
double bv = as_f64(b);
if (bv == 0.0)
return ray_typed_null(a->type);
int64_t result = (int64_t)floor((double)a->i64 / bv);
if (a->type == -RAY_TIME) return ray_time(result);
if (a->type == -RAY_DATE) return ray_date(result);
return ray_timestamp(result);
}
int64_t bv = as_i64(b);
if (bv == 0)
return ray_typed_null(a->type);
int64_t av = a->i64;
int64_t q = av / bv;
if ((av ^ bv) < 0 && q * bv != av) q--;
if (a->type == -RAY_TIME) return ray_time(q);
if (a->type == -RAY_DATE) return ray_date(q);
return ray_timestamp(q);
}
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot divide %s by %s",
ray_type_name(a->type), ray_type_name(b->type));
if (a->type == -RAY_U8) {
uint8_t bv = (uint8_t)as_i64(b);
if (bv == 0 || RAY_ATOM_IS_NULL(b)) return make_u8(0);
if (RAY_ATOM_IS_NULL(a)) return make_u8(0);
return make_u8((uint8_t)((uint8_t)as_i64(a) / bv));
}
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return ray_typed_null(a->type);
if (is_float_op(a, b)) {
double bv = as_f64(b);
if (bv == 0.0)
return ray_typed_null(a->type);
double result = floor(as_f64(a) / bv);
if (a->type == -RAY_F64) return make_f64(result);
if (a->type == -RAY_I16) return make_i16((int16_t)(int64_t)result);
if (a->type == -RAY_I32) return make_i32((int32_t)(int64_t)result);
if (result >= (double)INT64_MIN && result <= (double)INT64_MAX)
return make_i64((int64_t)result);
return ray_typed_null(-RAY_I64);
}
int64_t bv = as_i64(b);
if (bv == 0)
return ray_typed_null(a->type);
int64_t av = as_i64(a);
int64_t q = av / bv;
if ((av ^ bv) < 0 && q * bv != av) q--;
if (a->type == -RAY_I16) return make_i16((int16_t)q);
if (a->type == -RAY_I32) return make_i32((int32_t)q);
return make_i64(q);
}
ray_t* ray_mod_fn(ray_t* a, ray_t* b) {
if (is_temporal(a) && is_numeric(b)) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return ray_typed_null(a->type);
int64_t bv;
if (b->type == -RAY_F64) {
double bvf = b->f64;
if (bvf == 0.0)
return ray_typed_null(a->type);
bv = (int64_t)bvf;
} else {
bv = as_i64(b);
}
if (bv == 0)
return ray_typed_null(a->type);
int64_t av = a->i64;
int64_t q = av / bv;
if ((av ^ bv) < 0 && q * bv != av) q--;
int64_t result = av - bv * q;
if (a->type == -RAY_TIME) return ray_time(result);
if (a->type == -RAY_DATE) return ray_date(result);
return ray_timestamp(result);
}
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot mod %s by %s",
ray_type_name(a->type), ray_type_name(b->type));
if (b->type == -RAY_U8) {
uint8_t bv = b->u8;
if (bv == 0) return make_u8(0);
return make_u8((uint8_t)((uint8_t)as_i64(a) % bv));
}
if (a->type == -RAY_U8) {
}
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b)) {
int8_t rt = (b->type == -RAY_F64 || a->type == -RAY_F64) ? -RAY_F64 : b->type;
return ray_typed_null(rt);
}
if (is_float_op(a, b)) {
double av = as_f64(a), bv = as_f64(b);
if (bv == 0.0) {
int8_t rt = (b->type == -RAY_F64 || a->type == -RAY_F64) ? -RAY_F64 : b->type;
return ray_typed_null(rt);
}
double result = av - bv * floor(av / bv);
if (fabs(result) < 1e-12 || fabs(result - fabs(bv)) < 1e-12) result = bv > 0 ? 0.0 : -0.0;
if (b->type == -RAY_F64 || a->type == -RAY_F64) return make_f64(result);
if (b->type == -RAY_I32) return make_i32((int32_t)(int64_t)result);
if (b->type == -RAY_I16) return make_i16((int16_t)(int64_t)result);
return make_i64((int64_t)result);
}
int64_t av = as_i64(a), bv = as_i64(b);
if (bv == 0)
return ray_typed_null(b->type);
int64_t q = av / bv;
if ((av ^ bv) < 0 && q * bv != av) q--;
int64_t result = av - bv * q;
if (b->type == -RAY_I32) return make_i32((int32_t)result);
if (b->type == -RAY_I16) return make_i16((int16_t)result);
if (b->type == -RAY_U8) return make_u8((uint8_t)result);
return make_i64(result);
}
ray_t* ray_neg_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) { ray_retain(x); return x; }
if (x->type == -RAY_F64) return make_f64(-x->f64);
if (x->type == -RAY_I64) {
if (RAY_UNLIKELY(x->i64 == INT64_MIN)) return ray_typed_null(-RAY_I64);
return make_i64(-x->i64);
}
if (x->type == -RAY_I32) {
if (RAY_UNLIKELY(x->i32 == INT32_MIN)) return ray_typed_null(-RAY_I32);
return make_i32(-x->i32);
}
if (x->type == -RAY_I16) {
if (RAY_UNLIKELY(x->i16 == INT16_MIN)) return ray_typed_null(-RAY_I16);
return make_i16(-x->i16);
}
return ray_error("type", NULL);
}
ray_t* ray_round_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) return ray_typed_null(-RAY_F64);
if (x->type == -RAY_F64) return make_f64(round(x->f64));
if (is_numeric(x)) return make_f64(round(as_f64(x)));
return ray_error("type", NULL);
}
ray_t* ray_floor_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) { ray_retain(x); return x; }
if (x->type == -RAY_F64) return make_f64(floor(x->f64));
if (is_numeric(x)) { ray_retain(x); return x; }
return ray_error("type", NULL);
}
ray_t* ray_ceil_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) { ray_retain(x); return x; }
if (x->type == -RAY_F64) return make_f64(ceil(x->f64));
if (is_numeric(x)) { ray_retain(x); return x; }
return ray_error("type", NULL);
}
ray_t* ray_abs_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) { ray_retain(x); return x; }
if (x->type == -RAY_F64) return make_f64(fabs(x->f64));
if (x->type == -RAY_I64) {
if (RAY_UNLIKELY(x->i64 == INT64_MIN)) return ray_typed_null(-RAY_I64);
return make_i64(x->i64 < 0 ? -x->i64 : x->i64);
}
if (x->type == -RAY_I32) {
if (RAY_UNLIKELY(x->i32 == INT32_MIN)) return ray_typed_null(-RAY_I32);
return make_i32(x->i32 < 0 ? -x->i32 : x->i32);
}
if (x->type == -RAY_I16) {
if (RAY_UNLIKELY(x->i16 == INT16_MIN)) return ray_typed_null(-RAY_I16);
return make_i16(x->i16 < 0 ? -x->i16 : x->i16);
}
return ray_error("type", NULL);
}
ray_t* ray_sqrt_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) return ray_typed_null(-RAY_F64);
if (x->type == -RAY_F64) return make_f64(sqrt(x->f64));
if (is_numeric(x)) return make_f64(sqrt(as_f64(x)));
return ray_error("type", NULL);
}
ray_t* ray_log_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) return ray_typed_null(-RAY_F64);
if (x->type == -RAY_F64) return make_f64(log(x->f64));
if (is_numeric(x)) return make_f64(log(as_f64(x)));
return ray_error("type", NULL);
}
ray_t* ray_exp_fn(ray_t* x) {
if (RAY_ATOM_IS_NULL(x)) return ray_typed_null(-RAY_F64);
if (x->type == -RAY_F64) return make_f64(exp(x->f64));
if (is_numeric(x)) return make_f64(exp(as_f64(x)));
return ray_error("type", NULL);
}