text:
int_rules_1_1_3_3 = @theory begin

    #= ::Subsection::Closed:: =#
    #= 1.1.3.3*(a+b*x^n)^p*(c+d*x^n)^q =#
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n) ^ ~(q'), ~x) => Antiderivative(ExpandIntegrand((~a + ~b * (~x) ^ ~n) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ ~q, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (IGtQ(~p, 0) && IGtQ(~q, 0)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n) ^ ~(q'), ~x) => Antiderivative((~x) ^ (~n * (~p + ~q)) * (~b + ~a * (~x) ^ -(~n)) ^ ~p * (~d + ~c * (~x) ^ -(~n)) ^ ~q, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (IntegersQ(~p, ~q) && NegQ(~n)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n) ^ ~(q'), ~x) => -(Subst(Antiderivative(((~a + ~b * (~x) ^ -(~n)) ^ ~p * (~c + ~d * (~x) ^ -(~n)) ^ ~q) / (~x) ^ 2, ~x), ~x, 1 / ~x)) <-- FreeQ([~a, ~b, ~c, ~d, ~p, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && ILtQ(~n, 0))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n) ^ ~(q'), ~x) => With([g = Denominator(~n)], g * Subst(Antiderivative((~x) ^ (g - 1) * (~a + ~b * (~x) ^ (g * ~n)) ^ ~p * (~c + ~d * (~x) ^ (g * ~n)) ^ ~q, ~x), ~x, (~x) ^ (1 / g))) <-- FreeQ([~a, ~b, ~c, ~d, ~p, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && FractionQ(~n))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 3) ^ (1 / 3) * (~c + ~(d') * (~x) ^ 3)), ~x) => With([q = Rt((~b * ~c - ~a * ~d) / ~c, 3)], (atan((1 + (2 * q * ~x) / (~a + ~b * (~x) ^ 3) ^ (1 / 3)) / sqrt(3)) / (sqrt(3) * ~c * q) + log(~c + ~d * (~x) ^ 3) / (6 * ~c * q)) - log(q * ~x - (~a + ~b * (~x) ^ 3) ^ (1 / 3)) / (2 * ~c * q)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p / (~c + ~(d') * (~x) ^ ~n), ~x) => Subst(Antiderivative(1 / (~c - (~b * ~c - ~a * ~d) * (~x) ^ ~n), ~x), ~x, ~x / (~a + ~b * (~x) ^ ~n) ^ (1 / ~n)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~n * ~p + 1, 0) && IntegerQ(~n)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~(q'), ~x) => (-(~x) * (~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ ~q) / (~a * ~n * (~p + 1)) - ((~c * ~q) / (~a * (~p + 1))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q - 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~n * (~p + ~q + 1) + 1, 0) && (GtQ(~q, 0) && NeQ(~p, -1))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (((~a) ^ ~p * ~x) / ((~c) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (1 / ~n))) * HypergeometricFunctions._₂F₁(1 / ~n, -(~p), 1 + 1 / ~n, (-((~b * ~c - ~a * ~d)) * (~x) ^ ~n) / (~a * (~c + ~d * (~x) ^ ~n))) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~n * (~p + ~q + 1) + 1, 0) && ILtQ(~p, 0)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => ((~x * (~a + ~b * (~x) ^ ~n) ^ ~p) / (~c * ((~c * (~a + ~b * (~x) ^ ~n)) / (~a * (~c + ~d * (~x) ^ ~n))) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ (1 / ~n + ~p))) * HypergeometricFunctions._₂F₁(1 / ~n, -(~p), 1 + 1 / ~n, (-((~b * ~c - ~a * ~d)) * (~x) ^ ~n) / (~a * (~c + ~d * (~x) ^ ~n))) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && EqQ(~n * (~p + ~q + 1) + 1, 0))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q + 1)) / (~a * ~c) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~n * (~p + ~q + 2) + 1, 0) && EqQ(~a * ~d * (~p + 1) + ~b * ~c * (~q + 1), 0)))
    #= Antiderivative(((~a1)+(~b1')*(~x)^(~n2'))^(~p)*((~a2)+(~b2')*(~x)^(~n2'))^(~p)*((~c)+(~d')*(~x)^(~n))^(~q), ~x) :=  x*(a1+b1*x^(n/2))^(p+1)*(a2+b2*x^(n/2))^(p+1)*(c+d*x^n)^(q+1)/(a1*a2* c) <-- FreeQ([a1,b1,a2,b2,c,d,n,p,q],x) && EqQ(n2,n/2) &&  EqQ(a2*b1+a1*b2,0) && EqQ(n*(p+q+2)+1,0) &&  EqQ(a1*a2*d*(p+1)+b1*b2*c*(q+1),0) =#
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (-(~b) * ~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q + 1)) / (~a * ~n * (~p + 1) * (~b * ~c - ~a * ~d)) + ((~b * ~c + ~n * (~p + 1) * (~b * ~c - ~a * ~d)) / (~a * ~n * (~p + 1) * (~b * ~c - ~a * ~d))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ ~q, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~n * (~p + ~q + 2) + 1, 0) && ((LtQ(~p, -1) || Not(LtQ(~q, -1))) && NeQ(~p, -1))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n), ~x) => (~c * ~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1)) / ~a <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && EqQ(~a * ~d - ~b * ~c * (~n * (~p + 1) + 1), 0))
    @apply_utils Antiderivative((~a1 + ~(b1') * (~x) ^ ~(non2')) ^ ~(p') * (~a2 + ~(b2') * (~x) ^ ~(non2')) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n), ~x) => (~c * ~x * (~a1 + ~b1 * (~x) ^ (~n / 2)) ^ (~p + 1) * (~a2 + ~b2 * (~x) ^ (~n / 2)) ^ (~p + 1)) / (~a1 * ~a2) <-- FreeQ([~a1, ~b1, ~a2, ~b2, ~c, ~d, ~n, ~p], ~x) && (EqQ(~non2, ~n / 2) && (EqQ(~a2 * ~b1 + ~a1 * ~b2, 0) && EqQ(~a1 * ~a2 * ~d - ~b1 * ~b2 * ~c * (~n * (~p + 1) + 1), 0)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n), ~x) => (-((~b * ~c - ~a * ~d)) * ~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1)) / (~a * ~b * ~n * (~p + 1)) - ((~a * ~d - ~b * ~c * (~n * (~p + 1) + 1)) / (~a * ~b * ~n * (~p + 1))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p + 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (LtQ(~p, -1) || ILtQ(1 / ~n + ~p, 0)))
    @apply_utils Antiderivative((~a1 + ~(b1') * (~x) ^ ~(non2')) ^ ~(p') * (~a2 + ~(b2') * (~x) ^ ~(non2')) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n), ~x) => (-((~b1 * ~b2 * ~c - ~a1 * ~a2 * ~d)) * ~x * (~a1 + ~b1 * (~x) ^ (~n / 2)) ^ (~p + 1) * (~a2 + ~b2 * (~x) ^ (~n / 2)) ^ (~p + 1)) / (~a1 * ~a2 * ~b1 * ~b2 * ~n * (~p + 1)) - ((~a1 * ~a2 * ~d - ~b1 * ~b2 * ~c * (~n * (~p + 1) + 1)) / (~a1 * ~a2 * ~b1 * ~b2 * ~n * (~p + 1))) * Antiderivative((~a1 + ~b1 * (~x) ^ (~n / 2)) ^ (~p + 1) * (~a2 + ~b2 * (~x) ^ (~n / 2)) ^ (~p + 1), ~x) <-- FreeQ([~a1, ~b1, ~a2, ~b2, ~c, ~d, ~n], ~x) && (EqQ(~non2, ~n / 2) && (EqQ(~a2 * ~b1 + ~a1 * ~b2, 0) && (LtQ(~p, -1) || ILtQ(1 / ~n + ~p, 0))))
    @apply_utils Antiderivative((~c + ~(d') * (~x) ^ ~n) / (~a + ~(b') * (~x) ^ ~n), ~x) => (~c * ~x) / ~a - ((~b * ~c - ~a * ~d) / ~a) * Antiderivative(1 / (~b + ~a * (~x) ^ -(~n)), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && LtQ(~n, 0))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n), ~x) => (~d * ~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1)) / (~b * (~n * (~p + 1) + 1)) - ((~a * ~d - ~b * ~c * (~n * (~p + 1) + 1)) / (~b * (~n * (~p + 1) + 1))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && NeQ(~n * (~p + 1) + 1, 0))
    @apply_utils Antiderivative((~a1 + ~(b1') * (~x) ^ ~(non2')) ^ ~(p') * (~a2 + ~(b2') * (~x) ^ ~(non2')) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n), ~x) => (~d * ~x * (~a1 + ~b1 * (~x) ^ (~n / 2)) ^ (~p + 1) * (~a2 + ~b2 * (~x) ^ (~n / 2)) ^ (~p + 1)) / (~b1 * ~b2 * (~n * (~p + 1) + 1)) - ((~a1 * ~a2 * ~d - ~b1 * ~b2 * ~c * (~n * (~p + 1) + 1)) / (~b1 * ~b2 * (~n * (~p + 1) + 1))) * Antiderivative((~a1 + ~b1 * (~x) ^ (~n / 2)) ^ ~p * (~a2 + ~b2 * (~x) ^ (~n / 2)) ^ ~p, ~x) <-- FreeQ([~a1, ~b1, ~a2, ~b2, ~c, ~d, ~n, ~p], ~x) && (EqQ(~non2, ~n / 2) && (EqQ(~a2 * ~b1 + ~a1 * ~b2, 0) && NeQ(~n * (~p + 1) + 1, 0)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => Antiderivative(PolynomialDivide((~a + ~b * (~x) ^ ~n) ^ ~p, (~c + ~d * (~x) ^ ~n) ^ -(~q), ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (IGtQ(~n, 0) && (IGtQ(~p, 0) && (ILtQ(~q, 0) && GeQ(~p, -(~q))))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p / (~c + ~(d') * (~x) ^ ~n), ~x) => (~b / ~d) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p - 1), ~x) - ((~b * ~c - ~a * ~d) / ~d) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p - 1) / (~c + ~d * (~x) ^ ~n), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~p], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~n * (~p - 1) + 1, 0) && IntegerQ(~n)))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ ~n) * (~c + ~(d') * (~x) ^ ~n)), ~x) => (~b / (~b * ~c - ~a * ~d)) * Antiderivative(1 / (~a + ~b * (~x) ^ ~n), ~x) - (~d / (~b * ~c - ~a * ~d)) * Antiderivative(1 / (~c + ~d * (~x) ^ ~n), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (1 / 3) * (~c + ~(d') * (~x) ^ 2)), ~x) => With([q = Rt(~b / ~a, 2)], ((q * atanh(sqrt(3) / (q * ~x))) / (2 * 2 ^ (2 / 3) * sqrt(3) * (~a) ^ (1 / 3) * ~d) + (q * atanh((sqrt(3) * ((~a) ^ (1 / 3) - 2 ^ (1 / 3) * (~a + ~b * (~x) ^ 2) ^ (1 / 3))) / ((~a) ^ (1 / 3) * q * ~x))) / (2 * 2 ^ (2 / 3) * sqrt(3) * (~a) ^ (1 / 3) * ~d) + (q * atan(q * ~x)) / (6 * 2 ^ (2 / 3) * (~a) ^ (1 / 3) * ~d)) - (q * atan(((~a) ^ (1 / 3) * q * ~x) / ((~a) ^ (1 / 3) + 2 ^ (1 / 3) * (~a + ~b * (~x) ^ 2) ^ (1 / 3)))) / (2 * 2 ^ (2 / 3) * (~a) ^ (1 / 3) * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~b * ~c + 3 * ~a * ~d, 0) && PosQ(~b / ~a)))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (1 / 3) * (~c + ~(d') * (~x) ^ 2)), ~x) => With([q = Rt(-(~b) / ~a, 2)], (((q * atan(sqrt(3) / (q * ~x))) / (2 * 2 ^ (2 / 3) * sqrt(3) * (~a) ^ (1 / 3) * ~d) + (q * atan((sqrt(3) * ((~a) ^ (1 / 3) - 2 ^ (1 / 3) * (~a + ~b * (~x) ^ 2) ^ (1 / 3))) / ((~a) ^ (1 / 3) * q * ~x))) / (2 * 2 ^ (2 / 3) * sqrt(3) * (~a) ^ (1 / 3) * ~d)) - (q * atanh(q * ~x)) / (6 * 2 ^ (2 / 3) * (~a) ^ (1 / 3) * ~d)) + (q * atanh(((~a) ^ (1 / 3) * q * ~x) / ((~a) ^ (1 / 3) + 2 ^ (1 / 3) * (~a + ~b * (~x) ^ 2) ^ (1 / 3)))) / (2 * 2 ^ (2 / 3) * (~a) ^ (1 / 3) * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~b * ~c + 3 * ~a * ~d, 0) && NegQ(~b / ~a)))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (1 / 3) * (~c + ~(d') * (~x) ^ 2)), ~x) => With([q = Rt(~b / ~a, 2)], ((q * atan((q * ~x) / 3)) / (12 * Rt(~a, 3) * ~d) + (q * atan((Rt(~a, 3) - (~a + ~b * (~x) ^ 2) ^ (1 / 3)) ^ 2 / (3 * Rt(~a, 3) ^ 2 * q * ~x))) / (12 * Rt(~a, 3) * ~d)) - (q * atanh((sqrt(3) * (Rt(~a, 3) - (~a + ~b * (~x) ^ 2) ^ (1 / 3))) / (Rt(~a, 3) * q * ~x))) / (4 * sqrt(3) * Rt(~a, 3) * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~b * ~c - 9 * ~a * ~d, 0) && PosQ(~b / ~a)))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (1 / 3) * (~c + ~(d') * (~x) ^ 2)), ~x) => With([q = Rt(-(~b) / ~a, 2)], ((-q * atanh((q * ~x) / 3)) / (12 * Rt(~a, 3) * ~d) + (q * atanh((Rt(~a, 3) - (~a + ~b * (~x) ^ 2) ^ (1 / 3)) ^ 2 / (3 * Rt(~a, 3) ^ 2 * q * ~x))) / (12 * Rt(~a, 3) * ~d)) - (q * atan((sqrt(3) * (Rt(~a, 3) - (~a + ~b * (~x) ^ 2) ^ (1 / 3))) / (Rt(~a, 3) * q * ~x))) / (4 * sqrt(3) * Rt(~a, 3) * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (EqQ(~b * ~c - 9 * ~a * ~d, 0) && NegQ(~b / ~a)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ 2) ^ (2 / 3) / (~c + ~(d') * (~x) ^ 2), ~x) => (~b / ~d) * Antiderivative(1 / (~a + ~b * (~x) ^ 2) ^ (1 / 3), ~x) - ((~b * ~c - ~a * ~d) / ~d) * Antiderivative(1 / ((~a + ~b * (~x) ^ 2) ^ (1 / 3) * (~c + ~d * (~x) ^ 2)), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && EqQ(~b * ~c + 3 * ~a * ~d, 0))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (1 / 4) * (~c + ~(d') * (~x) ^ 2)), ~x) => With([q = Rt((~b) ^ 2 / ~a, 4)], (-(~b) / (2 * ~a * ~d * q)) * atan((~b + q ^ 2 * sqrt(~a + ~b * (~x) ^ 2)) / (q ^ 3 * ~x * (~a + ~b * (~x) ^ 2) ^ (1 / 4))) - (~b / (2 * ~a * ~d * q)) * atanh((~b - q ^ 2 * sqrt(~a + ~b * (~x) ^ 2)) / (q ^ 3 * ~x * (~a + ~b * (~x) ^ 2) ^ (1 / 4)))) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (EqQ(~b * ~c - 2 * ~a * ~d, 0) && PosQ((~b) ^ 2 / ~a))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (1 / 4) * (~c + ~(d') * (~x) ^ 2)), ~x) => With([q = Rt(-((~b) ^ 2) / ~a, 4)], (~b / (2 * sqrt(2) * ~a * ~d * q)) * atan((q * ~x) / (sqrt(2) * (~a + ~b * (~x) ^ 2) ^ (1 / 4))) + (~b / (2 * sqrt(2) * ~a * ~d * q)) * atanh((q * ~x) / (sqrt(2) * (~a + ~b * (~x) ^ 2) ^ (1 / 4)))) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (EqQ(~b * ~c - 2 * ~a * ~d, 0) && NegQ((~b) ^ 2 / ~a))
    #= Antiderivative(1/(((~a)+(~b')*(~x)^2)^(1/4)*((~c)+(~d')*(~x)^2)),~x) :=  With([q=Rt(-b^2/a,4)], b/(2*sqrt(2)*a*d*q)*atan(q*x/(sqrt(2)*(a+b*x^2)^(1/4))) + b/(4*sqrt(2)*a*d*q)*log((sqrt(2)*q*x+2*(a+b*x^2)^(1/4))/(sqrt(2)*q* x-2*(a+b*x^2)^(1/4)))) <-- FreeQ([a,b,c,d],x) && EqQ(b*c-2*a*d,0) &&  NegQ(b^2/a) =#
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (1 / 4) * (~c + ~(d') * (~x) ^ 2)), ~x) => ((2 * sqrt((-(~b) * (~x) ^ 2) / ~a)) / ~x) * Subst(Antiderivative((~x) ^ 2 / (sqrt(1 - (~x) ^ 4 / ~a) * ((~b * ~c - ~a * ~d) + ~d * (~x) ^ 4)), ~x), ~x, (~a + ~b * (~x) ^ 2) ^ (1 / 4)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (3 / 4) * (~c + ~(d') * (~x) ^ 2)), ~x) => (1 / ~c) * Antiderivative(1 / (~a + ~b * (~x) ^ 2) ^ (3 / 4), ~x) - (~d / ~c) * Antiderivative((~x) ^ 2 / ((~a + ~b * (~x) ^ 2) ^ (3 / 4) * (~c + ~d * (~x) ^ 2)), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && EqQ(~b * ~c - 2 * ~a * ~d, 0)
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 2) ^ (3 / 4) * (~c + ~(d') * (~x) ^ 2)), ~x) => (sqrt((-(~b) * (~x) ^ 2) / ~a) / (2 * ~x)) * Subst(Antiderivative(1 / (sqrt((-(~b) * ~x) / ~a) * (~a + ~b * ~x) ^ (3 / 4) * (~c + ~d * ~x)), ~x), ~x, (~x) ^ 2) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ 2) ^ ~(p') / (~c + ~(d') * (~x) ^ 2), ~x) => (~b / ~d) * Antiderivative((~a + ~b * (~x) ^ 2) ^ (~p - 1), ~x) - ((~b * ~c - ~a * ~d) / ~d) * Antiderivative((~a + ~b * (~x) ^ 2) ^ (~p - 1) / (~c + ~d * (~x) ^ 2), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (GtQ(~p, 0) && (EqQ(~p, 1 / 2) || EqQ(Denominator(~p), 4))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ 2) ^ ~p / (~c + ~(d') * (~x) ^ 2), ~x) => (~b / (~b * ~c - ~a * ~d)) * Antiderivative((~a + ~b * (~x) ^ 2) ^ ~p, ~x) - (~d / (~b * ~c - ~a * ~d)) * Antiderivative((~a + ~b * (~x) ^ 2) ^ (~p + 1) / (~c + ~d * (~x) ^ 2), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (LtQ(~p, -1) && (EqQ(Denominator(~p), 4) && (EqQ(~p, -5 / 4) || EqQ(~p, -7 / 4)))))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 4) / (~c + ~(d') * (~x) ^ 4), ~x) => (~a / ~c) * Subst(Antiderivative(1 / (1 - 4 * ~a * ~b * (~x) ^ 4), ~x), ~x, ~x / sqrt(~a + ~b * (~x) ^ 4)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (EqQ(~b * ~c + ~a * ~d, 0) && PosQ(~a * ~b))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 4) / (~c + ~(d') * (~x) ^ 4), ~x) => With([q = Rt(-(~a) * ~b, 4)], (~a / (2 * ~c * q)) * atan((q * ~x * (~a + q ^ 2 * (~x) ^ 2)) / (~a * sqrt(~a + ~b * (~x) ^ 4))) + (~a / (2 * ~c * q)) * atanh((q * ~x * (~a - q ^ 2 * (~x) ^ 2)) / (~a * sqrt(~a + ~b * (~x) ^ 4)))) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (EqQ(~b * ~c + ~a * ~d, 0) && NegQ(~a * ~b))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 4) / (~c + ~(d') * (~x) ^ 4), ~x) => (~b / ~d) * Antiderivative(1 / sqrt(~a + ~b * (~x) ^ 4), ~x) - ((~b * ~c - ~a * ~d) / ~d) * Antiderivative(1 / (sqrt(~a + ~b * (~x) ^ 4) * (~c + ~d * (~x) ^ 4)), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ 4) ^ (1 / 4) / (~c + ~(d') * (~x) ^ 4), ~x) => sqrt(~a + ~b * (~x) ^ 4) * sqrt(~a / (~a + ~b * (~x) ^ 4)) * Subst(Antiderivative(1 / (sqrt(1 - ~b * (~x) ^ 4) * (~c - (~b * ~c - ~a * ~d) * (~x) ^ 4)), ~x), ~x, ~x / (~a + ~b * (~x) ^ 4) ^ (1 / 4)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ 4) ^ (5 / 4) / (~c + ~(d') * (~x) ^ 4), ~x) => (~b / ~d) * Antiderivative((~a + ~b * (~x) ^ 4) ^ (1 / 4), ~x) - ((~b * ~c - ~a * ~d) / ~d) * Antiderivative((~a + ~b * (~x) ^ 4) ^ (1 / 4) / (~c + ~d * (~x) ^ 4), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative(1 / (sqrt(~a + ~(b') * (~x) ^ 4) * (~c + ~(d') * (~x) ^ 4)), ~x) => (1 / (2 * ~c)) * Antiderivative(1 / (sqrt(~a + ~b * (~x) ^ 4) * (1 - Rt(-(~d) / ~c, 2) * (~x) ^ 2)), ~x) + (1 / (2 * ~c)) * Antiderivative(1 / (sqrt(~a + ~b * (~x) ^ 4) * (1 + Rt(-(~d) / ~c, 2) * (~x) ^ 2)), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 4) ^ (3 / 4) * (~c + ~(d') * (~x) ^ 4)), ~x) => (~b / (~b * ~c - ~a * ~d)) * Antiderivative(1 / (~a + ~b * (~x) ^ 4) ^ (3 / 4), ~x) - (~d / (~b * ~c - ~a * ~d)) * Antiderivative((~a + ~b * (~x) ^ 4) ^ (1 / 4) / (~c + ~d * (~x) ^ 4), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && NeQ(~b * ~c - ~a * ~d, 0)
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ 3) ^ (1 / 3) / (~c + ~(d') * (~x) ^ 3), ~x) => With([q = Rt(~b / ~a, 3)], ((9 * ~a) / (~c * q)) * Subst(Antiderivative(~x / ((4 - ~a * (~x) ^ 3) * (1 + 2 * ~a * (~x) ^ 3)), ~x), ~x, (1 + q * ~x) / (~a + ~b * (~x) ^ 3) ^ (1 / 3))) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && EqQ(~b * ~c + ~a * ~d, 0))
    @apply_utils Antiderivative(1 / ((~a + ~(b') * (~x) ^ 3) ^ (2 / 3) * (~c + ~(d') * (~x) ^ 3)), ~x) => (~b / (~b * ~c - ~a * ~d)) * Antiderivative(1 / (~a + ~b * (~x) ^ 3) ^ (2 / 3), ~x) - (~d / (~b * ~c - ~a * ~d)) * Antiderivative((~a + ~b * (~x) ^ 3) ^ (1 / 3) / (~c + ~d * (~x) ^ 3), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && EqQ(~b * ~c + ~a * ~d, 0))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 2) / (~c + ~(d') * (~x) ^ 2) ^ (3 / 2), ~x) => (sqrt(~a + ~b * (~x) ^ 2) / (~c * Rt(~d / ~c, 2) * sqrt(~c + ~d * (~x) ^ 2) * sqrt((~c * (~a + ~b * (~x) ^ 2)) / (~a * (~c + ~d * (~x) ^ 2))))) * Elliptic.E(atan(Rt(~d / ~c, 2) * ~x), 1 - (~b * ~c) / (~a * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (PosQ(~b / ~a) && PosQ(~d / ~c))
    #= Antiderivative(sqrt((~a)+(~b')*(~x)^2)/((~c)+(~d')*(~x)^2)^(3/2),~x) :=  a*sqrt(c+d*x^2)*sqrt((c*(a+b*x^2))/(a*(c+d*x^2)))/(c^2*Rt(d/c,2)*sqrt( a+b*x^2))*Elliptic.E(atan(Rt(d/c,2)*x),1-b*c/(a*d)) <--  FreeQ([a,b,c,d],x) && PosQ(b/a) && PosQ(d/c) =#
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (-(~x) * (~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ ~q) / (~a * ~n * (~p + 1)) + (1 / (~a * ~n * (~p + 1))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q - 1) * Simp(~c * (~n * (~p + 1) + 1) + ~d * (~n * (~p + ~q + 1) + 1) * (~x) ^ ~n, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (LtQ(~p, -1) && (LtQ(0, ~q, 1) && IntBinomialQ(~a, ~b, ~c, ~d, ~n, ~p, ~q, ~x))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => ((~a * ~d - ~c * ~b) * ~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q - 1)) / (~a * ~b * ~n * (~p + 1)) - (1 / (~a * ~b * ~n * (~p + 1))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q - 2) * Simp(~c * (~a * ~d - ~c * ~b * (~n * (~p + 1) + 1)) + ~d * (~a * ~d * (~n * (~q - 1) + 1) - ~b * ~c * (~n * (~p + ~q) + 1)) * (~x) ^ ~n, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (LtQ(~p, -1) && (GtQ(~q, 1) && IntBinomialQ(~a, ~b, ~c, ~d, ~n, ~p, ~q, ~x))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (-(~b) * ~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q + 1)) / (~a * ~n * (~p + 1) * (~b * ~c - ~a * ~d)) + (1 / (~a * ~n * (~p + 1) * (~b * ~c - ~a * ~d))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ ~q * Simp(~b * ~c + ~n * (~p + 1) * (~b * ~c - ~a * ~d) + ~d * ~b * (~n * (~p + ~q + 2) + 1) * (~x) ^ ~n, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (LtQ(~p, -1) && (Not(Not(IntegerQ(~p)) && (IntegerQ(~q) && LtQ(~q, -1))) && IntBinomialQ(~a, ~b, ~c, ~d, ~n, ~p, ~q, ~x))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => Antiderivative(ExpandIntegrand((~a + ~b * (~x) ^ ~n) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ ~q, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (IGtQ(~n, 0) && (IntegersQ(~p, ~q) && GtQ(~p + ~q, 0))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (~d * ~x * (~a + ~b * (~x) ^ ~n) ^ (~p + 1) * (~c + ~d * (~x) ^ ~n) ^ (~q - 1)) / (~b * (~n * (~p + ~q) + 1)) + (1 / (~b * (~n * (~p + ~q) + 1))) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ (~q - 2) * Simp(~c * (~b * ~c * (~n * (~p + ~q) + 1) - ~a * ~d) + ~d * (~b * ~c * (~n * ((~p + 2 * ~q) - 1) + 1) - ~a * ~d * (~n * (~q - 1) + 1)) * (~x) ^ ~n, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (GtQ(~q, 1) && (NeQ(~n * (~p + ~q) + 1, 0) && (Not(IGtQ(~p, 1)) && IntBinomialQ(~a, ~b, ~c, ~d, ~n, ~p, ~q, ~x)))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (~x * (~a + ~b * (~x) ^ ~n) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ ~q) / (~n * (~p + ~q) + 1) + (~n / (~n * (~p + ~q) + 1)) * Antiderivative((~a + ~b * (~x) ^ ~n) ^ (~p - 1) * (~c + ~d * (~x) ^ ~n) ^ (~q - 1) * Simp(~a * ~c * (~p + ~q) + (~q * (~b * ~c - ~a * ~d) + ~a * ~d * (~p + ~q)) * (~x) ^ ~n, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (GtQ(~q, 0) && (GtQ(~p, 0) && IntBinomialQ(~a, ~b, ~c, ~d, ~n, ~p, ~q, ~x))))
    @apply_utils Antiderivative(1 / (sqrt(~a + ~(b') * (~x) ^ 2) * sqrt(~c + ~(d') * (~x) ^ 2)), ~x) => (sqrt(~a + ~b * (~x) ^ 2) / (~a * Rt(~d / ~c, 2) * sqrt(~c + ~d * (~x) ^ 2) * sqrt((~c * (~a + ~b * (~x) ^ 2)) / (~a * (~c + ~d * (~x) ^ 2))))) * Elliptic.F(atan(Rt(~d / ~c, 2) * ~x), 1 - (~b * ~c) / (~a * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (PosQ(~d / ~c) && (PosQ(~b / ~a) && Not(SimplerSqrtQ(~b / ~a, ~d / ~c))))
    #= Antiderivative(1/(sqrt((~a)+(~b')*(~x)^2)*sqrt((~c)+(~d')*(~x)^2)),~x) :=  sqrt(c+d*x^2)*sqrt(c*(a+b*x^2)/(a*(c+d*x^2)))/(c*Rt(d/c,2)*sqrt(a+b*x^ 2))*Elliptic.F(atan(Rt(d/c,2)*x),1-b*c/(a*d)) <-- FreeQ([a,b,c,d],x) &&  PosQ(d/c) && PosQ(b/a) && Not(SimplerSqrtQ(b/a,d/c)) =#
    @apply_utils Antiderivative(1 / (sqrt(~a + ~(b') * (~x) ^ 2) * sqrt(~c + ~(d') * (~x) ^ 2)), ~x) => (1 / (sqrt(~a) * sqrt(~c) * Rt(-(~d) / ~c, 2))) * Elliptic.F(asin(Rt(-(~d) / ~c, 2) * ~x), (~b * ~c) / (~a * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NegQ(~d / ~c) && (GtQ(~c, 0) && (GtQ(~a, 0) && Not(NegQ(~b / ~a) && SimplerSqrtQ(-(~b) / ~a, -(~d) / ~c)))))
    @apply_utils Antiderivative(1 / (sqrt(~a + ~(b') * (~x) ^ 2) * sqrt(~c + ~(d') * (~x) ^ 2)), ~x) => (-1 / (sqrt(~c) * Rt(-(~d) / ~c, 2) * sqrt(~a - (~b * ~c) / ~d))) * Elliptic.F(acos(Rt(-(~d) / ~c, 2) * ~x), (~b * ~c) / (~b * ~c - ~a * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NegQ(~d / ~c) && (GtQ(~c, 0) && GtQ(~a - (~b * ~c) / ~d, 0)))
    @apply_utils Antiderivative(1 / (sqrt(~a + ~(b') * (~x) ^ 2) * sqrt(~c + ~(d') * (~x) ^ 2)), ~x) => (sqrt(1 + (~d / ~c) * (~x) ^ 2) / sqrt(~c + ~d * (~x) ^ 2)) * Antiderivative(1 / (sqrt(~a + ~b * (~x) ^ 2) * sqrt(1 + (~d / ~c) * (~x) ^ 2)), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && Not(GtQ(~c, 0))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 2) / sqrt(~c + ~(d') * (~x) ^ 2), ~x) => ~a * Antiderivative(1 / (sqrt(~a + ~b * (~x) ^ 2) * sqrt(~c + ~d * (~x) ^ 2)), ~x) + ~b * Antiderivative((~x) ^ 2 / (sqrt(~a + ~b * (~x) ^ 2) * sqrt(~c + ~d * (~x) ^ 2)), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (PosQ(~d / ~c) && PosQ(~b / ~a))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 2) / sqrt(~c + ~(d') * (~x) ^ 2), ~x) => (~b / ~d) * Antiderivative(sqrt(~c + ~d * (~x) ^ 2) / sqrt(~a + ~b * (~x) ^ 2), ~x) - ((~b * ~c - ~a * ~d) / ~d) * Antiderivative(1 / (sqrt(~a + ~b * (~x) ^ 2) * sqrt(~c + ~d * (~x) ^ 2)), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (PosQ(~d / ~c) && NegQ(~b / ~a))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 2) / sqrt(~c + ~(d') * (~x) ^ 2), ~x) => (sqrt(~a) / (sqrt(~c) * Rt(-(~d) / ~c, 2))) * Elliptic.E(asin(Rt(-(~d) / ~c, 2) * ~x), (~b * ~c) / (~a * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NegQ(~d / ~c) && (GtQ(~c, 0) && GtQ(~a, 0)))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 2) / sqrt(~c + ~(d') * (~x) ^ 2), ~x) => (-(sqrt(~a - (~b * ~c) / ~d)) / (sqrt(~c) * Rt(-(~d) / ~c, 2))) * Elliptic.E(acos(Rt(-(~d) / ~c, 2) * ~x), (~b * ~c) / (~b * ~c - ~a * ~d)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NegQ(~d / ~c) && (GtQ(~c, 0) && GtQ(~a - (~b * ~c) / ~d, 0)))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 2) / sqrt(~c + ~(d') * (~x) ^ 2), ~x) => (sqrt(~a + ~b * (~x) ^ 2) / sqrt(1 + (~b / ~a) * (~x) ^ 2)) * Antiderivative(sqrt(1 + (~b / ~a) * (~x) ^ 2) / sqrt(~c + ~d * (~x) ^ 2), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NegQ(~d / ~c) && (GtQ(~c, 0) && Not(GtQ(~a, 0))))
    @apply_utils Antiderivative(sqrt(~a + ~(b') * (~x) ^ 2) / sqrt(~c + ~(d') * (~x) ^ 2), ~x) => (sqrt(1 + (~d / ~c) * (~x) ^ 2) / sqrt(~c + ~d * (~x) ^ 2)) * Antiderivative(sqrt(~a + ~b * (~x) ^ 2) / sqrt(1 + (~d / ~c) * (~x) ^ 2), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NegQ(~d / ~c) && Not(GtQ(~c, 0)))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~(p') * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => Antiderivative(ExpandIntegrand((~a + ~b * (~x) ^ ~n) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ ~q, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && IGtQ(~p, 0))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (~a) ^ ~p * (~c) ^ ~q * ~x * AppellF1(1 / ~n, -(~p), -(~q), 1 + 1 / ~n, (-(~b) * (~x) ^ ~n) / ~a, (-(~d) * (~x) ^ ~n) / ~c) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (NeQ(~n, -1) && ((IntegerQ(~p) || GtQ(~a, 0)) && (IntegerQ(~q) || GtQ(~c, 0)))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~n) ^ ~p * (~c + ~(d') * (~x) ^ ~n) ^ ~q, ~x) => (((~a) ^ IntPart(~p) * (~a + ~b * (~x) ^ ~n) ^ FracPart(~p)) / (1 + (~b * (~x) ^ ~n) / ~a) ^ FracPart(~p)) * Antiderivative((1 + (~b * (~x) ^ ~n) / ~a) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ ~q, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p, ~q], ~x) && (NeQ(~b * ~c - ~a * ~d, 0) && (NeQ(~n, -1) && Not(IntegerQ(~p) || GtQ(~a, 0))))
    @apply_utils Antiderivative((~(a') + ~(b') * (~u) ^ ~n) ^ ~(p') * (~(c') + ~(d') * (~u) ^ ~n) ^ ~(q'), ~x) => (1 / Coefficient(~u, ~x, 1)) * Subst(Antiderivative((~a + ~b * (~x) ^ ~n) ^ ~p * (~c + ~d * (~x) ^ ~n) ^ ~q, ~x), ~x, ~u) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p, ~q], ~x) && (LinearQ(~u, ~x) && NeQ(~u, ~x))
    @apply_utils Antiderivative((~u) ^ ~(p') * (~v) ^ ~(q'), ~x) => Antiderivative(NormalizePseudoBinomial(~u, ~x) ^ ~p * NormalizePseudoBinomial(~v, ~x) ^ ~q, ~x) <-- FreeQ([~p, ~q], ~x) && PseudoBinomialPairQ(~u, ~v, ~x)
    @apply_utils Antiderivative((~x) ^ ~(m') * (~u) ^ ~(p') * (~v) ^ ~(q'), ~x) => Antiderivative(NormalizePseudoBinomial((~x) ^ (~m / ~p) * ~u, ~x) ^ ~p * NormalizePseudoBinomial(~v, ~x) ^ ~q, ~x) <-- FreeQ([~p, ~q], ~x) && (IntegersQ(~p, ~m / ~p) && PseudoBinomialPairQ((~x) ^ (~m / ~p) * ~u, ~v, ~x))
    #= IntBinomialQ(a,b,c,d,n,p,q,x)*returns*True*iff  (a+b*x^n)^p*(c+d*x^n)^q*is*integrable*wrt*x*in*terms*of*non-Appell  functions. =#IntBinomialQ((~a), (~b), (~c), (~d), (~n), (~p), (~q), ~x) := IntegersQ(p, q) || IGtQ(p, 0) || IGtQ(q, 0) || (EqQ(n, 2) || EqQ(n, 4)) && (IntegersQ(p, 4*q) || IntegersQ(4*p, q)) || EqQ(n, 2) && (IntegersQ(2*p, 2*q) || IntegersQ(3*p, q) && EqQ(b*c + 3*a*d, 0) || IntegersQ(p, 3*q) && EqQ(3*b*c + a*d, 0)) || EqQ(n, 3) && (IntegersQ(p + 1/3, q) || IntegersQ(q + 1/3, p)) || EqQ(n, 3) && (IntegersQ(p + 2/3, q) || IntegersQ(q + 2/3, p)) && EqQ(b*c + a*d, 0)
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~(n')) ^ ~(p') * (~c + ~(d') * (~x) ^ ~(mn')) ^ ~(q'), ~x) => Antiderivative(((~a + ~b * (~x) ^ ~n) ^ ~p * (~d + ~c * (~x) ^ ~n) ^ ~q) / (~x) ^ (~n * ~q), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p], ~x) && (EqQ(~mn, -(~n)) && (IntegerQ(~q) && (PosQ(~n) || Not(IntegerQ(~p)))))
    @apply_utils Antiderivative((~a + ~(b') * (~x) ^ ~(n')) ^ ~p * (~c + ~(d') * (~x) ^ ~(mn')) ^ ~q, ~x) => (((~x) ^ (~n * FracPart(~q)) * (~c + ~d * (~x) ^ -(~n)) ^ FracPart(~q)) / (~d + ~c * (~x) ^ ~n) ^ FracPart(~q)) * Antiderivative(((~a + ~b * (~x) ^ ~n) ^ ~p * (~d + ~c * (~x) ^ ~n) ^ ~q) / (~x) ^ (~n * ~q), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~n, ~p, ~q], ~x) && (EqQ(~mn, -(~n)) && (Not(IntegerQ(~q)) && Not(IntegerQ(~p))))
end

max_stars_repo_path: src/rules/1 Algebraic functions/1.1 Binomial products/1.1.3 General/1.1.3.3 (a+b x^n)^p (c+d x^n)^q.jl


text:
include("components.jl")

bus = Bus(0x0)

register_a = Register(0x0, bus)
register_b = Register(0x0, bus)
register_mar = Register(0x0, bus)
register_ir = Register(0x0, bus)
register_out = Register(0x0, bus)

alu = ALU(register_a, register_b, bus)
pc = ProgramCounter(0x00, bus)
ram = RAM(register_mar, bus)

AO = () -> output(register_a)
AI = () -> input(register_a)
BO = () -> output(register_b)
BI = () -> input(register_b)
ΣO = () -> output(alu)
MI = () -> input(register_mar)
RO = () -> output(ram)
RI = () -> input(ram)
IO = () -> output(register_ir)
II = () -> input(register_ir)
CO = () -> output(pc)
CI = () -> input(pc)
CE = () -> enable(pc)
OI = () -> input(register_out)

# Standard operations
so = [
    [CO, MI],
    [RO, II, CE]
]

instruction_map = Dict(
    "LDA"  => 0x0,
    "LDB"  => 0x1,
    "STA"  => 0x2,
    "STB"  => 0x3,
    "ADD"  => 0x4,
    "ADDR" => 0x5,
    "JMP"  => 0x6,
    "OUT"  => 0x7,
    "OUTR" => 0x8,
    "HLT"  => 0xf
)
instructions = [
    # 0x0 - LDA
    [
        [IO, MI],
        [RO, AI]
    ],
    # 0x1 - LDB
    [
        [IO, MI],
        [RO, BI]
    ],
    # 0x2 - STA
    [
        [IO, MI],
        [AO, RI]
    ],
    # 0x3 - STB
    [
        [IO, MI],
        [BO, RI]
    ],
    # 0x4 - ADD
    [
        [ΣO, AI]
    ],
    # 0x5 - ADDR
    [
        [IO, MI],
        [ΣO, RI]
    ],
    # 0x6 - JMP
    [
        [IO, CI]
    ],
    # 0x7 - OUT
    [
        [AO, OI]
    ],
    # 0x8 - OUTR
    [
        [IO, MI],
        [RO, OI]
    ],
    [],
    [],
    [],
    [],
    [],
    [],
    # 0xf - HLT
    [
    ]
]

function step()
    for s ∈ so
        for f ∈ s
            f()
        end
    end
    op = value(register_ir) >> 4
    for s ∈ instructions[op+1]
        for f ∈ s
            f()
        end
    end
end

function reset()
    set_value(pc, 0x0)
    set_value(register_out, 0x0)
end

function load(program::Vector{UInt8})
    if length(program) > 16
        @error "Cannot load program more than 16 bytes in length"
        return
    end
    reset()

    ram.contents = UInt8[program..., zeros(UInt8, 16 - length(program))...]
end

function compile_file(file::AbstractString)
    function map_instruction(str)
        str = uppercase(str)
        if str ∈ keys(instruction_map)
            return instruction_map[str]
        end
        throw(ErrorException("Instruction $(str) does not exist"))
    end

    open(file, "r") do io
        lines = readlines(file)
        prg = UInt8[]
        for line ∈ lines
            els = split(line)
            if length(els) == 2
                op = map_instruction(els[1])
                arg = parse(UInt8, els[2])
                push!(prg, op << 4 + arg)
            elseif length(els) == 1
                if startswith(els[1], "0x")
                    push!(prg, parse(UInt8, els[1]))
                else
                    op = map_instruction(els[1])
                    push!(prg, op << 4)
                end
            elseif length(els) == 0
                push!(prg, 0x0)
            else
                throw(ErrorException("Error parsing asm file: Instructions must have 0, 1 or 2 operands"))
            end
        end

        prg
    end
end

function load_file(file::AbstractString)
    load(compile_file(file))
end

function write_program(file::AbstractString, prg::Vector{UInt8})
    open(file, "w") do io
        for i ∈ prg
            write(io, i)
        end
    end
end

function read_program(file::AbstractString)
    open(file, "r") do io
        prg = UInt8[]
        while !eof(io)
            push!(prg, read(io, UInt8))
        end

        prg
    end
end

function hex(n)
    return "0x" * string(n; base=16)
end

function state()
    return """
        Register A: $(hex(value(register_a)))
        Register B: $(hex(value(register_b)))
        Register MAR: $(hex(value(register_mar)))
        Register IR: $(hex(value(register_ir)))
        Register OUT: $(hex(value(register_out)))

        Program Counter: $(hex(value(pc)))
    """
end

ext: jl


text:
import LinearAlgebra

"""Remove peaks near `mass` from spectrum `ms`.
Peaks within `tol` of `mass` are removed."""
function subtract(ms::MassSpectrum, mass, tol)
    ms[findall(peak -> abs(peak[1] - mass) > tol, ms)]
end

function subtract(m1::MassSpectrum, m2::MassSpectrum, tol)
    reduce(ms -> subtract(ms, m, tol), m2.mass, m1)
end

"""
Calculated the distance between two mass spectra.

The return value is between 0.0 and 1.0 and is calculated based
on the modelling each spectrum's peaks as a two-dimensional Gaussian in the
mass-log10(intensity) plane and subtracting the two. The square of the resulting
difference signal has a nice closed-form integral over the mass-intensity plane.
"""
function distance(ms1::MassSpectrum, ms2::MassSpectrum, σmass, σintensity)
    mass = [ms1.mass; ms2.mass]
    intensity = [log.(ms1.intensity); log.(ms2.intensity)]
    α = [ms1.intensity; -1 .* ms2.intensity]
    d = 0.0
    max_d = 0.0
    for i in eachindex(mass)
        for j in eachindex(mass)
            mass_contrib = -(mass[i] - mass[j])^2 / (4σmass^2)
            intensity_contrib = -(intensity[i] - intensity[j])^2 / (4σintensity^2)
            term = α[i] * α[j] * exp(mass_contrib + intensity_contrib)
            d += term
            if term > 0.0
                max_d += term
            end
        end
    end
    max(0.0, d) / max_d
end

function discretize(ms::MassSpectrum, rng)
    wv = weights(ms.intensity)
    fit(Histogram, ms.mass, wv, rng; closed=:left).weights
end

"""
Convert binned intensity data to a mass spectrum.
"""
function undiscretize(mass_bins::AbstractArray, intensities::AbstractArray, intensity_threshold = 0.05maximum(intensities))
    indices = intensities .> intensity_threshold
    MassSpectrum(mass_bins[indices], intensities[indices])
end

function LinearAlgebra.normalize!(ms::MassSpectrum, p::Real=1)
    LinearAlgebra.normalize!(ms.intensity, p)
    ms
end

LinearAlgebra.normalize(ms::MassSpectrum, p::Real=1) = LinearAlgebra.normalize!(deepcopy(ms), p)

Statistics.mean(ms::MassSpectrum) = Statistics.mean(ms.intensity)


text:
module BadelineFallingBlock

using ..Ahorn, Maple

const placements = Ahorn.PlacementDict(
    "Badeline Boss Falling Block" => Ahorn.EntityPlacement(
        Maple.BadelineFallingBlock,
        "rectangle",
    )
)

Ahorn.minimumSize(entity::Maple.BadelineFallingBlock) = 8, 8
Ahorn.resizable(entity::Maple.BadelineFallingBlock) = true, true

Ahorn.selection(entity::Maple.BadelineFallingBlock) = Ahorn.getEntityRectangle(entity)

function Ahorn.renderAbs(ctx::Ahorn.Cairo.CairoContext, entity::Maple.BadelineFallingBlock, room::Maple.Room)
    Ahorn.drawTileEntity(ctx, room, entity, material='g')
end

end


text:
# Don't care about the ID when comparing
@valueequals [id] @fieldproxy mutable struct Trigger{T}
    name::String
    data::Dict{String, Any}

    id::Integer

    Trigger{T}(name::String, data::Dict{String, Any}, id::Integer=nextEntityId()) where T = new{T}(name, data, id)
    Trigger{T}(name::String, id::Integer=nextEntityId(); kwargs...) where T = new{T}(name, Dict{String, Any}(String(k) => v for (k, v) in kwargs), id)

    Trigger(name::String, data::Dict{String, Any}, id::Integer=nextEntityId()) = new{Symbol(name)}(name, data, id)
    Trigger(name::String, id::Integer=nextEntityId(); kwargs...) = new{Symbol(name)}(name, Dict{String, Any}(String(k) => v for (k, v) in kwargs), id)
end

const defaultTriggerWidth = 16
const defaultTriggerHeight = 16

@mapdef Trigger "altMusicTrigger" AltMusicTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; track::String="", resetOnLeave::Bool=true)
@mapdef Trigger "ambienceParamTrigger" AmbienceParamTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; parameter::String="", from::Number=0.0, to::Number=0.0, direction::String="NoEffect")
@mapdef Trigger "bloomFadeTrigger" BloomFadeTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; bloomAddFrom::Number=0.0, bloomAddTo::Number=0.0, positionMode::String="NoEffect")
@mapdef Trigger "cameraOffsetTrigger" CameraOffsetTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; cameraX::Number=0.0, cameraY::Number=0.0)
@mapdef Trigger "checkpointBlockerTrigger" CheckpointBlockerTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight)
@mapdef Trigger "creditsTrigger" CreditsTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; event::String="")
@mapdef Trigger "eventTrigger" EventTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; event::String="")
@mapdef Trigger "goldenBerryCollectTrigger" GoldenBerryCollectionTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight)
@mapdef Trigger "lightFadeTrigger" LightFadeTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; lightAddFrom::Number=0.0, lightAddTo::Number=0.0, positionMode::String="NoEffect")
@mapdef Trigger "lookoutBlocker" LookoutBlocker(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight)
@mapdef Trigger "minitextboxTrigger" MiniTextBoxTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; dialog_id::String="", mode::String="OnPlayerEnter", only_once::Bool=true, death_count::Integer=-1)
@mapdef Trigger "musicFadeTrigger" MusicFadeTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; direction::String="leftToRight", fadeA::Number=0.0, fadeB::Number=1.0, parameter::String="")
@mapdef Trigger "musicTrigger" MusicTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; track::String="", resetOnLeave::Bool=true, progress::Int=0)
@mapdef Trigger "noRefillTrigger" NoRefillTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, state::Bool=true)
@mapdef Trigger "oshiroTrigger" OshiroTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, state::Bool=true)
@mapdef Trigger "stopBoostTrigger" StopBoostTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight)
@mapdef Trigger "windAttackTrigger" WindAttackTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight)
@mapdef Trigger "windTrigger" WindTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, pattern::String="None")
@mapdef Trigger "spawnFacingTrigger" SpawnFacingTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, facing::String="Right")

