docs: def flattenTrees(root, nodeSelector: Callable[[LNode], bool]):
      """
      Walk all nodes and discover trees of nodes (usually operators)
      and reduce them to single node with multiple outputs
  
      :attention: selected nodes has to have single output
                  and has to be connected to nets with single driver
      """
      for ch in root.children:
          if ch.children:
              flattenTrees(ch, nodeSelector)
  
      # collect all nodes which can be potentialy reduced
      reducibleChildren = set()
      for ch in root.children:
          if nodeSelector(ch):
              reducibleChildren.add(ch)
  
      while reducibleChildren:
          # try to pick a node from random tree and search it's root
          _treeRoot = reducibleChildren.pop()
          reducibleChildren.add(_treeRoot)
          # we need to keep order of inputs, use preorder
          treeRoot = searchRootOfTree(reducibleChildren, _treeRoot)
  
          reducedNodes, inputEdges = collectNodesInTree(treeRoot, reducibleChildren)
          # if tree is big enoguh for reduction, reduce it to single node
          if len(reducedNodes) > 1:
              newName = reducedNodes[0].name
              newNode = root.addNode(newName)
  
              o = newNode.addPort("", PortType.OUTPUT, PortSide.EAST)
  
              oEdges = treeRoot.east[0].outgoingEdges
              for outputedge in list(oEdges):
                  dsts = list(outputedge.dsts)
                  assert len(dsts) > 0
                  outputedge.remove()
                  root.addHyperEdge([o, ], dsts, originObj=outputedge.originObj)
  
              for i, (iN, iP, iE) in enumerate(inputEdges):
                  name = None
                  index = len(inputEdges) - i - 1
                  if hasattr(iE.originObj, "_dtype"):
                      w = iE.originObj._dtype.bit_length()
                      if w > 1:
                          name = "[%d:%d]" % ((index + 1) * w, index * w)
                      else:
                          name = None
  
                  if name is None:
                      name = "[%d]" % (index)
  
                  inp = newNode.addPort(name,
                                        PortType.INPUT, PortSide.WEST)
                  iE.removeTarget(iP)
                  iE.addTarget(inp)
  
              for n in reducedNodes:
                  root.children.remove(n)
                  reducibleChildren.remove(n)
          else:
              reducibleChildren.remove(reducedNodes[0])