new modules and neatened exposure of official stdlib code

This commit is contained in:
2022-02-04 14:06:51 +11:00
committed by aaron-jack-manning
parent e9368e17c1
commit e25b69bd44
41 changed files with 389 additions and 224 deletions

50
lib/array.ml Normal file
View File

@ -0,0 +1,50 @@
open General
let of_list = Stdlib.Array.of_list
let length = Stdlib.Array.length
let concat = Stdlib.Array.concat
let append = Stdlib.Array.append
let get = Stdlib.Array.get
let set = Stdlib.Array.set
let map = Stdlib.Array.map
let create = Stdlib.Array.init
let heap_sort = Stdlib.Array.sort
let merge_sort = Stdlib.Array.stable_sort
let copy = Stdlib.Array.copy
let linear_search (arr : 'a array) (value : 'a) =
let i = ref 0 in
while (!i < length arr) & (get arr !i <> value) do
i := Int.(!i + 1)
done;
get arr !i
let rec binary_search_helper (arr : 'a array) (value : 'a) (first : int) (last : int) =
let midpoint = Int.((first + last) / 2) in
if midpoint < first or midpoint > last then
None
else if value < (get arr midpoint) then
binary_search_helper arr value first Int.(midpoint - 1)
else if (get arr midpoint) < value then
binary_search_helper arr value Int.(midpoint + 1) last
else if get arr midpoint = value then
Some (get arr midpoint)
else
None
let binary_search (arr : 'a array) (value : 'a) =
binary_search_helper arr value 0 Int.(length arr - 1)

28
lib/array.mli Normal file
View File

