1116. Print Zero Even Odd

You have a function printNumber that can be called with an integer parameter and prints it to the console.

  • For example, calling printNumber(7) prints 7 to the console.

You are given an instance of the class ZeroEvenOdd that has three functions: zero, even, and odd. The same instance of ZeroEvenOdd will be passed to three different threads:

  • Thread A: calls zero() that should only output 0's.
  • Thread B: calls even() that should only output even numbers.
  • Thread C: calls odd() that should only output odd numbers.

Modify the given class to output the series "010203040506..." where the length of the series must be 2n.

Implement the ZeroEvenOdd class:

  • ZeroEvenOdd(int n) Initializes the object with the number n that represents the numbers that should be printed.
  • void zero(printNumber) Calls printNumber to output one zero.
  • void even(printNumber) Calls printNumber to output one even number.
  • void odd(printNumber) Calls printNumber to output one odd number.

 

Example 1:

Input: n = 2
Output: "0102"
Explanation: There are three threads being fired asynchronously.
One of them calls zero(), the other calls even(), and the last one calls odd().
"0102" is the correct output.

Example 2:

Input: n = 5
Output: "0102030405"

 

Constraints:

  • 1 <= n <= 1000

Rust Solution

use std::sync::Condvar;
use std::sync::Mutex;

struct ZeroEvenOdd {
    state: (Mutex<i32>, Condvar),
    n: i32,
}

impl ZeroEvenOdd {
    fn new(n: i32) -> Self {
        let state = (Mutex::new(0), Condvar::new());
        ZeroEvenOdd { state, n }
    }

    fn zero(&self, print_number: impl Fn(i32)) {
        self.job(true, print_number, |state| {
            !(*state == 2 * self.n || *state % 2 == 0)
        });
    }

    fn odd(&self, print_number: impl Fn(i32)) {
        self.job(false, print_number, |state| {
            !(*state == 2 * self.n || *state % 2 == 1 && *state % 4 == 1)
        });
    }

    fn even(&self, print_number: impl Fn(i32)) {
        self.job(false, print_number, |state| {
            !(*state == 2 * self.n || *state % 2 == 1 && *state % 4 == 3)
        });
    }

    fn job(
        &self,
        zero: bool,
        print_number: impl Fn(i32),
        condition: impl FnMut(&mut i32) -> bool + Copy,
    ) {
        loop {
            let (lock, cvar) = &self.state;
            let mut g = cvar.wait_while(lock.lock().unwrap(), condition).unwrap();
            if *g == 2 * self.n {
                break;
            }
            print_number(if zero { 0 } else { (*g + 1) / 2 });
            *g += 1;
            cvar.notify_all();
        }
    }
}

#[test]
fn test() {
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use threadpool::ThreadPool;

    let zero_even_odd = Arc::new(ZeroEvenOdd::new(5));
    let (tx, rx) = channel();
    let pool = ThreadPool::new(4);

    for i in 0..3 {
        let zero_even_odd = zero_even_odd.clone();
        let tx = tx.clone();
        pool.execute(move || match i {
            0 => zero_even_odd.zero(|x| tx.send(x.to_string()).unwrap()),
            1 => zero_even_odd.even(|x| tx.send(x.to_string()).unwrap()),
            2 => zero_even_odd.odd(|x| tx.send(x.to_string()).unwrap()),
            _ => panic!(),
        });
    }
    pool.join();
    let nums: Vec<String> = rx.try_iter().collect();
    let res = nums.join("");
    let ans = "0102030405".to_string();
    assert_eq!(res, ans);
}

Having problems with this solution? Click here to submit an issue on github.