@mapdef Trigger "cameraTargetTrigger" CameraTargetTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; lerpStrength::Number=1.0, positionMode::String="NoEffect", xOnly::Bool=false, yOnly::Bool=false, deleteFlag::String="", nodes::Array{Tuple{Integer, Integer}, 1}=Tuple{Integer, Integer}[])
@mapdef Trigger "cameraAdvanceTargetTrigger" CameraAdvanceTargetTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; lerpStrengthX::Number=1.0, lerpStrengthY::Number=1.0, positionModeX::String="NoEffect", positionModeY::String="NoEffect", xOnly::Bool=false, yOnly::Bool=false, nodes::Array{Tuple{Integer, Integer}, 1}=Tuple{Integer, Integer}[])
@mapdef Trigger "changeRespawnTrigger" ChangeRespawnTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; nodes::Array{Tuple{Integer, Integer}, 1}=Tuple{Integer, Integer}[])
@mapdef Trigger "respawnTargetTrigger" RespawnTargetTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; nodes::Array{Tuple{Integer, Integer}, 1}=Tuple{Integer, Integer}[])

@mapdef Trigger "birdPathTrigger" BirdPathTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight)
@mapdef Trigger "blackholeStrength" BlackHoleStrengthTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; strength::String="Mild")
@mapdef Trigger "detachFollowersTrigger" DetachFollowersTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; @kwesc(:global)::Bool=true)
@mapdef Trigger "moonGlitchBackgroundTrigger" MoonGlitchBackgroundTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; duration::String="Short", stay::Bool=false, glitch::Bool=true)
@mapdef Trigger "rumbleTrigger" RumbleTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight; manualTrigger::Bool=false, persistent::Bool=false, constrainHeight::Bool=false)

