1// Panel: N (possibly ragged) series stored contiguously (CSR layout), plus an aligned dense view when the series2// share a common time index. The CSR layout is what the batched CPU/Metal kernels consume directly.3#pragma once4#include <vector>5#include <span>6#include <string>7#include <Eigen/Dense>8#include "raid/core/types.hpp"9#include "raid/core/error.hpp"1011namespace raid {1213struct Panel {14 std::vector<std::string> ids; // n_series15 std::vector<i64> offsets; // n_series + 1, offsets into t / v16 std::vector<Timestamp> t; // concatenated timestamps17 std::vector<f64> v; // concatenated values1819 usize n_series() const { return ids.size(); }20 usize total_size() const { return v.size(); }21 usize series_size(usize i) const { return static_cast<usize>(offsets[i + 1] - offsets[i]); }22 std::span<const f64> values(usize i) const { return std::span<const f64>(v).subspan(static_cast<usize>(offsets[i]), series_size(i)); }23 std::span<const Timestamp> times(usize i) const { return std::span<const Timestamp>(t).subspan(static_cast<usize>(offsets[i]), series_size(i)); }24 void push_back(std::string id, std::vector<Timestamp> ts, std::vector<f64> vs) {25 RAID_REQUIRE(ts.size() == vs.size(), invalid_argument, "Panel::push_back size mismatch");26 if (offsets.empty()) offsets.push_back(0);27 ids.push_back(std::move(id));28 t.insert(t.end(), ts.begin(), ts.end()); v.insert(v.end(), vs.begin(), vs.end());29 offsets.push_back(static_cast<i64>(v.size()));30 }31 bool is_rectangular() const { if (n_series() == 0) return true; const usize m = series_size(0); for (usize i = 1; i < n_series(); ++i) if (series_size(i) != m) return false; return true; }32 /// Dense T × N column-major matrix (requires rectangular panel).33 Eigen::MatrixXd dense() const {34 RAID_REQUIRE(is_rectangular(), invalid_argument, "Panel::dense requires equal-length series");35 const usize T = n_series() ? series_size(0) : 0;36 Eigen::MatrixXd m(static_cast<Eigen::Index>(T), static_cast<Eigen::Index>(n_series()));37 for (usize j = 0; j < n_series(); ++j) { auto s = values(j); for (usize i = 0; i < T; ++i) m(static_cast<Eigen::Index>(i), static_cast<Eigen::Index>(j)) = s[i]; }38 return m;39 }40};4142} // namespace raid43