import Data.List (genericIndex, sort)
import Data.Numbers.Primes (primeFactors)
import Test.QuickCheck
-- 1ª definición
-- =============
maximoProductoParticiones1 :: Integer -> Integer
maximoProductoParticiones1 n =
maximum [product xs | xs <- particiones1 n]
-- (particiones1 n) es la lista de las particiones de n. Por ejemplo,
-- λ> particiones1 4
-- [[4],[3,1],[2,2],[2,1,1],[1,1,1,1]]
-- λ> particiones1 5
-- [[5],[4,1],[3,2],[3,1,1],[2,2,1],[2,1,1,1],[1,1,1,1,1]]
particiones1 :: Integer -> [[Integer]]
particiones1 0 = [[]]
particiones1 n = [x:y | x <- [n,n-1..1]
, y <- particiones1 (n-x)
, [x] >= take 1 y]
-- 2ª definición
-- =============
maximoProductoParticiones2 :: Integer -> Integer
maximoProductoParticiones2 n =
maximum [product xs | xs <- particiones2 n]
-- (particiones2 n) es la lista de las particiones de n. Por ejemplo,
-- λ> particiones2 4
-- [[4],[3,1],[2,2],[2,1,1],[1,1,1,1]]
-- λ> particiones2 5
-- [[5],[4,1],[3,2],[3,1,1],[2,2,1],[2,1,1,1],[1,1,1,1,1]]
particiones2 :: Integer -> [[Integer]]
particiones2 n = aux `genericIndex` n
where aux = [] : map particiones [1..]
where particiones n = [n] : [x:p | x <- [n,n-1..1]
, p <- aux `genericIndex` (n-x)
, x >= head p]
-- 3ª definición
-- =============
maximoProductoParticiones3 :: Integer -> Integer
maximoProductoParticiones3 0 = 1
maximoProductoParticiones3 n =
maximum [(n-i) * maximoProductoParticiones3 i | i <- [0..n-1]]
-- 4ª definición
-- =============
maximoProductoParticiones4 :: Integer -> Integer
maximoProductoParticiones4 n =
maximosProductosParticiones `genericIndex` n
maximosProductosParticiones :: [Integer]
maximosProductosParticiones = 1 : f [1]
where f xs = y : f (y:xs)
where y = maximum (zipWith (*) [1..] xs)
-- 5ª definición
-- =============
maximoProductoParticiones5 :: Integer -> Integer
maximoProductoParticiones5 1 = 1
maximoProductoParticiones5 n
| r == 0 = 3^q
| r == 1 = 4 * 3^(q-1)
| r == 2 = 2 * 3 ^q
where (q,r) = quotRem n 3
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> maximoProductoParticiones1 20
-- 1458
-- (5.42 secs, 832,283,184 bytes)
-- λ> maximoProductoParticiones2 20
-- 1458
-- (0.02 secs, 0 bytes)
-- λ> maximoProductoParticiones3 20
-- 1458
-- (3.18 secs, 524,500,000 bytes)
-- λ> maximoProductoParticiones4 20
-- 1458
-- (0.00 secs, 0 bytes)
--
-- λ> maximoProductoParticiones2 50
-- 86093442
-- (9.18 secs, 980,524,320 bytes)
-- λ> maximoProductoParticiones4 50
-- 86093442
-- (0.01 secs, 1,381,192 bytes)
-- λ> maximoProductoParticiones5 50
-- 86093442
-- (0.00 secs, 0 bytes)
--
-- λ> length (show (maximoProductoParticiones4 2000))
-- 319
-- (2.93 secs, 638,270,872 bytes)
-- λ> length (show (maximoProductoParticiones5 2000))
-- 319
-- (0.01 secs, 0 bytes)
-- Comprobación
-- ============
-- La propiedad es
prop_factores :: Integer -> Property
prop_factores n =
n > 0 ==> primeFactors (maximoProductoParticiones5 n) `contenido` [2,3]
contenido :: Eq a => [a] -> [a] -> Bool
contenido xs ys = all (`elem` ys) xs
-- La comprobación es
-- λ> quickCheck prop_factores
-- +++ OK, passed 100 tests.