text:
using CImGui
using CImGui.CSyntax
using CImGui.CSyntax.CStatic
using CImGui: ImVec2, ImVec4, IM_COL32, ImS32, ImU32, ImS64, ImU64

"""
    ShowDemoWindowWidgets()
"""
function ShowDemoWindowWidgets()
    CImGui.CollapsingHeader("Widgets") || return

    if CImGui.TreeNode("Basic")
        @cstatic clicked=Cint(0) begin
            CImGui.Button("Button") && (clicked += 1;)
            if clicked & 1 != 0
                CImGui.SameLine()
                CImGui.Text("Thanks for clicking me!")
            end
        end

        @cstatic check=true @c CImGui.Checkbox("checkbox", &check)

        @cstatic e=Cint(0) begin
            @c CImGui.RadioButton("radio a", &e, 0); CImGui.SameLine()
            @c CImGui.RadioButton("radio b", &e, 1); CImGui.SameLine()
            @c CImGui.RadioButton("radio c", &e, 2);
        end

        # color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
        for i = 0:7-1
            i > 0 && CImGui.SameLine()
            CImGui.PushID(i)
            CImGui.PushStyleColor(CImGui.ImGuiCol_Button, CImGui.HSV(i/7.0, 0.6, 0.6))
            CImGui.PushStyleColor(CImGui.ImGuiCol_ButtonHovered, CImGui.HSV(i/7.0, 0.7, 0.7))
            CImGui.PushStyleColor(CImGui.ImGuiCol_ButtonActive, CImGui.HSV(i/7.0, 0.8, 0.8))
            CImGui.Button("Click")
            CImGui.PopStyleColor(3)
            CImGui.PopID()
        end

        # use AlignTextToFramePadding() to align text baseline to the baseline of framed elements (otherwise a Text+SameLine+Button sequence will have the text a little too high by default)
        CImGui.AlignTextToFramePadding()
        CImGui.Text("Hold to repeat:")
        CImGui.SameLine()

        # arrow buttons with Repeater
        @cstatic counter=Cint(0) begin
            spacing = CImGui.GetStyle().ItemInnerSpacing.x
            CImGui.PushButtonRepeat(true)
            CImGui.ArrowButton("##left", CImGui.ImGuiDir_Left) && (counter-=1;)
            CImGui.SameLine(0.0, spacing)
            CImGui.ArrowButton("##right", CImGui.ImGuiDir_Right) && (counter+=1;)
            CImGui.PopButtonRepeat()
            CImGui.SameLine()
            CImGui.Text("$counter")
        end

        CImGui.Text("Hover over me")
        CImGui.IsItemHovered() && CImGui.SetTooltip("I am a tooltip")

        CImGui.SameLine()
        CImGui.Text("- or me")
        if CImGui.IsItemHovered()
            CImGui.BeginTooltip()
            CImGui.Text("I am a fancy tooltip")
            @cstatic arr=Cfloat[0.6, 0.1, 1.0, 0.5, 0.92, 0.1, 0.2] CImGui.PlotLines("Curve", arr, length(arr))
            CImGui.EndTooltip()
        end

        CImGui.Separator()

        CImGui.LabelText("label", "Value")

        @cstatic item_current=Cint(0)
                 items = ["AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO"]
                begin
            # using the _simplified_ one-liner Combo() api here
            # see "Combo" section for examples of how to use the more complete BeginCombo()/EndCombo() api.
            @c CImGui.Combo("combo", &item_current, items, length(items))
            CImGui.SameLine()
            ShowHelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n")
        end

        @cstatic str0="Hello, world!"*"\0"^115 i0=Cint(123) begin
            CImGui.InputText("input text", str0, length(str0))
            CImGui.SameLine()
            ShowHelpMarker("USER:\nHold SHIFT or use mouse to select text.\n"*"CTRL+Left/Right to word jump.\n"*"CTRL+A or double-click to select all.\n"*"CTRL+X,CTRL+C,CTRL+V clipboard.\n"*"CTRL+Z,CTRL+Y undo/redo.\n"*"ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp).")

            @c CImGui.InputInt("input int", &i0)
            CImGui.SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n  e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n")
        end

        @cstatic f0=Cfloat(0.001) d0=Cdouble(999999.00000001) f1=Cfloat(1.e10) begin
            @c CImGui.InputFloat("input float", &f0, 0.01, 1.0, "%.3f")
            @c CImGui.InputDouble("input double", &d0, 0.01, 1.0, "%.8f")
            @c CImGui.InputFloat("input scientific", &f1, 0.0, 0.0, "%e")
            CImGui.SameLine()
            ShowHelpMarker("You can input value using the scientific notation,\n  e.g. \"1e+8\" becomes \"100000000\".\n")
        end
        @cstatic vec4a=Cfloat[0.10, 0.20, 0.30, 0.44] CImGui.InputFloat3("input float3", vec4a)

        @cstatic i1=Cint(50) i2=Cint(42) begin
            @c CImGui.DragInt("drag int", &i1, 1)
            CImGui.SameLine()
            ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value.")

            @c CImGui.DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%")
        end

        @cstatic f1=Cfloat(1.00) f2=Cfloat(0.0067) begin
            @c CImGui.DragFloat("drag float", &f1, 0.005)
            @c CImGui.DragFloat("drag small float", &f2, 0.0001, 0.0, 0.0, "%.06f ns")
        end

        @cstatic i1=Cint(0) f1=Cfloat(0.123) f2=Cfloat(0.0) angle=Cfloat(0.0) begin
            @c CImGui.SliderInt("slider int", &i1, -1, 3)
            CImGui.SameLine()
            ShowHelpMarker("CTRL+click to input value.")

            @c CImGui.SliderFloat("slider float", &f1, 0.0, 1.0, "ratio = %.3f")
            @c CImGui.SliderFloat("slider float (log)", &f2, -10.0, 10.0, "%.4f", CImGui.ImGuiSliderFlags_Logarithmic)

            @c CImGui.SliderAngle("slider angle", &angle)
        end

        @cstatic col1=Cfloat[1.0,0.0,0.2] col2=Cfloat[0.4,0.7,0.0,0.5] begin
            CImGui.ColorEdit3("color 1", col1)
            CImGui.SameLine()
            ShowHelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n")
            CImGui.ColorEdit4("color 2", col2)
        end

        @cstatic listbox_item_current=Cint(1) listbox_items=["Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon"] begin
            # list box
            @c CImGui.ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, length(listbox_items), 4)
        end
        # CImGui.PushItemWidth(-1)
        # CImGui.ListBox("##listbox2", &listbox_item_current2, listbox_items, length(listbox_items), 4)
        # CImGui.PopItemWidth()

        CImGui.TreePop()
    end

    if CImGui.TreeNode("Trees")
        if CImGui.TreeNode("Basic trees")
            for i = 0:4
                if CImGui.TreeNode(Ptr{Cvoid}(i), "Child $i")
                    CImGui.Text("blah blah")
                    CImGui.SameLine()
                    CImGui.SmallButton("button") && @info "Trigger `Basic trees`'s button | find me here: $(@__FILE__) at line $(@__LINE__)"
                    CImGui.TreePop()
                end
            end
            CImGui.TreePop()
        end

        if CImGui.TreeNode("Advanced, with Selectable nodes")
            ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open.")
            align_label_with_current_x_position= @cstatic align_label_with_current_x_position=false begin
                @c CImGui.Checkbox("Align label with current X position)", &align_label_with_current_x_position)
                CImGui.Text("Hello!")
                align_label_with_current_x_position && CImGui.Unindent(CImGui.GetTreeNodeToLabelSpacing())
            end

            @cstatic selection_mask=Cint(1 << 2) begin  # dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit.
                node_clicked = -1  # temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc.
                CImGui.PushStyleVar(CImGui.ImGuiStyleVar_IndentSpacing, CImGui.GetFontSize()*3) # increase spacing to differentiate leaves from expanded contents.
                for i = 0:5
                    # disable the default open on single-click behavior and pass in Selected flag according to our selection state.
                    node_flags = CImGui.ImGuiTreeNodeFlags_OpenOnArrow | CImGui.ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) != 0 ? CImGui.ImGuiTreeNodeFlags_Selected : 0)
                    if i < 3
                        # Node
                        node_open = CImGui.TreeNodeEx(Ptr{Cvoid}(i), node_flags, "Selectable Node $i")
                        CImGui.IsItemClicked() && (node_clicked = i;)
                        node_open && (CImGui.Text("Blah blah\nBlah Blah"); CImGui.TreePop();)
                    else
                        # Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text().
                        node_flags |= CImGui.ImGuiTreeNodeFlags_Leaf | CImGui.ImGuiTreeNodeFlags_NoTreePushOnOpen # CImGui.ImGuiTreeNodeFlags_Bullet
                        CImGui.TreeNodeEx(Ptr{Cvoid}(i), node_flags, "Selectable Leaf $i")
                        CImGui.IsItemClicked() && (node_clicked = i;)
                    end
                end
                if node_clicked != -1
                    # update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame.
                    if CImGui.GetIO().KeyCtrl
                        selection_mask ⊻= 1 << node_clicked           # CTRL+click to toggle
                    else #if (!(selection_mask & (1 << node_clicked))) # Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection
                        selection_mask = 1 << node_clicked            # Click to single-select
                    end
                end
                CImGui.PopStyleVar()
            end # @cstatic
            align_label_with_current_x_position && CImGui.Indent(CImGui.GetTreeNodeToLabelSpacing())
            CImGui.TreePop()
        end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Collapsing Headers")
        @cstatic closable_group=true begin
            @c CImGui.Checkbox("Enable extra group", &closable_group)
            if CImGui.CollapsingHeader("Header")
                CImGui.Text("IsItemHovered: $(CImGui.IsItemHovered())")
                foreach(i->CImGui.Text("Some content $i"), 0:4)
            end
            if @c CImGui.CollapsingHeader("Header with a close button", &closable_group)
                CImGui.Text("IsItemHovered: $(CImGui.IsItemHovered())")
                foreach(i->CImGui.Text("More content $i"), 0:4)
            end
        end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Bullets")
        CImGui.BulletText("Bullet point 1")
        CImGui.BulletText("Bullet point 2\nOn multiple lines")
        CImGui.Bullet()
        CImGui.Text("Bullet point 3 (two calls)")
        CImGui.Bullet()
        CImGui.SmallButton("Button") && @info "Trigger `Bullets`'s Button | find me here: $(@__FILE__) at line $(@__LINE__)"
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Text")
        if CImGui.TreeNode("Colored Text")
            # using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
            CImGui.TextColored(ImVec4(1.0,0.0,1.0,1.0), "Pink")
            CImGui.TextColored(ImVec4(1.0,1.0,0.0,1.0), "Yellow")
            CImGui.TextDisabled("Disabled")
            CImGui.SameLine()
            ShowHelpMarker("The TextDisabled color is stored in ImGuiStyle.")
            CImGui.TreePop()
        end

        if CImGui.TreeNode("Word Wrapping")
            # using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
            CImGui.TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages.")
            CImGui.Spacing()

            @cstatic wrap_width = Cfloat(200.0) begin
                @c CImGui.SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f")

                CImGui.Text("Test paragraph 1:")
                pos = CImGui.GetCursorScreenPos()
                draw_list = CImGui.GetWindowDrawList()
                CImGui.AddRectFilled(draw_list, ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + CImGui.GetTextLineHeight()), IM_COL32(255,0,255,255))
                CImGui.PushTextWrapPos(CImGui.GetCursorPos().x + wrap_width)
                CImGui.Text("The lazy dog is a good dog. This paragraph is made to fit within $wrap_width pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.")
                CImGui.AddRect(draw_list, CImGui.GetItemRectMin(), CImGui.GetItemRectMax(), IM_COL32(255,255,0,255))
                CImGui.PopTextWrapPos()

                CImGui.Text("Test paragraph 2:")
                pos = CImGui.GetCursorScreenPos()
                CImGui.AddRectFilled(draw_list, ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + CImGui.GetTextLineHeight()), IM_COL32(255,0,255,255))
                CImGui.PushTextWrapPos(CImGui.GetCursorPos().x + wrap_width)
                CImGui.Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee   ffffffff. gggggggg!hhhhhhhh")
                CImGui.AddRect(draw_list, CImGui.GetItemRectMin(), CImGui.GetItemRectMax(), IM_COL32(255,255,0,255))
                CImGui.PopTextWrapPos()
            end
            CImGui.TreePop()
        end

        # if CImGui.TreeNode("UTF-8 Text")
        #     # UTF-8 test with Japanese characters
        #     # (Needs a suitable font, try Noto, or Arial Unicode, or M+ fonts. Read misc/fonts/README.txt for details.)
        #     # - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
        #     # - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. Visual Studio save your file as 'UTF-8 without signature')
        #     # - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE.
        #     #   Instead we are encoding a few strings with hexadecimal constants. Don't do this in your application!
        #     #   Please use u8"text in any language" in your application!
        #     # Note that characters values are preserved even by InputText() if the font cannot be displayed, so you can safely copy & paste garbled characters into another application.
        #     CImGui.TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->AddFontFromFileTTF() manually to load extra character ranges. Read misc/fonts/README.txt for details.")
        #     CImGui.Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)") # Normally we would use u8"blah blah" with the proper characters directly in the string.
        #     CImGui.Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)")
        #     static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"
        #     CImGui.InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf))
        #     CImGui.TreePop()
        # end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Images")
        io = CImGui.GetIO()
        CImGui.TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!")

        # Here we are grabbing the font texture because that's the only one we have access to inside the demo code.
        # Remember that ImTextureID is just storage for whatever you want it to be, it is essentially a value that will be passed to the render function inside the ImDrawCmd structure.
        # If you use one of the default imgui_impl_XXXX.cpp renderer, they all have comments at the top of their file to specify what they expect to be stored in ImTextureID.
        # (for example, the imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. The imgui_impl_glfw_gl3.cpp renderer expect a GLuint OpenGL texture identifier etc.)
        # If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers to CImGui.Image(), and gather width/height through your own functions, etc.
        # Using ShowMetricsWindow() as a "debugger" to inspect the draw data that are being passed to your render will help you debug issues if you are confused about this.
        # Consider using the lower-level ImDrawList::AddImage() API, via CImGui.GetWindowDrawList()->AddImage().
        my_tex_id = unsafe_load(io.Fonts.TexID)
        my_tex_w = unsafe_load(io.Fonts.TexWidth)
        my_tex_h = unsafe_load(io.Fonts.TexHeight)

        CImGui.Text(@sprintf("%.0fx%.0f", my_tex_w, my_tex_h))
        pos = CImGui.GetCursorScreenPos()
        CImGui.Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), (0,0), (1,1), (255,255,255,255), (255,255,255,128))
        if CImGui.IsItemHovered()
            CImGui.BeginTooltip()
            region_sz = 32.0
            region_x = unsafe_load(io.MousePos).x - pos.x - region_sz * 0.5
            if region_x < 0.0
                region_x = 0.0
            elseif region_x > my_tex_w - region_sz
                region_x = my_tex_w - region_sz
            end
            region_y = unsafe_load(io.MousePos).y - pos.y - region_sz * 0.5
            if region_y < 0.0
                region_y = 0.0
            elseif region_y > my_tex_h - region_sz
                region_y = my_tex_h - region_sz
            end
            zoom = 4.0
            CImGui.Text(@sprintf("Min: (%.2f, %.2f)", region_x, region_y))
            CImGui.Text(@sprintf("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz))
            uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h)
            uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h)
            CImGui.Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, (255,255,255,255), (255,255,255,128))
            CImGui.EndTooltip()
        end
        CImGui.TextWrapped("And now some textured buttons..")
        @cstatic pressed_count=Cint(0) begin
            for i = 0:8-1
                CImGui.PushID(i)
                frame_padding = -1 + i     # -1 = uses default padding
                if CImGui.ImageButton(my_tex_id, (32,32), (0,0), ImVec2(32.0/my_tex_w,32/my_tex_h), frame_padding, (0,0,0,255))
                    pressed_count += 1
                end
                CImGui.PopID()
                CImGui.SameLine()
            end
            CImGui.NewLine()
            CImGui.Text("Pressed $pressed_count times.")
        end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Combo")
        # expose flags as checkbox for the demo
        flags = @cstatic flags=Cuint(0) begin
            @c CImGui.CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, CImGui.ImGuiComboFlags_PopupAlignLeft)
            if @c(CImGui.CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, CImGui.ImGuiComboFlags_NoArrowButton)) != 0
                flags &= ~CImGui.ImGuiComboFlags_NoPreview # clear the other flag, as we cannot combine both
            end
            if @c(CImGui.CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, CImGui.ImGuiComboFlags_NoPreview)) != 0
                flags &= ~CImGui.ImGuiComboFlags_NoArrowButton # clear the other flag, as we cannot combine both
            end
        end

        # general BeginCombo() API, you have full control over your selection data and display type.
        # (your selection data could be an index, a pointer to the object, an id for the object, a flag stored in the object itself, etc.)
        items = ["AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO"]
        @cstatic item_current="AAAA" begin
            # here our selection is a single pointer stored outside the object.
            if CImGui.BeginCombo("combo 1", item_current, flags) # the second parameter is the label previewed before opening the combo.
                for n = 0:length(items)-1
                    is_selected = item_current == items[n+1]
                    CImGui.Selectable(items[n+1], is_selected) && (item_current = items[n+1];)
                    is_selected && CImGui.SetItemDefaultFocus() # set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
                end
                CImGui.EndCombo()
            end
        end

        # simplified one-liner Combo() API, using values packed in a single constant string
        @cstatic item_current_2=Cint(0) begin
            @c CImGui.Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0")
        end

        # simplified one-liner Combo() using an array of const char*
        @cstatic item_current_3=Cint(-1) begin
            # if the selection isn't within 0..count, Combo won't display a preview
            @c CImGui.Combo("combo 3 (array)", &item_current_3, items, length(items))
        end

        # simplified one-liner Combo() using an accessor function
        # function ItemGetter(data::Ptr{Ptr{Cchar}}, idx::Cint, out_str::Ptr{Ptr{Cchar}})::Bool
        #     unsafe_store!(out_str, unsafe_load(data, idx+1), idx+1)  # FIXME
        #     return true
        # end
        # FuncHolder = @cfunction($ItemGetter, Bool, (Ptr{Ptr{Cchar}},Cint,Ptr{Ptr{Cchar}}))
        # @cstatic item_current_4=Cint(0) begin
        #     @c CImGui.Combo("combo 4 (function)", &item_current_4, FuncHolder, items, length(items))
        # end

        CImGui.TreePop()
    end

    if CImGui.TreeNode("Selectables")
        # Selectable() has 2 overloads:
        # - The one taking "bool selected" as a read-only selection information. When Selectable() has been clicked is returns true and you can alter selection state accordingly.
        # - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
        #  The earlier is more flexible, as in real application your selection may be stored in a different manner (in flags within objects, as an external list, etc).
        if CImGui.TreeNode("Basic")
            @cstatic selection = [false, true, false, false, false] begin
                CImGui.Selectable("1. I am selectable", pointer(selection))
                CImGui.Selectable("2. I am selectable", pointer(selection)+sizeof(Bool))
                CImGui.Text("3. I am not selectable")
                CImGui.Selectable("4. I am selectable", pointer(selection)+3*sizeof(Bool))
                if CImGui.Selectable("5. I am double clickable", pointer(selection)+4*sizeof(Bool), CImGui.ImGuiSelectableFlags_AllowDoubleClick)
                    CImGui.IsMouseDoubleClicked(0) && (selection[5] = !selection[5];)
                end
            end
            CImGui.TreePop()
        end
        if CImGui.TreeNode("Selection State: Single Selection")
            @cstatic selected = Cint(-1) begin
                for n = 0:4
                    buf = @sprintf "Object %d" n
                    CImGui.Selectable(buf, selected == n) && (selected = n;)
                end
            end
            CImGui.TreePop()
        end
        if CImGui.TreeNode("Selection State: Multiple Selection")
            ShowHelpMarker("Hold CTRL and click to select multiple items.")
            @cstatic selection=[false, false, false, false, false] begin
                for n = 0:4
                    buf = @sprintf "Object %d" n
                    if CImGui.Selectable(buf, selection[n+1])
                        # clear selection when CTRL is not held
                        !CImGui.GetIO().KeyCtrl && fill!(selection, false)
                        selection[n+1] ⊻= 1
                    end
                end
            end
            CImGui.TreePop()
        end
        if CImGui.TreeNode("Rendering more text into the same line")
            # using the Selectable() override that takes "bool* p_selected" parameter and toggle your booleans automatically.
            @cstatic selected=[false, false, false] begin
                CImGui.Selectable("main.c", pointer(selected))
                CImGui.SameLine(300)
                CImGui.Text(" 2,345 bytes")
                CImGui.Selectable("Hello.cpp", pointer(selected)+sizeof(Bool))
                CImGui.SameLine(300)
                CImGui.Text("12,345 bytes")
                CImGui.Selectable("Hello.h", pointer(selected)+2sizeof(Bool))
                CImGui.SameLine(300)
                CImGui.Text(" 2,345 bytes")
            end
            CImGui.TreePop()
        end
        if CImGui.TreeNode("In columns")
            CImGui.Columns(3, C_NULL, false)
            @cstatic selected=[false for i = 1:16] begin
                for i = 0:16-1
                    label = @sprintf("Item %d", i)
                    CImGui.Selectable(label, pointer(selected)+i*sizeof(Bool)) && @info "Trigger | find me here: $(@__FILE__) at line $(@__LINE__)"
                    CImGui.NextColumn()
                end
                CImGui.Columns(1)
            end
            CImGui.TreePop()
        end
        if CImGui.TreeNode("Grid")
            @cstatic selected=[true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true] begin
                for i = 0:4*4-1
                    CImGui.PushID(i)
                    if CImGui.Selectable("Sailor", pointer(selected)+i*sizeof(Bool), 0, ImVec2(50,50))
                        # note: We _unnecessarily_ test for both x/y and i here only to silence some static analyzer. The second part of each test is unnecessary.
                        x = i % 4
                        y = i / 4
                        x > 0 && (selected[i] ⊻= 1;)
                        (x < 3 && i < 15) && (selected[i + 2] ⊻= 1;)
                        (y > 0 && i > 3) && (selected[i - 3] ⊻= 1;)
                        (y < 3 && i < 12) && (selected[i + 5] ⊻= 1;)
                    end
                    (i % 4) < 3 && CImGui.SameLine()
                    CImGui.PopID()
                end
            end
            CImGui.TreePop()
        end
        if CImGui.TreeNode("Alignment")
            ShowHelpMarker("Alignment applies when a selectable is larger than its text content.\nBy default, Selectables uses style.SelectableTextAlign but it can be overriden on a per-item basis using PushStyleVar().");
            @cstatic selected=[true, false, true, false, true, false, true, false, true] begin
                for y = 0:3-1
                    for x = 0:3-1
                        alignment = ImVec2(x / 2.0, y / 2.0)
                        name = @sprintf("(%.1f,%.1f)", alignment.x, alignment.y)
                        x > 0 && CImGui.SameLine()
                        CImGui.PushStyleVar(CImGui.ImGuiStyleVar_SelectableTextAlign, alignment)
                        CImGui.Selectable(name, pointer(selected)+(3*y+x)*sizeof(Bool), CImGui.ImGuiSelectableFlags_None, ImVec2(80,80))
                        CImGui.PopStyleVar()
                    end
                end
            end
            CImGui.TreePop()
        end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Filtered Text Input")
        @cstatic buf1="\0"^64 CImGui.InputText("default", buf1, 64)
        @cstatic buf2="\0"^64 CImGui.InputText("decimal", buf2, 64, CImGui.ImGuiInputTextFlags_CharsDecimal)
        @cstatic buf3="\0"^64 CImGui.InputText("hexadecimal", buf3, 64, CImGui.ImGuiInputTextFlags_CharsHexadecimal | CImGui.ImGuiInputTextFlags_CharsUppercase)
        @cstatic buf4="\0"^64 CImGui.InputText("uppercase", buf4, 64, CImGui.ImGuiInputTextFlags_CharsUppercase)
        @cstatic buf5="\0"^64 CImGui.InputText("no blank", buf5, 64, CImGui.ImGuiInputTextFlags_CharsNoBlank)

        # FIXME
        # function FilterImGuiLetters(Ptr{CImGui.ImGuiInputTextCallbackData} data)::Cint
        #     if data->EventChar < 256 && strchr("imgui", (char)data->EventChar)
        #         return 0
        #     else
        #         return 1
        #     end
        # end
        # funcptr = @cfunction($FilterImGuiLetters, Cvoid, (Ptr{CImGui.ImGuiInputTextCallbackData},))
        # @cstatic buf6="\0"^64 CImGui.InputText("\"imgui\" letters", buf6, 64, CImGui.ImGuiInputTextFlags_CallbackCharFilter, funcptr)

        CImGui.Text("Password input")
        @cstatic bufpass="password123"*"\0"^53 begin
            CImGui.InputText("password", bufpass, 64, CImGui.ImGuiInputTextFlags_Password | CImGui.ImGuiInputTextFlags_CharsNoBlank)
            CImGui.SameLine()
            ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n")
            CImGui.InputText("password (clear)", bufpass, 64, CImGui.ImGuiInputTextFlags_CharsNoBlank)
        end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Multi-line Text Input")
        # note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize
        # and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.
        @cstatic read_only=false (text="/*\n"*
                                       " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"*
                                       " more formally, the invalid operand with locked CMPXCHG8B\n"*
                                       " instruction bug, is a design flaw in the majority of\n"*
                                       " Intel Pentium, Pentium MMX, and Pentium OverDrive\n"*
                                       " processors (all in the P5 microarchitecture).\n"*
                                       "*/\n\n"*
                                       "label:\n"*
                                       "\tlock cmpxchg8b eax\n"*"\0"^(1024*16-249)) begin
            ShowHelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp)")
            @c CImGui.Checkbox("Read-only", &read_only)
            flags = CImGui.ImGuiInputTextFlags_AllowTabInput | (read_only ? CImGui.ImGuiInputTextFlags_ReadOnly : 0)
            CImGui.InputTextMultiline("##source", text, length(text), ImVec2(-1.0, CImGui.GetTextLineHeight() * 16), flags)
            CImGui.TreePop()
        end
    end

    if CImGui.TreeNode("Plots Widgets")
        animate, _ = @cstatic animate=true arr=Cfloat[0.6, 0.1, 1.0, 0.5, 0.92, 0.1, 0.2] begin
            @c CImGui.Checkbox("Animate", &animate)
            CImGui.PlotLines("Frame Times", arr, length(arr))
            # create a dummy array of contiguous float values to plot
            # Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter.
            @cstatic values=fill(Cfloat(0),90) values_offset=Cint(0) refresh_time=Cdouble(0) begin
                (!animate || refresh_time == 0.0) && (refresh_time = CImGui.GetTime();)

                while refresh_time < CImGui.GetTime() # create dummy data at fixed 60 hz rate for the demo
                    @cstatic phase=Cfloat(0) begin
                        values[values_offset+1] = cos(phase)
                        values_offset = (values_offset+1) % length(values)
                        phase += 0.10*values_offset
                        refresh_time += 1.0/60.0
                    end
                end
                CImGui.PlotLines("Lines", values, length(values), values_offset, "avg 0.0", -1.0, 1.0, (0,80))
                CImGui.PlotHistogram("Histogram", arr, length(arr), 0, C_NULL, 0.0, 1.0, (0,80))
            end
        end # @cstatic
        # use functions to generate output
        # FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count.
        Sin(::Ptr{Cvoid}, i::Cint) = Cfloat(sin(i * 0.1))
        Saw(::Ptr{Cvoid}, i::Cint) = Cfloat((i & 1) != 0 ? 1.0 : -1.0)
        Sin_ptr = @cfunction($Sin, Cfloat, (Ptr{Cvoid}, Cint))
        Saw_ptr = @cfunction($Saw, Cfloat, (Ptr{Cvoid}, Cint))

        @cstatic func_type=Cint(0) display_count=Cint(70) begin
            CImGui.Separator()
            CImGui.PushItemWidth(100)
            @c CImGui.Combo("func", &func_type, "Sin\0Saw\0")
            CImGui.PopItemWidth()
            CImGui.SameLine()
            @c CImGui.SliderInt("Sample count", &display_count, 1, 400)
            func = func_type == 0 ? Sin_ptr : Saw_ptr
            CImGui.PlotLines("Lines", func, C_NULL, display_count, 0, C_NULL, -1.0, 1.0, (0,80))
            CImGui.PlotHistogram("Histogram", func, C_NULL, display_count, 0, C_NULL, -1.0, 1.0, (0,80))
            CImGui.Separator()
        end

        # animate a simple progress bar
        @cstatic progress=Cfloat(0) progress_dir=Cfloat(1) begin
            if animate
                progress += progress_dir * 0.4 * unsafe_load(CImGui.GetIO().DeltaTime)
                progress ≥ 1.1 && (progress = 1.1; progress_dir *= -1.0;)
                progress ≤ -0.1 && (progress = -0.1; progress_dir *= -1.0;)
            end

            # typically we would use ImVec2(-1.0,0.0) to use all available width, or ImVec2(width,0.0) for a specified width. ImVec2(0.0,0.0) uses ItemWidth.
            CImGui.ProgressBar(progress, ImVec2(0.0,0.0))
            CImGui.SameLine(0.0, CImGui.GetStyle().ItemInnerSpacing.x)
            CImGui.Text("Progress Bar")

            progress_saturated = (progress < 0.0) ? 0.0 : (progress > 1.0) ? 1.0 : progress
            buf = @sprintf("%d/%d", progress_saturated*1753, 1753)
            CImGui.ProgressBar(progress, ImVec2(0,0), buf)
        end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Color/Picker Widgets")
        @cstatic color=Cfloat[114/255, 144/255, 154/255, 200/255] backup_color=Cfloat[0,0,0,0] saved_palette_init=true saved_palette=fill(ImVec4(0,0,0,0), 32) alpha_preview=true alpha_half_preview=false drag_and_drop=true options_menu=true hdr=false begin
            @c CImGui.Checkbox("With Alpha Preview", &alpha_preview)
            @c CImGui.Checkbox("With Half Alpha Preview", &alpha_half_preview)
            @c CImGui.Checkbox("With Drag and Drop", &drag_and_drop)
            @c CImGui.Checkbox("With Options Menu", &options_menu)
            CImGui.SameLine()
            ShowHelpMarker("Right-click on the individual color widget to show options.")
            @c CImGui.Checkbox("With HDR", &hdr)
            CImGui.SameLine()
            ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets.")
            misc_flags = (hdr ? CImGui.ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : CImGui.ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? CImGui.ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? CImGui.ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : CImGui.ImGuiColorEditFlags_NoOptions)

            CImGui.Text("Color widget:")
            CImGui.SameLine()
            ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n")
            CImGui.ColorEdit3("MyColor##1", color, misc_flags)

            CImGui.Text("Color widget HSV with Alpha:")
            CImGui.ColorEdit4("MyColor##2", color, CImGui.ImGuiColorEditFlags_DisplayHSV | misc_flags)

            CImGui.Text("Color widget with Float Display:")
            CImGui.ColorEdit4("MyColor##2f", color, CImGui.ImGuiColorEditFlags_Float | misc_flags)

            CImGui.Text("Color button with Picker:")
            CImGui.SameLine()
            ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup.")
            CImGui.ColorEdit4("MyColor##3", color, CImGui.ImGuiColorEditFlags_NoInputs | CImGui.ImGuiColorEditFlags_NoLabel | misc_flags)
            CImGui.Text("Color button with Custom Picker Popup:")

            # generate a dummy default palette. The palette will persist and can be edited.
            if saved_palette_init
                for n = 0:length(saved_palette)-1
                    tmp = saved_palette[n+1]
                    x, y, z = tmp.x, tmp.y, tmp.z
                    @c CImGui.ColorConvertHSVtoRGB(n/31, 0.8, 0.8, &x, &y, &z)
                    tmp = saved_palette[n+1]
                    saved_palette[n+1] = ImVec4(x, y, z, 1.0) # alpha
                end
                saved_palette_init = false
            end

            open_popup = CImGui.ColorButton("MyColor##3b", ImVec4(color...), misc_flags)
            CImGui.SameLine()
            open_popup |= CImGui.Button("Palette")
            if open_popup
                CImGui.OpenPopup("mypicker")
                backup_color = color
            end

            if CImGui.BeginPopup("mypicker")
                CImGui.Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!")
                CImGui.Separator()
                CImGui.ColorPicker4("##picker", color, misc_flags | CImGui.ImGuiColorEditFlags_NoSidePreview | CImGui.ImGuiColorEditFlags_NoSmallPreview)
                CImGui.SameLine()

                CImGui.BeginGroup() # Lock X position
                CImGui.Text("Current")
                CImGui.ColorButton("##current", ImVec4(color...), CImGui.ImGuiColorEditFlags_NoPicker | CImGui.ImGuiColorEditFlags_AlphaPreviewHalf, (60,40))
                CImGui.Text("Previous")
                CImGui.ColorButton("##previous", ImVec4(backup_color...), CImGui.ImGuiColorEditFlags_NoPicker | CImGui.ImGuiColorEditFlags_AlphaPreviewHalf, (60,40)) && (color = backup_color;)
                CImGui.Separator()
                CImGui.Text("Palette")
                for n = 0:length(saved_palette)-1
                    CImGui.PushID(n)
                    (n % 8) != 0 && CImGui.SameLine(0.0, CImGui.GetStyle().ItemSpacing.y)
                    if CImGui.ColorButton("##palette", saved_palette[n+1], CImGui.ImGuiColorEditFlags_NoAlpha | CImGui.ImGuiColorEditFlags_NoPicker | CImGui.ImGuiColorEditFlags_NoTooltip, (20,20))
                        tmp = saved_palette[n+1]
                        x, y, z = tmp.z, tmp.y, tmp.z
                        color = [x, y, z, color[4]] # preserve alpha!
                    end

                    # allow user to drop colors into each palette entry
                    # (Note that ColorButton is already a drag source by default, unless using ImGuiColorEditFlags_NoDragDrop)
                    if CImGui.BeginDragDropTarget()
                        payload = CImGui.AcceptDragDropPayload(CImGui.IMGUI_PAYLOAD_TYPE_COLOR_3F)
                        if payload != C_NULL
                            ptr = CImGui.Get(payload, :Data)
                            x = unsafe_load(Ptr{Cfloat}(ptr), 1)
                            y = unsafe_load(Ptr{Cfloat}(ptr), 2)
                            z = unsafe_load(Ptr{Cfloat}(ptr), 3)
                            w = saved_palette[n].w
                            saved_palette[n+1] = ImVec4(x,y,z,w)
                        end
                        payload = CImGui.AcceptDragDropPayload(CImGui.IMGUI_PAYLOAD_TYPE_COLOR_4F)
                        if payload != C_NULL
                            ptr = CImGui.Get(payload, :Data)
                            x = unsafe_load(Ptr{Cfloat}(ptr), 1)
                            y = unsafe_load(Ptr{Cfloat}(ptr), 2)
                            z = unsafe_load(Ptr{Cfloat}(ptr), 3)
                            w = unsafe_load(Ptr{Cfloat}(ptr), 4)
                            saved_palette[n+1] = ImVec4(x,y,z,w)
                        end
                        CImGui.EndDragDropTarget()
                    end
                    CImGui.PopID()
                end
                CImGui.EndGroup()
                CImGui.EndPopup()
            end

            CImGui.Text("Color button only:")
            CImGui.ColorButton("MyColor##3c", ImVec4(color...), misc_flags, (80,80))

            CImGui.Text("Color picker:")
            @cstatic alpha=true alpha_bar=true side_preview=true ref_color=true ref_color_v=Cfloat[1.0,0.0,1.0,0.5] inputs_mode=Cint(2) picker_mode=Cint(0) begin
                @c CImGui.Checkbox("With Alpha", &alpha)
                @c CImGui.Checkbox("With Alpha Bar", &alpha_bar)
                @c CImGui.Checkbox("With Side Preview", &side_preview)
                if side_preview
                    CImGui.SameLine()
                    @c CImGui.Checkbox("With Ref Color", &ref_color)
                    if ref_color
                        CImGui.SameLine()
                        CImGui.ColorEdit4("##RefColor", pointer(ref_color_v), CImGui.ImGuiColorEditFlags_NoInputs | misc_flags)
                    end
                end
                @c CImGui.Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0");
                @c CImGui.Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0");
                CImGui.SameLine()
                ShowHelpMarker("User can right-click the picker to change mode.")
                flags = misc_flags
                !alpha && (flags |= CImGui.ImGuiColorEditFlags_NoAlpha;) # this is by default if you call ColorPicker3() instead of ColorPicker4()
                alpha_bar && (flags |= CImGui.ImGuiColorEditFlags_AlphaBar;)
                !side_preview && (flags |= CImGui.ImGuiColorEditFlags_NoSidePreview;)
                picker_mode == 1 && (flags |= CImGui.ImGuiColorEditFlags_PickerHueBar;)
                picker_mode == 2 && (flags |= CImGui.ImGuiColorEditFlags_PickerHueWheel;)
                inputs_mode == 1 && (flags |= CImGui.ImGuiColorEditFlags_NoInputs;)
                inputs_mode == 2 && (flags |= CImGui.ImGuiColorEditFlags_DisplayRGB;)
                inputs_mode == 3 && (flags |= CImGui.ImGuiColorEditFlags_DisplayHSV;)
                inputs_mode == 4 && (flags |= CImGui.ImGuiColorEditFlags_DisplayHex;)
                CImGui.ColorPicker4("MyColor##4", color, flags, ref_color ? pointer(ref_color_v) : C_NULL)
            end
            CImGui.Text("Programmatically set defaults:")
            CImGui.SameLine()
            ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible.")
            if CImGui.Button("Default: Uint8 + HSV + Hue Bar")
                CImGui.SetColorEditOptions(CImGui.ImGuiColorEditFlags_Uint8 | CImGui.ImGuiColorEditFlags_DisplayHSV | CImGui.ImGuiColorEditFlags_PickerHueBar)
            end
            if CImGui.Button("Default: Float + HDR + Hue Wheel")
                CImGui.SetColorEditOptions(CImGui.ImGuiColorEditFlags_Float | CImGui.ImGuiColorEditFlags_HDR | CImGui.ImGuiColorEditFlags_PickerHueWheel)
            end
        end # @cstatic
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Range Widgets")
        @cstatic _begin=Cfloat(10) _end=Cfloat(90) begin_i=Cint(100) end_i=Cint(1000) begin
            @c CImGui.DragFloatRange2("range", &_begin, &_end, 0.25, 0.0, 100.0, "Min: %.1f %%", "Max: %.1f %%")
            @c CImGui.DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units")
        end
        CImGui.TreePop()
    end

    # FIXME
    # if CImGui.TreeNode("Data Types")
        # The DragScalar/InputScalar/SliderScalar functions allow various data types: signed/unsigned int/long long and float/double
        # To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type,
        # and passing all arguments by address.
        # This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types.
        # In practice, if you frequently use a given type that is not covered by the normal API entry points, you can wrap it
        # yourself inside a 1 line function which can take typed argument as value instead of void*, and then pass their address
        # to the generic function. For example:
        #   bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
        #   {
        #      return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
        #   }

        # Limits (as helper variables that we can take the address of)
        # Note that the SliderScalar function has a maximum usable range of half the natural type maximum, hence the /2 below.
        # s32_zero = ImS32(0); s32_one = ImS32(1); s32_fifty = ImS32(50); s32_min = ImS32(typemin(Cint)/2);  s32_max = ImS32(1073741824);   s32_hi_a = ImS32(1073741724);   s32_hi_b = ImS32(1073741824)
        # u32_zero = ImU32(0); u32_one = ImU32(1); u32_fifty = ImU32(50); u32_min = ImU32(0);                u32_max = ImU32(0x7fffffff);  u32_hi_a = ImU32(0x7fffffff - 100);  u32_hi_b = ImU32(0x7fffffff)
        # s64_zero = ImS64(0); s64_one = ImS64(1); s64_fifty = ImS64(50); s64_min = ImS64(typemin(Int64)/2); s64_max = ImS64(typemax(Int64)/2);  s64_hi_a = ImS64(typemax(Int64)/2 - 100);  s64_hi_b = ImS64(typemax(Int64)/2)
        # u64_zero = ImU64(0); u64_one = ImU64(1); u64_fifty = ImU64(50); u64_min = ImU64(0);                u64_max = ImU64(typemax(UInt64)/2); u64_hi_a = ImU64(typemax(UInt64)/2 - 100); u64_hi_b = ImU64(typemax(UInt64)/2)
        # f32_zero = Cfloat(0); f32_one = Cfloat(1); f32_lo_a = Cfloat(-10000000000.0); f32_hi_a = Cfloat(10000000000.0);
        # f64_zero = Cdouble(0); f64_one = Cdouble(1); f64_lo_a = Cdouble(-1000000000000000.0); f64_hi_a = Cdouble(1000000000000000.0);

        # State
        # drag_speed = Cfloat(0.2)
        # @cstatic s32_v=ImS32(-1) u32_v=ImU32(0xffffffff) s64_v=ImS64(-1) u64_v=ImU64(0xffffffffffffffff) f32_v=Cfloat(0.123) f64_v=Cdouble(90000.01234567890123456789) drag_clamp=false begin
        #     CImGui.Text("Drags:")
        #     @c CImGui.Checkbox("Clamp integers to 0..50", &drag_clamp)
        #     CImGui.SameLine()
        #     ShowHelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value.")
        #     if drag_clamp
        #         @c CImGui.DragScalar("drag s32", CImGui.ImGuiDataType_S32, &s32_v, drag_speed, &s32_zero, &s32_fifty)
        #         @c CImGui.DragScalar("drag u32", CImGui.ImGuiDataType_U32, &u32_v, drag_speed, &u32_zero, &u32_fifty, "%u ms")
        #         @c CImGui.DragScalar("drag s64", CImGui.ImGuiDataType_S64, &s64_v, drag_speed, &s64_zero, &s64_fifty)
        #         @c CImGui.DragScalar("drag u64", CImGui.ImGuiDataType_U64, &u64_v, drag_speed, &u64_zero, &u64_fifty)
        #     else
        #         @c CImGui.DragScalar("drag s32", CImGui.ImGuiDataType_S32, &s32_v, drag_speed, C_NULL, C_NULL)
        #         @c CImGui.DragScalar("drag u32", CImGui.ImGuiDataType_U32, &u32_v, drag_speed, C_NULL, C_NULL, "%u ms")
        #         @c CImGui.DragScalar("drag s64", CImGui.ImGuiDataType_S64, &s64_v, drag_speed, C_NULL, C_NULL)
        #         @c CImGui.DragScalar("drag u64", CImGui.ImGuiDataType_U64, &u64_v, drag_speed, C_NULL, C_NULL)
        #     end
        #     @c CImGui.DragScalar("drag float", CImGui.ImGuiDataType_Float, &f32_v, 0.005, &f32_zero, &f32_one, "%f", 1.0)
        #     @c CImGui.DragScalar("drag float ^2", CImGui.ImGuiDataType_Float, &f32_v, 0.005, &f32_zero, &f32_one, "%f", 2.0)
        #     CImGui.SameLine()
        #     ShowHelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range.")
        #     @c CImGui.DragScalar("drag double", CImGui.ImGuiDataType_Double, &f64_v, 0.0005, &f64_zero, C_NULL, "%.10f grams", 1.0)
        #     @c CImGui.DragScalar("drag double ^2", CImGui.ImGuiDataType_Double, &f64_v, 0.0005, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0)
        #
        #     CImGui.Text("Sliders")
        #     @c CImGui.SliderScalar("slider s32 low",     CImGui.ImGuiDataType_S32,    &s32_v, &s32_zero, &s32_fifty,"%d")
        #     @c CImGui.SliderScalar("slider s32 high",    CImGui.ImGuiDataType_S32,    &s32_v, &s32_hi_a, &s32_hi_b, "%d")
        #     @c CImGui.SliderScalar("slider s32 full",    CImGui.ImGuiDataType_S32,    &s32_v, &s32_min,  &s32_max,  "%d")
        #     @c CImGui.SliderScalar("slider u32 low",     CImGui.ImGuiDataType_U32,    &u32_v, &u32_zero, &u32_fifty,"%u")
        #     @c CImGui.SliderScalar("slider u32 high",    CImGui.ImGuiDataType_U32,    &u32_v, &u32_hi_a, &u32_hi_b, "%u")
        #     @c CImGui.SliderScalar("slider u32 full",    CImGui.ImGuiDataType_U32,    &u32_v, &u32_min,  &u32_max,  "%u")
        #     @c CImGui.SliderScalar("slider s64 low",     CImGui.ImGuiDataType_S64,    &s64_v, &s64_zero, &s64_fifty,"%I64d")
        #     @c CImGui.SliderScalar("slider s64 high",    CImGui.ImGuiDataType_S64,    &s64_v, &s64_hi_a, &s64_hi_b, "%I64d")
        #     @c CImGui.SliderScalar("slider s64 full",    CImGui.ImGuiDataType_S64,    &s64_v, &s64_min,  &s64_max,  "%I64d")
        #     @c CImGui.SliderScalar("slider u64 low",     CImGui.ImGuiDataType_U64,    &u64_v, &u64_zero, &u64_fifty,"%I64u ms")
        #     @c CImGui.SliderScalar("slider u64 high",    CImGui.ImGuiDataType_U64,    &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms")
        #     @c CImGui.SliderScalar("slider u64 full",    CImGui.ImGuiDataType_U64,    &u64_v, &u64_min,  &u64_max,  "%I64u ms")
        #     @c CImGui.SliderScalar("slider float low",   CImGui.ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one)
        #     @c CImGui.SliderScalar("slider float low^2", CImGui.ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one,  "%.10f", 2.0)
        #     @c CImGui.SliderScalar("slider float high",  CImGui.ImGuiDataType_Float,  &f32_v, &f32_lo_a, &f32_hi_a, "%e")
        #     @c CImGui.SliderScalar("slider double low",  CImGui.ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  "%.10f grams", 1.0)
        #     @c CImGui.SliderScalar("slider double low^2",CImGui.ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  "%.10f", 2.0)
        #     @c CImGui.SliderScalar("slider double high", CImGui.ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams", 1.0)
        #
        #     @cstatic inputs_step=true begin
        #         CImGui.Text("Inputs")
        #         @c CImGui.Checkbox("Show step buttons", &inputs_step)
        #         if inputs_step
        #             @c CImGui.InputScalar("input s32",     CImGui.ImGuiDataType_S32,    &s32_v, &s32_one, C_NULL, "%d")
        #             @c CImGui.InputScalar("input s32 hex", CImGui.ImGuiDataType_S32,    &s32_v, &s32_one, C_NULL, C_NULL, "%08X", CImGui.ImGuiInputTextFlags_CharsHexadecimal)
        #             @c CImGui.InputScalar("input u32",     CImGui.ImGuiDataType_U32,    &u32_v, &u32_one, C_NULL, C_NULL, "%u")
        #             @c CImGui.InputScalar("input u32 hex", CImGui.ImGuiDataType_U32,    &u32_v, &u32_one, C_NULL, C_NULL, "%08X", CImGui.ImGuiInputTextFlags_CharsHexadecimal)
        #             @c CImGui.InputScalar("input s64",     CImGui.ImGuiDataType_S64,    &s64_v, &s64_one, C_NULL)
        #             @c CImGui.InputScalar("input u64",     CImGui.ImGuiDataType_U64,    &u64_v, &u64_one, C_NULL)
        #             @c CImGui.InputScalar("input float",   CImGui.ImGuiDataType_Float,  &f32_v, &f32_one, C_NULL)
        #             @c CImGui.InputScalar("input double",  CImGui.ImGuiDataType_Double, &f64_v, &f64_one, C_NULL)
        #         else
        #             @c CImGui.InputScalar("input s32",     CImGui.ImGuiDataType_S32,    &s32_v, &s32_one, C_NULL, C_NULL, "%d")
        #             @c CImGui.InputScalar("input s32 hex", CImGui.ImGuiDataType_S32,    &s32_v, &s32_one, C_NULL, C_NULL, "%08X", CImGui.ImGuiInputTextFlags_CharsHexadecimal)
        #             @c CImGui.InputScalar("input u32",     CImGui.ImGuiDataType_U32,    &u32_v, &u32_one, C_NULL, C_NULL, "%u")
        #             @c CImGui.InputScalar("input u32 hex", CImGui.ImGuiDataType_U32,    &u32_v, &u32_one, C_NULL, C_NULL, "%08X", CImGui.ImGuiInputTextFlags_CharsHexadecimal)
        #             @c CImGui.InputScalar("input s64",     CImGui.ImGuiDataType_S64,    &s64_v, &s64_one, C_NULL)
        #             @c CImGui.InputScalar("input u64",     CImGui.ImGuiDataType_U64,    &u64_v, &u64_one, C_NULL)
        #             @c CImGui.InputScalar("input float",   CImGui.ImGuiDataType_Float,  &f32_v, &f32_one, C_NULL)
        #             @c CImGui.InputScalar("input double",  CImGui.ImGuiDataType_Double, &f64_v, &f64_one, C_NULL)
        #         end
        #     end
        # end # @cstatic
        # CImGui.TreePop()
    # end

    if CImGui.TreeNode("Multi-component Widgets")
        @cstatic vec4f=Cfloat[0.10, 0.20, 0.30, 0.44] vec4i=Cint[1, 5, 100, 255] begin
            CImGui.InputFloat2("input float2", vec4f)
            CImGui.DragFloat2("drag float2", vec4f, 0.01, 0.0, 1.0)
            CImGui.SliderFloat2("slider float2", vec4f, 0.0, 1.0)
            CImGui.InputInt2("input int2", vec4i)
            CImGui.DragInt2("drag int2", vec4i, 1, 0, 255)
            CImGui.SliderInt2("slider int2", vec4i, 0, 255)
            CImGui.Spacing()

            CImGui.InputFloat3("input float3", vec4f)
            CImGui.DragFloat3("drag float3", vec4f, 0.01, 0.0, 1.0)
            CImGui.SliderFloat3("slider float3", vec4f, 0.0, 1.0)
            CImGui.InputInt3("input int3", vec4i)
            CImGui.DragInt3("drag int3", vec4i, 1, 0, 255)
            CImGui.SliderInt3("slider int3", vec4i, 0, 255)
            CImGui.Spacing()

            CImGui.InputFloat4("input float4", vec4f)
            CImGui.DragFloat4("drag float4", vec4f, 0.01, 0.0, 1.0)
            CImGui.SliderFloat4("slider float4", vec4f, 0.0, 1.0)
            CImGui.InputInt4("input int4", vec4i)
            CImGui.DragInt4("drag int4", vec4i, 1, 0, 255)
            CImGui.SliderInt4("slider int4", vec4i, 0, 255)
        end
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Vertical Sliders")
        spacing = 4
        CImGui.PushStyleVar(CImGui.ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing))

        @cstatic int_value=Cint(0) begin
            @c CImGui.VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5)
            CImGui.SameLine()
        end

        @cstatic values=Cfloat[0.0, 0.60, 0.35, 0.9, 0.70, 0.20, 0.0] values2=Cfloat[0.20, 0.80, 0.40, 0.25] begin
            CImGui.PushID("set1")
            for i = 0:7-1
                i > 0 && CImGui.SameLine()
                CImGui.PushID(i)
                CImGui.PushStyleColor(CImGui.ImGuiCol_FrameBg, CImGui.HSV(i/7.0, 0.5, 0.5))
                CImGui.PushStyleColor(CImGui.ImGuiCol_FrameBgHovered, CImGui.HSV(i/7.0, 0.6, 0.5))
                CImGui.PushStyleColor(CImGui.ImGuiCol_FrameBgActive, CImGui.HSV(i/7.0, 0.7, 0.5))
                CImGui.PushStyleColor(CImGui.ImGuiCol_SliderGrab, CImGui.HSV(i/7.0, 0.9, 0.9))
                CImGui.VSliderFloat("##v", (18,160), pointer(values)+i*sizeof(Cfloat), 0.0, 1.0, "")
                if CImGui.IsItemActive() || CImGui.IsItemHovered()
                    CImGui.SetTooltip(@sprintf("%.3f", values[i+1]))
                end
                CImGui.PopStyleColor(4)
                CImGui.PopID()
            end
            CImGui.PopID()

            CImGui.SameLine()
            CImGui.PushID("set2")

            rows = Cint(3)
            small_slider_size = ImVec2(18, (160.0-(rows-1)*spacing)/rows)
            for nx = 0:4-1
                nx > 0 && CImGui.SameLine()
                CImGui.BeginGroup()
                for ny = 0:rows-1
                    CImGui.PushID(nx*rows+ny)
                    CImGui.VSliderFloat("##v", small_slider_size, pointer(values2)+nx*sizeof(Cfloat), 0.0, 1.0, "")
                    if CImGui.IsItemActive() || CImGui.IsItemHovered()
                        CImGui.SetTooltip(@sprintf("%.3f", values2[nx+1]))
                    end
                    CImGui.PopID()
                end
                CImGui.EndGroup()
            end
            CImGui.PopID()

            CImGui.SameLine()
            CImGui.PushID("set3")
            for i = 0:4-1
                i > 0 && CImGui.SameLine()
                CImGui.PushID(i)
                CImGui.PushStyleVar(CImGui.ImGuiStyleVar_GrabMinSize, 40)
                CImGui.VSliderFloat("##v", ImVec2(40,160), pointer(values)+i*sizeof(Cfloat), 0.0, 1.0, "%.2f\nsec")
                CImGui.PopStyleVar()
                CImGui.PopID()
            end
            CImGui.PopID()
            CImGui.PopStyleVar()
        end # @cstatic
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Drag and Drop")
        # ColorEdit widgets automatically act as drag source and drag target.
        # They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F to allow your own widgets
        # to use colors in their drag and drop interaction. Also see the demo in Color Picker -> Palette demo.
        CImGui.BulletText("Drag and drop in standard widgets")
        CImGui.Indent()
        @cstatic col1=Cfloat[1.0,0.0,0.2] col2=Cfloat[0.4,0.7,0.0,0.5] begin
            CImGui.ColorEdit3("color 1", col1)
            CImGui.ColorEdit4("color 2", col2)
            CImGui.Unindent()
        end

        @cstatic mode=Cint(0) names=["Bobby", "Beatrice", "Betty", "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn"] begin
            CImGui.BulletText("Drag and drop to copy/swap items")
            CImGui.Indent()
            Mode_Copy, Mode_Move, Mode_Swap = 0, 1, 2
            CImGui.RadioButton("Copy", mode == Mode_Copy) && (mode = Mode_Copy;)
            CImGui.SameLine()
            CImGui.RadioButton("Move", mode == Mode_Move) && (mode = Mode_Move;)
            CImGui.SameLine()
            CImGui.RadioButton("Swap", mode == Mode_Swap) && (mode = Mode_Swap;)
            for n = 0:length(names)-1
                CImGui.PushID(n)
                (n % 3) != 0 && CImGui.SameLine()
                CImGui.Button(names[n+1], (60,60))

                # our buttons are both drag sources and drag targets here!
                if CImGui.BeginDragDropSource(CImGui.ImGuiDragDropFlags_None)
                    @c CImGui.SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(Cint)) # set payload to carry the index of our item (could be anything)
                    mode == Mode_Copy && CImGui.Text("Copy $(names[n+1])") # display preview (could be anything, e.g. when dragging an image we could decide to display the filename and a small preview of the image, etc.)
                    mode == Mode_Move && CImGui.Text("Move $(names[n+1])")
                    mode == Mode_Swap && CImGui.Text("Swap $(names[n+1])")
                    CImGui.EndDragDropSource()
                end
                if CImGui.BeginDragDropTarget()
                    payload = CImGui.AcceptDragDropPayload("DND_DEMO_CELL")
                    if payload != C_NULL
                        @assert CImGui.Get(payload, :DataSize) == sizeof(Cint)
                        payload_n = unsafe_load(Ptr{Cint}(CImGui.Get(payload, :Data)))
                        if mode == Mode_Copy
                            names[n+1] = names[payload_n+1]
                        end
                        if mode == Mode_Move
                            names[n+1] = names[payload_n+1]
                            names[payload_n+1] = ""
                        end
                        if mode == Mode_Swap
                            tmp = names[n+1]
                            names[n+1] = names[payload_n+1]
                            names[payload_n+1] = tmp
                        end
                    end
                    CImGui.EndDragDropTarget()
                end
                CImGui.PopID()
            end
            CImGui.Unindent()
        end # @cstatic
        CImGui.TreePop()
    end

    if CImGui.TreeNode("Querying Status (Active/Focused/Hovered etc.)")
        # Display the value of IsItemHovered() and other common item state functions. Note that the flags can be combined.
        # (because BulletText is an item itself and that would affect the output of IsItemHovered() we pass all state in a single call to simplify the code).
        @cstatic item_type=Cint(1) b=false col4f=Cfloat[1.0, 0.5, 0.0, 1.0] begin
            @c CImGui.RadioButton("Text", &item_type, 0)
            @c CImGui.RadioButton("Button", &item_type, 1)
            @c CImGui.RadioButton("Checkbox", &item_type, 2)
            @c CImGui.RadioButton("SliderFloat", &item_type, 3)
            @c CImGui.RadioButton("ColorEdit4", &item_type, 4)
            @c CImGui.RadioButton("ListBox", &item_type, 5)
            @c CImGui.Separator()
            ret = false;
            item_type == 0 && CImGui.Text("ITEM: Text") # testing text items with no identifier/interaction
            item_type == 1 && (ret = CImGui.Button("ITEM: Button");) # testing button
            item_type == 2 && (ret = @c CImGui.Checkbox("ITEM: Checkbox", &b);) # testing checkbox
            item_type == 3 && (ret = CImGui.SliderFloat("ITEM: SliderFloat", pointer(col4f), 0.0, 1.0);) # testing basic item
            item_type == 4 && (ret = CImGui.ColorEdit4("ITEM: ColorEdit4", col4f);) # testing multi-component items (IsItemXXX flags are reported merged)
            @cstatic current=Cint(1) begin
                if item_type == 5
                    items = ["Apple", "Banana", "Cherry", "Kiwi"]
                    ret = @c CImGui.ListBox("ITEM: ListBox", &current, items, length(items), length(items))
                end
            end
            CImGui.BulletText(
                "Return value = $ret\n"*
                "IsItemFocused() = $(CImGui.IsItemFocused())\n"*
                "IsItemHovered() = $(CImGui.IsItemHovered())\n"*
                "IsItemHovered(_AllowWhenBlockedByPopup) = $(CImGui.IsItemHovered(CImGui.ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n"*
                "IsItemHovered(_AllowWhenBlockedByActiveItem) = $(CImGui.IsItemHovered(CImGui.ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n"*
                "IsItemHovered(_AllowWhenOverlapped) = $(CImGui.IsItemHovered(CImGui.ImGuiHoveredFlags_AllowWhenOverlapped))\n"*
                "IsItemHovered(_RectOnly) = $(CImGui.IsItemHovered(CImGui.ImGuiHoveredFlags_RectOnly))\n"*
                "IsItemActive() = $(CImGui.IsItemActive())\n"*
                "IsItemEdited() = $(CImGui.IsItemEdited())\n"*
                "IsItemActivated() = $(CImGui.IsItemActivated())\n"*
                "IsItemDeactivated() = $(CImGui.IsItemDeactivated())\n"*
                "IsItemDeactivatedEdit() = $(CImGui.IsItemDeactivatedAfterEdit())\n"*
                "IsItemVisible() = $(CImGui.IsItemVisible())\n"*
                "GetItemRectMin() = ($(CImGui.GetItemRectMin().x), $(CImGui.GetItemRectMin().y))\n"*
                "GetItemRectMax() = ($(CImGui.GetItemRectMax().x), $(CImGui.GetItemRectMax().y))\n"*
                "GetItemRectSize() = ($(CImGui.GetItemRectSize().x), $(CImGui.GetItemRectSize().y))")
        end

        @cstatic embed_all_inside_a_child_window=false begin
            @c CImGui.Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window)
            embed_all_inside_a_child_window && CImGui.BeginChild("outer_child", ImVec2(0, CImGui.GetFontSize() * 20), true)

            # testing IsWindowFocused() function with its various flags. Note that the flags can be combined.
            CImGui.BulletText(
                "IsWindowFocused() = $(CImGui.IsWindowFocused())\n"*
                "IsWindowFocused(_ChildWindows) = $(CImGui.IsWindowFocused(CImGui.ImGuiFocusedFlags_ChildWindows))\n"*
                "IsWindowFocused(_ChildWindows|_RootWindow) = $(CImGui.IsWindowFocused(CImGui.ImGuiFocusedFlags_ChildWindows | CImGui.ImGuiFocusedFlags_RootWindow))\n"*
                "IsWindowFocused(_RootWindow) = $(CImGui.IsWindowFocused(CImGui.ImGuiFocusedFlags_RootWindow))\n"*
                "IsWindowFocused(_AnyWindow) = $(CImGui.IsWindowFocused(CImGui.ImGuiFocusedFlags_AnyWindow))\n")

            # testing IsWindowHovered() function with its various flags. Note that the flags can be combined.
            CImGui.BulletText(
                "IsWindowHovered() = $(CImGui.IsWindowHovered())\n"*
                "IsWindowHovered(_AllowWhenBlockedByPopup) = $(CImGui.IsWindowHovered(CImGui.ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n"*
                "IsWindowHovered(_AllowWhenBlockedByActiveItem) = $(CImGui.IsWindowHovered(CImGui.ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n"*
                "IsWindowHovered(_ChildWindows) = $(CImGui.IsWindowHovered(CImGui.ImGuiHoveredFlags_ChildWindows))\n"*
                "IsWindowHovered(_ChildWindows|_RootWindow) = $(CImGui.IsWindowHovered(CImGui.ImGuiHoveredFlags_ChildWindows | CImGui.ImGuiHoveredFlags_RootWindow))\n"*
                "IsWindowHovered(_RootWindow) = $(CImGui.IsWindowHovered(CImGui.ImGuiHoveredFlags_RootWindow))\n"*
                "IsWindowHovered(_AnyWindow) = $(CImGui.IsWindowHovered(CImGui.ImGuiHoveredFlags_AnyWindow))\n")

            CImGui.BeginChild("child", (0, 50), true)
            CImGui.Text("This is another child window for testing the _ChildWindows flag.")
            CImGui.EndChild()
            embed_all_inside_a_child_window && CImGui.EndChild()
        end

        # calling IsItemHovered() after begin returns the hovered status of the title bar.
        # this is useful in particular if you want to create a context menu (with BeginPopupContextItem) associated to the title bar of a window.
        @cstatic test_window=false begin
            @c CImGui.Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window)
            if test_window
                @c CImGui.Begin("Title bar Hovered/Active tests", &test_window)
                if CImGui.BeginPopupContextItem() # <-- this is using IsItemHovered()
                    CImGui.MenuItem("Close") && (test_window = false;)
                    CImGui.EndPopup()
                end
                CImGui.Text("IsItemHovered() after begin = $(CImGui.IsItemHovered()) (== is title bar hovered)\n"*
                            "IsItemActive() after begin = $(CImGui.IsItemActive()) (== is window being clicked/moved)\n")
                CImGui.End()
            end
        end
        CImGui.TreePop()
    end
