-- I1M 2021-22
-- Departamento de Ciencias de la Computación e I.A.
-- Universidad de Sevilla
-- =====================================================================
-- ---------------------------------------------------------------------
-- Importación de librerías auxiliares --
-- ---------------------------------------------------------------------
import Test.QuickCheck
-- ---------------------------------------------------------------------
-- Introducción: El objetivo de esta relación de ejercicios es resolver
-- la ecuación
-- a! * b! = a! + b! + c!
-- donde a, b y c son números naturales.
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 1. Definir la función
-- factorial :: Integer -> Integer
-- tal que (factorial n) es el factorial de n. Por ejemplo,
-- factorial 5 == 120
-- ---------------------------------------------------------------------
factorial :: Integer -> Integer
factorial n = product [1..n]
-- ---------------------------------------------------------------------
-- Ejercicio 2. Definir la constante
-- factoriales :: [Integer]
-- tal que factoriales es la lista de los factoriales de los números
-- naturales. Por ejemplo,
-- take 7 factoriales == [1,1,2,6,24,120,720]
-- ---------------------------------------------------------------------
factoriales :: [Integer]
factoriales = [factorial x | x <- [0..]]
-- ---------------------------------------------------------------------
-- Ejercicio 3. Definir, usando factoriales, la función
-- esFactorial :: Integer -> Bool
-- tal que (esFactorial n) se verifica si existe un número natural m
-- tal que n es m!. Por ejemplo,
-- esFactorial 120 == True
-- esFactorial 20 == False
-- ---------------------------------------------------------------------
esFactorial :: Integer -> Bool
esFactorial n = head (dropWhile (<n) factoriales) == n
-- ---------------------------------------------------------------------
-- Ejercicio 4. Definir la constante
-- posicionesFactoriales :: [(Integer,Integer)]
-- tal que posicionesFactoriales es la lista de los factoriales con su
-- posición. Por ejemplo,
-- *Main> take 7 posicionesFactoriales
-- [(0,1),(1,1),(2,2),(3,6),(4,24),(5,120),(6,720)]
-- ---------------------------------------------------------------------
posicionesFactoriales :: [(Integer,Integer)]
posicionesFactoriales = zip [0..] factoriales
-- ---------------------------------------------------------------------
-- Ejercicio 5. Definir la función
-- invFactorial :: Integer -> Maybe Integer
-- tal que (invFactorial x)
-- invFactorial 120 == Just 5
-- invFactorial 20 == Nothing
-- ---------------------------------------------------------------------
invFactorial :: Integer -> Maybe Integer
invFactorial x | not (esFactorial x) = Nothing
| otherwise = Just (fst (head (dropWhile ((<x) . snd) posicionesFactoriales)))
-- ---------------------------------------------------------------------
-- Ejercicio 6. Definir la constante
-- pares :: [(Integer,Integer)]
-- tal que pares e la lista de todos los pares de números naturales. Por
-- ejemplo,
-- *Main> take 11 pares
-- [(0,0),(0,1),(1,1),(0,2),(1,2),(2,2),(0,3),(1,3),(2,3),(3,3),(0,4)]
-- ---------------------------------------------------------------------
pares :: [(Integer,Integer)]
pares = [(x,y) | y <- [0..], x <- [0..y]]
-- ---------------------------------------------------------------------
-- Ejercicio 7. Definir la constante
-- solucionFactoriales :: (Integer,Integer,Integer)
-- tal que solucionFactoriales es una terna (a,b,c) que es una solución
-- de la ecuación
-- a! * b! = a! + b! + c!
-- Calcular el valor de solucionFactoriales.
-- ---------------------------------------------------------------------
solucionFactoriales :: (Integer,Integer,Integer)
solucionFactoriales = (a,b,c) where
(a,b) = head [(x,y) | (x,y) <- pares, esFactorial ((factorial x) * (factorial y) - (factorial x) - (factorial y))]
Just c = invFactorial ((factorial a) * (factorial b) - (factorial a) - (factorial b))
-- El cálculo es
-- λ> solucionFactoriales
-- (3,3,4)
-- ---------------------------------------------------------------------
-- Ejercicio 8. Comprobar con QuickCheck que solucionFactoriales es la
-- única solución de la ecuación
-- a! * b! = a! + b! + c!
-- con a, b y c números naturales
-- ---------------------------------------------------------------------
prop_solucionFactoriales :: Integer -> Integer -> Integer -> Property
prop_solucionFactoriales x y z = x >= 0 && y >= 0 && z >= 0 && (x,y,z) /= solucionFactoriales ==>
not (esFactorial ((factorial x) * (factorial y) - (factorial x) - (factorial y)))
-- ---------------------------------------------------------------------
-- Nota: El ejercicio se basa en el artículo "Ecuación con factoriales"
-- del blog Gaussianos publicado en
-- http://gaussianos.com/ecuacion-con-factoriales
-- ---------------------------------------------------------------------