Dump any data-structure or object, traverse
    it depth-first in-order and apply a unified diff.

    Depth-first in-order is just like structure would be printed.

    :param             a: data_structure a
    :param             b: data_structure b
    :param             n: lines of context
    :type              n: int
    :param          sort: sort the data-structure

    ATTENTION: Sorting means changing the data-structure. The test-result may
    differ. But in case of dictionaries the results become comparable because
    the sorting negates the hash-algorithms "de-sorting".

    >>> a = recursive_sort(freeze([
    ...     'a',
    ...     [3, 4],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([3,5,2])])}]},
    ...     []
    ... ]))
    >>> b = recursive_sort(freeze([
    ...     'a',
    ...     [7, 3],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([2,5,3])])}]},
    ...     []
    ... ]))
    >>> transparent_repr("\\n".join(tree_diff(a, b).split("\\n")[2:]))
    @@ -7,6 +7,6 @@
           'w'),),
         3),
        'a'),),
      'a',
      (3,
    -  4))
    +  7))

    >>> a = [
    ...     'a',
    ...     [3, 4],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([3,5,2])])}]},
    ...     []
    ... ]
    >>> b = [
    ...     'a',
    ...     [7, 3],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([2,5,3])])}]},
    ...     []
    ... ]
    >>> transparent_repr("\\n".join(
    ...     tree_diff(a, b, sort=True
    ... ).split("\\n")[2:]))
    @@ -11,6 +11,6 @@
               '3',
               4)]),)],
          3)),)],
      'a',
      (3,
    -  4))
    +  7))

def tree_diff(a, b, n=5, sort=False):
    """Dump any data-structure or object, traverse
    it depth-first in-order and apply a unified diff.

    Depth-first in-order is just like structure would be printed.

    :param             a: data_structure a
    :param             b: data_structure b
    :param             n: lines of context
    :type              n: int
    :param          sort: sort the data-structure

    ATTENTION: Sorting means changing the data-structure. The test-result may
    differ. But in case of dictionaries the results become comparable because
    the sorting negates the hash-algorithms "de-sorting".

    >>> a = recursive_sort(freeze([
    ...     'a',
    ...     [3, 4],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([3,5,2])])}]},
    ...     []
    ... ]))
    >>> b = recursive_sort(freeze([
    ...     'a',
    ...     [7, 3],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([2,5,3])])}]},
    ...     []
    ... ]))
    >>> transparent_repr("\\n".join(tree_diff(a, b).split("\\n")[2:]))
    @@ -7,6 +7,6 @@
           'w'),),
         3),
        'a'),),
      'a',
      (3,
    -  4))
    +  7))

    >>> a = [
    ...     'a',
    ...     [3, 4],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([3,5,2])])}]},
    ...     []
    ... ]
    >>> b = [
    ...     'a',
    ...     [7, 3],
    ...     {'a': [3, {'w' : set([4, '3', frozenset([2,5,3])])}]},
    ...     []
    ... ]
    >>> transparent_repr("\\n".join(
    ...     tree_diff(a, b, sort=True
    ... ).split("\\n")[2:]))
    @@ -11,6 +11,6 @@
               '3',
               4)]),)],
          3)),)],
      'a',
      (3,
    -  4))
    +  7))

    """
    a = dump(a)
    b = dump(b)
    if not sort:
        a = vformat(a).split("\n")
        b = vformat(b).split("\n")
    else:
        a = vformat(recursive_sort(a)).split("\n")
        b = vformat(recursive_sort(b)).split("\n")
    return "\n".join(difflib.unified_diff(a, b, n=n, lineterm=""))