short_iml_101 = """IML (Imandra Modeling Language)
- syntax: a formalized Higher-Order subset of OCaml extended with theorem proving \
tactics and verification annotations
- features: counter-example generation, automated induction, conditional rewriting, \
bounded and unbounded verification, theorem proving, model checking, lemma discovery
- logic: Computational Higher-Order Logic (HOL) based on a pure subset of OCaml. \
Recursion and Induction based on ordinals up to ε_0.
- tags: #reasoner #automated reasoning engine #interactive theorem prover \
#automated theorem prover #functional programming language #formal verification
"""

# TODO: tell llm that not all List functions return option. Eg, List.hd are the same as
# OCaml.

# This is actually not needed. Sometimes gpt-4o-mini is just not good at coding.
# 6. Order of function definitions

# Just like OCaml, IML uses a single-pass compiler that processes code from top to \
# bottom. So, if you define a function after using it, IML will complain. This is \
# different from languages like Python where the order of function definitions does \
# not matter. Bear this in mind when writing IML code and make adjustments on the \
# order of function definitions if necessary.

role_prompt = """<role>
You are a software engineer with expertise in OCaml, functional programming, and \
formal semantics. You are tasked to generate code in Imandra Modeling Language (IML) \
either by translating given source code or by generating code from scratch.
</role>\
"""

iml_intro = """
<instructions>
IML is a "pure" (no side effects) subset of OCaml and will be used as input language \
to an automated reasoning engine. Thus, IML code has mechanized formal semantics and \
represents some kind of formalism, making it different from regular OCaml code where \
some side effects are allowed.

We will introduce IML by emphasizing its difference from OCaml and its unique features
as an input language to an automated reasoning engine.

# Imandra Modeling Language (IML): An Introduction

## 1. Core Differences Between IML and OCaml

### 1.1 Standard Library Differences

OCaml standard library modules are either **unavailable** or have **significantly \
different signatures** in IML:

- **Example**: `List.nth` in OCaml has signature `'a list -> int -> 'a`, while in IML \
it's `int -> 'a list -> 'a option`.
  - Parameter order is reversed (index first, then list)
  - Return type uses `option` since IML is pure and cannot raise exceptions for \
invalid indices

**Available modules** in IML include: `Int`, `LChar`, `LString`, `List`, `Map`, \
`Multiset`, `Option`, `Real`, `Result`, `Set`, and `String` — all with IML-specific \
signatures. We'll go through the signatures of these modules in the next section.

### 1.2 Numerical Representation and Precision

IML uses arbitrary precision arithmetic with different numerical type interpretations:

- Literal `3.14` in IML has type `real` (not `float` as in OCaml)
- For type conversion, use:
  - `Real.of_int : int -> real`
  - `Real.to_int : real -> int`

### 1.3 Error Handling Approach

IML is a pure language with no exceptions:

- The `failwith` function available in OCaml **cannot be used** in IML
- Instead, either:
  - Transform partial functions into total functions
  - Use monadic error handling with `Result` or `Option` modules

### 1.4 Type System Constraints

Unlike OCaml, IML restricts function representation in composite types:

- Functions **cannot** be part of algebraic data types, records, or tuples
- For state transition modeling, define:
  - A dedicated event type (with parameterized constructors if needed)
  - A step function that applies events to states

## 2. IML as an Automated Reasoning Engine Input Language

Here's the improved section on termination proving that includes information about \
the Ordinal module:

### 2.1 Termination Proving

Since IML serves as both a programming language and a logic, function termination \
must be provable:

- For common recursion patterns, termination is proven automatically
- For complex cases, provide explicit termination measures using `[@@measure ...]`

**Ordinals in Termination Proofs:**
Termination measures in IML must return values of type `Ordinal.t`. This type \
represents ordinals up to ε₀ in Cantor Normal Form, providing a well-founded ordering \
essential for proving termination. When defining measures, you can use functions like \
`Ordinal.of_int` to convert integers to ordinals, or construct more complex ordinals \
for nested recursion.

**Example with explicit measure:**
```iml
let left_pad_measure n xs =
  Ordinal.of_int (n - List.length xs)

let rec left_pad c n xs =
  if List.length xs >= n then
    xs
  else
    left_pad c n (c :: xs)
[@@measure left_pad_measure n xs]
```

The measure shows that `n - List.length xs` decreases with each recursive call and \
remains non-negative, proving termination. The `Ordinal.of_int` function converts this \
integer difference to an ordinal value that Imandra can use to establish a \
well-founded ordering.

Note that `[@@measure ...]` can only be used for top-level functions, i.e., functions \
that are not defined within other functions.

As a **last resort** when termination is difficult to prove, use `[@@no_validate]`:

```iml
let rec left_pad c n xs =
  if List.length xs >= n then
    xs
  else
    left_pad c n (c :: xs)
[@@no_validate]
```

**Important limitation**: Using `[@@no_validate]` significantly reduces Imandra's \
reasoning capabilities for this function.

### 2.2 Opaque Functions

Opaque functions allow you to define function signatures without implementing their \
behavior in detail. Think of them as mocking funcitonalities that help we pass \
type checking.

#### 2.2.1 Definition

To create an opaque function, attach the `[@@opaque]` attribute to a function with an \
explicit type signature on the function name:

```iml
let sqrt: real -> real = () [@@opaque]
```

#### 2.2.2 Important Syntax Rules

1. **Type annotation placement**: The type annotation must be placed on the function \
name itself, not on parameters or the function body:
   - ✅ `let sqrt: real -> real = () [@@opaque]`
   - ❌ `let sqrt x: real -> real = () [@@opaque]`

2. **Unit**: The implementation body is a unit value `()` no matter what the type is.

#### 2.2.3 When to Use Opaque Functions

Opaque functions are valuable when:
- Working with functions that aren't yet expressible in IML (e.g., operations \
  involving irrational numbers, bitwise operations, etc.)
- Integrating with external third-party libraries
</instructions>
"""

