-- gps.hs: General Program Synthesis Benchmark Suite
--
-- Copyright (C) 2021-2025 Rudy Matela
-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
{-# LANGUAGE CPP #-}
import Conjure
import System.Environment (getArgs)
import Data.Char (isLetter) -- GPS bench #5
import Data.Char (isSpace) -- GPS bench #7
import Data.Ratio ((%), numerator, denominator) -- GPS bench #10
import Data.List (findIndex) -- GPS bench #12
import Data.Maybe (fromJust) -- GPS bench #12
import Data.List (sort) -- GPS bench #16
import Data.Char (chr,ord) -- GPS bench #24
-- GPS bench #16:
#if __GLASGOW_HASKELL__ >= 710
import Data.List (isSubsequenceOf)
#else
isSubsequenceOf :: Eq a => [a] -> [a] -> Bool
isSubsequenceOf [] _ = True
isSubsequenceOf (_:_) [] = False
isSubsequenceOf (x:xs) (y:ys)
| x == y = xs `isSubsequenceOf` ys
| otherwise = (x:xs) `isSubsequenceOf` ys
#endif
gps1p :: Int -> Float -> Float
gps1p 0 1.0 = 1.0
gps1p 1 0.0 = 1.0
gps1p 1 1.0 = 2.0
gps1p 1 1.5 = 2.5
gps1g :: Int -> Float -> Float
gps1g x f = fromIntegral x + f
gps1c :: IO ()
gps1c = conjure "gps1" gps1p
[ fun "+" ((+) :: Float -> Float -> Float)
, fun "fromIntegral" (fromIntegral :: Int -> Float)
]
gps1c30p :: IO ()
gps1c30p = conjure "gps1" gps1p
[ con (0 :: Float)
, con (1 :: Float)
, con (1/2 :: Float)
, con (-1 :: Float)
, fun "+" ((+) :: Float -> Float -> Float)
, fun "*" ((*) :: Float -> Float -> Float)
, fun "-" ((-) :: Float -> Float -> Float)
, fun "abs" (abs :: Float -> Float)
, fun "negate" (negate :: Float -> Float)
, fun "id" (id :: Float -> Float)
, fun "fromIntegral" (fromIntegral :: Int -> Float)
, con (0 :: Int)
, con (1 :: Int)
, con (-1 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "*" ((*) :: Int -> Int -> Int)
, fun "-" ((-) :: Int -> Int -> Int)
, fun "div" (div :: Int -> Int -> Int)
, fun "mod" (mod :: Int -> Int -> Int)
, fun "^" ((^) :: Int -> Int -> Int)
, fun "abs" (abs :: Int -> Int)
, fun "negate" (negate :: Int -> Int)
, fun "id" (id :: Int -> Int)
, fun "round" (round :: Float -> Int)
, fun "truncate" (truncate :: Float -> Int)
, con False
, con True
, fun "&&" (&&)
, fun "||" (||)
, fun "not" not
]
gps2p :: Int -> Maybe String
gps2p 0 = Just "small"
gps2p 500 = Just "small"
gps2p 1000 = Nothing
gps2p 1500 = Nothing
gps2p 2000 = Just "large"
gps2p 2500 = Just "large"
gps2g :: Int -> Maybe String
gps2g n
| n < 1000 = Just "small"
| 2000 <= n = Just "large"
| otherwise = Nothing
gps2c :: IO ()
gps2c = conjure "gps2" gps2p
[ con "small"
, con "large"
, con (1000 :: Int)
, con (2000 :: Int)
, fun "Just" (Just :: String -> Maybe String)
, fun "Nothing" (Nothing :: Maybe String)
, fun "<=" ((<=) :: Int -> Int -> Bool)
, fun "<" ((<) :: Int -> Int -> Bool)
, guard
, maxTests 5040
]
gps3p :: Int -> Int -> Int -> [Int]
gps3p 0 9 1 = [0,1,2,3,4,5,6,7,8]
gps3p 2 9 2 = [2,4,6,8]
gps3g1 :: Int -> Int -> Int -> [Int]
gps3g1 start end step = enumFromThenTo start (step+start) (end-1)
gps3g2 :: Int -> Int -> Int -> [Int]
gps3g2 start end step
| start < end = start : gps3g2 (start+step) end step -- 11
| otherwise = [] -- 12
gps3c :: IO ()
gps3c = do
conjure "gps3" gps3p
[ con (1 :: Int)
, fun "enumFromThenTo" ((\x y z -> take 720 $ enumFromThenTo x y z) :: Int -> Int -> Int -> [Int])
, fun "+" ((+) :: Int -> Int -> Int)
, fun "-" ((-) :: Int -> Int -> Int)
]
-- not possible, no recursive descent
conjure "gps3_alt" gps3p
[ con ([] :: [Int])
, fun ":" ((:) :: Int -> [Int] -> [Int])
, fun "+" ((+) :: Int -> Int -> Int)
, fun "<" ((<) :: Int -> Int -> Bool)
, guard
, dontRequireDescent
-- TODO: shouldn't a solution appear here?
]
gps4p :: String -> String -> String -> Bool
gps4p "" "a" "aa" = True
gps4p "aa" "a" "" = False
gps4p "a" "aa" "" = False
gps4p "a" "aa" "aaa" = True
gps4p "a" "aaa" "aa" = False
gps4p "aa" "a" "aaa" = False
gps4p "aa" "aaa" "a" = False
gps4p "aaa" "a" "aa" = False
gps4p "aaa" "aa" "a" = False
gps4g :: String -> String -> String -> Bool
gps4g s1 s2 s3 = length s1 < length s2 && length s2 < length s3
gps4c :: IO ()
gps4c = do
conjure "gps4" gps4p
[ fun "length" (length :: String -> Int)
, fun "<" ((<) :: Int -> Int -> Bool)
, fun "&&" (&&)
]
gps5p :: String -> String
gps5p "a" = "aa"
gps5p "b" = "bb"
gps5p " " = " "
gps5p "!" = "!!!"
gps5p "aa" = "aaaa"
gps5p " a" = " aa"
gps5p "!a" = "!!!aa"
gps5g :: String -> String
gps5g "" = ""
gps5g (c:cs)
| isLetter c = c:c:gps5g cs
| c == '!' = c:c:c:gps5g cs
| otherwise = c:gps5g cs
gps5c :: IO ()
gps5c = conjure "gps5" gps5p
[ con ""
, fun ":" ((:) :: Char -> String -> String)
, con '!'
, fun "==" ((==) :: Char -> Char -> Bool)
, fun "isLetter" (isLetter :: Char -> Bool)
, guard
-- BENCHMARK: increase to 26 to find
, maxSize 12 -- 26
]
-- found (lapmatrud unplugged):
-- -- 65.5s, 10856 candidates of size 26
-- -- 65.5s, tested 20317 candidates
-- gps5 "" = ""
-- gps5 (c:cs)
-- | isLetter c = c:c:gps5 cs
-- | c == '!' = c:c:c:gps5 cs
-- | otherwise = c:gps5 cs
-- GPS Benchmark #6 -- Collatz/Hailstone numbers --
-- Given an integer, find the number of terms in the Collatz (hailstone)
-- sequence starting from that integer.
-- NOTE: The sequence is actually always infinite,
-- so I assume it is the count until reaching 1.
gps6p :: Int -> Int
gps6p 1 = 1
gps6p 2 = 2
gps6p 3 = 8
gps6p 4 = 3
gps6p 5 = 6
gps6p 6 = 9
gps6p 12 = 10
gps6p 60 = 20
gps6p 360 = 20
gps6g :: Int -> Int
gps6g = tnp1
where
tnp1 n | n <= 0 = undefined
tnp1 1 = 1 -- 1
tnp1 n = 1 + gps6g (if even n -- 7
then n `div` 2 -- 10
else 3*n + 1) -- 15
nextCollatz' :: Int -> Int
nextCollatz' 1 = 4
nextCollatz' 2 = 1
nextCollatz' 3 = 10
nextCollatz' 4 = 2
nextCollatz' 5 = 16
nextCollatz' 6 = 3
nextCollatz :: Int -> Int
nextCollatz n
| even n = n `div` 2
| otherwise = 3*n + 1
gps6c :: IO ()
gps6c = do
-- This one is out of reach performance wise:
-- Speculate hangs with this background.
-- Removing three or setting maxEqSize to 4 makes it unhang.
-- But a size of 15 or 17 is simplyl out of our reach.
conjure "gps6direct" gps6p
[ con (1 :: Int)
, con (2 :: Int)
, con (3 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "*" ((*) :: Int -> Int -> Int)
, fun "`div`" (div :: Int -> Int -> Int)
, fun "even" (even :: Int -> Bool)
, iif (undefined :: Int)
, maxSize 6
, maxEquationSize 3 -- degenerate case in Speculate
]
conjure "gps6step" gps6p
[ con (1 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "nextCollatz" nextCollatz
, dontRequireDescent
]
conjure "nextCollatz" nextCollatz
[ con (1 :: Int)
, con (2 :: Int)
, con (3 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "*" ((*) :: Int -> Int -> Int)
, fun "`div`" (div :: Int -> Int -> Int)
, fun "even" (even :: Int -> Bool)
, guard
, target 720720
, maxEquationSize 3 -- degenerate case in Speculate
-- BENCHMARK: set maxSize 11
, maxSize 6 -- 11
]
-- -- on lapmatrud (2025-08), found after
-- -- 18.5s, 754952 candidates of size 11
-- -- 19.4s, tested 568616 candidates
-- nextCollatz x
-- | even x = x `div` 2
-- | otherwise = 1 + x * 3
-- GPS Benchmark #7 -- Replace Space with Newline (P 4.3)
-- Given a string input, print the string, replacing spaces with newlines.
-- Also, return the integer count of the non-whitespace characters.
-- The input string will not have tabs or newlines.
gps7p :: String -> (String, Int)
gps7p "a" = ("a", 1)
gps7p "aa" = ("aa", 2)
gps7p "a a" = ("a\na", 2)
gps7p "a\na" = ("a\na", 2)
gps7g :: String -> (String, Int)
gps7g s = (init $ unlines $ words s, length (filter (not . isSpace) s))
gps7c :: IO ()
gps7c = conjure "gps7" gps7p
[ fun "," ((,) :: String -> Int -> (String, Int))
, fun "init" (init :: String -> String)
, fun "unlines" unlines
, fun "words" words
, fun "length" (length :: String -> Int)
, fun "filter" (filter :: (Char -> Bool) -> String -> String)
, fun "not" not
, fun "." ((.) :: (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool) -- cheat?
, fun "isSpace" (isSpace :: Char -> Bool)
]
-- GPS Benchmark #8 -- String Differences
gps8p :: String -> String -> [(Int, Char, Char)]
gps8p "a" "a" = []
gps8p "a" "b" = [(0,'a','b')]
gps8p "aa" "ab" = [(1,'a','b')]
gps8p "dealer" "dollar" = [(1,'e','o'), (2,'a','l'),(4,'e','a')]
gps8g :: String -> String -> [(Int, Char, Char)]
gps8g = diffs 0
where
diffs _ [] _ = []
diffs _ _ [] = []
diffs n (c:cs) (d:ds) = if c == d
then diffs (n+1) cs ds
else (n,c,d) : diffs (n+1) cs ds
-- out of reach as Conjure cannot invent helper functions
-- even if that would be solved,
-- I conjecture it would be out-of-reach performance-wise.
gps8c :: IO ()
gps8c = conjure "gps8" gps8p
[
]
-- GPS Benchmark #9 -- Even Squares
-- given an integer _n_, print all of the positive even perfect squares less
-- than _n_ on separate lines.
gps9p :: Int -> [Int]
gps9p 10 = [4]
gps9p 100 = [4,16,36,64]
gps9p 1000 = [4,16,36,64,100,144,196,256,324,400,484,576,676,784,900]
-- non-optimal performance, but does the job
-- gps9g :: Int -> [Int]
-- gps9g n = [x*x | x <- [1..n], x*x < n, even (x*x)]
gps9g :: Int -> [Int]
gps9g n = filter (n >) (filter even (map sq [1..n]))
where
sq = (^2)
gps9c :: IO ()
gps9c = conjure "gps9" gps9p
[ con (1 :: Int)
, fun "map" (map :: (Int -> Int) -> [Int] -> [Int])
, fun "filter" (filter :: (Int -> Bool) -> [Int] -> [Int])
, fun ".." (enumFromTo :: Int -> Int -> [Int])
, fun ">" ((>) :: Int -> Int -> Bool)
, fun "even" (even :: Int -> Bool)
, fun "sq" ((^2) :: Int -> Int) -- invented separately
, maxTests 60
]
-- GPS Benchmark #10 -- Wallis Pi
-- (quarter pi approximation)
-- 2 4 4 6 6 8 8
-- - x - x - x - x - x - x - x ...
-- 3 3 5 5 7 7 9
gps10p :: Int -> Rational
gps10p 1 = 2/3
gps10p 2 = 8/9
gps10p 3 = 32/45
gps10p 4 = 64/75
gps10p 5 = 128/175
gps10p 6 = 1024/1225
gps10g :: Int -> Rational
gps10g n = product $ take n $ iterate wallisNext (2/3)
wallisNextP :: Rational -> Rational
wallisNextP q
| q == 2/3 = 4/3
| q == 4/3 = 4/5
| q == 4/5 = 6/5
| q == 6/5 = 6/7
| q == 6/7 = 8/7
| q == 8/7 = 8/9
wallisNext :: Rational -> Rational
wallisNext q = if n < d
then (n+2) % d
else n % (d+2)
where
n = numerator q
d = denominator q
-- wallisNext (x % y) = (y + (y + 2)) % (x + (x + 2)) -- which simplifies to...
-- wallisNext (x % y) = (x + x * y) % (x + x * x) -- which simplifies to...
-- wallisNext (x % y) = (y + 1) % (x + 1) -- this correct version
gps10c :: IO ()
gps10c = do
conjure "wallisNext" wallisNextP
[ con (1 :: Integer)
, con (2 :: Integer)
, fun "+" ((+) :: Integer -> Integer -> Integer)
, fun "*" ((*) :: Integer -> Integer -> Integer)
, fun "%" ((%) :: Integer -> Integer -> Rational)
, fun "<" ((<) :: Integer -> Integer -> Bool)
-- , fun "numerator" (numerator :: Rational -> Integer)
-- , fun "denominator" (denominator :: Rational -> Integer)
, guard
]
-- simplified background
conjure "wallisNext" wallisNextP
[ con (0 :: Integer)
, con (1 :: Integer)
, fun "+" ((+) :: Integer -> Integer -> Integer)
, fun "*" ((*) :: Integer -> Integer -> Integer)
, fun "%" ((%) :: Integer -> Integer -> Rational)
]
conjure "gps10" gps10p
[ con (2 :: Integer)
, con (3 :: Integer)
, fun "%" ((%) :: Integer -> Integer -> Rational)
-- , con (2/3 :: Rational)
, fun "product" (product :: [Rational] -> Rational)
, fun "take" (take :: Int -> [Rational] -> [Rational])
, fun "iterate" ((\f -> take 720 . iterate f) :: (Rational -> Rational) -> Rational -> [Rational])
, fun "wallisNext" wallisNext
]
-- GPS Benchmark #11 -- Lengths Backwards
gps11p :: [String] -> [Int]
gps11p ["a"] = [1]
gps11p ["aa","a"] = [1,2]
gps11p ["a","aa"] = [2,1]
gps11p ["a","aa","aaa"] = [3,2,1]
gps11g :: [String] -> [Int]
gps11g = reverse . map length
gps11g2 :: [String] -> [Int]
gps11g2 [] = []
gps11g2 (s:ss) = gps11g2 ss ++ [length s]
gps11c :: IO ()
gps11c = do
conjure "gps11" gps11p
[ fun "reverse" (reverse :: [Int] -> [Int])
, fun "length" (length :: String -> Int)
, fun "map" (map :: (String -> Int) -> [String] -> [Int])
]
conjure "gps11" gps11p
[ con ([] :: [Int])
, fun ":" ((:) :: Int -> [Int] -> [Int])
, fun "++" ((++) :: [Int] -> [Int] -> [Int])
, fun "length" (length :: String -> Int)
]
-- GPS Benchmark #12 -- Last index or zero
gps12p :: [Int] -> Int
gps12p [0] = 0
gps12p [0,0] = 1
gps12p [0,1,0] = 2
gps12p [1,0,1] = 1
gps12p [0,1,0,1] = 2
gps12p [1,0,1,0] = 3
gps12g :: [Int] -> Int
gps12g xs = length xs - fromJust (findIndex (0==) (reverse xs)) - 1
gps12c :: IO ()
gps12c = do
conjure "gps12" gps12p
[ fun "length" (length :: [Int] -> Int)
, fun "reverse" (reverse :: [Int] -> [Int])
, fun "findIndex" (findIndex :: (Int -> Bool) -> [Int] -> Maybe Int)
, fun "fromJust" (fromJust :: Maybe Int -> Int)
, fun "-" ((-) :: Int -> Int -> Int)
, fun "==" ((==) :: Int -> Int -> Bool)
, con (0 :: Int)
, con (1 :: Int)
, maxSize 11
]
-- GPS Benchmark #13 -- Vector Average --
gps13p :: [Rational] -> Rational
gps13p [0,2] = 1
gps13p [1,3] = 2
gps13p [1,2,3] = 2 -- not hit, but nvm
gps13p [0,0,2,2] = 1
gps13g :: [Rational] -> Rational
gps13g qs = foldr (+) 0 qs / fromIntegral (length qs)
gps13c :: IO ()
gps13c = do
conjure "gps13" gps13p
[ fun "0" (0 :: Rational)
, fun "+" ((+) :: Rational -> Rational -> Rational)
, fun "/" ((/) :: Rational -> Rational -> Rational)
, fun "foldr" (foldr :: (Rational -> Rational -> Rational) -> Rational -> [Rational] -> Rational)
, fun "length" (length :: [Rational] -> Int)
, fun "fromIntegral" (fromIntegral :: Int -> Rational)
]
-- GPS Benchmark #14 -- Count Odds --
gps14p :: [Int] -> Int
gps14p [0,0] = 0
gps14p [1,3] = 2
gps14p [0,1,2] = 1
gps14g :: [Int] -> Int
gps14g xs = length (filter odd xs)
where
odd x = x `mod` 2 /= 0
gps14g2 :: [Int] -> Int
gps14g2 [] = 0
gps14g2 (x:xs) = if x `mod` 2 == 0
then gps14g2 xs
else 1 + gps14g2 xs
odd' :: Int -> Bool
odd' 0 = False
odd' 1 = True
odd' 2 = False
odd' 3 = True
odd' 4 = False
odd' 5 = True
gps14c :: IO ()
gps14c = do
conjure "odd" odd'
[ con (0 :: Int)
, con (1 :: Int)
, con (2 :: Int)
, fun "`mod`" (mod :: Int -> Int -> Int)
, fun "/=" ((/=) :: Int -> Int -> Bool)
]
conjure "gps14" gps14p
[ fun "odd" (odd :: Int -> Bool)
, fun "filter" (filter :: (Int -> Bool) -> [Int] -> [Int])
, fun "length" (length :: [Int] -> Int)
]
-- hah! I was expecting Conjure to use an if like above, but it was smarter:
-- gps14 [] = 0
-- gps14 (x:xs) = x `mod` 2 + gps14 xs
conjure "gps14" gps14p
[ con (0 :: Int)
, con (1 :: Int)
, con (2 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "`mod`" (mod :: Int -> Int -> Int)
, fun "==" ((==) :: Int -> Int -> Bool)
, guard
]
-- GPS Benchmark #15 -- Mirror Image --
gps15p :: [Int] -> [Int] -> Bool
gps15p [0] [0] = True
gps15p [0,1] [1,0] = True
gps15p [0,1] [0,1] = False
gps15p [0,0,1] [1,0,0] = True
gps15p [0,0,1] [0,0,1] = False
gps15g :: [Int] -> [Int] -> Bool
gps15g xs ys = reverse xs == ys
gps15c :: IO ()
gps15c = do
conjure "gps15" gps15p
[ fun "==" ((==) :: [Int] -> [Int] -> Bool)
, fun "reverse" (reverse :: [Int] -> [Int])
]
-- GPS Benchmark #16 -- Super Anagrams --
gps16p :: String -> String -> Bool
gps16p "a" "aa" = True
gps16p "aa" "a" = False
gps16p "ab" "ba" = True
gps16p "ba" "ab" = True
gps16p "ab" "c" = False
gps16p "ab" "aba" = True
gps16g :: String -> String -> Bool
gps16g cs ds = sort cs `isSubsequenceOf` sort ds
gps16c :: IO ()
gps16c = do
conjure "gps16" gps16p
[ fun "`isSubsequenceOf`" (isSubsequenceOf :: String -> String -> Bool)
, fun "sort" (sort :: String -> String)
]
-- GPS Benchmark #17 -- Sum of Squares --
-- Given integer _n_, return the sum of squaring each integer in 1 to n
gps17p :: Int -> Int
gps17p 1 = 1
gps17p 2 = 5
gps17p 3 = 14
gps17p 4 = 30
gps17g :: Int -> Int
gps17g n = n * n + gps17g (n - 1)
gps17c :: IO ()
gps17c = conjure "gps17" gps17p
[ con (0 :: Int)
, con (1 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "*" ((*) :: Int -> Int -> Int)
, fun "-" ((-) :: Int -> Int -> Int)
]
-- GPS Benchmark #18 -- Vectors Summed --
-- zip with sum
gps18p :: [Int] -> [Int] -> [Int]
gps18p [0] [0] = [0]
gps18p [0,1] [0,1] = [0,2]
gps18p [1,0] [0,1] = [1,1]
gps18g :: [Int] -> [Int] -> [Int]
gps18g xs ys = zipWith (+) xs ys
gps18g' :: [Int] -> [Int] -> [Int]
gps18g' [] [] = []
gps18g' (x:xs) (y:ys) = x + y : gps18g' xs ys
gps18c :: IO ()
gps18c = do
conjure "gps18" gps18p
[ con ([] :: [Int])
, fun "+" ((+) :: Int -> Int -> Int)
, fun ":" ((:) :: Int -> [Int] -> [Int])
]
conjure "gps18" gps18p
[ fun "+" ((+) :: Int -> Int -> Int)
, fun "zipWith" (zipWith :: (Int -> Int -> Int) -> [Int] -> [Int] -> [Int])
]
-- GPS Benchmark #19 -- X-Word Lines --
gps19p :: Int -> String -> String
gps19p 1 "a a a" = "a\na\na\n"
gps19p 1 "a\na\na" = "a\na\na\n"
gps19p 2 "a a a" = "a a\na\n"
gps19g :: Int -> String -> String
gps19g x xs = unlines (map unwords (chunk x (words xs)))
chunk :: Int -> [String] -> [[String]]
chunk x [] = []
chunk x ss = take x ss : chunk x (drop x ss)
gps19c :: IO ()
gps19c = do
-- speculate takes too long
conjure "gps19" gps19p $ take 0
[ fun "words" (words :: String -> [String])
-- , fun "words" (lines :: String -> [String])
, fun "unwords" (unwords :: [String] -> String)
, fun "unlines" (unlines :: [String] -> String)
-- , fun "chunk" (chunk :: Int -> [String] -> [[String]])
-- , fun "map" (map :: ([String] -> String) -> [[String]] -> [String])
]
-- GPS Benchmark #20 -- Pig Latin --
gps20s :: (String -> String) -> [Property]
gps20s pig =
[ property $ pig "hello world" == "ellohay orldway"
, property $ pig "a string" == "aay tringsay"
]
gps20g :: String -> String
gps20g = pig
pig :: String -> String
pig = unwords . map pig1 . words
pig1 :: String -> String
pig1 (c:cs) = if isVowel c
then (c:cs) ++ "ay"
else cs ++ (c:"ay")
pig1Spec :: (String -> String) -> [Property]
pig1Spec pig1 =
[ property $ pig1 "hello" == "ellohay"
, property $ pig1 "world" == "orldway"
, property $ pig1 "string" == "tringsay"
, property $ pig1 "east" == "eastay"
]
isVowel :: Char -> Bool
isVowel 'a' = True
isVowel 'e' = True
isVowel 'i' = True
isVowel 'o' = True
isVowel 'u' = True
isVowel 'y' = True
isVowel _ = False
isVowel' :: Char -> Bool
isVowel' 'a' = True
isVowel' 'e' = True
isVowel' 'i' = True
isVowel' 'o' = True
isVowel' 'u' = True
isVowel' 'y' = True
isVowel' ' ' = False
isVowel' 'b' = False
isVowel' 'c' = False
isVowel' 'd' = False
isVowel' 'f' = False
isVowel' 'g' = False
gps20c :: IO ()
gps20c = do
conjure "isVowel" isVowel'
[ con 'a'
, con 'e'
, con 'i'
, con 'o'
, con 'u'
, con 'y'
, con True
, con False
]
conjureFromSpec "pig1" pig1Spec
[ con "ay"
, guard
, fun "isVowel" isVowel
, fun "++" ((++) :: String -> String -> String)
, fun ":" ((:) :: Char -> String -> String)
, target 50400
]
conjureFromSpec "gps20c" gps20s
[ fun "words" (words :: String -> [String])
, fun "unwords" (unwords :: [String] -> String)
, fun "map" (map :: (String -> String) -> [String] -> [String])
, fun "pig1" pig1
]
-- GPS Benchmark #21 -- Negative To Zero --
gps21p :: [Int] -> [Int]
gps21p [1] = [1]
gps21p [-1] = [0]
gps21p [1,-1] = [1,0]
gps21p [-1,1] = [0,1]
gps21p [-1,-1] = [0,0]
gps21g :: [Int] -> [Int]
gps21g [] = []
gps21g (x:xs) = (if x < 0 then 0 else x) : gps21g xs
gps21c :: IO ()
gps21c = conjure "gps21" gps21p
[ con ([] :: [Int])
, con (0 :: Int)
, fun ":" ((:) :: Int -> [Int] -> [Int])
, fun "<" ((<) :: Int -> Int -> Bool)
, guard
]
-- GPS Benchmark #22 -- Scrabble Score --
gps22s :: (String -> Int) -> [Property]
gps22s scrabble =
[ property $ scrabble "a" == 1
, property $ scrabble "hello" == 8
, property $ scrabble "world" == 9
, property $ scrabble "scrabble" == 14
]
gps22g :: String -> Int
gps22g = scrabble
scrabble :: String -> Int
scrabble s = foldr (+) 0 (map scrabble1 s)
scrabble1 :: Char -> Int
scrabble1 'd' = 2
scrabble1 'g' = 2
scrabble1 'b' = 3
scrabble1 'c' = 3
scrabble1 'm' = 3
scrabble1 'p' = 3
scrabble1 'f' = 4
scrabble1 'h' = 4
scrabble1 'v' = 4
scrabble1 'w' = 4
scrabble1 'y' = 4
scrabble1 'k' = 5
scrabble1 'j' = 8
scrabble1 'x' = 8
scrabble1 'q' = 10
scrabble1 'z' = 10
scrabble1 _ = 1 -- aeilnorstu
gps22c :: IO ()
gps22c = do
conjure "scrabble1" scrabble1 $
[ con (1 :: Int)
-- comment-out the following to find function
-- at size 137 after 1025 candidates in 6.0s
-- limited here for faster runtimes
, maxSize 13
] ++ map con "dgbcmpfhvwykjxqz"
conjureFromSpec "gps22" gps22s
[ con (0 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "map" (map :: (Int -> Int) -> [Int] -> [Int])
, fun "scrabble1" scrabble1
]
-- GPS Benchmark #23 -- Word Stats --
gps23p :: String -> ([(Int,Int)], Int, Double)
gps23p = undefined
-- gps23p "" = ([], 0, 1.0/0.0)
gps23c :: IO ()
gps23c = do
conjure "gps23" gps23p []
-- GPS Benchmark #24 -- Checksum --
gps24p :: String -> Char
gps24p "a" = 'A'
gps24p "aa" = '"'
gps24p "a a" = 'B'
gps24p "b" = 'B'
gps24g :: String -> Char
gps24g s = chr (sum (map ord s) `mod` 64 + ord ' ')
gps24c :: IO ()
gps24c = conjure "gps24" gps24p
[ con ' '
, con (64 :: Int)
, fun "+" ((+) :: Int -> Int -> Int)
, fun "`mod`" (mod :: Int -> Int -> Int)
, fun "sum" (sum :: [Int] -> Int)
, fun "ord" ord
, fun "chr" chr
, fun "map" (map :: (Char -> Int) -> String -> [Int])
-- The following flag is here so behaviour is consistent between different
-- GHC versions (8.8 and later). It makes no significant difference in
-- runtime for this example.
, maxEarlyTests 0
]
-- GPS Benchmark #25 -- Digits --
gps25p :: Int -> [Int]
gps25p 0 = [0]
gps25p 1 = [1]
gps25p 12 = [2,1]
gps25p (-21) = [1,-2]
gps25g :: Int -> [Int]
gps25g 0 = [0] -- 3
gps25g n = if abs n < 10 -- 8
then [n] -- 11
else abs (n `rem` 10) : gps25g (n `quot` 10) -- 20
-- out of reach performance-wise
gps25c :: IO ()
gps25c = conjure "gps25" gps25p
[ con (0 :: Int)
, con (10 :: Int)
, con ([] :: [Int])
, fun ":" ((:) :: Int -> [Int] -> [Int])
, guard
, fun "abs" (abs :: Int -> Int)
, fun "<" ((<) :: Int -> Int -> Bool)
, fun "rem" (rem :: Int -> Int -> Int)
, fun "quot" (quot :: Int -> Int -> Int)
]
-- GPS Benchmark #26 -- Grade --
gps26p :: Int -> Int -> Int -> Int -> Int -> Char
gps26p 4 3 2 1 5 = 'A'
gps26p 4 3 2 1 4 = 'A'
gps26p 4 3 2 1 3 = 'B'
gps26p 4 3 2 1 2 = 'C'
gps26p 4 3 2 1 1 = 'D'
gps26p 4 3 2 1 0 = 'F'
gps26g :: Int -> Int -> Int -> Int -> Int -> Char
gps26g a b c d x
| x >= a = 'A' -- 5
| x >= b = 'B' -- 10
| x >= c = 'C' -- 15
| x >= d = 'D' -- 20
| otherwise = 'F' -- 21
-- out of reach performance-wise, OOM at size 21, see:
-- -- 20.6s, 4000000 candidates of size 16
-- -- 38.2s, 0 candidates of size 20
-- gps: out of memory
gps26c :: IO ()
gps26c = conjure "gps26" gps26p
[ con 'A'
, con 'B'
, con 'C'
, con 'D'
, con 'F'
, guard
, fun ">=" ((>=) :: Int -> Int -> Bool)
]
-- GPS Benchmark #27 -- Median --
-- Given three integers, print their median.
gps27p :: Int -> Int -> Int -> Int
gps27p 0 1 2 = 1
gps27p 0 2 1 = 1
gps27p 2 3 1 = 2
gps27p 1 0 2 = 1
gps27p 4 3 1 = 3
gps27p 4 1 3 = 3
gps27g :: Int -> Int -> Int -> Int
gps27g x y z
| y <= x && x <= z || z <= x && x <= y = x -- 16
| x <= y && y <= z || z <= y && y <= x = y -- 32
| otherwise = z -- 33
gps27c :: IO ()
gps27c = do
-- simply unreachable performance-wise
-- it gets OOM at size 25 after >83s.
conjure "gps27_median" gps27p
[ fun "<=" ((<=) :: Int -> Int -> Bool)
, fun "&&" (&&)
, fun "||" (||)
, guard
]
conjure "gps27b_median" gps27p
[ fun "min" (min :: Int -> Int -> Int)
, fun "max" (max :: Int -> Int -> Int)
]
-- GPS Benchmark #28 -- Smallest --
gps28p :: Int -> Int -> Int -> Int -> Int
gps28p 0 1 2 3 = 0
gps28p 3 2 1 0 = 0
gps28p 1 0 2 3 = 0
gps28p 3 2 0 1 = 0
gps28p 1 1 1 2 = 1
gps28c :: IO ()
gps28c = conjure "gps28" gps28p
[ fun "`min`" (min :: Int -> Int -> Int)
]
-- GPS Benchmark #29 -- Syllables --
gps29s :: (String -> Int) -> [Property]
gps29s sys =
[ property $ sys "pub" == 1
, property $ sys "hello" == 2
, property $ sys "world" == 1
, property $ sys "string" == 1
, property $ sys "haskell" == 2
, property $ sys "photography" == 4
]
gps29g :: String -> Int
gps29g "" = 0
gps29g (c:cs) = if isVowel c
then 1 + gps29g cs
else gps29g cs
gps29c :: IO ()
gps29c = conjureFromSpec "gps29" gps29s
[ con (0 :: Int)
, con (1 :: Int)
, fun "+" ((+) :: Int->Int->Int)
, guard
, fun "isVowel" isVowel
]
main :: IO ()
main = do
as <- getArgs
case as of
[] -> sequence_ gpss
(n:_) -> gpss !! (read n - 1)
gpss :: [IO ()]
gpss = [ gps1c
, gps2c
, gps3c
, gps4c
, gps5c
, gps6c
, gps7c
, gps8c
, gps9c
, gps10c
, gps11c
, gps12c
, gps13c
, gps14c
, gps15c
, gps16c
, gps17c
, gps18c
, gps19c
, gps20c
, gps21c
, gps22c
, gps23c
, gps24c
, gps25c
, gps26c
, gps27c
, gps28c
, gps29c
]