1use std::collections::BTreeMap;
25use std::sync::{Arc, Mutex};
26
27use crate::expansion::{Coefficient, Expansion, ExpansionError, Source};
28use crate::matrix_exp::exp_matrix;
29use crate::nested::{GermRecipe, NestedGerm};
30use crate::number::Number;
31use crate::numeric::Rational;
32use crate::tower::{self, Op, Scale, TowerValue};
33
34pub(crate) type NMatrix = Vec<Vec<Number>>;
36
37fn text(e: ExpansionError) -> String {
38 e.to_string()
39}
40
41fn zeros(n: usize) -> NMatrix {
42 vec![vec![Number::zero(); n]; n]
43}
44
45fn identity(n: usize) -> NMatrix {
46 (0..n)
47 .map(|i| (0..n).map(|j| if i == j { Number::one() } else { Number::zero() }).collect())
48 .collect()
49}
50
51fn mat_mul(a: &NMatrix, b: &NMatrix) -> Result<NMatrix, String> {
52 let n = a.len();
53 let mut out = zeros(n);
54 for (i, row) in a.iter().enumerate() {
55 for (k, x) in row.iter().enumerate() {
56 if x.is_zero().map_err(text)? {
57 continue;
58 }
59 for j in 0..n {
60 out[i][j] = out[i][j].add(&x.mul(&b[k][j]).map_err(text)?).map_err(text)?;
61 }
62 }
63 }
64 Ok(out)
65}
66
67fn mat_add(a: &NMatrix, b: &NMatrix) -> Result<NMatrix, String> {
68 a.iter()
69 .zip(b)
70 .map(|(x, y)| x.iter().zip(y).map(|(p, q)| p.add(q).map_err(text)).collect())
71 .collect()
72}
73
74fn mat_scale(a: &NMatrix, k: &Number) -> Result<NMatrix, String> {
75 a.iter()
76 .map(|row| row.iter().map(|v| v.mul(k).map_err(text)).collect())
77 .collect()
78}
79
80fn is_zero_matrix(a: &NMatrix) -> Result<bool, String> {
81 for row in a {
82 for v in row {
83 if !v.is_zero().map_err(text)? {
84 return Ok(false);
85 }
86 }
87 }
88 Ok(true)
89}
90
91fn rational(r: Rational) -> Number {
92 <Number as TowerValue>::from_rational(r)
93}
94
95fn mat_vec(a: &NMatrix, x: &[Number]) -> Result<Vec<Number>, String> {
96 a.iter()
97 .map(|row| {
98 row.iter().zip(x).try_fold(Number::zero(), |acc, (e, v)| {
99 acc.add(&e.mul(v).map_err(text)?).map_err(text)
100 })
101 })
102 .collect()
103}
104
105fn lcm(a: u32, b: u32) -> u32 {
106 fn gcd(a: u64, b: u64) -> u64 {
107 if b == 0 {
108 a
109 } else {
110 gcd(b, a % b)
111 }
112 }
113 let g = gcd(u64::from(a), u64::from(b));
114 u32::try_from(u64::from(a) / g * u64::from(b)).expect("a grid of moderate index")
115}
116
117fn smallest_among<'a>(numbers: impl Iterator<Item = &'a Number>) -> Option<Arc<Scale>> {
119 let mut smallest: Option<Arc<Scale>> = None;
120 for n in numbers {
121 for s in tower::number_scales(n) {
122 smallest = Some(match smallest {
123 None => s,
124 Some(t) if s.order(&t) == Some(std::cmp::Ordering::Less) => s,
125 Some(t) => t,
126 });
127 }
128 }
129 smallest
130}
131
132fn expansions(a: &NMatrix, scale: &Arc<Scale>) -> Result<(Vec<Vec<Expansion<Number>>>, u32), String> {
134 let mut grid = 1;
135 let mut out = Vec::with_capacity(a.len());
136 for row in a {
137 let mut r = Vec::with_capacity(row.len());
138 for v in row {
139 let e = tower::number_germ(v, scale)?.expansion.clone();
140 grid = lcm(grid, e.ramification());
141 r.push(e);
142 }
143 out.push(r);
144 }
145 let out = out
146 .into_iter()
147 .map(|row| row.into_iter().map(|e| e.refined(grid / e.ramification())).collect())
148 .collect();
149 Ok((out, grid))
150}
151
152fn term(e: &[Vec<Expansion<Number>>], k: i64) -> Result<NMatrix, String> {
154 e.iter()
155 .map(|row| row.iter().map(|x| x.coefficient(k).map_err(text)).collect())
156 .collect()
157}
158
159fn log_term(terms: &dyn Fn(usize) -> Result<NMatrix, String>, n: usize, k: usize) -> Result<NMatrix, String> {
161 let mut total = zeros(n);
162 let mut power: Vec<NMatrix> = (0..=k).map(|j| if j == 0 { identity(n) } else { zeros(n) }).collect();
163 for count in 1..=k {
164 let mut next: Vec<NMatrix> = vec![zeros(n); k + 1];
165 for (have, p) in power.iter().enumerate() {
166 if is_zero_matrix(p)? {
167 continue;
168 }
169 for step in 1..=(k - have) {
170 next[have + step] = mat_add(&next[have + step], &mat_mul(p, &terms(step)?)?)?;
171 }
172 }
173 power = next;
174 let sign = if count % 2 == 1 { 1 } else { -1 };
175 let c = rational(Rational::from_ratio_i64(sign, count as i64).expect("count ≥ 1"));
176 total = mat_add(&total, &mat_scale(&power[k], &c)?)?;
177 }
178 Ok(total)
179}
180
181#[derive(Debug)]
185pub(crate) struct MatrixExpSpec {
186 pub(crate) a: NMatrix,
187 pub(crate) t: Rational,
188 scale: Arc<Scale>,
189 entries: Vec<Vec<Expansion<Number>>>,
190 found: Mutex<Vec<NMatrix>>,
192}
193
194impl MatrixExpSpec {
195 fn coefficient(&self, m: usize) -> Result<NMatrix, String> {
198 if let Some(found) = self.found.lock().expect("unpoisoned").get(m) {
199 return Ok(found.clone());
200 }
201 let n = self.a.len();
202 let blocks: Vec<NMatrix> = (0..=m).map(|j| term(&self.entries, j as i64)).collect::<Result<_, _>>()?;
203 let size = (m + 1) * n;
204 let mut big = vec![vec![Number::zero(); size]; size];
205 for bi in 0..=m {
206 for bj in bi..=m {
207 for i in 0..n {
208 for j in 0..n {
209 big[bi * n + i][bj * n + j] = blocks[bj - bi][i][j].clone();
210 }
211 }
212 }
213 }
214 let e = matrix_exp(&big, &self.t)?;
215 let mut found = self.found.lock().expect("unpoisoned");
216 while found.len() <= m {
217 let k = found.len();
218 found.push((0..n).map(|i| (0..n).map(|j| e[i][k * n + j].clone()).collect()).collect());
219 }
220 Ok(found[m].clone())
221 }
222}
223
224#[derive(Debug)]
225struct MatrixExpEntry(Arc<MatrixExpSpec>, usize, usize);
226
227impl Source<Number> for MatrixExpEntry {
228 fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
229 if k < 0 {
230 return Ok(Number::zero());
231 }
232 self.0
233 .coefficient(k as usize)
234 .map(|m| m[self.1][self.2].clone())
235 .map_err(ExpansionError::Coefficient)
236 }
237}
238
239pub(crate) fn matrix_exp(a: &NMatrix, t: &Rational) -> Result<NMatrix, String> {
242 let n = a.len();
243 let rationals: Option<Vec<Vec<Rational>>> = a
244 .iter()
245 .map(|row| row.iter().map(|x| x.standard_rational()).collect())
246 .collect();
247 if let Some(r) = rationals {
248 let e = exp_matrix(&r, t).ok_or_else(|| "the exponential of a matrix that is not square".to_string())?;
249 return Ok(e.into_iter().map(|row| row.into_iter().map(tower::from_exact_real).collect()).collect());
250 }
251 if n == 1 {
252 let ta = Number::arith(Op::Mul, rational(t.clone()), a[0][0].clone())?;
253 return Ok(vec![vec![ta.exp().map_err(text)?]]);
254 }
255 let Some(scale) = smallest_among(a.iter().flatten()) else {
256 return Err("the exponential of a matrix of irrational standard numbers is not computed".to_string());
257 };
258 let (entries, grid) = expansions(a, &scale)?;
259 for row in &entries {
260 for e in row {
261 for k in e.floor()..0 {
262 if !e.coefficient(k).map_err(text)?.is_zero().map_err(text)? {
263 return Err(format!(
264 "the exponential of a matrix with an entry infinite in {} is beyond every power of it",
265 scale.name
266 ));
267 }
268 }
269 }
270 }
271 let spec = Arc::new(MatrixExpSpec {
272 a: a.clone(),
273 t: t.clone(),
274 scale: scale.clone(),
275 entries,
276 found: Mutex::new(Vec::new()),
277 });
278 Ok((0..n)
279 .map(|i| {
280 (0..n)
281 .map(|j| {
282 let expansion = Expansion::source(Arc::new(MatrixExpEntry(spec.clone(), i, j)), 0, grid);
283 Number::hyper(tower::HyperValue {
284 value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
285 &spec.scale,
286 GermRecipe::MatrixExp(spec.clone(), i, j),
287 expansion,
288 ))),
289 scale: spec.scale.clone(),
290 })
291 })
292 .collect()
293 })
294 .collect())
295}
296
297#[derive(Debug)]
302pub(crate) struct FlowSpec {
303 pub(crate) a: NMatrix,
304 pub(crate) t: Rational,
305 pub(crate) delta: Rational,
306 pub(crate) start: Vec<Number>,
307 scale: Arc<Scale>,
308 entries: Vec<Vec<Expansion<Number>>>,
309 grid: u32,
311 starts: Vec<Expansion<Number>>,
312 q: u32,
314 floor: i64,
315 exp_t: NMatrix,
316 exponent: Mutex<Vec<NMatrix>>,
317 expansion: Mutex<Vec<NMatrix>>,
318 state: Mutex<BTreeMap<i64, Vec<Number>>>,
319}
320
321impl FlowSpec {
322 fn width(&self) -> usize {
323 self.a.len()
324 }
325
326 fn term(&self, k: usize) -> Result<NMatrix, String> {
327 term(&self.entries, k as i64)
328 }
329
330 fn exponent(&self, j: usize) -> Result<NMatrix, String> {
332 let mut cache = self.exponent.lock().expect("unpoisoned");
333 while cache.len() < j {
334 let i = cache.len() + 1;
335 let n = self.width();
336 let terms = |k: usize| self.term(k);
337 let c = mat_add(
338 &mat_scale(&log_term(&terms, n, i + self.grid as usize)?, &rational(self.t.clone()))?,
339 &mat_scale(&log_term(&terms, n, i)?, &rational(self.delta.clone()))?,
340 )?;
341 cache.push(c);
342 }
343 Ok(cache[j - 1].clone())
344 }
345
346 fn commuting_through(&self, reach: usize) -> Result<bool, String> {
349 let cs: Vec<NMatrix> = (1..=reach).map(|j| self.exponent(j)).collect::<Result<_, _>>()?;
350 for (i, a) in cs.iter().enumerate() {
351 for b in &cs[i + 1..] {
352 let (ab, ba) = (mat_mul(a, b)?, mat_mul(b, a)?);
353 let difference: NMatrix = ab
354 .iter()
355 .zip(&ba)
356 .map(|(x, y)| x.iter().zip(y).map(|(p, q)| p.sub(q).map_err(text)).collect())
357 .collect::<Result<_, String>>()?;
358 if !is_zero_matrix(&difference)? {
359 return Ok(false);
360 }
361 }
362 }
363 Ok(true)
364 }
365
366 fn expansion(&self, m: usize) -> Result<NMatrix, String> {
367 let mut cache = self.expansion.lock().expect("unpoisoned");
368 if cache.is_empty() {
369 cache.push(identity(self.width()));
370 }
371 while cache.len() <= m {
372 let mm = cache.len();
373 let mut total = zeros(self.width());
374 for k in 1..=mm {
375 let c = self.exponent(k)?;
376 if is_zero_matrix(&c)? {
377 continue;
378 }
379 let term = mat_mul(&c, &cache[mm - k])?;
380 total = mat_add(&total, &mat_scale(&term, &rational(Rational::from_i64(k as i64)))?)?;
381 }
382 cache.push(mat_scale(&total, &rational(Rational::from_ratio_i64(1, mm as i64).expect("mm ≥ 1")))?);
383 }
384 Ok(cache[m].clone())
385 }
386
387 fn state(&self, m: i64) -> Result<Vec<Number>, String> {
389 if let Some(s) = self.state.lock().expect("unpoisoned").get(&m) {
390 return Ok(s.clone());
391 }
392 let r = i64::from(self.q / self.grid);
393 let reach = ((m - self.floor).max(0) / r) as usize;
394 let nonzero_exponent = (1..=reach)
395 .map(|j| self.exponent(j).and_then(|c| is_zero_matrix(&c).map(|z| !z)))
396 .collect::<Result<Vec<bool>, String>>()?;
397 if nonzero_exponent.iter().any(|b| *b) && !self.commuting_through(reach)? {
398 return Err(format!(
399 "the {} term of this flow is not computed exactly: its update matrices do not commute",
400 crate::puiseux::power_text(&self.scale.name, &Rational::from_ratio_i64(m, i64::from(self.q)).expect("q > 0"))
401 ));
402 }
403 let n = self.width();
404 let mut inner = vec![Number::zero(); n];
405 for a in 0..=reach {
406 let e = self.expansion(a)?;
407 if is_zero_matrix(&e)? {
408 continue;
409 }
410 let start: Vec<Number> = self
411 .starts
412 .iter()
413 .map(|g| g.coefficient(m - a as i64 * r).map_err(text))
414 .collect::<Result<_, _>>()?;
415 let moved = mat_vec(&e, &start)?;
416 inner = inner
417 .iter()
418 .zip(&moved)
419 .map(|(x, y)| x.add(y).map_err(text))
420 .collect::<Result<_, _>>()?;
421 }
422 let s = mat_vec(&self.exp_t, &inner)?;
423 self.state.lock().expect("unpoisoned").insert(m, s.clone());
424 Ok(s)
425 }
426}
427
428#[derive(Debug)]
429struct FlowComponent(Arc<FlowSpec>, usize);
430
431impl Source<Number> for FlowComponent {
432 fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
433 self.0.state(k).map(|s| s[self.1].clone()).map_err(ExpansionError::Coefficient)
434 }
435}
436
437pub(crate) fn flow(scale: &Arc<Scale>, a: &NMatrix, t: Rational, delta: Rational, start: &[Number]) -> Result<Vec<Number>, String> {
440 let n = a.len();
441 let (entries, grid) = expansions(a, scale)?;
442 let at_zero = term(&entries, 0)?;
443 let difference: NMatrix = at_zero
444 .iter()
445 .zip(identity(n))
446 .map(|(x, y)| x.iter().zip(&y).map(|(p, q)| p.sub(q).map_err(text)).collect())
447 .collect::<Result<_, String>>()?;
448 if !is_zero_matrix(&difference)? {
449 return Err(format!(
450 "the update changes its state at every step by an amount that is no infinitesimal of {}, so this supertask is no flow of it",
451 scale.name
452 ));
453 }
454 for k in 1..grid as i64 {
455 if !is_zero_matrix(&term(&entries, k)?)? {
456 return Err(format!(
457 "the update has a term of a fractional order of {} below its first, so this supertask is not a flow",
458 scale.name
459 ));
460 }
461 }
462 let starts: Vec<Expansion<Number>> = start
463 .iter()
464 .map(|x| tower::number_germ(x, scale).map(|g| g.expansion.clone()))
465 .collect::<Result<_, _>>()?;
466 let q = starts.iter().fold(grid, |l, e| lcm(l, e.ramification()));
467 let starts: Vec<Expansion<Number>> = starts.into_iter().map(|e| e.refined(q / e.ramification())).collect();
468 let floor = starts.iter().map(Expansion::floor).min().unwrap_or(0);
469 let exp_t = matrix_exp(&term(&entries, i64::from(grid))?, &t)?;
470 let spec = Arc::new(FlowSpec {
471 a: a.clone(),
472 t,
473 delta,
474 start: start.to_vec(),
475 scale: scale.clone(),
476 entries,
477 grid,
478 starts,
479 q,
480 floor,
481 exp_t,
482 exponent: Mutex::new(Vec::new()),
483 expansion: Mutex::new(Vec::new()),
484 state: Mutex::new(BTreeMap::new()),
485 });
486 Ok((0..n)
487 .map(|i| {
488 let expansion = Expansion::source(Arc::new(FlowComponent(spec.clone(), i)), floor, q);
489 Number::hyper(tower::HyperValue {
490 value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
491 scale,
492 GermRecipe::Flow(spec.clone(), i),
493 expansion,
494 ))),
495 scale: scale.clone(),
496 })
497 })
498 .collect())
499}
500
501#[derive(Debug)]
506pub(crate) struct PowerSpec {
507 pub(crate) a: NMatrix,
508 pub(crate) count: Number,
509 pub(crate) start: Vec<Number>,
510 entries: Vec<Vec<Expansion<Number>>>,
511 starts: Vec<Expansion<Number>>,
512 logs: Mutex<Vec<Vec<NMatrix>>>,
514 found: Mutex<BTreeMap<i64, Vec<Number>>>,
515}
516
517impl PowerSpec {
518 fn width(&self) -> usize {
519 self.a.len()
520 }
521
522 fn log_powers(&self, m: usize) -> Result<Vec<Vec<NMatrix>>, String> {
524 let n = self.width();
525 let mut logs = self.logs.lock().expect("unpoisoned");
526 if logs.first().is_none_or(|row| row.len() <= m) {
527 let terms = |k: usize| term(&self.entries, k as i64);
528 let l: Vec<NMatrix> = (0..=m)
529 .map(|k| if k == 0 { Ok(zeros(n)) } else { log_term(&terms, n, k) })
530 .collect::<Result<_, _>>()?;
531 let mut powers: Vec<Vec<NMatrix>> = vec![(0..=m).map(|k| if k == 0 { identity(n) } else { zeros(n) }).collect()];
532 for j in 1..=m {
533 let previous = &powers[j - 1];
534 let mut next = vec![zeros(n); m + 1];
535 for (a, p) in previous.iter().enumerate() {
536 if is_zero_matrix(p)? {
537 continue;
538 }
539 for (b, lb) in l.iter().enumerate().skip(1) {
540 if a + b > m {
541 break;
542 }
543 next[a + b] = mat_add(&next[a + b], &mat_mul(p, lb)?)?;
544 }
545 }
546 powers.push(next);
547 }
548 *logs = powers;
549 }
550 Ok(logs.clone())
551 }
552
553 fn power(&self, m: usize) -> Result<NMatrix, String> {
555 let logs = self.log_powers(m)?;
556 let n = self.width();
557 let mut total = zeros(n);
558 let mut weight = Number::one();
559 for (j, by_order) in logs.iter().enumerate().take(m + 1) {
560 if j > 0 {
561 weight = weight
562 .mul(&self.count)
563 .map_err(text)?
564 .mul(&rational(Rational::from_ratio_i64(1, j as i64).expect("j ≥ 1")))
565 .map_err(text)?;
566 }
567 total = mat_add(&total, &mat_scale(&by_order[m], &weight)?)?;
568 }
569 Ok(total)
570 }
571
572 fn state(&self, m: i64) -> Result<Vec<Number>, String> {
574 if let Some(s) = self.found.lock().expect("unpoisoned").get(&m) {
575 return Ok(s.clone());
576 }
577 let floor = self.starts.iter().map(Expansion::floor).min().unwrap_or(0);
578 let reach = (m - floor).max(0) as usize;
579 let n = self.width();
580 let mut out = vec![Number::zero(); n];
581 for a in 0..=reach {
582 let p = self.power(a)?;
583 if is_zero_matrix(&p)? {
584 continue;
585 }
586 let start: Vec<Number> = self
587 .starts
588 .iter()
589 .map(|g| g.coefficient(m - a as i64).map_err(text))
590 .collect::<Result<_, _>>()?;
591 let moved = mat_vec(&p, &start)?;
592 out = out.iter().zip(&moved).map(|(x, y)| x.add(y).map_err(text)).collect::<Result<_, _>>()?;
593 }
594 self.found.lock().expect("unpoisoned").insert(m, out.clone());
595 Ok(out)
596 }
597}
598
599fn logarithmic_power(
606 scale: &Arc<Scale>,
607 a: &NMatrix,
608 count: Number,
609 counted: Expansion<Number>,
610 start: &[Number],
611) -> Result<Vec<Number>, String> {
612 let n = a.len();
613 let (entries, grid) = expansions(a, scale)?;
614 if !is_zero_matrix(&mat_add(&term(&entries, 0)?, &mat_scale(&identity(n), &rational(Rational::from_i64(-1)))?)?)? {
615 return Err(format!(
616 "the update holds {} and changes its state at every step by more than a value of it, so its power is not computed",
617 scale.name
618 ));
619 }
620 let starts: Vec<Expansion<Number>> = start
621 .iter()
622 .map(|x| tower::number_germ(x, scale).map(|g| g.expansion.clone()))
623 .collect::<Result<_, _>>()?;
624 let q = starts.iter().fold(lcm(grid, counted.ramification()), |l, e| lcm(l, e.ramification()));
625 let entries: Vec<Vec<Expansion<Number>>> = entries
626 .into_iter()
627 .map(|row| row.into_iter().map(|e| e.refined(q / grid)).collect())
628 .collect();
629 let counted = counted.refined(q / counted.ramification());
630 let logs = Arc::new(LogSpec {
631 entries: entries.clone(),
632 width: n,
633 found: Mutex::new(Vec::new()),
634 });
635 let scaled: Vec<Vec<Expansion<Number>>> = (0..n)
636 .map(|i| {
637 (0..n)
638 .map(|j| counted.mul(&Expansion::source(Arc::new(LogEntry(logs.clone(), i, j)), 1, q)))
639 .collect()
640 })
641 .collect();
642 for row in &scaled {
643 for e in row {
644 for k in e.floor()..0 {
645 if !e.coefficient(k).map_err(text)?.is_zero().map_err(text)? {
646 return Err(format!(
647 "this supertask's step count {} outgrows its steps: the change they add up to is infinite in {}",
648 count.display_string(),
649 scale.name
650 ));
651 }
652 }
653 }
654 }
655 let exponential = Arc::new(MatrixExpSpec {
656 a: a.clone(),
657 t: Rational::one(),
658 scale: scale.clone(),
659 entries: scaled,
660 found: Mutex::new(Vec::new()),
661 });
662 let starts: Vec<Expansion<Number>> = starts.into_iter().map(|e| e.refined(q / e.ramification())).collect();
663 let spec = Arc::new(PowerSpec {
664 a: a.clone(),
665 count,
666 start: start.to_vec(),
667 entries,
668 starts: starts.clone(),
669 logs: Mutex::new(Vec::new()),
670 found: Mutex::new(BTreeMap::new()),
671 });
672 Ok((0..n)
673 .map(|i| {
674 let expansion = (0..n).fold(Expansion::constant(Number::zero()), |sum, j| {
675 let entry = Expansion::source(Arc::new(MatrixExpEntry(exponential.clone(), i, j)), 0, q);
676 sum.add(&entry.mul(&starts[j]))
677 });
678 Number::hyper(tower::HyperValue {
679 value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
680 scale,
681 GermRecipe::Power(spec.clone(), i),
682 expansion,
683 ))),
684 scale: scale.clone(),
685 })
686 })
687 .collect())
688}
689
690#[derive(Debug)]
692struct LogSpec {
693 entries: Vec<Vec<Expansion<Number>>>,
694 width: usize,
695 found: Mutex<Vec<NMatrix>>,
697}
698
699impl LogSpec {
700 fn order(&self, k: usize) -> Result<NMatrix, String> {
701 let mut found = self.found.lock().expect("unpoisoned");
702 while found.len() <= k {
703 let at = found.len();
704 let next = if at == 0 {
705 zeros(self.width)
706 } else {
707 log_term(&|step| term(&self.entries, step as i64), self.width, at)?
708 };
709 found.push(next);
710 }
711 Ok(found[k].clone())
712 }
713}
714
715#[derive(Debug)]
716struct LogEntry(Arc<LogSpec>, usize, usize);
717
718impl Source<Number> for LogEntry {
719 fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
720 if k < 1 {
721 return Ok(Number::zero());
722 }
723 self.0.order(k as usize).map(|m| m[self.1][self.2].clone()).map_err(ExpansionError::Coefficient)
724 }
725}
726
727#[derive(Debug)]
728struct PowerComponent(Arc<PowerSpec>, usize);
729
730impl Source<Number> for PowerComponent {
731 fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
732 self.0.state(k).map(|s| s[self.1].clone()).map_err(ExpansionError::Coefficient)
733 }
734}
735
736pub(crate) fn power(scale: &Arc<Scale>, a: &NMatrix, count: Number, start: &[Number]) -> Result<Vec<Number>, String> {
740 let counted = tower::number_germ(&count, scale)?.expansion.clone();
741 if counted.floor() < 0 {
742 return logarithmic_power(scale, a, count, counted, start);
743 }
744 let n = a.len();
745 let (entries, grid) = expansions(a, scale)?;
746 let at_zero = term(&entries, 0)?;
747 let difference: NMatrix = at_zero
748 .iter()
749 .zip(identity(n))
750 .map(|(x, y)| x.iter().zip(&y).map(|(p, q)| p.sub(q).map_err(text)).collect())
751 .collect::<Result<_, String>>()?;
752 if !is_zero_matrix(&difference)? {
753 if n == 1 {
754 let g = tower::number_germ(&a[0][0], scale)?;
755 let logarithm = Arc::new(NestedGerm::ln(&g, &a[0][0].display_string())?);
756 let times = Arc::new(NestedGerm::mul(&logarithm, &tower::number_germ(&count, scale)?));
757 let grown = Arc::new(NestedGerm::exp(×, ×.render())?);
758 let end = NestedGerm::mul(&grown, &tower::number_germ(&start[0], scale)?);
759 return Ok(vec![Number::hyper(tower::HyperValue {
760 value: tower::HyperForm::Nested(Arc::new(end)),
761 scale: scale.clone(),
762 })]);
763 }
764 return Err(format!(
765 "the update holds {} and changes its state at every step by more than a value of it, so its power is not computed",
766 scale.name
767 ));
768 }
769 let starts: Vec<Expansion<Number>> = start
770 .iter()
771 .map(|x| tower::number_germ(x, scale).map(|g| g.expansion.clone()))
772 .collect::<Result<_, _>>()?;
773 let q = starts.iter().fold(grid, |l, e| lcm(l, e.ramification()));
774 let entries: Vec<Vec<Expansion<Number>>> = entries
775 .into_iter()
776 .map(|row| row.into_iter().map(|e| e.refined(q / grid)).collect())
777 .collect();
778 let starts: Vec<Expansion<Number>> = starts.into_iter().map(|e| e.refined(q / e.ramification())).collect();
779 let floor = starts.iter().map(Expansion::floor).min().unwrap_or(0);
780 let spec = Arc::new(PowerSpec {
781 a: a.clone(),
782 count,
783 start: start.to_vec(),
784 entries,
785 starts,
786 logs: Mutex::new(Vec::new()),
787 found: Mutex::new(BTreeMap::new()),
788 });
789 Ok((0..n)
790 .map(|i| {
791 let expansion = Expansion::source(Arc::new(PowerComponent(spec.clone(), i)), floor, q);
792 Number::hyper(tower::HyperValue {
793 value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
794 scale,
795 GermRecipe::Power(spec.clone(), i),
796 expansion,
797 ))),
798 scale: scale.clone(),
799 })
800 })
801 .collect())
802}