text:
# Autogenerated wrapper script for PulseAudio_jll for aarch64-linux-gnu
export libalsa_util, libbluez5_util, libcli, liboss_util, libprotocol_cli, libprotocol_http, libprotocol_native, libprotocol_simple, libpulse, libpulse_mainloop_glib, libpulse_simple, libpulsecommon, libpulsecore, libpulsedsp, libraop, librtp, module_allow_passthrough, module_alsa_card, module_alsa_sink, module_alsa_source, module_always_sink, module_always_source, module_augment_properties, module_bluetooth_discover, module_bluetooth_policy, module_bluez5_device, module_bluez5_discover, module_card_restore, module_cli, module_cli_protocol_tcp, module_cli_protocol_unix, module_combine, module_combine_sink, module_console_kit, module_dbus_protocol, module_default_device_restore, module_detect, module_device_manager, module_device_restore, module_echo_cancel, module_equalizer_sink, module_filter_apply, module_filter_heuristics, module_hal_detect, module_http_protocol_tcp, module_http_protocol_unix, module_intended_roles, module_ladspa_sink, module_loopback, module_match, module_mmkbd_evdev, module_native_protocol_fd, module_native_protocol_tcp, module_native_protocol_unix, module_null_sink, module_null_source, module_oss, module_pipe_sink, module_pipe_source, module_position_event_sounds, module_raop_sink, module_remap_sink, module_remap_source, module_rescue_streams, module_role_cork, module_role_ducking, module_rtp_recv, module_rtp_send, module_rygel_media_server, module_simple_protocol_tcp, module_simple_protocol_unix, module_sine, module_sine_source, module_stream_restore, module_suspend_on_idle, module_switch_on_connect, module_switch_on_port_available, module_tunnel_sink, module_tunnel_sink_new, module_tunnel_source, module_tunnel_source_new, module_udev_detect, module_virtual_sink, module_virtual_source, module_virtual_surround_sink, module_volume_restore, pacat, pacmd, pactl, pasuspender, pulseaudio

