(* TEST
   * expect *)

module type Endpoint_intf = sig
  type t
end
;;
[%%expect{|
module type Endpoint_intf = sig type t end
|}]

module type S = sig
  module Endpoint : Endpoint_intf

  type finite = [ `Before of Endpoint.t ]
  type infinite = [ `Until_infinity ]

  type +'a range = private { until : 'a } constraint 'a = [< finite | infinite ]

  val until : 'a range -> 'a
end
;;
[%%expect{|
module type S =
  sig
    module Endpoint : Endpoint_intf
    type finite = [ `Before of Endpoint.t ]
    type infinite = [ `Until_infinity ]
    type +'a range = private { until : 'a; }
      constraint 'a = [< `Before of Endpoint.t | `Until_infinity ]
    val until :
      ([< `Before of Endpoint.t | `Until_infinity ] as 'a) range -> 'a
  end
|}]

module type Ranged = sig
  module Endpoint : Endpoint_intf
  module Range : S with type Endpoint.t = Endpoint.t
end
;;
[%%expect{|
module type Ranged =
  sig
    module Endpoint : Endpoint_intf
    module Range :
      sig
        module Endpoint : sig type t = Endpoint.t end
        type finite = [ `Before of Endpoint.t ]
        type infinite = [ `Until_infinity ]
        type +'a range = private { until : 'a; }
          constraint 'a = [< `Before of Endpoint.t | `Until_infinity ]
        val until :
          ([< `Before of Endpoint.t | `Until_infinity ] as 'a) range -> 'a
      end
  end
|}]

module Assume (Given : sig
    module Make_range (Endpoint : Endpoint_intf) :
      S with module Endpoint = Endpoint

    module Make_ranged (Range : S) :
      Ranged with module Endpoint = Range.Endpoint
              and module Range = Range
  end) =
struct
  module Point = struct
    type t
  end

  open Given

  module Test_range = Make_range(Point)
  module Test_ranged = Make_ranged(Test_range)
end
;;
[%%expect{|
module Assume :
  functor
    (Given : sig
               module Make_range :
                 functor (Endpoint : Endpoint_intf) ->
                   sig
                     module Endpoint : sig type t = Endpoint.t end
                     type finite = [ `Before of Endpoint.t ]
                     type infinite = [ `Until_infinity ]
                     type +'a range = private { until : 'a; }
                       constraint 'a =
                         [< `Before of Endpoint.t | `Until_infinity ]
                     val until :
                       ([< `Before of Endpoint.t | `Until_infinity ] as 'a)
                       range -> 'a
                   end
               module Make_ranged :
                 functor (Range : S) ->
                   sig
                     module Endpoint : sig type t = Range.Endpoint.t end
                     module Range :
                       sig
                         module Endpoint : sig type t = Range.Endpoint.t end
                         type finite = [ `Before of Endpoint.t ]
                         type infinite = [ `Until_infinity ]
                         type +'a range =
                           'a Range.range = private {
                           until : 'a;
                         }
                           constraint 'a =
                             [< `Before of Endpoint.t | `Until_infinity ]
                         val until :
                           ([< `Before of Endpoint.t | `Until_infinity ]
                            as 'a)
                           range -> 'a
                       end
                   end
             end)
    ->
    sig
      module Point : sig type t end
      module Test_range :
        sig
          module Endpoint : sig type t = Point.t end
          type finite = [ `Before of Endpoint.t ]
          type infinite = [ `Until_infinity ]
          type +'a range =
            'a Given.Make_range(Point).range = private {
            until : 'a;
          } constraint 'a = [< `Before of Endpoint.t | `Until_infinity ]
          val until :
            ([< `Before of Endpoint.t | `Until_infinity ] as 'a) range -> 'a
        end
      module Test_ranged :
        sig
          module Endpoint : sig type t = Test_range.Endpoint.t end
          module Range :
            sig
              module Endpoint : sig type t = Test_range.Endpoint.t end
              type finite = [ `Before of Endpoint.t ]
              type infinite = [ `Until_infinity ]
              type +'a range = 'a Test_range.range = private { until : 'a; }
                constraint 'a = [< `Before of Endpoint.t | `Until_infinity ]
              val until :
                ([< `Before of Endpoint.t | `Until_infinity ] as 'a) range ->
                'a
            end
        end
    end
|}]
