text:
export BrickletEPaper296x128ReadBlackWhiteLowLevel
struct BrickletEPaper296x128ReadBlackWhiteLowLevel
    pixels_length::Integer
    pixels_chunk_offset::Integer
    pixels_chunk_data::Vector{Bool}
end

export BrickletEPaper296x128ReadColorLowLevel
struct BrickletEPaper296x128ReadColorLowLevel
    pixels_length::Integer
    pixels_chunk_offset::Integer
    pixels_chunk_data::Vector{Bool}
end

export BrickletEPaper296x128SPITFPErrorCount
struct BrickletEPaper296x128SPITFPErrorCount
    error_count_ack_checksum::Integer
    error_count_message_checksum::Integer
    error_count_frame::Integer
    error_count_overflow::Integer
end

export BrickletEPaper296x128Identity
struct BrickletEPaper296x128Identity
    uid::String
    connected_uid::String
    position::Char
    hardware_version::Vector{Integer}
    firmware_version::Vector{Integer}
    device_identifier::Integer
end

export BrickletEPaper296x128
"""
Three color 296x128 e-paper display
"""
mutable struct BrickletEPaper296x128 <: TinkerforgeDevice
    ipcon::IPConnection
    deviceInternal::PyObject

    """
    Creates an object with the unique device ID *uid* and adds it to
    the IP Connection *ipcon*.
    """
    function BrickletEPaper296x128(uid::String, ipcon::IPConnection)
        package = pyimport("tinkerforge.bricklet_e_paper_296x128")
        deviceInternal = package.BrickletEPaper296x128(uid, ipcon.ipconInternal)

        return new(ipcon, deviceInternal)
    end
end

export draw
"""
    $(SIGNATURES)

Draws the current black/white and red or gray buffer to the e-paper display.

The Bricklet does not have any double-buffering. You should not call
this function while writing to the buffer. See :func:`Get Draw Status`.
"""
function draw(device::BrickletEPaper296x128)
    device.deviceInternal.draw()
end

export get_draw_status
"""
    $(SIGNATURES)

Returns one of three draw statuses:

* Idle
* Copying: Data is being copied from the buffer of the Bricklet to the buffer of the display.
* Drawing: The display is updating its content (during this phase the flickering etc happens).

You can write to the buffer (through one of the write or draw functions) when the status is
either *idle* or *drawing*. You should not write to the buffer while it is being *copied* to the
display. There is no double-buffering.
"""
function get_draw_status(device::BrickletEPaper296x128)
    return device.deviceInternal.get_draw_status()
end

