1use num_complex::Complex64;
7use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
8use std::fmt;
9use std::ops::{Add, Neg, Sub};
10
11use crate::traits::{Bosonic, Fermionic, Statistics, StatisticsType};
12
13#[derive(Debug, Clone, Copy)]
22pub struct MatsubaraFreq<S: StatisticsType> {
23 n: i64,
24 _phantom: std::marker::PhantomData<S>,
25}
26
27pub type FermionicFreq = MatsubaraFreq<Fermionic>;
29pub type BosonicFreq = MatsubaraFreq<Bosonic>;
30
31impl<S: StatisticsType> MatsubaraFreq<S> {
32 pub fn n(&self) -> i64 {
34 self.n
35 }
36
37 pub fn new(n: i64) -> Result<Self, String> {
54 let allowed = match S::STATISTICS {
56 Statistics::Fermionic => n % 2 != 0, Statistics::Bosonic => n % 2 == 0, };
59
60 if !allowed {
61 return Err(format!(
62 "Frequency n={} is not allowed for {} statistics",
63 n,
64 S::STATISTICS.as_str()
65 ));
66 }
67
68 Ok(Self {
69 n,
70 _phantom: std::marker::PhantomData,
71 })
72 }
73
74 pub unsafe fn new_unchecked(n: i64) -> Self {
80 Self {
81 n,
82 _phantom: std::marker::PhantomData,
83 }
84 }
85
86 pub fn get_n(&self) -> i64 {
88 self.n
89 }
90
91 pub fn value(&self, beta: f64) -> f64 {
99 self.n as f64 * std::f64::consts::PI / beta
100 }
101
102 pub fn value_imaginary(&self, beta: f64) -> Complex64 {
110 Complex64::new(0.0, self.value(beta))
111 }
112
113 pub fn statistics(&self) -> Statistics {
115 S::STATISTICS
116 }
117
118 pub fn into_i64(self) -> i64 {
120 self.n
121 }
122}
123
124impl Default for FermionicFreq {
126 fn default() -> Self {
127 unsafe { Self::new_unchecked(1) }
129 }
130}
131
132impl Default for BosonicFreq {
133 fn default() -> Self {
134 unsafe { Self::new_unchecked(0) }
136 }
137}
138
139impl<S: StatisticsType> From<MatsubaraFreq<S>> for i64 {
141 fn from(freq: MatsubaraFreq<S>) -> Self {
142 freq.n
143 }
144}
145
146impl<S: StatisticsType> Add for MatsubaraFreq<S> {
150 type Output = Self;
151
152 fn add(self, other: Self) -> Self {
153 let result = self.n + other.n;
154 debug_assert!(
155 Self::new(result).is_ok(),
156 "MatsubaraFreq::Add produced invalid frequency n={} for {:?} statistics",
157 result,
158 S::STATISTICS,
159 );
160 unsafe { Self::new_unchecked(result) }
161 }
162}
163
164impl<S: StatisticsType> Sub for MatsubaraFreq<S> {
167 type Output = Self;
168
169 fn sub(self, other: Self) -> Self {
170 let result = self.n - other.n;
171 debug_assert!(
172 Self::new(result).is_ok(),
173 "MatsubaraFreq::Sub produced invalid frequency n={} for {:?} statistics",
174 result,
175 S::STATISTICS,
176 );
177 unsafe { Self::new_unchecked(result) }
178 }
179}
180
181impl<S: StatisticsType> Neg for MatsubaraFreq<S> {
183 type Output = Self;
184
185 fn neg(self) -> Self {
186 unsafe { Self::new_unchecked(-self.n) }
188 }
189}
190
191impl<S: StatisticsType> PartialEq for MatsubaraFreq<S> {
193 fn eq(&self, other: &Self) -> bool {
194 self.n == other.n
195 }
196}
197
198impl<S: StatisticsType> Eq for MatsubaraFreq<S> {}
199
200impl<S: StatisticsType> PartialOrd for MatsubaraFreq<S> {
201 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
202 Some(self.cmp(other))
203 }
204}
205
206impl<S: StatisticsType> Ord for MatsubaraFreq<S> {
207 fn cmp(&self, other: &Self) -> Ordering {
208 self.n.cmp(&other.n)
209 }
210}
211
212impl<S: StatisticsType> std::hash::Hash for MatsubaraFreq<S> {
214 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
215 self.n.hash(state);
216 }
217}
218
219impl<S: StatisticsType> fmt::Display for MatsubaraFreq<S> {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 match self.n {
223 0 => write!(f, "0"),
224 1 => write!(f, "π/β"),
225 -1 => write!(f, "-π/β"),
226 n => write!(f, "{}π/β", n),
227 }
228 }
229}
230
231#[allow(dead_code)]
234pub(crate) fn sign<S: StatisticsType>(freq: &MatsubaraFreq<S>) -> i32 {
235 if freq.n > 0 {
236 1
237 } else if freq.n < 0 {
238 -1
239 } else {
240 0
241 }
242}
243
244#[allow(dead_code)]
246pub(crate) fn fermionic_sign<S: StatisticsType>() -> i32 {
247 match S::STATISTICS {
248 Statistics::Fermionic => -1,
249 Statistics::Bosonic => 1,
250 }
251}
252
253#[allow(dead_code)]
255pub(crate) fn zero() -> BosonicFreq {
256 unsafe { BosonicFreq::new_unchecked(0) }
257}
258
259#[allow(dead_code)]
261pub(crate) fn is_zero<S: StatisticsType>(freq: &MatsubaraFreq<S>) -> bool {
262 match S::STATISTICS {
263 Statistics::Fermionic => false, Statistics::Bosonic => freq.n == 0,
265 }
266}
267
268#[allow(dead_code)]
270pub(crate) fn is_less<S1: StatisticsType, S2: StatisticsType>(
271 a: &MatsubaraFreq<S1>,
272 b: &MatsubaraFreq<S2>,
273) -> bool {
274 a.get_n() < b.get_n()
275}
276
277#[allow(dead_code)]
279pub(crate) fn create_statistics(zeta: i64) -> Result<Statistics, String> {
280 match zeta {
281 1 => Ok(Statistics::Fermionic),
282 0 => Ok(Statistics::Bosonic),
283 _ => Err(format!("Unknown statistics type: zeta={}", zeta)),
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_fermionic_frequency_creation() {
293 let freq1 = FermionicFreq::new(1).unwrap();
295 assert_eq!(freq1.get_n(), 1);
296
297 let freq3 = FermionicFreq::new(3).unwrap();
298 assert_eq!(freq3.get_n(), 3);
299
300 let freq_neg1 = FermionicFreq::new(-1).unwrap();
301 assert_eq!(freq_neg1.get_n(), -1);
302
303 assert!(FermionicFreq::new(0).is_err());
305 assert!(FermionicFreq::new(2).is_err());
306 assert!(FermionicFreq::new(-2).is_err());
307 }
308
309 #[test]
310 fn test_bosonic_frequency_creation() {
311 let freq0 = BosonicFreq::new(0).unwrap();
313 assert_eq!(freq0.get_n(), 0);
314
315 let freq2 = BosonicFreq::new(2).unwrap();
316 assert_eq!(freq2.get_n(), 2);
317
318 let freq_neg2 = BosonicFreq::new(-2).unwrap();
319 assert_eq!(freq_neg2.get_n(), -2);
320
321 assert!(BosonicFreq::new(1).is_err());
323 assert!(BosonicFreq::new(3).is_err());
324 assert!(BosonicFreq::new(-1).is_err());
325 }
326
327 #[test]
328 fn test_frequency_values() {
329 let beta = 2.0;
330 let pi = std::f64::consts::PI;
331
332 let fermionic = FermionicFreq::new(1).unwrap();
333 assert!((fermionic.value(beta) - pi / 2.0).abs() < 1e-14);
334
335 let bosonic = BosonicFreq::new(0).unwrap();
336 assert_eq!(bosonic.value(beta), 0.0);
337
338 let bosonic2 = BosonicFreq::new(2).unwrap();
339 assert!((bosonic2.value(beta) - pi).abs() < 1e-14);
340 }
341
342 #[test]
343 fn test_imaginary_values() {
344 let beta = 2.0;
345 let pi = std::f64::consts::PI;
346
347 let fermionic = FermionicFreq::new(1).unwrap();
348 let imag = fermionic.value_imaginary(beta);
349 assert!((imag.re - 0.0).abs() < 1e-14);
350 assert!((imag.im - pi / 2.0).abs() < 1e-14);
351 }
352
353 #[test]
354 fn test_operator_overloading() {
355 let bfreq2 = BosonicFreq::new(2).unwrap();
357 let bfreq4 = BosonicFreq::new(4).unwrap();
358
359 let sum = bfreq2 + bfreq4;
360 assert_eq!(sum.get_n(), 6);
361
362 let diff = bfreq4 - bfreq2;
363 assert_eq!(diff.get_n(), 2);
364
365 let freq1 = FermionicFreq::new(1).unwrap();
367 let neg = -freq1;
368 assert_eq!(neg.get_n(), -1);
369
370 let neg_b = -bfreq2;
371 assert_eq!(neg_b.get_n(), -2);
372 }
373
374 #[test]
375 fn test_comparison_operators() {
376 let freq1 = FermionicFreq::new(1).unwrap();
377 let freq3 = FermionicFreq::new(3).unwrap();
378 let freq1_copy = FermionicFreq::new(1).unwrap();
379
380 assert_eq!(freq1, freq1_copy);
381 assert_ne!(freq1, freq3);
382 assert!(freq1 < freq3);
383 assert!(freq3 > freq1);
384 assert!(freq1 <= freq1_copy);
385 assert!(freq1 >= freq1_copy);
386 }
387
388 #[test]
389 fn test_utility_functions() {
390 let fermionic = FermionicFreq::new(1).unwrap();
391 let bosonic = BosonicFreq::new(0).unwrap();
392 let bosonic2 = BosonicFreq::new(2).unwrap();
393
394 assert_eq!(sign(&fermionic), 1);
396 assert_eq!(sign(&bosonic), 0);
397 assert_eq!(sign(&-fermionic), -1);
398
399 assert_eq!(fermionic_sign::<Fermionic>(), -1);
401 assert_eq!(fermionic_sign::<Bosonic>(), 1);
402
403 let zero_freq = zero();
405 assert_eq!(zero_freq.get_n(), 0);
406
407 assert!(!is_zero(&fermionic));
409 assert!(is_zero(&bosonic));
410 assert!(!is_zero(&bosonic2));
411
412 assert!(is_less(&fermionic, &bosonic2));
414 assert!(!is_less(&bosonic2, &fermionic));
415 }
416
417 #[test]
418 fn test_statistics_creation() {
419 assert_eq!(create_statistics(1).unwrap(), Statistics::Fermionic);
420 assert_eq!(create_statistics(0).unwrap(), Statistics::Bosonic);
421 assert!(create_statistics(2).is_err());
422 }
423
424 #[test]
425 fn test_display() {
426 let freq1 = FermionicFreq::new(1).unwrap();
427 let freq_neg1 = FermionicFreq::new(-1).unwrap();
428 let freq0 = BosonicFreq::new(0).unwrap();
429 let freq2 = BosonicFreq::new(2).unwrap();
430
431 assert_eq!(format!("{}", freq1), "π/β");
432 assert_eq!(format!("{}", freq_neg1), "-π/β");
433 assert_eq!(format!("{}", freq0), "0");
434 assert_eq!(format!("{}", freq2), "2π/β");
435 }
436
437 #[test]
438 fn test_default_implementations() {
439 let default_fermionic = FermionicFreq::default();
440 assert_eq!(default_fermionic.get_n(), 1);
441
442 let default_bosonic = BosonicFreq::default();
443 assert_eq!(default_bosonic.get_n(), 0);
444 }
445
446 #[test]
447 fn test_conversion_to_i64() {
448 let freq = FermionicFreq::new(3).unwrap();
449 let n: i64 = freq.into();
450 assert_eq!(n, 3);
451
452 let n_direct = freq.into_i64();
453 assert_eq!(n_direct, 3);
454 }
455
456 #[test]
457 fn test_sign() {
458 let fermionic_pos = FermionicFreq::new(3).unwrap();
459 assert_eq!(sign(&fermionic_pos), 1);
460
461 let fermionic_neg = FermionicFreq::new(-5).unwrap();
462 assert_eq!(sign(&fermionic_neg), -1);
463
464 let bosonic_zero = BosonicFreq::new(0).unwrap();
465 assert_eq!(sign(&bosonic_zero), 0);
466
467 let bosonic_pos = BosonicFreq::new(4).unwrap();
468 assert_eq!(sign(&bosonic_pos), 1);
469 }
470
471 #[test]
472 fn test_fermionic_sign() {
473 assert_eq!(fermionic_sign::<Fermionic>(), -1);
474 assert_eq!(fermionic_sign::<Bosonic>(), 1);
475 }
476
477 #[test]
478 fn test_zero() {
479 let zero_freq = zero();
480 assert_eq!(zero_freq.get_n(), 0);
481 assert_eq!(zero_freq.value(1.0), 0.0);
482 }
483
484 #[test]
485 fn test_is_zero() {
486 let fermionic = FermionicFreq::new(1).unwrap();
488 assert!(!is_zero(&fermionic));
489
490 let bosonic_zero = BosonicFreq::new(0).unwrap();
492 assert!(is_zero(&bosonic_zero));
493
494 let bosonic_nonzero = BosonicFreq::new(2).unwrap();
496 assert!(!is_zero(&bosonic_nonzero));
497 }
498
499 #[test]
500 fn test_is_less() {
501 let freq1 = FermionicFreq::new(1).unwrap();
502 let freq3 = FermionicFreq::new(3).unwrap();
503 assert!(is_less(&freq1, &freq3));
504 assert!(!is_less(&freq3, &freq1));
505
506 let fermionic = FermionicFreq::new(1).unwrap();
508 let bosonic = BosonicFreq::new(2).unwrap();
509 assert!(is_less(&fermionic, &bosonic));
510
511 assert!(!is_less(&freq1, &freq1));
513 }
514
515 #[test]
516 fn test_create_statistics() {
517 let fermionic = create_statistics(1).unwrap();
519 assert_eq!(fermionic, Statistics::Fermionic);
520
521 let bosonic = create_statistics(0).unwrap();
523 assert_eq!(bosonic, Statistics::Bosonic);
524
525 assert!(create_statistics(2).is_err());
527 assert!(create_statistics(-1).is_err());
528 }
529}