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.

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. \
</role>\
"""

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

1. I'll improve and reorganize your IML introduction to make it more precise, \
informative, and better structured. Here's the revised version:

# Imandra Modeling Language (IML): An Introduction

## Core Differences Between IML and OCaml

### 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.

### 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`

### 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

### 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

## IML as an Automated Reasoning Engine Input Language

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

### 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.

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. 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.

## 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 = fun x -> 1.0 [@@opaque]
```

## 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 = fun x -> 1.0 [@@opaque]`
   - ❌ `let sqrt x: real -> real = fun x -> 1.0 [@@opaque]`

2. **Dummy implementation**: While the implementation body is a placeholder, it must \
match the declared return type. In the example above, `1.0` is a dummy value that \
satisfies the return type `real`.

## 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>
"""

iml_101 = role_prompt + instructions + flatten_iml_api_reference


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 = fun x -> 1. [@@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 = fun a b -> 1 [@@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 = fun n -> 1 [@@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,
]

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.
"""


final_caveats = """\
- 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.
- 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. 
"""
