#! /usr/bin/env python
# -*- coding: utf-8 -*-

try:
    import builtins
except:
    import __builtin__ as builtins

import argparse
import re
import sys
import timeit
import warnings

import vcsn

if '-DNDEBUG' not in ' '.join([vcsn.config['cppflags'],
                               vcsn.config['cxxflags']]):
    warnings.warn('not compiled with -DNDEBUG, benches are irrelevant')
if '-O3' not in vcsn.config['cxxflags']:
    warnings.warn('not compiled with -O3, benches are irrelevant')

parser = argparse.ArgumentParser(description='Bench some algorithms.')
parser.add_argument('-O', '--only', metavar='RE',
                    type=re.compile, default='.*',
                    help='run only benches whose title is matched by RE')
parser.add_argument('-r', '--repeat', metavar='NUM',
                    type=int, default='3',
                    help='''number runs to perform, the best of which being kept.
                    The higher, the more stable the results are expected to be.''')
parser.add_argument('-v', '--verbose', action='store_true', help='Be verbose')

args = parser.parse_args()

last_setup = ['pass']


def ws_signature(ws):
    if ws.endswith('min'):
        return ws[:-3].upper() + 'min'
    elif ws.endswith('mp'):
        return ws[:-2].upper() + 'min'
    else:
        return ws.upper()

def ctx_signature(ctx):
    '''A short, nice looking, description of a context.  To replace eventually
    with `ctx.format()`.'''
    ctx = re.sub(r'lal_char\((.*?)\)',
                 r'[\1]',
                 ctx)
    ctx = re.sub(r'lan_char\((.*?)\)',
                 r'[\1]?',
                 ctx)
    ctx = re.sub(r'law_char\((.*?)\)',
                 r'[\1]*',
                 ctx)
    ctx = re.sub(r'lat<(.*?)>',
                 lambda m: 'x'.join([x.strip()
                                     for x in m.group(1).split(',')]),
                 ctx)
    ctx = re.sub(r'^(.*), *(.*)$',
                 lambda m: '{} -> {}'.format(m.group(1), ws_signature(m.group(2))),
                 ctx)
    return ctx


def bench(stmt, comment, title=None, setup=['pass'], number=1):
    "Report the best timing of three batches of number runs of cmd."
    if title is None:
        title = stmt

    if args.only.search(title + " # " + comment):

        if 1 < number:
            comment += ', {}x'.format(number)

        # The default context, which we use extensively.
        b = vcsn.context('lal_char(abc), b')

        try:
            # Run the set up if it is not the last one we ran.
            if not isinstance(setup, list):
                setup = [setup]
            global last_setup
            if setup != last_setup:
                for s in setup:
                    if args.verbose:
                        print("run:", s)
                    exec(s)
                last_setup = setup

            # Inject the result into `builtins`, so that the stmt run by
            # timeit finds them.  See
            # http://stackoverflow.com/a/5390326/4065671.
            builtins.__dict__.update(locals())
            builtins.__dict__.update({'vcsn': vcsn})
            if args.verbose:
                print("run:", stmt)
            t = round(min(timeit.repeat(stmt, number=number, repeat=args.repeat)),
                      2)
            t = "{:5.2f}s".format(t)
            err = None
        except RuntimeError as exc:
            t = " FAIL"
            err = exc
        print("{:7s}: {:20s} # {}".format(t, title, comment))
        if err:
            print(err, file=sys.stderr)
        sys.stdout.flush()

# Check the cost of dyn calls.
#
# We used to check a call to "b.format('text')", but that makes us too
# sensitive to the cost of the formatting itself.  Any operation can
# hardly be simpler than `automaton.is_proper`, which just returns
# "true" for an automaton on a free labelset.  So this does measure
# the pure speed of our interface with Python, and dispatch.
#
# To check the cost of the dispatch, it must be done in C++.
#
# FWIW:
#
# %timeit a.is_empty()
# 1000000 loops, best of 3: 1.03 µs per loop
#
# %timeit a.proper()
# 10000 loops, best of 3: 62.8 µs per loop
#
# %timeit a.is_proper()
# 1000000 loops, best of 3: 1 µs per loop
#
# So really, is_proper looks the right tool.
bench('a.is_proper()',
      'a = ""',
      setup='''a = vcsn.automaton('', 'daut')''',
      number=1000000)