@mapdef Trigger "everest/customBirdTutorialTrigger" EverestCustomBirdTutorialTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, birdId::String="", showTutorial::Bool=true)
@mapdef Trigger "everest/flagTrigger" EverestFlagTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, flag::String="", state::Bool=false, mode::String="OnPlayerEnter", only_once::Bool=false, death_count::Integer=-1)
@mapdef Trigger "everest/dialogTrigger" EverestDialogTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, endLevel::Bool=false, onlyOnce::Bool=true, dialogId::String="", deathCount::Int=-1)
@mapdef Trigger "everest/crystalShatterTrigger" EverestCrystalShatterTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, mode::String="All")
@mapdef Trigger "everest/completeAreaTrigger" EverestCompleteAreaTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight)
@mapdef Trigger "everest/changeInventoryTrigger" EverestChangeInventoryTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, inventory::String="Default")
@mapdef Trigger "everest/lavaBlockerTrigger" EverestLavaBlockerTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, canReenter::Bool=false)
@mapdef Trigger "everest/coreModeTrigger" EverestCoreModeTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, mode::String="None")
@mapdef Trigger "everest/musicLayerTrigger" EverestMusicLayerTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, layers::String="", enable::Bool=false)
@mapdef Trigger "everest/smoothCameraOffsetTrigger" EverestSmoothCameraOffsetTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, offsetXFrom::Number=0.0, offsetXTo::Number=0.0, offsetYFrom::Number=0.0, offsetYTo::Number=0.0, positionMode::String="NoEffect", onlyOnce::Bool=false, xOnly::Bool=false, yOnly::Bool=false)
@mapdef Trigger "everest/activateDreamBlocksTrigger" ActivateDreamBlocksTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, fullRoutine::Bool=false, activate::Bool=true, fastAnimation::Bool=false)
@mapdef Trigger "everest/CustomHeightDisplayTrigger" CustomHeightDisplayTrigger(x::Integer, y::Integer, width::Integer=defaultTriggerWidth, height::Integer=defaultTriggerHeight, vanilla::Bool=false, target::Int=0, from::Int=0, text::String="{x}m", progressAudio::Bool=false, displayOnTransition::Bool=false)
ActivateSpaceJamsTrigger = ActivateDreamBlocksTrigger

