diff --git a/fp/src/_doctest_support.rs b/fp/src/_doctest_support.rs new file mode 100644 index 0000000..6a3238d --- /dev/null +++ b/fp/src/_doctest_support.rs @@ -0,0 +1,34 @@ +//! Hidded modules only used for testing in the documentation + +pub mod _doctest_fp_ext { + use crate::field_ops::FieldOps; + use crate::fp_element::FpElement; + use crate::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; + use crypto_bigint::{const_prime_monty_params, Uint}; + + const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); + pub type Fp19 = FpElement; + + pub struct QuadPoly; + pub struct TSQuad; + + impl IrreduciblePoly for QuadPoly { + fn modulus() -> [Fp19; 2] { + [Fp19::one(), Fp19::zero()] + } + } + + impl TonelliShanksConstants for TSQuad { + const ORDER: Uint<1> = Uint::<1>::from_u64(360); + const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); + const S: u64 = 3; + const T: Uint<1> = Uint::<1>::from_u64(45); + const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); + const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); + fn root_of_unity() -> [FpElement; 2] { + [Fp19::from_u64(3), Fp19::from_u64(3)] + } + } + + pub type F19_2 = FpExt; +} diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs index 32b80d8..60e6b00 100644 --- a/fp/src/fp_element.rs +++ b/fp/src/fp_element.rs @@ -6,8 +6,8 @@ use std::fmt; use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom}; use crypto_bigint::{ - NonZero, RandomMod, Uint, modular::{ConstMontyForm, ConstPrimeMontyParams}, + NonZero, RandomMod, Uint, }; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 875df5e..65913eb 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -25,10 +25,6 @@ //! const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); //! type Fp19 = FpElement; //! -//! fn fp(n: u64) -> Fp19 { -//! Fp19::from_u64(n) -//! } -//! //! /* Setput the irreducible polynomial */ //! struct QuadPoly; //! impl IrreduciblePoly for QuadPoly { @@ -61,9 +57,9 @@ //! type F19_2 = FpExt; //! //! /* Some standard tests */ -//! let a = F19_2::new([fp(3), fp(2)]); -//! let ainv = F19_2::new([fp(9), fp(13)]); -//! let asquare = F19_2::new([fp(5), fp(12)]); +//! let a = F19_2::from_u64_vec([3, 2]); +//! let ainv = F19_2::from_u64_vec([9, 13]); +//! let asquare = F19_2::from_u64_vec([5, 12]); //! assert_eq!(a.invert().unwrap(), ainv); //! assert_eq!(a.square(), asquare); //! assert_eq!(asquare.sqrt().unwrap().square(), asquare); @@ -118,12 +114,16 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// /// The polynomial $f(x) = x^2 + 1$ is irreducible over $\mathbb{F}_{19}$. /// ``` -/// # use crypto_bigint::{const_prime_monty_params, Uint}; -/// # use fp::fp_element::FpElement; -/// # use fp::field_ops::FieldOps; -/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::field_ops::FieldOps; +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); /// type Fp19 = FpElement; +/// +/// /* Define an irreducible polynomial */ /// struct MyQuadPoly; /// impl IrreduciblePoly for MyQuadPoly { /// fn modulus() -> [FpElement; 2] { @@ -137,11 +137,15 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; /// /// Note that $f(x) = x^3 + 17$ is irreducible over $\mathbb{F}_{19}$. /// ``` -/// # use crypto_bigint::{const_prime_monty_params, Uint}; -/// # use fp::fp_element::FpElement; -/// # use fp::field_ops::FieldOps; -/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::field_ops::FieldOps; +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); +/// +/// /* Define an irreducible polynomial */ /// type Fp19 = FpElement; /// struct MyCubicPoly; /// impl IrreduciblePoly for MyCubicPoly { @@ -182,11 +186,15 @@ implement for a new field /// # Example: $\mathbb{F}_{19^2}$ /// Note that we have a factorisation $19^2 - 1 = 2^3 \cdot 45$ /// ``` -/// # use crypto_bigint::{const_prime_monty_params, Uint}; -/// # use fp::fp_element::FpElement; -/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// use crypto_bigint::{const_prime_monty_params, Uint}; +/// use fp::fp_element::FpElement; +/// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; +/// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); /// type Fp19 = FpElement; +/// +/// /* Write out the precomputed Tonelli--Shanks constants */ /// struct TSQuad; /// impl TonelliShanksConstants for TSQuad { /// // p^2 - 1 @@ -270,18 +278,20 @@ where /// use fp::fp_element::FpElement; /// use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; /// +/// /* Setup the underlying prime field */ /// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); /// type Fp19 = FpElement; /// +/// /* Setup the irreducible poynomial */ /// struct QuadPoly; -/// struct TSQuad; -/// /// impl IrreduciblePoly for QuadPoly { /// fn modulus() -> [Fp19; 2] { /// [Fp19::one(), Fp19::zero()] /// } /// } /// +/// /* Write out the precomputed Tonelli--Shanks constants */ +/// struct TSQuad; /// impl TonelliShanksConstants for TSQuad { /// const ORDER: Uint<1> = Uint::<1>::from_u64(360); /// const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); @@ -336,35 +346,7 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::new([Fp19::from_u64(3), Fp19::from_u64(5)]); // a = 3 + 5 x /// ``` pub fn new(coeffs: [FpElement; M]) -> Self { @@ -391,40 +373,12 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::new([Fp19::from_u64(3), Fp19::from_u64(5)]); - /// let b = F19_2::from_u64([3, 5]); + /// let b = F19_2::from_u64_vec([3, 5]); /// assert_eq!(a, b); /// ``` - pub fn from_u64(coeffs: [u64; M]) -> Self { + pub fn from_u64_vec(coeffs: [u64; M]) -> Self { let coeffs_fp = std::array::from_fn(|i| FpElement::from_u64(coeffs[i])); Self::new(coeffs_fp) } @@ -446,36 +400,9 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; - /// let a = F19_2::from_u64([3, 5]); + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use crypto_bigint::Uint; // unclear why this is needed, it should be imported above + /// let a = F19_2::from_u64_vec([3, 5]); /// let b = F19_2::from_uint([Uint::<1>::from_u64(3), Uint::<1>::from_u64(5)]); /// assert_eq!(a, b); /// ``` @@ -499,37 +426,9 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; + /// # use fp::_doctest_support::_doctest_fp_ext::*; /// let a = F19_2::from_base(Fp19::from_u64(3)); - /// let b = F19_2::from_u64([3, 0]); + /// let b = F19_2::from_u64_vec([3, 0]); /// assert_eq!(a, b); // a = 3 + 0 x /// ``` pub fn from_base(a: FpElement) -> Self { @@ -553,36 +452,8 @@ where /// # Examples /// /// ``` - /// # use crypto_bigint::{const_prime_monty_params, Uint}; - /// # use fp::field_ops::{FieldOps, FieldRandom}; - /// # use fp::fp_element::FpElement; - /// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants}; - /// # const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2); - /// # type Fp19 = FpElement; - /// # - /// # struct QuadPoly; - /// # struct TSQuad; - /// # - /// # impl IrreduciblePoly for QuadPoly { - /// # fn modulus() -> [Fp19; 2] { - /// # [Fp19::one(), Fp19::zero()] - /// # } - /// # } - /// # - /// # impl TonelliShanksConstants for TSQuad { - /// # const ORDER: Uint<1> = Uint::<1>::from_u64(360); - /// # const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180); - /// # const S: u64 = 3; - /// # const T: Uint<1> = Uint::<1>::from_u64(45); - /// # const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22); - /// # const TWOSM1: Uint<1> = Uint::<1>::from_u64(4); - /// # fn root_of_unity() -> [FpElement; 2] { - /// # [Fp19::from_u64(3), Fp19::from_u64(3)] - /// # } - /// # } - /// # - /// # type F19_2 = FpExt; - /// let a = F19_2::from_u64([3, 5]); + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// let a = F19_2::from_u64_vec([3, 5]); /// assert_eq!(*a.coeff(0), Fp19::from_u64(3)); /// assert_eq!(*a.coeff(1), Fp19::from_u64(5)); /// ``` @@ -1166,8 +1037,31 @@ where })) } + /// /// Schoolbook multiplication followed by reduction modulo $f(x)$. /// + /// # Arguments + /// + /// * `rhs` - An element of $\mathbb{F}\_{p^M}$ to multiply by + /// (type: `&Self`). + /// + /// # Returns + /// + /// The product `self * rhs` (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let b = F19_2::from_u64_vec([7, 11]); + /// let prod = F19_2::from_u64_vec([4, 11]); + /// assert_eq!(a.mul(&b), prod); + /// ``` + /// + /// # Note + /// /// Product has degree $\leq 2M−2$, then each high-degree term is /// replaced using $x^M \equiv −\sum_j \texttt{modulus}\[j\] x^j$ /// until all degrees are below $M$. @@ -1190,6 +1084,23 @@ where /// Inversion via polynomial extended GCD. /// + /// # Returns + /// + /// Either the inverse of `self` or `none` if `self == 0` (type: + /// `CtOption`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let inv = F19_2::from_u64_vec([4, 6]); + /// assert_eq!(a.invert().unwrap(), inv); + /// ``` + /// + /// # Note + /// /// Finds $s$ such that $\texttt{self} \times s \equiv 1 \pmod{f}$ /// by computing $\gcd(\texttt{self}, f) = g$ (a nonzero constant /// if `self` is nonzero) and then returning $s g^{-1} \pmod{f}$. @@ -1218,21 +1129,60 @@ where // --- Frobenius ---------------------------------------------------------- - /// `φ_p(a) = a^p` — the p-power Frobenius endomorphism. + /// Computes the $p$-power Frobenius endomorphism $\phi_p(a) = a^p$. + /// + /// # Returns /// - /// Computed via square-and-multiply using the characteristic p retrieved - /// from the base field. + /// The $p$-power of `self` (type: Self) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let frob_a = F19_2::from_u64_vec([3, 14]); + /// assert_eq!(a.frobenius(), frob_a); + /// ``` + /// + /// # Note + /// + /// Computed via square-and-multiply using the characteristic $p$ + /// retrieved from the base field. fn frobenius(&self) -> Self { let p = FpElement::::characteristic(); - ::pow(self, &p) + // can use pow_vartime since the exponent is always the same + ::pow_vartime(self, &p) } // --- Norm and trace ----------------------------------------------------- - /// `N_{Fp^M/Fp}(a) = ∏_{i=0}^{M-1} φ_p^i(a)` — product of all Galois conjugates. + /// Computes the norm of `self` to $\mathbb{F}\_{p}$ embedded as an element + /// of $\mathbb{F}\_{p^M}$. + /// + /// As a reminder the norm is defined as the product over all the + /// Galois conjugates that is + /// $$ N\_{\mathbb{F}\_{p^M}/\mathbb{F}\_p}(a) = \prod\_{i=0}^{M-1} \phi\_p^i(a) $$ + /// + /// # Returns + /// + /// The norm of self (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let nm_a = F19_2::from_u64_vec([15, 0]); + /// assert_eq!(a.norm(), nm_a); + /// ``` /// - /// The result lies in Fp (all higher coefficients are 0), but is returned - /// embedded in Fp^M for uniformity with the [`FieldOps`] signature. + /// # Note + /// + /// The result lies in $\mathbb{F}\_p$ (all higher coefficients + /// are 0), but is returned embedded in $\mathbb{F}\_{p^M}$ for + /// uniformity with the [`FieldOps`] signature. fn norm(&self) -> Self { let mut result = self.clone(); let mut conj = self.frobenius(); @@ -1243,9 +1193,32 @@ where result } - /// `Tr_{Fp^M/Fp}(a) = Σ_{i=0}^{M-1} φ_p^i(a)` — sum of all Galois conjugates. + /// Computes the trace of `self` to $\mathbb{F}\_{p}$ embedded as an element + /// of $\mathbb{F}\_{p^M}$. + /// + /// As a reminder the trace is defined as the sum over all the + /// Galois conjugates that is + /// $$ \mathrm{tr}\_{\mathbb{F}\_{p^M}/\mathbb{F}\_p}(a) = \sum\_{i=0}^{M-1} \phi\_p^i(a) $$ + /// + /// # Returns /// - /// Like `norm`, the result lies in Fp but is returned embedded in Fp^M. + /// The trace of self (type: `Self`). + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 5]); + /// let tr_a = F19_2::from_u64_vec([6, 0]); + /// assert_eq!(a.trace(), tr_a); + /// ``` + /// + /// # Note + /// + /// The result lies in $\mathbb{F}\_p$ (all higher coefficients + /// are 0), but is returned embedded in $\mathbb{F}\_{p^M}$ for + /// uniformity with the [`FieldOps`] signature. fn trace(&self) -> Self { let mut result = self.clone(); let mut conj = self.frobenius(); @@ -1258,25 +1231,37 @@ where // --- Square root -------------------------------------------------------- - /// Tonelli--Shanks squareroot algorithm + /// Computes the square root of `self` /// - /// Implementation of the Tonelli--Shanks square root algorithm. Requires - /// only a factorisation as $p^M - 1 = 2^K N$ so can compute this at - /// compile time by truncating zeros. + /// # Returns /// - /// # Arguments + /// $\sqrt{\texttt{self}}$ a choice of squareroot (type: `Self`) /// - /// * `&self` - An element of Fp^M (type: &self) + /// # Examples /// - /// # Returns + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let sqrt_a = a.sqrt().unwrap(); + /// assert_eq!(sqrt_a.mul(&sqrt_a), a); + /// ``` /// - /// `self^(1/2)` a choice of squareroot (type: Self) + /// # Note + /// + /// This is an implementation of the Tonelli--Shanks squareroot + /// algorithm. + /// + /// # TODO + /// + /// - [ ] Generate the addition chain for the projenator exponent + /// this is constant time since the projenator exponent is + /// always(!) the same (for a fixed field). fn sqrt(&self) -> CtOption { let mut x = *self; let exp = TSCONSTS::PROJENATOR_EXP; let exp_limbs = exp.as_limbs().map(|limb| limb.0); - // TODO: generate the addition chain for this specific constant - // this is constant time since exp_limbs is always(!) the same + let w = x.pow_vartime(&exp_limbs); ts_loop(&mut x, &w); CtOption::new( @@ -1285,19 +1270,32 @@ where ) } - /// Inverse and sqrt in one exponentiation + /// Returns both the inverse and sqrt of `self` /// - /// Computes the inverse and squareroot of `self` in one - /// exponentiation using the tricks in Scott's article + /// # Returns /// - /// # Arguments + /// $(1/\texttt{self}, \sqrt{\texttt{self}})$ which is + /// `self.invert()` and `self.sqrt()` (type: `CtOption`, + /// `CtOption`) /// - /// * `&self` - An element of Fp^M (type: &self) + /// # Examples /// - /// # Returns + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let (inv_a, sqrt_a) = a.inverse_and_sqrt(); + /// let inv_a = inv_a.unwrap(); + /// let sqrt_a = sqrt_a.unwrap(); + /// assert!(bool::from(inv_a.mul(&a).is_one())); + /// assert_eq!(sqrt_a.mul(&sqrt_a), a); + /// ``` /// - /// `(myinv, mysqrt)` which is `self.invert()` and `self.sqrt()` - /// (type: `CtOption`, `CtOption`) + /// # Note + /// + /// Computes the inverse and squareroot of `self` in one + /// exponentiation using the tricks in Scott's article + /// . fn inverse_and_sqrt(&self) -> (CtOption, CtOption) { let is_invertible = !self.is_zero(); @@ -1315,7 +1313,6 @@ where let e1_limbs = e1.as_limbs().map(|limb| limb.0); // Compute x^(2^(S - 1) - 1) * (x * w^4)^(2^(S - 1)) - // pow_vartime? let first_term = self.pow_vartime(&e1_limbs); let xw4 = self * &w.pow_vartime(&[4 as u64]); @@ -1332,36 +1329,65 @@ where ) } - /// Inverse of squareroot of `self` in 1 exponentiation + /// Computes the inverse and squareroot of `self`. /// - /// Computes 1/sqrt(self) using the trick from Mike Scott's - /// "Tricks of the trade" article Section 2 - /// + /// # Returns /// - /// # Arguments + /// $1 / \sqrt{\texttt{self}}$, the inverse of the squareroot of + /// `self` (type: `CtOption`) /// - /// * `&self` - Description of &self (type: self) + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let inv_sqrt_a = a.inv_sqrt(); + /// let inv_sqrt_a = inv_sqrt_a.unwrap(); + /// assert_eq!(inv_sqrt_a.mul(&inv_sqrt_a), a.invert().unwrap()); + /// ``` /// - /// # Returns + /// # Note /// - /// The inverse of the squareroot of `self` (type: `CtOption`) + /// Computes $1/\sqrt{\texttt{self}$ using the trick from Scott's + /// "Tricks of the trade" article Section 2 + /// fn inv_sqrt(&self) -> CtOption { let (inv, sqrt) = self.inverse_and_sqrt(); inv.and_then(|a| sqrt.map(|b| &a * &b)) } - /// Inverse of `self` and squareroot of `rhs` in 1 exponentiation + /// Inverse of `self` and squareroot of `rhs`. /// - /// Computes `1/self` and `rhs.sqrt()` simulaineously using the - /// trick from Mike Scott's "Tricks of the trade" article Section - /// 2 + /// # Arguments + /// + /// * `rhs` - element of $\mathbb{F}\_{p^M}$ (type: `&Self`) /// /// # Returns /// - /// The inverse of `self` and square root fo `rhs`. Theq former is - /// none if and only if `self` is nonzero and the latter is not - /// none if and only if there exists a squareroot of `rhs` in FpM - /// (type: (`CtOption`, `CtOption`)) + /// The pair $(1 / \texttt{self}, \sqrt{\texttt{rhs}})$. The + /// former is none if and only if `self` is nonzero and the latter + /// is none if and only if there is no squareroot of `rhs` in + /// $\mathbb{F}\_{p^M}$ (type: (`CtOption`, + /// `CtOption`)) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let b = F19_2::from_u64_vec([2, 11]); + /// let (inv_a, sqrt_b) = a.invertme_sqrtother(&b); + /// let inv_a = inv_a.unwrap(); + /// let sqrt_b = sqrt_b.unwrap(); + /// assert!(bool::from(inv_a.mul(&a).is_one())); + /// assert_eq!(sqrt_b.square(), b); + /// ``` + /// + /// # Note + /// + /// Computes `1/self` and `rhs.sqrt()` simulaineously using the + /// trick from Scott's article Section 2 + /// fn invertme_sqrtother(&self, rhs: &Self) -> (CtOption, CtOption) { let is_invertible = !self.is_zero(); @@ -1386,20 +1412,33 @@ where /// Computes the squareroot of a ratio `self/rhs` /// - /// Computes `sqrt(self/rhs)` in one exponentiation using the - /// trick from Mike Scott's "Tricks of the trade" article Section - /// 2 - /// /// # Arguments - /// - /// * `&self` - Element of FpM (type: self) - /// * `rhs` - Element of FpM (type: &Self) + /// * `rhs` - Element of $\mathbb{F}\_{p^M}$ (type: `&Self`) /// /// # Returns /// - /// The squareroot of the ratio `self/rhs` is not none if and only - /// if `rhs` is invertible and the ratio has an FpM squareroot - /// (type: `CtOption`) + /// $\sqrt{\texttt{self}/\texttt{rhs}}$, the squareroot of the + /// ratio which is not none if and only if `rhs` is invertible and + /// the ratio has an $\mathbb{F}\_{p^M}$ rational squareroot (type: + /// `CtOption`) + /// + /// # Examples + /// + /// ``` + /// # use fp::_doctest_support::_doctest_fp_ext::*; + /// # use fp::field_ops::FieldOps; + /// let a = F19_2::from_u64_vec([3, 11]); + /// let b = F19_2::from_u64_vec([2, 11]); + /// let sqrt_a_over_b = a.sqrt_ratio(&b); + /// let sqrt_a_over_b = sqrt_a_over_b.unwrap(); + /// assert_eq!(sqrt_a_over_b.square().mul(&b), a); + /// ``` + /// + /// # Note + /// + /// Computes $\sqrt{\texttt{self}/\texttt{rhs}}$ in one + /// exponentiation using the trick from Scott's "Tricks of the + /// trade" article Section 2 fn sqrt_ratio(&self, rhs: &Self) -> CtOption { let x = self * &(&(self.square()) * rhs); let (myinv, mysqrt) = x.inverse_and_sqrt(); @@ -1414,20 +1453,25 @@ where CtOption::new(ans_value, ans_is_some) } - /// a is a QR in Fp^M iff a^{(p^M-1)/2} = 1. + /// Implements the "Legendre symbol" which is 1 if and only if + /// `self` is a quadratic residue in $\mathbb{F}\_{p^M}$. /// - /// Implements the "Legendre symbol" which is 1 if and only if we - /// have a quadratic residue in FpM - /// WARNING: Not constant time if `self` is zero + /// As a reminder the "Legendre symbol" for $a \in \mathbb{F}\_{p^M}$ is defined as + /// $$ \begin{cases} 0 & \text{ if $a = 0$,} \\\\ 1 & \text{if $a$ + /// is a QR,} \\\\ -1 & \text{if $a$ is not a QR.} \end{cases} $$ /// - /// # Arguments + /// # Returns /// - /// * `&self` - Element of FpM (type: self) + /// The Legendre symbol of `self` (type: `i8`) /// - /// # Returns + /// # WARNING + /// + /// Not constant time if `self` is zero + /// + /// # Note /// - /// Either `0` if `&self` is `0`, `1` if `&self` is a QR or `-1` if - /// `&self` is not a QR. (type: i8) + /// Implemented using the fact that $a$ is a QR in + /// $\mathbb{F}\_{p^M}$ if and only if $a^{(p^M-1)/2} = 1$. fn legendre(&self) -> i8 { let exp = TSCONSTS::HALF_ORDER; let exp_limbs = exp.as_limbs().map(|limb| limb.0); diff --git a/fp/src/lib.rs b/fp/src/lib.rs index c787936..045d797 100644 --- a/fp/src/lib.rs +++ b/fp/src/lib.rs @@ -22,3 +22,6 @@ pub mod field_ops; pub mod fp_element; /// Prime-field extension towers and related helper traits. pub mod fp_ext; + +#[doc(hidden)] +pub mod _doctest_support;