module Engine export Construction, mprod import Subscripts using LinearAlgebra using AbstractAlgebra using Groebner # --- primitve elements --- abstract type Element{T} end mutable struct Point{T} <: Element{T} coords::Vector{MPolyRingElem{T}} vec::Union{Vector{MPolyRingElem{T}}, Nothing} rel::Nothing ## [to do] constructor argument never needed? Point{T}( coords::Vector{MPolyRingElem{T}} = MPolyRingElem{T}[], vec::Union{Vector{MPolyRingElem{T}}, Nothing} = nothing ) where T = new(coords, vec, nothing) end ##coordnames(_::Point) = [:xₚ, :yₚ, :zₚ] function buildvec!(pt::Point) coordring = parent(pt.coords[1]) pt.vec = [one(coordring), dot(pt.coords, pt.coords), pt.coords...] end mutable struct Sphere{T} <: Element{T} coords::Vector{MPolyRingElem{T}} vec::Union{Vector{MPolyRingElem{T}}, Nothing} rel::Union{MPolyRingElem{T}, Nothing} ## [to do] constructor argument never needed? Sphere{T}( coords::Vector{MPolyRingElem{T}} = MPolyRingElem{T}[], vec::Union{Vector{MPolyRingElem{T}}, Nothing} = nothing, rel::Union{MPolyRingElem{T}, Nothing} = nothing ) where T = new(coords, vec, rel) end ##coordnames(_::Sphere) = [:rₛ, :sₛ, :xₛ, :yₛ, :zₛ] function buildvec!(sph::Sphere) coordring = parent(sph.coords[1]) sph.vec = sph.coords sph.rel = mprod(sph.coords, sph.coords) + one(coordring) end const coordnames = IdDict{Symbol, Vector{Union{Symbol, Nothing}}}( nameof(Point) => [nothing, nothing, :xₚ, :yₚ, :zₚ], nameof(Sphere) => [:rₛ, :sₛ, :xₛ, :yₛ, :zₛ] ) coordname(elem::Element, index) = coordnames[nameof(typeof(elem))][index] function pushcoordname!(coordnamelist, indexed_elem::Tuple{Any, Element}, coordindex) elemindex, elem = indexed_elem name = coordname(elem, coordindex) if !isnothing(name) subscript = Subscripts.sub(string(elemindex)) push!(coordnamelist, Symbol(name, subscript)) end end function takecoord!(coordlist, indexed_elem::Tuple{Any, Element}, coordindex) elem = indexed_elem[2] if !isnothing(coordname(elem, coordindex)) push!(elem.coords, popfirst!(coordlist)) end end # --- primitive relations --- abstract type Relation{T} end mprod(v, w) = (v[1]*w[2] + w[1]*v[2]) / 2 - dot(v[3:end], w[3:end]) # elements: point, sphere struct LiesOn{T} <: Relation{T} elements::Vector{Element{T}} LiesOn{T}(pt::Point{T}, sph::Sphere{T}) where T = new{T}([pt, sph]) end equation(rel::LiesOn) = dot(rel.elements[1].vec, rel.elements[2].vec) # elements: sphere, sphere struct AlignsWithBy{T} <: Relation{T} elements::Vector{Element{T}} cos_angle::T AlignsWithBy{T}(sph1::Sphere{T}, sph2::Sphere{T}, cos_angle::T) where T = new{T}([sph1, sph2], cos_angle) end equation(rel::AlignsWithBy) = dot(rel.elements[1].vec, rel.elements[2].vec) - rel.cos_angle # --- constructions --- mutable struct Construction{T} elements::Set{Element{T}} relations::Set{Relation{T}} function Construction{T}(; elements = Set{Element{T}}(), relations = Set{Relation{T}}()) where T allelements = union(elements, (rel.elements for rel in relations)...) new{T}(allelements, relations) end end function Base.push!(ctx::Construction{T}, elem::Element{T}) where T push!(ctx.elements, elem) end function Base.push!(ctx::Construction{T}, rel::Relation{T}) where T push!(ctx.relations, rel) union!(ctx.elements, rel.elements) end function realize(ctx::Construction{T}) where T # collect coordinate names coordnamelist = Symbol[] elemenum = enumerate(ctx.elements) for coordindex in 1:5 for indexed_elem in elemenum pushcoordname!(coordnamelist, indexed_elem, coordindex) end end display(collect(elemenum)) display(coordnamelist) println() # construct coordinate ring coordring, coordqueue = polynomial_ring(parent_type(T)(), coordnamelist, ordering = :degrevlex) # retrieve coordinates for (_, elem) in elemenum empty!(elem.coords) end for coordindex in 1:5 for indexed_elem in elemenum takecoord!(coordqueue, indexed_elem, coordindex) end end # construct coordinate vectors for (_, elem) in elemenum buildvec!(elem) display(elem.coords) display(elem.vec) println() end # turn relations into equations vcat( equation.(ctx.relations), [elem.rel for elem in ctx.elements if !isnothing(elem.rel)] ) end end # ~~~ sandbox setup ~~~ CoeffType = Rational{Int64} a = Engine.Point{CoeffType}() s = Engine.Sphere{CoeffType}() a_on_s = Engine.LiesOn{CoeffType}(a, s) ctx = Engine.Construction{CoeffType}(elements = Set([a]), relations= Set([a_on_s])) eqns_a_s = Engine.realize(ctx) b = Engine.Point{CoeffType}() b_on_s = Engine.LiesOn{CoeffType}(b, s) Engine.push!(ctx, b) Engine.push!(ctx, s) Engine.push!(ctx, b_on_s) eqns_ab_s = Engine.realize(ctx) spheres = [Engine.Sphere{CoeffType}() for _ in 1:3] tangencies = [ Engine.AlignsWithBy{CoeffType}( spheres[n], spheres[mod1(n+1, length(spheres))], -1//1 ) for n in 1:3 ] ctx_chain = Engine.Construction{CoeffType}(elements = Set(spheres), relations = Set(tangencies))