-- -----------------------------------------------------------------------------
-- Informática de 1º de Grado en Matemáticas. Grupo 2.
-- Examen 1 de evaluación alternativa (12 de noviembre 2021).
-- -----------------------------------------------------------------------------
import Test.QuickCheck
import Data.Char
-- -----------------------------------------------------------------------------
-- Ejercicio 1.1. Un número imparitario es aquel que tiene una cantidad impar
-- de cifras pares y una cantidad par de cifras impares. Por ejemplo, 235 es
-- imparitario porque tiene 1 par (el 2) y 2 impares (el 3 y 5). De igual forma,
-- 101, 103, 105, 107, 291, 14238 y 1106871 son imparitarios, mientras que 21,
-- 2468 y 11258 no lo son.
--
-- Define la función (numeroImparitario n), tal que indique si un número n
-- es imparitario. Por ejemplo,
-- λ> numeroImparitario 103 == True
-- λ> numeroImparitario 291 == True
-- λ> numeroImparitario 1106871 == True
-- λ> numeroImparitario 21 == False
-- λ> numeroImparitario 2468 == False
-- λ> numeroImparitario 11358 == True
-- λ> numeroImparitario 11258 == False
-- -----------------------------------------------------------------------------
numeroImparitario :: Integer -> Bool
numeroImparitario n = odd p && even i
where cs = show n
p = sum [1 | c <- cs, elem c "02468"]
i = sum [1 | c <- cs, elem c "13579"]
cifras :: Integer -> [Int]
cifras x = [read [c] | c <- show x]
numeroImparitario' :: Integer -> Bool
numeroImparitario' n = odd p && even i
where cs = cifras n
p = sum [1 | c <- cs, even c]
i = sum [1 | c <- cs, odd c]
-- -----------------------------------------------------------------------------
-- Ejercicio 1.2. Comprueba con QuickCheck que si n es un número positivo
-- de más de tres cifras e imparitario, m también es imparitario, donde
-- m es n+3, si la última cifra de n es 8 o 9, o n+2, en otro caso. Por ejemplo,
-- son imparitarios 291 y 291+2=293, 299 y 299+3=301, y 358 y 358+3=361.
-- -----------------------------------------------------------------------------
prop_imparitario :: Integer -> Property
prop_imparitario x = numeroImparitario n ==> numeroImparitario n'
where n' = if rem n 10 == 9 || rem n 10 == 8 then n+3 else n+2
n = 100 + abs x
-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------
-- Ejercicio 2. Representaremos la agenda de nuestras asignaturas como sigue:
-- una lista de tuplas con primera componente el nombre abreviado de la
-- asignatura, y segunda componente una lista de ternas, donde cada
-- terna indica un día y un rago de horario representado como un entero (por
-- ejemplo, 900 es 9:00, 1130 es 11:30, etc.)
agenda :: [(String,[(String,Int,Int)])]
agenda = [ ("Inf",[("Mie",900,1100),("Vie",900,1100)]),
("Alg",[("Mie",1130,1330),("Ju",1130,1330)]),
("Fis",[("Lu",900,1100),("Mar",900,1100)]),
("Cal",[("Lu",1130,1330),("Ju",900,1100)]),
("Est",[("Mar",1130,1330),("Vie",900,1100)]) ]
-- Define la función (asignatura ds h as), tal que devuelva la lista de
-- asignaturas donde debemos estar el día ds a las h horas, según la agenda
-- as. Si estamos en el descanso o no hay asignaturas, devolver la lista
-- vacía. El nombre abreviado del día a buscar no debe diferenciar entre
-- minúsculas y mayúsculas (es decir, "lu" es igual que "Lu"). Por ejemplo,
-- λ> asignatura "Lu" 930 agenda
-- ["Fis"]
-- λ> asignatura "lu" 945 agenda
-- ["Fis"]
-- λ> asignatura "Vie" 930 agenda
-- ["Inf","Est"]
-- λ> asignatura "Mar" 1115 agenda
-- []
-- -----------------------------------------------------------------------------
asignatura :: String -> Int -> [(String,[(String,Int,Int)])] -> [String]
asignatura ds h as = [ ns | (ns,hs) <- as, (ds',h1,h2) <- hs, (minusculas ds') == (minusculas ds), h1 <= h, h2 >= h ]
where minusculas xs = [toLower x | x <- xs]
-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------
-- Ejercicio 3. Dos vectores son más compatibles cuanto mayor sea su producto
-- escalar (hasta llegar a ser paralelos). Representaremos los vectores como
-- listas de números. Define la función (masCompatibles xs yss), tal que dado
-- un vector xs y una lista de vectores yss, devuelva la lista de los vectores
-- más compatibles de yss respecto de xs. Por ejemplo
-- λ> masCompatibles [1,0,1] [[0,1,0], [2,3,1], [-1,7,1],[3,1,0]]
-- [[2,3,1],[3,1,0]]
-- λ> masCompatibles [1,0,1] [[0,1,0], [2,3,1], [-1,7,1],[4,-1,4]]
-- [[4,-1,4]]
-- -----------------------------------------------------------------------------
masCompatibles :: (Ord a,Num a) => [a] -> [[a]] -> [[a]]
masCompatibles xs yss = [ys | ys <-yss, productoEscalar xs ys == m]
where m = maximum productos
productos = [productoEscalar xs ys | ys <- yss]
productoEscalar xs ys = sum [x*y | (x,y) <- zip xs ys]
-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------
-- Ejercicio 4.1 Definir, por comprensión, la función (coincidenciasC k xs ys),
-- tal que verifique si las listas xs e ys coinciden en, al menos, k de sus
-- posiciones. Por ejemplo,
-- λ> coincidenciasC 7 "salamanca" "salamandra" == True
-- λ> coincidenciasC 2 [1,2,3,4,5] [1,3,3,8,1] == True
-- λ> coincidenciasC 4 "almendra" "almazara" == True
-- λ> coincidenciasC 6 "almendra" "almazara" == False
-- -----------------------------------------------------------------------------
coincidenciasC :: Eq a => Int -> [a] -> [a] -> Bool
coincidenciasC n xs ys = sum [1 | (x,y) <- zip xs ys, x==y] >= n
-- -----------------------------------------------------------------------------
-- Ejercicio 4.2 Definir, por recursión, la función (coincidencias k xs ys),
-- tal que verifique si las listas xs e ys coinciden en, al menos, k de sus
-- posiciones. Por ejemplo,
-- λ> coincidenciasR 7 "salamanca" "salamandra" == True
-- λ> coincidenciasR 2 [1,2,3,4,5] [1,3,3,8,1] == True
-- λ> coincidenciasR 4 "almendra" "almazara" == True
-- λ> coincidenciasR 6 "almendra" "almazara" == False
-- -----------------------------------------------------------------------------
coincidenciasR :: Eq a => Int -> [a] -> [a] -> Bool
coincidenciasR 0 _ _ = True
coincidenciasR n [] _ = False
coincidenciasR n _ [] = False
coincidenciasR n (x:xs) (y:ys) | x == y = coincidenciasR (n-1) xs ys
| otherwise = coincidenciasR n xs ys
-- -----------------------------------------------------------------------------