const blacklistedTriggerAttrs = String["nodes"]

function Base.Dict(t::Trigger)
    res = Dict{String, Any}()

    res["__name"] = t.name
    res["id"] = t.id

    for (key, value) in t.data
        if !(key in blacklistedTriggerAttrs)
            res[key] = value
        end
    end

    if haskey(t.data, "nodes")
        if length(t.data["nodes"]) > 0
            res["__children"] = Dict{String, Any}[]

            for node in t.data["nodes"]
                push!(res["__children"], Dict{String, Any}(
                    "__name" => "node",
                    "x" => node[1],
                    "y" => node[2]
                ))
            end
        end
    end

    return res
end

max_stars_repo_name: CelestialCartographers/Maple

max_issues_repo_name: CelestialCartographers/Maple

max_forks_repo_name: CelestialCartographers/Maple


text:
function plot_wilson_loop_winding(solver::Peacock.FDFD.Solver, ks, polarisation, bands::AbstractVector{<:Int};
    dk_outer=nothing, dk_inner=nothing, delta_brillouin_zone=BrillouinZoneCoordinate(0,1),
    labels=[], markersize=nothing)

    if labels == []
        labels = [hasproperty(x,:label) ? x.label : "" for x in ks]
    end
    ks = [typeof(x)==BrillouinZoneCoordinate ? get_k(x,solver.basis) : x for x in ks]
    ks_outer, _ = sample_path(ks, dk=dk_outer)
    function get_data(ks_outer) #not efficient
        spaces = []
        for k in ks_outer
            spaces_inner = HilbertSpace_FDFD[]
            delta_k = get_k(delta_brillouin_zone, solver.basis)
            ks_inner = [k, k+delta_k]
            ks_inner, _ = sample_path(ks_inner, dk=dk_inner)
            for k in ks_inner
                modes = Peacock.FDFD.solve(solver, k, polarisation)
                space = HilbertSpace_FDFD(modes[bands])
                push!(spaces_inner, space)
            end
            spaces_inner = [spaces_inner; spaces_inner[1]]
            push!(spaces, spaces_inner)
        end
        return spaces
    end
    plot_band_diagram(my_solve, ks, dk=dk_outer, labels=labels, show_vlines=false, markersize=markersize)
    ylim(-pi,pi)
    yticks((-1:1)*pi, labels=["-π","0","+π"])
    ylabel("Wilson spectrum")
