text:
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                        TESTING EXPRESSIONS                                              #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
export NumberQ, IntegerQ, OddQ, EvenQ, RationalQ, RealQ, PositiveQ, NonpositiveQ, NegativeQ, NonnegativeQ,
EqualQ, Rationalize, ARationalize, NO, SignBit, CopySign, FlipSign, Positive, Nonpositive, Negative,
Nonnegative, Zero, One, Sign, KroneckerDelta, Abs, Min, Max, Plus, Subtract, Times, Divide, Power, Sqrt,
RSqrt, Surd, Hypot, LogHypot, Exp, Expm1, Ln, Lnp1, Log10, Log, Pi, SqrtPi, LogSqrt2Pi, Sin, SinPi, Cos,
CosPi, Tan, Cot, Csc, Sec, ArcSin, ArcCos, ArcTan, ArcCot, Sinh, Cosh, Tanh, Coth, ArcSinh, ArcCosh,
ArcTanh, ArcCoth, Factorial, Gamma, UGamma, LGamma, PGamma, QGamma, Pochhammer, Beta, Binomial, GBinomial,
ExpIntegralEi, ExpIntegralE, Erf, Erfc, Erfi
###########################################################################################################
################################################## NUMBER #################################################
function NumberQ(nmbrx)
    typeof(nmbrx)<:Number
end
###########################################################################################################
################################################# INTEGER #################################################
function IntegerQ(x::arb)
    return Bool(ccall((:arb_is_int, Nemo.libarb), Cint, (Ref{arb},), x))
 end

IntegerQ(x::Real) = IntegerQ(RF(x))

function IntegerQ(x::acb)
    return Bool(ccall((:acb_is_int, Nemo.libarb), Cint, (Ref{acb},), x))
 end

function IntegerQ(x::Complex)
	xre, xim = reim(x)
	x = CF(xre, xim)
	return IntegerQ(x)
end

IntegerQ(x...) = IntegerQ.((x))
###########################################################################################################
################################################### ODD ###################################################(44)
OddQ(x::arb) = ifelse(IntegerQ(x), rem(convert(Float64, x), 2) != 0, false)
OddQ(x::Real) = ifelse(IntegerQ(x), rem(x, 2) != 0, false)
OddQ(x::acb) = ifelse(IntegerQ(x), rem(real(convert(ComplexF64, x)),2) != 0, false)
OddQ(x::Complex) = ifelse(IntegerQ(x), rem(real(x), 2) != 0, false)
OddQ(x...) = OddQ.((x))
###########################################################################################################
################################################### EVEN ##################################################
EvenQ(x::arb) = ifelse(IntegerQ(x), rem(convert(Float64, x), 2) == 0, false)
EvenQ(x::Real) = ifelse(IntegerQ(x), rem(x, 2) == 0, false)
EvenQ(x::acb) = ifelse(IntegerQ(x), rem(real(convert(ComplexF64, x)),2) == 0, false)
EvenQ(x::Complex) = ifelse(IntegerQ(x), rem(real(x), 2) == 0, false)
EvenQ(x...) = EvenQ.((x))
###########################################################################################################
################################################# RATIONAL ################################################
function RationalQ(rtnlx)
	typeof(rtnlx) == Rational{Int64} ||
	typeof(rtnlx) == Rational{Int128} ||
	typeof(rtnlx) == Rational{BigInt} ||
	typeof(rtnlx) == Complex{Rational{Int64}} ||
	typeof(rtnlx) == Complex{Rational{Int128}} ||
	typeof(rtnlx) == Complex{Rational{BigInt}} ?
	true : false
end
RationalQ(rtnlx...) = RationalQ.((rtnlx))
###########################################################################################################
################################################## REAL ###################################################
function RealQ(x::acb)
    return Bool(ccall((:acb_is_real, Nemo.libarb), Cint, (Ref{acb},), x))
 end

 RealQ(x::Complex) = isreal(x)
###########################################################################################################
################################################# POSITIVE ################################################
################# Return `false` for x<=0 and `true` for x>0.
function PositiveQ(x::arb)
    return Bool(ccall((:arb_is_positive, Nemo.libarb), Cint, (Ref{arb},), x))
 end

PositiveQ(x::Real) = PositiveQ(RF(x))
PositiveQ(x::acb) = RealQ(x) && PositiveQ(real(x))
PositiveQ(x::Complex) = RealQ(x) && PositiveQ(real(x))
###########################################################################################################
############################################### NONPOSITIVE ###############################################
################# Return `true` for x<=0 and `false` for x>0.
function NonpositiveQ(x::arb)
    return Bool(ccall((:arb_is_nonpositive, Nemo.libarb), Cint, (Ref{arb},), x))
 end

NonpositiveQ(x::Real) = NonpositiveQ(RF(x))
NonpositiveQ(x::acb) = RealQ(x) && NonpositiveQ(real(x))
NonpositiveQ(x::Complex) = RealQ(x) && NonpositiveQ(real(x))
###########################################################################################################
################################################# NEGATIVE ################################################
################# Return `false` for x>=0 and `true` for x<0.
function NegativeQ(x::arb)
    return Bool(ccall((:arb_is_negative, Nemo.libarb), Cint, (Ref{arb},), x))
 end

NegativeQ(x::Real) = NegativeQ(RF(x))
NegativeQ(x::acb) = RealQ(x) && NegativeQ(real(x))
NegativeQ(x::Complex) = RealQ(x) && NegativeQ(real(x))
###########################################################################################################
############################################### NONNEGATIVE ###############################################
################# Return `true` for x>=0 and `false` for x<0.
function NonnegativeQ(x::arb)
    return Bool(ccall((:arb_is_nonnegative, Nemo.libarb), Cint, (Ref{arb},), x))
 end

NonnegativeQ(x::Real) = NonnegativeQ(RF(x))
NonnegativeQ(x::acb) = RealQ(x) && NonnegativeQ(real(x))
NonnegativeQ(x::Complex) = RealQ(x) && NonnegativeQ(real(x))
###########################################################################################################
################################################## EQUAL ##################################################
function EqualQ(x::arb, y::arb)
    r = ccall((:arb_equal, Nemo.libarb), Cint, (Ref{arb}, Ref{arb}), x, y)
    return Bool(r)
end
EqualQ(x::Real, y::Real) = EqualQ(RF(x), RF(y))

function EqualQ(x::acb, y::acb)
    r = ccall((:acb_equal, Nemo.libarb), Cint, (Ref{acb}, Ref{acb}), x, y)
    return Bool(r)
end
EqualQ(x::Complex, y::Complex) = EqualQ(CF(real(x), imag(x)), CF(real(y), imag(y)))

EqualQ(x::arb, y::Real) = EqualQ(x, RF(y))
EqualQ(x::Real, y::arb) = EqualQ(RF(x), y)

EqualQ(x::acb, y::Complex) = EqualQ(x, CF(real(y), imag(y)))
EqualQ(x::Complex, y::acb) = EqualQ(CF(real(x), imag(x)), y)

EqualQ(x::arb, y::acb) = ifelse(imag(y) == 0, EqualQ(x, real(y)), false)
EqualQ(x::acb, y::arb) = ifelse(imag(x) == 0, EqualQ(real(x), y), false)

EqualQ(x::arb, y::Complex) = ifelse(imag(y) == 0, EqualQ(x, real(y)), false)
EqualQ(x::Complex, y::arb) = ifelse(imag(x) == 0, EqualQ(real(x), y), false)

EqualQ(x::acb, y::Real) = ifelse(imag(x) == 0, EqualQ(real(x), y), false)
EqualQ(x::Real, y::acb) = ifelse(imag(y) == 0, EqualQ(x, real(y)), false)

EqualQ(x::Real, y::Complex) = ifelse(imag(y) == 0, EqualQ(x, real(y)), false)
EqualQ(x::Complex, y::Real) = ifelse(imag(x) == 0, EqualQ(real(x), y), false)
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                     NUMBER REPRESENTATIONS                                              #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
############################################## ARGUMENT LIST ##############################################
ArgListOpt(Opt,arglox) = arglox

ArgListOpt(Opt,arglox,argloy) = Opt(arglox,argloy)

ArgListOpt(Opt,arglox,argloy,argloz...) = ArgListOpt(Opt, Opt(arglox,argloy), argloz...)
###########################################################################################################
############################################### RATIONALIZE ###############################################(40)
Rationalize(x::Rational) = x
Rationalize(x::Complex{Rational{Int64}}) = x
Rationalize(x::Complex{Rational{Int128}}) = x
Rationalize(x::Complex{Rational{BigInt}}) = x

function Rationalize(x::String)
    if occursin("im", "$x") == false
        return rationalize(BigInt, BigFloat(x))
    elseif occursin("+", "$x") == true
        arrx = split(x, "+")
        restrx = BigFloat(arrx[1])
        if occursin("*", "$arrx[2]") == true
            imstrx = BigFloat(split(arrx[2], "*")[1])
        else
            imstrx = BigFloat(split(arrx[2], "i")[1])
        end
        return rationalize(BigInt, restrx) + im*rationalize(BigInt, imstrx)
    else
        arrx = split(x, "-")
        restrx = BigFloat(arrx[1])
        if occursin("*", "$arrx[2]") == true
            imstrx = BigFloat(split(arrx[2], "*")[1])
        else
            imstrx = BigFloat(split(arrx[2], "i")[1])
        end
        return rationalize(BigInt, restrx) - im*rationalize(BigInt, imstrx)
    end
end

function Rationalize(x::Real)
	restrx = real(x)
	strx = "$restrx"
	return rationalize(BigInt, BigFloat(strx))
end

function Rationalize(x::Complex)
	restrx = BigFloat(real(x))
	imstrx = BigFloat(imag(x))
	reres = rationalize(BigInt, restrx)
	imres = rationalize(BigInt, imstrx)
	return reres + imres*im
end
###########################################################################################################
############################################ ARRAY RATIONALIZE ############################################
ARationalize(x...) = Rationalize.((x))

#function ARationalize(x)
#	arrx = []
#	for s in 1 : length(x)
#		append!(arrx, Rationalize(x[s]))
#	end
#	return arrx
#end
###########################################################################################################
############################################### NEMO OUTPUT ###############################################
function NO(x::arb)
    x = "$x"
    if occursin("+/-", "$x") == true
        x = split("$x", " +/-")[1]
        x = split("$x", "[")[2]
        BigFloat(x)
    else
        BigFloat(x)
    end
end

function NO(x::acb)
    return NO(real(x)) + im * NO(imag(x))
end

