Skip to main content

relp_num/traits/
factorization.rs

1//! # Number factorization
2//!
3//! Factorize integers and rational numbers into numbers that are often primes.
4use std::fmt::Debug;
5use std::hash::Hash;
6use std::ops::{Add, AddAssign, Sub, SubAssign};
7
8use num_traits::One;
9
10use crate::non_zero::{NonZero, NonZeroSign};
11use crate::Signed;
12
13/// Creating a factorization of an integer or rational number.
14///
15/// This factorization does not necessarily consist of primes, and it is not necessarily complete:
16/// finding all prime factors of a large number is expensive, so the routines give up at some point
17/// and report whatever they did not decompose in
18/// [`NonZeroFactorization::residual`](NonZeroFactorization#structfield.residual).
19///
20/// # Invariant
21///
22/// Whatever the parameters the implementation was tuned with, the factorization always describes
23/// the entire value:
24///
25/// ```text
26/// value == sign * residual * product(factor ^ power)
27/// ```
28///
29/// So a factorization never silently loses part of the value; when a cofactor could not be split,
30/// it ends up in `residual` and
31/// [`NonZeroFactorization::is_complete`](NonZeroFactorization::is_complete) is `false`.
32pub trait NonZeroFactorizable: NonZero + Clone {
33    /// Some number greater than 1, probably a prime but not necessarily.
34    type Factor: NonZero + Eq + PartialEq + Ord + PartialOrd + Hash + Clone + Debug;
35    /// How often the factor appears in the number.
36    ///
37    /// This is marked Copy, because a 64-bit power already allows for values up to 2^(2^64), which
38    /// has about 5.6 * 10^18 decimal digits. Finding primes that are larger than that is too
39    /// expensive.
40    type Power: Add<Output=Self::Power> + AddAssign + Sub<Output=Self::Power> + SubAssign + One + Signed + Eq + Copy + Clone + Debug;
41    /// The part of the value that was not decomposed.
42    ///
43    /// This is one exactly when the factorization is complete. It is a separate type because it is
44    /// not necessarily representable as a [`Factor`](NonZeroFactorizable::Factor): a cofactor of a
45    /// big integer can be larger than a machine word, and a rational number leaves a residual on
46    /// both sides of the fraction.
47    type Residual: FactorizationResidual;
48
49    /// Decompose into factors.
50    ///
51    /// Note that these factors will often be, but are not guaranteed to be, primes.
52    ///
53    /// The value is always described completely: see the invariant on
54    /// [`NonZeroFactorizable`] and on [`NonZeroFactorization`].
55    fn factorize(&self) -> NonZeroFactorization<Self::Factor, Self::Power, Self::Residual>;
56}
57
58/// The part of a value that a factorization did not decompose.
59///
60/// A residual of one means that nothing was left over, so the factors describe the value exactly.
61///
62/// This trait is deliberately weaker than [`num_traits::One`]: it demands no multiplication,
63/// because the residual of a rational factorization is a `(numerator, denominator)` pair, which has
64/// no meaningful product.
65pub trait FactorizationResidual: Eq + Clone + Debug {
66    /// The residual of a factorization that decomposed the entire value.
67    fn one() -> Self;
68    /// Whether nothing was left undecomposed.
69    fn is_one(&self) -> bool;
70}
71
72macro_rules! residual_from_num_traits_one {
73    ($($ty:ty)*) => {
74        $(
75            impl FactorizationResidual for $ty {
76                #[inline]
77                fn one() -> Self {
78                    <$ty as One>::one()
79                }
80
81                #[inline]
82                fn is_one(&self) -> bool {
83                    <$ty as One>::is_one(self)
84                }
85            }
86        )*
87    }
88}
89residual_from_num_traits_one!(u8 u16 u32 u64 u128 usize);
90
91/// A rational number leaves a residual on both sides of the fraction.
92///
93/// The two halves are `(numerator residual, denominator residual)`; they can't be merged into a
94/// single value, as that value would generally not be an integer.
95impl<Numerator, Denominator> FactorizationResidual for (Numerator, Denominator)
96where
97    Numerator: FactorizationResidual,
98    Denominator: FactorizationResidual,
99{
100    #[inline]
101    fn one() -> Self {
102        (Numerator::one(), Denominator::one())
103    }
104
105    #[inline]
106    fn is_one(&self) -> bool {
107        self.0.is_one() && self.1.is_one()
108    }
109}
110
111/// Factorization of a nonzero integer or rational number.
112///
113/// Includes a sign.
114///
115/// # Invariant
116///
117/// The three fields together always describe the entire value:
118///
119/// ```text
120/// value == sign * residual * product(factor ^ power)
121/// ```
122///
123/// The factorization routines are deliberately incomplete, because splitting a large cofactor is
124/// expensive. Whatever they did not decompose is in `residual` rather than dropped, so the identity
125/// above holds regardless of how they were tuned. Use [`Self::is_complete`] to find out whether the
126/// value was decomposed entirely.
127#[derive(Eq, PartialEq, Clone, Debug)]
128pub struct NonZeroFactorization<Factor, Power, Residual> {
129    /// Whether the number is negative.
130    pub sign: NonZeroSign,
131    /// `(factor, power)` tuples.
132    ///
133    /// These factors are often, but not necessarily, primes; they are all larger than 1. The
134    /// factors should all be smaller than 64 bits and can have negative powers; that is, appear in
135    /// the denominator. The powers can't be zero, as this is a sparse representation.
136    ///
137    /// When this field is empty, nothing was decomposed and the value is `sign * residual`. In
138    /// particular, an empty list does **not** mean that the value is `1` or `-1`; that is the case
139    /// only when the `residual` is one as well.
140    pub factors: Vec<(Factor, Power)>,
141    /// The part of the value that was not decomposed.
142    ///
143    /// One exactly when the factors describe the entire value, see [`Self::is_complete`]. It is
144    /// always positive; the sign of the value is in `sign`.
145    pub residual: Residual,
146}
147
148impl<Factor, Power, Residual: FactorizationResidual> NonZeroFactorization<Factor, Power, Residual> {
149    /// Whether the value was decomposed entirely, that is, whether the residual is one.
150    ///
151    /// When this is `false`, the `factors` describe only the part of the value that could be split
152    /// off cheaply, and multiplying them together does not reproduce the value: the `residual` is
153    /// the missing factor.
154    #[must_use]
155    #[inline]
156    pub fn is_complete(&self) -> bool {
157        self.residual.is_one()
158    }
159}