use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc;
use sva_formula::{Hash, Held as Representation, NodeId};
use sva_samples::Extent;
use super::Render;
use super::frontier::Frontier;
use super::table::segments::Segments;
use super::table::{self, Table};
use crate::cache::{Memory, Offered, Stored};
use crate::schedule;
use crate::typing::Typing;
struct Offer {
at: usize,
stored: Stored,
own: Own,
slot: Option<Hash>,
}
enum Own {
Moves,
Shares(Offered),
Copies,
}
pub(crate) struct Offers {
pending: Vec<Offer>,
keys: BTreeMap<usize, Hash>,
range: Extent,
}
impl Offers {
pub(crate) fn of(
held: &mut Render,
(keys, found): (&BTreeMap<String, Hash>, &Frontier),
memory: &Memory,
) -> Offers {
let mut pending = Vec::new();
if let Some(table) = &mut held.table {
let tys = &held.tys;
for (path, key) in keys {
if found.stored.contains_key(path) || !found.visited.contains(path) {
continue;
}
let Some((id, at)) = tys.id(path).and_then(|id| Some((id, table.of(id)?))) else {
continue;
};
if let Some(stored) = offerable(table, tys, (id, at), (path, *key)) {
pending.push(Offer {
at,
stored,
own: Own::Moves,
slot: table.slot(at),
});
}
}
}
let keys: BTreeMap<usize, Hash> = pending.iter().map(|o| (o.at, o.stored.key)).collect();
if let Some(table) = &mut held.table {
for offer in &mut pending {
if moved(table, offer.at, &keys).is_none() {
offer.own = table.offered(offer.at).map_or(Own::Copies, Own::Shares);
}
if let Own::Shares(source) = &offer.own {
let stored = offer.stored.clone();
memory.offer(stored, source.clone(), (offer.slot, false));
}
}
}
Offers {
pending,
keys,
range: held.range.unwrap_or(Extent::NOWHERE),
}
}
pub(crate) fn whole(&mut self, table: &Table, memory: &Memory) {
let done = |offer: &Offer| {
let value = &table.values[offer.at];
if !matches!(offer.own, Own::Shares(_)) {
return false;
}
let support = value.support();
let mut computed = Segments::default();
value.evaluated.iter().for_each(|e| computed.add(*e));
support.is_bounded() && computed.covers(&Segments::of(support))
};
let (whole, rest): (Vec<Offer>, Vec<Offer>) = std::mem::take(&mut self.pending)
.into_iter()
.partition(done);
self.pending = rest;
self.offer(whole, table, memory);
}
pub(crate) fn rest(&mut self, table: &Table, memory: &Memory) {
let mut rest = std::mem::take(&mut self.pending);
rest.sort_by_key(|offer| matches!(offer.own, Own::Moves));
self.offer(rest, table, memory);
}
fn offer(&mut self, offers: Vec<Offer>, table: &Table, memory: &Memory) {
for mut offer in offers {
offer.stored.label = table.label(offer.at);
let source = match offer.own {
Own::Shares(source) => source,
Own::Moves => match moved(table, offer.at, &self.keys) {
Some(source) => source,
None => continue,
},
Own::Copies => {
let over = self.range.intersect(offer.stored.support);
Offered::Held(vec![Arc::new(table.samples(offer.at, over))])
}
};
memory.offer(offer.stored, source, (offer.slot, true));
}
}
}
fn offerable(
table: &Table,
tys: &Typing,
(id, at): (NodeId, usize),
(path, key): (&str, Hash),
) -> Option<Stored> {
let value = &table.values[at];
let own = tys.name(id) == path;
let readable = readable(tys, id) && value.alias().is_none();
let kept =
value.pure && value.period.is_none() && !matches!(value.kind, table::Kind::Frames { .. });
if !own || !kept || !(at == table.root || readable) {
return None;
}
let (priced, moved) = under(table, at);
let ty = tys.ty(id);
Some(Stored {
key,
label: table.label(at),
width: u8::try_from(value.width).expect("a width the typing held"),
codomain: ty.codomain,
rate: ty.rate,
grid: tys.grid(id),
support: value.support(),
priced,
moved,
readable,
held: Vec::new(),
})
}
pub(crate) fn readable(tys: &Typing, id: NodeId) -> bool {
tys.ty(id).held == Representation::Sampled && !schedule::anywhere(tys, id)
}
fn moved(table: &Table, at: usize, offered: &BTreeMap<usize, Hash>) -> Option<Offered> {
let (moved, by) = moves(table, at)?;
let resident = match &table.values[moved].kind {
table::Kind::Resident(stored) => Some(stored.key),
_ => None,
};
let of = offered.get(&moved).copied().or(resident)?;
Some(Offered::Moves { of, by })
}
fn moves(table: &Table, at: usize) -> Option<(usize, i64)> {
let (mut read, mut by) = table.values[at].moves()?;
while let Some((next, shift)) = table.values[read].moves() {
(read, by) = (next, by + shift);
}
Some((read, by))
}
fn under(table: &Table, at: usize) -> (u128, f64) {
let (mut seen, mut open) = (BTreeSet::from([at]), vec![at]);
let (mut priced, mut moved) = (0u128, 0.0f64);
while let Some(at) = open.pop() {
priced += table.planned[at];
moved = moved.max(table.values[at].moved);
open.extend(
table.values[at]
.reads
.iter()
.filter(|read| seen.insert(**read)),
);
}
(priced, moved)
}