text:
#==
    Propagate positions
    g = Lark("""start: a
                    a: "a"
                 """, lexer="standard",parser="lalr",propagate_positions=true)

    r = Lerche.parse(g,"a")
    println("$(Lerche.meta(r.children[1]))")
    @test Lerche.meta(r.children[1]).line == 1

end
==#

#== The following tests copy those in lark.py with the following exceptions:
(1) No unicode tests (Julia is natively unicode)
(2) No CYK/Earley/Dynamic lexer/skipped tests (not implemented in Lerche)
(3)

==#

# This doesn't seem to work in Lark 0.7 either.

#==
@testset "Propagate positions" begin
    g = Lark("""start: a
                    a: "a"
                 """, lexer="standard",parser="lalr",propagate_positions=true)

    r = Lerche.parse(g,"a")
    @test_skip Lerche.meta(r.children[1]).line == 1

end
==#
@testset "Big list" begin
    g = "start: " * join(["\"$i\"" for i in 1:250])
    Lark(g,parser="lalr")
end

@testset "Infinite recursion" begin

    g = """start: a
           a: a | "a"
        """
    @test_throws Exception Lark(g,parser="lalr")
end

# For use in token transform test
# Two concrete types distinguished by whether or not
# to handle tokens
abstract type TVT <: Transformer end
struct TVTT <: TVT end
struct TVTF <: TVT end
@rule a(t::TVT,children) = children[1]*"!"
@terminal A(t::TVT,tok) = Lerche.update(tok,value=uppercase(tok.value))
Lerche.visit_tokens(t::TVTF) = false
Lerche.visit_tokens(t::TVTT) = true #default anyway

make_parser_test(lexer,parser) = begin
    make_lark(grammar;kwargs...) = begin
        Lark(grammar,lexer=lexer,parser=parser,propagate_positions=true;kwargs...)
    end

    @testset "Test expansions" begin
    #Test expand1
    g = Lark("""start: a
                    ?a: b
                    b: "x"
                 """,parser="lalr",lexer="standard",debug=true)

    r = Lerche.parse(g,"x")
    @test r.children[1].data == "b"

    g = Lark("""start: a
                    ?a: b -> c
                    b: "x"
                 """,parser="lalr",lexer="standard")

    r = Lerche.parse(g,"x")

    @test r.children[1].data == "c"

    g = Lark("""start: a
                        ?a: B -> c
                        B: "x"
                     """,parser="lalr",lexer="standard")
    r = Lerche.parse(g,"x")
    @test r.children[1].data == "c"

    g = Lark("""start: a
                    ?a: b b -> c
                    b: "x"
                 """,parser="lalr",lexer="standard")
    r = Lerche.parse(g,"xx")
    @test r.children[1].data == "c"

    end

    @testset "test comment in rule definition" begin
        g = Lark("""start: a
                a: "a"
                // A comment
                // Another comment
                | "b"
                // Still more
                c: "unrelated"
            """)
        r = Lerche.parse(g,"b")
        @test r.children[1].data == "a"
    end

    @testset "test visit tokens" begin

        g = """start: a
            a : A
            A: "x"
            """
        p = Lark(g, parser="lalr")
        tf = TVTF()
        r = transform(tf, Lerche.parse(p,"x"))
        @test r.children == ["x!"]
        tt = TVTT()
        r = transform(tt,Lerche.parse(p,"x"))
        @test r.children == ["X!"]

        # Test internal transformer
        p = Lark(g, parser="lalr", transformer=TVTT())
        r = Lerche.parse(p,"x")
        @test r.children == ["X!"]
    end

    @testset "Test basic 1 ($lexer, $parser)" begin
    g = make_lark("""start: a+ b a* "b" a*
                        b: "b"
                        a: "a"
                     """,debug=true)

    r = Lerche.parse(g,"aaabaab")
    @test join([x.data for x in r.children],"") == "aaabaa"
    r = Lerche.parse(g,"aaabaaba")
    @test join([x.data for x in r.children],"") == "aaabaaa"
    @test_throws UnexpectedToken Lerche.parse(g, "aaabaa")
    end

    @testset "Test_basic2 ($lexer, $parser)" begin
            # Multiple parsers and colliding tokens
        g = make_lark("""start: B A
                         B: "12"
                         A: "1" """)
        x = Lerche.parse(g,"121")
        @test x.data == "start"
        @test x.children == ["12", "1"]

        g2 = make_lark("""start: B A
                         B: "12"
                         A: "2" """)
        x = Lerche.parse(g2,"122")
        @test x.data == "start" && x.children == ["12", "2"]
    end

    @testset "Test unicode" begin
        g = make_lark("""start: B A
                         B: "♫♪"
                         A: "♫" """)
        x = Lerche.parse(g,"♫♪♫")
    end

    @testset "EBNF stack depth" begin
        g = make_lark("""start: a+
                         a : "a" """)

        Lerche.parse(g, repeat("a",500))
    end

    @testset "Expand lists with one item" begin
        g = make_lark("""start: list
                            ?list: item+
                            item : A
                            A: "a"
                        """)
        r = Lerche.parse(g,"a")

        # because 'list' is an expand-if-contains-one rule and we only provided one element it should have expanded to 'item'
        @test [subtree.data for subtree in r.children] == ["item"]

        # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
        @test length(r.children) == 1
    end
    @testset "test_expand1_lists_with_one_item_2" begin
            g = make_lark("""start: list
                            ?list: item+ "!"
                            item : A
                            A: "a"
                        """)
            r = Lerche.parse(g,"a!")

            # because 'list' is an expand-if-contains-one rule and we only provided one element it should have expanded to 'item'
        @test [subtree.data for subtree in r.children] == ["item"]

        # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule

        @test length(r.children)== 1
    end

    @testset "test_dont_expand1_lists_with_multiple_items" begin
        g = make_lark("""start: list
                                ?list: item+
                                item : A
                                A: "a"
                            """)
        r = Lerche.parse(g,"aa")

        # because 'list' is an expand-if-contains-one rule and we've provided more than one element it should *not* have expanded
        @test [subtree.data for subtree in r.children] == ["list"]

        # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
        @test length(r.children)== 1

        # Sanity check: verify that 'list' contains the two 'item's we've given it
        list = r.children[1]
        @test [item.data for item in list.children] == ["item", "item"]
    end

    @testset "test_dont_expand1_lists_with_multiple_items_2" begin
        g = make_lark("""start: list
                                ?list: item+ "!"
                                item : A
                                A: "a"
                            """)
        r = Lerche.parse(g,"aa!")

        # because 'list' is an expand-if-contains-one rule and we've provided more than one element it should *not* have expanded
        @test [subtree.data for subtree in r.children] == ["list"]

        # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
        @test length(r.children)==1

        # Sanity check: verify that 'list' contains the two 'item's we've given it
        list = r.children[1]
        @test [item.data for item in list.children] == ["item", "item"]
    end

    @testset "test_empty_expand1_list" begin
        g = make_lark("""start: list
                                ?list: item*
                                item : A
                                A: "a"
                             """)
        r = Lerche.parse(g,"")

        # because 'list' is an expand-if-contains-one rule and we've provided less than one element (i.e. none) it should *not* have expanded
        @test [subtree.data for subtree in r.children] == ["list"]

        # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
        @test length(r.children)== 1

        # Sanity check: verify that 'list' contains no 'item's as we've given it none
        list = r.children[1]
        @test [item.data for item in list.children] == []
    end

    @testset "test_empty_expand1_list_2" begin
            g = make_lark("""start: list
                            ?list: item* "!"?
                            item : A
                            A: "a"
                         """)
            r = Lerche.parse(g,"")
        # because 'list' is an expand-if-contains-one rule and we've provided less than one element (i.e. none) it should *not* have expanded
        @test [subtree.data for subtree in r.children] == ["list"]

        # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
        @test length(r.children)== 1

        # Sanity check: verify that 'list' contains no 'item's as we've given it none
        list = r.children[1]
        @test [item.data for item in list.children] == []
    end

    @testset "test_empty_flatten_list" begin
            g = make_lark("""start: list
                            list: | item "," list
                            item : A
                            A: "a"
                         """)
            r = Lerche.parse(g,"")

            # Because 'list' is a flatten rule it's top-level element should *never* be expanded
            @test [subtree.data for subtree in r.children] == ["list"]

            # Sanity check: verify that 'list' contains no 'item's as we've given it none
            list = r.children[1]
            @test [item.data for item in list.children] == []
    end

    @testset "test_token_collision" begin
            g = make_lark(raw"""start: "Hello" NAME
                        NAME: /\w/+
                        %ignore " "
                    """)
            x = Lerche.parse(g,"Hello World")
            @test x.children == ["World"]
            x = Lerche.parse(g,"Hello HelloWorld")
            @test x.children == ["HelloWorld"]
    end

    @testset "test_token_collision_WS" begin
            g = make_lark(raw"""start: "Hello" NAME
                        NAME: /\w/+
                        %import common.WS
                        %ignore WS
                    """)
            x = Lerche.parse(g,"Hello World")
            @test x.children == ["World"]
            x = Lerche.parse(g,"Hello HelloWorld")
        @test x.children == ["HelloWorld"]
    end

    @testset "test_token_collision2" begin
            g = make_lark("""
                    !start: "starts"
                    %import common.LCASE_LETTER
                    """)
            x = Lerche.parse(g,"starts")
        @test x.children == ["starts"]
    end

    @testset "test_templates" begin
            g = make_lark(raw"""
                       start: "[" sep{NUMBER, ","} "]"
                       sep{item, delim}: item (delim item)*
                       NUMBER: /\d+/
                       %ignore " "
                       """)
            x = Lerche.parse(g,"[1, 2, 3, 4]")
            @test x.children == [Tree("sep", ["1", "2", "3", "4"])]
            x = Lerche.parse(g,"[1]")
        @test x.children == [Tree("sep", ["1"])]
    end

    @testset "test_templates_recursion" begin
            g = make_lark(raw"""
                       start: "[" _sep{NUMBER, ","} "]"
                       _sep{item, delim}: item | _sep{item, delim} delim item
                       NUMBER: /\d+/
                       %ignore " "
                       """)
            x = Lerche.parse(g,"[1, 2, 3, 4]")
            @test x.children == ["1", "2", "3", "4"]
            x = Lerche.parse(g,"[1]")
        @test x.children == ["1"]
    end

    @testset "test_templates_import" begin
            g = Lerche.open("test_templates_import.lark", rel_to=@__FILE__)
            x = Lerche.parse(g,"[1, 2, 3, 4]")
            @test x.children == [Tree("sep", ["1", "2", "3", "4"])]
            x = Lerche.parse(g,"[1]")
            @test x.children == [Tree("sep", ["1"])]
    end

    @testset "test_templates_alias" begin
            g = make_lark(raw"""
                       start: expr{"C"}
                       expr{t}: "A" t
                              | "B" t -> b
                       """)
            x = Lerche.parse(g,"AC")
            @test x.children == [Tree("expr", [])]
            x = Lerche.parse(g,"BC")
            @test x.children == [Tree("b", [])]
        end

        @testset "test_templates_modifiers" begin
            g = make_lark(raw"""
                       start: expr{"B"}
                       !expr{t}: "A" t
                       """)
            x = Lerche.parse(g,"AB")
            @test x.children == [Tree("expr", ["A", "B"])]
            g = make_lark(raw"""
                       start: _expr{"B"}
                       !_expr{t}: "A" t
                       """)
            x = Lerche.parse(g,"AB")
            @test x.children == ["A", "B"]
            g = make_lark(raw"""
                       start: expr{b}
                       b: "B"
                       ?expr{t}: "A" t
                       """)
            x = Lerche.parse(g,"AB")
            @test x.children == [Tree("b",[])]
        end

        @testset "test_templates_templates" begin
            g = make_lark("""start: a{b}
                         a{t}: t{"a"}
                         b{x}: x""")
            x = Lerche.parse(g,"a")
            @test x.children == [Tree("a", [Tree("b",[])])]
        end

        @testset "test_g_regex_flags" begin
            g = make_lark("""
                    start: "a" /b+/ C
                    C: "C" | D
                    D: "D" E
                    E: "e"
                    """, g_regex_flags=2)   #re.I
            x1 = Lerche.parse(g,"ABBc")
            x2 = Lerche.parse(g,"abdE")
        end

    @testset "test_undefined_rule" begin
           @test_throws GrammarError  make_lark("""start: a""")
    end

    @testset "test_undefined_token" begin
           @test_throws GrammarError make_lark("""start: A""")
    end

    @testset "test_rule_collision" begin
            g = make_lark("""start: "a"+ "b"
                             | "a"+ """)
            x = Lerche.parse(g,"aaaa")
            x = Lerche.parse(g,"aaaab")
    end

    @testset "test_rule_collision2" begin
            g = make_lark("""start: "a"* "b"
                             | "a"+ """)
            x = Lerche.parse(g,"aaaa")
            x = Lerche.parse(g,"aaaab")
            x = Lerche.parse(g,"b")
    end

    @testset "test_token_not_anon" begin
            """Tests that "a" is matched as an anonymous token, and not A.
            """

            g = make_lark("""start: "a"
                        A: "a" """)
            x = Lerche.parse(g,"a")
            @test length(x.children)==0 #(" \"a\" should be considered anonymous")

            g = make_lark("""start: "a" A
                        A: "a" """)
            x = Lerche.parse(g,"aa")
            @test length(x.children)==1 #("only \"a\" should be considered anonymous")
            @test x.children[1].type_== "A"

            g = make_lark("""start: /a/
                        A: /a/ """)
            x = Lerche.parse(g,"a")
            @test length(x.children)== 1
            @test x.children[1].type_== "A" # ("A isn't associated with /a/")
    end

        @testset "test_maybe" begin
            g = make_lark("""start: ["a"] """)
            x = Lerche.parse(g,"a")
            x = Lerche.parse(g,"")
        end

        @testset "test_start" begin
            g = make_lark("""a: "a" a? """, start="a")
            x = Lerche.parse(g,"a")
            x = Lerche.parse(g,"aa")
            x = Lerche.parse(g,"aaa")
        end

        @testset "test_alias" begin
            g = make_lark("""start: "a" -> b """)
            x = Lerche.parse(g,"a")
            @test x.data== "b"
        end

        @testset "test_token_ebnf" begin
            g = make_lark("""start: A
                      A: "a"* ("b"? "c".."e")+
                      """)
            x = Lerche.parse(g,"abcde")
            x = Lerche.parse(g,"dd")
        end

    @testset "test_backslash" begin
        # Note extra backslashes relative to Python
        # due to different treatment of raw strings
            g = make_lark(raw"""start: "\\\\" "a"
                      """)
            x = Lerche.parse(g,raw"\a")

            g = make_lark(raw"""start: /\\/ /a/
                      """)
            x = Lerche.parse(g,raw"\a")
        end

        @testset "test_special_chars" begin
            g = make_lark(raw"""start: "\n"
                      """)
            x = Lerche.parse(g,"\n")

            g = make_lark(raw"""start: /\n/
                      """)
            x = Lerche.parse(g,"\n")
        end

    @testset "test_backslash2" begin
        # Note extra backslashes relative to Python
        # due to different treatment of raw strings
            g = make_lark(raw"""start: "\\"" "-"
                      """)
            x = Lerche.parse(g,"\"-")

            g = make_lark(raw"""start: /\// /-/
                      """)
            x = Lerche.parse(g,"/-")
        end

        # @testset "test_token_recurse" begin
        #     g = make_lark("""start: A
        #                  A: B
        #                  B: A
        #               """)

        @testset "test_empty" begin
            # Fails an Earley implementation without special handling for empty rules,
            # or re-processing of already completed rules.
            g = make_lark(raw"""start: _empty a "B"
                          a: _empty "A"
                          _empty:
                            """)
            x = Lerche.parse(g,"AB")
        end

        @testset "test_regex_quote" begin
            g = raw"""
            start: SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING
            SINGLE_QUOTED_STRING  : /'[^']*'/
            DOUBLE_QUOTED_STRING  : /"[^"]*"/
            """

            g = make_lark(g)
            @test  Lerche.parse(g,"\"hello\"").children == ["\"hello\""]
            @test  Lerche.parse(g,"'hello'").children == ["'hello'"]
        end

        @testset "test_float_without_lexer" begin
            g = make_lark("""start: ["+"|"-"] float
                         float: digit* "." digit+ exp?
                              | digit+ exp
                         exp: ("e"|"E") ["+"|"-"] digit+
                         digit: "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
                      """)
            Lerche.parse(g,"1.2")
            Lerche.parse(g,"-.2e9")
            Lerche.parse(g,"+2e-9")
            @test_throws UnexpectedToken Lerche.parse(g,"+2e-9e")
        end

        @testset "test_keep_all_tokens" begin
            l = make_lark("""start: "a"+ """, keep_all_tokens=true)
            tree = Lerche.parse(l,"aaa")
            @test tree.children == ["a", "a", "a"]
        end

        @testset "test_token_flags" begin
            l = make_lark("""!start: "a"i+
                      """
                      )
            tree = Lerche.parse(l,"aA")
            @test tree.children == ["a", "A"]

            l = make_lark("""!start: /a/i+
                      """
                      )
            tree = Lerche.parse(l,"aA")
            @test tree.children == ["a", "A"]

            # g = """!start: "a"i "a"
            #     """
            # self.assertRaises(GrammarError, make_lark, g)

            # g = """!start: /a/i /a/
            #     """
            # self.assertRaises(GrammarError, make_lark, g)

            g = """start: NAME "," "a"
                   NAME: /[a-z_]/i /[a-z0-9_]/i*
                """
            l = make_lark(g)
            tree = Lerche.parse(l,"ab,a")
            @test tree.children == ["ab"]
            tree = Lerche.parse(l,"AB,a")
            @test tree.children == ["AB"]
        end

        @testset "test_token_flags3" begin
            l = make_lark("""!start: ABC+
                      ABC: "abc"i
                      """
                      )
            tree = Lerche.parse(l,"aBcAbC")
            @test tree.children == ["aBc", "AbC"]
        end

        @testset "test_token_flags2" begin
            g = """!start: ("a"i | /a/ /b/?)+
                """
            l = make_lark(g)
            tree = Lerche.parse(l,"aA")
            @test tree.children == ["a", "A"]
        end

        @testset "test_token_flags_verbose" begin
            g = make_lark(raw"""start: NL | ABC
                          ABC: / [a-z] /x
                          NL: /\n/
                      """)
            x = Lerche.parse(g,"a")
            @test x.children == ["a"]
        end

        @testset "test_token_flags_verbose_multiline" begin
            g = make_lark(raw"""start: ABC
                          ABC: /  a      b c
                               d
                                e f
                           /x
                       """)
            x = Lerche.parse(g,"abcdef")
            @test x.children == ["abcdef"]
        end

        @testset "test_token_multiline_only_works_with_x_flag" begin
            g = raw"""start: ABC
                    ABC: /  a      b c
                              d
                                e f
                            /i
                      """
            @test_throws GrammarError make_lark(g)
        end

        @testset "test_twice_empty" begin
            g = """!start: ("A"?)?
                """
            l = make_lark(g)
            tree = Lerche.parse(l,"A")
            @test tree.children == ["A"]

            tree = Lerche.parse(l,"")
            @test tree.children == []
        end

        @testset "test_undefined_ignore" begin
            g = """!start: "A"

                %ignore B
                """
            @test_throws GrammarError  make_lark( g)
        end

        @testset "test_alias_in_terminal" begin
            g = """start: TERM
                TERM: "a" -> alias
                """
            @test_throws  Exception make_lark(g)
        end

        @testset "test_line_and_column" begin
            g = raw"""!start: "A" bc "D"
                !bc: "B\nC"
                """
            l = make_lark(g)
            a, bc, d = Lerche.parse(l,"AB\nCD").children
            @test a.line== 1
            @test a.column== 1

            bc = bc.children[1]
            @test bc.line== 1
            @test bc.column== 2

            @test d.line== 2
            @test d.column== 2

            @test a.end_line== 1
            @test a.end_column== 2
            @test bc.end_line== 2
            @test bc.end_column== 2
            @test d.end_line== 2
            @test d.end_column== 3
        end

        @testset "test_reduce_cycle" begin
            """Tests an edge-condition in the LALR parser, in which a transition state looks exactly like the end state.
            It seems that the correct solution is to explicitely distinguish finalization in the reduce() function.
            """

            l = make_lark("""
                term: A
                    | term term

                A: "a"

            """, start="term")

            tree = Lerche.parse(l,"aa")
            @test length(tree.children)== 2
        end

