open! Base

(* this no longer allocates closures. hurray! *)
module T0 = struct
  type 'a t = 'a list [@@deriving_inline hash]

  let _ = fun (_ : 'a t) -> ()

  let hash_fold_t :
    'a.
    (Ppx_hash_lib.Std.Hash.state -> 'a -> Ppx_hash_lib.Std.Hash.state)
    -> Ppx_hash_lib.Std.Hash.state
    -> 'a t
    -> Ppx_hash_lib.Std.Hash.state
    =
    hash_fold_list
  ;;

  let _ = hash_fold_t

  [@@@deriving.end]
end

module T1 = struct
  type 'a t = 'a option list [@@deriving_inline hash]

  let _ = fun (_ : 'a t) -> ()

  let hash_fold_t :
    'a.
    (Ppx_hash_lib.Std.Hash.state -> 'a -> Ppx_hash_lib.Std.Hash.state)
    -> Ppx_hash_lib.Std.Hash.state
    -> 'a t
    -> Ppx_hash_lib.Std.Hash.state
    =
    fun _hash_fold_a hsv arg ->
    hash_fold_list (fun hsv arg -> hash_fold_option _hash_fold_a hsv arg) hsv arg
  ;;

  let _ = hash_fold_t

  [@@@deriving.end]
end

module T2 = struct
  type 'a t = ('a * 'a) list [@@deriving_inline hash]

  let _ = fun (_ : 'a t) -> ()

  let hash_fold_t :
    'a.
    (Ppx_hash_lib.Std.Hash.state -> 'a -> Ppx_hash_lib.Std.Hash.state)
    -> Ppx_hash_lib.Std.Hash.state
    -> 'a t
    -> Ppx_hash_lib.Std.Hash.state
    =
    fun _hash_fold_a hsv arg ->
    hash_fold_list
      (fun hsv arg ->
         let e0, e1 = arg in
         let hsv = _hash_fold_a hsv e0 in
         let hsv = _hash_fold_a hsv e1 in
         hsv)
      hsv
      arg
  ;;

  let _ = hash_fold_t

  [@@@deriving.end]
end

module T3 = struct
  type 'a t =
    | Leaf
    | Node of 'a t list
  [@@deriving_inline hash]

  let _ = fun (_ : 'a t) -> ()

  let rec hash_fold_t
    : type a.
      (Ppx_hash_lib.Std.Hash.state -> a -> Ppx_hash_lib.Std.Hash.state)
      -> Ppx_hash_lib.Std.Hash.state
      -> a t
      -> Ppx_hash_lib.Std.Hash.state
    =
    fun _hash_fold_a hsv arg ->
    match arg with
    | Leaf -> Ppx_hash_lib.Std.Hash.fold_int hsv 0
    | Node _a0 ->
      let hsv = Ppx_hash_lib.Std.Hash.fold_int hsv 1 in
      let hsv = hsv in
      hash_fold_list (fun hsv arg -> hash_fold_t _hash_fold_a hsv arg) hsv _a0
  ;;

  let _ = hash_fold_t

  [@@@deriving.end]

  let hash_fold_t_no_closure_allocation
    : type a.
      (Ppx_hash_lib.Std.Hash.state -> a -> Ppx_hash_lib.Std.Hash.state)
      -> Ppx_hash_lib.Std.Hash.state
      -> a t
      -> Ppx_hash_lib.Std.Hash.state
    =
    fun _hash_fold_a ->
      let rec hash_fold_t_of_a hsv arg =
        match arg with
        | Leaf -> Ppx_hash_lib.Std.Hash.fold_int hsv 0
        | Node _a0 ->
          hash_fold_list hash_fold_t_of_a (Ppx_hash_lib.Std.Hash.fold_int hsv 1) _a0
      in
      hash_fold_t_of_a
  ;;
end

module T4 = struct
  type 'a t =
    | Leaf
    | Node of ('a * 'a) t list
  [@@deriving_inline hash]

  let _ = fun (_ : 'a t) -> ()

  let rec hash_fold_t
    : type a.
      (Ppx_hash_lib.Std.Hash.state -> a -> Ppx_hash_lib.Std.Hash.state)
      -> Ppx_hash_lib.Std.Hash.state
      -> a t
      -> Ppx_hash_lib.Std.Hash.state
    =
    fun _hash_fold_a hsv arg ->
    match arg with
    | Leaf -> Ppx_hash_lib.Std.Hash.fold_int hsv 0
    | Node _a0 ->
      let hsv = Ppx_hash_lib.Std.Hash.fold_int hsv 1 in
      let hsv = hsv in
      hash_fold_list
        (fun hsv arg ->
           hash_fold_t
             (fun hsv arg ->
                let e0, e1 = arg in
                let hsv = _hash_fold_a hsv e0 in
                let hsv = _hash_fold_a hsv e1 in
                hsv)
             hsv
             arg)
        hsv
        _a0
  ;;

  let _ = hash_fold_t

  [@@@deriving.end]

  let rec hash_fold_t_lazy_closure_allocation
    : type a.
      (Ppx_hash_lib.Std.Hash.state -> a -> Ppx_hash_lib.Std.Hash.state)
      -> Ppx_hash_lib.Std.Hash.state
      -> a t
      -> Ppx_hash_lib.Std.Hash.state
    =
    fun _hash_fold_a ->
      let hash_fold_t_of_tuple =
        lazy
          (hash_fold_t_lazy_closure_allocation (fun hsv arg ->
             let e0, e1 = arg in
             _hash_fold_a (_hash_fold_a hsv e0) e1))
      in
      fun hsv arg ->
        match arg with
        | Leaf -> Ppx_hash_lib.Std.Hash.fold_int hsv 0
        | Node _a0 ->
          hash_fold_list
            (Lazy.force hash_fold_t_of_tuple)
            (Ppx_hash_lib.Std.Hash.fold_int hsv 1)
            _a0
  ;;
end