flatten_iml_api_reference = """
<detailed IML modules>
Now we'll go through the signatures of Prelude (not need to import) and modules.

<standard_library>
Prelude (readily available, no need to prefix with module name)
  val || : bool -> bool -> bool
  val && : bool -> bool -> bool
  val = : 'a -> 'a -> bool
  val <> : 'a -> 'a -> bool
  val not : bool -> bool
  val ==> : bool -> bool -> bool
  val <== : bool -> bool -> bool
  val <==> : bool -> bool -> bool
  val + : int -> int -> int
  val const : 'a -> 'b -> 'a
  val >= : int -> int -> bool
  val mk_nat : int -> int
  val < : int -> int -> bool
  val <= : int -> int -> bool
  val > : int -> int -> bool
  val min : int -> int -> int
  val max : int -> int -> int
  val <. : real -> real -> bool
  val <=. : real -> real -> bool
  val >. : real -> real -> bool
  val >=. : real -> real -> bool
  val min_r : real -> real -> real
  val max_r : real -> real -> real
  val ~- : int -> int
  val abs : int -> int
  val - : int -> int -> int
  val ~+ : int -> int
  val * : int -> int -> int
  val / : int -> int -> int
  val mod : int -> int -> int
  val compare : int -> int -> int  (*  Total order, if x = y then 0 else if x < y \
then -1 else 1  *)

type list =
  | []
  | ( :: ) of 'a * 'a list

type result =
  | Ok of 'a
  | Error of 'b
 (*  Result type, representing either a successul result [Ok x] or an error
     [Error x].  *)
</standard_library>

<module_Set>
Module: `Set`
```iml
module type Set = sig
  type Set.t = ('a, bool) Map.t
  val empty : ('a, bool) Map.t
  val full : ('a, bool) Map.t
  val is_empty : 'a Set.t -> bool
  val is_valid : 'a Set.t -> bool
  val mem : 'a -> 'a Set.t -> bool
  val subset : 'a Set.t -> 'a Set.t -> bool
  val add : 'a -> 'a Set.t -> 'a Set.t
  val remove : 'a -> 'a Set.t -> 'a Set.t
  val inter : 'a Set.t -> 'a Set.t -> 'a Set.t
  val union : 'a Set.t -> 'a Set.t -> 'a Set.t
  val complement : 'a Set.t -> 'a Set.t
  val diff : 'a Set.t -> 'a Set.t -> 'a Set.t
  val of_list : 'a list -> ('a, bool) Map.t
  val (++) : 'a Set.t -> 'a Set.t -> 'a Set.t
  val (--) : 'a Set.t -> 'a Set.t -> 'a Set.t
end
```
</module_Set>

<module_Result>
Module: `Result`
```iml
module type Result = sig
  type ('a, 'b) t = ('a, 'b) result
  val return : 'a -> ('a, 'b) result
  val fail : 'a -> ('b, 'a) result
  val map : ('b -> 'c) -> ('b, 'a) result -> ('c, 'a) result
  val map_err : ('a -> 'c) -> ('b, 'a) result -> ('b, 'c) result
  val get_or : default:'a -> ('a, 'b) result -> 'a
  val map_or : default:'a -> ('c -> 'a) -> ('c, 'b) result -> 'a
  val (>|=) : ('b, 'a) result -> ('b -> 'c) -> ('c, 'a) result
  val flat_map : ('b -> ('c, 'a) result) -> ('b, 'a) result -> ('c, 'a) result
  val (>>=) : ('b, 'a) result -> ('b -> ('c, 'a) result) -> ('c, 'a) result
  val fold : ('b -> 'c) -> ('a -> 'c) -> ('b, 'a) result -> 'c
  val is_ok : ('a, 'b) result -> bool
  val is_error : ('a, 'b) result -> bool
  val monoid_product : ('a, 'b) result -> ('c, 'b) result -> ('a * 'c, 'b) result
  val let+ : ('c, 'a) result
  val and+ : (('a * 'c), 'b) result
  val let* : ('c, 'a) result
  val and* : (('a * 'c), 'b) result
end
```
</module_Result>

<module_Real>
Module: `Real`
```iml
module type Real = sig
  type t = real
  val of_int : int -> real
  val _to_int_round_down : real -> int
  val to_int : real -> int
  val (+) : real -> real -> real
  val (-) : real -> real -> real
  val (~-) : real -> real
  val (*) : real -> real -> real
  val (/) : real -> real -> real
  val (<) : real -> real -> bool
  val (<=) : real -> real -> bool
  val (>) : real -> real -> bool
  val (>=) : real -> real -> bool
  val abs : real -> real
  val min : real -> real -> real
  val max : real -> real -> real
  val of_float : float -> real
  val pow : real -> int -> real
end
```
</module_Real>

<module_Option>
Module: `Option`
```iml
module type Option = sig
  type 'a t = 'a option
  val map : ('a -> 'b) -> 'a option -> 'b option
  val map_or : default:'a -> ('b -> 'a) -> 'b option -> 'a
  val is_some : 'a option -> bool
  val is_none : 'a option -> bool
  val return : 'a -> 'a option
  val (>|=) : 'a option -> ('a -> 'b) -> 'b option
  val flat_map : ('a -> 'b option) -> 'a option -> 'b option
  val (>>=) : 'a option -> ('a -> 'b option) -> 'b option
  val or_ : 'a option -> 'a option -> 'a option
  val (<+>) : 'a option -> 'a option -> 'a option
  val exists : ('a -> bool) -> 'a option -> bool
  val for_all : ('a -> bool) -> 'a option -> bool
  val get_or : default:'a -> 'a option -> 'a
  val fold : ('a -> 'b -> 'a) -> 'a -> 'b option -> 'a
  val (<$>) : ('a -> 'b) -> 'a option -> 'b option
  val monoid_product : 'a option -> 'b option -> ('a * 'b) option
  val let+ : 'a option -> ('a -> 'b) -> 'b option
  val and+ : 'a option -> 'b option -> ('a * 'b) option
  val let* : 'a option -> ('a -> 'b option) -> 'b option
  val and* : 'a option -> 'b option -> ('a * 'b) option
end
```
</module_Option>

<module_Int>
Module: `Int`
```iml
module type Int = sig
  type t = int
  val (+) : int -> int -> int
  val (-) : int -> int -> int
  val (~-) : int -> int
  val (*) : int -> int -> int
  val (/) : int -> int -> int
  val (mod) : int -> int -> int
  val (<) : int -> int -> bool
  val (<=) : int -> int -> bool
  val (>) : int -> int -> bool
  val (>=) : int -> int -> bool
  val min : int -> int -> int
  val max : int -> int -> int
  val abs : int -> int
  val to_string : int -> string
  val compare : int -> int -> int
  val equal : 'a -> 'a -> bool
  val pow : int -> int -> int
  val mod_zero_prod : int -> int -> int -> bool
  val mod_sub_id : int -> int -> bool
end
```
</module_Int>

<module_List>
Module: `List`
```iml
module type List = sig
  type 'a t = 'a list
  val empty : 'a list
  val is_empty : 'a list -> bool
  val cons : 'a -> 'a list -> 'a list
  val return : 'a -> 'a list
  val hd : 'a list -> 'a
  val tl : 'a list -> 'a list
  val head_opt : 'a list -> 'a option
  val append : 'a list -> 'a list -> 'a list
  val append_to_nil : 'a list -> bool
  val append_single : 'a -> 'a list -> 'a list -> bool
  val rev : 'a list -> 'a list
  val length : 'a list -> int
  val len_nonnegative : 'a list -> bool
  val len_zero_is_empty : 'a list -> bool
  val len_append : 'a list -> 'a list -> bool
  val split : ('a * 'b) list -> 'a list * 'b list
  val map : ('a -> 'b) -> 'a list -> 'b list
  val map2 : ('c -> 'a -> 'b) -> 'c list -> 'a list -> ('b list, string) result
  val for_all : ('a -> bool) -> 'a list -> bool
  val exists : ('a -> bool) -> 'a list -> bool
  val fold_left : ('b -> 'a -> 'b) -> 'b -> 'a list -> 'b
  val fold_right : ('b -> 'a -> 'a) -> 'b list -> 'a -> 'a
  val mapi : (int -> 'b -> 'a) -> 'b list -> 'a list
  val filter : ('a -> bool) -> 'a list -> 'a list
  val filter_map : ('a -> 'b option) -> 'a list -> 'b list
  val flat_map : ('b -> 'a list) -> 'b list -> 'a list
  val find : ('a -> bool) -> 'a list -> 'a option
  val mem : 'a -> 'a list -> bool
  val mem_assoc : 'a -> ('a * 'b) list -> bool
  val nth : int -> 'a list -> 'a option
  val assoc : 'a -> ('a * 'b) list -> 'b option
  val bounded_recons : int -> 'a list -> 'a list
  val take : int -> 'a list -> 'a list
  val drop : int -> 'a list -> 'a list
  val range : int -> int -> int list
  val (--) : int -> int -> int list
  val insert_sorted : leq:('a -> 'a -> bool) -> 'a -> 'a list -> 'a list
  val sort : leq:('a -> 'a -> bool) -> 'a list -> 'a list
  val is_sorted : leq:('a -> 'a -> bool) -> 'a list -> bool
  val monoid_product : 'a list -> 'b list -> ('a * 'b) list
  val (>|=) : 'a list -> ('a -> 'b) -> 'b list
  val (>>=) : 'b list -> ('b -> 'a list) -> 'a list
  val let+ : 'b list -> ('b -> 'a) -> 'a list
  val and+ : 'a list -> 'b list -> ('a * 'b) list
  val let* : 'b list -> ('b -> 'a list) -> 'a list
  val and* : 'a list -> 'b list -> ('a * 'b) list
end
```
</module_List>

<module_Map>
Module: `Map`
```iml
module type Map = sig
  type Map.t = {{| l : ('a * 'b) list; | default : 'b}}
  val const : 'b -> ('a, 'b) Map.t
  val add' : ('a, 'b) Map.t -> 'a -> 'b -> ('a, 'b) Map.t
  val add : 'a -> 'b -> ('a, 'b) Map.t -> ('a, 'b) Map.t
  val get_default : ('a, 'b) Map.t -> 'b
  val get' : ('a, 'b) Map.t -> 'a -> 'b
  val get : 'a -> ('a, 'b) Map.t -> 'b
  val of_list : 'b -> ('a * 'b) list -> ('a, 'b) Map.t
end
```
</module_Map>

<module_Multiset>
Module: `Multiset`
```iml
module type Multiset = sig
  type Multiset.t = ('a, int) Map.t
  val empty : ('a, int) Map.t
  val add : 'a -> ('a, int) Map.t -> ('a, int) Map.t
  val find : 'a -> ('a, int) Map.t -> int
  val mem : 'a -> ('a, int) Map.t -> bool
  val remove : 'a -> ('a, int) Map.t -> ('a, int) Map.t
  val of_list : 'a list -> ('a, int) Map.t
end
```
</module_Multiset>

<module_LChar>
In IML, we mainly use `LChar` and `LString`, (logic-mode character and logic-mode\
string). To create a logic-mode string (LString.t), you can use the syntax sugar\
`{{l|hello|l}}`.

```iml repl
> let y = {{l|hello|l}};;
val y: LChar.t list
```
</module_LChar>

<module_LChar>
Module: `LChar`
```iml
module type LChar = sig
  type LChar.t =
 LChar.Char of bool * bool * bool * bool * bool * bool * bool * bool
  val zero : LChar.t
  val is_printable : LChar.t -> bool
end
```
</module_LChar>

<module_LString>
Module: `LString`
```iml
module type LString = sig
  type t = LChar.t list
  val empty : LChar.t list
  val of_list : 'a -> 'a
  val length : LChar.t list -> int
  val len_pos : LChar.t list -> bool
  val len_zero_inversion : LChar.t list -> bool
  val append : LChar.t list -> LChar.t list -> LChar.t list
  val (^^) : LChar.t list -> LChar.t list -> LChar.t list
  val for_all : (LChar.t -> bool) -> LChar.t list -> bool
  val exists : (LChar.t -> bool) -> LChar.t list -> bool
  val concat : LChar.t list -> LChar.t list list -> LChar.t list
  val is_printable : LChar.t list -> bool
  val sub : LChar.t list -> int -> int -> LChar.t list
  val prefix : LChar.t list -> LChar.t list -> bool
  val suffix : LChar.t list -> LChar.t list -> bool
  val contains : LChar.t list -> LChar.t list -> bool
  val take : int -> LString.t -> LString.t
  val drop : int -> LString.t -> LString.t
end
```
</module_LString>

<module_String>
Module: `String`
```iml
module type String = sig
(* Note:
- These strings correspond to OCaml native strings, and do not have a particular \
unicode encoding.
- Rather, they should be seen as sequences of bytes, and it is also this way that \
Imandra considers them.
 *)
  type String.t = string
  val empty : string
  val length : String.t -> int
  val append : String.t -> String.t -> String.t
  val concat : string -> String.t list -> string
  val prefix : String.t -> String.t -> bool
  val suffix : String.t -> String.t -> bool
  val contains : String.t -> String.t -> bool
  val unsafe_sub : String.t -> int -> int -> String.t
  val sub : string -> int -> int -> String.t option
  val of_int : int -> string
  val unsafe_to_nat : String.t -> int
  val to_nat : string -> int option
  val is_nat : string -> bool
  val is_int : string -> bool
  val unsafe_to_int : string -> int
  val to_int : string -> int option
end
```
</module_String>
"""