using alsa_jll
using BlueZ_jll
using Check_jll
using Dbus_jll
using eudev_jll
using FFTW_jll
using Gdbm_jll
using Glib_jll
using GStreamer_jll
using libsndfile_jll
using libasyncns_jll
using libcap_jll
using Libiconv_jll
using Libtool_jll
using OpenSSL_jll
using SBC_jll
using SoXResampler_jll
using SpeexDSP_jll
JLLWrappers.@generate_wrapper_header("PulseAudio")
JLLWrappers.@declare_library_product(libalsa_util, "libalsa-util.so")
JLLWrappers.@declare_library_product(libbluez5_util, "libbluez5-util.so")
JLLWrappers.@declare_library_product(libcli, "libcli.so")
JLLWrappers.@declare_library_product(liboss_util, "liboss-util.so")
JLLWrappers.@declare_library_product(libprotocol_cli, "libprotocol-cli.so")
JLLWrappers.@declare_library_product(libprotocol_http, "libprotocol-http.so")
JLLWrappers.@declare_library_product(libprotocol_native, "libprotocol-native.so")
JLLWrappers.@declare_library_product(libprotocol_simple, "libprotocol-simple.so")
JLLWrappers.@declare_library_product(libpulse, "libpulse.so.0")
JLLWrappers.@declare_library_product(libpulse_mainloop_glib, "libpulse-mainloop-glib.so.0")
JLLWrappers.@declare_library_product(libpulse_simple, "libpulse-simple.so.0")
JLLWrappers.@declare_library_product(libpulsecommon, "libpulsecommon-15.0.so")
JLLWrappers.@declare_library_product(libpulsecore, "libpulsecore-15.0.so")
JLLWrappers.@declare_library_product(libpulsedsp, "libpulsedsp.so")
JLLWrappers.@declare_library_product(libraop, "libraop.so")
JLLWrappers.@declare_library_product(librtp, "librtp.so")
JLLWrappers.@declare_library_product(module_allow_passthrough, "module-allow-passthrough.so")
JLLWrappers.@declare_library_product(module_alsa_card, "module-alsa-card.so")
JLLWrappers.@declare_library_product(module_alsa_sink, "module-alsa-sink.so")
JLLWrappers.@declare_library_product(module_alsa_source, "module-alsa-source.so")
JLLWrappers.@declare_library_product(module_always_sink, "module-always-sink.so")
JLLWrappers.@declare_library_product(module_always_source, "module-always-source.so")
JLLWrappers.@declare_library_product(module_augment_properties, "module-augment-properties.so")
JLLWrappers.@declare_library_product(module_bluetooth_discover, "module-bluetooth-discover.so")
JLLWrappers.@declare_library_product(module_bluetooth_policy, "module-bluetooth-policy.so")
JLLWrappers.@declare_library_product(module_bluez5_device, "module-bluez5-device.so")
JLLWrappers.@declare_library_product(module_bluez5_discover, "module-bluez5-discover.so")
JLLWrappers.@declare_library_product(module_card_restore, "module-card-restore.so")
JLLWrappers.@declare_library_product(module_cli, "module-cli.so")
JLLWrappers.@declare_library_product(module_cli_protocol_tcp, "module-cli-protocol-tcp.so")
JLLWrappers.@declare_library_product(module_cli_protocol_unix, "module-cli-protocol-unix.so")
JLLWrappers.@declare_library_product(module_combine, "module-combine.so")
JLLWrappers.@declare_library_product(module_combine_sink, "module-combine-sink.so")
JLLWrappers.@declare_library_product(module_console_kit, "module-console-kit.so")
JLLWrappers.@declare_library_product(module_dbus_protocol, "module-dbus-protocol.so")
JLLWrappers.@declare_library_product(module_default_device_restore, "module-default-device-restore.so")
JLLWrappers.@declare_library_product(module_detect, "module-detect.so")
JLLWrappers.@declare_library_product(module_device_manager, "module-device-manager.so")
JLLWrappers.@declare_library_product(module_device_restore, "module-device-restore.so")
JLLWrappers.@declare_library_product(module_echo_cancel, "module-echo-cancel.so")
JLLWrappers.@declare_library_product(module_equalizer_sink, "module-equalizer-sink.so")
JLLWrappers.@declare_library_product(module_filter_apply, "module-filter-apply.so")
JLLWrappers.@declare_library_product(module_filter_heuristics, "module-filter-heuristics.so")
JLLWrappers.@declare_library_product(module_hal_detect, "module-hal-detect.so")
JLLWrappers.@declare_library_product(module_http_protocol_tcp, "module-http-protocol-tcp.so")
JLLWrappers.@declare_library_product(module_http_protocol_unix, "module-http-protocol-unix.so")
JLLWrappers.@declare_library_product(module_intended_roles, "module-intended-roles.so")
JLLWrappers.@declare_library_product(module_ladspa_sink, "module-ladspa-sink.so")
JLLWrappers.@declare_library_product(module_loopback, "module-loopback.so")
JLLWrappers.@declare_library_product(module_match, "module-match.so")
JLLWrappers.@declare_library_product(module_mmkbd_evdev, "module-mmkbd-evdev.so")
JLLWrappers.@declare_library_product(module_native_protocol_fd, "module-native-protocol-fd.so")
JLLWrappers.@declare_library_product(module_native_protocol_tcp, "module-native-protocol-tcp.so")
JLLWrappers.@declare_library_product(module_native_protocol_unix, "module-native-protocol-unix.so")
JLLWrappers.@declare_library_product(module_null_sink, "module-null-sink.so")
JLLWrappers.@declare_library_product(module_null_source, "module-null-source.so")
JLLWrappers.@declare_library_product(module_oss, "module-oss.so")
JLLWrappers.@declare_library_product(module_pipe_sink, "module-pipe-sink.so")
JLLWrappers.@declare_library_product(module_pipe_source, "module-pipe-source.so")
JLLWrappers.@declare_library_product(module_position_event_sounds, "module-position-event-sounds.so")
JLLWrappers.@declare_library_product(module_raop_sink, "module-raop-sink.so")
JLLWrappers.@declare_library_product(module_remap_sink, "module-remap-sink.so")
JLLWrappers.@declare_library_product(module_remap_source, "module-remap-source.so")
JLLWrappers.@declare_library_product(module_rescue_streams, "module-rescue-streams.so")
JLLWrappers.@declare_library_product(module_role_cork, "module-role-cork.so")
JLLWrappers.@declare_library_product(module_role_ducking, "module-role-ducking.so")
JLLWrappers.@declare_library_product(module_rtp_recv, "module-rtp-recv.so")
JLLWrappers.@declare_library_product(module_rtp_send, "module-rtp-send.so")
JLLWrappers.@declare_library_product(module_rygel_media_server, "module-rygel-media-server.so")
JLLWrappers.@declare_library_product(module_simple_protocol_tcp, "module-simple-protocol-tcp.so")
JLLWrappers.@declare_library_product(module_simple_protocol_unix, "module-simple-protocol-unix.so")
JLLWrappers.@declare_library_product(module_sine, "module-sine.so")
JLLWrappers.@declare_library_product(module_sine_source, "module-sine-source.so")
JLLWrappers.@declare_library_product(module_stream_restore, "module-stream-restore.so")
JLLWrappers.@declare_library_product(module_suspend_on_idle, "module-suspend-on-idle.so")
JLLWrappers.@declare_library_product(module_switch_on_connect, "module-switch-on-connect.so")
JLLWrappers.@declare_library_product(module_switch_on_port_available, "module-switch-on-port-available.so")
JLLWrappers.@declare_library_product(module_tunnel_sink, "module-tunnel-sink.so")
JLLWrappers.@declare_library_product(module_tunnel_sink_new, "module-tunnel-sink-new.so")
JLLWrappers.@declare_library_product(module_tunnel_source, "module-tunnel-source.so")
JLLWrappers.@declare_library_product(module_tunnel_source_new, "module-tunnel-source-new.so")
JLLWrappers.@declare_library_product(module_udev_detect, "module-udev-detect.so")
JLLWrappers.@declare_library_product(module_virtual_sink, "module-virtual-sink.so")
JLLWrappers.@declare_library_product(module_virtual_source, "module-virtual-source.so")
JLLWrappers.@declare_library_product(module_virtual_surround_sink, "module-virtual-surround-sink.so")
JLLWrappers.@declare_library_product(module_volume_restore, "module-volume-restore.so")
JLLWrappers.@declare_executable_product(pacat)
JLLWrappers.@declare_executable_product(pacmd)
JLLWrappers.@declare_executable_product(pactl)
JLLWrappers.@declare_executable_product(pasuspender)
JLLWrappers.@declare_executable_product(pulseaudio)
function __init__()
    JLLWrappers.@generate_init_header(alsa_jll, BlueZ_jll, Check_jll, Dbus_jll, eudev_jll, FFTW_jll, Gdbm_jll, Glib_jll, GStreamer_jll, libsndfile_jll, libasyncns_jll, libcap_jll, Libiconv_jll, Libtool_jll, OpenSSL_jll, SBC_jll, SoXResampler_jll, SpeexDSP_jll)
    JLLWrappers.@init_library_product(
        libalsa_util,
        "lib/pulse-15.0/modules/libalsa-util.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libbluez5_util,
        "lib/pulse-15.0/modules/libbluez5-util.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libcli,
        "lib/pulse-15.0/modules/libcli.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        liboss_util,
        "lib/pulse-15.0/modules/liboss-util.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libprotocol_cli,
        "lib/pulse-15.0/modules/libprotocol-cli.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libprotocol_http,
        "lib/pulse-15.0/modules/libprotocol-http.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libprotocol_native,
        "lib/pulse-15.0/modules/libprotocol-native.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libprotocol_simple,
        "lib/pulse-15.0/modules/libprotocol-simple.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpulse,
        "lib/libpulse.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpulse_mainloop_glib,
        "lib/libpulse-mainloop-glib.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpulse_simple,
        "lib/libpulse-simple.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpulsecommon,
        "lib/pulseaudio/libpulsecommon-15.0.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpulsecore,
        "lib/pulseaudio/libpulsecore-15.0.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpulsedsp,
        "lib/pulseaudio/libpulsedsp.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libraop,
        "lib/pulse-15.0/modules/libraop.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        librtp,
        "lib/pulse-15.0/modules/librtp.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_allow_passthrough,
        "lib/pulse-15.0/modules/module-allow-passthrough.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_alsa_card,
        "lib/pulse-15.0/modules/module-alsa-card.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_alsa_sink,
        "lib/pulse-15.0/modules/module-alsa-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_alsa_source,
        "lib/pulse-15.0/modules/module-alsa-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_always_sink,
        "lib/pulse-15.0/modules/module-always-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_always_source,
        "lib/pulse-15.0/modules/module-always-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_augment_properties,
        "lib/pulse-15.0/modules/module-augment-properties.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_bluetooth_discover,
        "lib/pulse-15.0/modules/module-bluetooth-discover.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_bluetooth_policy,
        "lib/pulse-15.0/modules/module-bluetooth-policy.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_bluez5_device,
        "lib/pulse-15.0/modules/module-bluez5-device.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_bluez5_discover,
        "lib/pulse-15.0/modules/module-bluez5-discover.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_card_restore,
        "lib/pulse-15.0/modules/module-card-restore.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_cli,
        "lib/pulse-15.0/modules/module-cli.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_cli_protocol_tcp,
        "lib/pulse-15.0/modules/module-cli-protocol-tcp.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_cli_protocol_unix,
        "lib/pulse-15.0/modules/module-cli-protocol-unix.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_combine,
        "lib/pulse-15.0/modules/module-combine.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_combine_sink,
        "lib/pulse-15.0/modules/module-combine-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_console_kit,
        "lib/pulse-15.0/modules/module-console-kit.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_dbus_protocol,
        "lib/pulse-15.0/modules/module-dbus-protocol.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_default_device_restore,
        "lib/pulse-15.0/modules/module-default-device-restore.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_detect,
        "lib/pulse-15.0/modules/module-detect.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_device_manager,
        "lib/pulse-15.0/modules/module-device-manager.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_device_restore,
        "lib/pulse-15.0/modules/module-device-restore.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_echo_cancel,
        "lib/pulse-15.0/modules/module-echo-cancel.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_equalizer_sink,
        "lib/pulse-15.0/modules/module-equalizer-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_filter_apply,
        "lib/pulse-15.0/modules/module-filter-apply.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_filter_heuristics,
        "lib/pulse-15.0/modules/module-filter-heuristics.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_hal_detect,
        "lib/pulse-15.0/modules/module-hal-detect.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_http_protocol_tcp,
        "lib/pulse-15.0/modules/module-http-protocol-tcp.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_http_protocol_unix,
        "lib/pulse-15.0/modules/module-http-protocol-unix.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_intended_roles,
        "lib/pulse-15.0/modules/module-intended-roles.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_ladspa_sink,
        "lib/pulse-15.0/modules/module-ladspa-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_loopback,
        "lib/pulse-15.0/modules/module-loopback.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_match,
        "lib/pulse-15.0/modules/module-match.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_mmkbd_evdev,
        "lib/pulse-15.0/modules/module-mmkbd-evdev.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_native_protocol_fd,
        "lib/pulse-15.0/modules/module-native-protocol-fd.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_native_protocol_tcp,
        "lib/pulse-15.0/modules/module-native-protocol-tcp.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_native_protocol_unix,
        "lib/pulse-15.0/modules/module-native-protocol-unix.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_null_sink,
        "lib/pulse-15.0/modules/module-null-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_null_source,
        "lib/pulse-15.0/modules/module-null-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_oss,
        "lib/pulse-15.0/modules/module-oss.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_pipe_sink,
        "lib/pulse-15.0/modules/module-pipe-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_pipe_source,
        "lib/pulse-15.0/modules/module-pipe-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_position_event_sounds,
        "lib/pulse-15.0/modules/module-position-event-sounds.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_raop_sink,
        "lib/pulse-15.0/modules/module-raop-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_remap_sink,
        "lib/pulse-15.0/modules/module-remap-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_remap_source,
        "lib/pulse-15.0/modules/module-remap-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_rescue_streams,
        "lib/pulse-15.0/modules/module-rescue-streams.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_role_cork,
        "lib/pulse-15.0/modules/module-role-cork.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_role_ducking,
        "lib/pulse-15.0/modules/module-role-ducking.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_rtp_recv,
        "lib/pulse-15.0/modules/module-rtp-recv.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_rtp_send,
        "lib/pulse-15.0/modules/module-rtp-send.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_rygel_media_server,
        "lib/pulse-15.0/modules/module-rygel-media-server.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_simple_protocol_tcp,
        "lib/pulse-15.0/modules/module-simple-protocol-tcp.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_simple_protocol_unix,
        "lib/pulse-15.0/modules/module-simple-protocol-unix.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_sine,
        "lib/pulse-15.0/modules/module-sine.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_sine_source,
        "lib/pulse-15.0/modules/module-sine-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_stream_restore,
        "lib/pulse-15.0/modules/module-stream-restore.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_suspend_on_idle,
        "lib/pulse-15.0/modules/module-suspend-on-idle.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_switch_on_connect,
        "lib/pulse-15.0/modules/module-switch-on-connect.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_switch_on_port_available,
        "lib/pulse-15.0/modules/module-switch-on-port-available.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_tunnel_sink,
        "lib/pulse-15.0/modules/module-tunnel-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_tunnel_sink_new,
        "lib/pulse-15.0/modules/module-tunnel-sink-new.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_tunnel_source,
        "lib/pulse-15.0/modules/module-tunnel-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_tunnel_source_new,
        "lib/pulse-15.0/modules/module-tunnel-source-new.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_udev_detect,
        "lib/pulse-15.0/modules/module-udev-detect.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_virtual_sink,
        "lib/pulse-15.0/modules/module-virtual-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_virtual_source,
        "lib/pulse-15.0/modules/module-virtual-source.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_virtual_surround_sink,
        "lib/pulse-15.0/modules/module-virtual-surround-sink.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        module_volume_restore,
        "lib/pulse-15.0/modules/module-volume-restore.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_executable_product(
        pacat,
        "bin/pacat",
    )

    JLLWrappers.@init_executable_product(
        pacmd,
        "bin/pacmd",
    )

    JLLWrappers.@init_executable_product(
        pactl,
        "bin/pactl",
    )

    JLLWrappers.@init_executable_product(
        pasuspender,
        "bin/pasuspender",
    )

    JLLWrappers.@init_executable_product(
        pulseaudio,
        "bin/pulseaudio",
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
module StagedFilters

"""
Savitzky-Golay filter of window half-width M and degree
N. M is the number of points before and after to interpolate, i.e. the full
width of the window is 2M+1.
"""
abstract type AbstractStagedFilters end

struct SavitzkyGolayFilter{M,N} <: AbstractStagedFilters end
wrapL(i, n) = ifelse(1 ≤ i, i, i + n)
wrapR(i, n) = ifelse(i ≤ n, i, i - n)

"""
smooth!(filter,data, smoothed) -
apply `filter` to `data` writing result to `smoothed`.
Note that feeding `Int`s and not floats as data will result in a performance slowdown.
"""
@generated function smooth!(::Type{SavitzkyGolayFilter{M,N}}, data :: AbstractArray{T}, smoothed :: AbstractArray{S}) where {M,N,T,S}

  J = T[(i - M - 1 )^(j - 1) for i = 1:2M + 1, j = 1:N + 1]
  e₁ = [one(T); zeros(T,N)]
  C = J' \ e₁
  pre = :(for i = 1:$M end)
  main = :(for i = $(M + 1):n - $M end)
  post = :(for i = n - $(M - 1):n end)

  for loop in (pre, main, post)
      body = loop.args[2].args

      idx = loop !== pre ? :(i - $M) : :(wrapL(i - $M, n))   # Manually start the first iteration. See the "false" branch below.
      push!(body, :( x = muladd($(C[1]), data[$idx], $(zero(T))))) # Swap `muladd` instead of the additions. Note the index of 1.

      for j = reverse(1:M-1) # Because we bumped out the first iteration, we have to reduce the for loop index by one.
          idx = loop !== pre ? :(i - $j) : :(wrapL(i - $j, n))
          push!(body, :( x = muladd($(C[M + 1 - j]),data[$idx],x))) # muladd
      end

      push!(body, :( x = muladd($(C[M + 1]), data[i], x))) # muladd

      for j = 1:M
          idx = loop !== post ? :(i + $j) : :(wrapR(i + $j, n))
          push!(body, :( x = muladd($(C[M + 1 + j]), data[$idx], x))) # muladd
      end
      push!(body, :(smoothed[i] = x))
  end

 last_expr = quote
          n = length(data)
          n == length(smoothed) || throw(DimensionMismatch())
          @inbounds $pre; @inbounds  $main; @inbounds $post
          return smoothed
  end

  return last_expr = Base.remove_linenums!(last_expr)
end;

export SavitzkyGolayFilter, smooth!

end # module

ext: jl


text:
const EXT = ".lark"

const IMPORT_PATHS = [joinpath(@__DIR__,"grammars")]

const _RE_FLAGS = "imslux"

const _EMPTY = Terminal("__empty__")

# is_terminal(sym) = isupper(sym)

const _TERMINAL_NAMES = Dict(
    "." => "DOT",
    "," => "COMMA",
    ":" => "COLON",
    ";" => "SEMICOLON",
    "+" => "PLUS",
    "-" => "MINUS",
    "*" => "STAR",
    "/" => "SLASH",
    "\\" => "BACKSLASH",
    "|" => "VBAR",
    "?" => "QMARK",
    "!" => "BANG",
    "@" => "AT",
    "#" => "HASH",
    "\$" => "DOLLAR",
    "%" => "PERCENT",
    "^" => "CIRCUMFLEX",
    "&" => "AMPERSAND",
    "_" => "UNDERSCORE",
    "<" => "LESSTHAN",
    ">" => "MORETHAN",
    "=" => "EQUAL",
    "\"" => "DBLQUOTE",
    "'" => "QUOTE",
    "`" => "BACKQUOTE",
    "~" => "TILDE",
    "(" => "LPAR",
    ")" => "RPAR",
    "{" => "LBRACE",
    "}" => "RBRACE",
    "[" => "LSQB",
    "]" => "RSQB",
    "\n" => "NEWLINE",
    "\r\n" => "CRLF",
    "\t" => "TAB",
    " " => "SPACE",
)

# Grammar Parser
const TERMINALS = Dict(
    "_LPAR"=> raw"\(",
    "_RPAR"=> raw"\)",
    "_LBRA"=> raw"\[",
    "_RBRA"=> raw"\]",
    "_LBRACE"=> raw"{",
    "_RBRACE"=> raw"}",
    "OP"=> "[+*]|[?](?![a-z])",
    "_COLON"=> ":",
    "_COMMA"=> ",",
    "_OR"=> raw"\|",
    "_DOT"=> raw"\.(?!\.)",
    "_DOTDOT"=> raw"\.\.",
    "TILDE"=> "~",
    "RULE"=> "!?[_?]?[a-z][_a-z0-9]*",
    "TERMINAL"=> "_?[A-Z][_A-Z0-9]*",
    "STRING"=> raw""""(\\\\"|\\\\|[^"\n])*?"i?""",
    "REGEXP"=> raw"/(?!/)(\\/|\\\\|[^/])*?/[" * _RE_FLAGS * "]*",
    "_NL"=> raw"(\r?\n)+\s*",
    "WS"=> raw"[ \t]+",
    "COMMENT"=> raw"\s*//[^\n]*",
    "_TO"=> "->",
    "_IGNORE"=> raw"%ignore",
    "_DECLARE"=> raw"%declare",
    "_IMPORT"=> raw"%import",
    "NUMBER"=> raw"[+-]?\d+",
)

const RULES = Dict(
    "start"=> ["_list"],
    "_list"=>  ["_item", "_list _item"],
    "_item"=>  ["rule", "term", "statement", "_NL"],

    "rule"=> ["RULE template_params _COLON expansions _NL",
              "RULE template_params _DOT NUMBER _COLON expansions _NL"],
    "template_params"=> ["_LBRACE _template_params _RBRACE",
                     ""],
    "_template_params" => ["RULE",
                       "_template_params _COMMA RULE"],
    "expansions"=> ["alias",
                   "expansions _OR alias",
                   "expansions _NL _OR alias"],

    "?alias"=>     ["expansion _TO RULE", "expansion"],
    "expansion"=> ["_expansion"],

    "_expansion"=> ["", "_expansion expr"],

    "?expr"=> ["atom",
              "atom OP",
              "atom TILDE NUMBER",
              "atom TILDE NUMBER _DOTDOT NUMBER",
              ],

    "?atom"=> ["_LPAR expansions _RPAR",
              "maybe",
              "value"],

    "value"=> ["terminal",
              "nonterminal",
              "literal",
               "range",
               "template_usage"],

    "terminal"=> ["TERMINAL"],
    "nonterminal"=> ["RULE"],

    "?name"=> ["RULE", "TERMINAL"],

    "maybe"=> ["_LBRA expansions _RBRA"],
    "range"=> ["STRING _DOTDOT STRING"],
    "template_usage"=> ["RULE _LBRACE _template_args _RBRACE"],
    "_template_args"=> ["value",
                       "_template_args _COMMA value"],

    "term"=> ["TERMINAL _COLON expansions _NL",
              "TERMINAL _DOT NUMBER _COLON expansions _NL"],
    "statement"=> ["ignore", "import", "declare"],
    "ignore"=> ["_IGNORE expansions _NL"],
    "declare"=> ["_DECLARE _declare_args _NL"],
    "import"=> ["_IMPORT _import_path _NL",
               "_IMPORT _import_path _LPAR name_list _RPAR _NL",
               "_IMPORT _import_path _TO name _NL"],

    "_import_path"=> ["import_lib", "import_rel"],
    "import_lib"=> ["_import_args"],
    "import_rel"=> ["_DOT _import_args"],
    "_import_args"=> ["name", "_import_args _DOT name"],

    "name_list"=> ["_name_list"],
    "_name_list"=> ["name", "_name_list _COMMA name"],

    "_declare_args"=> ["name", "_declare_args name"],
    "literal"=> ["REGEXP", "STRING"],
)

mutable struct EBNF_to_BNF <: Transformer_InPlace
    new_rules::Array{Tuple{String,Tree,Any}}
    rules_by_expr::Dict{Union{String,LarkSymbol,Tree},NonTerminal}
    prefix::String
    i::Int
    rule_options::Union{Nothing,RuleOptions}
end

EBNF_to_BNF() = EBNF_to_BNF([],Dict(),"anon",0,nothing)

_add_recurse_rule(etb::EBNF_to_BNF,type_,expr) = begin
    if expr in collect(keys(etb.rules_by_expr))
        return etb.rules_by_expr[expr]
    end
    new_name = "__$(etb.prefix)_$(type_)_$(etb.i)"
    etb.i += 1
    t = NonTerminal(new_name)
    tree = Tree("expansions",[Tree("expansion",[expr]),Tree("expansion",[t,expr])])
    push!(etb.new_rules,(new_name,tree,etb.rule_options))
    etb.rules_by_expr[expr] = t
    return t
end

@inline_rule expr(etb::EBNF_to_BNF,rule,op,args...) = begin
    if op.value == "?"
        return Tree("expansions",[rule,Tree("expansion",[])])
    elseif op.value == "+"
        return _add_recurse_rule(etb,"plus",rule)
    elseif op.value == "*"
        new_name = _add_recurse_rule(etb,"star",rule)
        return Tree("expansions",[new_name, Tree("expansion",[])])
    elseif op.value == "~"
        if length(args) == 1
            mn = mx = Base.parse(Int64,args[1])
        else
            mn,mx = Base.parse.(Int,args)
            if mx < mn || mn < 0
                throw(GrammarError("Bad range for $rule ($mn..$mx isn't allowed)"))
            end
        end
        return Tree("expansions", [Tree("expansion",fill(rule,n)) for n in mn:mx])
    end
end

@inline_rule maybe(etb::EBNF_to_BNF,rule) = begin
    keep_all_tokens = etb.rule_options !== nothing && etb.rule_options.keep_all_tokens
    will_not_get_removed(sym) = begin
        if sym isa NonTerminal
            return !(sym.name[1] == '_')
        end
        if sym isa Terminal
            return keep_all_tokens || !sym.filter_out
        end
        @assert false
    end

    if any(scan_values(rule,will_not_get_removed))
        empty = _EMPTY
    else
        empty = Tree("expansion",[])
    end
    return Tree("expansions",[rule,empty])
end

struct SimplifyRule_Visitor <: Visitor end

# So what this does is to find children with the same data type as the parent,
# and bring them to the same level of the tree
_flatten!(tree::Tree) = begin
    while true
        to_expand = [i for (i,child) in enumerate(tree.children) if child isa Tree && child.data == tree.data]
        if length(to_expand) == 0 break end
        expand_kids_by_index!(tree,to_expand)
    end
end

# Changes the argument but we can't add exclamation marks or it will
# spoil the link between rule and function
@rule expansion(srv::SimplifyRule_Visitor,tree::Tree) = begin
    _flatten!(tree)
    for (i,child) in enumerate(tree.children)
        if child isa Tree && child.data == "expansions"
            tree.data = "expansions"
            new_children = []
            for option in unique(child.children)
                temp_tree = Tree("expansion", [if i==j
                                           option
                                           else
                                           other
                                           end
                                               for (j,other) in enumerate(tree.children)])
                visitation = visit(srv,temp_tree)
                new_children = push!(new_children,visitation)
            end
            tree.children = new_children
            _flatten!(tree)
            break
        end
    end
    #println("After expansion:\n $(pretty(tree))")
end

@rule alias(srv::SimplifyRule_Visitor,tree::Tree) = begin
    rule,alias_name = tree.children
    if rule.data == "expansions"
        aliases = []
        for child in tree.children[1].children
            push!(aliases,Tree("alias",[child,alias_name]))
        end
        tree.data = "expansions"
        tree.children = aliases
    end
end

@rule expansions(srv::SimplifyRule_Visitor,tree) = begin
    _flatten!(tree)
    unique!(tree.children)
end

struct RuleTreeToText <: Transformer end

@rule expansions(rtt::RuleTreeToText,x) = begin
    return x
end

@rule expansion(rtt::RuleTreeToText,symbols) = (symbols,nothing)

@rule alias(rtt::RuleTreeToText,x) = begin
    (expansion, _alias), alias = x
    @assert _alias == nothing
    return expansion, alias.value
end

struct CanonizeTree <: Transformer_InPlace end

@inline_rule maybe(ct::CanonizeTree,expr) = Tree("expr",[expr, Token("OP","?",pos_in_stream=-1)])

@inline_rule tokenmods(ct::CanonizeTree,args...) = begin
    if length(args) == 1
        return Array(args)
    end
    tokenmods, value = args   #What does this mean people?
    return push!(tokenmods,value)
end

"""
Create a unique list of anonymous terminals. Attempt to give
meaningful names to them when we add them
"""
mutable struct PrepareAnonTerminals <: Transformer_InPlace
    terminals
    term_set::Set
    term_reverse::Dict{Pattern,TerminalDef}
    i
    rule_options
end

PrepareAnonTerminals(terminals) = PrepareAnonTerminals(terminals,Set([td.name for td in terminals]),
                                                       Dict([td.pattern => td for td in terminals]),
                                                       0,nothing)

@inline_rule pattern(panon::PrepareAnonTerminals,p) = begin
    value = p.value
    if p in collect(keys(panon.term_reverse)) && p.flags != panon.term_reverse[p].pattern.flags
        throw(GrammarError("Conflicting flags for the same terminal: $p"))
    end
    term_name = nothing
    if p isa PatternStr
        try
            term_name = panon.term_reverse[p].name
        catch e
            if e isa KeyError
                try
                    term_name = _TERMINAL_NAMES[value]
                catch f
                    if f isa KeyError
                        # following test to make sure the group name in regular expression is allowed
                        if all(x->isletter(x)||isnumeric(x),value) && isletter(value[1]) && !(uppercase(value) in panon.term_set)
                            term_name = uppercase(value)
                        end
                    else
                        rethrow(f)
                    end
                end
                if term_name in panon.term_set
                    term_name = nothing
                end
            else   #not a key error
                rethrow(e)
            end
        end
    elseif p isa PatternRE
        if p in keys(panon.term_reverse)
            term_name = panon.term_reverse[p].name
        end
    else
        @assert false p
    end

    if term_name == nothing
        term_name = "__ANON_$(panon.i)"
        panon.i += 1
    end

    if !(term_name in panon.term_set)
        @assert !(p in keys(panon.term_reverse))
        push!(panon.term_set,term_name)
        termdef = TerminalDef(term_name,p)
        panon.term_reverse[p] = termdef
        push!(panon.terminals,termdef)
    end

    filter_out = panon.rule_options !== nothing && panon.rule_options.keep_all_tokens ? false : p isa PatternStr
    return Terminal(term_name,filter_out = filter_out)
end

mutable struct _ReplaceSymbols <: Transformer_InPlace
    names::Dict
end

_ReplaceSymbols() = _ReplaceSymbols(Dict())

@rule value(rs::_ReplaceSymbols,c) = begin
    if length(c) == 1 && c[1] isa Token && c[1].value in keys(rs.names)
        return rs.names[c[1].value]
    end
    return __default__(rs,"value",c,nothing)
end

@rule template_usage(rs::_ReplaceSymbols,c) = begin
    if c[1] in keys(rs.names)
        return __default__(rs,"template_usage", vcat([rs.names[c[1]].name], c[2:end]),nothing)
    end
    return __default__(rs,"template_usage",c,nothing)
end

"""Apply the templates, creating new rules that represent the used templates"""
mutable struct ApplyTemplates <: Transformer_InPlace
    rule_defs
    replacer::_ReplaceSymbols
    created_templates
end

ApplyTemplates(rule_defs) = ApplyTemplates(rule_defs,_ReplaceSymbols(),Set())

@rule template_usage(at::ApplyTemplates, c) = begin
    name = c[1]
    args = c[2:end]
    result_name = "$name{$(join(args,","))}"
    if !(result_name in at.created_templates)
        push!(at.created_templates,result_name)
        (_n, params, tree, options) = first(t for t in at.rule_defs if t[1] == name)
        @assert length(params) == length(args) args
        result_tree = deepcopy(tree)
        at.replacer.names = Dict(zip(params, args))
        transform(at.replacer,result_tree)
        push!(at.rule_defs,(result_name, [], result_tree, deepcopy(options)))
    end
    return NonTerminal(result_name)
end

_rfind(s,choices) = begin
    biggest = -1
    for c in choices
        p = findlast("$c",s)
        if !isnothing(p)
            biggest = max(biggest,p[1])
        end
    end
    return biggest
end

#==
Fixing escaping.

The python code iterates over each character of the supplied string,
appending it to the output string.

If that character is a backslash, the next character is also added. If
this second character is also a backslash, 2 backslashes are inserted
before it. If the second letter is anything other than 'unftr', only a
single further backslash is inserted.

So a double backslash on input becomes 4 on output. An unrecognised
backslash combination becomes double backslash - character. A
recognised combination remains as-is.

Following these shenanigans, any quotes are escaped and the string
is evaluated as a unicode string literal.

After calling this routine in _literal_to_pattern, double backslashes
are removed for strings, but remain for regular expressions.

Julia does not eval the string. However, we still need to
be able to make substitutions for common escapes \ + n/f/t/r.

The point of this routine is to interpret the escapes correctly,
after they have been read in (not when they are read in). So the

==#
_fix_escaping(s) = begin
    w = ""
    i = iterate(s)
    while i != nothing
        n,state = i
        w *= n
        if n == '\\'
            n2,state = iterate(s,state)
            if n2 == '\\'
                w *= "\\\\"
            elseif !(occursin(n2,"unftr\""))
                w *= "\\"
            end
            w *= n2
        end
        i = iterate(s,state)
    end
    new_s = unescape_string(w)
    return new_s
end

## TODO write this properly
_literal_to_pattern(literal) = begin
    v = literal.value
    flag_start = _rfind(v, "/\"") + 1
    @assert flag_start > 0
    flags = v[flag_start:end]
    @assert all(f -> occursin(f, _RE_FLAGS), flags)
    if literal.type_ == "STRING" && occursin('\n',v)
        throw(GrammarError("You cannot put newlines in string literals"))
    end
    if literal.type_ == "REGEXP" && occursin('\n',v) && !(occursin('x',flags))
        throw(GrammarError("You can only use newlines in regular expressions with the `x` (verbose) flag"))
    end
    v = v[1:flag_start-1]
    @assert v[1] == v[end] && occursin(v[1], "\"/")
    x = v[2:prevind(v,end,1)]   #drop delimiters
    ### TODO fix escaping
    s = _fix_escaping(x)
    if literal.type_ == "STRING"
        s = replace(s,"\\\\"=>"\\")
        return PatternStr(s,flags)
    elseif literal.type_ == "REGEXP"
        return PatternRE(s,flags)
    else
        @assert false, "Invariant failed: literal.type_ not in ['String','Regexp']"
    end
end

struct PrepareLiterals <: Transformer_InPlace end

@inline_rule literal(pl::PrepareLiterals, literal) = Tree("pattern",[_literal_to_pattern(literal)])

@inline_rule range(pl::PrepareLiterals,start,rend) = begin
    @assert start.type_ == rend.type_ == "STRING"
    start = start.value[2:end-1]
    rend = rend.value[2:end-1]
    @assert length(start) == length(rend) == 1
    regexp = "[$start-$rend]"    ###Make sure _repr_ and _str_ work here
    return Tree("pattern",[PatternRE(regexp)])
end

_make_joined_pattern(regexp,flags_set) = begin
    flags = ()
    if length(flags_set) > 1
        throw(GrammarError("Lerche doesn't support joining terminals with conflicting flags"))
    elseif length(flags_set) == 1
        flags = first(flags_set)
    end
    return PatternRE(regexp,flags)
end

struct TerminalTreeToPattern <: Transformer end

@rule pattern(tttp::TerminalTreeToPattern, ps) = begin
    p = ps[1]
    return p
end

@rule expansion(tttp::TerminalTreeToPattern, items) = begin
    if length(items) == 1
        return items[1]
    end
    pattern = "$(join((to_regexp(i) for i in items),""))"
    return _make_joined_pattern(pattern,Set([i.flags for i in items]))
end

@rule expansions(tttp::TerminalTreeToPattern, exps) = begin
    if length(exps) == 1
        return exps[1]
    end
    pattern = "(?:$(join((to_regexp(i) for i in exps),"|")))"
    return _make_joined_pattern(pattern, Set([i.flags for i in exps]))
end

@rule expr(tttp::TerminalTreeToPattern,args) = begin
    inner,op = args[1:2]
    if op == "~"
        if length(args) == 3
            op = "{$(Base.parse(Int64,args[3]))}"
        else
            mn,mx = Base.parse.(Int64,args[3:end])
            if mx < mn
                throw(GrammarError("Bad range for $inner ($mn..$mx isn't allowed)"))
            end
            op = "{$mn,$mx}"
        end
    else
        @assert length(args) == 2
        op = op.value
    end
    f = PatternRE("(?:$(to_regexp(inner)))$op",inner.flags)
    return f
end

@rule alias(tttp::TerminalTreeToPattern,t) = throw(GrammarError("Aliasing not allowed in terminals (You used -> in the wrong place)"))

@rule value(tttp::TerminalTreeToPattern,v) = v[1]

struct PrepareSymbols <: Transformer_InPlace end

@rule value(ps::PrepareSymbols,v) = begin
    v = v[1]
    if v isa Tree
        return v
    elseif v.type_ == "RULE"
        return NonTerminal(v.value)
    elseif v.type_ == "TERMINAL"
        return Terminal(v.value, filter_out = startswith(v.value,"_"))
    end
    @assert false
end

_choice_of_rules(rules) = Tree("expansions",[Tree("expansion",[Token("RULE",name)]) for name in rules])

nr_deepcopy_tree(t) = transform(Transformer_NonRecursive(),t)

# Note order of fields matches call order
mutable struct Grammar
    rule_defs::Array{Tuple{String,Array{Any,1},Tree,RuleOptions}}
    term_defs::Array{Tuple{String,Tuple{Tree,Int}}}
    ignore::Array{String}
end

compile(g::Grammar, start, terminals_to_keep) = begin
    # Deepcopy to allow multiple calling
    term_defs = deepcopy(g.term_defs)
    rule_defs = [(n,p,nr_deepcopy_tree(t),o) for (n,p,t,o) in g.rule_defs]

    # ===================
    #  Compile Terminals
    # ===================

    # Convert terminal-trees to strings/regexps
    for (name, (term_tree, priority)) in term_defs
        if term_tree == nothing  # Terminal added through %declare
            continue
        end
        expansions = collect(find_data(term_tree,"expansion"))
        if length(expansions) == 1 && length(expansions[1].children) == 0
            throw(GrammarError("Terminals cannot be empty ($name)"))
        end
    end
    transformer = PrepareLiterals() * TerminalTreeToPattern()
    terminals = [TerminalDef(name, transform(transformer,term_tree), priority)
                 for (name, (term_tree, priority)) in term_defs if term_tree!=[]]

    # =================
    #  Compile Rules
    # =================

    # 1. Pre-process terminals
    anon_tokens_transf = PrepareAnonTerminals(terminals)
    transformer = PrepareLiterals() * PrepareSymbols() * anon_tokens_transf   # Adds to terminals

    # 2. Inline Templates

    transformer *= ApplyTemplates(rule_defs)

    # 3. Convert EBNF to BNF (and apply step 1 and 2)
    ebnf_to_bnf = EBNF_to_BNF()
    rules = []
    i = 1
    while i <= length(rule_defs)  #instead of for loop as rule_defs might grow
        name, params, rule_tree, options = rule_defs[i]
        i +=1
        if length(params) != 0   #Don't transform templates
            continue
        end
        rule_options = options.keep_all_tokens ? RuleOptions(keep_all_tokens = true) : nothing
        ebnf_to_bnf.rule_options = rule_options  #drop check that options != nothing
        ebnf_to_bnf.prefix = name
        anon_tokens_transf.rule_options = rule_options
        tree = transform(transformer,rule_tree)
        push!(rules,(name, transform(ebnf_to_bnf,tree), options))
    end

    append!(rules, ebnf_to_bnf.new_rules)

    @assert length(rules) == length(Set(name for (name, _t, _o) in rules)) "Whoops, name collision"

    # 4. Compile tree to Rule objects
    rule_tree_to_text = RuleTreeToText()

    simplify_rule = SimplifyRule_Visitor()
    compiled_rules = Rule[]
    for (name, tree, options) in rules
        visit(simplify_rule,tree)
        #println("Tree after simplification:\n$tree")
        expansions = transform(rule_tree_to_text,tree)
        #println("Tree after expansions:\n$tree, returned $expansions")
        for (i,(expansion, alias)) in enumerate(expansions)
            if alias != nothing && first(name) =='_'
                throw(GrammarError("Rule $name is marked for expansion (it starts with an underscore) and isn't allowed to have aliases (alias=$alias)"))
            end
            empty_indices = [x==_EMPTY for x in expansion]
            if any(empty_indices)
                exp_options = options !== nothing ? copy(options) : RuleOptions()
                exp_options.empty_indices = empty_indices
                expansion = [x for x in expansion if x!=_EMPTY]
            else
                exp_options = options
            end

            @assert all(x-> x isa LarkSymbol,expansion) expansion

            rule = Rule(NonTerminal(name), expansion, order=i,alias=alias, options=exp_options)
            push!(compiled_rules,rule)
        end
    end

    if length(Set(compiled_rules)) != length(compiled_rules)
        duplicates = classify(compiled_rules, x->x)
        if dups in values(duplicates)
            if length(dups) > 1
                if dups[1].expansion != []
                    throw(error("Rules defined twice: $dups\n\n(Might happen due to colliding expansion of optionals: [] or ?)"))
                end
                @assert length(unique(((r.alias,r.order,r.options) for r in dups))) == length(dups)
            end
        end
        compiled_rules = collect(Set(compiled_rules))

    end

    # Filter out unused rules
    while true
        c = length(compiled_rules)
        used_rules = Set([s for r in compiled_rules
                            for s in r.expansion
                            if s isa NonTerminal
                            && s != r.origin])
        union!(used_rules, Set([NonTerminal(s) for s in start]))
        compiled_rules, unused = classify_bool(compiled_rules, r -> r.origin in used_rules)
        #println("$(typeof(compiled_rules))")
        for r in unused
            @debug "Unused rule: $r"
        end
        if length(compiled_rules) == c
            break
        end
    end

    # Filter out unused terminals
    used_terms = Set([t.name for r in compiled_rules
                      for t in r.expansion
                      if t isa Terminal])
    terminals, unused = classify_bool(terminals, t -> t.name in used_terms || t.name in g.ignore || t.name in terminals_to_keep)
    if length(unused) > 0
        @debug "Unused terminals: $([t.name for t in unused])"
    end

    return terminals, compiled_rules, g.ignore
end

const _imported_grammars = Dict()

import_from_grammar_into_namespace(grammar,namespace,aliases) = begin
    imported_terms = Dict(grammar.term_defs)
    imported_rules = Dict(n => (n,p,deepcopy(t),o) for (n,p,t,o) in grammar.rule_defs)
    term_defs = []
    rule_defs = []

    rule_dependencies(symbol) = begin
        if symbol.type_ != "RULE"
            return []
        end
        params = nothing
        tree = nothing
        try
            _, params, tree, _ = imported_rules[symbol]
        catch e
            if e isa KeyError
                throw(GrammarError("Missing symbol '$symbol' in grammar $namespace"))
            else
                rethrow(e)
            end
        end
        return setdiff(_find_used_symbols(tree),Set(params))
    end

    get_namespace_name(name,params) = begin
        if params !== nothing
            try
                return params[name]
            catch e
                if !(e isa KeyError)
                    rethrow(e)
                end
            end
        end
        try
            return aliases[name].value
        catch e
            if e isa KeyError
                if name[1] == '_'
                    return "_$(namespace)__$(name[2:end])"
                end
                return "$(namespace)__$name"
            end
            rethrow(e)
        end
    end

    to_import = collect(bfs(keys(aliases), rule_dependencies))
    for symbol in to_import
        if symbol.type_ == "TERMINAL"
            push!(term_defs,(get_namespace_name(symbol, nothing), imported_terms[symbol]))
        else
            @assert symbol.type_ == "RULE"
            _, params, tree, options = imported_rules[symbol]
            params_map = Dict([p => (p[1]!='_' ? "$(namespace)__$p" : "_$(namespace)__$p") for p in params])
            for t in tree
                for (i, c) in enumerate(t.children)
                    if c isa Token && c.type_ in ("RULE", "TERMINAL")
                        t.children[i] = Token(c.type_, get_namespace_name(c, params_map))
                    end
                end
            end
            params = [params_map[p] for p in params]
            push!(rule_defs,(get_namespace_name(symbol,params_map), params, tree, options))
        end
    end
    return term_defs, rule_defs
end

resolve_term_references(term_defs) = begin
    term_dict = Dict(k=>t for (k,(t,_p)) in term_defs)
    @assert length(term_dict) == length(term_defs) "Same name defined twice?"
    while true
        changed = false
        for (name, (token_tree, _p)) in term_defs
            if token_tree == nothing  # Terminal added through %declare
                continue
            end

            for exp in find_data(token_tree,"value")
                item ,= exp.children
                if item isa Token
                    if item.type_ == "RULE"
                        throw(GrammarError("Rules aren't allowed inside terminals ($item in $name)"))
                    end
                    if item.type_ == "TERMINAL"
                        term_value = term_dict[item]
                        @assert term_value != nothing
                        exp.children[1] = term_value
                        changed = true
                    end
                end
            end
        end

        if !changed
            break
        end
    end
    for (name, term) in term_dict
        if term != nothing    # Not just declared
            for child in term.children
                # Python version uses `id` here; we can't
                if term in child   #iterate for trees is iter_subtree
                    throw(GrammarError("Recursion in terminal '$name' (recursion is only allowed in rules, not terminals)"))
                end
            end
        end
    end
end

options_from_rule(name, params, x...) = begin
    if length(x) > 1
        priority, expansions = x
        priority = parse(Int,priority)
    else
        expansions = x[1]
        priority = nothing
    end
    params = params !== nothing ? [t.value for t in params.children] : []

    keep_all_tokens = startswith(name,"!")
    name = lstrip(name,'!')
    expand1 = startswith(name,"?")
    name = lstrip(name,'?')

    return name, params, expansions, RuleOptions(keep_all_tokens=keep_all_tokens, expand1=expand1, priority=priority, template_source=(params !== nothing ? name : nothing))
end

symbols_from_strcase(x) = begin
    if isupper(x)
        return Terminal(x, filter_out=first(x)=='_')
    else
        return NonTerminal(x)
    end
end

# emulate Python isupper, which returns true if all cased characters are uppercase.
# It returns false if there are no letters at all
# We interpret this as all letters are uppercase.  We differ from Python in that
# we allow strings with all non-letters to return "true".

isupper(s) = begin
    return all(y -> !isletter(y) || isuppercase(y),s)
end

struct PrepareGrammar <:Transformer_InPlace end

@inline_rule terminal(pg::PrepareGrammar, name) = name

@inline_rule nonterminal(pg::PrepareGrammar, name) = name

_find_used_symbols(tree) = begin
    @assert tree.data == "expansions"
    return Set([t for x in find_data(tree,"expansion") for t in scan_values(x,t -> t.type_ in ("RULE","TERMINAL"))])
end

const ERRORS = [
    ("Unclosed parenthesis", ["a: (\n"]),
    ("Unmatched closing parenthesis", ["a: )\n", "a: [)\n", "a: (]\n"]),
    ("Expecting rule or terminal definition (missing colon)", ["a\n", "A\n", "a->\n", "A->\n", "a A\n"]),
    ("Illegal name for rules or terminals", ["Aa:\n"]),
    ("Alias expects lowercase name", ["a: -> \"a\"\n"]),
    ("Unexpected colon", ["a::\n", "a: b:\n", "a: B:\n", "a: \"a\":\n"]),
    ("Misplaced operator", ["a: b??", "a: b(?)", "a:+\n", "a:?\n", "a:*\n", "a:|*\n"]),
    ("Expecting option ('|') or a new rule or terminal definition", ["a:a\n()\n"]),
    ("Terminal names cannot contain dots", ["A.B\n"]),
    ("%import expects a name", ["%import \"a\"\n"]),
    ("%ignore expects a value", ["%ignore %import\n"]),
]

"""
The GrammarLoader class is responsible for loading in the parser for the Lark EBNF itself.
"""
struct GrammarLoader
    parser
    global_keep_all_tokens
end

GrammarLoader(global_keep_all_tokens) = begin
    terminals = [TerminalDef(name, PatternRE(value)) for (name, value) in TERMINALS]
    rules = [options_from_rule(name, nothing, x) for (name, x) in  RULES]
    rules = [Rule(NonTerminal(r), symbols_from_strcase.(split(x)),order=i, alias=nothing, options=o) for (r, _p, xs, o) in rules for (i,x) in enumerate(xs)]
    #== debugging
    for r in rules
        println("Rules: $r")
        println("===")
    end
    ==#
    callback = create_callback(ParseTreeBuilder(rules))
    lexer_conf = LexerConf(terminals, ignore=["WS", "COMMENT"])
    parser_conf = ParserConf(rules, callback, ["start"])
    GrammarLoader(LALR_TraditionalLexer(lexer_conf, parser_conf),global_keep_all_tokens)
end

# Lark cycles through all paths, suppressing IOErrors, and if none are
# successful it deliberately opens a file to create a new IOError.
import_grammar(gl::GrammarLoader,grammar_path;base_path=nothing, import_paths=[]) = begin
    if !(grammar_path in keys(_imported_grammars))
        # import_paths take priority over base_path since they should handle relative imports and ignore everything else.
        to_try = vcat(import_paths, base_path !== nothing ? [base_path] : IMPORT_PATHS)
        text = nothing
        joined_path = nothing  #scope
        for source in to_try
            try
                joined_path = joinpath(source,grammar_path)
                text = read(joined_path,String)
            catch e
                if !(e isa SystemError)
                    rethrow(e)
                end
                continue
            end
            if isnothing(text) continue end
            grammar = load_grammar(gl, text, grammar_name=joined_path, import_paths=import_paths)
            _imported_grammars[grammar_path] = grammar
            break
        end
        if text === nothing   #failed, raise not found error
            Base.open(grammar_path,"r")
            @assert false
        end
    end
    return _imported_grammars[grammar_path]
end

"Parse grammar_text, verify, and create Grammar object. Display nice messages on error."
load_grammar(gl::GrammarLoader, grammar_text; grammar_name="<?>", import_paths=[]) = begin
    try
        tree = transform(CanonizeTree(),parse(gl.parser,grammar_text*"\n") )
    catch e
        if e isa UnexpectedCharacters
            context = get_context(e,grammar_text)
            throw(GrammarError("Unexpected input at line $(e.line) column $e.column in $(grammar_name): \n\n$context"))

        elseif e isa UnexpectedToken
            context = get_context(e,grammar_text)
            error = match_examples(e,partial(parse,gl.parser), ERRORS, use_accepts=true)
            if error != nothing
                throw(GrammarError("$error at line $(e.line) column $(e.column)\n\n$(context)"))

            elseif occursin("STRING",e.expected)
                throw(GrammarError("Expecting a value at line $(e.line) column $(e.column)\n\n$context"))
            end
        end
        rethrow(e)
    end

    tree = transform(PrepareGrammar(),tree)

    # Extract grammar items
    defs = classify(tree.children, key=c -> c.data, value=c -> c.children)
    term_defs = pop!(defs,"term", [])
    rule_defs = pop!(defs,"rule", [])
    statements = pop!(defs,"statement", [])
    @assert length(defs) == 0

    term_defs = [if length(td)==3 td else [td[1], 1, td[2]] end for td in term_defs]
    term_defs = [(name.value, (t, if !(typeof(p) <: Int) Base.parse(Int64,p.value) else p end)) for (name, p, t) in term_defs]
    rule_defs = Any[options_from_rule(x[1],x[2:end]...) for x in rule_defs]

    #println("Check: term_defs $term_defs\nrule_defs $rule_defs")

    # Execute statements
    ignore, imports = [] , Dict()
    for a_stmt in statements
        @assert length(a_stmt) == 1
        stmt = a_stmt[1]
        if stmt.data == "ignore"
            t = stmt.children[1]
            push!(ignore,t)
        elseif stmt.data == "import"
            if length(stmt.children) > 1
                path_node, arg1 = stmt.children
            else
                path_node = stmt.children[1]
                arg1 = nothing
            end
            if arg1 isa Tree  # Multi import
                dotted_path = tuple(path_node.children)
                names = arg1.children
                aliases = Dict(zip(names,names))  # Can't have aliased multi import, so all aliases will be the same as names
            else  # Single import
                dotted_path = tuple(path_node.children[1:end-1])
                name = path_node.children[end]  # Get name from dotted path
                aliases = Dict(name=> arg1 !== nothing ? arg1 : name )
            end # Aliases if exist

            if path_node.data == "import_lib"  # Import from library
                base_path = nothing
            else  # Relative import
                if grammar_name == "<string>"  # Import relative to script file path if grammar is coded in script
                    base_file = abspath(PROGRAM_FILE)
                else
                    base_file = grammar_name  # Import relative to grammar file path if external grammar file
                end
                if base_file !== nothing
                    base_path = first(splitdir(base_file))
                else
                    base_path = pwd()
                end
            end

            try
                import_base_path, import_aliases = imports[dotted_path]
                @assert base_path == import_base_path "Inconsistent base path: $base_path != $import_base_path"
                merge!(import_aliases,aliases)
            catch e
                if e isa KeyError
                    imports[dotted_path] = (base_path, aliases)
                else
                    rethrow(e)
                end
            end
        elseif stmt.data == "declare"
            for t in stmt.children
                push!(term_defs,[t.value, (nothing,nothing)])
            end
        else
            @assert false stmt
        end
    end

    # import grammars
    for (dotted_path, ba) in imports
        base_path, aliases = ba
        grammar_path = joinpath([x.value for x in dotted_path...]...) * EXT
        g = import_grammar(gl, grammar_path, base_path=base_path, import_paths=import_paths)
        new_td, new_rd = import_from_grammar_into_namespace(g, join([x.value for x in dotted_path...],"__"), aliases)

        append!(term_defs,new_td)
        append!(rule_defs,new_rd)
    end

    # Verify correctness 1
    for (name, _) in term_defs
        if startswith(name,"__")
            throw(GrammarError("Names starting with double-underscore are reserved (Error at $name)"))
        end
    end

    # Handle ignore tokens
    # XXX A slightly hacky solution. Recognition of %ignore TERMINAL as separate comes from the lexer's
    #     inability to handle duplicate terminals (two names, one value)
    ignore_names = []
    for t in ignore
        if t.data=="expansions" && length(t.children) == 1
            t2 = t.children[1]
            if t2.data=="expansion" && length(t2.children) == 1
                item = t2.children[1]
                if item.data == "value"
                    item = item.children[1]
                    if item isa Token && item.type_ == "TERMINAL"
                        push!(ignore_names,item.value)
                        continue
                    end
                end
            end
        end

        name = "__IGNORE_$(length(ignore_names))"
        push!(ignore_names,name)
        push!(term_defs,(name, (t, 0)))
    end
    # Verify correctness 2
    terminal_names = Set()
    for (name, _) in term_defs
        if name in terminal_names
            throw(GrammarError("Terminal '$name' defined more than once"))
        end

        push!(terminal_names,name)
    end

    if Set(ignore_names) > terminal_names
        throw(GrammarError("Terminals $(setdiff(ignore_names,terminal_names)) were marked to ignore but were not defined!"))
    end

    resolve_term_references(term_defs)
    rules = rule_defs
    rule_names = Dict()
    # Keep RuleOptions immutable by creating a whole new instance
    if gl.global_keep_all_tokens
        rules = [(a,b,c,RuleOptions(d,true)) for (a,b,c,d) in rules]
    end
    for (name, params, _x, option) in rules
        if gl.global_keep_all_tokens
            option = RuleOptions(option,true)
        end
        if startswith(name, "__")
            throw(GrammarError("Names starting with double-underscore are reserved (Error at $name)"))
        end

        if name in keys(rule_names)
            throw(GrammarError("Rule '$name' defined more than once"))
        end
        rule_names[name] = length(params)
    end

    #println("Rule names: $rule_names")

    for (name, params, expansions, _o) in rules
        for (i,p) in enumerate(params)
            if p in keys(rule_names)
                throw(GrammarError("Template Parameter conflicts with rule $p (in template $name)"))
            end
            if p in params[1:i-1]
                throw(GrammarError("Duplicate Template Parameter $p (in template $name)"))
            end
        end
        for temp in find_data(expansions,"template_usage")
            sym = temp.children[1]
            args = temp.children[2:end]
            if !(sym in params)
                if !(sym in keys(rule_names))
                    throw(GrammarError("Template '$sym' used but not defined (in rule $name)"))
                end
                if length(args) != rule_names[sym]
                    throw(GrammarError("Wrong number of template arguments used for $sym (expected $(rule_names[sym]), got $(length(args))) (in rule $name)"))
                end
            end
        end

        for sym in _find_used_symbols(expansions)
            if sym.type_ == "TERMINAL"
                if !(sym in terminal_names)
                    throw(GrammarError("Token '$sym' used but not defined (in rule $name)"))
                end
            else
                if !(sym in keys(rule_names)) && !(sym in params)
                    throw(GrammarError("Rule '$sym' used but not defined (in rule $name)"))
                end
            end
        end
    end
    return Grammar(rules, term_defs, ignore_names)

end

# Load grammar from a string
load_grammar(grammar,source,import_paths,global_keep_all_tokens) = begin
    if global_keep_all_tokens
        load_grammar(_lark_grammar_t,grammar,grammar_name=source,import_paths=import_paths)
    else
        load_grammar(_lark_grammar_f,grammar,grammar_name=source,import_paths=import_paths)
    end
end


text:
"""
`abstract ImageTransformation`

Description
============

Abstract supertype for all image transformation. Every subtype of
`ImageTransformation` must implement the `transform` and the `multiplier`
methods.

Methods
========

- **`transform`** : Applies the transformation to the given Image
or set of images. This effectifely specifies the exact effect the
transformation has on the input it receives

- **`multiplier`** : Specifies how many unique output variations
the transformation can result in (counting the case of not being applied
to the input).

Author(s)
==========

- Christof Stocker (Github: https://github.com/Evizero)

see also
=========

`transform`, `Either`
"""
abstract ImageTransformation

multiplier(::ImageTransformation) = throw(ArgumentError("Every ImageTransformation needs to specify a multiplication factor"))

# TODO: make this cleaner for dispatch
@inline function transform(op::ImageTransformation, imgs) #::Tuple)
    transform(op, imgs[1]), transform(op, imgs[2])
end

function _log_operation!(img::Image, op::ImageTransformation)
    history = get!(properties(img), "transformations", Array{ImageTransformation,1}())
    push!(history, op)
    img
end

# ==========================================================

"""
`NoOp <: ImageTransformation`

Identity transformation that does not do anything with the given image
but instead passes it through unchanged and uncopied.

Mainly used is in combination with `Either`
"""
immutable NoOp <: ImageTransformation
end

Base.showcompact(io::IO, op::NoOp) = print(io, "No transformation.")
multiplier(po::NoOp) = 1
transform{T}(po::NoOp, img::T) = img

# ==========================================================

"""
`Either <: ImageTransformation`

Description
============

    Allows for choosing between different `ImageTransformation` at random.
    This is particularly useful if one for example wants to first
    either rotate the image 90 degree clockwise or anticlockwise
    (but never both) and then apply some other transformation(s)
    afterwards.

    By default each specified image transformation has the same probability
    of occurance. This default behaviour can be overwritten by
    specifying the parameter `chance` manually

Usage
======

    Either(transformations; chance = ones(length(transformations))/length(transformations))

    Either(transformations...; chance = ones(length(transformations))/length(transformations))

Arguments
==========

- **`transformations`** : Vector of `ImageTransformation`. Defines all
distinct image transformations to choose from.

- **`chance`** : Vector of `Float64`. Probabilities or ratio for each
individual image transformations in `transformations` to occur.
Has to be the same length as `transformations`.

Methods
========

- **`transform`** : When called, applies one of the image transformations
in `transformations` at random according to the specified probabilities
in `chance`.

- **`multiplier`** : Specifies how many unique output variations
the transformation can result in. In this case this is the sum of the
multiplier for each image transformation specified in `transformations`

Author(s)
==========

- Christof Stocker (Github: https://github.com/Evizero)

Examples
========

    using Augmentor
    using TestImages

    # load an example image
    img = testimage("lena")

    # Specify transformation to either apply FlipX, FlipY, or do nothing
    # Each transformation as equal probability to occur
    op = Either(FlipX(), FlipY(), NoOp())

    # Apply random transformation
    img_new = transform(op, img)

    # Same as before but with a 80% chance to do nothing
    op = Either(FlipX(), FlipY(), NoOp(); chance = [.1, .1, .8])
    img_new = transform(op, img)

see also
=========

`transform`, `ImageTransformation`, `NoOp`
"""
immutable Either <: ImageTransformation
    transformations::Vector{ImageTransformation}
    chance::Vector{Float64}
    cum_chance::Vector{Float64}

    function Either(transformations::Vector{ImageTransformation}; chance::Vector{Float64} = ones(length(transformations))/length(transformations))
        length(transformations) > 0 || throw(ArgumentError("number of specified image transformations need to be greater than 0"))
        length(transformations) == length(chance) || throw(ArgumentError("length of chance has to be equal the number of specified image transformations"))
        sum_chance = sum(chance)
        @assert sum_chance > 0.
        chance = chance ./ sum_chance
        cum_chance = cumsum(chance)
        new(transformations, chance, cum_chance)
    end
end

Either(args::ImageTransformation...; nargs...) = Either(collect(ImageTransformation, args); nargs...)

function ProbableOperation{T<:ImageTransformation}(op::T; chance::Real = .5)
    0 <= chance <= 1. || throw(ArgumentError("chance has to be between 0 and 1"))
    p1 = Float64(chance)
    p2 = 1 - p1
    Either(op, NoOp(), chance = [p1, p2])
end

function Base.show(io::IO, op::Either)
    print(io, "Either (1 out of ", length(op.transformations), " transformation(s))")
    for (op_i, p_i) in zip(op.transformations, op.chance)
        println(io)
        print(io, "  - ", round(p_i*100, 1), "% chance to: ")
        Base.showcompact(io, op_i)
    end
end

function Base.showcompact(io::IO, op::Either)
    print(io, "Either:")
    for (op_i, p_i) in zip(op.transformations, op.chance)
        print(io, " (", round(Int, p_i*100), "%) ")
        Base.showcompact(io, op_i)
    end
end

function multiplier(op::Either)
    m = 0
    for (op_i, p_i) in zip(op.transformations, op.chance)
        if p_i > 0
            m += multiplier(op_i)
        end
    end
    m
end

function transform(op::Either, img)
    p = rand()
    for (i, p_i) in enumerate(op.cum_chance)
        if p <= p_i
            return transform(op.transformations[i], img)
        end
    end
    img
end


text:
using XlsxWriter
using Dates

function test()

	# Create an new Excel file and add a worksheet.
	wb = Workbook("demo.xlsx")
	set_calc_mode!(wb, manual)
	ws = add_worksheet!(wb)

	# Widen the first column to make the text clearer.
	set_column!(ws, "A:A", 20)
	set_column!(ws, 3, 3, 21)
	set_column!(ws, "E:E", 22)

	# Add a bold format to use to highlight cells.
	bold = add_format!(wb, Dict("bold"=>true))
	set_font_name!(bold, "Courier New")
	set_font_size!(bold, 16)

	date_format = add_format!(wb, Dict("num_format"=>"d mmmm yyyy"))
	set_font_color!(date_format, "red")
	# Write some simple text.
	write!(ws, "A1", "Hello")

	# Text with formatting.
	write!(ws, 1,1, "World", fmt=bold)

	# Write some numbers, with row/column notation.
	write!(ws, 2, 2, 123)
	write!(ws, 3, 1, 123.456)
	write!(ws, 3, 2, true)
	write!(ws, 3, 3, now(), fmt=date_format)
	write!(ws, 3, 4, Url("http://localhost"))
	write!(ws, 3, 5, "=3 + 4")

	write_formula!(ws, 3, 6, "=13 + 14", fmt=bold, result=9)

	define_name!(wb, "duck", "=Sheet1!\$C\$3")
	write_formula!(ws, 3, 7, "=duck*2", result=8)

	write_row!(ws, 3, 7, ["6", 7, 8.8])
	write_column!(ws, 4, 7, ["46", 47, 48.8])
	write_matrix!(ws, 7, 1, [["105" "106" "107"]; ["201" 202 203]], fmt=bold)

	write_row!(ws, 10, 1, [-2, 2, 3, -1])
	add_sparkline!(ws, 10, 5, Dict("range"=>"Sheet1!B11:E11"))

	#freeze_panes!(ws, 1, 1)

	merge_range!(ws, 5, 9, 5, 12, "Merged")

	close(wb)

	isfile("demo.xlsx")
end

test()


text:
using GNSSTools
using MatrixEquations
using LinearAlgebra
using PyPlot
pygui(true)
include("kalman_filter_gain_error_functions.jl")

h_parms = [1.9e-6, 2e-6, 9.7e-8, 9.1e-11, 9.9e-15] ./ 16.3676e6^2  # TCXO
# h_parms = [2.2e-9, 1.6e-10, 1.6e-10, 7.1e-13, 7.2e-16] ./ 10e6^2  # OCXO
h₀ = h_parms[3]
h₋₁ = h_parms[2]
h₋₂ = h_parms[1]

T = 1e-3
qₐ = 1
f_L = L1_freq
state_num = 3
CN0 = 45
B = 15

A = calcA(T, state_num)
C = calcC(T, state_num)
# R = [phase_noise_variance(20, T)]
R = [55*π/180] .^2
Q = calcQ(T, h₀, h₋₂, qₐ, f_L, state_num)
P, CLSEIG, F = ared(A', C', R[1], Q)
K = (P*C')*pinv(C*P*C' + R)
pᵩ = sqrt((C*P*C')[1]) * 180/π

σ²ᵩ = phase_noise_variance(CN0, T)
qₐ = 0.2^2
ω³₀ = 2π*f_L*sqrt(qₐ/(σ²ᵩ*T))/GNSSTools.c
ω₀ = (ω³₀)^(1/3)
ω²₀ = ω₀^2
K2 = [2*ω₀, 2*ω²₀, ω³₀]
B2 = calc_B(K2...)

# h_parms = [1.9e-6, 2e-6, 9.7e-8, 9.1e-11, 9.9e-15] ./ 16.3676e6^2  # TCXO
h_parms = [2.2e-9, 1.6e-10, 1.6e-10, 7.1e-13, 7.2e-16] ./ 10e6^2  # OCXO
h₀ = h_parms[3]
h₋₁ = h_parms[2]
h₋₂ = h_parms[1]
qₐ = 0.2^2
CN0s = [25, 35, 45]
Ts = Array(range(1.0, 20.0, step=1.0))*1e-3
fig = figure(figsize=(7.5, 4))
ax1 = fig.add_subplot(1,2,1)
xlabel("Integration Time (ms)")
ylabel("Phase Uncertainty (deg)")
ax2 = fig.add_subplot(1,2,2)
xlabel("Integration Time (ms)")
ylabel("Equivalent Bandwidth (Hz)")
for CN0 in CN0s
    σᵩ = zeros(length(Ts))
    Bs = zeros(length(Ts))
    for i in 1:length(Ts)
        T = Ts[i]
        A = calcA(T, state_num)
        C = calcC(T, state_num)
        R = [phase_noise_variance(CN0, T)]
        Q = calcQ(T, h₀, h₋₂, qₐ, f_L, state_num)
        P, CLSEIG, F = ared(A', C', R[1], Q)
        # pᵩ = sqrt((C*P*C')[1])
        pᵩ = sqrt(P[1])
        K = (P*C')*pinv(C*P*C' + R)
        σᵩ[i] = pᵩ*180/π
        Bs[i] = calc_B(K...)
        # Bs[i] = get_B(init_H(K...))
    end
    ax1.plot(Ts.*1000, σᵩ, label="$(CN0) dB⋅Hz")
    ax2.plot(Ts.*1000, Bs, label="$(CN0) dB⋅Hz")
end
legend()
subplots_adjust(wspace=0.3, bottom=0.15, left=0.08, right=0.92, top=0.92)

# h_parms = [1.9e-6, 2e-6, 9.7e-8, 9.1e-11, 9.9e-15] ./ 16.3676e6^2  # TCXO
h_parms = [2.2e-9, 1.6e-10, 1.6e-10, 7.1e-13, 7.2e-16] ./ 10e6^2  # OCXO
h₀ = h_parms[3]
h₋₁ = h_parms[2]
h₋₂ = h_parms[1]
qₐ = 0.05^2
CN0s = [10, 15, 20, 25]
Ts = Array(range(20, 140, step=1.0))*1e-3
fig = figure()
for CN0 in CN0s
    σᵩ = zeros(length(Ts))
    for i in 1:length(Ts)
        T = Ts[i]
        A = calcA(T, state_num)
        C = calcC(T, state_num)
        R = [phase_noise_variance(CN0, T)]
        Q = calcQ(T, h₀, h₋₂, qₐ, f_L, state_num)
        P, CLSEIG, F = ared(A', C', R[1], Q)
        K = (P*C')*pinv(C*P*C' + R)
        σᵩ[i] = sqrt(P[1])*180/π
    end
    plot(Ts.*1000, σᵩ, label="$(CN0) dB⋅Hz")
end
xlabel("Integration Time (ms)")
ylabel("Phase Uncertainty (deg)")
legend()


text:
testfolder = dirname(@__FILE__())

functionalitytests = [
    "sintest1"
  ]

println("============================")
println("Running functionality tests:")
println("============================")

for t in functionalitytests
  tfile = joinpath(testfolder,"functionality",string(t,".jl"))
  println("  * $(tfile) *")
  include(tfile)
  println()
end


text:
module IndexedGraphs

import SparseArrays:
    sparse, SparseMatrixCSC, nnz, nzrange, rowvals, nonzeros, spzeros

import Base:
    show, ==

using Reexport
@reexport import Graphs:
    AbstractGraph, AbstractEdge, src, dst, edgetype, has_vertex, has_edge, ne, nv,
    edges, vertices, neighbors, inneighbors, outneighbors, is_directed, is_bipartite,
    DijkstraState, dijkstra_shortest_paths,
    SimpleGraph, SimpleDiGraph, AbstractSimpleGraph

import Graphs.LinAlg:
    adjacency_matrix

import LinearAlgebra:
    issymmetric

import TrackingHeaps:
    TrackingHeap, pop!, NoTrainingWheels, MinHeapOrder

export
    idx, ==,
    AbstractIndexedGraph, inedges, outedges,
    # undirected graphs
    IndexedGraph, get_edge,
    # directed graphs
    AbstractIndexedDiGraph, IndexedDiGraph, IndexedBiDiGraph,
    # factor graphs
    FactorGraph, VariableOrFactor, Variable, Factor, FactorGraphEdge,
    nvariables, nfactors, variables, factors, bipartite_view

"""
    AbstractIndexedEdge{T<:Integer} <: AbstractEdge{T}

Abstract type for indexed edge.
`AbstractIndexedEdge{T}`s must have the following elements:
- `idx::T` integer positive index
"""
abstract type AbstractIndexedEdge{T<:Integer} <: AbstractEdge{T}; end

"""
    IndexedEdge{T<:Integer} <: AbstractIndexedEdge{T}

Edge type for `IndexedGraph`s. Edge indices can be used to access edge
properties stored in separate containers.
"""
struct IndexedEdge{T<:Integer} <: AbstractIndexedEdge{T}
	src::T
	dst::T
	idx::T
end

src(e::IndexedEdge) = e.src
dst(e::IndexedEdge) = e.dst
idx(e::AbstractIndexedEdge) = e.idx

function Base.:(==)(e1::T, e2::T) where {T<:AbstractIndexedEdge}
    fns = fieldnames(T)
    all( getproperty(e1, fn) == getproperty(e2, fn) for fn in fns )
end

function Base.show(io::IO, e::AbstractIndexedEdge)
    print(io, "Indexed Edge $(src(e)) => $(dst(e)) with index $(idx(e))")
end

include("utils.jl")

include("abstractindexedgraph.jl")
include("indexedgraph.jl")
include("indexeddigraph.jl")
include("indexedbidigraph.jl")
include("factorgraph.jl")

include("algorithms/dijkstra.jl")

end # end module


text:
using Documenter, Zarr

makedocs(
    modules = [Zarr],
    clean = false,
    format = Documenter.HTML(),
    sitename = "Zarr.jl",
    authors = "Fabian Gans, Martijn Visser",
    pages = [
        "Home" => "index.md",
        "Tutorial" => "tutorial.md",
        "Function Reference" => "reference.md",
        "Storage Backends" => "storage.md",
        "Accessing cloud data Examples" => "s3examples.md",
        "Operations on Zarr Arrays" => "operations.md",
        "Dealing with missing values" => "missings.md",
    ]
)

zarrpath = joinpath(@__DIR__, "data", "example.zarr")
isdir(zarrpath) && rm(zarrpath, recursive=true)

deploydocs(
    repo = "github.com/meggart/Zarr.jl.git",
)


text:
module CTProj3

using DataFrames, JuMP,  NLopt, Distributions, Gadfly#Plots
#pyplot()
flush(STDOUT)
Gadfly.push_theme(:dark)

#=markdown path
weave(Pkg.dir("C:\\Users\\Clinton\\Dropbox\\Coursework\\Theory\\PS3\\GMM.jl"),
  informat="script",
  out_path = "C:\\Users\\Clinton\\Dropbox\\Coursework\\Theory\\PS3\\outGMM.html",
  doctype = "md2html")
=#

#start the program
#in/out: nothing
function p5GMM()
  dfGMM= readtable("psgmmdata.csv")

  #get matrix for the dependent variable
  vDV = convert(Array,dfGMM[:ct_ct_1])
  vRM = Array(dfGMM[:,[:rm_rf]])
  vHML = Array(dfGMM[:,[:hml]])
  vSMB = Array(dfGMM[:,[:smb]])
  dERM = mean(dfGMM[:rm_rf])

  println("Means:")
  display(mean(Array(dfGMM[:,[:ct_ct_1,:rm_rf,:hml,:smb]]),1))

  println("Correlation Matrix:")
  display(cor(Array(dfGMM[:,[:ct_ct_1,:rm_rf,:hml,:smb]])))

  #this is a vectorized function for getting the g and d vectors
  ff(b) = vDV.^(-b)
  fdf(b) = -vDV.^(-b).*log(vDV)
  ffm(b,vM) = vM'*vDV.^(-b)
  vp = zeros(length(vDV))

  #lets do some graphs!
  γ=0:.1:100

  plOut = plot(melt(DataFrame(γ=γ,Rm=((x->(vRM'*ff(x))[1]).(γ)),
      HML=((x->(vHML'*ff(x))[1]).(γ)),
      SMB=((x->(vSMB'*ff(x))[1]).(γ))),:γ),
    x=:γ,y=:value,color=:variable,Geom.line,
    Guide.title("E(Ct+1/Ct*Re) vs γ"),
    Guide.xlabel("γ"),Guide.ylabel("E(Ct+1/Ct*Re)"),
    Guide.colorkey("Legend"))
  draw(PNG("InitialPlots.png", 7inch, 5inch), plOut)

  display(plOut)

  testSingleGMMEst!(ff,fdf,vp,vRM,
    eye(1),dERM , test="Rm Only")
  testSingleGMMEst!(ff,fdf,vp,vHML,
    eye(1),dERM , test="Hml Only")
  testSingleGMMEst!(ff,fdf,vp,vSMB,
    eye(1),dERM , test="SMB Only")
  testSingleGMMEst!(ff,fdf,vp,Array(dfGMM[:,[:rm_rf,:hml]]),
    eye(2),dERM , test="Rm_HML")
  testSingleGMMEst!(ff,fdf,vp,Array(dfGMM[:,[:rm_rf,:smb]]),
    eye(2),dERM , test="Rm_SMB")

end

#a wrapper for each of the moment sets
#in: dependent variable function, its derivtive, moment vectors, W matrix,
#the name of the test
#out: value 1 for success. Also, W is changed by reference
function testSingleGMMEst!(ff,fdf,vp,mMom, mW, dERM; test="")

  #some intermediate variables for future calculations
  γ=0:.1:100

  println("Running first stage for: ",test)
  db1,dVar1,valpha,valphaVar,dchi2,dchi2p,vgWg1,va =
     singleGMMEst!(ff,fdf,vp,mMom,mW,dERM=dERM,pltRng=γ)
  print("\nFirst stage ",test, " results\n",
    "\nγ: ", db1,
    "\nσ: ", dVar1^0.5,
    "\nχ2 Stat: ", size(mMom,2)>1 ? dchi2 : "NA",
    "\nχ2 p Val: ", size(mMom,2)>1 ? dchi2p : "NA",
    "\nα (E[mRe]-E[Re]): ",valpha,
    "\nα Var: ",valphaVar,
    "\nW (normalized by 1/mean(|S|)): ", mW,
    "\na: ", va,
    "\n")

  println("Running second stage for: ",test)
  db2,dVar,valpha,valphaVar,dchi2,dchi2p,vgWg2,va =
     singleGMMEst!(ff,fdf,vp,mMom,mW,dERM=dERM,pltRng=γ)

  print("\nSecond stage ",test, " results",
    "\nγ: ", db2,
    "\nσ: ", dVar^0.5,
    "\nχ2 Stat: ", size(mMom,2)>1 ? dchi2 : "NA",
    "\nχ2 p Val: ", size(mMom,2)>1 ? dchi2p : "NA",
    "\nα (E[mRe]-E[Re]): ",valpha,
    "\nα Var: ",valphaVar,
    "\nW (normalized by 1/mean(|S|)): ", mW,
    "\na: ", va,
    "\n")

  if(db1!=db2)
    plOut = plot(melt(DataFrame(γ=γ,FirstStage=vgWg1,SecondStage=vgWg2),:γ)
      , x=:γ,y=:value,color=:variable,Geom.line,
      Guide.title(test),
      Guide.xlabel("γ"),Guide.ylabel("gWg"),
      Guide.colorkey("Legend"))
    draw(PNG(test*".png", 7inch, 5inch), plOut)
    display(plOut)
  else
    plOut = plot(melt(DataFrame(γ=γ,gWg=vgWg1),:γ)
      , x=:γ,y=:value,Geom.line,
      Guide.title(test),
      Guide.xlabel("γ"),Guide.ylabel("gWg"))
    draw(PNG(test*".png", 5inch, 5inch), plOut)
    display(plOut)
  end

  return (1)
end

#gets the GMM estiamte fo a single parameter
#in:  dependent variable function, its derivtive, moment vectors, W matrix
#out: parameter value, its variance, alpha, its variance,
#     chisq stat (2> moments only), its p value,
#     updated weighting W (by ref)
function singleGMMEst!(ff,fdf,vp,mMom,mW; dERM = 0, pltRng = 0:1:100)

  #Different optimization models work: GN_ISRES, LN_COBYLA, LN_NELDERMEAD,
  #LN_BOBYQA, # GN_CRS2_LM, GN_DIRECT, LD_CCSAQ, LD_SLSQP,
  # LD_TNEWTON_PRECOND_RESTART, LD_LBFGS. Many others do not work.
  # I use LD_LBFGS due to prevelence and relative simplicity
  # I think LN_COBYLA is also a good choice
  modOp=Model(solver=NLoptSolver(algorithm=:LD_LBFGS))
  #modOp=Model(solver=NLoptSolver(algorithm=:LN_COBYLA))
  iN = Int(size(mMom,1))
  iM= Int(size(mMom,2))

  vg = Vector{Float64}(iM)
  vEp = sum(vp)/iN
  vf = Vector{Float64}(iN)

  #note this could be made faster through devectorization or BLAS routines
  #vectorized version
  function fgWg(b)
    vg = (mMom'*ff(b))./iN.-vEp
    return (vg'*mW*vg)[1]
  end

  #start the optimization macro
  @variable(modOp,-100<=jb<=100, start=1)
  JuMP.register(modOp,:ff,1,ff,autodiff=true)
  JuMP.register(modOp,:fgWg,1,fgWg,autodiff=true)
  @NLobjective(modOp, Min, fgWg(jb))
  output = solve(modOp)

  #get outputs and an average of f
  dbVal=getvalue(jb)
  vfBar=(mMom'*ff(dbVal)./iN)
  vf = ff(dbVal)
  vg = vfBar-vEp

  #fully devecotrized calculation for s
  mS=zeros(iM,iM)
  for i=1:iN
    for j=1:iM #this loop adds the de-meaned  covar matrix at the ith data point
      for k = 1:iM
        mS[k,j]+=(mMom[i,k]*vf[i]-vfBar[k])*(vf[i]*mMom[i,j]-vfBar[j])
      end
    end
  end
  mS=mS/iN

  #calculate the standard errors
  vd = (mMom'*fdf(dbVal))./iN
  ddWdInv = 1/((vd'*mW*vd)[1])

  #calculate the alpha stats
  valpha = vg #- mMom'*ones(iN)./iN #Not sure about this part
  vWd_dwdInv_d = mW*vd*ddWdInv*vd'
  valphaVar = (eye(iM)-vWd_dwdInv_d')*mS*(eye(iM)-vWd_dwdInv_d)/iN

  #Get the stats for the identification test
  if iM>1
    dchi2 = (vg'*(pinv(valphaVar))*vg*(iM-1))[1]
    dchi2p = 1-cdf(Chisq(iM-1),dchi2)
  else
    dchi2 = 0.0
    dchi2p = 0.0
  end

  #recalc the weighting matrix
  dNormConst = (1/abs(mean(mS))) #improves numerical stability/scaling
  vgWg = fgWg.(pltRng)
  mW.=(mS*dNormConst)\eye(iM)

  ddWdInv = 1/((vd'*mW*vd)[1])
  dse2 = ddWdInv^2*(vd'*mW*mS*mW*vd)[1]/iN

  va = mW*vd.*dNormConst

  return dbVal, dse2, valpha,valphaVar,dchi2,dchi2p, vgWg, va
end

#call the main wrapper function
p5GMM()

end

max_stars_repo_name: clintonTE/CCA

max_issues_repo_name: clintonTE/CCA

max_forks_repo_name: clintonTE/CCA


text:
module FlameGraphs

using Profile, LeftChildRightSiblingTrees
using Base.StackTraces: StackFrame
using Profile: StackFrameTree
using Colors, FixedPointNumbers, IndirectArrays
using FileIO

# AbstractTree interface for StackFrameTree:
using AbstractTrees
AbstractTrees.children(node::StackFrameTree) = node.down
AbstractTrees.printnode(io::IO, node::StackFrameTree) = print(io, node.frame)

export flamegraph, flamepixels, flametags, FlameColors, StackFrameCategory

include("graph.jl")
include("render.jl")
include("sfcategory.jl")
include("io.jl")

function _precompile_()
    ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
    @assert precompile(flametags, (Node{NodeData}, Matrix{RGB{N0f8}}))
    @assert precompile(flamegraph, ())
    @assert precompile(NodeData, (StackTraces.StackFrame, UInt8, UnitRange{Int64}))
    if isdefined(Base, :bodyfunction)
        m = which(flamegraph, (Vector{UInt64},))
        f = Base.bodyfunction(m)
        @assert precompile(f, (Dict{UInt64,Vector{Base.StackTraces.StackFrame}}, Bool, Bool, Symbol, Bool, Vector{Tuple{Symbol,Symbol}}, Nothing, typeof(flamegraph), Vector{UInt64}))
        m = which(flamepixels, (FlameColors, Node))
        f = Base.bodyfunction(m)
        @assert precompile(f, (Nothing, typeof(flamepixels), FlameColors, Node{NodeData}))
    end
end
VERSION >= v"1.4.2" && _precompile_() # https://github.com/JuliaLang/julia/pull/35378

end # module


text:
global MAPLIGHT_API = URI("http://maplight.org")

set_api_endpoint(endpoint) = (global MAPLIGHT_API = endpoint)


text: # add code here


text:
using Test
using InteractionWeightedDIDs

using DataFrames
using Dates: Date, Year
using DiffinDiffsBase: ValidTimeType, @fieldequal, valid_didargs
using FixedEffectModels: Combination, nunique, _multiply
using FixedEffects
using InteractionWeightedDIDs: FETerm, _parsefeterm, getfename,
    checkvcov!, parsefeterms!, groupfeterms, makefes, checkfes!, makefesolver,
    _feresiduals!, makeyxcols, maketreatcols, solveleastsquares!, estvcov,
    solveleastsquaresweights
using LinearAlgebra
using StatsBase: Weights, uweights
using StatsProcedures: required, default, transformed, combinedargs, _byid

import Base: ==

@fieldequal FixedEffect
@fieldequal Vcov.ClusterCovariance
@fieldequal RegressionBasedDIDResult

const tests = [
    "utils",
    "procedures",
    "did"
]

printstyled("Running tests:\n", color=:blue, bold=true)

@time for test in tests
    include("$test.jl")
    println("\033[1m\033[32mPASSED\033[0m: $(test)")
end


text:
function add_pm_cost!(
    PM_gen::Dict{String, Any},
    op_cost::T,
    base::Float64,
) where {T <: PSY.ThreePartCost}
    PM_gen["startup"] = PSY.get_start_up(op_cost)
    PM_gen["shutdown"] = PSY.get_shut_down(op_cost)
    add_pm_var_cost!(PM_gen, PSY.get_variable(op_cost), base, PSY.get_fixed(op_cost))
    return PM_gen
end

function add_pm_cost!(
    PM_gen::Dict{String, Any},
    op_cost::T,
    base::Float64,
) where {T <: PSY.TwoPartCost}
    PM_gen["shutdown"] = 0.0
    PM_gen["startup"] = 0.0
    add_pm_var_cost!(PM_gen, PSY.get_variable(op_cost), base, PSY.get_fixed(op_cost))
    return PM_gen
end

# polynomial cost
function add_pm_var_cost!(
    PM_gen::Dict{String, Any},
    var_cost::PSY.VariableCost{Tuple{Float64, Float64}},
    base::Float64,
    fixed::Float64,
)
    cost = PSY.get_cost(var_cost)
    PM_gen["model"] = 2
    PM_gen["cost"] = Vector{Float64}()
    PM_gen["ncost"] = cost[1] != 0.0 ? 3 : cost[2] != 0.0 ? 2 : 0
    PM_gen["ncost"] == 0 && return PM_gen

    for idx in (4 - PM_gen["ncost"]):2
        push!(PM_gen["cost"], cost[idx])
        PM_gen["cost"] = PM_gen["cost"] .* base
    end
    push!(PM_gen["cost"], fixed)
    return PM_gen
end

# pwl cost
function add_pm_var_cost!(
    PM_gen::Dict{String, Any},
    var_cost::PSY.VariableCost{Vector{Tuple{Float64, Float64}}},
    base::Float64,
    fixed::Float64,
)
    PM_gen["model"] = 1
    PM_gen["ncost"] = length(var_cost)
    PM_gen["cost"] = Vector{Float64}()
    for c in PSY.get_cost(var_cost)
        push!(PM_gen["cost"], last(c))
        push!(PM_gen["cost"], first(c) + fixed)
    end
    return PM_gen
end

# scalar cost
function add_pm_var_cost!(
    PM_gen::Dict{String, Any},
    var_cost::PSY.VariableCost{Float64},
    base::Float64,
    fixed::Float64,
)
    PM_gen["model"] = 2
    PM_gen["ncost"] = length(var_cost)
    PM_gen["cost"] = [PSY.get_cost(var_cost) + fixed]
    return PM_gen
end

function pm_gen_core(gen::T, ix::Int) where {T <: PSY.Generator}
    PM_gen = Dict{String, Any}(
        "index" => ix,
        "name" => PSY.get_name(gen),
        "gen_bus" => PSY.get_number(PSY.get_bus(gen)),
        "source_id" => convert(Vector{Any}, split(PSY.get_name(gen), "-")),
        "mbase" => PSY.get_base_power(gen),
        "gen_status" => Int(PSY.get_available(gen)),
        "pg" => PSY.get_active_power(gen),
        "qg" => PSY.get_reactive_power(gen),
        "vg" => PSY.get_magnitude(PSY.get_bus(gen)),
        "pmax" => PSY.get_max_active_power(gen),
        "qmax" => PSY.get_max_reactive_power(gen),
        "type" => string(PSY.get_prime_mover(gen)),
    )
    return PM_gen
end

function get_component_to_pm(ix::Int, gen::T) where {T <: PSY.ThermalGen}
    PM_gen = pm_gen_core(gen, ix)
    ramplims = PSY.get_ramp_limits(gen)
    ramp = isnothing(ramplims) ? 9999.0 : getfield(ramplims, :up)
    merge!(
        PM_gen,
        Dict{String, Any}(
            "pmin" => PSY.get_active_power_limits(gen).min,
            "fuel" => string(PSY.get_fuel(gen)),
            "ramp_10" => ramp,
            "qmin" => PSY.get_reactive_power_limits(gen).min,
            "gen_status" => Int(PSY.get_status(gen)),
        ),
    )
    add_pm_cost!(PM_gen, PSY.get_operation_cost(gen), gen.internal.units_info.base_value)
    return PM_gen
end

function get_component_to_pm(ix::Int, gen::T) where {T <: PSY.RenewableGen}
    PM_gen = pm_gen_core(gen, ix)
    merge!(
        PM_gen,
        Dict{String, Any}("pmin" => 0.0, "qmin" => PSY.get_reactive_power_limits(gen).min),
    )
    add_pm_cost!(PM_gen, PSY.get_operation_cost(gen), gen.internal.units_info.base_value)
    return PM_gen
end

function get_component_to_pm(ix::Int, gen::T) where {T <: PSY.RenewableFix}
    PM_gen = pm_gen_core(gen, ix)
    merge!(PM_gen, Dict{String, Any}("pmin" => 0.0, "qmin" => 0.0))
    return PM_gen
end

function get_component_to_pm(ix::Int, gen::T) where {T <: PSY.HydroGen}
    PM_gen = pm_gen_core(gen, ix)
    ramp = PSY.get_ramp_limits(gen).up
    merge!(
        PM_gen,
        Dict{String, Any}(
            "pmin" => PSY.get_active_power_limits(gen).min,
            "ramp_10" => ramp,
            "qmin" => PSY.get_reactive_power_limits(gen).min,
        ),
    )
    add_pm_cost!(PM_gen, PSY.get_operation_cost(gen), gen.internal.units_info.base_value)
    return PM_gen
end

function get_time_series_to_pm!(
    pm_data::Dict{String, Any},
    pm_category::String,
    pm_id::String,
    device::T,
    start_time::Dates.DateTime,
    time_periods::UnitRange{Int},
) where {T <: PSY.RenewableGen}
    psy_forecast_name = "max_active_power" # change this line for different forecasts
    pm_field_name = "pmax" # change this line to apply forecast to different fields

    ts_data = PSY.get_time_series_values(
        PSY.AbstractDeterministic,
        device,
        psy_forecast_name,
        start_time = start_time,
    )
    if haskey(pm_data, "nw")
        pm_update = Dict{String, Any}("nw" => Dict{String, Any}())
        for (t, nw) in pm_data["nw"]
            pm_update["nw"][t] = Dict(
                pm_category => Dict(
                    pm_id =>
                        Dict(pm_field_name => ts_data[time_periods[parse(Int, t)]]),
                ),
            )
        end
    else
        throw(@error "cannot apply time series to single period data")
    end

    PM.update_data!(pm_data, pm_update)
end

function get_time_series_to_pm!(
    pm_data::Dict{String, Any},
    pm_category::String,
    pm_id::String,
    device::T,
    start_time::Dates.DateTime,
    time_period::Int,
) where {T <: PSY.RenewableGen}
    psy_forecast_name = "max_active_power" # change this line for different forecasts
    pm_field_name = "pmax"# change this line to apply forecast to different fields

    ts_data = PSY.get_time_series_values(
        PSY.AbstractDeterministic,
        device,
        psy_forecast_name,
        start_time = start_time,
    )
    if haskey(pm_data, "nw")
        throw(@error "cannot apply single time period data to multi-network")
    else
        pm_update = Dict{String, Any}(
            pm_category => Dict(pm_id => Dict(pm_field_name => ts_data[time_period])),
        )
    end

    PM.update_data!(pm_data, pm_update)
end

max_stars_repo_name: NREL-SIIP/PowerModelsInterface

max_issues_repo_name: NREL-SIIP/PowerModelsInterface

max_forks_repo_name: NREL-SIIP/PowerModelsInterface


text:
#!/usr/bin/env julia

#
# A test script that checks correctness of algorithms that evaluate incomplete
# gamma and beta functions and their inverses.
# All input arguments have been carefully selected to cover all branches
# of evaluation algorithms.
#
# For more information how to reproduce exact (i.e. unrounded) results, see
# https://github.com/jkovacic/math/blob/master/scripts/test/specfun.mac and
# https://github.com/jkovacic/math/blob/master/scripts/test/specfun.py
#

using IncGammaBeta
using Base.Test

# Round all results to 'i' decimal places:
function my_round(i::Integer)
    return x -> round(x, i)
end

mrd = my_round(6)

print_with_color(:blue, "Running tests...\n")

@test mrd( inc_gamma_upper(2.0, 0.5) ) == 0.909796
@test mrd( inc_gamma_upper(2.0, 5.0) ) == 0.040428
@test mrd( inc_gamma_lower(3.0, 2.5) ) == 0.912374
@test mrd( inc_gamma_lower(1.5, 4.0) ) == 0.84545
@test mrd( inc_gamma_upper_reg(3.0, 0.5) ) == 0.985612
@test mrd( inc_gamma_lower_reg(1.5, 3.2) ) == 0.906309
@test mrd( inc_gamma_upper(2.0+im, 1.0-im) ) == 0.294741 + 1.123545im
@test mrd( inc_gamma_lower(1.4-0.7im, 0.3+0.1im) ) == -0.016291 + 0.130522im
@test mrd( inc_gamma_upper_reg(-1.1+0.8im, 2.4-1.3im) ) == 0.001972 + 0.016253im
@test mrd( inc_gamma_lower_reg(-3.2-1.4im, -2.0-0.5im) ) == 0.239716 + 0.435784im

@test mrd( inc_beta_lower(2.0, 5.0, 0.2) ) == 0.011488
@test mrd( inc_beta_lower(2.0, 5.0, 0.7) ) == 0.032969
@test mrd( inc_beta_upper(1.0, 2.0, 0.15) ) == 0.36125
@test mrd( inc_beta_upper(1.0, 2.0, 0.82) ) == 0.0162
@test mrd( inc_beta_lower_reg(3.0, 2.0, 0.12) ) == 0.00629
@test mrd( inc_beta_upper_reg(4.0, 1.0, 0.32) ) == 0.989514
@test mrd( inc_beta_lower(2.0-im, 3.0+im, 0.1+0.1im) ) == -0.01428 - 0.010563im
@test mrd( inc_beta_upper(-1.2+im, -3.0-im, 0.1-0.2im) ) == 35.01302 + 12.573969im
@test mrd( inc_beta_lower_reg(2.2-0.2im, 1.4+0.7im, -0.3+0.4im) ) ==  0.82213 - 0.401782im
@test mrd( inc_beta_upper_reg(-3.8-0.7im, -3.4+2.7im, -0.6-0.1im) ) == 0.001099 + 0.002128im

@test mrd( inc_gamma_lower_reg_inv(0.2, 0.3) ) == 0.001588
@test mrd( inc_gamma_lower_reg_inv(3.0, 0.7) ) == 3.615568
@test mrd( inc_gamma_upper_reg_inv(0.3, 0.4) ) == 0.141253
@test mrd( inc_gamma_upper_reg_inv(5.2, 0.82) ) == 3.129677
@test mrd( inc_gamma_lower_inv(0.24, 0.94) ) == 0.002024
@test mrd( inc_gamma_lower_inv(2.8, 0.17) ) == 0.987392
@test mrd( inc_gamma_upper_inv(0.65, 0.86) ) == 0.217367
@test mrd( inc_gamma_upper_inv(3.5, 0.43) ) == 5.609014

@test mrd( inc_beta_lower_reg_inv(0.3, 0.2, 0.7) ) == 0.978553
@test mrd( inc_beta_lower_reg_inv(2.4, 3.5, 0.6) ) == 0.449428
@test mrd( inc_beta_upper_reg_inv(0.9, 1.5, 0.7) ) == 0.181633
@test mrd( inc_beta_upper_reg_inv(1.9, 2.7, 0.25) ) == 0.56483
@test mrd( inc_beta_lower_inv(2.8, 0.3, 2.0) ) == 0.999734
@test mrd( inc_beta_lower_inv(1.1, 1.3, 0.4) ) == 0.519011
@test mrd( inc_beta_upper_inv(0.4, 0.5, 1.8) ) == 0.410869
@test mrd( inc_beta_upper_inv(1.7, 1.1, 0.2) ) == 0.720553

print_with_color(:blue, "Tests completed successfully.\n")