export write_black_white_low_level
"""
    $(SIGNATURES)

Writes black/white pixels to the specified window into the buffer.

The pixels are written into the window line by line top to bottom
and each line is written from left to right.

The value 0 (false) corresponds to a black pixel and the value 1 (true) to a
white pixel.

This function writes the pixels into the black/white pixel buffer, to draw the
buffer to the display use :func:`Draw`.

Use :func:`Write Color` to write red or gray pixels.
"""
function write_black_white_low_level(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
    device.deviceInternal.write_black_white_low_level(x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
end

export read_black_white_low_level
"""
    $(SIGNATURES)

Returns the current content of the black/white pixel buffer for the specified window.

The pixels are read into the window line by line top to bottom and
each line is read from left to right.

The current content of the buffer does not have to be the current content of the display.
It is possible that the data was not drawn to the display yet and after a restart of
the Bricklet the buffer will be reset to black, while the display retains its content.
"""
function read_black_white_low_level(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end)
    return device.deviceInternal.read_black_white_low_level(x_start, y_start, x_end, y_end)
end

export write_color_low_level
"""
    $(SIGNATURES)

The E-Paper 296x128 Bricklet is available with the colors black/white/red and
black/white/gray. Depending on the model this function writes either red or
gray pixels to the specified window into the buffer.

The pixels are written into the window line by line top to bottom
and each line is written from left to right.

The value 0 (false) means that this pixel does not have color. It will be either black
or white (see :func:`Write Black White`). The value 1 (true) corresponds to a red or gray
pixel, depending on the Bricklet model.

This function writes the pixels into the red or gray pixel buffer, to draw the buffer
to the display use :func:`Draw`.

Use :func:`Write Black White` to write black/white pixels.
"""
function write_color_low_level(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
    device.deviceInternal.write_color_low_level(x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
end

export read_color_low_level
"""
    $(SIGNATURES)

Returns the current content of the red or gray pixel buffer for the specified window.

The pixels are written into the window line by line top to bottom
and each line is written from left to right.

The current content of the buffer does not have to be the current content of the display.
It is possible that the data was not drawn to the display yet and after a restart of
the Bricklet the buffer will be reset to black, while the display retains its content.
"""
function read_color_low_level(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end)
    return device.deviceInternal.read_color_low_level(x_start, y_start, x_end, y_end)
end

export fill_display
"""
    $(SIGNATURES)

Fills the complete content of the display with the given color.

This function writes the pixels into the black/white/red|gray pixel buffer, to draw the buffer
to the display use :func:`Draw`.
"""
function fill_display(device::BrickletEPaper296x128, color)
    device.deviceInternal.fill_display(color)
end

export draw_text
"""
    $(SIGNATURES)

Draws a text with up to 50 characters at the pixel position (x, y).

You can use one of 9 different font sizes and draw the text in
black/white/red|gray. The text can be drawn horizontal or vertical.

This function writes the pixels into the black/white/red|gray pixel buffer, to draw the buffer
to the display use :func:`Draw`.
"""
function draw_text(device::BrickletEPaper296x128, position_x, position_y, font, color, orientation, text)
    device.deviceInternal.draw_text(position_x, position_y, font, color, orientation, text)
end

export draw_line
"""
    $(SIGNATURES)

Draws a line from (x, y)-start to (x, y)-end in the given color.

This function writes the pixels into the black/white/red|gray pixel buffer, to draw the buffer
to the display use :func:`Draw`.
"""
function draw_line(device::BrickletEPaper296x128, position_x_start, position_y_start, position_x_end, position_y_end, color)
    device.deviceInternal.draw_line(position_x_start, position_y_start, position_x_end, position_y_end, color)
end

export draw_box
"""
    $(SIGNATURES)

Draws a box from (x, y)-start to (x, y)-end in the given color.

If you set fill to true, the box will be filled with the
color. Otherwise only the outline will be drawn.

This function writes the pixels into the black/white/red|gray pixel buffer, to draw the buffer
to the display use :func:`Draw`.
"""
function draw_box(device::BrickletEPaper296x128, position_x_start, position_y_start, position_x_end, position_y_end, fill, color)
    device.deviceInternal.draw_box(position_x_start, position_y_start, position_x_end, position_y_end, fill, color)
end

export set_update_mode
"""
    $(SIGNATURES)

.. note::
 The default update mode corresponds to the default e-paper display
 manufacturer settings. All of the other modes are experimental and
 will result in increased ghosting and possibly other long-term
 side effects.

 If you want to know more about the inner workings of an e-paper display
 take a look at this excellent video from Ben Krasnow:
 `https://www.youtube.com/watch?v=MsbiO8EAsGw <https://www.youtube.com/watch?v=MsbiO8EAsGw>`__.

 If you are not sure about this option, leave the update mode at default.

Currently there are three update modes available:

* Default: Settings as given by the manufacturer. An update will take about
  7.5 seconds and during the update the screen will flicker several times.
* Black/White: This will only update the black/white pixel. It uses the manufacturer
  settings for black/white and ignores the red or gray pixel buffer. With this mode the
  display will flicker once and it takes about 2.5 seconds. Compared to the default settings
  there is more ghosting.
* Delta: This will only update the black/white pixel. It uses an aggressive method where
  the changes are not applied for a whole buffer but only for the delta between the last
  and the next buffer. With this mode the display will not flicker during an update and
  it takes about 900-950ms. Compared to the other two settings there is more ghosting. This
  mode can be used for something like a flicker-free live update of a text.

With the black/white/red display if you use either the black/white or the delta mode,
after a while of going back and forth between black and white the white color will
start to appear red-ish or pink-ish.

If you use the aggressive delta mode and rapidly change the content, we recommend that you
change back to the default mode every few hours and in the default mode cycle between the
three available colors a few times. This will get rid of the ghosting and after that you can
go back to the delta mode with flicker-free updates.
"""
function set_update_mode(device::BrickletEPaper296x128, update_mode)
    device.deviceInternal.set_update_mode(update_mode)
end

export get_update_mode
"""
    $(SIGNATURES)

Returns the update mode as set by :func:`Set Update Mode`.
"""
function get_update_mode(device::BrickletEPaper296x128)
    return device.deviceInternal.get_update_mode()
end

export set_display_type
"""
    $(SIGNATURES)

Sets the type of the display. The e-paper display is available
in black/white/red and black/white/gray. This will be factory set
during the flashing and testing phase. The value is saved in
non-volatile memory and will stay after a power cycle.
"""
function set_display_type(device::BrickletEPaper296x128, display_type)
    device.deviceInternal.set_display_type(display_type)
end

export get_display_type
"""
    $(SIGNATURES)

Returns the type of the e-paper display. It can either be
black/white/red or black/white/gray.
"""
function get_display_type(device::BrickletEPaper296x128)
    return device.deviceInternal.get_display_type()
end

export set_display_driver
"""
    $(SIGNATURES)

Sets the type of display driver. The Bricklet can currently support
SSD1675A and SSD1680. This will be factory set
during the flashing and testing phase. The value is saved in
non-volatile memory and will stay after a power cycle.

.. versionadded:: 2.0.3\$nbsp;(Plugin)
"""
function set_display_driver(device::BrickletEPaper296x128, display_driver)
    device.deviceInternal.set_display_driver(display_driver)
end

export get_display_driver
"""
    $(SIGNATURES)

Returns the e-paper display driver.

.. versionadded:: 2.0.3\$nbsp;(Plugin)
"""
function get_display_driver(device::BrickletEPaper296x128)
    return device.deviceInternal.get_display_driver()
end

export get_spitfp_error_count
"""
    $(SIGNATURES)

Returns the error count for the communication between Brick and Bricklet.

The errors are divided into

* ACK checksum errors,
* message checksum errors,
* framing errors and
* overflow errors.

The errors counts are for errors that occur on the Bricklet side. All
Bricks have a similar function that returns the errors on the Brick side.
"""
function get_spitfp_error_count(device::BrickletEPaper296x128)
    return device.deviceInternal.get_spitfp_error_count()
end

export set_bootloader_mode
"""
    $(SIGNATURES)

Sets the bootloader mode and returns the status after the requested
mode change was instigated.

You can change from bootloader mode to firmware mode and vice versa. A change
from bootloader mode to firmware mode will only take place if the entry function,
device identifier and CRC are present and correct.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function set_bootloader_mode(device::BrickletEPaper296x128, mode)
    return device.deviceInternal.set_bootloader_mode(mode)
end

export get_bootloader_mode
"""
    $(SIGNATURES)

Returns the current bootloader mode, see :func:`Set Bootloader Mode`.
"""
function get_bootloader_mode(device::BrickletEPaper296x128)
    return device.deviceInternal.get_bootloader_mode()
end

export set_write_firmware_pointer
"""
    $(SIGNATURES)

Sets the firmware pointer for :func:`Write Firmware`. The pointer has
to be increased by chunks of size 64. The data is written to flash
every 4 chunks (which equals to one page of size 256).

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function set_write_firmware_pointer(device::BrickletEPaper296x128, pointer)
    device.deviceInternal.set_write_firmware_pointer(pointer)
end

export write_firmware
"""
    $(SIGNATURES)

Writes 64 Bytes of firmware at the position as written by
:func:`Set Write Firmware Pointer` before. The firmware is written
to flash every 4 chunks.

You can only write firmware in bootloader mode.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function write_firmware(device::BrickletEPaper296x128, data)
    return device.deviceInternal.write_firmware(data)
end

export set_status_led_config
"""
    $(SIGNATURES)

Sets the status LED configuration. By default the LED shows
communication traffic between Brick and Bricklet, it flickers once
for every 10 received data packets.

You can also turn the LED permanently on/off or show a heartbeat.

If the Bricklet is in bootloader mode, the LED is will show heartbeat by default.
"""
function set_status_led_config(device::BrickletEPaper296x128, config)
    device.deviceInternal.set_status_led_config(config)
end

export get_status_led_config
"""
    $(SIGNATURES)

Returns the configuration as set by :func:`Set Status LED Config`
"""
function get_status_led_config(device::BrickletEPaper296x128)
    return device.deviceInternal.get_status_led_config()
end

export get_chip_temperature
"""
    $(SIGNATURES)

Returns the temperature as measured inside the microcontroller. The
value returned is not the ambient temperature!

The temperature is only proportional to the real temperature and it has bad
accuracy. Practically it is only useful as an indicator for
temperature changes.
"""
function get_chip_temperature(device::BrickletEPaper296x128)
    return device.deviceInternal.get_chip_temperature()
end

export reset
"""
    $(SIGNATURES)

Calling this function will reset the Bricklet. All configurations
will be lost.

After a reset you have to create new device objects,
calling functions on the existing ones will result in
undefined behavior!
"""
function reset(device::BrickletEPaper296x128)
    device.deviceInternal.reset()
end

export write_uid
"""
    $(SIGNATURES)

Writes a new UID into flash. If you want to set a new UID
you have to decode the Base58 encoded UID string into an
integer first.

We recommend that you use Brick Viewer to change the UID.
"""
function write_uid(device::BrickletEPaper296x128, uid)
    device.deviceInternal.write_uid(uid)
end

export read_uid
"""
    $(SIGNATURES)

Returns the current UID as an integer. Encode as
Base58 to get the usual string version.
"""
function read_uid(device::BrickletEPaper296x128)
    return device.deviceInternal.read_uid()
end

export get_identity
"""
    $(SIGNATURES)

Returns the UID, the UID where the Bricklet is connected to,
the position, the hardware and firmware version as well as the
device identifier.

The position can be 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h' (Bricklet Port).
A Bricklet connected to an :ref:`Isolator Bricklet <isolator_bricklet>` is always at
position 'z'.

The device identifier numbers can be found :ref:`here <device_identifier>`.
|device_identifier_constant|
"""
function get_identity(device::BrickletEPaper296x128)
    return device.deviceInternal.get_identity()
end

export write_black_white
"""
Writes black/white pixels to the specified window into the buffer.

The pixels are written into the window line by line top to bottom
and each line is written from left to right.

The value 0 (false) corresponds to a black pixel and the value 1 (true) to a
white pixel.

This function writes the pixels into the black/white pixel buffer, to draw the
buffer to the display use :func:`Draw`.

Use :func:`Write Color` to write red or gray pixels.
"""
function write_black_white(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end, pixels)

        x_start = UInt16(x_start)
    y_start = UInt8(y_start)
    x_end = UInt16(x_end)
    y_end = UInt8(y_end)
    pixels = convert(Vector{Bool}, pixels)

    if length(pixels) > 65535
        throw(TinkerforgeInvalidParameterError("Pixels can be at most 65535 items long"))
    end

    pixels_length = length(pixels)
    pixels_chunk_offset = 0

    if pixels_length == 0
        pixels_chunk_data = [False] * 432
        ret = write_black_white_low_level(device, x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
    else
        lock(device.stream_lock) do
            while pixels_chunk_offset < pixels_length
                pixels_chunk_data = create_chunk_data(pixels, pixels_chunk_offset, 432, False)
                ret = write_black_white_low_level(device, x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
                pixels_chunk_offset += 432
            end
        end
    end

        return ret
end

export read_black_white
"""
Returns the current content of the black/white pixel buffer for the specified window.

The pixels are read into the window line by line top to bottom and
each line is read from left to right.

The current content of the buffer does not have to be the current content of the display.
It is possible that the data was not drawn to the display yet and after a restart of
the Bricklet the buffer will be reset to black, while the display retains its content.
"""
function read_black_white(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end)

        x_start = UInt16(x_start)
    y_start = UInt8(y_start)
    x_end = UInt16(x_end)
    y_end = UInt8(y_end)

    lock(device.stream_lock) do
        ret = read_black_white_low_level(device, x_start, y_start, x_end, y_end)
            pixels_length = ret.pixels_length
        pixels_out_of_sync = ret.pixels_chunk_offset != 0

        pixels_data = ret.pixels_chunk_data

        while !pixels_out_of_sync && length(pixels_data) < pixels_length
            ret = read_black_white_low_level(device, x_start, y_start, x_end, y_end)
                pixels_length = ret.pixels_length
            pixels_out_of_sync = ret.pixels_chunk_offset != length(pixels_data)
            pixels_data += ret.pixels_chunk_data
        end

        if pixels_out_of_sync # discard remaining stream to bring it back in-sync
            while ret.pixels_chunk_offset + 464 < pixels_length
                ret = read_black_white_low_level(device, x_start, y_start, x_end, y_end)
                    pixels_length = ret.pixels_length
            end

            throw(TinkerforgeStreamOutOfSyncError("Pixels stream is out-of-sync"))
        end
    end

        return pixels_data[:pixels_length]
end

export write_color
"""
The E-Paper 296x128 Bricklet is available with the colors black/white/red and
black/white/gray. Depending on the model this function writes either red or
gray pixels to the specified window into the buffer.

The pixels are written into the window line by line top to bottom
and each line is written from left to right.

The value 0 (false) means that this pixel does not have color. It will be either black
or white (see :func:`Write Black White`). The value 1 (true) corresponds to a red or gray
pixel, depending on the Bricklet model.

This function writes the pixels into the red or gray pixel buffer, to draw the buffer
to the display use :func:`Draw`.

Use :func:`Write Black White` to write black/white pixels.
"""
function write_color(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end, pixels)

        x_start = UInt16(x_start)
    y_start = UInt8(y_start)
    x_end = UInt16(x_end)
    y_end = UInt8(y_end)
    pixels = convert(Vector{Bool}, pixels)

    if length(pixels) > 65535
        throw(TinkerforgeInvalidParameterError("Pixels can be at most 65535 items long"))
    end

    pixels_length = length(pixels)
    pixels_chunk_offset = 0

    if pixels_length == 0
        pixels_chunk_data = [False] * 432
        ret = write_color_low_level(device, x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
    else
        lock(device.stream_lock) do
            while pixels_chunk_offset < pixels_length
                pixels_chunk_data = create_chunk_data(pixels, pixels_chunk_offset, 432, False)
                ret = write_color_low_level(device, x_start, y_start, x_end, y_end, pixels_length, pixels_chunk_offset, pixels_chunk_data)
                pixels_chunk_offset += 432
            end
        end
    end

        return ret
end

export read_color
"""
Returns the current content of the red or gray pixel buffer for the specified window.

The pixels are written into the window line by line top to bottom
and each line is written from left to right.

The current content of the buffer does not have to be the current content of the display.
It is possible that the data was not drawn to the display yet and after a restart of
the Bricklet the buffer will be reset to black, while the display retains its content.
"""
function read_color(device::BrickletEPaper296x128, x_start, y_start, x_end, y_end)

        x_start = UInt16(x_start)
    y_start = UInt8(y_start)
    x_end = UInt16(x_end)
    y_end = UInt8(y_end)

    lock(device.stream_lock) do
        ret = read_color_low_level(device, x_start, y_start, x_end, y_end)
            pixels_length = ret.pixels_length
        pixels_out_of_sync = ret.pixels_chunk_offset != 0

        pixels_data = ret.pixels_chunk_data

        while !pixels_out_of_sync && length(pixels_data) < pixels_length
            ret = read_color_low_level(device, x_start, y_start, x_end, y_end)
                pixels_length = ret.pixels_length
            pixels_out_of_sync = ret.pixels_chunk_offset != length(pixels_data)
            pixels_data += ret.pixels_chunk_data
        end

        if pixels_out_of_sync # discard remaining stream to bring it back in-sync
            while ret.pixels_chunk_offset + 464 < pixels_length
                ret = read_color_low_level(device, x_start, y_start, x_end, y_end)
                    pixels_length = ret.pixels_length
            end

            throw(TinkerforgeStreamOutOfSyncError("Pixels stream is out-of-sync"))
        end
    end

        return pixels_data[:pixels_length]
end

export register_callback
"""
Registers the given *function* with the given *callback_id*.
"""
function register_callback(device::BrickletEPaper296x128, callback_id, function_)
    if isnothing(function_)
        device.registered_callbacks.pop(callback_id, None)
    else
        device.registered_callbacks[callback_id] = function_
    end
end


text:
using Test
using HTTP
using HTTP.IOExtras
using HTTP.Parsers
using HTTP.Messages
using HTTP.Sockets
using HTTP.MessageRequest: bodylength

mutable struct FunctionIO <: IO
    f::Function
    buf::IOBuffer
    done::Bool
end
FunctionIO(f::Function) = FunctionIO(f, IOBuffer(), false)

mutable struct Loopback <: IO
    got_headers::Bool
    buf::IOBuffer
    io::Base.BufferStream
end
Loopback() = Loopback(false, IOBuffer(), Base.BufferStream())

config = [
    :socket_type => Loopback,
    :retry => false,
    :connection_limit => 1
]

server_events = []

call(fio::FunctionIO) = !fio.done && (fio.buf = IOBuffer(fio.f()) ; fio.done = true)

Base.bytesavailable(fio::FunctionIO) = (call(fio); bytesavailable(fio.buf))
Base.bytesavailable(lb::Loopback) = bytesavailable(lb.io)
Base.close(lb::Loopback) = (close(lb.io); close(lb.buf))
Base.eof(fio::FunctionIO) = (call(fio); eof(fio.buf))
Base.eof(lb::Loopback) = eof(lb.io)
Base.isopen(lb::Loopback) = isopen(lb.io)
Base.read(fio::FunctionIO, a...) = (call(fio); read(fio.buf, a...))
Base.readavailable(fio::FunctionIO) = (call(fio); readavailable(fio.buf))
Base.readavailable(lb::Loopback) = readavailable(lb.io)
Base.unsafe_read(lb::Loopback, p::Ptr, n::Integer) = unsafe_read(lb.io, p, n)

HTTP.IOExtras.tcpsocket(::Loopback) = Sockets.TCPSocket()
HTTP.ConnectionPool.tcpstatus(c::HTTP.ConnectionPool.Connection{Loopback}) = "🤖 "

lbreq(req, headers, body; method="GET", kw...) =
      HTTP.request(method, "http://test/$req", headers, body; config..., kw...)

lbopen(f, req, headers) =
    HTTP.open(f, "PUT", "http://test/$req", headers; config...)

function reset(lb::Loopback)
    truncate(lb.buf, 0)
    lb.got_headers = false
end

"""
    escapelines(string)

Escape `string` and insert '\n' after escaped newline characters.
"""
function escapelines(s::String)
    s = Base.escape_string(s)
    s = replace(s, "\\n" => "\\n\n    ")
    return string("    ", strip(s))
end

function on_headers(f::Function, lb::Loopback)
    if lb.got_headers
        return
    end

    buf = copy(lb.buf)
    seek(buf, 0)
    req = Request()

    try
        readheaders(buf, req)
        lb.got_headers = true
    catch e
        if !(e isa EOFError || e isa HTTP.ParseError)
            rethrow(e)
        end
    end

    if lb.got_headers
            f(req)
    end
end

function on_body(f::Function, lb::Loopback)
    s = String(take!(copy(lb.buf)))
    req = nothing

    try
        req = parse(HTTP.Request, s)
    catch e
        if !(e isa EOFError || e isa HTTP.ParseError)
            rethrow(e)
        end
    end

    if req !== nothing
        reset(lb)
        @async try
            f(req)
        catch e
            println("⚠ on_body exception: $(sprint(showerror, e))\n$(stacktrace(catch_backtrace()))")
        end
    end
end

function Base.unsafe_write(lb::Loopback, p::Ptr{UInt8}, n::UInt)
    global server_events

    if !isopen(lb.buf)
        throw(ArgumentError("stream is closed or unusable"))
    end

    n = unsafe_write(lb.buf, p, n)

    on_headers(lb) do req
        println("📡  $(HTTP.sprintcompact(req))")
        push!(server_events, "Request: $(HTTP.sprintcompact(req))")

        if req.target == "/abort"
            reset(lb)
            response = HTTP.Response(403, ["Connection" => "close",
                                          "Content-Length" => 0]; request=req)
            push!(server_events, "Response: $(HTTP.sprintcompact(response))")
            write(lb.io, response)
        end
    end

    on_body(lb) do req
        l = length(req.body)
        response = HTTP.Response(200, ["Content-Length" => l],
                                      body = req.body; request=req)
        if req.target == "/echo"
            push!(server_events, "Response: $(HTTP.sprintcompact(response))")
            write(lb.io, response)
        elseif (m = match(r"^/delay([0-9]*)$", req.target)) !== nothing
            t = parse(Int, first(m.captures))
            sleep(t/10)
            push!(server_events, "Response: $(HTTP.sprintcompact(response))")
            write(lb.io, response)
        else
            response = HTTP.Response(403,
                                     ["Connection" => "close",
                                      "Content-Length" => 0]; request=req)
            push!(server_events, "Response: $(HTTP.sprintcompact(response))")
            write(lb.io, response)
        end
    end

    return n
end

function async_test(m=["GET","GET","GET","GET","GET"];kw...)
    r1 = r2 = r3 = r4 = r5 = nothing
    t1 = time()

    @sync begin
        @async r1 = lbreq("delay1", [], FunctionIO(()->(sleep(0.00); "Hello World! 1"));
                          method=m[1], kw...)
        @async r2 = lbreq("delay2", [],
                          FunctionIO(()->(sleep(0.01); "Hello World! 2"));
                          method=m[2], kw...)
        @async r3 = lbreq("delay3", [],
                          FunctionIO(()->(sleep(0.02); "Hello World! 3"));
                          method=m[3], kw...)
        @async r4 = lbreq("delay4", [],
                          FunctionIO(()->(sleep(0.03); "Hello World! 4"));
                          method=m[4], kw...)
        @async r5 = lbreq("delay5", [],
                          FunctionIO(()->(sleep(0.04); "Hello World! 5"));
                          method=m[5], kw...)
    end
    t2 = time()

    @test String(r1.body) == "Hello World! 1"
    @test String(r2.body) == "Hello World! 2"
    @test String(r3.body) == "Hello World! 3"
    @test String(r4.body) == "Hello World! 4"
    @test String(r5.body) == "Hello World! 5"

    return t2 - t1
end

function HTTP.ConnectionPool.getconnection(::Type{Loopback},
                                           host::AbstractString,
                                           port::AbstractString;
                                           kw...)::Loopback
    return Loopback()
end

@testset "loopback" begin
    global server_events

    @testset "FunctionIO" begin
        io = FunctionIO(()->"Hello World!")
        @test String(read(io)) == "Hello World!"
    end

    @testset "lbreq - IOBuffer" begin
        r = lbreq("echo", [], ["Hello", IOBuffer(" "), "World!"]);
        @test String(r.body) == "Hello World!"
    end

    @testset "lbreq - FunctionIO" begin
        r = lbreq("echo", [], FunctionIO(()->"Hello World!"))
        @test String(r.body) == "Hello World!"
    end

    @testset "lbreq - Array of Strings" begin
        r = lbreq("echo", [], ["Hello", " ", "World!"]);
        @test String(r.body) == "Hello World!"
    end

    @testset "lbreq - Array of Bytes - Echo" begin
        r = lbreq("echo", [], [HTTP.bytes("Hello"),
                             HTTP.bytes(" "),
                             HTTP.bytes("World!")]);
        @test String(r.body) == "Hello World!"
    end

    @testset "lbreq - Array of Bytes - Delay" begin
        r = lbreq("delay10", [], [HTTP.bytes("Hello"),
                                  HTTP.bytes(" "),
                                  HTTP.bytes("World!")]);
        @test String(r.body) == "Hello World!"
    end

    @testset "lbopen - Body - Delay" begin
        body = nothing
        body_sent = false
        r = lbopen("delay10", []) do http
            @sync begin
                @async begin
                    write(http, "Hello World!")
                    closewrite(http)
                    body_sent = true
                end
                startread(http)
                body = read(http)
                closeread(http)
            end
        end
        @test String(body) == "Hello World!"
    end

    # "If [the response] indicates the server does not wish to receive the
    #  message body and is closing the connection, the client SHOULD
    #  immediately cease transmitting the body and close the connection."
    # https://tools.ietf.org/html/rfc7230#section-6.5
    @testset "lbopen - Body - Abort" begin
        body = nothing
        body_aborted = false
        body_sent = false
        @test_throws HTTP.StatusError begin
            r = lbopen("abort", []) do http
                @sync begin
                    @async try
                        sleep(0.1)
                        write(http, "Hello World!")
                        closewrite(http)
                        body_sent = true
                    catch e
                        if e isa ArgumentError &&
                            e.msg == "stream is closed or unusable"
                            body_aborted = true
                        else
                            rethrow(e)
                        end
                    end
                    startread(http)
                    body = read(http)
                    closeread(http)
                end
            end
        end
        @test body_aborted == true
        @test body_sent == false
    end

    @testset "libreq - Sleep" begin
        r = lbreq("echo", [], [
            FunctionIO(()->(sleep(0.1); "Hello")),
            FunctionIO(()->(sleep(0.1); " World!"))])
        @test String(r.body) == "Hello World!"

        hello_sent = false
        world_sent = false
        @test_throws HTTP.StatusError begin
            r = lbreq("abort", [], [
                FunctionIO(()->(hello_sent = true; sleep(0.1); "Hello")),
                FunctionIO(()->(world_sent = true; " World!"))])
        end
        @test hello_sent
        @test !world_sent
    end

    @testset "ASync - Pipeline limit = 0" begin
        server_events = []
        t = async_test(;pipeline_limit=0)
        @show t
        if haskey(ENV, "HTTP_JL_TEST_TIMING_SENSITIVE")
            @test server_events == [
                "Request: GET /delay1 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay1 HTTP/1.1)",
                "Request: GET /delay2 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay2 HTTP/1.1)",
                "Request: GET /delay3 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay3 HTTP/1.1)",
                "Request: GET /delay4 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay4 HTTP/1.1)",
                "Request: GET /delay5 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay5 HTTP/1.1)"]
        end
    end

    @testset "ASync - Pipeline limit = 1" begin
        server_events = []
        t = async_test(;pipeline_limit=1)
        if haskey(ENV, "HTTP_JL_TEST_TIMING_SENSITIVE")
            @test server_events == [
                "Request: GET /delay1 HTTP/1.1",
                "Request: GET /delay2 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay1 HTTP/1.1)",
                "Request: GET /delay3 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay2 HTTP/1.1)",
                "Request: GET /delay4 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay3 HTTP/1.1)",
                "Request: GET /delay5 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay4 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay5 HTTP/1.1)"]
        end
    end

    @testset "ASync - Pipeline limit = 2" begin
        server_events = []
        t = async_test(;pipeline_limit=2)
        if haskey(ENV, "HTTP_JL_TEST_TIMING_SENSITIVE")
            @test server_events == [
                "Request: GET /delay1 HTTP/1.1",
                "Request: GET /delay2 HTTP/1.1",
                "Request: GET /delay3 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay1 HTTP/1.1)",
                "Request: GET /delay4 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay2 HTTP/1.1)",
                "Request: GET /delay5 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay3 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay4 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay5 HTTP/1.1)"]
        end
    end

    @testset "ASync - Pipeline limit = 3" begin
        server_events = []
        t = async_test(;pipeline_limit=3)
        @show t
        if haskey(ENV, "HTTP_JL_TEST_TIMING_SENSITIVE")
            @test server_events == [
                "Request: GET /delay1 HTTP/1.1",
                "Request: GET /delay2 HTTP/1.1",
                "Request: GET /delay3 HTTP/1.1",
                "Request: GET /delay4 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay1 HTTP/1.1)",
                "Request: GET /delay5 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay2 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay3 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay4 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay5 HTTP/1.1)"]
        end
    end

    @testset "ASync - " begin
        server_events = []
        t = async_test()
        @show t
        if haskey(ENV, "HTTP_JL_TEST_TIMING_SENSITIVE")
            @test server_events == [
                "Request: GET /delay1 HTTP/1.1",
                "Request: GET /delay2 HTTP/1.1",
                "Request: GET /delay3 HTTP/1.1",
                "Request: GET /delay4 HTTP/1.1",
                "Request: GET /delay5 HTTP/1.1",
                "Response: HTTP/1.1 200 OK <= (GET /delay1 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay2 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay3 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay4 HTTP/1.1)",
                "Response: HTTP/1.1 200 OK <= (GET /delay5 HTTP/1.1)"]
        end
    end

    # "A user agent SHOULD NOT pipeline requests after a
    #  non-idempotent method, until the final response
    #  status code for that method has been received"
    # https://tools.ietf.org/html/rfc7230#section-6.3.2
    @testset "ASync - " begin
        server_events = []
        t = async_test(["POST","GET","GET","GET","GET"])
        @test server_events[1:2] == [
            "Request: POST /delay1 HTTP/1.1",
            "Response: HTTP/1.1 200 OK <= (POST /delay1 HTTP/1.1)"]
    end

    @testset "ASync - " begin
        server_events = []
        t = async_test(["GET","GET","POST", "GET","GET"])
        @test server_events[1:6] == [
            "Request: GET /delay1 HTTP/1.1",
            "Request: GET /delay2 HTTP/1.1",
            "Request: POST /delay3 HTTP/1.1",
            "Response: HTTP/1.1 200 OK <= (GET /delay1 HTTP/1.1)",
            "Response: HTTP/1.1 200 OK <= (GET /delay2 HTTP/1.1)",
            "Response: HTTP/1.1 200 OK <= (POST /delay3 HTTP/1.1)"] ||
        server_events[1:6] == [
            "Request: GET /delay1 HTTP/1.1",
            "Request: GET /delay2 HTTP/1.1",
            "Response: HTTP/1.1 200 OK <= (GET /delay1 HTTP/1.1)",
            "Request: POST /delay3 HTTP/1.1",
            "Response: HTTP/1.1 200 OK <= (GET /delay2 HTTP/1.1)",
            "Response: HTTP/1.1 200 OK <= (POST /delay3 HTTP/1.1)"]
    end

    HTTP.ConnectionPool.closeall()
end

ext: jl


text:
using Plots
include("../estimate_angles.jl")
include("../snake_forward.jl")
include("../../phantoms/sperm_phantom.jl")
using ResumableFunctions
using LsqFit
using Statistics

######################PREPROCESS#############################
r(s) = 1.0
global images, CL = get_sperm_phantom(400,r,collect(-36.5:0.1:36.5)))

function process_frame(frame)
    #translate so it starts at zero
    center_line = CL[frame].-CL[frame][1,:]'

    #find angles and rotatet the tail so it is on average parallel with the x-axis
    # this is done by converting points to complex numbers, finding the angles, taking average and turning -average
    head = center_line[1,:]
    cks = get_ck(center_line)

    xy = get_xy(cks)[2:end,:]

    angles_est = angle.(cks[2:end])
    average_angle = sum(angles_est)/length(angles_est)
    center_line = rotate_points(xy, -average_angle)
    plot(center_line[:,1], center_line[:,2], label="center line", aspect_ratio=:equal, legend=true)
    ################################################################################################

    #Define thickness function - same as in phantom TODO pass r to phantom function
    r(t) = 1.0

    #Determine the outline
    outline = get_outline(center_line, r)[1]
    plot!(outline[:,1], outline[:,2], label = "outline")

    #Define angles and bin  width for forward projection
    detmin, detmax = -36.5, 36.5
    bin_width = 0.125
    bins = collect(detmin:bin_width:detmax)
    angles = [0.0]
    projection = parallel_forward(outline,angles,bins)

    rebinned_bins, rebinned_projection = rebin(projection[:,1], bins, 39)

    # #plot forward projection
    y_vals = cat(rebinned_bins,rebinned_projection,dims=2)
    plot_vals = y_vals
    #plot!((bins.-1.0), projection,label="projection", color=:green, linetype=:steppost)
    plot!(plot_vals[:,1], plot_vals[:,2],label="projection", color=:green, linetype=:steppost)
    savefig(@sprintf "image_%d" frame)

    return estimate_curve(rebinned_projection, rebinned_bins, angles, head, r, 30 )
end

cwd = @__DIR__
savepath = normpath(joinpath(@__DIR__, "result"))
!isdir(savepath) && mkdir(savepath)
cd(savepath)
for i in 1:27
    plot()
    println(i)
    process_frame(i)
    savefig(@sprintf "angle_test_result_%d" i)
end
#process_frame(21)
#savefig(@sprintf "angle_test_result_%d" i)

max_stars_repo_name: QPImaging/xfromProjections

max_issues_repo_name: QPImaging/xfromProjections

max_forks_repo_name: QPImaging/xfromProjections


text:
# This file was generated, do not modify it. # hide
clf = machine(BayesianQDA(), X3, y)
fit!(clf, rows=train)
ŷ = predict_mode(clf, rows=test)

acc = mean(ŷ .== y[test])

max_stars_repo_name: vdayanand/MLJTutorials

max_issues_repo_name: vdayanand/MLJTutorials

max_forks_repo_name: vdayanand/MLJTutorials


text:
using MultiResolutionIterators
using Test

struct OneToOneIndexer end
MultiResolutionIterators.levelname_map(::OneToOneIndexer) = [
    :doc=>1,
    :para=>2,
    :sent=>3,
    :word=>4,
    :char=>5
]

struct ManyToOneIndexer end
MultiResolutionIterators.levelname_map(::ManyToOneIndexer) = [
    :doc=>1,
    :para=>2, :section=>2, :block=>2,
    :sent=>3,
    :word=>4, :token=>4,
    :char=>5
]

@testset "Unit common $indexer" for indexer in [OneToOneIndexer(), ManyToOneIndexer()]
    @testset "include lvls" begin
        @test lvls(indexer)|>Set == Set()
        @test lvls(indexer, 2)|>Set ==
              lvls(indexer, :para)|>Set ==
              [2] |>Set

        @test lvls(indexer, 4)|>Set ==
            lvls(indexer, :word)|>Set ==
            [4] |>Set

        @test lvls(indexer, 2, 4)|>Set ==
            lvls(indexer, :para, :word)|>Set ==
            [2, 4] |>Set

    end

    @testset "exclude levels (!lvls)" begin
        @test (!lvls)(indexer)|>Set == 1:5 |>Set

        @test (!lvls)(indexer, 2)|>Set ==
              (!lvls)(indexer, :para)|>Set ==
              [1,3,4,5] |>Set

        @test (!lvls)(indexer, 4)|>Set ==
              (!lvls)(indexer, :word)|>Set ==
              [1,2,3,5] |>Set

        @test (!lvls)(indexer, 2, 4)|>Set ==
          (!lvls)(indexer, :para, :word)|>Set ==
          [1,3,5] |>Set
    end
end

@testset "ManyToOneIndexer overlap" begin
    indexer = ManyToOneIndexer()
    @testset "lvls" begin
        @test_throws ArgumentError lvls(indexer, :fish)

        @test lvls(indexer)|>Set == Set()
        @test lvls(indexer, :para)|>Set ==
              lvls(indexer, :para, :section)|>Set ==
              [2] |>Set

        @test lvls(indexer, :token)|>Set ==
            lvls(indexer, :word, :word)|>Set ==
            [4] |>Set

        @test lvls(indexer, :para, :word)|>Set ==
            lvls(indexer, :para, :block, :word)|>Set ==
            [2, 4] |>Set

    end

    @testset "(!lvls)" begin
        @test (!lvls)(indexer)|>Set == 1:5 |>Set

        @test (!lvls)(indexer, :para)|>Set ==
              (!lvls)(indexer, :para, :section)|>Set ==
              [1,3,4,5] |>Set

        @test (!lvls)(indexer, :token)|>Set ==
              (!lvls)(indexer, :word, :token)|>Set ==
              [1,2,3,5] |>Set

        @test (!lvls)(indexer, :para, :word,)|>Set ==
          (!lvls)(indexer, :para, :block, :word)|>Set ==
          [1,3,5] |>Set
      end
end

@testset "Dict checking" for indexer in [OneToOneIndexer(), ManyToOneIndexer()]
    @test lvls(indexer, Dict(2=>"a")) ==
          lvls(indexer, Dict(:para=>"a")) ==
          Dict(2=>"a")

    @test lvls(indexer, Dict(4=>"b")) ==
        lvls(indexer, Dict(:word=>"b")) ==
        Dict(4=>"b")

    @test lvls(indexer, Dict(4=>"b", 2=>"a")) ==
        lvls(indexer, Dict(:word=>"b", :para=>"a")) ==
        Dict(4=>"b", 2=>"a")

    @test lvls(indexer, Dict(1=>"d", 4=>"b", 2=>"a")) ==
        lvls(indexer, Dict(:doc=>"d", :word=>"b", :para=>"a")) ==
        Dict(1=>"d", 4=>"b", 2=>"a")
end


text:
using .Gadfly

using NeXLCore
using Colors

function Gadfly.plot(rur::RecordingUpdateRule, measured::KRatio, celm = nothing)
    elm = measured.element
    ks, cs, lbls = [], [], []
    for i in eachindex(rur.comps)
        estkr = rur.comps[i][elm] * rur.zafs[i][elm] / rur.meas[elm].standard[elm]
        push!(ks, estkr)
        push!(cs, rur.comps[i][elm])
        push!(lbls, "$(i)")
    end
    if isnothing(celm)
        celm = rur.comps[end][elm]
    end
    l1 = layer(x = ks, y = cs, label = lbls, Geom.point, Geom.label(position = :above))
    l2 = layer(x = [value(measured.kratio)], y = [celm], Geom.point, Theme(default_color = "red"))
    plot(l2, l1, Guide.XLabel("k[$(elm.symbol)]"), Guide.YLabel("C[$(elm.symbol)]"))
end

function Gadfly.plot(
    rur::RecordingUpdateRule,
    measured::Vector{KRatio},
    celm::Union{Nothing,Material} = nothing,
    width = 3,
)
    plots =
        Union{Plot,Gadfly.Context}[plot(rur, kr, isnothing(celm) ? celm : value(celm[kr.element])) for kr in measured]
    height = convert(Int, floor((length(plots) + width - 1) / width))
    for i = length(plots)+1:height*width
        push!(plots, Gadfly.context())
    end
    gridstack(reshape(plots, height, width))
end

function Gadfly.plot(tmc::Type{<:MatrixCorrection}, mat::Material, shells::Vector{AtomicSubShell}, beamEnergy::Float64)
    zz, sh, prz = Float64[], String[], Float64[]
    r = range(tmc, mat, beamEnergy, false)
    for shell in shells
        mc = matrixcorrection(tmc, mat, shell, beamEnergy)
        for z in range(0.0, stop = r, length = 100)
            push!(zz, z)
            push!(sh, repr(shell))
            push!(prz, ϕ(mc, z))
        end
    end
    df = DataFrame(ρz = zz, Shell = sh, ϕρz = prz)
    plot(
        df,
        x = :ρz,
        y = :ϕρz,
        color = :Shell,
        Geom.line,
        Coord.Cartesian(xmin = 0.0, xmax = r),
        Guide.xlabel("ρz [g/cm²]"),
        Guide.ylabel("ϕ(ρz)"),
    )
end

function Gadfly.plot(
    tmc::Type{<:MatrixCorrection},
    mat::Material,
    cxrs::Vector{CharXRay},
    beamEnergy::Float64,
    takeOffAngle,
)
    zz, sh, prz, lsty = Float64[], String[], Float64[], Int[]
    r = range(tmc, mat, beamEnergy, false)
    for cxr in cxrs
        shell = inner(cxr)
        mc = matrixcorrection(tmc, mat, shell, beamEnergy)
        for z in range(0.0, stop = r, length = 100)
            push!(zz, z)
            push!(sh, "$cxr")
            push!(prz, ϕ(mc, z))
            push!(lsty, 1)
            push!(zz, z)
            push!(sh, "$cxr")
            push!(prz, ϕabs(mc, z, cxr, takeOffAngle))
            push!(lsty, 2)
        end
    end
    df = DataFrame(ρz = zz, Line = sh, LineStyle = lsty, ϕρz = prz)
    plot(
        df,
        x = :ρz,
        y = :ϕρz,
        color = :Line,
        linestyle = :LineStyle,
        Geom.line,
        Scale.linestyle_discrete(),
        Coord.Cartesian(xmin = 0.0, xmax = r),
        Guide.xlabel("ρz [g/cm²]"),
        Guide.ylabel("ϕ(ρz)"),
        Guide.title("$(name(mat)) at $(0.001*beamEnergy) keV"),
    )
end

function Gadfly.plot(
    tmcs::AbstractVector{DataType},
    mat::Material,
    cxr::CharXRay,
    beamEnergy::Float64,
    takeOffAngle::Float64=deg2rad(40.0),
)
    zz, sh, prz, lsty = Float64[], String[], Float64[], Int[]
    r = 1.2*(range(Kanaya1972, mat, beamEnergy, false) - range(Kanaya1972, mat, energy(inner(cxr)), false))
    shell = inner(cxr)
    for z in range(0.0, stop = r, length = 100)
        for tmc in tmcs
            mc = matrixcorrection(tmc, mat, shell, beamEnergy)
            push!(zz, z)
            push!(sh, repr(tmc, context=:compact=>true)*":gen")
            push!(prz, ϕ(mc, z))
            push!(lsty, 1)
            push!(zz, z)
            push!(sh, repr(tmc, context=:compact=>true)*":abs")
            push!(prz, ϕabs(mc, z, cxr, takeOffAngle))
            push!(lsty, 2)
        end
    end
    df = DataFrame(ρz = zz, Line = sh, LineStyle = lsty, ϕρz = prz)
    plot(
        df,
        x = :ρz,
        y = :ϕρz,
        color = :Line,
        linestyle = :LineStyle,
        Geom.line,
        Scale.linestyle_discrete(),
        Coord.Cartesian(xmin = 0.0, xmax = r),
        Guide.xlabel("ρz [g/cm²]"),
        Guide.ylabel("ϕ(ρz)"),
        Guide.title("$(repr(cxr)) in $(name(mat)) at $(0.001*beamEnergy) keV"),
    )
end

function Gadfly.plot(krs::AbstractArray{KRatio}, mc=XPP, fc=ReedFluorescence, cc=Coating; palette=NeXLPalette)
    mfs, kok, dkok, color = String[], Float64[], Float64[], Color[]
    next = 1
    matcolors = Dict{String,RGB{Float64}}()
    for kr in filter(kr->haskey(kr.unkProps, :Composition), krs)
        unkComp = kr.unkProps[:Composition]
        if hasminrequired(mc, kr.unkProps) && hasminrequired(fc, kr.unkProps) && #
           hasminrequired(mc, kr.stdProps) && hasminrequired(fc, kr.stdProps) && #
           (value(unkComp[kr.element]) > 0.0) && (value(kr.standard[kr.element]) > 0.0)
             # Compute the k-ratio
             kc = gZAFc(kr, unkComp, mc, fc, cc) * (value(unkComp[kr.element]) / value(kr.standard[kr.element]))
             push!(mfs, name(shell(brightest(kr.xrays)))) # value(unkComp[kr.element]))
             push!(kok, value(kr.kratio) / kc)
             push!(dkok, σ(kr.kratio) / kc)
             matname = "C($(symbol(kr.element)), $(name(unkComp)), $(kr.unkProps[:BeamEnergy]/1000.0) keV, $(name(kr.stdProps[:Composition])))"
             if !haskey(matcolors, matname)
                 matcolors[matname] = palette[next]
                 next += 1
             end
             push!(color, matcolors[matname])
        end
    end
    plot(
        x = mfs,
        y = kok,
        ymin = kok .- dkok,
        ymax = kok .+ dkok,
        color = color,
        Geom.errorbar,
        Stat.x_jitter(range = 0.8),
        Guide.manual_color_key("Material", [keys(matcolors)...], [values(matcolors)...]), # Guide.yrug,
        Guide.xlabel("Shell"),
        Guide.ylabel("k[Measured]/k[Calculated]"),
    )
end

function Gadfly.plot(krs::AbstractArray{KRatio}, unkComp::Material, mc=XPP, fc=ReedFluorescence, cc=Coating; palette=NeXLPalette)
    mfs, kok, dkok, color = String[], Float64[], Float64[], Color[]
    next = 1
    matcolors = Dict{String,RGB{Float64}}()
    for kr in filter(kr->name(get(kr.unkProps, :Composition, unkComp)) == name(unkComp), krs)
        if hasminrequired(mc, kr.unkProps) &&
           hasminrequired(fc, kr.unkProps) && #
           hasminrequired(mc, kr.stdProps) &&
           hasminrequired(fc, kr.stdProps) && #
           (value(unkComp[kr.element]) > 0.0) &&
           (value(kr.standard[kr.element]) > 0.0)
            # Compute the k-ratio
            kc = gZAFc(kr, unkComp, mc, fc, cc) * (value(unkComp[kr.element]) / value(kr.standard[kr.element]))
            push!(mfs, name(shell(brightest(kr.xrays)))) # value(unkComp[kr.element]))
            push!(kok, value(kr.kratio) / kc)
            push!(dkok, σ(kr.kratio) / kc)
            matname = "$(name(unkComp)) $(kr.unkProps[:BeamEnergy]/1000.0) keV"
            if !haskey(matcolors, matname)
                matcolors[matname] = palette[next]
                next += 1
            end
            push!(color, matcolors[matname])
        end
    end
    plot(
        x = mfs,
        y = kok,
        ymin = kok .- dkok,
        ymax = kok .+ dkok,
        color = color,
        Geom.errorbar,
        Stat.x_jitter(range = 0.4),
        Guide.manual_color_key("Material", [keys(matcolors)...], [values(matcolors)...]), # Guide.yrug,
        Guide.xlabel("Shell"),
        Guide.ylabel("k[Measured]/k[Calculated]"),
    )
end

Gadfly.plot(irs::AbstractArray{IterationResult}; known::Union{Material,Missing} = missing, delta::Bool = false) =
    plot([ir.comp for ir in irs], known = known, delta = delta, label = "Measurement")

plot2(irs::AbstractVector{IterationResult}; known::Union{Material, Missing}=missing) =
    NeXLCore.plot2([ir.comp for ir in irs], known=known, label="Measurement")

max_stars_repo_name: NicholasWMRitchie/NeXLMatrixCorrection

max_issues_repo_name: NicholasWMRitchie/NeXLMatrixCorrection

max_forks_repo_name: NicholasWMRitchie/NeXLMatrixCorrection


text:
"""
    ActionSetting

The current settings of an [`Action`](@ref) which are saved in `action.current_setting`.

# Fields
- `line_width::Float64`: the current line width
- `mul_line_width::Float64`: the current multiplier for line width.
    The actual line width is then: `mul_line_width * line_width`
- `opacity::Float64`: the current opacity
- `mul_opacity::Float64`: the current multiplier for opacity.
    The actual opacity is then: `mul_opacity * opacity`
- `fontsize::Float64` the current font size
- `show_action::Bool` is set to false if scale would be 0.0 which is forbidden by Cairo
- `current_scale::Tuple{Float64, Float64}`: the current scale
- `desired_scale::Tuple{Float64, Float64}`: the new desired scale
- `mul_scale::Float64`: the multiplier for the new desired scale.
    The actual new scale is then: `mul_scale * desired_scale`
"""
mutable struct ActionSetting
    line_width::Float64
    mul_line_width::Float64 # the multiplier of line width is between 0 and 1
    opacity::Float64
    mul_opacity::Float64 # the multiplier of opacity is between 0 and 1
    fontsize::Float64
    # scale has three fields instead of just the normal two
    # current scale
    # desired scale and scale multiplier => `desired_scale*mul_scale` is the new desired scale
    # the scale change needs to be computed using `current_scale` and the desired scale
    # current_scale should never be 0 as this breaks scaleto has various other bad effects
    # see: https://github.com/JuliaGraphics/Luxor.jl/issues/114
    # in this case show will be set to false and the action will not be called
    show_action::Bool
    current_scale::Tuple{Float64,Float64}
    desired_scale::Tuple{Float64,Float64}
    mul_scale::Float64 # the multiplier of scale is between 0 and 1
end

ActionSetting() = ActionSetting(1.0, 1.0, 1.0, 1.0, 10.0, true, (1.0, 1.0), (1.0, 1.0), 1.0)

"""
    update_ActionSetting!(as::ActionSetting, by::ActionSetting)

Set the fields of `as` to the same as `by`. Basically copying them over.
"""
function update_ActionSetting!(as::ActionSetting, by::ActionSetting)
    as.line_width = by.line_width
    as.mul_line_width = by.mul_line_width
    as.opacity = by.opacity
    as.mul_opacity = by.mul_opacity
    as.fontsize = by.fontsize
    as.show_action = by.show_action
    as.current_scale = by.current_scale
    as.desired_scale = by.desired_scale
    as.mul_scale = by.mul_scale
end

function update_background_settings!(setting::ActionSetting, action::AbstractAction)
    in_global_layer = get(action.opts, :in_global_layer, false)
    if in_global_layer
        update_ActionSetting!(setting, action.current_setting)
    end
end

function update_action_settings!(action::AbstractAction, setting::ActionSetting)
    update_ActionSetting!(action.current_setting, setting)
end


text:
# This file is a part of DataDeps.jl. License is MIT.

"""
    preupload_check(datadep, local_filepath[s])::Bool)

Peforms preupload checks on the local files without having to download them.
This is tool for creating or updating DataDeps,
allowing the author to check the files before they are uploaded (or if downloaded directly).
This checking includes checking the checksum, and the making sure the `post_fetch_method` runs without errors.
It basically performs datadep resolution, but bypasses the step of downloading the files.
The results of performing the `post_fetch_method` are not kept.
As normal if the DataDep being checked does not have a checksum, or if the checksum does not match,
then a warning message will be displayed.
Similarly, if the `post_fetch_method` throws an exception, a warning will be displayed.

Returns: true or false, depending on if the checks were all good, or not.

Arguements:

 - `datadep`: Either an instance of a DataDep type, or the name of a registered DataDep as a AbstractString
 - `local_filepath`: a filepath or (recursive) list of filepaths.
    This is what would be returned by fetch in normal datadep use.
"""
function preupload_check(dd::DataDep, local_filepath)
    checksum_passes = run_checksum(dd.hash, local_filepath)
    postfetch_passes = postfetch_check(dd.post_fetch_method, local_filepath)

    return checksum_passes && postfetch_passes
end

function preupload_check(datadep_name::AbstractString, fetched_files)
    return preupload_check(registry[datadep_name], fetched_files)
end

"""
    postfetch_check(post_fetch_method, local_path)

Executes the post_fetch_method on the given local path,
in a temportary directory.
Returns true if there are no exceptions.
Performs in (async) parallel if multiple paths are given
"""
function postfetch_check(post_fetch_method, local_path)
    return mktempdir() do working_dir
        working_path = cp(local_path, joinpath(working_dir, basename(local_path)))
        try
            run_post_fetch(post_fetch_method, working_path)
            return true
        catch err
            @warn "Post-fetch method threw an exception" local_path exception=err
            return false
        end
    end
end

function postfetch_check(post_fetch_method, fetched_paths::AbstractVector)
    asyncmap(fp->postfetch_check(post_fetch_method, fp),  fetched_paths)
end

function postfetch_check(post_fetch_methods::AbstractVector, fetched_paths::AbstractVector)
    asyncmap(postfetch_check,  post_fetch_methods, fetched_paths)
end


text:
"""
    compute_derivative

Compute and return directional derivative

# Arguments
- `∇f`: evaluation of the objective gradient
- `p`: search direction
- `∇fp`: objective gradient times times search direction, i.e., `∇f' * p`
- `μ`: penalty parameter
- `cons_viol`: constraint violations
"""
compute_derivative(∇fp::T, μ::T, cons_viol::T) where {T} = ∇fp - μ * cons_viol
compute_derivative(∇fp::T, μ::Tv, cons_viol::Tv) where {T, Tv<:AbstractArray{T}} = ∇fp - μ' * cons_viol
compute_derivative(∇fp::T, μ::T, cons_viol::Tv) where {T, Tv<:AbstractArray{T}} = ∇fp - μ * sum(cons_viol)
compute_derivative(∇f::Tv, p::Tv, μ::T, cons_viol::Tv) where {T, Tv<:AbstractArray{T}} = ∇f' * p - μ * sum(cons_viol)
compute_derivative(∇f::Tv, p::Tv, μ::Tv, cons_viol::Tv) where {T, Tv<:AbstractArray{T}} = ∇f' * p - μ' * cons_viol


text:
using Base.Test
import Multirate
import DSP
import Multirate.NaiveResamplers

# Disable time and printf macros when not running interactivly ( for travis )
if !isinteractive()
    macro time( ex )
        quote
            $(esc(ex))
        end
    end

    macro printf( args... )
    end
end

function Base.isapprox( x1::Vector, x2::Vector )
    Nx1 = length( x1 )
    Nx2 = length( x2 )

    if Nx1 != Nx2
        @printf("x1 & x2 are different lengths vectors\n")
        return false
    end

    for i = 1:Nx1
        if !isapprox( x1[i], x2[i] )
            @printf( "Something went wrong at index %d\n", i )
            return false
        end
    end

    return true
end

#==============================================================================#
#               ____ _ _  _ ____ _    ____    ____ ____ ___ ____               #
#               [__  | |\ | | __ |    |___    |__/ |__|  |  |___               #
#               ___] | | \| |__] |___ |___    |  \ |  |  |  |___               #
#==============================================================================#

function test_singlerate( h, x )
    xLen       = length( x )
    hLen       = length( h )
    pivotPoint = min( rand(50:150, 1)[1], ifloor( xLen/4 ))
    x1         = x[ 1 : pivotPoint ]
    x2         = x[ pivotPoint+1 : end ]

    @printf( "\n\n" )
    @printf( "____ _ _  _ ____ _    ____    ____ ____ ___ ____\n" )
    @printf( "[__  | |\\ | | __ |    |___    |__/ |__|  |  |___\n" )
    @printf( "___] | | \\| |__] |___ |___    |  \\ |  |  |  |___\n" )
    @printf( "\nTesting single-rate fitering, h is %s, x is %s. xLen = %d, hLen = %d", string(eltype(h)), string(eltype(x)), xLen, hLen )

    @printf( "\n\tBase.filt\n\t\t")
    @time baseResult = Base.filt( h, 1.0, x )

    if method_exists( DSP.firfilt, ( typeof(h), typeof(x) ))
        @printf( "\n\tDSP.firfilt\n\t\t")
        @time dspResult = DSP.firfilt( h, x )
    end

    @printf( "\n\tMultirate.filt( h, x, 1//1 )\n\t\t" )
    @time statelesResult = Multirate.filt( h, x )

    @printf( "\n\tMultirate.filt. length( x1 ) = %d, length( x2 ) = %d\n\t\t", length( x1 ), length( x2 ) )
    self = Multirate.FIRFilter( h, 1//1 )
    @time begin
        y1 = Multirate.filt( self, x1 )
        y2 = Multirate.filt( self, x2 )
    end
    statefulResult = [ y1, y2 ]

    @printf( "\n\tMultirate.filt filt. Piecewise for first %d inputs\n\t\t", length( x1 ) )
    Multirate.reset( self )
    @time begin
        for i in 1:length(x1)
            y1[i] = Multirate.filt( self, x1[i:i] )[1]
        end
        y2 = Multirate.filt( self, x2 )
    end
    piecewiseResult = [ y1, y2 ]

     if isapprox( baseResult, statelesResult ) && isapprox( baseResult, statefulResult ) && isapprox( baseResult, piecewiseResult )
         return true
     end

     display( [ baseResult statelesResult statefulResult piecewiseResult ] )

     return false

end

#==============================================================================#
#                      ___  ____ ____ _ _  _ ____ ___ ____                     #
#                      |  \ |___ |    | |\/| |__|  |  |___                     #
#                      |__/ |___ |___ | |  | |  |  |  |___                     #
#==============================================================================#

function test_decimation( h, x, decimation )
    xLen       = length( x )
    hLen       = length( h )
    pivotPoint = min( rand(50:150, 1)[1], ifloor( xLen/4 ))
    x1         = x[ 1 : pivotPoint ]
    x2         = x[ pivotPoint+1 : end ]

    @printf( "\n\n" )
    @printf( "___  ____ ____ _ _  _ ____ ___ _ ____ _  _ \n" )
    @printf( "|  \\ |___ |    | |\\/| |__|  |  | |  | |\\ | \n" )
    @printf( "|__/ |___ |___ | |  | |  |  |  | |__| | \\| \n" )
    @printf( "\nTesting decimation. h::%s, x::%s. xLen = %d, hLen = %d, decimation = %d", string(typeof(h)), string(typeof(h)), xLen, hLen, decimation )

    @printf( "\n\tNaive decimation with Base.filt\n\t\t")
    @time begin
        baseResult   = Base.filt( h, one(eltype(h)), x )
        baseResult   = baseResult[1:decimation:end]
    end

    if method_exists( DSP.firfilt, ( typeof(h), typeof(x) ))
        @printf( "\n\tNaive decimation with DSP.firfilt\n\t\t")
        @time begin
            dspResult = DSP.firfilt( h, x )
            dspResult = dspResult[1:decimation:end]
        end
    end

    @printf( "\n\tMultirate.filt( h, x, 1//%d)\n\t\t", decimation )
    @time statelesResult = Multirate.filt( h, x, 1//decimation )

    @printf( "\n\tMultirate.filt decimation. length( x1 ) = %d, length( x2 ) = %d\n\t\t", length( x1 ), length( x2 ) )
    self = Multirate.FIRFilter( h, 1//decimation )
    @time begin
        y1 = Multirate.filt( self, x1 )
        y2 = Multirate.filt( self, x2 )
    end
    statefulResult = [ y1, y2 ]

    @printf( "\n\tMultirate.filt decimation. Piecewise for first %d inputs.\n\t\t", length( x1 ) )
    Multirate.reset( self )
    y1 = similar( x, 0 )
    @time begin
        for i in 1:length(x1)
            append!( y1, Multirate.filt( self, x1[i:i] ) )
        end
        y2 = Multirate.filt( self, x2 )
    end
    piecewiseResult = [ y1, y2 ]

    if isapprox( baseResult, statelesResult ) && isapprox( baseResult, statefulResult ) && isapprox( baseResult, piecewiseResult )
        return true
    end

    display( [ baseResult statefulResult piecewiseResult ] )
    return false

end

#==============================================================================#
#               _ _  _ ___ ____ ____ ___  _    ____ ____ ___ ____              #
#               | |\ |  |  |___ |__/ |__] |    |  | |__|  |  |___              #
#               | | \|  |  |___ |  \ |    |___ |__| |  |  |  |___              #
#==============================================================================#

function test_interpolation( h, x, interpolation )
    xLen       = length( x )
    hLen       = length( h )
    pivotPoint = min( rand(50:150, 1)[1], ifloor( xLen/4 ))
    x1         = x[ 1 : pivotPoint ]
    x2         = x[ pivotPoint+1 : end ]

    @printf( "\n\n" )
    @printf( "_ _  _ ___ ____ ____ ___  _    ____ ____ ___ _ ____ _  _ \n" )
    @printf( "| |\\ |  |  |___ |__/ |__] |    |  | |__|  |  | |  | |\\ | \n" )
    @printf( "| | \\|  |  |___ |  \\ |    |___ |__| |  |  |  | |__| | \\| \n" )
    @printf( "\nTesting interpolation, h::%s, x::%s. xLen = %d, hLen = %d, interpolation = %d", typeof(h), typeof(x), xLen, hLen, interpolation )

    @printf( "\n\tNaive interpolation with Base.filt\n\t\t")
    @time begin
        xZeroStuffed = zeros( eltype(x), xLen * interpolation )
        for n = 0:xLen-1;
            xZeroStuffed[ n*interpolation+1 ] = x[ n+1 ]
        end
        baseResult = Base.filt( h, one(eltype(h)), xZeroStuffed )
    end

    if method_exists( DSP.firfilt, ( typeof(h), typeof(x) ))
        @printf( "\n\tNaive interpolation with DSP.firfilt\n\t\t")
        @time begin
            xZeroStuffed = zeros( eltype(x), xLen * interpolation )
            for n = 0:xLen-1;
                xZeroStuffed[ n*interpolation+1 ] = x[ n+1 ]
            end
            dspResult = DSP.firfilt( h, xZeroStuffed )
        end
    end

    @printf( "\n\tMultirate.filt( h, x, %d//1 )\n\t\t", interpolation )
    @time statelesResult = Multirate.filt( h, x, interpolation//1 )

    @printf( "\n\tMultirate.filt interpolation. length( x1 ) = %d, length( x2 ) = %d\n\t\t", length( x1 ), length( x2 ) )
    self = Multirate.FIRFilter( h, interpolation//1 )
    @time begin
        y1 = Multirate.filt( self, x1 )
        y2 = Multirate.filt( self, x2 )
    end
    statefulResult = [ y1, y2 ]

    @printf( "\n\tMultirate.filt interpolation. Piecewise for first %d inputs\n\t\t", length( x1 ) )
    Multirate.reset( self )
    y1 = similar( x, 0 )
    @time begin
        for i in 1:length(x1)
            append!( y1, Multirate.filt( self, x1[i:i] ) )
        end
        y2 = Multirate.filt( self, x2 )
    end
    piecewiseResult = [ y1, y2 ]

    if isapprox( baseResult, statelesResult ) && isapprox( baseResult, statefulResult ) && isapprox( piecewiseResult, baseResult )
        return true
    end

    display( [ [1:length(baseResult)] baseResult statefulResult piecewiseResult ] )

    return false
end

#==============================================================================#
#           ____ ____ ___     ____ ____ ____ ____ _  _ ___  _    ____          #
#           |__/ |__|  |      |__/ |___ [__  |__| |\/| |__] |    |___          #
#           |  \ |  |  |  .   |  \ |___ ___] |  | |  | |    |___ |___          #
#==============================================================================#

function test_rational( h, x, ratio )
    xLen       = length( x )
    hLen       = length( h )
    pivotPoint = min( rand(50:150, 1)[1], ifloor( xLen/4 ))
    x1         = x[ 1 : pivotPoint ]
    x2         = x[ pivotPoint+1 : end ]
    upfactor   = num( ratio )
    downfactor = den( ratio )
    resultType = promote_type( eltype(h), eltype(x) )

    @printf( "\n\n" )
    @printf( "      ____ ____ ___ _ ____ _  _ ____ _    \n" )
    @printf( "      |__/ |__|  |  | |  | |\\ | |__| |    \n" )
    @printf( "      |  \\ |  |  |  | |__| | \\| |  | |___ \n" )
    @printf( "                                          \n" )
    @printf( "____ ____ ____ ____ _  _ ___  _    _ _  _ ____\n" )
    @printf( "|__/ |___ [__  |__| |\\/| |__] |    | |\\ | | __\n" )
    @printf( "|  \\ |___ ___] |  | |  | |    |___ | | \\| |__]\n" )
    @printf( "\n\nTesting rational resampling, h::%s, x::%s. xLen = %d, hLen = %d, ratio = %d//%d", string(typeof(h)), string(typeof(x)), xLen, hLen, upfactor, downfactor )

    @printf( "\n\tNaive rational resampling with Base.filt\n\t\t")
    @time begin
        xStuffed   = zeros( resultType, length(x) * upfactor )
        baseResult = Array( resultType, int( ceil( length(x) * ratio )))

        for n = 0:length(x)-1;
            xStuffed[ n*upfactor+1 ] = x[ n+1 ]
        end

        baseResult = Base.filt( h, one(eltype(h)), xStuffed );
        baseResult = [ baseResult[n] for n = 1:downfactor:length( baseResult ) ]
    end

    if method_exists( DSP.firfilt, ( typeof(h), typeof(x) ))
        @printf( "\n\tNaive rational resampling DSP.firfilt\n\t\t")
        @time begin
            xStuffed  = zeros( resultType, length(x) * upfactor )
            dspResult = Array( resultType, int( ceil( length(x) * ratio )))

            for n = 0:length(x)-1;
                xStuffed[ n*upfactor+1 ] = x[ n+1 ]
            end

            dspResult = DSP.firfilt( h, xStuffed );
            dspResult = [ dspResult[n] for n = 1:downfactor:length( dspResult ) ]
        end
    end

    @printf( "\n\tMultirate.filt( h, x, %d//%d )\n\t\t", upfactor, downfactor )
    @time statelesResult = Multirate.filt( h, x, ratio )

    @printf( "\n\tMultirate.filt rational resampling. length( x1 ) = %d, length( x2 ) = %d\n\t\t", length( x1 ), length( x2 ) )
    self = Multirate.FIRFilter( h, ratio )
    @time begin
        s1 = Multirate.filt( self, x1 )
        s2 = Multirate.filt( self, x2 )
    end
    statefulResult = [ s1, s2 ]

    @printf( "\n\tMultirate.filt rational. Piecewise for all %d inputs\n\t\t", length( x ) )
    self = Multirate.FIRFilter( h, ratio )
    y1 = similar( x, 0 )
    @time begin
        for i in 1:length(x)
            append!( y1, Multirate.filt( self, x[i:i] ) )
        end
    end
    piecewiseResult = y1

    if isapprox( baseResult, statelesResult ) && isapprox( baseResult, statefulResult ) && isapprox( baseResult, piecewiseResult )
        return true
    end

    display( [  baseResult statefulResult statefulResult piecewiseResult ] )

    return false
end

#==============================================================================#
#        ____ ____ ___      ____ ____ ____ ____ _  _ ___  _    _ _  _ ____     #
#        |__| |__/ |__]     |__/ |___ [__  |__| |\/| |__] |    | |\ | | __     #
#        |  | |  \ |__] .   |  \ |___ ___] |  | |  | |    |___ | | \| |__]     #
#==============================================================================#

function test_arbitrary( Th, x, resampleRate, numFilters )
    cutoffFreq      = 0.45
    transitionWidth = 0.05
    (hLen, β)       = Multirate.kaiserlength( transitionWidth, samplerate = numFilters )
    hLen            = iceil(  hLen/numFilters  ) .* numFilters
    h               = Multirate.firdes( hLen, cutoffFreq, DSP.kaiser, samplerate = 32, beta = β ) .* numFilters
    h               = convert( Vector{Th}, h )

    @printf( "____ ____ ___      ____ ____ ____ ____ _  _ ___  _    _ _  _ ____\n" )
    @printf( "|__| |__/ |__]     |__/ |___ [__  |__| |\\/| |__] |    | |\\ | | __\n" )
    @printf( "|  | |  \\ |__] .   |  \\ |___ ___] |  | |  | |    |___ | | \\| |__]\n" )
    @printf( "\n\nh::%s, x::%s, rate = %f, N𝜙 = %d, xLen = %d, ", string(typeof(h)), string(typeof(x)), resampleRate, numFilters, length(x) )

    @printf( "\n\tNaive arbitrary resampling\n\t\t" )
    @time naiveResult = NaiveResamplers.naivefilt( h, x, resampleRate, numFilters )

    @printf( "\n\tStateless arbitrary resampling\n\t\t" )
    @time statelessResult = Multirate.filt( h, x, resampleRate, numFilters )

    @printf( "\n\tPiecewise arbitrary resampling\n\t\t" )
    self           = Multirate.FIRFilter( h, resampleRate, numFilters )
    piecwiseResult = eltype(x)[]
    sizehint( piecwiseResult, iceil( length(x)*resampleRate ) )
    @time for i in 1:length( x )
        thisY = filt( self, x[i:i] )
        append!( piecwiseResult, thisY )
    end

    commonLen = min( length(naiveResult), length(statelessResult), length(piecwiseResult) )

    resize!( naiveResult, commonLen )
    resize!( statelessResult, commonLen )
    resize!( piecwiseResult, commonLen )

    if isapprox( naiveResult, statelessResult ) && isapprox( naiveResult, piecwiseResult )
        return true
    end

    display( [  [1:commonLen] naiveResult statelessResult  piecwiseResult abs(naiveResult.-statelessResult) abs(naiveResult.-piecwiseResult) ] )

    display( [ [1:commonLen-1] diff(naiveResult) diff(statelessResult) diff(piecwiseResult) ] )
    return false
end

#==============================================================================#
#                  ____ _  _ _  _    ___ ____ ____ ___ ____                    #
#                  |__/ |  | |\ |     |  |___ [__   |  [__                     #
#                  |  \ |__| | \|     |  |___ ___]  |  ___]                    #
#==============================================================================#

function test_all()
    for interpolation in sort([1, unique(rand(2:32,8))] ),
            decimation in sort([1, unique(rand(2:32,8))] ),
                Th in [Float32, Float64],
                    Tx in [Float32, Float64, Complex64, Complex128]

        h     = rand(Th, rand(16:128,1)[1] )
        xLen  = int(rand( 200:300, 1 )[1])
        xLen  = xLen-mod( xLen, decimation )
        x     = rand( Tx, xLen )
        ratio = interpolation//decimation

        if ratio == 1
            @test test_singlerate( h, x )
        end

        if decimation != 1
            @test test_decimation( h, x, decimation )
        end

        if interpolation != 1
            @test test_interpolation( h, x, interpolation )
        end

        if num(ratio) == interpolation && den(ratio) == decimation && num(ratio) != 1 && den(ratio) != 1
            @test test_rational( h, x, ratio )
            if Tx in [ Float32, Complex64 ]
                @test test_arbitrary( Th, x, float64(ratio)+rand(), 32 )
            end
        end
    end
end

function test_nextphase()
    for interpolation in 1:8
        for decimation in 1:8
            ratio           = interpolation//decimation
            interpolation   = num(ratio)
            decimation      = den(ratio)
            x               = repmat( [1:interpolation], decimation )
            reference = [ x[n] for n = 1:decimation:length( x ) ]
            result = [ 1 ]
            for i in 2:interpolation
                append!( result, [ Multirate.nextphase( result[end], ratio ) ] )
            end
            @test isapprox( reference, result )
        end
    end
end

test_nextphase()
test_all()


text:
mutable struct ExactState <: QState
	site:: ITensor
	ExactState(T::ITensor) = new(T)
	function ExactState(N::Int, init::Vector{T} where T<:Number)
		norm(init) != 1 && error("initial state must have norm 1\n")
		net = ITensor[]
		for i =1: N
			push!(net, ITensor(init, Index(2,"Site, q=$(i)")))
		end
		new(prod(net))
	end
end #struct

function ExactState(N::Int)
	shape = ones(Int, N).*2
	D = zeros(shape...)
	D[1]=1
	inds = [Index(2, "Site, q=$(i)") for i=1:N]
	return ExactState(ITensor(D, inds...))
end

copy(state::ExactState) = ExactState(copy(state.site))
isapprox(state1::ExactState, state2::ExactState) = isapprox(state1.site,state2.site)
numQubits(state::ExactState) = order(state.site)

function toMPSState(state::ExactState; kwargs...)
	numQubits = length(IndexSet(state.site))
	leftLink = nothing
	remain = copy(state.site)
	sites = ITensor[]
	for i =1:numQubits-1
		if leftLink != nothing
			U,S,V,leftLink,v = svd(remain,
								   IndexSet(leftLink, findindex(remain, "q=$(i)"));
								   kwargs...)
		else
			U,S,V,leftLink,v = svd(remain, findindex(remain, "q=$(i)"); kwargs...)
		end
		push!(sites, replacetags(U, "u", "l=$(i)"))
		S = replacetags(S, "u", "l=$(i)")
		leftLink = replacetags(leftLink, "u", "l=$(i)")
		remain = S*V
	end
	push!(sites, remain)
	return MPSState(sites,QubitSiteMap(numQubits), numQubits-1, numQubits+1)
end
function ρ(state::ExactState, config::Vector{Int})
	# todo: check error input
	pairs = Vector{Tuple{Index, Int}}()
	for q = 1: length(config)
		ind = findindex(state.site, "q=$q")
		push!(pairs, (ind, config[q]+1))
	end
	return state.site[pairs]
end


text:
using EEG
using Base.Test
using Logging
using MAT, BDF
using SIUnits, SIUnits.ShortUnits
using FileFind
using Plots
using FactCheck

Logging.configure(level=DEBUG)
Logging.configure(output=open("logfile.log", "a"))
unicodeplots()

#
# Run all tests
#

tests = AbstractString[]
function add_test(fname)
    global tests
    if endswith(fname, ".jl")
        if !contains(fname, "runtests")
            push!(tests, fname)
        end
    end
end
FileFind.find(".", add_test)

for t in tests
    include(t)
end
FactCheck.exitstatus()


text:
function boxplot(;y::AbstractVector = Int[], group::AbstractVector = Int[])

    #If x is missing, create ones array of same length
    if group == Int[]
      group = [" " for x in 1:length(y)]
    end

    v = VegaVisualization(height = 200, width = 600, name = "boxplot")

    #Set box size
    v.signals = [Dict{Any, Any}("name" => "boxSize", "init" => 40)]

    #Add data defaults
    add_data!(v, x = group , y = y)

    #Get unique table name
    table = v.data[1].name

    #Calculate moments
    stats = VegaData(name = "stats",
                     source = table,
                     transform = [VegaTransform(Dict{Any, Any}("type" => "aggregate", "groupby" => "x", "summarize" => Dict{Any, Any}("y" => ["min", "max", "median", "q1", "q3", "valid"])))]
                    )

    push!(v.data, stats)

    #Calculate whiskers
    whiskers = VegaData(name = "iqrcalcs",
                        source = "stats",
                        transform = [VegaTransform(Dict{Any, Any}("type" => "formula", "field" => "lower", "expr" => "max(datum.min_y,datum.q1_y-1.5*(datum.q3_y-datum.q1_y))")),
                                     VegaTransform(Dict{Any, Any}("type" => "formula", "field" => "upper", "expr" => "min(datum.max_y,datum.q1_y+1.5*(datum.q3_y-datum.q1_y))"))
                                    ]
                        )

    push!(v.data, whiskers)

    #Axes
    v.axes = Array{VegaAxis}(2)
    v.axes[1] = VegaAxis(_type = "x", scale = "x")
    v.axes[2] = VegaAxis(_type = "y", scale = "y", tickSize = 0, offset = 5, properties = Dict{Any, Any}("axis" => Dict{Any, Any}("stroke" => Dict{Any, Any}("value" => "transparent"))))

    #Scales
    v.scales = Array{VegaScale}(3)
    v.scales[2] = VegaScale(name = "y", _type = "ordinal", range = "height", points = true, round = true,
                            padding = 1.5, domain = VegaDataRef(data = table, field = "x"))

    v.scales[1] = VegaScale(name = "x", _type = "linear", domain = VegaDataRef(data = table, field = "y"),
                            range = "width", round = true, nice = true)

    v.scales[3] = VegaScale(name = "group", _type = "ordinal", domain = VegaDataRef(data = table, field = "x"),
                            range = "ordinal20", round = true, nice = true)

    #Marks
    v.marks = Array{VegaMark}(8)
    v.marks[2] = VegaMark(_type = "rect",
                          from = VegaMarkFrom(data = "iqrcalcs"),
                                             properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "lower"),
                                                                                x2 = VegaValueRef(scale = "x", field = "q1_y"),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                height = VegaValueRef(value = 1),
                                                                                fill = VegaValueRef(value = "#888")

                                                                                )
                                                         )
    )

    v.marks[8] = VegaMark(_type = "rect",
                          from = VegaMarkFrom(data = "iqrcalcs"),
                                             properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "q3_y"),
                                                                                x2 = VegaValueRef(scale = "x", field = "upper"),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                height = VegaValueRef(value = 1),
                                                                                fill = VegaValueRef(value = "#888")

                                                                                )
                                                         )
    )

    v.marks[1] = VegaMark(_type = "rect",
                          from = VegaMarkFrom(data = "iqrcalcs"),
                                              properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "q1_y"),
                                                                                x2 = VegaValueRef(scale = "x", field = "q3_y"),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                height = VegaValueRef(signal = "boxSize"),
                                                                                fill = VegaValueRef(scale = "group", field = "x"),
                                                                                stroke = VegaValueRef(value = "#888")

                                                                                )
                         )
    )

     v.marks[3] = VegaMark(_type = "rect",
                          from = VegaMarkFrom(data = "iqrcalcs"),
                          properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "median_y"),
                                                                                width = VegaValueRef(value = 2),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                height = VegaValueRef(signal = "boxSize"),
                                                                                fill = VegaValueRef(value = "#000")
                                                                                )
                         )
    )

     v.marks[4] = VegaMark(_type = "rect",
                          from = VegaMarkFrom(data = "iqrcalcs"),
                          properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "lower"),
                                                                                width = VegaValueRef(value = 1),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                height = VegaValueRef(signal = "boxSize", mult = 0.5),
                                                                                fill = VegaValueRef(value = "#000")
                                                                                )
                         )
    )

     v.marks[5] = VegaMark(_type = "rect",
                          from = VegaMarkFrom(data = "iqrcalcs"),
                          properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "upper"),
                                                                                width = VegaValueRef(value = 1),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                height = VegaValueRef(signal = "boxSize", mult = 0.5),
                                                                                fill = VegaValueRef(value = "#000")
                                                                                )
                         )
    )

    v.marks[6] = VegaMark(_type = "symbol",
                      from = VegaMarkFrom(data = "iqrcalcs"),
                          properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "min_y"),
                                                                                size = VegaValueRef(value = 40),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                stroke = VegaValueRef(value = "#000")

                                                                                )
                         )
    )

    v.marks[7] = VegaMark(_type = "symbol",
                      from = VegaMarkFrom(data = "iqrcalcs"),
                          properties = VegaMarkProperties(enter = VegaMarkPropertySet(x = VegaValueRef(scale = "x", field = "max_y"),
                                                                                size = VegaValueRef(value = 40),
                                                                                yc = VegaValueRef(scale = "y", field = "x"),
                                                                                stroke = VegaValueRef(value = "#000")

                                                                                )
                         )
    )

    #Default to Paired color scale, 12
    colorscheme!(v; palette = ("Paired", 12))
    return v

    return v

end


text:
# parameters
mass_bin_name = "1540_1560" # ARGS[1]#
tslice = "t1"
path_wavelist = "src"
path_to_working_folder = "/afs/cern.ch/user/m/mimikhas/work/private/pwa_data"#"data"

#########################################################
@show ARGS
push!(LOAD_PATH,"src")
print(typeof(mass_bin_name))
using SDMHelper
using PWAHelper

const BmatMC = let
    println("1. load data")
    @time PsiMC = read_precalc_basis(
        joinpath(path_to_working_folder,"functions_$(mass_bin_name)_$(tslice)_mc.bin"));

    println("2. calculate sums")
    Nd, Nwaves = size(PsiMC)
    @time v = [sum(PsiMC[e,i]'*PsiMC[e,j] for e in 1:Nd)
        for i=1:Nwaves, j=1:Nwaves] ./ Nd;
    v
end

println("3. save the result")
write_cmatrix(BmatMC,
    joinpath(path_to_working_folder,"integrmat_$(mass_bin_name)_$(tslice)_mc.txt"))


text:
"""
# Log Function

```julia
log(ts::TS, complex::Bool = false)
```

This method computes the log value of non-index columns in the TS
object.

# Examples
```jldoctest; setup = :(using TSx, DataFrames, Dates, Random, Statistics)
julia> using Random
julia> random(x) = rand(MersenneTwister(123), x...);
julia> ts = TS(random(([1, 2, 3, 4, missing], 10)))
julia> show(ts)
(10 x 1) TS with Int64 Index

 Index  x1
 Int64  Int64?
────────────────
     1  missing
     2        2
     3        2
     4        3
     5        4
     6        3
     7        3
     8  missing
     9        2
    10        3

julia> log(ts)
(10 x 1) TS with Int64 Index

 Index  x1_log
 Int64  Float64?
───────────────────────
     1  missing
     2        0.693147
     3        0.693147
     4        1.09861
     5        1.38629
     6        1.09861
     7        1.09861
     8  missing
     9        0.693147
    10        1.09861

```
"""
function Base.log(ts::TS)
    df = select(ts.coredata, :Index,
                Not(:Index) .=> (x -> log.(x))
                => colname -> string(colname, "_log"))
    TS(df)
end


text:
"""
	mask(cmd0::String="", arg1=nothing; kwargs...)

Reads (length,azimuth) pairs from file and plot a windmask diagram.

Full option list at [`psmask`]($(GMTdoc)mask.html)

Parameters
----------

- **I** | **inc** :: [Type => Number | Str]

    Set the grid spacing.
    ($(GMTdoc)mask.html#i)
- $(GMT.opt_R)

- $(GMT.opt_B)
- **C** | **end_clip_path** :: [Type => Bool]

    Mark end of existing clip path. No input file is needed.
    ($(GMTdoc)mask.html#c)
- **D** | **dump** :: [Type => Str]

    Dump the (x,y) coordinates of each clipping polygon to one or more output files
    (or stdout if template is not given).
    ($(GMTdoc)mask.html#d)
- **F** | **oriented_polygons** :: [Type => Str | []]

    Force clip contours (polygons) to be oriented so that data points are to the left (-Fl [Default]) or right (-Fr)
    ($(GMTdoc)mask.html#f)
- **G** | **fill** :: [Type => Number | Str]

    Set fill shade, color or pattern for positive and/or negative masks [Default is no fill].
    ($(GMTdoc)mask.html#g)
- $(GMT.opt_J)
- $(GMT.opt_Jz)
- **L** | **node_grid** :: [Type => Str]

    Save the internal grid with ones (data constraint) and zeros (no data) to the named nodegrid.
    ($(GMTdoc)mask.html#l)
- **N** | **invert** :: [Type => Bool]

    Invert the sense of the test, i.e., clip regions where there is data coverage.
    ($(GMTdoc)mask.html#n)
- $(GMT.opt_P)
- **Q** | **cut_number** :: [Type => Number | Str]

    Do not dump polygons with less than cut number of points [Dumps all polygons].
    ($(GMTdoc)mask.html#q)
- **S** | **search_radius** :: [Type => Number | Str]

    Sets radius of influence. Grid nodes within radius of a data point are considered reliable.
    ($(GMTdoc)mask.html#s)
- **T** | **tiles** :: [Type => Bool]

    Plot tiles instead of clip polygons. Use -G to set tile color or pattern. Cannot be used with -D.
    ($(GMTdoc)mask.html#t)
- $(GMT.opt_U)
- $(GMT.opt_V)
- $(GMT.opt_X)
- $(GMT.opt_Y)
- $(GMT.opt_bi)
- $(GMT.opt_di)
- $(GMT.opt_e)
- $(GMT.opt_h)
- $(GMT.opt_i)
- $(GMT.opt_p)
- $(GMT.opt_r)
- $(GMT.opt_t)
- $(GMT.opt_swap_xy)
"""
function mask(cmd0::String="", arg1=nothing; first=true, kwargs...)

    gmt_proggy = (IamModern[1]) ? "mask "  : "psmask "
	length(kwargs) == 0 && return monolitic(gmt_proggy, cmd0, arg1)
	d = KW(kwargs)
	help_show_options(d)		# Check if user wants ONLY the HELP mode
    K, O = set_KO(first)		# Set the K O dance

	cmd, opt_B, opt_J, opt_R = parse_BJR(d, "", "", O, " -JX12c/12c")
	cmd, = parse_common_opts(d, cmd, [:I :UVXY :JZ :c :e :p :r :t :params], first)
	cmd  = parse_these_opts(cmd, d, [[:C :end_clip_path], [:D :dump], [:F :oriented_polygons],
	                                 [:L :node_grid], [:N :invert], [:Q :cut_number], [:S :search_radius], [:T :tiles]])

	# If file name sent in, read it and compute a tight -R if this was not provided
	cmd, arg1, opt_R, = read_data(d, cmd0, cmd, arg1, opt_R)

	cmd = add_opt_fill(cmd, d, [:G :fill], 'G')

	return finish_PS_module(d, gmt_proggy * cmd, "", K, O, true, arg1)
end

# ---------------------------------------------------------------------------------------------------
mask!(cmd0::String="", arg1=nothing; kw...) = mask(cmd0, arg1; first=false, kw...)
mask(arg1,  cmd0::String=""; kw...)  = mask(cmd0, arg1; first=true, kw...)
mask!(arg1, cmd0::String=""; kw...) = mask(cmd0, arg1; first=false, kw...)

const psmask  = mask			# Alias
const psmask! = mask!			# Alias


text:
module EMLlead_lag
	using DanaTypes
	using DotPlusInheritance
	using Reexport
	@reexport using ...types.EMLtypes
	import EMLtypes.length
	include("lead_lag/Lead_lag.jl")
end


text:
origin(prim::HyperCube) = prim.origin
widths{N,T}(prim::HyperCube{N,T}) = Vec{N,T}(prim.width)
maximum{N,T}(prim::HyperCube{N,T}) = origin(prim)+widths(prim)
minimum{N,T}(prim::HyperCube{N,T}) = origin(prim)


text:
# build.jl
using ApplicationBuilder
build_app_bundle("main.jl", appname="ebit_ode_server");


text:
#=
Copyright (c) 2015, Intel Corporation
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
=#

module StringTest
using ParallelAccelerator

#ParallelAccelerator.DomainIR.set_debug_level(4)
#ParallelAccelerator.ParallelIR.set_debug_level(4)
#ParallelAccelerator.CGen.set_debug_level(4)
#ParallelAccelerator.set_debug_level(4)

@acc function f1()
    x = "hello"
    y = x[3]
    return UInt8(y)
end

@acc function f2()
    x = "hello"
    return length(x)
end

@acc function f3(x)
    println(x, " world!")
    return "OK"
end

@acc function f4(x)
    return x * "bar"
end

@acc function f5(x)
    string(x) * "bar"
end

function test1()
    f1() == 108 # ASCII 'l' is 108
end

function test2()
    f2() == 5
end

function test3()
    f3("Hello")  == "OK"
end

function test4()
    f4("foo")  == "foobar"
end

function test5()
    f5(1)  == "1bar"
end

end

using Base.Test

println("Testing strings...")
@test StringTest.test1()
@test StringTest.test2()
@test StringTest.test3()
@test StringTest.test4()
@test StringTest.test5()
println("Done testing strings...")