-- |
-- 第1章 練習問題 A
-- 
module TFwH.Chap01.ExA where

import Prelude hiding (sum,concat)

-- |
-- 指定した整数の2倍
--
-- @
-- double x = 2 * x
-- @
--
-- >>> map double [1, 4, 4, 3]
-- [2,8,8,6]
-- >>> map (double . double) [1, 4, 4, 3]
-- [4,16,16,12]
-- >>> map double []
-- []
--
double :: Integer -> Integer
double x = 2 * x

-- |
-- 整数のリストの総和
--
-- @
-- sum [] = 0
-- sum (x:xs) = x + sum xs
-- @
--
-- @
-- 法則: sum . map double = double . sum
-- @
--
-- prop> (sum . map double) (xs :: [Integer]) == (double . sum) xs
-- 
-- @
-- [] の場合:
--   sum (map double [])
-- =   { mapの定義 }
--   sum []
-- =   { sumの定義 }
--   0
-- =   { 算術 }
--   2 * 0
-- =   { doubleの定義 }
--   double 0
-- =   { sumの定義 }
--   double (sum [])
--
-- x:xs の場合:
--   sum (map double (x:xs))
-- =   { mapの定義 }
--   sum (double x : map double (x:xs))
-- =   { sumの定義 }
--   double x + sum (map double xs)
-- =   { 帰納法の仮定 }
--   double x + double (sum xs)
-- =   { doubleの定義 }
--   2 * x + 2 * sum xs
-- =   { 分配則 }
--   2 * (x + sum xs)
-- =   { sumの定義 }
--   2 * (sum (x:xs))
-- =   { doubleの定義 }
--   double (sum (x:xs))
-- @
--
sum :: [Integer] -> Integer
sum [] = 0
sum (x:xs) = x + sum xs

-- |
-- リストのリストを連結
--
-- @
-- concat [] = []
-- concat (xs:xss) = xs ++ concat xss
-- @
--
-- @
-- 法則: sum . map sum = sum . concat
-- @
--
-- @
-- [] の場合:
--   sum (map sum [])
-- =   { mapの定義 }
--   sum []
-- =   { concatの定義 }
--   sum (concat [])
--
-- xs:xss の場合
--   sum (map sum (xs:xss))
-- =   { mapの定義 }
--   sum (sum xs : map sum xss)
-- =   { sumの定義 }
--   sum xs + sum (map sum xss)
-- =   { 帰納法の仮定 }
--   sum xs + sum (concat xss)
-- =   { sum xs + sum ys = sum (xs ++ ys) だから (1)}
--   sum ( xs ++ concat xss)
-- =   { concatの定義 }
--   sum ( concat (xs:xss))
-- @
--
-- @
-- 主張(1) xs 上の帰納法
--
-- []の場合:
--   sum [] + sum ys
-- =  { sumの定義 }
--   0 + sum ys
-- =  { 0は(+)の単位元 }
--   sum ys
-- =  { []は(++)の単位元 }
--   sum ([] ++ ys)
--
-- x:xsの場合:
--   sum (x:xs) + sum ys
-- =   { sumの定義 }
--   (x + sum xs) + sum ys
-- =   { (+)の結合性 }
--   x + (sum xs + sum ys)
-- =   { 帰納法の仮定 }
--   x + sum (xs ++ ys)
-- =   { sum の定義 }
--   sum (x : (xs ++ ys))
-- =   { (++)の定義 }
--   sum ((x:xs) ++ ys)
-- @

concat :: [[a]] -> [a]
concat [] = []
concat (xs:xss) = xs ++ concat xss

-- |
-- 整列
--
-- @
-- sort [] = []
-- sort (x:xs) = insert x (sort xs)
-- @
--
-- @
-- 法則: sum . sort = sum
-- @
--
-- @
-- [] の場合:
--   sum (sort [])
-- =   { sortの定義 }
--   sum []
--
-- (x:xs)
--   sum (sort (x:xs))
-- =   { sortの仕様 }
--   sum (insert x (sort xs))
-- =   { sum (insert y ys) = y + sum ys だから (2)}
--   x + sum (sort xs)
-- =   { 帰納法の仮定 }
--   x + sum xs
-- =   { sumの定義 }
--   sum (x:xs)
-- @
--
sort :: Ord a => [a] -> [a]
sort [] = []
sort (x:xs) = insert x (sort xs)

-- |
-- ソート済みのリストに新しい要素を挿入
--
-- @
-- insert x [] = [x]
-- insert x (y:ys) = if x <= y then x : y : ys
--                   else y : insert x ys
-- @
--
-- @
-- 主張(2) ys 上の帰納法
--
-- []の場合:
--   sum (insert x [])
-- =   { insertの定義 }
--   sum [x]
-- =   { sumの定義 }
--   x + sum []
--
-- y:ysの場合: x <= y ならば
--   sum (insert x (y:ys))
-- =   { insertの定義 }
--   sum (x:(y:ys))
-- =   { sumの定義 }
--   x + sum (y:ys)
--
-- y:ysの場合: x > y ならば
--   sum (insert x (y:ys))
-- =   { insertの定義 }
--   sum (y : insert x ys)
-- =   { sumの定義 }
--   y + sum (insert x ys)
-- =   { 帰納法の仮定 }
--   y + (x + sum ys)
-- =   { (+)の結合性 }
--   (y + x) + sum ys
-- =   { (+)の可換性 }
--   (x + y) + sum ys
-- =   { (+)の結合性 }
--   x + (y + sum ys)
-- =   { sumの定義 }
--   x + sum (y:ys)
-- @
--
insert :: Ord a => a -> [a] -> [a]
insert x [] = [x]
insert x (y:ys) = if x <= y then x : y : ys
                  else y : insert x ys