1use crate::affine::exact_affine_read;
4use crate::closed_form::{Body, Edge, Fold, Part, map_children};
5use crate::complex::C64;
6use crate::through::Reads;
7
8pub fn split(f: &Body, reads: &dyn Reads) -> Option<Body> {
9 let (kink, (at, below, above)) = first(f, reads)?;
10 let half = |with: &Body, l: Edge, r: Edge| {
11 let scope = reads.scope();
12 Part::bare(Body::Crop {
13 of: Part::bare(replaced(f, (scope, &kink, with), reads)),
14 l,
15 r,
16 rise: 0.0,
17 fall: 0.0,
18 })
19 };
20 Some(Body::Add(vec![
21 half(&below, Edge::NegInf, Edge::at(at)),
22 half(&above, Edge::at(at), Edge::PosInf),
23 ]))
24}
25
26#[derive(Clone, Copy, Debug)]
29pub enum Line {
30 Flat(f64),
31 Sloped { slope: f64, root: f64 },
32}
33
34impl Line {
35 fn slope(self) -> f64 {
36 match self {
37 Line::Flat(_) => 0.0,
38 Line::Sloped { slope, .. } => slope,
39 }
40 }
41
42 fn scaled(self, k: f64, divide: bool) -> Line {
43 let by = |v: f64| if divide { v / k } else { v * k };
44 match self {
45 Line::Flat(v) => Line::Flat(by(v)),
46 Line::Sloped { slope, root } => Line::Sloped {
47 slope: by(slope),
48 root,
49 },
50 }
51 }
52
53 fn plus(self, other: Line) -> Line {
54 match (self, other) {
55 (Line::Flat(a), Line::Flat(b)) => Line::Flat(a + b),
56 (Line::Flat(c), Line::Sloped { slope, root })
57 | (Line::Sloped { slope, root }, Line::Flat(c)) => match c == 0.0 {
58 true => Line::Sloped { slope, root },
59 false => Line::Sloped {
60 slope,
61 root: root - c / slope,
62 },
63 },
64 (Line::Sloped { slope: s, root: r }, Line::Sloped { slope: u, root: q }) => {
65 match (s + u, r == q) {
66 (0.0, _) => Line::Flat(-(s * r + u * q)),
67 (sum, true) => Line::Sloped {
68 slope: sum,
69 root: r,
70 },
71 (sum, false) => Line::Sloped {
72 slope: sum,
73 root: (s * r + u * q) / sum,
74 },
75 }
76 }
77 }
78 }
79}
80
81pub(crate) fn line(f: &Body, reads: &dyn Reads) -> Option<Line> {
82 if let Body::Node(id) = f {
83 return reads.line(*id);
84 }
85 let line = |g: &Body| line(g, reads);
86 let real = |c: C64| c.is_real().then_some(c.re);
87 let read = match f {
88 Body::Const(c) => Some(Line::Flat(real(*c)?)),
89 Body::Line => Some(Line::Sloped {
90 slope: 1.0,
91 root: 0.0,
92 }),
93 Body::Shift { by, of } => match line(&of.body)? {
94 Line::Sloped { slope, root } => Some(Line::Sloped {
95 slope,
96 root: root + by,
97 }),
98 flat => Some(flat),
99 },
100 Body::Add(parts) => parts
101 .iter()
102 .try_fold(Line::Flat(0.0), |acc, p| Some(acc.plus(line(&p.body)?))),
103 Body::Mul(parts) => {
104 let mut sloped: Option<Line> = None;
105 let mut k = 1.0;
106 for p in parts {
107 match line(&p.body)? {
108 Line::Flat(v) => k *= v,
109 held if sloped.is_none() => sloped = Some(held),
110 _ => return None,
111 }
112 }
113 Some(sloped.map_or(Line::Flat(k), |l| l.scaled(k, false)))
114 }
115 Body::Div(num, den) => match line(&den.body)? {
116 Line::Flat(k) if k != 0.0 => Some(line(&num.body)?.scaled(k, true)),
117 _ => None,
118 },
119 _ => None,
120 };
121 let read = read.or_else(|| {
122 let (a, b) = exact_affine_read(f, reads)?;
123 match (real(a)?, real(b)?) {
124 (0.0, b) => Some(Line::Flat(b)),
125 (a, b) => Some(Line::Sloped {
126 slope: a,
127 root: -b / a,
128 }),
129 }
130 })?;
131 match read {
132 Line::Sloped { slope: 0.0, .. } => Some(Line::Flat(0.0)),
133 held => Some(held),
134 }
135 .filter(|l| match *l {
136 Line::Flat(v) => v.is_finite(),
137 Line::Sloped { slope, root } => slope.is_finite() && root.is_finite(),
138 })
139}
140
141fn crossing(f: &Body, reads: &dyn Reads) -> Option<Crossing> {
145 let Body::Fold(op @ (Fold::Min | Fold::Max), args) = f else {
146 return None;
147 };
148 let [p, q] = args.as_slice() else {
149 return None;
150 };
151 let (l1, l2) = (line(&p.body, reads)?, line(&q.body, reads)?);
152 if l1.slope() == l2.slope() {
153 return None;
154 }
155 let max = *op == Fold::Max;
156 match (l1, l2) {
157 (Line::Flat(c), sloped @ Line::Sloped { slope, root })
158 | (sloped @ Line::Sloped { slope, root }, Line::Flat(c)) => {
159 let taken = |t: f64| {
160 let v = slope * (t - root);
161 if max { v >= c } else { v <= c }
162 };
163 let flat = Body::Const(C64::real(c));
164 let estimate = root + c / slope;
165 match (slope > 0.0) == max {
166 true => Some((first_double(taken, estimate)?, flat, written(sloped))),
167 false => Some((
168 first_double(|t| !taken(t), estimate)?,
169 written(sloped),
170 flat,
171 )),
172 }
173 }
174 (Line::Sloped { slope: s, root: r }, Line::Sloped { slope: u, root: v }) => {
175 let at = match r == v {
176 true => r,
177 false => (s * r - u * v) / (s - u),
178 };
179 let p_below = (s > u) != max;
180 let (below, above) = if p_below { (l1, l2) } else { (l2, l1) };
181 at.is_finite().then(|| (at, written(below), written(above)))
182 }
183 (Line::Flat(_), Line::Flat(_)) => None,
184 }
185}
186
187fn written(l: Line) -> Body {
188 match l {
189 Line::Flat(c) => Body::Const(C64::real(c)),
190 Line::Sloped { slope, root } => Body::Mul(vec![
191 Part::bare(Body::Const(C64::real(slope))),
192 Part::bare(Body::Shift {
193 by: root,
194 of: Part::bare(Body::Line),
195 }),
196 ]),
197 }
198}
199
200fn ordered(x: i64) -> i64 {
202 x ^ (((x >> 63) as u64) >> 1) as i64
203}
204
205fn key(x: f64) -> i64 {
206 ordered(x.to_bits() as i64)
207}
208
209fn double(k: i64) -> f64 {
210 f64::from_bits(ordered(k) as u64)
211}
212
213fn first_double(holds: impl Fn(f64) -> bool, near: f64) -> Option<f64> {
216 let finite = |k: i64| double(k).is_finite().then_some(k);
217 let near = finite(key(near))?;
218 let mut step = 1i64;
219 let (mut no, mut yes) = (near, near);
220 match holds(double(near)) {
221 true => loop {
222 no = finite(yes.checked_sub(step)?)?;
223 if !holds(double(no)) {
224 break;
225 }
226 yes = no;
227 step = step.checked_mul(2)?;
228 },
229 false => loop {
230 yes = finite(no.checked_add(step)?)?;
231 if holds(double(yes)) {
232 break;
233 }
234 no = yes;
235 step = step.checked_mul(2)?;
236 },
237 }
238 while yes - no > 1 {
239 let mid = no + (yes - no) / 2;
240 match holds(double(mid)) {
241 true => yes = mid,
242 false => no = mid,
243 }
244 }
245 Some(double(yes))
246}
247
248pub(crate) fn in_time(f: &Body) -> bool {
250 matches!(
251 f,
252 Body::Add(_)
253 | Body::Mul(_)
254 | Body::Div(..)
255 | Body::Pow(..)
256 | Body::Apply(..)
257 | Body::Fold(..)
258 | Body::Crop { .. }
259 | Body::Join(_)
260 | Body::Channel(..)
261 )
262}
263
264pub type Crossing = (f64, Body, Body);
265
266pub(crate) fn first(f: &Body, reads: &dyn Reads) -> Option<(Body, Crossing)> {
268 if let Body::Node(id) = f {
269 return reads.kink(*id);
270 }
271 if let Some(found) = crossing(f, reads) {
272 return Some((f.clone(), found));
273 }
274 if !in_time(f) {
275 return None;
276 }
277 crate::closed_form::children(f)
278 .into_iter()
279 .find_map(|p| first(&p.body, reads))
280}
281
282type Replacing<'a> = (usize, &'a Body, &'a Body);
284
285fn replaced(f: &Body, at: Replacing, reads: &dyn Reads) -> Body {
286 let (scope, kink, with) = at;
287 if f == kink {
288 return with.clone();
289 }
290 if let Body::Node(id) = f {
291 return reads
292 .replaced(*id, scope, kink, with)
293 .map_or_else(|| f.clone(), Body::Node);
294 }
295 if !in_time(f) {
296 return f.clone();
297 }
298 map_children(f, |p| replaced_part(p, at, reads))
299}
300
301pub(crate) fn replaced_part(p: &Part, at: Replacing, reads: &dyn Reads) -> Part {
302 Part::new(p.origin, replaced(&p.body, at, reads))
303}