1use num_complex::Complex64;
7use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
8use std::fmt;
9use std::ops::{Add, Neg, Sub};
10
11use crate::error::Error;
12use crate::traits::{Bosonic, Fermionic, Statistics, StatisticsType};
13
14#[derive(Debug, Clone, Copy)]
55pub struct MatsubaraFreq<S: StatisticsType> {
56 n: i64,
57 _phantom: std::marker::PhantomData<S>,
58}
59
60pub type FermionicFreq = MatsubaraFreq<Fermionic>;
62pub type BosonicFreq = MatsubaraFreq<Bosonic>;
63
64impl<S: StatisticsType> MatsubaraFreq<S> {
65 pub fn n(&self) -> i64 {
69 self.n
70 }
71
72 pub fn new(n: i64) -> Result<Self, Error> {
90 let allowed = match S::STATISTICS {
92 Statistics::Fermionic => n % 2 != 0, Statistics::Bosonic => n % 2 == 0, };
95
96 if !allowed {
97 return Err(Error::InvalidMatsubaraIndex {
98 n,
99 statistics: S::STATISTICS,
100 });
101 }
102
103 Ok(Self {
104 n,
105 _phantom: std::marker::PhantomData,
106 })
107 }
108
109 pub unsafe fn new_unchecked(n: i64) -> Self {
115 Self {
116 n,
117 _phantom: std::marker::PhantomData,
118 }
119 }
120
121 pub fn get_n(&self) -> i64 {
123 self.n
124 }
125
126 pub fn value(&self, beta: f64) -> f64 {
134 self.n as f64 * std::f64::consts::PI / beta
135 }
136
137 pub fn value_imaginary(&self, beta: f64) -> Complex64 {
145 Complex64::new(0.0, self.value(beta))
146 }
147
148 pub fn statistics(&self) -> Statistics {
150 S::STATISTICS
151 }
152
153 pub fn into_i64(self) -> i64 {
155 self.n
156 }
157}
158
159impl Default for FermionicFreq {
161 fn default() -> Self {
162 unsafe { Self::new_unchecked(1) }
164 }
165}
166
167impl Default for BosonicFreq {
168 fn default() -> Self {
169 unsafe { Self::new_unchecked(0) }
171 }
172}
173
174impl<S: StatisticsType> From<MatsubaraFreq<S>> for i64 {
176 fn from(freq: MatsubaraFreq<S>) -> Self {
177 freq.n
178 }
179}
180
181macro_rules! impl_freq_arithmetic {
190 ($lhs:ty, $rhs:ty => $out:ty) => {
191 impl Add<$rhs> for $lhs {
192 type Output = $out;
193
194 fn add(self, other: $rhs) -> $out {
195 unsafe { <$out>::new_unchecked(self.n + other.n) }
198 }
199 }
200
201 impl Sub<$rhs> for $lhs {
202 type Output = $out;
203
204 fn sub(self, other: $rhs) -> $out {
205 unsafe { <$out>::new_unchecked(self.n - other.n) }
207 }
208 }
209 };
210}
211
212impl_freq_arithmetic!(FermionicFreq, FermionicFreq => BosonicFreq);
213impl_freq_arithmetic!(FermionicFreq, BosonicFreq => FermionicFreq);
214impl_freq_arithmetic!(BosonicFreq, FermionicFreq => FermionicFreq);
215impl_freq_arithmetic!(BosonicFreq, BosonicFreq => BosonicFreq);
216
217impl<S: StatisticsType> Neg for MatsubaraFreq<S> {
219 type Output = Self;
220
221 fn neg(self) -> Self {
222 unsafe { Self::new_unchecked(-self.n) }
224 }
225}
226
227impl<S: StatisticsType> PartialEq for MatsubaraFreq<S> {
229 fn eq(&self, other: &Self) -> bool {
230 self.n == other.n
231 }
232}
233
234impl<S: StatisticsType> Eq for MatsubaraFreq<S> {}
235
236impl<S: StatisticsType> PartialOrd for MatsubaraFreq<S> {
237 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
238 Some(self.cmp(other))
239 }
240}
241
242impl<S: StatisticsType> Ord for MatsubaraFreq<S> {
243 fn cmp(&self, other: &Self) -> Ordering {
244 self.n.cmp(&other.n)
245 }
246}
247
248impl<S: StatisticsType> std::hash::Hash for MatsubaraFreq<S> {
250 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
251 self.n.hash(state);
252 }
253}
254
255impl<S: StatisticsType> fmt::Display for MatsubaraFreq<S> {
257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258 match self.n {
259 0 => write!(f, "0"),
260 1 => write!(f, "π/β"),
261 -1 => write!(f, "-π/β"),
262 n => write!(f, "{}π/β", n),
263 }
264 }
265}
266
267#[allow(dead_code)]
270pub(crate) fn sign<S: StatisticsType>(freq: &MatsubaraFreq<S>) -> i32 {
271 if freq.n > 0 {
272 1
273 } else if freq.n < 0 {
274 -1
275 } else {
276 0
277 }
278}
279
280#[allow(dead_code)]
282pub(crate) fn fermionic_sign<S: StatisticsType>() -> i32 {
283 match S::STATISTICS {
284 Statistics::Fermionic => -1,
285 Statistics::Bosonic => 1,
286 }
287}
288
289#[allow(dead_code)]
291pub(crate) fn zero() -> BosonicFreq {
292 unsafe { BosonicFreq::new_unchecked(0) }
293}
294
295#[allow(dead_code)]
297#[doc(hidden)]
298pub fn is_zero<S: StatisticsType>(freq: &MatsubaraFreq<S>) -> bool {
299 match S::STATISTICS {
300 Statistics::Fermionic => false, Statistics::Bosonic => freq.n == 0,
302 }
303}
304
305#[allow(dead_code)]
307pub(crate) fn is_less<S1: StatisticsType, S2: StatisticsType>(
308 a: &MatsubaraFreq<S1>,
309 b: &MatsubaraFreq<S2>,
310) -> bool {
311 a.get_n() < b.get_n()
312}
313
314#[allow(dead_code)]
316pub(crate) fn create_statistics(zeta: i64) -> Result<Statistics, String> {
317 match zeta {
318 1 => Ok(Statistics::Fermionic),
319 0 => Ok(Statistics::Bosonic),
320 _ => Err(format!("Unknown statistics type: zeta={}", zeta)),
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn test_fermionic_frequency_creation() {
330 let freq1 = FermionicFreq::new(1).unwrap();
332 assert_eq!(freq1.get_n(), 1);
333
334 let freq3 = FermionicFreq::new(3).unwrap();
335 assert_eq!(freq3.get_n(), 3);
336
337 let freq_neg1 = FermionicFreq::new(-1).unwrap();
338 assert_eq!(freq_neg1.get_n(), -1);
339
340 assert!(FermionicFreq::new(0).is_err());
342 assert!(FermionicFreq::new(2).is_err());
343 assert!(FermionicFreq::new(-2).is_err());
344 }
345
346 #[test]
347 fn test_bosonic_frequency_creation() {
348 let freq0 = BosonicFreq::new(0).unwrap();
350 assert_eq!(freq0.get_n(), 0);
351
352 let freq2 = BosonicFreq::new(2).unwrap();
353 assert_eq!(freq2.get_n(), 2);
354
355 let freq_neg2 = BosonicFreq::new(-2).unwrap();
356 assert_eq!(freq_neg2.get_n(), -2);
357
358 assert!(BosonicFreq::new(1).is_err());
360 assert!(BosonicFreq::new(3).is_err());
361 assert!(BosonicFreq::new(-1).is_err());
362 }
363
364 #[test]
365 fn test_frequency_values() {
366 let beta = 2.0;
367 let pi = std::f64::consts::PI;
368
369 let fermionic = FermionicFreq::new(1).unwrap();
370 assert!((fermionic.value(beta) - pi / 2.0).abs() < 1e-14);
371
372 let bosonic = BosonicFreq::new(0).unwrap();
373 assert_eq!(bosonic.value(beta), 0.0);
374
375 let bosonic2 = BosonicFreq::new(2).unwrap();
376 assert!((bosonic2.value(beta) - pi).abs() < 1e-14);
377 }
378
379 #[test]
380 fn test_imaginary_values() {
381 let beta = 2.0;
382 let pi = std::f64::consts::PI;
383
384 let fermionic = FermionicFreq::new(1).unwrap();
385 let imag = fermionic.value_imaginary(beta);
386 assert!((imag.re - 0.0).abs() < 1e-14);
387 assert!((imag.im - pi / 2.0).abs() < 1e-14);
388 }
389
390 #[test]
391 fn test_operator_overloading() {
392 let bfreq2 = BosonicFreq::new(2).unwrap();
394 let bfreq4 = BosonicFreq::new(4).unwrap();
395
396 let sum = bfreq2 + bfreq4;
397 assert_eq!(sum.get_n(), 6);
398
399 let diff = bfreq4 - bfreq2;
400 assert_eq!(diff.get_n(), 2);
401
402 let freq1 = FermionicFreq::new(1).unwrap();
404 let neg = -freq1;
405 assert_eq!(neg.get_n(), -1);
406
407 let neg_b = -bfreq2;
408 assert_eq!(neg_b.get_n(), -2);
409 }
410
411 #[test]
417 fn test_sum_and_difference_follow_the_statistics() {
418 let f1 = FermionicFreq::new(1).unwrap();
419 let f3 = FermionicFreq::new(3).unwrap();
420 let b2 = BosonicFreq::new(2).unwrap();
421
422 let sum: BosonicFreq = f1 + f3;
423 assert_eq!(sum, BosonicFreq::new(4).unwrap());
424 let diff: BosonicFreq = f1 - f3;
425 assert_eq!(diff, BosonicFreq::new(-2).unwrap());
426
427 let fb_sum: FermionicFreq = f1 + b2;
428 assert_eq!(fb_sum, FermionicFreq::new(3).unwrap());
429 let fb_diff: FermionicFreq = f3 - b2;
430 assert_eq!(fb_diff, FermionicFreq::new(1).unwrap());
431 let bf_sum: FermionicFreq = b2 + f1;
432 assert_eq!(bf_sum, FermionicFreq::new(3).unwrap());
433 let bf_diff: FermionicFreq = b2 - f3;
434 assert_eq!(bf_diff, FermionicFreq::new(-1).unwrap());
435
436 let bb: BosonicFreq = b2 + b2;
437 assert_eq!(bb, BosonicFreq::new(4).unwrap());
438 }
439
440 #[test]
441 fn test_comparison_operators() {
442 let freq1 = FermionicFreq::new(1).unwrap();
443 let freq3 = FermionicFreq::new(3).unwrap();
444 let freq1_copy = FermionicFreq::new(1).unwrap();
445
446 assert_eq!(freq1, freq1_copy);
447 assert_ne!(freq1, freq3);
448 assert!(freq1 < freq3);
449 assert!(freq3 > freq1);
450 assert!(freq1 <= freq1_copy);
451 assert!(freq1 >= freq1_copy);
452 }
453
454 #[test]
455 fn test_utility_functions() {
456 let fermionic = FermionicFreq::new(1).unwrap();
457 let bosonic = BosonicFreq::new(0).unwrap();
458 let bosonic2 = BosonicFreq::new(2).unwrap();
459
460 assert_eq!(sign(&fermionic), 1);
462 assert_eq!(sign(&bosonic), 0);
463 assert_eq!(sign(&-fermionic), -1);
464
465 assert_eq!(fermionic_sign::<Fermionic>(), -1);
467 assert_eq!(fermionic_sign::<Bosonic>(), 1);
468
469 let zero_freq = zero();
471 assert_eq!(zero_freq.get_n(), 0);
472
473 assert!(!is_zero(&fermionic));
475 assert!(is_zero(&bosonic));
476 assert!(!is_zero(&bosonic2));
477
478 assert!(is_less(&fermionic, &bosonic2));
480 assert!(!is_less(&bosonic2, &fermionic));
481 }
482
483 #[test]
484 fn test_statistics_creation() {
485 assert_eq!(create_statistics(1).unwrap(), Statistics::Fermionic);
486 assert_eq!(create_statistics(0).unwrap(), Statistics::Bosonic);
487 assert!(create_statistics(2).is_err());
488 }
489
490 #[test]
491 fn test_display() {
492 let freq1 = FermionicFreq::new(1).unwrap();
493 let freq_neg1 = FermionicFreq::new(-1).unwrap();
494 let freq0 = BosonicFreq::new(0).unwrap();
495 let freq2 = BosonicFreq::new(2).unwrap();
496
497 assert_eq!(format!("{}", freq1), "π/β");
498 assert_eq!(format!("{}", freq_neg1), "-π/β");
499 assert_eq!(format!("{}", freq0), "0");
500 assert_eq!(format!("{}", freq2), "2π/β");
501 }
502
503 #[test]
504 fn test_default_implementations() {
505 let default_fermionic = FermionicFreq::default();
506 assert_eq!(default_fermionic.get_n(), 1);
507
508 let default_bosonic = BosonicFreq::default();
509 assert_eq!(default_bosonic.get_n(), 0);
510 }
511
512 #[test]
513 fn test_conversion_to_i64() {
514 let freq = FermionicFreq::new(3).unwrap();
515 let n: i64 = freq.into();
516 assert_eq!(n, 3);
517
518 let n_direct = freq.into_i64();
519 assert_eq!(n_direct, 3);
520 }
521
522 #[test]
523 fn test_sign() {
524 let fermionic_pos = FermionicFreq::new(3).unwrap();
525 assert_eq!(sign(&fermionic_pos), 1);
526
527 let fermionic_neg = FermionicFreq::new(-5).unwrap();
528 assert_eq!(sign(&fermionic_neg), -1);
529
530 let bosonic_zero = BosonicFreq::new(0).unwrap();
531 assert_eq!(sign(&bosonic_zero), 0);
532
533 let bosonic_pos = BosonicFreq::new(4).unwrap();
534 assert_eq!(sign(&bosonic_pos), 1);
535 }
536
537 #[test]
538 fn test_fermionic_sign() {
539 assert_eq!(fermionic_sign::<Fermionic>(), -1);
540 assert_eq!(fermionic_sign::<Bosonic>(), 1);
541 }
542
543 #[test]
544 fn test_zero() {
545 let zero_freq = zero();
546 assert_eq!(zero_freq.get_n(), 0);
547 assert_eq!(zero_freq.value(1.0), 0.0);
548 }
549
550 #[test]
551 fn test_is_zero() {
552 let fermionic = FermionicFreq::new(1).unwrap();
554 assert!(!is_zero(&fermionic));
555
556 let bosonic_zero = BosonicFreq::new(0).unwrap();
558 assert!(is_zero(&bosonic_zero));
559
560 let bosonic_nonzero = BosonicFreq::new(2).unwrap();
562 assert!(!is_zero(&bosonic_nonzero));
563 }
564
565 #[test]
566 fn test_is_less() {
567 let freq1 = FermionicFreq::new(1).unwrap();
568 let freq3 = FermionicFreq::new(3).unwrap();
569 assert!(is_less(&freq1, &freq3));
570 assert!(!is_less(&freq3, &freq1));
571
572 let fermionic = FermionicFreq::new(1).unwrap();
574 let bosonic = BosonicFreq::new(2).unwrap();
575 assert!(is_less(&fermionic, &bosonic));
576
577 assert!(!is_less(&freq1, &freq1));
579 }
580
581 #[test]
582 fn test_create_statistics() {
583 let fermionic = create_statistics(1).unwrap();
585 assert_eq!(fermionic, Statistics::Fermionic);
586
587 let bosonic = create_statistics(0).unwrap();
589 assert_eq!(bosonic, Statistics::Bosonic);
590
591 assert!(create_statistics(2).is_err());
593 assert!(create_statistics(-1).is_err());
594 }
595
596 #[test]
598 fn test_new_rejects_wrong_parity_with_typed_error() {
599 use crate::error::Error;
600
601 assert_eq!(
602 FermionicFreq::new(2).unwrap_err(),
603 Error::InvalidMatsubaraIndex {
604 n: 2,
605 statistics: Statistics::Fermionic,
606 }
607 );
608 assert_eq!(
609 BosonicFreq::new(-3).unwrap_err(),
610 Error::InvalidMatsubaraIndex {
611 n: -3,
612 statistics: Statistics::Bosonic,
613 }
614 );
615 assert_eq!(
616 BosonicFreq::new(-3).unwrap_err().to_string(),
617 "Matsubara frequency n = -3 is not allowed for bosonic statistics"
618 );
619 assert_eq!(FermionicFreq::new(-1).unwrap().n(), -1);
620 assert_eq!(BosonicFreq::new(-2).unwrap().n(), -2);
621 }
622}