end

function get_data2(kx, ky, weighting; bands=1:2)
    data = zeros(ComplexF64, length(kx),length(ky)+1, length(bands), length(weighting))
    for i in 1:length(kx), j in 1:length(ky), n in 1:length(bands)
        modes = Peacock.FDFD.solve(solver,[kx[i],ky[j]],TM,bands=bands)
        data0 = get_field_FDFD(modes[n].data, modes[n].basis,k0=modes[n].k0)
        data0 = data0[:] / sqrt(abs(dot(data0[:], weighting.*data0[:])))
        data[i,j,n,:] = data0[:]
        if j == length(ky)
            data[i,end,n,:] = data[i,1,n,:]
        end
    end
    return data
end

@everywhere using Peacock
function get_data(kx, ky, weighting; bands=1:2)
    data = zeros(ComplexF64, length(kx),length(ky)+1, length(bands), length(weighting))
    @sync begin
        @distributed for i in 1:length(kx)
            for j in 1:length(ky)
                for n in 1:length(bands)
                    modes = Peacock.FDFD.solve(solver,[kx[i],ky[j]],TM,bands=bands)
                    data0 = get_field_FDFD(modes[n].data, modes[n].basis,k0=modes[n].k0)
                    data0 = data0[:] / sqrt(abs(dot(data0[:], weighting.*data0[:])))
                    data[i,j,n,:] = data0[:]
                    if j == length(ky)
                        data[i,end,n,:] = data[i,1,n,:]
                    end
                end
            end
        end
    end
    return data
end

function get_data(kx, ky, weighting; bands=1:2)
    data = zeros(ComplexF64, length(kx),length(ky)+1, length(bands), length(weighting))
    inds = CartesianIndices((1:length(kx),1:length(ky),1:length(bands)))
    @sync begin
        @distributed for ind in inds
            i, j, n = ind.I
            modes = Peacock.FDFD.solve(solver,[kx[i],ky[j]],TM,bands=bands)
            data0 = get_field_FDFD(modes[n].data, modes[n].basis,k0=modes[n].k0)
            data0 = data0[:] / sqrt(abs(dot(data0[:], weighting.*data0[:])))
            data[i,j,n,:] = data0[:]
            if j == length(ky)
                data[i,end,n,:] = data[i,1,n,:]

            end
        end
    end
    return data
end


text:
"""
    DFTU(;l ::Int = -1,
          U ::T   = zero(T),
          J0::T  = zero(T),
          α ::T   = zero(T),
          β ::T   = zero(T),
          J ::Vector{T} = T[zero(T)])

DFT+U parameters for a given [`Atom`](@ref).
"""
@with_kw mutable struct DFTU
    l::Int      = -1
    U::Float64  = 0.0
    J0::Float64 = 0.0
    #QE params
    α::Float64 = 0.0
    β::Float64 = 0.0
    J::Vector{Float64} = [0.0]
end

function Base.:(==)(x::DFTU, y::DFTU)
    fnames = fieldnames(DFTU)
    for fn in fnames
        if getfield(x, fn) != getfield(y, fn)
            return false
        end
    end
    return true
end

StructTypes.StructType(::Type{DFTU}) = StructTypes.Mutable()

"""
    Element(symbol::Symbol, Z::Int, name::String, atomic_weight::Float64, color::NTuple{3, Float64})

Represents an element. Most conveniently used trough the function [`element`](@ref).
"""
struct Element
    symbol        :: Symbol
    Z             :: Int64
    name          :: String
    atomic_weight :: Float64
    color         :: NTuple{3,Float64}
end
Element() = Element(:nothing, -1, "", -1, (0.0, 0.0, 0.0))
StructTypes.StructType(::Type{Element}) = StructTypes.Struct()

"""
Reads all the elements from the file.
"""
const ELEMENTS = Element[]

open(joinpath(@__DIR__, "..", "..", "assets", "elements.txt"), "r") do f
    while !eof(f)
        line = split(readline(f))
        push!(ELEMENTS,
              Element(Symbol(line[4]), Meta.parse(line[1]), line[9], Meta.parse(line[10]),
                      (Meta.parse.(line[5:7],)...,) ./ 65535))
    end
end
#undefined element
push!(ELEMENTS, Element(:undef, 0, "undef", 0, (0.0, 0.0, 0.0)))

"""
    element(sym::Symbol)

Returns the predefined [`Element`](@ref) with symbol `sym`,
i.e. `element(:Si)` will return the pregenerated Silicon [`Element`](@ref).
"""
function element(sym::Symbol)
    # This we do to clean up atom names such as :Fe1
    if tryparse(Int, String(sym)[end:end]) !== nothing
        sym = Symbol(String(sym)[1:end-1])
    end
    found = filter(x -> x.symbol == sym, ELEMENTS)
    if isempty(found)
        return ELEMENTS[end]
    end
    return found[1]
end

element(z::Int) = getfirst(x -> x.Z == z, ELEMENTS)

# TODO Multiple l per atom in Elk??
#We use angstrom everywhere
"""
    Atom(name::Symbol, element::Element, position_cart::Point3{Length}, position_cryst::Point3;
         pseudo::String = "",
         projections::Vector{Projection} = Projection[],
         magnetization::Vec3 = Vec3(0.0, 0.0, 0.0),
         dftu::DFTU = DFTU())

Representation of an `atom`.

The `name` of the `atom` is used as an identifier for the `atom` type, in the sense that atoms with the same `pseudo`, `projections`, `magnetization` and [`dftu`](@ref DFTU) attributes should belong to the same type. This also means that during sanity checks atoms that are not of the same type will be given different names. This is done in this way because it often makes sense to change these parameters on all atoms of the same kind at the same time, but still allow the flexibility to change them for individual atoms as well.

`position_cart` should have a valid `Unitful.Length` type such as `Ang`.

See documentation for [`Element`](@ref) for further information on this attribute.
"""
@with_kw_noshow mutable struct Atom
    name::Symbol
    position_cart::Point3{typeof(0.0Ang)}
    position_cryst::Point3{Float64}
    element::Element = element(name)
    pseudo::String = ""
    projections::Vector{Projection} = Projection[]
    magnetization::Vec3{Float64} = zero(Vec3{Float64})
    dftu::DFTU = DFTU()
end
StructTypes.StructType(::Type{Atom}) = StructTypes.Struct()

"Takes a Vector of atoms and returns a Vector with the atoms having unique symbols."
function Base.unique(atoms::Vector{Atom})
    uni = Atom[]
    for at in atoms
        if findfirst(x -> isequal_species(x, at), uni) === nothing
            push!(uni, at)
        end
    end
    return uni
end

function isequal_species(at1::Atom, at2::Atom)
    for f in fieldnames(typeof(at1))
        if f in (:position_cart, :position_cryst, :projections)
            continue
        end
        if getfield(at1, f) != getfield(at2, f)
            return false
        end
    end
    return true
end

function Base.range(at::Atom)
    projs = at.projections
    @assert length(projs) != 0 "At $(at.name) has no defined projections. Please use `setprojections!` first."
    return projs[1].start:projs[end].last
end

Base.range(v::Vector{Atom}) = vcat(range.(v)...)

function projections_string(at::Atom)
    return "$(at.name): " * join(map(x -> x.orbital.name, at.projections), ";")
end

function Base.:(==)(at1::Atom, at2::Atom)
    return at1.name == at2.name && norm(at1.position_cart - at2.position_cart) < 1e-6Ang
end

"""
	set_position!(at::Atom, pos::AbstractVector{T}, unit_cell::Mat3) where {T<:Real}

Updates the position of the atom to this. The unit cell is used to make sure both `position_cryst` and `position_cart` are correct.
"""
function set_position!(at::Atom, pos::AbstractVector{T}, unit_cell::Mat3) where {T<:Real}
    at.position_cryst = Point3{T}(pos...)
    at.position_cart  = unit_cell * at.position_cryst
    return at
end

function set_position!(at::Atom, pos::AbstractVector{T}, unit_cell::Mat3) where {T<:Length}
    at.position_cart  = Point3{T}(pos...)
    at.position_cryst = unit_cell^-1 * at.position_cart
    return at
end