end


text:
using Plots, UnPack
using MOONS.Grids
using MOONS.Fields
FT = Float64
a, b, n = FT(0.0), FT(1.0), 50
β = FT(1.01)
g = Cube(a, b, n; warpfun=Roberts_both, args=(β,))

cent = CellCenter(g)

@inbounds for local_grid in GridField(g,cent)
    @unpack ijk,x,y,z = local_grid
    cent[ijk] = sin(3*π*x)*sin(3*π*y)*sin(3*π*z)
end

@unpack xn, yn, zn, xc, yc, zc = all_coords(g)

plot(yc,zc, midslice(cent, 1), seriestype=:wireframe)
savefig("wireframe.svg")
contourf(yc,zc, midslice(cent, 1), c = :viridis)
savefig("contourf.svg")

# Extrap

FT = Float64
a, b, n = FT(0.0), FT(1.0), 20
g = Cube(a, b, n)
@unpack xn, yn, zn, xc, yc, zc = all_coords(g)

cent = CellCenter(g)
face = CellFace(g, 1)
corn = CellCorner(g)
edge = CellEdge(g, 1)
@inbounds for local_grid in GridField(g,cent)
    cent[local_grid.ijk] = cos(3*π*local_grid.x)
end
@inbounds for local_grid in GridField(g,face)
    face[local_grid.ijk] = cos(3*π*local_grid.x)
