insert :: Term f -> a -> Index f a -> Index f a
insert !t x !idx = aux (Term.singleton t) idx
  where
    aux t Nil = singletonList t x
    aux (Cons t ts) idx@Index{prefix = Cons u us}
      | skeleton t == skeleton u =
        withPrefix t (aux ts idx{prefix = us})
    aux (ConsSym{hd = t, rest = ts}) idx@Index{prefix = ConsSym{hd = u, rest = us}}
      | t `sameSymbolAs` u =
        withPrefix (build (atom t)) (aux ts idx{prefix = us})
    aux t idx@Index{prefix = Cons{}} = aux t (expand idx)

    aux Empty idx =
      idx { size = 0, here = x:here idx }
    aux t@ConsSym{hd = App f _, rest = u} idx =
      idx {
        size = lenList t `min` size idx,
        fun  = update (fun_id f) idx' (fun idx) }
      where
        idx' = aux u (fun idx ! fun_id f)
    aux t@ConsSym{hd = Var _, rest = u} idx =
      idx {
        size = lenList t `min` size idx,
        var  = aux u (var idx) }

    Var _ `sameSymbolAs` Var _ = True
    App f _ `sameSymbolAs` App g _ = f == g
    _ `sameSymbolAs` _ = False

    skeleton t = build (subst (const (Term.var (V 0))) t)

    atom (Var x) = Term.var x
    atom (App f _) = con f

-- Add a prefix to an index.
-- Does not update the size field.
insert :: Term f -> a -> Index f a -> Index f a
insert !t x !idx = aux (Term.singleton t) idx
  where
    aux t Nil = singletonList t x
    aux (Cons t ts) idx@Index{prefix = Cons u us}
      | skeleton t == skeleton u =
        withPrefix t (aux ts idx{prefix = us})
    aux (ConsSym{hd = t, rest = ts}) idx@Index{prefix = ConsSym{hd = u, rest = us}}
      | t `sameSymbolAs` u =
        withPrefix (build (atom t)) (aux ts idx{prefix = us})
    aux t idx@Index{prefix = Cons{}} = aux t (expand idx)

    aux Empty idx =
      idx { size = 0, here = x:here idx }
    aux t@ConsSym{hd = App f _, rest = u} idx =
      idx {
        size = lenList t `min` size idx,
        fun  = update (fun_id f) idx' (fun idx) }
      where
        idx' = aux u (fun idx ! fun_id f)
    aux t@ConsSym{hd = Var _, rest = u} idx =
      idx {
        size = lenList t `min` size idx,
        var  = aux u (var idx) }

    Var _ `sameSymbolAs` Var _ = True
    App f _ `sameSymbolAs` App g _ = f == g
    _ `sameSymbolAs` _ = False

    skeleton t = build (subst (const (Term.var (V 0))) t)

    atom (Var x) = Term.var x
    atom (App f _) = con f

-- Add a prefix to an index.
-- Does not update the size field.
insert !t x !idx = aux (Term.singleton t) idx
  where
    aux t Nil = singletonList t x
    aux (Cons t ts) idx@Index{prefix = Cons u us}
      | skeleton t == skeleton u =
        withPrefix t (aux ts idx{prefix = us})
    aux (ConsSym{hd = t, rest = ts}) idx@Index{prefix = ConsSym{hd = u, rest = us}}
      | t `sameSymbolAs` u =
        withPrefix (build (atom t)) (aux ts idx{prefix = us})
    aux t idx@Index{prefix = Cons{}} = aux t (expand idx)

    aux Empty idx =
      idx { size = 0, here = x:here idx }
    aux t@ConsSym{hd = App f _, rest = u} idx =
      idx {
        size = lenList t `min` size idx,
        fun  = update (fun_id f) idx' (fun idx) }
      where
        idx' = aux u (fun idx ! fun_id f)
    aux t@ConsSym{hd = Var _, rest = u} idx =
      idx {
        size = lenList t `min` size idx,
        var  = aux u (var idx) }

    Var _ `sameSymbolAs` Var _ = True
    App f _ `sameSymbolAs` App g _ = f == g
    _ `sameSymbolAs` _ = False

    skeleton t = build (subst (const (Term.var (V 0))) t)

    atom (Var x) = Term.var x
    atom (App f _) = con f

-- Add a prefix to an index.
-- Does not update the size field.