Skip to content

Instantly share code, notes, and snippets.

@NolwennD
Created February 9, 2025 16:54
Show Gist options
  • Save NolwennD/8e29edc2c829d2f038a109b468308704 to your computer and use it in GitHub Desktop.
Save NolwennD/8e29edc2c829d2f038a109b468308704 to your computer and use it in GitHub Desktop.
FizzBuzz branchless in rust
[package]
name = "kata-rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
spectral = "0.6.0"
struct Cycle<T> {
period: usize,
pattern: Vec<T>,
}
impl<T> Cycle<T> {
fn of_pattern(pattern: Vec<T>) -> Self {
Self {
period: pattern.len(),
pattern,
}
}
fn at(&self, index: usize) -> &T {
self.pattern.get(index % self.period()).unwrap()
}
fn zip<U, R>(&self, other: Cycle<U>, combiner: &dyn Fn(&T, &U) -> R) -> Cycle<R> {
let new_pattern = (0..self.period() * other.period())
.map(|index| combiner(self.at(index), other.at(index)))
.collect();
Cycle::of_pattern(new_pattern)
}
fn period(&self) -> usize {
self.period
}
}
fn fizz_buzz(value: usize) -> String {
let fizz_cycle = Cycle::of_pattern(vec!["", "", "Fizz"]);
let buzz_cycle = Cycle::of_pattern(vec!["", "", "", "", "Buzz"]);
let combiner =
|fizz: &&str, buzz: &&str| Some(format!("{fizz}{buzz}")).filter(|r| !r.is_empty());
let fizz_buzz_cycle = fizz_cycle.zip(buzz_cycle, &combiner);
fizz_buzz_cycle
.at(value - 1)
.clone()
.unwrap_or(format!("{value}"))
}
#[cfg(test)]
mod tests {
use spectral::assert_that;
use super::*;
#[test]
fn should_get_value_inside_pattern() {
let pattern = vec![1, 2, 3];
assert_that(&Cycle::of_pattern(pattern).at(1)).is_equal_to(&2);
}
#[test]
fn should_get_value_outside_pattern() {
let pattern = vec![1, 2, 3];
assert_that(&Cycle::of_pattern(pattern).at(3)).is_equal_to(&1);
}
#[test]
fn should_get_value_outside_pattern_after_zip() {
let first = vec![1, 2, 3];
let second = vec![4, 5];
//[1, 2, 3][1, 2, 3]
//[4, 5][4, 5][4, 5]
let combiner = |a: &i32, b: &i32| a + b;
assert_that(
&Cycle::of_pattern(first)
.zip(Cycle::of_pattern(second), &combiner)
.at(6),
)
.is_equal_to(&5);
}
#[quickcheck_macros::quickcheck]
fn multiples_of_three_start_with_fizz(multiple_of_three: MultipleOfThree) -> bool {
fizz_buzz(multiple_of_three.0).starts_with("Fizz")
}
#[test]
fn fizz_buzz_of_regular_number_is_itself() {
let values = vec![(1, "1"), (2, "2"), (77, "77")];
for value in values {
assert_that!(fizz_buzz(value.0)).is_equal_to(value.1.to_string());
}
}
#[derive(Debug, Clone)]
struct MultipleOfThree(usize);
impl quickcheck::Arbitrary for MultipleOfThree {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let values = (1..=33).map(|i| i * 3).collect::<Vec<usize>>();
g.choose(&values)
.map(|value| MultipleOfThree(*value))
.unwrap()
}
}
#[quickcheck_macros::quickcheck]
fn multiples_of_five_end_with_buzz(multiple_of_five: MultipleOfFive) -> bool {
fizz_buzz(multiple_of_five.0).ends_with("Buzz")
}
#[derive(Debug, Clone)]
struct MultipleOfFive(usize);
impl quickcheck::Arbitrary for MultipleOfFive {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let values = (1..=20).map(|i| i * 5).collect::<Vec<usize>>();
g.choose(&values)
.map(|value| MultipleOfFive(*value))
.unwrap()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment