Add new problem modules and utility functions for Euler and Rosalind challenges
Some checks failed
mint_ci / Check Python code using ruff (push) Successful in 19s
Rust-lint / Run rust tests (push) Failing after 55s
Rust-lint / Check Rust code with rustfmt and clippy (push) Failing after 36s

- Introduced `prob700`, `prob808`, and `prob816` modules for new problem implementations in the Project Euler series.
- Added utility functions for calculating Euler coins and their sums, as well as methods for finding reversible primes and calculating shortest distances in a modular context.
- Updated the `mod.rs` files to include the new problem modules in both the Project Euler and Rosalind sections.
- Enhanced the `integer` and `modulo` utility modules with new functions for coprimality checks and modular multiplication.
- Included unit tests for the new functionalities to ensure correctness and reliability.
- Refactored existing tests for consistency in the `finding_protein_motif` module.
This commit is contained in:
2025-05-23 11:12:37 +09:00
parent f4d4c81d31
commit b25ed7e13d
10 changed files with 608 additions and 10 deletions

View File

@@ -4,7 +4,10 @@ pub mod prob497;
pub mod prob61;
pub mod prob650;
pub mod prob66;
pub mod prob700;
pub mod prob719;
pub mod prob808;
pub mod prob816;
pub mod prob885;
pub mod prob932;
pub mod prob934;

View File

@@ -0,0 +1,198 @@
use num_integer::{Integer, gcd};
use std::collections::HashSet;
fn euler_inverse(p: i64, q: i64) -> i64 {
let g = gcd(p, q) as i64;
if g != 1 {
panic!("p and q are not coprime");
}
let (mut rn, mut rn1) = (p, q);
let (mut an, mut an1) = (1, 0);
while rn1 != 0 {
let (quot, rem) = rn.div_rem(&rn1);
let temp = an1;
an1 = an - quot * an1;
an = temp;
rn = rn1;
rn1 = rem;
}
an
}
fn naive_eulercoins(p: i64, q: i64) -> Vec<i64> {
let mut coins = vec![p];
let mut current = p;
while current != 1 {
current = (current + p) % q;
if &current < coins.last().unwrap() {
coins.push(current);
}
}
coins
}
pub fn naive_eulercoins_sum(p: i64, q: i64) -> i64 {
let coins = naive_eulercoins(p, q);
coins.iter().sum()
}
pub fn eulercoins(p: i64, q: i64) -> HashSet<i64> {
let inv = euler_inverse(p, q);
// for left index, large number
let mut left_index = 0;
let mut left_coin = i64::MAX;
let mut test_number = 0;
let left_step = p;
// for right index, small number
let mut right_index = i64::MAX;
let mut right_coin = 0;
let mut test_index = 0;
let right_step = inv;
let mut coins = HashSet::new();
let mut bigstep;
while left_index < right_index {
left_index += 1;
test_number = (test_number + left_step).rem_euclid(q);
if test_number < left_coin {
left_coin = test_number;
if coins.contains(&left_coin) {
break;
}
coins.insert(left_coin);
bigstep = (q - test_number) / left_step;
test_number += bigstep * left_step;
left_index += bigstep;
}
right_coin += 1;
test_index = (test_index + right_step).rem_euclid(q);
if test_index < right_index {
right_index = test_index;
if coins.contains(&right_coin) {
break;
}
coins.insert(right_coin);
bigstep = (q - test_index) / right_step;
test_index += bigstep * right_step;
right_coin += bigstep;
}
}
coins
}
pub fn eulercoins_sum(p: i64, q: i64) -> i64 {
let coins = eulercoins(p, q);
coins.iter().sum()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::prime::is_prime;
#[test]
#[ignore]
fn test_naive_eulercoins() {
assert_eq!(
naive_eulercoins_sum(1_983_365_009, 8_977_183_777),
6696721770
);
}
#[test]
fn test_euler_coefficient() {
assert_eq!(euler_inverse(3, 2), 1);
assert_eq!(euler_inverse(2, 3), -1);
assert_eq!(euler_inverse(5, 3), -1);
assert_eq!(euler_inverse(3, 5), 2);
assert_eq!(euler_inverse(17, 13), -3);
assert_eq!(euler_inverse(13, 17), 4);
}
#[test]
fn test_eulercoins() {
assert_eq!(eulercoins(2, 3), HashSet::from([2, 1]));
assert_eq!(eulercoins(3, 5), HashSet::from([1, 3]));
assert_eq!(eulercoins(13, 17), HashSet::from([1, 5, 9, 13]));
assert_eq!(
eulercoins(1_983_365_009, 8_977_183_777),
HashSet::from([
1_983_365_009,
939641268,
835558795,
731476322,
627393849,
523311376,
419228903,
315146430,
211063957,
106981484,
2899011,
281923,
202142,
122361,
42580,
5379,
452,
45,
43,
41,
39,
37,
35,
33,
31,
29,
27,
25,
23,
21,
19,
17,
15,
13,
11,
9,
7,
5,
3,
1,
])
);
}
#[test]
fn test_eulercoins_many_case() {
for p in 5..=100 {
for q in p + 1..=200 {
if !is_prime(p as usize) || !is_prime(q as usize) {
continue;
}
let coins = eulercoins(p, q);
let naive_coins = naive_eulercoins(p, q);
assert_eq!(coins, HashSet::from_iter(naive_coins.into_iter()));
}
}
}
#[test]
fn test_eulercoins_sum() {
assert_eq!(eulercoins_sum(1_983_365_009, 8_977_183_777), 6696721770);
assert_eq!(
eulercoins_sum(1_504_170_715_041_707, 4_503_599_627_370_517),
1517926517777556
);
}
}

