Compressed lists in Cubical Agda

Posted on May 31, 2021 by Guilherme

Motivation

There is a simple problem of string compression that can be found in Hacker Rank or in LeetCode that consists of compressing each element of string by the number of times that it appears. For example, “aa” is compressed to “a2” and “bbccc” is compressed to “b2c3”. In this post, I will create a data structure in cubical Agda that has compressed and uncompressed formats.

Imports

Importing Cubical Libraries:

{-# OPTIONS --cubical #-}

open import Cubical.Relation.Nullary.Base
open import Cubical.Relation.Nullary.Properties
open import Cubical.Foundations.Prelude
open import Cubical.Data.List
open import Cubical.Data.Nat

module _ where
  module compListMod {} (A : Set ) (discA : Discrete A) where

Definition

Compressed lists can be an empty list or a valued repeated one or more times joined with another compressed list. Two compressed lists are equal when they are joined with the same value. For example: “a1a2” ≡ “a3” ≡ “a1a2”. In the next lines, the definition of compressed lists with these properties:

    infixr 5 _qt1+_∷_
    data CompressedList : Set  where
      []           : CompressedList
      _qt1+_∷_       : A    CompressedList  CompressedList
      sameQuantity : (m n : ) (x : A) (xs : CompressedList)
         x qt1+ m  x qt1+ n  xs  x qt1+ 1 + m + n  xs
      isSetComp    : isSet CompressedList

Transforming compressed lists

From compressed lists, I will generate their lists. For example, “a2” will be transformed to “aa”.

    _qtL1+_∷_ : A    List A  List A
    ListSet : isSet (List A)

    compToList : CompressedList  List A
    compToList [] = []
    compToList (x qt1+ n  xs) = x qtL1+ n  compToList xs
    compToList (sameQuantity zero n x xs i) = x  (x qtL1+ n  compToList xs)
    compToList (sameQuantity (suc m) n x xs i) = x  compToList (sameQuantity m n x xs i)
    compToList (isSetComp xs ys p q i j) =
      ListSet _ _  k  compToList (p k))  k  compToList (q k)) i j

    x qtL1+ zero  xs = x  xs
    x qtL1+ suc n  xs = x  (x qtL1+ n  xs)

    ListSet = Discrete→isSet (discreteList discA)

The Discrete→isSet is an important theorem. It needs to be used because if there are multiple ways of a value of a list is equal to another value of the list, the proof can become very messy or maybe impossible to do.

The simplest way to transform a list into a compressed list is to associate the quantity of 1 of each element of the list in the following lines:

    listToComp : List A  CompressedList
    listToComp [] = []
    listToComp (x  xs) = x qt1+ 0  listToComp xs

Examples

Some examples of the use of compressed lists:

  open compListMod  discreteℕ

  _ : compToList (100 qt1+ 0  [])  100  []
  _ = refl

  _ : compToList (200 qt1+ 1  100 qt1+ 2  [])  200  200  100  100  100  []
  _ = refl

  _ : listToComp (100  [])  100 qt1+ 0  []
  _ = refl

  _ : listToComp (200  200  100  [])  200 qt1+ 1  100 qt1+ 0  []
  _ = sameQuantity 0 0 200 (100 qt1+ 0  [])