function LShape(domain::LShape, parent::ArbField)
    LShape(parent)
end

function Base.show(io::IO, domain::LShape)
    print(io, "L-shaped domain")
end

function area(domain::LShape)
    domain.parent(3)
end

function boundary_parameterization(t, domain::LShape, i::Integer)
    1 <= i <= 4 || throw(ErrorException("give 1 ≤ i ≤ 4 not i = $i"))

    if i == 1
        v = SVector(1, 0)
        w = SVector(1, 1)
    elseif i == 2
        v = SVector(1, 1)
        w = SVector(-1, 1)
    elseif i == 3
        v = SVector(-1, 1)
        w = SVector(-1, -1)
    elseif i == 4
        v = SVector(-1, -1)
        w = SVector(0, -1)
    end

    xy = v .+ t .* (w .- v)

    θ = atan(xy[2], xy[1])
    if typeof(t) == arb_series
        θ = ifelse(θ[0] < 0, θ + 2domain.parent(π), θ)
    else
        θ = ifelse(θ < 0, θ + 2domain.parent(π), θ)
    end
    (sqrt(xy[1]^2 + xy[2]^2), θ)
end

function boundary_points(
    domain::LShape,
    u::AbstractEigenfunction,
    ::Integer,
    n::Integer;
    distribution = :linear,
)
    points = Vector{Tuple{arb,arb}}(undef, n)

    for i = 1:n
        θ = 1.5 * domain.parent(π) * i / (n + 1)
        r = 1 / max(abs(sin(θ)), abs(cos(θ)))

        points[i] = (r, θ)
    end

    return points, fill(nothing, n)
end

function interior_points(domain::LShape, n::Integer; rng = MersenneTwister(42))
    # The points are generated by randomly picking a point in the
    # square [-1, 1]×[-1, 1] and keeping it if it is inside the
    # domain.
    points = Vector{Tuple{arb,arb}}(undef, n)

    for i = 1:n
        x = 2rand(rng) - 1
        y = 2rand(rng) - 1

        while x >= 0 && y <= 0
            x = 2rand(rng) - 1
            y = 2rand(rng) - 1
        end

        x, y = domain.parent(x), domain.parent(y)
        θ = atan(y, x)
        θ = ifelse(θ < 0, θ + 2domain.parent(π), θ)
        points[i] = (hypot(x, y), θ)
    end

    points
end