View File

@@ -0,0 +1,149 @@
use crate::utils::integer::is_square;
use crate::utils::prime::{is_prime, prime_iter};
use std::collections::HashMap;
pub fn is_revserible(prime: u64) -> bool {
let rev_square = (prime * prime)
.to_string()
.chars()
.rev()
.collect::<String>()
.parse::<u64>()
.unwrap();
is_square(rev_square) && rev_square != prime * prime && is_prime(rev_square.isqrt())
}
pub fn reverse(n: u64) -> u64 {
n.to_string()
.chars()
.rev()
.collect::<String>()
.parse::<u64>()
.unwrap()
}
pub fn length(n: u64) -> usize {
n.to_string().len()
}
pub fn find_reversible_primes_in_range(n: usize) -> Vec<u64> {
let mut cache_rev_squares: HashMap<u64, u64> = HashMap::new();
let mut reversible_prime_squares = Vec::new();
let mut square;
for prime in prime_iter::<u64>(n) {
square = prime * prime;
if let Some(rev_square) = cache_rev_squares.remove(&square) {
reversible_prime_squares.push(rev_square);
reversible_prime_squares.push(square);
} else if length(square) % 2 == 1 {
let rev_square = reverse(square);
if rev_square != square && (rev_square % 10 == 1 || rev_square % 10 == 9) {
cache_rev_squares.insert(rev_square, square);
}
}
}
reversible_prime_squares.sort();
reversible_prime_squares
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_revserible() {
assert_eq!(is_revserible(3), false);
assert_eq!(is_revserible(5), false);
assert_eq!(is_revserible(7), false);
assert_eq!(is_revserible(11), false);
assert_eq!(is_revserible(13), true);
}
#[test]
fn test_find_reversible_primes_in_range() {
assert_eq!(
find_reversible_primes_in_range(500_000_000),
vec![
169,
961,
12769,
96721,
1042441,
1062961,
1216609,
1442401,
1692601,
9066121,
121066009,
900660121,
12148668841,
12367886521,
12568876321,
14886684121,
1000422044521,
1002007006009,
1020506060401,
1040606050201,
1210684296721,
1212427816609,
1212665666521,
1214648656321,
1234367662441,
1236568464121,
1254402240001,
1256665662121,
1276924860121,
1442667634321,
9006007002001,
9066187242121,
100042424498641,
100222143232201,
100240164024001,
100402824854641,
100420461042001,
102012282612769,
102014060240401,
102232341222001,
104042060410201,
121002486012769,
121264386264121,
121462683462121,
123212686214641,
146412686212321,
146458428204001,
146894424240001,
967210684200121,
967216282210201,
10020032222212321,
10022014702860169,
10201204627026169,
10203042928272769,
10424432666212321,
10444842248400121,
12100484224844401,
12100662429066121,
12102442783276609,
12104622861462121,
12120294922868521,
12122012862600169,
12122034882612769,
12126416822640121,
12144284865634321,
12166092426600121,
12321222223002001,
12321266623442401,
12343656848244121,
12586822949202121,
90667238724420121,
96100626821022121,
96106820741022001,
96162072640210201,
96721628843022121,
96727282924030201
]
);
}
}

