code: private Word<I> prepareSplit(AbstractBaseDTNode<I, D> node, Splitter<I, D> splitter) {
          int symbolIdx = splitter.symbolIdx;
          I symbol = alphabet.getSymbol(symbolIdx);
          Word<I> discriminator = splitter.getDiscriminator().prepend(symbol);
  
          Deque<AbstractBaseDTNode<I, D>> dfsStack = new ArrayDeque<>();
  
          AbstractBaseDTNode<I, D> succSeparator = splitter.succSeparator;
  
          dfsStack.push(node);
          assert node.getSplitData() == null;
  
          while (!dfsStack.isEmpty()) {
              AbstractBaseDTNode<I, D> curr = dfsStack.pop();
              assert curr.getSplitData() == null;
  
              curr.setSplitData(new SplitData<>(IncomingList::new));
  
              for (TTTTransition<I, D> trans : curr.getIncoming()) {
                  D outcome = query(trans, discriminator);
                  curr.getSplitData().getIncoming(outcome).insertIncoming(trans);
                  markAndPropagate(curr, outcome);
              }
  
              if (curr.isInner()) {
                  for (AbstractBaseDTNode<I, D> child : curr.getChildren()) {
                      dfsStack.push(child);
                  }
              } else {
                  TTTState<I, D> state = curr.getData();
                  assert state != null;
  
                  TTTTransition<I, D> trans = state.getTransition(symbolIdx);
                  D outcome = predictSuccOutcome(trans, succSeparator);
                  assert outcome != null;
                  curr.getSplitData().setStateLabel(outcome);
                  markAndPropagate(curr, outcome);
              }
  
          }
  
          return discriminator;
      }
docs: Prepare a split operation on a block, by marking all the nodes and transitions in the subtree (and annotating
  them with {@link SplitData} objects).
  
  @param node
  the block root to be split
  @param splitter
  the splitter to use for splitting the block
  
  @return the discriminator to use for splitting