# Only for standard lexer
        @testset "test_lexer_prioritization" begin
            "Tests effect of priority on result"

            grammar = """
            start: A B | AB
            A.2: "a"
            B: "b"
            AB: "ab"
            """
            l = make_lark(grammar)
            res = Lerche.parse(l,"ab")

            @test res.children== ["a", "b"]
            @test res.children!= ["ab"]

            grammar = """
            start: A B | AB
            A: "a"
            B: "b"
            AB.3: "ab"
            """
            l = make_lark(grammar)
            res = Lerche.parse(l,"ab")

            @test res.children != ["a", "b"]
            @test res.children == ["ab"]

            grammar = """
            start: A B | AB
            A: "a"
            B.-20: "b"
            AB.-10: "ab"
            """
            l = make_lark(grammar)
            res = Lerche.parse(l,"ab")
            @test res.children == ["a", "b"]

            # Lark difference: priority goes in an Int64 so can't be as big as
            # in Python
            grammar = """
            start: A B | AB
            A.-99999999999999: "a"
            B: "b"
            AB: "ab"
            """
            l = make_lark(grammar)
            res = Lerche.parse(l,"ab")

            @test res.children == ["ab"]

        end

        @testset "test_import" begin
            grammar = """
            start: NUMBER WORD

            %import common.NUMBER
            %import common.WORD
            %import common.WS
            %ignore WS

            """
            l = make_lark(grammar)
            x = Lerche.parse(l,"12 elephants")
            @test x.children== ["12", "elephants"]
        end

        @testset "test_relative_import" begin
            grammar = """
            start: NUMBER WORD

            %import .grammars.test.NUMBER
            %import common.WORD
            %import common.WS
            %ignore WS

            """
            l = make_lark(grammar)
            x = Lerche.parse(l,"12 lions")
            @test x.children == ["12", "lions"]
        end

        @testset "test_multi_import" begin
            grammar = """
            start: NUMBER WORD

            %import common (NUMBER, WORD, WS)
            %ignore WS

            """
            l = make_lark(grammar)
            x = Lerche.parse(l,"12 toucans")
            @test x.children == ["12", "toucans"]
        end

        @testset "test_relative_multi_import" begin
            grammar = """
           start: NUMBER WORD

           %import .grammars.test (NUMBER, WORD, WS)
           %ignore WS

           """
            l = make_lark(grammar)
            x = Lerche.parse(l,"12 capybaras")
            @test x.children== ["12", "capybaras"]
        end

        @testset "test_import_errors" begin
            grammar = """
            start: NUMBER WORD

            %import .grammars.bad_test.NUMBER
            """
            @test_throws SystemError make_lark(grammar)

            grammar = """
            start: NUMBER WORD

            %import bad_test.NUMBER
            """
            @test_throws SystemError make_lark(grammar)
        end

        @testset "test_utf8" begin
            g = """start: a
                   a: "±a"
                """
            l = make_lark(g)
            @test Lerche.parse(l,"±a") == Tree("start", [Tree("a", [])])

            g = """start: A
                   A: "±a"
                """
            l = make_lark(g)
            @test Lerche.parse(l,"±a") ==Tree("start", [Token("A","±a")])
        end

        @testset "test_ignore" begin
            g = raw"""
            COMMENT: /(!|(\/\/))[^\n]*/
            %ignore COMMENT
            %import common.WS -> _WS
            %import common.INT
            start: "INT"i _WS+ INT _WS*
            """

            p = make_lark(g)

            tree = Lerche.parse(p,"int 1 ! This is a comment\n")
            @test tree.children == ["1"]
            tree = Lerche.parse(p,"int 1 ! This is a comment")    # A trailing ignore token can be tricky!
            @test tree.children == ["1"]

            p = make_lark(raw"""
                start : "a"*
                %ignore "b" """)
            tree = Lerche.parse(p,"bb")

            @test tree.children == []
        end

        @testset "test_regex_escaping" begin
            g = make_lark("start: /[ab]/")
            Lerche.parse(g,"a")
            Lerche.parse(g,"b")

            @test_throws  UnexpectedInput Lerche.parse(g, "c")

            g = make_lark(raw"start: /\w/")
            Lerche.parse(g,"a")

            g = make_lark(raw"start: /\\w/")
            @test_throws  UnexpectedInput Lerche.parse(g,"a")
            Lerche.parse(g,raw"\w")

            Lerche.parse(make_lark(raw"""start: /\[/"""),"[")

            Lerche.parse(make_lark(raw"start: /\//"),"/")

            Lerche.parse(make_lark(raw"start: /\\/"),"\\")

            Lerche.parse(make_lark(raw"start: /\[ab]/"),"[ab]")

            Lerche.parse(make_lark(raw"start: /\\[ab]/"),"\\a")

            Lerche.parse(make_lark(raw"start: /\t/"),"\t")

            Lerche.parse(make_lark(raw"start: /\\t/"),"\\t")

            Lerche.parse(make_lark(raw"start: /\\\t/"),"\\\t")

            Lerche.parse(make_lark(raw"""start: "\t" """),"\t")

            Lerche.parse(make_lark(raw"""start: "\\t" """),"\\t")

            Lerche.parse(make_lark(raw"""start: "\\\t" """),"\\\t")
        end

        @testset "test_ranged_repeat_rules" begin
            g = """!start: "A"~3
                """
            l = make_lark(g)
            @test Lerche.parse(l,"AAA")== Tree("start", ["A", "A", "A"])
            @test_throws UnexpectedInput Lerche.parse(l, "AA")
            @test_throws UnexpectedInput Lerche.parse(l, "AAAA")

            g = """!start: "A"~0..2
                """
            l = make_lark(g)
            @test Lerche.parse(l,"")== Tree("start", [])
            @test Lerche.parse(l,"A")== Tree("start", ["A"])
            @test Lerche.parse(l,"AA")== Tree("start", ["A", "A"])
            @test_throws UnexpectedInput Lerche.parse(l, "AAA")

            g = """!start: "A"~3..2
                """
            @test_throws GrammarError make_lark( g)

            g = """!start: "A"~2..3 "B"~2
                """
            l = make_lark(g)
            @test Lerche.parse(l,"AABB")== Tree("start", ["A", "A", "B", "B"])
            @test Lerche.parse(l,"AAABB")== Tree("start", ["A", "A", "A", "B", "B"])
            @test_throws UnexpectedInput Lerche.parse(l, "AAAB")
            @test_throws UnexpectedInput Lerche.parse(l, "AAABBB")
            @test_throws UnexpectedInput Lerche.parse(l, "ABB")
            @test_throws UnexpectedInput Lerche.parse(l, "AAAABB")
        end

        @testset "test_ranged_repeat_terms" begin
            g = """!start: AAA
                    AAA: "A"~3
                """
            l = make_lark(g)
            @test Lerche.parse(l,"AAA")== Tree("start", ["AAA"])
            @test_throws UnexpectedInput Lerche.parse(l, "AA")
            @test_throws UnexpectedInput Lerche.parse(l, "AAAA")

            g = """!start: AABB CC
                    AABB: "A"~0..2 "B"~2
                    CC: "C"~1..2
                """
            l = make_lark(g)
            @test Lerche.parse(l,"AABBCC")== Tree("start", ["AABB", "CC"])
            @test Lerche.parse(l,"BBC")== Tree("start", ["BB", "C"])
            @test Lerche.parse(l,"ABBCC")== Tree("start", ["ABB", "CC"])
            @test_throws UnexpectedInput Lerche.parse(l, "AAAB")
            @test_throws UnexpectedInput Lerche.parse(l, "AAABBB")
            @test_throws UnexpectedInput Lerche.parse(l, "ABB")
            @test_throws UnexpectedInput Lerche.parse(l, "AAAABB")
        end

        @testset "test_priority_vs_embedded" begin
            g = """
            A.2: "a"
            WORD: ("a".."z")+

            start: (A | WORD)+
            """
            l = make_lark(g)
            t = Lerche.parse(l,"abc")
            @test t.children== ["a", "bc"]
            @test t.children[1].type_ == "A"
        end

        @testset "test_line_counting" begin
            p = make_lark("start: /[^x]+/")

            text = "hello\nworld"
            t = Lerche.parse(p,text)
            tok = t.children[1]
            @test tok== text
            @test tok.line== 1
            @test tok.column== 1
            @test tok.end_line== 2
            @test tok.end_column== 6
        end

        @testset "test_empty_end" begin
            p = make_lark("""
                start: b c d
                b: "B"
                c: | "C"
                d: | "D"
            """)
            res = Lerche.parse(p,"B")
            @test length(res.children)== 3
        end

        @testset "test_multi_start" begin
            p = make_lark("""
                a: "x" "a"?
                b: "x" "b"?
                """, start=["a", "b"])

            @test Lerche.parse(p,"xa", start="a") == Tree("a", Union{Token,Tree}[])
            @test Lerche.parse(p,"xb", start="b") == Tree("b", Union{Token,Tree}[])
        end

        @testset "test_lexer_detect_newline_tokens" begin
            # Detect newlines in regular tokens
            println("lexer is $lexer, parser is $parser")
            test_gram = raw"""start: "go" tail*
            !tail : SA "@" | SB "@" | SC "@" | SD "@"
            SA : "a" /\n/
            SB : /b./s
            SC : "c" /[^a-z]/
            SD : "d" /\s/
            """
            g = make_lark(test_gram)
            a,b,c,d = [x.children[2] for x in Lerche.parse(g,"goa\n@b\n@c\n@d\n@").children]
            @test a.line == 2
            @test b.line == 3
            @test c.line == 4
            @test d.line == 5

            # Detect newlines in ignored tokens
            for re in ["/\\n/", "/[^a-z]/", "/\\s/"]
                g = make_lark("""!start: "a" "a"
                             %ignore $re""")
                a, b = Lerche.parse(g,"a\na").children
                @test a.line == 1
                @test b.line == 2
            end
        end
