Python program:
from collections import deque
from sys import maxsize as INT_MAX
MAX = 100001
class Node :
    def __init__ ( self , data ) :
        self . data = data
        self . left = None
        self . right = None
level = [ 0 ] * MAX
def findLevels ( root : Node ) :
    global level
    if root is None :
        return
    q = deque ( )
    q . append ( ( root , 0 ) )
    while q :
        p = q [ 0 ]
        q . popleft ( )
        level [ p [ 0 ] . data ] = p [ 1 ]
        if p [ 0 ] . left :
            q . append ( ( p [ 0 ] . left , p [ 1 ] + 1 ) )
        if p [ 0 ] . right :
            q . append ( ( p [ 0 ] . right , p [ 1 ] + 1 ) )
Euler = [ 0 ] * MAX
idx = 0
def eulerTree ( root : Node ) :
    global Euler , idx
    idx += 1
    Euler [ idx ] = root . data
    if root . left :
        eulerTree ( root . left )
        idx += 1
        Euler [ idx ] = root . data
    if root . right :
        eulerTree ( root . right )
        idx += 1
        Euler [ idx ] = root . data
vis = [ 0 ] * MAX
L = [ 0 ] * MAX
H = [ 0 ] * MAX
def preprocessEuler ( size : int ) :
    global L , H , vis
    for i in range ( 1 , size + 1 ) :
        L [ i ] = level [ Euler [ i ] ]
        if vis [ Euler [ i ] ] == 0 :
            H [ Euler [ i ] ] = i
            vis [ Euler [ i ] ] = 1
seg = [ 0 ] * ( 4 * MAX )
for i in range ( 4 * MAX ) :
    seg [ i ] = [ 0 , 0 ]
def minPair ( a : list , b : list ) -> list :
    if a [ 0 ] <= b [ 0 ] :
        return a
    else :
        return b
def buildSegTree ( low : int , high : int , pos : int ) -> list :
    if low == high :
        seg [ pos ] [ 0 ] = L [ low ]
        seg [ pos ] [ 1 ] = low
        return seg [ pos ]
    mid = low + ( high - low ) // 2
    buildSegTree ( low , mid , 2 * pos )
    buildSegTree ( mid + 1 , high , 2 * pos + 1 )
    seg [ pos ] = min ( seg [ 2 * pos ] , seg [ 2 * pos + 1 ] )
def LCA ( qlow : int , qhigh : int , low : int , high : int , pos : int ) -> list :
    if qlow <= low and qhigh >= high :
        return seg [ pos ]
    if qlow > high or qhigh < low :
        return [ INT_MAX , 0 ]
    mid = low + ( high - low ) // 2
    return minPair ( LCA ( qlow , qhigh , low , mid , 2 * pos ) , LCA ( qlow , qhigh , mid + 1 , high , 2 * pos + 1 ) )
def findDistance ( n1 : int , n2 : int , size : int ) -> int :
    prevn1 = n1
    prevn2 = n2
    n1 = H [ n1 ]
    n2 = H [ n2 ]
    if n2 < n1 :
        n1 , n2 = n2 , n1
    lca = LCA ( n1 , n2 , 1 , size , 1 ) [ 1 ]
    lca = Euler [ lca ]
    return level [ prevn1 ] + level [ prevn2 ] - 2 * level [ lca ]
def preProcessing ( root : Node , N : int ) :
    eulerTree ( root )
    findLevels ( root )
    preprocessEuler ( 2 * N - 1 )
    buildSegTree ( 1 , 2 * N - 1 , 1 )
if __name__ == " _ _ main _ _ " :
    N = 8
    root = Node ( 1 )
    root . left = Node ( 2 )
    root . right = Node ( 3 )
    root . left . left = Node ( 4 )
    root . left . right = Node ( 5 )
    root . right . left = Node ( 6 )
    root . right . right = Node ( 7 )
    root . right . left . right = Node ( 8 )
    preProcessing ( root , N )
    print ( " Dist ( 4 , ▁ 5 ) ▁ = " , findDistance ( 4 , 5 , 2 * N - 1 ) )
    print ( " Dist ( 4 , ▁ 6 ) ▁ = " , findDistance ( 4 , 6 , 2 * N - 1 ) )
    print ( " Dist ( 3 , ▁ 4 ) ▁ = " , findDistance ( 3 , 4 , 2 * N - 1 ) )
    print ( " Dist ( 2 , ▁ 4 ) ▁ = " , findDistance ( 2 , 4 , 2 * N - 1 ) )
    print ( " Dist ( 8 , ▁ 5 ) ▁ = " , findDistance ( 8 , 5 , 2 * N - 1 ) )

Index/key variables: i, idx, lca, low, n1, n2, pos, prevn1, prevn2
Accumulator variables: q
Iterator variables: i
Class/struct names: Node