ryg_rans_rs_cli/container/
model.rs1use crate::error::{AppError, FormatError};
7use sha2::{Digest, Sha256};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct FrequencyModel {
12 pub frequencies: Vec<u32>,
14 pub cumulative: Vec<u32>,
16 pub scale_bits: u8,
18 pub active_symbols: usize,
20}
21
22impl FrequencyModel {
23 pub fn build(histogram: &[u64; 256], scale_bits: u8) -> Result<Self, AppError> {
35 let target_total = 1u64 << scale_bits;
36
37 let total_count: u64 = histogram.iter().sum();
39 if total_count == 0 {
40 return Err(AppError::Format(FormatError {
41 detail: "empty histogram: no data to build model".into(),
42 block_index: None,
43 offset: None,
44 }));
45 }
46
47 let mut frequencies = [0u64; 256];
48 let mut remainders = [0u64; 256];
49 let mut active_count = 0usize;
50
51 for (sym, &count) in histogram.iter().enumerate() {
53 if count == 0 {
54 continue;
55 }
56 let quota_num = count * target_total;
57 let base = quota_num / total_count;
58 let rem = quota_num % total_count;
59
60 frequencies[sym] = base.max(1); remainders[sym] = rem;
62 active_count += 1;
63 }
64
65 let mut current_sum: u64 = frequencies.iter().sum();
67
68 if current_sum < target_total {
70 let mut candidates: Vec<(u64, usize)> = remainders
72 .iter()
73 .enumerate()
74 .filter(|&(sym_idx, &r)| r > 0 && frequencies[sym_idx] > 0)
75 .map(|(sym, &rem)| (rem, sym))
76 .collect();
77 candidates.sort_by(|a, b| {
79 b.0.cmp(&a.0) .then_with(|| histogram[a.1].cmp(&histogram[b.1]).reverse()) .then_with(|| a.1.cmp(&b.1)) });
83
84 for (_, sym) in candidates.iter().cycle() {
85 if current_sum >= target_total {
86 break;
87 }
88 frequencies[*sym] += 1;
89 current_sum += 1;
90 }
91 }
92
93 if current_sum > target_total {
95 let excess = (current_sum - target_total) as usize;
96
97 let mut candidates: Vec<(u64, usize)> = (0..256)
99 .filter(|&sym| frequencies[sym] > 1)
100 .map(|sym| (remainders[sym], sym))
101 .collect();
102 candidates.sort_by(|a, b| {
104 a.0.cmp(&b.0) .then_with(|| b.1.cmp(&a.1)) .then_with(|| b.1.cmp(&a.1)) });
108
109 for (_, sym) in candidates.iter() {
110 if excess == 0 {
111 break;
112 }
113 if frequencies[*sym] > 1 {
114 frequencies[*sym] -= 1;
115 }
116 }
117 }
118
119 debug_assert_eq!(frequencies.iter().sum::<u64>(), target_total);
121
122 let freq_u32: Vec<u32> = frequencies.iter().map(|&f| f as u32).collect();
124
125 let mut cum = Vec::with_capacity(257);
127 let mut acc = 0u32;
128 cum.push(0);
129 for &f in freq_u32.iter() {
130 acc = acc.checked_add(f).unwrap();
131 cum.push(acc);
132 }
133
134 Ok(Self {
135 frequencies: freq_u32,
136 cumulative: cum,
137 scale_bits,
138 active_symbols: active_count,
139 })
140 }
141
142 pub fn to_bytes(&self) -> Vec<u8> {
144 let entries: Vec<(u8, u32)> = (0..=255u16)
145 .filter(|&s| self.frequencies[s as usize] > 0)
146 .map(|s| (s as u8, self.frequencies[s as usize]))
147 .collect();
148
149 let mut buf = Vec::with_capacity(2 + entries.len() * 5);
150 buf.extend_from_slice(&(entries.len() as u16).to_le_bytes());
151 for (sym, freq) in &entries {
152 buf.push(*sym);
153 buf.extend_from_slice(&freq.to_le_bytes());
154 }
155 buf
156 }
157
158 pub fn from_bytes(bytes: &[u8], scale_bits: u8) -> Result<Self, AppError> {
160 if bytes.len() < 2 {
161 return Err(AppError::Format(FormatError {
162 detail: "model too short".into(),
163 block_index: None,
164 offset: None,
165 }));
166 }
167
168 let entry_count = u16::from_le_bytes([bytes[0], bytes[1]]) as usize;
169 let expected_len = 2 + entry_count * 5;
170 if bytes.len() < expected_len {
171 return Err(AppError::Format(FormatError {
172 detail: format!(
173 "model truncated: expected {} bytes, got {}",
174 expected_len,
175 bytes.len()
176 ),
177 block_index: None,
178 offset: None,
179 }));
180 }
181
182 let mut frequencies = vec![0u32; 256];
183 let mut prev_sym: Option<u8> = None;
184
185 for i in 0..entry_count {
186 let offset = 2 + i * 5;
187 let sym = bytes[offset];
188 let freq = u32::from_le_bytes([
189 bytes[offset + 1],
190 bytes[offset + 2],
191 bytes[offset + 3],
192 bytes[offset + 4],
193 ]);
194
195 if let Some(prev) = prev_sym {
197 if sym <= prev {
198 return Err(AppError::Format(FormatError {
199 detail: format!("duplicate or non-ascending symbol {} after {}", sym, prev),
200 block_index: None,
201 offset: None,
202 }));
203 }
204 }
205
206 if freq == 0 {
207 return Err(AppError::Format(FormatError {
208 detail: format!("zero frequency for symbol {}", sym),
209 block_index: None,
210 offset: None,
211 }));
212 }
213
214 frequencies[sym as usize] = freq;
215 prev_sym = Some(sym);
216 }
217
218 let total = 1u64 << scale_bits;
220 let sum: u64 = frequencies.iter().map(|&f| f as u64).sum();
221 if sum != total {
222 return Err(AppError::Format(FormatError {
223 detail: format!(
224 "model frequency sum {} does not match target {}",
225 sum, total
226 ),
227 block_index: None,
228 offset: None,
229 }));
230 }
231
232 let mut cum = Vec::with_capacity(257);
234 let mut acc = 0u32;
235 cum.push(0);
236 for &f in frequencies.iter() {
237 acc = acc.checked_add(f).unwrap();
238 cum.push(acc);
239 }
240
241 let active = frequencies.iter().filter(|&&f| f > 0).count();
242
243 Ok(Self {
244 frequencies,
245 cumulative: cum,
246 scale_bits,
247 active_symbols: active,
248 })
249 }
250
251 pub fn sha256(&self) -> [u8; 32] {
253 let bytes = self.to_bytes();
254 let mut hasher = Sha256::new();
255 hasher.update(&bytes);
256 let result = hasher.finalize();
257 let mut hash = [0u8; 32];
258 hash.copy_from_slice(&result);
259 hash
260 }
261
262 pub fn build_uniform(scale_bits: u8) -> Self {
264 let total = 1u64 << scale_bits;
265 let base = total / 256;
266 let mut frequencies = vec![base as u32; 256];
267 let remainder = total - base * 256;
268 if remainder > 0 {
269 frequencies[255] += remainder as u32;
270 }
271
272 let mut cum = Vec::with_capacity(257);
273 let mut acc = 0u32;
274 cum.push(0);
275 for &f in frequencies.iter() {
276 acc = acc.checked_add(f).unwrap();
277 cum.push(acc);
278 }
279
280 Self {
281 frequencies,
282 cumulative: cum,
283 scale_bits,
284 active_symbols: 256,
285 }
286 }
287}
288
289pub fn compute_histogram(data: &[u8]) -> [u64; 256] {
291 let mut hist = [0u64; 256];
292 for &b in data {
293 hist[b as usize] += 1;
294 }
295 hist
296}