-- sort.hs: conjuring a sort function
--
-- Copyright (C) 2021-2025 Rudy Matela
-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
import Conjure
import Data.List (insert)

sort' :: [Int] -> [Int]
sort' []       =  []
sort' [0]      =  [0]
sort' [1]      =  [1]
sort' [0,2]    =  [0,2]
sort' [1,0]    =  [0,1]
sort' [0,1,2]  =  [0,1,2]
sort' [2,1,0]  =  [0,1,2]
sort' [1,0,1]  =  [0,1,1]
sort' [0,1,0,1]  =  [0,0,1,1]

insert' :: Int -> [Int] -> [Int]
insert' 0 []  =  [0]
insert' 0 [1,2]  =  [0,1,2]
insert' 1 [0,2]  =  [0,1,2]
insert' 2 [0,1]  =  [0,1,2]

merge' :: [Int] -> [Int] -> [Int]
merge' [] []  =  []
merge' [0] []  =  [0]
merge' [] [0]  =  [0]
merge' [0] [1]  =  [0,1]
merge' [1] [0]  =  [0,1]
merge' [1] [0,2]  =  [0,1,2]
merge' [0,2] [1]  =  [0,1,2]
merge' [2] [0,1]  =  [0,1,2]
merge' [0,1] [0,1]  =  [0,0,1,1]
merge' [0,1] [2,3]  =  [0,1,2,3]
merge' [0,2] [1,3]  =  [0,1,2,3]
merge' [0,1] [1,2]  =  [0,1,1,2]
merge' [1,2] [0,1]  =  [0,1,1,2]

main :: IO ()
main = do
  -- an insert function
  conjure "insert" insert'
    [ fun "[]" ([] :: [Int])
    , fun ":" ((:) :: Int -> [Int] -> [Int])
    , fun "<=" ((<=) :: Int -> Int -> Bool)
    , guard
    ]

  -- recursive insertion sort
  conjure "sort" sort'
    [ con ([] :: [Int])
    , fun "insert" (insert :: Int -> [Int] -> [Int])
    ]

  -- folding insertion sort
  conjure "sort" sort'
    [ con ([] :: [Int])
    , fun "insert" (insert :: Int -> [Int] -> [Int])
    , fun "foldr" (foldr :: (Int -> [Int] -> [Int]) -> [Int] -> [Int] -> [Int])
    ]

  -- found!  candidate #309279 @ size 22 after ~11s
  -- merge [] xs  =  xs
  -- merge (x:xs) []  =  x:xs
  -- merge (x:xs) (y:ys)
  --   | x <= y  =  x:merge xs (y:ys)
  --   | otherwise  =  y:merge ys (x:xs)
  -- set target to 360 000 to reach it
  conjure "merge" merge'
    [ con ([] :: [Int])
    , fun ":" ((:) :: Int -> [Int] -> [Int])
    , fun "<=" ((<=) :: Int -> Int -> Bool)
    , guard
    , maxTests 1080
    , target 3600  -- set to 360 000 to reach solution
    ]

  -- Produces a inefficient degenerate version of qsort
  -- where filter is applied _after_ sorting.
  conjure "qsort" sort'
    [ con ([] :: [Int])
    , fun ":" ((:) :: Int -> [Int] -> [Int])
    , fun "++" ((++) :: [Int] -> [Int] -> [Int])
    , fun "<=" ((<=) :: Int -> Int -> Bool)
    , fun ">"  ((>)  :: Int -> Int -> Bool)
    , fun "filter" (filter :: (Int -> Bool) -> [Int] -> [Int])
    -- if we disable the descent requirement and carry on,
    -- we eventually get the efficient qsort
    -- , dontRequireDescent
    -- , carryOn
    ]