end
@inbounds for local_grid in GridField(g,corn)
    corn[local_grid.ijk] = cos(3*π*local_grid.x)
end
@inbounds for local_grid in GridField(g,edge)
    edge[local_grid.ijk] = cos(3*π*local_grid.x)
end
extrap!(face, cent, g, 1)
extrap!(edge, corn, g, 1)
plot(xn[1:4], midline(face, 1)[1:4], label="face (extrapolated)")
plot!(xc[1:4], midline(cent, 1)[1:4], label="cent")
savefig("extrap_c2v.svg")
plot(xc[1:4], midline(edge, 1)[1:4], label="edge (extrapolated)")
plot!(xn[1:4], midline(corn, 1)[1:4], label="corn")
savefig("extrap_v2c.svg")

FT = Float64
ax, bx = FT(0.0), FT(1.0)
ay, by = FT(0.0), FT(2.0)
n = 2 ^ 5
g = Grid(
    (Coordinates(ax,bx,n),
     Coordinates(ay,by,n),
     Coordinates(-FT(0.5),FT(0.5), 1))
    )

f = CellCorner(g)
∇f = CellEdges(g)
∇f_exact = CellEdges(g)

@inbounds for local_grid in GridField(g,f)
    @unpack ijk,x,y = local_grid
    f[ijk] = sin(3*π*x)*sin(3*π*y)