end

const TO_TEST = [("standard","lalr"),
                 ("contextual","lalr")
                 ]

for (lexer,parser) in TO_TEST
    make_parser_test(lexer,parser)
end


text:
# TODO - should we just rename the Core* structs?
const Client = CoreClient
const Session = CoreSession
const Transaction = CoreTransaction
const Database = CoreDatabase
const DatabaseManager = CoreDatabaseManager

# general
export Client, Session, Transaction, Database, DatabaseManager
export TypeDBOptions

# constants
export VALUE_TYPE

# concepts
# - ConceptManager.jl functions requires these types for dispatch
export AttributeType, EntityType, RelationType, RoleType, ThingType
export Attribute, Entity, Relation, Thing
export ConceptManager

# functions (listed in alphabetical order)
export get_instances,
       get_owns,
       get_owners,
       get_plays,
       get_regex,
       get_rule,
       get_rules,
       get_subtypes,
       get_supertype,
       get_supertypes,
       set_abstract,
       set_label,
       set_owns,
       set_plays,
       set_regex,
       set_supertype,
       unset_abstract

# others
export contains_database

ext: jl


text:
# possible start for a testset

# data = NBodyIPs.Data.read("~/Dropbox/PIBmat/Ti_DFTB_Data/Ti_N54_T2000.xyz")
#
# D = Dictionary("@analytic r -> (2.0/r)^3",
#                "(:cos, 6.0, 9.0)")
#
# s = serialize(D)
# deserialize(Dictionary, s)
#
# println("generating basis functions")
#
# rcuts = [9.2, 6.2, 4.5]   # 9.2
# TRANSFORM = "@analytic r -> (2.9/r)^3"
# CUTOFF = ["(:cos, $(0.66*rcut), $(rcut))" for rcut in rcuts]
#
# D2 = Dictionary(TRANSFORM, CUTOFF[1])
# D3 = Dictionary(TRANSFORM, CUTOFF[2])
# D4 = Dictionary(TRANSFORM, CUTOFF[3])
#
# B1 = [NBody(1.0)]
# B2 = gen_basis(2, D2, 12)
# B3 = gen_basis(3, D3, 10)
# B4 = gen_basis(4, D4, 6)
# B = [B1; B2; B3; B4]
# c = rand(length(B))
# IP = NBodyIP(B, c)
#
# V = IP.orders[3]
# s = serialize(V)
# V3 = deserialize(NBody, s)
#
# NBodyIPs.IO.write(@__DIR__() * "/test.jld", IP)
# IP = NBodyIPs.IO.read(@__DIR__() * "/test.jld")

# testing the custom JLD serialization

using JuLIP, NBodyIPs, DataFrames, Plots, JLD
using NBodyIPs.Data: Dat

include(homedir() * "/Dropbox/PIBmat/Ti_DFTB_Data/Ti.jl")
data = Ti.read_Ti(; exclude = ["wire", "surface", "omega", "hcp"])

# reference energy
B1 = [ NBody(Ti.get_E0()) ]

# generate long-range 2B basis
r0 = rnn(:Ti)
TLONG = "@analytic r -> exp( - 2.5 * (r/$r0 - 1))"
TSHORT = "@analytic r -> ($r0/r)^8"
CUT2 = "(:cos, 5.5, 7.5)"
B2 = [ gen_basis(2, Dictionary(TLONG, CUT2), 9);
       gen_basis(2, Dictionary(TSHORT, CUT2), 6) ]

# 3B BASIS
TRANS3 = "@analytic r -> exp( - 3.0 * (r/$r0 - 1))"
CUT3 = "(:cos, 5.0, 6.5)"
B3 = gen_basis(3, Dictionary(TRANS3, CUT3), 8)

B = [B1; B2; B3]
@show length(B)

lsq = kron(data[1:10], B)
JLD.save("temp.jld", "d1", lsq)
d1 = JLD.load("temp.jld", "d1")

JLD.@load "temp.jld" lsq

using FileIO
save("ip_temp.jld2", "IP", IP4)
IP4b = load("ip_temp.jld2", "IP")
IP4bf = fast(IP4b)
using StaticArrays
for n in [1,3,6]
    for _ = 1:10
        r = SVector((3.0 + rand(n))...)
        @assert IP4(r) ≈ IP4b(r)
        @assert IP4(r) ≈ IP4bf(r)
    end
end

import JSON
using NBodyIPs
IP = load_ip("co_fits/Ti_4B_med.jld2")

IPj = NBodyIPs.saveas_json(IP)

f = open("temp.json", "w")
print(f, JSON.json(IPj, 3))
close(f)

IPstr = JSON.parsefile("temp.json")
IP2 = NBodyIPs.loadas_json(NBodyIP, IPstr)

save("co_fits/Ti_4B_med.json", IP)
IP2 = load_ip("co_fits/Ti_4B_med.json")

for dim in [1, 3, 6]
   for n = 1:10
      r =  SVector((3.0 + rand(dim))...)
      @show abs(IP2(r) - IP(r))
   end
end


text:
const ESC = "\u001B" # escape code
const moves = Dict( "left" => "[1D", "right" => "[1C", "up" => "[1A", "down" => "[1B",
                    "linestart" => "[9D", "topleft" => "[H", "bottomright" => "[24;79H")

print("$ESC[2J")     # clear terminal first
print("$ESC[10;10H") # move cursor to (10, 10) say
const count = [0]
for d in ["left", "right", "up", "down", "linestart", "bottomright"]
    sleep(3) # three second pause for display between cursor movements
    print("$ESC$(moves[d])")
    print(count[1] += 1)
end
println()
println()

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
using DifferentialEquations, Random, Distributions
using Plots, ColorSchemes
using DataFrames, CSV, LaTeXStrings
using Statistics, GLM, Measures
using LightGraphs, SimpleWeightedGraphs#, GraphPlot
using Compose, Cairo, Fontconfig
using LsqFit, Dates
import Gadfly
g = Gadfly # so we can use gadfly too!

pyplot(grid=false)
#  plotly()

epsilon = 1e-8

fnt = Plots.font("Arial")
default(titlefont=fnt, guidefont=fnt, tickfont=fnt, legendfont=fnt)

################################################################################
#### FIGURE 2B
################################################################################

c_starts = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

function one_cr_model(dc, c, k, t)
    ka = k['a']
    ks = k['s']
    ki = k['i']

    dc[1] = ka * c[2] - ks * c[1]
    dc[2] = ks * c[1] - ka * c[2] - ki * c[2] * c[1]
    dc[3] = ki * c[2] * c[1]

    @assert abs(sum(dc))<=epsilon
end

function run_one_cr_recruitment(c0_val)
    t = fill(Float64, 0)
    u = fill(Float64[], 0)

    k_sil = Dict('a' => 1.0,
                 's' => 1.0,
                 'i' => 0,
                 'p' => 0)
    kt_pairs = [[k_sil, (0.0, 2.0)]]

    c0 = [c0_val; 1.0-c0_val; 0.0]

    for pair in kt_pairs
        k = pair[1]
        times = pair[2]
        prob = ODEProblem(one_cr_model, c0, times, k)
        sol = solve(prob, saveat=0.001)
        t = [t; sol.t]
        u = [u; sol.u]
        c0 = sol.u[end]
    end
    a1s = [x[1] for x in u]

    return [t, a1s]
end

