dsa_rust/tgg/
tgg_05.rs

1// This module didn't make the cut: Suppres unused code warnings
2#![allow(dead_code)]
3
4// Ch 5: Recursion
5//////////////////
6
7// This factorial function is defined with u32 which a) cannot take
8// negative values by default and b) has a max return of
9// 4,294,967,295, or 12! because 13! is 6,227,020,800.
10// If you bump it up to u128 you can calculate up to 34!.
11/** Computs a n! up to 12 in O(n) time */
12pub fn factorial_0(mut n: u32) -> u32 {
13    if n <= 1 {
14        return n;
15    } else {
16        let nex = factorial_0(n - 1);
17        n *= nex;
18    };
19    n
20}
21// Translated from the book's Java example in O(n) time
22// Note that this version uses a signed integer primitive so the function
23// also includes a bounds check for values < 0.
24// Clippy wants a match statement tho
25//pub fn factorial_1(n: i32) -> i32 {
26//    //use std::cmp::Ordering;
27//    //match n.cmp(&0) {
28//    //    Ordering::Equal => return 1,
29//    //    Ordering::Less => {
30//    //        println!("Error: Cannot compute factorials for n < 0");
31//    //        return n;
32//    //    },
33//    //    _ => return n * factorial_1(n - 1),
34//    //}
35//    if n < 0 {
36//        println!("Error: Cannot compute factorials for n < 0");
37//        return n;
38//    } else if n == 0 {
39//        return 1;
40//    } else {
41//        return n * factorial_1(n - 1);
42//    }
43//}
44// After analyzing the book's example (cheating)
45/** Recursive implementation of a factorial calculator up to 12! in O(n) time */
46pub fn factorial_2(n: u32) -> u32 {
47    if n <= 1 {
48        return n;
49    }
50    n * factorial_2(n - 1)
51}
52// Refactoring the recursive function for an iterative one
53/** Iterative implementation of a factorial calculator up to 12! in O(n) time */
54pub fn factorial_3(mut n: u32) -> u32 {
55    let mut fac = n;
56    while n > 1 {
57        fac *= n - 1;
58        n -= 1;
59    }
60    fac
61}
62// Reimplementation as an iterative function for a more logical evaluation (cheat)
63/** Iterative implementation of a factorial calculator up to 12! in O(n) time */
64pub fn factorial_4(n: u32) -> u32 {
65    let mut fac = 1;
66    for e in 2..=n {
67        fac *= e
68    }
69    fac
70}
71
72/** Recursive implementation of a binary search in O(log n) time.
73 * Returns the index of the target within a given array, if present.
74 * Otherwise the function returns -1. */
75pub fn bin_search_0(a: &Vec<i32>, t: i32, left: i32, right: i32) -> i32 {
76    use std::cmp::Ordering;
77
78    // Recursive base case
79    if left > right {
80        -1
81    } else {
82        let mid = (left + right) / 2;
83        //if t == a[mid as usize] {
84        //    return mid as i32;
85        //} else if t < a[mid as usize] {
86        //    return bin_search_0(&a, t, left, mid - 1);
87        //} else {
88        //    return bin_search_0(&a, t, mid + 1, right);
89        //}
90        // clippy wants match block
91        match t.cmp(&a[mid as usize]) {
92            Ordering::Equal => mid,
93            Ordering::Less => bin_search_0(a, t, left, mid - 1),
94            Ordering::Greater => bin_search_0(a, t, mid + 1, right),
95        }
96    }
97}
98pub fn bin_search_0_wrapper(a: &Vec<i32>, target: i32) -> i32 {
99    let right = a.clone().len() as i32 - 1;
100    bin_search_0(a, target, 0, right)
101}
102// The recursive binary search algorithm represents tail recursion.
103// Even though Rust (likely) reimplements this automatically this is done
104// manually as a fun exercise.
105/** Iterative reimplementation of a recursive binary search algorithm in O(log n) time.
106 * This algorithm takes a referenced vec and returns the index of the target, if it exists.
107 * Otherwise it returns -1 to indicate that the target is not in the vec. */
108pub fn bin_search_1(data: &[i32], target: i32) -> i32 {
109    let mut low = 0;
110    let mut high = data.len() - 1;
111    while low <= high {
112        let mid: usize = (low + high) / 2;
113        //if target == data[mid] {
114        //    return mid as i32;
115        //} else if target < data[mid] {
116        //    high = mid - 1
117        //} else {
118        //    low = mid + 1
119        //}
120        // clippy wants a match block tho
121        use std::cmp::Ordering;
122        match target.cmp(&data[mid]) {
123            Ordering::Equal => return mid as i32,
124            Ordering::Less => high = mid - 1,
125            Ordering::Greater => low = mid + 1,
126        }
127    }
128    -1
129}
130#[test]
131pub fn bin_search_test() {
132    let v = vec![12, 26, 31, 48, 52, 61, 75, 80, 93];
133    assert_eq!(2, bin_search_0_wrapper(&v, 31));
134    assert_eq!(6, bin_search_1(&v, 75));
135}
136
137// Initially it appears this algorithm runs in O(n^2) time, but it actually
138// runs in O(n) time because it touches (and performs O(1) operations) on
139// n nodes in the tree exactly once. This algorithm represents multiple recursion
140// because for each invocation there are x number of directory nodes to sum.
141/** Walks a directory tree printing out names and sizes in O(n) time */
142use std::path::Path;
143pub fn disk_usage(root: &Path) -> u64 {
144    let mut dir_size = 0;
145    if root.is_dir() {
146        for e in root.read_dir().expect("read_dir call failed") {
147            let entry = e.expect("failure to deconstruct value");
148            dir_size += disk_usage(&entry.path());
149            //if let Ok(entry) = e {
150            //    dir_size += disk_usage(&entry.path());
151            //}
152        }
153        let this_dir = std::fs::metadata(root)
154            .expect("metadata call failed [0]")
155            .len();
156        //dir_size += this_dir;
157        println!("d {:>7}B  {}", dir_size + this_dir, root.display());
158    } else if root.is_file() {
159        let size = std::fs::metadata(root)
160            .expect("metadata call failed [1]")
161            .len();
162        println!("  {:>7}B  {}", size, root.display());
163        return size;
164    }
165    dir_size
166}
167
168// Sum of array of integers to n indexes in O(n) time using linear recursion
169// Iterative implementation (so easy, so intuitive)
170pub fn array_sum_0(v: &[i32]) -> i32 {
171    let mut sum = 0;
172    //for e in 0..v.len() {
173    //    sum += v[e]
174    //}
175    for (i, _) in v.iter().enumerate() {
176        sum += v[i]
177    }
178    sum
179}
180// Reimplementation using an iterator instead of a range
181pub fn array_sum_1(v: Vec<i32>) -> i32 {
182    let mut sum = 0;
183    for e in v.iter() {
184        sum += v[*e as usize]
185    }
186    sum
187}
188// Recursive implementation (is dumb)
189pub fn array_sum_2(data: &Vec<i32>, n: usize) -> i32 {
190    if n == 0 {
191        data[0]
192    } else {
193        array_sum_2(data, n - 1) + data[n]
194    }
195}
196// Sum of an array of integers to n in O(n) time using O(log n) space with binary recursion
197pub fn array_sum_4(data: Vec<i32>) -> i32 {
198    let h = data.clone().len() - 1;
199    fn array_sum_3(data: Vec<i32>, low: usize, high: usize) -> i32 {
200        //if low > high {
201        //    0
202        //} else if low == high {
203        //    return data[low];
204        //} else {
205        //    let mid = (low + high) / 2;
206        //    return array_sum_3(data.clone(), low, mid) + array_sum_3(data, mid + 1, high);
207        //}
208        // clippy wants a match block tho
209        use std::cmp::Ordering;
210        match low.cmp(&high) {
211            Ordering::Equal => data[low],
212            Ordering::Greater => 0,
213            Ordering::Less => {
214                let mid = (low + high) / 2;
215                array_sum_3(data.clone(), low, mid) + array_sum_3(data, mid + 1, high)
216            }
217        }
218    }
219    array_sum_3(data, 0, h)
220}
221// Represents a public interface for the unsightly recursive implementation in array_sum_3()
222//pub fn array_sum_4(data: Vec<i32>) -> i32 {
223//    let h = data.clone().len() - 1;
224//    return array_sum_3(data, 0, h)
225//}
226#[test]
227pub fn array_sum_test() {
228    // Iterative impelmentation test
229    let t = [6, 7, 8, 9];
230    assert_eq!(30, array_sum_0(&t));
231    // Recursive implementation test
232    // Dumb, error prone implementation
233    let t = vec![6, 7, 8, 9];
234    assert_eq!(30, array_sum_2(&t, 3));
235
236    // Binary recursive implementation test
237    let t = vec![6, 7, 8, 9];
238    assert_eq!(30, array_sum_4(t));
239}
240
241// Iteratively reverses the elements of an array
242/** Iteratively reverses the elements of an array in O(n) time */
243pub fn array_reversal_0(mut v: Vec<i32>) -> Vec<i32> {
244    let mut high = v.len() - 1; // Match the index values, not # of elements
245    let mut low = 0;
246    let mut temp;
247    println!("Iterative approach\nOriginal: {v:?}");
248    while high > low {
249        temp = v[low];
250        v[low] = v[high];
251        v[high] = temp;
252        high -= 1;
253        low += 1;
254    }
255    println!("Reversed: {v:?}");
256    v
257}
258// Linear recursion to reverse the elements of an array in place
259// (with liberal type conversion)
260pub fn array_reversal_1(v: &mut Vec<i32>, low: i32, high: i32) -> &mut Vec<i32> {
261    if low < high {
262        //let temp = v[low as usize];
263        //v[low as usize] = v[high as usize];
264        //v[high as usize] = temp;
265        v.swap(low as usize, high as usize);
266        array_reversal_1(v, low + 1, high - 1);
267    }
268    v
269}
270// Recursive reimplementation but everything is usize
271pub fn array_reversal_2(v: &mut Vec<usize>, low: usize, high: usize) -> &mut Vec<usize> {
272    if low < high {
273        //let temp = v[low];
274        //v[low] = v[high];
275        //v[high] = temp;
276        v.swap(low, high);
277        array_reversal_2(v, low + 1, high - 1);
278    }
279    v
280}
281#[test]
282pub fn array_reversal_test() {
283    // Tests the iterative approach
284    let v = vec![1, 2, 3, 4, 5, 6, 7, 8];
285    let rev = vec![8, 7, 6, 5, 4, 3, 2, 1];
286    assert_eq!(array_reversal_0(v), rev);
287
288    // Tests the dumb recursive approach
289    let mut v = vec![11, 22, 33, 44, 55, 66, 77, 88];
290    let rev = vec![88, 77, 66, 55, 44, 33, 22, 11];
291    let high = v.len() as i32 - 1;
292    array_reversal_1(&mut v, 0, high);
293    assert_eq!(v, rev)
294}
295
296// Computing powers
297// First attempt uses iteration
298pub fn powers_0(x: u32, n: u32) -> u32 {
299    let mut product = 1;
300    for _ in 1..=n {
301        product *= x;
302    }
303    product
304}
305// My attempt at linear recursion used to compute powers
306pub fn powers_1(x: u32, mut product: u32, n: u32) -> u32 {
307    if n > 1 {
308        product *= x;
309        return powers_1(x, product, n - 1);
310    }
311    product
312}
313// Transling the book's Java example that runs in O(n) time
314pub fn powers_2(x: u32, n: u32) -> u32 {
315    if n > 1 {
316        return x * powers_2(x, n - 1);
317    }
318    x
319}
320// Reimplementation using binary recursion that computes powers in O(log n) time
321// Relies on truncation in integer division; if n is even the result is x^n, and
322// if n is even the result is x^(n-1) * x
323pub fn powers_3(x: u32, n: u32) -> u32 {
324    if n == 0 {
325        1
326    } else {
327        let partial = powers_3(x, n / 2); // Relies on truncation
328        let mut result = partial * partial;
329        if n % 2 == 1 {
330            result *= x
331        }
332        result
333    }
334}
335#[test]
336pub fn powers_test() {
337    assert_eq!(128, powers_0(2, 7));
338    assert_eq!(0, powers_0(0, 7));
339    assert_eq!(1, powers_0(1, 7));
340    assert_eq!(128, powers_1(2, 2, 7));
341    assert_eq!(0, powers_1(0, 0, 7));
342    assert_eq!(1, powers_1(1, 1, 7));
343    assert_eq!(256, powers_3(2, 8));
344    assert_eq!(128, powers_3(2, 7));
345    assert_eq!(0, powers_3(0, 7));
346    assert_eq!(1, powers_3(1, 7));
347    assert_eq!(19683, powers_3(3, 9));
348}
349
350// Another stab at Fibonacci sequence
351pub fn fib_0(n: i32) -> Vec<i32> {
352    let mut seq = Vec::new();
353    let mut first = 0;
354    let mut next = 1;
355    let mut this: i32;
356    seq.push(first);
357    seq.push(next);
358    for _ in 2..n {
359        this = first + next;
360        seq.push(this);
361        first = next;
362        next = this;
363    }
364    seq
365}
366
367// EXTRA CREDIT
368///////////////
369
370// The internet's version of the recursive solution
371/** A recursive implementation of the Tower of Hanoi solution that
372 * runs in O(2^n) time. This algorithm works by breaking the
373 * problem set into source, auxiliary, and destination pegs. */
374pub fn tower_of_hanoi(n: u32, src: char, dest: char, aux: char) {
375    if n == 1 {
376        println!("Move disk 1 from peg {src} to peg {dest}");
377        return;
378    }
379    tower_of_hanoi(n - 1, src, aux, dest);
380    println!("Move disk {n} from peg {src} to peg {dest}"); // Trace
381    tower_of_hanoi(n - 1, aux, dest, src);
382}