lang_agnostic_meta_eg_overview = """\
Now, let's see a few educational examples. Each example demonstrates key \
concepts in IML through:

1. Explanatory comments before functions that highlight important concepts
2. REPL evaluation results showing type signatures and values. Comments marked with \
(* val ... *) show the REPL's response, helping you understand type inference and \
evaluation.

These comments are only for educational purposes. They teach you about the thinking \
process of writing IML code but they are not desired to be any generated IML code.\
"""

lang_agnostic_meta_eg_1 = """\
(* Float-like literal is interpreted as type `real`, arbitary precision real number. *)

let pi = 3.14159

(* val pi : real = 314159/100000 *)


(* Arithemetic for real:

- multiplication `*.`

- division `/.`

- etc *)

let circle_area (d : real) : real =

let r = d /. 2.0 in

pi *. r *. r

(* val circle_area : real -> real = <fun> *)\
"""

lang_agnostic_meta_eg_2 = """\
(* Note: `int` is the type for integers. *)
let count_negatives_in_row (row : int list) : int =
    List.fold_left (fun acc num -> if num < 0 then acc + 1 else acc) 0 row
  (* val count_negatives_in_row : int list -> int = <fun> *)

  let x = count_negatives_in_row [-1; -2; -3; 4; 5]
  (* val x : int = 3 *)

  (* Note:
    - `List.nth: int -> 'a list -> 'a option`, NOT returning 'a as in OCaml
    - That's why we need binding operator `let+` and `let*` from `Option` module
   *)
  let count_negative_numbers_in_a_column_wise_row_wise_sorted_matrix
      (m : int list list)
      (n : int)
      (cols : int)
      : int option =
    List.fold_left
      (fun acc i ->
        (* import Option module locally to use `let*`. could also be imported at the\
beginning by `open Option` *)
        let open Option in
        let* acc_val = acc in
        let+ row_count = map count_negatives_in_row (List.nth i m) in
        acc_val + row_count)
      (Some 0)
      (0 -- (n - 1))
  (* val count_negative_numbers_in_a_column_wise_row_wise_sorted_matrix :
    int list list -> int -> int -> int option = <fun> *)
"""

