{-# LANGUAGE CPP #-} {- 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@] * Describes a list of /prime factors/. * The product of this list of prime-factors represents the /composite/ integer from which they were originally extracted. -} module Factory.Data.PrimeFactors( -- * Types -- ** Type-synonyms Factors, -- * Functions insert', -- invert, product', reduce, -- reduceSorted, -- sumExponents, -- ** Operators (>*<), (>/<), (>^) ) where import qualified Control.Arrow import Control.Arrow((&&&)) import qualified Data.List import qualified Data.Ord import qualified Factory.Math.DivideAndConquer as Math.DivideAndConquer import qualified Factory.Data.Exponential as Data.Exponential import Factory.Data.Exponential((<^), (=~)) #if MIN_VERSION_toolshed(11,1,1) import qualified ToolShed.Data.List #endif infixl 7 >/<, >*< --Same as (/). infixr 8 >^ --Same as (^). {- | * Each element of this list represents one /prime-factor/, expressed as an /exponential/ with a /prime/ base, of the original integer. * Whilst it only makes sense for both the /base/ and /exponent/ to be integral, these constrains are applied at the function-level as required. -} type Factors base exponent = [Data.Exponential.Exponential base exponent] {- | * Sorts a list representing a product of /prime factors/ by increasing /base/. * Multiplies 'Data.Exponential.Exponential's of similar /base/. -} reduce :: (Ord base, Num exponent, Ord exponent) => Factors base exponent -> Factors base exponent reduce = reduceSorted . Data.List.sort {-primarily by base-} -- | Multiplies 'Data.Exponential.Exponential's of similar /base/. reduceSorted :: (Eq base, Num exponent) => Factors base exponent -> Factors base exponent --reduceSorted = map (Data.Exponential.getBase . head &&& sumExponents) . Data.List.groupBy (=~) --Slow reduceSorted [] = [] reduceSorted (x : xs) | null matched = x : reduceSorted remainder | otherwise = Control.Arrow.second (+ sumExponents matched) x : reduceSorted remainder where (matched, remainder) = span (=~ x) xs {- | * Insert a 'Data.Exponential.Exponential', into a list representing a product of /prime factors/, multiplying with any incumbent of like /base/. * The list should be sorted by increasing /base/. * Preserves the sort-order. * CAVEAT: this is tolerably efficient for sporadic insertion; to insert a list, use '>*<'. -} insert' :: (Ord base, Num exponent) => Data.Exponential.Exponential base exponent -> Factors base exponent -> Factors base exponent insert' e [] = [e] insert' e l@(x : xs) = case Data.Ord.comparing Data.Exponential.getBase e x of LT -> e : l GT -> x : insert' e xs --Recurse. _ -> Control.Arrow.second (+ Data.Exponential.getExponent e) x : xs --Multiply by adding exponents. {- | * Multiplies two lists each representing a product of /prime factors/, and sorted by increasing /base/. * Preserves the sort-order. -} (>*<) :: (Ord base, Num exponent, Ord exponent) => Factors base exponent -> Factors base exponent -> Factors base exponent l >*< r = #if MIN_VERSION_toolshed(11,1,1) reduceSorted $ ToolShed.Data.List.merge l r #else reduce $ l ++ r --CAVEAT: concatenation disorders the list, necessitating a re-sort. #endif -- | Invert the product of a list /prime factors/, by negating each of the /exponents/. invert :: Num exponent => Factors base exponent -> Factors base exponent invert = map Data.Exponential.invert {- | * Divides two lists, each representing a product of /prime factors/, and sorted by increasing /base/. * Preserves the sort-order. -} (>/<) :: (Integral base, Integral exponent) => Factors base exponent -- ^ The list of /prime factors/ in the /numerator/. -> Factors base exponent -- ^ The list of /prime factors/ in the /denominator/. -> (Factors base exponent, Factors base exponent) -- ^ The ratio of /numerator/ and /denominator/, after like /prime factors/ are cancelled. numerator >/< denominator = filter ( (> 0) . Data.Exponential.getExponent ) &&& invert . filter ( (< 0) . Data.Exponential.getExponent ) $ numerator >*< invert denominator {- | * Raise the product of a list /prime factors/ to the specified power. * CAVEAT: this merely involves raising each element to the specified power; cf. raising a /polynomial/ to a power. -} (>^) :: Num exponent => Factors base exponent -> exponent -> Factors base exponent factors >^ power = map (<^ power) factors -- | Sum the /exponents/ of the specified list; as required to multiply exponentials with identical /base/. sumExponents :: Num exponent => Factors base exponent -> exponent sumExponents = foldr ((+) . Data.Exponential.getExponent) 0 -- | Multiply a list of /prime factors/. product' :: (Num base, Integral exponent) => Math.DivideAndConquer.BisectionRatio -> Math.DivideAndConquer.MinLength -> Factors base exponent -- ^ The list on which to operate. -> base -- ^ The result. product' bisectionRatio minLength = Math.DivideAndConquer.product' bisectionRatio minLength . map Data.Exponential.evaluate