#include "lang/internal.h"
#include "ops/ops.h"
#include "lang/format.h"
#include <assert.h>
int char_str_cmp(ray_t* a, ray_t* b, int *out) {
const char *ap, *bp;
size_t al, bl;
int a_cs = (a->type == -RAY_STR);
int b_cs = (b->type == -RAY_STR);
if (!a_cs || !b_cs) return -1;
ap = ray_str_ptr(a); al = ray_str_len(a);
bp = ray_str_ptr(b); bl = ray_str_len(b);
size_t mn = al < bl ? al : bl;
int c = memcmp(ap, bp, mn);
if (c != 0) { *out = c; return 0; }
*out = (al > bl) ? 1 : (al < bl) ? -1 : 0;
return 0;
}
int sym_atom_cmp(ray_t* a, ray_t* b) {
if (a->i64 == b->i64) return 0;
ray_t* sa = ray_sym_str(a->i64);
ray_t* sb = ray_sym_str(b->i64);
assert(sa && sb && "sym_atom_cmp: corrupted intern table — "
"valid SYM atom must resolve to interned string");
int r = ray_str_cmp(sa, sb);
ray_release(sa);
ray_release(sb);
return r;
}
static inline int both_int_atoms(const ray_t* a, const ray_t* b) {
int ia = a->type == -RAY_I64 || a->type == -RAY_I32 || a->type == -RAY_I16 || a->type == -RAY_U8;
int ib = b->type == -RAY_I64 || b->type == -RAY_I32 || b->type == -RAY_I16 || b->type == -RAY_U8;
return ia && ib;
}
ray_t* ray_gt_fn(ray_t* a, ray_t* b) {
{ int c; if (char_str_cmp(a, b, &c) == 0) return make_bool(c > 0 ? 1 : 0); }
if (a->type == -RAY_SYM && b->type == -RAY_SYM)
return make_bool(sym_atom_cmp(a, b) > 0 ? 1 : 0);
if (a->type == -RAY_GUID && b->type == -RAY_GUID)
return make_bool(memcmp(ray_data(a->obj), ray_data(b->obj), 16) > 0 ? 1 : 0);
if (is_temporal(a) && is_temporal(b)) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return make_bool(RAY_ATOM_IS_NULL(b) && !RAY_ATOM_IS_NULL(a) ? 1 : 0);
return make_bool(temporal_as_ns(a) > temporal_as_ns(b) ? 1 : 0);
}
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot compare %s and %s",
ray_type_name(a->type), ray_type_name(b->type));
int na = RAY_ATOM_IS_NULL(a), nb = RAY_ATOM_IS_NULL(b);
if (na && nb) return make_bool(0);
if (na) return make_bool(0);
if (nb) return make_bool(1);
if (both_int_atoms(a, b)) return make_bool(as_i64(a) > as_i64(b) ? 1 : 0);
return make_bool(as_f64(a) > as_f64(b) ? 1 : 0);
}
ray_t* ray_lt_fn(ray_t* a, ray_t* b) {
{ int c; if (char_str_cmp(a, b, &c) == 0) return make_bool(c < 0 ? 1 : 0); }
if (a->type == -RAY_SYM && b->type == -RAY_SYM)
return make_bool(sym_atom_cmp(a, b) < 0 ? 1 : 0);
if (a->type == -RAY_GUID && b->type == -RAY_GUID)
return make_bool(memcmp(ray_data(a->obj), ray_data(b->obj), 16) < 0 ? 1 : 0);
if (is_temporal(a) && is_temporal(b)) {
if (RAY_ATOM_IS_NULL(a) || RAY_ATOM_IS_NULL(b))
return make_bool(RAY_ATOM_IS_NULL(a) && !RAY_ATOM_IS_NULL(b) ? 1 : 0);
return make_bool(temporal_as_ns(a) < temporal_as_ns(b) ? 1 : 0);
}
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot compare %s and %s",
ray_type_name(a->type), ray_type_name(b->type));
int na = RAY_ATOM_IS_NULL(a), nb = RAY_ATOM_IS_NULL(b);
if (na && nb) return make_bool(0);
if (na) return make_bool(1);
if (nb) return make_bool(0);
if (both_int_atoms(a, b)) return make_bool(as_i64(a) < as_i64(b) ? 1 : 0);
return make_bool(as_f64(a) < as_f64(b) ? 1 : 0);
}
ray_t* ray_gte_fn(ray_t* a, ray_t* b) {
{ int c; if (char_str_cmp(a, b, &c) == 0) return make_bool(c >= 0 ? 1 : 0); }
if (a->type == -RAY_SYM && b->type == -RAY_SYM)
return make_bool(sym_atom_cmp(a, b) >= 0 ? 1 : 0);
if (a->type == -RAY_GUID && b->type == -RAY_GUID)
return make_bool(memcmp(ray_data(a->obj), ray_data(b->obj), 16) >= 0 ? 1 : 0);
if (is_temporal(a) && is_temporal(b)) {
if (RAY_ATOM_IS_NULL(a) && RAY_ATOM_IS_NULL(b)) return make_bool(1);
if (RAY_ATOM_IS_NULL(a)) return make_bool(0);
if (RAY_ATOM_IS_NULL(b)) return make_bool(1);
return make_bool(temporal_as_ns(a) >= temporal_as_ns(b) ? 1 : 0);
}
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot compare %s and %s",
ray_type_name(a->type), ray_type_name(b->type));
int na = RAY_ATOM_IS_NULL(a), nb = RAY_ATOM_IS_NULL(b);
if (na && nb) return make_bool(1);
if (na) return make_bool(0);
if (nb) return make_bool(1);
if (both_int_atoms(a, b)) return make_bool(as_i64(a) >= as_i64(b) ? 1 : 0);
return make_bool(as_f64(a) >= as_f64(b) ? 1 : 0);
}
ray_t* ray_lte_fn(ray_t* a, ray_t* b) {
{ int c; if (char_str_cmp(a, b, &c) == 0) return make_bool(c <= 0 ? 1 : 0); }
if (a->type == -RAY_SYM && b->type == -RAY_SYM)
return make_bool(sym_atom_cmp(a, b) <= 0 ? 1 : 0);
if (a->type == -RAY_GUID && b->type == -RAY_GUID)
return make_bool(memcmp(ray_data(a->obj), ray_data(b->obj), 16) <= 0 ? 1 : 0);
if (is_temporal(a) && is_temporal(b)) {
if (RAY_ATOM_IS_NULL(a) && RAY_ATOM_IS_NULL(b)) return make_bool(1);
if (RAY_ATOM_IS_NULL(a)) return make_bool(1);
if (RAY_ATOM_IS_NULL(b)) return make_bool(0);
return make_bool(temporal_as_ns(a) <= temporal_as_ns(b) ? 1 : 0);
}
if (!is_numeric(a) || !is_numeric(b))
return ray_error("type", "cannot compare %s and %s",
ray_type_name(a->type), ray_type_name(b->type));
int na = RAY_ATOM_IS_NULL(a), nb = RAY_ATOM_IS_NULL(b);
if (na && nb) return make_bool(1);
if (na) return make_bool(1);
if (nb) return make_bool(0);
if (both_int_atoms(a, b)) return make_bool(as_i64(a) <= as_i64(b) ? 1 : 0);
return make_bool(as_f64(a) <= as_f64(b) ? 1 : 0);
}
ray_t* ray_min2_fn(ray_t* a, ray_t* b) {
ray_t* cmp = ray_lte_fn(a, b);
if (!cmp || RAY_IS_ERR(cmp)) return cmp ? cmp : ray_error("oom", NULL);
ray_t* out = cmp->b8 ? a : b;
ray_retain(out);
ray_release(cmp);
return out;
}
ray_t* ray_max2_fn(ray_t* a, ray_t* b) {
ray_t* cmp = ray_gte_fn(a, b);
if (!cmp || RAY_IS_ERR(cmp)) return cmp ? cmp : ray_error("oom", NULL);
ray_t* out = cmp->b8 ? a : b;
ray_retain(out);
ray_release(cmp);
return out;
}
ray_t* ray_eq_fn(ray_t* a, ray_t* b) {
RAY_ASSERT_VALUE(a); RAY_ASSERT_VALUE(b);
int na = RAY_ATOM_IS_NULL(a), nb = RAY_ATOM_IS_NULL(b);
if (na && nb) return make_bool(1);
if (na || nb) return make_bool(0);
{ int c; if (char_str_cmp(a, b, &c) == 0) return make_bool(c == 0 ? 1 : 0); }
if (a->type == -RAY_BOOL && b->type == -RAY_BOOL)
return make_bool(a->b8 == b->b8 ? 1 : 0);
if (a->type == -RAY_SYM && b->type == -RAY_SYM)
return make_bool(a->i64 == b->i64 ? 1 : 0);
if (a->type == -RAY_GUID && b->type == -RAY_GUID)
return make_bool(memcmp(ray_data(a->obj), ray_data(b->obj), 16) == 0 ? 1 : 0);
if (is_temporal(a) && is_temporal(b))
return make_bool(temporal_as_ns(a) == temporal_as_ns(b) ? 1 : 0);
if (!is_numeric(a) || !is_numeric(b)) return ray_error("type", "=: incomparable operand types, got %s and %s", ray_type_name(a->type), ray_type_name(b->type));
if (is_float_op(a, b))
return make_bool(as_f64(a) == as_f64(b) ? 1 : 0);
return make_bool(as_i64(a) == as_i64(b) ? 1 : 0);
}
ray_t* ray_neq_fn(ray_t* a, ray_t* b) {
RAY_ASSERT_VALUE(a); RAY_ASSERT_VALUE(b);
int na = RAY_ATOM_IS_NULL(a), nb = RAY_ATOM_IS_NULL(b);
if (na && nb) return make_bool(0);
if (na || nb) return make_bool(1);
{ int c; if (char_str_cmp(a, b, &c) == 0) return make_bool(c != 0 ? 1 : 0); }
if (a->type == -RAY_BOOL && b->type == -RAY_BOOL)
return make_bool(a->b8 != b->b8 ? 1 : 0);
if (a->type == -RAY_SYM && b->type == -RAY_SYM)
return make_bool(a->i64 != b->i64 ? 1 : 0);
if (a->type == -RAY_GUID && b->type == -RAY_GUID)
return make_bool(memcmp(ray_data(a->obj), ray_data(b->obj), 16) != 0 ? 1 : 0);
if (is_temporal(a) && is_temporal(b))
return make_bool(temporal_as_ns(a) != temporal_as_ns(b) ? 1 : 0);
if (!is_numeric(a) || !is_numeric(b)) return ray_error("type", "<>: incomparable operand types, got %s and %s", ray_type_name(a->type), ray_type_name(b->type));
if (is_float_op(a, b))
return make_bool(as_f64(a) != as_f64(b) ? 1 : 0);
return make_bool(as_i64(a) != as_i64(b) ? 1 : 0);
}
#define BOOL_VEC_BINOP(a, b, op) do { \
int64_t n = a->len < b->len ? a->len : b->len; \
ray_t* r = ray_vec_new(RAY_BOOL, n); \
if (RAY_IS_ERR(r)) return r; \
const uint8_t* restrict da = (const uint8_t*)ray_data(a); \
const uint8_t* restrict db = (const uint8_t*)ray_data(b); \
uint8_t* restrict dr = (uint8_t*)ray_data(r); \
for (int64_t i = 0; i < n; i++) \
dr[i] = (uint8_t)((da[i] != 0) op (db[i] != 0)); \
r->len = n; \
return r; \
} while(0)
#define BOOL_VEC_SCALAR_L(vec, sv, op) do { \
int64_t n = vec->len; \
ray_t* r = ray_vec_new(RAY_BOOL, n); \
if (RAY_IS_ERR(r)) return r; \
bool* dv = (bool*)ray_data(vec); \
bool* dr = (bool*)ray_data(r); \
for (int64_t i = 0; i < n; i++) dr[i] = dv[i] op sv; \
r->len = n; \
return r; \
} while(0)
ray_t* ray_and_fn(ray_t* a, ray_t* b) {
if (ray_is_vec(a) && a->type == RAY_BOOL && ray_is_vec(b) && b->type == RAY_BOOL)
BOOL_VEC_BINOP(a, b, &);
if (ray_is_vec(a) && a->type == RAY_BOOL && ray_is_atom(b))
BOOL_VEC_SCALAR_L(a, is_truthy(b), &&);
if (ray_is_atom(a) && ray_is_vec(b) && b->type == RAY_BOOL)
BOOL_VEC_SCALAR_L(b, is_truthy(a), &&);
return make_bool((is_truthy(a) && is_truthy(b)) ? 1 : 0);
}
ray_t* ray_or_fn(ray_t* a, ray_t* b) {
if (ray_is_vec(a) && a->type == RAY_BOOL && ray_is_vec(b) && b->type == RAY_BOOL)
BOOL_VEC_BINOP(a, b, |);
if (ray_is_vec(a) && a->type == RAY_BOOL && ray_is_atom(b))
BOOL_VEC_SCALAR_L(a, is_truthy(b), ||);
if (ray_is_atom(a) && ray_is_vec(b) && b->type == RAY_BOOL)
BOOL_VEC_SCALAR_L(b, is_truthy(a), ||);
return make_bool((is_truthy(a) || is_truthy(b)) ? 1 : 0);
}
static ray_t* eval_and_short(ray_t* arg) {
ray_t* v = ray_eval(arg);
if (!v || RAY_IS_ERR(v)) return v;
if (ray_is_lazy(v)) v = ray_lazy_materialize(v);
return v;
}
ray_t* ray_and_vary_fn(ray_t** args, int64_t n) {
if (n < 1) return ray_error("arity", "expected at least 1 arg, got %lld", (long long)n);
ray_t* acc = eval_and_short(args[0]);
if (!acc || RAY_IS_ERR(acc)) return acc;
if (n == 1) return acc;
if (ray_is_atom(acc) && !is_truthy(acc)) return acc;
for (int64_t i = 1; i < n; i++) {
ray_t* v = eval_and_short(args[i]);
if (!v || RAY_IS_ERR(v)) { ray_release(acc); return v; }
ray_t* next = ray_and_fn(acc, v);
ray_release(acc);
ray_release(v);
if (!next || RAY_IS_ERR(next)) return next;
acc = next;
if (ray_is_atom(acc) && !is_truthy(acc)) return acc;
}
return acc;
}
ray_t* ray_or_vary_fn(ray_t** args, int64_t n) {
if (n < 1) return ray_error("arity", "expected at least 1 arg, got %lld", (long long)n);
ray_t* acc = eval_and_short(args[0]);
if (!acc || RAY_IS_ERR(acc)) return acc;
if (n == 1) return acc;
if (ray_is_atom(acc) && is_truthy(acc)) return acc;
for (int64_t i = 1; i < n; i++) {
ray_t* v = eval_and_short(args[i]);
if (!v || RAY_IS_ERR(v)) { ray_release(acc); return v; }
ray_t* next = ray_or_fn(acc, v);
ray_release(acc);
ray_release(v);
if (!next || RAY_IS_ERR(next)) return next;
acc = next;
if (ray_is_atom(acc) && is_truthy(acc)) return acc;
}
return acc;
}
ray_t* ray_not_fn(ray_t* x) {
if (ray_is_vec(x) && x->type == RAY_BOOL) {
int64_t n = x->len;
ray_t* r = ray_vec_new(RAY_BOOL, n);
if (RAY_IS_ERR(r)) return r;
bool* src = (bool*)ray_data(x);
bool* dr = (bool*)ray_data(r);
for (int64_t i = 0; i < n; i++) dr[i] = !src[i];
r->len = n;
return r;
}
if (ray_is_vec(x) && (x->type == RAY_U8 || x->type == RAY_I16 ||
x->type == RAY_I32 || x->type == RAY_I64 ||
x->type == RAY_F32 || x->type == RAY_F64 ||
x->type == RAY_DATE || x->type == RAY_TIME ||
x->type == RAY_TIMESTAMP)) {
int64_t n = x->len;
ray_t* r = ray_vec_new(RAY_BOOL, n);
if (RAY_IS_ERR(r)) return r;
uint8_t* dr = (uint8_t*)ray_data(r);
const void* src = ray_data(x);
for (int64_t i = 0; i < n; i++) {
bool z;
switch (x->type) {
case RAY_U8: z = ((const uint8_t*)src)[i] == 0; break;
case RAY_I16: z = ((const int16_t*)src)[i] == 0; break;
case RAY_I32: case RAY_DATE: case RAY_TIME:
z = ((const int32_t*)src)[i] == 0; break;
case RAY_F32: z = ((const float*)src)[i] == 0.0f; break;
case RAY_F64: z = ((const double*)src)[i] == 0.0; break;
default: z = ((const int64_t*)src)[i] == 0; break;
}
dr[i] = z || ray_vec_is_null(x, i);
}
r->len = n;
return r;
}
return make_bool(is_truthy(x) ? 0 : 1);
}