function plot_c0_vals(c0_vals)
    cscheme = get(ColorSchemes.viridis, range(0.0, stop=0.9, length=length(c0_vals)))
    p = plot()
    for i in 1:length(c0_vals)
        c0 = c0_vals[i]
        t, d = run_one_cr_recruitment(c0)
        p = plot!(p, t, d,
                 label=string(c0), color=cscheme[i],
                 xlabel="Days",
                 ylabel="Fraction Active",
                 yticks = 0:0.5:1.0,
                 ylims=(-0.1, 1.1),
                 xlims=(-0.1, 2.1),
                 xticks=0:1.0:2.0,
                 legend=:outerright,
                 legendtitle="A(0)",
                 #  legendtitlefontsize=14,
                 linewidth=2,
                 size=(600, 300))
    end

    return p
end

savefig(plot_c0_vals(c_starts), "fig_2b.pdf")

################################################################################
#### FIGURE 2C
################################################################################
function steady_state(r)
    ka = 1.0
    ks = r
    #  return (-ka + sqrt(ka^2 + 4*ka*ks))/(2*ks)
    return ka/(ka + ks)
end

xs = collect(0:0.001:10)
ys = [steady_state(x) for x in xs]

fnt = Plots.font("Arial")
default(titlefont=fnt, guidefont=fnt, tickfont=fnt, legendfont=fnt)

p = plot(xs, ys,
         xlabel=L"$k_s/k_a$",
         ylabel="Steady-State
Fraction Active",
         label="", color=:green,
         yticks = [0.0, 1.0],
         ylims=(-0.1, 1.1),
         xlims=(-0.1, 10),
         xticks=[0, 10],
         linewidth=2,
         size=(300, 261))
savefig(p, "fig_2c.pdf")

################################################################################
#### FIGURE 2E
################################################################################

function ksval(p)
    exp = 3.0
    kd = 0.33
    return (p^exp)/(p^exp + (kd)^exp)
end

pvals = 0:0.01:100
kvals = [ksval(p) for p in pvals]

p = plot(pvals, kvals,
         linewidth=2,
         xlims=[0, 1.5],
         xticks=[0, 1],
         xformatter = x -> [L"$0$", L"$[CR]_{max}$"][Int(x+1)],
         ylims=[0, 1],
         yticks=[0, 1],
         yformatter = y -> [L"$0$", L"$k_{s\ max}$"][Int(y+1)],
         xlabel="[CR]",
         ylabel=L"$k_s($CR$)$",
         label=false,
         size=(300, 261))
savefig(p, "fig_2e.pdf")


text:
__precompile__()

module Wavelets

include("util.jl")

include("wt.jl")
include("transforms.jl")

include("threshold.jl")
include("plot.jl")

using Reexport
@reexport using .Util, .WT, .Transforms, .Threshold, .Plot

end


text:
import Base: eltype

# definition of the `Status` struct.
# TODO: this bakes in a lot of assumptions about ordering and padding
if !@isdefined(Status)
    let
        struct_fields = Any[]
        offset = 0
        i = 0

        while offset < MPI_Status_Source_offset
            push!(struct_fields, :($(Symbol(:_pad, i+=1))::Cint))
            offset += 4
        end
        @assert offset == MPI_Status_Source_offset
        push!(struct_fields, :(source::Cint))
        offset += 4

        while offset < MPI_Status_Tag_offset
            push!(struct_fields, :($(Symbol(:_pad, i+=1))::Cint))
            offset += 4
        end
        @assert offset == MPI_Status_Tag_offset
        push!(struct_fields, :(tag::Cint))
        offset += 4

        while offset < MPI_Status_Error_offset
            push!(struct_fields, :($(Symbol(:_pad, i+=1))::Cint))
            offset += 4
        end
        @assert offset == MPI_Status_Error_offset
        push!(struct_fields, :(error::Cint))
        offset += 4

        while offset < MPI_Status_size
            push!(struct_fields, :($(Symbol(:_pad, i+=1))::Cint))
            offset += 4
        end

        @eval struct Status
            $(struct_fields...)
        end
    end

    @assert fieldoffset(Status, findfirst(isequal(:source), fieldnames(Status))) == MPI_Status_Source_offset
    @assert fieldoffset(Status, findfirst(isequal(:tag), fieldnames(Status))) == MPI_Status_Tag_offset
    @assert fieldoffset(Status, findfirst(isequal(:error), fieldnames(Status))) == MPI_Status_Error_offset
    @assert sizeof(Status) == MPI_Status_size
end

Get_source(status::Status) = Int(status.source)
Get_tag(status::Status) = Int(status.tag)
Get_error(status::Status) = Int(status.error)

@mpi_handle Request buffer

const REQUEST_NULL = _Request(MPI_REQUEST_NULL, nothing)
Request() = Request(REQUEST_NULL.val, nothing)

function Probe(src::Integer, tag::Integer, comm::Comm)
    stat_ref = Ref{Status}()
    @mpichk ccall((:MPI_Probe, libmpi), Cint,
          (Cint, Cint, MPI_Comm, Ptr{Status}),
          src, tag, comm, stat_ref)
    stat_ref[]
end

function Iprobe(src::Integer, tag::Integer, comm::Comm)
    flag = Ref{Cint}()
    stat_ref = Ref{Status}()
    @mpichk ccall((:MPI_Iprobe, libmpi), Cint,
          (Cint, Cint, MPI_Comm, Ptr{Cint}, Ptr{Status}),
          src, tag, comm, flag, stat_ref)
    if flag[] == 0
        return false, nothing
    end
    true, stat_ref[]
end

function Get_count(stat::Status, ::Type{T}) where T
    count = Ref{Cint}()
    @mpichk ccall((:MPI_Get_count, libmpi), Cint,
                  (Ptr{Status}, MPI_Datatype, Ptr{Cint}),
                  Ref(stat), mpitype(T), count)
    Int(count[])
end

"""
    Send(buf::MPIBuffertype{T}, count::Integer, datatype::Datatype,
         dest::Integer, tag::Integer, comm::Comm) where T

Complete a blocking send of `count` elements of type `datatype` from `buf` to MPI
rank `dest` of communicator `comm` using the message tag `tag`
"""
function Send(buf, count::Integer, datatype::Union{Datatype, MPI_Datatype},
              dest::Integer, tag::Integer, comm::Comm)
    # int MPI_Send(const void* buf, int count, MPI_Datatype datatype, int dest,
    #              int tag, MPI_Comm comm)
    @mpichk ccall((:MPI_Send, libmpi), Cint,
          (MPIPtr, Cint, MPI_Datatype, Cint, Cint, MPI_Comm),
          buf, count, datatype, dest, tag, comm)
end

"""
    Send(buf::MPIBuffertype{T}, count::Integer, dest::Integer, tag::Integer,
         comm::Comm) where T

Complete a blocking send of `count` elements of `buf` to MPI rank `dest`
of communicator `comm` using with the message tag `tag`
"""
function Send(buf, count::Integer, dest::Integer,
              tag::Integer, comm::Comm)
    Send(buf, count, mpitype(eltype(buf)), dest, tag, comm)
end

"""
    Send(buf::AbstractArray{T}, dest::Integer, tag::Integer, comm::Comm) where T

Complete a blocking send of `buf` to MPI rank `dest` of communicator `comm`
using with the message tag `tag`
"""
function Send(buf::AbstractArray{T}, dest::Integer, tag::Integer, comm::Comm) where T
    Send(buf, length(buf), dest, tag, comm)
end

"""
    Send(obj::T, dest::Integer, tag::Integer, comm::Comm) where T

Complete a blocking send of `obj` to MPI rank `dest` of communicator `comm`
using with the message tag `tag`.
"""
function Send(obj::T, dest::Integer, tag::Integer, comm::Comm) where T
    buf = [obj]
    Send(buf, dest, tag, comm)
end

"""
    send(obj, dest::Integer, tag::Integer, comm::Comm)

Complete a blocking send of using a serialized version of `obj` to MPI rank
`dest` of communicator `comm` using with the message tag `tag`.
"""
function send(obj, dest::Integer, tag::Integer, comm::Comm)
    buf = MPI.serialize(obj)
    Send(buf, dest, tag, comm)
end

"""
    Isend(buf::MPIBuffertype{T}, count::Integer, datatype::Datatype,
          dest::Integer, tag::Integer, comm::Comm) where T

Starts a nonblocking send of `count` elements of type `datatype` from `buf` to
MPI rank `dest` of communicator `comm` using with the message tag `tag`

Returns the commication `Request` for the nonblocking send.
"""
function Isend(buf, count::Integer, datatype::Union{Datatype, MPI_Datatype},
               dest::Integer, tag::Integer, comm::Comm)
    req = Request()
    # int MPI_Isend(const void* buf, int count, MPI_Datatype datatype, int dest,
    #               int tag, MPI_Comm comm, MPI_Request *request)
    @mpichk ccall((:MPI_Isend, libmpi), Cint,
          (MPIPtr, Cint, MPI_Datatype, Cint, Cint, MPI_Comm, Ptr{MPI_Request}),
                  buf, count, datatype, dest, tag, comm, req)
    req.buffer = buf
    return req
end

"""
    Isend(buf::MPIBuffertype{T}, count::Integer, dest::Integer, tag::Integer,
          comm::Comm) where T

Starts a nonblocking send of `count` elements of `buf` to MPI rank `dest`
of communicator `comm` using with the message tag `tag`

Returns the commication `Request` for the nonblocking send.
"""
function Isend(buf, count::Integer,
               dest::Integer, tag::Integer, comm::Comm)
    Isend(buf, count, mpitype(eltype(buf)), dest, tag, comm)
end

"""
    Isend(buf::Array{T}, dest::Integer, tag::Integer, comm::Comm) where T

Starts a nonblocking send of `buf` to MPI rank `dest` of communicator `comm`
using with the message tag `tag`

Returns the commication `Request` for the nonblocking send.
"""
function Isend(buf::AbstractArray{T}, dest::Integer, tag::Integer, comm::Comm) where T
    Isend(buf, length(buf), mpitype(T), dest, tag, comm)
end

"""
    Isend(obj::T, dest::Integer, tag::Integer, comm::Comm) where T

Starts a nonblocking send of `obj` to MPI rank `dest` of communicator `comm`
using with the message tag `tag`.

Returns the commication `Request` for the nonblocking send.
"""
function Isend(obj::T, dest::Integer, tag::Integer, comm::Comm) where T
    buf = [obj]
    Isend(buf, dest, tag, comm)
end

"""
    isend(obj, dest::Integer, tag::Integer, comm::Comm)

Starts a nonblocking send of using a serialized version of `obj` to MPI rank
`dest` of communicator `comm` using with the message tag `tag`.

Returns the commication `Request` for the nonblocking send.
"""
function isend(obj, dest::Integer, tag::Integer, comm::Comm)
    buf = MPI.serialize(obj)
    Isend(buf, dest, tag, comm)
end

"""
    Recv!(buf::MPIBuffertype{T}, count::Integer, datatype::Datatype,
          src::Integer, tag::Integer, comm::Comm) where T

Completes a blocking receive of up to `count` elements of type `datatype` into `buf`
from MPI rank `src` of communicator `comm` using with the message tag `tag`

Returns the `Status` of the receive
"""
function Recv!(buf, count::Integer, datatype::Union{Datatype,MPI_Datatype}, src::Integer,
               tag::Integer, comm::Comm)
    stat_ref = Ref{Status}()
    # int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source,
    #              int tag, MPI_Comm comm, MPI_Status *status)
    @mpichk ccall((:MPI_Recv, libmpi), Cint,
                  (MPIPtr, Cint, MPI_Datatype, Cint, Cint, MPI_Comm, Ptr{Status}),
                  buf, count, datatype, src, tag, comm, stat_ref)
    return stat_ref[]
end

"""
    Recv!(buf::MPIBuffertype{T}, count::Integer, src::Integer, tag::Integer,
          comm::Comm) where T

Completes a blocking receive of up to `count` elements into `buf` from MPI rank
`src` of communicator `comm` using with the message tag `tag`

Returns the `Status` of the receive
"""
function Recv!(buf, count::Integer, src::Integer,
               tag::Integer, comm::Comm)
    Recv!(buf, count, mpitype(eltype(buf)), src, tag, comm)
end

"""
    Recv!(buf::Array{T}, src::Integer, tag::Integer, comm::Comm) where T

Completes a blocking receive into `buf` from MPI rank `src` of communicator
`comm` using with the message tag `tag`

Returns the `Status` of the receive
"""
function Recv!(buf::AbstractArray{T}, src::Integer, tag::Integer, comm::Comm) where T
    Recv!(buf, length(buf), src, tag, comm)
end

function Recv(::Type{T}, src::Integer, tag::Integer, comm::Comm) where T
    buf = Ref{T}()
    stat = Recv!(buf, 1, src, tag, comm)
    (buf[], stat)
end

function recv(src::Integer, tag::Integer, comm::Comm)
    stat = Probe(src, tag, comm)
    count = Get_count(stat, UInt8)
    buf = Array{UInt8}(undef, count)
    stat = Recv!(buf, Get_source(stat), Get_tag(stat), comm)
    (MPI.deserialize(buf), stat)
