code: public LCAInfo<S, I, O> findLCA(final ADTNode<S, I, O> s1, final ADTNode<S, I, O> s2) {
  
          final Map<ADTNode<S, I, O>, ADTNode<S, I, O>> s1ParentsToS1 = new HashMap<>();
  
          ADTNode<S, I, O> s1Iter = s1;
          ADTNode<S, I, O> s2Iter = s2;
  
          while (s1Iter.getParent() != null) {
              s1ParentsToS1.put(s1Iter.getParent(), s1Iter);
              s1Iter = s1Iter.getParent();
          }
  
          final Set<ADTNode<S, I, O>> s1Parents = s1ParentsToS1.keySet();
  
          while (s2Iter.getParent() != null) {
  
              if (s1Parents.contains(s2Iter.getParent())) {
                  if (!ADTUtil.isSymbolNode(s2Iter.getParent())) {
                      throw new IllegalStateException("Only Symbol Nodes should be LCAs");
                  }
  
                  final ADTNode<S, I, O> lca = s2Iter.getParent();
                  final O s1Out = ADTUtil.getOutputForSuccessor(lca, s1ParentsToS1.get(lca));
                  final O s2Out = ADTUtil.getOutputForSuccessor(lca, s2Iter);
  
                  return new LCAInfo<>(lca, s1Out, s2Out);
              }
  
              s2Iter = s2Iter.getParent();
          }
  
          throw new IllegalStateException("Nodes do not share a parent node");
      }
docs: Return the lowest common ancestor for the given two nodes.
  
  @param s1
  first node
  @param s2
  second node
  
  @return A {@link LCAInfo} containing the lowest common {@link ADTNode}, the output determining the subtree of the
  first node and the output determining the subtree of the second node