-- TODO: build some generic benchmark harness
[case testBenchmarkTree]
from typing import Optional
class Node:
    def __init__(self, value: int) -> None:
        self.value = value
        self.left = None  # type: Optional[Node]
        self.right = None  # type: Optional[Node]
    def sum(self) -> int:
        left = 0
        if self.left is not None:
            left = self.left.sum()
        right = 0
        if self.right is not None:
            right = self.right.sum()
        return self.value + left + right
def sum_tree(x: Optional[Node]) -> int:
    if x is None:
        return 0
    return x.value + sum_tree(x.left) + sum_tree(x.right)
def build(n: int) -> Optional[Node]:
    if n == 0:
        return None
    x = Node(n)
    x.left = build(n - 1)
    x.right = x.left
    return x

def bench_sum(x: Optional[Node]) -> None:
    for i in range(1000000):
        sum_tree(x)
def bench_sum_method(x: Node) -> None:
    for i in range(1000000):
        x.sum()
[file driver.py]
from typing import Optional
import native
import interpreted
from timeit import timeit
from time import time
import os

def dumb_time(f):
    t0 = time()
    f()
    t1 = time()
    return t1 - t0

def basic_test(m):
    tree = m.build(5)
    assert(m.sum_tree(tree) == 57)
    assert(tree.sum() == 57)
    return tree

def test(m):
    tree = basic_test(m)

    g = {**globals(), **locals()}
    sum = timeit('m.sum_tree(tree)', globals=g)
    sum2 = timeit('tree.sum()', globals=g)
    fsum = dumb_time(lambda: m.bench_sum(tree))
    fsum2 = dumb_time(lambda: m.bench_sum_method(tree))
    build = timeit('m.build(5)', globals=g)
    return (sum, sum2, fsum, fsum2, build)

# Basic functionality test
basic_test(native)

# Benchmark if we are benchmarking
if os.environ.get('MYPYC_RUN_BENCH') == '1':
    nsum, nsum2, nfsum, nfsum2, nbuild = test(native)
    isum, isum2, ifsum, ifsum2, ibuild = test(interpreted)
    print(nsum, nsum2, nfsum, nbuild)
    print("Sum speedup:", isum/nsum)
    print("Sum method speedup:", isum2/nsum2)
    print("Sum (fast) speedup:", ifsum/nfsum)
    print("Sum (fast) method speedup:", ifsum2/nfsum2)
    print("Build speedup:", ibuild/nbuild)

[case testBenchmarkVisitorTree]
from mypy_extensions import trait
from typing import cast, Generic, TypeVar, Any

T = TypeVar('T')
class Tree:
    def accept(self, v: 'TreeVisitor[T]') -> T:
        pass
class Leaf(Tree):
    def accept(self, v: 'TreeVisitor[T]') -> T:
        return v.visit_leaf(self)
class Node(Tree):
    def __init__(self, value: int, left: Tree, right: Tree) -> None:
        self.value = value
        self.left = left
        self.right = right
    def accept(self, v: 'TreeVisitor[T]') -> T:
        return v.visit_node(self)

@trait
class TreeVisitor(Generic[T]):
    def visit_leaf(self, x: Leaf) -> T: return cast(T, None)
    def visit_node(self, x: Node) -> T: return cast(T, None)

class SumVisitor(TreeVisitor[int]):
    def sum(self, x: Tree) -> int:
        return x.accept(self)
    def visit_leaf(self, x: Leaf) -> int:
        return 0
    def visit_node(self, x: Node) -> int:
        return x.value + self.sum(x.left) + self.sum(x.right)

def equal(x: Tree, y: Tree) -> bool:
    return EqualVisitor(x).equal(y)

class EqualVisitor(TreeVisitor[bool]):
    def __init__(self, left: Tree) -> None:
        self.left = left
    def equal(self, right: Tree) -> bool:
        return right.accept(self)
    def visit_leaf(self, right: Leaf) -> bool:
        return isinstance(self.left, Leaf)
    def visit_node(self, right: Node) -> bool:
        if isinstance(self.left, Node):
            # our boolean stuff is crap
            if (self.left.value == right.value and equal(self.left.left, right.left)
                    and equal(self.left.right, right.right)):
                return True
        return False

def sum_tree(x: Tree) -> int:
    return SumVisitor().sum(x)

def build(n: int) -> Tree:
    if n == 0:
        return Leaf()
    return Node(n, build(n - 1), build(n - 1))

def bench_sum_tree(x: Tree) -> None:
    for i in range(100000):
        sum_tree(x)
def bench_equal_tree(x: Tree, y: Tree) -> None:
    for i in range(100000):
        equal(x, y)

[file driver.py]
from typing import Optional
import interpreted
import native
from timeit import timeit
from time import time
import os
import sys

# Side test: some stuff about MROs and generics
if sys.version_info[:3] > (3, 5, 2):
    assert tuple(x.__name__ for x in interpreted.SumVisitor.mro()) == ('SumVisitor', 'TreeVisitor', 'Generic', 'object')
    assert tuple(x.__name__ for x in native.SumVisitor.mro()) == ('SumVisitor', 'TreeVisitor', 'Generic', 'object')
    assert str(native.TreeVisitor[native.T]) == "native.TreeVisitor[~T]"

assert native.TreeVisitor.__name__ == "TreeVisitor"
assert native.SumVisitor.__name__ == "SumVisitor"

def dumb_time(f):
    t0 = time()
    f()
    t1 = time()
    return t1 - t0

def basic_test(m):
    tree = m.build(5)
    tree2 = m.build(5)
    tree2.right.right.right.value = 10
    assert m.sum_tree(tree) == 57
    assert m.equal(tree, tree)
    assert not m.equal(tree, tree2)

    assert isinstance(native.SumVisitor(), native.TreeVisitor)

    return tree

def test(m):
    tree = basic_test(m)

    g = {**globals(), **locals()}
    fsum = dumb_time(lambda: m.bench_sum_tree(tree))
    feq = dumb_time(lambda: m.bench_equal_tree(tree, tree))
    return fsum, feq

basic_test(native)

if os.environ.get('MYPYC_RUN_BENCH') == '1':
    nfsum, nfeq = test(native)
    ifsum, ifeq = test(interpreted)
    print(nfsum)
    print("Sum speedup:", ifsum/nfsum)
    print("Equal speedup:", ifeq/nfeq)
