import Data.List
import Data.Array (accumArray, assocs)
-- 1ª solución --
-- ===========
maxGradoPerimetral1 :: Int -> (Int,[Int])
maxGradoPerimetral1 p = (m,[x | (n,x) <- ts, n == m])
where ts = [(length (triangulos x),x) | x <- [1..p]]
(m,_) = maximum ts
-- (triangulos p) es el conjunto de triángulos rectángulos de perímetro
-- p. Por ejemplo,
-- triangulos 120 == [(20,48,52),(24,45,51),(30,40,50)]
triangulos :: Int -> [(Int,Int,Int)]
triangulos p =
[(a,b,c) | a <- [1..q],
b <- [a..q],
let c = p-a-b,
a*a+b*b == c*c]
where q = p `div` 2
-- 2ª solución --
-- ===========
maxGradoPerimetral2 :: Int -> (Int,[Int])
maxGradoPerimetral2 p = (m,[x | (n,x) <- ts, n == m])
where ts = [(n,x) | (x,n) <- numeroPerimetrosTriangulos p, n > 0]
(m,_) = maximum ts
-- (numeroPerimetrosTriangulos p) es la lista formado por los números de
-- 1 a p y la cantidad de triángulos rectángulos enteros cuyo perímetro
-- es dicho número. Por ejemplo,
-- ghci> [(p,n) | (p,n) <- numeroPerimetrosTriangulos 70, n > 0]
-- [(12,1),(24,1),(30,1),(36,1),(40,1),(48,1),(56,1),(60,2),(70,1)]
numeroPerimetrosTriangulos :: Int -> [(Int,Int)]
numeroPerimetrosTriangulos p =
assocs (accumArray (\x _ -> 1+x) 0 (1,p) (perimetrosTriangulos p))
-- (perimetrosTriangulos p) es la lista formada por los perímetros y los
-- lados de los triángulos rectángulos enteros cuyo perímetro es menor o
-- igual que p. Por ejemplo,
-- ghci> perimetrosTriangulos 70
-- [(12,(3,4,5)), (30,(5,12,13)),(24,(6,8,10)), (56,(7,24,25)),
-- (40,(8,15,17)), (36,(9,12,15)),(60,(10,24,26)),(48,(12,16,20)),
-- (60,(15,20,25)),(70,(20,21,29))]
perimetrosTriangulos :: Int -> [(Int,(Int,Int,Int))]
perimetrosTriangulos p =
[(q,(a,b,c)) | a <- [1..p1],
b <- [a..p1],
esCuadrado (a*a+b*b),
let c = raizCuadradaE (a*a+b*b),
let q = a+b+c,
q <= p]
where p1 = p `div` 2
-- (esCuadrado n) se verifica si n es un cuadrado. Por ejemplo,
-- esCuadrado 25 == True
-- esCuadrado 27 == False
esCuadrado :: Int -> Bool
esCuadrado n = a*a == n
where a = raizCuadradaE n
-- (raizCuadradaE n) es la raíz cuadrada entera de n. Por ejemplo,
-- raizCuadradaE 25 == 5
-- raizCuadradaE 27 == 5
-- raizCuadradaE 35 == 5
-- raizCuadradaE 36 == 6
raizCuadradaE :: Int -> Int
raizCuadradaE = floor . sqrt . fromIntegral
-- 3ª solución --
-- ===========
maxGradoPerimetral3 :: Int -> (Int,[Int])
maxGradoPerimetral3 p = (m,[x | (n,x) <- ts, n == m])
where ts = [(n,x) | (x,n) <- numeroPerimetrosTriangulos2 p, n > 0]
(m,_) = maximum ts
-- (numeroPerimetrosTriangulos2 p) es la lista formado por los números de
-- 1 a p y la cantidad de triángulos rectángulos enteros cuyo perímetro
-- es dicho número. Por ejemplo,
-- ghci> [(p,n) | (p,n) <- numeroPerimetrosTriangulos2 70, n > 0]
-- [(12,1),(24,1),(30,1),(36,1),(40,1),(48,1),(56,1),(60,2),(70,1)]
numeroPerimetrosTriangulos2 :: Int -> [(Int,Int)]
numeroPerimetrosTriangulos2 p =
[(head xs, length xs) | xs <- group (sort (perimetrosTriangulos2 p))]
-- (perimetrosTriangulos2 p) es la lista formada por los perímetros de
-- los triángulos rectángulos enteros cuyo perímetro es menor o igual
-- que p. Por ejemplo,
-- perimetrosTriangulos2 70 == [12,30,24,56,40,36,60,48,60,70]
perimetrosTriangulos2 :: Int -> [Int]
perimetrosTriangulos2 p =
[q | a <- [1..p1],
b <- [a..p1],
esCuadrado (a*a+b*b),
let c = raizCuadradaE (a*a+b*b),
let q = a+b+c,
q <= p]
where p1 = p `div` 2
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- ghci> maxGradoPerimetral1 1000
-- (8,[840])
-- (120.08 secs, 21116625136 bytes)
-- ghci> maxGradoPerimetral2 1000
-- (8,[840])
-- (0.66 secs, 132959056 bytes)
-- ghci> maxGradoPerimetral3 1000
-- (1000,[1])
-- (0.66 secs, 133443816 bytes)