Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
dba3fd5
first commit
oboulant Apr 24, 2025
2bc8f3c
saddle-points
oboulant Apr 28, 2025
81cc38f
use `flat_map()` instead of `map().flatten()`
oboulant Apr 28, 2025
506e564
say
oboulant May 2, 2025
f33fb79
correction after feedback
oboulant May 2, 2025
741ca1c
scrabble
oboulant May 7, 2025
517c0e7
sieve
oboulant May 12, 2025
ce53234
simple linked list
oboulant May 14, 2025
d89b095
spiral_matrix
oboulant May 17, 2025
9b79e90
tournament
oboulant May 20, 2025
f8ef18f
triangle
oboulant May 21, 2025
c1621d8
two-bucket
oboulant Jun 6, 2025
4528bcb
vlq
oboulant Jun 6, 2025
a53c884
Merge branch 'exercism:main' into main
oboulant Jun 10, 2025
9b08fe5
robot simulator
oboulant Jun 10, 2025
9d81344
Merge branch 'main' of https://github.com/oboulant/exercism-rust
oboulant Jun 10, 2025
5d7c10d
robot name
oboulant Jun 12, 2025
a49dabf
protein translation
oboulant Jun 12, 2025
a8e7fab
working without exponentials
oboulant Jun 13, 2025
3c0ba71
wordy
oboulant Jun 13, 2025
7e472d3
only locking when necessary on write and fix a bug on reset
oboulant Jun 17, 2025
83428d7
accumulate
oboulant Jun 23, 2025
402f312
custom-set
oboulant Jun 23, 2025
a5b42bd
affine-cipher
oboulant Jun 23, 2025
7fc387a
atbash-cipher
oboulant Jun 23, 2025
8cac6e6
crypto square
oboulant Jun 29, 2025
9cedeb7
diamond
oboulant Jun 29, 2025
520a8c5
largest-series-product
oboulant Jun 30, 2025
571b9e5
luhn-from
oboulant Jun 30, 2025
c40ddbc
luhn-trait
oboulant Jun 30, 2025
7356cdc
list-ops
oboulant Jul 3, 2025
7a6a132
phone-number
oboulant Jul 3, 2025
e52533a
using .first() to access first element
oboulant Jul 3, 2025
32a91f1
rail-fence-cipher
oboulant Jul 3, 2025
9d845b5
roman-numerals
oboulant Jul 3, 2025
5275025
correct string formating
oboulant Jul 3, 2025
2adc0d9
rotational-cipher
oboulant Jul 3, 2025
42bd28c
simple-cipher
oboulant Jul 8, 2025
42f2f49
fix some warnings
oboulant Jul 8, 2025
f305f99
Merge branch 'main' of https://github.com/exercism/rust into exercism…
oboulant Jul 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion exercises/practice/acronym/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
fn acronym_from_camelcase(word: &str) -> String {
let mut res = String::new();
res.push(word.chars().next().unwrap().to_uppercase().next().unwrap());

println!("res: {}", res);

let remaining_capitals = word[1..].chars()
.filter(|c| c.is_uppercase())
.collect::<String>();

println!("remaining_capitals: {}", remaining_capitals);

if remaining_capitals.chars().count() < word.chars().count() - 1 {
res.push_str(&remaining_capitals);
}


println!("res before return: {}", res);

res

}

pub fn abbreviate(phrase: &str) -> String {
todo!("Given the phrase '{phrase}', return its acronym");

let cleaned_phrase: String = phrase.chars()
.filter(|&c| c.is_alphanumeric() || c == '-' || c.is_whitespace())
.map(|c| if c == '-' {' '} else {c})
.collect();

println!("{}", cleaned_phrase);

cleaned_phrase.split_whitespace()
// .map(|word| word.chars().next().unwrap().to_uppercase().to_string())
.map(acronym_from_camelcase)
.collect::<String>()

// "PNG".to_string()
}
1 change: 1 addition & 0 deletions exercises/practice/affine-cipher/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ edition = "2024"
# The full list of available libraries is here:
# https://github.com/exercism/rust-test-runner/blob/main/local-registry/Cargo.toml
[dependencies]
once_cell = "1.18"
76 changes: 74 additions & 2 deletions exercises/practice/affine-cipher/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
use once_cell::sync::Lazy;

static NB_LETTERS_IN_ALPHABET: Lazy<i32> = Lazy::new(|| {
26
});

