Skip to main content

binius_math/
line.rs

1// Copyright 2024-2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3
4//! Extrapolation of the line through two points.
5
6use binius_field::field::FieldOps;
7
8use crate::multilinear::hypercube::{Hypercube, OneCube};
9
10/// Extrapolates a line through two points.
11///
12/// The two points are `(0, x_0)` and `(1, x_1)`.
13/// The line through them, evaluated at the parameter `z`, is
14///
15/// ```text
16/// x_0 + (x_1 - x_0) * z
17/// ```
18///
19/// The two points are also the halves a variable splits a multilinear into.
20/// Read that way, this binds that variable to `z`.
21#[inline]
22pub fn extrapolate_line<F: FieldOps>(x0: F, x1: F, z: F) -> F {
23	// Binding a variable of the Boolean cube is exactly this line.
24	OneCube::extrapolate_line(x0, x1, z)
25}
26
27#[cfg(test)]
28mod tests {
29	use binius_field::{Field, Random, field::FieldOps};
30	use rand::prelude::*;
31
32	use super::*;
33	use crate::test_utils::{B128, Packed128b};
34
35	type P = Packed128b;
36	type F = B128;
37
38	#[test]
39	fn extrapolate_line_reads_the_endpoints_at_zero_and_one() {
40		let mut rng = StdRng::seed_from_u64(0);
41
42		// The line is pinned by its two endpoints, which is what fixes the argument order:
43		//
44		//     z = 0  ->  x_0
45		//     z = 1  ->  x_1
46		let x0 = F::random(&mut rng);
47		let x1 = F::random(&mut rng);
48		assert_eq!(extrapolate_line(x0, x1, F::ZERO), x0);
49		assert_eq!(extrapolate_line(x0, x1, F::ONE), x1);
50
51		// A packed field is a vector of independent lanes, and the same must hold lane by lane.
52		let x0_packed = P::random(&mut rng);
53		let x1_packed = P::random(&mut rng);
54		assert_eq!(extrapolate_line(x0_packed, x1_packed, P::zero()), x0_packed);
55		assert_eq!(extrapolate_line(x0_packed, x1_packed, P::one()), x1_packed);
56	}
57
58	#[test]
59	fn extrapolate_line_matches_the_closed_form() {
60		let mut rng = StdRng::seed_from_u64(0);
61
62		// Away from the endpoints the value is the closed form of the line through them.
63		// Drawing `z` from a proper subfield also covers the cheaper subfield multiplication.
64		for _ in 0..10 {
65			let x0 = F::random(&mut rng);
66			let x1 = F::random(&mut rng);
67			let z = F::from(rng.next_u64() as u128);
68			assert_eq!(extrapolate_line(x0, x1, z), x0 + (x1 - x0) * z);
69		}
70	}
71}