from typing import Set
from typing import Tuple


def countActive3DCubesAfterXCycles(activeSet: Set[Tuple[int, int, int]], cycles: int) -> int:
   """
   Runs X cycles of the process according to the below rules, and outputs num active 3D cubes:
   - If a cube is active and exactly 2 or 3 of its neighbors are also active, the cube remains active.
     Otherwise, the cube becomes inactive.
   - If a cube is inactive but exactly 3 of its neighbors are active, the cube becomes active. Otherwise,
     the cube remains inactive.
   """
   for _ in range(cycles):
      newSet: Set[Tuple[int, int, int]] = activeSet.copy()
      cubesToCheck: Set[Tuple[int, int, int]] = set()
      
      for (x, y, z) in activeSet:
         cubesToCheck.add( (x, y, z) )
         
         for dx in range(-1, 2, 1):
            for dy in range(-1, 2, 1):
               for dz in range(-1, 2, 1):
                  cubesToCheck.add( (x + dx, y + dy, z + dz) )
         
      for (x, y, z) in cubesToCheck:
         numActiveCubes: int = 0
         
         for dx in range(-1, 2, 1):
            for dy in range(-1, 2, 1):
               for dz in range(-1, 2, 1):
                  if dx != 0 or dy != 0 or dz != 0:
                     newX, newY, newZ = x + dx, y + dy, z + dz

                     if (newX, newY, newZ) in activeSet:
                        numActiveCubes += 1
         
         if (x, y, z) in activeSet and numActiveCubes in {2, 3}:
            newSet.add( (x, y, z) )
         
         if (x, y, z) in activeSet and numActiveCubes not in {2, 3}:
            newSet.remove( (x, y, z) )
         
         if (x, y, z) not in activeSet and numActiveCubes == 3:
            newSet.add( (x, y, z) )
      
      activeSet = newSet
   
   return len(activeSet)