aftovolio-0.8.0.0: Aftovolio/Halfsplit.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_HADDOCK -show-extensions #-}
{- |
Module : Aftovolio.Halfsplit
Copyright : (c) OleksandrZhabenko 2023, 2025
License : MIT
Stability : Experimental
Maintainer : oleksandr.zhabenko@yahoo.com
-}
module Aftovolio.Halfsplit where
import Data.Char (isDigit)
import Data.List hiding (foldr)
import Data.Lists.FLines (newLineEnding)
import Data.Tuple (fst)
import GHC.Base
import GHC.Enum (fromEnum)
import GHC.Int (Int8)
import GHC.Num (abs, (+), (-))
import GHC.Real (quot, quotRem, odd)
import System.IO (getLine, putStr, putStrLn)
import Text.Read (readMaybe)
import Data.Maybe (Maybe,fromMaybe)
import Text.Show (Show (..))
--import Debug.Trace
-- | Converts the data that is an instance of 'Show' typeclass to be printed in two-column way.
halfsplit ::
(Show a, Eq b) =>
(a -> b) ->
-- | Whether to filter out all groups of \'={digits}\' from the lines.
Bool ->
Int8 ->
[a] ->
String
halfsplit g filtering = halfsplit1G g filtering ""
{-# INLINE halfsplit #-}
{- | Converts the data that is an instance of 'Show' typeclass to be printed in two-column way with
customizable ending of each line. Filters out all groups of \'={digits}\' from the lines.
-}
halfsplit1G ::
(Show a, Eq b) =>
(a -> b) ->
-- | Whether to filter out all groups of \'={digits}\' from the lines.
Bool ->
-- | Additional 'String' added to every line before the newline character.
String ->
Int8 ->
[a] ->
String
halfsplit1G g filtering appendstr m xs
| null xs = []
| otherwise =
let (n, rr2) = quotRem (fromEnum m) (if m < 0 then -10 else 10)
rr3 = abs rr2
noChange = odd rr3
l0 = length . showFiltering filtering . head $ xs -- For empty lines
r
| rr3 > 1 && rr3 < 6 = (noChangeReverse noChange ws, noChangeReverse (not noChange) w2s)
| otherwise = splitTwoBasic filtering (rr3 == 1) l0 xs
where (ws,w2s) = splitTwo filtering (rr3 > 3) l0 g xs
in ((\(rs, qs) -> mergePartsLine n (appendstr `mappend` newLineEnding) rs qs) r)
`mappend` appendstr
showFiltering
:: (Show a)
=> Bool
-> a
-> String
showFiltering filtering = (if filtering then removeChangesOfDurations else id) . show
{-# INLINE showFiltering #-}
splitTwoBasic
:: (Show a) => Bool
-> Bool
-> Int
-> [a]
-> ([String],[String])
splitTwoBasic filtering first l0 xs = ((if rrr == 0 then id else (replicate l0 ' ' :)) . map (showFiltering filtering) . noChangeReverse first $ ys, map (showFiltering filtering) . noChangeReverse (not first) $ ts)
where
(ys, ts) = splitAt l xs -- Is used for basic splitting
(l, rrr) = length xs `quotRem` 2 -- For basic splitting
{-# INLINABLE splitTwoBasic #-}
noChangeF
:: Bool
-> ([a] -> [a])
-> [a]
-> [a]
noChangeF nochange f xs = if nochange then xs else f xs
{-# INLINE noChangeF #-}
noChangeReverse
:: Bool
-> [a]
-> [a]
noChangeReverse nochange = noChangeF nochange (reverse)
{-# INLINE noChangeReverse #-}
splitTwo
:: (Show a, Eq b)
-- | Whether to filter out all groups of \'={digits}\' from the lines.
=> Bool
-> Bool
-> Int
-> (a -> b)
-> [a]
-> ([String],[String])
splitTwo filtering emptyline l0 g xs = (y10s, t10s)
where
rss = (if emptyline then intersperse [""] else id) . map (map (showFiltering filtering)) . groupBy (\x y -> g x == g y) $ xs
(l2, r20) = (sum . map length $ rss) `quotRem` 2
(y100s, t10s) = splitAt l2 . concat $ rss
y10s
| r20 == 0 = y100s
| otherwise = "": y100s
{-# INLINABLE splitTwo #-}
-- | A generalized version of 'halfsplit3G' with the possibility to prepend and append strings to it. These 'String's are not filtered out for the groups of \'={digits}\' from the prepending and appending 'String's.
halfsplit2G ::
(Show a, Eq b) =>
(a -> b) ->
-- | Whether to filter out all groups of \'={digits}\' from the lines.
Bool ->
-- | Additional 'String' added to every line before the newline character.
String ->
-- | A 'String' that is prepended to the 'halfsplit1G' result.
String ->
-- | A 'String' that is appended to the 'halfsplit1G' result.
String ->
Int8 ->
[a] ->
String
halfsplit2G g filtering appendstr prestr poststr m xs = prestr `mappend` halfsplit1G g filtering appendstr m xs `mappend` poststr
{-# INLINABLE halfsplit2G #-}
-- | Filters out all groups of \"={digits}\" and \"_{digits}\" from the 'String'
removeChangesOfDurations :: String -> String
removeChangesOfDurations ('=' : xs) = removeChangesOfDurations (dropWhile isDigit xs)
removeChangesOfDurations ('_' : xs) = removeChangesOfDurations (dropWhile isDigit xs) -- added since aftovolio-0.8.0.0
removeChangesOfDurations (x : xs) = x : removeChangesOfDurations xs
removeChangesOfDurations _ = []
mergePartsLine :: Int -> String -> [String] -> [String] -> String
mergePartsLine n newlined xs ys =
intercalate newlined
. showWithSpacesBefore (replicate n (if n < 0 then '\t' else ' ')) xs $ ys
splitGroups :: Int -> [[a]] -> ([[a]], [[a]], Int)
splitGroups l tss = foldr h ([], [], 0) tss
where
h js (rss, mss, k)
| k < l = (rss, js : mss, k + length js)
| otherwise = (js : rss, mss, k + length js)
showWithSpaces :: (Show a) => Int -> a -> String
showWithSpaces n x
| l < n = xs `mappend` replicate (n - l) ' '
| otherwise = xs
where
xs = show x
l = length xs
showWithSpacesBefore
:: String
-> [String]
-> [String]
-> [String]
showWithSpacesBefore inserts (ts:xs:xss) (us:ys:yss)
| null xs = ws : (replicate (length ts) ' ' `mappend` inserts `mappend` ys) : showWithSpacesBefore inserts xss yss
| otherwise = ws : showWithSpacesBefore inserts (xs:xss) (ys:yss)
where ws = ts `mappend` inserts `mappend` us
showWithSpacesBefore inserts [xs] (ys:_) = [(if null xs then replicate (length ys) ' ' else xs) `mappend` inserts `mappend` ys]
showWithSpacesBefore inserts _ _ = []
print23 :: Bool -> String -> String -> Int -> [String] -> IO ()
print23 filtering prestr poststr n xss = do
putStrLn prestr
let linez = zip xss [1 ..]
if n >= 2 && n <= l - 1
then do
let linez3 =
(\(x : y : t : xs) -> x : (' ' : y) : (' ' : ' ' : t) : xs)
. map fst
. filter (\(ts, m) -> m `elem` [n - 1 .. n + 1]) $
linez
mapM putSLn linez3 >> putStrLn poststr
else
( case n of
1 -> putStr " " >> mapM putSLn (take 2 xss)
m ->
if m == l
then mapM putSLn ((\(x : y : xs) -> x : (' ' : y) : xs) . drop (l - 2) $ xss)
else pure []
)
>> putStrLn poststr
where
l = length xss
putSLn = putStrLn . (if filtering then removeChangesOfDurations else id)
readNums :: [String] -> [Int]
readNums = concatMap readNs
where readNs :: String -> [Int]
readNs xs
| any (== '-') xs =
let (ys, ts) = break (== '-') xs
us = dropWhile (not . isDigit) ts
in [(fromMaybe 1 (readMaybe ys:: Maybe Int))..(fromMaybe 1 (readMaybe us:: Maybe Int))]
| otherwise = [fromMaybe 1 (readMaybe xs:: Maybe Int)]
{-# INLINE readNums #-}