View File

@@ -0,0 +1,135 @@
use crate::utils::modulo::modulo_mul;
fn make_point_array(s0: u64, n: usize, modulus: u64) -> Vec<(u64, u64)> {
let mut s2n = s0;
let mut s2n1 = modulo_mul(s0, s0, modulus);
let mut points = vec![(s2n, s2n1)];
for _ in 1..n {
s2n = modulo_mul(s2n1, s2n1, modulus);
s2n1 = modulo_mul(s2n, s2n, modulus);
points.push((s2n, s2n1));
}
points
}
pub fn naive_shortest_distance(s0: u64, n: usize, modulus: u64) -> String {
let points = make_point_array(s0, n, modulus);
let mut min_distance_square = i64::MAX;
for i in 0..n {
for j in i + 1..n {
let dx1 = (points[i].0 as i64 - points[j].0 as i64).abs();
let dx2 = (points[i].1 as i64 - points[j].1 as i64).abs();
let distance_square = dx1 * dx1 + dx2 * dx2;
if distance_square < min_distance_square {
min_distance_square = distance_square;
}
}
}
format!("{:.9}", (min_distance_square as f64).sqrt())
}
pub fn dc_shortest_distance(points: &[(u64, u64)]) -> i64 {
let n = points.len();
if n <= 3 {
let mut min_distance_square = i64::MAX;
for i in 0..n {
for j in i + 1..n {
let dx1 = points[i].0 as i64 - points[j].0 as i64;
let dx2 = points[i].1 as i64 - points[j].1 as i64;
let distance_square = dx1 * dx1 + dx2 * dx2;
if distance_square < min_distance_square {
min_distance_square = distance_square;
}
}
}
return min_distance_square;
}
let mid = n / 2;
let min_from_left = dc_shortest_distance(&points[..mid]);
let min_from_right = dc_shortest_distance(&points[mid..]);
let mut min_distance_square = min_from_left.min(min_from_right);
let min_distance = min_distance_square.isqrt() as u64 + 1;
let mid_x = points[mid].0;
let mut strip = vec![points[mid]];
let mut cursor = mid - 1;
while mid_x < points[cursor].0 + min_distance {
strip.push(points[cursor]);
cursor = match cursor.checked_sub(1) {
Some(c) => c,
None => break,
};
}
cursor = mid + 1;
while cursor < n && points[cursor].0 < mid_x + min_distance {
strip.push(points[cursor]);
cursor += 1;
}
strip.sort_by_key(|p| p.1);
let length = strip.len();
for i in 0..length {
cursor = i + 1;
while cursor < length && strip[cursor].1 < min_distance + strip[i].1 {
let dx1 = strip[i].0 as i64 - strip[cursor].0 as i64;
let dx2 = strip[i].1 as i64 - strip[cursor].1 as i64;
let distance_square = dx1 * dx1 + dx2 * dx2;
if distance_square < min_distance_square {
min_distance_square = distance_square;
}
cursor += 1;
}
}
min_distance_square
}
pub fn shortest_distance(s0: u64, n: usize, modulus: u64) -> String {
let mut points = make_point_array(s0, n, modulus);
points.sort_by_key(|p| p.0);
let min_distance_square = dc_shortest_distance(&points);
format!("{:.9}", (min_distance_square as f64).sqrt())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_make_point_array() {
let points = make_point_array(2, 10, 1000);
assert_eq!(points.len(), 10);
assert_eq!(
points,
vec![
(2, 4),
(16, 256),
(536, 296),
(616, 456),
(936, 96),
(216, 656),
(336, 896),
(816, 856),
(736, 696),
(416, 56)
]
)
}
#[test]
fn test_naive_shortest_distance() {
assert_eq!(
naive_shortest_distance(290797, 14, 50515093),
"546446.466846479"
);
}
#[test]
fn test_shortest_distance() {
assert_eq!(shortest_distance(290797, 14, 50515093), "546446.466846479");
assert_eq!(
shortest_distance(290797, 2_000_000, 50515093),
"20.880613018"
);
}
}

