use serde::{Deserialize, Serialize};
use crate::error::{Error, Result};
use crate::time::{Frames, TimeBase};
use super::kind::{Framed, KindTag, LatentKind};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(bound(serialize = "", deserialize = ""))]
pub struct Latent<K: LatentKind> {
repr: K::Repr,
time_base: TimeBase,
}
impl<K: LatentKind> Latent<K> {
pub fn new(repr: K::Repr, time_base: TimeBase) -> Self {
Self { repr, time_base }
}
pub fn repr(&self) -> &K::Repr {
&self.repr
}
pub fn time_base(&self) -> TimeBase {
self.time_base
}
pub fn frames(&self) -> Frames {
Frames(self.repr.frames() as u64)
}
pub fn duration_seconds(&self) -> f64 {
self.time_base.seconds(self.frames())
}
pub fn into_parts(self) -> (K::Repr, TimeBase) {
(self.repr, self.time_base)
}
pub fn blocks(&self, block: Frames) -> Result<Vec<LatentBlock<K>>> {
let frames = self.repr.frames();
let size = usize::try_from(block.get())
.map_err(|_| Error::validation("block size overflows usize"))?;
if size == 0 {
return Err(Error::validation("block size must be nonzero"));
}
if !frames.is_multiple_of(size) {
return Err(Error::validation(format!(
"{frames} frames is not a whole number of {size}-frame blocks"
)));
}
Ok((0..frames / size)
.map(|i| LatentBlock::new(self.repr.slice_frames(i * size, size)))
.collect())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LatentBlock<K: LatentKind>(K::Repr);
impl<K: LatentKind> LatentBlock<K> {
pub fn new(repr: K::Repr) -> Self {
Self(repr)
}
pub fn repr(&self) -> &K::Repr {
&self.0
}
pub fn frames(&self) -> Frames {
Frames(self.0.frames() as u64)
}
pub fn into_repr(self) -> K::Repr {
self.0
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AnyLatent {
Continuous(Latent<super::Continuous>),
Tokens(Latent<super::Tokens>),
}
impl AnyLatent {
pub fn tag(&self) -> KindTag {
match self {
AnyLatent::Continuous(_) => KindTag::Continuous,
AnyLatent::Tokens(_) => KindTag::Tokens,
}
}
pub fn time_base(&self) -> TimeBase {
match self {
AnyLatent::Continuous(latent) => latent.time_base(),
AnyLatent::Tokens(latent) => latent.time_base(),
}
}
pub fn frames(&self) -> Frames {
match self {
AnyLatent::Continuous(latent) => latent.frames(),
AnyLatent::Tokens(latent) => latent.frames(),
}
}
pub fn downcast<K: LatentKind>(self) -> Result<Latent<K>> {
let actual = self.tag();
K::unwrap_any(self).ok_or(Error::KindMismatch {
expected: K::TAG,
actual,
})
}
}
impl<K: LatentKind> From<Latent<K>> for AnyLatent {
fn from(latent: Latent<K>) -> Self {
K::wrap_any(latent)
}
}
#[cfg(test)]
mod tests {
use std::num::NonZeroU32;
use super::*;
use crate::latent::{Continuous, FrameSeq, TokenGrid, Tokens};
use crate::time::SampleRate;
fn tb() -> TimeBase {
TimeBase::new(
SampleRate::try_from(48_000).unwrap(),
NonZeroU32::new(480).unwrap(),
)
}
fn seq(frames: usize, dim: usize) -> FrameSeq {
let values: Vec<f32> = (0..frames * dim).map(|i| i as f32).collect();
FrameSeq::new(frames, dim, values).unwrap()
}
#[test]
fn latent_knows_its_clock() {
let latent = Latent::<Continuous>::new(seq(100, 4), tb());
assert_eq!(latent.frames(), Frames(100));
assert!((latent.duration_seconds() - 1.0).abs() < 1e-12);
}
#[test]
fn blocks_cut_exactly_or_reject() {
let latent = Latent::<Continuous>::new(seq(6, 2), tb());
let blocks = latent.blocks(Frames(2)).unwrap();
assert_eq!(blocks.len(), 3);
assert!(blocks.iter().all(|b| b.frames() == Frames(2)));
assert_eq!(blocks[1].repr().values(), &[4.0, 5.0, 6.0, 7.0]);
assert!(latent.blocks(Frames(0)).is_err());
assert!(latent.blocks(Frames(4)).is_err()); }
#[test]
fn downcast_is_the_one_checked_gate() {
let any: AnyLatent = Latent::<Continuous>::new(seq(2, 2), tb()).into();
assert_eq!(any.tag(), KindTag::Continuous);
assert!(any.clone().downcast::<Continuous>().is_ok());
let err = any.downcast::<Tokens>().unwrap_err();
assert!(matches!(
err,
Error::KindMismatch {
expected: KindTag::Tokens,
actual: KindTag::Continuous
}
));
}
#[test]
fn token_latents_flow_the_same_way() {
let grid = TokenGrid::new(vec![vec![1, 2, 3], vec![4, 5, 6]]).unwrap();
let any: AnyLatent = Latent::<Tokens>::new(grid, tb()).into();
assert_eq!(any.frames(), Frames(3));
let back = any.downcast::<Tokens>().unwrap();
assert_eq!(back.repr().codebooks(), 2);
}
#[test]
fn serde_round_trips_typed_and_dynamic() {
let latent = Latent::<Continuous>::new(seq(2, 3), tb());
let json = serde_json::to_string(&latent).unwrap();
assert_eq!(
serde_json::from_str::<Latent<Continuous>>(&json).unwrap(),
latent
);
let any = AnyLatent::from(latent);
let json = serde_json::to_string(&any).unwrap();
assert_eq!(serde_json::from_str::<AnyLatent>(&json).unwrap(), any);
}
}