Skip to main content

multiscreen_rs/
tile.rs

1use crate::error::{Error, Result};
2use crate::layout::TokenSpan;
3use serde::{Deserialize, Serialize};
4
5/// Logical `N_L x N_H` screening grid from the Multiscreen paper.
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
7pub struct ScreeningGridConfig {
8    /// Paper symbol `N_L`: number of residual screening layers represented by
9    /// the layout grid.
10    #[serde(alias = "rows")]
11    pub layer_count: usize,
12    /// Paper symbol `N_H`: number of parallel gated screening tiles per layer.
13    #[serde(alias = "columns")]
14    pub head_count: usize,
15}
16
17impl Default for ScreeningGridConfig {
18    fn default() -> Self {
19        Self {
20            layer_count: 2,
21            head_count: 2,
22        }
23    }
24}
25
26impl ScreeningGridConfig {
27    pub fn validate(&self) -> Result<()> {
28        if self.layer_count == 0 {
29            return Err(Error::Config(
30                "screening_grid layer_count must be greater than zero".into(),
31            ));
32        }
33        if self.head_count == 0 {
34            return Err(Error::Config(
35                "screening_grid head_count must be greater than zero".into(),
36            ));
37        }
38        Ok(())
39    }
40
41    pub fn tile_slots(&self) -> usize {
42        self.layer_count * self.head_count
43    }
44
45    #[deprecated(note = "use tile_slots for paper-aligned naming")]
46    pub fn cells(&self) -> usize {
47        self.tile_slots()
48    }
49}
50
51/// Controls how screens are split into sliding tiles.
52#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
53pub struct TileConfig {
54    #[serde(alias = "tile_size")]
55    pub tokens_per_tile: usize,
56    #[serde(alias = "tile_stride")]
57    pub tile_stride_tokens: usize,
58    #[serde(alias = "grid")]
59    pub screening_grid: ScreeningGridConfig,
60}
61
62impl Default for TileConfig {
63    fn default() -> Self {
64        Self {
65            tokens_per_tile: 32,
66            tile_stride_tokens: 16,
67            screening_grid: ScreeningGridConfig::default(),
68        }
69    }
70}
71
72impl TileConfig {
73    pub fn validate(&self) -> Result<()> {
74        if self.tokens_per_tile == 0 {
75            return Err(Error::Config(
76                "tokens_per_tile must be greater than zero".into(),
77            ));
78        }
79        if self.tile_stride_tokens == 0 {
80            return Err(Error::Config(
81                "tile_stride_tokens must be greater than zero".into(),
82            ));
83        }
84        self.screening_grid.validate()
85    }
86}
87
88/// A tile over a contiguous token span, mapped to paper-style layer/head slots.
89#[derive(Clone, Debug, PartialEq, Eq)]
90pub struct Tile {
91    pub index: usize,
92    pub screen_index: usize,
93    pub layer_index: usize,
94    pub head_index: usize,
95    pub span: TokenSpan,
96}
97
98impl Tile {
99    pub fn new(
100        index: usize,
101        screen_index: usize,
102        layer_index: usize,
103        head_index: usize,
104        span: TokenSpan,
105    ) -> Self {
106        Self {
107            index,
108            screen_index,
109            layer_index,
110            head_index,
111            span,
112        }
113    }
114
115    pub fn len(&self) -> usize {
116        self.span.len()
117    }
118
119    pub fn is_empty(&self) -> bool {
120        self.span.is_empty()
121    }
122}