1use std::{
2 fmt::Display,
3 ops::{Deref, DerefMut},
4 sync::LazyLock,
5};
6
7use bincode_trait_derive::{Decode, Encode};
8use linnet::half_edge::involution::EdgeIndex;
9use schemars::{JsonSchema, json_schema};
10use serde::{Deserialize, Serialize};
11use symbolica::{
12 atom::{Atom, AtomCore, AtomView, Symbol},
13 domains::{
14 algebraic_number::AlgebraicExtension,
15 integer::IntegerRing,
16 rational::{FractionField, Q},
17 },
18 function, parse,
19 poly::polynomial::PolynomialRing,
20 printer::{PrintMode, PrintOptions},
21 symbol,
22};
23
24use crate::GammaLoopContext;
25
26use super::{GS, W_};
27
28pub static Q_I: LazyLock<AlgebraicExtension<FractionField<IntegerRing>>> =
29 LazyLock::new(|| AlgebraicExtension::new_complex(Q));
30static RAW_UFO_MOMENTUM: LazyLock<Symbol> = LazyLock::new(|| symbol!("UFO::P"));
31static RAW_UFO_PSLASH: LazyLock<Symbol> = LazyLock::new(|| symbol!("UFO::PSlash"));
32
33pub static COMPLEXRATPOLYFIELD: LazyLock<
34 FractionField<PolynomialRing<AlgebraicExtension<FractionField<IntegerRing>>, u16>>,
35> = LazyLock::new(|| FractionField::new(PolynomialRing::<_, u16>::new(Q_I.clone())));
36
37pub static LOGPRINTOPTS: LazyLock<PrintOptions> = LazyLock::new(|| PrintOptions {
38 hide_all_namespaces: true,
39 color_namespace: false,
40 color_builtin_symbols: false,
41 color_top_level_sum: false,
42 terms_on_new_line: false,
43 print_ring: false,
44 include_attributes: false,
45 symmetric_representation_for_finite_field: false,
46 explicit_rational_polynomial: false,
47 number_thousands_separator: None,
48 multiplication_operator: '*',
49 double_star_for_exponentiation: false,
50 num_exp_as_superscript: false,
51 mode: PrintMode::Symbolica,
52 precision: None,
53 pretty_matrix: false,
54 max_terms: None,
55 custom_print_mode: Default::default(),
56 hide_namespace: Some(std::borrow::Cow::Borrowed("gammalooprs")),
57 ..PrintOptions::new()
58});
59
60#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Encode, Decode)]
61#[trait_decode(trait = GammaLoopContext)]
62pub struct StringSerializedAtom(pub Atom);
63
64impl JsonSchema for StringSerializedAtom {
65 fn schema_name() -> std::borrow::Cow<'static, str> {
66 "ParseableAtom".into()
67 }
68
69 fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
70 json_schema!({
71 "description": "An atom that is serialized as a string. Do not make any assumptions about the state",
72 "type": ["string"]
73 })
74 }
75}
76
77impl Deref for StringSerializedAtom {
78 type Target = Atom;
79
80 fn deref(&self) -> &Self::Target {
81 &self.0
82 }
83}
84
85impl DerefMut for StringSerializedAtom {
86 fn deref_mut(&mut self) -> &mut Self::Target {
87 &mut self.0
88 }
89}
90
91impl Display for StringSerializedAtom {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 write!(f, "{}", self.0)
94 }
95}
96
97impl Serialize for StringSerializedAtom {
98 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
99 where
100 S: serde::Serializer,
101 {
102 self.0.to_canonical_string().serialize(serializer)
103 }
104}
105
106impl<'de> Deserialize<'de> for StringSerializedAtom {
107 fn deserialize<D>(deserializer: D) -> std::result::Result<StringSerializedAtom, D::Error>
108 where
109 D: serde::Deserializer<'de>,
110 {
111 Ok(StringSerializedAtom(parse!(String::deserialize(
112 deserializer
113 )?)))
114 }
115}
116
117pub trait DOD {
118 fn edge_dod(&self, eid: EdgeIndex) -> i32;
120
121 fn all_dod(&self) -> i32;
123
124 fn trailing_exponent(&self) -> i32;
125}
126
127impl DOD for Atom {
128 fn edge_dod(&self, eid: EdgeIndex) -> i32 {
129 self.as_view().edge_dod(eid)
130 }
131
132 fn all_dod(&self) -> i32 {
133 self.as_view().all_dod()
134 }
135
136 fn trailing_exponent(&self) -> i32 {
137 self.as_view().trailing_exponent()
138 }
139}
140
141impl DOD for AtomView<'_> {
142 fn edge_dod(&self, eid: EdgeIndex) -> i32 {
143 self.replace(GS.emr_mom(eid, W_.a___))
144 .with(GS.emr_mom(eid, W_.a___) / GS.rescale)
145 .trailing_exponent()
146 }
147
148 fn all_dod(&self) -> i32 {
149 self.replace(function!(GS.emr_mom, W_.a___))
150 .with(function!(GS.emr_mom, W_.a___) / GS.rescale)
151 .replace(function!(*RAW_UFO_MOMENTUM, W_.a___))
152 .with(function!(*RAW_UFO_MOMENTUM, W_.a___) / GS.rescale)
153 .replace(function!(*RAW_UFO_PSLASH, W_.a___))
154 .with(function!(*RAW_UFO_PSLASH, W_.a___) / GS.rescale)
155 .trailing_exponent()
156 }
157
158 fn trailing_exponent(&self) -> i32 {
159 let series = self.series(GS.rescale, Atom::Zero, 1).unwrap();
160 let dod = series.get_trailing_exponent();
161
162 if dod.is_integer() {
163 -(dod.numerator().to_i64().unwrap() as i32)
164 } else {
165 panic!("{dod} for {self}")
166 }
167 }
168}
169
170#[test]
171fn test_dod() {
172 let (e1, e2) = (EdgeIndex(1), EdgeIndex(2));
173
174 let atom = (GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e2, Atom::Zero)
175 + GS.emr_mom(e2, Atom::Zero))
176 / (GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e1, Atom::Zero));
177
178 let atom2 =
179 Atom::num(1) / (GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e1, Atom::Zero) + parse!("m"));
180 let atom3 =
181 GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e2, Atom::Zero) + GS.emr_mom(e2, Atom::Zero);
182
183 assert_eq!(-1, atom.edge_dod(e1));
184 assert_eq!(1, atom.edge_dod(e2));
185 assert_eq!(-2, atom2.edge_dod(e1));
186 assert_eq!(1, atom3.edge_dod(e1));
187 assert_eq!(1, atom3.edge_dod(e2));
188}