1use std::fmt;
47use std::sync::{Arc, Mutex};
48
49use crate::algebraic::negative_root;
50use crate::exact_real::{ExactReal, RealError, DEFAULT_BUDGET};
51use crate::hyperreal::Hyperreal;
52use crate::matrix_exp::exp_matrix;
53use crate::numeric::Rational;
54use crate::puiseux::{expansion_term, remainder, Puiseux};
55
56pub const SIGN_ORDERS: i64 = 64;
58
59#[derive(Clone, Debug, PartialEq, Eq)]
61pub enum GermError {
62 NotComputed { order: Rational },
65 StandardOnly { order: Rational },
68 Inseparable(String),
70 Flat,
72 DivisionByZero,
74 NegativeRoot(u32),
76 Nonpositive,
78 BeyondEveryPower,
80 CarriesLog { infinite: bool },
83}
84
85impl fmt::Display for GermError {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 GermError::NotComputed { order } => write!(
89 f,
90 "the {} term of this flow is not computed exactly: its update matrices do not commute",
91 order_text(order)
92 ),
93 GermError::StandardOnly { order } => write!(
94 f,
95 "the {} term of this value is not known: a nonlinear flow is known by its standard part",
96 order_text(order)
97 ),
98 GermError::Inseparable(why) => f.write_str(why),
99 GermError::Flat => write!(
100 f,
101 "cannot tell this value from zero within its first {SIGN_ORDERS} orders in the infinitesimal"
102 ),
103 GermError::DivisionByZero => f.write_str("Division by zero"),
104 GermError::NegativeRoot(n) => f.write_str(&negative_root(*n)),
105 GermError::Nonpositive => f.write_str("logarithm of a nonpositive number"),
106 GermError::BeyondEveryPower => {
107 f.write_str("the exponential of this value is beyond every power of dt")
108 }
109 GermError::CarriesLog { infinite: false } => f.write_str("the logarithm of this value carries ln(dt)"),
110 GermError::CarriesLog { infinite: true } => f.write_str("the logarithm of this value carries ln(1/dt)"),
111 }
112 }
113}
114
115impl From<RealError> for GermError {
116 fn from(e: RealError) -> GermError {
117 GermError::Inseparable(e.to_string())
118 }
119}
120
121fn order_text(order: &Rational) -> String {
123 if order.is_integer() {
124 format!("dt^{order}")
125 } else {
126 format!("dt^({order})")
127 }
128}
129
130fn exponent(k: i64, q: u32) -> Rational {
132 Rational::from_ratio_i64(k, i64::from(q)).expect("q is positive")
133}
134
135fn gcd(a: u64, b: u64) -> u64 {
136 if b == 0 {
137 a
138 } else {
139 gcd(b, a % b)
140 }
141}
142
143fn lcm(a: u32, b: u32) -> u32 {
144 let g = gcd(u64::from(a), u64::from(b));
145 u32::try_from(u64::from(a) / g * u64::from(b)).expect("a germ's ramification fits")
146}
147
148#[derive(Debug)]
150pub struct Flow {
151 grid: u32,
153 terms: Vec<Vec<Vec<Rational>>>,
155 t: Rational,
156 delta: Rational,
157 start: Vec<Germ>,
158 commuting: bool,
159 exp_t: Vec<Vec<ExactReal>>,
160 expansion: Mutex<Vec<Vec<Vec<Rational>>>>,
162 exponent: Mutex<Vec<Vec<Vec<Rational>>>>,
164 q: u32,
167 floor: i64,
169 state: Mutex<std::collections::BTreeMap<i64, Vec<ExactReal>>>,
171}
172
173type Matrix = Vec<Vec<Rational>>;
174
175fn zeros(n: usize) -> Matrix {
176 vec![vec![Rational::zero(); n]; n]
177}
178
179fn identity(n: usize) -> Matrix {
180 (0..n)
181 .map(|i| (0..n).map(|j| if i == j { Rational::one() } else { Rational::zero() }).collect())
182 .collect()
183}
184
185fn mat_mul(a: &Matrix, b: &Matrix) -> Matrix {
186 let n = a.len();
187 (0..n)
188 .map(|i| {
189 (0..n)
190 .map(|j| (0..n).fold(Rational::zero(), |acc, k| acc.add(&a[i][k].mul(&b[k][j]))))
191 .collect()
192 })
193 .collect()
194}
195
196fn mat_add(a: &Matrix, b: &Matrix) -> Matrix {
197 a.iter()
198 .zip(b)
199 .map(|(x, y)| x.iter().zip(y).map(|(p, q)| p.add(q)).collect())
200 .collect()
201}
202
203fn mat_scale(a: &Matrix, s: &Rational) -> Matrix {
204 a.iter().map(|row| row.iter().map(|v| v.mul(s)).collect()).collect()
205}
206
207fn is_zero_matrix(a: &Matrix) -> bool {
208 a.iter().all(|row| row.iter().all(Rational::is_zero))
209}
210
211impl Flow {
212 pub fn new(terms: Vec<Matrix>, t: Rational, delta: Rational, start: Vec<Germ>) -> Option<Arc<Flow>> {
215 Flow::on_grid(1, terms, t, delta, start)
216 }
217
218 pub fn on_grid(grid: u32, terms: Vec<Matrix>, t: Rational, delta: Rational, start: Vec<Germ>) -> Option<Arc<Flow>> {
222 let n = start.len();
223 let g = grid as usize;
224 if n == 0
225 || grid == 0
226 || terms.len() < g
227 || terms.iter().any(|m| m.len() != n || m.iter().any(|r| r.len() != n))
228 || !terms[..g - 1].iter().all(is_zero_matrix)
229 {
230 return None;
231 }
232 let commuting = terms.iter().enumerate().all(|(i, a)| {
233 terms[i + 1..].iter().all(|b| mat_mul(a, b) == mat_mul(b, a))
234 });
235 let exp_t = exp_matrix(&terms[g - 1], &t)?;
236 let q = start.iter().map(Germ::ramification).fold(grid, lcm);
237 let start: Vec<Germ> = start.iter().map(|g| g.refined(q / g.q)).collect();
238 let floor = start.iter().map(Germ::floor).min().unwrap_or(0);
239 Some(Arc::new(Flow {
240 grid,
241 terms,
242 t,
243 delta,
244 start,
245 commuting,
246 exp_t,
247 expansion: Mutex::new(Vec::new()),
248 exponent: Mutex::new(Vec::new()),
249 q,
250 floor,
251 state: Mutex::new(std::collections::BTreeMap::new()),
252 }))
253 }
254
255 fn width(&self) -> usize {
256 self.start.len()
257 }
258
259 fn term(&self, k: usize) -> Matrix {
260 self.terms.get(k - 1).cloned().unwrap_or_else(|| zeros(self.width()))
261 }
262
263 fn log_term(&self, k: usize) -> Matrix {
265 let n = self.width();
266 let mut total = zeros(n);
267 let mut power: Vec<Matrix> = (0..=k).map(|j| if j == 0 { identity(n) } else { zeros(n) }).collect();
268 for count in 1..=k {
269 let mut next: Vec<Matrix> = vec![zeros(n); k + 1];
270 for (have, p) in power.iter().enumerate() {
271 if p.iter().all(|r| r.iter().all(Rational::is_zero)) {
272 continue;
273 }
274 for step in 1..=(k - have) {
275 next[have + step] = mat_add(&next[have + step], &mat_mul(p, &self.term(step)));
276 }
277 }
278 power = next;
279 let sign = if count % 2 == 1 { 1 } else { -1 };
280 let c = Rational::from_ratio_i64(sign, count as i64).expect("count ≥ 1");
281 total = mat_add(&total, &mat_scale(&power[k], &c));
282 }
283 total
284 }
285
286 fn exponent(&self, j: usize) -> Matrix {
288 let mut cache = self.exponent.lock().expect("unpoisoned");
289 while cache.len() < j {
290 let i = cache.len() + 1;
291 let c = mat_add(
292 &mat_scale(&self.log_term(i + self.grid as usize), &self.t),
293 &mat_scale(&self.log_term(i), &self.delta),
294 );
295 cache.push(c);
296 }
297 cache[j - 1].clone()
298 }
299
300 fn expansion(&self, m: usize) -> Matrix {
301 let mut cache = self.expansion.lock().expect("unpoisoned");
302 if cache.is_empty() {
303 cache.push(identity(self.width()));
304 }
305 while cache.len() <= m {
306 let mm = cache.len();
307 let mut total = zeros(self.width());
308 for k in 1..=mm {
309 let c = self.exponent(k);
310 if is_zero_matrix(&c) {
311 continue;
312 }
313 let term = mat_mul(&c, &cache[mm - k]);
314 total = mat_add(&total, &mat_scale(&term, &Rational::from_i64(k as i64)));
315 }
316 cache.push(mat_scale(&total, &Rational::from_ratio_i64(1, mm as i64).expect("mm ≥ 1")));
317 }
318 cache[m].clone()
319 }
320
321 fn state(&self, m: i64) -> Result<Vec<ExactReal>, GermError> {
325 let r = i64::from(self.q / self.grid);
326 let reach = (m - self.floor).max(0) / r;
327 if !self.commuting && (1..=reach as usize).any(|j| !is_zero_matrix(&self.exponent(j))) {
328 return Err(GermError::NotComputed { order: exponent(m, self.q) });
329 }
330 if let Some(s) = self.state.lock().expect("unpoisoned").get(&m) {
331 return Ok(s.clone());
332 }
333 let n = self.width();
334 let mut inner = vec![ExactReal::from_i64(0); n];
335 for a in 0..=reach as usize {
336 let e = self.expansion(a);
337 if is_zero_matrix(&e) {
338 continue;
339 }
340 let start: Vec<ExactReal> = self
341 .start
342 .iter()
343 .map(|g| g.coefficient(m - a as i64 * r))
344 .collect::<Result<_, _>>()?;
345 for (i, slot) in inner.iter_mut().enumerate() {
346 for (j, x) in start.iter().enumerate() {
347 if !e[i][j].is_zero() {
348 *slot = slot.add(&ExactReal::from_rational(e[i][j].clone()).mul(x));
349 }
350 }
351 }
352 }
353 let s: Vec<ExactReal> = (0..n)
354 .map(|i| (0..n).fold(ExactReal::from_i64(0), |acc, j| acc.add(&self.exp_t[i][j].mul(&inner[j]))))
355 .collect();
356 self.state.lock().expect("unpoisoned").insert(m, s.clone());
357 Ok(s)
358 }
359}
360
361#[derive(Debug)]
362enum Node {
363 Series(Hyperreal),
365 Constant(ExactReal),
367 Add(Germ, Germ),
368 Mul(Germ, Germ),
369 Neg(Germ),
370 Recip(Germ, i64, ExactReal),
372 Flow(Arc<Flow>, usize),
374 Standard(ExactReal),
376 Unlimited(i32),
378 Refine(Germ, u32),
381 Puiseux(Puiseux),
383 Exp(Germ),
385 Ln(Germ, ExactReal),
387 Root { g: Germ, n: u32, v: i64, c: ExactReal, lead: ExactReal },
389}
390
391#[derive(Clone, Debug)]
393pub struct Germ {
394 node: Arc<Node>,
395 floor: i64,
397 q: u32,
399 cache: Arc<Mutex<Vec<ExactReal>>>,
400}
401
402impl Germ {
403 pub fn is_transmissible(&self) -> bool {
406 match self.node.as_ref() {
407 Node::Series(_) | Node::Unlimited(_) => true,
408 Node::Constant(x) | Node::Standard(x) => x.is_transmissible(),
409 Node::Add(a, b) | Node::Mul(a, b) => a.is_transmissible() && b.is_transmissible(),
410 Node::Neg(a) | Node::Recip(a, _, _) | Node::Refine(a, _) | Node::Exp(a) | Node::Ln(a, _) => {
411 a.is_transmissible()
412 }
413 Node::Root { g, .. } => g.is_transmissible(),
414 Node::Puiseux(_) => true,
415 Node::Flow(flow, _) => flow.start.iter().all(Germ::is_transmissible),
416 }
417 }
418
419 fn of(node: Node, floor: i64, q: u32) -> Germ {
420 Germ {
421 node: Arc::new(node),
422 floor,
423 q,
424 cache: Arc::new(Mutex::new(Vec::new())),
425 }
426 }
427
428 pub fn from_hyperreal(h: &Hyperreal) -> Germ {
430 let floor = h.valuation().unwrap_or(0).min(0);
431 Germ::of(Node::Series(h.clone()), floor, 1)
432 }
433
434 pub fn from_puiseux(p: &Puiseux) -> Germ {
436 Germ::of(Node::Puiseux(p.clone()), p.order(), p.ramification())
437 }
438
439 pub fn from_i64(n: i64) -> Germ {
440 Germ::from_hyperreal(&Hyperreal::from_i64(n))
441 }
442
443 pub fn constant(x: &ExactReal) -> Germ {
445 Germ::of(Node::Constant(x.clone()), 0, 1)
446 }
447
448 pub fn as_hyperreal(&self) -> Option<&Hyperreal> {
450 match self.node.as_ref() {
451 Node::Series(h) => Some(h),
452 _ => None,
453 }
454 }
455
456 pub fn flow(flow: &Arc<Flow>, i: usize) -> Germ {
458 Germ::of(Node::Flow(flow.clone(), i), flow.floor, flow.q)
459 }
460
461 pub fn standard(x: &ExactReal) -> Germ {
464 Germ::of(Node::Standard(x.clone()), 0, 1)
465 }
466
467 pub fn unlimited(sign: i32) -> Germ {
469 Germ::of(Node::Unlimited(sign.signum()), 0, 1)
470 }
471
472 pub fn ramification(&self) -> u32 {
474 self.q
475 }
476
477 fn refined(&self, m: u32) -> Germ {
479 if m == 1 {
480 return self.clone();
481 }
482 Germ::of(Node::Refine(self.clone(), m), self.floor * i64::from(m), self.q * m)
483 }
484
485 fn met(&self, o: &Germ) -> (Germ, Germ, u32) {
487 let q = lcm(self.q, o.q);
488 (self.refined(q / self.q), o.refined(q / o.q), q)
489 }
490
491 fn sign_orders(&self) -> i64 {
493 SIGN_ORDERS * i64::from(self.q)
494 }
495
496 pub fn unlimited_sign(&self) -> Option<i32> {
498 match self.node.as_ref() {
499 Node::Unlimited(sign) => Some(*sign),
500 _ => None,
501 }
502 }
503
504 fn limited_by_construction(&self) -> bool {
506 self.floor >= 0 && self.unlimited_sign().is_none()
507 }
508
509 pub fn floor(&self) -> i64 {
511 self.floor
512 }
513
514 pub fn coefficient(&self, k: i64) -> Result<ExactReal, GermError> {
516 if k < self.floor {
517 return Ok(ExactReal::from_i64(0));
518 }
519 let at = (k - self.floor) as usize;
520 if let Some(c) = self.cache.lock().expect("unpoisoned").get(at) {
521 return Ok(c.clone());
522 }
523 let c = match self.node.as_ref() {
524 Node::Series(h) => ExactReal::from_algebraic(h.laurent_coefficient(k)),
525 Node::Constant(x) => {
526 if k == 0 {
527 x.clone()
528 } else {
529 ExactReal::from_i64(0)
530 }
531 }
532 Node::Add(a, b) => a.coefficient(k)?.add(&b.coefficient(k)?),
533 Node::Neg(a) => a.coefficient(k)?.neg(),
534 Node::Mul(a, b) => {
535 let mut total = ExactReal::from_i64(0);
536 for i in a.floor..=(k - b.floor) {
537 total = total.add(&a.coefficient(i)?.mul(&b.coefficient(k - i)?));
538 }
539 total
540 }
541 Node::Recip(g, order, lead) => {
542 if k == -order {
543 lead.recip()?
544 } else {
545 let mut total = ExactReal::from_i64(0);
546 for i in (-order)..k {
547 total = total.add(&self.coefficient(i)?.mul(&g.coefficient(order + k - i)?));
548 }
549 total.neg().div(lead)?
550 }
551 }
552 Node::Flow(flow, i) => flow.state(k)?[*i].clone(),
553 Node::Standard(x) => {
554 if k == 0 {
555 x.clone()
556 } else {
557 return Err(GermError::StandardOnly { order: Rational::from_i64(k) });
558 }
559 }
560 Node::Unlimited(_) => return Err(GermError::StandardOnly { order: Rational::from_i64(k) }),
561 Node::Refine(g, m) => {
562 let m = i64::from(*m);
563 if k.rem_euclid(m) == 0 {
564 g.coefficient(k.div_euclid(m))?
565 } else {
566 ExactReal::from_i64(0)
567 }
568 }
569 Node::Puiseux(p) => ExactReal::from_algebraic(p.coefficient(k)),
570 Node::Exp(g) => {
571 if k == 0 {
572 g.coefficient(0)?.exp()
573 } else {
574 let mut total = ExactReal::from_i64(0);
575 for j in 1..=k {
576 let gj = g.coefficient(j)?;
577 if !gj.is_exactly_zero() {
578 total = total.add(&ExactReal::from_i64(j).mul(&gj).mul(&self.coefficient(k - j)?));
579 }
580 }
581 total.mul(&ExactReal::from_rational(Rational::from_ratio_i64(1, k).expect("k ≥ 1")))
582 }
583 }
584 Node::Ln(g, g0) => {
585 if k == 0 {
586 g0.log()?
587 } else {
588 let mut inner = ExactReal::from_i64(0);
589 for j in 1..k {
590 let gk = g.coefficient(k - j)?;
591 if !gk.is_exactly_zero() {
592 inner = inner.add(&ExactReal::from_i64(j).mul(&self.coefficient(j)?).mul(&gk));
593 }
594 }
595 let inner = inner.mul(&ExactReal::from_rational(Rational::from_ratio_i64(1, k).expect("k ≥ 1")));
596 g.coefficient(k)?.sub(&inner).div(g0)?
597 }
598 }
599 Node::Root { g, n, v, c, lead } => {
600 let j = k - self.floor;
601 if j == 0 {
602 lead.clone()
603 } else {
604 let alpha = Rational::from_ratio_i64(1, i64::from(*n)).expect("n ≥ 1").add(&Rational::one());
605 let mut total = ExactReal::from_i64(0);
606 for i in 1..=j {
607 let gi = g.coefficient(v + i)?;
608 if !gi.is_exactly_zero() {
609 let weight = alpha.mul(&Rational::from_i64(i)).sub(&Rational::from_i64(j));
610 total = total.add(&ExactReal::from_rational(weight).mul(&gi).mul(&self.coefficient(k - i)?));
611 }
612 }
613 total.div(&ExactReal::from_i64(j).mul(c))?
614 }
615 }
616 };
617 let mut cache = self.cache.lock().expect("unpoisoned");
618 if cache.len() == at {
619 cache.push(c.clone());
620 }
621 Ok(c)
622 }
623
624 pub fn add(&self, o: &Germ) -> Germ {
625 match (self.unlimited_sign(), o.unlimited_sign()) {
626 (Some(s), None) if o.limited_by_construction() => return Germ::unlimited(s),
627 (None, Some(s)) if self.limited_by_construction() => return Germ::unlimited(s),
628 (Some(s), Some(t)) if s == t => return Germ::unlimited(s),
629 _ => {}
630 }
631 let (a, b, q) = self.met(o);
632 let floor = a.floor.min(b.floor);
633 Germ::of(Node::Add(a, b), floor, q)
634 }
635
636 pub fn neg(&self) -> Germ {
637 if let Some(s) = self.unlimited_sign() {
638 return Germ::unlimited(-s);
639 }
640 Germ::of(Node::Neg(self.clone()), self.floor, self.q)
641 }
642
643 pub fn sub(&self, o: &Germ) -> Germ {
644 self.add(&o.neg())
645 }
646
647 pub fn mul(&self, o: &Germ) -> Germ {
648 let known = |g: &Germ| match g.unlimited_sign() {
649 Some(s) => Some(s),
650 None if g.limited_by_construction() => g
651 .standard_part()
652 .ok()
653 .flatten()
654 .and_then(|x| x.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET).ok())
655 .map(|o| o as i32),
656 None => None,
657 };
658 if self.unlimited_sign().is_some() || o.unlimited_sign().is_some() {
659 if let (Some(s), Some(t)) = (known(self), known(o)) {
660 if s != 0 && t != 0 {
661 return Germ::unlimited(s * t);
662 }
663 }
664 }
665 let (a, b, q) = self.met(o);
666 let floor = a.floor + b.floor;
667 Germ::of(Node::Mul(a, b), floor, q)
668 }
669
670 pub fn leading(&self) -> Result<(i64, ExactReal), GermError> {
672 for k in self.floor..self.floor + self.sign_orders() {
673 let c = self.coefficient(k)?;
674 if !c.is_exactly_zero() {
675 let sign = c.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)?;
676 if sign != std::cmp::Ordering::Equal {
677 return Ok((k, c));
678 }
679 }
680 }
681 Err(GermError::Flat)
682 }
683
684 pub fn recip(&self) -> Result<Germ, GermError> {
685 let (order, lead) = match self.leading() {
686 Ok(l) => l,
687 Err(GermError::Flat) => return Err(GermError::DivisionByZero),
688 Err(e) => return Err(e),
689 };
690 Ok(Germ::of(Node::Recip(self.clone(), order, lead), -order, self.q))
691 }
692
693 pub fn root(&self, n: u32) -> Result<Germ, GermError> {
696 assert!(n >= 1, "a root has a positive index");
697 if n == 1 {
698 return Ok(self.clone());
699 }
700 if let Some(sign) = self.unlimited_sign() {
701 return if sign < 0 && n % 2 == 0 {
702 Err(GermError::NegativeRoot(n))
703 } else {
704 Ok(Germ::unlimited(sign))
705 };
706 }
707 let (v, c) = self.leading()?;
708 if n % 2 == 0 && c.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? == std::cmp::Ordering::Less {
709 return Err(GermError::NegativeRoot(n));
710 }
711 let m = (u64::from(n) / gcd(u64::from(n), v.unsigned_abs())) as u32;
712 let g = self.refined(m);
713 let v = v * i64::from(m);
714 let lead = c.nth_root(n)?;
715 let floor = v / i64::from(n);
716 let q = g.q;
717 Ok(Germ::of(Node::Root { g, n, v, c, lead }, floor, q))
718 }
719
720 pub fn exp(&self) -> Result<Germ, GermError> {
722 if self.unlimited_sign().is_some() || self.standard_part()?.is_none() {
723 return Err(GermError::BeyondEveryPower);
724 }
725 Ok(Germ::of(Node::Exp(self.clone()), 0, self.q))
726 }
727
728 pub fn ln(&self) -> Result<Germ, GermError> {
731 if let Some(sign) = self.unlimited_sign() {
732 return Err(if sign > 0 { GermError::CarriesLog { infinite: true } } else { GermError::Nonpositive });
733 }
734 let (order, lead) = self.leading()?;
735 if lead.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? == std::cmp::Ordering::Less {
736 return Err(GermError::Nonpositive);
737 }
738 match order.cmp(&0) {
739 std::cmp::Ordering::Less => Err(GermError::CarriesLog { infinite: true }),
740 std::cmp::Ordering::Greater => Err(GermError::CarriesLog { infinite: false }),
741 std::cmp::Ordering::Equal => Ok(Germ::of(Node::Ln(self.clone(), lead), 0, self.q)),
742 }
743 }
744
745 pub fn div(&self, o: &Germ) -> Result<Germ, GermError> {
746 Ok(self.mul(&o.recip()?))
747 }
748
749 pub fn signum(&self) -> Result<i32, GermError> {
751 match self.node.as_ref() {
752 Node::Series(h) => return Ok(h.signum()),
753 Node::Unlimited(sign) => return Ok(*sign),
754 _ => {}
755 }
756 let (_, lead) = self.leading()?;
757 Ok(match lead.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? {
758 std::cmp::Ordering::Less => -1,
759 std::cmp::Ordering::Equal => 0,
760 std::cmp::Ordering::Greater => 1,
761 })
762 }
763
764 pub fn standard_part(&self) -> Result<Option<ExactReal>, GermError> {
766 if self.unlimited_sign().is_some() {
767 return Ok(None);
768 }
769 for k in self.floor..0 {
770 let c = self.coefficient(k)?;
771 if !c.is_exactly_zero() && c.compare(&ExactReal::from_i64(0), DEFAULT_BUDGET)? != std::cmp::Ordering::Equal {
772 return Ok(None);
773 }
774 }
775 Ok(Some(self.coefficient(0)?))
776 }
777
778 pub fn render(&self, name: &str) -> String {
781 if let Some(sign) = self.unlimited_sign() {
782 return if sign < 0 { "-unlimited".to_string() } else { "unlimited".to_string() };
783 }
784 let mut terms: Vec<String> = Vec::new();
785 let mut k = self.floor;
786 let mut last_shown = None;
787 while terms.len() < 2 && k < self.floor + self.sign_orders() {
788 match self.coefficient(k) {
789 Ok(c) if c.is_exactly_zero() => {}
790 Ok(c) => {
791 terms.push(expansion_term(name, &c.to_string(), &exponent(k, self.q)));
792 last_shown = Some(k);
793 }
794 Err(_) => break,
795 }
796 k += 1;
797 }
798 let k = last_shown.map_or(k, |order| order + 1);
799 let rest = remainder(name, &exponent(k, self.q));
800 let mut out = String::new();
801 for (i, t) in terms.iter().enumerate() {
802 match (i, t.strip_prefix('-')) {
803 (0, _) => out.push_str(t),
804 (_, Some(rest)) => {
805 out.push_str(" - ");
806 out.push_str(rest);
807 }
808 (_, None) => {
809 out.push_str(" + ");
810 out.push_str(t);
811 }
812 }
813 }
814 if out.is_empty() {
815 rest
816 } else {
817 format!("{out} + {rest}")
818 }
819 }
820}
821
822impl crate::exact_codec::ExactCodec for Germ {
823 fn write(&self, out: &mut Vec<u8>) {
833 use crate::exact_codec::write_len;
834 match self.node.as_ref() {
835 Node::Refine(g, _) => g.write(out),
836 Node::Puiseux(p) => {
837 out.push(10);
838 p.write(out);
839 }
840 Node::Exp(g) => {
841 out.push(11);
842 g.write(out);
843 }
844 Node::Ln(g, _) => {
845 out.push(12);
846 g.write(out);
847 }
848 Node::Root { g, n, .. } => {
849 out.push(13);
850 g.write(out);
851 write_len(*n as usize, out);
852 }
853 Node::Series(h) => {
854 out.push(0);
855 h.write(out);
856 }
857 Node::Constant(x) => {
858 out.push(6);
859 x.write(out);
860 }
861 Node::Standard(x) => {
862 out.push(7);
863 x.write(out);
864 }
865 Node::Unlimited(sign) => {
866 out.push(8);
867 out.push(u8::from(*sign > 0));
868 }
869 Node::Add(a, b) => {
870 out.push(1);
871 a.write(out);
872 b.write(out);
873 }
874 Node::Mul(a, b) => {
875 out.push(2);
876 a.write(out);
877 b.write(out);
878 }
879 Node::Neg(a) => {
880 out.push(3);
881 a.write(out);
882 }
883 Node::Recip(a, _, _) => {
884 out.push(4);
885 a.write(out);
886 }
887 Node::Flow(flow, i) => {
888 if flow.grid == 1 {
889 out.push(5);
890 } else {
891 out.push(14);
892 write_len(flow.grid as usize, out);
893 }
894 let n = flow.width();
895 write_len(n, out);
896 write_len(flow.terms.len(), out);
897 for m in &flow.terms {
898 for row in m {
899 for v in row {
900 v.write(out);
901 }
902 }
903 }
904 flow.t.write(out);
905 flow.delta.write(out);
906 for g in &flow.start {
907 g.write(out);
908 }
909 (*i as u64).write(out);
910 }
911 }
912 }
913
914 fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
915 read_germ(buf, pos, 0)
916 }
917}
918
919fn read_germ(buf: &[u8], pos: &mut usize, depth: usize) -> Option<Germ> {
920 use crate::exact_codec::{read_byte, read_len, ExactCodec, MAX_WIRE_DEGREE, MAX_WIRE_DEPTH};
921 if depth > MAX_WIRE_DEPTH {
922 return None;
923 }
924 Some(match read_byte(buf, pos)? {
925 0 => Germ::from_hyperreal(&Hyperreal::read(buf, pos)?),
926 6 => Germ::constant(&ExactReal::read(buf, pos)?),
927 7 => Germ::standard(&ExactReal::read(buf, pos)?),
928 8 => match read_byte(buf, pos)? {
929 0 => Germ::unlimited(-1),
930 1 => Germ::unlimited(1),
931 _ => return None,
932 },
933 1 => {
934 let a = read_germ(buf, pos, depth + 1)?;
935 a.add(&read_germ(buf, pos, depth + 1)?)
936 }
937 2 => {
938 let a = read_germ(buf, pos, depth + 1)?;
939 a.mul(&read_germ(buf, pos, depth + 1)?)
940 }
941 3 => read_germ(buf, pos, depth + 1)?.neg(),
942 4 => read_germ(buf, pos, depth + 1)?.recip().ok()?,
943 10 => Germ::from_puiseux(&Puiseux::read(buf, pos)?),
944 11 => read_germ(buf, pos, depth + 1)?.exp().ok()?,
945 12 => read_germ(buf, pos, depth + 1)?.ln().ok()?,
946 13 => {
947 let g = read_germ(buf, pos, depth + 1)?;
948 let n = read_len(buf, pos, MAX_WIRE_DEGREE).filter(|n| *n >= 2)?;
949 let n = u32::try_from(n).ok()?;
950 g.root(n).ok()?
951 }
952 tag @ (5 | 14) => {
953 let grid = if tag == 5 {
954 1
955 } else {
956 u32::try_from(read_len(buf, pos, MAX_WIRE_DEGREE).filter(|g| *g >= 2)?).ok()?
957 };
958 let n = read_len(buf, pos, MAX_WIRE_DEGREE)?;
959 let count = read_len(buf, pos, MAX_WIRE_DEGREE)?;
960 let mut terms = Vec::with_capacity(count);
961 for _ in 0..count {
962 let mut m = Vec::with_capacity(n);
963 for _ in 0..n {
964 let mut row = Vec::with_capacity(n);
965 for _ in 0..n {
966 row.push(Rational::read(buf, pos)?);
967 }
968 m.push(row);
969 }
970 terms.push(m);
971 }
972 let t = Rational::read(buf, pos)?;
973 let delta = Rational::read(buf, pos)?;
974 let mut start = Vec::with_capacity(n);
975 for _ in 0..n {
976 start.push(read_germ(buf, pos, depth + 1)?);
977 }
978 let i = u64::read(buf, pos)? as usize;
979 if i >= n {
980 return None;
981 }
982 Germ::flow(&Flow::on_grid(grid, terms, t, delta, start)?, i)
983 }
984 _ => return None,
985 })
986}
987
988#[cfg(test)]
989#[path = "../../logicaffeine_tests/unit/logicaffeine_base/germ/tests.rs"]
990mod tests;