open Expr open Simplify open Diff open Substitute type direction = FromLeft | FromRight | Bidirectional let rec limit expr var point direction = let try_direct_sub () = try let substituted = substitute var point expr in let simplified = simplify substituted in match simplified with | Const c when not (Float.is_nan c || Float.is_infinite c) -> Some simplified | _ -> None with _ -> None in let try_lhopital () = let numerator, denominator = match expr with | Div (n, d) -> (n, d) | _ -> (expr, Const 1.0) in let num_at_point = substitute var point numerator |> simplify in let den_at_point = substitute var point denominator |> simplify in match (num_at_point, den_at_point) with | (Const 0.0, Const 0.0) -> let num_deriv = diff var numerator in let den_deriv = diff var denominator in let new_expr = Div (num_deriv, den_deriv) in limit new_expr var point direction | _ -> None in let try_series_expansion () = None in match try_direct_sub () with | Some result -> Some result | None -> match try_lhopital () with | Some result -> Some result | None -> try_series_expansion () let limit_at_infinity expr var = let rec find_highest_power = function | Pow (Var v, Const n) when v = var -> Some (int_of_float n) | Div (num, den) -> let num_pow = find_highest_power num |> Option.value ~default:0 in let den_pow = find_highest_power den |> Option.value ~default:0 in Some (num_pow - den_pow) | Add (e1, e2) | Sub (e1, e2) -> let p1 = find_highest_power e1 |> Option.value ~default:0 in let p2 = find_highest_power e2 |> Option.value ~default:0 in Some (max p1 p2) | Mul (e1, e2) -> let p1 = find_highest_power e1 |> Option.value ~default:0 in let p2 = find_highest_power e2 |> Option.value ~default:0 in Some (p1 + p2) | _ -> None in match find_highest_power expr with | Some p when p > 0 -> Some (SymConst E) | Some p when p < 0 -> Some (Const 0.0) | Some _ -> Some (Const 1.0) | None -> None