end
@inbounds for local_grid in GridField(g,∇f_exact[1])
    @unpack ijk,x,y = local_grid
    ∇f_exact[1][ijk] = 3*π*cos(3*π*x)*sin(3*π*y)
end
@inbounds for local_grid in GridField(g,∇f_exact[2])
    @unpack ijk,x,y = local_grid
    ∇f_exact[2][ijk] = 3*π*sin(3*π*x)*cos(3*π*y)
end
∇!(∇f, f, g)

@unpack xc, yc, zc, xn, yn, zn = all_coords(g)

contourf(xn,yn, midslice(f, 3)', c = :viridis, xlabel="x", ylabel="y")
savefig("f.svg")

contourf(xc,yn, midslice(∇f[1], 3)', c = :viridis, xlabel="x", ylabel="y")
savefig("∇f_1.svg")
contourf(xn,yc, midslice(∇f[2], 3)', c = :viridis, xlabel="x", ylabel="y")
savefig("∇f_2.svg")

contourf(xc,yn, midslice(∇f_exact[1], 3)', c = :viridis, xlabel="x", ylabel="y")
savefig("∇f_1_exact.svg")
contourf(xn,yc, midslice(∇f_exact[2], 3)', c = :viridis, xlabel="x", ylabel="y")
savefig("∇f_2_exact.svg")

ext: jl


text:
struct CompleteGraph{T<:Integer} <: LG.AbstractGraph{T}
    nv::T
end

LG.eltype(::CompleteGraph{T}) where {T} = T
LG.edgetype(::CompleteGraph{T}) where {T} = LG.Edge{T}
LG.is_directed(::Type{<:CompleteGraph}) = false
LG.nv(g::CompleteGraph) = g.nv
LG.ne(g::CompleteGraph) = div(nv(g) * (nv(g)-1), 2)
LG.vertices(g::CompleteGraph) = 1:LG.nv(g)

LG.edges(g::CompleteGraph) = [Edge(i, j) for i in 1:nv(g)-1 for j in i+1:nv(g)]

function LG.outneighbors(g::CompleteGraph, v)
    return [v1 for v1 in vertices(g) if v1 != v]
end

LG.inneighbors(g::CompleteGraph, v) = outneighbors(g, v)

LG.has_vertex(g::CompleteGraph, v) = 1 <= v <= LG.nv(g)

function LG.has_edge(g::CompleteGraph, v1, v2)
    if !has_vertex(g, v1) || !has_vertex(g, v2)
        return false
    end
    return v1 != v2
end

# =======================================================
#         overrides
# =======================================================

# we use this check so that we have the same convention as in LightGraphs
LG.is_connected(g::CompleteGraph) = nv(g) > 0

function LG.connected_components(g::CompleteGraph)

    nv(g) == 0 && return typeof(vertices(g))[]
    return [vertices(g)]
end

# has_self_loops is defined in terms of this
LG.num_self_loops(::CompleteGraph) = 0

LG.is_bipartite(g::CompleteGraph) = (nv(g) <= 2)

function LG.squash(g::CompleteGraph)
    nvg = nv(g)
    for T ∈ (UInt8, UInt16, UInt32, UInt64)
        nv(g) < typemax(T) && return CompleteGraph(T(nvg))
    end
end

# ---- degree -----------------------------------

function LG.Δ(g::CompleteGraph)
    nvg = nv(g)
    return ifelse(nvg == 0, typemin(Int), Int(nvg) - 1)
end

LG.Δout(g::CompleteGraph) = Δ(g)
LG.Δin(g::CompleteGraph) = Δ(g)

function LG.δ(g::CompleteGraph)
    nvg = nv(g)
    return ifelse(nvg == 0, typemax(Int), Int(nvg) - 1)
end

LG.δout(g::CompleteGraph) = δ(g)
LG.δin(g::CompleteGraph) = δ(g)

LG.degree(g::CompleteGraph, vs::AbstractVector=vertices(g)) = Fill(Δ(g), length(vs))

LG.indegree(g::CompleteGraph, vs::AbstractVector=vertices(g)) = degree(g, vs)
LG.outdegree(g::CompleteGraph, vs::AbstractVector=vertices(g)) = degree(g, vs)


text:
module WiringDiagrams

using Reexport

include("Directed.jl")
include("Undirected.jl")
include("MonoidalDirected.jl")
include("MonoidalUndirected.jl")
include("Algorithms.jl")
include("Expressions.jl")
include("Serialization.jl")
include("GraphML.jl")
include("JSON.jl")

@reexport using .DirectedWiringDiagrams
@reexport using .UndirectedWiringDiagrams
@reexport using .MonoidalDirectedWiringDiagrams
@reexport using .MonoidalUndirectedWiringDiagrams
@reexport using .WiringDiagramAlgorithms
@reexport using .WiringDiagramExpressions

using .WiringDiagramSerialization
export convert_from_graph_data, convert_to_graph_data
@reexport using .GraphMLWiringDiagrams
@reexport using .JSONWiringDiagrams

end


text:
using ConvDiffMIPDECO
using Test
using jInv.Mesh

domain = [0. 3. 0 1.]
n      = [60 20].-1
M      = getRegularMesh(domain,n)
@time Mass, Mass_const, SM = getFEMMatrices2D(M)

f = getFEMsource2D(M)
v = Mass_const*f
u = SM\v

max_stars_repo_name: JuliaInv/ConvDiffMIPDECO

max_issues_repo_name: JuliaInv/ConvDiffMIPDECO

max_forks_repo_name: JuliaInv/ConvDiffMIPDECO


text:
using InteractiveUtils
using InteractiveUtils: typesof
using Core: Typeof
import Base: copy!
import Base.Broadcast: broadcasted, materialize!

mutable struct Context <: AContext
  cache::Union{IdDict{Any,Any},Nothing}
end

Context() = Context(nothing)

cache(cx::Context) = cx.cache === nothing ? (cx.cache = IdDict()) : cx.cache

struct Pullback{S,T}
  t::T
end

Pullback{S}(x) where S = Pullback{S,typeof(x)}(x)

struct CompileError
  T
  e
end

function Base.showerror(io::IO, e::CompileError)
  print(io, "Compiling $(e.T): ")
  showerror(io, e.e)
end

# interface2.jl

# Wrappers
_pullback(f, args...) = _pullback(Context(), f, args...)

tailmemaybe(::Nothing) = nothing
tailmemaybe(x::Tuple) = Base.tail(x)

function pullback(f, args...)
  y, back = _pullback(f, args...)
  y, Δ -> tailmemaybe(back(Δ))
end

sensitivity(y::Number) = one(y)
sensitivity(y::Complex) = error("Output is complex, so the gradient is not defined.")
sensitivity(y::AbstractArray) = error("Output is an array, so the gradient is not defined. Perhaps you wanted jacobian.")
sensitivity(y) = error("Output should be scalar; gradients are not defined for output $(repr(y))")

"""
    gradient(f, args...)

Returns a tuple containing `∂f/∂x` for each argument `x`,
the derivative (for scalar `x`) or the gradient.

`f(args...)` must be a real number, see [`jacobian`](@ref) for array output.

See also [`withgradient`](@ref) to keep the value `f(args...)`,
and [`pullback`](@ref) for value and back-propagator.

```jldoctest; setup=:(using Zygote)
julia> gradient(*, 2.0, 3.0, 5.0)
(15.0, 10.0, 6.0)

julia> gradient(x -> sum(abs2,x), [7.0, 11.0, 13.0])
([14.0, 22.0, 26.0],)

julia> gradient([7, 11], 0, 1) do x, y, d
         p = size(x, d)
         sum(x.^p .+ y)
       end
([14.0, 22.0], 2.0, nothing)
```
"""
function gradient(f, args...)
  y, back = pullback(f, args...)
  grad = back(sensitivity(y))
  isnothing(grad) ? nothing : map(_project, args, grad)
end

# Base.adjoint(f::Function) = x -> gradient(f, x)[1]  # piracy!
Base.adjoint(f::Function) = x -> begin  # still piracy! avoids projection for legacy reasons
  y, back = pullback(f, x)
  back(sensitivity(y))[1]
end

"""
    withgradient(f, args...)
    withgradient(f, ::Params)

Returns both the value of the function and the [`gradient`](@ref),
as a named tuple.

```jldoctest; setup=:(using Zygote)
julia> y, ∇ = withgradient(/, 1, 2)
(val = 0.5, grad = (0.5, -0.25))

julia> ∇ == gradient(/, 1, 2)
true
```
"""
function withgradient(f, args...)
  y, back = pullback(f, args...)
  grad = back(sensitivity(y))
  results = isnothing(grad) ? map(_ -> nothing, args) : map(_project, args, grad)
  (val=y, grad=results)
end

# Param-style wrappers

"""
    gradient(() -> loss(), ps::Params) -> Grads

Gradient with implicit parameters. Takes a zero-argument function,
and returns a dictionary-like container, whose keys are arrays `x in ps`.

```jldoctest; setup=:(using Zygote)
julia> x = [1 2 3; 4 5 6]; y = [7, 8]; z = [1, 10, 100];

julia> g = gradient(Params([x, y])) do
         sum(x .* y .* z')
       end
Grads(...)

julia> g[x]
2×3 Matrix{Float64}:
 7.0  70.0  700.0
 8.0  80.0  800.0

julia> haskey(g, z)  # only x and y are parameters
false
```
"""
gradient

"""
    Params([A, B])

Container for implicit parameters, used when differentiating
a zero-argument funtion `() -> loss(A, B)` with respect to `A, B`.
"""
struct Params
  order::Buffer # {Any, Vector{Any}}
  params::IdSet{Any} # TODO store ids only
end

Params() = Params(Buffer([], false), IdSet())
Params(xs) = Params(Buffer(xs, false), IdSet(xs))
Params(ps::Params) = ps
Params(xs::Tuple) = Params(collect(xs))

@forward Params.order Base.iterate, Base.length, Base.getindex
@forward Params.params Base.in

Base.map(::typeof(_project), args::Tuple{Params}, grad) = grad  # skip _project in gradient(f, ::Params)

function Base.union!(ps::Params, itrs...)
  foreach(itr -> foreach(x -> push!(ps, x), itr), itrs)
  return ps
end

Base.copy(ps::Params) = union!(Params(), ps)
Base.union(ps::Params, itrs...) = union!(copy(ps), itrs...)
Base.issetequal(ps1::Params, ps2::Params) = issetequal(ps1.params, ps2.params)
Base.issetequal(ps1::Params, x::Base.AbstractSet) = issetequal(ps1.params, x)
Base.issetequal(x::Base.AbstractSet, ps1::Params) = issetequal(x, ps1.params)

function Base.intersect!(ps::Params, itrs...)
  for itr in itrs
    for x in collect(ps)
      x ∉ itr && delete!(ps, x)
    end
  end
  return ps
end

Base.intersect(ps::Params, itrs...) = intersect!(copy(ps), itrs...)

function Base.push!(ps::Params, x)
  if !(x in ps.params)
    push!(ps.order, x)
    push!(ps.params, x)
  end
  return ps
end

Base.push!(ps::Params, x...) = (foreach(x -> push!(ps, x), x); ps)

function Base.delete!(ps::Params, x)
  if x in ps.params
    delete!(ps.params, x)
    i = findfirst(y -> y === x, ps.order)
    deleteat!(ps.order, i)
  end
  return ps
end

Base.Broadcast.broadcasted(f, ps::Params) = broadcasted(f, ps.order)

@adjoint function Broadcast.broadcasted(f::Function, ps::Params)
  f.(ps), _ -> throw(ArgumentError("Zygote.Params does not support broadcasting within gradients, try iteration `for p in ps`"))
end

Base.:(==)(x::Params, y::Params) = x.order.data == y.order.data

function Base.show(io::IO, ps::Params)
  print(io, "Params([")
  join(io, ps.order, ", ")
  print(io, "])")
end

"""
    copy!(ps::Params, x::AbstractVector)
    copy!(x::AbstractVector, ps::Params)

Copies the content of array `x` into the parameters `ps` or viceversa.
The length of `x` has to be equal to the sum of the lengths
of all parameters.
"""
function copy!(ps::Params, x::AbstractVector)
  @assert length(x) == sum(length(p) for p in ps)
  i = 0
  for p in ps
    p .= reshape(x[i+1:i+length(p)], size(p))
    i += length(p)
  end
  ps
end

function copy!(x::AbstractVector, ps::Params)
  @assert length(x) == sum(length(p) for p in ps)
  i = 0
  for p in ps
    x[i+1:i+length(p)] .= vec(p)
    i += length(p)
  end
  ps
end

"""
    Grads(...)

Dictionary-like container returned when taking gradients with
respect to implicit parameters. For an array `W`, appearing
within `Params([W, A, B...])`, the gradient is `g[W]`.
"""
struct Grads
  grads::IdDict{Any,Any}
  params::Params
end

Base.show(io::IO, ps::Grads) = print(io, "Grads(...)")

@forward Grads.grads  Base.setindex!
@forward Grads.params  Base.length

const ADictOrGrads = Union{AbstractDict, Grads}

# Dictionary interface.
# Don't use the IdDict directly since it may contain some spurious pairs.
Base.haskey(gs::Grads, x) = x ∈ gs.params
Base.keys(gs::Grads) = gs.params
Base.values(gs::Grads) = (gs.grads[p] for p in gs.params)

function Base.iterate(gs::Grads, state...)
  res = iterate(gs.params, state...)
  isnothing(res) && return nothing
  p, next_state = res
  return gs[p], next_state
end

function Base.getindex(gs::Grads, x)
  isbits(x) && error("Only reference types can be differentiated with `Params`.")
  return gs.grads[x]
end

"""
    copy!(gs::Grads, x::AbstractVector)
    copy!(x::AbstractVector, gs::Grads)

Copies the content of array `x` into the gradient object `gs` or vice versa. The
length of `x` has to be equal to the sum of the lengths of all gradients.
"""
function copy!(gs::Grads, x::AbstractVector)
  i = 0
  for p in gs.params
    gs[p] .= reshape(x[i+1:i+length(p)], size(p))
    i += length(p)
  end
  x
end

function copy!(x::AbstractVector,  gs::Grads)
  i = 0
  for p in gs.params
    x[i+1:i+length(p)] .= vec(gs[p])
    i += length(p)
  end
  x
end

function Base.merge!(gs_dst::Grads, gs_srcs::Grads...)
  for gs_src in gs_srcs
    union!(gs_dst.params, gs_src.params)
    merge!(gs_dst.grads, gs_src.grads)
  end
  gs_dst
end

function Base.copy(gs::Grads)
  gs_new = Grads(IdDict(), gs.params)
  merge!(gs_new, gs)
end

broadcasted(f, gs::Grads, gss::ADictOrGrads...) = map(f, gs, gss...)

broadcasted(f, a::Numeric, gs::Grads) = map(x -> f(a, x), gs)
broadcasted(f, gs::Grads, a::Numeric) = map(x -> f(x, a), gs)

function materialize!(gs1::Grads, gs2::Grads)
  issetequal(gs1.params, gs2.params) ||
    throw(ArgumentError("Expected Grads objects with the same Params."))
  for p in gs1.params
    gs1[p] = gs2[p]
  end
  return gs1
end

function Base.map(f, gs1::Grads, gss::ADictOrGrads...)
  gsout = Grads(IdDict{Any,Any}(), Params(gs1.params))
  return map!(f, gsout, gs1, gss...)
end

function Base.map!(f, gsout::Grads, gss::ADictOrGrads...)
  all(issetequal(gsout.params, keys(gs)) for gs in gss) ||
    throw(ArgumentError("map! expects Grads objects with the same Params."))
  for p in gsout.params
    gsout[p] = f((_getformap(gs, p) for gs in gss)...)
  end
  return gsout
end

function _getformap(gs, p)
  g = gs[p]
  isnothing(g) ? fill!(similar(p), 0) : g
end

function pullback(f, ps::Params)
  cx = Context()
  y, back = _pullback(cx, f)
  y, function (Δ)
    for p in ps
      cache(cx)[p] = nothing
    end
    back(Δ)
    Grads(cx.cache, ps) # TODO make a copy
  end
end

# Code Reflection

function code_ir(f, T)
  m = meta(Tuple{Typeof(f),T.parameters...})
  return IR(m)
end

function code_irm(ex)
  isexpr(ex, :call) || error("@code_ir f(args...)")
  f, args = ex.args[1], ex.args[2:end]
  :(code_ir($(esc(f)), typesof($(esc.(args)...))))
end

macro code_ir(ex)
  code_irm(ex)
end

macro code_adjoint(ex)
  :(Adjoint($(code_irm(ex)), varargs = varargs($(esc(:($InteractiveUtils.@which $ex))), length(($(esc.(ex.args)...),)))))
end


text:
using Clang.Generators
using Clp_jll

cd(@__DIR__)

clp_include_dir = joinpath(Clp_jll.artifact_dir, "include") |> normpath

coin_include_dir = joinpath(Clp_jll.CoinUtils_jll.artifact_dir, "include", "coin") |>normpath

options = load_options(joinpath(@__DIR__, "generate.toml"))

args = get_default_args()
push!(args, "-I$clp_include_dir", "-I$coin_include_dir")

headers = [
    joinpath(clp_include_dir, "coin", "Clp_C_Interface.h")
    joinpath(coin_include_dir, "Coin_C_defines.h")
]

ctx = create_context(headers, args, options)

build!(ctx)


text:
export nn_lattice!

@inline @inbounds function nn_loop_if!(r,  nn_arr,  i::Integer,  j::Integer, rb, ss)
    mul!(rb, U_sa, r)
    ss .= @. Integer(round(rb./g))
    sint = Integer(ss[1] * 9 + ss[2] * 3 + ss[3])
    if sint == 4
        nn_arr[i,  1] = j
    elseif sint == 2
        nn_arr[i,  2] = j
    elseif sint == 10
        nn_arr[i,  3] = j
    elseif sint == -8
        nn_arr[i,  4] = j
    elseif sint == 12
        nn_arr[i,  5] = j
    elseif sint == 6
        nn_arr[i,  6] = j
    elseif sint == -4
        nn_arr[i,  7] = j
    elseif sint == -2
        nn_arr[i,  8] = j
    elseif sint == -10
        nn_arr[i,  9] = j
    elseif sint == 8
        nn_arr[i,  10] = j
    elseif sint == -12
        nn_arr[i,  11] = j
    elseif sint == -6
        nn_arr[i,  12] = j
    end
end

@inline @inbounds function nn_loop!(x,  nn_arr, temp_vec, r, rmi, rb, s)
    for i in 1:N
        for j in 1:N
            @views temp_vec .= (x[j+2,  :] .- x[i+2,  :])./cell
            temp_vec .= temp_vec .- floor.(temp_vec .+ 1.0/2)
            r .= temp_vec .* cell
            r_dot = norm(r)
            if r_dot <= (g*1.01)
                nn_loop_if!(r,  nn_arr,   i,  j, rb, s)
            end
        end
    end
end

@inline function nn_lattice!(s::Simulation)
    temp_vec = zeros(Float64, 3)
    r = zeros(Float64, 3)
    rmi = 0.0
    rb = zeros(Float64, 3)
    ss = zeros(Float64, 3)
    nn_arr = nn_loop!(s.x, s.nn_arr, temp_vec, r, rmi, rb, ss)

end


text:
struct BSONError
    _wrap_::Vector{UInt8}

    BSONError() = new(alloc_buf(512))
end

export BSONError

const domainDescs = Dict{Int64,AbstractString}(1 => "JSON", 2 => "READER")
const errorDescs = Dict{Int64,Any}(1 => (Dict{Int64,AbstractString}(
        1 => "READ_CORRUPT_JS",
        2 => "READ_INVALID_PARAM",
        3 => "READ_CB_FAILURE"
    )), 2 => Dict{Int64,AbstractString}(1 => "BADFD"))

function Base.convert(::Type{String}, bsonError::BSONError)
    uint32s = reinterpret(UInt32, bsonError._wrap_)
    domain = uint32s[1]
    code = uint32s[2]
    return String(bsonError._wrap_[9:end])
end

Base.string(bsonError::BSONError) = String(bsonError._wrap_)

Base.error(bsonError::BSONError) = error(string(bsonError))


text:
module Serd
export RDF, read_rdf_file, read_rdf_string, write_rdf, write_rdf_statement,
  rdf_writer

include("CSerd.jl")
include("RDF.jl")
using .CSerd
using .RDF

# Reader
########

""" Read RDF from file.
"""
function read_rdf_file(path::String; kw...)::Vector{Statement}
  stmts = Statement[]
  read_rdf_file(path; kw...) do stmt
    push!(stmts, stmt)
  end
  stmts
end

""" Read RDF from string.
"""
function read_rdf_string(text::String; kw...)::Vector{Statement}
  stmts = Statement[]
  read_rdf_string(text; kw...) do stmt
    push!(stmts, stmt)
  end
  stmts
end

""" Read RDF from file in SAX (event-driven) style.
"""
function read_rdf_file(handler::Function, path::String;
                       syntax::String="turtle")::Nothing
  reader = rdf_reader(handler, syntax)
  serd_reader_read_file(reader, path)
  serd_reader_free(reader)
end

""" Read RDF from string in SAX (event-driven) style.
"""
function read_rdf_string(handler::Function, text::String;
                         syntax::String="turtle")::Nothing
  reader = rdf_reader(handler, syntax)
  serd_reader_read_string(reader, text)
  serd_reader_free(reader)
end

""" Create RDF reader with given event handler.
"""
function rdf_reader(handler::Function, syntax::String)::SerdReader
  base_sink(uri::SerdNode) = handler(BaseURI(uri.value))
  prefix_sink(name::SerdNode, uri::SerdNode) = handler(Prefix(name.value, uri.value))
  statement_sink(stmt::SerdStatement) = handler(from_serd(stmt))
  end_sink(node::SerdNode) = nothing # FIXME: What do with this?

  serd_reader_new(serd_syntax(syntax), base_sink, prefix_sink,
                  statement_sink, end_sink)
end

# Writer
########

""" Write RDF to IO stream.
"""
function write_rdf(io::IO, stmts::Vector{<:Statement};
                   syntax::String="turtle")::Nothing
  writer = rdf_writer(syntax, io)
  for stmt in stmts
    write_rdf_statement(writer, stmt)
  end
  close(writer)
end
function write_rdf(stmts::Vector{<:Statement}; kw...)
  write_rdf(stdout, stmts; kw...)
end

""" Write a single RDF statement.
"""
function write_rdf_statement(writer::SerdWriter, stmt::Statement)
  serd_writer_write_statement(writer, to_serd(stmt))
end
function write_rdf_statement(writer::SerdWriter, stmt::BaseURI)
  uri = SerdNode(stmt.uri, SERD_URI)
  serd_writer_set_base_uri(writer, uri)
end
function write_rdf_statement(writer::SerdWriter, stmt::Prefix)
  name = SerdNode(stmt.name, SERD_LITERAL)
  uri = SerdNode(stmt.uri, SERD_URI)
  serd_writer_set_prefix(writer, name, uri)
end

""" Create RDF writer with given IO stream.
"""
function rdf_writer(syntax::String, io::IO)::SerdWriter
  serd_writer_new(serd_syntax(syntax), SerdStyles(0), io)
end

# Constants
###########

# https://www.w3.org/TR/rdf11-concepts/#section-Datatypes
const NS_XSD = "http://www.w3.org/2001/XMLSchema#"
const XSD_BOOLEAN = "$(NS_XSD)boolean"
const XSD_INTEGER = "$(NS_XSD)integer"
const XSD_DECIMAL = "$(NS_XSD)decimal"
const XSD_INT = "$(NS_XSD)int"
const XSD_SHORT = "$(NS_XSD)short"
const XSD_LONG = "$(NS_XSD)long"
const XSD_FLOAT = "$(NS_XSD)float"
const XSD_DOUBLE = "$(NS_XSD)double"

rdf_datatype(::Type{Bool}) = XSD_BOOLEAN
rdf_datatype(::Type{Int16}) = XSD_SHORT
rdf_datatype(::Type{Int32}) = XSD_INT
rdf_datatype(::Type{Int64}) = XSD_INT # XXX: Technically, should be XSD_LONG
rdf_datatype(::Type{Float32}) = XSD_FLOAT
rdf_datatype(::Type{Float64}) = XSD_DOUBLE
rdf_datatype(::Type{T}) where T <: Integer = XSD_INTEGER
rdf_datatype(::Type{T}) where T <: Real = XSD_DECIMAL

const julia_datatypes = Dict{String,Type}(
  XSD_BOOLEAN => Bool,
  XSD_INTEGER => BigInt,
  XSD_DECIMAL => BigFloat,
  XSD_INT => Int, # or Int32, but better to use platform-specific Int type
  XSD_SHORT => Int16,
  XSD_LONG => Int64,
  XSD_FLOAT => Float32,
  XSD_DOUBLE => Float64,
)
julia_datatype(datatype::String) = julia_datatypes[datatype]

const serd_syntaxes = Dict{String,SerdSyntax}(
  "turtle"   => SERD_TURTLE,
  "ntriples" => SERD_NTRIPLES,
  "nquads"   => SERD_NQUADS,
  "trig"     => SERD_TRIG,
)
serd_syntax(syntax::String) = serd_syntaxes[lowercase(syntax)]

# Data types: Julia to C
########################

to_serd(node::ResourceURI) = SerdNode(node.uri, SERD_URI)
to_serd(node::ResourceCURIE) = SerdNode("$(node.prefix):$(node.name)", SERD_CURIE)
to_serd(node::Blank) = SerdNode(node.name, SERD_BLANK)
to_serd(stmt::Triple) = to_serd(nothing, stmt.subject, stmt.predicate, stmt.object)
to_serd(stmt::Quad) = to_serd(stmt.graph, stmt.subject, stmt.predicate, stmt.object)

function to_serd(graph::Union{<:Node,Nothing},
                 subject::Node, predicate::Node, object::Node)
  if !isnothing(graph)
    graph = to_serd(graph)
  end
  subject = to_serd(subject)
  predicate = to_serd(predicate)
  if isa(object, Literal)
    object_datatype = isa(object.value, AbstractString) ?
      nothing : SerdNode(rdf_datatype(typeof(object.value)), SERD_URI)
    object_lang = isempty(object.language) ?
      nothing : SerdNode(object.language, SERD_LITERAL)
    object = SerdNode(string(object.value), SERD_LITERAL)
  else
    object = to_serd(object)
    object_datatype = nothing
    object_lang = nothing
  end
  SerdStatement(0, graph, subject, predicate, object, object_datatype, object_lang)
end

# Data types: C to Julia
########################

function from_serd(node::SerdNode)::Node
  if node.typ == SERD_URI
    ResourceURI(node.value)
  elseif node.typ == SERD_CURIE
    prefix, name = split(node.value, ':', limit=2)
    ResourceCURIE(prefix, name)
  elseif node.typ == SERD_BLANK
    Blank(node.value)
  else
    error("Cannot convert SERD node of type $(node.typ)")
  end
end

function from_serd(stmt::SerdStatement)::Statement
  subject = from_serd(stmt.subject)
  predicate = from_serd(stmt.predicate)
  object = if stmt.object.typ == SERD_LITERAL
    if isnothing(stmt.object_datatype)
      if isnothing(stmt.object_lang)
        Literal(stmt.object.value)
      else
        Literal(stmt.object.value, stmt.object_lang.value)
      end
    else
      typ = julia_datatype(stmt.object_datatype.value)
      Literal(parse(typ, stmt.object.value))
    end
  else
    from_serd(stmt.object)
  end
  if isnothing(stmt.graph)
    Triple(subject, predicate, object)
  else
    Quad(subject, predicate, object, from_serd(stmt.graph))
  end
end

end


text:
export AR, Autoregressive, ARsafe, ARunsafe, ARMeta

import LazyArrays
import StatsFuns: log2π

struct AR end

const Autoregressive = AR

struct ARsafe end
struct ARunsafe end

struct ARMeta{F <: VariateForm, S}
    order :: Int
    stype :: S
end

function ARMeta(::Type{ Univariate }, order, stype::S) where S
    order === 1 || @warn "ARMeta{Univariate} has been created with order equals to $(order). Order has been forced to be equal to 1."
    return ARMeta{Univariate, S}(1, stype)
end

function ARMeta(::Type{Multivariate}, order, stype::S) where S
    return ARMeta{Multivariate, S}(order, stype)
end

getvform(meta::ARMeta{F}) where { F } = F
getorder(meta::ARMeta)                = meta.order
getstype(meta::ARMeta)                = meta.stype

is_multivariate(meta::ARMeta) = getvform(meta) === Multivariate
is_univariate(meta::ARMeta)   = getvform(meta) === Univariate

is_safe(meta::ARMeta)   = getstype(meta) === ARsafe()
is_unsafe(meta::ARMeta) = getstype(meta) === ARunsafe()

@node AR Stochastic [ y, x, θ, γ ]

default_meta(::Type{ AR }) = error("Autoregressive node requires meta flag explicitly specified")

@average_energy AR (q_y_x::MultivariateNormalDistributionsFamily, q_θ::NormalDistributionsFamily, q_γ::GammaShapeRate, meta::ARMeta) = begin
    mθ, Vθ   = mean_cov(q_θ)
    myx, Vyx = mean_cov(q_y_x)
    mγ       = mean(q_γ)

    order = getorder(meta)

    mx, Vx   = ar_slice(getvform(meta), myx, (order + 1):2order), ar_slice(getvform(meta), Vyx, (order + 1):2order, (order + 1):2order)
    my1, Vy1 = first(myx), first(Vyx)
    Vy1x     = ar_slice(getvform(meta), Vyx, 1, order+1:2order)

    # Euivalento to AE = (-mean(log, q_γ) + log2π + mγ*(Vy1+my1^2 - 2*mθ'*(Vy1x + mx*my1) + tr(Vθ*Vx) + mx'*Vθ*mx + mθ'*(Vx + mx*mx')*mθ)) / 2
    AE = (-mean(log, q_γ) + log2π + mγ * (Vy1 + my1^2 - 2*mθ'*(Vy1x + mx*my1) + mul_trace(Vθ, Vx) + dot(mx, Vθ, mx) + dot(mθ, Vx, mθ) + abs2(dot(mθ, mx)))) / 2

    # correction
    if is_multivariate(meta)
        AE += entropy(q_y_x)
        idc = LazyArrays.Vcat(1, (order + 1):2order)
        myx_n = view(myx, idc)
        Vyx_n = view(Vyx, idc, idc)
        q_y_x = MvNormalMeanCovariance(myx_n, Vyx_n)
        AE -= entropy(q_y_x)
    end

    return AE
end

# Helpers for AR rules

"""
    ar_slice(::T, array, ranges...)

Returns `array[ranges...]` in case if T is Multivariate, and `first(array[ranges...])` in case if T is Univariate
"""
function ar_slice end

ar_slice(::Type{Multivariate}, array, ranges...) = view(array, ranges...)
ar_slice(::Type{Univariate}, array, ranges...) = first(view(array, ranges...))

"""
    ar_unit(::T, order)

Returns `[ 1.0, 0.0 ... 0.0 ]` with length equal to order in case if T is Multivariate, and `1.0` in case if T is Univariate
"""
function ar_unit end

ar_unit(::Type{ V }, order) where { V <: VariateForm } = ar_unit(Float64, V, order)

function ar_unit(::Type{T}, ::Type{Multivariate}, order) where { T <: Real }
    return StandardBasisVector(order, 1, one(T))
    # c    = zeros(T, order)
    # c[1] = one(T)
    # return c
end

function ar_unit(::Type{T}, ::Type{Univariate}, order) where { T <: Real }
    return one(T)
end

## Allocation-free AR Precision Matrix

struct ARPrecisionMatrix{T} <: AbstractMatrix{T}
    order :: Int
    γ     :: T
end

Base.size(precision::ARPrecisionMatrix) = (precision.order, precision.order)
Base.getindex(precision::ARPrecisionMatrix, i::Int, j::Int) = (i === 1 && j === 1) ? precision.γ : ((i === j) ? convert(eltype(precision), huge) : zero(eltype(precision)))

Base.eltype(::Type{ <: ARPrecisionMatrix{T} }) where T = T
Base.eltype(::ARPrecisionMatrix{T})            where T = T

add_precision(matrix::AbstractMatrix, precision::ARPrecisionMatrix) = broadcast(+, matrix, precision)
add_precision(value::Real, precision::Real)                         = value + precision

add_precision!(matrix::AbstractMatrix, precision::ARPrecisionMatrix) = broadcast!(+, matrix, precision)
add_precision!(value::Real, precision::Real)                         = value + precision

function Base.broadcast!(::typeof(+), matrix::AbstractMatrix, precision::ARPrecisionMatrix)
    matrix[1, 1] += precision.γ
    for j in 2:first(size(matrix))
        matrix[j, j] += convert(eltype(precision), huge)
    end
    return matrix
end

ar_precision(::Type{Multivariate}, order, γ) = ARPrecisionMatrix(order, γ)
ar_precision(::Type{Univariate}, order, γ)   = γ

## Allocation-free AR Transition matrix

struct ARTransitionMatrix{T} <: AbstractMatrix{T}
    order :: Int
    inv_γ :: T

    function ARTransitionMatrix(order::Int, γ::T) where { T <: Real }
        return new{T}(order, inv(γ))
    end
end

Base.size(transition::ARTransitionMatrix) = (transition.order, transition.order)
Base.getindex(transition::ARTransitionMatrix, i::Int, j::Int) = (i === 1 && j === 1) ? transition.inv_γ : zero(eltype(transition))

Base.eltype(::Type{ <: ARTransitionMatrix{T} }) where T = T
Base.eltype(::ARTransitionMatrix{T})            where T = T

add_transition(matrix::AbstractMatrix, transition::ARTransitionMatrix) = broadcast(+, matrix, transition)
add_transition(value::Real, transition::Real)                          = value + transition

add_transition!(matrix::AbstractMatrix, transition::ARTransitionMatrix) = broadcast!(+, matrix, transition)
add_transition!(value::Real, transition::Real)                          = value + transition

function Base.broadcast!(::typeof(+), matrix::AbstractMatrix, transition::ARTransitionMatrix)
    matrix[1] += transition.inv_γ
    return matrix
end

ar_transition(::Type{ Multivariate }, order, γ) = ARTransitionMatrix(order, γ)
ar_transition(::Type{ Univariate }, order, γ)   = inv(γ)


text:
module SearchSortedNearest

export searchsortednearest, searchsortednext, searchsortedprevious

# from: https://discourse.julialang.org/t/findnearest-function/4143/5
"""
    searchsortednearest(a, x; by=<transform>, lt=<comparison>, distance=(a,b) -> abs(a-b), rev=false)

Find the index of (sorted) collection `a` that has the smallest `distance` to `x`.
Ties go to the smallest index.

# Examples

    using SearchSortedNearest

    searchsortednearest(1:10, 1.1) == 1
    searchsortednearest(1:10, 1.9) == 2
"""
function searchsortednearest(a, x; by=identity, lt=isless, rev=false, distance=(a,b)->abs(a-b))
    i = searchsortedfirst(a, x; by, lt, rev)
    if i == 1
    elseif i > length(a)
        i = length(a)
    elseif a[i] == x
    else
        i = lt(distance(by(a[i]), by(x)), distance(by(a[i - 1]), by(x))) ? i : i - 1
    end
    return i
end

function searchsortednext(a, x; by=identity, lt=isless, rev=false, distance=(a,b)->abs(a-b))
    i = searchsortedfirst(a, x; by, lt, rev)
    if i == 1
    elseif i > length(a)
        i = length(a)
    else
        i = i
    end
    return i
end

function searchsortedprevious(a, x; by=identity, lt=isless, rev=false, distance=(a,b)->abs(a-b))
    i = searchsortedfirst(a, x; by, lt, rev)
    if i == 1
    elseif i > length(a)
        i = length(a)
    else
        i = i - 1
    end
    return i
end

end