###############################################################################
#
#   Abstract types
#
###############################################################################

# broad mathematical domains
# these contain the type classes of parent objects

abstract type Set end

abstract type Group <: Set end # with * as operation

abstract type AdditiveGroup <: Set end # with + as operation

abstract type AbstractPermutationGroup <: Group end

abstract type NCRing <: Set end

abstract type Ring <: NCRing end

abstract type Field <: Ring end

# elements of mathematical domains

abstract type SetElem end

abstract type GroupElem <: SetElem end # with * as operation

abstract type AdditiveGroupElem <: SetElem end # with + as operation

abstract type AbstractPerm <: GroupElem end

abstract type NCRingElem <: SetElem end

abstract type RingElem <: NCRingElem end

abstract type FieldElem <: RingElem end

# parameterized domains

abstract type Module{T} <: AdditiveGroup end

abstract type FPModule{T} <: Module{T} end

abstract type Ideal{T} <: Set end

abstract type IdealSet{T} <: Set end

# elements of parameterised domains

abstract type ModuleElem{T} <: AdditiveGroupElem end

abstract type FPModuleElem{T} <: ModuleElem{T} end

abstract type IdealElem{T} <: SetElem end

abstract type Map{D, C, S, T} <: SetElem end

abstract type SetMap end

abstract type FunctionalMap <: SetMap end

abstract type IdentityMap <: SetMap end

abstract type FPModuleHomomorphism <: FunctionalMap end

# rings, fields etc, parameterised by an element type
# these are the type classes of different kinds of
# mathematical rings/fields/etc, which have a base ring,
# and for which a generic implementation is possible
# over that base ring

abstract type PolyRing{T} <: Ring end

abstract type NCPolyRing{T} <: NCRing end

abstract type MPolyRing{T} <: Ring end

abstract type UniversalPolyRing{T} <: Ring end

abstract type LaurentPolyRing{T} <: Ring end

abstract type LaurentMPolyRing{T} <: Ring end

abstract type SeriesRing{T} <: Ring end

abstract type MSeriesRing{T} <: Ring end

abstract type ResidueRing{T} <: Ring end

abstract type ResidueField{T} <: Field end

abstract type FracField{T} <: Field end

abstract type MatRing{T} <: NCRing end

abstract type FreeAssociativeAlgebra{T} <: NCRing end

# Abstract types for number fields, parmeterised by the element type of
# the base field.
abstract type NumField{T} <: Field end

# A type for number fields, which are represented using a primitive element.
# (simple number fields)
abstract type SimpleNumField{T} <: NumField{T} end

# mathematical objects parameterised by an element type
# these are the type classes of mathematical objects
# that have some kind of base ring, and a generic
# implementation is meaningful over that base ring

abstract type PolyRingElem{T} <: RingElem end

abstract type NCPolyRingElem{T} <: NCRingElem end

abstract type MPolyRingElem{T} <: RingElem end

abstract type UniversalPolyRingElem{T} <: RingElem end

abstract type LaurentPolyRingElem{T} <: RingElem end

abstract type LaurentMPolyRingElem{T} <: RingElem end

abstract type ResElem{T} <: RingElem end

abstract type ResFieldElem{T} <: FieldElem end

abstract type FracElem{T} <: FieldElem end

abstract type SeriesElem{T} <: RingElem end

abstract type MSeriesElem{T} <: RingElem end

abstract type RelPowerSeriesRingElem{T} <: SeriesElem{T} end

abstract type AbsPowerSeriesRingElem{T} <: SeriesElem{T} end

abstract type AbsMSeriesElem{T} <: MSeriesElem{T} end

abstract type MatElem{T} <: ModuleElem{T} end

abstract type MatRingElem{T} <: NCRingElem end

abstract type FreeAssociativeAlgebraElem{T} <: NCRingElem end

abstract type NumFieldElem{T} <: FieldElem end

abstract type SimpleNumFieldElem{T} <: NumFieldElem{T} end

# additional abstract types for parents, added ad hoc to form
# collections of types as needed by applications

abstract type FinField <: Field end    # for FqPolyRepFieldElem, fqPolyRepFieldElem, etc

# additional abstract types for elements, added ad hoc to form
# collections of types as needed by applications

abstract type FinFieldElem <: FieldElem end # for FqPolyRepFieldElem, fqPolyRepFieldElem, etc

################################################################################
#
#   Promotion system
#
# The promote_rule functions are not extending Base.promote_rule. The
# AbstractAlgebra promotion system is orthogonal to the built-in julia promotion
# system. The julia system assumes that whenever you have a method signature of
# the form Base.promote_rule(::Type{T}, ::Type{S}) = R, then there is also a
# corresponding Base.convert(::Type{R}, ::T) and similar for S. Since we
# cannot use the julia convert system (we need an instance of the type and not
# the type), we cannot use the julia promotion system.
#
# The AbstractAlgebra promotion system is used to define catch all functions
# for arithmetic between arbitrary ring elements.
#
################################################################################

promote_rule(T, U) = Union{}

promote_rule(a::Type{S}, b::Type{T}) where {S <: Real, T <: Real} = Base.promote_rule(a, b)