end

"""
    Irecv!(buf::MPIBuffertype{T}, count::Integer, datatype::Datatype,
           src::Integer, tag::Integer, comm::Comm) where T

Starts a nonblocking receive of up to `count` elements of type `datatype` into `buf`
from MPI rank `src` of communicator `comm` using with the message tag `tag`

Returns the communication `Request` for the nonblocking receive.
"""
function Irecv!(buf, count::Integer, datatype::Union{Datatype, MPI_Datatype},
                    src::Integer, tag::Integer, comm::Comm) where T
    req = Request()
    # int MPI_Irecv(void* buf, int count, MPI_Datatype datatype, int source,
    #               int tag, MPI_Comm comm, MPI_Request *request)
    @mpichk ccall((:MPI_Irecv, libmpi), Cint,
                  (MPIPtr, Cint, MPI_Datatype, Cint, Cint, MPI_Comm, Ptr{MPI_Request}),
                  buf, count, datatype, src, tag, comm, req)
    req.buffer = buf
    return req
end

"""
    Irecv!(buf::MPIBuffertype{T}, count::Integer, src::Integer, tag::Integer,
           comm::Comm) where T

Starts a nonblocking receive of up to `count` elements into `buf`
from MPI rank `src` of communicator `comm` using with the message tag `tag`

Returns the communication `Request` for the nonblocking receive.
"""
function Irecv!(buf, count::Integer,
                    src::Integer, tag::Integer, comm::Comm)
    Irecv!(buf, count, mpitype(eltype(buf)), src, tag, comm)
end

"""
    Irecv!(buf::Array{T}, src::Integer, tag::Integer, comm::Comm) where T

Starts a nonblocking receive into `buf` from MPI rank `src` of communicator
`comm` using with the message tag `tag`

Returns the communication `Request` for the nonblocking receive.
"""
function Irecv!(buf::AbstractArray{T}, src::Integer, tag::Integer,
                             comm::Comm) where T
    Irecv!(buf, length(buf), mpitype(T), src, tag, comm)
end

function irecv(src::Integer, tag::Integer, comm::Comm)
    (flag, stat) = Iprobe(src, tag, comm)
    if !flag
        return (false, nothing, nothing)
    end
    count = Get_count(stat, UInt8)
    buf = Array{UInt8}(undef, count)
    stat = Recv!(buf, Get_source(stat), Get_tag(stat), comm)
    (true, MPI.deserialize(buf), stat)
end

"""
    Wait!(req::Request)

Wait on the request `req` to be complete. Returns the `Status` of the request.
"""
function Wait!(req::Request)
    stat_ref = Ref{Status}()
    # int MPI_Wait(MPI_Request *request, MPI_Status *status)
    @mpichk ccall((:MPI_Wait, libmpi), Cint,
                  (Ptr{MPI_Request}, Ptr{Status}),
                  req, stat_ref)
    req.buffer = nothing
    stat
end

function Test!(req::Request)
    flag = Ref{Cint}()
    stat_ref = Ref{Status}()
    # int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status)
    @mpichk ccall((:MPI_Test, libmpi), Cint,
                  (Ptr{MPI_Request}, Ptr{Cint}, Ptr{Status}),
                  req, flag, stat_ref)
    if flag[] == 0
        return (false, nothing)
    end
    req.buffer = nothing
    (true, stat_ref[])
end

"""
    Waitall!(reqs::Vector{Request})

Wait on all the requests in the array `reqs` to be complete. Returns an arrays
of the all the requests statuses.
"""
function Waitall!(reqs::Vector{Request})
    count = length(reqs)
    reqvals = [reqs[i].val for i in 1:count]
    stats = Array{Status}(undef, count)
    # int MPI_Waitall(int count, MPI_Request array_of_requests[],
    #                 MPI_Status array_of_statuses[])
    @mpichk ccall((:MPI_Waitall, libmpi), Cint,
                  (Cint, Ptr{MPI_Request}, Ptr{Status}),
                  count, reqvals, stats)
    for i in 1:count
        reqs[i].val = reqvals[i]
        reqs[i].buffer = nothing
    end
    return stats
end

function Testall!(reqs::Vector{Request})
    count = length(reqs)
    reqvals = [reqs[i].val for i in 1:count]
    flag = Ref{Cint}()
    stats = Array{Status}(undef, count)
    # int MPI_Testall(int count, MPI_Request array_of_requests[], int *flag,
    #                 MPI_Status array_of_statuses[])
    @mpichk ccall((:MPI_Testall, libmpi), Cint,
                  (Cint, Ptr{MPI_Request}, Ptr{Cint}, Ptr{Status}),
                  count, reqvals, flag, stats)
    if flag[] == 0
        return (false, nothing)
    end
    for i in 1:count
        reqs[i].val = reqvals[i]
        reqs[i].buffer = nothing
    end
    (true, stats)
end

"""
    Waitany!(reqs::Vector{Request})

Wait on any the requests in the array `reqs` to be complete. Returns the index
of the completed request and its `Status` as a tuple.
"""
function Waitany!(reqs::Vector{Request})
    count = length(reqs)
    reqvals = [reqs[i].val for i in 1:count]
    ind = Ref{Cint}()
    stat_ref = Ref{Status}()
    # int MPI_Waitany(int count, MPI_Request array_of_requests[], int *index,
    #                 MPI_Status *status)
    @mpichk ccall((:MPI_Waitany, libmpi), Cint,
                  (Cint, Ptr{MPI_Request}, Ptr{Cint}, Ptr{Status}),
                  count, reqvals, ind, stat_ref)
    index = Int(ind[]) + 1
    reqs[index].val = reqvals[index]
    reqs[index].buffer = nothing
    (index, stat_ref[])
end

function Testany!(reqs::Vector{Request})
    count = length(reqs)
    reqvals = [req.val for req in reqs]
    ind = Ref{Cint}()
    flag = Ref{Cint}()
    stat_ref = Ref{Status}()
    # int MPI_Testany(int count, MPI_Request array_of_requests[], int *index,
    #                 int *flag, MPI_Status *status)
    @mpichk ccall((:MPI_Testany, libmpi), Cint,
                  (Cint, Ptr{MPI_Request}, Ptr{Cint}, Ptr{Cint}, Ptr{Status}),
                  count, reqvals, ind, flag, stat_ref)
    if flag[] == 0 || ind[] == MPI_UNDEFINED
        return (false, 0, nothing)
    end
    index = Int(ind[]) + 1
    reqs[index].val = reqvals[index]
    reqs[index].buffer = nothing
    (true, index, stat_ref[])
end

function Waitsome!(reqs::Vector{Request})
    count = length(reqs)
    reqvals = [reqs[i].val for i in 1:count]
    outcnt = Ref{Cint}()
    inds = Array{Cint}(undef, count)
    stats = Array{Status}(undef,count)
    # int MPI_Waitsome(int incount, MPI_Request array_of_requests[],
    #                  int *outcount, int array_of_indices[],
    #                  MPI_Status array_of_statuses[])
    @mpichk ccall((:MPI_Waitsome, libmpi), Cint,
          (Cint, Ptr{MPI_Request}, Ptr{Cint}, Ptr{Cint}, Ptr{Status}),
          count, reqvals, outcnt, inds, stats)
    outcount = Int(outcnt[])
    # This can happen if there were no valid requests
    if outcount == MPI_UNDEFINED
        outcount = 0
    end
    indices = Array{Int}(undef, outcount)
    for i in 1:outcount
        ind = Int(inds[i])+1
        reqs[ind].val = reqvals[ind]
        reqs[ind].buffer = nothing
        indices[i] = ind
    end
    (indices, stats)
end

function Testsome!(reqs::Vector{Request})
    count = length(reqs)
    reqvals = [req.val for req in reqs]
    outcnt = Ref{Cint}()
    inds = Array{Cint}(undef, count)
    stats = Array{Status}(undef, count)
    # int MPI_Testsome(int incount, MPI_Request array_of_requests[],
    #                  int *outcount, int array_of_indices[],
    #                  MPI_Status array_of_statuses[])
    @mpichk ccall((:MPI_Testsome, libmpi), Cint,
                  (Cint, Ptr{MPI_Request}, Ptr{Cint}, Ptr{Cint}, Ptr{Status}),
                  count, reqvals, outcnt, inds, stats)
    outcount = outcnt[]
    # This can happen if there were no valid requests
    if outcount == MPI_UNDEFINED
        outcount = 0
    end
    indices = Array{Int}(undef, outcount)
    for i in 1:outcount
        ind = Int(inds[i])+1
        reqs[ind].val = reqvals[ind]
        reqs[ind].buffer = nothing
        indices[i] = ind
    end
    (indices, stats)
end

function Cancel!(req::Request)
    # int MPI_Cancel(MPI_Request *request)
    @mpichk ccall((:MPI_Cancel, libmpi), Cint, (Ptr{MPI_Request},), req)
    req.buffer = nothing
    nothing
end


text:
function draw_axes(ax::Axis)
	vlines!(ax, 0.0, color=(:gray40, 0.6), linewidth=1)
	hlines!(ax, 0.0, color=(:gray40, 0.6), linewidth=1)
end

@doc raw"""
    viz_response(data,
                 title="", xlabel="time, t",
                 ylabel="output, y(t)",
                 savename=nothing)

plot `data[:, :output]` vs. `data[:, :t]` to visualize the response of a system to an input.
typically the data frame, `data`, is returned from [`simulate`](@ref).

# Arguments
* `data::DataFrame`: data frame of time series data, containing a `:t` column for times and `:output` column for the outputs.
* `title::String`: title of plot
* `xlabel::String`: x-label
* `ylabel::String`: y-label
* `savename::Union{Nothing, String}`: filename to save as a figure in .png format.

# Returns
`CairoMakie.jl` `Figure` object. this will display in a Pluto.jl notebook.

`CairoMakie.jl` commands can be invoked after `viz_response` to make further changes to the figure panel by e.g.:
```julia
fig = viz_response(data)
ax = current_axis(fig)
ax.xlabel = "new xlabel"
xlims!(ax, 0, 15)
```

# Example
```
g = 4 / (4 * s ^ 2 + 0.8 * s + 1)
U = 1 / s
Y = g * U
data = simulate(Y, 50.0)
fig = viz_response(data)
```
"""
function viz_response(data::DataFrame;
                      title::String="",
                      xlabel::String="time, t",
                      ylabel::String="output, y(t)",
                      savename::Union{Nothing, String}=nothing
    )

    fig = Figure()
    ax  = Axis(fig[1, 1], xlabel=xlabel, ylabel=ylabel, title=title)
    draw_axes(ax)
    lines!(data[:, :t], data[:, :output])
    if ! isnothing(savename)
        save(savename, fig, px_per_unit=1)
    end
    return fig
end

"""
    viz_poles_and_zeros(g, savename=nothing, title::String="poles and zeros")

plot the zeros and poles of the transfer function `g` in the complex plane.

returns a `CairoMakie.jl` `Figure` object for further modification.
"""
function viz_poles_and_zeros(tf::TransferFunction; savename::Union{Nothing, String}=nothing, title::String="poles and zeros")
    z, p, k = zeros_poles_gain(tf)

    fig = Figure()
    ax  = Axis(fig[1, 1], xlabel="Re", ylabel="Im", title=title)
    draw_axes(ax)
    scatter!(real.(z), imag.(z), marker=:circle, label="zeros", markersize=15)
    scatter!(real.(p), imag.(p), marker=:x, label="poles", markersize=15)
    axislegend()
    if ! isnothing(savename)
        save(savename, fig, px_per_unit=1)
    end
    return fig
end

"""
    nyquist_diagram(tf, nb_pts=500, ω_max=10.0, savename=nothing)

plot the Nyquist diagram for a transfer function `tf` to visualize its frequency response. `s=-1` is plotted as a red `+`. `nb_pts` changes the resolution. `ω_max` gives maximum frequency considered.

returns a `CairoMakie.jl` `Figure` object for further modification.
"""
function nyquist_diagram(tf::TransferFunction; nb_pts::Int=500, ω_max::Float64=10.0, savename::Union{Nothing, String}=nothing)
    ω_neg = range(-ω_max, 0.0, length=nb_pts)
    ω_pos = range(0.0, ω_max, length=nb_pts)

    g_iω_neg = [evaluate(tf, ω_i * im) for ω_i in ω_neg]
    g_iω_pos = [evaluate(tf, ω_i * im) for ω_i in ω_pos]

    fig = Figure()
	ax  = Axis(fig[1, 1], xlabel="Re[G(iω)]", ylabel="Im[G(iω)]", title="Nyquist diagram")
    draw_axes(ax)
    lines!(real(g_iω_neg), imag(g_iω_neg))
    lines!(real(g_iω_pos), imag(g_iω_pos))
    # plot -1
    scatter([-1], [0], marker=:+, markersize=15, color="red")
    if ! isnothing(savename)
        save(savename, fig, px_per_unit=1)
    end
    return fig
