#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) {
if (ray_has_timespan(a) || ray_has_timespan(b)) return ray_timespan_binary(OP_GT, a, 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) {
if (ray_has_timespan(a) || ray_has_timespan(b)) return ray_timespan_binary(OP_LT, a, 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) {
if (ray_has_timespan(a) || ray_has_timespan(b)) return ray_timespan_binary(OP_GE, a, 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) {
if (ray_has_timespan(a) || ray_has_timespan(b)) return ray_timespan_binary(OP_LE, a, 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);
}
static int minmax_rank(int8_t t) {
switch (t) {
case -RAY_BOOL: return 0;
case -RAY_U8: return 1;
case -RAY_I16: return 2;
case -RAY_I32: return 3;
case -RAY_I64: return 4;
case -RAY_F32: return 5;
case -RAY_F64: return 6;
default: return -1;
}
}
static int8_t minmax_type(ray_t* a, ray_t* b) {
if (a->type == b->type) return 0;
int ra = minmax_rank(a->type), rb = minmax_rank(b->type);
if (ra >= 0 && rb >= 0) return ra > rb ? a->type : b->type;
if ((a->type == -RAY_DATE && b->type == -RAY_TIMESTAMP) ||
(a->type == -RAY_TIMESTAMP && b->type == -RAY_DATE))
return -RAY_TIMESTAMP;
return 0;
}
static ray_t* minmax_promote(ray_t* x, int8_t t) {
if (RAY_ATOM_IS_NULL(x)) return ray_typed_null(t);
if (t == -RAY_TIMESTAMP) return ray_timestamp(temporal_as_ns(x));
if (t == -RAY_F64) return make_f64(as_f64(x));
if (t == -RAY_F32) return ray_f32((float)as_f64(x));
int64_t v = x->type == -RAY_BOOL ? (int64_t)(x->b8 != 0) : as_i64(x);
return make_typed_int(t, v);
}
static ray_t* minmax_pick(ray_t* a, ray_t* b, ray_t* cmp) {
if (!cmp || RAY_IS_ERR(cmp)) return cmp ? cmp : ray_error("oom", NULL);
ray_t* out = cmp->b8 ? a : b;
ray_release(cmp);
int8_t t = minmax_type(a, b);
if (t && out->type != t) return minmax_promote(out, t);
ray_retain(out);
return out;
}
ray_t* ray_min2_fn(ray_t* a, ray_t* b) { return minmax_pick(a, b, ray_lte_fn(a, b)); }
ray_t* ray_max2_fn(ray_t* a, ray_t* b) { return minmax_pick(a, b, ray_gte_fn(a, b)); }
ray_t* ray_eq_fn(ray_t* a, ray_t* b) {
if (ray_has_timespan(a) || ray_has_timespan(b)) return ray_timespan_binary(OP_EQ, a, 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) {
if (ray_has_timespan(a) || ray_has_timespan(b)) return ray_timespan_binary(OP_NE, a, 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)
static ray_t* logic_map(ray_binary_fn fn, const char* name, ray_t* a, ray_t* b) {
int ac = is_collection(a), bc = is_collection(b);
if (ac && bc && ray_len(a) != ray_len(b))
return ray_error("length", "%s: operand lengths must match, got %lld and %lld",
name, (long long)ray_len(a), (long long)ray_len(b));
int64_t n = ac ? ray_len(a) : ray_len(b);
ray_t* out = ray_list_new(n > 0 ? n : 1);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
for (int64_t i = 0; i < n; i++) {
int la = 0, lb = 0;
ray_t* ea = ac ? collection_elem(a, i, &la) : a;
ray_t* eb = bc ? collection_elem(b, i, &lb) : b;
ray_t* r = (RAY_IS_ERR(ea) || RAY_IS_ERR(eb))
? ray_error("type", NULL) : fn(ea, eb);
if (la) ray_release(ea);
if (lb) ray_release(eb);
if (!r || RAY_IS_ERR(r)) { ray_release(out); return r ? r : ray_error("oom", NULL); }
out = ray_list_append(out, r);
ray_release(r);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
}
if (n == 0) { ray_release(out); return ray_vec_new(RAY_BOOL, 0); }
return ray_list_collapse_bool(out);
}
static inline bool is_bool_vec(ray_t* x) {
return ray_is_vec(x) && x->type == RAY_BOOL;
}
ray_t* ray_and_fn(ray_t* a, ray_t* b) {
if (is_bool_vec(a) && is_bool_vec(b)) {
if (a->len != b->len)
return ray_error("length", "and: operand lengths must match, got %lld and %lld",
(long long)a->len, (long long)b->len);
BOOL_VEC_BINOP(a, b, &);
}
if (is_bool_vec(a) && ray_is_atom(b))
BOOL_VEC_SCALAR_L(a, is_truthy(b), &&);
if (ray_is_atom(a) && is_bool_vec(b))
BOOL_VEC_SCALAR_L(b, is_truthy(a), &&);
if (is_collection(a) || is_collection(b))
return logic_map(ray_and_fn, "and", a, b);
return make_bool((is_truthy(a) && is_truthy(b)) ? 1 : 0);
}
ray_t* ray_or_fn(ray_t* a, ray_t* b) {
if (is_bool_vec(a) && is_bool_vec(b)) {
if (a->len != b->len)
return ray_error("length", "or: operand lengths must match, got %lld and %lld",
(long long)a->len, (long long)b->len);
BOOL_VEC_BINOP(a, b, |);
}
if (is_bool_vec(a) && ray_is_atom(b))
BOOL_VEC_SCALAR_L(a, is_truthy(b), ||);
if (ray_is_atom(a) && is_bool_vec(b))
BOOL_VEC_SCALAR_L(b, is_truthy(a), ||);
if (is_collection(a) || is_collection(b))
return logic_map(ray_or_fn, "or", a, b);
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;
}
if (is_list(x)) {
int64_t n = ray_len(x);
if (n == 0) return ray_vec_new(RAY_BOOL, 0);
ray_t* out = ray_list_new(n);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
ray_t** e = (ray_t**)ray_data(x);
for (int64_t i = 0; i < n; i++) {
ray_t* r = ray_not_fn(e[i]);
if (!r || RAY_IS_ERR(r)) { ray_release(out); return r ? r : ray_error("oom", NULL); }
out = ray_list_append(out, r);
ray_release(r);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
}
return ray_list_collapse_bool(out);
}
return make_bool(is_truthy(x) ? 0 : 1);
}