@@ -452,3 +452,59 @@ function _number_of_direct_product_factors end
452452Return the homomorphism from the domain `D` into the codomain `C` defined by the data. 
453453""" 
454454function  hom end 
455+ 
456+ # ##############################################################################
457+ # 
458+ # 
459+ # 
460+ # ##############################################################################
461+ 
462+ #  Calling `_implements(T, f)` checks whether a "sensible" method for the unary
463+ #  function `f` is implemented for inputs of type `T`. The argument order is
464+ #  meant to be similar to e.g. `isa`, and thus indicates `T implements f`.
465+ # 
466+ #  For example, `_implements(MyRingElem, is_unit)` should return true if
467+ #  invoking `is_unit` on elements of type `MyRingElem` is supported.
468+ # 
469+ #  The generic fallback uses `hasmethod`. However, this may return `true` in
470+ #  cases where it shouldn't, as we often provide generic methods for that rely
471+ #  on other methods being implemented -- either for the same type, or for types
472+ #  derived from it. For example the `is_nilpotent(::PolyElem{T})` method needs
473+ #  `is_nilpotent(::T)` in order to work.
474+ # 
475+ #  To reflect this, additional `_implements` methods need to be provided.
476+ #  We currently do this for at least the following functions:
477+ #  - factor
478+ #  - is_irreducible
479+ #  - is_nilpotent
480+ #  - is_squarefree
481+ #  - is_unit
482+ #  - is_zero_divisor
483+ # 
484+ _implements (:: Type{T} , f:: Any ) where  {T} =  hasmethod (f, Tuple{T})
485+ 
486+ #  Alternatively, the first argument can be a concrete object. By default we
487+ #  then redispatch to the type based version. But one may also choose to
488+ #  implement custom methods for this: certain operations will only work for
489+ #  *some* instances. E.g. for `Z/nZ` it may happen that for `n` a prime we can
490+ #  perform a certain operation, but not if `n` is composite.
491+ # 
492+ #  In that case the recommendation is that `_implements` invoked on the type
493+ #  returns `false`, but invoked on a concrete instance of a type, it may use
494+ #  specifics of the instance to also return `true` if appropriate.
495+ function  _implements (x:: T , f:: Any ) where  {T}
496+   @assert  ! (x isa  Type) #  paranoia
497+   return  _implements (T, f)
498+ end 
499+ 
500+ #  helper for `_implements` which checks if `f` has a method explicitly for
501+ #  a concrete type `T` (i.e. not a generic method that can be specialized to `T`
502+ #  but really one that is implement for `T` and `T` only).
503+ function  _implements_directly (:: Type{T} , f:: Any ) where  {T}
504+   isconcretetype (T) ||  return  false   #  TODO : drop this?
505+   meth =  methods (f, Tuple{T})
506+   #  TODO : deal with type parameters: if `T` is `FreeAssociativeAlgebraElem{ZZRingElem}`
507+   #  and `f` has a method for `FreeAssociativeAlgebraElem` then we should still consider
508+   #  this a match.
509+   return  any (m ->  m. sig ==  Tuple{typeof (f), T}, meth)
510+ end 
0 commit comments