{-# OPTIONS --without-K --rewriting #-}

open import lib.Base
open import lib.Function
open import lib.PathFunctor
open import lib.PathGroupoid

open import lib.path-seq.Concat

module lib.path-seq.Split {i} {A : Type i} where

point-from-start : (n : ) {a a' : A} (s : a =-= a')  A
point-from-start O {a} s = a
point-from-start (S n) {a = a} [] = a
point-from-start (S n) (p ◃∙ s) = point-from-start n s

drop : (n : ) {a a' : A} (s : a =-= a')  point-from-start n s =-= a'
drop 0 s = s
drop (S n) [] = []
drop (S n) (p ◃∙ s) = drop n s

tail : {a a' : A} (s : a =-= a')  point-from-start 1 s =-= a'
tail = drop 1

private
  last1 : {a a' : A} (s : a =-= a')  A
  last1 {a = a} [] = a
  last1 {a = a} (p ◃∙ []) = a
  last1 (p ◃∙ s@(_ ◃∙ _)) = last1 s

  strip : {a a' : A} (s : a =-= a')  a =-= last1 s
  strip [] = []
  strip (p ◃∙ []) = []
  strip (p ◃∙ s@(_ ◃∙ _)) = p ◃∙ strip s

  point-from-end : (n : ) {a a' : A} (s : a =-= a')  A
  point-from-end O {a} {a'} s = a'
  point-from-end (S n) s = point-from-end n (strip s)

!- : (n : ) {a a' : A} (s : a =-= a')  a =-= point-from-end n s
!- O s = s
!- (S n) s = !- n (strip s)

take : (n : ) {a a' : A} (s : a =-= a')  a =-= point-from-start n s
take O s = []
take (S n) [] = []
take (S n) (p ◃∙ s) = p ◃∙ take n s

private
  take-drop-split' : {a a' : A} (n : ) (s : a =-= a')
     s == take n s ∙∙ drop n s
  take-drop-split' O s = idp
  take-drop-split' (S n) [] = idp
  take-drop-split' (S n) (p ◃∙ s) = ap  v  p ◃∙ v) (take-drop-split' n s)

abstract
  take-drop-split : {a a' : A} (n : ) (s : a =-= a')
      s ◃∎ =ₛ  (take n s) ◃∙  (drop n s) ◃∎
  take-drop-split n s = =ₛ-in $
     s
      =⟨ ap  (take-drop-split' n s) 
     (take n s ∙∙ drop n s)
      =⟨ ↯-∙∙ (take n s) (drop n s) 
     (take n s)   (drop n s) =∎

private
  split : {a a' : A} (s : a =-= a')
     Σ A  a''  Σ (a =-= a'')  _  a'' == a'))
  split {a = a} [] = (a , ([] , idp))
  split {a = a} (p ◃∙ []) = (a , ([] , p))
  split (p ◃∙ s@(_ ◃∙ _)) =
    let (a'' , (t , q)) = split s
    in (a'' , (p ◃∙ t) , q)

  point-from-end' : (n : ) {a a' : A} (s : a =-= a')  A
  point-from-end' n {a = a} [] = a
  point-from-end' O (p ◃∙ s) = point-from-end' O s
  point-from-end' (S n) (p ◃∙ s) = point-from-end' n (fst (snd (split (p ◃∙ s))))

#- : (n : ) {a a' : A} (s : a =-= a')  point-from-end' n s =-= a'
#- n [] = []
#- O (p ◃∙ s) = #- O s
#- (S n) (p ◃∙ s) = let (a' , (t , q)) = split (p ◃∙ s) in #- n t ∙▹ q

infix 120 _!0 _!1 _!2 _!3 _!4 _!5
_!0 = !- 0
_!1 = !- 1
_!2 = !- 2
_!3 = !- 3
_!4 = !- 4
_!5 = !- 5

0! = drop 0
1! = drop 1
2! = drop 2
3! = drop 3
4! = drop 4
5! = drop 5

infix 120 _#0 _#1 _#2 _#3 _#4 _#5
_#0 = #- 0
_#1 = #- 1
_#2 = #- 2
_#3 = #- 3
_#4 = #- 4
_#5 = #- 5

0# = take 0
1# = take 1
2# = take 2
3# = take 3
4# = take 4
5# = take 5