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> {
148 type Output = Self;
149
150 fn add(self, other: Self) -> Self {
151 unsafe { Self::new_unchecked(self.n + other.n) }
152 }
153}
154
155impl<S: StatisticsType> Sub for MatsubaraFreq<S> {
157 type Output = Self;
158
159 fn sub(self, other: Self) -> Self {
160 unsafe { Self::new_unchecked(self.n - other.n) }
161 }
162}
163
164impl<S: StatisticsType> Neg for MatsubaraFreq<S> {
166 type Output = Self;
167
168 fn neg(self) -> Self {
169 unsafe { Self::new_unchecked(-self.n) }
170 }
171}
172
173impl<S: StatisticsType> PartialEq for MatsubaraFreq<S> {
175 fn eq(&self, other: &Self) -> bool {
176 self.n == other.n
177 }
178}
179
180impl<S: StatisticsType> Eq for MatsubaraFreq<S> {}
181
182impl<S: StatisticsType> PartialOrd for MatsubaraFreq<S> {
183 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
184 Some(self.cmp(other))
185 }
186}
187
188impl<S: StatisticsType> Ord for MatsubaraFreq<S> {
189 fn cmp(&self, other: &Self) -> Ordering {
190 self.n.cmp(&other.n)
191 }
192}
193
194impl<S: StatisticsType> std::hash::Hash for MatsubaraFreq<S> {
196 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
197 self.n.hash(state);
198 }
199}
200
201impl<S: StatisticsType> fmt::Display for MatsubaraFreq<S> {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 match self.n {
205 0 => write!(f, "0"),
206 1 => write!(f, "π/β"),
207 -1 => write!(f, "-π/β"),
208 n => write!(f, "{}π/β", n),
209 }
210 }
211}
212
213#[allow(dead_code)]
216pub(crate) fn sign<S: StatisticsType>(freq: &MatsubaraFreq<S>) -> i32 {
217 if freq.n > 0 {
218 1
219 } else if freq.n < 0 {
220 -1
221 } else {
222 0
223 }
224}
225
226#[allow(dead_code)]
228pub(crate) fn fermionic_sign<S: StatisticsType>() -> i32 {
229 match S::STATISTICS {
230 Statistics::Fermionic => -1,
231 Statistics::Bosonic => 1,
232 }
233}
234
235#[allow(dead_code)]
237pub(crate) fn zero() -> BosonicFreq {
238 unsafe { BosonicFreq::new_unchecked(0) }
239}
240
241#[allow(dead_code)]
243pub(crate) fn is_zero<S: StatisticsType>(freq: &MatsubaraFreq<S>) -> bool {
244 match S::STATISTICS {
245 Statistics::Fermionic => false, Statistics::Bosonic => freq.n == 0,
247 }
248}
249
250#[allow(dead_code)]
252pub(crate) fn is_less<S1: StatisticsType, S2: StatisticsType>(
253 a: &MatsubaraFreq<S1>,
254 b: &MatsubaraFreq<S2>,
255) -> bool {
256 a.get_n() < b.get_n()
257}
258
259#[allow(dead_code)]
261pub(crate) fn create_statistics(zeta: i64) -> Result<Statistics, String> {
262 match zeta {
263 1 => Ok(Statistics::Fermionic),
264 0 => Ok(Statistics::Bosonic),
265 _ => Err(format!("Unknown statistics type: zeta={}", zeta)),
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_fermionic_frequency_creation() {
275 let freq1 = FermionicFreq::new(1).unwrap();
277 assert_eq!(freq1.get_n(), 1);
278
279 let freq3 = FermionicFreq::new(3).unwrap();
280 assert_eq!(freq3.get_n(), 3);
281
282 let freq_neg1 = FermionicFreq::new(-1).unwrap();
283 assert_eq!(freq_neg1.get_n(), -1);
284
285 assert!(FermionicFreq::new(0).is_err());
287 assert!(FermionicFreq::new(2).is_err());
288 assert!(FermionicFreq::new(-2).is_err());
289 }
290
291 #[test]
292 fn test_bosonic_frequency_creation() {
293 let freq0 = BosonicFreq::new(0).unwrap();
295 assert_eq!(freq0.get_n(), 0);
296
297 let freq2 = BosonicFreq::new(2).unwrap();
298 assert_eq!(freq2.get_n(), 2);
299
300 let freq_neg2 = BosonicFreq::new(-2).unwrap();
301 assert_eq!(freq_neg2.get_n(), -2);
302
303 assert!(BosonicFreq::new(1).is_err());
305 assert!(BosonicFreq::new(3).is_err());
306 assert!(BosonicFreq::new(-1).is_err());
307 }
308
309 #[test]
310 fn test_frequency_values() {
311 let beta = 2.0;
312 let pi = std::f64::consts::PI;
313
314 let fermionic = FermionicFreq::new(1).unwrap();
315 assert!((fermionic.value(beta) - pi / 2.0).abs() < 1e-14);
316
317 let bosonic = BosonicFreq::new(0).unwrap();
318 assert_eq!(bosonic.value(beta), 0.0);
319
320 let bosonic2 = BosonicFreq::new(2).unwrap();
321 assert!((bosonic2.value(beta) - pi).abs() < 1e-14);
322 }
323
324 #[test]
325 fn test_imaginary_values() {
326 let beta = 2.0;
327 let pi = std::f64::consts::PI;
328
329 let fermionic = FermionicFreq::new(1).unwrap();
330 let imag = fermionic.value_imaginary(beta);
331 assert!((imag.re - 0.0).abs() < 1e-14);
332 assert!((imag.im - pi / 2.0).abs() < 1e-14);
333 }
334
335 #[test]
336 fn test_operator_overloading() {
337 let freq1 = FermionicFreq::new(1).unwrap();
338 let freq3 = FermionicFreq::new(3).unwrap();
339
340 let sum = freq1 + freq3;
342 assert_eq!(sum.get_n(), 4);
343
344 let diff = freq3 - freq1;
346 assert_eq!(diff.get_n(), 2);
347
348 let neg = -freq1;
350 assert_eq!(neg.get_n(), -1);
351 }
352
353 #[test]
354 fn test_comparison_operators() {
355 let freq1 = FermionicFreq::new(1).unwrap();
356 let freq3 = FermionicFreq::new(3).unwrap();
357 let freq1_copy = FermionicFreq::new(1).unwrap();
358
359 assert_eq!(freq1, freq1_copy);
360 assert_ne!(freq1, freq3);
361 assert!(freq1 < freq3);
362 assert!(freq3 > freq1);
363 assert!(freq1 <= freq1_copy);
364 assert!(freq1 >= freq1_copy);
365 }
366
367 #[test]
368 fn test_utility_functions() {
369 let fermionic = FermionicFreq::new(1).unwrap();
370 let bosonic = BosonicFreq::new(0).unwrap();
371 let bosonic2 = BosonicFreq::new(2).unwrap();
372
373 assert_eq!(sign(&fermionic), 1);
375 assert_eq!(sign(&bosonic), 0);
376 assert_eq!(sign(&-fermionic), -1);
377
378 assert_eq!(fermionic_sign::<Fermionic>(), -1);
380 assert_eq!(fermionic_sign::<Bosonic>(), 1);
381
382 let zero_freq = zero();
384 assert_eq!(zero_freq.get_n(), 0);
385
386 assert!(!is_zero(&fermionic));
388 assert!(is_zero(&bosonic));
389 assert!(!is_zero(&bosonic2));
390
391 assert!(is_less(&fermionic, &bosonic2));
393 assert!(!is_less(&bosonic2, &fermionic));
394 }
395
396 #[test]
397 fn test_statistics_creation() {
398 assert_eq!(create_statistics(1).unwrap(), Statistics::Fermionic);
399 assert_eq!(create_statistics(0).unwrap(), Statistics::Bosonic);
400 assert!(create_statistics(2).is_err());
401 }
402
403 #[test]
404 fn test_display() {
405 let freq1 = FermionicFreq::new(1).unwrap();
406 let freq_neg1 = FermionicFreq::new(-1).unwrap();
407 let freq0 = BosonicFreq::new(0).unwrap();
408 let freq2 = BosonicFreq::new(2).unwrap();
409
410 assert_eq!(format!("{}", freq1), "π/β");
411 assert_eq!(format!("{}", freq_neg1), "-π/β");
412 assert_eq!(format!("{}", freq0), "0");
413 assert_eq!(format!("{}", freq2), "2π/β");
414 }
415
416 #[test]
417 fn test_default_implementations() {
418 let default_fermionic = FermionicFreq::default();
419 assert_eq!(default_fermionic.get_n(), 1);
420
421 let default_bosonic = BosonicFreq::default();
422 assert_eq!(default_bosonic.get_n(), 0);
423 }
424
425 #[test]
426 fn test_conversion_to_i64() {
427 let freq = FermionicFreq::new(3).unwrap();
428 let n: i64 = freq.into();
429 assert_eq!(n, 3);
430
431 let n_direct = freq.into_i64();
432 assert_eq!(n_direct, 3);
433 }
434
435 #[test]
436 fn test_sign() {
437 let fermionic_pos = FermionicFreq::new(3).unwrap();
438 assert_eq!(sign(&fermionic_pos), 1);
439
440 let fermionic_neg = FermionicFreq::new(-5).unwrap();
441 assert_eq!(sign(&fermionic_neg), -1);
442
443 let bosonic_zero = BosonicFreq::new(0).unwrap();
444 assert_eq!(sign(&bosonic_zero), 0);
445
446 let bosonic_pos = BosonicFreq::new(4).unwrap();
447 assert_eq!(sign(&bosonic_pos), 1);
448 }
449
450 #[test]
451 fn test_fermionic_sign() {
452 assert_eq!(fermionic_sign::<Fermionic>(), -1);
453 assert_eq!(fermionic_sign::<Bosonic>(), 1);
454 }
455
456 #[test]
457 fn test_zero() {
458 let zero_freq = zero();
459 assert_eq!(zero_freq.get_n(), 0);
460 assert_eq!(zero_freq.value(1.0), 0.0);
461 }
462
463 #[test]
464 fn test_is_zero() {
465 let fermionic = FermionicFreq::new(1).unwrap();
467 assert!(!is_zero(&fermionic));
468
469 let bosonic_zero = BosonicFreq::new(0).unwrap();
471 assert!(is_zero(&bosonic_zero));
472
473 let bosonic_nonzero = BosonicFreq::new(2).unwrap();
475 assert!(!is_zero(&bosonic_nonzero));
476 }
477
478 #[test]
479 fn test_is_less() {
480 let freq1 = FermionicFreq::new(1).unwrap();
481 let freq3 = FermionicFreq::new(3).unwrap();
482 assert!(is_less(&freq1, &freq3));
483 assert!(!is_less(&freq3, &freq1));
484
485 let fermionic = FermionicFreq::new(1).unwrap();
487 let bosonic = BosonicFreq::new(2).unwrap();
488 assert!(is_less(&fermionic, &bosonic));
489
490 assert!(!is_less(&freq1, &freq1));
492 }
493
494 #[test]
495 fn test_create_statistics() {
496 let fermionic = create_statistics(1).unwrap();
498 assert_eq!(fermionic, Statistics::Fermionic);
499
500 let bosonic = create_statistics(0).unwrap();
502 assert_eq!(bosonic, Statistics::Bosonic);
503
504 assert!(create_statistics(2).is_err());
506 assert!(create_statistics(-1).is_err());
507 }
508}