moma/
lib.rs

1//! # MOMA: Moving Origin Modular Arithmetic
2//!
3//! A framework for exploring number theory concepts using a "moving origin"
4//! in modular arithmetic.
5//!
6//! The core idea of MOMA is that for a given modulus `m`, the "zero point" or "origin"
7//! for the calculation `x mod m` is not fixed. Instead, it shifts based on a
8//! contextual value, typically a prime number `p`. This allows for the study of
9//! properties of numbers (like primes) in a dynamic relational context.
10//!
11//! ## Core Concepts
12//!
13//! - **`MomaRing`**: The central object for calculations. It is defined by a `modulus` and an `OriginStrategy`.
14//! - **`OriginStrategy`**: A trait that defines *how* the origin moves. The crate provides several
15//!   common strategies, and users can easily implement their own.
16//! - **Residue**: The result of a MOMA calculation, computed as `(value + origin) % modulus`.
17//!
18//! ## Example Usage
19//!
20//! ```
21//! use moma::{MomaRing, strategy}; 
22//!
23//! // Create a MOMA ring with modulus 37.
24//! // The origin will be the gap between a prime and the previous prime.
25//! let ring = MomaRing::new(37, strategy::PrimeGap);
26//!
27//! // Let's analyze the prime p = 29.
28//! let p = 29;
29//!
30//! // The value we want to find the residue of. In my original research,
31//! // this was the sum of a prime and its predecessor.
32//! let value_to_test = p + primes::prev_prime(p); // 29 + 23 = 52
33//!
34//! // Calculate the MOMA residue.
35//! // The origin for p=29 is (29 - 23) = 6.
36//! // The residue is (52 + 6) % 37 = 58 % 37 = 21.
37//! let residue = ring.residue(value_to_test, p);
38//!
39//! println!("For p={}, the MOMA residue of {} is {}", p, value_to_test, residue);
40//! assert_eq!(residue, 21);
41//! ```
42
43// Re-export key components for easier access.
44pub use crate::analysis::{CompositeDampener, MassField};
45pub use crate::core::{MomaRing, OriginStrategy};
46
47/// Core MOMA structures and traits.
48pub mod core {
49    use crate::primes;
50
51    /// Defines a strategy for calculating the moving origin for a given prime context.
52    ///
53    /// This trait is the cornerstone of MOMA's flexibility. By implementing this trait,
54    /// users can create custom logic for how the modular origin should be determined.
55    pub trait OriginStrategy {
56        /// Calculates the origin based on a contextual prime `p`.
57        ///
58        /// # Parameters
59        /// - `p`: The prime number providing the context for the origin calculation.
60        fn calculate_origin(&self, p: u64) -> u64;
61    }
62
63    /// The central struct for performing Moving Origin Modular Arithmetic.
64    ///
65    /// A `MomaRing` is configured with a modulus and a chosen `OriginStrategy`.
66    /// It then calculates residues by shifting the input value by the dynamically
67    /// computed origin before applying the modulus.
68    pub struct MomaRing<S: OriginStrategy> {
69        pub modulus: u64,
70        strategy: S,
71    }
72
73    impl<S: OriginStrategy> MomaRing<S> {
74        /// Creates a new `MomaRing` with a given modulus and origin strategy.
75        ///
76        /// # Parameters
77        /// - `modulus`: The modulus for the arithmetic operations.
78        /// - `strategy`: An instance of a struct that implements `OriginStrategy`.
79        pub fn new(modulus: u64, strategy: S) -> Self {
80            Self { modulus, strategy }
81        }
82
83        /// Calculates the MOMA residue for a value within a prime context.
84        ///
85        /// This is the primary operation of the `MomaRing`. It first calculates the
86        /// origin using the ring's strategy and the provided `prime_context`,
87        /// then computes `(value + origin) % modulus`.
88        ///
89        /// # Parameters
90        /// - `value`: The input value to map to the ring.
91        /// - `prime_context`: The prime number used to determine the origin shift.
92        pub fn residue(&self, value: u64, prime_context: u64) -> u64 {
93            // Ensure modulus is not zero to prevent division by zero panic.
94            if self.modulus == 0 {
95                return value;
96            }
97            let origin = self.strategy.calculate_origin(prime_context);
98            (value.wrapping_add(origin)) % self.modulus
99        }
100
101        /// A convenience method for calculating the "signature" of a prime.
102        ///
103        /// The signature is defined as the residue of the sum of a prime and its
104        /// immediate predecessor. This is a common use case in MOMA-based analysis.
105        ///
106        /// # Parameters
107        /// - `p`: The prime for which to calculate the signature.
108        pub fn signature(&self, p: u64) -> u64 {
109            if p < 3 { return 0; } // prev_prime(2) is problematic, handle edge case.
110            let input = p.wrapping_add(primes::prev_prime(p));
111            self.residue(input, p)
112        }
113    }
114}
115
116/// Implementations of various origin strategies.
117pub mod strategy {
118    use crate::core::OriginStrategy;
119    use crate::primes;
120
121    /// An origin strategy where the origin is fixed to a constant value.
122    #[derive(Debug, Clone, Copy)]
123    pub struct Fixed(pub u64);
124    impl OriginStrategy for Fixed {
125        fn calculate_origin(&self, _p: u64) -> u64 {
126            self.0
127        }
128    }
129
130    /// An origin strategy where the origin is the gap between a prime and its predecessor.
131    /// `origin(p) = p - p_prev`
132    #[derive(Debug, Clone, Copy)]
133    pub struct PrimeGap;
134    impl OriginStrategy for PrimeGap {
135        fn calculate_origin(&self, p: u64) -> u64 {
136            if p < 3 { return 0; }
137            p - primes::prev_prime(p)
138        }
139    }
140
141    /// An origin strategy where the origin is the sum of prime factors of all
142    /// composite numbers in the gap between a prime and its successor.
143    /// `origin(p) = Σ mass(c)` for `c` in `(p, p_next)`.
144    #[derive(Debug, Clone, Copy)]
145    pub struct CompositeMass;
146    impl OriginStrategy for CompositeMass {
147        fn calculate_origin(&self, p: u64) -> u64 {
148            let p_next = primes::next_prime(p);
149            (p + 1..p_next)
150                .filter(|&n| !primes::is_prime(n))
151                .map(primes::prime_factor_mass)
152                .sum()
153        }
154    }
155}
156
157/// Tools for number-theoretic analysis related to MOMA.
158pub mod analysis {
159    use crate::primes;
160
161    /// A tool to analyze the "dampening" of composite numbers within a range.
162    ///
163    /// The dampening score measures how many composites in a range [lower, upper]
164    /// are divisible by a given set of small primes. A higher score means the
165    /// composites in the range are "less random" and more likely to be multiples
166    /// of small primes.
167    pub struct CompositeDampener {
168        pub lower: u64,
169        pub upper: u64,
170        pub small_primes: Vec<u64>,
171    }
172
173    impl CompositeDampener {
174        /// Calculates the dampening score for the given range.
175        ///
176        /// The score is the ratio of composites hit by `small_primes` to the total
177        /// number of composites in the range.
178        pub fn score(&self) -> f64 {
179            let composites: Vec<u64> = (self.lower + 1..self.upper)
180                .filter(|&n| !primes::is_prime(n))
181                .collect();
182
183            if composites.is_empty() {
184                return 0.0;
185            }
186
187            let hits = composites
188                .iter()
189                .filter(|&c| self.small_primes.iter().any(|sp| c % sp == 0))
190                .count();
191
192            hits as f64 / composites.len() as f64
193        }
194    }
195
196    /// A tool to analyze the "mass" of composite numbers between consecutive primes.
197    pub struct MassField {
198        pub range_start: u64,
199        pub range_end: u64,
200    }
201
202    impl MassField {
203        /// Generates a map of `(prime, composite_mass_in_next_gap)`.
204        ///
205        /// The "mass" is the sum of the count of prime factors for each composite
206        /// number between a prime `p` and its successor `p_next`.
207        pub fn generate_mass_map(&self) -> Vec<(u64, u64)> {
208            let mut map = Vec::new();
209            let mut p = primes::next_prime(self.range_start.saturating_sub(1));
210
211            while p < self.range_end {
212                let p_next = primes::next_prime(p);
213                let mass = (p + 1..p_next)
214                    .filter(|&n| !primes::is_prime(n))
215                    .map(primes::prime_factor_mass)
216                    .sum();
217                map.push((p, mass));
218                p = p_next;
219            }
220            map
221        }
222    }
223}
224
225/// Utility functions for prime number operations.
226///
227/// NOTE: For a high-performance production crate, consider replacing these
228/// with a dependency on a specialized library like `primal`.
229pub mod primes {
230
231    /// A basic primality test.
232    pub fn is_prime(n: u64) -> bool {
233        if n < 2 { return false; }
234        if n == 2 || n == 3 { return true; }
235        if n % 2 == 0 || n % 3 == 0 { return false; }
236        let mut i = 5;
237        while i * i <= n {
238            if n % i == 0 || n % (i + 2) == 0 {
239                return false;
240            }
241            i += 6;
242        }
243        true
244    }
245
246    /// Finds the next prime number strictly greater than `n`.
247    pub fn next_prime(n: u64) -> u64 {
248        if n < 2 { return 2; }
249        // Start with the next odd number.
250        let mut x = if n % 2 == 0 { n + 1 } else { n + 2 };
251        loop {
252            if is_prime(x) {
253                return x;
254            }
255            x += 2; // Only check odd numbers.
256        }
257    }
258
259    /// Finds the greatest prime number strictly less than `n`.
260    /// Returns 0 if no such prime exists (e.g., for n <= 2).
261    pub fn prev_prime(n: u64) -> u64 {
262        if n <= 2 { return 0; }
263        let mut x = n - 1;
264        while x >= 2 {
265            if is_prime(x) {
266                return x;
267            }
268            x -= 1;
269        }
270        0
271    }
272
273    /// Calculates the "mass" of a number, defined as the count of its prime factors
274    /// with multiplicity. For example, `prime_factor_mass(12) = mass(2*2*3) = 3`.
275    pub fn prime_factor_mass(n: u64) -> u64 {
276        if n < 2 { return 0; }
277        let mut count = 0;
278        let mut temp_n = n;
279        let mut factor = 2;
280        while factor * factor <= temp_n {
281            while temp_n % factor == 0 {
282                count += 1;
283                temp_n /= factor;
284            }
285            factor += 1;
286        }
287        if temp_n > 1 {
288            count += 1;
289        }
290        count
291    }
292}