lang_agnostic_meta_eg_3 = """\
(* Note:
- integer division and modulo in IML: `mod: int -> int -> int`. And `/`.
 *)
 let is_divisor (n : int) (divisor : int) : bool =
    n mod divisor = 0
  (* val is_divisor : int -> int -> bool = <fun> *)


  let add_divisors (n : int) (divisor : int) (current_sum : int) : int =
    if divisor = n / divisor then
      current_sum + divisor
    else
      current_sum + divisor + n / divisor
  (* val add_divisors : int -> int -> int -> int = <fun> *)

  let sqrt : real -> real = () [@@opaque]
  (* val sqrt : real -> real = <fun> *)

  (* Note:
  - conversion between `int` and `real`: `Real.of_int` and `Real.to_int`.
   *)
  let sum_of_all_proper_divisors_of_a_natural_number (num : int) : int =
    let calculate_sum_of_divisors (n : int) (limit : int) : int =
      List.fold_left
        (fun acc i ->
          if is_divisor n i then
            acc + add_divisors n i 0
          else
            acc)
        0
        (2 -- limit)
    in
    let limit = Real.to_int (Real.of_int num |> sqrt) in
    calculate_sum_of_divisors num limit + 1
  (* val sum_of_all_proper_divisors_of_a_natural_number : int -> int = <fun> *)
"""

