1use std::ops::Range;
7use ternary_signal::Signal;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Clone, Debug)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub struct FieldVector {
22 signals: Vec<Signal>,
23}
24
25impl FieldVector {
26 pub fn new(dims: usize) -> Self {
28 Self {
29 signals: vec![Signal::ZERO; dims],
30 }
31 }
32
33 pub fn from_signals(signals: Vec<Signal>) -> Self {
35 Self { signals }
36 }
37
38 #[inline]
40 pub fn dims(&self) -> usize {
41 self.signals.len()
42 }
43
44 #[inline]
46 pub fn get(&self, idx: usize) -> Signal {
47 self.signals[idx]
48 }
49
50 #[inline]
52 pub fn set(&mut self, idx: usize, signal: Signal) {
53 self.signals[idx] = signal;
54 }
55
56 #[inline]
58 pub fn get_current(&self, idx: usize) -> i32 {
59 self.signals[idx].current()
60 }
61
62 #[inline]
64 pub fn set_current(&mut self, idx: usize, value: i32) {
65 self.signals[idx] = Signal::from_current(value);
66 }
67
68 #[inline]
73 pub fn get_i16(&self, idx: usize) -> i16 {
74 let s = self.signals[idx];
75 (s.polarity as i16) * (s.magnitude as i16)
76 }
77
78 #[inline]
83 pub fn set_i16(&mut self, idx: usize, value: i16) {
84 self.signals[idx] = Signal::from_signed_i32(value as i32);
85 }
86
87 pub fn decay(&mut self, retention: u8) {
93 for s in &mut self.signals {
94 let current = s.current();
95 if current == 0 {
96 continue;
97 }
98 let decayed = (current as i64 * retention as i64 / 255) as i32;
100 if decayed == 0 {
101 *s = Signal::ZERO;
102 } else {
103 *s = Signal::from_current(decayed);
104 }
105 }
106 }
107
108 pub fn add(&mut self, other: &FieldVector) {
110 debug_assert_eq!(self.dims(), other.dims());
111 for i in 0..self.signals.len() {
112 let a = self.get_current(i);
113 let b = other.get_current(i);
114 let sum = (a as i64 + b as i64).clamp(-65025, 65025) as i32;
115 self.set_current(i, sum);
116 }
117 }
118
119 pub fn add_to_range(&mut self, signals: &[Signal], range: Range<usize>) {
121 let range_len = range.len();
122 for (i, &s) in signals.iter().take(range_len).enumerate() {
123 let idx = range.start + i;
124 if idx < self.signals.len() {
125 let current = self.get_current(idx);
126 let delta = s.current();
127 let sum = (current as i64 + delta as i64).clamp(-65025, 65025) as i32;
128 self.set_current(idx, sum);
129 }
130 }
131 }
132
133 pub fn set_range(&mut self, signals: &[Signal], range: Range<usize>) {
135 let range_len = range.len();
136 for (i, &s) in signals.iter().take(range_len).enumerate() {
137 let idx = range.start + i;
138 if idx < self.signals.len() {
139 self.signals[idx] = s;
140 }
141 }
142 }
143
144 pub fn get_range(&self, range: Range<usize>) -> Vec<Signal> {
146 (range.start..range.end.min(self.dims()))
147 .map(|i| self.signals[i])
148 .collect()
149 }
150
151 pub fn range_energy(&self, range: Range<usize>) -> u64 {
154 (range.start..range.end.min(self.dims()))
155 .map(|i| {
156 let eff = self.signals[i].effective_magnitude() as u64;
157 eff * eff
158 })
159 .sum()
160 }
161
162 pub fn range_active(&self, range: Range<usize>, threshold: u64) -> bool {
164 self.range_energy(range) > threshold
165 }
166
167 pub fn is_zero(&self) -> bool {
169 self.signals.iter().all(|s| s.magnitude == 0)
170 }
171
172 pub fn non_zero_count(&self) -> usize {
174 self.signals.iter().filter(|s| s.magnitude > 0).count()
175 }
176
177 pub fn max_magnitude(&self) -> u16 {
179 self.signals.iter().map(|s| s.effective_magnitude()).max().unwrap_or(0)
180 }
181
182 pub fn scale(&mut self, factor: u8) {
184 for s in &mut self.signals {
185 let current = s.current();
186 let scaled = (current as i64 * factor as i64 / 255) as i32;
187 *s = Signal::from_current(scaled);
188 }
189 }
190
191 pub fn as_slice(&self) -> &[Signal] {
193 &self.signals
194 }
195
196 pub fn as_mut_slice(&mut self) -> &mut [Signal] {
198 &mut self.signals
199 }
200}
201
202impl Default for FieldVector {
203 fn default() -> Self {
204 Self::new(64)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_new_is_zero() {
214 let v = FieldVector::new(128);
215 assert!(v.is_zero());
216 assert_eq!(v.non_zero_count(), 0);
217 }
218
219 #[test]
220 fn test_set_get_signal() {
221 let mut v = FieldVector::new(64);
222 v.set(0, Signal::positive(200));
223 v.set(10, Signal::negative(128));
224
225 assert_eq!(v.get(0).polarity, 1);
226 assert_eq!(v.get(0).magnitude, 200);
227 assert_eq!(v.get(10).polarity, -1);
228 assert_eq!(v.get(10).magnitude, 128);
229 }
230
231 #[test]
232 fn test_current_interface() {
233 let mut v = FieldVector::new(64);
234 v.set_current(0, 5000); v.set_current(1, -3000); let val0 = v.get_current(0);
238 let val1 = v.get_current(1);
239 assert!((val0 - 5000).abs() < 20, "expected ~5000, got {}", val0);
241 assert!((val1 + 3000).abs() < 20, "expected ~-3000, got {}", val1);
242 }
243
244 #[test]
245 fn test_full_range_add() {
246 let mut v = FieldVector::new(4);
247 v.set(0, Signal::positive_amplified(200, 100)); let mut other = FieldVector::new(4);
250 other.set(0, Signal::positive_amplified(100, 50)); v.add(&other);
253 let result = v.get_current(0);
254 assert!((result - 25000).abs() < 100, "expected ~25000, got {}", result);
256 }
257
258 #[test]
259 fn test_decay_full_range() {
260 let mut v = FieldVector::new(64);
261 v.set(0, Signal::positive_amplified(255, 100)); v.set(1, Signal::negative_amplified(200, 50)); v.decay(128); let val0 = v.get_current(0);
267 let val1 = v.get_current(1);
268 assert!((val0 - 12800).abs() < 200, "expected ~12800, got {}", val0);
270 assert!((val1 + 5020).abs() < 200, "expected ~-5020, got {}", val1);
272 }
273
274 #[test]
275 fn test_range_energy_full() {
276 let mut v = FieldVector::new(128);
277 for i in 0..4 {
279 v.set(i, Signal::positive_amplified(100, 50)); }
281
282 let energy = v.range_energy(0..4);
283 assert_eq!(energy, 100_000_000);
285 }
286
287 #[test]
288 fn test_add_to_range_full() {
289 let mut v = FieldVector::new(64);
290 let signals = vec![Signal::positive_amplified(100, 10); 4]; v.add_to_range(&signals, 0..4);
292
293 let val = v.get_current(0);
294 assert!((val - 1000).abs() < 10, "expected ~1000, got {}", val);
295 }
296
297 #[test]
298 fn test_set_range() {
299 let mut v = FieldVector::new(64);
300 let signals = vec![Signal::negative(50); 4];
301 v.set_range(&signals, 10..14);
302
303 assert_eq!(v.get(9).magnitude, 0);
304 assert_eq!(v.get(10).magnitude, 50);
305 assert_eq!(v.get(10).polarity, -1);
306 assert_eq!(v.get(13).magnitude, 50);
307 assert_eq!(v.get(14).magnitude, 0);
308 }
309
310 #[test]
311 fn test_get_range() {
312 let mut v = FieldVector::new(64);
313 v.set(5, Signal::positive(100));
314 v.set(6, Signal::negative(200));
315
316 let range = v.get_range(5..7);
317 assert_eq!(range.len(), 2);
318 assert_eq!(range[0].magnitude, 100);
319 assert_eq!(range[1].magnitude, 200);
320 }
321
322 #[test]
323 fn test_scale_full_range() {
324 let mut v = FieldVector::new(4);
325 v.set(0, Signal::positive_amplified(200, 100)); v.scale(128); let val = v.get_current(0);
330 assert!((val - 10039).abs() < 200, "expected ~10039, got {}", val);
332 }
333
334 #[test]
335 fn test_saturation_at_max() {
336 let mut v = FieldVector::new(4);
337 v.set(0, Signal::positive_amplified(255, 255)); let mut other = FieldVector::new(4);
340 other.set(0, Signal::positive_amplified(255, 255)); v.add(&other);
343 let result = v.get_current(0);
344 assert!(result <= 65025, "should clamp to 65025, got {}", result);
346 }
347
348 #[test]
350 fn test_i16_interface() {
351 let mut v = FieldVector::new(64);
352 v.set_i16(0, 200);
353 v.set_i16(1, -128);
354
355 assert_eq!(v.get_i16(0), 200);
356 assert_eq!(v.get_i16(1), -128);
357 }
358}