"""
	scale_bondlength!(at1::Atom, at2::Atom, scale::Real, cell::Mat3)

Scales the bondlength between two atoms. The center of mass remains the same.
"""
function scale_bondlength!(at1::Atom, at2::Atom, scale::Real, cell::Mat3)
    p1 = at.position_cryst
    p2 = at.position_cryst
    mid = (p1 + p2) / 2
    orig_dist = norm(p1 - p2)
    direction = (p1 - mid) / norm(p1 - mid)
    new_dist = orig_dist * scale
    new_p1 = mid + new_dist * direction / 2
    new_p2 = mid - new_dist * direction / 2
    set_position!(at1, new_p1, cell)
    return set_position!(at2, new_p2, cell)
end


text:
module Utils

using ..AbsTypes: Machine
using Statistics
using DataFrames
using CSV
using PooledArrays

import MLBase: Kfold

using Random: randperm

export holdout, kfold, score, infer_eltype, nested_dict_to_tuples,
       nested_dict_set!, nested_dict_merge, createmachine,
       mergedict, getiris, getprofb,
       skipmean,skipmedian,skipstd,
       aggregatorclskipmissing,
       find_catnum_columns,
       train_test_split

"""
    find_catnum_columns(instances::DataFrame,maxuniqcat::Int=0)

Finds all categorial and numerical columns.
Categorical columns are those that do not have Real type nor
do all their elements correspond to Real. Also, columns
with size of unique instances are less than `maxuniqcat` are
considered categorical.
"""
function find_catnum_columns(finstances::DataFrame, maxuniqcat::Int=0)
   # assume complete cases and remove fields with Union{Missing,Type}
   instances = identity.(finstances)
   nominal_columns = Int[]
   real_columns = Int[]
   for column in 1:size(instances, 2)
      vdat = instances[:, column:column] # returns a 1-column dataframe
      col_eltype = infer_eltype(vdat)
      # nominal if column type is not real or only small number of unique instances
      # otherwise, real
      if  !<:(col_eltype, Real)
         push!(nominal_columns, column)
      elseif nrow(unique(vdat)) <= maxuniqcat
         push!(nominal_columns, column)
      else
         push!(real_columns, column)
      end
   end
   return (nominal_columns,real_columns)
end

"""
    holdout(n, right_prop)

Holdout method that partitions a collection
into two partitions.

- `n`: Size of collection to partition
- `right_prop`: Percentage of collection placed in right partition

Returns: two partitions of indices, left and right
"""
function holdout(n, right_prop)
   shuffled_indices = randperm(n)
   partition_pivot = round(Int,right_prop * n)
   right = shuffled_indices[1:partition_pivot]
   left = shuffled_indices[partition_pivot+1:end]
   return (left, right)
end

"""
    kfold(num_instances, num_partitions)

Returns k-fold partitions.

- `num_instances`: total number of instances
- `num_partitions`: number of partitions required

Returns: training set partition.
"""
function kfold(num_instances, num_partitions)
   return collect(Kfold(num_instances, num_partitions))
end

"""
    score(metric::Symbol, actual::Vector, predicted::Vector)

Score learner predictions against ground truth values.

Available metrics:
- :accuracy

- `metric`: metric to assess with
- `actual`: ground truth values
- `predicted`: predicted values

Returns: score of learner
"""
function score(metric::Symbol, actual::Vector, predicted::Vector)
   if metric == :accuracy
      return mean(actual .== predicted) * 100.0
   elseif metric == :mae
      return mean(abs2.(actual .- predicted))
   elseif metric == :mse
      return mean((actual .- predicted).^2)
   elseif metric == :rmse
      return sqrt(mean((actual .- predicted).^2))
   else
      println("available metric: :mae, :mse, :accuracy, :rmse")
      throw(ArgumentError("Metric $metric not implemented for score."))
   end
end

"""
    infer_eltype(vector::Vector)

Returns element type of vector unless it is Any.
If Any, returns the most specific type that can be
inferred from the vector elements.

- `vector`: vector to infer element type on

Returns: inferred element type
"""
function infer_eltype(vector::Vector)
   # Obtain element type of vector
   vec_eltype = eltype(vector)

   # If element type of Vector is Any and not empty,
   # and all elements are of the same type,
   # then return the vector elements' type.
   if vec_eltype == Any && !isempty(vector)
      all_elements_same_type = all(x -> typeof(x) == typeof(first(vector)), vector)
      if all_elements_same_type
         vec_eltype = typeof(first(vector))
      end
   end

   # Return inferred element type
   return vec_eltype
end

function infer_eltype(df::DataFrame)
   infer_eltype(Matrix(df))
end

function infer_eltype(pd::PooledArray)
   infer_eltype(collect(pd))
end

function infer_eltype(mtrx::Matrix)
   # Obtain element type of matrix
   mat_eltype = eltype(mtrx)

   # If element type of Matrix is Any and not empty,
   # and all elements are of the same type,
   # then return the matrix elements' type.
   if mat_eltype == Any && !isempty(mtrx)
      all_elements_same_type = all(x -> typeof(x) == typeof(first(mtrx)), mtrx)
      if all_elements_same_type
         mat_eltype = typeof(first(mtrx))
      end
   end

   # Return inferred element type
   return mat_eltype
end

"""
    nested_dict_to_tuples(dict::Dict)

Converts nested dictionary to list of tuples

- `dict`: dictionary that can have other dictionaries as values

Returns: list where elements are ([outer-key, inner-key, ...], value)
"""
function nested_dict_to_tuples(dict::Dict)
   set = Set()
   for (entry_id, entry_val) in dict
      if typeof(entry_val) <: Dict
         inner_set = nested_dict_to_tuples(entry_val)
         for (inner_entry_id, inner_entry_val) in inner_set
            new_entry = (vcat([entry_id], inner_entry_id), inner_entry_val)
            push!(set, new_entry)
         end
      else
         new_entry = ([entry_id], entry_val)
         push!(set, new_entry)
      end
   end
   return set
end

"""
    nested_dict_set!(dict::Dict, keys::Array{T, 1}, value) where {T}

Set value in a nested dictionary.

- `dict`: nested dictionary to assign value
- `keys`: keys to access nested dictionaries in sequence
- `value`: value to assign
"""
function nested_dict_set!(dict::Dict, keys::Array{T, 1}, value) where {T}
   inner_dict = dict
   for key in keys[1:end-1]
      inner_dict = inner_dict[key]
   end
   inner_dict[keys[end]] = value
end

"""
    nested_dict_merge(first::Dict, second::Dict)

Second nested dictionary is merged into first.

If a second dictionary's value as well as the first
are both dictionaries, then a merge is conducted between
the two inner dictionaries.
Otherwise the second's value overrides the first.

- `first`: first nested dictionary
- `second`: second nested dictionary

Returns: merged nested dictionary
"""
function nested_dict_merge(first::Dict, second::Dict)
   target = copy(first)
   for (second_key, second_value) in second
      values_both_dict =
      typeof(second_value) <: Dict &&
      typeof(get(target, second_key, nothing)) <: Dict
      if values_both_dict
         target[second_key] = nested_dict_merge(target[second_key], second_value)
      else
         target[second_key] = second_value
      end
   end
   return target
end

mergedict = nested_dict_merge

"""
    createmachine(prototype::Machine, options=nothing)

Create machine

- `prototype`: prototype machine to base new machine on
- `args`: additional options to override prototype's options

Returns: new machine
"""
function createmachine(prototype::Machine, args::Dict=Dict())
   new_args = copy(prototype.model)
   if args != Dict()
      new_args = nested_dict_merge(new_args, args)
   end

   prototype_type = typeof(prototype)
   return prototype_type(new_args)
end

"""
    aggregatorclskipmissing(fn::Function)

Function to create aggregator closure with skipmissing features
"""
function aggregatorclskipmissing(fn::Function)
   function skipagg(x::Union{AbstractArray,DataFrame})
      if length(collect(skipmissing(x))) == 0
         return missing
      else
         return fn(skipmissing(x))
      end
   end
   return skipagg
end

function skipmean(x::T) where {T<:Union{AbstractArray,DataFrame}}
   if length(collect(skipmissing(x))) == 0
      missing
   else
      mean(skipmissing(x))
   end
end

function skipmedian(x::T) where {T<:Union{AbstractArray,DataFrame}}
   if length(collect(skipmissing(x))) == 0
      missing
   else
      median(skipmissing(x))
   end
end

function skipstd(x::T) where {T<:Union{AbstractArray,DataFrame}}
   if length(collect(skipmissing(x))) == 0
      missing
   else
      std(skipmissing(x))
   end
end

function getiris()
   iris = CSV.read(joinpath(Base.@__DIR__,"../data","iris.csv"),DataFrame)
   return iris
end

function getprofb()
   profb = CSV.read(joinpath(Base.@__DIR__,"../data","profb.csv"),DataFrame)
   return profb
end

function train_test_split(xdf::DataFrame,yvec::Vector; testprop=0.30, shuffle=true)
   @assert nrow(xdf) == length(yvec)
   sz = length(yvec)
   ndx = collect(1:sz)
   if shuffle == true
      ndx = randperm(sz)
   end
   pivot = round(Int,testprop * sz)
   right = ndx[1:pivot]
   left = ndx[pivot+1:end]
   trX = xdf[left,:]
   trY = yvec[left]
   tstX = xdf[right,:]
   tstY = yvec[right]
   return(;trX,trY,tstX,tstY)
end

#function getiris()
#  iris = Arrow.Table(joinpath(Base.@__DIR__,"../data","iris.arrow"))
#  return iris
#end
#
#function getprofb()
#  profb = Arrow.Table(joinpath(Base.@__DIR__,"../data","profb.arrow"))
#  return profb
#end

end


text:
#src Generate README.md by running `docs/make.jl`

# <img align="right" width="180px" src="https://avatars.githubusercontent.com/u/58918144?s=200&v=4">

# <!-- DO NOT EDIT README.md directly, instead edit docs/README.jl and generate the markdown-->

# # Logic<wbr>Circuits<wbr>.jl

# [![Unit Tests](https://github.com/Juice-jl/LogicCircuits.jl/workflows/Unit%20Tests/badge.svg)](https://github.com/Juice-jl/LogicCircuits.jl/actions?query=workflow%3A%22Unit+Tests%22+branch%3Amaster) [![codecov](https://codecov.io/gh/Juice-jl/LogicCircuits.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/Juice-jl/LogicCircuits.jl) [![](https://img.shields.io/badge/docs-stable-green.svg)](https://juice-jl.github.io/LogicCircuits.jl/stable) [![](https://img.shields.io/badge/docs-dev-blue.svg)](https://juice-jl.github.io/LogicCircuits.jl/dev)

# This package provides basic functionality for doing logical reasoning using logical circuits. It has the stand-alone functionality illustrated below, and it serves as the logical foundations for other [Juice packages](https://github.com/Juice-jl) (Julia Circuit Empanada).

include("usage.jl")

# Please see [![](https://img.shields.io/badge/docs-stable-green.svg)](https://juice-jl.github.io/LogicCircuits.jl/stable) or [![](https://img.shields.io/badge/docs-dev-blue.svg)](https://juice-jl.github.io/LogicCircuits.jl/dev) for further details.

# ## Development

# If you are interested in modifying the package please see the [development readme](https://juice-jl.github.io/LogicCircuits.jl/dev/development/).

# ## Acknowledgements

# To acknowledge this package, please cite:
# ```
# @inproceedings{DangAAAI21,
#     title   = {Juice: A Julia Package for Logic and Probabilistic Circuits},
#     author = {Dang, Meihua and Khosravi, Pasha and Liang, Yitao and Vergari, Antonio and Van den Broeck, Guy},
#     booktitle = {Proceedings of the 35th AAAI Conference on Artificial Intelligence (Demo Track)},
#     year    = {2021}
# }
# ```

max_issues_repo_name: Juice-jl/LogicCircuits

max_forks_repo_name: Juice-jl/LogicCircuits


text:
# Runs a simple Metropolis-Hastings (ie MCMC) algoritm to simulate two
# jointly distributed random variuables with probability density
# p(x,y)=exp(-(x^2+y^2)/s^2)/consNorm, where s>0 and consNorm is a
# normalization constant.
#
# Author: H. Paul Keeler, 2019.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts

#Note: Need the .+ for adding a scalar to an array
#Also need . for sqrt, exp, cos, sin etc and assinging scalars to arrays

using Distributions #for random simulations
using PyPlot #for plotting
using StatsBase #for histograms etc
using Random
using LinearAlgebra
using HCubature #for numerical integration
#using LaTeXStrings #for LateX in labels/titles etc
PyPlot.close("all");  # close all PyPlot figures

#set random seed for reproducibility
#Random.seed!(1234)

# Simulation window parameters
xMin = -1;
xMax = 1;
yMin = -1;
yMax = 1;

numbSim = 10 ^ 5;  # number of random variables simulated
numbSteps = 25;  # number of steps for the Markov process
numbBins = 50;  # number of bins for histogram
sigma = 2;  # standard deviation for normal random steps

# probability density parameters
s = .5;  # scale parameter for distribution to be simulated

function fun_lambda(x,y)
    return (exp.(-(x.^2+y.^2)./s^2));
end

#normalization constant -- UNDER CONSTRUCTION
consNorm,errorCub=hcubature(x -> fun_lambda(x[1],x[2]), [xMin, yMin], [xMax, yMax]);

