"""
The purpose of this file is to demonstrate how one might use a condition variable to obtain mutual exclusion and waiting for a condition.
"""

from threading import Thread, Condition

class CollatzEven(Thread):
  """The even number case of the https://en.wikipedia.org/wiki/Collatz_conjecture
  n → n÷2 if n is even and greater than 1"""
  def __init__(self, mutable, cv):
    Thread.__init__(self)
    self.value = mutable
    self.steps = 0
    self.cv = cv
  def run(self):
    with self.cv:
      while True:
        while (self.value[0] > 1) and (self.value[0] % 2):
          self.cv.wait()
        if self.value[0] <= 1: break
        print(self.value[0], end=' → ')
        self.value[0] //= 2
        print(self.value[0])
        self.steps += 1
        self.cv.notify()

class CollatzOdd(Thread):
  """The odd number case of the https://en.wikipedia.org/wiki/Collatz_conjecture
  n → 3n+1 if n is odd and greater than 1"""
  def __init__(self, mutable, cv):
    Thread.__init__(self)
    self.value = mutable
    self.steps = 0
    self.cv = cv
  def run(self):
    with self.cv:
      while True:
        while (self.value[0] > 1) and not (self.value[0] % 2):
          self.cv.wait()
        if self.value[0] <= 1: break
        print(self.value[0], end=' → ')
        self.value[0] *= 3
        self.value[0] += 1
        print(self.value[0])
        self.steps += 1
        self.cv.notify() 

cv = Condition()
start = 340
value = [start] # in a list because lists are mutable
ts = [ CollatzEven(value, cv), CollatzOdd(value, cv) ]
for t in ts: t.start()
for t in ts: t.join()
print(f'Collatz({start}) resulted in', value[0], 'after',ts[0].steps,'even-number steps and',ts[1].steps,'odd-number steps')