1use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7
8#[derive(Clone, Debug)]
9pub struct FunctionalArea {
10 pub id: u64,
11 pub name: String, pub blocks: Vec<u32>, pub primary_domain: String,
14 pub cross_modal_connections: Vec<u64>, pub plasticity_index: f32, pub compensation_level: f32, }
18
19#[derive(Clone, Debug)]
20pub struct SensoriMotorMap {
21 pub input_block: u32,
22 pub output_blocks: Vec<u32>,
23 pub strength: f32,
24 pub remapping_count: u32,
25 pub efficiency: f32,
26}
27
28pub struct FunctionalPlasticity {
29 pub areas: HashMap<u64, FunctionalArea>,
30 pub sensorimotor_maps: Vec<SensoriMotorMap>,
31 pub crossmodal_connections: HashMap<(u64, u64), f32>, pub damage_compensation: HashMap<u64, f32>, }
34
35impl Default for FunctionalPlasticity {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl FunctionalPlasticity {
42 pub fn new() -> Self {
43 Self {
44 areas: HashMap::new(),
45 sensorimotor_maps: Vec::new(),
46 crossmodal_connections: HashMap::new(),
47 damage_compensation: HashMap::new(),
48 }
49 }
50
51 pub fn create_area(&mut self, name: &str, domain: &str, seed_blocks: Vec<u32>) -> u64 {
53 let area_id = Self::area_hash(name, domain);
54
55 let area = FunctionalArea {
56 id: area_id,
57 name: name.to_string(),
58 blocks: seed_blocks,
59 primary_domain: domain.to_string(),
60 cross_modal_connections: Vec::new(),
61 plasticity_index: 0.5,
62 compensation_level: 0.0,
63 };
64
65 self.areas.insert(area_id, area);
66 area_id
67 }
68
69 pub fn map_sensorimotor(&mut self, input: u32, outputs: Vec<u32>) -> f32 {
71 let strength = (outputs.len() as f32 / 10.0).min(1.0);
72
73 self.sensorimotor_maps.push(SensoriMotorMap {
74 input_block: input,
75 output_blocks: outputs,
76 strength,
77 remapping_count: 0,
78 efficiency: 0.5,
79 });
80
81 strength
82 }
83
84 pub fn remap_pathway(&mut self, old_input: u32, new_input: u32) -> bool {
86 if let Some(map) = self
87 .sensorimotor_maps
88 .iter_mut()
89 .find(|m| m.input_block == old_input)
90 {
91 map.input_block = new_input;
92 map.remapping_count += 1;
93 map.efficiency = (map.efficiency + 0.05).min(1.0);
94 return true;
95 }
96 false
97 }
98
99 pub fn connect_areas(&mut self, area1_id: u64, area2_id: u64) -> bool {
101 if self.areas.contains_key(&area1_id) && self.areas.contains_key(&area2_id) {
102 let strength = 0.3;
103 self.crossmodal_connections
104 .insert((area1_id, area2_id), strength);
105 self.crossmodal_connections
106 .insert((area2_id, area1_id), strength);
107
108 if let Some(a1) = self.areas.get_mut(&area1_id) {
109 a1.cross_modal_connections.push(area2_id);
110 }
111 if let Some(a2) = self.areas.get_mut(&area2_id) {
112 a2.cross_modal_connections.push(area1_id);
113 }
114
115 return true;
116 }
117 false
118 }
119
120 pub fn strengthen_crossmodal(&mut self, area1_id: u64, area2_id: u64, use_success: bool) {
122 let key = (area1_id, area2_id);
123 if let Some(strength) = self.crossmodal_connections.get_mut(&key) {
124 let delta = if use_success { 0.05 } else { -0.02 };
125 *strength = (*strength + delta).clamp(0.0, 1.0);
126 }
127 }
128
129 pub fn damage_area(&mut self, area_id: u64, severity: f32) {
131 let connected: Vec<u64> = if let Some(area) = self.areas.get_mut(&area_id) {
132 area.plasticity_index = (area.plasticity_index - severity).max(0.0);
133 area.cross_modal_connections.clone()
134 } else {
135 Vec::new()
136 };
137
138 for connected_id in connected {
140 let comp_strength = (severity * 0.7).min(1.0);
141 self.damage_compensation.insert(connected_id, comp_strength);
142
143 if let Some(conn_area) = self.areas.get_mut(&connected_id) {
144 conn_area.compensation_level =
145 (conn_area.compensation_level + comp_strength).min(1.0);
146 conn_area.plasticity_index = (conn_area.plasticity_index + 0.1).min(1.0);
147 }
148 }
149 }
150
151 pub fn recover_compensation(&mut self, area_id: u64, recovery_rate: f32) -> f32 {
153 if let Some(comp) = self.damage_compensation.get_mut(&area_id) {
154 *comp = (*comp - recovery_rate).max(0.0);
155 return *comp;
156 }
157 0.0
158 }
159
160 pub fn stats(&self) -> (u32, u32, usize, f32) {
162 let total_areas = self.areas.len() as u32;
163 let total_blocks: u32 = self.areas.values().map(|a| a.blocks.len() as u32).sum();
164 let total_maps = self.sensorimotor_maps.len();
165 let avg_plasticity = if !self.areas.is_empty() {
166 self.areas.values().map(|a| a.plasticity_index).sum::<f32>() / self.areas.len() as f32
167 } else {
168 0.0
169 };
170
171 (total_areas, total_blocks, total_maps, avg_plasticity)
172 }
173
174 pub fn areas_by_domain(&self, domain: &str) -> Vec<&FunctionalArea> {
176 self.areas
177 .values()
178 .filter(|a| a.primary_domain == domain)
179 .collect()
180 }
181
182 pub fn most_plastic(&self, k: usize) -> Vec<(&str, f32)> {
184 let mut areas: Vec<_> = self
185 .areas
186 .values()
187 .map(|a| (a.name.as_str(), a.plasticity_index))
188 .collect();
189 areas.sort_by(|a, b| b.1.total_cmp(&a.1));
190 areas.into_iter().take(k).collect()
191 }
192
193 pub fn save(&self, dir: &Path) -> Result<(), String> {
194 let path = dir.join("functional_plasticity.bin");
195 let mut data = Vec::new();
196
197 data.extend_from_slice(b"FNPL");
198 data.push(1);
199
200 let area_count = self.areas.len() as u32;
202 data.extend_from_slice(&area_count.to_le_bytes());
203 for area in self.areas.values() {
204 data.extend_from_slice(&area.id.to_le_bytes());
205
206 let name_bytes = area.name.as_bytes();
207 data.push(name_bytes.len() as u8);
208 data.extend_from_slice(name_bytes);
209
210 let domain_bytes = area.primary_domain.as_bytes();
211 data.push(domain_bytes.len() as u8);
212 data.extend_from_slice(domain_bytes);
213
214 data.extend_from_slice(&area.plasticity_index.to_le_bytes());
215 data.extend_from_slice(&area.compensation_level.to_le_bytes());
216
217 let block_count = area.blocks.len() as u16;
218 data.extend_from_slice(&block_count.to_le_bytes());
219 for &block in &area.blocks {
220 data.extend_from_slice(&block.to_le_bytes());
221 }
222 }
223
224 let map_count = self.sensorimotor_maps.len() as u32;
226 data.extend_from_slice(&map_count.to_le_bytes());
227 for map in &self.sensorimotor_maps {
228 data.extend_from_slice(&map.input_block.to_le_bytes());
229 data.extend_from_slice(&map.strength.to_le_bytes());
230 data.extend_from_slice(&map.efficiency.to_le_bytes());
231 data.extend_from_slice(&map.remapping_count.to_le_bytes());
232
233 let out_count = map.output_blocks.len() as u16;
234 data.extend_from_slice(&out_count.to_le_bytes());
235 for &out in &map.output_blocks {
236 data.extend_from_slice(&out.to_le_bytes());
237 }
238 }
239
240 let tmp_path = dir.join("functional_plasticity.bin.tmp");
241 fs::write(&tmp_path, data).map_err(|e| e.to_string())?;
242 fs::rename(&tmp_path, &path).map_err(|e| e.to_string())
243 }
244
245 pub fn load(dir: &Path) -> Result<Self, String> {
246 let path = dir.join("functional_plasticity.bin");
247 if !path.exists() {
248 return Ok(Self::new());
249 }
250
251 let data = fs::read(&path).map_err(|e| e.to_string())?;
252 if data.len() < 5 || &data[0..4] != b"FNPL" {
253 return Ok(Self::new());
254 }
255
256 let mut idx = 5;
257 let mut areas = HashMap::new();
258 let mut sensorimotor_maps = Vec::new();
259
260 if idx + 4 <= data.len() {
262 let area_count =
263 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
264 as usize;
265 idx += 4;
266
267 for _ in 0..area_count {
268 if idx + 18 > data.len() {
269 break;
270 }
271
272 let id = u64::from_le_bytes([
273 data[idx],
274 data[idx + 1],
275 data[idx + 2],
276 data[idx + 3],
277 data[idx + 4],
278 data[idx + 5],
279 data[idx + 6],
280 data[idx + 7],
281 ]);
282 idx += 8;
283
284 if idx >= data.len() {
285 break;
286 }
287 let name_len = data[idx] as usize;
288 idx += 1;
289
290 if idx + name_len > data.len() {
291 break;
292 }
293 let name = String::from_utf8_lossy(&data[idx..idx + name_len]).to_string();
294 idx += name_len;
295
296 if idx >= data.len() {
297 break;
298 }
299 let domain_len = data[idx] as usize;
300 idx += 1;
301
302 if idx + domain_len > data.len() {
303 break;
304 }
305 let primary_domain =
306 String::from_utf8_lossy(&data[idx..idx + domain_len]).to_string();
307 idx += domain_len;
308
309 if idx + 12 > data.len() {
310 break;
311 }
312 let plasticity_index =
313 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
314 idx += 4;
315 let compensation_level =
316 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
317 idx += 4;
318
319 let block_count = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
320 idx += 2;
321
322 let mut blocks = Vec::new();
323 for _ in 0..block_count {
324 if idx + 4 > data.len() {
325 break;
326 }
327 let block = u32::from_le_bytes([
328 data[idx],
329 data[idx + 1],
330 data[idx + 2],
331 data[idx + 3],
332 ]);
333 blocks.push(block);
334 idx += 4;
335 }
336
337 areas.insert(
338 id,
339 FunctionalArea {
340 id,
341 name,
342 blocks,
343 primary_domain,
344 cross_modal_connections: Vec::new(),
345 plasticity_index,
346 compensation_level,
347 },
348 );
349 }
350 }
351
352 if idx + 4 <= data.len() {
354 let map_count =
355 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
356 as usize;
357 idx += 4;
358
359 for _ in 0..map_count {
360 if idx + 20 > data.len() {
361 break;
362 }
363
364 let input_block =
365 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
366 idx += 4;
367
368 let strength =
369 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
370 idx += 4;
371
372 let efficiency =
373 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
374 idx += 4;
375
376 let remapping_count =
377 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
378 idx += 4;
379
380 let out_count = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
381 idx += 2;
382
383 let mut output_blocks = Vec::new();
384 for _ in 0..out_count {
385 if idx + 4 > data.len() {
386 break;
387 }
388 let out = u32::from_le_bytes([
389 data[idx],
390 data[idx + 1],
391 data[idx + 2],
392 data[idx + 3],
393 ]);
394 output_blocks.push(out);
395 idx += 4;
396 }
397
398 sensorimotor_maps.push(SensoriMotorMap {
399 input_block,
400 output_blocks,
401 strength,
402 remapping_count,
403 efficiency,
404 });
405 }
406 }
407
408 Ok(Self {
409 areas,
410 sensorimotor_maps,
411 crossmodal_connections: HashMap::new(),
412 damage_compensation: HashMap::new(),
413 })
414 }
415
416 pub fn load_or_init(dir: &Path) -> Self {
417 Self::load(dir).unwrap_or_else(|_| Self::new())
418 }
419
420 fn area_hash(name: &str, domain: &str) -> u64 {
421 let mut hash: u64 = 0xcbf29ce484222325;
422 for &b in (name.to_string() + domain).as_bytes().iter().take(16) {
423 hash = hash.wrapping_mul(0x100000001b3) ^ (b as u64);
424 }
425 hash
426 }
427}