lang_agnostic_meta_eg_4 = """\
(* Another example with conversion between `real` and `int`. *)
let sum_of_first_n_numbers (n: int) : real =
  (Real.of_int n *. (Real.of_int n +. 1.0)) /. 2.0
(* val sum_of_first_n_numbers : int -> real = <fun> *)

let sum_of_squares_of_first_n_numbers (n: int) : real =
  (Real.of_int n *. (Real.of_int n +. 1.0) *. (2.0 *. Real.of_int n +. 1.0)) /. 6.0

(* val sum_of_squares_of_first_n_numbers : int -> real = <fun> *)


let sum_matrix_element_absolute_difference_row_column_numbers_2 (n: int) : int =
  let sum_first_n = sum_of_first_n_numbers n in
  let sum_squares_n = sum_of_squares_of_first_n_numbers n in
  (sum_first_n +. sum_squares_n) |> Real.to_int
(* val sum_matrix_element_absolute_difference_row_column_numbers_2 : int -> int = \
<fun> *)
"""

lang_agnostic_meta_eg_5 = """\
(* Note:
- for opeartions that is unavailable in IML, `[@@opaque]` can be attached
  to a dummy implementation with correct type signature. Such implementation
  can be used to bypass unavailable functions during compilation.
- Example of unavailable operations: bitwise operations (e.g., `land`, `lxor`),
  irrational arithmetic (e.g., square root, exponentiation), random.
 *)
let xor : int -> int -> int = () [@@opaque]
(* val xor : int -> int -> int = <fun> *)

let rec helper state pairs =
  match pairs with
  | [] -> state
  | (a, b) :: rest ->
    let new_state = min state (xor a b) in
    helper new_state rest
(* val helper : int -> (int * int) list -> int = <fun> *)

let find_minimum_xor (arr : int list) : int =
  let initial_state = 999999 in
  let pairs = List.monoid_product arr arr |> List.filter (fun (a, b) -> a < b) in
  helper initial_state pairs
(* val find_minimum_xor : int list -> int = <fun> *)

let minimum_xor_value_pair (arr : int list) : int =
  let sorted_arr = List.sort ~leq:(fun a b -> a <= b) arr in
  find_minimum_xor sorted_arr
(* val minimum_xor_value_pair : int list -> int = <fun> *)
"""