View File

@@ -47,7 +47,7 @@ mod tests {
let protein = "MRASRPVVHPVEAPPPAALAVAAAAVAVEAGVGAGGGAAAHGGENAQPRGVRMKDPPGAPGTPGGLGLRLVQAFFAAAALAVMASTDDFPSVSAFCYLVAAAILQCLWSLSLAVVDIYALLVKRSLRNPQAVCIFTIGDGITGTLTLGAACASAGITVLIGNDLNICANNHCASFETATAMAFISWFALAPSCVLNFWSMASR";
let motif = "N{P}[ST]{P}";
let positions = find_protein_motif(protein, motif);
assert_eq!(positions, vec![]);
assert_eq!(positions, Vec::<usize>::new());
let protein = "GCATGATACATG";
let motif = "CAT";
@@ -66,7 +66,7 @@ mod tests {
let uniprot_id = "A2Z669";
let positions = find_protein_motif_in_uniprot(uniprot_id, motif);
assert_eq!(positions, vec![]);
assert_eq!(positions, Vec::<usize>::new());
sleep(Duration::from_millis(10));

View File

@@ -3,4 +3,5 @@ pub mod enumerating_gene_orders;
pub mod finding_protein_motif;
pub mod inferring_mrna_from_protein;
pub mod open_reading_frames;
pub mod rna_secondary_structure;
pub mod rna_splicing;

View File

View File

@@ -1,4 +1,4 @@
use num::integer::{Integer, Roots};
use num::integer::{Integer, Roots, gcd};
use std::cmp::Ordering;
pub fn is_square<T>(n: T) -> bool
@@ -9,6 +9,13 @@ where
s * s == n
}
pub fn is_coprime<T>(a: T, b: T) -> bool
where
T: Integer + Copy,
{
gcd(a, b) == T::one()
}
pub fn has_split_of_sum(n: usize, sum: usize) -> Option<Vec<usize>> {
match n.cmp(&sum) {
Ordering::Less => None,

View File

@@ -1,6 +1,9 @@
use num::traits::{Euclid, PrimInt};
use num::{
bigint::{BigInt, ToBigInt},
traits::{Euclid, PrimInt},
};
pub fn checked_modulo_add<T: PrimInt + Euclid>(a: T, b: T, modulo: T) -> T {
pub fn modulo_add<T: PrimInt + Euclid>(a: T, b: T, modulo: T) -> T {
match a.checked_add(&b) {
Some(c) => c % modulo,
None => {
@@ -16,23 +19,57 @@ pub fn checked_modulo_add<T: PrimInt + Euclid>(a: T, b: T, modulo: T) -> T {
}
}
pub fn modulo_mul<T>(a: T, b: T, modulo: T) -> T
where
T: PrimInt + Euclid + ToBigInt,
BigInt: std::ops::Rem<T>,
{
match a.checked_mul(&b) {
Some(c) => c.rem_euclid(&modulo),
None => {
let a_big = a.to_bigint().unwrap();
let b_big = b.to_bigint().unwrap();
let modulo_big = modulo.to_bigint().unwrap();
let c_big = a_big * b_big;
let c = c_big.rem_euclid(&modulo_big);
T::from(c.to_u32_digits().1[0]).unwrap()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_checked_modulo_add() {
fn test_modulo_add() {
assert_eq!(
checked_modulo_add::<u32>(1_000_000_000, 4_000_000_000, 1_543_545_676),
modulo_add::<u32>(1_000_000_000, 4_000_000_000, 1_543_545_676),
369_362_972
);
assert_eq!(
checked_modulo_add::<i32>(1_000_000_000, 2_000_000_000, 1_543_545_676),
modulo_add::<i32>(1_000_000_000, 2_000_000_000, 1_543_545_676),
1_456_454_324
);
assert_eq!(
checked_modulo_add::<i32>(-1_000_000_000, -2_000_000_000, 1_543_545_676),
modulo_add::<i32>(-1_000_000_000, -2_000_000_000, 1_543_545_676),
87_091_352
);
}
#[test]
fn test_modulo_mul() {
assert_eq!(
modulo_mul::<u32>(1_000_000_000, 4_000_000_000, 1_543_545_676),
866_330_992
);
assert_eq!(
modulo_mul::<i32>(1_000_000_000, 2_000_000_000, 1_543_545_676),
433_165_496
);
assert_eq!(
modulo_mul::<i32>(-1_000_000_000, 2_000_000_000, 1_543_545_676),
1_110_380_180
);
}
}

View File

@@ -1,4 +1,4 @@
use num::{FromPrimitive, ToPrimitive, Unsigned, integer::Roots, iter::range_inclusive};
use num::{FromPrimitive, PrimInt, ToPrimitive, Unsigned, integer::Roots, iter::range_inclusive};
pub fn is_prime<T>(n: T) -> bool
where
@@ -18,6 +18,60 @@ where
}
}
struct PrimeSieve<T> {
_marker: std::marker::PhantomData<T>,
sieve: Vec<bool>,
cursor: usize,
max: usize,
}
impl<T> PrimeSieve<T>
where
T: PrimInt + FromPrimitive,
{
fn new(n: usize) -> Self {
Self {
_marker: std::marker::PhantomData,
sieve: vec![true; n + 1],
cursor: 2,
max: n,
}
}
}
impl<T> Iterator for PrimeSieve<T>
where
T: PrimInt + ToPrimitive + FromPrimitive,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
while self.cursor <= self.max && !self.sieve[self.cursor] {
self.cursor += 1;
}
if self.cursor > self.max {
return None;
}
let prime = T::from_usize(self.cursor).unwrap();
let end = self.max / self.cursor;
(2..=end).for_each(|i| {
self.sieve[i * self.cursor] = false;
});
self.cursor += 1;
Some(prime)
}
}
pub fn prime_iter<T>(n: usize) -> impl Iterator<Item = T>
where
T: PrimInt + ToPrimitive + FromPrimitive,
{
PrimeSieve::<T>::new(n).into_iter()
}
#[cfg(test)]
mod tests {
use super::*;
@@ -44,4 +98,18 @@ mod tests {
assert_eq!(is_prime::<u32>(10000006), false);
assert_eq!(is_prime::<u32>(10000019), true);
}
#[test]
fn test_prime_sieve() {
let mut sieve = PrimeSieve::<i32>::new(20);
assert_eq!(sieve.next(), Some(2));
assert_eq!(sieve.next(), Some(3));
assert_eq!(sieve.next(), Some(5));
assert_eq!(sieve.next(), Some(7));
assert_eq!(sieve.next(), Some(11));
assert_eq!(sieve.next(), Some(13));
assert_eq!(sieve.next(), Some(17));
assert_eq!(sieve.next(), Some(19));
assert_eq!(sieve.next(), None);
}
}