/// While the problem description indicates a return status of 1 should be returned on errors,
/// it is much more common to return a `Result`, so we provide an error type for the result here.
#[derive(Debug, Eq, PartialEq)]
Expand All @@ -8,11 +14,77 @@ pub enum AffineCipherError {
/// Encodes the plaintext using the affine cipher with key (`a`, `b`). Note that, rather than
/// returning a return code, the more common convention in Rust is to return a `Result`.
pub fn encode(plaintext: &str, a: i32, b: i32) -> Result<String, AffineCipherError> {
todo!("Encode {plaintext} with the key ({a}, {b})");

match find_mmi(a, *NB_LETTERS_IN_ALPHABET) {
Some(_) => {
let res = plaintext
.chars()
.filter(|c| {
c.is_alphanumeric()
})
.map(|c| {
c.to_ascii_lowercase()
})
.map(|c| {
if c.is_ascii_digit() {
c
}
else {
let i = c as u8 - b'a';
let letter_number: u8 = ((a * i as i32 + b ).rem_euclid(*NB_LETTERS_IN_ALPHABET)) as u8;
(b'a' + letter_number) as char
}
})
.collect::<Vec<char>>()
.chunks(5)
.map(|chunk| {
chunk.iter().collect::<String>()
})
.collect::<Vec<_>>()
.join(" ");

Ok(res)
},
None => {
Err(AffineCipherError::NotCoprime(a))
}
}
}

/// Decodes the ciphertext using the affine cipher with key (`a`, `b`). Note that, rather than
/// returning a return code, the more common convention in Rust is to return a `Result`.
pub fn decode(ciphertext: &str, a: i32, b: i32) -> Result<String, AffineCipherError> {
todo!("Decode {ciphertext} with the key ({a}, {b})");
match find_mmi(a, *NB_LETTERS_IN_ALPHABET) {
Some(mmi) =>
{
let res = ciphertext
.chars()
.filter(|c| {
c.is_alphanumeric()
})
.map(|c| {
if c.is_ascii_digit() {
c
}
else {
let y = c as u8 -b'a';
// needs `rem_euclid()` because % can give negative results
let d: u8 = ((mmi * (y as i32 - b)).rem_euclid(*NB_LETTERS_IN_ALPHABET)) as u8;
(b'a' + d) as char
}
})
.collect::<String>();

Ok(res)
},
None => {
// The MMI only exists if `a` and `NB_LETTERS_IN_ALPHABET` are coprime
Err(AffineCipherError::NotCoprime(a))
}
}
}

fn find_mmi(a: i32, m: i32) -> Option<i32> {
// cannot be greater than `m` (otherwise would be cancel out in the mod)
(0..m).find(|x| (a * *x) % m == 1)
}
34 changes: 33 additions & 1 deletion exercises/practice/all-your-base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,37 @@ pub enum Error {
/// However, your function must be able to process input with leading 0 digits.
///
pub fn convert(number: &[u32], from_base: u32, to_base: u32) -> Result<Vec<u32>, Error> {
todo!("Convert {number:?} from base {from_base} to base {to_base}")

if from_base < 2 {
return Err(Error::InvalidInputBase);
}
else if to_base < 2 {
return Err(Error::InvalidOutputBase);
}

let mut res: Vec<u32> = Vec::new();
let mut num = 0;

for digit in number {
if *digit >= from_base {
return Err(Error::InvalidDigit(*digit));
}
num = num * from_base + digit;
}
println!("num: {}", num);

while num > 0 {
res.push(num % to_base);
num /= to_base;
}

if res.is_empty() {
res.push(0);
return Ok(res);
}

res.reverse();

Ok(res)

}
2 changes: 2 additions & 0 deletions exercises/practice/allergies/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ edition = "2024"
# The full list of available libraries is here:
# https://github.com/exercism/rust-test-runner/blob/main/local-registry/Cargo.toml
[dependencies]
strum = "0.27"
strum_macros = "0.27"
37 changes: 22 additions & 15 deletions exercises/practice/allergies/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
pub struct Allergies;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;

#[derive(Debug, PartialEq, Eq)]
pub struct Allergies {
score: u32
}

#[derive(EnumIter, Debug, PartialEq, Eq, Clone, Copy)]
pub enum Allergen {
Eggs,
Peanuts,
Shellfish,
Strawberries,
Tomatoes,
Chocolate,
Pollen,
Cats,
Eggs = 0b00000001,
Peanuts = 0b00000010,
Shellfish = 0b00000100,
Strawberries = 0b00001000,
Tomatoes = 0b00010000,
Chocolate = 0b00100000,
Pollen = 0b01000000,
Cats = 0b10000000,
}

impl Allergies {
pub fn new(score: u32) -> Self {
todo!("Given the '{score}' score, construct a new Allergies struct.");
Self {
score
}
}

pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
todo!("Determine if the patient is allergic to the '{allergen:?}' allergen.");
self.score & *allergen as u32 != 0
}

pub fn allergies(&self) -> Vec<Allergen> {
todo!(
"Return the list of allergens contained within the score with which the Allergies struct was made."
);
Allergen::iter()
.filter(|a| self.is_allergic_to(a))
.collect()
}
}
1 change: 1 addition & 0 deletions exercises/practice/alphametics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ edition = "2024"
# The full list of available libraries is here:
# https://github.com/exercism/rust-test-runner/blob/main/local-registry/Cargo.toml
[dependencies]
itertools = "0.14.0"
59 changes: 57 additions & 2 deletions exercises/practice/alphametics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,60 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use itertools::Itertools;

pub fn solve(input: &str) -> Option<HashMap<char, u8>> {
todo!("Solve the alphametic {input:?}")
println!("{}", input);

let firsts: HashSet<char> = input
.split(&['+', '='])
.filter_map(|s| s.trim().chars().next())
.collect();

println!("{:?}", firsts);

let (letters, factors) = parse(input);

println!("letter: {:?}, factors: {:?}", letters, factors);

for perm in (0..=9).permutations(letters.len()) {
if cal_perm_sum(&perm, &factors) == 0
&& !perm.iter().enumerate().any(|(i, v)| firsts.contains(letters.get(i).unwrap()) && *v == 0) {
return Some(HashMap::from_iter(
perm.iter().enumerate().map(|(i, v)| (*letters.get(i).unwrap(), *v as u8))
));
}
}

None
}

fn cal_perm_sum(perm: &[i64], factors: &[i64]) -> i64 {
perm.iter().enumerate().map(|(i, v)| *v * factors.get(i).unwrap()).sum()
}

// 26 + 26 + 766 = 1970

fn parse(input: &str) -> (Vec<char>, Vec<i64>) {
let mut factors = HashMap::new();
let mut pos = 0;
let mut sign = -1;

for c in input.chars().filter(|c| !c.is_whitespace()).rev() {
match c {
'=' => {
sign = 1;
pos = 0;
},
'+' => {
pos = 0;
},
_ => {
let factor = factors.entry(c).or_insert(0);
*factor += sign * 10_i64.pow(pos);
pos += 1;
}
}
}

factors.iter().unzip()

}
41 changes: 39 additions & 2 deletions exercises/practice/anagram/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
use std::collections::HashSet;

pub fn anagrams_for<'a>(word: &str, possible_anagrams: &[&str]) -> HashSet<&'a str> {
todo!("For the '{word}' word find anagrams among the following words: {possible_anagrams:?}");
fn is_anagram(word :&str, candidate :&str) -> bool {
if word.eq_ignore_ascii_case(candidate) {
return false;
}

// anagrams have same length
if word.len() != candidate.len() {
return false;
}

let mut word_c :Vec<char> = word.chars().collect();
let mut candidate_c :Vec<char> = candidate.chars().collect();

word_c.sort_unstable();
candidate_c.sort_unstable();

word_c == candidate_c

}

pub fn anagrams_for<'a>(word: &str, possible_anagrams: &[&'a str]) -> HashSet<&'a str> {
println!("{}", is_anagram(word, possible_anagrams[0]));

// let mut res = HashSet::new();

let binding = word.to_lowercase();
let word_lower: &str = binding.as_str();

// for possible_anagram in possible_anagrams {
// if is_anagram(word_lower, possible_anagram.to_lowercase().as_str()) {
// res.insert(*possible_anagram);
// }
// }

possible_anagrams.iter().filter(|x| {
is_anagram(word_lower, x.to_lowercase().as_str())
}).map(|x| *x).collect::<HashSet<&str>>()

// res
}
5 changes: 4 additions & 1 deletion exercises/practice/armstrong-numbers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
pub fn is_armstrong_number(num: u32) -> bool {
todo!("true if {num} is an armstrong number")
let nb_digit = num.to_string().chars().count();
num.to_string().chars()
.map(|c| c.to_digit(10).unwrap().pow(nb_digit as u32))
.sum::<u32>() == num
}
1 change: 1 addition & 0 deletions exercises/practice/atbash-cipher/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ edition = "2024"
# The full list of available libraries is here:
# https://github.com/exercism/rust-test-runner/blob/main/local-registry/Cargo.toml
[dependencies]
once_cell = "1.18"
Loading