end

"""
    root_locus(g_ol, max_mag_Kc=10.0, nb_pts=500, savename=nothing)

visualize the root locus plot of an open-loop transfer function `g_ol`.

# Arguments
* `g_ol::TransferFunction`: the open-loop transfer function of the closed loop system
* `max_mag_Kc::Float64=10.0`: the maximum magnitude by which the gain of `g_ol` is
    scaled in order to see the roots traversing the plane
* `nb_pts::Int=500`: the number of gains to explore. increase for higher resolution.

# returns
a `CairoMakie.jl` `Figure` object for further modification.
"""
function root_locus(g_ol::TransferFunction;
        max_mag_Kc::Float64=10.0, nb_pts::Int=500, savename::Union{Nothing, String}=nothing)
    # compute zeros, poles, and gain of open loop transfer function
    z, p, k = zeros_poles_k(g_ol)

    # decide set of controller gains to explore
    #   make sure Kc is the same sign as gain of G_OL
    #   so G_OL has positive gain
    Kcs = k * 10.0 .^ range(-6, log(max_mag_Kc), length=nb_pts)
    pushfirst!(Kcs, 0.0)

    # store roots of characteristic eqn. here as Kc varies
    rloc = zeros(Complex, length(Kcs), length(p))
    fill!(rloc, NaN)

    # roots of 1 + G_OL when Kc = 0 are poles of G_OL
    rloc[1, :] = p

    # loop thru controller gains, compute roots of 1 + G_OL(s) = 0
    for i = 2:length(Kcs)
        # compute characteristic polynomial 1 + Kc * G_OL(s)
        c_poly = characteristic_polynomial(Kcs[i] * g_ol)
        # compute roots of characteristic polynomial
        roots_c_poly = roots(c_poly)
        # store roots
        entry_filled = [false for _ = 1:length(p)]
        for i_p = 1:length(roots_c_poly)
            ### find closest previous root
            # first, compute distances from each
            distance_to_previous_roots = abs.(rloc[i-1, :] .- roots_c_poly[i_p])
            distance_to_previous_roots[entry_filled] .= Inf
            # get closest
            id_closest_root = argmin(distance_to_previous_roots)
            entry_filled[id_closest_root] = true
            # store root in this entry
            rloc[i, id_closest_root] = roots_c_poly[i_p]
        end
    end

    fig = Figure()
	ax  = Axis(fig[1, 1], xlabel="Re", ylabel="Im", title="root locus")
    draw_axes(ax)
    # plot poles; corresponds to Kc = 0
    scatter!(real.(p), imag.(p), marker=:x, label="poles", markersize=15, color="black")
    # plot zeros; corresponds to |Kc| → ∞
    if length(z) > 0
        scatter!(real.(z), imag.(z), marker=:circle, label="zeros", markersize=15, color="black")
    end
    # plot roots traversing plane
    for i = 1:length(p)
        lines!(real.(rloc[:, i]), imag.(rloc[:, i]))
    end
    axislegend()
    if ! isnothing(savename)
        save(savename, fig, px_per_unit=1)
    end
    return fig
end

"""
    axs = bode_plot(tf, log10_ω_min=-4.0, log10_ω_max=4.0, nb_pts=300)

draw the Bode plot of a transfer function `tf` to visualize its frequency response.
returns the two axes of the plot for further tuning via `matplotlib` commands.

adjust the range of frequencies that the Bode plot presents with `log10_ω_min` and `log10_ω_max`.

increase the resolution of the Bode plot with `nb_pts`.

returns a `CairoMakie.jl` `Figure` object for further modification.
"""
function bode_plot(g::TransferFunction; log10_ω_min::Float64=-3.0, log10_ω_max::Float64=3.0,
                   nb_pts::Int=300, savename::Union{Nothing, String}=nothing)
    ω = 10.0 .^ range(log10_ω_min, log10_ω_max, length=nb_pts)
    g_iω = [evaluate(g, im * ω_i) for ω_i in ω]
    ∠g_iω = zeros(length(g_iω))

    circle_counter = 0
    ∠g_iω[1] = angle(g_iω[1])
    for i = 2:length(g_iω)
        ∠g_iω[i] = angle(g_iω[i]) - circle_counter * 2 * π
        if ∠g_iω[i] - ∠g_iω[i-1] > π
            ∠g_iω[i] -= 2 * π
            circle_counter += 1
        end
    end

    fig = Figure(resolution=(800, 600))
    axs = [Axis(fig[1, 1], xscale=log10, yscale=log10,
		        ylabel="|g(iω)|", title="Bode plot"),
           Axis(fig[2, 1], xscale=log10,
			    xlabel="ω", ylabel="∠g(iω)")
			]
    linkxaxes!(axs...)
	for ax in axs # to make plot easy to read
		ax.xticksvisible = true
		ax.xminorticksvisible = true
		ax.xminorticks = IntervalsBetween(9)
		ax.xminorgridvisible = true

		ax.yticksvisible = true
		ax.yminorticksvisible = true
		ax.yminorticks = IntervalsBetween(9)
		ax.yminorgridvisible = true
	end
	axs[2].yticks = MultiplesTicks(4, pi, "π")
    lines!(axs[1], ω, abs.(g_iω))
    lines!(axs[2], ω, ∠g_iω / π)
    if ! isnothing(savename)
        save(savename, fig, px_per_unit=1)
    end
    return fig
end

@doc raw"""
    mk_gif(data, title="", xlabel="time, t",
                 ylabel="output, y(t)",
                 savename="response")

make a .gif of the process response.
`data` is a data frame with two columns, `:t` and `:output`, likely returned from [`simulate`](@ref).
accepts same arguments as [`viz_response`](@ref).
ImageMagick must be installed to create the .gif.
the .gif is saved as a file `savename`.

# Arguments
* `data::DataFrame`: data frame of time series data, containing a `:t` column for times and `:output` column for the outputs.
* `title::String`: title of plot
* `xlabel::String`: x-label
* `ylabel::String`: y-label
* `savename::String`: filename to save as a .gif. .gif extension automatically appended if not provided.
"""
function mk_gif(data::DataFrame;
                title::String="",
                xlabel::String="time, t",
                ylabel::String="output, y(t)",
                savename::String="response.gif"
    )
    if nrow(data) > 999
        error("too many points and thus images; reduce the number of points")
    end

    # determine x-, y-limits
    Δx = maximum(data[:, :t]) - minimum(data[:, :t])
    Δy = maximum(data[:, :output]) - minimum(data[:, :output])
    xlims = (minimum(data[:, :t])      - 0.01 * Δx, maximum(data[:, :t])      + 0.01 * Δx)
    ylims = (minimum(data[:, :output]) - 0.01 * Δy, maximum(data[:, :output]) + 0.01 * Δy)

    # name to save image i as.
    step_to_image(i::Int) = @sprintf("__y_of_t_snapshot_%03d.png", i)

    # save series of images
    for i = 1:nrow(data)
        fig = viz_response(data[1:i, :], title=title, xlabel=xlabel, ylabel=ylabel)
        ax = current_axis()
        xlims!(ax, xlims...)
        ylims!(ax, ylims...)
        save(step_to_image(i), fig)
    end

    if ! occursin(".gif", savename)
        savename *= ".gif"
    end
    try
        run(`convert -delay 20 -loop 0 __y_of_t_snapshot_\*.png $savename`)
        @info "see " * savename
    catch
        @warn "You must install ImageMagick for `convert` to run. Exiting..."
    end

    # clean up
    for i = 1:nrow(data)
        rm(step_to_image(i))
    end
end

max_stars_repo_name: SimonEnsemble/Controlz

max_issues_repo_name: SimonEnsemble/Controlz

max_forks_repo_name: SimonEnsemble/Controlz


text:
# SpinnakerGUI.jl

using Spinnaker

function cam_init(;camid::Int64=0,silent=false,bufferMode="NewestOnly")
    global camSettings
    !silent && (@info "Checking for cameras")
    camlist = Spinnaker.CameraList()
    if length(camlist) == 0
        error("No camera found")
    else
        !silent && (@info "Selecting camera $camid")
        cam = camlist[camid]
        !silent && (@info "Switching to continuous mode")
        acquisitionmode!(cam,"Continuous")
        triggermode!(cam,"Off")
        !silent && (@info "Reading settings from camera")
        camSettingsRead!(cam,camSettings)
        camSettingsLimitsRead!(cam,camSettingsLimits)
        buffermode!(cam,bufferMode)
        !silent && (@info "Camera ready")
    end
    return cam
end

function canGetImage(cam)
    try
        getimage(cam,timeout=2000)
        return true
    catch e
        return false
    end
end

"""
startcheckrunningfix!(cam;bufferMode="NewestOnly")

Attempt a start!(cam) and check if able to grab images. If not, repeatedly reinitialize camera until it works,
up to attempsmax (5 by default)
This function exists because of instability experienced with the Grasshopper 3.

"""
function startcheckrunningfix!(cam;bufferMode="NewestOnly",maxattempts=5)
    try
        start!(cam)
    catch e
        reinitcam(bufferMode=bufferMode,maxattempts=maxattempts)
    end
    if canGetImage(cam)
        @info "Camera started"
    else
        reinitcam(bufferMode=bufferMode,maxattempts=maxattempts)
    end
end

function reinitcam(;bufferMode="NewestOnly",maxattempts=5)
    global cam
    @info "Camera Issue: Reinitializing camera"
    attempts = 0
    while !canGetImage(cam) || attempts < maxattempts
        isrunning(cam) && stop!(cam)
        cam = cam_init(silent=true,bufferMode=bufferMode)
        start!(cam)
        attempts += 1
    end
    if attempts == maxattempts
        error("Camera couldn't be reinitialized (tried $attempts times)")
    else
        @info "Camera reinitialized"
    end
end

function runCamera()
    global perfGrabFramerate
    global cam, camImage, camImageFrameBuffer
    global camSettings, camSettingsLimits
    global sessionStat
    global camImageFrameBuffer

    camImage = Array{UInt8}(undef,camSettings.width,camSettings.height)

    startcheckrunningfix!(cam,bufferMode="OldestFirst")
    camSettingsLimitsRead!(cam,camSettingsLimits) #some things change once running

    perfGrabTime = time()
    grabNotRunningTimer = Timer(0.0,interval=1/5)
    while !sessionStat.terminate
        if isrunning(cam)
            if sessionStat.resolutionupdate
                camImage = Array{UInt8}(undef,camSettings.width,camSettings.height)
                sessionStat.resolutionupdate = false
            end
            try
                cim_id, cim_timestamp, cim_exposure = getimage!(cam,camImage,normalize=false,timeout=0)
                if sessionStat.recording
                    push!(camImageFrameBuffer,copy(camImage))
                    sessionStat.bufferedframes = length(camImageFrameBuffer)
                    sessionStat.droppedframes = bufferunderrun(cam)
                end
                # Loop timing
                perfGrabFramerate = 1/(time() - perfGrabTime)
                perfGrabTime = time()
            catch err
                if occursin("SPINNAKER_ERR_TIMEOUT(-1011)",sprint(showerror, err))
                    # No frame available
                    #println("No frame available")
                elseif err isa EOFError || occursin("SPINNAKER_ERR_IO(-1010)",sprint(showerror, err))
                    @warn "Noncritical framegrab error"
                else
                    rethrow()
                end
            end
            yield()
        else
            wait(grabNotRunningTimer)
            #println("Not running")
        end

    end
    isrunning(cam) && stop!(cam)

end


text:
using DiffEqProblemLibrary, Sundials, Base.Test

prob = prob_ode_2Dlinear
integrator = init(prob,CVODE_BDF())
step!(integrator)
integrator(integrator.t,Val{1})

for i in integrator
    @show i.t
end

integrator.t == 1.0
step!(integrator)
integrator.t > 1.0


text:
"""
Test whether a floating point number is close to
zero.

Arguments
---------
number:AbstractFloat
    input number

threshold=1e-7:AbstractFloat
    (optional) threshold for the being nearly zero
"""
function nearly_zero(number::AbstractFloat, threshold::AbstractFloat=1e-7)
    return abs(number) < threshold
end


text:
using InfiniteOpt, Ipopt, Plots
using KNITRO

# Parmeters
# Time span
t0 = 0
tf = 1
# Initial values
x1 = 5

# Model
model = InfiniteModel(KNITRO.Optimizer)

# infinite_parameter
@infinite_parameter(model, t ∈ [t0, tf], num_supports = 101)

# variable
@variable(model, x , Infinite(t))