NO(x...) = NO.((x))
###########################################################################################################
################################################# SIGNBIT #################################################
################# Returns true if the value of the sign of x is negative, otherwise false.
SignBit(x) = NegativeQ(x)
###########################################################################################################
################################################ COPY SIGN ################################################
################# Return `z` which has the magnitude of `x` and the same sign as `y`.
CopySign(x, y) = ifelse(SignBit(x) != SignBit(y), -x, x)
###########################################################################################################
################################################ FLIP SIGN ################################################
################# Return `x` with its sign flipped if `y` is negative.
FlipSign(x, y) = ifelse(SignBit(y), -x, x)
###########################################################################################################
############################################## POSITIVE SIGN ##############################################(39)
################# Return `-1` for x<=0 and `1` for x>0.
Positive(x) = ifelse(PositiveQ(x), parent(x)(1), parent(x)(-1))
###########################################################################################################
############################################ NONPOSITIVE SIGN #############################################
################# Return `1` for x<=0 and `-1` for x>0.
Nonpositive(x) = ifelse(NonpositiveQ(x), parent(x)(1), parent(x)(-1))
###########################################################################################################
############################################## NEGATIVE SIGN ##############################################
################# Return `-1` for x>=0 and `1` for x<0.
Negative(x) = ifelse(NegativeQ(x), parent(x)(1), parent(x)(-1))
###########################################################################################################
############################################ NONNEGATIVE SIGN #############################################
################# Return `1` for x>=0 and `-1` for x<0.
Nonnegative(x) = ifelse(NonnegativeQ(x), parent(x)(1), parent(x)(-1))
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                          COMPLEX COMPONENTS                                             #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
################################################## UNITY ##################################################
function Zero(r::AcbField)
    z = acb()
    z.parent = r
    return z
end
Zero(R::ArbField) = R(0)

function Zero(z::arb)
    ccall((:arb_zero, Nemo.libarb), Nothing, (Ref{arb},), z)
    return z
end

function Zero(z::acb)
    ccall((:acb_zero, Nemo.libarb), Nothing, (Ref{acb},), z)
    return z
 end
 Zero(x::Number) = oftype(x,0)
 Zero(::Type{T}) where {T<:Number} = convert(T,0)

 function One(r::AcbField)
    z = acb()
    ccall((:acb_one, Nemo.libarb), Nothing, (Ref{acb}, ), z)
    z.parent = r
    return z
  end
One(R::ArbField) = R(1)

One(x::acb) = parent(x)(1)
One(x::arb) = parent(x)(1)
One(::Type{T}) where {T<:Number} = convert(T,1)
One(onex::T) where {T<:Number} = One(T)
###########################################################################################################
################################################### SIGN ##################################################
#Sign(x::Number) = x == 0 ? x / Abs(oneunit(x)) : x / Abs(x)
Sign(x::arb) = ifelse(x < 0, parent(One(x))(-1), ifelse(x > 0, One(x), parent(One(x))(x)))
Sign(x::Real) = ifelse(x < 0, oftype(One(x),-1), ifelse(x > 0, One(x), parent(One(x))(x)))
Sign(x::acb) = x == 0 ? x / Abs(One(x)) : x / Abs(x)
Sign(x::Complex) = x == 0 ? x / Abs(One(x)) : x / Abs(x)

Sign(x, y) = Times(Sign(x), Sign(y))

Sign(a, b, c, xs...) = ArgListOpt(Sign, Sign(Sign(a,b),c), xs...)
###########################################################################################################
############################################# KRONECKER DELTA #############################################(38)
KroneckerDelta(x, y) = ifelse(EqualQ(x,y), One(x), zero(x))
###########################################################################################################
################################################ ABSOLUTE #################################################(41)
function Abs(x::arb)
    z = parent(x)()
    ccall((:arb_abs, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}), z, x)
    return z
end

