#include "lang/internal.h"
#include "ops/ops.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;
}
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);
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);
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);
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);
return make_bool(as_f64(a) <= as_f64(b) ? 1 : 0);
}
int is_comparable(ray_t* x) {
return is_numeric(x) || is_temporal(x);
}
ray_t* ray_eq_fn(ray_t* a, ray_t* b) {
int na = (!a || RAY_ATOM_IS_NULL(a)), nb = (!b || 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", NULL);
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) {
int na = (!a || RAY_ATOM_IS_NULL(a)), nb = (!b || 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", NULL);
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; \
bool* da = (bool*)ray_data(a); \
bool* db = (bool*)ray_data(b); \
bool* dr = (bool*)ray_data(r); \
for (int64_t i = 0; i < n; i++) dr[i] = da[i] op db[i]; \
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 < 2) return ray_error("arity", "expected at least 2 args, got %lld", (long long)n);
ray_t* acc = eval_and_short(args[0]);
if (!acc || RAY_IS_ERR(acc)) 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 < 2) return ray_error("arity", "expected at least 2 args, got %lld", (long long)n);
ray_t* acc = eval_and_short(args[0]);
if (!acc || RAY_IS_ERR(acc)) 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;
}
return make_bool(is_truthy(x) ? 0 : 1);
}