# Check formatting.  was used to check dyn:: round-trip.
bench('b.format("text")',
      'b = [abc] -> B',
      number=100000)

# I/O on expressions.
e = '[ab]{20000}'
bench('b.expression(e)',
      'e = {}'.format(e),
      setup='e = "{}"'.format(e),
      number=1000)

e = '"(\e+a)" * 500'
bench('b.expression(e)',
      'e = {}'.format(e),
      setup='e = {}'.format(e),
      number=100)

e = '"(\e+a)" * 500'
bench('r.format("text")',
      'r = b.expression({})'.format(e),
      setup=['e = {}'.format(e),
             'r = b.expression(e)'],
      number=1000)

# Output should be fast.
r = 'a?{500}'
for fmt in ['dot', 'efsm', 'fado', 'grail', 'tikz']:
    bench('a.format("{}")'.format(fmt),
          'a = std({})'.format(r),
          setup=['r = "{}"'.format(r),
                 'a = b.expression(r).standard()'],
          number=5)

# Input should be too.
r = 'a?{500}'
for fmt in ['dot', 'efsm', 'fado']:
    bench('vcsn.automaton(a, "{}")'.format(fmt),
          's = {}(std({}))'.format(fmt, r),
          'read(s)',
          setup=['r = "{}"'.format(r),
                 'a = b.expression(r).standard().format("{}")'.format(fmt)])

## -------------- ##
## derived_term.  ##
## -------------- ##


def bench_derived_term(alphabet, exp, algo, number):
    ctx = "lal_char({}), z".format(alphabet)
    cmd = 'r.derived_term("{}")'.format(algo)
    if 'lazy' in algo:
        # Accessible resolves the lazy states.
        cmd = cmd + '.accessible()'
    bench(cmd,
          'r = {}, c = {}'.format(exp, ctx_signature(ctx)),
          setup=['ctx = "{}"'.format(ctx),
                 'e = "{}"'.format(exp),
                 'c = vcsn.context(ctx)',
                 'r = c.expression(e)'],
          number=number)


def bench_dt(alphabet, size, algo, number):
    bench_derived_term(alphabet,
                       "(a+b)*b(<2>a+<2>b){{{}}}".format(size),
                       algo,
                       number=number)

bench_dt('ab',  150, 'derivation', 20)
bench_dt('a-z', 150, 'derivation', 20)
bench_dt('a-z', 150, 'expansion',  20)
bench_dt('ab',  300, 'derivation', 10)
bench_dt('a-z', 300, 'derivation', 10)
bench_dt('a-z', 300, 'expansion',  10)
bench_dt('a-z', 300, 'lazy,expansion',  10)

# Measure the cost of a lazy construction.
bench_dt('a-z', 3, 'expansion', 5000)
bench_dt('a-z', 3, 'lazy,expansion', 5000)

bench_derived_term('a', 'a?{150}', 'derivation', 1)
bench_derived_term('a', 'a?{150}', 'expansion',  2)
bench_derived_term('a', 'a?{150}', 'lazy,expansion',  2)

# standard
ctx = 'lal_char(a-z), z'
e = "(a+b)*b(<2>a+<2>b){20000}"
bench('r.standard()',
      'r = {}, c = {}'.format(e, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'e = "{}"'.format(e),
             'r = vcsn.context(ctx).expression(e)'],
      number=10)

# thompson
ctx = 'lan_char(a-z), z'
bench('r.thompson()',
      'r = {}, c = {}'.format(e, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'e = "{}"'.format(e),
             'r = vcsn.context(ctx).expression(e)'],
      number=10)


## ------------- ##
## Determinize.  ##
## ------------- ##

# These are the well known worst cases.  21 is too long, slowly moving
# to use 18 as reference.
for n in [18, 21]:
    bench('a.determinize()',
          'a = ladybird({})'.format(n),
          setup=['n = {}'.format(n),
                 'c = vcsn.context("lal_char(abc), b")',
                 'a = c.ladybird(n)'])

# In the case of 18, check that we scale well with the size of the
# (context's) alphabet.
n = 18
ctx = 'lal_char(a-zA-Z0-9), b'
bench('a.determinize()',
      'a = ladybird({}), c = {}'.format(n, ctx_signature(ctx)),
      setup=['n = {}'.format(n),
             'c = vcsn.context("{}")'.format(ctx),
             'a = c.ladybird(n)'])

