1use crate::bigint::BigInt;
60use crate::error::MathError;
61use crate::extensible::{mul_fft_standalone, mul_karatsuba_standalone, mul_toom_cook_standalone};
62use crate::field::{FieldElement, FieldOps};
63#[cfg(feature = "alloc")]
64extern crate alloc;
65#[cfg(feature = "alloc")]
66use alloc::vec::Vec;
67
68pub trait FieldExtensions {
73 fn try_sqrt(&self) -> Option<Self>
78 where
79 Self: Sized;
80
81 fn legendre(&self) -> i32;
86
87 fn is_quadratic_residue(&self) -> bool {
89 self.legendre() == 1
90 }
91
92 fn order(&self) -> Option<BigInt>;
97}
98
99impl FieldExtensions for FieldElement {
100 fn try_sqrt(&self) -> Option<Self> {
101 #[cfg(feature = "alloc")]
104 {
105 crate::field::sqrt(self)
106 }
107 #[cfg(not(feature = "alloc"))]
108 {
109 None }
111 }
112
113 fn legendre(&self) -> i32 {
114 #[cfg(feature = "alloc")]
115 {
116 crate::field::legendre_symbol(self)
117 }
118 #[cfg(not(feature = "alloc"))]
119 {
120 if self == &FieldElement::from_u64(0) {
123 0
124 } else {
125 let square = self.square();
127 if square == FieldElement::from_u64(0) {
128 0 } else {
130 1 }
132 }
133 }
134 }
135
136 fn order(&self) -> Option<BigInt> {
137 #[cfg(feature = "alloc")]
139 {
140 crate::field::order(self)
141 }
142 #[cfg(not(feature = "alloc"))]
143 {
144 None }
146 }
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151pub enum Algorithm {
152 Binary,
154 SlidingWindow(u32),
156 MontgomeryLadder,
158 FixedWindow(u32),
160 Adaptive,
162}
163
164#[derive(Clone, Debug)]
166pub struct ExponentiationConfig {
167 pub algorithm: Algorithm,
169 pub max_bits: Option<u32>,
171 pub constant_time: bool,
173}
174
175impl Default for ExponentiationConfig {
176 fn default() -> Self {
177 Self {
178 algorithm: Algorithm::Adaptive,
179 max_bits: None,
180 constant_time: true,
181 }
182 }
183}
184
185#[derive(Clone, Debug)]
190pub struct ExponentiationBuilder<'a> {
191 base: &'a FieldElement,
192 exponent: &'a BigInt,
193 config: ExponentiationConfig,
194}
195
196impl<'a> ExponentiationBuilder<'a> {
197 pub fn new(base: &'a FieldElement, exponent: &'a BigInt) -> Self {
199 Self {
200 base,
201 exponent,
202 config: ExponentiationConfig::default(),
203 }
204 }
205
206 pub fn algorithm(mut self, algorithm: Algorithm) -> Self {
208 self.config.algorithm = algorithm;
209 self
210 }
211
212 pub fn max_bits(mut self, bits: u32) -> Self {
214 self.config.max_bits = Some(bits);
215 self
216 }
217
218 pub fn constant_time(mut self, enabled: bool) -> Self {
220 self.config.constant_time = enabled;
221 self
222 }
223
224 pub fn build(self) -> ConfiguredExponentiation<'a> {
226 ConfiguredExponentiation {
227 base: self.base,
228 exponent: self.exponent,
229 config: self.config,
230 }
231 }
232}
233
234#[derive(Clone, Debug)]
236pub struct ConfiguredExponentiation<'a> {
237 base: &'a FieldElement,
238 exponent: &'a BigInt,
239 config: ExponentiationConfig,
240}
241
242impl<'a> ConfiguredExponentiation<'a> {
243 pub fn compute(self) -> FieldElement {
245 match self.config.algorithm {
246 Algorithm::Binary => {
247 self.base.pow(self.exponent)
249 }
250 Algorithm::SlidingWindow(_window) => {
251 self.base.pow(self.exponent)
254 }
255 Algorithm::MontgomeryLadder => {
256 self.base.pow(self.exponent)
258 }
259 Algorithm::FixedWindow(window_size) => self.fixed_window_exp(window_size),
260 Algorithm::Adaptive => {
261 let bit_length = self.exponent.bit_length();
263 if bit_length < 64 {
264 self.base.pow(self.exponent)
266 } else {
267 self.base.pow(self.exponent)
269 }
270 }
271 }
272 }
273
274 fn fixed_window_exp(self, window_size: u32) -> FieldElement {
279 let w = window_size.clamp(1, 6);
281
282 let exp_bit_length = self.exponent.bit_length() as usize;
284 if exp_bit_length == 0 {
285 return FieldElement::from_u64(1);
286 }
287
288 if exp_bit_length <= w as usize {
290 return self.base.pow(self.exponent);
291 }
292
293 let table_size = 1 << w; let mut table = Vec::with_capacity(table_size);
296
297 table.push(FieldElement::from_u64(1));
299
300 table.push(*self.base);
302
303 for i in 2..table_size {
305 table.push(table[i - 1].mul(self.base));
306 }
307
308 let mut result = FieldElement::from_u64(1);
310 let mut bit_pos = (exp_bit_length - 1) as u32;
311
312 loop {
313 let remaining_bits = bit_pos + 1;
315 let current_window = if remaining_bits >= w {
316 w
317 } else {
318 remaining_bits
319 };
320
321 let mut window_value = 0u32;
322 for j in 0..current_window {
323 if bit_pos >= j {
325 let bit = self.exponent.get_bit(bit_pos - j);
326 if bit != 0 {
327 window_value |= 1 << j;
328 }
329 }
330 }
332
333 for _ in 0..current_window {
335 result = result.mul(&result);
336 }
337
338 if window_value > 0 {
340 result = result.mul(&table[window_value as usize]);
341 }
342
343 if current_window > bit_pos {
345 break; }
347 bit_pos -= current_window;
348 }
349
350 result
351 }
352
353 pub fn get_algorithm(&self) -> Algorithm {
355 self.config.algorithm
356 }
357
358 pub fn get_max_bits(&self) -> Option<u32> {
360 self.config.max_bits
361 }
362
363 pub fn get_constant_time(&self) -> bool {
365 self.config.constant_time
366 }
367}
368
369#[derive(Clone, Debug)]
371pub struct MultiplicationConfig {
372 pub algorithm: MultiplicationAlgorithm,
374 pub optimize_squaring: bool,
376}
377
378impl Default for MultiplicationConfig {
379 fn default() -> Self {
380 Self {
381 algorithm: MultiplicationAlgorithm::Standard,
382 optimize_squaring: true,
383 }
384 }
385}
386
387#[derive(Clone, Copy, Debug, PartialEq, Eq)]
389pub enum MultiplicationAlgorithm {
390 Standard,
392 Schoolbook,
394 Karatsuba,
396 ToomCook,
398 FFT,
400}
401
402pub trait ConfigurableArithmetic {
407 fn mul_with_config(&self, rhs: &Self, config: &MultiplicationConfig) -> Self;
409
410 fn pow_with_config(&self, exp: &BigInt, config: &ExponentiationConfig) -> Self;
412}
413
414impl ConfigurableArithmetic for FieldElement {
415 fn mul_with_config(&self, rhs: &Self, config: &MultiplicationConfig) -> Self {
416 match config.algorithm {
417 MultiplicationAlgorithm::Standard | MultiplicationAlgorithm::Schoolbook => {
418 self.mul(rhs)
420 }
421 MultiplicationAlgorithm::Karatsuba
422 | MultiplicationAlgorithm::ToomCook
423 | MultiplicationAlgorithm::FFT => {
424 let a_regular = crate::montgomery::from_montgomery_p(&self.to_bigint());
427 let b_regular = crate::montgomery::from_montgomery_p(&rhs.to_bigint());
428
429 let result_regular = a_regular.mul_with_config(&b_regular, config);
430
431 let mont_result = crate::montgomery::to_montgomery_p(&result_regular);
433 Self { inner: mont_result }
434 }
435 }
436 }
437
438 fn pow_with_config(&self, exp: &BigInt, config: &ExponentiationConfig) -> Self {
439 ExponentiationBuilder::new(self, exp)
441 .algorithm(config.algorithm)
442 .max_bits(config.max_bits.unwrap_or(256))
443 .constant_time(config.constant_time)
444 .build()
445 .compute()
446 }
447}
448
449impl ConfigurableArithmetic for BigInt {
450 fn mul_with_config(&self, rhs: &Self, config: &MultiplicationConfig) -> Self {
451 match config.algorithm {
452 MultiplicationAlgorithm::Standard | MultiplicationAlgorithm::Schoolbook => {
453 self.mul(rhs)
455 }
456 MultiplicationAlgorithm::Karatsuba => {
457 mul_karatsuba_standalone(self, rhs)
459 }
460 MultiplicationAlgorithm::ToomCook => {
461 mul_toom_cook_standalone(self, rhs)
463 }
464 MultiplicationAlgorithm::FFT => {
465 mul_fft_standalone(self, rhs)
467 }
468 }
469 }
470
471 fn pow_with_config(&self, exp: &BigInt, _config: &ExponentiationConfig) -> Self {
472 let mut result = BigInt::from_u64(1);
475 let mut base = *self;
476 let mut exp = *exp;
477
478 while !exp.is_zero() {
479 if exp.limbs()[0] % 2 == 1 {
480 result = result.mul(&base);
481 }
482 exp = exp.shr(1);
483 if !exp.is_zero() {
484 base = base.mul(&base);
485 }
486 }
487
488 result
489 }
490}
491
492#[derive(Clone, Debug)]
494#[cfg(feature = "alloc")]
495pub struct BatchConfig {
496 pub max_batch_size: usize,
498 pub parallel: bool,
500 pub allocation_strategy: AllocationStrategy,
502}
503
504#[cfg(feature = "alloc")]
505impl Default for BatchConfig {
506 fn default() -> Self {
507 Self {
508 max_batch_size: 1000,
509 parallel: false,
510 allocation_strategy: AllocationStrategy::default(),
511 }
512 }
513}
514
515#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
517pub enum AllocationStrategy {
518 #[default]
520 Preallocate,
521 OnDemand,
523 Reuse,
525}
526
527#[derive(Clone, Debug)]
529#[cfg(feature = "alloc")]
530pub struct BatchInverseBuilder<'a> {
531 elements: &'a [FieldElement],
532 config: BatchConfig,
533}
534
535#[cfg(feature = "alloc")]
536impl<'a> BatchInverseBuilder<'a> {
537 pub fn new(elements: &'a [FieldElement]) -> Self {
539 Self {
540 elements,
541 config: BatchConfig::default(),
542 }
543 }
544
545 pub fn max_batch_size(mut self, size: usize) -> Self {
547 self.config.max_batch_size = size;
548 self
549 }
550
551 pub fn parallel(mut self, enabled: bool) -> Self {
553 self.config.parallel = enabled;
554 self
555 }
556
557 pub fn allocation_strategy(mut self, strategy: AllocationStrategy) -> Self {
559 self.config.allocation_strategy = strategy;
560 self
561 }
562
563 pub fn get_max_batch_size(&self) -> usize {
565 self.config.max_batch_size
566 }
567
568 pub fn get_parallel(&self) -> bool {
570 self.config.parallel
571 }
572
573 pub fn get_allocation_strategy(&self) -> AllocationStrategy {
575 self.config.allocation_strategy
576 }
577
578 pub fn compute(self) -> Result<crate::field::BatchInverseResult, MathError> {
580 crate::field::batch_inverse_checked(self.elements)
583 }
584}
585
586pub trait FutureOperations {
591 fn batch_op_reserved(&self, _op: u32) -> Result<Self, MathError>
593 where
594 Self: Sized,
595 {
596 Err(MathError::NotImplemented)
597 }
598
599 fn crypto_op_reserved(&self, _op: u32, _params: &[u8]) -> Result<Vec<u8>, MathError> {
601 Err(MathError::NotImplemented)
602 }
603
604 fn supports_future_op(&self, op: u32) -> bool {
606 let _ = op;
607 false
608 }
609}
610
611impl FutureOperations for FieldElement {}
612
613#[derive(Clone, Copy, Debug, PartialEq, Eq)]
615pub struct ApiVersion {
616 pub major: u32,
618 pub minor: u32,
620 pub patch: u32,
622}
623
624impl ApiVersion {
625 pub const CURRENT: ApiVersion = ApiVersion {
627 major: 0,
628 minor: 8,
629 patch: 0,
630 };
631
632 pub fn supports(&self, feature: ApiFeature) -> bool {
634 match feature {
635 ApiFeature::BasicArithmetic => true,
636 ApiFeature::BatchOperations => self.minor >= 6,
637 ApiFeature::AdvancedArithmetic => self.minor >= 6,
638 ApiFeature::ExtensibleApi => self.minor >= 8,
639 }
640 }
641}
642
643#[derive(Clone, Copy, Debug, PartialEq, Eq)]
645pub enum ApiFeature {
646 BasicArithmetic,
648 BatchOperations,
650 AdvancedArithmetic,
652 ExtensibleApi,
654}
655
656pub trait AdvancedComputation {
662 #[cfg(feature = "alloc")]
668 fn evaluate_polynomial(&self, coeffs: &[Self], x: &Self) -> Self
669 where
670 Self: Sized;
671
672 fn gcd_extended(&self, other: &Self) -> Self
677 where
678 Self: Sized;
679}
680
681impl AdvancedComputation for FieldElement {
682 #[cfg(feature = "alloc")]
683 fn evaluate_polynomial(&self, coeffs: &[Self], x: &Self) -> Self {
684 let mut result = coeffs
686 .last()
687 .cloned()
688 .unwrap_or_else(|| FieldElement::from_u64(0));
689
690 for coeff in coeffs.iter().rev().skip(1) {
691 result = result.mul(x).add(coeff);
692 }
693
694 result
695 }
696
697 fn gcd_extended(&self, other: &Self) -> Self {
698 if *self == FieldElement::from_u64(0) && *other == FieldElement::from_u64(0) {
700 FieldElement::from_u64(0)
701 } else {
702 FieldElement::from_u64(1)
703 }
704 }
705}
706
707pub trait HardwareAcceleration {
713 fn hardware_accelerated(&self, operation: HardwareOperation) -> bool {
715 let _ = operation;
716 false }
718}
719
720#[derive(Clone, Copy, Debug, PartialEq, Eq)]
722pub enum HardwareOperation {
723 MontgomeryMul,
725 ModularExp,
727 BatchInverse,
729 MultiExp,
731 SquareRoot,
733}
734
735impl HardwareAcceleration for FieldElement {}
736
737pub trait MemoryOptimization {
743 fn preferred_alignment(&self) -> usize
745 where
746 Self: Sized,
747 {
748 core::mem::align_of::<Self>()
749 }
750}
751
752impl MemoryOptimization for FieldElement {}
753
754#[cfg(test)]
755mod tests {
756 use super::*;
757
758 #[test]
759 fn test_field_extensions() {
760 let zero = FieldElement::from_u64(0);
761 let one = FieldElement::from_u64(1);
762 let four = FieldElement::from_u64(4);
763
764 assert_eq!(zero.legendre(), 0);
766 assert_eq!(one.legendre(), 1); assert!(!zero.is_quadratic_residue());
770 assert!(one.is_quadratic_residue());
771
772 #[cfg(feature = "alloc")]
774 {
775 if let Some(sqrt_four) = four.try_sqrt() {
776 assert_eq!(sqrt_four.square(), four);
777 }
778 if let Some(sqrt_one) = one.try_sqrt() {
779 assert_eq!(sqrt_one.square(), one);
780 }
781 }
782
783 #[cfg(feature = "alloc")]
785 {
786 let order_one = one.order();
788 assert_eq!(order_one, Some(BigInt::from_u64(1)));
789
790 let order_zero = zero.order();
792 assert!(order_zero.is_none());
793 }
794 }
795
796 #[test]
797 fn test_exponentiation_builder() {
798 let base = FieldElement::from_u64(2);
799 let exp = BigInt::from_u64(10);
800
801 let result = ExponentiationBuilder::new(&base, &exp)
803 .algorithm(Algorithm::Binary)
804 .build()
805 .compute();
806
807 let expected = FieldElement::from_u64(1024); assert_eq!(result, expected);
809 }
810
811 #[test]
812 fn test_fixed_window_exponentiation() {
813 let base = FieldElement::from_u64(2);
814
815 let exp_one = BigInt::from_u64(1);
817 let binary_result_one = ExponentiationBuilder::new(&base, &exp_one)
818 .algorithm(Algorithm::Binary)
819 .build()
820 .compute();
821
822 assert_eq!(
823 binary_result_one, base,
824 "Binary exponentiation with exp=1 should return base"
825 );
826
827 let fixed_result_one = ExponentiationBuilder::new(&base, &exp_one)
828 .algorithm(Algorithm::FixedWindow(2))
829 .build()
830 .compute();
831
832 assert_eq!(
833 fixed_result_one, base,
834 "Fixed window with exp=1 should return base"
835 );
836
837 let exp_zero = BigInt::from_u64(0);
839 let result_zero = ExponentiationBuilder::new(&base, &exp_zero)
840 .algorithm(Algorithm::FixedWindow(2))
841 .build()
842 .compute();
843
844 assert_eq!(
845 result_zero,
846 FieldElement::from_u64(1),
847 "Fixed window with exp=0 should return 1"
848 );
849
850 let exp_three = BigInt::from_u64(3);
852 let result_three_binary = ExponentiationBuilder::new(&base, &exp_three)
853 .algorithm(Algorithm::Binary)
854 .build()
855 .compute();
856
857 let result_three_fixed = ExponentiationBuilder::new(&base, &exp_three)
858 .algorithm(Algorithm::FixedWindow(2))
859 .build()
860 .compute();
861
862 assert_eq!(
863 result_three_fixed, result_three_binary,
864 "Fixed window should match binary for exp=3"
865 );
866
867 let result_w1 = ExponentiationBuilder::new(&base, &exp_three)
869 .algorithm(Algorithm::FixedWindow(1))
870 .build()
871 .compute();
872
873 let result_w3 = ExponentiationBuilder::new(&base, &exp_three)
874 .algorithm(Algorithm::FixedWindow(3))
875 .build()
876 .compute();
877
878 assert_eq!(result_w1, result_three_binary, "Window size 1 should work");
879 assert_eq!(result_w3, result_three_binary, "Window size 3 should work");
880 }
881
882 #[test]
883 fn test_configurable_arithmetic() {
884 let a = FieldElement::from_u64(3);
885 let b = FieldElement::from_u64(4);
886 let exp = BigInt::from_u64(5);
887
888 let mul_config = MultiplicationConfig::default();
890 let product = a.mul_with_config(&b, &mul_config);
891 assert_eq!(product, FieldElement::from_u64(12));
892
893 let karatsuba_config = MultiplicationConfig {
895 algorithm: MultiplicationAlgorithm::Karatsuba,
896 optimize_squaring: true,
897 };
898 let product_karatsuba = a.mul_with_config(&b, &karatsuba_config);
899 assert_eq!(product_karatsuba, FieldElement::from_u64(12));
900
901 let toom_cook_config = MultiplicationConfig {
902 algorithm: MultiplicationAlgorithm::ToomCook,
903 optimize_squaring: true,
904 };
905 let product_toom_cook = a.mul_with_config(&b, &toom_cook_config);
906 assert_eq!(product_toom_cook, FieldElement::from_u64(12));
907
908 let fft_config = MultiplicationConfig {
909 algorithm: MultiplicationAlgorithm::FFT,
910 optimize_squaring: true,
911 };
912 let product_fft = a.mul_with_config(&b, &fft_config);
913 assert_eq!(product_fft, FieldElement::from_u64(12));
914
915 let pow_config = ExponentiationConfig::default();
917 let power = a.pow_with_config(&exp, &pow_config);
918 assert_eq!(power, FieldElement::from_u64(243)); }
920
921 #[cfg(feature = "alloc")]
922 #[test]
923 fn test_batch_builder() {
924 let elements = alloc::vec![
925 FieldElement::from_u64(2),
926 FieldElement::from_u64(3),
927 FieldElement::from_u64(5),
928 ];
929
930 let result = BatchInverseBuilder::new(&elements)
931 .max_batch_size(10)
932 .compute()
933 .unwrap();
934
935 assert_eq!(elements[0].mul(&result.get(0)), FieldElement::from_u64(1));
937 assert_eq!(elements[1].mul(&result.get(1)), FieldElement::from_u64(1));
938 assert_eq!(elements[2].mul(&result.get(2)), FieldElement::from_u64(1));
939 }
940
941 #[test]
942 fn test_api_version() {
943 let version = ApiVersion::CURRENT;
944
945 assert!(version.supports(ApiFeature::BasicArithmetic));
946 assert!(version.supports(ApiFeature::BatchOperations));
947 assert!(version.supports(ApiFeature::AdvancedArithmetic));
948 assert!(version.supports(ApiFeature::ExtensibleApi));
950 }
951
952 #[test]
953 fn test_future_operations() {
954 let element = FieldElement::from_u64(42);
955
956 assert!(!element.supports_future_op(0));
958 assert!(matches!(
959 element.batch_op_reserved(0),
960 Err(MathError::NotImplemented)
961 ));
962 }
963}