1use alloc::vec::Vec;
4
5use p3_binary_field::TowerLevel;
6
7#[must_use]
15pub fn domain_point<F: TowerLevel>(index: usize) -> F {
16 let mut point = F::ZERO;
17 let mut remaining = index;
18 let mut r = 0;
19 while remaining != 0 {
20 if remaining & 1 == 1 {
21 point += F::cantor_basis(r);
22 }
23 remaining >>= 1;
24 r += 1;
25 }
26 point
27}
28
29#[must_use]
39pub fn domain_point_steps<F: TowerLevel>(count: usize) -> Vec<F> {
40 let mut point = F::ZERO;
41 (0..count)
42 .map(|r| {
43 point += F::cantor_basis(r + 1);
44 point
45 })
46 .collect()
47}
48
49#[must_use]
55pub fn subspace_polynomial<F: TowerLevel>(j: usize, x: F) -> F {
56 let mut value = x;
57 for _ in 0..j {
58 value = value.square() + value;
59 }
60 value
61}
62
63#[cfg(test)]
64mod tests {
65 use p3_binary_field::{BinaryField8, BinaryField16, BinaryField128, TowerLevel};
66 use p3_field::PrimeCharacteristicRing;
67
68 use super::{domain_point, domain_point_steps, subspace_polynomial};
69
70 fn check_steps_chain_to_the_walk<F: TowerLevel>(count: usize) {
72 let steps = domain_point_steps::<F>(count);
73 let mut point = F::ZERO;
74 assert_eq!(point, domain_point::<F>(0));
75 for index in 1..1usize << count {
76 point += steps[index.trailing_zeros() as usize];
77 assert_eq!(point, domain_point::<F>(index << 1), "index={index}");
78 }
79 }
80
81 #[test]
84 fn domain_point_images_are_the_cantor_basis() {
85 for r in 0..8 {
86 assert_eq!(
87 domain_point::<BinaryField8>(1 << r),
88 BinaryField8::cantor_basis(r),
89 "r={r}"
90 );
91 }
92 for r in 0..usize::BITS as usize {
93 assert_eq!(
94 domain_point::<BinaryField128>(1 << r),
95 BinaryField128::cantor_basis(r),
96 "r={r}"
97 );
98 }
99 }
100
101 #[test]
103 #[should_panic = "Cantor basis index out of range"]
104 fn domain_point_rejects_an_index_past_the_basis() {
105 let _ = domain_point::<BinaryField8>(1 << 8);
106 }
107
108 #[test]
110 fn domain_point_is_linear_in_the_index() {
111 for a in 0..1usize << 8 {
112 for b in 0..1usize << 8 {
113 assert_eq!(
114 domain_point::<BinaryField8>(a ^ b),
115 domain_point::<BinaryField8>(a) + domain_point::<BinaryField8>(b)
116 );
117 }
118 }
119 }
120
121 #[test]
124 fn domain_point_steps_chain_to_the_even_domain_points() {
125 check_steps_chain_to_the_walk::<BinaryField8>(7);
126 check_steps_chain_to_the_walk::<BinaryField16>(10);
127 check_steps_chain_to_the_walk::<BinaryField128>(12);
128 }
129
130 #[test]
132 fn subspace_polynomial_vanishes_on_its_subspace() {
133 for j in 0..=8 {
134 for m in 0..1usize << 8 {
135 let value = subspace_polynomial::<BinaryField8>(j, domain_point(m));
136 assert_eq!(value == BinaryField8::ZERO, m < 1 << j, "j={j} m={m}");
137 }
138 }
139 }
140
141 #[test]
143 fn subspace_polynomial_shifts_the_basis() {
144 for j in 0..16 {
145 for i in j..16 {
146 assert_eq!(
147 subspace_polynomial::<BinaryField16>(j, BinaryField16::cantor_basis(i)),
148 BinaryField16::cantor_basis(i - j),
149 "j={j} i={i}"
150 );
151 }
152 }
153 }
154
155 #[test]
157 fn domain_points_agree_across_levels() {
158 for m in 0..1usize << 8 {
159 let small: u128 = domain_point::<BinaryField8>(m).to_repr().into();
160 let large: u128 = domain_point::<BinaryField128>(m).to_repr();
161 assert_eq!(small, large, "index {m}");
162 }
163 }
164}