# See how boolean vs. weighted determinization goes.
for n, number in [(13, 10), (14, 10), (16, 2)]:
    for algo in ['', '"weighted"']:
        for ws in ['b', 'f2']:
            ctx = 'lal_char(abc), ' + ws
            bench('a.determinize({})'.format(algo),
                  'a = de_bruijn({}), c = {}'.format(n, ctx_signature(ctx)),
                  setup=['c = vcsn.context("{}")'.format(ctx),
                         'n = {}'.format(n),
                         'a = c.de_bruijn(n)'],
                  number=number)

# Check the cost of laziness.
n = 13
bench('a.determinize("lazy,weighted").accessible()',
      'a = de_bruijn({}), c = {}'.format(n, ctx_signature(ctx)),
      setup=['c = vcsn.context("{}")'.format(ctx),
            'n = {}'.format(n),
             'a = c.de_bruijn(n)'],
      number=10)

# Something more realistic: the previous automata explode in an
# exponential number of states, and half of them end up being final.
# This exagerates the importance of the handling of the final states.
#
# The following bench tries to be more realistic (i.e., more NLP
# like): the automata are almost deterministic (and easy to
# determinize), are "wide", and have few final states.
n = 100
ctx = 'lal_char(a-zA-Z0-9), b'
r = '([^]+a){{{}}}'.format(n)
for algo in ['', '"weighted"', '"lazy,weighted"']:
    cmd = 'a.determinize({})'.format(algo)
    if 'lazy' in algo:
        # Accessible resolves the lazy states.
        cmd = cmd + '.accessible()'
    bench(cmd,
          'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
          setup=['r = "{}"'.format(r),
                 'c = vcsn.context("{}")'.format(ctx),
                 'a = c.expression(r).standard()'],
          number=2)


## ------ ##
## eval.  ##
## ------ ##

n = 150
# Many many states active concurrently.
# too slow: a = b.expression('(a+b)*a(a+b){{{n}}}).derived_term()
bench('a.eval("a"*{})'.format(n + 1),
      'a = de_bruijn({})'.format(n),
      setup=['n = {}'.format(n),
             'a = b.de_bruijn(n)'],
      number=2000)
# Something less wide.
ctx = 'lal_char(a-z), z'
n = 25
r = '[a-z]*'
bench('a.eval("abcxyz"*{})'.format(n),
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['c = vcsn.context("{}")'.format(ctx),
             'r = c.expression("{}")'.format(r),
             'a = r.standard()'],
      number=10000)


## ---------- ##
## shortest.  ##
## ---------- ##

n = 9
# too slow: a = b.expression('(a+b)*a(a+b){{{n}}}).derived_term()
bench('a.shortest(5)',
      'a = de_bruijn({})'.format(n),
      setup=['n = {}'.format(n),
             'a = b.de_bruijn(n)'],
      number=10)

bench('a.shortest(5000)',
      'a = de_bruijn({})'.format(n),
      setup=['n = {}'.format(n),
             'a = b.de_bruijn(n)'],
      number=10)

ctx = 'lal_char(a-e), z'
r = "[a-e]?{600}"
bench('a.shortest(5)',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).standard()'])

ctx = 'lat<lan_char(a), lan_char(x)>, q'
r = "(\e|x + a|\e)*"
bench('a.shortest(5000)',
      'a = derived_term({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).derived_term().strip()'],
      number=10)

ctx = 'lal_char(a-e), z'
r = "[a-e]{10000}"
bench('a.shortest()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).standard()'])

# lightest
ctx = 'lal_char(a-e), nmin'
r = "[a-e]?{150}"
bench('a.lightest(5)',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).standard()'],
      number=10)

# Building [a-z]?{300} is really time-consuming.  Make more iterations
# instead.
ctx = 'lal_char(a-e), nmin'
bench('a.lightest()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).standard()'],
      number=500)

ctx = 'lat<lan_char(a), lan_char(x)>, q'
r = "(\e|x + a|\e)*"
bench('a.lightest(5000)',
      'a = derived_term({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).derived_term().strip()'],
      number=10)

# lightest-automaton
ctx = 'lal_char(a-e), nmin'
r = "[a-e]?{150}"
for algo, number in [('a-star', 10), ('bellman-ford', 1), ('dijkstra', 500)]:
    bench('a.lightest_automaton("{}")'.format(algo),
          'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
          setup=['c = vcsn.context(ctx)',
                 'r = "{}"'.format(r),
                 'a = c.expression(r).standard()'],
          number=number)