function fun_p(x,y)
    return((fun_lambda(x,y)./consNorm).*(x.>=xMin).*(y.>=yMin).*(x.<=xMax).*(y.<=yMax));
end
xRand=(xMax-xMin).*rand(numbSim).+xMin; #random initial values
yRand=(yMax-yMin).*rand(numbSim).+yMin; #random initial values

probCurrent=fun_p(xRand,yRand); #current transition probabilities
for jj=1:numbSteps
    zxRand= xRand.+sigma.*rand(Normal(),numbSim);#take a (normally distributed) random step
    zyRand= yRand.+sigma.*rand(Normal(),numbSim);#take a (normally distributed) random step

    # Conditional random step needs to be symmetric in x and y
    # For example: Z|x ~ N(x,1) (or Y=x+N(0,1)) with probability density
    # p(z|x)=e(-(z-x)^2/2)/sqrt(2*pi)
    probProposal = fun_p(zxRand, zyRand);  # proposed probability

    # acceptance rejection step
    booleAccept=rand(numbSim) .< probProposal./probCurrent;
    # update state of random walk/Marjov chain
    xRand[booleAccept] = zxRand[booleAccept];
    yRand[booleAccept] = zyRand[booleAccept];
    # update transition probabilities
    probCurrent[booleAccept] = probProposal[booleAccept];
end

#histogram section: empirical probability density
histXY=fit(Histogram, (xRand,yRand),nbins=numbBins); #find histogram data
histXY=normalize(histXY,mode=:pdf); #normalize histogram
binEdges=histXY.edges; #retrieve bin edges
xValues=(binEdges[1][2:end]+binEdges[1][1:end-1])./2; #mid-points of bins
yValues=(binEdges[2][2:end]+binEdges[2][1:end-1])./2; #mid-points of bins
p_Estimate=(histXY.weights)
#create a meshgrid
X=[xValues[ii] for ii=1:length(xValues), jj=1:length(yValues)];
Y=[yValues[jj] for ii=1:length(xValues), jj=1:length(yValues)];

#analytic solution of probability density
p_Exact = fun_p(X, Y);

# Plotting
# Plot empirical estimate
fig1 = PyPlot.figure();
PyPlot.rc("text", usetex=true);
PyPlot.rc("font", family="serif");
surf(X, Y, p_Estimate, cmap=PyPlot.cm.plasma);
PyPlot.xlabel("x");
PyPlot.ylabel("y");
PyPlot.title("p(x,y) Estimate");

# Plot exact expression
fig2 = PyPlot.figure();
PyPlot.rc("text", usetex=true);
PyPlot.rc("font", family="serif")
surf(X, Y, p_Exact, cmap=PyPlot.cm.plasma);
PyPlot.xlabel("x");
PyPlot.ylabel("y");
PyPlot.title("p(x,y) Exact Expression");

#println("The program has completed.")

max_stars_repo_name: hpkeeler/posts

max_issues_repo_name: hpkeeler/posts

max_forks_repo_name: hpkeeler/posts


text:
using Documenter, MyPkg1

makedocs(;
    modules=[MyPkg1],
    format=Documenter.HTML(),
    pages=[
        "Home" => "index.md",
    ],
    repo="https://github.com/XiaodongMa-MRI/MyPkg1.jl/blob/{commit}{path}#L{line}",
    sitename="MyPkg1.jl",
    authors="Xiaodong Ma",
    assets=String[],
)

deploydocs(;
    repo="github.com/XiaodongMa-MRI/MyPkg1.jl",
)


text:
# Allow selection of different modules - TO DO INTEGRATE EACH MODEL INTO HERE

include("ImportModules.jl")

help = """

--- CIPHER TOOLS > ALL FOR HUMANS ---

Help - ?
Quit - Q
Install Requirements - R

ANALYSIS
| -> Cipher Identifier - [IN PROGRESS]
| -> Entropy - EN
| -> Frequency Analysis - FA

BRUTE FORCE
| -> Caesar Brute - CA
| -> Affine Brute - AF
| -> Atbash - AB
| -> ROT13 - RO
| -> Rail-fence - [COMING SOON]
| -> Baconian - [COMING SOON]
| -> Polybius Square - [COMING SOON]
| -> Simple Substitution - [COMING SOON]
| -> Transposition - [COMING SOON]

"""

print(help)

while true

    print(">> ")
    text = uppercase(readline())

    if text == "EN"

        println("\n--- CIPHER TOOLS > ENTROPY HUMANS ---")
        include("Entropyhumans.jl")

    elseif text == "FA"

        println("\n--- CIPHER TOOLS > FREQUENCY ANALYSIS HUMANS ---")
        include("FAhumans.jl")

    elseif text == "CA"

        println("\n--- CIPHER TOOLS > CAESAR HUMANS ---")
        include("Caesarhumans.jl")

    elseif text == "AF"

        println("\n--- CIPHER TOOLS > AFFINE HUMANS ---")
        include("Affinehumans.jl")

    elseif text == "AB"

        println("\n--- CIPHER TOOLS > ATBASH HUMANS ---")
        include("Atbashhumans.jl")

    elseif text == "RO"

        println("\n--- CIPHER TOOLS > ROT13 HUMMANS ---")
        include("Rot13humans.jl")

    elseif text == "?"

        print(help)

    elseif text == "R"

        include("Requirements.jl")

    elseif text == "Q"

        break

    else

        println("Not a recognised code. Type '?' for help.")

    end

end

max_stars_repo_name: Squalm/Cipher-Tools

max_issues_repo_name: Squalm/Cipher-Tools

max_forks_repo_name: Squalm/Cipher-Tools


text:
function elapsed_time_criterion(duration::Number, channel::RemoteChannel)
    start = time()
    while true
        try
            put!(channel, (time() - start) >= duration)
        catch
            break
        end
    end
end

function iterations_criterion(duration::Number, channel::RemoteChannel)
    start = 0
    while true
        try
            put!(channel, start >= duration)
            start += 1
        catch
            break
        end
    end
end


text:
module RangeHelpers

using LinearAlgebra: norm

export strictbelow, below, around, above, strictabove
export prolong
export anchorrange
export symrange
export asrange
export binwalls, bincenters
export subdivide
export searchsortedat
export samegrid

# Use README as docstring
@doc let path = joinpath(dirname(@__DIR__), "README.md")
    include_dependency(path)
    replace(read(path, String), "```julia" => "```jldoctest README")
    #read(path, String)
end RangeHelpers

################################################################################
##### range
################################################################################
@enum Direction strictbelow below around above strictabove

(direction::Direction)(value) = Approach(value, direction)
function direction_string(direction::Direction)
    if direction === strictbelow
        "strictbelow"
    elseif direction === below
        "below"
    elseif direction === around
        "around"
    elseif direction === above
        "above"
    elseif direction === strictabove
        "strictabove"
    else
        msg = """
        Unreachable direction = $direction
        """
        error(msg)
    end
end

struct Approach{V}
    value::V
    direction::Direction
end

function Base.show(io::IO, o::Approach)
    direction = o.direction
    print(io, direction_string(direction), "(", o.value, ")")
end

function opposite(direction::Direction)::Direction
    if direction === strictbelow
        strictabove
    elseif direction === below
        above
    elseif direction === around
        around
    elseif direction === above
        below
    elseif direction === strictabove
        strictbelow
    else
        msg = """
        Unreachable direction = $direction
        """
        error(msg)
    end
end

function int(value, direction)::Int
    if direction === strictbelow
        candidate = floor(Int, value)
        candidate < value ? candidate : candidate - 1
    elseif direction === below
        floor(Int, value)
    elseif direction === around
        round(Int, value)
    elseif direction === above
        ceil(Int, value)
    elseif direction === strictabove
        candidate = ceil(Int, value)
        candidate > value ? candidate : candidate + 1
    else
        msg = """
        Unreachable direction = $direction
        """
        error(msg)
    end
end

"""
    range(start, stop; length, step)
    range(start; stop, length, step)
    range(;start, stop, length, step)

Construct a range from the arguments. Three arguments must be given.

```jldoctest range
julia> using RangeHelpers

julia> using RangeHelpers: range

julia> range(start=1, stop=3, length=3)
1.0:1.0:3.0

julia> range(start=1, stop=3.1, step=around(1))
1.0:1.05:3.1

julia> range(start=1, stop=around(3.1), step=1)
1:1:3

julia> range(start=1, stop=above(3.1), step=1)
1:1:4

julia> range(start=1, stop=above(3.0), step=1)
1:1:3

julia> range(start=around(0.9), stop=3.0, step=1)
1.0:1.0:3.0

julia> range(start=strictbelow(1.0), stop=3.0, step=1)
0.0:1.0:3.0
```
See also the docs of `Base.range`.
"""
function range end

function range(;start=nothing, stop=nothing, length=nothing, step=nothing)
    range0(start, step, stop, length)
end

function range(start; stop=nothing, length=nothing, step=nothing)
    range1(start, step, stop, length)
end

function range(start, stop; length=nothing, step=nothing)
    range1(start, step, stop, length)
end

function range0(start::Nothing, step, stop, length)
    start = stop - (length-1) * step
    range(start, stop=stop, length=length::Integer)
end

function range0(start, step, stop, length)
    range(start, stop=stop, step=step, length=length)
end
module HandleUnitStep
    # Low level range methods, that allow remembering whether a range is a unit range
    struct One end

    # extensions
    first(x) = Base.first(x)
    first(x::Base.OneTo) = One()

    step(r::AbstractUnitRange) = One()
    step(r) = Base.step(r)

    *(x::Any, y::Any) = Base.:(*)(x,y)
    *(x::Any, y::One) = x
    *(x::One, y::Any) = y
    *(x::One, y::One) = One()
    val(::One) = true
    val(x)     = x

    start_step_stop(start::Any, step::Any, stop)      = start:step:stop
    start_step_stop(start::Any, step::One, stop)      = start:stop
    start_step_stop(start::One, step::One, stop) = Base.OneTo(stop)

    start_step_length(start::Any, step::Any, length) = Base.range(start, step=step, length=length)
    start_step_length(start::One, step::Any, length) = Base.range(val(start), step=step, length=length)
    start_step_length(start::One, step::One, length) = Base.OneTo(length)
    start_step_length(start::Any, step::One, length) = begin
        stop = start + oneunit(start)*(length-1)
        ret  = start_step_stop(start, step, stop)
        if Base.length(ret) != length
            msg = """
            Bug, please open an issue at https://github.com/jw3126/RangeHelpers.jl
            start_step_length(start, step, length)
            start = $(start)
            step = $(step)
            stop = $(stop)
            length = $(length)
            ret = $(ret)
            """
            error(msg)
        end
        return ret
    end
    function step_stop_length(step,stop,length)
        start = stop - (oneunit(stop)*step)*(length-1)
        ret = start_step_stop(start, step, stop)
        if Base.length(ret) != length
            msg = """
            Bug, please open an issue at https://github.com/jw3126/RangeHelpers.jl
            step_stop_length(step, stop, length)
            start= $(start)
            step = $(step)
            stop = $(stop)
            length = $(length)
            ret = $(ret)
            """
            error(msg)
        end
        ret
    end
    start_stop_length(start::Any, stop, length) = Base.range(start, stop=stop, length=length)
    start_stop_length(start::One, stop, length) = Base.range(val(start) , stop=stop, length=length)
end #module HandleUnitStep
const HUS = HandleUnitStep

module HandleApproach
function start_step_stop end
end
const HAP = HandleApproach

range1(start, step, stop, length)          = Base.range(start, stop=stop, step=step, length=length)
range1(start, step, stop, length::Nothing) = HUS.start_step_stop(start, step, stop)

range1(start::Approach, step, stop, length::Nothing) = HAP.start_step_stop(start,step,stop)
range1(start, step::Approach, stop, length::Nothing) = HAP.start_step_stop(start,step,stop)
range1(start, step, stop::Approach, length::Nothing) = HAP.start_step_stop(start,step,stop)

function HAP.start_step_stop(start::Approach, step, stop)
    flength = floatlength(start, stop, step)
    dir = if HUS.val(step) >= 0
        opposite(start.direction)
    else
        start.direction
    end
    length = int(flength, dir)
    ret = HUS.step_stop_length(step, stop, length)
end

function HAP.start_step_stop(start, step, stop::Approach)
    flength = floatlength(start, stop, step)
    dir = if HUS.val(step) >= 0
        stop.direction
    else
        opposite(stop.direction)
    end
    length = int(flength, dir)
    ret = HUS.start_step_length(start, step, length)
    #@show start step stop length ret
end

function HAP.start_step_stop(start, step::Approach, stop)
    flength = floatlength(start, stop, step)
    dir = if step.value >= 0
        opposite(step.direction)
    else
        step.direction
    end
    length = int(flength, dir)
    ret = HUS.start_stop_length(start, stop, length)
    @assert start == first(ret)
    @assert stop == last(ret)
    #@show ret start step stop typeof(start) typeof(step) typeof(stop)
    ret
