{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "%matplotlib notebook\n",
    "import numpy as np\n",
    "import numpy.polynomial.polynomial as poly\n",
    "\n",
    "import statistics\n",
    "import warnings\n",
    "import itertools\n",
    "import sqlite3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "db = sqlite3.connect('results.sqlite3')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fetch_run(names_or_ids):\n",
    "    run_info, grouped, cals = [], {}, {}\n",
    "    for name_or_id in names_or_ids:\n",
    "        if type(name_or_id) is str:\n",
    "            runs = db.execute('SELECT run_id FROM runs WHERE name LIKE ?', (name_or_id,)).fetchall()\n",
    "            if len(runs) > 1:\n",
    "                raise ValueError('Ambiguous run name {} matches run ids {}'.format(run_name, runs))\n",
    "\n",
    "            ((run_id,),), run_name = runs, name_or_id\n",
    "        else:\n",
    "            run_id, (run_name,) = name_or_id, db.execute('SELECT name FROM runs WHERE run_id == ?', (name_or_id,)).fetchone()\n",
    "        run_info.append((run_id, run_name))\n",
    "    \n",
    "        data = db.execute('''\n",
    "            SELECT channel, duty_cycle, voltage, voltage_stdev FROM measurements\n",
    "            WHERE run_id == ?\n",
    "            ORDER BY channel ASC, duty_cycle ASC;\n",
    "            ''', (run_id,)).fetchall()\n",
    "        _ch, cal_duty, *cal = data[0]\n",
    "        assert cal_duty == 0\n",
    "        cals[run_id] = cal\n",
    "        for ch, data in itertools.groupby(data, lambda elem: elem[0]):\n",
    "            if ch == -1: # skip cal data\n",
    "                continue\n",
    "            if ch in grouped:\n",
    "                warnings.warn('Duplicate data: Channel {} found in more than one run!'.format(ch))\n",
    "            grouped[ch] = [(duty, volt, stdev) for _ch, duty, volt, stdev in data]\n",
    "    return run_info, grouped, next(iter(cals.values())) # for now just use some random cal value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def apply_style(ax):\n",
    "    ax.spines['top'].set_visible(False)\n",
    "    ax.spines['right'].set_visible(False)\n",
    "    ax.spines['bottom'].set_color('#08bdf9')\n",
    "    ax.spines['left'].set_color('#08bdf9')\n",
    "    ax.tick_params(axis='x', colors='#01769D', which='both')\n",
    "    ax.tick_params(axis='y', colors='#01769D', which='both')\n",
    "    ax.xaxis.label.set_color('#01769D')\n",
    "    ax.yaxis.label.set_color('#01769D')\n",
    "    ax.grid(color='#08bdf9', linestyle=':')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "color_bright, color_dark = '#ffd2e9', '#fe3ea0'\n",
    "\n",
    "def plot_run(figtitle, *names_or_ids, figsize=None, combine_plots=False, svgfile=None):\n",
    "    run_info, data, cal = fetch_run(names_or_ids)\n",
    "    \n",
    "    if combine_plots:\n",
    "        rows, cols = 1, 1\n",
    "    else:\n",
    "        rows = (len(data)+1)//2\n",
    "        cols = 2 if len(data) > 1 else 1\n",
    "    fig, axs = plt.subplots(rows, cols, figsize=figsize or (8,3*max(2, rows)), squeeze=False)\n",
    "    if figtitle:\n",
    "        fig.suptitle(figtitle)\n",
    "    if combine_plots:\n",
    "        axs = np.array([axs[0,0]] * len(names_or_ids))\n",
    "\n",
    "    cal_volt, cal_stdev = cal\n",
    "    offsets = []\n",
    "    for ch, ax in zip(data, axs.flat):\n",
    "        ch_data = data[ch]\n",
    "        duty, volt, stdev = zip(*ch_data)\n",
    "        \n",
    "        duty = np.array(duty) / duty[0]\n",
    "        volt = np.array(volt) - cal_volt\n",
    "        vref = volt[0]\n",
    "        stdev = np.array(stdev)\n",
    "        \n",
    "        max_y = max(volt)/vref\n",
    "        min_x, max_x = min(duty), max(duty)\n",
    "        \n",
    "        offx, slope = fit_coefs = poly.polyfit(duty, volt, 1)\n",
    "        fit_func = poly.polyval(duty, fit_coefs)\n",
    "        ax.errorbar(duty, volt/vref, yerr=stdev/vref, color=color_bright, zorder=1)\n",
    "        ax.plot(duty, fit_func/vref, color=color_dark, zorder=2)\n",
    "        \n",
    "        apply_style(ax)\n",
    "        ax.set_xscale('log')\n",
    "        ax.set_yscale('log')\n",
    "        bit_offx = offx/slope\n",
    "        offsets.append(bit_offx)\n",
    "        \n",
    "        print('Channel {} offset: {:6.3f}lsb'.format(ch, bit_offx))\n",
    "        if figtitle:\n",
    "            ax.set_title('Channel {}, offset={:.3f}lsb'.format(ch, bit_offx))\n",
    "    \n",
    "        # reuse latest duty cycles here\n",
    "        ax.set_xticks(duty)\n",
    "        ax.set_xticklabels([str(i) for i in range(len(duty))])\n",
    "        ax.set_xlabel('bit index')\n",
    "        ax.set_yticks([2**i for i in range(len(duty))])\n",
    "        ax.set_yticklabels([str(2**i) for i in range(len(duty))])\n",
    "\n",
    "        ax.set_xlim([min_x*0.9, max_x*1.1])\n",
    "        ax.set_ylim([0, max_y*1.1])\n",
    "    \n",
    "    if len(names_or_ids) > 1:\n",
    "        print('Offset statistics: mean={:.4f}lsb, stdev={:.4f}lsb'.format(\n",
    "            statistics.mean(offsets), statistics.stdev(offsets)))\n",
    "    \n",
    "    if svgfile:\n",
    "        fig.savefig(svgfile)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fetch_runs(*names):\n",
    "    return [run_id for name in names for run_id, in db.execute('''\n",
    "    SELECT DISTINCT runs.run_id\n",
    "    FROM runs JOIN measurements USING (run_id)\n",
    "    WHERE name LIKE ? AND channel != -1\n",
    "    ''', (name,)).fetchall() ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        this.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,[encoded data, 69300 characters]\" width=\"800\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Channel 31 offset:  2.681lsb\n",
      "Channel 30 offset:  2.633lsb\n",
      "Channel 29 offset:  2.616lsb\n",
      "Channel 28 offset:  2.643lsb\n",
      "Offset statistics: mean=2.6432lsb, stdev=0.0276lsb\n"
     ]
    }
   ],
   "source": [
    "plot_run('All channels, blue runs', *fetch_runs('green1', 'green2', 'green3', 'green4'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        this.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,[encoded data, 24908 characters]\" width=\"600\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Channel 31 offset:  2.681lsb\n"
     ]
    }
   ],
   "source": [
    "plot_run(None, *fetch_runs('green1'), figsize=(6, 4), svgfile='/tmp/driver_linearity_raw.svg')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        this.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,[encoded data, 31236 characters]\" width=\"800\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def bitslide(nbits, offx_lsb):\n",
    "    return [ sum((2**n + offx_lsb) if i&(2**n) else 0 for n in range(nbits)) for i in range(2**nbits) ]\n",
    "\n",
    "def plot_bitslide(data):\n",
    "    fig, (axl, axr) = plt.subplots(1, 2, figsize=(8, 3))\n",
    "    apply_style(axl)\n",
    "    apply_style(axr)\n",
    "    axl.plot(data, color=color_dark)\n",
    "    axr.plot(data, color=color_dark)\n",
    "    axr.set_yscale('log')\n",
    "    axr.set_xscale('log')\n",
    "    axl.set_xlim((0, len(data)))\n",
    "    axr.set_xlim((0, len(data)))\n",
    "\n",
    "plot_bitslide(bitslide(8, 2.5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        this.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,[encoded data, 19680 characters]\" width=\"600\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def frob_export_for_blog(data, svgfile=None):\n",
    "    fig, ax = plt.subplots(1, 1, figsize=(6, 4))\n",
    "    apply_style(ax)\n",
    "    ax.plot(data, color=color_dark)\n",
    "    ax.set_yscale('log')\n",
    "    ax.set_xscale('log')\n",
    "    ax.set_xlim((0, len(data)))\n",
    "    if svgfile:\n",
    "        fig.savefig(svgfile)\n",
    "\n",
    "frob_export_for_blog(bitslide(8, 2.5), svgfile='/tmp/uncorrected_brightness_sim.svg')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x72df4cb50dd8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def cutoff_reference(nbits, offx_lsb, cutoff):\n",
    "    return [ sum((2**n + offx_lsb) if i&(2**n) else 0 for n in range(cutoff, nbits))\n",
    "            for i in range(2**nbits) ]\n",
    "\n",
    "plot_bitslide(cutoff_reference(8, 2.5, 3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x72df4cb4a748>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def improved_bitslide1(nbits, offx_lsb, cutoff):\n",
    "    bs = sorted(bitslide(cutoff, offx_lsb))\n",
    "    return [ sum((2**n + offx_lsb) if i&(2**n) else 0 for n in range(cutoff, nbits))\n",
    "            + bs[i%(2**cutoff)] for i in range(2**nbits) ]\n",
    "\n",
    "plot_bitslide(improved_bitslide1(8, 2.5, 5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x72df4cd3de80>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def improved_bitslide2(nbits, offx_lsb, cutoff):\n",
    "    bs = lambda x: min(bitslide(cutoff, offx_lsb), key=lambda y: abs(x-y))\n",
    "    return [ sum((2**n + offx_lsb) if i&(2**n) else 0 for n in range(cutoff, nbits))\n",
    "            + bs(i%(2**cutoff)) for i in range(2**nbits) ]\n",
    "\n",
    "plot_bitslide(improved_bitslide2(8, 2.5, 5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Channel 23 offset:  0.741lsb\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA5sAAAKSCAYAAABcE89sAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xd4VWXWhvF7AXYFFbsiiopKk6YoOhrriAV7HSu9KCr2\nnrGXsYsogmVQP3sFuxILSJEewF5RBCuigpS83x/nMEYECZBwUu7fdeXKya5rnxMn87De/e5IKSFJ\nkiRJUmmqlusCJEmSJEmVj2FTkiRJklTqDJuSJEmSpFJn2JQkSZIklTrDpiRJkiSp1Bk2JUmSJEml\nzrApSVquImLGQpZ1jojjlsO5B0REzYioFRFdl3DfuhFxzDKc+/yl3VeSpIoofM6mJGl5ioifU0o1\nc3DeSNk/ehGxGfBcSqnxEuyfB5yZUjpwKc8/I6W0Rgm2q5ZSKlqac0iSVJ7Y2ZQk5VxEXBoRPbOv\nB0XENRExLCLei4ids8urRcR12eVjIqJjdvlqEfFqRLwbEWMjom12ed2ImBgRvSJiJFAnIj6NiLWB\nq4EtImJURFwbEf+dv1923wci4oAFyrwa2CW7z2l/U88GEfFGdrtxEbFzRFwNrJJd1n8h1z8jIv4d\nEe8AOxWrk4hoERGDir1P/bLv0UcRcWqpfhCSJJWiGrkuQJKkhaieUmoVEW2AfGBvoD3wU3b5isDg\niHgZ+BI4OKX0S0TUBoYCz2aPUx84MaXUHSAi5g/nOQ9omFJqnl2+K3AG8GxE1AR2Ak5YoKbzyHQ2\n54fZjouo5zDgxZTS1RERwKoppcER0X3++RZiNWBcSunSBeqcr/jPWwN5QC3g/Yi4I6U072/fTUmS\ncsCwKUkqj57Mfh8J1M2+3gdoHBFHZH+uCWwFfAVcExH/AIqAjSJivew2n6eURhQ7bizsZCmlNyPi\n9ohYh0xYfKIEQ1kXVc8I4J6IWAF4JqU0tgTXO5c/rnmRdWYNTCnNBb6PiKnA+sDXJTiHJEnLlWFT\nklQe/Z79Po8//lYFcGpK6ZXiG0bEiUBtoFlKqSgiPgVWzq7+dQnO2R84DjgaOLkE2y+0nmxN/wD2\nB/pHxHUppQf4+wA5K/15EoW5/HGry8oLbPt7sddF+LdcklROec+mJGl5+7vQ9XfbvwR0i4gaABGx\nVUSsSmY46bRs0NydPzqhf3euGcCCk/XcD5wOpJTSpBLss9B6ImJT4NuUUj+gHzB/6OzsiKi+mGuc\n71OgRfb1YYvYR5Kkcs1/DZUkLW+rRMQXZAJWAm7kz/ckLup+xb7AZsCo7L2Q04CDgQeB5yJiODAG\nmLSQff/0c0rph4gYHBHjgBdSSuemlKZFxCTgqUXUPQ6YFxGjgftSSrdkZ7VdsJ484OyImEMmoM6/\n97MPMD4iRqaUjl/ENc53GdAvIr4Bhi2inoXtJ0lSueGjTyRJArJd0rFA85TSX54FKkmSlozDaCVJ\nVV5E7EmmI3qrQVOSpNJhZ1OSJEmSVOrsbEqSJEmSSp1hU5IkSZJU6gybkiRJkqRSZ9iUJEmSJJU6\nw6YkSZIkqdQZNiVJkiRJpc6wKUmSJEkqdYZNSZIkSVKpM2xKkiRJkkqdYVOSJEmSVOoMm5IkSZKk\nUmfYlCRJkiSVOsOmJEmSJKnUGTYlSZIkSaXOsClJkiRJKnWGTUmSJElSqTNsSpIkSZJKnWFTkiRJ\nklTqDJuSJEmSpFJn2JQkSZIklTrDpiRJkiSp1Bk2JUmSJEmlzrApSZIkSSp1hk1JkiRJUqkzbEqS\nJEmSSp1hU5IkSZJU6gybkiRJkqRSZ9iUJEmSJJU6w6YkSZIkqdQZNiVJkiRJpc6wKUmSJEkqdYZN\nSZIkSVKpM2xKkhYpIi6NiP65rmNBETEoItrluo4FRcQVEfFtRHyd/fmQiPgiIn6OiO1yXV+uRMSJ\nEfFWCba7NyIuWx41SZLKnmFTkqq4iDg2IkZExIyI+CoiBkZE62KbpJwVtxQi4oSIeDcipmeD3rUR\nUa3Y+v4R8XV2/XsR0b6UzrsJ0BPYJqW0UXbx9UC3lFLNlNLYpTxu3YgoKn4NS7j/sRHxWfbzfTIi\n1lzEdrtkt/k5+zUje95DFrLt6wvWFBGXRcS4iJgTEZcs5BQV6vdIkrTsDJuSVIVFRE/gRuAKYD1g\nU+AO4KBc1rWMVgFOA2oDrYA9gbOKrb8KqJtSqgW0Ba6IiGalcN7NgO9SSt8XW1YXmLiMxw0yQS2W\neMeIhsCdwL+A9YGZQO+FbZtSejultEY2GNcEDgBmAC8ucMxjger8NTx+CJwNDFjSOiVJlZNhU5Kq\nqIioCfybTOftmZTSzJTSvJTSwJTSucU2XSki7s92u8ZHRPNixzg3Ij7KriuMiIOLrTsxIt6KiOsj\n4oeI+Dgi9i22flC2G/Z2dv8XI2LtYut3jIjBEfFjRIyOiN1Kcl0ppbtSSoNTSnNTSlOAB4Gdi62f\nlFKaM/80ZELTFiV9zyLivxExLSI+jYgLs8v3BF4GNspey4MRMYPM39lxEfFhsfdrcnabSRGxe3Z5\nRMR52ffy24h4uFgH8o3s95+y+7UqSa1ZxwLPZt+P34CLgUMjYrUS7HsS8HhKaWbx6wcuIRMq/ySl\n1D+l9BLwy+IOHBE3RcTUiPgpIsZERINiq9eNiJez1zooIjYtQa2SpHLIsClJVddOwErA04vZ7kDg\nIaAW8BzQq9i6j4Cds52wfwMPRMT6xdbvAEwi02W8Hui3wLGPAU4E1s3WchZARGxMpkN2WUpprezy\nJyKi9hJeI8CuwITiCyKiV0T8mq3ta+D5Eh7rdmANMl3MPOCEiDg5pfQa0Ab4OtsZ/FdKaQ0yYbZx\nSmmriKgPdAdaZN+vfwKfZY97Gpku6z+AjYAfyXSY59cPUDN77GERsXM2hP+Q/V789Q/FhkE3BP43\nfDel9AkwG6j/dxcZEasAhwH3LbDqqmxdU0v0bi382PsAuwBbppTWBI4CineDjyXzu1Q7W/uDS3su\nSVJuGTYlqeqqTWbYZ9Fitns7pfRSSikB/YEm81eklJ5IKU3Nvn6MzFDKHYrt+3lK6Z7svvcDG0bE\nesXW35tS+jil9DvwKNA0u/xfwMBsp4xsmHsX2G9JLjAiTgZaAP8pvjyl1B1YnUzoeRL4vQTHqgYc\nCZyXUvotpfQ5cANw/OJ2zX6fB6wINIqIGimlL1JKn2bXdQIuTClNyXZdLwMOz54zFjgO2U7lWiml\ntbPfi79eO6U0JLvp6sD0BeqZTiYw/53DgW9TSv+b1CciWgKtgdsWs+/izMmev0FERErp/fm/Q1kD\ns9c3B7gQ2Cn7jw+SpArGsClJVdf3wDolmHjmm2KvfwNWnr9PZCbjGT2/u0amk7bOwvYtNhxz9b85\n9vx1dYEjs126H7LH3hnYoITXRnZI71XAvimlHxZcnzKGAHWAriU45DrACsAXxZZ9DpQoCKWUPgZO\nB/KBqRHxUETMv566wFPzr5fMfZ5zyNxnuSwT6/wC1FxgWU0y92L+nROA/87/ISKCTEf7tOw/HCzx\n/aPzpZQGkekQ9wK+iYg7I6L478SXxbb9FfiBTLdXklTBGDYlqep6B5gFHLy4DRcmey9dHzL3fK6V\nHe46gWUIIsV8Cfw326Wb37FbI6V0fQlr2xe4CzggpbS4CXpqULJ7Nr8jEwDrFltWF/iqJDUBpJQe\nTin9o9gxrs1+/wJos8D1rpa95/QvYTP+OnNs8Rlkf46I+feoTgC2K7ZfPTLd1Q8WVWNkZtXNo1jY\nJBNQWwCPRMQUYDiZz3lysXOVWErp9pRSSzL/OLE1f74HtE6xWlYH1iYz1FmSVMEYNiWpikop/Qxc\nCvSKiIMiYpWIqBERbSLimr/ZdX6YXA0oAr6LiGrZIauNSqm8B4ADI2Kf7LFXjojdImKxHa6I2CO7\n/2EppZELrFs3Io6KiNWyx/0ncDTwWrFtiiJiVxaQHW78KHBlRKweEXWBM8gMLV6siKgfEbtHxIpk\n7pucSWZoLWSC8VXzJ8PJ1tk2u+5bMu/z/wLxgjPHFvuav2xwdtMHybyPO2cnBfo38ES2Y7goJwCD\niw3xJaU0nUx3sSmZ8Dp/OHNzYFi25hoRsTKZ/2+xQkSstLCueUS0jIgdIqJG9j2YVex9ANgvIlpn\n36fLgaEppRIHeklS+WHYlKQqLKV0E5lnQ14ETCPTYevG308alLL7TiJzz+JQMsNhGwJvL+6Ui3i9\nYF2TyTx+5QIyYetzMpMEzf+79XdDSy8i04l7vlinb2Cx/bqS6Zz+AFxHZmjoAPhfV28GMH4Rx+5B\nZrjvJ8CbwAMppXv/ppbida4EXJO9nq/JTIp0QXbdLcAzwMsRMR0YQvbe1+zw4yuBwdlhtsXvif1b\n2a5uFzITPH1D5h8Ius9fHxHPR8R5C+x2HH+dGIiU0rT5X9lrSMC0lNLc7CZ3k3lvjs5e12/ZYy2o\nZnbbH4BPyXSM599Tm7K15pMZ5t2MzP27kqQKKDK3XkiSpIj4F9AgpXRhrmuRJKmiM2xKkiRJkkqd\nw2glSZIkSaXOsClJkiRJKnU1yvoEEeE4XUmSJEmqxFJKf3n02XLpbKaUSuXr0ksvLbVj+VV+v3bb\nbbec11CevyrTfwfl+bPO9fu8PM9f1ucqi88515+PXxXvMynv9eX6a0nen/L8Xpbn2spDfcvzcy7r\nay2L4+f688nVV2lc96I4jFblzmabbZbrEsq1vLy8XJdQasrzZ53r93l5nr+sz1UWn3OuPx9VPP7O\n/L3K8v6U9+vIdX2V6W9LWRw/159PZWTYVLlTngNIeVCZ/oewPH/WuX6fK9P/ITBsqjzwd+bvVZb3\np7xfR67rq0x/WwybFUOFCpv+AlQNfs5Vh5911eDnXDX4OVcdftZVg59z1VGWn3WZP2czIlJZn0OS\nJEmSlBsRQcrVBEGSJEmSpKrFsClJkiRJKnWGTUmSJElSqTNsSpIkSVIV9PlPnzPt12lldnzDpiRJ\nkiRVIUWpiNuG9aLhrS3p++I7ZXYeZ6OVJEmSpCriw+8/ZI9b2vP11Lms+vI9bLvuNqy6ambdwQfD\n6acv+TEXNRttjWUtVpIkSZJUvs0tmsv1b93M5YOuodrgi+l15Cl06l2damU41tWwKUmSJEmVWOG0\nQo54oB2ff7gGu/40nHv612Ojjcr+vN6zKUmSJEmV0Ox5s7ngpcvY/vbd+WZgRx7c51Ve/L/lEzTB\nzqYkSZIkVTrvfjWSw/u345sP6nDkqqO57clNqFVr+dZg2JQkSZKkSmLW3Fmc+Vw+/Ubey9rv3sgr\nFx7LP/7xl7l7lgvDpiRJkiRVAm99Ppgj+rfnx/cbc+oW47jymfVZaaXc1WPYlCRJkqQK7JfZv9D1\nsQt5pPAx6r13O69feSgNGuS6KicIkiRJkqQK64X3X6POVU14bMBPXLlxIROfKB9BE+xsSpIkSVKF\nM33WdI7vfxYvfPgSLb+5i8eub8Mmm+S6qj+zsylJkiRJFcgjowew8VWNeO3VGtzTspAh95e/oAl2\nNiVJkiSpQvjut+847O7TGfz5UPad3Z8H7spjzTVzXdWiGTYlSZIkqRxLKdFn8OOc9mIPVvn4GAZ0\nHMu+e6yW67IWy7ApSZIkSeXUV9OncEDv7oyf8h7H13yS3vfuxMor57qqkvGeTUmSJEkqZ1JKXPPC\n/Wx+3XZMm9CAYSeP5t7LK07QBDubkiRJklSufPTtF7S5ozOffvsNPbd4iasva0b16rmuasnZ2ZQk\nSZKkcqAoFXH2o73Z5qYWVJu8C++fNZzrzqiYQRPsbEqSJElSzo354iP279OBb7//nWt3eoOeVzYg\nItdVLRvDpiRJkiTlyLyieXS65xbu+/gqWs68kDGX92DddSpoK3MBhk1JkiRJyoE3Jk7g0PvbM/Pn\nVbjvgKEcv/+WuS6pVBk2JUmSJGk5mj13Dkfddi3PTL2FfVa4gidu6Mhqq1a+6XQMm5IkSZK0nDwz\nbDTHPdaOajM35IXjRvHPnerkuqQys1zCZn5+Pnl5eeTl5S2P00mSJElSuTJj5iza/udy3vilL0ev\nez3/PfN4atSo2DMAFRQUUFBQsMj1kVIq0wIiIpX1OSRJkiSpvOr30jt0f7kda85pwHNde7H9thvk\nuqRSFRGklP6SnB1GK0mSJEllYOqPv7LvdRcxbt7DnFL/Nm7udHiFf5zJkjBsSpIkSVIpu+aRQVw8\nvAObRmsmnV5I/U1q57qk5c6wKUmSJEml5KMvp7Pvjefw2QrPk7/DnVx01P65LilnDJuSJEmStIxS\ngjPvfJ5bPu5Co9Xa8OWZhWy4Vq1cl5VThk1JkiRJWgbvTvyeA3udwQ+rvU2vfe6jyz575LqkcsGw\nKUmSJElLYe5cOPn6J3jwh1PZeaMjGXDGeGqtulquyyo3DJuSJEmStIReHjKVo+7vzuw1C3nksMc5\nYsfWuS6p3DFsSpIkSVIJ/fJL4sgrHuDFdBb7b9ueR7s/wCorrJzrssolw6YkSZIklcADz31Jp2e7\nsNK6k3nluBfYs0HzXJdUrlXLdQGSJEmSVJ59+21ix+59OHFIcw7fcUemXj7CoFkCdjYlSZIkaSFS\nghvv+5jz3+lI7fV/ZWj7ArbfrGGuy6ow7GxKkiRJ0gI++nge2558M+d+2Ipue+7P5H8PMWguoeXS\n2czPzycvL4+8vLzlcTpJkiRJWipz58KFN03ixo/bU6feCozr/A4N1t8q12WVSwUFBRQUFCxyfaSU\nyrSAiEhlfQ5JkiRJWlYjRs3h4Ouu59stbuLCnS/j4jadqRYOBl2ciCClFAsu955NSZIkSVXab79B\nt8vG8OAv7dim2XoM7vQum61VN9dlVXjGdEmSJElV1guv/M4mx1/Mwyvsw3+O6MG4c14waJYSO5uS\nJEmSqpzvv4cTLxjGS6u0o+Wu9Xmy3Vg2XGPDXJdVqRg2JUmSJFUZKcH9D/1G9ycuhsYP0ffAWzih\nxRFE/OWWQy0jw6YkSZKkKuGLL+DIc95g9CYdyNt3Bx48bjzrrLpOrsuqtAybkiRJkiq1efPgP7f9\nzKVvncdKTZ7l/w7vzaEND8x1WZWeEwRJkiRJqrTGjYMGbV/k0qmNOfDg2Xx+bqFBczmxsylJkiSp\n0pk1Cy64/Afu+Lgnq+/0Bs8e3Y99ttwr12VVKXY2JUmSJFUqBQVQb7+n6B2N+dfhNfnsnPEGzRyw\nsylJkiSpUvjxRzjl3Gk89fsprLXPWF459hF22XSXXJdVZdnZlCRJklShpQSPPpqod9BDPLVBEzof\nWY+Pzhxj0MyxSCmV7QkiUlmfQ5IkSVLV9OWX0O70rxhauwvrb/05Dx9zDy03apnrsqqUiCCl9JcH\nldrZlCRJklThFBXB7bcntv1XX4Y0acrpR7Vk4unvGjTLETubkiRJkiqUCRPg+B6f8knDjtTZcjoP\nHXUPjddvnOuyqqycdjbz8/MpKChYHqeSJEmSVEn9/jtcfEkRrXrcyge7bc8FR/6T0ae8Y9DMkYKC\nAvLz8xe53s6mJEmSpHLvrbfgxJ7vMz2vPVtuEfQ/oh/1a9fPdVli0Z1Nw6YkSZKkcmv6dDj73Lk8\n/MUNROv/cOU+l9Jt+25UC6efKS8WFTZ9zqYkSZKkcunJJ6Fr/jiKDmxHy1Zrc88hI9hszc1yXZZK\nyM6mJEmSpHLl66+h26mzeTuuZF7z3tzQ5hpObnoyEX9pnqkcsLMpSZIkqVwrKoI+feC820aw4hHt\n2GmretzVdgwbrbFRrkvTUijRQOeIqBURj0XEpIiYEBGtiq07KyKKImLtsitTkiRJUmU2aRLssvtM\nLht6NjWOP5BbD7+QZ4992qBZgZW0s3kL8HxK6YiIqAGsChARmwB7AZ+XUX2SJEmSKrHZs+Gaa+CG\nx99ipSPas8c2Lbhtv/Gsu9q6uS5Ny2ix92xGxBrAmJTSFgtZ9xhwGfAs0CKl9MNCtvGeTUmSJEl/\nMWQItOs6g1m7nM/vmz3NnQf24qBtDsp1WVpCy3LPZj3gu4i4F9gOeBc4HdgT+DKlNN4bdSVJkiSV\n1M8/w/nnw8MjXqb6UZ04oOGe3LDPeNZaZa1cl6ZSVJKwWQNoDnRPKb0bETcB+cCuwN7Ftltk4szP\nz//f67y8PPLy8paiVEmSJEkV3bPPQtczfmS1Q89ktaNfp+9Bfdhni31yXZaWQEFBAQUFBYvdriTD\naNcH3kkp1cv+vAuZsNkI+I1MyNwE+ArYIaU0bYH9HUYrSZIkVXFTpkCPHvD2d88wd5/uHLXdwVy9\n59WssdIauS5Ny2iph9GmlKZGxJcRUT+l9AGZ4bMjU0p7FTv4p0DzlNKPpVq1JEmSpAotJejXD879\n97esf3IPVl9nJP0Oeohd6+6a69JUxko6G20P4MGIWAH4BDh5gfWJvxlGK0mSJKnq+eAD6NgpMbnW\nI0T30zmgxQn8O+8eVllhlVyXpuVgscNol/kEDqOVJEmSqpTZs+H66+GGPl+zcaeupLU+5t6D7mX7\njbfPdWkqA4saRlstF8VIkiRJqpyGDYPmLRIPf3AP1bo15dDWTRnZaaRBswoq6TBaSZIkSVqkGTPg\noovgoec/Y8NOnVix1g+81vYVtttgu1yXphyxsylJkiRpmQwcCA0bFTGsqBepw/b8a6c9GdphqEGz\nilsunc38/HyfrylJkiRVMlOnwumnw9uTPmCt0ztQvdY8Brd9m63X2TrXpWk5WNzzNp0gSJIkSdIS\nSQnuuw/OOW8uDTvcRGGta7lkt0vovn13qlernuvytJwt9XM2JUmSJGm+jz6Czp1hyrzxrHdBO2qs\nVYsRB45g87U2z3VpKme8Z1OSJEnSYs2ZA9deC61az6bGXv/mu/335Ix/dOaV418xaGqh7GxKkiRJ\n+lvvvgsdOsBK9d5l3QvbseJ6dRm1/yg2qblJrktTOWbYlCRJkrRQv/4KF18MDzw8kx3O+Tcj5t7L\nTXvcxDGNjiHiL7foSX/iMFpJkiRJf/HSS9CoEUyY8TY1z23Kapt8yviu4zm28bEGTZWInU1JkiRJ\n/zNtGvTsCW8N+4Wm513Au78+we373M4h2x6S69JUwdjZlCRJksSNN8LWW8Mmm8BLH77K1EObMGz0\nDLpHoUFTS8XnbEqSJElVXGEhdOkCs/iJOu3PYtT0V7jrgLvYd8t9c12aKoBFPWfTzqYkSZJURf32\nG5x3Huy+OzQ8/Gm+ObQRG6+/MoVdCw2aWmZ2NiVJkqQqaOBAOOUUaLrLVObtcyrvTx9L3wP78o+6\n/8h1aapg7GxKkiRJ4quv4PDDocdpicOv+C9DtmtCo422ZGyXsQZNlSo7m5IkSVIVMG8e9OoFl10G\nx3T9nPe26sx3M6fSr20/mm/YPNflqQKzsylJkiRVUe++C61awRNPFtH1nl48XLMle2y+G8M7DDdo\nqszY2ZQkSZIqqZ9/hosugkcegdOveJ+B1dsD0LdtX7ZZZ5scV6fKws6mJEmSVEWkBI8/Dg0awIxf\n59Dxv1dz40+7cEyjY3jz5DcNmlouauS6AEmSJEml57PPoHt3+PRTyL9rFL0mt+eb7zbg3Y7vUnfN\nurkuT1WInU1JkiSpEpgzB669Flq2hB1az+SAm8/nwklt6LljT54/9nmDppY7w6YkSZJUwQ0eDM2a\nwaBBcPtzb/FQraZ8/vMnjOsyjuO3O56Iv9xOJ5U5JwiSJEmSKqgffoBzz4Xnn4er/jODYTXP45n3\nn6bXfr04eJuDc12eqoicThCUn59PQUHB8jiVJEmSVOmlBP37ZyYAWmkluGXgC1wytRGz5/1OYddC\ng6aWi4KCAvLz8xe53s6mJEmSVIG8/z5065bpal5723f0//YMBn8xmLsPvJs96+2Z6/JUBfnoE0mS\nJKkCmzULLr0Udt4ZDjggcc79j3LisMasu+q6jO863qCpcsdHn0iSJEnl3GuvQdeu0KgRvDD4a64c\n3Y0P3/6Qp456ih032THX5UkLZdiUJEmSyqmpU+HMM+Gtt+C22xLTNunH/s9eQLftu/HI4Y+wUo2V\ncl2itEiGTUmSJKmcKSqCvn3hoovgxBPhucEfc8brnZgxdQavnfAajddvnOsSpcXynk1JkiSpHBk/\nHnbZBe69F158eR4bHnojezzUiv223I932r9j0FSFYWdTkiRJKgd+/RX+/e9MyLziCtjpoEI6DmjP\nqiusytAOQ9ly7S1zXaK0ROxsSpIkSTk2YAA0bAhffQWjxs5mytb57Nl/dzo068DrJ7xu0FSFZGdT\nkiRJypHJk+G002DcOLj7bqjVYDhtnm5HvbXqMabzGDauuXGuS5SWmp1NSZIkaTmbOxduvhmaNs08\nzmToyF95sehMDnr4IC7e9WKeOfoZg6YqPDubkiRJ0nI0YgR06QI1a8Lbb8PXK73ODvd1pHWd1ozv\nOp51Vl0n1yVKpcKwKUmSJC0H06dnHmXy2GNw3XVw4BE/cc4rZ/PSxy9x5wF3st9W++W6RKlUOYxW\nkiRJKkMpZQJmw4YwaxZMmAC1dniGxr0bsUL1FSjsVmjQVKVkZ1OSJEkqI59+Ct27wxdfwMMPw1ZN\np9LtxR6MnjKahw57iF3r7prrEqUyY2dTkiRJKmWzZ8M118D228Ouu8LIkYlP1+hPkzubsPmamzO2\ny1iDpio9O5uSJElSKXr77cwEQJtumpkMqPraX3DI4134esbXPH/s87TYqEWuS5SWCzubkiRJUin4\n/nvo0AGOOgouvRSeG1DEC9/dQYs+Ldhl010Y0XGEQVNVip1NSZIkaRmkBP37wznnwBFHwMSJ8M2c\n98m7vwNFqYg3T3qTbdfdNtdlSsudYVOSJElaSu+9B127ws8/w4ABsF2zOdzwzg3c8M4NXLrbpXTb\nvhvVwsGEqpr8zZckSZKW0KxZcMklsMsucPDBMGwYVN94NK36tmLQZ4MY0XEEp+xwikFTVZqdTUmS\nJGkJvPpqppvZpAmMGQPrbDCLS964jL6j+nL93tdzwnYnEBG5LlPKOcOmJEmSVAJTp0LPnjB4MNx+\nOxxwALz9xdvsdWcHGq/fmHFdx7HB6hvkukyp3DBsSpIkSX+jqAjuvhsuugjatYMJE6CoxgxOef58\nnnrvKW7sXsdsAAAgAElEQVRrcxuHbntorsuUyh3DpiRJkrQI48ZB584QAa+/Do0bw4sfvUiXAV3Y\nY/M9KOxayFqrrJXrMqVyybApSZIkLeDXXyE/H+6/H668Etq3hx9nfc+JT/fkzc/f5O4D72bvLfbO\ndZlSubZcpsfKz8+noKBgeZxKkiRJWibPPQcNG8I330BhIXTokHhi0mM07t2YtVdem/Fdxxs0JaCg\noID8/PxFro+UUpkWEBGprM8hSZIkLasvv4TTTssEzN69Yc894esZX9P9+e68/9379Gvbj53q7JTr\nMqVyJyJIKf1lCmYf/CNJkqQqbe5cuOkmaNYs8ziTceNgjz0S/Ub1o+mdTWm8XmNGdx5t0JSWkPds\nSpIkqcoaPhy6dIG11so80mTrreGTHz+h06Od+GnWT7x6wqs0Wb9JrsuUKiQ7m5IkSapypk+HU06B\ntm3hjDPg1Vdhy63mcfPQm9nh7h3Yd8t9GdphqEFTWgZ2NiVJklRlpASPPZYJmPvvDxMnwtprw4Rp\nE2j/bHtWrrEyQzsMZcu1t8x1qVKFZ9iUJElSlfDJJ9C9O0yeDI8+CjvvDLPnzeayN67htuG3ccXu\nV9CxRUeqhYP/pNLgf0mSJEmq1GbPhquugh12gLw8GDUqEzRHfDWCFn1aMOLrEYzuPJrOLTsbNKVS\nZGdTkiRJldZbb2UmANpsMxgxAjbfHH6b8xvnv3wJD4x7gJv+eRNHNzqaiL88tUHSMjJsSpIkqdL5\n/ns4+2x4+WW45RY49FCIgEGfDqLjcx1ptUkrxncdz7qrrZvrUqVKy7ApSZKkSiMluP9+OO88OOqo\nzARANWvC9FnTOeeVc3j+o+fpvX9vDqh/QK5LlSo9w6YkSZIqhffeywyZ/eUXGDgQWrTILH/2/Wfp\nNrAbB9Y/kMKuhdRauVZuC5WqCMOmJEmSKrSZMzMTAN15J1xyCXTrBtWrw7Rfp9HjhR6MnDKSBw99\nkN022y3XpUpVitNtSZIkqcJ6+WVo3DjT1RwzBk49FapVSzw47kGa9G7CprU2ZWyXsQZNKQfsbEqS\nJKnC+eYbOOMMGDoUevWC/fbLLP9y+pd0GdiFL6d/yYBjB9Byo5a5LVSqwuxsSpIkqcIoKoLevTPd\nzLp1YcKETNAsSkX0HtGb5n2as9MmO/Fup3cNmlKO2dmUJElShTB2LHTunLkfc9AgaNQos/yD7z+g\nw7MdmFs0lzdOeoMG6zbIbaGSADubkiRJKud++QXOOgv23hs6dIC33soEzblFc7lu8HW07teawxsc\nzlsnv2XQlMoRO5uSJEkqt555Bnr0gN12g8JCWG+9zPIx34yh/bPtqb1KbUZ0HMHma22e20Il/YVh\nU5IkSeXOl19mZpadNAnuvRf22COzfNbcWVz+xuXcPepurtv7Ok7c7kQiIrfFSlooh9FKkiSp3Jg7\nF268EZo1g+bNYdy4P4Lm4C8G0+yuZrz3/XuM7TKWk5qeZNCUyjE7m5IkSSoXhg/PTABUuza88w5s\ntVVm+S+zf+GC1y7g8YmPc1ub2ziswWG5LVRSiSy2sxkRK0XEsIgYHRHjI+LS7PIHIuK9iBgXEX0j\nonrZlytJkqTK5qefoFs3OOigzERAr7zyR9B86aOXaHRHI2bMnkFht0KDplSBLDZsppR+B3ZPKTUD\nmgJtImIH4IGU0jYppSbAqkCHsi1VkiRJlUlK8PDD0KABzJsHEyfCv/4FEfDDzB846emT6DKwC30O\n7MO9B93L2qusneuSJS2BEg2jTSn9ln25UnaflFJ6sdgmw4FNSrk2SZIkVVIff5zpZk6ZAo8/Dq1b\n/7Hu8YmP0+OFHhzR4AjGdx3P6iuunrtCJS21EoXNiKgGjAS2AHqllEYUW1cDOB7oUSYVSpIkqdKY\nPRuuvx5uugnOPRdOPx1WWCGzbsqMKXR/vjuTvpvE40c+Tus6rf/+YJLKtZJ2NouAZhFRE3g6Ihqk\nlCZmV98BvJFSGryo/fPz8//3Oi8vj7y8vKUuWJIkSRXTG29A166wxRYwciTUrZtZnlLivjH3ce6r\n59KpRSceOuwhVq6xcm6LlbRIBQUFFBQULHa7SCkt0YEj4hLgl5TSjdnJgrZLKR36N9unJT2HJEmS\nKo/vvoOzz4ZXX4VbboFDDsnclwnw6Y+f0mlAJ36Y+QP92vaj6QZNc1uspCUWEaSU/vIcopLMRrtO\nRNTKvl4F2At4LyI6APsAx5R2sZIkSar4UoJ774WGDaFWrcwEQIcemgma84rmccvQW9j+7u3Zu97e\nDOswzKApVTIlGUa7IXB/9r7NasAjKaXnI2IO8BkwNCIS8GRK6YqyK1WSJEkVxcSJmSGzv/0GL7wA\nzZsXW/ftRNo/254Vq6/IkPZDqF+7fu4KlVRmlngY7RKfwGG0kiRJVcbMmXDFFdCnD1x6aSZwVs8+\njX32vNlc+/a13Dr8Vi7f/XI6tehEtVjsQDtJ5dyihtGWaIIgSZIkaXFeeinzOJMWLWDsWNhooz/W\njfhqBO2fbU+dWnUY1WkUdWrVyV2hkpYLw6YkSZKWyZQpcMYZMHw49OoFbdr8se63Ob9x6aBL6T+u\nPzf+80aOaXQMEX9pgEiqhJbLuIX8/PwSTY0rSZKkimPePLjjDmjSBOrVg8LCPwfNgs8K2O7O7Zg8\nYzLju47n2MbHGjSlSqSgoOBPj7lckPdsSpIkaYmNGQOdO8OKK8Kdd2ZmnJ1v+qzpnPvquQz8cCC9\n9utF263b5q5QSWVuqR99IkmSJM33yy/Qsyf885/QqRO88cafg+Zz7z9Ho96NSClR2LXQoClVYd6z\nKUmSpL9VUJD5ev99eOop2GYbOOEE2GILqJZtXXz767ec9uJpDP9qOP89+L/svvnuuSxZUjlg2JQk\nSdLf2m47uPfezARAv/+eGUI7X0qJ/yv8P3q+1JPjmxzPuK7jWHWFVXNXrKRyw3s2JUmStEgvvAAn\nngibbQZ77QVDhkBeXmZdgx2/5L8/dOWL6V/Qr20/tt94+1yWKilHfM6mJEmSSuznnzP3Zr76Kjz8\nMOyxxx/rilIRfUb2ofugi+mxQw+ePOpJVqy+Yu6KlVQuGTYlSZL0J6++Cu3bw777wrhxULPmH+s+\n/P5DOj7XkVlzZ1FwYgEN12u46ANJqtKcjVaSJElAZqbZrl2hXTvo0wfuuuuPoDm3aC7XDb6Onfrt\nxMHbHMzgdoMNmpL+lp1NSZIkUVCQCZl5eZlu5ppr/rFu5Ncj6fBcB9ZddV2GdxxOvbXq5apMSRWI\nYVOSJKkK+/VXOP98eOKJTCfzgAOKrZv9K5cWXEr/cf25fu/rOb7J8UT8ZQ4QSVooh9FKkiRVUYMH\nQ9Om8OOPMH78n4Pmyx+/TOPejfnml28o7FrICdudYNCUtETsbEqSJFUxM2fCxRfDQw9Br15wyCF/\nrPvut+/o+VJP3vz8TXrv35s2W7XJXaGSKjQ7m5IkSVXIsGHQrBlMnpy5N3N+0Ewp8eC4B2l0RyNq\nr1Kbwm6FBk1Jy8TOpiRJUhXw+++Qnw/33gu33QZHHPHHus9++oyuA7vy1c9f8ewxz7LDxjvkrE5J\nlYedTUmSpEpu5Eho0QLefx/Gjv0jaM4rmsdN79xEyz4t2XXTXRnZaaRBU1KpsbMpSZJUSc2eDVdc\nkZll9qab4JhjYP4cP2O/GUvH5zqy2oqrMaT9EOrXrp/bYiVVOoZNSZKkSmjsWDjxRKhTB8aMgQ03\nzCyfOWcml795OX1H9eXqPa+mXbN2zjIrqUwYNiVJkiqROXPgmmsy92Vefz2ccMIf3cxBnw6i84DO\nNN2gKeO6jmOD1TfIbbGSKjXDpiRJUiVRWAgnnQTrrAOjRsEmm2SW/zjzR85+5Wxe/vhleu3XiwO3\nPjCndUqqGpwgSJIkqYKbOzfTzdx9d+jSBV54IRM0U0o8NuExGt7RkJVrrExht0KDpqTlxs6mJElS\nBfbee5lu5mqrwbvvQt26meWTf55Mt4Hd+PjHj3n8yMdpXad1TuuUVPXY2ZQkSaqA5s2DG26AXXbJ\n3Jf5yiuZoFmUiug1vBfN7mpGy41aMqrTKIOmpJywsylJklTBfPghnHwyVK8Ow4dDvXqZ5RO/nUjH\n5zoSBG+e9CbbrrttbguVVKXZ2ZQkSaogiorg1lthp53gyCNh0KBM0Px97u/kF+Sz2327cXyT43nz\nZIOmpNyzsylJklQBfPIJtGuXebTJkCFQv35m+eAvBtPxuY7Ur12fMZ3HsHHNjXNbqCRl2dmUJEkq\nx1KCO++EVq3ggAPgzTczQXP6rOl0G9iNIx8/kst3v5ynjnrKoCmpXLGzKUmSVE598QW0bw/Tp2dC\n5rbZkbHPvPcMp7xwCm22bMOEbhNYc+U1c1uoJC2EYVOSJKmcSQnuuQfOOw969oSzz4YaNWDKjCmc\n+sKpjJ82ngcOeYDdNtst16VK0iIZNiVJksqRyZOhY0eYNg1efx0aN848zqTPyL5c+PqFdG7RmQcO\nfYCVa6yc61Il6W8ZNiVJksqBlKB/fzjrLDjlFDj/fFhhBXj/u/fpNKATs+bO4vUTXqfx+o1zXaok\nlYhhU5IkKce++QY6dYLPP4eXX4amTWH2vNlc+eb13DT0Ji7Z7RK6b9+d6tWq57pUSSoxZ6OVJEnK\nkZTg//4Pttsu8zViRCZoDps8jBZ9WjBk8hBGdhpJj1Y9DJqSKhw7m5IkSTkwbRp06waTJsGAAbD9\n9jDj9xmc/cJFPDrxUW7c50aObnQ0EZHrUiVpqdjZlCRJWs4efxyaNIEtt4SRIzNBc+AHA2nUuxE/\nz/6Zwq6FHNP4GIOmpArNzqYkSdJy8v330L07jBkDTz8NO+4I036dxslPnMbwr4bTr20/9qq3V67L\nlKRSYWdTkiRpOXjmmcxjTDbeGEaPhlatEveNuY/GvRtTp2Ydxncdb9CUVKnY2ZQkSSpDP/4Ip50G\nQ4bAI4/AP/4BH//wMZ0f68yPs37kxX+9SLMNm+W6TEkqdXY2JUmSysjzz2e6mbVqwdixsNPOc7lu\n8HW06tuKNlu2YViHYQZNSZWWnU1JkqRSNn069OwJr78O/fvD7rvDqCmj6PBAB9ZZdR2GdxxOvbXq\n5bpMSSpTdjYlSZJK0csvZ7qZK64I48ZBq11+4+yXz6bNg204fcfTeem4lwyakqoEO5uSJEmlYMYM\nOPtseOEF6NcP9t4bXvn4FToP6EzrOq0p7FrIuqutm+syJWm5MWxKkiQto0GDoF072GOPTDdz7grf\nc+LTPXnjszfovX9v2mzVJtclStJy5zBaSZKkpfTrr3DqqXD88dCrF/Ttmxj4xUM0vKMha6+8NoXd\nCg2akqosO5uSJElL4a234OSToXVrGD8efo7P2e+hLnz181c8e8yz7LDxDrkuUZJyys6mJEnSEpg5\nMzPT7FFHwQ03wL33zeP+92+mRZ8W7LrprozsNNKgKUnY2ZQkSSqxoUPhxBOhefNMN/OruePYqV8H\nVltxNYa0H0L92vVzXaIklRt2NiVJkhZj1iw491w4+GC48kq4578zuWHMBez1373o3KIzr5/wukFT\nkhawXDqb+fn55OXlkZeXtzxOJ0mSVGrefTfTzdxmm8xMsxN/K2C7OzvRdIOmjOs6jg1W3yDXJUpS\nThQUFFBQULDI9ZFSKtMCIiKV9TkkSZJK2+zZcPnl0KcP3Hwz/POgHzn31XN48eMX6bVfL9pu3TbX\nJUpSuRARpJRiweUOo5UkSVrA6NGw/fYwdiyMHp1YYbvHadS7IStWX5EJ3SYYNCWpBJwgSJIkKWvO\nHLjqqswzM2+4AfLaTqbrC9358PsPefzIx2ldp3WuS5SkCsPOpiRJEpnZZVu1gmHDYOSoImZscwfN\n+zSj+QbNGd15tEFTkpaQnU1JklSlzZ0L118PN94I11wDOx44kaMHdATgjZPeoMG6DXJcoSRVTHY2\nJUlSlTVpEuy8M7z+OgwZ/jtfbpFP3v27cVzj43jr5LcMmpK0DOxsSpKkKmfePLjpJrj22syMs43a\nDOagAR2pX7s+YzqPYeOaG+e6REmq8AybkiSpSvngAzjpJFhpJXj17enc9dH5XP7EM9y6760cuu2h\nRPxl9n5J0lJwGK0kSaoSiorgllugdWs45hjoccczHPB8I+YWzWVCtwkc1uAwg6YklSI7m5IkqdL7\n5BM4+eTM8NlnB03hxkmnMv618TxwyAPsttluuS5PkiolO5uSJKnSKiqCO+6AHXaAA9sWcfxNd3Pw\ni9uxzTrbMLbLWIOmJJUhO5uSJKlS+vxzaN8efvkF+j//PtdM7MSssbN47YTXaLx+41yXJ0mVnp1N\nSZJUqaQEd98NLVvC7nvNZv9rruT4gp05bNvDGNJuiEFTkpYTO5uSJKnSmDwZOnSA776DW58YxtUT\nOlLn6zqM7DSSumvWzXV5klSl2NmUJEkVXkpw333QvDlsv/MvtL7sdHqOOJjzdzmfAccMMGhKUg7Y\n2ZQkSRXalCnQqRN8+SVc0v95/jOpG7vP2Z3CroXUXrV2rsuTpCorUkple4KIVNbnkCRJVU9K8H//\nB2ecAcd3mcbkRqczYsow7jrgLvaqt1euy5OkKiMiSCn95UHFDqOVJEkVztSpcNhhcOVViW533U//\nNRqz6ZqbML7reIOmJJUTdjYlSVKF8uij0KMHHNLuYz7Yugs/zvqevm370nzD5rkuTZKqJDubkiSp\nQvvuOzjqKLj40rkccfP1PLZmK9ps9U+Gdxxu0JSkcsgJgiRJUrn39NPQrRvsedwoVsnrwHtzajO8\n43DqrVUv16VJkhZhsZ3NiOgXEVMjYtwCy0+NiPciYnxEXFN2JUqSpKrqhx/guOPgzPN+I++qs3l5\nvTb0bH06Lx/3skFTksq5kgyjvRf4Z/EFEZEHHAg0Sik1Bv5T+qVJkqSqbMAAaNIEfl3/VVKXxkTN\nrxnfdTwnbHcCEX+5NUiSVM4sdhhtSuntiFjwSchdgWtSSnOz23xXFsVJkqSq56efMo8zeX3o9zS6\n6ExGzyqg9/69abNVm1yXJklaAks7QVB9YNeIGBoRgyKiZWkWJUmSqqaXXoLGTRJfrvUQv7dvxLab\nrUVht0KDpiRVQEs7QVANYM2U0o4RsT3wKLDIGyfy8/P/9zovL4+8vLylPK0kSaqMZsyAM8+EgW9/\nzsZndWXaCpN5tu0z7LDxDrkuTZK0gIKCAgoKCha7XYmes5kdRvtcSqlJ9ufnyQyjfTP780dAq5TS\n9wvZ1+dsSpKkRXr9dTi5/Tw2Ovh2Ptjgcs5qfSZntT6LFaqvkOvSJEklsKjnbJa0sxnZr/meBvYE\n3oyI+sAKCwuakiRJi/LLL3DeefDYW+NYs3tHVl53Vd45YAj1a9fPdWmSpFJQkkefPAQMAepHxBcR\ncTJwD1AvIsYDDwEnlG2ZkiSpMnnzTWjcbBZvrnAhRf/ai3P26MTrJ7xu0JSkSqREw2iX6QQOo5Uk\nSVm//QYXXAAPvP0GKx3ekZ23aMqtbW5lg9U3yHVpkqSltKhhtIZNSZK0XAwZAsd3+pHY5xxmbvwi\nvQ/oRdut2+a6LEnSMlrWezYlSZKWyqxZcPElib6Dn6DaMadxdNODuXqvCdRcqWauS5MklSE7m5Ik\nqcyMGAHHdpnMr3ndWaPuh9x7SF9a12md67IkSaVoUZ3NxU4QJEmStKR+/x0uuLCIPc69g6mHNKPz\ngc0Z1220QVOSqhA7m5IkqVSNGgVHnTKRH3buyBZbwH2H3U2DdRvkuixJUhlxgiBJklSmZs+Gy676\nnZtGXE31HXtxzb6X0aVlZ6qFA6kkqTJzgiBJklRmxo2Dw84YzDfbd+QfR9Sn76Gj2aTmJrkuS5KU\nQ4ZNSZK0VG6+GZ58Ej6bMp2vtrmAai2fZquPbuWf6x/KJjX/8g/ckqQqxrApSZKWSosWieuff5Qf\njuzJMU0O4La2hay1ylq5LkuSVE4YNiVJ0hKZPh26XvgRT/7enfX3nMJrxz7mLLOSpL/wjn1JklRi\njz7xO3WOu4Knau/IhUfvzUdnjTRoSpIWys6mJElarK+/hqPOHcTw9bqy/b5b89Bxo9i01qa5LkuS\nVI4ZNiVJ0iIVFcF/7pzGJW+dzcpbF/DAYbdyROODcl2WJKkCcBitJElaqImTitj6mLu5cHIjjjlw\nPSZfMMGgKUkqMTubkiTpT2bPhjOuHk+fKV3YpEkRw05+heYbbZfrsiRJFYydTUmS9D+vv/UrG514\nDn3n7Mnlh53IxxcMNmhKkpaKnU1JksTPP8Mx+c/xUrVTab3DP3i0w3g2WGP9XJclSarADJuSJFVx\n9zzxJacM6MGKG0/kiSPv4aAme+S6JElSJeAwWkmSqqjJX8+ladcb6fhuM47arRlT88cZNCVJpcbO\npiRJVUxKcEHvoVw/qQt11luXMZ3eofHGW+W6LElSJWPYlCSpCnl3wo8cdMsFfFv7Ga7Y9wbO3e9o\nIiLXZUmSKiHDpiRJVcDs2Ynjr/s/Hpt+Fq03P4RxPSZSe7U1c12WJKkSM2xKklTJPT7oA056vBvV\nVvueJ496ioNbtsp1SZKkKsCwKUlSJfXdT7M44JprGc7t/8/encdpPe//H3982ixJWSJFCimlpFAo\nRijkEMeeNSKpRpIjkhHZlTZakKX42tcWaRna972mclq0KKVSaZtmPr8/rs75OYiW65rPzFyP++3m\nJs01n/dTV8s8e70/7w+NKz/KG81aULigf/RLknKGf+JIkpQPdXpvGGmTmlO6UFVm3DeNqscfG3Uk\nSVKSsWxKkpSPzF6ymsu7P8CKAmPoUKsHj11/edSRJElJyrIpSVI+kJWdTZOefXhneQfOPLAJk9rO\noWSJolHHkiQlMcumJEl53FeTZ9D4vXvIyizEe1cP5/qUqlFHkiTJsilJUl61YctmrujyOKM39uea\nI5+m/4N3UKRwgahjSZIEWDYlScpzwjCk86DPafddK47cVI8JLWZzZuWSUceSJOl/WDYlScpD5q1c\nyuWvtmTppoW0PeVtnr47hSCIOpUkSX9k2ZQkKQ/IzMrk3re70G/B81Td3Jqlj35EmVJFoo4lSdJu\nWTYlScrlBs4aw83vNWPb2jL0vWwCTRqdGHUkSZL+lmVTkqRc6uct67im17/47sdBXFKgC//30rUU\nK+aeWUlS3mDZlCQplwnDkM7D3+GRkQ9x6LLrGNZsLhecUzzqWJIk7RXLpiRJucjsVRk0eu1elvy4\nkWZlvqLL62dQuHDUqSRJ2nuWTUmScoGtmVtp9dHTvDmrF+V/6MCcDs2peHLBqGNJkrTPLJuSJEXs\ni7lfc9v7zdm6qCbPnTeD1p1K+zgTSVKeZ9mUJCkiP276kZvebs3oxRM5Z31PPnjxUo4+OupUkiTF\nR4GoA0iSlGyysrN4bmQPyr9QjanDT+T/zpvNt69bNCVJ+YuTTUmSctCUlVO57p17WLboYK47+Fte\nfasyxYpFnUqSpPizbEqSlAM2bt/I/Z8/xoAZ71NyxnN8++itnH22N2ZKkvIvy6YkSQkUhiEfzvmY\nuz+5n22zG9Cm6hzSPjmCIkWiTiZJUmJZNiVJSpBF6xdx2/stmPL9Uk5e8B7vv1CXihWjTiVJUs7w\ngCBJkuJsR9YOnhz5DKd2PYtpn53HSydPY+pnFk1JUnJxsilJUhx9t/Q7bv2gGWsWluf8TZN4o195\njjkm6lSSJOU8y6YkSXGwdstaUgc+xOczv+GA9JcZ0OZqGjXyACBJUvKybEqStB+yw2z6TXuTNoPa\nsXP6TdxYai4vDSzGoYdGnUySpGhZNiVJ2kdzfppDk0/uZcGibRw+egjvvHA6554bdSpJknIHDwiS\nJGkvbcncwsPftKN2rxTmvH8DrYqOY95Ii6YkSb/lZFOSpL0waOEg7v7sPrb/uzaV5s/krZ7HULly\n1KkkScp9LJuSJO2B5RuX02rQ/aRnTCfri94827Q+97wCBdwjJEnSn7JsSpL0F3Zm76TnxJ48PuJJ\nCk1rzrk736HXlwdRpkzUySRJyt0sm5Ik7cbEFRNp+nkz1i4vQZHPRvPqk5W4+moIfKKJJEl/y7Ip\nSdLv/LLtFx4d8SgDpn1M8M0LXFOxMc+PDShRIupkkiTlHZZNSZJ2CcOQ9+e8z/2DH+CgZf/giJFz\neKPn4Zx3XtTJJEnKeyybkiQB36/7nuYD72PuDz+y7f8+ommjc3h0Ehx4YNTJJEnKmyybkqSktn3n\ndp4f8zydx3blsDkPU/rfqbz+bmGqVo06mSRJeZtlU5KUtEYuHkmzr+6lwLqK0G8qrduUpflbULBg\n1MkkScr7LJuSpKTz068/0fabtnw9fyRFhnfntAOuZOgoOO64qJNJkpR/+ChqSVLSyA6z6TulL1V6\nnsrMcSUJe8zlhTuv5IsvLJqSJMWbk01JUlKYtXoWzQY24+d12RR89xtq1DyN4TPg8MOjTiZJUv6U\nI2UzLS2NlJQUUlJScmI5SZL+69cdv/LEt0/wxtQ3KbfoSXaOaMq7vQtQr17UySRJytvS09NJT0/f\n7ceDMAwTGiAIgjDRa0iS9Ge+nP8lLQe3pFRmHb7v8RJNbzqaDh3goIOiTiZJUv4RBAFhGAa//363\n0UqS8p1lvyyj1ZBWTF8xhxKjXidr+YUM/wJOOy3qZJIkJQ8PCJIk5Rs7s3fSeVxnTu99OpsWns7G\n52Zy23kXMn68RVOSpJzmZFOSlC+MXz6eZl8144CdJTnys3EULl6BKROgXLmok0mSlJwsm5KkPG39\n1vW0G96OzzO+oPpPLzH17Rvo0jngxhsh+MPdI5IkKae4jVaSlCeFYciAmQOo/Eplli8rQOE+czlq\n9Y3MmR1w000WTUmSouZkU5KU5yz4eQHNBzZn9aafqTb3M+aOrMXrveHii6NOJkmS/sPJpiQpz9i2\ncxtp6Wmc8/o5HLWhIaufnES1w2sxa5ZFU5Kk3Ga/JptBELQG7gSygVnAHWEY7ohHMEmSfmvYomE0\nH6n4d08AACAASURBVNicE4pW5ZRvp5Hx43EMGQQ1akSdTJIk/Zl9nmwGQVAaaAnUCMOwGrHiekO8\ngkmSBLBq8yoaf9KYu764i3M3d2byQx9zRcpxTJxo0ZQkKTfb33s2CwJFgyDIBg4GVu5/JEmSIDvM\npvfk3nRI70DD0ndSfMAclpcoysSJcMIJUaeTJEl/Z5/LZhiGK4MgeAn4AdgCDA3DcFjckkmSktb0\nVdNp9lUzClCIhmtHMPjFqrzwAtxyi6fMSpKUV+xz2QyCoARwJXA88AvwURAEN4Vh+O7vX5uSkkK5\ncuUoV64cKSkppKSk7HNgSVL+tWn7Jh5Pf5wBswbQuNTTfNrhDsqfXYBZs+Coo6JOJ0mSANLT00lP\nT2fJkiUsWbJkt68LwjDcpwWCILgGaBCGYdNd/30LUCsMwxa/e124r2tIkpJDGIZ8lvEZqUNSOad0\nPbIHv8DE9JK8+ipcemnU6SRJ0l8JgoAwDP+w92h/7tn8AagdBMGBwHbgQmDSflxPkpSElm5YSovB\nLVj480JuOeRtXk9N4cYbYfZsOOSQqNNJkqR9tT/3bE4MguAjYBqQuevffeIVTJKUv2VmZdJlfBee\nH/M8t1dqzfa3P+KrlQfw5Zdw5plRp5MkSftrn7fR7vECbqOVJP3OmB/G0GxgM445pDRnrH6FPs+e\nyIMPQps2ULhw1OkkSdLeSMQ2WkmS9srPW37m4WEPM+j7QaRW6sIHj1/L+GIB48ZBhQpRp5MkSfFU\nIOoAkqT8LwxD3pr+FlVeqUKh4ECuWzOXF2+/jub3BgwfbtGUJCk/crIpSUqoeWvmce/Ae9m0YxOP\nV/iKzg+cQY0aMHMmlCoVdTpJkpQoTjYlSQmxecdmHhn+CHX71aXB8VdTdfxEnml5Bl26wPvvWzQl\nScrvLJuSpLgKw5D+M/tTqUcllv2yjI7HzKDbTa049JCCzJkDl18edUJJkpQT3EYrSYqbySsn02pw\nK3Zk7aBnyge8nnYOryyCTz+F2rWjTidJknKSjz6RJO231ZtX88jwRxj0/SCevOAphr1wB598XIDS\npaFsWSiwax9No0Zw//3RZpUkSfHlo08kSXG3I2sH3Sd055nRz3DbabfxzZUZPNiiOD/9BBMnQvXq\nUSeUJElR8Z5NSdI+GbxwMNVercawxcMYdcdoTl7yEhecXZy6dWHCBIumJEnJzsmmJGmvLPx5Ia2/\nbs38n+fTpUEXKhdqSNMbAjZtgvR0qFIl6oSSJCk3cLIpSdojG7dv5KFvHuLs18/mvOPPY+Y9s1ky\n9HLOOiugQQMYO9aiKUmS/r8cmWympaWRkpJCSkpKTiwnSYqj7DCbd2a8Q7vh7ah/Yn1m3TuLzauO\nocFFkJUFY8ZAxYpRp5QkSTktPT2d9PT03X7c02glSbs1YfkEWg1pBUC3S7pxxjG16NoVnn4a2reH\nli2hYMGIQ0qSpEh5Gq0kaY/9uOlH2g1vx9B/D+WZC5/hltNuYcH8AtStC4ULw/jxcNJJUaeUJEm5\nmfdsSpL+a/vO7Tw/5nmqvlqVo4seTUaLDBqfehsvPF+AOnWgcWMYOdKiKUmS/p6TTUkSYRgycOFA\nWn/dmkpHVmLcneOocEQFZs+GJk3g0ENh0iQoXz7qpJIkKa+wbEpSkstYm0Hrr1uzaP0iul3SjUsr\nXEpmJjz1FHTtCp06QdOmEPzhTgxJkqTds2xKUpL6ZdsvdPy2I2/NeIt2ddrR8oaWFClYhOnT4Y47\noFQpmDoVjjsu6qSSJCkv8p5NSUoy2WE2r099nUo9K7Fh2wbmNJ9Dm3PaQFYROnSA+vUhNRUGDbJo\nSpKkfedkU5KSyNhlY2k1uBVFChbhyxu/5IzSZwAweXJsmlm+PEyfDqVLRxxUkiTleZZNSUoCKzau\n4F/D/kX6knSevehZGldtTBAEbNsGaWnQrx907gw33eS9mZIkKT7cRitJ+di2ndt4ZtQznNbrNMoW\nL0tGiwxurnYzQRAwbhycfjp8/z3MnBl7rIlFU5IkxYuTTUnKh8Iw5PP5n9NmaBuqHlWVCXdN4MTD\nTwRgyxZ47DF4913o1g2uvTbisJIkKV+ybEpSPjN3zVzuH3I/yzcup1fDXlx84sX//dioUXDnnVCz\nZmyaWbJkhEElSVK+ZtmUpHxiw7YNpKWnMWDWANrXbU/zM5tTuGBhAH79Fdq1g48/hp49oVGjiMNK\nkqR8z3s2JSmPy8rOos+UPlTqUYktmVuY23wuqbVT/1s0R46EqlVhwwaYNcuiKUmScoaTTUnKw0Yt\nHUXqkFSKFinKoMaDqHFMjf9+bNMmeOgh+Oor6NULGjaMMKgkSUo6lk1JyoOW/bKMh4Y9xJgfxvD8\nxc9zfZXrCX5zlOzQodC0KVx8cWyaWaJEhGElSVJSsmxKUh6yNXMrL459kZcnvMx9Z97Ha/94jaJF\niv734xs2QJs2MGwY9O0L9etHGFaSJCW1HCmbaWlppKSkkJKSkhPLSVK+E4Yhn8z7hAe/eZAax9Rg\nctPJlD+s/P+8ZuBAaNYMLr88Ns089NCIwkqSpKSQnp5Oenr6bj8ehGGY0ABBEISJXkOS8rNZq2eR\nOiSVNVvW0PWSrtQrX+9/Pr5uHaSmwpgx8NprUK/ebi4kSZKUAEEQEIZh8Pvv9zRaScql1m1dR4tB\nLbjw7Qu5+pSrmXbPtD8UzU8/hVNPhcMPj00zLZqSJCm38J5NScpldmbvpM+UPqSlp3FN5WuYe99c\njjz4yP95zZo10LIlTJ0KH3wAdepEFFaSJGk3LJuSlIukL0kndUgqhx14GN/c8g2nlTrtfz4ehvDh\nh7Fts40bwxtvwMEHRxRWkiTpL1g2JSkXWLphKW2/acuEFRN48eIXuabyNf/zKBOA1auheXOYOze2\nfbZ27YjCSpIk7QHv2ZSkCG3J3EJaeho1+tSgSskqzLtvHtdWufZ/imYYwoABUK0anHwyTJtm0ZQk\nSbmfk01JikAYhnw490PaftOW2sfWZto90yhbvOwfXrdyZexxJosXxx5tcsYZEYSVJEnaB5ZNScph\nM1bNIHVIKuu3reftRm9zfrnz//CaMIQ334R//QvuvRc++giKFMn5rJIkSfvKsilJOWTtlrU8NuIx\nPsn4hLTz02hasymFCvzxt+EffoB77oFVq2DoUKhePYKwkiRJ+8l7NiUpwXZm76T7hO5U7lmZQgUK\nMe++edx75r1/KJphCL17Q82asUeZTJxo0ZQkSXmXk01JSqDhi4aTOiSVow85mhG3jeDUo07909ct\nXgx33QWbNkF6OlSpkrM5JUmS4s3JpiQlwOL1i7n6/au568u7ePKCJxl2y7A/LZrZ2dCjB5x5JjRo\nAGPHWjQlSVL+4GRTkuLo1x2/8szoZ3h18qu0rt2aAVcP4KDCB/3pa7//Hu68EzIzYfRoqFQph8NK\nkiQlkJNNSYqDMAx5b9Z7VOpZiUXrFzGj2Qzan9f+T4tmVhZ06RJ7VmajRjBqlEVTkiTlP042JWk/\nTf1xKq0Gt2JL5hbe++d71ClbZ7evzciAJk2gcGEYPx5OOikHg0qSJOUgJ5uStI9++vUnmn7RlMsG\nXMZtp93GpKaTdls0d+6E55+PnTJ7000wcqRFU5Ik5W9ONiVpL2VmZdJzUk86jerEzVVvJqNFBiUO\nLLHb18+eHZtmFisGkyZB+fI5GFaSJCkilk1J2gtD/z2U+4fcz3HFj+O727/jlJKn7Pa1mZnw3HPQ\ntSt06gRNm0IQ5GBYSZKkCFk2JWkP/Hvdv3lg6APM/mk2net35oqKVxD8RXOcPh3uuAOOPhqmTIGy\nZXMwrCRJUi7gPZuS9Bc279hMu2HtqPVaLc4+9mzmNp/LlZWu3G3R3LEDOnSA+vUhNRUGD7ZoSpKk\n5JQjk820tDRSUlJISUnJieUkab9lh9kMmDmAh4c/TL3y9Zh570xKFyv9l58zeXJsmlm+fGyyWfqv\nXy5JkpSnpaenk56evtuPB2EYJjRAEARhoteQpHiatGISrYa0Ymf2Trpd0o2zjzv7L1+/bRs88QS8\n8QZ07hw7bdZ7MyVJUrIIgoAwDP/w1Y/3bErSLqs3r6bd8HYM/n4wnep14vbqt1Mg+Ou7DcaPj500\ne8opMGMGlCqVQ2ElSZJyOe/ZlJT0dmTt4MWxL1LllSocftDhZNyXQZPTm/xl0dy6FR58EBo1grQ0\n+Ogji6YkSdJvOdmUlNQGLRxE669bc+JhJzKmyRgqHlnxbz9n9OjYNLNGDZg1C0qWzIGgkiRJeYxl\nU1JSWvjzQlp/3ZoFPy+gS4MuNDy54d9+zq+/wiOPwIcfQs+ecNVVORBUkiQpj3IbraSksnH7Rh76\n5iHOfv1szj/+fGY3n71HRXPkSKhWDdavh9mzLZqSJEl/x8mmpKSQHWbz9oy3eWT4IzQ4qQGzm8+m\n1CF/f5Plpk3w0EPw5ZfQqxdcfnkOhJUkScoHLJuS8r0JyyfQakgrAgI+u+Ezzipz1h593tCh0LQp\nXHRRbJpZokSCg0qSJOUjlk1J+daPm37k4eEPM2zRMJ658Blurnbz3z7KBGDDBmjTBoYNg759oX79\nHAgrSZKUz3jPpqR8Z/vO7Tw3+jmqvlqVYw45hoz7Mrj1tFv3qGgOHAhVq0KRIrGTZi2akiRJ+8bJ\npqR8IwxDvlrwFQ8MfYBKR1Zi3J3jqHBEhT363HXr4P77Y481eestqFcvwWElSZLyOcumpHwhY20G\n9w+5nyUbltD90u5cctIle/y5n30G990H//wnzJwJhxySwKCSJElJwrIpKU/7ZdsvPPHtE7GTZus+\nQouzWlCkYJE9+ty1a6FlS5g8Gf7v/6Bu3QSHlSRJSiLesykpT8rKzuK1qa9RsUdFNm7fyJzmc3jg\n7Af2uGh++GHs3swyZWDGDIumJElSvDnZlJTnjPlhDK2GtOLAQgcy8KaB1Cxdc48/d/Xq2JbZOXPg\n00+hdu0EBpUkSUpiTjYl5RkrNq7g5k9u5vqPrueB2g8w+o7Re1w0wxAGDIBq1aBCBZg2zaIpSZKU\nSE42JeV623Zu46WxL9F5fGea1WxGRosMDimy56f4rFwJzZrB4sWxR5uccUYCw0qSJAlwsikpFwvD\nkM8yPqPKK1WY/ONkJjWdRKcLO+1x0QxD6NcPqleH00+PHQRk0ZQkScoZTjYl5Upz18wldUgqKzet\npPflvbnohIv26vOXLYO774ZVq2Do0FjhlCRJUs5xsikpV1m/dT2pg1M5/83z+cfJ/2D6PdP3qmiG\nIfTpAzVqwLnnwsSJFk1JkqQoONmUlCv851EmHdI7cFWlq5jbfC4li5bcq2ssWQJ33QW//AIjR8Kp\npyYmqyRJkv7efpfNIAgKAJOB5WEYXrH/kSQlm1FLR9FqSCuKFSnG1zd/TfVSezeKzM6GV1+Fxx+H\ntm2hTRso5F+lSZIkRSoeX46lAnOBQ+NwLUlJZNkvy2j7TVvGLhvLCxe/wHVVriMIgr26xvffx6aZ\n27fD6NFQqVKCwkqSJGmv7Nc9m0EQHAtcBrwWnziSksHmHZvp+G1HqveuzslHnMy8++Zx/anX71XR\nzMqCLl1iz8q88kqLpiRJUm6zv5PNLkBboHgcskjK57bt3Ebvyb15ZvQzpJRLYcrdUyhXotxeXycj\nA5o0iW2VHTcOKlSIf1ZJkiTtn30um0EQNARWh2E4PQiCFGC3I4mUlBTKlStHuXLlSElJISUlZV+X\nlZQHZWZl8taMt+j4bUdOK3UaQ24estf3ZQLs3AmdO8Pzz0NaGjRvDgU8U1uSJClHpaenk56ezpIl\nS1iyZMluXxeEYbhPCwRB8DRwM7ATOAgoBnwShuGtv3tduK9rSMrbssNs3p/9Ph3SO3DcocfRqV4n\nzj7u7H261uzZsWlmsWLw2mtQvnycw0qSJGmfBEFAGIZ/GD7uc9n83cXPB9r82Wm0lk0p+YRhyJcL\nvqT9iPYcXPhgOtXrxIUnXLhP18rMhOeeg65doVMnaNoU9vIMIUmSJCXQ7sqmDweQFFfDFw3n0RGP\nsiVzC0/Ve4p/nPyPvT5h9j9mzIA77oCjjoIpU6Bs2TiHlSRJUsLEZbL5lws42ZSSwrhl43h0xKMs\n27iMjikduf7U6ykQ7NsNlTt2xKaYr74am2refrvTTEmSpNzKyaakhJixagbtR7ZnxqoZdDi/A7ed\ndhuFCxbe5+tNmRKbZh5/PEybBmXKxDGsJEmScoxlU9I+WfDzAjqM7ED6knTa1WnHh9d+yIGFDtzn\n623bBh07wuuvw0svQePGTjMlSZLyMsumpL2ydMNSOn7bkS8WfEHr2q157YrXOKTIIft1zfHjYyfN\nVqoUu0+zVKk4hZUkSVJkLJuS9siqzat4etTTDJg1gGY1m7GgxQIOO+iw/brm1q3w2GPQvz906wbX\nXus0U5IkKb+wbEr6S+u2ruOFMS/QZ2ofbql2C3Obz+XoQ47e7+uOHh2bZtaoAbNmQcmScQgrSZKk\nXGPfjoqUlO9t2r6Jp757ipO7n8zaLWuZds80Xr7k5f0umk88Edsme+GFUKQIrFoVm2i+/HKcgkuS\nJClXcLIp6X9s27mNXpN78ezoZ6lXvh7j7hxHhSMq7P91t0HnztC9OzRtCo88AsWKxSGwJEmSciXL\npiQAMrMyeXP6m3T8riM1jqnB0FuGUu3oavt93TCETz6Btm2henWYMAFOPDEOgSVJkpSrWTalJJcd\nZvN/s/+PDiM7UK5EOT689kNqH1s7LteeORNSU2HtWujbN7Z1VpIkScnBsiklqTAM+WL+F7Qf2Z5D\nihxCn3/0oV75enG59po10KFDbKKZlhbbNlvI320kSZKSil/+SUkmDEOGLRpG+5Ht2bZzG89c+AwN\nKzQkiMMzRzIzoWdP6NQJbroJ5s2Dww+PQ2hJkiTlOZZNKYmMXTaWR0c8yoqNK3jygie5tsq1FAji\ncyj14MHwwANQtix8+y1UrhyXy0qSJCmPsmxKSWD6qum0H9GeWT/N4vHzH+fW026lUIH4/PKfPz9W\nMhcujJ0227AhxGFIKkmSpDzO52xK+dj8tfO5/qPruXTApTQ4sQELWiygyelN4lI0N2yIlcxzz4V6\n9WD2bLj8coumJEmSYiybUj60dMNSmnzehDr96nB6qdP5vuX3tKzVkgMKHbDf187Kgj59oFIl2LwZ\n5s6FNm2gSJE4BJckSVK+4TZaKR9ZtXkVnb7rxLuz36X5Gc1Z2HIhJQ4sEbfrp6fD/ffDoYfG7tE8\n/fS4XVqSJEn5jGVTygfWbV3H82Oep8+UPtxe/Xbm3TePo4oeFbfrL1kCbdvCpEnw/PNw7bVul5Uk\nSdJfcxutlIdt2r6JJ799kpO7n8y6reuY0WwGnRt0jlvR3LwZ2reHmjWhWrXYo0yuu86iKUmSpL/n\nZFPKg7ZmbuXVya/y3JjnuOiEixh/13hOOvykuF0/OxvefRcefhhSUmDGDDj22LhdXpIkSUnAsinl\nIZlZmbwx7Q2eGvUUZ5Q+g2G3DKPq0VXjusaECZCaGjsI6IMP4Jxz4np5SZIkJQnLppQHZGVn8d7s\n90hLT+OEw07g4+s+5qwyZ8V1jZUrY5PM4cPh6afhlluggBvtJUmStI8sm1IuFoYhn2V8xmMjH6P4\ngcV57YrXSCmXEtc1tm2Dzp3hpZfg7rshIwOKFYvrEpIkSUpClk0pFwrDkG8WfcOjIx5lZ/ZOnrvo\nOS6rcBlBHE/mCUP45BN48MHYI0wmTYITTojb5SVJkpTkLJtSLjP6h9E8OuJRVm9eTccLOnJN5Wso\nEMR3P+uMGbHnZf78M7z+OtSrF9fLS5IkSZZNKbeY+uNU2o9oz9w1c3n8/Me55bRbKFQgvr9E16yB\nxx6DTz+FtDRo2hQK+buAJEmSEsDjP6SIZazN4LoPr+Pydy/nsgqXMb/FfO44/Y64Fs0dO6BLF6hc\nGQ48MHZf5r33WjQlSZKUOH6pKUVkyYYlPPHtEwxcMJA2Z7eh35X9KFqkaNzXGTwYWreGcuXg229j\nhVOSJElKNMumlMN+3PQjnUZ14r3Z73HfmfexsOVCih9YPO7rZGTAAw/A99/HppqXXQZxPF9IkiRJ\n+ktuo5VyyM9bfuZf3/yLU189lQMKHkDGfRl0vKBj3Ivmhg2xSWbdunDRRTB7NjRsaNGUJElSzsqR\nspmWlkZ6enpOLCXlOhu3b6Tjtx2p2KMiv2z/hRnNZvBSg5coWbRkXNfJyoLevaFiRfj1V5gzJzbZ\nLFIkrstIkiRJAKSnp5OWlrbbjwdhGCY0QBAEYaLXkHKjrZlbeWXSKzw/9nnqn1iftPPTOPHwExOy\nVnp67FEmxYvDyy/HnpspSZIk5YQgCAjD8A/76LxnU4qzHVk7eH3q63Qa1YmzypzF8FuHc+pRpyZk\nrcWLoW1bmDwZXngBrrnG7bKSJEnKHSybUpxkZWcxYNYA0tLTqHBEBT69/lPOLHNmQtbavBmeeQZ6\n9Yrdn/nOO3DQQQlZSpIkSdonlk1pP4VhyCfzPqFDegcOO/Aw+l3Zj/PLnZ+QtbKzoX9/eOQRuOAC\nmDEDjj02IUtJkiRJ+8WyKe2jMAwZ+u+hPDriUbLDbF68+EUuOekSggTtY50wAVq1gjCEDz+Es89O\nyDKSJElSXFg2pX0waukoHh3xKGu2rOHJC57k6lOupkCQmMOdV6yAdu1g+HB4+mm45RYo4EOLJEmS\nlMtZNqW9MGXlFNqPbE/G2gzSzk+jcbXGFCqQmF9GW7dC586xf+65BzIyoFixhCwlSZIkxZ1lU9oD\nc9fMpcPIDoxbPo5H6z7K5zd8TpGCiXmAZRjCxx/HTpmtUQMmTYITTkjIUpIkSVLCWDalv7B4/WLS\nvk1j8MLBtD2nLW9f9TYHFz44YetNnx57Xua6dfDGG7FDgCRJkqS8yDu/pD+xctNKmg9szpl9z6R8\nifIsbLmQtue2TVjRXLMmtlW2QQO44QaYOtWiKUmSpLzNsin9xtota2k7tC1VX61K0cJFyWiRQVpK\nGsUPLJ6Q9XbsgC5doHLl2HMyMzKgWTMo5J4DSZIk5XF+SSsBG7dvpPO4zvSY2IPrqlzHzGYzKXNo\nmYSuOWgQtG4N5cvDd9/BKackdDlJkiQpR1k2ldS2ZG6h58SevDD2BS6tcCkTm07khMMSexpPRgY8\n8AD8+9+xk2YvuwwS9GhOSZIkKTKWTSWlHVk7eG3qa3Qa1Ymzjz2b9NvTqVyyckLXXL8eOnaE/v1j\nz8387DMokpgDbSVJkqTIWTaVVLKys+g/sz9PfPsEFY+syBc3fEHN0jUTu2YWvPYadOgAjRrBnDlw\n1FEJXVKSJEmKnGVTSSE7zOaTeZ/QYWQHjjj4CN5q9BZ1j6+b8HVHjow9yqRECRgyBE4/PeFLSpIk\nSbmCZVP5WhiGDPl+CO1Htgegc4PONDixAUGCb5JcvBgefBCmTIEXX4R//tP7MiVJkpRcLJvKt75b\n+h2PjniUn7f8zJMXPMnVp1yd8JK5eTM88wz06hU7abZ//9gjTSRJkqRkY9lUvjN55WTaj2jPgp8X\nkJaSRuOqjSlYoGBC18zO/v8H/9SrBzNnQpnEPjlFkiRJytUsm8o35vw0hw7pHRi/fDzt67bnzhp3\nUqRg4o97HT8eUlNj3/74Y6hdO+FLSpIkSbmeZVN53qL1i0hLT+Prf39N23Pa0v+q/hxUOPF7V1es\ngIcfhhEjYltnb74ZChRI+LKSJElSnuCXxsqzVmxcwb1f3ctZfc/ixMNOZGHLhTx4zoMJL5pbt8JT\nT0G1alC2LMyfD7featGUJEmSfsvJpvKctVvW8uzoZ+k3vR93nn4n81vM54iDj0j4umEY2ybbti3U\nqAGTJsEJJyR8WUmSJClPsmwqz/hl2y90HteZnpN6cn2V65l17yxKFyudI2tPnx67L3PDBnjjDbjg\nghxZVpIkScqz3PinXG9L5haeH/M8FbpXYOkvS5nUdBI9G/bMkaK5Zg3ccw80aAA33hh7bqZFU5Ik\nSfp7lk3lWjuydtBzYk9O6nYSk1ZO4tvbv+XNRm9S/rDyiV97B3TuDJUrw8EHQ0YGNGsGhdwLIEmS\nJO0Rv3RWrrMzeyf9Z/YnLT2NyiUr89VNX1HjmBo5tv6gQdC6dex+zFGjoFKlHFtakiRJyjcsm8o1\ntu/czgdzPuDp0U9zVNGj6H91f+qUrZNj68+bBw88AIsWQZcucNllOba0JEmSlO9YNhW5VZtX0Wty\nL3pP6U3Vo6rS9ZKuXHzCxQRBkCPrr18PTzwBAwbAI4/AffdBkSI5srQkSZKUb1k2FZnJKyfTbUI3\nvlzwJddXuZ7htw6ncsnKObZ+Vhb07QuPPw6NGsGcOXDUUTm2vCRJkpSvWTaVozKzMvlk3id0m9iN\n5RuX0+LMFrx8ycscftDhOZpj5Ei4/3447DD4+muoXj1Hl5ckSZLyPcumcsTaLWvpO6Uvr0x+hRMP\nO5E2Z7fhiopXUKhAzv4UXLQI2raFqVPhhRfgn/+EHNqtK0mSJCUVy6YSaubqmXSb0I2P533MVZWu\n4ssbv6R6qZwfI27aBM88A717xw4B6t8fDjoox2NIkiRJScOyqbjLys7ii/lf0G1iNxb8vIDmZzRn\nQYsFlCxaMsezZGfDO+/EDv658EKYORPKlMnxGJIkSVLSsWwqbtZvXc8b096gx6QelDqkFKm1Uvnn\nKf+kcMHCkeQZPx5atYptk/34Y6hdO5IYkiRJUlKybGq/zVszj+4Tu/Pe7PdoWKEh71/zPmeVOSuy\nPCtWwMMPw4gR8Oyz0LgxFCgQWRxJkiQpKVk2tU+yw2yGfD+ErhO6MmPVDO6peQ9zm8/lmGLHRJZp\n61Z46SXo0gWaNYP58+GQQyKLI0mSJCU1y6b2yqbtm3hz+pt0n9idYgcUI7VWKl/c8AUHFDogskxh\nCB99FDtl9owzYPJkKF8+sjiSJEmSsGxqD32/7nt6TOzBOzPf4cLyF9Lvyn6cc9w5BBE/N2T6ceH2\ndAAAFb1JREFUdEhNhQ0b4M03ISUl0jiSJEmSdrFsarfCMGT44uF0ndCV8cvHc9fpdzH9nukcV/y4\nqKPx00/Qvj18/jl07Ah33QUFC0adSpIkSdJ/WDb1B7/u+JX+M/vTbWI3CgQFSK2VygfXfMBBhaN/\nMOWOHdCjR+yZmbfcErsvs0SJqFNJkiRJ+j3Lpv5r6Yal9JzUkzemvUGdsnXocWkPUsqlRL5VFmL3\nZQ4aBA88ACeeCKNGQaVKUaeSJEmStDv7XDaDIDgWeBsoBWQBfcMw7BavYMoZYRgy6odRdJ3QlfQl\n6dx+2u1MbDqREw47Iepo/zVvXqxkLl4cO2n2ssuiTiRJkiTp7wRhGO7bJwZBKaBUGIbTgyA4BJgC\nXBmGYcbvXhfu6xpKnG07t/HerPfoNrEbWzO30qpWK2497VYOKZJ7nhWyfj088QQMGACPPAL33QdF\nikSdSpIkSdJvBUFAGIZ/2A65z5PNMAxXAat2fXtzEATzgDJAxl9+oiK1ctNKXpn0Cn2n9qXmMTV5\n9sJnufjEiykQFIg62n/t3Al9+0JaGlx1FcydCyVLRp1KkiRJ0t6Iyz2bQRCUA6oDE+JxPcXf+OXj\n6TqhK19//zWNqzbmu9u/o+KRFaOO9QejRsUmmIcfDkOHwmmnRZ1IkiRJ0r7Y77K5awvtR0BqGIab\n/+w1KSkplCtXjnLlypGSkkKKD0PMETuydvDhnA/pNrEba7espeVZLenVsBfFDywedbQ/eO456NYN\n1q6Fk06KHQiUmgqNGsH990edTpIkSdJ/pKenk56ezpIlS1iyZMluX7fP92wCBEFQCPgKGByGYdfd\nvMZ7NnPY6s2r6T2lN70m96Jyycq0qtWKhhUaUrBA7nwQ5YgRsedk1q0bOwDo8MOjTiRJkiRpT8X9\nns1d3gDm7q5oKmdN/XEq3SZ04/P5n3Nd5esYestQTj3q1Khj7dbGjfDQQzBwIPTqBQ0bRp1IkiRJ\nUrzsz6NPzgUaA7OCIJgGhMAjYRgOiVc4/b2d2Tv5dN6ndJvYjaUbltLirBa8VP8ljjj4iKij/aWv\nv4a774aLL4bZs6F47tvZK0mSJGk/7Nc22j1awG20CfHzlp/pO7Uvr0x6hXIlytGqVisaVWpEoQJx\nOfMpYTZsiD0zc/jw2Imz9etHnUiSJEnS/kjUNlrlsFmrZ9F9Ync+nPshjSo14rMbPqPGMTWijrVH\nvvoKmjWDK66ITTOLFYs6kSRJkqREsWzmAVnZWXy14Cu6TezGvDXzuPeMe5nfYj5HFT0q6mh7ZN26\n2MmyY8fCO+/ABRdEnUiSJElSolk2c7EN2zbQb1o/ekzqwZEHH0lqrVSuqXwNRQoWiTraHvv009hz\nM6+9FmbOhKJFo04kSZIkKSdYNnOh+Wvn031id96d9S6XnHQJ7179LrWOrRV1rL2yZg20aAHTpsEH\nH0CdOlEnkiRJkpSTCkQdQDHZYTaDFw7m0gGXct6b53HYgYcxu/ls3v1n3iqaYQjvvw9Vq0LZsjBj\nhkVTkiRJSkZONiO2afsm3p7xNt0nduegwgeRWiuVT6//lAMLHRh1tL22ahU0bw4ZGfD551Ar73Rk\nSZIkSXFm2YzIovWL6DGxB2/NeIsLyl1A33/0pU7ZOgTBH04MzvXCEAYMgDZt4M474d134cC815Ul\nSZIkxZFlMweFYciIxSPoNrEbY5eNpUn1Jky7Zxpli5eNOto+W7Ei9jiTpUth0CCoWTPqRJIkSZJy\nA8tmDtiSuYUBMwfQbWI3wjCkVa1WvHv1uxQtknePZg1D6NcP/vWv2NbZjz+GInnnkFxJkiRJCWbZ\nTKBlvyyj56SevD7tdc4+9mxebvAy9crXy5NbZX/rhx/g7rvhp5/gm2+gevWoE0mSJEnKbTyNNs7C\nMGT0D6O59sNrqd67Ott3bmf8neP54sYvuPCEC/N00QxD6N07tlW2bl2YMMGiKUmSJOnPOdmMk207\nt/H+7PfpOqErm3dsplWtVrxxxRsUO6BY1NHiYvFiuOsu2LQJ0tOhSpWoE0mSJEnKzYIwDBO7QBCE\niV4jSj9u+pFXJ79Knyl9qF6qOqm1UmlwUgMKBPljaJydDa+8AmlpsfszW7eGQv4VhSRJkqRdgiAg\nDMM/bOG0NuyjiSsm0nVCVwYvHMyNp97IyNtGckrJU6KOFVcLF8YeZZKVBWPGQMWKUSeSJEmSlFc4\n2dwLO7J28PHcj+k6oSurf11Ny7Na0uT0JpQ4sETU0eIqKwu6doWnn4b27aFlSyhYMOpUkiRJknIj\nJ5v7Yc2va+g9pTevTn6VikdUpF2ddlx+8uUULJD/GlhGBjRpAoULw/jxcNJJUSeSJEmSlBfljxsL\nE2T6qunc8fkdnNzjZJZuWMrgxoMZcdsIrqx0Zb4rmjt3wnPPQZ060LgxjBxp0ZQkSZK075xs/s7O\n7J18nvE5XSd0ZfGGxTQ/ozkLWy7kyIOPjDpawsyeDXfcAcWLw6RJUL581IkkSZIk5XWWzV3WbV3H\na1Nfo+eknhx36HG0qtWKqypdReGChaOOljCZmfDss9CtG3TqBE2bQh5+DKgkSZKkXCTpy+acn+bQ\nbUI3Ppj7AVdUvIJPrvuEmqVrRh0r4aZPj00zS5WCqVPhuOOiTiRJkiQpP0nKspkdZjNwwUC6TujK\nnDVzaFazGRn3ZXD0IUdHHS3hduyAp56CXr3g+efhttucZkqSJEmKv6Qqm79s+4V+0/vRY2IPDjvo\nMFJrpXJdlesoUrBI1NFyxOTJsWlm+fKxyWbp0lEnkiRJkpRfJUXZXPDzArpP6M6AWQNocFID3rnq\nHWofW5sgSUZ627ZBWhr06wddusCNNzrNlCRJkpRY+bZshmHI0H8PpeuErkxeOZmmNZoy695ZlDm0\nTNTRctS4cbHnZlapAjNnwtH5f6ewJEmSpFwgCMMwsQsEQZjoNX5r847NvD3jbbpP7E6RgkVIrZXK\njafeyEGFD8qxDLnBli3w2GPw7rux02avvTbqRJIkSZLyoyAICMPwD3sn881kc/H6xfSY2IM3Z7zJ\n+cefT6+GvTjv+POSZqvsb40aBXfeCTVrxqaZJUtGnUiSJElSssnTZTMMQ9KXpNNtYjdGLR3FHdXv\nYMrdUyhXolzU0SKxeTO0awcffwyvvAKNGkWdSJIkSVKyypNlc2vmVgbMGkC3Cd3Ymb2TVrVa0f+q\n/hQtUjTqaJEZMQLuugvq1IHZs+Hww6NOJEmSJCmZ5al7NpdvXM4rk17htamvcVaZs0itlcpFJ1yU\nlFtl/2PjRnjoIRg4MPbszIYNo04kSZIkKZnk6Xs2s7KzuPnTm/n6+6+5pdotjGkyhgpHVIg6VuS+\n/hruvhsuvhhmzYISJaJOJEmSJEkxeWay+VnGZ9QrX49DDzg0Dqnytg0boE0bGD4c+vSB+vWjTiRJ\nkiQpWe1uslkgijD7olGlRhZN4Kuv4NRT4YADYtNMi6YkSZKk3ChPbKMVrFsHqakwdiy88w5ccEHU\niSRJkiRp9/LMZDOZffppbJp5+OGx52ZaNCVJkiTldk42c7E1a6BlS5g6FT74IPZYE0mSJEnKC5xs\n5kJhCO+/D1WrwrHHwvTpFk1JkiRJeYuTzVxm1Spo3hzmzYPPPoPataNOJEmSJEl7z8lmLhGG0L8/\nnHYaVKwI06ZZNCVJkiTlXU42c4EVK6BZM1iyBAYOhDPOiDqRJEmSJO0fJ5sRCkPo1w9OPx1q1IAp\nUyyakiRJkvIHJ5sR+eEHuPtu+OknGDoUqlePOpEkSZIkxY+TzRwWhtC7N9SsCXXrwoQJFk1JkiRJ\n+Y+TzRy0eDHcdRds2gTp6VClStSJJEmSJCkxnGzmgOxs6NEDzjwTGjSAsWMtmpIkSZLyNyebCfb9\n93DnnZCZCaNHQ6VKUSeSJEmSpMRzspkgWVnQuXPsWZmNGsGoURZNSZIkScnDyWYCZGRAkyZQuDCM\nHw8nnRR1IkmSJEnKWU4242jnTnjuOahTB266CUaOtGhKkiRJSk5ONuNk9uzYNPPQQ2HSJChfPupE\nkiRJkhQdJ5v7KTMTnnoKLrgg9liTb76xaEqSJEmSk839MH063HEHlCoFU6fCccdFnUiSJEmScgcn\nm/tgxw7o0AHq14fUVBg0yKIpSZIkSb/lZHMvTZ4cm2aWLx+bbJYuHXUiSZIkScp9nGzuoW3boF07\naNgQHn4YPv/coilJkiRJu+Nkcw+MGxc7abZyZZgxI3aPpiRJkiRp9yybf2HLFnjsMRgwALp1g2uv\nhSCIOpUkSZIk5X5uo92NUaOgenVYsQJmzYLrrrNoSpIkSdKecrL5O7/+Grs38+OPoUcPuOqqqBNJ\nkiRJUt7jZPM3Ro6EqlVhw4bYNNOiKUmSJEn7xskmsGkTPPQQfPUV9OoVO3FWkiRJkrTvkn6yOXQo\nnHoqZGbGppkWTUmSJEnaf0k72dywAdq0gWHDoG9fqF8/6kSSJEmSlH8k5WRz4MDYvZlFisSmmRZN\nSZIkSYqvpJpsrlsHqakwZgy89RbUqxd1IkmSJEnKn5Jmsvnpp7F7Mw87DGbOtGhKkiRJUiLl+8nm\nmjXQsiVMmQLvvw9160adSJIkSZLyv3w72QxD+OADqFYNjj0WZsywaEqSJElSTsmXk83Vq6F5c5g7\nN7Z9tnbtqBNJkiRJUnLJV5PNMIQBA2LTzJNPhmnTLJqSJEmSFIV8M9lcuRKaNYPFi2OPNjnjjKgT\nSZIkSVLyyvOTzTCEfv2genU4/fTYQUAWTUmSJEmKVp6ebC5bBnffDatWwdChscIpSZIkSYrefk02\ngyB4PQiC1UEQzIxXoL+Snp4OxKaZffpAjRpw7rkwcaJFMz/5z/us/M/3Ojn4PicH3+fk4XudHHyf\nk0ci3+v93UbbD2gQjyB7Ij09ncWL4aKLoG9fGDkS2reHwoVzKoFygr+5JQ/f6+Tg+5wcfJ+Th+91\ncvB9Th65tmyGYTgaWB+nLH8pOzs2wTzzTKhfH8aNg1NPzYmVldOWLFkSdYRcLT/95p+b3+uof5xz\ncv1Er5WI9znq90d5jz9n/lp++fHJ7f8fUefLT3+2JOL6Ub8/+VGeOCAoMxMuvBBmzoTRo+Ff/4JC\nefpuU/2V3FxAcoP89Bthbn6vo/5xzk9fEFg2lRv4c+av5Zcfn9z+/xF1vvz0Z4tlM28IwjDcvwsE\nwfHAl2EYVtvNx/dvAUmSJElSrhaGYfD770v4fPDPFpUkSZIk5W/x2EYb7PpHkiRJkiRg/x998i4w\nFjg5CIIfgiC4Iz6xJEmSJEl52X7fsylJkiRJ0u/lidNogyC4JAiCjCAIFgRB8K+o8ygxgiB4PQiC\n1UEQzIw6ixInCIJjgyAYEQTB3CAIZgVB0CrqTEqMIAgOCIJgQhAE03a9149HnUmJEwRBgSAIpgZB\n8EXUWZQ4QRAsCYJgxq5f1xOjzqPECIKgeBAEHwZBMC8IgjlBENSKOpPiLwiCk3f9Wp6669+/xPvr\nslw/2QyCoACwALgQWAlMAm4IwzAj0mCKuyAI6gCbgbd3d7qx8r4gCEoBpcIwnB4EwSHAFOBKf03n\nT0EQHByG4ZYgCAoCY4BWYRj6BWo+FARBa6AmcGgYhldEnUeJEQTBIqBmGIY58px1RSMIgjeBb8Mw\n7BcEQSHg4DAMN0YcSwm0q3MtB2qFYbgsXtfNC5PNs4CFYRguDcMwE/g/4MqIMykBwjAcDfiHVz4X\nhuGqMAyn7/r2ZmAeUCbaVEqUMAy37PrmAcROQM/df8OpfRIEwbHAZcBrUWdRwgXkja8ftY+CICgG\n1A3DsB9AGIY7LZpJ4SLg3/EsmpA3frMoA/z2f3o5fmEq5QtBEJQDqgMTok2iRNm1tXIasAr4JgzD\nSVFnUkJ0AdriXyYkgxD4OgiCSUEQNI06jBLiBGBtEAT9dm2v7BMEwUFRh1LCXQ+8F++L5oWy+WeP\nVfEPMymP27WF9iMgddeEU/lQGIbZYRieDhwL1AqCoHLUmRRfQRA0BFbv2rHg49Dyv3PCMDyD2CT7\nvl23wCh/KQTUAHqGYVgD2AI8HG0kJVIQBIWBK4AP433tvFA2lwNlf/PfxxK7d1NSHrXr/o+PgHfC\nMPw86jxKvF1bsNKBSyKOovg7F7hi17187wEXBEHwdsSZlCBhGK7a9e81wKfEbndS/rIcWBaG4eRd\n//0RsfKp/OtSYMquX9dxlRfK5iTgpCAIjg+CoAhwA+BJd/mXfyueHN4A5oZh2DXqIEqcIAiODIKg\n+K5vH0TsfhAPgspnwjB8JAzDsmEYnkDsz+gRYRjeGnUuxV8QBAfv2pVCEARFgfrA7GhTKd7CMFwN\nLAuC4ORd33UhMDfCSEq8G0nAFlqIjclztTAMs4IgaAEMJVaOXw/DcF7EsZQAQRC8C6QARwRB8APw\n+H9uTlf+EQTBuUBjYNaue/lC4JEwDIdEm0wJcAzw1q4T7goA74dhOCjiTJL23dHAp0EQhMS+hhwQ\nhuHQiDMpMVoBA3Ztr1wE3BFxHiXIb/4y+O6EXD+3P/pEkiRJkpT35IVttJIkSZKkPMayKUmSJEmK\nO8umJEmSJCnuLJuSpP/X3v2DZnWFcRz//tSiDlbqYCkU4qDoIBYiNNA6Owudiqig1ME/XTJl6Fzo\nKkgIJQjt5FTQQclQsA3UwWhQFCfBpeCkCFIF+zh4hEv6pknwkgTf72c673POuefed3t47j1HkiSp\ndyabkiRJkqTemWxKkiRJknpnsilJGkpJRpLcXaRvKsm+1p74n2tcTfJxH2tKkvSh8ZxNSdJQSjIC\nXKmqA0uMe15V21ZzTUmSPgRWNiVJw+yjJJeSzCe5nGQLQJLfk4wm+RHYmmQuyS8LJyd5lGRHq1je\nbxXRe0muJdncxhxMcifJLHC2M3dDkp+S3Gz937X4kSQzrf1ZkodJdq7GnyFJUp9MNiVJw2wvMFlV\nXwDPgTPdzqqaAF5U1WhVHRswv/t60G7gQlXtB54B37T4NHCuqr5eMPcU8LSqxoAvgdNJRqrqN+Dv\nJGeBKeCHqnryfo8pSdLqM9mUJA2zx1X1V2v/Chxa4fx02o+q6t33mLeAXe17zu1V9WeLd6ujh4Hj\nSW4DN4EdwJ7W9z0wAfxTVZdXeE+SJK0Lm9b6BiRJWkMLNy4YtJFBBsQGedlpvwa2LDE3wPmqmhnQ\n9znwL/DpMteWJGndsbIpSRpmI0nGWvtb4I8BY14l2biMa/0nsayqZ8DTJF+10NFO93XgTJJNAEn2\nJNnafk+3+3mQZHyZzyJJ0rpisilJGmb3gRNJ5oFPgMkW71Y4p4C7gzYIWjBuse3dTwIX2wZBLzrx\nn9v6c+04lEnevnE0AdyoqllgHDiVZO/KHkuSpLXn0SeSJEmSpN5Z2ZQkSZIk9c5kU5IkSZLUO5NN\nSZIkSVLvTDYlSZIkSb0z2ZQkSZIk9c5kU5IkSZLUO5NNSZIkSVLv3gAYhICGyYI9XAAAAABJRU5E\nrkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x72df4c700198>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_run('Linearity test run', *fetch_runs('test102'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        this.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,[encoded data, 27736 characters]\" width=\"800\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def simulate_bitslide(data):\n",
    "    nbits = len(data)\n",
    "    return [ sum(data[n] if i&(2**n) else 0 for n in range(nbits)) for i in range(2**nbits) ]\n",
    "\n",
    "info, data, (zero_cal, _stdev) = fetch_run(['test103'])\n",
    "data = np.array(next(iter(data.values())))[:,1] - zero_cal\n",
    "plot_bitslide(simulate_bitslide(data))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        this.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,[encoded data, 18764 characters]\" width=\"600\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "frob_export_for_blog(simulate_bitslide(data), svgfile='/tmp/corrected_brightness_sim.svg')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