@variable(model, 0 ≤ u ≤ 2, Infinite(t))

# objective
@objective(model, Max, ∫(2*x-3*u, t))

# constraint
@constraint(model, x(0) == x1)
@constraint(model, x_constr, ∂(x, t) == x + u)

# Optimization

print(model)

optimize!(model)

# Visulization
ts = value(t)
u = value(u)
x = value(x)

@show plot(ts, u)
@show plot(ts, x)


text:
struct MaxUCB <: Function
    c::Float64
end

function (mu::MaxUCB)(n::StateNode)
    bestucb = -Inf
    best = first(children(n))
    tn = totaln(n)
    for sanode in children(n)
        if c == 0 || tn == 0 || (tn == 1 && n(sanode) == 0)
            ucb = q(sanode)
        else
            ucb = q(sanode) + c*sqrt(log(tn)/n(sanode))
        end
        @assert !isnan(ucb)
        @assert !isequal(ucb, -Inf)
        if ucb > bestucb
            bestucb = ucb
            best = sanode
        end
    end
    return best
end

maxtried(n::StateNode) = reduce(children(n)) do an1, an2
    if n(an1) > n(an2)
        return an1
    else
        return an2
    end
end


text:
####
# This macro overrides mul! to call lazymul!
####

# support mul! by calling lazy mul
macro lazymul(Typ)
    ret = quote
        LinearAlgebra.mul!(dest::AbstractVector, A::$Typ, b::AbstractVector) =
            copyto!(dest, LazyArrays.Mul(A,b))

        LinearAlgebra.mul!(dest::AbstractMatrix, A::$Typ, b::AbstractMatrix) =
            copyto!(dest, LazyArrays.Mul(A,b))
        LinearAlgebra.mul!(dest::AbstractMatrix, A::$Typ, b::$Typ) =
            copyto!(dest, LazyArrays.Mul(A,b))

        Base.:*(A::$Typ, B::$Typ, C...) = LazyArrays.apply(*,A,B,C...)
        Base.:*(A::$Typ, B::AbstractMatrix, C...) = LazyArrays.apply(*,A,B,C...)
        Base.:*(A::$Typ, B::AbstractVector) = LazyArrays.apply(*,A,B)
        Base.:*(A::$Typ, B::LazyArrays.LazyVector) = LazyArrays.apply(*,A,B)
        Base.:*(A::AbstractMatrix, B::$Typ, C...) = LazyArrays.apply(*,A,B,C...)
        Base.:*(A::LinearAlgebra.AdjointAbsVec, B::$Typ, C...) = LazyArrays.apply(*,A,B,C...)
        Base.:*(A::LinearAlgebra.TransposeAbsVec, B::$Typ, C...) = LazyArrays.apply(*,A,B,C...)
    end
    if Typ ≠ :LazyMatrix
        ret = quote
            $ret
            Base.:*(A::$Typ, B::LazyArrays.LazyMatrix, C...) = LazyArrays.apply(*,A,B,C...)
            Base.:*(A::LazyArrays.LazyMatrix, B::$Typ, C...) = LazyArrays.apply(*,A,B,C...)
        end
    end
    for Struc in (:AbstractTriangular, :Diagonal)
        ret = quote
            $ret

            Base.:*(A::LinearAlgebra.$Struc, B::$Typ, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::$Typ, B::LinearAlgebra.$Struc, C...) = LazyArrays.apply(*,A,B, C...)
        end
    end
    for Mod in (:Adjoint, :Transpose, :Symmetric, :Hermitian)
        ret = quote
            $ret

            LinearAlgebra.mul!(dest::AbstractMatrix, A::$Typ, b::$Mod{<:Any,<:AbstractMatrix}) =
                copyto!(dest, LazyArrays.Mul(A,b))

            LinearAlgebra.mul!(dest::AbstractVector, A::$Mod{<:Any,<:$Typ}, b::AbstractVector) =
                copyto!(dest, LazyArrays.Mul(A,b))

            Base.:*(A::$Mod{<:Any,<:$Typ}, B::$Mod{<:Any,<:$Typ}, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::$Mod{<:Any,<:$Typ}, B::AbstractMatrix, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::AbstractMatrix, B::$Mod{<:Any,<:$Typ}, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::$Mod{<:Any,<:$Typ}, B::AbstractVector, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::$Mod{<:Any,<:$Typ}, B::LazyArrays.LazyVector, C...) = LazyArrays.apply(*,A,B, C...)

            Base.:*(A::$Mod{<:Any,<:$Typ}, B::$Typ, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::$Typ, B::$Mod{<:Any,<:$Typ}, C...) = LazyArrays.apply(*,A,B, C...)

            Base.:*(A::$Mod{<:Any,<:$Typ}, B::Diagonal, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::Diagonal, B::$Mod{<:Any,<:$Typ}, C...) = LazyArrays.apply(*,A,B, C...)

            Base.:*(A::LinearAlgebra.AbstractTriangular, B::$Mod{<:Any,<:$Typ}, C...) = LazyArrays.apply(*,A,B, C...)
            Base.:*(A::$Mod{<:Any,<:$Typ}, B::LinearAlgebra.AbstractTriangular, C...) = LazyArrays.apply(*,A,B, C...)
        end
        if Typ ≠ :LazyMatrix
            ret = quote
                $ret
                Base.:*(A::$Mod{<:Any,<:$Typ}, B::LazyArrays.LazyMatrix, C...) = LazyArrays.apply(*,A,B, C...)
            end
        end
    end

    esc(ret)
end

macro lazyldiv(Typ)
    esc(quote
        LinearAlgebra.ldiv!(A::$Typ, x::AbstractVector) = LazyArrays.materialize!(LazyArrays.Ldiv(A,x))
        LinearAlgebra.ldiv!(A::$Typ, x::AbstractMatrix) = LazyArrays.materialize!(LazyArrays.Ldiv(A,x))
        LinearAlgebra.ldiv!(A::$Typ, x::StridedVector) = LazyArrays.materialize!(LazyArrays.Ldiv(A,x))
        LinearAlgebra.ldiv!(A::$Typ, x::StridedMatrix) = LazyArrays.materialize!(LazyArrays.Ldiv(A,x))

        Base.:\(A::$Typ, x::AbstractVector) = LazyArrays.apply(\,A,x)
        Base.:\(A::$Typ, x::AbstractMatrix) = LazyArrays.apply(\,A,x)
    end)
end

@lazymul LazyMatrix
@lazyldiv LazyMatrix

*(A::AbstractMatrix, b::LazyVector) where T = apply(*,A,b)
*(A::Adjoint{<:Any,<:AbstractMatrix{T}}, b::LazyVector) where T = apply(*,A,b)


text:
# Model
include_model("double_integrator")

# Horizon
T = 11

tf = 1.0
h0 = tf / (T-1) # timestep

# Initial and final states
x1 = [1.0; 0.0]
xT = [0.0; 0.0]

xl, xu = state_bounds(model, T, x1 = x1, xT = xT)

# Objective
obj = quadratic_tracking_objective(
        [t < T ? Diagonal(ones(model.n)) : Diagonal(ones(model.n)) for t = 1:T],
        [Diagonal(1.0 * ones(model.m)) for t = 1:T-1],
        [xT for t = 1:T],
		[zeros(model.m) for t = 1:T])

# Problem
prob = trajectory_optimization_problem(model,
			   obj,
			   T,
               xl = xl,
               xu = xu)

# Initialization
x0 = linear_interpolation(x1, xT, T) # linear interpolation for states
u0 = [ones(model.m) for t = 1:T-1]

# Pack trajectories into vector
z0 = pack(x0, u0, prob)

# Solve
# include_snopt() # set path
# @time z, info = solve(prob, copy(z0), nlp = :SNOPT7)
@time z, info = solve(prob, copy(z0), nlp = :ipopt)

