phase_rs/
phase.rs

1//! Structure for representing phases, elements of the unit circle on the complex plane.
2
3use pretty::RcDoc;
4use winnow::{
5    LocatingSlice, ModalResult, Parser,
6    ascii::{float, multispace0},
7    combinator::{alt, delimited},
8};
9
10use crate::text::{HasParser, ToDoc};
11
12/// Represents a (global) phase operation.
13#[derive(Clone, Copy, Debug, PartialEq)]
14pub enum Phase {
15    /// Specifies the phase by an float, which should equal the specified angle divided by pi
16    Angle(f64),
17    /// -1 phase, equivalent to `Angle(1.0)`
18    MinusOne,
19    /// i phase, equivalent to `Angle(0.5)`
20    Imag,
21    /// -i phase, equivalent to `Angle(1.5)`
22    MinusImag,
23}
24
25impl Phase {
26    /// Construct a new `Phase` from a float representing the desired angle divided by pi.
27    /// Uses special phase enum variants when possible.
28    pub fn from_angle(f: f64) -> Self {
29        if f == 0.5 {
30            Phase::Imag
31        } else if f == 1.0 {
32            Phase::MinusOne
33        } else if f == 1.5 {
34            Phase::MinusImag
35        } else {
36            Phase::Angle(f)
37        }
38    }
39
40    /// Returns the angle specified by this phase, divided by pi.
41    /// e.g. if `phase.eval() == 1.0` then `phase` represents the angle `pi`
42    pub fn eval(&self) -> f64 {
43        match self {
44            Phase::Angle(a) => *a,
45            Phase::MinusOne => 1.0,
46            Phase::Imag => 0.5,
47            Phase::MinusImag => 1.5,
48        }
49    }
50}
51
52impl ToDoc for Phase {
53    fn to_doc(&self) -> RcDoc<'_> {
54        match self {
55            Phase::Angle(a) => RcDoc::text(format!("ph({a}pi)")),
56            Phase::MinusOne => RcDoc::text("-1"),
57            Phase::Imag => RcDoc::text("i"),
58            Phase::MinusImag => RcDoc::text("-i"),
59        }
60    }
61}
62
63impl HasParser for Phase {
64    fn parser(input: &mut LocatingSlice<&str>) -> ModalResult<Self> {
65        alt((
66            "-1".value(Phase::MinusOne),
67            "i".value(Phase::Imag),
68            "-i".value(Phase::MinusImag),
69            delimited(
70                ("ph(", multispace0),
71                float,
72                (multispace0, "pi", multispace0, ")"),
73            )
74            .map(Phase::Angle),
75        ))
76        .parse_next(input)
77    }
78}