lang_agnostic_meta_eg_6 = """\
(* Another example using `[@@opaque]` *)
open Option

let random_int : int -> int = () [@@opaque]
(* val random_int : int -> int = <fun> *)

let swap_elements lst i j =
  let get_i = List.nth i lst in
  let get_j = List.nth j lst in
  List.mapi (fun idx x ->
    if idx = i then
      get_j |> get_or ~default:x
    else if idx = j then
      get_i |> get_or ~default:x
    else
      x
  ) lst
(* val swap_elements : 'a list -> int -> int -> 'a list = <fun> *)

let shuffle_a_given_array (arr : int list) : int list =
  let rec helper state i =
    if i <= 0 then
      state
    else
      let j = random_int (i + 1) in
      let state = swap_elements state i j in
      helper state (i - 1)
  in
  helper arr (List.length arr - 1)
(* val shuffle_a_given_array : int list -> int list = <fun> *)
"""

lang_agnostic_meta_eg_7 = """\
(* Character and String:
- IML supports logic-mode character `LChar.t` and logic-mode string `LString.t`.
- `{l|...|l}` is used to create a logic-mode string, `LString.t`, aka, `LChar.t list`.
- To create a logic-mode character, `LChar.t`, you can use list operations on \
`LString.t`, eg `List.hd {l|...|l}`.
 *)
let char_0: LChar.t = List.hd {l|0|l}
let char_1: LChar.t = List.hd {l|1|l}
(* val char_0 : LChar.t = '0'
val char_1 : LChar.t = '1' *)

let count_bits (s : LString.t) : int * int =
  let zeros = List.fold_left (fun acc ch -> if ch = char_0 then acc + 1 else acc) 0 s in
  let ones = List.length s - zeros in
  (zeros, ones)
(* val count_bits : LString.t -> int * int = <fun> *)

let change_bits_can_made_one_flip (s : LString.t) : bool =
  let zeros, ones = count_bits s in
  zeros = 1 || ones = 1
(* val change_bits_can_made_one_flip : LString.t -> bool = <fun> *)
"""