# Visualize
using Plots
x, u = unpack(z, prob)
plot(hcat(x...)', width = 2.0)
plot(hcat(u...)', width = 2.0, linetype = :steppost)

max_issues_repo_name: thowell/DirectMotionPlanning


text:
using MLStyle
using DataFrames

include("MQuery.ConstantNames.jl")
include("MQuery.DynamicInfer.jl")
include("MQuery.Interfaces.jl")
include("MQuery.MacroProcessor.jl")
include("MQuery.Impl.jl")

using Base.Enums
@enum TypeChecking Dynamic Static
df = DataFrame(
        Symbol("Type checking") => [
            Dynamic, Static, Static, Dynamic, Static, Dynamic, Dynamic, Static
        ],
        :name => [
            "Julia", "C#", "F#", "Ruby", "Java", "JavaScript", "Python", "Haskell"
        ],
        :year => [
            2012, 2000, 2005, 1995, 1995, 1995, 1990, 1990
        ]
)

df |>
@where !startswith(_.name, "Java"),
@groupby _."Type checking" => TC, endswith(_.name, "#") => is_sharp,
@having TC === Dynamic || is_sharp,
@select join(_.name, " and ") => result, _.TC => TC

max_stars_repo_name: thautwarm/MLStyle-Playground

max_issues_repo_name: cherichy/MLStyle-Playground

max_forks_repo_name: cherichy/MLStyle-Playground


text:
"""
Create a new dataset with this driver.

What argument values are legal for particular drivers is driver specific,
and there is no way to query in advance to establish legal values.

The function will try to validate the creation option list passed to the driver
with the `GDALValidateCreationOptions()` method. This check can be disabled by
defining the configuration option `GDAL_VALIDATE_CREATION_OPTIONS=NO`.

After you have finished working with the returned dataset, it is required to
close it with GDALClose(). This does not only close the file handle, but also
ensures that all the data and metadata has been written to the dataset
(GDALFlushCache() is not sufficient for that purpose).

In GDAL 2, the arguments nXSize, nYSize and nBands can be passed to 0 when
creating a vector-only dataset for a compatible driver.

### Parameters
* `pszFilename`     the name of the dataset to create. UTF-8 encoded.
* `nXSize`          width of created raster in pixels.
* `nYSize`          height of created raster in pixels.
* `nBands`          number of bands.
* `eType`           type of raster.
* `options`         list of driver specific control parameters.

### Returns
`NULL` on failure, or a new `GDALDataset`.
"""
_create(hDriver::GDALDriverH,
        pszFilename::Ptr{UInt8},
        nXSize::Integer,
        nYSize::Integer,
        nBands::Integer,
        eType::GDALDataType,
        options::Ptr{Ptr{UInt8}}) =
    GDALCreate(hDriver, pszFilename, nXSize, nYSize,
               nBands, eType, options)::GDALDatasetH

function createdataset(driver::GDALDriverH,
                       filename::Union{String,String},
                       width::Integer,
                       height::Integer,
                       nband::Integer,
                       eType::GDALDataType,
                       options::Vector{String}=Vector{String}())
    pfilename = pointer(filename)
    dataset = _create(driver, pfilename, width, height, nband, eType,
                      Ptr{Ptr{UInt8}}(pointer(options)))
    (dataset == C_NULL) && error("Failed to create dataset")
    dataset
end

"""
Create a copy of a dataset.

This method will attempt to create a copy of a raster dataset with the
indicated filename, and in this drivers format. Band number, size, type,
projection, geotransform and so forth are all to be copied from the
provided template dataset.

Note: many sequential write once formats (such as JPEG and PNG) don't implement
the `Create()` method but do implement this `CreateCopy()` method. If the
driver doesn't implement `CreateCopy()`, but does implement `Create()` then the
default `CreateCopy()` mechanism built on calling `Create()` will be used.

It is intended that `CreateCopy()` will often be used with a source dataset
which is a virtual dataset allowing configuration of band types, and other
information without actually duplicating raster data (see the VRT driver). This
is what is done by the gdal_translate utility for example.

This function will validate the creation option list passed to the driver
with the `GDALValidateCreationOptions()` method. This check can be disabled by
defining the configuration option `GDAL_VALIDATE_CREATION_OPTIONS=NO`.

After you have finished working with the returned dataset, it is required to
close it with `GDALClose()`. This does not only close the file handle, but also
ensures that all the data and metadata has been written to the dataset
(`GDALFlushCache()` is not sufficient for that purpose).

In some situations, the new dataset can be created in another process through
the GDAL API Proxy mechanism.

### Parameters
* `pszFilename`     the name for the new dataset. UTF-8 encoded.
* `poSrcDS`         the dataset being duplicated.
* `bStrict`         `TRUE` if the copy must be strictly equivelent, or more
normally `FALSE` if the copy may adapt as needed for the output format.
* `papszOptions`    additional format dependent options controlling creation
of the output file. `The APPEND_SUBDATASET=YES` option can be specified to
avoid prior destruction of existing dataset.
* `pfnProgress`     a function to be used to report progress of the copy.
* `pProgressData`   application data passed into progress function.

### Returns
a pointer to the newly created dataset (may be read-only access).
"""
_createcopy(driver::GDALDriverH,
            pszFilename::Ptr{UInt8},
            poSrcDS::GDALDatasetH,
            bStrict::Integer,
            papszOptions::Ptr{Ptr{UInt8}},
            pfnProgress::GDALProgressFunc,
            pProgressData::Ptr{Void}) =
    GDALCreateCopy(driver, pszFilename, poSrcDS, bStrict, papszOptions,
                   pfnProgress, pProgressData)::GDALDatasetH

function createcopy(driver::GDALDriverH,
                    filename::Union{String,String},
                    source::GDALDatasetH,
                    bStrict::Bool = false,
                    options::Vector{String} = Vector{String}(),
                    pfnProgress::GDALProgressFunc = C_NULL,
                    pProgressData::Ptr{Void} = C_NULL)
    source == C_NULL && error("NULL source dataset")
    dataset = _createcopy(driver, pointer(filename), source, bStrict,
                          Ptr{Ptr{UInt8}}(pointer(options)), pfnProgress,
                          pProgressData)
    dataset == C_NULL && error("Failed to copy dataset")
    dataset
end

"""
Open a raster file as a GDALDataset.

This function will try to open the passed file, or virtual dataset name by
invoking the Open method of each registered `GDALDriver` in turn. The first
successful open will result in a returned dataset. If all drivers fail then
`NULL` is returned and an error is issued.

Several recommendations:

If you open a dataset object with `GA_Update` access, it is not recommended to
open a new dataset on the same underlying file. The returned dataset should
only be accessed by one thread at a time. To use it from different threads,
you must add all necessary code (mutexes, etc.) to avoid concurrent use of the
object. (Some drivers, such as GeoTIFF, maintain internal state variables that
are updated each time a new block is read, preventing concurrent use.)

For drivers supporting the VSI virtual file API, it is possible to open a file
in a `.zip` archive (see `VSIInstallZipFileHandler()`), a `.tar/.tar.gz/.tgz`
archive (see `VSIInstallTarFileHandler()`), or a HTTP / FTP server
(see `VSIInstallCurlFileHandler()`)

In some situations (dealing with unverified data), the datasets can be opened
in another process through the GDAL API Proxy mechanism.

See also: `GDALOpenShared()`, `GDALOpenEx()`

### Parameters
* `pszFilename`  the name of the file to access. In the case of exotic drivers
this may not refer to a physical file, but instead contain information for the
driver on how to access a dataset. It should be in UTF-8 encoding.
* `eAccess`      the desired access, either `GA_Update` or `GA_ReadOnly`. Many
drivers support only read only access.

### Returns
A `GDALDatasetH` handle or `NULL` on failure.
"""
_open(pszFilename::Ptr{UInt8}, eAccess::GDALAccess) =
    GDALOpen(pszFilename, eAccess)::GDALDatasetH

function opendataset(filename::String, access::GDALAccess=GA_ReadOnly)
    dataset = _open(pointer(filename), access)
    dataset == C_NULL && error("Could not open file \"$filename\"")
    dataset
end

"""
Open a raster file as a `GDALDataset`.

This function works the same as `GDALOpen()`, but allows the sharing of
`GDALDataset` handles for a dataset with other callers to `GDALOpenShared()`.

`GDALOpenShared()` will first consult its list of currently open and shared
`GDALDataset`s, and if the `GetDescription()` name for one exactly matches the
`pszFilename` passed to `GDALOpenShared()` it will be referenced and returned.

Starting with GDAL 1.6.0, if `GDALOpenShared()` is called on the same
`pszFilename` from two different threads, a different `GDALDataset` object will
be returned as it is not safe to use the same dataset from different threads,
unless the user does explicitly use mutexes in its code.

For drivers supporting the VSI virtual file API, it is possible to open a file
in a `.zip` archive (see `VSIInstallZipFileHandler()`), a `.tar/.tar.gz/.tgz`
archive (see `VSIInstallTarFileHandler()`) or on a HTTP / FTP server
(see `VSIInstallCurlFileHandler()`)

In some situations (dealing with unverified data), the datasets can be opened
in another process through the GDAL API Proxy mechanism.

### Parameters
* `pszFilename`   the name of the file to access. In the case of exotic drivers
this may not refer to a physical file, but instead contain information for the
driver on how to access a dataset. It should be in UTF-8 encoding.
* `eAccess`       the desired access, either `GA_Update` or `GA_ReadOnly`. Many
drivers support only read only access.

### Returns
A `GDALDatasetH` handle or `NULL` on failure.
"""
_openshared(pszFilename::Ptr{UInt8}, eAccess::GDALAccess) =
    GDALOpenShared(pszFilename, eAccess)::GDALDatasetH

"""
Close GDAL dataset.

For non-shared datasets (opened with `GDALOpen()`) the dataset is closed
using the C++ "delete" operator, recovering all dataset related resources.

For shared datasets (opened with `GDALOpenShared()`) the dataset is
dereferenced, and closed only if the referenced count has dropped below 1.

### Parameters
* `hDS`     The dataset to close.
"""
_close(dataset::GDALDatasetH) = GDALClose(dataset)

"""
Fetch files forming dataset.

Returns a list of files believed to be part of this dataset. If it returns an
empty list of files it means there is believed to be no local file system files
associated with the dataset (for instance a virtual dataset). The returned file
list is owned by the caller and should be deallocated with `CSLDestroy()`.

The returned filenames will normally be relative or absolute paths depending on
the path used to originally open the dataset. The strings will be UTF-8 encoded

### Returns
`NULL` or a `NULL` terminated array of file names.
"""
_getfilelist(dataset::GDALDatasetH) = GDALGetFileList(dataset)::Ptr{Ptr{UInt8}}

getfilelist(dataset::GDALDatasetH) = loadstringlist(_getfilelist(dataset))


text:
include("../src/delta.jl")

function test_delta_poly_length()
	print("delta_poly.length...")

	@test length(delta_poly(5)) == 5
	@test length(delta_poly(500)) == 500

	println("PASS")
end

function test_delta_poly_output()
	print("delta_poly.output...")

	R, q = PolynomialRing(ZZ, "q")
	delta_7 = 	q - 24q^2 + 252q^3 - 1472q^4 + 4830q^5 - 6048q^6
	delta_30 = 	q - 24q^2 + 252q^3 - 1472q^4 + 4830q^5 - 6048q^6 - 16744q^7 +
			84480q^8 - 113643q^9 - 115920q^10 + 534612q^11 - 370944q^12 -
			577738q^13 + 401856q^14 + 1217160q^15 + 987136q^16 - 6905934q^17 +
			2727432q^18 + 10661420q^19 - 7109760q^20 - 4219488q^21 -
			12830688q^22 + 18643272q^23 + 21288960q^24 - 25499225q^25 +
			13865712q^26 - 73279080q^27 + 24647168q^28 + 128406630q^29
	@test delta_7 == delta_poly(7)
	@test delta_30 == delta_poly(30)

	println("PASS")
end

function test_delta_qexp_output()
	print("delta_qexp.output...")

	delta_5 = delta_qexp(5)
	S = parent(delta_5)
	q = gen(S)
  d5 = q - 24q^2 + 252q^3 - 1472q^4 + O(q^5)
	@test delta_5 - d5 == 0

	delta_45 = delta_qexp(45, "q")
	S = parent(delta_45)
	q = gen(S)
	d45 =	q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 -
		16744*q^7 + 84480*q^8 - 113643*q^9 - 115920*q^10 + 534612*q^11 -
		370944*q^12 - 577738*q^13 + 401856*q^14 + 1217160*q^15 +
		987136*q^16 - 6905934*q^17 + 2727432*q^18 + 10661420*q^19 -
		7109760*q^20 - 4219488*q^21 - 12830688*q^22 + 18643272*q^23 +
		21288960*q^24 - 25499225*q^25 + 13865712*q^26 - 73279080*q^27 +
		24647168*q^28 + 128406630*q^29 - 29211840*q^30 - 52843168*q^31 -
		196706304*q^32 + 134722224*q^33 + 165742416*q^34 -
		80873520*q^35 + 167282496*q^36 - 182213314*q^37 - 255874080*q^38 -
		145589976*q^39 + 408038400*q^40 + 308120442*q^41 +
		101267712*q^42 - 17125708*q^43 - 786948864*q^44 + O(q^45)
	@test delta_45 - d45 == 0

	println("PASS")
end

function test_delta_qexp_var()
	print("delta_qexp.variable...")

	delta_x = delta_qexp(10, "x")
	S = parent(delta_x)
	x = gen(S)
	dx =	x - 24x^2 + 252x^3 - 1472x^4 + 4830x^5 - 6048x^6 -
		16744x^7 + 84480x^8 - 113643x^9 + O(x^10)
	@test delta_x - dx == 0

	println("PASS")
end

function test_delta()
	test_delta_poly_length()
	test_delta_poly_output()
	test_delta_qexp_output()
	test_delta_qexp_var()

	println("")
end


text:
function mark_all(r::Robot)
    nun_vert = get_num_steps_movements!(r,Sud)
    nun_hor = get_num_steps_movements!(r,West)
    #УТВ: Робот - в юго-западном углу

    side = Ost
    mark_row!(r,side)
    while isborder!(r,Nord)==false
        side=inverse(side)
        mark_row!(r,side)
    end
    #УТВ: Робот - у северной границы, в одном из углов

    movements!(r,Sud)
    movements!(r,West)
    #УТВ: Робот - в юго-западном углу

    movemens!(r,Ost,num_hor)
    movemens!(r,Nord,num_vert)
end


text:
"""
## One-electron integrals
### Overlap matrix elements
"""

function overlap( a::PGBF, b::PGBF )
  return a.norm*b.norm*overlap(a.expn,a.x,a.y,a.z,a.I,a.J,a.K,
  b.expn,b.x,b.y,b.z,b.I,b.J,b.K)
end

overlap(a::CGBF,b::CGBF) = contract(overlap,a,b)

function overlap(aexpn::Float64,ax::Float64,ay::Float64,az::Float64,
         aI::Int64,aJ::Int64,aK::Int64,
         bexpn::Float64,bx::Float64,by::Float64,bz::Float64,
         bI::Int64,bJ::Int64,bK::Int64)
  gamma = aexpn+bexpn
  px,py,pz = gaussian_product_center(aexpn,ax,ay,az,bexpn,bx,by,bz)
  rab2 = dist2(ax-bx,ay-by,az-bz)
  pre = (pi/gamma)^1.5*exp(-aexpn*bexpn*rab2/gamma)
  wx = overlap1d(aI,bI,px-ax,px-bx,gamma)
  wy = overlap1d(aJ,bJ,py-ay,py-by,gamma)
  wz = overlap1d(aK,bK,pz-az,pz-bz,gamma)
  return pre*wx*wy*wz
end

function gaussian_product_center(a::PGBF,b::PGBF)
  return (a.expn*[a.x,a.y,a.z]+b.expn*[b.x,b.y,b.z])/(a.expn+b.expn)
end

function gaussian_product_center(aexpn::Float64,ax::Float64,ay::Float64,az::Float64,
                  bexpn::Float64,bx::Float64,by::Float64,bz::Float64)
  return (aexpn*[ax,ay,az]+bexpn*[bx,by,bz])/(aexpn+bexpn)
end

function overlap1d(la::Int64,lb::Int64,ax::Float64,bx::Float64,gamma::Float64)
  total = 0
  for i in 0:div(la+lb,2)
    total += binomial_prefactor(2i,la,lb,ax,bx)*factorial2(2i-1)/(2gamma)^i
  end
  return total
end

function binomial_prefactor(s::Int64,ia::Int64,ib::Int64,xpa::Float64,xpb::Float64)
  #println("binomial_prefactor($s,$ia,$ib,$xpa,$xpb)")
  total = 0
  for t in 0:s
    if (s-ia) <= t <= ib
      total += binomial(ia,s-t)*binomial(ib,t)*xpa^(ia-s+t)*xpb^(ib-t)
    end
  end
  return total
end


text:
# Example 2

using Base.Iterators
using Arbitrary

# Define your own type
struct Point{T}
    x::T
    y::T
end

# Define a method for Arbitrary.arbitrary
function Arbitrary.arbitrary(::Type{Point{T}}, ast::ArbState) where {T}
    xs = Iterators.Stateful(arbitrary(T, ast))
    flatten([Point{T}[Point(T(0), T(0)),
                      Point(T(0), T(1)),
                      Point(T(1), T(0)),
                      Point(T(-1), T(-1))],
             Generate{Point{T}}(
                 () -> Point(popfirst!(xs), popfirst!(xs)))])
end

collect(take(arbitrary(Point{Int}), 20))