36namespace morph::table {
43 enum class Kind : std::uint8_t {
69 std::vector<ViewOp>
ops;
73 [[nodiscard]]
bool empty() const noexcept {
return ops.empty(); }
87 explicit SlotCounter(std::size_t size) : _tree(size + 1, 0) {}
91 void occupy(std::size_t slot) { change(slot, 1); }
95 void release(std::size_t slot) { change(slot, -1); }
100 [[nodiscard]] std::size_t
before(std::size_t slot)
const {
102 for (
auto index = slot; index > 0; index -= index & (~index + 1)) {
105 return static_cast<std::size_t
>(sum);
109 void change(std::size_t slot,
int delta) {
110 for (
auto index = slot + 1; index < _tree.size(); index += index & (~index + 1)) {
111 _tree[index] += delta;
115 std::vector<int> _tree;
121[[nodiscard]]
inline std::vector<bool>
longestIncreasing(std::span<std::uint32_t const> values) {
122 constexpr auto kNone = std::numeric_limits<std::size_t>::max();
123 std::vector<std::size_t> tails;
124 std::vector<std::size_t> parent(values.size(), kNone);
125 for (std::size_t i = 0; i < values.size(); ++i) {
126 auto const position =
127 std::ranges::lower_bound(tails, values[i], {}, [&](std::size_t index) {
return values[index]; });
128 auto const length =
static_cast<std::size_t
>(position - tails.begin());
130 parent[i] = tails[length - 1];
132 if (position == tails.end()) {
138 std::vector<bool> marked(values.size(),
false);
139 if (!tails.empty()) {
140 for (
auto index = tails.back(); index != kNone; index = parent[index]) {
141 marked[index] =
true;
148inline constexpr auto kAbsent = std::numeric_limits<std::uint32_t>::max();
165 std::vector<ViewOp>& out, std::size_t limit) {
166 for (std::size_t end = before.size(); end > 0;) {
171 std::size_t start = end - 1;
172 while (start > 0 && positions.
after[before[start - 1]] ==
kAbsent) {
176 if (out.size() > limit) {
196[[nodiscard]]
inline bool appendMoves(std::span<std::uint32_t const> ranks, std::vector<ViewOp>& out,
199 auto const stationaryCount =
static_cast<std::size_t
>(std::ranges::count(stationary,
true));
202 if (out.size() + (ranks.size() - stationaryCount) > limit) {
205 auto const kept = ranks.size();
206 std::vector<std::size_t> oldGap(kept);
207 std::vector<std::size_t> stationaryBeforeRank(kept + 1, 0);
208 std::size_t seen = 0;
209 for (std::size_t i = 0; i < kept; ++i) {
212 stationaryBeforeRank[ranks[i] + 1] = 1;
216 for (std::size_t rank = 0; rank < kept; ++rank) {
217 stationaryBeforeRank[rank + 1] += stationaryBeforeRank[rank];
220 std::vector<std::size_t> movedInGap(stationaryCount + 1, 0);
221 std::vector<std::size_t> unmovedInGap(stationaryCount + 1, 0);
222 for (std::size_t i = 0; i < kept; ++i) {
223 if (!stationary[i]) {
224 ++movedInGap[stationaryBeforeRank[ranks[i]]];
225 ++unmovedInGap[oldGap[i]];
228 std::vector<std::size_t> gapBase(stationaryCount + 2, 0);
229 for (std::size_t gap = 0; gap <= stationaryCount; ++gap) {
230 gapBase[gap + 1] = gapBase[gap] + movedInGap[gap] + unmovedInGap[gap] + (gap < stationaryCount ? 1 : 0);
232 SlotCounter occupancy{gapBase[stationaryCount + 1]};
233 std::vector<std::size_t> unmovedSlot(kept, 0);
234 std::vector<std::size_t> nextUnmoved(stationaryCount + 1, 0);
235 std::vector<std::size_t> indexOfRank(kept, 0);
236 for (std::size_t i = 0; i < kept; ++i) {
237 auto const gap = oldGap[i];
238 indexOfRank[ranks[i]] = i;
240 occupancy.
occupy(gapBase[gap] + movedInGap[gap] + unmovedInGap[gap]);
242 unmovedSlot[i] = gapBase[gap] + movedInGap[gap] + nextUnmoved[gap]++;
243 occupancy.
occupy(unmovedSlot[i]);
246 std::vector<std::size_t> nextMoved(stationaryCount + 1, 0);
247 for (std::size_t rank = 0; rank < kept; ++rank) {
248 auto const i = indexOfRank[rank];
252 auto const from = occupancy.
before(unmovedSlot[i]);
253 occupancy.
release(unmovedSlot[i]);
254 auto const gap = stationaryBeforeRank[rank];
255 auto const movedSlot = gapBase[gap] + nextMoved[gap]++;
256 auto const destination = occupancy.
before(movedSlot);
257 occupancy.
occupy(movedSlot);
270 std::vector<ViewOp>& out, std::size_t limit) {
271 for (std::size_t start = 0; start < after.size();) {
276 std::size_t end = start + 1;
277 while (end < after.size() && positions.
before[after[end]] ==
kAbsent) {
281 if (out.size() > limit) {
294 std::vector<ViewOp>& out) {
295 std::vector<std::uint32_t> changedAt;
296 for (
auto const identity : changed) {
299 changedAt.push_back(positions.
after[identity]);
302 std::ranges::sort(changedAt);
303 auto const [dupFirst, dupLast] = std::ranges::unique(changedAt);
304 changedAt.erase(dupFirst, dupLast);
305 for (std::size_t start = 0; start < changedAt.size();) {
306 std::size_t end = start + 1;
307 while (end < changedAt.size() && changedAt[end] == changedAt[end - 1] + 1) {
330[[nodiscard]]
inline ViewChange diffViews(std::span<std::uint32_t const> before, std::span<std::uint32_t const> after,
331 std::span<std::uint32_t const> changed, std::size_t identityLimit,
332 std::size_t resetThreshold) {
337 for (std::size_t i = 0; i < after.size(); ++i) {
338 positions.
after[after[i]] =
static_cast<std::uint32_t
>(i);
340 for (std::size_t i = 0; i < before.size(); ++i) {
341 positions.
before[before[i]] =
static_cast<std::uint32_t
>(i);
347 std::uint32_t rank = 0;
348 for (
auto const identity : after) {
350 rankOf[identity] = rank++;
353 std::vector<std::uint32_t> ranks;
355 for (
auto const identity : before) {
357 ranks.push_back(rankOf[identity]);
368 if (!fits || out.
ops.size() > resetThreshold) {
384 for (
auto const& op : change.
ops) {
385 auto const first =
static_cast<std::ptrdiff_t
>(op.first);
388 list.erase(list.begin() + first, list.begin() + first +
static_cast<std::ptrdiff_t
>(op.count));
391 T moved = std::move(list[op.first]);
392 list.erase(list.begin() + first);
393 list.insert(list.begin() +
static_cast<std::ptrdiff_t
>(op.to), std::move(moved));
397 list.insert(list.begin() + first, after.begin() + first,
398 after.begin() + first +
static_cast<std::ptrdiff_t
>(op.count));
401 list.assign(after.begin(), after.end());
A Fenwick tree of slot occupancy: how many occupied slots precede a slot.
void release(std::size_t slot)
Marks a slot free.
SlotCounter(std::size_t size)
An empty counter over size slots.
std::size_t before(std::size_t slot) const
Occupied slots before slot.
void occupy(std::size_t slot)
Marks a slot occupied.
The operations that turn one view order into another.
bool isReset() const noexcept
Whether the change is a whole-view reset.
bool empty() const noexcept
Whether nothing changed.
std::vector< ViewOp > ops
The operations, in replay order.
One list-model operation.
std::size_t count
Rows concerned (1 for Moved, 0 for Reset).
std::size_t first
First row concerned.
std::size_t to
Destination of a Moved row, in the list after its removal.
bool operator==(ViewOp const &other) const =default
Member-wise equality.
@ Inserted
count rows were inserted at first.
@ Reset
The whole view was replaced.
@ Changed
count rows at first have new content.
@ Removed
count rows at first were removed.
@ Moved
The row at first moved to to.
Where each identity sits in each view.
std::vector< std::uint32_t > after
Position in the new view, or kAbsent.
std::vector< std::uint32_t > before
Position in the old view, or kAbsent.
ViewChange diffViews(std::span< std::uint32_t const > before, std::span< std::uint32_t const > after, std::span< std::uint32_t const > changed, std::size_t identityLimit, std::size_t resetThreshold)
Computes the operations that turn before into after.
bool appendRemovals(std::span< std::uint32_t const > before, ViewPositions const &positions, std::vector< ViewOp > &out, std::size_t limit)
Appends the removals, back to front, coalesced into runs.
bool appendMoves(std::span< std::uint32_t const > ranks, std::vector< ViewOp > &out, std::size_t limit)
Appends the moves that put the kept rows in their new order.
constexpr auto kAbsent
Marks an identity absent from a view.
bool appendInsertions(std::span< std::uint32_t const > after, ViewPositions const &positions, std::vector< ViewOp > &out, std::size_t limit)
Appends the insertions, front to back, coalesced into runs.
std::vector< bool > longestIncreasing(std::span< std::uint32_t const > values)
Marks the elements of a longest strictly increasing subsequence.
void appendChanges(std::span< std::uint32_t const > changed, ViewPositions const &positions, std::vector< ViewOp > &out)
Appends the changes of rows in both views, in new positions, coalesced into runs.
void applyViewChange(std::vector< T > &list, ViewChange const &change, std::span< T const > after)
Applies change to list, following the replay contract.