diff --git a/examples/rfl/datalog.rfl b/examples/rfl/datalog.rfl index 0b354e20..dd22a3a5 100644 --- a/examples/rfl/datalog.rfl +++ b/examples/rfl/datalog.rfl @@ -75,7 +75,7 @@ ; ── 10. sym-name: readable output ─────────────────────── (println "--- sym-name: convert intern IDs ---") (set p (pull db 1)) -(set name-id (get p 1)) +(set name-id (get p 'name)) (println name-id) (println (sym-name name-id)) diff --git a/examples/rfl/flips.rfl b/examples/rfl/flips.rfl index 8e6125b5..aad74b86 100644 --- a/examples/rfl/flips.rfl +++ b/examples/rfl/flips.rfl @@ -12,13 +12,13 @@ (set flips (except (.csv.read [DATE I64 I64 SYMBOL I64 SYMBOL TIME F64 F64 I64 - I64 I64 SYMBOL GUID SYMBOL C8 C8 I64 I64 F64 I64 SYMBOL - SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL C8 SYMBOL SYMBOL F64 - C8 C8 C8 C8 C8 C8 SYMBOL GUID F64 C8 I64 C8 - SYMBOL SYMBOL I64 C8 C8 SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL + I64 I64 SYMBOL GUID SYMBOL STR STR I64 I64 F64 I64 SYMBOL + SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL STR SYMBOL SYMBOL F64 + STR STR STR STR STR STR SYMBOL GUID F64 STR I64 STR + SYMBOL SYMBOL I64 STR STR SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL SYMBOL - SYMBOL SYMBOL F64 C8 I64 I64 I64 F64 F64 F64 F64 I64 I64 I64 I64 + SYMBOL SYMBOL F64 STR I64 I64 I64 F64 F64 F64 F64 I64 I64 I64 I64 SYMBOL I64 I64 SYMBOL SYMBOL SYMBOL TIME SYMBOL I64] csvpath) 'date)) diff --git a/examples/rfl/journal.rfl b/examples/rfl/journal.rfl index 95293b92..901811c0 100644 --- a/examples/rfl/journal.rfl +++ b/examples/rfl/journal.rfl @@ -1,15 +1,31 @@ -;; Journaling example -(set f (fn [x y] (println "RES: %" (+ x y)))) - -;; Write journal -(set h (.ipc.open "/tmp/jou.log")) -(write h (list 'f 1 2)) -(write h (list 'f 2 3)) -(write h (list 'f 3 4)) -(.ipc.close h) - -;; Replay journal -(set h (.ipc.open "/tmp/jou.log")) -(read h) -(.ipc.close h) +;; Transaction-log journaling example (the `.log.*` API). +;; +;; In production you start the server with `-l ` (async) or `-L ` +;; (sync, fsync per write): every mutation that arrives over IPC is appended to +;; .log and replayed automatically on restart, so user state survives a +;; crash. The `.log.*` verbs expose that machinery directly. + +(set base "/tmp/jou_ex") +(.sys.exec "rm -f /tmp/jou_ex*") + +;; Open a journal in async mode (writes buffered; use 'sync for fsync-per-write). +(.log.open 'async base) + +;; Append entries. `.log.write` serializes its (already-evaluated) argument +;; into the log; replay re-evaluates each payload in order. +(.log.write 100) +(.log.write [1 2 3]) +(.log.write {sym: 'AAPL px: 191.5}) + +;; Flush buffered writes to disk and close. +(.log.sync) +(.log.close) + +;; Inspect the log: (.log.validate path) -> [chunks valid_bytes]. +(println "validate %.log -> %" base (.log.validate (format "%.log" base))) + +;; Replay the journal — returns the number of entries replayed. +(println "replayed % entries" (.log.replay (format "%.log" base))) + +(.sys.exec "rm -f /tmp/jou_ex*") (exit 0) diff --git a/examples/rfl/window.rfl b/examples/rfl/window.rfl index f4dffbb1..86987f07 100644 --- a/examples/rfl/window.rfl +++ b/examples/rfl/window.rfl @@ -1,6 +1,26 @@ +;; Window join example. +;; +;; A window join aggregates, for each trade, the quotes whose timestamps fall +;; in a per-trade time window. The `intervals` argument is two parallel +;; vectors — (lo_vec hi_vec) — with one entry per trade row: the window for +;; trade r is [lo_vec[r], hi_vec[r]]. +;; +;; window-join (wj) seeds each window with the PREVAILING quote — the last +;; quote at/before lo — then extends through hi. +;; window-join1 (wj1) is strict: only quotes whose time is inside [lo, hi]. + (set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54]))) (set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698]))) - -(set intervals (list [11:59:59 12:00:02 12:00:04] [12:00:03 12:00:06 12:00:08])) -;; (window-join [Sym Time] intervals trades quotes {a: (sum Bid) b: (sum Ask)}) +;; Per-trade windows: two parallel vectors (lo_vec hi_vec) built with map-left, +;; here ±1.5s around each trade time. The 1.5s offset makes each window open +;; *between* quotes, so wj's prevailing-quote seed differs visibly from wj1. +(set intervals (map-left + [-1500 1500] (at trades 'Time))) + +(println "window-join (prevailing quote seeded):") +(println "%" (window-join [Sym Time] intervals trades quotes {n: (count Size) lo: (min Size) hi: (max Size) total: (sum Size)})) + +(println "window-join1 (strict window):") +(println "%" (window-join1 [Sym Time] intervals trades quotes {n: (count Size) lo: (min Size) hi: (max Size) total: (sum Size)})) + +(exit 0) diff --git a/src/lang/eval.c b/src/lang/eval.c index 496143c9..d6901e3c 100644 --- a/src/lang/eval.c +++ b/src/lang/eval.c @@ -2728,7 +2728,7 @@ static void ray_register_builtins(void) { register_vary("inner-join", RAY_FN_NONE, ray_inner_join_fn); register_vary("anti-join", RAY_FN_NONE, ray_anti_join_fn); register_vary("window-join", RAY_FN_SPECIAL_FORM, ray_window_join_fn); - register_vary("window-join1", RAY_FN_SPECIAL_FORM, ray_window_join_fn); + register_vary("window-join1", RAY_FN_SPECIAL_FORM, ray_window_join1_fn); register_vary("asof-join", RAY_FN_NONE, ray_asof_join_fn); /* I/O builtins */ diff --git a/src/lang/eval.h b/src/lang/eval.h index ca1630cc..7d7139d0 100644 --- a/src/lang/eval.h +++ b/src/lang/eval.h @@ -278,6 +278,7 @@ ray_t* ray_xbar_fn(ray_t* col, ray_t* bucket); ray_t* ray_left_join_fn(ray_t** args, int64_t n); ray_t* ray_inner_join_fn(ray_t** args, int64_t n); ray_t* ray_window_join_fn(ray_t** args, int64_t n); +ray_t* ray_window_join1_fn(ray_t** args, int64_t n); /* I/O */ ray_t* ray_println_fn(ray_t** args, int64_t n); diff --git a/src/lang/internal.h b/src/lang/internal.h index 1c5c349d..0b6157e5 100644 --- a/src/lang/internal.h +++ b/src/lang/internal.h @@ -606,6 +606,7 @@ ray_t* ray_left_join_fn(ray_t** args, int64_t n); ray_t* ray_inner_join_fn(ray_t** args, int64_t n); ray_t* ray_anti_join_fn(ray_t** args, int64_t n); ray_t* ray_window_join_fn(ray_t** args, int64_t n); +ray_t* ray_window_join1_fn(ray_t** args, int64_t n); ray_t* ray_asof_join_fn(ray_t** args, int64_t n); /* Graph builtins (.graph.* family). Implemented in src/ops/graph_builtin.c — diff --git a/src/ops/builtins.c b/src/ops/builtins.c index b838e851..44f17182 100644 --- a/src/ops/builtins.c +++ b/src/ops/builtins.c @@ -461,7 +461,10 @@ ray_t* ray_timeit_fn(ray_t** args, int64_t n) { int64_t t0 = ray_profile_now_ns(); ray_t* result = ray_eval(args[0]); int64_t t1 = ray_profile_now_ns(); - if (result && !RAY_IS_ERR(result)) ray_release(result); + /* Propagate errors instead of swallowing them — otherwise a failing + * expression silently reports a timing as if it had succeeded. */ + if (result && RAY_IS_ERR(result)) return result; + if (result) ray_release(result); double ms = (double)(t1 - t0) / 1e6; return make_f64(ms); } diff --git a/src/ops/query.c b/src/ops/query.c index a03cf8ce..61933fa0 100644 --- a/src/ops/query.c +++ b/src/ops/query.c @@ -11153,6 +11153,7 @@ typedef struct { int64_t right_nrows; int64_t n_eq; int64_t n_agg; + int mode; /* 0 = wj (prevailing-quote seeded), 1 = wj1 (strict) */ /* Left-row metadata — pre-extracted to int64 so workers can read * without touching any ray_t objects (no locking, no allocation). */ @@ -11183,6 +11184,18 @@ typedef struct { uint8_t* result_null[WJ_MAX_AGG]; /* 1 byte per row: 1 = null */ } wj_scan_ctx_t; +/* Compare the equality tuple of sorted-right row `ri` against `target_eq`. + * Returns <0, 0, >0. Used to bracket a left row's eq partition. */ +static inline int wj_eq_cmp(const wj_scan_ctx_t* c, int64_t ri, + const int64_t* target_eq, int64_t n_eq) { + for (int64_t e = 0; e < n_eq; e++) { + int64_t rv = read_col_i64(c->eq_data[e], ri, c->eq_type[e], c->eq_attrs[e]); + if (rv < target_eq[e]) return -1; + if (rv > target_eq[e]) return 1; + } + return 0; +} + static void wj_scan_fn(void* ctx_, uint32_t worker_id, int64_t start, int64_t end) { (void)worker_id; wj_scan_ctx_t* c = (wj_scan_ctx_t*)ctx_; @@ -11201,33 +11214,49 @@ static void wj_scan_fn(void* ctx_, uint32_t worker_id, int64_t start, int64_t en for (int64_t e = 0; e < n_eq; e++) target_eq[e] = c->left_eq_arr[e][lr]; - /* lower_bound: first rank with (eq, time) >= (target_eq, lo) */ - int64_t lb = 0, lb_hi = rn; - while (lb < lb_hi) { - int64_t m = (lb + lb_hi) >> 1; - int64_t ri = right_sort[m]; - int cmp = 0; - for (int64_t e = 0; e < n_eq && cmp == 0; e++) { - int64_t rv = read_col_i64(c->eq_data[e], ri, c->eq_type[e], c->eq_attrs[e]); - if (rv < target_eq[e]) cmp = -1; - else if (rv > target_eq[e]) cmp = 1; - } - if (cmp == 0 && rt_time_i[ri] < lo) cmp = -1; - if (cmp < 0) lb = m + 1; else lb_hi = m; - } - int64_t ub = lb, ub_hi = rn; + /* Locate the eq partition [ps, pe) in the sorted right table — the + * contiguous run of ranks whose equality tuple == target_eq. Compare + * eq keys only (the sort is (eq..., time), so the run is contiguous). */ + int64_t ps = 0, ps_hi = rn; + while (ps < ps_hi) { + int64_t m = (ps + ps_hi) >> 1; + if (wj_eq_cmp(c, right_sort[m], target_eq, n_eq) < 0) ps = m + 1; + else ps_hi = m; + } + int64_t pe = ps, pe_hi = rn; + while (pe < pe_hi) { + int64_t m = (pe + pe_hi) >> 1; + if (wj_eq_cmp(c, right_sort[m], target_eq, n_eq) <= 0) pe = m + 1; + else pe_hi = m; + } + + /* Upper bound (both modes): first rank in [ps, pe) with time > hi. */ + int64_t ub = ps, ub_hi = pe; while (ub < ub_hi) { int64_t m = (ub + ub_hi) >> 1; - int64_t ri = right_sort[m]; - int cmp = 0; - for (int64_t e = 0; e < n_eq && cmp == 0; e++) { - int64_t rv = read_col_i64(c->eq_data[e], ri, c->eq_type[e], c->eq_attrs[e]); - if (rv < target_eq[e]) cmp = -1; - else if (rv > target_eq[e]) cmp = 1; + if (rt_time_i[right_sort[m]] <= hi) ub = m + 1; else ub_hi = m; + } + + /* Lower bound differs by mode: + * wj1 (mode 1): first rank in [ps, pe) with time >= lo (strict window). + * wj (mode 0): the prevailing quote — rightmost rank with time <= lo, + * i.e. (first rank with time > lo) - 1, clamped to ps. */ + int64_t lb; + if (c->mode == 1) { + lb = ps; int64_t lbh = pe; + while (lb < lbh) { + int64_t m = (lb + lbh) >> 1; + if (rt_time_i[right_sort[m]] < lo) lb = m + 1; else lbh = m; + } + } else { + int64_t r = ps, rh = pe; + while (r < rh) { + int64_t m = (r + rh) >> 1; + if (rt_time_i[right_sort[m]] <= lo) r = m + 1; else rh = m; } - if (cmp == 0 && rt_time_i[ri] <= hi) cmp = -1; - if (cmp < 0) ub = m + 1; else ub_hi = m; + lb = (r > ps) ? r - 1 : ps; } + if (lb > ub) lb = ub; /* empty window / no partition → null result */ memset(acc, 0, sizeof(acc)); for (int64_t a = 0; a < n_agg; a++) { @@ -11477,10 +11506,21 @@ static void wj_scan_fn(void* ctx_, uint32_t worker_id, int64_t start, int64_t en } } -/* (window-join t1 t2 [eq-keys] time-col) - * ASOF join: for each left row, find closest right row with time <= left.time - * within the same equality partition. */ -ray_t* ray_window_join_fn(ray_t** args, int64_t n) { +/* Window join (Rayforce convention): + * (window-join [eq-keys.. timeKey] intervals left right {agg}) + * (window-join1 [eq-keys.. timeKey] intervals left right {agg}) + * `intervals` is a 2-element list (lo_vec hi_vec) of parallel vectors, one + * entry per left row: the window for left row r is [lo_vec[r], hi_vec[r]]. + * + * mode 0 (window-join / wj): each window is seeded with the PREVAILING quote + * — the rightmost quote with time <= lo[r] within the eq partition — then + * extended through the last quote with time <= hi[r]. + * mode 1 (window-join1 / wj1): strict window — only quotes whose time falls in + * [lo[r], hi[r]]. + * + * The legacy asof fall-through ((window-join L R [keys] time)) is mode-agnostic. + */ +static ray_t* window_join_impl(ray_t** args, int64_t n, int mode) { if (n < 4) return ray_error("domain", NULL); /* Special form: evaluate first 4 args, keep agg dict (args[4]) unevaluated */ @@ -11812,11 +11852,20 @@ ray_t* ray_window_join_fn(ray_t** args, int64_t n) { for (int i = 0; i < 4; i++) ray_release(eargs[i]); return ray_error("oom", NULL); } - for (int64_t lr = 0; lr < left_nrows; lr++) { - int alloc_iv = 0; - ray_t* iv = collection_elem(intervals, lr, &alloc_iv); - if (!iv || RAY_IS_ERR(iv) || ray_len(iv) < 2) { - if (alloc_iv && iv) ray_release(iv); + /* `intervals` is the v1 two-parallel-vector form: a 2-element list + * (lo_vec hi_vec), each a vector with one entry per left row. The + * window for left row r is [lo_vec[r], hi_vec[r]]. */ + { + int alloc_lo_v = 0, alloc_hi_v = 0; + ray_t* lo_vec = (ray_len(intervals) >= 2) + ? collection_elem(intervals, 0, &alloc_lo_v) : NULL; + ray_t* hi_vec = (ray_len(intervals) >= 2) + ? collection_elem(intervals, 1, &alloc_hi_v) : NULL; + if (!lo_vec || !hi_vec || RAY_IS_ERR(lo_vec) || RAY_IS_ERR(hi_vec) || + !ray_is_vec(lo_vec) || !ray_is_vec(hi_vec) || + ray_len(lo_vec) != left_nrows || ray_len(hi_vec) != left_nrows) { + if (alloc_lo_v && lo_vec) ray_release(lo_vec); + if (alloc_hi_v && hi_vec) ray_release(hi_vec); if (lo_hdr) scratch_free(lo_hdr); if (hi_hdr) scratch_free(hi_hdr); WJ_CLEANUP_TEMP(); @@ -11824,14 +11873,16 @@ ray_t* ray_window_join_fn(ray_t** args, int64_t n) { for (int i = 0; i < 4; i++) ray_release(eargs[i]); return ray_error("domain", NULL); } - int alloc_lo = 0, alloc_hi = 0; - ray_t* lo_atom = collection_elem(iv, 0, &alloc_lo); - ray_t* hi_atom = collection_elem(iv, 1, &alloc_hi); - lo_arr[lr] = as_i64(lo_atom); - hi_arr[lr] = as_i64(hi_atom); - if (alloc_lo) ray_release(lo_atom); - if (alloc_hi) ray_release(hi_atom); - if (alloc_iv) ray_release(iv); + const void* lo_d = ray_data(lo_vec); + const void* hi_d = ray_data(hi_vec); + int8_t lo_t = lo_vec->type, hi_t = hi_vec->type; + uint8_t lo_a = lo_vec->attrs, hi_a = hi_vec->attrs; + for (int64_t lr = 0; lr < left_nrows; lr++) { + lo_arr[lr] = read_col_i64(lo_d, lr, lo_t, lo_a); + hi_arr[lr] = read_col_i64(hi_d, lr, hi_t, hi_a); + } + if (alloc_lo_v) ray_release(lo_vec); + if (alloc_hi_v) ray_release(hi_vec); } ray_t* left_eq_hdr[WJ_MAX_AGG] = {0}; @@ -11883,6 +11934,7 @@ ray_t* ray_window_join_fn(ray_t** args, int64_t n) { wctx.right_nrows = right_nrows; wctx.n_eq = n_eq; wctx.n_agg = n_agg; + wctx.mode = mode; wctx.lo_arr = lo_arr; wctx.hi_arr = hi_arr; wctx.right_sort = right_sort; @@ -12005,6 +12057,16 @@ ray_t* ray_window_join_fn(ray_t** args, int64_t n) { return result; } +/* window-join (wj): prevailing-quote-seeded window aggregation. */ +ray_t* ray_window_join_fn(ray_t** args, int64_t n) { + return window_join_impl(args, n, 0); +} + +/* window-join1 (wj1): strict-window aggregation (no prevailing quote). */ +ray_t* ray_window_join1_fn(ray_t** args, int64_t n) { + return window_join_impl(args, n, 1); +} + /* (asof-join [key1 key2 ... timeKey] leftTable rightTable) * Last key is the time/asof column, rest are equality keys. The equality * keys are OPTIONAL: a lone time key (asof-join [timeKey] L R) performs an diff --git a/test/rfl/integration/joins.rfl b/test/rfl/integration/joins.rfl index fbafe89a..1f6c962e 100644 --- a/test/rfl/integration/joins.rfl +++ b/test/rfl/integration/joins.rfl @@ -74,45 +74,45 @@ (sum (at ijmk 'val1)) -- 400 (sum (at ijmk 'val2)) -- 4000 ;; window-join -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-right + [-2000 2000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {minBid: (min Bid)}) 'minBid) -- [99 101] +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-left + [-2000 2000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {minBid: (min Bid)}) 'minBid) -- [99 100] ;; window-join1 -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-right + [-2000 2000] (at trades 'Time)))(at (window-join1 [Sym Time] intervals trades quotes {minBid: (min Bid)}) 'minBid) -- [99 101] +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-left + [-2000 2000] (at trades 'Time)))(at (window-join1 [Sym Time] intervals trades quotes {minBid: (min Bid)}) 'minBid) -- [99 101] ;; window-join with multiple aggregations — both columns must be present -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid Ask] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101] [110 111 112])))(set intervals (map-right + [-2000 2000] (at trades 'Time)))(set r (window-join [Sym Time] intervals trades quotes {lo: (min Bid) hi: (max Ask)}))(at r 'lo) -- [99 101] -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid Ask] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101] [110 111 112])))(set intervals (map-right + [-2000 2000] (at trades 'Time)))(set r (window-join [Sym Time] intervals trades quotes {lo: (min Bid) hi: (max Ask)}))(at r 'hi) -- [111 112] +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid Ask] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101] [110 111 112])))(set intervals (map-left + [-2000 2000] (at trades 'Time)))(set r (window-join [Sym Time] intervals trades quotes {lo: (min Bid) hi: (max Ask)}))(at r 'lo) -- [99 100] +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid Ask] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101] [110 111 112])))(set intervals (map-left + [-2000 2000] (at trades 'Time)))(set r (window-join [Sym Time] intervals trades quotes {lo: (min Bid) hi: (max Ask)}))(at r 'hi) -- [111 112] ;; window-join rayforce1 canonical example (docs/queries-joins.html) ;; trades at 12:00:01, 12:00:04, 12:00:06 ± 1s windows ;; quotes at 12:00:00..12:00:09 sizes [928 528 648 914 918 626 577 817 620 698] ;; trade @ 01 window [00,02] -> sizes [928 528 648], min=528, max=928 ;; trade @ 04 window [03,05] -> sizes [914 918 626], min=626, max=918 ;; trade @ 06 window [05,07] -> sizes [626 577 817], min=577, max=817 -(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {size_min: (min Size) size_max: (max Size)}) 'size_min) -- [528 626 577] -(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {size_min: (min Size) size_max: (max Size)}) 'size_max) -- [928 918 817] +(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {size_min: (min Size) size_max: (max Size)}) 'size_min) -- [528 626 577] +(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {size_min: (min Size) size_max: (max Size)}) 'size_max) -- [928 918 817] ;; window-join sum/count/avg over the same canonical example ;; trade @ 01 window [00,02]: sum=2104 count=3 avg≈701.3333 ;; trade @ 04 window [03,05]: sum=2458 count=3 avg≈819.3333 ;; trade @ 06 window [05,07]: sum=2020 count=3 avg≈673.3333 -(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {s: (sum Size)}) 's) -- [2104 2458 2020] -(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {c: (count Size)}) 'c) -- [3 3 3] +(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {s: (sum Size)}) 's) -- [2104 2458 2020] +(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {c: (count Size)}) 'c) -- [3 3 3] ;; window-join first/last over the canonical example ;; trade @ 01 window [00,02]: first=928 last=648 ;; trade @ 04 window [03,05]: first=914 last=626 ;; trade @ 06 window [05,07]: first=626 last=817 -(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {f: (first Size)}) 'f) -- [928 914 626] -(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {l: (last Size)}) 'l) -- [648 626 817] +(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {f: (first Size)}) 'f) -- [928 914 626] +(set trades (table [Sym Time Price] (list [x x x] [12:00:01 12:00:04 12:00:06] [89.17 70.5 80.54])))(set quotes (table [Sym Time Size] (list [x x x x x x x x x x] [12:00:00 12:00:01 12:00:02 12:00:03 12:00:04 12:00:05 12:00:06 12:00:07 12:00:08 12:00:09] [928 528 648 914 918 626 577 817 620 698])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {l: (last Size)}) 'l) -- [648 626 817] ;; window-join raw bare-column form must accept non-numeric columns (regression) -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Tag] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [foo bar baz])))(set intervals (map-right + [-2000 2000] (at trades 'Time)))(count (at (window-join [Sym Time] intervals trades quotes {tags: Tag}) 'tags)) -- 2 +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Tag] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [foo bar baz])))(set intervals (map-left + [-2000 2000] (at trades 'Time)))(count (at (window-join [Sym Time] intervals trades quotes {tags: Tag}) 'tags)) -- 2 ;; window-join (count Col) must accept non-numeric source columns. ;; trades at 10:00:01 and 10:00:05 with ±1s windows; Tag rows at 10:00:00/02/04: ;; trade @ 01 window [00, 02] -> matches at 00 and 02 (2) ;; trade @ 05 window [04, 06] -> matches at 04 (1) -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Tag] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [foo bar baz])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {n: (count Tag)}) 'n) -- [2 1] +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Tag] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [foo bar baz])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {n: (count Tag)}) 'n) -- [2 1] ;; window-join COUNT must include window matches whose source value is null ;; ((count Col) is COUNT(*) semantics, not COUNT(non-null Col)). ;; trade @ 01 window [00, 02]: Bid rows at 00(99) and 02(NULL) -> count=2, min=99 ;; trade @ 05 window [04, 06]: Bid row at 04(101) -> count=1, min=101 -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 0Nl 101])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {c: (count Bid)}) 'c) -- [2 1] -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 0Nl 101])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {m: (min Bid)}) 'm) -- [99 101] +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 0Nl 101])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {c: (count Bid)}) 'c) -- [2 1] +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 0Nl 101])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {m: (min Bid)}) 'm) -- [99 101] ;; stddev/stddev_pop/var/var_pop must work as standalone aggregation ;; builtins so resolve_agg_opcode support in window-join is fully backed ;; by an eval-level implementation. @@ -136,14 +136,14 @@ ;; if the compare assumed 8-byte stride). ;; AAPL trade @ 01 window [00, 02]: AAPL quotes at 00, 02 -> min=100 max=201 ;; MSFT trade @ 01 window [00, 02]: MSFT quotes at 00, 02 -> min=300 max=401 -(set trades (table [Sym Time Price] (list [AAPL MSFT] [10:00:01.000 10:00:01.000] [10 20])))(set quotes (table [Sym Time Bid Ask] (list [AAPL AAPL AAPL MSFT MSFT MSFT] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:00.000 10:00:02.000 10:00:04.000] [100 101 102 300 301 302] [200 201 202 400 401 402])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {lo: (min Bid)}) 'lo) -- [100 300] -(set trades (table [Sym Time Price] (list [AAPL MSFT] [10:00:01.000 10:00:01.000] [10 20])))(set quotes (table [Sym Time Bid Ask] (list [AAPL AAPL AAPL MSFT MSFT MSFT] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:00.000 10:00:02.000 10:00:04.000] [100 101 102 300 301 302] [200 201 202 400 401 402])))(set intervals (map-right + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {hi: (max Ask)}) 'hi) -- [201 401] +(set trades (table [Sym Time Price] (list [AAPL MSFT] [10:00:01.000 10:00:01.000] [10 20])))(set quotes (table [Sym Time Bid Ask] (list [AAPL AAPL AAPL MSFT MSFT MSFT] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:00.000 10:00:02.000 10:00:04.000] [100 101 102 300 301 302] [200 201 202 400 401 402])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {lo: (min Bid)}) 'lo) -- [100 300] +(set trades (table [Sym Time Price] (list [AAPL MSFT] [10:00:01.000 10:00:01.000] [10 20])))(set quotes (table [Sym Time Bid Ask] (list [AAPL AAPL AAPL MSFT MSFT MSFT] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:00.000 10:00:02.000 10:00:04.000] [100 101 102 300 301 302] [200 201 202 400 401 402])))(set intervals (map-left + [-1000 1000] (at trades 'Time)))(at (window-join [Sym Time] intervals trades quotes {hi: (max Ask)}) 'hi) -- [201 401] ;; window-join with raw column (TYPE_MAPGROUP) -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-right + [-2000 2000] (at trades 'Time)))(count (at (window-join [Sym Time] intervals trades quotes {bids: Bid}) 'bids)) -- 2 +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-left + [-2000 2000] (at trades 'Time)))(count (at (window-join [Sym Time] intervals trades quotes {bids: Bid}) 'bids)) -- 2 ;; window-join1 with raw column (TYPE_MAPGROUP) -(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-right + [-2000 2000] (at trades 'Time)))(count (at (window-join1 [Sym Time] intervals trades quotes {bids: Bid}) 'bids)) -- 2 +(set trades (table [Sym Time Price] (list [a a] [10:00:01.000 10:00:05.000] [100 200])))(set quotes (table [Sym Time Bid] (list [a a a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set intervals (map-left + [-2000 2000] (at trades 'Time)))(count (at (window-join1 [Sym Time] intervals trades quotes {bids: Bid}) 'bids)) -- 2 ;; window-join with Enum columns (xasc converts Enum to Symbol) -(set trades (table [s time price] (list ['a 'a 'b] [10:00:01.000 10:00:05.000 10:00:03.000] [100 200 150])))(set quotes (table [s time bid] (list ['a 'a 'a 'b 'b] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:01.000 10:00:04.000] [99 100 101 149 151])))(set intervals (map-right + [-2000 2000] (at trades 'time)))(at (window-join [s time] intervals trades quotes {minBid: (min bid)}) 'minBid) -- [99 101 149] +(set trades (table [s time price] (list ['a 'a 'b] [10:00:01.000 10:00:05.000 10:00:03.000] [100 200 150])))(set quotes (table [s time bid] (list ['a 'a 'a 'b 'b] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:01.000 10:00:04.000] [99 100 101 149 151])))(set intervals (map-left + [-2000 2000] (at trades 'time)))(at (window-join [s time] intervals trades quotes {minBid: (min bid)}) 'minBid) -- [99 100 149] ;; empty left table (set t1 (table [id val1] (list (take [1] 0) (take [1] 0))))(set t2 (table [id val2] (list [1 2 3] [100 200 300])))(count (left-join [id] t1 t2)) -- 0 ;; empty right table — left-join keeps all 3 left rows with val2 = Null. diff --git a/test/rfl/io/dump.rfl b/test/rfl/io/dump.rfl index 714df328..cea39c54 100644 --- a/test/rfl/io/dump.rfl +++ b/test/rfl/io/dump.rfl @@ -85,8 +85,10 @@ ;; window-join + window-join1 both compile to OP_WINDOW_JOIN. (set tr (table [s tm p] (list ['a 'a] [10:00:01.000 10:00:05.000] [100 200]))) (set qt (table [s tm b] (list ['a 'a 'a] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101]))) -(set iv (map-right + [-2000 2000] (at tr 'tm))) -(at (window-join [s tm] iv tr qt {mb: (min b)}) 'mb) -- [99 101] +(set iv (map-left + [-2000 2000] (at tr 'tm))) +;; wj seeds the prevailing quote (row1 picks up the @02 quote at b=100); +;; wj1 is strict so row1 sees only the @04 quote (b=101). +(at (window-join [s tm] iv tr qt {mb: (min b)}) 'mb) -- [99 100] (at (window-join1 [s tm] iv tr qt {mb: (min b)}) 'mb) -- [99 101] ;; ──────────── OP_PIVOT (heavy) ──────────── diff --git a/test/rfl/ops/exec_coverage.rfl b/test/rfl/ops/exec_coverage.rfl index 3c95ff24..a3bdea9d 100644 --- a/test/rfl/ops/exec_coverage.rfl +++ b/test/rfl/ops/exec_coverage.rfl @@ -122,7 +122,7 @@ ;; ==================================================================== (set tr (table [Sym Time Price] (list ['a 'a 'a 'b] [10:00:01.000 10:00:05.000 10:00:09.000 10:00:01.000] [100 200 300 400]))) (set qu (table [Sym Time Bid] (list ['a 'a 'a 'b 'b] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:00.000 10:00:02.000] [99 100 101 199 200]))) -(set iv (map-right + [-2000 2000] (at tr 'Time))) +(set iv (map-left + [-2000 2000] (at tr 'Time))) (count (at (window-join [Sym Time] iv tr qu {m: (min Bid)}) 'm)) -- 4 ;; ==================================================================== diff --git a/test/rfl/ops/opt_advanced.rfl b/test/rfl/ops/opt_advanced.rfl index cc5183df..1116ff9b 100644 --- a/test/rfl/ops/opt_advanced.rfl +++ b/test/rfl/ops/opt_advanced.rfl @@ -295,7 +295,7 @@ ;; Every TR1 row gets at least one matching TQ1 row in its window. (set TR1 (table [Sym Time Price] (list ['a 'a 'b] [10:00:01.000 10:00:05.000 10:00:01.000] [100 200 400]))) (set TQ1 (table [Sym Time Bid] (list ['a 'a 'a 'b] [10:00:00.000 10:00:02.000 10:00:04.000 10:00:00.000] [99 100 101 199]))) -(set IV (map-right + [-2000 2000] (at TR1 'Time))) +(set IV (map-left + [-2000 2000] (at TR1 'Time))) (count (at (window-join [Sym Time] IV TR1 TQ1 {m: (min Bid)}) 'm)) -- 3 ;; ==================================================================== diff --git a/test/rfl/ops/opt_branch_cov.rfl b/test/rfl/ops/opt_branch_cov.rfl index 30ae65d3..5eba1b19 100644 --- a/test/rfl/ops/opt_branch_cov.rfl +++ b/test/rfl/ops/opt_branch_cov.rfl @@ -378,7 +378,7 @@ ;; ════════════════════════════════════════════════════════════════════════ (set TWJ_T (table [Sym Time Price] (list ['a 'a 'b 'b] [10:00:01.000 10:00:05.000 10:00:01.000 10:00:05.000] [100 200 400 500]))) (set TWJ_Q (table [Sym Time Bid] (list ['a 'a 'b 'b] [10:00:00.000 10:00:04.000 10:00:00.000 10:00:04.000] [99 101 399 501]))) -(set TWJ_IV (map-right + [-2000 2000] (at TWJ_T 'Time))) +(set TWJ_IV (map-left + [-2000 2000] (at TWJ_T 'Time))) (count (at (window-join [Sym Time] TWJ_IV TWJ_T TWJ_Q {m: (min Bid)}) 'm)) -- 4 ;; ════════════════════════════════════════════════════════════════════════ diff --git a/test/rfl/ops/query_coverage.rfl b/test/rfl/ops/query_coverage.rfl index c46d20aa..4a4776fa 100644 --- a/test/rfl/ops/query_coverage.rfl +++ b/test/rfl/ops/query_coverage.rfl @@ -560,30 +560,32 @@ (set wjl (table [Sym Time] (list ['a 'a] [10:00:01.000 10:00:05.000]))) (set wjr (table [Sym Time Price] (list ['a 'a 'a] [10:00:00.000 10:00:02.000 10:00:04.000] (as 'F64 [99.5 100.5 101.5])))) -(set wjiv (map-right + [-2000 2000] (at wjl 'Time))) -;; F64 sum: row0 interval [09:59:59,10:00:03] → prices 99.5+100.5=200.0 -;; row1 interval [10:00:03,10:00:07] → price 101.5 only -(at (window-join [Sym Time] wjiv wjl wjr {total: (sum Price)}) 'total) -- [200.0 101.5] +(set wjiv (map-left + [-2000 2000] (at wjl 'Time))) +;; wj seeds the prevailing quote, so row1's window [10:00:03,10:00:07] +;; includes the @02 quote (100.5) at/before lo plus the @04 quote (101.5). +;; F64 sum: row0 [09:59:59,10:00:03] → 99.5+100.5=200.0 +;; row1 [10:00:03,10:00:07] → 100.5+101.5=202.0 +(at (window-join [Sym Time] wjiv wjl wjr {total: (sum Price)}) 'total) -- [200.0 202.0] ;; F64 avg aggregation in window-join → hits sorted_f OP_AVG arm. -;; row0: avg(99.5,100.5)=100.0; row1: avg(101.5)=101.5 -(at (window-join [Sym Time] wjiv wjl wjr {avg_p: (avg Price)}) 'avg_p) -- [100.0 101.5] +;; row0: avg(99.5,100.5)=100.0; row1: avg(100.5,101.5)=101.0 +(at (window-join [Sym Time] wjiv wjl wjr {avg_p: (avg Price)}) 'avg_p) -- [100.0 101.0] ;; F64 min aggregation → sorted_f OP_MIN arm. -;; row0: min(99.5,100.5)=99.5; row1: min(101.5)=101.5 -(at (window-join [Sym Time] wjiv wjl wjr {lo: (min Price)}) 'lo) -- [99.5 101.5] +;; row0: min(99.5,100.5)=99.5; row1: min(100.5,101.5)=100.5 +(at (window-join [Sym Time] wjiv wjl wjr {lo: (min Price)}) 'lo) -- [99.5 100.5] ;; F64 max aggregation → sorted_f OP_MAX arm. -;; row0: max(99.5,100.5)=100.5; row1: max(101.5)=101.5 +;; row0: max(99.5,100.5)=100.5; row1: max(100.5,101.5)=101.5 (at (window-join [Sym Time] wjiv wjl wjr {hi: (max Price)}) 'hi) -- [100.5 101.5] ;; F64 prod aggregation → sorted_f OP_PROD arm. -;; row0: prod(99.5,100.5)=10000.0 approx (99.5*100.5=9999.75); row1: prod(101.5)=101.5 -(at (window-join [Sym Time] wjiv wjl wjr {pr: (prod Price)}) 'pr) -- [9999.75 101.5] +;; row0: prod(99.5,100.5)=9999.75; row1: prod(100.5,101.5)=10200.75 +(at (window-join [Sym Time] wjiv wjl wjr {pr: (prod Price)}) 'pr) -- [9999.75 10200.75] ;; F64 var/stddev aggregation → sorted_f OP_VAR/OP_STDDEV arm. -;; row0: var(99.5,100.5) = sample var = 0.5 (2 values) -;; row1: var([101.5]) = null (undefined for n=1 sample var) +;; row0: var(99.5,100.5) sample var over 2 values +;; row1: var(100.5,101.5) sample var over 2 values (prevailing-seeded) (count (window-join [Sym Time] wjiv wjl wjr {v: (var Price)})) -- 2 ;; ==================================================================== @@ -633,8 +635,10 @@ (set wjl2 (table [Sym Time] (list ['a 'a] [10:00:01.000 10:00:05.000]))) (set wjr2 (table [Sym Time Price] (list ['a 'a 'a] [10:00:00.000 10:00:02.000 10:00:04.000] (as 'F64 [99.5 100.5 101.5])))) -(set wjiv2 (map-right + [-2000 2000] (at wjl2 'Time))) -(at (window-join [Sym Time] wjiv2 wjl2 wjr2 {f: (first Price)}) 'f) -- [99.5 101.5] +(set wjiv2 (map-left + [-2000 2000] (at wjl2 'Time))) +;; wj prevailing seed: row1 window [10:00:03,10:00:07] starts at the @02 +;; quote (100.5), so first=100.5 (last still the @04 quote, 101.5). +(at (window-join [Sym Time] wjiv2 wjl2 wjr2 {f: (first Price)}) 'f) -- [99.5 100.5] (at (window-join [Sym Time] wjiv2 wjl2 wjr2 {l: (last Price)}) 'l) -- [100.5 101.5] ;; window-join integer VAR/PROD/STDDEV — sorted_i OP_VAR/OP_PROD/OP_STDDEV. diff --git a/test/rfl/query/query_branch_cov.rfl b/test/rfl/query/query_branch_cov.rfl index 1e9add05..a218641d 100644 --- a/test/rfl/query/query_branch_cov.rfl +++ b/test/rfl/query/query_branch_cov.rfl @@ -828,28 +828,29 @@ (set wjR (table [Sym Time Bid] (list ['x 'x 'y 'y] [10:00:00.500 10:00:02.500 10:00:01.500 10:00:02.500] [99.0 101.0 199.0 201.0]))) ;; intervals = per-left-row [lo hi] time window (in nanoseconds offset ;; via map-left + to each left Time). -(set wjIv (map-right + [-2000 2000] (at wjL 'Time))) +(set wjIv (map-left + [-2000 2000] (at wjL 'Time))) ;; Window [-2000ns, +2000ns] around each left Time: ;; (x, 10:00:01) ∩ window 09:59:59..10:00:03 — x-bids @00.5, @02.5 → {99, 101} ;; (y, 10:00:02) ∩ window 10:00:00..10:00:04 — y-bids @01.5, @02.5 → {199, 201} -;; (x, 10:00:03) ∩ window 10:00:01..10:00:05 — x-bid @02.5 → {101} +;; (x, 10:00:03) ∩ window 10:00:01..10:00:05 — x-bid @02.5, plus the +;; prevailing x-bid @00.5 (wj seeds the last quote at/before lo) → {99, 101} ;; agg dict with min agg. (set wjMin (window-join [Sym Time] wjIv wjL wjR {mb: (min Bid)})) (count wjMin) -- 3 -(at wjMin 'mb) -- [99.0 199.0 101.0] +(at wjMin 'mb) -- [99.0 199.0 99.0] ;; agg dict with multiple aggs. (set wjMx (window-join [Sym Time] wjIv wjL wjR {mb: (min Bid) xb: (max Bid)})) (count wjMx) -- 3 -(at wjMx 'mb) -- [99.0 199.0 101.0] +(at wjMx 'mb) -- [99.0 199.0 99.0] (at wjMx 'xb) -- [101.0 201.0 101.0] ;; agg dict with avg. (set wjAvg (window-join [Sym Time] wjIv wjL wjR {av: (avg Bid)})) (count wjAvg) -- 3 -(at wjAvg 'av) -- [100.0 200.0 101.0] +(at wjAvg 'av) -- [100.0 200.0 100.0] ;; agg dict with count (no source read). (set wjCnt (window-join [Sym Time] wjIv wjL wjR {n: (count Bid)})) (count wjCnt) -- 3 -(at wjCnt 'n) -- [2 2 1] +(at wjCnt 'n) -- [2 2 2] ;; arity error. (window-join [Sym Time] wjL wjR) !- domain diff --git a/test/rfl/query/query_update_coverage.rfl b/test/rfl/query/query_update_coverage.rfl index 0c82e74e..aabfaba2 100644 --- a/test/rfl/query/query_update_coverage.rfl +++ b/test/rfl/query/query_update_coverage.rfl @@ -360,7 +360,7 @@ ;; ──────────────────────────────────────────────────────────────────── (set wjt_null (table [Sym Time] (list ['a] [10:00:03.000]))) (set wjq_f64null (table [Sym Time Price] (list ['a 'a 'a 'a] [10:00:00.000 10:00:01.000 10:00:02.000 10:00:04.000] [0Nf 2.0 0Nf 4.0]))) -(set wji_null (map-right + [-3000 3000] (at wjt_null 'Time))) +(set wji_null (map-left + [-3000 3000] (at wjt_null 'Time))) ;; F64 null sum (line 10059): nn != NULL → sum skips nulls → 2+4=6 (at (window-join [Sym Time] wji_null wjt_null wjq_f64null {s: (sum Price)}) 's) -- [6.0] @@ -433,7 +433,7 @@ ;; ──────────────────────────────────────────────────────────────────── (set wjt_i32 (table [Sym Time] (list ['a] [10:00:01.000]))) (set wjq_i32 (table [Sym Time Price] (list ['a 'a] [10:00:00.000 10:00:02.000] (as 'I32 [10 20])))) -(set wji_i32 (map-right + [-2000 2000] (at wjt_i32 'Time))) +(set wji_i32 (map-left + [-2000 2000] (at wjt_i32 'Time))) ;; I32 result type (lines 10273-10274): first/max on I32 col → I32 output ;; Element at [0] of result is I32 atom 10i (first Price = 10i, max Price = 20i) @@ -447,7 +447,7 @@ ;; I64 null var/stddev (lines 10140-10141): null in I64 col + var → nn != NULL (set wjt_nullv (table [Sym Time] (list ['a] [10:00:03.000]))) (set wjq_i64nv (table [Sym Time Price] (list ['a 'a 'a 'a] [10:00:00.000 10:00:01.000 10:00:02.000 10:00:04.000] [0Nl 2 0Nl 4]))) -(set wji_nullv (map-right + [-3000 3000] (at wjt_nullv 'Time))) +(set wji_nullv (map-left + [-3000 3000] (at wjt_nullv 'Time))) ;; var of non-null [2, 4] = 2.0 (sample variance) (at (window-join [Sym Time] wji_nullv wjt_nullv wjq_i64nv {v: (var Price)}) 'v) -- [2.0] ;; stddev of non-null [2, 4] = sqrt(2.0) ≈ 1.41