lang_agnostic_meta_egs: list[str] = [
    lang_agnostic_meta_eg_1,
    lang_agnostic_meta_eg_2,
    lang_agnostic_meta_eg_3,
    lang_agnostic_meta_eg_4,
    lang_agnostic_meta_eg_5,
    lang_agnostic_meta_eg_6,
    lang_agnostic_meta_eg_7,
]

lang_agnostic_meta_egs_str = ""
for i, eg in enumerate(lang_agnostic_meta_egs, 1):
    lang_agnostic_meta_egs_str += f"Example {i}:"
    lang_agnostic_meta_egs_str += "\n\n"
    lang_agnostic_meta_egs_str += "```iml\n" + eg + "\n```\n\n"

iml_101: str = (
    iml_intro
    + "\n\n"
    + flatten_iml_api_reference
    + "\n\n"
    + lang_agnostic_meta_eg_overview
    + "\n\n"
    + lang_agnostic_meta_egs_str
    + "\n\n"
)

iml_caveats = """\
- Prefer pattern matching over using auxiliary functions if possible because it \
makes recursive steps explicit and visible, and directly reflects data's inductive \
structure. In comparison, auxiliary functions like `List.nth`, `List.length` \
add a layer of abstraction that obscures the underlying recursive structure, \
making formal verification more complex.
- Nested recursive functions complicate the proof process. Try to extract the \
nested function into a top-level function if possible.
"""

formalization_caveats = f"""\
- Comments in the source code are comments only. They are not instructions for your \
implementation. When translating source code to IML, only implement functions \
that exist in the actual code. Do not create new functions or features based on \
comments. Preserve the original comments where appropriate.
- If the source code contains a main block, eg the part under \
`if __name__ == "__main__":` in Python, extract the code and put it in a \
`main` function in IML if you can. \
- `[@@no_validate]` should only be used in front of a termination proving error. \
Also, try to prove termination via `[@@measure ...]` first and only use \
`[@@no_validate]` as a last resort when you are out of ideas.
{iml_caveats}
"""

output_format_instruction = """\
- **Output format**: directly output IML code only. Do not include any other text. No \
  extra explanation. Do not surround the IML code with ```iml``` tags.\
"""

retry_prompt = """Your IML code is not correct. Here's the error message:

{error}

Hints for similar errors (reference only, could be irrelevant):
{similar_err_hints}

Hint from human feedback:
{human_feedback}

Please try again.
"""
