{-# OPTIONS --cubical --safe #-}

module Data.Bits.Fold where

open import Data.Bits
open import Strict
open import Prelude

module _ {a} {A : Type a} (zer : A  A) (one : A  A) (base : A) where
  foldr-bits : Bits  A
  foldr-bits [] = base
  foldr-bits (0∷ xs) = zer (foldr-bits xs)
  foldr-bits (1∷ xs) = one (foldr-bits xs)

foldr-universal :  (h : Bits  A) z o e 
                    (h []  e) 
                    (∀ xs  h (0∷ xs)  z (h xs)) 
                    (∀ xs  h (1∷ xs)  o (h xs)) 
                     xs  h xs  foldr-bits z o e xs
foldr-universal h z o e base zer one [] = base
foldr-universal h z o e base zer one (0∷ xs) = zer xs ; cong z (foldr-universal h z o e base zer one xs)
foldr-universal h z o e base zer one (1∷ xs) = one xs ; cong o (foldr-universal h z o e base zer one xs)

module _ {a} {A : Type a} (zer : A  A) (one : A  A) where
  foldl-go : Bits  A  A
  foldl-go []      base = base
  foldl-go (0∷ xs) base = foldl-go xs $! zer base
  foldl-go (1∷ xs) base = foldl-go xs $! one base

  foldl-bits : A  Bits  A
  foldl-bits = λ base xs  foldl-go xs $! base
  {-# INLINE foldl-bits #-}

foldl-universal :  (h : Bits  A  A) z o e 
                  (∀ xs bs  h (0∷ xs) bs  h xs (z bs)) 
                  (∀ xs bs  h (1∷ xs) bs  h xs (o bs)) 
                  (∀ bs  h [] bs  bs) 
                   xs  h xs e  foldl-bits z o e xs
foldl-universal h z o e zer one bs [] = bs e ; sym ($!-≡ (foldl-go z o []) e)
foldl-universal h z o e zer one bs (0∷ xs) = zer xs e ; foldl-universal h z o (z e) zer one bs xs ; sym ($!-≡ (foldl-go z o (0∷ xs)) e)
foldl-universal h z o e zer one bs (1∷ xs) = one xs e ; foldl-universal h z o (o e) zer one bs xs ; sym ($!-≡ (foldl-go z o (1∷ xs)) e)