-- I1M 2021-22
-- Aplicaciones de la programación funcional con listas infinitas.
-- Departamento de Ciencias de la Computación e I.A.
-- Universidad de Sevilla
-- =====================================================================
-- ---------------------------------------------------------------------
-- Introducción --
-- ---------------------------------------------------------------------
-- En esta relación se estudia distintas aplicaciones de la programación
-- funcional que usan listas infinitas
-- + enumeración de los números enteros,
-- + el problema de la bicicleta de Turing y
-- + la sucesión de Golomb,
-- ---------------------------------------------------------------------
-- § Enumeración de los números enteros --
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 1.1. Los números enteros se pueden ordenar como sigue
-- 0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5, -6, 6, -7, 7, ...
-- Definir, por comprensión, la constante
-- enteros :: [Int]
-- tal que enteros es la lista de los enteros con la ordenación
-- anterior. Por ejemplo,
-- take 10 enteros == [0,-1,1,-2,2,-3,3,-4,4,-5]
-- ---------------------------------------------------------------------
enteros :: [Int]
enteros = [(ceiling x) * (-1)^n | (x,n) <- zip [0,0.5..] [0..]]
-- Otra opción
enteros' :: [Int]
enteros' = 0:concat [[-x,x] | x <- [1..]]
-- ---------------------------------------------------------------------
-- Ejercicio 1.2. Definir la función
-- posicion :: Int -> Int
-- tal que (posicion x) es la posición del entero x en la ordenación
-- anterior. Por ejemplo,
-- posicion 2 == 4
-- ---------------------------------------------------------------------
posicion :: Int -> Int
posicion x | x == 0 = 0
| x < 0 = 2 * abs(x) - 1
| otherwise = 2 * x
-- ---------------------------------------------------------------------
-- § El problema de la bicicleta de Turing --
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 2.1. Cuentan que Alan Turing tenía una bicicleta vieja,
-- que tenía una cadena con un eslabón débil y además uno de los radios
-- de la rueda estaba doblado. Cuando el radio doblado coincidía con el
-- eslabón débil, entonces la cadena se rompía.
--
-- La bicicleta se identifica por los parámetros (i,d,n) donde
-- - i es el número del eslabón que coincide con el radio doblado al
-- empezar a andar,
-- - d es el número de eslabones que se desplaza la cadena en cada
-- vuelta de la rueda y
-- - n es el número de eslabones de la cadena (el número n es el débil).
-- Si i=2 y d=7 y n=25, entonces la lista con el número de eslabón que
-- toca el radio doblado en cada vuelta es
-- [2,9,16,23,5,12,19,1,8,15,22,4,11,18,0,7,14,21,3,10,17,24,6,...
-- Con lo que la cadena se rompe en la vuelta número 14.
--
-- Definir la función
-- eslabones :: Int -> Int -> Int -> [Int]
-- tal que (eslabones i d n) es la lista con los números de eslabones
-- que tocan el radio doblado en cada vuelta en una bicicleta de tipo
-- (i,d,n). Por ejemplo,
-- take 10 (eslabones 2 7 25) == [2,9,16,23,5,12,19,1,8,15]
-- ---------------------------------------------------------------------
eslabones :: Int -> Int -> Int -> [Int]
eslabones i d n = [(i + d * x) `mod` n | x <- [0..]]
-- ---------------------------------------------------------------------
-- Ejercicio 2.2. Definir la función
-- numeroVueltas :: Int -> Int -> Int -> Int
-- tal que (numeroVueltas i d n) es el número de vueltas que pasarán
-- hasta que la cadena se rompa en una bicicleta de tipo (i,d,n). Por
-- ejemplo,
-- numeroVueltas 2 7 25 == 14
-- ---------------------------------------------------------------------
numeroVueltas :: Int -> Int -> Int -> Int
numeroVueltas i d n = length (takeWhile (/=0) (eslabones i d n))
-- ---------------------------------------------------------------------
-- § La sucesión de Golomb --
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 3.1. [Basado en el problema 341 del proyecto Euler]. La
-- sucesión de Golomb {G(n)} es una sucesión auto descriptiva: es la
-- única sucesión no decreciente de números naturales tal que el número
-- n aparece G(n) veces en la sucesión. Los valores de G(n) para los
-- primeros números son los siguientes:
-- n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...
-- G(n) 1 2 2 3 3 4 4 4 5 5 5 6 6 6 6 ...
-- En los apartados de este ejercicio se definirá una función para
-- calcular los términos de la sucesión de Golomb.
--
-- Definir la función
-- golomb :: Int -> Int
-- tal que (golomb n) es el n-ésimo término de la sucesión de Golomb.
-- Por ejemplo,
-- golomb 5 == 3
-- golomb 9 == 5
-- Indicación: Se puede usar la función sucGolomb del apartado 2.
-- ---------------------------------------------------------------------
golomb :: Int -> Int
golomb n = sucGolomb !! (n-1)
-- ---------------------------------------------------------------------
-- Ejercicio 3.2. Definir la función
-- sucGolomb :: [Int]
-- tal que sucGolomb es la lista de los términos de la sucesión de
-- Golomb. Por ejemplo,
-- take 15 sucGolomb == [1,2,2,3,3,4,4,4,5,5,5,6,6,6,6]
-- ---------------------------------------------------------------------
sucGolomb :: [Int]
sucGolomb = concat [x:replicate ((golomb x)-1) x | x <- [1..]]
-- ---------------------------------------------------------------------
-- Ejercicio 3.3. Definir la función
-- subSucGolomb :: Int -> [Int]
-- tal que (subSucGolomb x) es la lista de los términos de la sucesión
-- de Golomb a partir de la primera ocurrencia de x. Por ejemplo,
-- take 10 (subSucGolomb 4) == [4,4,4,5,5,5,6,6,6,6]
-- ---------------------------------------------------------------------
subSucGolomb :: Int -> [Int]
subSucGolomb x = dropWhile (<x) sucGolomb