{- Copyright (C) 2011 Dr. Alistair Ward This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. -} {- | [@AUTHOR@] Dr. Alistair Ward [@DESCRIPTION@] * Exports a common interface for primality-implementations. * Provides utilities for these implementations. -} module Factory.Math.Primality( -- * Type-classes Algorithmic(..), -- * Functions carmichaelNumbers, -- ** Predicates areCoprime, isFermatWitness, isCarmichaelNumber ) where import qualified Control.DeepSeq import qualified Factory.Math.Power as Math.Power -- | Defines the methods expected of a primality-testing algorithm. class Algorithmic algorithm where isPrime :: (Control.DeepSeq.NFData i, Integral i, Show i) => algorithm -> i -> Bool {- | 'True' if the two specified integers are /relatively prime/, i.e. if they share no common positive factors except one. * @1@ and @-1@ are the only numbers which are /coprime/ to themself. * <https://en.wikipedia.org/wiki/Coprime>. * <http://mathworld.wolfram.com/RelativelyPrime.html>. -} areCoprime :: Integral i => i -> i -> Bool areCoprime i = (== 1) . gcd i {- | * Tests /Fermat's Little Theorem/ for all applicable values, as a probabilistic primality-test. * <https://en.wikipedia.org/wiki/Fermat%27s_little_theorem>. * <https://en.wikipedia.org/wiki/Fermat_primality_test>. * <https://en.wikipedia.org/wiki/Fermat_pseudoprime>. * CAVEAT: this primality-test fails for the /Carmichael numbers/. * TODO: confirm that all values must be tested. -} isFermatWitness :: (Integral i, Show i) => i -> Bool isFermatWitness i = not . all isFermatPseudoPrime $ filter (areCoprime i) [2 .. pred i] where isFermatPseudoPrime base = Math.Power.raiseModulo base (pred i) i == 1 -- CAVEAT: a /Fermat Pseudo-prime/ must also be a /composite/ number. {- | * A /Carmichael number/ is an /odd/ /composite/ number which satisfies /Fermat's little theorem/. * <https://en.wikipedia.org/wiki/Carmichael_number>. * <http://mathworld.wolfram.com/CarmichaelNumber.html>. -} isCarmichaelNumber :: ( Algorithmic algorithm, Control.DeepSeq.NFData i, Integral i, Show i ) => algorithm -> i -> Bool isCarmichaelNumber algorithm i = not $ or [ i <= 2, even i, isFermatWitness i, isPrime algorithm i ] -- | An ordered list of the /Carmichael/ numbers; <https://en.wikipedia.org/wiki/Carmichael_number>. carmichaelNumbers :: ( Algorithmic algorithm, Control.DeepSeq.NFData i, Integral i, Show i ) => algorithm -> [i] carmichaelNumbers algorithm = isCarmichaelNumber algorithm `filter` [3, 5 ..]