end

value(x) = x
value(x::Approach) = x.value

floatlength(start, stop, step) = _floatlength(HUS.val(start), HUS.val(stop), HUS.val(step))
function _floatlength(start, stop, step)
    1 + (value(stop) - value(start)) / value(step)
end

################################################################################
##### prolong
################################################################################
"""
    prolong(r::AbstractRange;start=nothing, stop=nothing, pre=nothing, post=nothing)

Prolong an existing range `r` according to the arguments:

```jldoctest
julia> using RangeHelpers

julia> r = 1.0:0.5:3.0
1.0:0.5:3.0

julia> prolong(r, stop=around(4))
1.0:0.5:4.0

julia> prolong(r, pre=1)
0.5:0.5:3.0

julia> prolong(r, pre=1, post=2)
0.5:0.5:4.0

julia> prolong(r, start=below(0.4), stop=around(4.1))
0.0:0.5:4.0
```
"""
function prolong(r::AbstractRange;start=nothing, stop=nothing, pre=nothing, post=nothing)
    r2 = prolong_start_stop(r, start, stop)
    r3 = prolong_pre_post(r2, pre, post)
    return r3
end

function prolong_start_stop(r, start::Nothing, stop::Nothing)
    r
end
function prolong_start_stop(r, start::Approach, stop::Nothing)
    range(start, step=HUS.step(r), stop=last(r))
end
function prolong_start_stop(r, start::Nothing, stop::Approach)
    HAP.start_step_stop(HUS.first(r), HUS.step(r), stop)
end
function prolong_start_stop(r, start::Approach, stop::Approach)
    anchor = first(r)
    flength = floatlength(anchor, stop, step(r))
    dir = step(r) >= 0 ? stop.direction : opposite(stop.direction)
    len1 = int(flength, dir)
    stop1 = anchor + (len1-1) * step(r)
    return HAP.start_step_stop(start, HUS.step(r), stop1)
end

prolong_pre_post(r, pre::Nothing, post::Nothing) = r
function prolong_pre_post(r, pre::Integer, post::Nothing)
    len = length(r) + pre
    HUS.step_stop_length(HUS.step(r), last(r), len)
end
function prolong_pre_post(r, pre::Nothing, post::Integer)
    len = length(r) + post
    HUS.start_step_length(HUS.first(r), HUS.step(r), len)
end
function prolong_pre_post(r, pre, post)
    len = length(r) + pre + post
    start = first(r) - pre*step(r)
    HUS.start_step_length(start, HUS.step(r), len)
end

################################################################################
##### anchorrange
################################################################################
"""

    anchorrange(anchor; step, start, stop, pre, post)

Return a range, that approximately has `anchor` on its grid.

```jldoctest
julia> using RangeHelpers

julia> anchorrange(15.5, start=above(11), step=2, stop=below(15))
11.5:2.0:13.5
```
"""
function anchorrange(anchor; step, kw...)
    r0 = anchor:step:anchor
    return prolong(r0; kw...)
end

################################################################################
##### symrange
################################################################################

"""
    symrange(;center=0, step, length, start, stop)

Construct a range, that is symmetric around center.

```jldoctest
julia> using RangeHelpers

julia> symrange(length=2, step=1)
-0.5:1.0:0.5

julia> symrange(length=3, step=1)
-1.0:1.0:1.0

julia> symrange(length=3, step=1, center=4)
3.0:1.0:5.0

julia> symrange(start=around(-4.1), step=2)
-4.0:2.0:4.0

julia> symrange(start=around(-4.1), step=2, center=1)
-4.0:2.0:6.0

julia> symrange(stop=around(4.1), step=2, center=1)
-2.0:2.0:4.0
```
"""
function symrange(;center=0, start=nothing, step=nothing, stop=nothing, length=nothing)
    _symrange(center, start, step, stop, length)
end

function _symrange(center, start, step, stop, length)
    msg = """
    Cannot construct symmetric range from arguments:
    center = $center
    start = $start
    step = $step
    stop = $stop
    length = $length
    """
    throw(ArgumentError(msg))
end

function _symrange(center, start::Nothing, step, stop::Nothing, length)
    c = float(center*one(step))
    if isodd(length)
        hl = Int((length - 1)/2)
        anchorrange(c, pre=hl, post=hl, step=step)
    else
        hl = length ÷ 2
        walls = anchorrange(c, pre=hl, post=hl, step=step)
        bincenters(walls)
    end
end

function _symrange(center, start::Approach, step, stop::Nothing, length::Nothing)
    r = HAP.start_step_stop(start, step/2, center)
    start_ = if last(r) == center
        first(r)
    else
        last(r)
    end
    stop_ = around(2*center-start_)
    return HAP.start_step_stop(start_, step, stop_)
end

function _symrange(center, start::Nothing, step, stop::Approach, length::Nothing)
    r = HAP.start_step_stop(center, step/2, stop)
    stop_ = if first(r) == center
        last(r)
    else
        first(r)
    end
    start_ = around(2*center-stop_)
    return HAP.start_step_stop(start_, step, stop_)
end

################################################################################
##### asrange
################################################################################

"""
    asrange(itr; check=true, atol, rtol)::AbstractRange

Convert `itr` into a range, optionally validating the result.
```jldoctest
julia> using RangeHelpers

julia> asrange([1,2,3.0])
1.0:1.0:3.0

julia> asrange([1,2,3])
1:1:3

julia> asrange(1:3)
1:3

julia> asrange([1,2,4.0])
ERROR: ArgumentError: Cannot construct range from `itr`
itr = [1.0, 2.0, 4.0]
[...]

julia> asrange([1,2,4.0], atol=10)
1.0:1.5:4.0
```
"""
function asrange end

asrange(r::AbstractRange; kw...) = r
function asrange(itr; kw...)
    start = first(itr)
    stop  = last(itr)
    len = length(itr)
    ret = Base.range(start, stop=stop,length=len)
    validate_asrange(ret, itr; kw...)
end
function validate_asrange(ret, itr; check=true, kw...)
    if check
        if !isapprox(ret, itr; kw...)
            msg = """
            Cannot construct range from `itr`
            itr = $(itr)
            """
            throw(ArgumentError(msg))
        end
    end
    ret
end

function asrange(arr::AbstractArray{<:Integer}; kw...)
    start = first(arr)
    if length(arr) > 1
        step = arr[firstindex(arr)+1] - start
    else
        step = oneunit(start)
    end
    len = length(arr)
    ret = Base.range(start, step=step, length=len)
    validate_asrange(ret, arr; kw...)
end

################################################################################
##### bincenters, binwalls
################################################################################
"""
    binwalls(r::AbstractRange; first=true, last=true)::AbstractRange

If `r` is interpreted as a collection of bin centers, `binwalls` returns the bin boundaries.

```jldoctest
julia> using RangeHelpers: binwalls

julia> binwalls(0.0:2.0:10.0)
-1.0:2.0:11.0

julia> binwalls(0.0:2.0:10.0, first=false)
1.0:2.0:11.0

julia> binwalls(0.0:2.0:10.0, last=false)
-1.0:2.0:9.0
```
See also [`bincenters`](@ref).
"""
function binwalls(r::AbstractRange; first=true, last=true)::AbstractRange
    h = (1//2) * step(r)
    start = if first
        Base.first(r) - h
    else
        Base.first(r) + h
    end
    stop = if last
        Base.last(r) + h
    else
        Base.last(r) - h
    end
    len = length(r) + first + last - 1
    return Base.range(start, stop=stop, length = len)
end

"""
    bincenters(r::AbstractRange)::AbstractRange

If `r` is interpreted as a collection of bin boundaries, `bincenters` returns the bin centers.
```jldoctest
julia> using RangeHelpers: bincenters

julia> bincenters(1:10.0)
1.5:1.0:9.5
```
See also [`binwalls`](@ref).
"""
function bincenters(r::AbstractRange)::AbstractRange
    return binwalls(r, first=false, last=false)
end

################################################################################
##### subdivide
################################################################################
const ALLOWED_subdivide_modes = (:walls, :centers)

"""
    subdivide(r::AbstractRange, factor::Integer, mode=:walls)

Create a range with smaller step from `r`. Possible values for mode are $(ALLOWED_subdivide_modes).

```jldoctest
julia> using RangeHelpers

julia> r = 1:3.0
1.0:1.0:3.0

julia> subdivide(r, 2)
1.0:0.5:3.0

julia> subdivide(r, 4)
1.0:0.25:3.0

julia> subdivide(r, 2, mode=:walls)
1.0:0.5:3.0

julia> subdivide(r, 2, mode=:centers)
0.75:0.5:3.25

julia> subdivide(r, 10, mode=:centers)
0.55:0.1:3.45
```
"""
function subdivide(r::AbstractRange, factor::Integer; mode=:walls)
    if factor < 1
        msg = """
        factor >= 1 must hold. Got:
        factor = $factor
        """
        throw(ArgumentError(msg))
    end
    if !(mode in ALLOWED_subdivide_modes)
        msg = """
        The mode must be one of $(ALLOWED_subdivide_modes). Got:
        mode = $mode
        """
        throw(ArgumentError(msg))
    end
    h = (factor - 1)/(2*factor) * step(r)
    if mode === :walls
        h = zero(h)
        len = factor*(length(r) - 1) + 1
    elseif mode === :centers
        len = factor*length(r)
    else
        error("Unreachable mode = $mode")
    end
    start = first(r) - h
    stop = last(r) + h
    return Base.range(start, stop=stop, length=len)
end

################################################################################
##### searchsortedat
################################################################################
"""
    searchsortedat(coll, x)

Return the index of a sorted collection `coll` whose corresponding
element is closest to `x`. If `coll` is not sorted, `searchsortedat` might
silently return a wrong result.

```jldoctest
julia> using RangeHelpers

julia> searchsortedat(100:100:1000, around(200))
2

julia> searchsortedat(100:100:1000, around(249))
2

julia> searchsortedat(100:100:1000, around(251))
3

julia> searchsortedat(100:100:1000, below(251))
2

julia> searchsortedat(100:100:1000, above(249))
3

julia> searchsortedat(100:100:1000, 300)
3

julia> searchsortedat(100:100:1000, 301)
ERROR: ArgumentError: coll does not contain x:
coll = 100:100:1000
x = 301
Try `searchsortedat(coll, around(x))`
```
"""
function searchsortedat(coll, app::Approach)
    # TODO support same keywords as searchsorted and friends
    dir = app.direction
    x = app.value
    if dir === strictbelow
        i = searchsortedfirst(coll, x)
        return i - 1
    elseif dir === below
        return searchsortedlast(coll, x)
    elseif dir === around
        ilo = searchsortedlast(coll, x)
        if ilo < firstindex(coll)
            return firstindex(coll)
        end
        ihi = min(ilo+1, lastindex(coll))
        if norm(coll[ilo] - x) < norm(coll[ihi] - x)
            return ilo
        else
            return ihi
        end
    elseif dir === above
        return searchsortedfirst(coll, x)
    elseif dir === strictabove
        i = searchsortedlast(coll, x)
        return i+1
    else
        msg = "Unreachable direction = $dir"
        error(msg)
    end
end
function searchsortedat(coll, x)
    inds = searchsorted(coll, x)
    if isempty(inds)
        msg = """
        coll does not contain x:
        coll = $coll
        x = $x
        Try `searchsortedat(coll, around(x))`
        """
        throw(ArgumentError(msg))
    else
        first(inds)
    end
end

function element_with_largest_norm(r::AbstractRange)::eltype(r)
    x1 = first(r)
    isempty(r) && return x1
    x2 = last(r)
    if norm(x2) > norm(x1)
        return x2
    else
        return x1
    end
end

"""

    samegrid(r1::AbstractRange, r2::AbstractRange; atol=0, rtol=0, kw...)::Bool

Check if `r1` and `r2` are defined on the same grid. That is if there exist equal prolongations
of `r1` and `r1`.
```jldoctest
julia> using RangeHelpers: samegrid

julia> samegrid(1:10, 11:12)
true

julia> samegrid(1:10, 11:1.1:12)
false

julia> samegrid(1:10, 1:0)
true

julia> samegrid(1:10, 0.1:0)
false

julia> samegrid(1:10, 5:-1:3)
true

julia> samegrid(1:10, 4.1:1:5, rtol=0.2)
true

```
"""
function samegrid(r1::AbstractRange, r2::AbstractRange; atol=0, rtol=0, kw...)::Bool
    astep1 = abs(step(r1))
    astep2 = abs(step(r2))
    if !isapprox(astep1, astep2; atol=atol, rtol=rtol, kw...)
        return false
    end
    dx = (1//2)*astep1 + (1//2)*astep2
    s1 = element_with_largest_norm(r1)
    s2 = element_with_largest_norm(r2)
    n = round(Int, (s2-s1)/ dx)
    s1_shifted = s1 + n * dx
    return isapprox(s1_shifted, s2; atol=atol, rtol=rtol, kw...)
end

end #module