๐Ÿฆ€ Functional Rust

071: GCD and LCM

Difficulty: Beginner Category: Mathematics Concept: Greatest Common Divisor (Euclidean algorithm) and Least Common Multiple Key Insight: GCD is naturally recursive; LCM = a*b/gcd(a,b). Both languages express this identically.
// 071: GCD and LCM

// Approach 1: Recursive GCD (Euclidean)
fn gcd(a: i64, b: i64) -> i64 {
    if b == 0 { a.abs() } else { gcd(b, a % b) }
}

fn lcm(a: i64, b: i64) -> i64 {
    if a == 0 || b == 0 { 0 } else { (a * b).abs() / gcd(a, b) }
}

// Approach 2: Iterative GCD
fn gcd_iter(mut a: i64, mut b: i64) -> i64 {
    a = a.abs();
    b = b.abs();
    while b != 0 {
        let t = b;
        b = a % b;
        a = t;
    }
    a
}

// GCD/LCM of a slice
fn gcd_list(v: &[i64]) -> i64 {
    v.iter().copied().reduce(gcd).unwrap_or(0)
}

fn lcm_list(v: &[i64]) -> i64 {
    v.iter().copied().reduce(lcm).unwrap_or(1)
}

// Approach 3: Extended GCD
fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) {
    if b == 0 {
        (a, 1, 0)
    } else {
        let (g, x, y) = extended_gcd(b, a % b);
        (g, y, x - (a / b) * y)
    }
}

fn main() {
    println!("gcd(12, 8) = {}", gcd(12, 8));
    println!("lcm(4, 6) = {}", lcm(4, 6));
    println!("gcd_list([12,18,24]) = {}", gcd_list(&[12, 18, 24]));
    println!("lcm_list([4,6,8]) = {}", lcm_list(&[4, 6, 8]));
    let (g, x, y) = extended_gcd(35, 15);
    println!("extended_gcd(35,15) = ({}, {}, {})", g, x, y);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_gcd() {
        assert_eq!(gcd(12, 8), 4);
        assert_eq!(gcd(17, 5), 1);
        assert_eq!(gcd(0, 5), 5);
        assert_eq!(gcd(100, 0), 100);
    }

    #[test]
    fn test_lcm() {
        assert_eq!(lcm(4, 6), 12);
        assert_eq!(lcm(3, 5), 15);
        assert_eq!(lcm(0, 5), 0);
    }

    #[test]
    fn test_gcd_iter() {
        assert_eq!(gcd_iter(12, 8), 4);
        assert_eq!(gcd_iter(-12, 8), 4);
    }

    #[test]
    fn test_list_ops() {
        assert_eq!(gcd_list(&[12, 18, 24]), 6);
        assert_eq!(lcm_list(&[4, 6, 8]), 24);
    }

    #[test]
    fn test_extended_gcd() {
        let (g, x, y) = extended_gcd(35, 15);
        assert_eq!(g, 5);
        assert_eq!(35 * x + 15 * y, 5);
    }
}
(* 071: GCD and LCM *)

(* Approach 1: Recursive GCD *)
let rec gcd a b =
  if b = 0 then abs a
  else gcd b (a mod b)

let lcm a b =
  if a = 0 || b = 0 then 0
  else abs (a * b) / gcd a b

(* Approach 2: GCD of a list *)
let gcd_list = function
  | [] -> 0
  | x :: xs -> List.fold_left gcd x xs

let lcm_list = function
  | [] -> 1
  | x :: xs -> List.fold_left lcm x xs

(* Approach 3: Extended GCD โ€” returns (g, x, y) where ax + by = g *)
let rec extended_gcd a b =
  if b = 0 then (a, 1, 0)
  else
    let g, x, y = extended_gcd b (a mod b) in
    (g, y, x - (a / b) * y)

(* Tests *)
let () =
  assert (gcd 12 8 = 4);
  assert (gcd 17 5 = 1);
  assert (gcd 0 5 = 5);
  assert (gcd 100 0 = 100);
  assert (lcm 4 6 = 12);
  assert (lcm 3 5 = 15);
  assert (lcm 0 5 = 0);
  assert (gcd_list [12; 18; 24] = 6);
  assert (lcm_list [4; 6; 8] = 24);
  let (g, x, y) = extended_gcd 35 15 in
  assert (g = 5);
  assert (35 * x + 15 * y = 5);
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Core Insight

The Euclidean algorithm: `gcd(a, b) = gcd(b, a mod b)` with base case `gcd(a, 0) = a`. LCM derives from GCD. The recursive structure is identical in both languages.

OCaml Approach

  • Recursive function with pattern matching
  • Tail-recursive naturally (last call is recursive)
  • `abs` for handling negative inputs

Rust Approach

  • Recursive or iterative (loop with swap)
  • No guaranteed TCO but small depth for GCD
  • Can use `.abs()` method on integers

Comparison Table

FeatureOCamlRust
GCD`let rec gcd a b = ...``fn gcd(a, b) -> ...`
Pattern`match b with 0 -> a``if b == 0 { a }`
LCM`a b / gcd a b``a b / gcd(a, b)`
OverflowNo (arbitrary precision with Zarith)Possible with i32/i64