function Abs(x::Real)
    z = BigFloat()
    ccall((:mpfr_abs, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
    z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function Abs(x::acb)
    z = acb()
    ccall((:acb_abs, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    z.parent = AcbField(parent(x).prec)
    return z
end
Abs(x::Complex) = Abs(CF(real(x), imag(x)))

Abs(x...) = Abs.((x))
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                         ORDER STATISTICS                                                #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
################################################# MINIMUM #################################################
function Min(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_min, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end

function Min(x::Real, y::Real)
	z = BigFloat()
	ccall(
	(:mpfr_min, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end
Min(x::acb, y::acb) = imag(x) == 0 && imag(y) == 0 && Min(real(x), real(y))
Min(x::Complex, y::Complex) = imag(x) == 0 && imag(y) == 0 && Min(real(x), real(y))

Min(x::arb, y::acb) =  Min(CF(x, parent(x)(0)), y)
Min(x::arb, y::Real) = Min(x, RF(y))
Min(x::arb, y::Complex) = Min(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Min(x::acb, y::arb) =  Min(x, CF(y, parent(y)(0)))
Min(x::acb, y::Real) = Min(x, CF(y, parent(y)(0)))
Min(x::acb, y::Complex) = Min(x, CF(real(y), imag(y)))

Min(x::Real, y::arb) = Min(RF(x), y)
Min(x::Real, y::acb) = Min(CF(x, parent(x)(0)), y)
Min(x::Real, y::Complex) = Min(Complex(x, oftype(x,0)), y)

Min(x::Complex, y::arb) = Min(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Min(x::Complex, y::acb) = Min(CF(real(x), imag(x)), y)
Min(x::Complex, y::Real) = Min(x, Complex(y, oftype(y,0)))

Min(a, b, c, xs...) = ArgListOpt(Min, Min(Min(a,b),c), xs...)
###########################################################################################################
################################################# MAXIMUM #################################################
function Max(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_max, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end

function Max(x::Real, y::Real)
	z = BigFloat()
	ccall(
	(:mpfr_max, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

Max(x::acb, y::acb) = imag(x) == 0 && imag(y) == 0 && Max(real(x), real(y))
Max(x::Complex, y::Complex) = imag(x) == 0 && imag(y) == 0 && Max(real(x), real(y))

Max(x::arb, y::acb) =  Max(CF(x, parent(x)(0)), y)
Max(x::arb, y::Real) = Max(x, RF(y))
Max(x::arb, y::Complex) = Max(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Max(x::acb, y::arb) =  Max(x, CF(y, parent(y)(0)))
Max(x::acb, y::Real) = Max(x, CF(y, parent(y)(0)))
Max(x::acb, y::Complex) = Max(x, CF(real(y), imag(y)))

Max(x::Real, y::arb) = Max(RF(x), y)
Max(x::Real, y::acb) = Max(CF(x, parent(x)(0)), y)
Max(x::Real, y::Complex) = Max(Complex(x, oftype(x,0)), y)

Max(x::Complex, y::arb) = Max(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Max(x::Complex, y::acb) = Max(CF(real(x), imag(x)), y)
Max(x::Complex, y::Real) = Max(x, Complex(y, oftype(y,0)))

Max(a, b, c, xs...) = ArgListOpt(Max, Max(Max(a,b),c), xs...)
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                         ARITHMETIC FUNCTIONS                                            #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
################################################# ADDITION ################################################(42)
function Plus(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_add, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end
function Plus(x::Real, y::Real)
	z = BigFloat()
	ccall((:mpfr_add, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Plus(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_add, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Ref{acb}, Int),
    z, x, y, parent(x).prec)
    return z
end
Plus(x::Complex, y::Complex) = Plus(CF(real(x), imag(x)), CF(real(y), imag(y)))

Plus(x::arb, y::acb) = Plus(CF(x, parent(x)(0)), y)
Plus(x::arb, y::Real) = Plus(x, RF(y))
Plus(x::arb, y::Complex) = Plus(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Plus(x::acb, y::arb) = Plus(x, CF(y, parent(y)(0)))
Plus(x::acb, y::Real) = Plus(x, CF(y, parent(y)(0)))
Plus(x::acb, y::Complex) = Plus(x, CF(real(y), imag(y)))

Plus(x::Real, y::arb) = Plus(RF(x), y)
Plus(x::Real, y::acb) = Plus(CF(x, parent(x)(0)), y)
Plus(x::Real, y::Complex) = Plus(Complex(x, oftype(x,0)), y)

Plus(x::Complex, y::arb) = Plus(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Plus(x::Complex, y::acb) = Plus(CF(real(x), imag(x)), y)
Plus(x::Complex, y::Real) = Plus(x, Complex(y, oftype(y,0)))

Plus(a, b, c, xs...) = ArgListOpt(Plus, Plus(Plus(a,b),c), xs...)
###########################################################################################################
############################################### SUBTRACTİON ###############################################
function Subtract(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_sub, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end
function Subtract(x::Real, y::Real)
	z = BigFloat()
	ccall((:mpfr_sub, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Subtract(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_sub, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Ref{acb}, Int),
    z, x, y, parent(x).prec)
    return z
end
Subtract(x::Complex, y::Complex) = Subtract(CF(real(x), imag(x)), CF(real(y), imag(y)))

Subtract(x::arb, y::acb) = Subtract(CF(x, parent(x)(0)), y)
Subtract(x::arb, y::Real) = Subtract(x, RF(y))
Subtract(x::arb, y::Complex) = Subtract(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Subtract(x::acb, y::arb) = Subtract(x, CF(y, parent(y)(0)))
Subtract(x::acb, y::Real) = Subtract(x, CF(y, parent(y)(0)))
Subtract(x::acb, y::Complex) = Subtract(x, CF(real(y), imag(y)))

Subtract(x::Real, y::arb) = Subtract(RF(x), y)
Subtract(x::Real, y::acb) = Subtract(CF(x, parent(x)(0)), y)
Subtract(x::Real, y::Complex) = Subtract(Complex(x, oftype(x,0)), y)

Subtract(x::Complex, y::arb) = Subtract(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Subtract(x::Complex, y::acb) = Subtract(CF(real(x), imag(x)), y)
Subtract(x::Complex, y::Real) = Subtract(x, Complex(y, oftype(y,0)))

Subtract(a, b, c, xs...) = ArgListOpt(Subtract, Subtract(Subtract(a,b),c), xs...)
###########################################################################################################
############################################## MULTIPLICATION #############################################
function Times(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_mul, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end
function Times(x::Real, y::Real)
	z = BigFloat()
	ccall((:mpfr_mul, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Times(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_mul, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Ref{acb}, Int),
    z, x, y, parent(x).prec)
    return z
end
Times(x::Complex, y::Complex) = Times(CF(real(x), imag(x)), CF(real(y), imag(y)))

Times(x::arb, y::acb) = Times(CF(x, parent(x)(0)), y)
Times(x::arb, y::Real) = Times(x, RF(y))
Times(x::arb, y::Complex) = Times(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Times(x::acb, y::arb) = Times(x, CF(y, parent(y)(0)))
Times(x::acb, y::Real) = Times(x, CF(y, parent(y)(0)))
Times(x::acb, y::Complex) = Times(x, CF(real(y), imag(y)))

Times(x::Real, y::arb) = Times(RF(x), y)
Times(x::Real, y::acb) = Times(CF(x, parent(x)(0)), y)
Times(x::Real, y::Complex) = Times(Complex(x, oftype(x,0)), y)

Times(x::Complex, y::arb) = Times(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Times(x::Complex, y::acb) = Times(CF(real(x), imag(x)), y)
Times(x::Complex, y::Real) = Times(x, Complex(y, oftype(y,0)))

Times(a, b, c, xs...) = ArgListOpt(Times, Times(Times(a,b),c), xs...)
###########################################################################################################
################################################ DIVISION #################################################
function Divide(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_div, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end
function Divide(x::Real, y::Real)
	z = BigFloat()
	ccall((:mpfr_div, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Divide(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_div, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Ref{acb}, Int),
    z, x, y, parent(x).prec)
    return z
end
Divide(x::Complex, y::Complex) = Divide(CF(real(x), imag(x)), CF(real(y), imag(y)))

Divide(x::arb, y::acb) = Divide(CF(x, parent(x)(0)), y)
Divide(x::arb, y::Real) = Divide(x, RF(y))
Divide(x::arb, y::Complex) = Divide(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Divide(x::acb, y::arb) = Divide(x, CF(y, parent(y)(0)))
Divide(x::acb, y::Real) = Divide(x, CF(y, parent(y)(0)))
Divide(x::acb, y::Complex) = Divide(x, CF(real(y), imag(y)))

Divide(x::Real, y::arb) = Divide(RF(x), y)
Divide(x::Real, y::acb) = Divide(CF(x, parent(x)(0)), y)
Divide(x::Real, y::Complex) = Divide(Complex(x, oftype(x,0)), y)

Divide(x::Complex, y::arb) = Divide(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Divide(x::Complex, y::acb) = Divide(CF(real(x), imag(x)), y)
Divide(x::Complex, y::Real) = Divide(x, Complex(y, oftype(y,0)))

Divide(a, b, c, xs...) = ArgListOpt(Divide, Divide(Divide(a,b),c), xs...)
################# Return the multiplicative inverse of x, i.e. 1/x
function Divide(x::arb)
    z = parent(x)()
    ccall((:arb_inv, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return parent(x)(z)
end
Divide(x::Real) = Divide(RF(x))

function Divide(x::acb)
    z = parent(x)()
    ccall((:acb_inv, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end
Divide(x::Complex) = Divide(CF(real(x), imag(x)))
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
# 									  ELEMENTARY FUNCTIONS                                                #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
################################################## POWER ##################################################(43)
function Power(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_pow, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end
function Power(x::Real, y::Real)
	z = BigFloat()
	ccall((:mpfr_pow, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Power(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_pow, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Ref{acb}, Int),
    z, x, y, parent(x).prec)
    return z
end
Power(x::Complex, y::Complex) = Power(CF(real(x), imag(x)), CF(real(y), imag(y)))

Power(x::arb, y::acb) = Power(CF(x, parent(x)(0)), y)
Power(x::arb, y::Real) = Power(x, RF(y))
Power(x::arb, y::Complex) = Power(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Power(x::acb, y::arb) = Power(x, CF(y, parent(y)(0)))
Power(x::acb, y::Real) = Power(x, CF(y, parent(y)(0)))
Power(x::acb, y::Complex) = Power(x, CF(real(y), imag(y)))

Power(x::Real, y::arb) = Power(RF(x), y)
Power(x::Real, y::acb) = Power(CF(x, parent(x)(0)), y)
Power(x::Real, y::Complex) = Power(Complex(x, oftype(x,0)), y)

Power(x::Complex, y::arb) = Power(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Power(x::Complex, y::acb) = Power(CF(real(x), imag(x)), y)
Power(x::Complex, y::Real) = Power(x, Complex(y, oftype(y,0)))

Power(a, b, c, xs...) = ArgListOpt(Power, Power(Power(a,b),c), xs...)
###########################################################################################################
############################################### SQUARE ROOT ###############################################
function Sqrt(x::arb)
    z = parent(x)()
    ccall((:arb_sqrt, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Sqrt(x::Real)
    z = BigFloat()
    ccall((:mpfr_sqrt, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
    z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function Sqrt(x::acb)
    z = parent(x)()
    ccall((:acb_sqrt, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Sqrt(x::Complex) = Sqrt(CF(real(x), imag(x)))
###########################################################################################################
########################################### INVERSE SQUARE ROOT ###########################################
################# Return 1/Sqrt(x)
function RSqrt(x::arb)
    z = parent(x)()
    ccall((:arb_rsqrt, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function RSqrt(x::Real)
    z = BigFloat()
    ccall((:mpfr_rec_sqrt, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
    z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function RSqrt(x::acb)
    z = parent(x)()
    ccall((:acb_rsqrt, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

RSqrt(x::Complex) = RSqrt(CF(real(x), imag(x)))
###########################################################################################################
################################################ NTH ROOT #################################################(49)
function Surd(x::arb, k::UInt)
    z = parent(x)()
    ccall((:arb_root, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, UInt, Int), z, x, k, parent(x).prec)
    return z
end
Surd(x::arb, k::Int) = Surd(x, UInt(k))

function Surd(x::Real, y::UInt)
    z = BigFloat()
    ccall((:mpfr_root, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, UInt, Int32),
    z, x, y, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end
Surd(x::Real, k::Int) = Surd(x, UInt(k))

function Surd(x::acb, k::UInt)
    z = parent(x)()
    ccall((:acb_root_ui, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, UInt, Int), z, x, k, parent(x).prec)
    return z
end
Surd(x::acb, k::Int) = Surd(x, UInt(k))

Surd(x::Complex, k::UInt) = Surd(CF(real(x), imag(x)), UInt(k))
Surd(x::Complex, k::Int) = Surd(x, UInt(k))
###########################################################################################################
################################################## HYPOT ##################################################
function Hypot(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_hypot, Nemo.libarb), Nothing,(Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end

function Hypot(x::Real, y::Real)
    z = BigFloat()
    ccall((:mpfr_hypot, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
    z, x, y, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end
################# Return Log(Sqrt(x^2 + y^3)).
function LogHypot(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_log_hypot, Nemo.libarb), Nothing,(Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end
LogHypot(x::Real, y::Real) = LogHypot(RF(x), RF(y))
###########################################################################################################
############################################### EXPONENTIAL ###############################################
function Exp(x::arb)
    z = parent(x)()
    ccall((:arb_exp, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Exp(x::Real)
	z = BigFloat()
	ccall((:mpfr_exp, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Exp(x::acb)
    z = parent(x)()
    ccall((:acb_exp, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Exp(x::Complex) = Exp(CF(real(x), imag(x)))
################# Return Exp(x) - 1, evaluated accurately for small x.
function Expm1(x::arb)
    z = parent(x)()
    ccall((:arb_expm1, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
 end

 function Expm1(x::Real)
	z = BigFloat()
	ccall((:mpfr_expm1, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Expm1(x::acb)
    z = parent(x)()
    ccall((:acb_expm1, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
 end
 Expm1(x::Complex) = Expm1(CF(real(x), imag(x)))
###########################################################################################################
################################################ LOGARİTHM ################################################
function Ln(x::arb)
    z = parent(x)()
    ccall((:arb_log, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Ln(x::Real)
	z = BigFloat()
	ccall((:mpfr_log, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Ln(x::acb)
    z = parent(x)()
    ccall((:acb_log, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Ln(x::Complex) = Ln(CF(real(x), imag(x)))
################# Return Ln(1+x), evaluated accurately for small x.
function Lnp1(x::arb)
    z = parent(x)()
    ccall((:arb_log1p, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Lnp1(x::Real)
	z = BigFloat()
	ccall((:mpfr_log1p, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Lnp1(x::acb)
    z = parent(x)()
    ccall((:acb_log1p, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end
Lnp1(x::Complex) = Lnp1(CF(real(x), imag(x)))
################# Log_{10}
function Log10(r::ArbField)
    z = r()
    ccall((:arb_const_log10, Nemo.libarb), Nothing, (Ref{arb}, Int), z, prec(r))
    return z
end

Log(x::arb) = Divide(Ln(x), Log10(ArbField(sprec)))

function Log(x::Real)
	z = BigFloat()
	ccall((:mpfr_log10, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

Log(x::acb) = Divide(Ln(x), Log10(ArbField(sprec)))
Log(x::Complex) = Divide(Ln(x), Log10(ArbField(sprec)))
################# Log_{b}(x), in Mathematica Log(b,x)
function Log(x::arb, b::UInt)
    z = parent(x)()
    ccall((:arb_log_base_ui, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, UInt, Int),
    z, x, b, parent(x).prec)
    return z
end
Log(x::arb, b::Int) = Log(x, UInt(b))
Log(x::arb, b::arb) = Log(x, Int(convert(Float64, b)))
Log(x::Real, b::Int) = Log(RF(x), UInt(b))
Log(x::Real, b::Real) = Log(RF(x), UInt(b))
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                     TRIGONOMETRIC FUNCTIONS                                             #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
function Pi(r::ArbField)
    z = r()
    ccall((:arb_const_pi, Nemo.libarb), Nothing, (Ref{arb}, Int), z, prec(r))
    return z
end

function Pi(r::AcbField)
    z = r()
    ccall((:acb_const_pi, Nemo.libarb), Nothing, (Ref{acb}, Int), z, prec(r))
    return z
end
################# Sqrt(Pi)
function SqrtPi(r::ArbField)
    z = r()
    ccall((:arb_const_sqrt_pi, Nemo.libarb), Nothing, (Ref{arb}, Int), z, prec(r))
    return z
end
################# Log(Sqrt(2Pi))
function LogSqrt2Pi(r::ArbField)
    z = r()
    ccall((:arb_const_log_sqrt2pi, Nemo.libarb), Nothing, (Ref{arb}, Int), z, prec(r))
    return z
end
###########################################################################################################
################################################### SINE ##################################################(44)
function Sin(x::arb)
    z = parent(x)()
    ccall((:arb_sin, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Sin(x::Real)
	z = BigFloat()
	ccall((:mpfr_sin, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Sin(x::acb)
    z = parent(x)()
    ccall((:acb_sin, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Sin(x::Complex) = Sin(CF(real(x), imag(x)))
################# Sin(Pi*x)
function SinPi(x::arb)
    z = parent(x)()
    ccall((:arb_sin_pi, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end
SinPi(x::Real) = SinPi(RF(x))

function SinPi(x::acb)
    z = parent(x)()
    ccall((:acb_sin_pi, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end
SinPi(x::Complex) = SinPi(CF(real(x), imag(x)))
###########################################################################################################
################################################## COSINE #################################################
function Cos(x::arb)
    z = parent(x)()
    ccall((:arb_cos, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
 end

 function Cos(x::Real)
	z = BigFloat()
    ccall((:mpfr_cos, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function Cos(x::acb)
    z = parent(x)()
    ccall((:acb_cos, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Cos(x::Complex) = Cos(CF(real(x), imag(x)))
################# Cos(Pi*x)
function CosPi(x::arb)
    z = parent(x)()
    ccall((:arb_cos_pi, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end
CosPi(x::Real) = CosPi(RF(x))

function CosPi(x::acb)
    z = parent(x)()
    ccall((:acb_cos_pi, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end
CosPi(x::Complex) = CosPi(CF(real(x), imag(x)))
###########################################################################################################
################################################# TANGENT #################################################
function Tan(x::arb)
    z = parent(x)()
    ccall((:arb_tan, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Tan(x::Real)
	z = BigFloat()
	ccall((:mpfr_tan, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Tan(x::acb)
    z = parent(x)()
    ccall((:acb_tan, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Tan(x::Complex) = Tan(CF(real(x), imag(x)))
###########################################################################################################
################################################ COTANGENT ################################################
function Cot(x::arb)
    z = parent(x)()
    ccall((:arb_cot, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Cot(x::Real)
	z = BigFloat()
	ccall((:mpfr_cot, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Cot(x::acb)
    z = parent(x)()
    ccall((:acb_cot, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Cot(x::Complex) = Cot(CF(real(x), imag(x)))
###########################################################################################################
################################################# COSECAND ################################################
function Csc(x::arb)
	z = parent(x)()
	ccall((:arb_csc, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
	return z
end

function Csc(x::Real)
	z = BigFloat()
	ccall((:mpfr_csc, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Csc(x::acb)
	z = parent(x)()
	ccall((:acb_csc, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
	return z
end

Csc(x::Complex) = Csc(CF(real(x), imag(x)))
###########################################################################################################
################################################## SECAND #################################################
function Sec(x::arb)
	z = parent(x)()
	ccall((:arb_sec, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
	return z
end

function Sec(x::Real)
	z = BigFloat()
	ccall((:mpfr_cot, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Sec(x::acb)
	z = parent(x)()
	ccall((:acb_sec, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
	return z
end

Sec(x::Complex) = Sec(CF(real(x), imag(x)))
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                             INVERSE TRIGONOMETRIC FUNCTIONS                                             #
###########################################################################################################
###########################################################################################################
###########################################################################################################
################################################# ARCSINE #################################################(42)
function ArcSin(x::arb)
	z = parent(x)()
	ccall((:arb_asin, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
	return z
end

function ArcSin(x::Real)
	z = BigFloat()
	ccall((:mpfr_asin, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function ArcSin(x::acb)
	z = parent(x)()
	ccall((:acb_asin, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
	return z
end

ArcSin(x::Complex) = ArcSin(CF(real(x), imag(x)))
###########################################################################################################
################################################ ARCCOSINE ################################################
function ArcCos(x::arb)
	z = parent(x)()
	ccall((:arb_acos, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
	return z
end

function ArcCos(x::Real)
	z = BigFloat()
	ccall((:mpfr_acos, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function ArcCos(x::acb)
	z = parent(x)()
	ccall((:acb_acos, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
	return z
end

ArcCos(x::Complex) = ArcCos(CF(real(x), imag(x)))
###########################################################################################################
################################################ ARCTANGENT ###############################################
function ArcTan(x::arb)
    z = parent(x)()
    ccall((:arb_atan, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function ArcTan(x::Real)
	z = BigFloat()
	ccall((:mpfr_atan, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function ArcTan(x::acb)
    z = parent(x)()
    ccall((:acb_atan, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

ArcTan(x::Complex) = ArcTan(CF(real(x), imag(x)))
################# ArcTan(y,x) = ArcTan(y/x)
function ArcTan(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_atan2, Nemo.libarb), Nothing,
                (Ref{arb}, Ref{arb}, Ref{arb}, Int), z, x, y, parent(x).prec)
    return z
end

function ArcTan(y::Real, x::Real)
    z = BigFloat()
    ccall((:mpfr_atan2, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int),
    z, y, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end
ArcTan(x::acb, y::acb) = imag(x) == 0 && imag(y) == 0 && ArcTan(real(x), real(y))
ArcTan(x::Complex, y::Complex) = imag(x) == 0 && imag(y) == 0 && ArcTan(real(x), real(y))

ArcTan(x::arb, y::acb) =  ArcTan(CF(x, parent(x)(0)), y)
ArcTan(x::arb, y::Real) = ArcTan(x, RF(y))
ArcTan(x::arb, y::Complex) = ArcTan(CF(x, parent(x)(0)), CF(real(y), imag(y)))

ArcTan(x::acb, y::arb) =  ArcTan(x, CF(y, parent(y)(0)))
ArcTan(x::acb, y::Real) = ArcTan(x, CF(y, parent(y)(0)))
ArcTan(x::acb, y::Complex) = ArcTan(x, CF(real(y), imag(y)))

ArcTan(x::Real, y::arb) = ArcTan(RF(x), y)
ArcTan(x::Real, y::acb) = ArcTan(CF(x, parent(x)(0)), y)
ArcTan(x::Real, y::Complex) = ArcTan(Complex(x, oftype(x,0)), y)

ArcTan(x::Complex, y::arb) = ArcTan(CF(real(x), imag(x)), CF(y, parent(y)(0)))
ArcTan(x::Complex, y::acb) = ArcTan(CF(real(x), imag(x)), y)
ArcTan(x::Complex, y::Real) = ArcTan(x, Complex(y, oftype(y,0)))

ArcTan(a, b, c, xs...) = ArgListOpt(ArcTan, ArcTan(ArcTan(a,b),c), xs...)
###########################################################################################################
############################################### ARCCOTANGENT ##############################################
ArcCot(x) = ArcTan(Divide(One(x), x))
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                     HYPERBOLIC FUNCTIONS                                                #
###########################################################################################################
###########################################################################################################
###########################################################################################################
############################################# HYPERBOLIC SINE #############################################(38)
function Sinh(x::arb)
    z = parent(x)()
    ccall((:arb_sinh, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Sinh(x::Real)
	z = BigFloat()
	ccall((:mpfr_sinh, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
    z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Sinh(x::acb)
    z = parent(x)()
    ccall((:acb_sinh, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Sinh(x::Complex) = Sinh(CF(real(x), imag(x)))
###########################################################################################################
############################################ HYPERBOLIC COSINE ############################################
function Cosh(x::arb)
    z = parent(x)()
    ccall((:arb_cosh, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Cosh(x::Real)
	z = BigFloat()
	ccall((:mpfr_cosh, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Cosh(x::acb)
    z = parent(x)()
    ccall((:acb_cosh, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Cosh(x::Complex) = Cosh(CF(real(x), imag(x)))
###########################################################################################################
############################################ HYPERBOLIC TANGENT ###########################################
function Tanh(x::arb)
    z = parent(x)()
    ccall((:arb_tanh, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Tanh(x::Real)
	z = BigFloat()
	ccall((:mpfr_tanh, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Tanh(x::acb)
    z = parent(x)()
    ccall((:acb_tanh, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Tanh(x::Complex) = Tanh(CF(real(x), imag(x)))
###########################################################################################################
########################################### HYPERBOLIC COTANGENT ##########################################
function Coth(x::arb)
    z = parent(x)()
    ccall((:arb_coth, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Coth(x::Real)
	z = BigFloat()
	ccall((:mpfr_coth, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function Coth(x::acb)
    z = parent(x)()
    ccall((:acb_coth, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Coth(x::Complex) = Coth(CF(real(x), imag(x)))
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                                  INVERSE HYPERBOLIC FUNCTIONS						      		          #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
############################################ HYPERBOLIC ARCSINE ###########################################(37)
function ArcSinh(x::arb)
	z = parent(x)()
	ccall((:arb_asinh, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
	return z
end

function ArcSinh(x::Real)
	z = BigFloat()
	ccall((:mpfr_asinh, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function ArcSinh(x::acb)
	z = parent(x)()
	ccall((:acb_asinh, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
	return z
end

ArcSinh(x::Complex) = ArcSinh(CF(real(x), imag(x)))
###########################################################################################################
########################################### HYPERBOLIC ARCCOSINE ##########################################
function ArcCosh(x::arb)
	z = parent(x)()
	ccall((:arb_acosh, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
	return z
end

function ArcCosh(x::Real)
	z = BigFloat()
	ccall((:mpfr_acosh, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function ArcCosh(x::acb)
	z = parent(x)()
	ccall((:acb_acosh, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
	return z
end

ArcCosh(x::Complex) = ArcCosh(CF(real(x), imag(x)))
###########################################################################################################
########################################## HYPERBOLIC ARCTANGENT ##########################################
function ArcTanh(x::arb)
	z = parent(x)()
	ccall((:arb_atanh, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
	return z
end

function ArcTanh(x::Real)
	z = BigFloat()
	ccall(
	(:mpfr_atanh, :libmpfr),Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function ArcTanh(x::acb)
	z = parent(x)()
	ccall((:acb_atanh, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
	return z
end

ArcTanh(x::Complex) = ArcTanh(CF(real(x), imag(x)))
###########################################################################################################
######################################### HYPERBOLIC ARCCOTANGENT #########################################
ArcCoth(x) = ArcTanh(Divide(One(x), x))
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
#                              			GAMMA BETA ERF								      	              #
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
###########################################################################################################
################################################ FACTORİALS ###############################################
function Factorial(n::UInt, r::ArbField)
    z = r()
    ccall((:arb_fac_ui, Nemo.libarb), Nothing, (Ref{arb}, UInt, Int), z, n, r.prec)
    return z
end
Factorial(n::Int, r::ArbField) = n < 0 ? Factorial(r(n)) : Factorial(UInt(n), r)

Factorial(x::arb) = Gamma(Plus(x, One(x)))
Factorial(x::Real) = Factorial(RF(x))
Factorial(x::acb) = Gamma(Plus(x, One(x)))
Factorial(x::Complex) = Factorial(CF(real(x), imag(x)))
###########################################################################################################
################################################## GAMMA ##################################################
function Gamma(x::arb)
    z = parent(x)()
    ccall((:arb_gamma, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Gamma(x::Real)
    z = BigFloat()
    ccall((:mpfr_gamma, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function Gamma(x::acb)
    z = parent(x)()
    ccall((:acb_gamma, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

Gamma(x::Complex) = Gamma(CF(real(x), imag(x)))
################# Log(Gamma(x))

###########################################################################################################
######################################### UPPER INCOMPLETE GAMMA ##########################################
function UGamma(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_gamma_upper, Nemo.libarb), Nothing,(Ref{arb}, Ref{arb}, Ref{arb}, Int, Int),
    z, x, y, 0, parent(x).prec)
    return z
end

function UGamma(x::Real, y::Real)
	z = BigFloat()
	ccall((:mpfr_gamma_inc, :libmpfr),Int32,(Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, y, Base.MPFR.ROUNDING_MODE[])
	return RF(z)
end

function UGamma(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_hypgeom_gamma_upper, Nemo.libarb), Nothing,(Ref{acb}, Ref{acb}, Ref{acb}, Int, Int),
    z, x, y, 0, parent(x).prec)
    return z
end
UGamma(x::Complex, y::Complex) = UGamma(CF(real(x), imag(x)), CF(real(x), imag(x)))

UGamma(x::arb, y::acb) =  UGamma(CF(x, parent(x)(0)), y)
UGamma(x::arb, y::Real) = UGamma(x,RF(y))
UGamma(x::arb, y::Complex) = UGamma(CF(x, parent(x)(0)), CF(real(y), imag(y)))

UGamma(x::acb, y::arb) =  UGamma(x, CF(y, parent(y)(0)))
UGamma(x::acb, y::Real) = UGamma(x, CF(y, parent(y)(0)))
UGamma(x::acb, y::Complex) = UGamma(x, CF(real(y), imag(y)))

UGamma(x::Real, y::arb) = UGamma(RF(x),y)
UGamma(x::Real, y::acb) = UGamma(CF(x, parent(x)(0)), y)
UGamma(x::Real, y::Complex) = UGamma(CF(x, parent(x)(0)), CF(real(y), imag(y)))

UGamma(x::Complex, y::arb) = UGamma(CF(real(x), imag(x)), CF(y, parent(y)(0)))
UGamma(x::Complex, y::acb) = UGamma(CF(real(x), imag(x)), y)
UGamma(x::Complex, y::Real) = UGamma(CF(real(x), imag(x)), CF(y, parent(y)(0)))

UGamma(a, b, c, xs...) = ArgListOpt(UGamma, UGamma(UGamma(a,b),c), xs...)
###########################################################################################################
######################################### LOWER INCOMPLETE GAMMA ##########################################
function LGamma(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_gamma_lower, Nemo.libarb), Nothing,(Ref{arb}, Ref{arb}, Ref{arb}, Int, Int),
    z, x, y, 0, parent(x).prec)
    return z
end
LGamma(x::Real, y::Real) = LGamma(RF(x), RF(y))

function LGamma(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_hypgeom_gamma_lower, Nemo.libarb), Nothing,(Ref{acb}, Ref{acb}, Ref{acb}, Int, Int),
    z, x, y, 0, parent(x).prec)
    return z
end
LGamma(x::Complex, y::Complex) = LGamma(CF(real(x), imag(x)), CF(real(x), imag(x)))

LGamma(x::arb, y::acb) =  LGamma(CF(x, parent(x)(0)), y)
LGamma(x::arb, y::Real) = LGamma(x,RF(y))
LGamma(x::arb, y::Complex) = LGamma(CF(x, parent(x)(0)), CF(real(y), imag(y)))

LGamma(x::acb, y::arb) =  LGamma(x, CF(y, parent(y)(0)))
LGamma(x::acb, y::Real) = LGamma(x, CF(y, parent(y)(0)))
LGamma(x::acb, y::Complex) = LGamma(x, CF(real(y), imag(y)))

LGamma(x::Real, y::arb) = LGamma(RF(x),y)
LGamma(x::Real, y::acb) = LGamma(CF(x, parent(x)(0)), y)
LGamma(x::Real, y::Complex) = LGamma(CF(x, parent(x)(0)), CF(real(y), imag(y)))

LGamma(x::Complex, y::arb) = LGamma(CF(real(x), imag(x)), CF(y, parent(y)(0)))
LGamma(x::Complex, y::acb) = LGamma(CF(real(x), imag(x)), y)
LGamma(x::Complex, y::Real) = LGamma(CF(real(x), imag(x)), CF(y, parent(y)(0)))

LGamma(a, b, c, xs...) = ArgListOpt(LGamma, LGamma(LGamma(a,b),c), xs...)
###########################################################################################################
############################################ REGULARIZED GAMMA ############################################
PGamma(x, y) = Divide(LGamma(x, y), Gamma(x))
PGamma(a, b, c, xs...) = ArgListOpt(PGamma, PGamma(PGamma(a,b),c), xs...)
#################
QGamma(x, y) = Divide(UGamma(x, y), Gamma(x))
QGamma(a, b, c, xs...) = ArgListOpt(QGamma, QGamma(QGamma(a,b),c), xs...)
###########################################################################################################
################################################ POCHHAMMER ###############################################
function Pochhammer(x::arb, n::UInt)
    z = parent(x)()
    ccall((:arb_rising_ui, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, UInt, Int),
    z, x, n, parent(x).prec)
    return z
end

Pochhammer(x::arb, n::Int) = NonnegativeQ(n) && Pochhammer(x, UInt(n))
Pochhammer(x::arb, n::arb) = Pochhammer(x, Int(convert(Float64, n)))
Pochhammer(x::Real, n::Int) = NonnegativeQ(n) && Pochhammer(RF(x), UInt(n))
Pochhammer(x::Real, n::Real) = Pochhammer(RF(x), RF(n))

function Pochhammer(x::acb, n::UInt)
    z = parent(x)()
    ccall((:acb_rising_ui, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, UInt, Int),
    z, x, n, parent(x).prec)
    return z
end

Pochhammer(x::acb, n::Int) = NonnegativeQ(n) && Pochhammer(x, UInt(n))
Pochhammer(x::acb, n::acb) = Pochhammer(x, Int(convert(ComplexF64, n)))
Pochhammer(x::Complex, n::Int) = NonnegativeQ(n) && Pochhammer(CF(real(x), imag(x)), UInt(n))
Pochhammer(x::Complex, n::Complex) = Pochhammer(CF(real(x), imag(x)), CF(real(n), imag(n)))

Pochhammer(x::arb, y::acb) =  Pochhammer(CF(x, parent(x)(0)), y)
Pochhammer(x::arb, y::Real) = Pochhammer(x, RF(y))
Pochhammer(x::arb, y::Complex) = Pochhammer(CF(x, parent(x)(0)), CF(real(y), imag(y)))

Pochhammer(x::acb, y::arb) =  Pochhammer(x, CF(y, parent(y)(0)))
Pochhammer(x::acb, y::Real) = Pochhammer(x, CF(y, parent(y)(0)))
Pochhammer(x::acb, y::Complex) = Pochhammer(x, CF(real(y), imag(y)))

Pochhammer(x::Real, y::arb) = Pochhammer(RF(x), y)
Pochhammer(x::Real, y::acb) = Pochhammer(CF(x, parent(x)(0)), y)
Pochhammer(x::Real, y::Complex) = Pochhammer(Complex(x, oftype(x,0)), y)

Pochhammer(x::Complex, y::arb) = Pochhammer(CF(real(x), imag(x)), CF(y, parent(y)(0)))
Pochhammer(x::Complex, y::acb) = Pochhammer(CF(real(x), imag(x)), y)
Pochhammer(x::Complex, y::Real) = Pochhammer(x, Complex(y, oftype(y,0)))
###########################################################################################################
################################################## BETA ###################################################(44)
################# 0 < x <= 1 ! while x = 1, Beta(a,b,x) = Beta(a,b) in mathematica Beta(x,a,b)
function Beta(a::arb, b::arb, x::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_beta_lower, Nemo.libarb), Nothing,(Ref{arb}, Ref{arb}, Ref{arb}, Ref{arb}, Int, Int),
    z, a, b, x, 0, parent(x).prec)
    return z
end
Beta(a::Real, b::Real, x::Real) = Beta(RF(a), RF(b), RF(x))

function Beta(a::acb, b::acb, x::acb)
    z = parent(x)()
    ccall((:arb_hypgeom_beta_lower, Nemo.libarb), Nothing,(Ref{acb}, Ref{acb}, Ref{acb}, Ref{acb}, Int, Int),
    z, a, b, x, 0, parent(x).prec)
    return z
end
Beta(a::Complex, b::Complex, x::Complex) = Beta(
    CF(real(a), imag(a)), CF(real(b), imag(b)), CF(real(x), imag(x))
)

Beta(a, b) = Beta(a, b, parent(a)(1))
###########################################################################################################
################################################ BINOMIAL #################################################
function Binomial(x::arb, n::UInt)
    z = parent(x)()
    ccall((:arb_bin_ui, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, UInt, Int),
    z, x, n, parent(x).prec)
    return z
end

Binomial(x::arb, n::Int) = n < 0 ? 0 : Binomial(x, UInt(n))
Binomial(x::arb, n::arb) = n < 0 ? 0 : Binomial(x, Int(convert(Float64, n)))
Binomial(x::Real, n::Int) = n < 0 ? 0 : Binomial(RF(x), n)
Binomial(x::Real, n::Real) = n < 0 ? 0 : Binomial(RF(x), Int(n))
###########################################################################################################
########################################## GENERALİZED BINOMIAL ###########################################
################# F[n,n',m,N]
function GBinomial(z::Int, x::Int, y::Int)
    llim = Int(((z - x) + abs(z - x)) / 2)
    ulim = min(z, y)
    r = zeros(RF, ulim - llim + 1)
    @threads for s in llim : ulim
        r[s - llim + 1] = Times(Power(-1, s), Binomial(x, z - s), Binomial(y, s))
    end
    return sum(r)
end
GBinomial(x::Real, y::Real, z::Real) = GBinomial(Int(x), Int(y), Int(z))
GBinomial(x::arb, y::arb, z::arb) = GBinomial(NO(x), NO(y), NO(z))
function GBinomial(x::Real, y::Real, z::Int, lim::Int)
		r = zeros(RF, lim + 1)
        @threads for s in 0 : lim
            if s <= z
			    sign = Power(-1, s)
                r[s + 1] = Times(sign, Binomial(x, z - s), Binomial(y, s))
            else
                break
            end
        end
        return sum(r)
end
GBinomial(x::Real, y::Real, z::Real, lim::Real) = GBinomial(x, y, Int(z), Int(lim))
GBinomial(x::arb, y::arb, z::arb, lim::arb) = GBinomial(NO(x), NO(y), NO(z), NO(lim))
###########################################################################################################
########################################## EXPONENTIAL INTEGRALS ##########################################
################# E1(x) = -Ei(-x)
function ExpIntegralEi(x::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_ei, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function ExpIntegralEi(x::Real)
    z = BigFloat()
    ccall((:mpfr_eint, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function ExpIntegralEi(x::acb)
    z = parent(x)()
    ccall((:arb_hypgeom_ei, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end

ExpIntegralEi(x::Complex) = ExpIntegralEi(CF(real(x), imag(x)))
################# E_{x}(y)
function ExpIntegralE(x::arb, y::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_expint, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Ref{arb}, Int),
    z, x, y, parent(x).prec)
    return z
end
ExpIntegralE(x::Real, y::Real) = ExpIntegralE(RF(x), RF(y))

function ExpIntegralE(x::acb, y::acb)
    z = parent(x)()
    ccall((:acb_hypgeom_expint, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Ref{acb}, Int),
    z, x, y, parent(x).prec)
    return z
end
ExpIntegralE(x::Complex, y::Complex) = ExpIntegralE(CF(real(x), imag(x)), CF(real(y), imag(y)))

ExpIntegralE(x::arb, y::acb) =  ExpIntegralE(CF(x, parent(x)(0)), y)
ExpIntegralE(x::arb, y::Real) = ExpIntegralE(x, RF(y))
ExpIntegralE(x::arb, y::Complex) = ExpIntegralE(CF(x, parent(x)(0)), CF(real(y), imag(y)))

ExpIntegralE(x::acb, y::arb) =  ExpIntegralE(x, CF(y, parent(y)(0)))
ExpIntegralE(x::acb, y::Real) = ExpIntegralE(x, CF(y, parent(y)(0)))
ExpIntegralE(x::acb, y::Complex) = ExpIntegralE(x, CF(real(y), imag(y)))

ExpIntegralE(x::Real, y::arb) = ExpIntegralE(RF(x), y)
ExpIntegralE(x::Real, y::acb) = ExpIntegralE(CF(x, parent(x)(0)), y)
ExpIntegralE(x::Real, y::Complex) = ExpIntegralE(Complex(x, oftype(x,0)), y)

ExpIntegralE(x::Complex, y::arb) = ExpIntegralE(CF(real(x), imag(x)), CF(y, parent(y)(0)))
ExpIntegralE(x::Complex, y::acb) = ExpIntegralE(CF(real(x), imag(x)), y)
ExpIntegralE(x::Complex, y::Real) = ExpIntegralE(x, Complex(y, oftype(y,0)))
###########################################################################################################
########################################## PROBABILITY INTEGRALS ##########################################
function Erf(x::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_erf, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Erf(x::Real)
    z = BigFloat()
    ccall((:mpfr_erf, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function Erf(x::acb)
    z = parent(x)()
    ccall((:acb_hypgeom_erf, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end
Erf(x::Complex) = Erf(CF(real(x), imag(x)))
################# Erfc(x) gives the complementary error function 1 - Erf(x).
################# This function avoids catastrophic cancellation for large positive x.
function Erfc(x::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_erfc, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end

function Erfc(x::Real)
    z = BigFloat()
    ccall((:mpfr_erfc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32),
	z, x, Base.MPFR.ROUNDING_MODE[])
    return RF(z)
end

function Erfc(x::acb)
    z = parent(x)()
    ccall((:acb_hypgeom_erfc, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end
Erfc(x::Complex) = Erfc(CF(real(x), imag(x)))
################# Erfi(x) gives the imaginary error function -iErf(ix).
function Erfi(x::arb)
    z = parent(x)()
    ccall((:arb_hypgeom_erfi, Nemo.libarb), Nothing, (Ref{arb}, Ref{arb}, Int), z, x, parent(x).prec)
    return z
end
Erfi(x::Real) = Erfi(RF(x))

function Erfi(x::acb)
    z = parent(x)()
    ccall((:acb_hypgeom_erfi, Nemo.libarb), Nothing, (Ref{acb}, Ref{acb}, Int), z, x, parent(x).prec)
    return z
end
Erfi(x::Complex) = Erfi(CF(real(x), imag(x)))
###########################################################################################################
################################################### END ###################################################


text:
using LightGraphs

forward_directions = CartesianIndex.([(1, 0), (0, 1)])
directions = [forward_directions; -forward_directions]

"""Find a label adjacent to a corridor at coord, return nothing if there is none"""
function find_adjacent_label(plan, coord)
    for dir in directions
        if plan[coord + dir] ∈ 'A':'Z'
            label = string(plan[coord + dir], plan[coord + 2dir])
            if dir ∉ forward_directions label = reverse(label) end
            return label
        end
    end
    return nothing
end

"""Build a (non recursive) graph from the plan ASCII art matrix passed in"""
function parse_plan(plan)
    # Build graph of connected corridors and portals
    nodes = [coord for coord in CartesianIndices(plan) if plan[coord] == '.']
    nodemap = Dict(coord => i for (i, coord) in enumerate(nodes))
    graph = SimpleGraph(length(nodes))
    labels = Dict(coord => find_adjacent_label(plan, coord) for coord in nodes)
    # Add edges between corridors
    for (i, coord) in enumerate(nodes)
        for dir in forward_directions
            if (coord + dir) ∈ keys(nodemap) add_edge!(graph, i, nodemap[coord + dir]) end
        end
    end
    # Add edges between portals
    labelmap = Dict()
    portals_first = Dict(labels[coord] => coord for coord in nodes if !isnothing(labels[coord]))
    portals_second = Dict(labels[coord] => coord for coord in reverse(nodes) if !isnothing(labels[coord]))
    for (label, coord) in portals_first
        second_coord = portals_second[label]
        if second_coord != coord
            add_edge!(graph, nodemap[coord], nodemap[second_coord])
            labelmap[label] = (coord, second_coord)
        else
            labelmap[label] = coord
        end
    end
    return graph, nodes, nodemap, labelmap
end

"""Build a recursive graph from the plan ASCII art matrix passed in, with levels layers of recursion"""
function parse_plan_recursive(plan, levels)
    # Build graph of connected corridors and portals
    nodes = [coord for coord in CartesianIndices(plan) if plan[coord] == '.']
    nodemap = Dict(coord => i for (i, coord) in enumerate(nodes))
    labels = Dict(coord => find_adjacent_label(plan, coord) for coord in nodes)
    npl = length(nodes)
    graph = SimpleGraph(npl * levels)
    # Add edges between corridors
    for level in 0:(levels-1)
        for (i, coord) in enumerate(nodes)
            for dir in forward_directions
                if (coord + dir) ∈ keys(nodemap) add_edge!(graph, npl * level + i, npl * level + nodemap[coord + dir]) end
            end
        end
    end
    # Add edges between portals from level n to level n+1
    outer_x = extrema(map(x -> x[1], nodes))
    outer_y = extrema(map(x -> x[2], nodes))
    outer = Dict(label => coord for (coord, label) in labels if coord[1] ∈ outer_x || coord[2] ∈ outer_y && !isnothing(label))
    inner = Dict(label => coord for (coord, label) in labels if coord[1] ∉ outer_x && coord[2] ∉ outer_y && !isnothing(label))
    for level in 0:(levels-2)
        for label in keys(inner)
            add_edge!(graph, npl * level + nodemap[inner[label]], npl * (level + 1) + nodemap[outer[label]])
        end
    end
    return graph, nodes, nodemap, outer
end

# Part 1: shortest path through maze
plan = hcat(open(f -> collect.(readlines(f)), "day20_input.txt")...)
graph, nodes, nodemap, labelmap = parse_plan(plan)
shortest_path = yen_k_shortest_paths(graph, nodemap[labelmap["AA"]], nodemap[labelmap["ZZ"]]).dists[1]
println("Part 1: Shortest path: $(shortest_path) steps")

# Part 2: build recursive maze, although just pretend and use a fixed number of levels (bit brute forcey but whatevs)
plan = hcat(open(f -> collect.(readlines(f)), "day20_input.txt")...)
graph, nodes, nodemap, labelmap = parse_plan_recursive(plan, 40)
shortest_path = yen_k_shortest_paths(graph, nodemap[labelmap["AA"]], nodemap[labelmap["ZZ"]]).dists[1]
println("Part 2: Shortest path: $(shortest_path) steps")

ext: jl

max_stars_repo_name: ericgreveson/adventofcode

max_issues_repo_name: ericgreveson/adventofcode

max_forks_repo_name: ericgreveson/adventofcode


text:
"Time series model with a parameter for the dimension of the timeseries model."
abstract type TimeSeriesModel{D,T} end

"""
Sub type to specify the need for additional routines approximating the acv from the sdf.
Such routines require additional storage and have different default functions in some cases.
"""
abstract type UnknownAcvTimeSeriesModel{D,T} <: TimeSeriesModel{D,T} end

"""
    AdditiveTimeSeriesModel(model1, model2)

Constructs an additive model from two timeseries models.
"""
struct AdditiveTimeSeriesModel{M₁,M₂,D,T} <: TimeSeriesModel{D,T}
    model1::M₁
    model2::M₂
    function AdditiveTimeSeriesModel(
        model1::M₁,
        model2::M₂,
    ) where {M₁<:TimeSeriesModel{D,T},M₂<:TimeSeriesModel{D,T}} where {D,T}
        new{M₁,M₂,D,T}(model1, model2)
    end
    function AdditiveTimeSeriesModel{M₁,M₂,D,T}(θ) where {M₁<:TimeSeriesModel{D,T},M₂<:TimeSeriesModel{D,T}} where {D,T}
        @views new{M₁,M₂,D,T}(M₁(θ[1:npars(M₁)]), M₂(θ[npars(M₁)+1:end]))
    end
end

"""
    M₁::Type{<:TimeSeriesModel{D,T}} + M₂::Type{<:TimeSeriesModel{D,T}} -> Type{AdditiveTimeSeriesModel{M₁,M₂,D,T}}
"""
Base.:+(M₁::Type{<:TimeSeriesModel{D,T}}, M₂::Type{<:TimeSeriesModel{D,T}}) where {D,T} =
    AdditiveTimeSeriesModel{M₁,M₂,D,T}

"""
    M₁::Type{<:TimeSeriesModel{D,T}} + M₂::Type{<:TimeSeriesModel{D,T}} -> AdditiveTimeSeriesModel{M₁,M₂,D,T}
"""
Base.:+(model1::TimeSeriesModel, model2::TimeSeriesModel) = AdditiveTimeSeriesModel(model1,model2)

# Broadcasting support for models
Base.broadcastable(model::TimeSeriesModel) = Ref(model)

"""
    ndims(::TimeSeriesModel) -> Integer
    ndims(::Type{<:TimeSeriesModel}) -> Integer

Return the dimension of a timeseries model.
"""
ndims(::TimeSeriesModel{D,T}) where {D,T} = D
ndims(::Type{<:TimeSeriesModel{D,T}}) where {D,T} = D

"""
    nlowertriangle_dimension(::TimeSeriesModel) -> Integer
    nlowertriangle_dimension(::Type{<:TimeSeriesModel}) -> Integer

Return the number of elements in the lower triangle the spectral density matrix function of a timeseries model.
"""
nlowertriangle_dimension(::TimeSeriesModel{D,T}) where {D,T} = triangularnumber(D)
nlowertriangle_dimension(::Type{<:TimeSeriesModel{D,T}}) where {D,T} =
    triangularnumber(D)

"""
    parameternames(::TimeSeriesModel)
    parameternames(::Type{<:TimeSeriesModel})

Return the parameter names for a given timeseries model.
"""
parameternames(model::TimeSeriesModel) = parameternames(typeof(model))
parameternames(model::Type{<:TimeSeriesModel}) = fieldnames(model)[1:npars(model)]
parameternames(model::Type{<:AdditiveTimeSeriesModel{M₁,M₂,D,T}}) where {M₁<:TimeSeriesModel{D,T},M₂<:TimeSeriesModel{D,T}} where {D,T} =
    (parameternames(M₁)...,parameternames(M₂))

npars(model::TimeSeriesModel) = npars(typeof(model))
npars(
    ::Type{AdditiveTimeSeriesModel{M₁,M₂,D,T}},
) where {M₁<:TimeSeriesModel{D,T},M₂<:TimeSeriesModel{D,T}} where {D,T} = npars(M₁) + npars(M₂)

"""
    nlowertriangle_parameter(::TimeSeriesModel) -> Integer
    nlowertriangle_parameter(::Type{<:TimeSeriesModel}) -> Integer

Return the number of elements in the lower triangle of the hessian matrix for a model with given number of parameters.
"""
nlowertriangle_parameter(model::TimeSeriesModel) = nlowertriangle_parameter(typeof(model))
nlowertriangle_parameter(model::Type{<:TimeSeriesModel}) =
    StaticArrays.triangularnumber(npars(model))

"""
    indexLT(i,j,d)

Return the lower triangle index for an elements of a symmetric matrix assuming the lower triangle is encoded down the columns.
"""
indexLT(i::Int, j::Int, d::Int) =
    i >= j ? (((2d - j) * (j - 1)) ÷ 2) + i : (((2d - i) * (i - 1)) ÷ 2) + j

"
    nalias(model::TimeSeriesModel)

Returns the number of times a sdf should be alised for a given model."
nalias(::TimeSeriesModel) = 5 # default aliasing method

"
    minbins(model::TimeSeriesModel)

Returns the minimum number of bins required for a good approximation of the sdf from the acv for a given model."
minbins(::Type{<:TimeSeriesModel}) = 8192 # default min bins method

"""
    checkparameterlength(x,model::Type{<:TimeSeriesModel})

checks if the parameter vector is the correct length for the given model.
"""
@inline function checkparameterlength(x,model::Type{<:TimeSeriesModel})
    length(x) == npars(model) || throw(ArgumentError("$model model has $(npars(model)) parameters, but $(length(x)) were provided."))
    Base.require_one_based_indexing(x) || throw(ArgumentError("parameter vector should be a type using 1 indexing."))
end


text:
export AbstractTracerProvider,
    global_tracer_provider, Tracer, Span, with_span, is_recording, set_status!, end!

using Random

abstract type AbstractTracerProvider end

struct DummyTracerProvider <: AbstractTracerProvider end

const GLOBAL_TRACER_PROVIDER = Ref{AbstractTracerProvider}(DummyTracerProvider())

"""
get the global tracer provider
"""
global_tracer_provider() = GLOBAL_TRACER_PROVIDER[]

"""
set the global tracer provider to `p`
"""
global_tracer_provider(p) = GLOBAL_TRACER_PROVIDER[] = p

"""
    Tracer(;provider=global_tracer_provider(), instrumentation=InstrumentationInfo())

For instrumentation library developers, `instrumentation` must be configured clearly instead of the default value.
"""
Base.@kwdef struct Tracer{P<:AbstractTracerProvider}
    instrumentation::InstrumentationInfo = InstrumentationInfo()
    provider::P = global_tracer_provider()
end

struct Span{T<:Tracer,SC<:SpanContext}
    name::Ref{String}
    tracer::T
    span_context::SC
    parent_span_context::Union{Nothing,SpanContext}
    kind::SpanKind
    start_time::UInt
    end_time::Ref{Union{Nothing,UInt}}
    attributes::DynamicAttrs
    links::Limited{Vector{Link}}
    events::Limited{Vector{Event}}
    status::Ref{SpanStatus}
end

function Span(
    name::String,
    tracer::Tracer{DummyTracerProvider};
    context = current_context(),
    start_time = UInt(time() * 10^9),
)
    parent_span = current_span(context)
    parent_span_ctx = span_context(parent_span)
    if isnothing(parent_span_ctx)
        Span(
            Ref(name),
            tracer,
            INVALID_SPAN_CONTEXT,
            parent_span_ctx,
            SPAN_KIND_INTERNAL,
            start_time,
            Ref{Union{Nothing,UInt}}(start_time),
            DynamicAttrs(),
            Limited(Link[]),
            Limited(Event[]),
            Ref(SpanStatus(SPAN_STATUS_UNSET)),
        )
    else
        parent_span
    end
end

"""
    with_span(f, s::AbstractSpan;kw...)

Call function `f` with the current span set to `s`.

# Keyword arguments

  - `end_on_exit=true`, controls whether to call [`end!`](@ref) after `f` or not.
  - `record_exception=true`, controls whether to record the exception.
  - `set_status_on_exception=true`, decides whether to set status to [`SPAN_STATUS_ERROR`](@ref) automatically when an exception is caught.
"""
function with_span(
    f,
    s::Span;
    end_on_exit = true,
    record_exception = true,
    set_status_on_exception = true,
)
    with_context(; SPAN_KEY_IN_CONTEXT => s) do
        try
            f()
        catch ex
            if is_recording(s)
                if record_exception
                    push!(s, ex; is_rethrow_followed = true)
                end
                if set_status_on_exception
                    set_status!(s, SPAN_STATUS_ERROR, string(ex))
                end
            end
            rethrow(ex)
        finally
            if end_on_exit
                end!(s)
            end
        end
    end
end

"""
    is_recording(s::Span)

Returns `true` if this span `s` is recording information like [`Event`](@ref) operations, attribute modification using [`setindex!`](@ref), etc.
"""
is_recording(s::Span) = isnothing(s.end_time[])

"""
    (s::Span)[key] = val

Set the attributes in span `s`. Only valid when the span is not ended yet.
"""
function Base.setindex!(s::Span, val, key)
    if is_recording(s)
        s.attributes[key] = val
    else
        @warn "the span is not recording."
    end
end

Base.getindex(s::Span, key) = s.attributes[key]
Base.haskey(s::Span, key) = haskey(s.attributes, key)

function Base.push!(s::Span, event::Event)
    if is_recording(s)
        push!(s.events, event)
    else
        @warn "the span is not recording."
    end
end

function Base.push!(s::Span, link::Link)
    if is_recording(s)
        push!(s.links, link)
    else
        @warn "the span is not recording."
    end
end

"""
    set_status!(s::Span, code::SpanStatusCode, description=nothing)

Update the status of span `s` by following the original specification. `description` is only considered when the `code` is `SPAN_STATUS_ERROR`. Only valid when the span is not ended yet.
"""
function set_status!(s::Span, code::SpanStatusCode, description = nothing)
    if is_recording(s)
        if s.status[].code === SPAN_STATUS_OK
            # no further updates
        else
            if code === SPAN_STATUS_UNSET
                # ignore
            else
                s.status[] = SpanStatus(code, description)
            end
        end
    else
        @warn "the span is not recording."
    end
end

"""
    end!(s::Span, t=UInt(time()*10^9))

Set the end time of the span and trigger span processors.
"""
function end!(s::Span{Tracer{DummyTracerProvider}}, t = UInt(time() * 10^9))
    if is_recording(s)
        s.end_time[] = t
    else
        @warn "the span is not recording."
    end
end

"""
A specialized variant of `add_event!` to record exceptions. Usually used in a `try... catch...end` to capture the backtrace. If the `ex` is `rethrow`ed in the `catch...end`, `is_rethrow_followed` should be set to `true`.
"""
function Base.push!(s::Span, ex::Exception; is_rethrow_followed = false)
    msg_io = IOBuffer()
    showerror(msg_io, ex)
    msg = String(take!(msg_io))

    st_io = IOBuffer()
    showerror(st_io, CapturedException(ex, catch_backtrace()))
    st = String(take!(st_io))

    attrs = StaticAttrs((;
        Symbol("exception.type") => string(typeof(ex)),
        Symbol("exception.type") => string(typeof(ex)),
        Symbol("exception.message") => msg,
        Symbol("exception.stacktrace") => st,
        Symbol("exception.escaped") => is_rethrow_followed,
    ))

    push!(s, Event(name = "exception", attributes = attrs))
end


text:
mutable struct Network

    alloc::Bool
    ptr::Ptr{Void}

    function Network(a, ptr)
        this = new(a, ptr)
        finalizer(this, dealloc)
        this
    end
end

Network() = Network(true, ccall((:NET_new, libpfnet), Ptr{Void}, (Int,), 1))
Network(num_periods::Int) = Network(true, ccall((:NET_new, libpfnet), Ptr{Void}, (Int,), num_periods))
Network(ptr::Ptr{Void}) = Network(false, ptr)

clear_flags(net::Network) = ccall((:NET_clear_flags, libpfnet), Void, (Ptr{Void},), net.ptr)

function dealloc(net::Network)
    if net.alloc
        ccall((:NET_del, libpfnet), Void, (Ptr{Void},), net.ptr)
    end
    net.alloc = false
    net.ptr = C_NULL
end

num_bounded(net::Network) = ccall((:NET_get_num_bounded, libpfnet), Int, (Ptr{Void},), net.ptr)
num_buses(net::Network) = ccall((:NET_get_num_buses, libpfnet), Int, (Ptr{Void},), net.ptr)
num_generators_not_on_outage(net::Network) = ccall((:NET_get_num_gens_not_on_outage, libpfnet), Int, (Ptr{Void},), net.ptr)
num_slack_buses(net::Network) = ccall((:NET_get_num_slack_buses, libpfnet), Int, (Ptr{Void},), net.ptr)
num_periods(net::Network) = ccall((:NET_get_num_periods, libpfnet), Int, (Ptr{Void},), net.ptr)
num_vars(net::Network) = ccall((:NET_get_num_vars, libpfnet), Int, (Ptr{Void},), net.ptr)

function set_flags(net::Network, obj_type::String, flags, properties, quantities)
    flags = typeof(flags) == Array{String,1} ? flags : [flags]
    properties = typeof(properties) == Array{String,1} ? properties : [properties]
    quantities = typeof(quantities) == Array{String,1} ? quantities : [quantities]
    f(x,y) = x | y
    ccall((:NET_set_flags, libpfnet),
          Void,
          (Ptr{Void}, UInt8, UInt8, UInt8, UInt8,),
          net.ptr,
          str2obj[obj_type],
          reduce(f, [str2flag[x] for x in flags]),
          reduce(f, [str2prop[obj_type][x] for x in properties]),
          reduce(f, [str2q[obj_type][x] for x in quantities]))
end

show_components(net::Network) = ccall((:NET_show_components, libpfnet), Void, (Ptr{Void},), net.ptr)


text:
push!(LOAD_PATH,"../src/")

#####

using Pkg, Documenter
using ExactODEReduction

makedocs(
    modules = [ ExactODEReduction ],
    sitename = "Documenter.jl",
    authors = "Elizaveta Demitraki, Alexander Demin, and Gleb Pogudin",
    pages = [
        "Home" => "index.md",
        "Examples" => Any[
            "examples/example_subspaces.md",
            "examples/example_full.md"
        ],
        "Documentation" => Any[
            "documentation/types.md",
            "documentation/functions.md",
            "documentation/structures.md",
        ]
    ]
)

makedocs(sitename="ExactODEReduction.jl")

deploydocs(
    branch = "gh-pages",
    repo = "github.com/x3042/Exact-reduction-of-ODE-systems.git",
    devbranch = "main",
)


text:
"""
```
Lifting_Programmes::Dict{String, Programme}
```
Dictionary with preloaded programmes. These are the programmes I've created and added to the dictionary.

```
julia> import Lifting: Lifting_Programmes
julia> keys(Lifting_Programmes)
Base.KeySet for a Dict{String,Programme} with 1 entry. Keys:
  "nSunsCAP3_OHP_6Day_LP"
```
"""
Lifting_Programmes = Dict{String,Programme}()

include("nSunsCAP3_OHP_6Day_LP.jl")
include("DCG_YugeStr.jl")
include("nSunsCAP3.jl")
include("nSunsCAP3_RPE.jl")
include("DCG_CAP4.jl")

max_stars_repo_name: dcelisgarza/Lifting


text:
export DiskVector

type DiskVector{T} <: AVector{T}
  length::Int
  file::String
  handle::IOStream
  pos::Int
end

typealias DiskVectorT{T} Type{DiskVector{T}}

function (::DiskVectorT{T}){T}()
  file = tempname()
  v = DiskVector{T}(0, file, open(file, "a+"), 1)
  finalizer(v, close)
  return v
end

function (::DiskVectorT{T}){T}(xs)
  v = DiskVector{T}()
  for x in xs
    push!(v, x)
  end
  return v
end

DiskVector() = DiskVector{Any}()
DiskVector(xs) = DiskVector{eltype(xs)}(xs)

function Base.close(v::DiskVector)
  isopen(v.handle) && close(v.handle)
  isfile(v.file) && rm(v.file)
end

Base.size(v::DiskVector) = (v.length,)

function Base.push!{T}(v::DiskVector{T}, x::T)
  # warn("pushing single value")
  seekto(v, :end)
  if isbits(T)
    write(v.handle, x)
  else
    serialize(v.handle, x)
  end
  v.length += 1
  v.pos += 1
  return v
end

function Base.append!{T}(v::DiskVector{T}, xs::AbstractVector{T})
  seekto(v, :end)
  @assert isbits(T)
  write(v.handle, xs)
  v.length += length(xs)
  v.pos = v.length+1
  return v
end

function seekto(v::DiskVector, i)
  if i == :end
    if v.pos ≤ v.length
      seekend(v.handle)
      v.pos = v.length+1
    end
  elseif v.pos ≠ i
    @assert isbits(eltype(v))
    v.pos = i
    seek(v.handle, sizeof(eltype(v))*(i-1))
  end
end

function Base.getindex(v::DiskVector, i::Integer)
  # warn("reading single value")
  seekto(v, i)
  v.pos += 1
  return read(v.handle, eltype(v))
end

function Base.setindex!{T}(v::DiskVector{T}, x::T, i::Integer)
  @assert isbits(x)
  # warn("writing single value")
  seekto(v, i)
  write(v.handle, x)
  v.pos += 1
  return x
end

function Base.copy!{T}(v::AbstractVector{T}, xs::DiskVector{T})
  @assert isbits(T)
  seekstart(xs.handle)
  read!(xs.handle, v)
  return v
end

function Base.collect(xs::DiskVector)
  if isempty(xs)
    Vector{eltype(xs)}()
  else
    copy!(Vector{eltype(xs)}(length(xs)), xs)
  end
end

function Base.similar(v::DiskVector, n::Integer)
  @assert n == 0
  DiskVector{eltype(v)}()
end

# run(`open $(dirname(tempname()))`)


text:
# defining range to work with
r = 1:20

# set of lists I will go through
l1 = [i % 2 for i in r]
l2 = [i for i in r]
l3 = [i * 2 for i in r]
l4 = ["hello ", "how ", "are ", "you?"]

# print them out
# println(l1)
# println(l2)
# println(l3)

# ***** FOR LOOPS *****
# simple loop with editing elements
for i in r
    l2[i] += i
end

# loop accessing a string container
for word in l4
    print(word)
end

println(" ")

# same output as previous loop but using index instead
for i in 1:size(l4, 1)
    print(l4[i])
end

# nested loops
z = zeros(3, 4)
for c in 1:size(z, 2)
    for r in 1:size(z, 1)
        z[r, c] += r + c
    end
end

display(z)

# ***** WHILE LOOPS *****
global i
i = 0

while i < 5
    println(i)
    # i += 1        # incorrect
    global i += 1   # correct
end

max_stars_repo_name: nunesmelo/djs-office-hours

max_issues_repo_name: nunesmelo/djs-office-hours

max_forks_repo_name: nunesmelo/djs-office-hours


text:
using Random, Test

function entry_results_test(switches)
    @testset "Entry results" begin
        F1 = Float64;
        println("\n----------");
        println(switches);

        er = make_test_entry_results(switches);

        J = n_types(switches);
        nc = n_colleges(switches);
        nl = n_locations(switches);

        @test all(capacities(er) .>= 0.0)

        entryProbLocal_jcM = entry_probs_jc(er, :local);
        entryProbNonLocal_jcM = entry_probs_jc(er, :nonlocal);
        entryProb_jcM = entry_probs_jc(er);
        @test all(0.0 .<= entryProbLocal_jcM .<= 1.0)
        @test all(0.0 .<= entryProbNonLocal_jcM .<= 1.0)
        @test all(0.0 .<= entryProb_jcM .<= 1.0)
        @test isapprox(entryProb_jcM, entryProbLocal_jcM .+ entryProbNonLocal_jcM)

        entryProbLocal_jV = entry_probs_j(er, :local);
        @test isapprox(entryProbLocal_jV,  vec(sum(entryProbLocal_jcM, dims = 2)))
        @test isa(entryProbLocal_jV, Vector)
        entryProbNonLocal_jV = entry_probs_j(er, :nonlocal);
        @test isa(entryProbNonLocal_jV, Vector)
        @test isapprox(entryProbNonLocal_jV,  sum(entryProbNonLocal_jcM, dims = 2))
        @test isapprox(entry_probs_j(er),
            entryProbLocal_jV .+ entryProbNonLocal_jV)

        typeMasses = type_mass_jl(er);
        @test all(typeMasses .>= 0.0)
        @test size(typeMasses) == (J, nl)

        @test size(expected_values_jl(er)) == (J, nl)
        @test size(colleges_full(er)) == (nc, nl)

        @test size(enrollment_cl(er)) == (nc, nl)
        enroll_clM = enrollment_cl(er);
        for ic = 1 : nc
            @test isapprox(enroll_clM[ic,:], enrollment_cl(er, ic))
        end

        fracLocal_jV = frac_local_j(er);
        fracLocal_cV = frac_local_c(er);
        if nl == 1
            @test length(capacities(er)) == nc

            @test frac_local(er) == one(F1);
            @test frac_local_j(er) == ones(F1, J)
            @test frac_local_c(er) == ones(F1, nc)
            # This may not hold in test data
            # @test all(0.0 .<= enrollments(er) .< capacities(er) .* 1.2)
            # enroll_cV = enrollments(er);
            # for ic = 1 : nc
            #     @test isapprox(enroll_cV[ic], CollegeEntry.enrollment(er, ic))
            # end

        else
            @test 0.0 < frac_local(er) < 1.0
            @test all(0.0 .< fracLocal_jV .< one(F1))
            @test isa(fracLocal_jV, Vector)
            @test length(fracLocal_jV) == J

            # Frac local can be 1 (when colleges are local only)
            @test all(0.0 .< fracLocal_cV .<= one(F1))
            @test isa(fracLocal_cV, Vector)
            @test length(fracLocal_cV) == nc
        end
	end
end

function subset_er_test(switches)
    @testset "Subset entry results" begin
        er = make_test_entry_results(switches);
        idxV = 2 : 2 : n_types(er);
        er2 = CollegeEntry.subset_types(er, idxV);
        @test validate_er(er2; validateFracLocal = false)
        @test n_types(er2) == length(idxV)
    end
end

function fix_type_probs_test(switches)
    rng = MersenneTwister(95);
	@testset "Fix type entry probs" begin
        er = make_test_entry_results(switches);
        fracLocal_jV = frac_local_j(er);

        J = n_types(er);
        typeTotalV = 0.3 .+ 0.6 .* rand(rng, J);
        CollegeEntry.fix_type_entry_probs!(er, typeTotalV);

        @test isapprox(entry_probs_j(er, :all),  typeTotalV)
        @test isapprox(frac_local_j(er), fracLocal_jV)
    end
end

@testset "Entry Results" begin
    for switches in test_entry_switches(8, 3)
        entry_results_test(switches)
        subset_er_test(switches)
        fix_type_probs_test(switches)
    end
end

# --------------