nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
rows_helpers.hpp
1// ops/include/nk/ops/rows_helpers.hpp
2//
3// Shared helpers for "treat each row as a tuple" operations
4// (unique 'rows', sortrows, future ismember 'rows' etc.).
5// All paths assume a column-major DOUBLE matrix.
6
7#pragma once
8
9#include <memory_resource>
10#include <nk/value/value.hpp>
11
12#include <cmath>
13#include <cstddef>
14
15namespace nk::ops {
16
17// Lex-compare rows `a` and `b` of a column-major DOUBLE buffer.
18// Returns -1 / 0 / +1 in standard cmp convention. NaN is treated as
19// strictly greater than any non-NaN (push to end on ascending sort) —
20// matches MATLAB sort/sortrows ordering for finite vs NaN entries.
21inline int rowLexCmp(const double *p, std::size_t cols, std::size_t rows,
22 std::size_t a, std::size_t b)
23{
24 for (std::size_t c = 0; c < cols; ++c) {
25 const double av = p[c * rows + a];
26 const double bv = p[c * rows + b];
27 const bool aNan = std::isnan(av);
28 const bool bNan = std::isnan(bv);
29 if (aNan && bNan) continue;
30 if (aNan) return 1;
31 if (bNan) return -1;
32 if (av < bv) return -1;
33 if (av > bv) return 1;
34 }
35 return 0;
36}
37
38// Lex-compare rows by an explicit column priority list. Each entry of
39// `cols1based` is the 1-based column index to compare on; a negative
40// entry flips the direction for that key (descending). Out-of-range
41// entries throw via the caller's pre-validation (this function trusts
42// the caller). Pointer + size so the same helper composes with
43// std::vector / std::pmr::vector / arrays.
44inline int rowLexCmpByCols(const double *p, std::size_t totalCols,
45 std::size_t rows, std::size_t a, std::size_t b,
46 const int *cols1based, std::size_t nCols)
47{
48 for (std::size_t k = 0; k < nCols; ++k) {
49 const int rawCol = cols1based[k];
50 const bool desc = rawCol < 0;
51 const std::size_t cIdx = static_cast<std::size_t>(desc ? -rawCol : rawCol) - 1;
52 (void) totalCols; // caller validated rawCol in range
53 const double av = p[cIdx * rows + a];
54 const double bv = p[cIdx * rows + b];
55 const bool aNan = std::isnan(av);
56 const bool bNan = std::isnan(bv);
57 if (aNan && bNan) continue;
58 // NaN goes to the end on ascending; flip on descending.
59 if (aNan) return desc ? -1 : 1;
60 if (bNan) return desc ? 1 : -1;
61 if (av < bv) return desc ? 1 : -1;
62 if (av > bv) return desc ? -1 : 1;
63 }
64 return 0;
65}
66
67// Gather the rows of `x` indicated by `origRows` into a new DOUBLE matrix
68// of shape (nRows, x.dims().cols()), preserving column-major layout.
69// `x` must be a 2D DOUBLE matrix. Pointer + size so any contiguous
70// row-index container (std::vector / pmr::vector / array) works.
71inline Value collectRowsByIndex(std::pmr::memory_resource *mr, const Value &x,
72 const std::size_t *origRows, std::size_t nRows)
73{
74 const std::size_t rows = x.dims().rows();
75 const std::size_t cols = x.dims().cols();
76 auto r = Value::matrix(nRows, cols, ValueType::DOUBLE, mr);
77 const double *src = x.doubleData();
78 double *dst = r.doubleDataMut();
79 for (std::size_t newRow = 0; newRow < nRows; ++newRow) {
80 const std::size_t srcRow = origRows[newRow];
81 for (std::size_t c = 0; c < cols; ++c)
82 dst[c * nRows + newRow] = src[c * rows + srcRow];
83 }
84 return r;
85}
86
87} // namespace nk::ops
Value dst(const Value &x, std::pmr::memory_resource *mr=nullptr)
Type-II Discrete Sine Transform.