# sort.
ctx = "lal_char(a-e), z"
r = "[a-e]?{700}"
bench('a.sort()',
      'a = std({})'.format(r),
      setup=['c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).standard()'])

# proper.
r = "a?{1200}"
bench('a.proper()',
      'a = thompson({})'.format(r),
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lan_char(a), b").expression(r).thompson()'])

# to-expression.
bench('a.expression("associative", "delgado")',
      'a = ladybird(2000)',
      setup=['a = vcsn.context("lal_char(abc), b").ladybird(2000)'],
      number=5)

r = '[a-d]?{100}'
bench('a.expression("associative", "naive")',
      'a = std({})'.format(r),
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lal_char(a-d), b").expression(r).standard()'])

bench('a.expression("associative", "naive")',
      'a = ladybird(8000)',
      setup=['a = vcsn.context("lal_char(abc), b").ladybird(8000)'],
      number=10)

r = '[a-d]?{15}'
bench('a.expression("linear", "delgado")',
      'a = std({})'.format(r),
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lal_char(a-d), b").expression(r).standard()'])

bench('a.expression("linear", "delgado")',
      'a = ladybird(2000)',
      setup=['a = vcsn.context("lal_char(abc), b").ladybird(2000)'])

r = '[a-d]?{9}'
bench('a.expression("linear", "naive")',
      'a = std({})'.format(r),
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lal_char(a-d), b").expression(r).standard()'])


bench('a.expression("linear", "naive")',
      'a = ladybird(4000)',
      setup=['a = vcsn.context("lal_char(abc), b").ladybird(4000)'])

# Other conjunction and power testcases, with more outgoing transitions
# per state.  This stresses much better the new conjunction algorithm.
r = "[a-e]?{50}"
bench('a.conjunction(a)',
      'a = std({})'.format(r),
      'a.conjunction(a)',
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lal_char(a-e), z").expression(r).standard()'])

bench('a.shuffle(a)',
      'a = std({})'.format(r),
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lal_char(a-e), z").expression(r).standard()'])

# infiltration.
r = "[a-e]?{30}"
bench('a.infiltration(a)',
      'a = std({})'.format(r),
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lal_char(a-e), z").expression(r).standard()'])

# Conjunction with spontaneous transitions.
r = "[a-e]?{80}"
bench('a.conjunction(a)',
      'a = thompson({})'.format(r),
      'a.conjunction(a)',
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lan_char(a-e), z").expression(r).thompson()'])

# power.
n = 12
r = "[a-e]*b(<2>[a-e])*"
bench('a & {}'.format(n),
      'a = std({})'.format(r),
      setup=['r = "{}"'.format(r),
             'a = vcsn.context("lal_char(a-e), z").expression(r).standard()'])

# compose.
ctx = "lat<lan<char(a-z)>, lan<char(a-z)>>, b"
r = "([a-z]|[a-z])?{20}"
bench('a.compose(a2)',
      'a2 = std({}), a = std(a|a)'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'c = vcsn.context(ctx)',
             'a = c.expression("a|a").standard()',
             'a2 = c.expression(r).automaton()'],
      number=10)

ctx = "lat<lal_char(a-z), lal_char(a-z)>, b"
r = "['(a,a)'-'(i,z)']{4}"
bench('a.compose(a)',
      'a = std({})'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'c = vcsn.context(ctx)',
             'a = c.expression(r).standard()'])

ctx = "lat<lan_char(a-z), lan_char(a-z)>, b"
bench('a.compose(a)',
      'a = thompson({})'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'c = vcsn.context(ctx)',
             'a = c.expression(r).thompson()'])

# has_bounded_lag
ctx = "lat<lan_char(abc), lan_char(xyz)>, b"
r = "['(a,x)'-'(b,y)']{1000}*"
bench('a.has_bounded_lag()',
      'a = std({})'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'c = vcsn.context(ctx)',
             'a = c.expression(r).standard()'])
ctx = "lat<lan_char(abc), lan_char(xyz)>, b"
r = "['(a,x)'-'(b,y)']*{600}"
bench('a.has_bounded_lag()',
      'a = std({})'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'c = vcsn.context(ctx)',
             'a = c.expression(r).standard()'],
      number=10)


def bench_minimize(ctx, r, algos):
    for algo in algos:
        bench('a.minimize("{}")'.format(algo),
              'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
              setup=['ctx = vcsn.context("{}")'.format(ctx),
                     'r = ctx.expression("{}")'.format(r),
                     'a = r.standard()'])
bench_minimize('lal_char(a-k), b', '[a-g]{800}',
               ['signature', 'hopcroft', 'moore'])
bench_minimize('lal_char(a-k), b', '[a-k]{2000}', ['moore'])
bench_minimize('lal_char(a-k), b', '[a-g]{300}', ['weighted', 'hopcroft'])
bench_minimize('lal_char(a-k), z', '[a-g]{300}', ['weighted'])


bench('a.synchronizing_word()',
      'a = de_bruijn(6)',
      setup='a = vcsn.context("lal_char(abc), b").de_bruijn(6).determinize()')

# reduce.
ctx = "lal_char(a-k), z"
r = "[a-g]{300}"
bench('a.reduce()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'c = vcsn.context(ctx)',
             'r = "{}"'.format(r),
             'a = c.expression(r).standard()'])
ctx = "lal_char(a-k), q"
bench('a.reduce()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r).standard()'])

# twins property.
ctx = "lal_char(abc), zmin"
r = "[a-c]{200}*+[a-c]{200}*"
bench('a.has_twins_property()',
      'a = std({}, "associative"), c = {}'.format(r, ctx_signature(ctx)),
      setup=['ctx = "{}".format(ctx)',
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r, "associative").standard()'],
      number=20)

# cycle ambiguous.
ctx = "lal_char(abc), z"
r = "[a-c]{2000}(<2>ab+a<3>b)"
bench('a.is_cycle_ambiguous()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r).standard()'],
      number=20)

