import Langlib.Classes.ContextSensitive.Inclusion.RecursivelyEnumerable

/-! # Non-Contracting Grammar Definitions

This file defines non-contracting grammars as unrestricted grammars whose rules
never decrease the length of the rewritten sentential form. It also contains the
grammar-level translation machinery relating non-contracting grammars to
context-sensitive grammars.

## Overview

A **non-contracting grammar** (also called monotone grammar) is an unrestricted grammar
where every rule satisfies `|output| ≥ |input|`, i.e., the right-hand side is at least
as long as the left-hand side.

The equivalence of these two formulations is a classical result in formal language theory.
Every context-sensitive grammar is non-contracting (easy direction), and every non-contracting
grammar can in principle be converted to an equivalent context-sensitive grammar (hard direction).

## Main declarations

- `grammar_noncontracting` — predicate for non-contracting unrestricted grammars
- `is_noncontracting` — language class of non-contracting grammars

## References

The equivalence is standard; see e.g. Hopcroft, Motwani, Ullman,
"Introduction to Automata Theory, Languages, and Computation", Chapter 9.
-/

open List Relation

variable {T : Type}

/-! ## Non-contracting grammars -/

/-- An unrestricted grammar is *non-contracting* (monotone) if for every rule, the output
    string is at least as long as the full input pattern `input_L ++ [input_N] ++ input_R`. -/
def grammar_noncontracting (g : grammar T) : Prop :=
  ∀ r ∈ g.rules, r.output_string.length ≥ r.input_L.length + 1 + r.input_R.length

/-- Predicate: a language is generated by some non-contracting grammar. -/
def is_noncontracting (L : Language T) : Prop :=
  ∃ g : grammar T, grammar_noncontracting g ∧ grammar_language g = L
