{-|
Module      : Data.List.Scroll
Description : Reordering an item within a list
Copyright   : (c) Felpe Santa-Cruz, 2021
License     : BSD3
Maintainer  : fesanmar@gmail.com
Stability   : stable
Portability : POSIX

This module provides functions for
relocate an item within a list.
-}
module Data.List.Scroll
    ( up
    , down
    ) where

{-|
  The 'up' function moves an element 'n' positions to 
  the beginning of a list. If the index provided is out 
  of range, the list is returned without any modification.
  On the other hand, extra steps will be ignored.

  Some examples are given below:

  >>> up 2 2 ["one", "two", "three"]
  ["three", "one", "two"]

  >>> up 4 1 ["one", "two", "three"]
  ["one", "two", "three"]

  >>> up 2 3 ["one", "two", "three"]
  ["three", "one", "two"]
-}
up :: Int -> Int -> [a] -> [a]
up :: Int -> Int -> [a] -> [a]
up Int
index Int
steps [a]
items
  | Int
steps Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 Bool -> Bool -> Bool
|| Int
index Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 Bool -> Bool -> Bool
|| Int
index Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
items = [a]
items
  | Bool
otherwise = Int -> Int -> [a] -> [a]
forall a. Int -> Int -> [a] -> [a]
up Int
prev (Int
steps Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) [a]
oneStepUpList
  where
    oneStepUpList :: [a]
oneStepUpList = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
prev [a]
items [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ ([a]
items [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
index) a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
items [a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
prev a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop Int
next [a]
items
    prev :: Int
prev = Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
    next :: Int
next = Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1

{-|
  The 'down' function moves an element 'n' positions to 
  the end of a list. If the index provided is out of range,
  the list is returned without any modification. On the other 
  hand, extra steps will be ignored.

  Some examples are given below:

  >>> down 0 1 ["one", "two", "three"]
  ["two", "one", "three"]

  >>> down 4 1 ["one", "two", "three"]
  ["one", "two", "three"]

  >>> down 0 4 ["one", "two", "three"]
  ["two", "three", "one"]
-}
down :: Int -> Int -> [a] -> [a]
down :: Int -> Int -> [a] -> [a]
down Int
index Int
steps [a]
items = [a] -> [a]
forall a. [a] -> [a]
reverse ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> [a] -> [a]
forall a. Int -> Int -> [a] -> [a]
up Int
reverseIndex Int
steps ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ [a] -> [a]
forall a. [a] -> [a]
reverse [a]
items
  where
    reverseIndex :: Int
reverseIndex = [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
items Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
index Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1