text:
int_rules_3_1_4 = @theory begin

    #= ::Subsection::Closed:: =#
    #= 3.1.4*(f*x)^m*(d+e*x^r)^q*(a+b*log(c*x^n))^p =#
    @apply_utils Antiderivative((~x) ^ ~(m') * (~d + ~(e') / ~x) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => Antiderivative((~e + ~d * ~x) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~m, ~n, ~p], ~x) && (EqQ(~m, ~q) && IntegerQ(~q))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => With([u = IntHide((~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ ~q, ~x)], u * (~a + ~b * log(~c * (~x) ^ ~n)) - ~b * ~n * Antiderivative(SimplifyIntegrand(u / ~x, ~x), ~x)) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n, ~r], ~x) && (IGtQ(~q, 0) && IGtQ(~m, 0))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => With([u = IntHide((~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ ~q, ~x)], Dist(~a + ~b * log(~c * (~x) ^ ~n), u) - ~b * ~n * Antiderivative(SimplifyIntegrand(u / ~x, ~x), ~x)) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n, ~r], ~x) && (IGtQ(~q, 0) && (IntegerQ(~m) && Not(EqQ(~q, 1) && EqQ(~m, -1))))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => ((~f * ~x) ^ (~m + 1) * (~d + ~e * (~x) ^ ~r) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n))) / (~d * ~f * (~m + 1)) - ((~b * ~n) / (~d * (~m + 1))) * Antiderivative((~f * ~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ (~q + 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~q, ~r], ~x) && (EqQ(~m + ~r * (~q + 1) + 1, 0) && NeQ(~m, -1))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~r) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~n)) ^ ~(p'), ~x) => ((~f) ^ ~m / ~n) * Subst(Antiderivative((~d + ~e * ~x) ^ ~q * (~a + ~b * log(~c * ~x)) ^ ~p, ~x), ~x, (~x) ^ ~n) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~q, ~r], ~x) && (EqQ(~m, ~r - 1) && (IGtQ(~p, 0) && ((IntegerQ(~m) || GtQ(~f, 0)) && EqQ(~r, ~n))))
    @apply_utils Antiderivative(((~(f') * ~x) ^ ~(m') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p')) / (~d + ~(e') * (~x) ^ ~r), ~x) => ((~f) ^ ~m * log(1 + (~e * (~x) ^ ~r) / ~d) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / (~e * ~r) - ((~b * (~f) ^ ~m * ~n * ~p) / (~e * ~r)) * Antiderivative((log(1 + (~e * (~x) ^ ~r) / ~d) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ (~p - 1)) / ~x, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~r], ~x) && (EqQ(~m, ~r - 1) && (IGtQ(~p, 0) && ((IntegerQ(~m) || GtQ(~f, 0)) && NeQ(~r, ~n))))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~r) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => ((~f) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / (~e * ~r * (~q + 1)) - ((~b * (~f) ^ ~m * ~n * ~p) / (~e * ~r * (~q + 1))) * Antiderivative(((~d + ~e * (~x) ^ ~r) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ (~p - 1)) / ~x, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~q, ~r], ~x) && (EqQ(~m, ~r - 1) && (IGtQ(~p, 0) && ((IntegerQ(~m) || GtQ(~f, 0)) && (NeQ(~r, ~n) && NeQ(~q, -1)))))
    @apply_utils Antiderivative((~f * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~r) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => ((~f * ~x) ^ ~m / (~x) ^ ~m) * Antiderivative((~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~q, ~r], ~x) && (EqQ(~m, ~r - 1) && (IGtQ(~p, 0) && Not(IntegerQ(~m) || GtQ(~f, 0))))
    #= Antiderivative((~x)^(~m')*((~a')+(~b')*log((~c')*(~x)^(~n')))^(~p')/((~d)+(~e')*(~x)^(~r')),~x)  := 1/e*Antiderivative(x^(m-r)*(a+b*log(c*x^n))^p,x) - d/e*Antiderivative((x^(m-r)*(a+b*log(c*x^n))^p)/(d+e*x^r),x) <--  FreeQ([a,b,c,d,e,m,n,r],x) && IGtQ(p,0) && IGtQ(r,0) && IGeQ(m-r,0) =#
    @apply_utils Antiderivative((~(a') + ~(b') * log(~(c') * (~x) ^ ~n)) / (~x * (~d + ~(e') * (~x) ^ ~(r'))), ~x) => (1 / ~n) * Subst(Antiderivative((~a + ~b * log(~c * ~x)) / (~x * (~d + ~e * (~x) ^ (~r / ~n))), ~x), ~x, (~x) ^ ~n) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n, ~r], ~x) && IntegerQ(~r / ~n)
    #= Antiderivative(((~a')+(~b')*log((~c')*(~x)^(~n')))^(~p')/((~x)*((~d)+(~e')*(~x))),~x) :=  1/d*Antiderivative((a+b*log(c*x^n))^p/x,x) -  e/d*Antiderivative((a+b*log(c*x^n))^p/(d+e*x),x) <-- FreeQ([a,b,c,d,e,n],x) &&  IGtQ(p,0) =#
    #= Antiderivative(((~a')+(~b')*log((~c')*(~x)^(~n')))^(~p')/((~x)*((~d)+(~e')*(~x)^(~r'))),~x) :=   (r*log(x)-log(1+(e*x^r)/d))*(a+b*log(c*x^n))^p/(d*r) - b*n*p/d*Antiderivative(log(x)*(a+b*log(c*x^n))^(p-1)/x,x) + b*n*p/(d*r)*Antiderivative(log(1+(e*x^r)/d)*(a+b*log(c*x^n))^(p-1)/x,x) <--  FreeQ([a,b,c,d,e,n,r],x) && IGtQ(p,0) =#
    @apply_utils Antiderivative((~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p') / (~x * (~d + ~(e') * (~x) ^ ~(r'))), ~x) => (-(log(1 + ~d / (~e * (~x) ^ ~r))) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / (~d * ~r) + ((~b * ~n * ~p) / (~d * ~r)) * Antiderivative((log(1 + ~d / (~e * (~x) ^ ~r)) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ (~p - 1)) / ~x, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n, ~r], ~x) && IGtQ(~p, 0)
    @apply_utils Antiderivative(((~x) ^ ~(m') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p')) / (~d + ~(e') * (~x) ^ ~(r')), ~x) => (1 / ~d) * Antiderivative((~x) ^ ~m * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) - (~e / ~d) * Antiderivative(((~x) ^ (~m + ~r) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / (~d + ~e * (~x) ^ ~r), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~m, ~n, ~r], ~x) && (IGtQ(~p, 0) && (IGtQ(~r, 0) && ILtQ(~m, -1)))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * ~x) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => (-((~f * ~x) ^ (~m + 1)) * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / (~d * ~f * (~q + 1)) + ((~b * ~n * ~p) / (~d * (~q + 1))) * Antiderivative((~f * ~x) ^ ~m * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ (~p - 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~q], ~x) && (EqQ(~m + ~q + 2, 0) && (IGtQ(~p, 0) && LtQ(~q, -1)))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~d + ~(e') * ~x) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => With([u = IntHide((~x) ^ ~m * (~d + ~e * ~x) ^ ~q, ~x)], Dist(~a + ~b * log(~c * (~x) ^ ~n), u, ~x) - ~b * ~n * Antiderivative(SimplifyIntegrand(u / ~x, ~x), ~x)) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n], ~x) && (ILtQ(~m + ~q + 2, 0) && IGtQ(~m, 0))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * ~x) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => (-((~f * ~x) ^ (~m + 1)) * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / (~d * ~f * (~q + 1)) + ((~m + ~q + 2) / (~d * (~q + 1))) * Antiderivative((~f * ~x) ^ ~m * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) + ((~b * ~n * ~p) / (~d * (~q + 1))) * Antiderivative((~f * ~x) ^ ~m * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ (~p - 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~n], ~x) && (ILtQ(~m + ~q + 2, 0) && (IGtQ(~p, 0) && (LtQ(~q, -1) && GtQ(~m, 0))))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * ~x) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => ((~f * ~x) ^ ~m * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n))) / (~e * (~q + 1)) - (~f / (~e * (~q + 1))) * Antiderivative((~f * ~x) ^ (~m - 1) * (~d + ~e * ~x) ^ (~q + 1) * (~a * ~m + ~b * ~n + ~b * ~m * log(~c * (~x) ^ ~n)), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n], ~x) && (ILtQ(~q, -1) && GtQ(~m, 0))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ 2) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => (-((~f * ~x) ^ (~m + 1)) * (~d + ~e * (~x) ^ 2) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n))) / (2 * ~d * ~f * (~q + 1)) + (1 / (2 * ~d * (~q + 1))) * Antiderivative((~f * ~x) ^ ~m * (~d + ~e * (~x) ^ 2) ^ (~q + 1) * (~a * (~m + 2 * ~q + 3) + ~b * ~n + ~b * (~m + 2 * ~q + 3) * log(~c * (~x) ^ ~n)), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n], ~x) && (ILtQ(~q, -1) && ILtQ(~m, 0))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~d + ~(e') * (~x) ^ 2) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => (((~d) ^ IntPart(~q) * (~d + ~e * (~x) ^ 2) ^ FracPart(~q)) / (1 + (~e / ~d) * (~x) ^ 2) ^ FracPart(~q)) * Antiderivative((~x) ^ ~m * (1 + (~e / ~d) * (~x) ^ 2) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n], ~x) && (IntegerQ(~m / 2) && (IntegerQ(~q - 1 / 2) && Not(LtQ(~m + 2 * ~q, -2) || GtQ(~d, 0))))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~d1 + ~(e1') * ~x) ^ ~q * (~d2 + ~(e2') * ~x) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => (((~d1 + ~e1 * ~x) ^ ~q * (~d2 + ~e2 * ~x) ^ ~q) / (1 + ((~e1 * ~e2) / (~d1 * ~d2)) * (~x) ^ 2) ^ ~q) * Antiderivative((~x) ^ ~m * (1 + ((~e1 * ~e2) / (~d1 * ~d2)) * (~x) ^ 2) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)), ~x) <-- FreeQ([~a, ~b, ~c, ~d1, ~e1, ~d2, ~e2, ~n], ~x) && (EqQ(~d2 * ~e1 + ~d1 * ~e2, 0) && (IntegerQ(~m) && IntegerQ(~q - 1 / 2)))
    @apply_utils Antiderivative(((~d + ~(e') * ~x) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p')) / ~x, ~x) => ~d * Antiderivative(((~d + ~e * ~x) ^ (~q - 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / ~x, ~x) + ~e * Antiderivative((~d + ~e * ~x) ^ (~q - 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n], ~x) && (IGtQ(~p, 0) && (GtQ(~q, 0) && IntegerQ(2 * ~q)))
    @apply_utils Antiderivative(((~d + ~(e') * ~x) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p')) / ~x, ~x) => (1 / ~d) * Antiderivative(((~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / ~x, ~x) - (~e / ~d) * Antiderivative((~d + ~e * ~x) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n], ~x) && (IGtQ(~p, 0) && (LtQ(~q, -1) && IntegerQ(2 * ~q)))
    @apply_utils Antiderivative(((~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n')))) / ~x, ~x) => With([u = IntHide((~d + ~e * (~x) ^ ~r) ^ ~q / ~x, ~x)], u * (~a + ~b * log(~c * (~x) ^ ~n)) - ~b * ~n * Antiderivative(Dist(1 / ~x, u, ~x), ~x)) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n, ~r], ~x) && IntegerQ(~q - 1 / 2)
    @apply_utils Antiderivative(((~d + ~(e') * (~x) ^ ~(r')) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p')) / ~x, ~x) => (1 / ~d) * Antiderivative(((~d + ~e * (~x) ^ ~r) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / ~x, ~x) - (~e / ~d) * Antiderivative((~x) ^ (~r - 1) * (~d + ~e * (~x) ^ ~r) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~n, ~r], ~x) && (IGtQ(~p, 0) && ILtQ(~q, -1))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => With([u = IntHide((~f * ~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ ~q, ~x)], Dist(~a + ~b * log(~c * (~x) ^ ~n), u, ~x) - ~b * ~n * Antiderivative(SimplifyIntegrand(u / ~x, ~x), ~x) <-- (EqQ(~r, 1) || EqQ(~r, 2)) && (IntegerQ(~m) && IntegerQ(~q - 1 / 2)) || InverseFunctionFreeQ(u, ~x)) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~q, ~r], ~x) && (IntegerQ(2 * ~q) && (IntegerQ(~m) && IntegerQ(~r) || IGtQ(~q, 0)))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))), ~x) => With([u = ExpandIntegrand(~a + ~b * log(~c * (~x) ^ ~n), (~f * ~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ ~q, ~x)], Antiderivative(u, ~x) <-- SumQ(u)) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~q, ~r], ~x) && (IntegerQ(~q) && (GtQ(~q, 0) || IntegerQ(~m) && IntegerQ(~r)))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~n)) ^ ~(p'), ~x) => (1 / ~n) * Subst(Antiderivative((~x) ^ (Simplify((~m + 1) / ~n) - 1) * (~d + ~e * (~x) ^ (~r / ~n)) ^ ~q * (~a + ~b * log(~c * ~x)) ^ ~p, ~x), ~x, (~x) ^ ~n) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~m, ~n, ~p, ~q, ~r], ~x) && (IntegerQ(~q) && (IntegerQ(~r / ~n) && (IntegerQ(Simplify((~m + 1) / ~n)) && (GtQ((~m + 1) / ~n, 0) || IGtQ(~p, 0)))))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => With([u = ExpandIntegrand((~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, (~f * ~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ ~q, ~x)], Antiderivative(u, ~x) <-- SumQ(u)) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~p, ~q, ~r], ~x) && (IntegerQ(~q) && (GtQ(~q, 0) || IGtQ(~p, 0) && (IntegerQ(~m) && IntegerQ(~r))))
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~d + ~(e') * (~x) ^ ~(r')) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => Unintegrable((~f * ~x) ^ ~m * (~d + ~e * (~x) ^ ~r) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~m, ~n, ~p, ~q, ~r], ~x)
    @apply_utils Antiderivative((~(f') * ~x) ^ ~(m') * (~u) ^ ~(q') * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => Antiderivative((~f * ~x) ^ ~m * ExpandToSum(~u, ~x) ^ ~q * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~f, ~m, ~n, ~p, ~q], ~x) && (BinomialQ(~u, ~x) && Not(BinomialMatchQ(~u, ~x)))
    @apply_utils Antiderivative((~f + ~(g') * ~x) ^ ~(m') * (~d + ~(e') * ~x) ^ ~q * (~(a') + ~(b') * log(~(c') * (~x) ^ ~(n'))) ^ ~(p'), ~x) => ((~f + ~g * ~x) ^ (~m + 1) * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ ~p) / ((~q + 1) * (~e * ~f - ~d * ~g)) - ((~b * ~n * ~p) / ((~q + 1) * (~e * ~f - ~d * ~g))) * Antiderivative(((~f + ~g * ~x) ^ (~m + 1) * (~d + ~e * ~x) ^ (~q + 1) * (~a + ~b * log(~c * (~x) ^ ~n)) ^ (~p - 1)) / ~x, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~e, ~f, ~g, ~m, ~n, ~q], ~x) && (NeQ(~e * ~f - ~d * ~g, 0) && (EqQ(~m + ~q + 2, 0) && (IGtQ(~p, 0) && LtQ(~q, -1))))
end

max_stars_repo_path: src/rules/3 Logarithms/3.1.4 (f x)^m (d+e x^r)^q (a+b log(c x^n))^p.jl


text:
function PositionAngleDetector(arg0::OrbitType, arg1::PositionAngle, arg2::jdouble)
    return PositionAngleDetector((OrbitType, PositionAngle, jdouble), arg0, arg1, arg2)
end

function PositionAngleDetector(arg0::jdouble, arg1::jdouble, arg2::OrbitType, arg3::PositionAngle, arg4::jdouble)
    return PositionAngleDetector((jdouble, jdouble, OrbitType, PositionAngle, jdouble), arg0, arg1, arg2, arg3, arg4)
end

function g(obj::PositionAngleDetector, arg0::SpacecraftState)
    return jcall(obj, "g", jdouble, (SpacecraftState,), arg0)
end

function get_angle(obj::PositionAngleDetector)
    return jcall(obj, "getAngle", jdouble, ())
end

function get_orbit_type(obj::PositionAngleDetector)
    return jcall(obj, "getOrbitType", OrbitType, ())
end

function get_position_angle(obj::PositionAngleDetector)
    return jcall(obj, "getPositionAngle", PositionAngle, ())
end

function init(obj::PositionAngleDetector, arg0::SpacecraftState, arg1::AbsoluteDate)
    return jcall(obj, "init", void, (SpacecraftState, AbsoluteDate), arg0, arg1)
end

ext: jl


text:
include("meta.jl")

mutable struct EngineerSpec
    input::Dict{String, Any}
    output::Dict{String, Any}
    error::Dict{String, Any}
end

mutable struct EngineerStatus
    status::Dict{String, Any}
end

mutable struct Engineer
    typemeta::TypeMeta
    objectmeta::ObjectMeta
    spec::EngineerSpec
    status::EngineerStatus
end

max_stars_repo_name: pbarker/kubemeta

max_issues_repo_name: pbarker/kubemeta

max_forks_repo_name: pbarker/kubemeta


text:
kmax = 10

# general partitionize-based intensity function
Ω = partitionIntensityFunction(p->sum(p), kmax)
@test typeof(Ω) == IntensityFunction

# all-or-nothing cut
Ω = allOrNothingIntensityFunction(x->(x[1]+1)^(-x[2]), kmax)
@test typeof(Ω) == IntensityFunction

# number of unique elements in z: generalizes all-or-nothing
Ω = sumOfExteriorDegreesIntensityFunction((x, α)->(x[1]+1)^(-α[x[2]]*x[2]), kmax)
@test typeof(Ω) == IntensityFunction

max_stars_repo_name: nveldt/TestPkg

max_issues_repo_name: nveldt/TestPkg

max_forks_repo_name: nveldt/TestPkg


text:
using Test, LinearMaps, LinearAlgebra, SparseArrays, Quaternions

@testset "conversion" begin
    A = 2 * rand(ComplexF64, (20, 10)) .- 1
    v = rand(ComplexF64, 10)
    w = rand(ComplexF64, 20)
    V = rand(ComplexF64, 10, 3)
    W = rand(ComplexF64, 20, 3)
    α = rand()
    β = rand()
    M = @inferred LinearMap(A)
    N = @inferred LinearMap(M)

    @test Matrix(M) == A
    @test Array(M) == A
    @test convert(AbstractArray, M) == A
    @test convert(AbstractMatrix, M) === A
    @test convert(Matrix, M) === A
    @test convert(Array, M) === A
    @test convert(AbstractMatrix, M) == A
    @test convert(Matrix, M) == A
    @test convert(Array, M) == A
    @test Matrix(M') == A'
    @test Matrix(transpose(M)) == copy(transpose(A))
    @test convert(AbstractMatrix, M') isa Adjoint
    @test convert(Matrix, M*3I) == A*3
    @test convert(Matrix, M+M) == A + A

    # UniformScalingMap
    J = LinearMap(α*I, 10)
    JM = convert(AbstractMatrix, J)
    @test JM == Diagonal(fill(α, 10))

    # ScaledMap
    q = rand(ComplexF64)
    A = rand(ComplexF64, 3, 3)
    @test convert(Matrix, q*LinearMap(A)) ≈ q*A
    qAs = convert(SparseMatrixCSC, q*LinearMap(A))
    @test qAs ≈ q*A
    @test qAs isa SparseMatrixCSC

    # CompositeMap of MatrixMap and UniformScalingMap
    q = Quaternion(rand(4)...)
    A = Quaternion.(rand(3,3), rand(3,3), rand(3,3), rand(3,3))
    for mat in (Matrix, SparseMatrixCSC)
        Aqmat = convert(mat, LinearMap(A)*q)
        @test Aqmat ≈ A*q
        @test Aqmat isa mat
        qAmat = convert(mat, q*LinearMap(A))
        @test qAmat ≈ q*A
        @test qAmat isa mat
    end

    # sparse matrix generation/conversion
    @test sparse(M) == sparse(Array(M))
    @test convert(SparseMatrixCSC, M) == sparse(Array(M))

    B = copy(A)
    B[rand(1:length(A), 30)] .= 0
    MS = LinearMap(B)
    @test sparse(MS) == sparse(Array(MS)) == sparse(B)

    J = LinearMap(I, 10)
    @test J isa LinearMap{Bool}
    @test sparse(J) == Matrix{Bool}(I, 10, 10)
    @test nnz(sparse(J)) == 10
end


text:
module JLL

using GAP
using GAP_pkg_cvec_jll

function __init__()
    # ensure GAP finds kernel extensions or other binaries
    sopath = joinpath(GAP_pkg_cvec_jll.find_artifact_dir(), "lib", "gap")
    @debug "GAP package 'cvec' sopath = " * sopath
    setproperty!(GAP.Globals.DirectoriesPackageProgramsOverrides, :cvec, GapObj(sopath))
end

end # module


text:
module Currency

import ForwardDiff: Dual
import ..extract_derivative
import Base.promote_type
export CurrencyUnit, CurrencyQuantity, unit, USD, GBP, currval

struct CurrencyUnit{S}
end

struct CurrencyQuantity{U<:CurrencyUnit,T<:Real} <: Number
    val::T
    CurrencyQuantity{U,T}(val::T) where {U<:CurrencyUnit,T<:Real} = new(val)
end
CurrencyQuantity{S}(x::T) where {S,T<:Real} = CurrencyQuantity{S,T}(x)

import Base: show, zero, one, length, float, convert
show(io::IO, x::CurrencyUnit{S}) where {S} = print(io, S)
show(io::IO, x::CurrencyQuantity{U}) where {U} = print(io, x.val, U())

unit(x::CurrencyQuantity{U}) where {U} = U()
unit(x::Real) = one(x)

zero(::CurrencyQuantity{U,T}) where {U,T} = CurrencyQuantity{U,T}(zero(T))
zero(::Type{CurrencyQuantity{U,T}}) where {U,T} = CurrencyQuantity{U,T}(zero(T))
one(::CurrencyQuantity{U,T}) where {U,T} = CurrencyQuantity{U,T}(one(T))
one(::Type{CurrencyQuantity{U,T}}) where {U,T} = CurrencyQuantity{U,T}(one(T))

convert(::Type{CurrencyQuantity{U,S}}, x::CurrencyQuantity{U,T}) where {U,S,T} = CurrencyQuantity{U,S}(convert(S,x.val))
float(x::CurrencyQuantity{U,T}) where {U,T} = CurrencyQuantity{U}(float(x.val))

# Make CurrencyUnit broadcast as a scalar
Base.Broadcast.broadcastable(c::CurrencyUnit) = Ref(c)

import Base: +, -, *, /, abs, ==, <, <=, div, rem, isless, isequal, isapprox

(-)(x::CurrencyQuantity{U}) where {U<:CurrencyUnit} = CurrencyQuantity{U}(-x.val)

(+)(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U<:CurrencyUnit} = CurrencyQuantity{U}(x.val+y.val)
(-)(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U<:CurrencyUnit} = CurrencyQuantity{U}(x.val-y.val)

(*)(::U, x::Real) where {U<:CurrencyUnit} = CurrencyQuantity{U}(x)
(*)(x::Real, ::U) where {U<:CurrencyUnit} = CurrencyQuantity{U}(x)

(*)(x::CurrencyQuantity{U}, y::Real) where {U<:CurrencyUnit} = CurrencyQuantity{U}(x.val*y)
(*)(x::Real, y::CurrencyQuantity{U}) where {U<:CurrencyUnit} = CurrencyQuantity{U}(x*y.val)

(/)(x::CurrencyQuantity{U}, y::Real) where {U<:CurrencyUnit} = CurrencyQuantity{U}(x.val/y)
(/)(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U<:CurrencyUnit} = x.val/y.val
(/)(x::CurrencyQuantity{U}, ::U) where {U<:CurrencyUnit} = x.val

abs(x::CurrencyQuantity{U}) where {U} = CurrencyQuantity{U}(abs(x.val))

(==)(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U} = x.val == y.val
(<)(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U} = x.val < y.val
(<=)(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U} = x.val <= y.val

div(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U} = div(x.val, y.val)
rem(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U} = CurrencyQuantity{U}(rem(x.val, y.val))

isless(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U} = isless(x.val, y.val)
isequal(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}) where {U} = isequal(x.val, y.val)

function isapprox(x::CurrencyQuantity{U}, y::CurrencyQuantity{U}; kwargs...) where U
    isapprox(x.val, y.val; kwargs...)
end

function (::Base.Colon)(start::CurrencyQuantity{U}, stop::CurrencyQuantity{U}) where {U}
    r = start.val:stop.val
    StepRangeLen{CurrencyQuantity{U,eltype(r)}}(r.ref, r.step, r.len, r.offset)
end
function (::Base.Colon)(start::CurrencyQuantity{U}, step::CurrencyQuantity{U}, stop::CurrencyQuantity{U}) where {U}
    r = start.val:step.val:stop.val
    StepRangeLen{CurrencyQuantity{U,eltype(r)}}(r.ref, r.step, r.len, r.offset)
end

macro defcurrency(S)
    :($(esc(S)) = CurrencyUnit{$(QuoteNode(S))}())
end
@defcurrency USD
@defcurrency GBP

# Play nice with Dual Numbers
Dual(x::CurrencyQuantity{U,T}, v) where {U,T} = Dual(x.val, v) * U()

promote_type(::Type{CurrencyQuantity{S,T}}, ::Type{V}) where {S,T,V} =
    CurrencyQuantity{S,promote_type(T,V)}
promote_type(::Type{CurrencyQuantity{S,T}}, ::Type{D}) where {S,T,D<:Dual} =
    CurrencyQuantity{S,promote_type(T,D)}

extract_derivative(x::CurrencyQuantity{S, D}) where {S,D<:Dual} =
    extract_derivative(x.val) * S()
currval(x::CurrencyQuantity{S,V}) where {S,V} = x.val
currval(x::T) where {T<:Real} = x

end # module Currencies


text:
function readBIOM(path::String, type::Type=BIOM)::Dict{String, Any}
    dict = h5open(path, "r") do h5
        read(h5)
    end
    if type == Dict
        return dict
    elseif type == BIOM
        return BIOM(dict)
    else
        throw(ArgumentError("$(type) not implemented."))
    end
end

function isBIOM(path::String)::Bool
    di = readBIOM(path, Dict)
    isBIOM(di)
end

function isBIOM(di::Dict{String, Any})::Bool
    k₁ = keys(di)
    if  length(k₁) != 2 && ["sample", "observation"] ⊆ k₁
        return false
    else
        k₂ₒ = keys(di["observation"])
        k₂ₛ = keys(di["sample"])
        if  length(k₂ₒ) != 4 && ["ids", "matrix", "metadata", "group-metadata"] ⊆ k₂ₒ &&
            length(k₂ₛ) != 4 && ["ids", "matrix", "metadata", "group-metadata"] ⊆ k₂ₛ
            return false
        else
            k₃ₒ = keys(di["observation"]["matrix"])
            k₃ₛ = keys(di["sample"]["matrix"])
            if  length(k₃ₒ) != 3 && ["data", "indptr", "indices"] ⊆ k₃ₒ &&
                length(k₃ₛ) != 3 && ["data", "indptr", "indices"] ⊆ k₃ₛ
                return false
            else
                return true
            end
        end
    end

end

function writeBIOM(path::String, df::DataFrame;
                    sample_meta::Union{Nothing,Dict{String, <:Any}}       = nothing,
                    sample_group_meta::Union{Nothing,Dict{String, <:Any}} = nothing,
                    obs_meta::Union{Nothing,Dict{String, <:Any}}          = nothing,
                    obs_group_meta::Union{Nothing,Dict{String, <:Any}}    = nothing)
    if  ["sample", "observation", "data"] ⊈ names(df)
        Throw(ArgumentError("Something bad happend."))
    end
    dfₒ   = sort(df, :observation)
    dfₛ   = sort(df, :sample)
	dataₒ = Array{Float64}(dfₒ.data)
	samₒ  = Array{Int32}(dfₒ.sample)
	obsₒ  = Array{Int32}([[findfirst(==(i), dfₒ.observation)-1 for i in unique(dfₒ.observation)]... , length(dfₒ.observation)])
	idₒ   = Array{String}(string.(sort(unique(dfₒ.observation))))
    dataₛ = Array{Float64}(dfₛ.data)
	obsₛ  = Array{Int32}(dfₛ.observation)
	samₛ  = Array{Int32}([[findfirst(==(i), dfₛ.sample)-1 for i in unique(dfₛ.sample)]... , length(dfₛ.sample)])
	idₛ   = Array{String}(string.(sort(unique(dfₛ.sample))))
    HDF5.h5write(path, "sample/ids", idₛ)
    HDF5.h5write(path, "sample/matrix/data", dataₛ)
    HDF5.h5write(path, "sample/matrix/indptr", samₛ)
    HDF5.h5write(path, "sample/matrix/indices",obsₛ)
    HDF5.h5write(path, "observation/ids", idₒ)
    HDF5.h5write(path, "observation/matrix/data", dataₒ)
    HDF5.h5write(path, "observation/matrix/indptr", obsₒ)
    HDF5.h5write(path, "observation/matrix/indices", samₒ)
    if sample_meta !== nothing
        for k in keys(sample_meta)
            HDF5.h5write(path, "sample/metadata/$(k)", sample_meta[k])
        end
    end
    if sample_group_meta !== nothing
        for k in keys(sample_group_meta)
            HDF5.h5write(path, "sample/group-metadata/$(k)", sample_group_meta[k])
        end
    end
    if obs_meta !== nothing
        for k in keys(obs_meta)
            HDF5.h5write(path, "observation/metadata/$(k)", obs_meta[k])
        end
    end
    if obs_group_meta !== nothing
        for k in keys(obs_group_meta)
            HDF5.h5write(path, "sample/group-metadata/$(k)", obs_group_meta[k])
        end
    end
end


text:
MatchingUInt(::Type{_Vec{W,Float64}}) where {W} = _Vec{W,UInt64}
MatchingUInt(::Type{Tuple{VecUnroll{N,W,Float64,Vec{W,Float64}}}}) where {N,W} = VecUnroll{N,W,UInt64,Vec{W,UInt6}}

@generated MatchingUInt(::Type{Vec{W,Float32}}) where {W} = Vec{W >>> 1,UInt64}
# @generated MatchingUInt(::Type{Tuple{Vec{W,Float32},Vararg{Vec{W,Float32},N}}}) where {N,W} = Tuple{Vec{W>>>1,UInt64},Vararg{Vec{W>>>1,UInt64},N}}
@generated MatchingUInt(::Type{VecUnroll{N,W,Float32,Vec{W,Float32}}}) where {N,W} = VecUnroll{N,W>>>1,UInt64,Vec{W>>>1,UInt64}}

# @generated MatchingFloat32(::Type{Vec{W,UInt64}}) where {W} = Vec{W<<1,Float32}
# @generated MatchingUInt32(::Type{Vec{W,UInt64}}) where {W} = Vec{W<<1,UInt32}

@inline function Random.rand(rng::AbstractVRNG{P}, ::Type{Vec{W,UInt64}}) where {W,P}
    state = getstate(rng, Val{1}(), StaticInt{W}())
    state, out = nextstate(state, Val{1}())
    storestate!(rng, state)
    data(out)[1]
end
@inline function Random.rand(rng::AbstractVRNG{P}, ::Type{VecUnroll{N,W,UInt64,Vec{W,UInt64}}}) where {W,P,N}
    state = getstate(rng, Val{P}(), StaticInt{W}())
    state, out = nextstate(state, Val{N}())
    storestate!(rng, state)
    out
end

@inline random_uniform(u::AbstractSIMD{W,UInt64}, ::Type{T}) where {W,T} = floatbitmask(u, T) - oneopenconst(T)
# @inline random_uniform(u::Vec{W,UInt64}, ::Type{Float32}) where {W} = floatbitmask(u, Float32) - oneopenconst(Float32)
# @inline random_uniform(u::Vec{W,UInt64}, ::Type{Float32}) where {W} = vsub(floatbitmask(vreinterpret(Vec{W+W,UInt32}, u), Float32), oneopenconst(Float32))
# @generated function random_uniform(u::VecUnroll{N,W,UInt64}, ::Type{T}) where {N,W,T}
#     Expr(
#         :block,
#         Expr(:meta,:inline),
#         Expr(:tuple, [Expr(:call, :random_uniform, Expr(:macrocall, Symbol("@inbounds"), LineNumberNode(@__LINE__,Symbol(@__FILE__)), Expr(:ref, :u, n)), T) for n ∈ 1:N+1]...)
#     )
# end
@inline function Random.rand(rng::AbstractVRNG, ::Type{VecUnroll{N,W,T,Vec{W,T}}}) where {N,W,T<:Union{Float32,Float64}}
    random_uniform(rand(rng, MatchingUInt(VecUnroll{N,W,T,Vec{W,T}})), T)
end
"""
Samples uniformly from (0.0,1.0)
"""
@inline function Random.rand(rng::AbstractVRNG, ::Type{Vec{W,Float64}}) where {W}
    u = rand(rng, Vec{W,UInt64})
    random_uniform(u, Float64)
end
@generated function Random.rand(rng::AbstractVRNG, ::Type{Vec{W,T}}) where {W,T}
    L = (W * sizeof(T)) >> 3
    @assert L << 3 == W * sizeof(T)
    quote
        $(Expr(:meta,:inline))
        u = rand(rng, Vec{$L,UInt64})
        random_uniform(u, $T)
    end
end
"""
if l < u,
Samples uniformly from [l,u)
else,
Samples uniformly from (u,l]

That is, the "l" side of the interval is closed, and the "u" side is open.
"""
@inline function Random.rand(rng::AbstractVRNG, ::Type{V}, l::T, u::T) where {W,T<:Union{Float32,Float64},V <: AbstractSIMD{W,T}}
    s = u - l
    b = l - s
    u = rand(rng, MatchingUInt(Vec{W,T}))
    vfmadd(s, floatbitmask(u, T), b)
end
@inline oneopenconst(::Type{Float64}) = 0.9999999999999999
@inline oneopenconst(::Type{Float32}) = 0.99999994f0

# @generated function Random.rand(rng::AbstractVRNG, ::Type{NTuple{N,Vec{W,T}}}, l::T, u::T) where {N,W,T<:Union{Float32,Float64}}
#     quote
#         $(Expr(:meta,:inline))
#         s = u - l
#         b = l - s
#         u = rand(rng, MatchingUInt(NTuple{N,Vec{W,T}}))
#         $(Expr(:tuple, [:(vfmadd(s, floatbitmask(@inbounds(u[$n]), T), b)) for n ∈ 1:N]...))
#     end
# end

@inline function randnormal(u1::AbstractSIMD{W,UInt64}, u2::AbstractSIMD{W,UInt64}, ::Type{T}) where {W,T<:Union{Float32,Float64}}
    s, c = randsincos(u1, T)
    r = sqrt(nlog01(u2,T))
    s * r, c * r
end
@inline function randnormal(u1::AbstractSIMD{1,UInt64}, u2::AbstractSIMD{1,UInt64}, ::Type{Float64})
    s, c = randsincos(u1(1), Float64)
    r = sqrt(nlog01(u2(1), Float64))
    Vec{1,Float64}((Core.VecElement(s * r),)), Vec{1,Float64}((Core.VecElement(c * r),))
end

@generated function random_normal(vu::VecUnroll{Nm1,W,UInt64,Vec{W,UInt64}}, ::Type{T}) where {Nm1,W,T}
    # @assert isodd(Nm1)
    N = Nm1 + 1
    q = Expr(:block, Expr(:meta, :inline), :(u = data(vu)))
    ib = Expr(:block)
    n = 0
    if n < Nm1
        u1t = Expr(:tuple); u2t = Expr(:tuple)
        while n < Nm1
            push!(u1t.args, Expr(:ref, :u, n+1))
            push!(u2t.args, Expr(:ref, :u, n+2))
            # push!(ib.args, Expr(:(=), Expr(:tuple, Symbol(:n_,n), Symbol(:n_,n+1)), Expr(:call, :randnormal, Expr(:ref, :u, n+1), Expr(:ref, :u, n+2), T)))
            n += 2
        end
        push!(ib.args, :((sr,cr) = randnormal(VecUnroll($u1t), VecUnroll($u2t), $T)))
        push!(ib.args, :(srd = data(sr))); push!(ib.args, :(crd = data(cr)))
    end
    nout = Expr(:tuple)
    for n ∈ 1:N>>1
        push!(nout.args, Expr(:ref, :srd, n))
        push!(nout.args, Expr(:ref, :crd, n))
    end
    if n < N # then there is odd remainder
        # we split the vector in two, gen randnormal, and then recombine.
        Wl = (W << 3) ÷ sizeof(T)
        Wh = Wl >>> 1
        t1 = Expr(:tuple); t2 = Expr(:tuple); t3 = Expr(:tuple);
        append!(t1.args, 0:Wh-1); append!(t2.args, Wh:Wl-1); append!(t3.args, 0:Wl-1)
        lm = Expr(:call, Expr(:curly, :Val, t1))
        um = Expr(:call, Expr(:curly, :Val, t2))
        cm = Expr(:call, Expr(:curly, :Val, t3))
        remq = quote
            ulast = u[$N]
            (sₗ, cᵤ) = randnormal(shufflevector(ulast, $lm), shufflevector(ulast, $um), $T)
        end
        push!(ib.args, remq)
        push!(nout.args, :(shufflevector(sₗ, cᵤ, $cm)))
    end
    push!(ib.args, :(nout = $nout))
    push!(q.args, Expr(:macrocall, Symbol("@inbounds"), LineNumberNode(@__LINE__, Symbol(@__FILE__)), ib))
                                   # push!(q.args, Expr(:tuple, [Symbol(:n_,n) for n ∈ 0:N-1]...))
    push!(q.args, :(VecUnroll(nout)))
    q
end

@inline Random.randn(rng::AbstractVRNG, ::Type{VecUnroll{N,W,T}}) where {N,W,T} = randn(rng, VecUnroll{N,W,T,Vec{W,T}})
@inline function Random.randn(rng::AbstractVRNG, ::Type{VecUnroll{N,W,T,Vec{W,T}}}) where {N,W,T}
    u = rand(rng, MatchingUInt(NTuple{N,Vec{W,T}}))
    random_normal(u, T)
end

@inline function random_uniform(state::AbstractState, ::Val{N}, ::Type{T}) where {N,T}
    state, u = nextstate(state, Val{N}())
    state, random_uniform(u, T)
end
@inline function random_normal(state::AbstractState, ::Val{N}, ::Type{T}) where {N,T}
    state, u = nextstate(state, Val{N}())
    state, random_normal(u, T)
end
@inline scalar_less(n::MM, i) = data(n) < i

@inline zero_pointer(A::AbstractArray) = zero_offsets(stridedpointer(A));
@inline zero_pointer(x) = x
@inline _vload(ptr::VectorizationBase.AbstractStridedPointer, args::Vararg{Any,K}) where {K} = vload(ptr, args...)
@inline _vload(x::Number, args::Vararg{Any,K}) where {K} = x

function random_sample_u2!(f::F, rng::AbstractVRNG{P}, x::AbstractArray{T}, α, β, γ) where {F,P,T}
    state = getstate(rng, Val{2}(), pick_vector_width(UInt64))
    GC.@preserve x begin
        ptrx = zero_pointer(x); ptrβ = zero_pointer(β); ptrγ = zero_pointer(γ);
        W = (pick_vector_width(T) * pick_vector_width(UInt64)) ÷ pick_vector_width(Float64); W2 = W+W
        N = length(x)
        n = MM(W, 0)
        while scalar_less(n, vadd(N, 1 - 2W))
            state, zvu2 = f(state, Val{2}(), T)
            z₁, z₂ = data(zvu2)
            x₁ = vload(ptrx, (n,)); β₁ = _vload(ptrβ, (n,)); γ₁ = _vload(ptrγ, (n,));
            x₂ = vload(ptrx, (vadd(W, n),)); β₂ = _vload(ptrβ, (vadd(W, n),)); γ₂ = _vload(ptrγ, (vadd(W, n),));
            vstore!(ptrx, α * x₁ + z₁ * γ₁ + β₁, (n,));
            vstore!(ptrx, α * x₂ + z₁ * γ₂ + β₂, (vadd(W, n),));
            n = vadd(W2, n)
        end
        m = VectorizationBase.mask(W, N)
        if scalar_less(n, vsub(N, W))
            state, zvu2 = f(state, Val{2}(), T)
            z₁, z₂ = data(zvu2)
            x₁ = vload(ptrx, (n,)); β₁ = _vload(ptrβ, (n,)); γ₁ = _vload(ptrγ, (n,));
            x₂ = vload(ptrx, (vadd(W, n),), m); β₂ = _vload(ptrβ, (vadd(W, n),), m); γ₂ = _vload(ptrγ, (vadd(W, n),), m);
            vstore!(ptrx, α * x₁ + z₁ * γ₁ + β₁, (n,));
            vstore!(ptrx, α * x₂ + z₂ * γ₂ + β₂, (vadd(W, n),), m);
        elseif scalar_less(n, N)
            state, zvu1 = f(state, Val{1}(), T)
            (z₁,) = data(zvu1)
            x₁ = vload(ptrx, (n,), m); β₁ = _vload(ptrβ, (n,), m); γ₁ = _vload(ptrγ, (n,), m);
            vstore!(ptrx, α * x₁ + z₁ * γ₁ + β₁, (n,), m);
        end
        storestate!(rng, state)
    end # GC preserve
    x
end
function random_sample_u2!(f::F, rng::AbstractVRNG{P}, x::AbstractArray{T}, ::StaticInt{0}, β, γ) where {F,P,T}
    state = getstate(rng, Val{2}(), pick_vector_width(UInt64))
    GC.@preserve x begin
        ptrx = zero_pointer(x); ptrβ = zero_pointer(β); ptrγ = zero_pointer(γ);
        W = (pick_vector_width(T) * pick_vector_width(UInt64)) ÷ pick_vector_width(Float64); W2 = W+W
        N = length(x)
        n = MM(W, 0)
        while scalar_less(n, vadd(N, 1 - 2W))
            state, zvu2 = f(state, Val{2}(), T)
            (z₁,z₂) = data(zvu2)
            β₁ = _vload(ptrβ, (n,)); γ₁ = _vload(ptrγ, (n,));
            β₂ = _vload(ptrβ, (vadd(W, n),)); γ₂ = _vload(ptrγ, (vadd(W, n),));
            vstore!(ptrx, z₁ * γ₁ + β₁, (n,));
            vstore!(ptrx, z₂ * γ₂ + β₂, (vadd(W, n),));
            n = vadd(W2, n)
        end
        m = VectorizationBase.mask(W, N)
        if scalar_less(n, vsub(N, W))
            state, zvu2 = f(state, Val{2}(), T)
            (z₁,z₂) = data(zvu2)
            β₁ = _vload(ptrβ, (n,)); γ₁ = _vload(ptrγ, (n,));
            β₂ = _vload(ptrβ, (vadd(W, n),), m); γ₂ = _vload(ptrγ, (vadd(W, n),), m);
            vstore!(ptrx, z₁ * γ₁ + β₁, (n,));
            vstore!(ptrx, z₂ * γ₂ + β₂, (vadd(W, n),), m);
        elseif scalar_less(n, N)
            state, zvu1 = f(state, Val{1}(), T)
            (z₁,) = data(zvu1)
            β₁ = _vload(ptrβ, (n,), m); γ₁ = _vload(ptrγ, (n,), m);
            vstore!(ptrx, z₁ * γ₁ + β₁, (n,), m);
        end
        storestate!(rng, state)
    end # GC preserve
    # @show state getstate(rng, Val{2}(), Val{W64}())
    # @assert state === getstate(rng, Val{2}(), Val{W64}())
    x
end

function Random.rand!(
    rng::AbstractVRNG, x::AbstractArray{T}, α::Number = StaticInt{0}(), β = StaticInt{0}(), γ = StaticInt{1}()
) where {T <: Union{Float32,Float64}}
    random_sample_u2!(random_uniform, rng, x, α, β, γ)
end
function Random.randn!(
    rng::AbstractVRNG, x::AbstractArray{T}, α::Number = StaticInt{0}(), β = StaticInt{0}(), γ = StaticInt{1}()
) where {T<:Union{Float32,Float64}}
    random_sample_u2!(random_normal, rng, x, α, β, γ)
end
@inline function random_unsigned(state::AbstractState, ::Val{N}, ::Type{T}) where {N,T}
    nextstate(state, Val{N}())
end
function Random.rand!(rng::AbstractVRNG, x::AbstractArray{UInt64})
    random_sample_u2!(random_unsigned, rng, x, StaticInt{0}(), StaticInt{0}(), StaticInt{1}())
end

Random.rand(rng::AbstractVRNG, d1::Integer, dims::Vararg{Integer,N}) where {N} = rand!(rng, Array{Float64}(undef, d1, dims...))
Random.randn(rng::AbstractVRNG, d1::Integer, dims::Vararg{Integer,N}) where {N} = randn!(rng, Array{Float64}(undef, d1, dims...))
# Random.randexp(rng::AbstractVRNG, d1::Integer, dims::Vararg{Integer,N}) where {N} = randexp!(rng, Array{Float64}(undef, d1, dims...))

struct Buffer256{T} <: DenseVector{T}
    ptr::Ptr{T}
end
Base.pointer(b::Buffer256) = b.ptr
Base.length(::Buffer256) = 256
Base.size(::Buffer256) = (256,)
Base.getindex(b::Buffer256, i::Int) = vload(stridedpointer(b), (i,))
Base.strides(::Buffer256) = (1,)
VectorizationBase.ArrayInterface.contiguous_axis(::Type{<:Buffer256}) = VectorizationBase.One()
VectorizationBase.ArrayInterface.contiguous_batch_size(::Type{<:Buffer256}) = VectorizationBase.Zero()
VectorizationBase.ArrayInterface.stride_rank(::Type{<:Buffer256}) = (VectorizationBase.One(),)

@inline function Random.rand(rng::AbstractVRNG)
    i = getrand64counter(rng)
    b = randbuffer64(rng)
    setrand64counter!(rng, i + 0x01)
    iszero(i) && rand!(rng, b)
    vloadu(pointer(b), VectorizationBase.LazyMulAdd{8,0}(i % UInt32))
end

# Box-Muller for scalars is probably only faster with AVX512, so we use Ziggurat if we don't have it.
@inline function randn_scalar(rng::AbstractVRNG, ::VectorizationBase.True)
    i = getrandn64counter(rng)
    b = randnbuffer64(rng)
    setrandn64counter!(rng, i + 0x01)
    iszero(i) && randn!(rng, b)
    vloadu(pointer(b), VectorizationBase.LazyMulAdd{8,0}(i % UInt32))
end
@inline randn_scalar(rng::AbstractVRNG, ::VectorizationBase.False) = Random._randn(rng, rand(rng, Random.UInt52Raw{UInt64}()))
@inline Random.randn(rng::AbstractVRNG) = randn_scalar(rng, VectorizationBase.has_feature(Val(:x86_64_avx512f)))

@inline function Random.rand(rng::AbstractVRNG, ::Random.SamplerType{UInt64})
    i = getrandu64counter(rng)
    b = randubuffer64(rng)
    setrandu64counter!(rng, i + 0x01)
    iszero(i) && rand!(rng, b)
    vloadu(pointer(b), VectorizationBase.LazyMulAdd{8,0}(i % UInt32))
end
@inline Random.rand(rng::AbstractVRNG, ::Random.UInt52Raw{UInt64}) = Random.rand(rng, Random.SamplerType{UInt64}())
for T ∈ [:Int8,:UInt8,:Int16,:UInt16,:Int32,:UInt32,:Int64]
  @eval @inline Random.rand(rng::AbstractVRNG, ::Random.SamplerType{$T}) = Random.rand(rng, Random.SamplerType{UInt64}()) % $T
end
@inline function Random.rand(rng::AbstractVRNG, ::Random.SamplerType{T}) where {T<:Union{UInt128,Int128}}
  i = getrandu64counter(rng)
  b = randubuffer64(rng)
  if i == 0xff
    rand!(rng, b)
    setrandu64counter!(rng, 0x02)
  else
    setrandu64counter!(rng, 0x02 + i)
    iszero(i) && rand!(rng, b)
  end
    vloadu(Base.unsafe_convert(Ptr{T}, pointer(b)), VectorizationBase.LazyMulAdd{8,0}(i % UInt32))
end
Random.rng_native_52(::AbstractVRNG) = UInt64


text:
using CairoMakie
n = 50
t = LinRange(0, 5*π, n)
x(t; a = 0.0, b = -2.4) = (a + b*t) * cos(t)
y(t; a = 0.0, b = -2.4) = (a + b*t) * sin(t)
with_theme(theme_dark()) do
    fig = Figure(resolution = (600,400))
    ax = Axis(fig[1,1]; aspect = DataAspect(), xlabel = "x", ylabel = "y")
    for (k,i) in enumerate(-4:-2)
        scatterlines!(x.(t; b = i), y.(t; b = i); color = 1:n,
      	colormap = (:Spectral_11, 0.5), markercolor = 1:n,
      	markersize = LinRange(3,30/k,n), markercolormap = :Spectral)
    end
    save("scatterlines_archimedes_spiral.png", fig, px_per_unit = 2)
    display(fig)
end


text:
################################################################################
#
#  Restrict and extend scalars from L^n to Q^(d * n)
#
################################################################################

mutable struct VecSpaceRes{S, T}
  field::S
  domain_dim::Int
  codomain_dim::Int
  absolute_basis::Vector{T}
  absolute_degree::Int
end

function VecSpaceRes(K::S, n::Int) where {S}
  B = absolute_basis(K)
  d = absolute_degree(K)
  domain_dim = n * d
  codomain_dim = n

  return VecSpaceRes{S, elem_type(K)}(K, domain_dim, codomain_dim, B, d)
end

function Base.show(io::IO, f::VecSpaceRes)
  n = f.domain_dim
  m = f.codomain_dim
  println(io, "Restriction of scalars QQ^", n , " -> K^", m)
  println(io, "where K is")
  println(io, f.field)
end

(f::VecSpaceRes)(a) = image(f, a)

function image(f::VecSpaceRes{S, T}, v::Vector) where {S, T}
  if v isa Vector{fmpq}
    vv = v
  else
    vv = map(fmpq, v)::Vector{fmpq}
  end
  return _image(f, vv)
end

function _image(f::VecSpaceRes{S, T}, v::Vector{fmpq}) where {S, T}
  n = f.codomain_dim
  d = f.absolute_degree
  m = f.domain_dim
  B = f.absolute_basis
  @req length(v) == m "Vector must have length $m ($(length(v)))"
  z = Vector{T}(undef, n)
  l = 1
  for i in 1:n
    z[i] = zero(f.field)
    for k in 1:d
      z[i] = z[i] + v[l] * B[k]
      l = l + 1
    end
  end
  return z
end

Base.:(\)(f::VecSpaceRes, a) = preimage(f, a)

function preimage(f::VecSpaceRes{S, T}, v::Vector) where {S, T}
  if v isa Vector{T}
    vv = v
  else
    vv = map(f.field, v)::Vector{T}
  end
  return _preimage(f, vv)
end

function _preimage(f::VecSpaceRes{S, T}, w::Vector{T}) where {S, T}
  n = f.codomain_dim
  d = f.absolute_degree
  @req length(w) == n "Vector must have length $n ($(length(w)))"
  z = Vector{fmpq}(undef, f.domain_dim)
  k = 1
  for i in 1:n
    y = w[i]
    @assert parent(y) === f.field
    co = absolute_coordinates(y)
    for j in 1:d
      z[k] = co[j]
      k = k + 1
    end
  end
  return z
end

################################################################################
#
#  Generators for ideals
#
################################################################################

function gens(I::NfRelOrdFracIdl)
  res = elem_type(nf(order(I)))[]
  for (a, c) in pseudo_basis(I, copy = false)
    b = numerator(c)
    d = denominator(c)
    if isdefined(b, :princ_gen)
      push!(res, elem_in_nf(b.princ_gen) * a//d)
    elseif isdefined(b, :gen_one) && isdefined(b, :gen_two)
      push!(res, b.gen_one//d * a)
      push!(res, elem_in_nf(b.gen_two)//d * a)
    else
      for y in basis(b)
        push!(res, elem_in_nf(y)//d * a)
      end
    end
  end
  return res
end

function gens(c::NfAbsOrdFracIdl)
  res = elem_type(nf(order(c)))[]
  b = numerator(c)
  d = denominator(c)
  if isdefined(b, :princ_gen)
    push!(res, elem_in_nf(b.princ_gen)//d)
  elseif isdefined(b, :gen_one) && isdefined(b, :gen_two)
    push!(res, nf(order(c))(b.gen_one//d))
    push!(res, elem_in_nf(b.gen_two)//d)
  else
    for y in basis(b)
      push!(res, elem_in_nf(y)//d)
    end
  end
  return res
end

################################################################################
#
#  Kummer generators for local quadratic unramified extensions
#
################################################################################

# Return an element Delta, such that K_p(\sqrt{Delta}) is the unique quadratic unramified extension.

@doc Markdown.doc"""
    kummer_generator_of_local_unramified_quadratic_extension(p::Idl) -> NumFieldElem

Given a dyadic prime ideal $\mathfrak p$ of a number field $K$, return a local
unit $\Delta$, such that $K(\sqrt(\Delta))$ is unramified at $\mathfrak p$.
"""
function kummer_generator_of_local_unramified_quadratic_extension(p)
  @assert isdyadic(p)
  K = nf(order(p))
  k, h = ResidueField(order(p), p)
  kt, t = PolynomialRing(k, "t", cached = false)
  a = rand(k)
  f = t^2 - t + a
  while !isirreducible(f)
    a = rand(k)
    f = t^2 - t + a
  end
  Kt, t = PolynomialRing(K, "t", cached = false)
  g = t^2 - t + elem_in_nf(h\a)
  aa = elem_in_nf(h\a)
  gg = evaluate(g, inv(K(2)) * (t + 1))
  @assert iszero(coeff(gg, 1))
  D = -coeff(gg, 0) * 4
  @assert quadratic_defect(D, p) == valuation(4, p)
  DD = -4 * aa + 1
  @assert D == DD
  @assert quadratic_defect(DD, p) == valuation(4, p)
  return DD
end

# {Given a unit at the prime p, find a local unit v in the same square class such that v-1 generates the quadratic defect of u}

function _find_special_class(u, p)
  R = order(p)
  K = nf(R)
  @assert valuation(u, p) == 0
  k, _h = ResidueField(R, p)
  h = extend(_h, K)
  fl, s = issquare_with_square_root(h(u))
  @assert fl
  u = divexact(u, (h\s)^2)
  @assert isone(h(u))
  e = valuation(2, p)
  pi = elem_in_nf(uniformizer(p))
  val = isone(u) ? inf : valuation(u - 1, p)
  while val < 2 * e
    if isodd(val)
      return u
    end
    fl, s = issquare_with_square_root(h((u - 1)//pi^val))
    @assert fl
    # TODO:FIXME the div is wrong for negative valuations I think
    @assert val >= 0
    u = divexact(u, (1 + (h\s) * pi^(div(val, 2)))^2)
    val = valuation(u - 1, p)
  end
  kt, t = PolynomialRing(k, "t", cached = false)
  return val == 2 * e && isirreducible(kt([h(divexact(u - 1, 4)), one(k), one(k)])) ? u : one(K)
end

################################################################################
#
#  Should go somewhere else
#
################################################################################

# This can be done more efficiently
function image(f::NfToNfRel, I::NfAbsOrdIdl, OK)
  return reduce(+, (OK(f(elem_in_nf(b))) * OK for b in basis(I)), init = 0 * OK)
end

function image(f::NfToNfRel, I::NfAbsOrdIdl)
  OK = maximal_order(codomain(f))
  return image(f, I, OK)
end

function image(f::NfRelToNfRelMor_nf_elem_nf_elem, I::NfRelOrdIdl)
  OK = order(I)
  return reduce(+, (OK(f(elem_in_nf(b))) * OK for b in absolute_basis(I)), init = 0 * OK)
end

function preimage(f::NfToNfRel, I::NfRelOrdIdl, OK)
  return reduce(+, (OK(f\(b)) * OK for b in absolute_basis(I)), init = 0 * OK)
end

function preimage(f::NfToNfRel, I::NfRelOrdIdl)
  OK = maximal_order(domain(f))
  return preimage(f, I, OK)
end

function image(S::T, A::NfOrdFracIdl) where {T <: Map}
  return S(numerator(A))//denominator(A)
end

################################################################################
#
#  "Strong" approximation
#
################################################################################

#absolute_minimum(I::NfAbsOrdIdl) = minimum(I)

# Cohen 1.3.11
function _strong_approximation(S, ep, xp)
  OK = order(S[1])
  if length(S) > 1
    @assert sum(S) == 1 * OK
  end
  if all(x -> x >= 0, ep) && all(isintegral, xp)
    return _strong_approximation_easy(S, ep, xp)
  else
    d = reduce(lcm, (denominator(x) for x in xp), init = one(fmpz))
    for i in 1:length(S)
      l = valuation(d, S[i]) - ep[i]
      if l < 0
        d = d * absolute_minimum(S[i])^(l)
      end
      @assert valuation(d, S[i]) + ep[i] >= 0
    end
  end
  _ep = fmpz[]
  _xp = nf_elem[]
  _S = support(d * OK)
  _SS = ideal_type(OK)[]
  for i in 1:length(S)
    push!(_ep, ep[i] + valuation(d, S[i]))
    push!(_xp, d * xp[i])
    push!(_SS, S[i])
  end
  for p in _S
    if !(p in _SS)
      push!(_ep, valuation(d, p))
      push!(_xp, zero(nf(OK)))
      push!(_SS, p)
    end
  end

  y = _strong_approximation_easy(_SS, _ep, _xp)
  z = y//d

  for i in 1:length(ep)
    @assert valuation(xp[i] - z, S[i]) == ep[i]
  end
  return z
end

function _strong_approximation_easy(S, ep, xp)
  if length(S) == 1
    p = S[1]
    ap = uniformizer(p)
    z = xp[1] - ap^(ep[1])
    @assert valuation(xp[1] - z, S[1]) == ep[1]
    @assert z == 0 || all(p -> (valuation(z, p) >= 0 || p in S), support(z * order(S[1])))
    return z
  end
  OK = order(S[1])
  # assume ep non-negative and xp in R
  I = reduce(*, (S[i]^(ep[i] + 1) for i in 1:length(S)), init = 1 * OK)
  aps = ideal_type(OK)[]
  bps = elem_type(nf(OK))[]
  for i in 1:length(S)
    bp = elem_in_nf(uniformizer(S[i]))^(ep[i])
    push!(bps, bp)
    ap = divexact(I, S[i]^(ep[i] + 1))
    @assert ap * S[i]^(ep[i] + 1) == I
    push!(aps, ap)
  end
  as = _idempotents(aps)
  for i in 1:length(as)
    @assert as[i] in aps[i]
  end
  @assert sum(as) == one(OK)
  @assert length(as) == length(aps)
  z = elem_in_nf(reduce(+, (as[i]* (xp[i] + bps[i]) for i in 1:length(as)), init = zero(OK)))
  for i in 1:length(ep)
    @assert valuation(z - xp[i], S[i]) == ep[i]
  end
  return z
end

function _idempotents(x::Vector)
  @assert length(x) >= 2

  k = length(x)
  O = order(x[1])
  @assert sum(x) == 1 * O
  d = degree(O)
  # form the matrix
  #
  # ( 1 |  1  | 0 )
  # ( 0 | M_x | I )
  # ( 0 | M_y | 0 )

  V = zero_matrix(FlintZZ, d * k + 1, d * k + 1)

  u = coordinates(one(O))

  V[1, 1] = 1

  for i in 1:d
    V[1, i + 1] = u[i]
  end

  for i in 1:k
    VV = view(V, (2 + (i - 1)*d):(2 + i*d - 1), 2:(2 + d))
    B = basis_matrix(x[i], copy = false)
    for m in 1:d
      for n in 1:d
        VV[m, n] = B[m, n]
      end
    end
  end

  #println("V:\n", sprint(show, "text/plain", V))

  for i in 1:(k - 1)
    VV = view(V, (2 + (i-1)*d):(2 + i*d - 1), (i*d + 2):((i + 1)*d + 1))
    for m in 1:d
      VV[m, m] = 1
    end
    #println("V:\n", sprint(show, "text/plain", V))
  end

  #println("V:\n", sprint(show, "text/plain", V))

  m = lcm(fmpz[minimum(x[i], copy = false) for i in 1:length(x)])

  H = hnf_modular_eldiv!(V, m) # upper right

  for i in 2:(1 + d)
    if H[1, i] != 0
      error("Ideals are not coprime")
    end
  end

  els = elem_type(O)[]

  #println("H:\n", sprint(show, "text/plain", H))

  for i in 1:(k - 1)
    B = basis(x[i], copy = false)
    z = zero(O)
    for j in 1:d
      z += H[1, 1 + j + (i)*d] * B[j]
    end
    push!(els, z)
  end

  @assert one(O) + sum(els[i] for i in 1:length(els)) in x[end]

  push!(els, (one(O) + sum(els[i] for i in 1:k - 1)))
  for i in 1:length(els) - 1
    els[i] = -els[i]
  end
  @assert sum(els) == one(O)
  return els
end

################################################################################
#
#
#
################################################################################

################################################################################
#
#  Some helper functions
#
################################################################################

# Careful, starts at 0!
function absolute_coeff(z::nf_elem, i)
  return coeff(z, i)
end

function absolute_coeff(z::NumFieldElem, i)
  d = absolute_degree(base_field(parent(z)))
  rowindex = fld(i, d)
  colindex = (i % d)
  return absolute_coeff(coeff(z, rowindex), colindex)
end

istotally_real(::FlintRationalField) = true

istotally_positive(x::fmpq) = x > 0

# This function is really slow...
function denominator(M::fmpq_mat)
  d = one(FlintZZ)
  for i in 1:nrows(M)
    for j in 1:ncols(M)
      d = lcm!(d, d, denominator(M[i, j]))
    end
  end
  return d
end

function _weak_approximation_coprime(IP, S, M)
  R = order(M)
  A, _exp, _log = infinite_primes_map(R, IP, M)

  t = (1 + _exp(A([ S[j] == 1 ? 0 : -1 for j in 1:length(IP)])))
  @assert all(i -> sign(t, IP[i]) == S[i], 1:length(IP))
  return t
end

################################################################################
#
#  Helper functions (sort them later)
#
################################################################################

function image(f::NumFieldMor, I::NfRelOrdIdl{T, S}) where {T, S}
  #f has to be an automorphism!!!!
  O = order(I)
  @assert ismaximal(O) # Otherwise the order might change
  K = nf(O)

  B = absolute_basis(I)

  if I.is_prime == 1
    lp = prime_decomposition(O, minimum(I))
    for (Q, e) in lp
      if I.splitting_type[2] == e
        if all(b -> f(b) in Q, B)
          return Q
        end
      end
    end
  end

  pb = pseudo_basis(I)
  pm = basis_pmatrix(I)

  m = zero(matrix(pm))

  c = coefficient_ideals(pm)

  for i in 1:length(pb)
    cc = coordinates(O(f(pb[i][1])))
    for j in 1:length(cc)
      m[i, j] = cc[j]
    end
  end

  J = ideal(O, pseudo_matrix(m, c))

  if isdefined(I, :minimum)
    J.minimum = I.minimum
  end

  J.has_norm = I.has_norm

  if isdefined(I, :norm)
    J.norm = I.norm
  end

  if isdefined(I, :is_prime)
    J.is_prime = I.is_prime
  end

  if isdefined(I, :splitting_type)
    J.splitting_type = I.splitting_type
  end

  return J
end

function image(f::NumFieldMor, I::NfRelOrdFracIdl{T, S}; order = order(I)) where {T, S}
  #S has to be an automorphism!!!!
  O = order
  @assert ismaximal(O) # Otherwise the order might change
  K = nf(O)
  @assert K === codomain(f)

  pb = pseudo_basis(I)

  z = sum(b * (f(a) * O) for (a, b) in pb)
  return z
end

# An element is locally a square if and only if the quadratic defect is 0, that is
# the valuation is inf.
# (see O'Meara, Introduction to quadratic forms, 3rd edition, p. 160)
function islocal_square(a, p)
  return quadratic_defect(a, p) isa PosInf
end

function _map(a::AbstractAlgebra.MatrixElem, f)
  z = similar(a)
  for i in 1:nrows(a)
    for j in 1:ncols(a)
      z[i, j] = f(a[i, j])
    end
  end
  return z
end

# I think I need a can_change_base_ring version

function element_with_signs(K, D::Dict{InfPlc, Int})
  return _element_with_signs(K, D)
end

function _element_with_signs(K, D)
  OK = maximal_order(K)
  G, mG = infinite_primes_map(OK, real_places(K), 1*OK)
  r = real_places(K)
  z = id(G)
  for (v, s) in D
    for i in 1:length(r)
      if s == 1
        ss = 0
      else
        ss = 1
      end

      if v == r[i]
        z = z + ss * G[i]
      end
    end
  end
  zz = elem_in_nf(mG(z))::elem_type(K)
  @assert all(u -> sign(zz, u[1]) == u[2], D)
  return zz
end

function element_with_signs(K, P::Vector{InfPlc}, S::Vector{Int})
  return _element_with_signs(K, zip(P, S))
end

function element_with_signs(K, A::Vector{Tuple{InfPlc, Int}})
  return _element_with_signs(K, A)
end

function prime_ideals_up_to(O::NfRelOrd, n::Union{Int, fmpz})
  p = 2
  z = ideal_type(O)[]
  while p <= n
    lp = prime_decomposition(base_ring(O), p)
    for q in lp
      if norm(q[1]) > n
        continue
      else
        lq = prime_decomposition(O, q[1])
        for Q in lq
          if absolute_norm(Q[1]) <= n
            push!(z, Q[1])
          end
        end
      end
    end
    p = next_prime(p)
  end
  return sort!(z, by = a -> absolute_norm(a))
end

################################################################################
#
#  Trace of fractional ideals
#
################################################################################

function tr(I::NfRelOrdFracIdl)
  E = nf(order(I))
  K = base_field(E)
  return fractional_ideal(maximal_order(K), [trace(b) for b in absolute_basis(I)])
end

function tr(I::NfOrdFracIdl)
  E = nf(order(I))
  K = base_field(E)
  return reduce(gcd, [trace(b) for b in basis(I)]; init = fmpq(0))
end

################################################################################
#
#  Is integral for fractional ideals
#
################################################################################

function isintegral(I::NfOrdFracIdl)
  @assert ismaximal(order(I))
  simplify(I)
  return denominator(I) == 1
end

################################################################################
#
#  Local norms in quadratic extensions
#
################################################################################

@doc Markdown.doc"""
    islocal_norm(L::NumField, a::NumFieldElem, P)

Given a number field $L/K$, an element $a \in K$ and a prime ideal $P$ of $K$,
returns whether $a$ is a local norm at $P$.

The number field $L/K$ must be a simple extension of degree 2.
"""
islocal_norm(::NumField, ::NumFieldElem, ::Any)

function islocal_norm(K::AnticNumberField, a::fmpq, p::fmpz)
  degree(K) != 2 && error("Degree of number field must be 2")
  x = gen(K)
  b = (2 * x - tr(x))^2
  @assert degree(minpoly(b)) == 1
  bQ = coeff(b, 0)
  return hilbert_symbol(a, bQ, p) == 1
end

function islocal_norm(K::AnticNumberField, a::fmpq, P::NfOrdIdl)
  p = minimum(P)
  return islocal_norm(K, a, p)
end

function islocal_norm(K::AnticNumberField, a::RingElement, P::NfOrdIdl)
  return islocal_norm(K, FlintQQ(a), P)
end

function islocal_norm(K::AnticNumberField, a::RingElement, p::fmpz)
  return islocal_norm(K, FlintQQ(a), p)
end

function islocal_norm(K::AnticNumberField, a::RingElement, p::Integer)
  return islocal_norm(K, FlintQQ(a), fmpz(p))
end

function islocal_norm(K::NfRel{T}, a::T, P) where {T} # ideal of parent(a)
  nf(order(P)) != parent(a) && error("Prime ideal must have the same base field as the second argument")
  degree(K) != 2 && error("Degree of number field must be 2")
  x = gen(K)
  b = (2 * x - tr(x))^2
  @assert degree(minpoly(b)) == 1
  bQ = coeff(b, 0)
  return hilbert_symbol(a, bQ, P) == 1
end

################################################################################
#
#  Special local units
#
################################################################################

# Return a local unit u (that is, valuation(u, P) = 0) with trace one.
# P must be even and inert (P is lying over p)
function _special_unit(P, p::fmpz)
  R = order(P)
  E = nf(R)
  @assert degree(E) == 2
  x = gen(E)
  x = x - trace(x)//2
  a = coeff(x^2, 0)
  K = base_field(E)
  pi = elem_in_nf(uniformizer(p))
  v = valuation(a, p)
  if v != 0
    @assert iseven(v)
    a = a//pi^v
    x = x//pi^(div(v, 2))
  end
  k = GF(p, cached = false)
  hex(x) = k(numerator(x)) * inv(k(denominator(x)))
  hexinv(x) = FlintQQ(lift(x))
  t = hexinv(sqrt(hex(a)))
  a = a//t^2
  x = x//t
  w = valuation(a - 1, p)
  e = valuation(order(p)(2), p)
  while w < 2*e
    @assert iseven(w)
    s = sqrt(hex((a - 1)//pi^w))
    t = 1 + (hexinv(s)) * pi^(div(w, 2))
    a = a//t^2
    x = x//t
    w = valuation(a - 1, p)
  end
  @assert w == 2 * e
  u = (1 + x)//2
  @assert trace(u) == 1
  @assert valuation(u, P) == 0
  return u
end

function _special_unit(P, p)
  @assert ramification_index(P) == 1
  @assert isdyadic(p)
  R = order(P)
  E = nf(R)
  @assert degree(E) == 2
  x = gen(E)
  x = x - trace(x)//2
  a = coeff(x^2, 0)
  K = base_field(E)
  pi = elem_in_nf(uniformizer(p))
  v = valuation(a, p)
  if v != 0
    @assert iseven(v)
    a = a//pi^v
    x = x//pi^(div(v, 2))
  end
  k, h = ResidueField(order(p), p)
  hex = extend(h, K)
  t = hex \ sqrt(hex(a))
  a = a//t^2
  x = x//t
  w = valuation(a - 1, p)
  e = valuation(order(p)(2), p)
  while w < 2*e
    @assert iseven(w)
    s = sqrt(hex((a - 1)//pi^w))
    t = 1 + (hex \ s) * pi^(div(w, 2))
    a = a//t^2
    x = x//t
    w = valuation(a - 1, p)
  end
  @assert w == 2 * e
  u = (1 + x)//2
  @assert trace(u) == 1
  @assert valuation(u, P) == 0
  return u
end

################################################################################
#
#  Valuation of trace ideal generated by elements
#
################################################################################

# L is a list of (integral) number field elements
# and p a prime ideal of the maximal.
# Return v(tr(<L>), P).
function trace_ideal_valuation(L, p)
  R = order(p)
  v = valuation(different(R), p)
  V = unique!(valuation(l, p) for l in L if !iszero(l))
  X = Int[ 2 *div(l + v, 2) for l in V]
  if length(X) == 0
    return inf
  else
    minimum(X)
  end
end

function _get_norm_valuation_from_gram_matrix(G, P)
  n = ncols(G)
  R = order(P)
  L = nf(R)
  K = base_field(L)
  trrr = R * (tr(fractional_ideal(order(P), [G[i, j] for i in 1:n for j in i+1:n])))
  if iszero(trrr)
    T = inf
  else
    T = valuation(trrr, P)
  end
  #T = trace_ideal_valuation((G[i, j] for i in 1:n for j in i+1:n), P)
  diag = minimum(iszero(G[i, i]) ? inf : valuation(G[i, i], P) for i in 1:n)
  if T isa PosInf
    return diag
  else
    return min(T, diag)
  end
end

################################################################################
#
#  Absolute basis of ideals
#
################################################################################

function absolute_basis(I::NfOrdFracIdl)
  return basis(I)
end

function absolute_basis(I::NfRelOrdFracIdl)
  res = elem_type(nf(order(I)))[]
  pb = pseudo_basis(I)
  pbb = pseudo_basis(order(I))
  for i in 1:length(pb)
    (e, I) = pb[i]
    for b in absolute_basis(I)
      push!(res, e * b)
    end
  end
  return res
end

function absolute_basis(I::NfRelOrdIdl)
  res = elem_type(nf(order(I)))[]
  pb = pseudo_basis(I)
  pbb = pseudo_basis(order(I))
  for i in 1:length(pb)
    (e, I) = pb[i]
    for b in absolute_basis(I)
      push!(res, e * b)
    end
  end
  return res
end

################################################################################
#
#  Ramification index
#
################################################################################

ramification_index(P) = P.splitting_type[1]

################################################################################
#
#  Treat FlintQQ as a number field
#
################################################################################

order(::fmpz) = FlintZZ

uniformizer(p::fmpz) = p

isdyadic(p::fmpz) = p == 2

################################################################################
#
#  Is ramified
#
################################################################################

function isramified(R, p)
  D = prime_decomposition(R, p)
  for (_, e) in D
    if e > 1
      return true
    end
  end
  return false
end

################################################################################
#
#  Normic defect
#
################################################################################

function normic_defect(E, a, p)
  R = maximal_order(E)
  if iszero(a) || islocal_norm(E, a, p)
    inf
  end
  return valuation(a, p) + valuation(discriminant(R), p) - 1
end

################################################################################
#
#  Find quaternion algebras
#
################################################################################

# Given an element b in a number field K and sets of finite and infinite
# places P and I of K, return an element a in K such that
# { v: (a,b)_v = -1 } = P \cup I
# Note that the function computes the unit and class groups of K!
# TODO: use factored elements
function _find_quaternion_algebra(b, P, I)
  @assert iseven(length(I) + length(P))
  @assert all(p -> !islocal_square(b, p), P)
  @assert all(p -> isnegative(evaluate(b, p)), I)

  K = parent(b)
  if length(P) > 0
    R = order(P[1])
  else
    R = maximal_order(K)
  end

  n = length(P)
  m = length(I)

  _J = b * R
  #_P = Dict{}()
  __P = copy(P)
  #for p in P
  #  _P[p] = true
  #end
  for p in support(_J)
    if !(p in __P)
      push!(__P, p)
    end
      #_P[p] = true
  end
  for p in prime_decomposition(R, 2)
    if !(p[1] in __P)
      push!(__P, p[1])
    end
  end
  for p in real_places(K)
    if !(p in I) && isnegative(b, p)
      push!(I, p)
    end
  end

  F = Nemo.GF(2)

  target = matrix(F, 1, length(__P) + length(I), vcat(fill(1, n), fill(0, length(__P) - n), fill(1, m), fill(0, length(I) - m)))
  if iszero(target)
    return one(K)
  end

  #__P = convert(Vector{NfOrdIdl}, collect(keys(_P)))

  found = false
  U, h = unit_group(R)
  sign_vector = g -> begin
    return matrix(F, 1, length(__P) + length(I),
                 vcat([div(1 - hilbert_symbol(K(g), b, p), 2) for p in __P ], [ div(1 - sign(Int, evaluate(g, p)), 2) for p in I]))
  end

  L, f = sunit_group(identity.(__P))
  M = zero_matrix(F, 0, length(__P) + length(I))
  elts = nf_elem[]

  for i in 1:ngens(L)
    v = sign_vector(f(L[i]))
    if rank(M) == rank(vcat(M, v))
      continue
    end
    M = vcat(M, v)
    push!(elts, f(L[i])) # cache
    fl, w = can_solve_with_solution(M, target, side = :left)
    if fl
      found = true
      break
    end
  end

  if !found
    Cl, mCl = class_group(R)
    A = abelian_group(fill(0, length(__P)))
    hh = hom(A, Cl, [mCl\(p) for p in __P])
    S, mS = image(hh, false)
    Q, mQ = quo(Cl, [mS(S[i]) for i in 1:ngens(S)])

    p = 2
    while !found
      p = next_prime(p)
      for (q, e) in prime_decomposition(R, p)
        if q in __P
          continue
        end
        o = order(mQ(mCl\(q)))
        c = -(hh\(o * (mCl\(q))))
        fl, x = isprincipal(q * prod(__P[i]^Int(c.coeff[i]) for i in 1:length(__P)))
        @assert fl
        v = sign_vector(elem_in_nf(x))
        if rank(M) == rank(vcat(M, v + target))
          found = true
          M = vcat(M, v)
          push!(elts, elem_in_nf(x))
          break
        end
      end
    end
  end
  fl, v = can_solve_with_solution(M, target, side = :left)
  @assert fl
  z = evaluate(FacElem(Dict(elts[i] => Int(lift(v[1, i])) for i in 1:ncols(v))))
  @assert sign_vector(z) == target
  return z
end

function _find_quaternion_algebra(b::fmpq, P, I)
  K, a = rationals_as_number_field()
  bK = K(b)
  OK = maximal_order(K)
  PK = ideal_type(OK)[]
  for p in P
    push!(PK, prime_decomposition(OK, p)[1][1])
  end
  if length(I) == 0
    IK = InfPlc[]
  else
    @assert length(I) == 1
    IK = infinite_places(K)
  end
  c = _find_quaternion_algebra(bK, PK, IK)::nf_elem
  return coeff(c, 0)
end

################################################################################
#
#  Weak approximation
#
################################################################################

function _weak_approximation(I::Vector{InfPlc}, val::Vector{Int})
  K = number_field(first(I))
  if degree(K) == 2
    return _weak_approximation_quadratic(I, val)
  else
    return _weak_approximation_generic(I, val)
  end
end

function _weak_approximation_generic(I::Vector{InfPlc}, val::Vector{Int})
  K = number_field(first(I))
  OK = maximal_order(K)
  local A::GrpAbFinGen
  A, exp, log = infinite_primes_map(OK, I, 1 * OK)
  uni = infinite_places_uniformizers(K)
  target_signs = zeros(Int, ngens(A))

  if all(isequal(1), val)
    return one(K)
  elseif all(isequal(-1), val)
    return -one(K)
  end

  for P in I
    v = log(uni[P])::GrpAbFinGenElem
    for i in 1:ngens(A)
      if v.coeff[i] == 1
        target_signs[i] = val[i] == -1 ? 1 : 0
        break
      end
    end
  end
  c = K(exp(A(target_signs))::elem_type(OK))
  for i in 1:length(I)
    @assert sign(c, I[i]) == val[i]
  end
  return c
end

function _weak_approximation_quadratic(I::Vector{InfPlc}, val::Vector{Int})
  K = number_field(first(I))
  if length(I) == 1
    return K(val)
  else
    if val[1] == val[2]
      return K(val[1])
    else
      x = gen(K)
      s1 = sign(x, I[1])
      s2 = sign(x, I[2])
      if s1 == val[1] && s2 == val[2]
        return x
      elseif s1 == -val[1] && s2 == -val[2]
        return -x
      else
        return _weak_approximation_generic(I, val)
      end
    end
  end
end

################################################################################
#
#  Decreasing non-negative inter sequences
#
################################################################################

# Compute all decreasing non-negative integer sequenes of length len with sum
# equal to sum.
# This is not optimized.
function _integer_lists(sum::Int, len::Int)
  if sum == 0
    return Vector{Int}[fill(0, len)]
  end
  if len == 1
    return Vector{Int}[Int[sum]]
  end
  res = Vector{Vector{Int}}()
  for i in 0:sum
    rec = _integer_lists(sum - i, len - 1)::Vector{Vector{Int}}
    if isempty(rec)
      push!(res, append!(Int[i], fill(0, len - 1)))
    else
      for v in rec
        push!(res, append!(Int[i], v))
      end
    end
  end
  return res
end

################################################################################
#
#  Support
#
################################################################################

function support(I::T) where T <: Union{NfAbsOrdIdl, NfRelOrdIdl, NfAbsOrdFracIdl, NfRelOrdFracIdl}
  lp = factor(I)
  return collect(keys(lp))
end

function support(a::NumFieldElem{fmpq}, R::NfAbsOrd = maximal_order(parent(a)))
  @assert nf(R) == parent(a)
  return support(a * R)
end

function support(d::fmpz)
  res = fmpz[]
  for (p, _) in factor(d)
    push!(res, p)
  end
  return res
end

function support(a::fmpq)
  d = denominator(a)
  n = numerator(a)
  res = fmpz[]
  for (p, _) in factor(d)
    push!(res, p)
  end
  for (p, _) in factor(n)
    push!(res, p)
  end
  return res
end

################################################################################
#
#  p-uniformizer
#
################################################################################

p_uniformizer(P::NfOrdIdl) = uniformizer(P)

isdyadic(p) = isdyadic(minimum(p))

################################################################################
#
#  Local non-norms
#
################################################################################

# find an element of K, which is not a local norm at p
# p must be ramified
# See [Kir16, Corollary 3.3.17]
function _non_norm_rep(E, K, p)
  K = base_field(E)
  if isramified(maximal_order(E), p)
    if !isdyadic(p)
      U, mU = unit_group(maximal_order(K))
      for i in 1:ngens(U)
        u = mU(U[i])
        if !islocal_norm(E, elem_in_nf(u), p)
          return elem_in_nf(u)
        end
      end
      B = elem_in_nf.(basis(p))
      k = 0
      while true
        if k > 10000
          throw(error("Something wrong in non_norm_rep"))
        end
        y = rand(K, -5:5)
        if iszero(y)
          continue
        end
        if !islocal_norm(E, y, p)
          return y
        end
        k += 1
      end
    else
      lP = prime_decomposition(maximal_order(E), p)
      @assert length(lP) == 1 && lP[1][2] == 2
      Q = lP[1][1]
      e = valuation(different(maximal_order(E)), Q)
      U, mU = unit_group(maximal_order(K))
      for i in 1:ngens(U)
        u = mU(U[i])
        if !islocal_norm(E, elem_in_nf(u), p) && (valuation(u - 1, p) == e - 1)
          return elem_in_nf(u)
        end
      end
      # We look for a local unit u such that v_p(u - 1) = e - 1 and
      # u not a local norm
      tu = elem_in_nf(mU(U[1]))
      tuo = order(U[1])
      B = elem_in_nf.(basis(p^(e - 1)))
      k = 0
      while true
        if k > 10000
          throw(error("Something wrong in non_norm_rep"))
        end
        y = (1 + rand(B, -1:1)) * tu^(rand(1:tuo))
        @assert valuation(y, p) == 0
        if !islocal_norm(E, y, p) && valuation(y - 1, p) == e - 1
          return y
        end
        k += 1
      end
    end
    throw(error("This should not happen ..."))
  else
    lP = prime_decomposition(maximal_order(E), p)
    if length(lP) == 2
      error("This dosses not make any sense!")
    else
      return elem_in_nf(p_uniformizer(p))
     end
  end
end

# P must be inert and odd
# Find an element which is a locally a norm, but not a square
function _non_square_norm(P)
  @assert !isdyadic(P)
  #@assert isinert(P)
  R = order(P)
  p = minimum(P)
  k, h = ResidueField(order(P), P)
  kp, hp = ResidueField(order(p), p)
  local u
  while true
    r = rand(k)
    u = h\r
    if !iszero(r) && !issquare(hp(norm(u)))[1]
      break
    end
  end
  return u
end

################################################################################
#
#  Signs for arb and acb
#
################################################################################

function sign(::Type{Int}, x::arb)
  if ispositive(x)
    return 1
  elseif isnegative(x)
    return -1
  else
    error("Could not determine sign")
  end
end

function sign(::Type{Int}, x::acb)
  if isreal(x)
    return sign(Int, real(x))
  else
    error("Element is not real")
  end
end

################################################################################
#
#  Exponentiation for fractional ideals
#
################################################################################

function ^(A::NfRelOrdFracIdl, a::Int)
  if a == 0
    B = one(nf(order(A))) * order(A)
    return B
  end

  if a == 1
    return A # copy?
  end

  if a < 0
    return inv(A^(-a))
  end

  if a == 2
    return A*A
  end

  if mod(a, 2) == 0
    return (A^div(a, 2))^2
  else
    return A * A^(a - 1)
  end
end

################################################################################
#
#  Matrices as Vector
#
################################################################################

_eltseq(M::MatElem) = [M[i, j] for i in 1:nrows(M) for j in 1:ncols(M)]

################################################################################
#
#  Create a matrix from rows
#
################################################################################

function matrix(K::Ring, R::Vector{<:Vector})
  if length(R) == 0
    return zero_matrix(K, 0, 0)
  else
    n = length(R)
    m = length(R[1])
    z = zero_matrix(K, n, m)
    for i in 1:n
      @assert length(R[i]) == m
      for j in 1:m
        z[i, j] = R[i][j]
      end
    end
    return z
  end
end

################################################################################
#
#  Absolute primitive element and minpoly
#
################################################################################

absolute_minpoly(a::nf_elem) = minpoly(a)

################################################################################
#
#  Maximal subspaces
#
################################################################################

function maximal_subspaces(k::Field, n::Int)
  I = identity_matrix(k, n)
  L = typeof(I)[]
  for i in 1:n
    II = remove_row(I, i)
    if i == 1
      push!(L, II)
      continue
    end
    #V = Iterators.product([k for j in 1:(i - 1)]...)
    VV = [collect(k) for j in 1:(i - 1)]
    V = cartesian_product_iterator(VV, inplace = false)
    for v in V
      III = deepcopy(II)
      for l in 1:(i - 1)
        III[l, i] = v[l]
      end
      push!(L, III)
    end
  end
  return L
end


text:
function get_grouped_by(organisms::Vector{DetailedOrganism}, properties::PropertyType...)
    return _group_by(organisms, get_group_function(properties))
end

get_group_function(properties) = x -> [get(x, p) for p in properties]

function _group_by(list, group_function)
    groups = Dict()

    for item in list
        group = group_function(item)

        if haskey(groups, group)
            push!(groups[group], item)
        else
            groups[group] = [item]
        end
    end

    return groups
end

max_stars_repo_name: tochsner/ALifeBenchmark

max_issues_repo_name: tochsner/ALifeBenchmark

max_forks_repo_name: tochsner/ALifeBenchmark


text:
@gen (static) function lambda_fa(arg1::Real, arg2::Real)
	fa = @trace(gamma(arg1, arg2), :fa)
	return fa
end

@gen (static) function miss_rate(arg1::Real, arg2::Real)
	miss = @trace(beta(arg1, arg2), :miss)
	return miss
end

"""
	make_visual_system(params::Video_Params)

Samples the matrix describing the initial visual system.
"""
@gen (static) function make_visual_system(params::Video_Params)
	arg1 = fill(1.0, length(params.possible_objects))
	arg2 = fill(1.0, length(params.possible_objects))
	fa = @trace(Map(lambda_fa)(arg1, arg2), :lambda_fa) #these are lambdas per receptive field
	miss = @trace(Map(miss_rate)(arg1, arg2), :miss_rate) #miss rate is misnomer. it's actually more like detection rate. it's mean of geometric distribution

	v = hcat(fa, miss)
   	return convert(Matrix{Real}, v)
end
################################################################################

"""
	main(num_videos::Int64, num_frames::Int64)

Simulates the visual system, `num_videos` scenes each with
`num_frames` frames, and percepts generated by the visual system.

arg lesioned is about whether or not to have the V kernel reflect an ideal observed
updating their beliefs as opposed to just resampling V from the prior
"""
@gen (static) function main(lesioned::Bool, num_videos::Int64, num_frames::Int64, params::Video_Params)

	receptive_fields = make_receptive_fields(params)

	init_v_matrix = @trace(make_visual_system(params), :init_v_matrix)
	alphas = fill(1, size(init_v_matrix))
	betas = fill(1, size(init_v_matrix))
	v_matrix_state = (init_v_matrix, alphas, betas)
	#println("alphas in main ", v_matrix_state[2])
    videos = @trace(video_chain(num_videos, v_matrix_state, lesioned, num_frames, params, receptive_fields), :videos)
	return videos
end

export main
export make_visual_system

max_stars_repo_name: zhangir-azerbayev/MetaGen

max_issues_repo_name: zhangir-azerbayev/MetaGen

max_forks_repo_name: zhangir-azerbayev/MetaGen


text:
# FDSN web service specification tests

using Compat.Test
import Compat.Dates
@static if VERSION < v"0.7"
    using Missings
end
using SeisRequests

@testset "FDSNWS" begin
    @testset "FDSNEvent" begin
        # Subtyping
        @test FDSNEvent <: SeisRequests.FDSNRequest
        # Construction
        @test_throws ArgumentError FDSNEvent(starttime=Dates.now(),
                                             endtime=Dates.now()-Dates.Second(1))
        @test_throws ArgumentError FDSNEvent(maxradius=20, longitude=0)
        @test_throws ArgumentError FDSNEvent(limit=-1)
        @test_throws ArgumentError FDSNEvent(offset=-5.0)
        @test_throws ArgumentError FDSNEvent(minlatitude=100)
        @test_throws ArgumentError FDSNEvent(maxlatitude=-100)
        @test_throws ArgumentError FDSNEvent(minlongitude=200)
        @test_throws ArgumentError FDSNEvent(maxlongitude=-181)
        @test_throws ArgumentError FDSNEvent(latitude=100)
        @test_throws ArgumentError FDSNEvent(longitude=360)
        @test_throws ArgumentError FDSNEvent(longitude=0, latitude=0, minradius=-5)
        @test_throws ArgumentError FDSNEvent(longitude=0, latitude=0, maxradius=181)
        @test_throws ArgumentError FDSNEvent(format="weird_format_please")
        @test_throws ArgumentError FDSNEvent(nodata=101)
        let req = FDSNEvent()
            for field in fieldnames(FDSNEvent)
                if field == :nodata
                    @test getfield(req, field) == 204
                else
                    @test getfield(req, field) === missing
                end
            end
        end
        # Methods
        let req = FDSNEvent(longitude=45, latitude=15, maxradius=5, minmagnitude=4,
                            starttime=Dates.DateTime(2000),
                            endtime=Dates.DateTime(2000)+Dates.Day(1))
            @test SeisRequests.protocol_string(req) == "fdsnws"
            @test SeisRequests.service_string(req) == "event"
            @test SeisRequests.request_uri(req) ==
                "http://service.iris.edu/fdsnws/event/1/query?" *
                "starttime=2000-01-01T00:00:00&endtime=2000-01-02T00:00:00&" *
                "latitude=15.0&longitude=45.0&maxradius=5.0&minmagnitude=4.0&" *
                "nodata=204"
            # Get request: will fail if IRISWS not working for some reason
            response = get_request(req, verbose=false)
            @test response.status in keys(SeisRequests.STATUS_CODES)
        end
    end

    @testset "FDSNDataSelect" begin
        # Subtyping
        @test FDSNDataSelect <: SeisRequests.FDSNRequest
        # Construction
        @test_throws ArgumentError FDSNDataSelect(starttime=Dates.now(),
                                                  endtime=Dates.now()-Dates.Second(1))
        for field in (:network, :station, :location, :channel)
            @test_throws ArgumentError FDSNDataSelect(; field=>"β is not ASCII")
        end
        @test_throws ArgumentError FDSNDataSelect(quality="A")
        @test_throws ArgumentError FDSNDataSelect(minimumlength=-2.0)
        @test_throws ArgumentError FDSNDataSelect(nodata=101)
        let req = FDSNDataSelect()
            for field in fieldnames(FDSNDataSelect)
                if field == :nodata
                    @test getfield(req, field) == 204
                else
                    @test getfield(req, field) === missing
                end
            end
        end
        let req = FDSNDataSelect(location="  ")
            @test req.location == "--"
        end
        # Methods
        let req = FDSNDataSelect(network="IU", station="ANMO", location="*",
                                 channel="BH?", starttime=Dates.DateTime(2000),
                                 endtime=Dates.DateTime(2000)+Dates.Second(1))
            @test SeisRequests.protocol_string(req) == "fdsnws"
            @test SeisRequests.service_string(req) == "dataselect"
            @test SeisRequests.request_uri(req) ==
                "http://service.iris.edu/fdsnws/dataselect/1/query?" *
                "starttime=2000-01-01T00:00:00&endtime=2000-01-01T00:00:01&" *
                "network=IU&station=ANMO&location=*&channel=BH?&nodata=204"
            # Get request: will fail if IRISWS not working for some reason
            response = get_request(req, verbose=false)
            @test response.status in keys(SeisRequests.STATUS_CODES)
        end
    end

    @testset "FDSNStation" begin
        # Subtyping
        @test FDSNStation <: SeisRequests.FDSNRequest
        # Construction
        @test_throws ArgumentError FDSNStation(starttime=Dates.now(),
                                               endtime=Dates.now()-Dates.Second(1))
        @test_throws ArgumentError FDSNStation(maxradius=20, longitude=0)
        @test_throws ArgumentError FDSNStation(minlatitude=100)
        @test_throws ArgumentError FDSNStation(maxlatitude=-100)
        @test_throws ArgumentError FDSNStation(minlongitude=200)
        @test_throws ArgumentError FDSNStation(maxlongitude=-181)
        @test_throws ArgumentError FDSNStation(latitude=100)
        @test_throws ArgumentError FDSNStation(longitude=360)
        @test_throws ArgumentError FDSNStation(longitude=0, latitude=0, minradius=-5)
        @test_throws ArgumentError FDSNStation(longitude=0, latitude=0, maxradius=181)
        @test_throws ArgumentError FDSNStation(level="weird_level_please")
        @test_throws ArgumentError FDSNStation(nodata=101)
        for field in (:network, :station, :location, :channel)
            @test_throws ArgumentError FDSNStation(; field=>"β is not ASCII")
        end
        let req = FDSNStation()
            for field in fieldnames(FDSNStation)
                if field == :nodata
                    @test getfield(req, field) == 204
                else
                    @test getfield(req, field) === missing
                end
            end
        end
        # Methods
        let req = FDSNStation(network="GB", station="JSA", location="--", channel="?H?",
                              level="channel", format="text")
            @test SeisRequests.protocol_string(req) == "fdsnws"
            @test SeisRequests.service_string(req) == "station"
            @test SeisRequests.request_uri(req) ==
                "http://service.iris.edu/fdsnws/station/1/query?" *
                "network=GB&station=JSA&location=--&channel=?H?&level=channel&" *
                "format=text&nodata=204"
            # Get request: will fail if IRISWS not working for some reason
            response = get_request(req, verbose=false)
            @test response.status in keys(SeisRequests.STATUS_CODES)
        end
    end
end


text:
const version = "1.8.4"

if Sys.isapple()
    const _icons = normpath(joinpath(@__DIR__, "../res/julia-icns.icns"))
end

our_download(x) = download(x, basename(x))

cd(@__DIR__) do
    our_download("http://junolab.s3.amazonaws.com/blink/julia.png")

    rm(joinpath(@__DIR__, "electron"), force=true, recursive=true)

    if Sys.isapple()
        file = "electron-v$version-darwin-x64.zip"
        our_download("https://github.com/electron/electron/releases/download/v$version/$file")
        run(`unzip -q $file -d electron`)
        rm(file)
        run(`mv electron/Electron.app electron/Julia.app`)
        run(`mv electron/Julia.app/Contents/MacOS/Electron electron/Julia.app/Contents/MacOS/Julia`)
        run(`sed -i.bak 's/Electron/Julia/' electron/Julia.app/Contents/Info.plist`)
        run(`cp $_icons electron/Julia.app/Contents/Resources/electron.icns`)
        run(`touch electron/Julia.app`)  # Apparently this is necessary to tell the OS to double-check for the new icons.
    end

    if Sys.iswindows()
        arch = Int == Int64 ? "x64" : "ia32"
        file = "electron-v$version-win32-$arch.zip"
        our_download("https://github.com/electron/electron/releases/download/v$version/$file")
        run(`$(joinpath(Sys.BINDIR, "7z")) x $file -oelectron -aoa`)
        rm(file)
    end

    if Sys.islinux()
        arch = Int == Int64 ? "x64" : "ia32"
        file = "electron-v$version-linux-$arch.zip"
        our_download("https://github.com/electron/electron/releases/download/v$version/$file")
        run(`unzip -q $file -d electron`)
        rm(file)
    end
end


text:
"""
Module with tests of SymbolicTransform.

* Author: Hilding Elmqvist, Mogram AB
* Date: July-August 2016
* License: MIT

"""
module testSymbolicTransform

using Modia.SymbolicTransform
using Modia.Utilities

# Desired:
#   using Test
#   using LinearAlgebra
#
# In order that these packages need not to be defined in the user environment, they are included via Modia:
import Modia

@static if VERSION < v"0.7.0-DEV.2005"
  using Base.Test
else
  using Modia.Test
end

@static if VERSION < v"0.7.0-DEV.2005"
else
  using Modia.LinearAlgebra
end

using Base.Meta: isexpr

# Copied from Instantiation.jl
prettyfy(ex) = ex
#prettyfy(der::Der) = Symbol("der("*string(der.base.name)*")")
##prettyfy(der::Der) = Symbol("der("*replace(string(der.base.name), ".", "_")*")")
#prettyfy(get::GetField) = get.name
##prettyfy(get::GetField) = Symbol(replace(string(get.name), ".", "_")) # get.name # Handle dummy derivatives
#prettyfy(s::Symbol) = s
function prettyfy(ex::Expr)
  if isexpr(ex, :quote) || isexpr(ex, :line)
    nothing
  elseif isexpr(ex, :block)
    prettyfy(ex.args[2])
  else
    Expr(ex.head, [prettyfy(arg) for arg in ex.args]...)
  end
end

# Pretty printing of expressions
const oper = [:!, :(!=), :(!==), :%, :&, :*, :+, :-, :/, ://, :<, :<:, :<<, :(<=),
               :<|, :(==), :(===), :>, :>:, :(>=), :>>, :>>>, :\, :^, #= :colon, =#
               :ctranspose, :getindex, :hcat, :hvcat, :setindex!, :transpose, :vcat,
               :xor, :|, :|>, :~ #= , :× =# , :÷, :∈, :∉, :∋, :∌, :∘, :√, :∛, :∩, :∪, :≠, :≤,
               :≥ #=, :⊆, :⊈, :⊊, :⊻, :⋅=#]

const operator_table = Dict(getfield(Base,name) => name for name in
    filter(name->isdefined(Base,name), oper))

prettyPrint(ex) = get(operator_table, ex, ex)
function prettyPrint(e::Expr)
    ex = prettyfy(e)
    if ex.head === :quote
      return ex
    elseif ex.head === :(:=)
      return string(prettyPrint(ex.args[1]), " := ", prettyPrint(ex.args[2]))
    end
    Expr(ex.head, [prettyPrint(arg) for arg in ex.args]...)
end

function showSolve(e, x)
  println("\nSolve: ", x, " from: ", prettyPrint(e))
  sol, solved = solve(e, x)
  if ! solved
    println("NOT SOLVED")
  end
  println(prettyPrint(sol))
  return string(prettyPrint(sol))
end

function showDifferentiate(e)
  println("\nEquation: ", prettyPrint(e))
  der = differentiate(e)
  println("Differentiated: ", prettyPrint(der))
  return string(prettyPrint(der))
end

function testSolve()
  println("\nTest solve")

  sol = showSolve(:(y = x), :x)
  @test sol == "x = y"

  sol = showSolve(:(y = x + z), :x)
  @test sol == "x = y - z"

  sol = showSolve(Expr(:(=), :y, Expr(:call, :+, :x, :z, :v, :w)), :x)
  @test prettyPrint(sol) == "x = y - (z + v + w)"

  sol = showSolve(Expr(:(=), :y, Expr(:call, :+, :x, :z, :v, :w)), :z)
  @test sol == "z = (y - x) - (v + w)"

  sol = showSolve(Expr(:(=), :y, Expr(:call, :+, :x, :z, :v, :w)), :v)
  @test sol == "v = ((y - x) - z) - w"

  sol = showSolve(Expr(:(=), :y, Expr(:call, :+, :x, :z, :v, :w)), :w)
  @test sol == "w = ((y - x) - z) - v"

  sol = showSolve(:(y = x - z), :x)
  @test sol == "x = y + z"

  sol = showSolve(:(y = x - z - w), :x)
  @test sol == "x = (y + w) + z"

  sol = showSolve(Expr(:(=), :y, Expr(:call, :-, :x, :z, :v, :w)), :x)
  @test sol == "x = y + (z + v + w)"

  sol = showSolve(Expr(:(=), :y, Expr(:call, :-, :x, :z, :v, :w)), :v) # Solve: v from: y = x - z - v - w
  @test sol == "v = ((x - y) - z) - w"

  sol = showSolve(:(y = z - x), :x)
  @test sol == "x = z - y"

  sol = showSolve(:(y = x*z), :x)
  @test sol == "x = y / z"

  sol = showSolve(:(y = x*z*z*z), :x)
  @test sol == "x = y / (z * z * z)"

  sol = showSolve(Expr(:(=), :y, Expr(:call, :/, :x, :z, :w)), :x)
  @test sol == "x = y * (z * w)"

  sol = showSolve(Expr(:(=), :y, Expr(:call, /, :x, :z, :w)), :z)
  @test sol == "z = (x / y) / w"

  sol = showSolve(:(y = x / z), :x)
  @test sol == "x = y * z"

  sol = showSolve(:(y = x / z), :z)
  @test sol == "z = x / y"

  sol = showSolve(:(y = x \ z), :x)
  @test sol == "x \\ z = y"
  println("\n\n----------------------\n")

end

function testDifferentiate()
  println("\nTest differentiate")

  der = showDifferentiate(:(x + 5 + z = w))
  @test der == "der(x) + der(z) = der(w)"

  der = showDifferentiate(differentiate(:(x + 5 + z = w)))
  @test der == "der(der(x)) + der(der(z)) = der(der(w))"

  der = showDifferentiate(Expr(:(=), Expr(:call, :+, :x), :w))
  @test der == "der(x) = der(w)"

  der = showDifferentiate(:(2 + 3 = w))
  @test der == "0.0 = der(w)"

  der = showDifferentiate(Expr(:(=), Expr(:call, :-, :x), :w))
  @test der == "-(der(x)) = der(w)"

  der = showDifferentiate(:(x - 5 - z = w))
  @test der == "der(x) - der(z) = der(w)"

  der = showDifferentiate(:(5x = w))
  @test der == "5 * der(x) = der(w)"

  der = showDifferentiate(:(x * 5 * z = w))
  @test der == "der(x) * 5 * z + x * 5 * der(z) = der(w)"

  der = showDifferentiate(:(4 * 5 * 6 = w))
  @test der == "0.0 = der(w)"

  der = showDifferentiate(:(y = x/y))
  @test der == "der(y) = der(x) / y + (x / y ^ 2) * der(y)"

  der = showDifferentiate(:(y = x/5))
  @test der == "der(y) = der(x) / 5"

  der = showDifferentiate(:(y = 5/y))
  @test der == "der(y) = (5 / y ^ 2) * der(y)"

  der = showDifferentiate(:(y = [1, x]))
  @test der == "der(y) = [0.0, der(x)]"

  der = showDifferentiate(:(y = [2x 3x; 4x 5x]))
  @test der == "der(y) = [2 * der(x) 3 * der(x); 4 * der(x) 5 * der(x)]"

  der = showDifferentiate(:(y = [2*x 3x; 4x 5x]*[1, x]))
  @test der == "der(y) = [2 * der(x) 3 * der(x); 4 * der(x) 5 * der(x)] * [1, x] + [2x 3x; 4x 5x] * [0.0, der(x)]"

  der = showDifferentiate(:(y = transpose(B) + B´))
  @test der == "der(y) = transpose(der(B)) + der(B´)"

  der = showDifferentiate(:(y = x[5, 6]))
  @test der == "der(y) = (der(x))[5, 6]"

  der = showDifferentiate(:(y = x[5:7]))
  @test der == "der(y) = (der(x))[5:7]"

#=    der = showDifferentiate(:(y = [x for x in z]))
  @test der == "der(y) = [x for x = der(z)]"

  der = showDifferentiate(:(y = [x[i] for i in 1:5]))
  @test der == "der(y) = [x[i] for i = nothing]"
=#
  der = showDifferentiate(:(y = sin(x)))
  @test der == "der(y) = cos(x) * der(x)"

  der = showDifferentiate(:(y = cos(x)))
  @test der == "der(y) = -(sin(x)) * der(x)"

  der = showDifferentiate(:(y = tan(x)))
  @test der == "der(y) = (1 / cos(x) ^ 2) * der(x)"

  der = showDifferentiate(:(y = exp(x)))
  @test der == "der(y) = exp(x) * der(x)"

  der = showDifferentiate(:(y = x^y))
  @test der == "der(y) = y * x ^ (y - 1) * der(x) + x ^ y * log(x) * der(y)"

  der = showDifferentiate(:(y = log(x)))
  @test der == "der(y) = (1 / x) * der(x)"

  der = showDifferentiate(:(y = asin(x)))
  @test der == "der(y) = (1 / sqrt(1 - x ^ 2)) * der(x)"

  der = showDifferentiate(:(y = acos(x)))
  @test der == "der(y) = (-1 / sqrt(1 - x ^ 2)) * der(x)"

  der = showDifferentiate(:(y = atan(x)))
  @test der == "der(y) = (1 / (1 + x ^ 2)) * der(x)"

  der = showDifferentiate(:(y = f(x, 5, z)))
  @test der == "der(y) = f_der_1(x, 5, z) * der(x) + f_der_3(x, 5, z) * der(z)"

  der = showDifferentiate(:(y = f(x, 5, g(z))))
  @test der == "der(y) = f_der_1(x, 5, g(z)) * der(x) + f_der_3(x, 5, g(z)) * (g_der_1(z) * der(z))"

  der = showDifferentiate(:(y = true ? x : y))
@static if VERSION < v"0.7.0-DEV.2005"
    @test der == "der(y) = if true\n        der(x)\n    else \n        der(y)\n    end"
  else
    @test der == "der(y) = if true\n        der(x)\n    else\n        der(y)\n    end"
  end
#=
  der = showDifferentiate(:(y = if b; x elseif false; y else z end))
  @test der == "der(y) = if b\n        der(x)\n    else\n        if false\n            der(y)\n        else \n            der(z)\n        end\n    end"
=#
  der = showDifferentiate(:(y = time))
  @test der == "der(y) = 1.0"

  setTimeInvariants([:a])
  der = showDifferentiate(:(y = a*x))
  @test der == "der(y) = a * der(x)"

  println("\n\n----------------------\n")

  # check: time
end

@testset "Symbolic" begin

  @testset "Solve" begin
    testSolve()
  end # testset

  @testset "Differentiate" begin
    testDifferentiate()
  end

end

end


text:
#-----------------------------------------------------------------------------------
# PatternStructure.Trees;
#-----------------------------------------------------------------------------------
import ..PatternStructure.Trees: PatternTree, PatternRoot, PatternNode,  PatternLeaf,
                                 PatternGate, PatternStep, PatternCheck, PatternHead,
                                 ExprHead, makenode, newnode!, newleaf!
"""
PatternTree is an expression pattern represented as a tree
data structure. It can be a root, check, node or leaf.

Subtypes:
- `PatternLeaf`
- `PatternNode`
- `PatternRoot`
- `PatternGate`

Relevant functions:
- `insert!`
- `newleaf!`
- `newnode!`

"""
PatternTree;

"""
`PatternRoot` is the root of a pattern tree. It can
have one child. It's used to simplify initialization
of pattern trees.

Related:
- `PatternTree`

"""
PatternRoot;

"""
`PatternNode` is a node from a pattern tree. It can
have any number of children. It has a head value
much like Exprs. It also has a `step` functor that
is used to extract argument from the expression
it's trying to match. Nodes also keep track of
which of its children, if any, are slurps.

Related:
- `PatternStep`
- `PatternHead`
- `PatternTree`

"""
PatternNode;

"""
`PatternGate` has one child and a test. A expression
is only matched against the child if it passes the
test.

example:
```julia
  tree = analyze(:(x+y)).child
  vars = Variables(constants(tree))
  test = PredicateCheck(ex->all(iseven, ex.args[2:end]))
  gate = PatternGate(test)
  insert!(gate, tree)

  matchtree(gate, :(2+4), vars) == true  # passes test and matches the pattern
  matchtree(gate, :(2-4), vars) == false # passes test but doesn't match the pattern
  matchtree(gate, :(1+4), vars) == false # fails test, no attempt to match

# this particular pattern can be created more easily with:
  test  = ex->all(iseven, ex.args[2:end])
  match = matcher(:(:P{(x+y), \$test}))
  match(:(2+4)) == true
  match(:(2-4)) == false
  match(:(1+4)) == false
```

Related:
- `PatternTree`
- `PatternCheck`

"""
PatternGate;

"""
PatternLeaves signify the end of the pattern. Matching anything to
a leaf always return true.

Related:
- `PatternTree`

"""
PatternLeaf;

"""
PatternChecks are tests to be applied to expressions.
If an pattern is passed as an argument to a check,
the expression will be matched against the pattern
if it passes the test.

Types of test:

- `TypeCheck`
- `EqualityCheck`
- `PredicateCheck`

"""
PatternCheck;

"""
PatternStep is one component of pattern nodes. It is used to extract
the arguments of the expression in a way that they can be matched by
the children of the node.

Types of step:

- `ArgsStep`
- `QuoteStep`
- `BlockStep`
- `SlurpStep`

"""
PatternStep;

"""
Represents the type of a `PatternNode`.

Subtypes:
- `ExprHead`
- `SlurpHead`

"""
PatternHead;

"""
`makenode(head, step) -> PatternNode`

Creates a `PatternNode` with the provided `head` and `step`.

Related:
- `PatternNode`
- `PatternStep`

"""
makenode;

"""
`newnode!(head, step, parent) -> PatternNode`

Creates a `PatternNode` from the provided `head` and `step`
and inserts the node into `parent`.
(returns the new node)

Related:
- `PatternNode`
- `PatternStep`
- `PatternGate`

"""
newnode!;

"""
`newleaf!(check, parent) -> PatternLeaf`

Creates a `PatternLeaf`, inserts it into a `PatternGate`
along with the check, and then inserts the gate into `parent`.

Related:
- `PatternLeaf`
- `PatternStep`
- `PatternGate`

"""
newleaf!;

#-----------------------------------------------------------------------------------
# PatternStructure.Checks;
#-----------------------------------------------------------------------------------
import ..PatternStructure.Checks: Binding, EqualityCheck, TypeCheck, PredicateCheck,
                                  ArgsStep, BlockStep, QuoteStep, SlurpStep

"""
`Binding` represents an assignment to a name.
Any symbol that starts with a letter, `'@'`, `'#'` (gensyms)
are automatically considered to be bindings.
Use `:binding{x}`\\`:B{x}` to transform `x` into a binding
name, with `x` being any symbol.

`:literal{P}`\\`:L{P}` makes so that no symbol in `P` is considered
a binding name. `:autobinding{P}`\\`:A{P}` removes the `:literal`
effect.

"""
Binding;

"""

`EqualityCheck` is a `PatternCheck` that tests
if an expression is equal to some value.

"""
EqualityCheck;

"""
`TypeCheck` is a `PatternCheck` that tests
if an expression is of some type.

"""
TypeCheck;

"""
`PredicateCheck` is a `PatternCheck` that tests
if an expression fulfills some predicate.

"""
PredicateCheck;

"""
`ArgsStep` is a `PatternStep` that extracts
the arguments from a expression unmodified.

"""
ArgsStep;

"""
`BlockStep` is a `PatternStep` that extracts
the non-line-number arguments from a expression.

"""
BlockStep;

"""
`QuoteStep` is a `PatternStep` that extracts
the value of a `QuoteNode` or the arguments
from a `:quote` expression.

"""
QuoteStep;

"""
Since slurps already work with extracted
arguments, there is no need for a step for
them, this being a placeholder.
Calling `SlurpStep` causes an error.

"""
SlurpStep;

#-----------------------------------------------------------------------------------
# PatternStructure.SlurpTypes;
#-----------------------------------------------------------------------------------
import ..PatternStructure.SlurpTypes: SlurpHead, LazySlurp, GreedySlurp,
                                      GenericGreedySlurp, GenericLazySlurp,
                                      SimpleLastSlurp

"""
Represents a slurp algorithm. Can be a `LazySlurp` or a `GreedySlurp`, and
many variations of each.

Subtypes:
- `LazySlurp`
- `GreedySlurp`
- `GenericLazySlurp`
- `GenericGreedySlurp`
- `SimpleLastSlurp`

"""
SlurpHead;

"""
Match as many arguments as possible, then try
to match the rest of pattern, putting back
arguments from the slurp if neccessary.

"""
LazySlurp;

"""
Only try to match the slurp if the pattern after it
can't match the expression.

"""
GreedySlurp;

"""
Slowest version of a `LazySlurp`, but works for any pattern.

"""
GenericLazySlurp;

"""
Slowest version of a `GreedySlurp`, but works for any pattern.

"""
GenericGreedySlurp;

"""
Assumes the slurp is the last one, and its match a single symbol.
If the expression is of the form `*{s}, a₁,...,aₙ`, capture
everything minus the last `n` arguments.

"""
SimpleLastSlurp

#-----------------------------------------------------------------------------------
# PatternStructure.Special;
#-----------------------------------------------------------------------------------
import ..PatternStructure.Special: is_binding_name

"""
`is_binding_name(sym) -> Bool`

Determines if `sym` is to be automatically considered
a binding name. Returns `true` if `sym` starts with a
letter, `_`, `#` or `@`.

"""
is_binding_name;