# ambiguous.
ctx = "lal_char(abc), z"
r = "[a-c]{2000}(<2>ab+a<3>b)"
bench('a.is_ambiguous()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r).standard()'],
      number=20)

# is_functional.
ctx = "lat<lal_char(ab), lal_char(xy)>, b"
r = "(a|x){2000}(b|y)"
bench('a.is_functional()',
      'a = std({})'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r).standard()'],
      number=100)

# accessible.
ctx = "lan_char(a-z), b"
r = "[a-m]{20000}"
bench('a.accessible()',
      'a = thompson({}).proper(False)'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r).thompson().proper(False)'])

# scc.
ctx = "lal_char(abc), b"
r = '(abc)*{1000}'
for algo in ['dijkstra', 'kosaraju', 'tarjan_iterative', 'tarjan_recursive']:
    bench('a.scc("{}")'.format(algo),
          'a = std({})'.format(r),
          setup=['ctx = "{}"'.format(ctx),
                 'r = "{}"'.format(r),
                 'c = vcsn.context(ctx)',
                 'a = c.expression(r).standard()'],
          number=20)

# polynomial.trie.
r = '[a-j]{6}'
bench('p.trie()',
      'p = [a-j]{6}',
      setup=['c = vcsn.context("law_char(a-j), b")',
             'm = c.polynomial("[a-j]")',
             'p = m ** 6'])

# synchronize
ctx = "lat<law_char, law_char>, b"
r = r"""(abc|de)((f|\e)+((g|h)(i|j)*(\e|k)))(l|mn){2000}"""
bench('a.synchronize()',
      'a = std({})'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'c = vcsn.context(ctx)',
             'a = c.expression(r).standard()'])

# is_synchronized
ctx = "lat<law_char, law_char>, b"
r = r"""(abc|de)((f|\e)+((g|h)(i|j)*(\e|k)))(l|mn){500000}"""
bench('a.is_synchronized()',
      'a = std({})'.format(r),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'c = vcsn.context(ctx)',
             'a = c.expression(r).standard()'])

# weight_series
ctx = "lal_char(a-z), nmin"
r = "a{12000}+<1>[b-z]{12000}"
bench('a.weight_series()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r).standard()'],
      number=50)

ctx = "lal_char(a-z), z"
r = "[a-z]{200}"
bench('a.weight_series()',
      'a = std({}), c = {}'.format(r, ctx_signature(ctx)),
      setup=['ctx = "{}"'.format(ctx),
             'r = "{}"'.format(r),
             'a = vcsn.context(ctx).expression(r).standard()'],
      number=10)