@ -0,0 +1,28 @@
open General
val of_list : 'a list -> 'a array
val length : 'a array -> int
val concat : 'a array list -> 'a array
val append : 'a array -> 'a array -> 'a array
val get : 'a array -> int -> 'a
val set : 'a array -> int -> 'a -> unit
val map : ('a -> 'b) -> 'a array -> 'b array
val create : int -> (int -> 'a) -> 'a array
val heap_sort : ('a -> 'a -> int) -> 'a array -> unit
val merge_sort : ('a -> 'a -> int) -> 'a array -> unit
val copy : 'a array -> 'a array
val linear_search : 'a array -> 'a -> 'a
val binary_search : 'a array -> 'a -> 'a option

View File

@ -1,3 +1,7 @@
open General
let of_string = FromStdlib.bool_of_string_opt
let of_string = function
| "true" -> Some true
| "false" -> Some false
| _ -> None

View File

@ -1,4 +1,5 @@
open General
(** Converts the string to a bool, returning option type to account for invalid strings. *)
val of_string : string -> bool option

View File

@ -1,3 +1,11 @@
open General
let of_int = FromStdlib.char_of_int
external unsafe_char_of_int : int -> char = "%identity"
let of_int n =
if n < 0 or n > 255 then
None
else
Some (unsafe_char_of_int n)

View File

@ -1,4 +1,5 @@
open General
(** Converts an int to a char. *)
val of_int : int -> char
val of_int : int -> char option

View File

@ -1,6 +1,10 @@
open General
let failwith = FromStdlib.failwith
external raise : exn -> 'a = "%raise"
let failwith m = raise (Failure m)
let guarantee b =
if not b then
@ -8,4 +12,7 @@ let guarantee b =
let guarantee_equal a b =
if a <> b then
failwith "guarantee_equal failed."
failwith "guarantee_equal failed."

View File

@ -1,5 +1,6 @@
open General
(** Throws an exception with a message. *)
val failwith : string -> 'a

29
lib/file.ml Normal file
View File

@ -0,0 +1,29 @@
open General
let open_out = Stdlib.open_out
let close_out = Stdlib.close_out
let printf = Stdlib.Printf.fprintf
let write_all_text filepath text =
let channel = open_out filepath in
let _ = printf channel "%s" text in
let _ = close_out channel in
()
let open_in = Stdlib.open_in
let close_in = Stdlib.close_in
let read_n_chars = Stdlib.really_input_string
let in_channel_length = Stdlib.in_channel_length
let read_all_text filepath =
let channel = open_in filepath in
let contents = read_n_chars channel (in_channel_length channel) in
let _ = close_in channel in
contents

26
lib/file.mli Normal file
View File

@ -0,0 +1,26 @@
open General
(** Opens and returns an output channel at the specified path. *)
val open_out : string -> Stdlib.out_channel
(** Closes the provided output channel. *)
val close_out : Stdlib.out_channel -> unit
(** Formatted printing to a provided output channel. *)
val printf : Stdlib.out_channel -> ('a, Stdlib.out_channel, unit) Stdlib.format -> 'a
(** Writes the given string to a file at the specified filepath. *)
val write_all_text : string -> string -> unit
(** Opens and returns an input channel at the specified path. *)
val open_in : string -> Stdlib.in_channel
(** Closes the provided input channel. *)
val close_in : Stdlib.in_channel -> unit
(** Reads the specified number of characters from the provided input channel as a string. *)
val read_n_chars : Stdlib.in_channel -> int -> string
(** Reads from the specified file as a string. *)
val read_all_text : string -> string

View File

@ -1,11 +1,22 @@
open General
let ( + ) a b = FromStdlib.plus_float a b
let ( - ) a b = FromStdlib.minus_float a b
external ( + ) : float -> float -> float = "%addfloat"
let ( * ) a b = FromStdlib.multiply_float a b
external ( - ) : float -> float -> float = "%subfloat"
let ( / ) a b = FromStdlib.divide_float a b
external ( * ) : float -> float -> float = "%mulfloat"
let of_string = FromStdlib.float_of_string_opt
external ( / ) : float -> float -> float = "%divfloat"
external float_of_string : string -> float = "caml_float_of_string"
let of_string s =
try
Some (float_of_string s)
with
Failure _ -> None
external of_int : int -> float = "%floatofint"

View File

@ -1,12 +1,16 @@
open General
val ( + ) : float -> float -> float
val ( - ) : float -> float -> float
external ( + ) : float -> float -> float = "%addfloat"
val ( * ) : float -> float -> float
external ( - ) : float -> float -> float = "%subfloat"
val ( / ) : float -> float -> float
external ( * ) : float -> float -> float = "%mulfloat"
external ( / ) : float -> float -> float = "%divfloat"
(** Converts the string to a float, returning option type to account for invalid strings. *)
val of_string : string -> float option
val of_string : string -> float option
(** Converts the int to a float. *)
val of_int : int -> float

View File

@ -1,79 +0,0 @@
(* https://github.com/ocaml/ocaml/blob/cce52acc7c7903e92078e9fe40745e11a1b944f0/stdlib/pervasives.ml *)
(* For fatal.ml *)
let failwith = Stdlib.failwith
(* For terminal.ml *)
let printf = Printf.printf
type 'a printf_format = ('a, out_channel, unit) format
(* For int.ml *)
external int_of_char : char -> int = "%identity"
let int_of_string_opt = int_of_string_opt
external plus_int : int -> int -> int = "%addint"
external minus_int : int -> int -> int = "%subint"
external multiply_int : int -> int -> int = "%mulint"
external divide_int : int -> int -> int = "%divint"
external mod_int : int -> int -> int = "%modint"
(* For float.ml *)
let float_of_string_opt = float_of_string_opt
external plus_float : float -> float -> float = "%addfloat"
external minus_float : float -> float -> float = "%subfloat"
external multiply_float : float -> float -> float = "%mulfloat"
external divide_float : float -> float -> float = "%divfloat"
(* For char.ml *)
let char_of_int = char_of_int
(* For string.ml *)
let string_of_bool = string_of_bool
let string_of_int = string_of_int
let string_of_float = string_of_float
external string_length : string -> int = "%string_length"
(* This block are not exposed *)
external bytes_create : int -> bytes = "caml_create_bytes"
external string_blit : string -> int -> bytes -> int -> int -> unit
= "caml_blit_string" [@@noalloc]
external bytes_blit : bytes -> int -> bytes -> int -> int -> unit
= "caml_blit_bytes" [@@noalloc]
external bytes_unsafe_to_string : bytes -> string = "%bytes_to_string"
let string_concat s1 s2 =
let l1 = string_length s1 and l2 = string_length s2 in
let s = bytes_create (l1 + l2) in
string_blit s1 0 s 0 l1;
string_blit s2 0 s l1 l2;
bytes_unsafe_to_string s
(* For bool.ml *)
let bool_of_string_opt = bool_of_string_opt
(* For general.ml *)
external equal : 'a -> 'a -> bool = "%equal"
external not_equal : 'a -> 'a -> bool = "%notequal"
external less_than : 'a -> 'a -> bool = "%lessthan"
external greater_than : 'a -> 'a -> bool = "%greaterthan"
external less_than_or_equal : 'a -> 'a -> bool = "%lessequal"
external greater_than_or_equal : 'a -> 'a -> bool = "%greaterequal"
external not : bool -> bool = "%boolnot"
external or_ : bool -> bool -> bool = "%sequor"
external and_ : bool -> bool -> bool = "%sequand"
external pipeline : 'a -> ('a -> 'b) -> 'b = "%revapply"
external ignore : 'a -> unit = "%ignore"

View File

@ -1,61 +0,0 @@
(* For fatal.ml *)
val failwith : string -> 'a
(* For terminal.ml *)
val printf : ('a, out_channel, unit) format -> 'a
type 'a printf_format = ('a, out_channel, unit) format
(* For int.ml *)
external int_of_char : char -> int = "%identity"
val int_of_string_opt : string -> int option
external plus_int : int -> int -> int = "%addint"
external minus_int : int -> int -> int = "%subint"
external multiply_int : int -> int -> int = "%mulint"
external divide_int : int -> int -> int = "%divint"
external mod_int : int -> int -> int = "%modint"
(* For float.ml *)
val float_of_string_opt : string -> float option
external plus_float : float -> float -> float = "%addfloat"
external minus_float : float -> float -> float = "%subfloat"
external multiply_float : float -> float -> float = "%mulfloat"
external divide_float : float -> float -> float = "%divfloat"
(* For char.ml *)
val char_of_int : int -> char
(* For string.ml *)
val string_of_bool : bool -> string
val string_of_int : int -> string
val string_of_float : float -> string
external string_length : string -> int = "%string_length"
val string_concat : string -> string -> string
(* For bool.ml *)
val bool_of_string_opt : string -> bool option
(* For general.ml *)
external equal : 'a -> 'a -> bool = "%equal"
external not_equal : 'a -> 'a -> bool = "%notequal"
external less_than : 'a -> 'a -> bool = "%lessthan"
external greater_than : 'a -> 'a -> bool = "%greaterthan"
external less_than_or_equal : 'a -> 'a -> bool = "%lessequal"
external greater_than_or_equal : 'a -> 'a -> bool = "%greaterequal"
external not : bool -> bool = "%boolnot"
external or_ : bool -> bool -> bool = "%sequor"
external and_ : bool -> bool -> bool = "%sequand"
external pipeline : 'a -> ('a -> 'b) -> 'b = "%revapply"
external ignore : 'a -> unit = "%ignore"

View File

@ -12,7 +12,7 @@ type 'a tree =
| Leaf
| Branch of 'a * 'a tree list
let ignore = FromStdlib.ignore
let ignore (a : 'a) = ()
let id (x : 'a) = x
@ -20,23 +20,38 @@ let ( >> ) f g x = g (f x)
let ( << ) g f x = g (f x)
let ( |> ) = FromStdlib.pipeline
let ( |> ) f g = g f
let ( = ) = FromStdlib.equal
external ( = ) : 'a -> 'a -> bool = "%equal"
let ( <> ) = FromStdlib.not_equal
external ( <> ) : 'a -> 'a -> bool = "%notequal"
let ( < ) = FromStdlib.less_than
external ( < ) : 'a -> 'a -> bool = "%lessthan"
let ( > ) = FromStdlib.greater_than
external ( > ) : 'a -> 'a -> bool = "%greaterthan"
let ( <= ) = FromStdlib.less_than_or_equal
external ( <= ) : 'a -> 'a -> bool = "%lessequal"
let ( >= ) = FromStdlib.greater_than_or_equal
external ( >= ) : 'a -> 'a -> bool = "%greaterequal"
let not = FromStdlib.not
let not = function
| true -> false
| false -> true
let ( or ) = FromStdlib.or_
external ( or ) : bool -> bool -> bool = "%sequor"
external ( & ) : bool -> bool -> bool = "%sequand"
type 'a ref =
{ mutable contents : 'a }
let ref a =
{ contents = a }
let ( := ) (cell : 'a ref) (contents : 'a) =
cell.contents <- contents
let ( ! ) (cell : 'a ref) =
cell.contents
let ( & ) = FromStdlib.and_

View File

@ -1,6 +1,7 @@
(** Purely functional queue, implemented as a pair of lists. *)
type 'a queue = { front : 'a list; back : 'a list }
(** Result type for error handling. *)
type 'a result =
| Error of string
| Success of 'a
@ -10,7 +11,7 @@ type 'a stack =
| Empty
| Stacked of 'a * 'a stack
(* A purely functional tree with arbitrarily many branches at each node. *)
(**A purely functional tree with arbitrarily many branches at each node. *)
type 'a tree =
| Leaf
| Branch of 'a * 'a tree list
@ -31,28 +32,40 @@ val ( << ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
val ( |> ) : 'a -> ('a -> 'b) -> 'b
(** Checks for structural equality. *)
val ( = ) : 'a -> 'a -> bool
external ( = ) : 'a -> 'a -> bool = "%equal"
(** Checks for structural inequality. *)
val ( <> ) : 'a -> 'a -> bool
external ( <> ) : 'a -> 'a -> bool = "%notequal"
(** Polymorphic less than. *)
val ( < ) : 'a -> 'a -> bool
external ( < ) : 'a -> 'a -> bool = "%lessthan"
(** Polymorphic greater than. *)
val ( > ) : 'a -> 'a -> bool
external ( > ) : 'a -> 'a -> bool = "%greaterthan"
(** Polymorphic less than or equal to. *)
val ( <= ) : 'a -> 'a -> bool
external ( <= ) : 'a -> 'a -> bool = "%lessequal"
(** Polymorphic greater than or equal to. *)
val ( >= ) : 'a -> 'a -> bool
external ( >= ) : 'a -> 'a -> bool = "%greaterequal"
(** Logical negation. *)
val not : bool -> bool
(** Logical or (infix). *)
val ( or ) : bool -> bool -> bool
external ( or ) : bool -> bool -> bool = "%sequor"
(** Logical and. *)
val ( & ) : bool -> bool -> bool
external ( & ) : bool -> bool -> bool = "%sequand"
(** Reference cells for mutable variables. *)
type 'a ref = { mutable contents : 'a; }
(** Creates a reference cell. *)
val ref : 'a -> 'a ref
(** Updates a reference cell. *)
val ( := ) : 'a ref -> 'a -> unit
(** Returns the contents of a reference cell. *)
val ( ! ) : 'a ref -> 'a

View File

@ -1,16 +1,26 @@
open General
let ( + ) a b = FromStdlib.plus_int a b
let ( - ) a b = FromStdlib.minus_int a b
external ( + ) : int -> int -> int = "%addint"
let ( * ) a b = FromStdlib.multiply_int a b
external ( - ) : int -> int -> int = "%subint"
let ( / ) a b = FromStdlib.divide_int a b
external ( * ) : int -> int -> int = "%mulint"
let ( mod ) a b = FromStdlib.mod_int a b
external ( / ) : int -> int -> int = "%divint"
external ( mod ) : int -> int -> int = "%modint"
external of_char : char -> int = "%identity"
external int_of_string : string -> int = "caml_int_of_string"
let of_string s =
try
Some (int_of_string s)
with
| Failure _ -> None
let of_char = FromStdlib.int_of_char
let of_string = FromStdlib.int_of_string_opt

View File

@ -1,17 +1,18 @@
open General
val ( + ) : int -> int -> int
val ( - ) : int -> int -> int
external ( + ) : int -> int -> int = "%addint"
val ( * ) : int -> int -> int
external ( - ) : int -> int -> int = "%subint"
val ( / ) : int -> int -> int
external ( * ) : int -> int -> int = "%mulint"
val ( mod ) : int -> int -> int
external ( / ) : int -> int -> int = "%divint"
external ( mod ) : int -> int -> int = "%modint"
(** Converts the char to an int. *)
val of_char : char -> int
external of_char : char -> int = "%identity"
(** Converts the string to an int, returning option type to account for invalid strings. *)
val of_string : string -> int option

View File

@ -1,7 +1,15 @@
open General
let empty : 'a list = []
let rec length_helper (list : 'a list) (length : int) =
match list with
| [] -> length
| _ :: xs -> length_helper xs Int.(length + 1)
let length (list : 'a list) = length_helper list 0
let head (ls : 'a list) : 'a option =
match ls with
| x :: xs -> Some x
@ -142,4 +150,7 @@ let rec initialize_helper (f : int -> 'a) (length : int) (index : int) (acc : 'a
initialize_helper f length Int.(index + 1) ((f index) :: acc)
let initialize (f : int -> 'a) (length : int) : 'a list =
initialize_helper f length 0 []
initialize_helper f length 0 []
let of_array =
Stdlib.Array.to_list

View File

@ -1,8 +1,12 @@
open General
(** The empty list *)
val empty : 'a list
(** Calculates the length of the given list. Runs in O(n) and should be stored rather then recalculated if many calls are made on an unchanging list. *)
val length : 'a list -> int
(** Returns Some [x] if [x] is the head of the list, or None for the empty list. Runs in O(1). *)
val head : 'a list -> 'a option
@ -55,4 +59,7 @@ val filter_rev_tr : ('a -> bool) -> 'a list -> 'a list
val find : ('a -> bool) -> 'a list -> ('a * int) option
(** Initializes a list using a length and a function from the index to the desired list value. Runs in O(n). *)
val initialize : (int -> 'a) -> int -> 'a list
val initialize : (int -> 'a) -> int -> 'a list
(* Creates a list from the provided array. *)
val of_array : 'a array -> 'a list

View File

@ -3,13 +3,10 @@ STANDARD_COMPILE = ocamlopt $(STANDARD_FLAGS) -nopervasives -c
LIB_NAME = library
build:
# fromStdlib manages things that need to be exposed from the standard library
ocamlopt $(STANDARD_FLAGS) -c fromStdlib.mli fromStdlib.ml
make clean
# types and functions that should be opened module wide
ocamlopt $(STANDARD_FLAGS) -nopervasives -c general.mli general.ml
# the following files make up the core custom standard library code
$(STANDARD_COMPILE) fatal.mli fatal.ml
$(STANDARD_COMPILE) int.mli int.ml
$(STANDARD_COMPILE) float.mli float.ml
@ -24,9 +21,11 @@ build:
$(STANDARD_COMPILE) char.mli char.ml
$(STANDARD_COMPILE) bool.mli bool.ml
$(STANDARD_COMPILE) terminal.mli terminal.ml
$(STANDARD_COMPILE) file.mli file.ml
$(STANDARD_COMPILE) array.mli array.ml
$(STANDARD_COMPILE) random.mli random.ml
ocamlopt -a fromStdlib.cmx general.cmx fatal.cmx int.cmx float.cmx option.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx string.cmx char.cmx bool.cmx terminal.cmx -o $(LIB_NAME).cmxa
ocamlopt -a general.cmx fatal.cmx int.cmx float.cmx option.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx string.cmx char.cmx bool.cmx terminal.cmx file.cmx array.cmx random.cmx -o $(LIB_NAME).cmxa
clean:
rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma

View File

@ -1,5 +1,6 @@
open General
module type Map = sig
type key

View File

@ -1,5 +1,6 @@
open General
module type Map = sig
(** The type of keys in the map. *)
type key

View File

@ -1,5 +1,6 @@
open General
let return (x : 'a) : 'a option = Some x
let ( ~= ) = return

View File

@ -1,5 +1,6 @@
open General
(* Monadic return for option type. Trivially boxes the variable of type 'a into a 'a option by applying the Some constructor. *)
val return : 'a -> 'a option

View File

@ -1,6 +1,8 @@
open General
open List
let enqueue (a : 'a) (qu : 'a queue) : 'a queue =
{ qu with front = a :: qu.back }

View File

@ -1,5 +1,6 @@
open General
(** Adds an element to the back of the queue, returning the new queue. Runs in O(1). *)
val enqueue : 'a -> 'a queue -> 'a queue

17
lib/random.ml Normal file
View File

@ -0,0 +1,17 @@
open General
let init = Stdlib.Random.self_init
let integer low high =
init ();
Int.((Stdlib.Random.int (high - low)) + low)
let float low high =
init ();
Float.((Stdlib.Random.float (high - low)) + low)
let boolean =
init ();
Stdlib.Random.bool

11
lib/random.mli Normal file
View File

@ -0,0 +1,11 @@
open General
(** Generates a random integer between low (inclusive) and high (exclusive). *)
val integer : int -> int -> int
(** Generates a random float between low (inclusive) and high (exclusive). *)
val float : float -> float -> float
(** Generates a random boolean at even odds. *)
val boolean : unit -> bool

View File

@ -1,5 +1,6 @@
open General
module type RBTreeSet = sig
type member

View File

@ -1,5 +1,6 @@
open General
module type RBTreeSet = sig
(** Type of members in the set. *)
type member

View File

@ -1,5 +1,6 @@
open General
let pop (st : 'a stack) : 'a option * 'a stack =
match st with
| Empty -> (None, Empty)

View File

@ -1,5 +1,6 @@
open General
(** Removes the top element from the stack, returning a tuple of the new stack, and None if the stack was empty, or Some [x] if [x] was on top of the stack. Popping an empty stack will result in the returned stack also being empty. Runs in O(1). *)
val pop : 'a stack -> 'a option * 'a stack

View File

@ -1,11 +1,49 @@
open General
let ( + ) = FromStdlib.string_concat
let length = FromStdlib.string_length
external bytes_create : int -> bytes = "caml_create_bytes"
external string_blit : string -> int -> bytes -> int -> int -> unit = "caml_blit_string" [@@noalloc]
external bytes_blit : bytes -> int -> bytes -> int -> int -> unit = "caml_blit_bytes" [@@noalloc]
external bytes_unsafe_to_string : bytes -> string = "%bytes_to_string"
external format_int : string -> int -> string = "caml_format_int"
external format_float : string -> float -> string = "caml_format_float"
let of_int = FromStdlib.string_of_int
let of_float = FromStdlib.string_of_float
external length : string -> int = "%string_length"
let of_bool = FromStdlib.string_of_bool
let of_int n =
format_int "%d" n
let of_bool bool =
if bool then
"true"
else
"false"
let ( + ) s1 s2 =
let l1 = length s1 in
let l2 = length s2 in
let s = bytes_create Int.(l1 + l2) in
string_blit s1 0 s 0 l1;
string_blit s2 0 s l1 l2;
bytes_unsafe_to_string s
external string_get : string -> int -> char = "%string_safe_get"
let valid_float_lexem s =
let l = length s in
let rec loop i =
if i >= l then s + "." else
match string_get s i with
| '0' .. '9' | '-' -> loop Int.(i + 1)
| _ -> s
in
loop 0
let of_float f =
valid_float_lexem (format_float "%.12g" f)

View File

@ -1,10 +1,8 @@
open General
(** Concatenates two strings together in the provided order. *)
val ( + ) : string -> string -> string
(** Calculates the length of the provided string. *)
val length : string -> int
external length : string -> int = "%string_length"
(** Converts an int to a string. *)
val of_int : int -> string
@ -14,3 +12,6 @@ val of_float : float -> string
(** Converts a boolean to a string. *)
val of_bool : bool -> string
(** Concatenates two strings together in the provided order. *)
val ( + ) : string -> string -> string

View File

@ -1,6 +1,8 @@
open General
let printf = FromStdlib.printf
let printf = Stdlib.Printf.printf
let print s = printf "%s" s
let print s = printf "%s" s
let read_line = Stdlib.read_line

View File

@ -1,7 +1,11 @@
open General
(** Formatted print to terminal. *)
val printf : 'a FromStdlib.printf_format -> 'a
val printf : ('a, Stdlib.out_channel, unit) Stdlib.format -> 'a
(** Prints the provided string to the terminal. *)
val print : string -> unit
val print : string -> unit
(** Reads an input line from the terminal. *)
val read_line : unit -> string

View File

@ -1,5 +1,6 @@
open General
let combine (tr1 : 'a tree) (tr2 : 'a tree) (topBranch : 'a) : 'a tree =
Branch (topBranch, tr1 :: tr2 :: [])

View File

@ -1,5 +1,6 @@
open General
(* Combines two trees of the same type, with the specified value at the new top node. Runs in O(1). *)
val combine : 'a tree -> 'a tree -> 'a -> 'a tree