blob: 92c30535353e5f2e206fc5bad3899c482529acf4 [file] [edit]
type nat = Z.t
type int = Z.t
type rat = Q.t
type real = float
type num =
[
| `Nat of nat
| `Int of int
| `Rat of rat
| `Real of real
]
type typ = [`NatT | `IntT | `RatT | `RealT]
type unop = [`PlusOp | `MinusOp]
type binop = [`AddOp | `SubOp | `MulOp | `DivOp | `ModOp | `PowOp]
type cmpop = [`LtOp | `GtOp | `LeOp | `GeOp]
let to_string = function
| `Nat n -> Z.to_string n
| `Int i -> (if i >= Z.zero then "+" else "-") ^ Z.to_string (Z.abs i)
| `Rat q -> Z.to_string (Q.num q) ^ "/" ^ Z.to_string (Q.den q)
| `Real r -> Printf.sprintf "%.17g" r
let string_of_typ = function
| `NatT -> "nat"
| `IntT -> "int"
| `RatT -> "rat"
| `RealT -> "real"
let string_of_unop = function
| `PlusOp -> "+"
| `MinusOp -> "-"
let string_of_binop = function
| `AddOp -> "+"
| `SubOp -> "-"
| `MulOp -> "*"
| `DivOp -> "/"
| `ModOp -> "\\"
| `PowOp -> "^"
let string_of_cmpop = function
| `LtOp -> "<"
| `GtOp -> ">"
| `LeOp -> "<="
| `GeOp -> ">="
let sub t1 t2 =
match t1, t2 with
| _, `RealT
| `NatT, _
| `IntT, `RatT -> true
| _, _ -> t1 = t2
let to_typ = function
| `Nat _ -> `NatT
| `Int _ -> `IntT
| `Rat _ -> `RatT
| `Real _ -> `RealT
let typ_unop op t1 t2 : bool =
match op with
| `PlusOp | `MinusOp -> t1 = t2 && sub `IntT t1
let typ_binop op t1 t2 t3 : bool =
match op with
| `AddOp | `MulOp -> t1 = t2 && t1 = t3
| `SubOp -> t1 = t2 && sub `IntT t3
| `DivOp -> t1 = t2 && sub `RatT t3
| `ModOp -> t1 = t2 && t1 = t3 && sub t1 `IntT
| `PowOp -> t1 = t3 && (t2 = `NatT || t2 = `IntT && sub `RatT t1)
let rec cvt typ num : num option =
let ( let* ) = Option.bind in
match num, typ with
| `Nat _, `NatT -> Some num
| `Nat n, `IntT -> Some (`Int n)
| `Nat n, `RatT -> Some (`Rat (Q.of_bigint n))
| `Nat n, `RealT -> Some (`Real (Z.to_float n))
| `Int i, `NatT -> if Z.sign i >= 0 then Some (`Nat i) else None
| `Int _, `IntT -> Some num
| `Int i, `RatT -> Some (`Rat (Q.of_bigint i))
| `Int i, `RealT -> Some (`Real (Z.to_float i))
| `Rat _, `NatT -> let* num' = cvt `IntT num in cvt typ num'
| `Rat q, `IntT -> if Q.den q = Z.one then Some (`Int (Q.num q)) else None
| `Rat _, `RatT -> Some num
| `Rat q, `RealT -> Some (`Real (Q.to_float q))
| `Real _, (`NatT | `IntT) -> let* num' = cvt `RatT num in cvt typ num'
| `Real r, `RatT -> Some (`Rat (Q.of_float r))
| `Real _, `RealT -> Some num
let rec narrow : num -> num = function
| `Int i when i >= Z.zero -> `Nat i
| `Rat q when Q.den q = Z.one -> narrow (`Int (Q.num q))
| `Real z when z = Z.(to_float (of_float z)) -> narrow (`Int (Z.of_float z))
| num -> num
let widen num1 num2 : num * num =
match num1, num2 with
| `Real _, _ -> num1, Option.get (cvt `RealT num2)
| _, `Real _ -> Option.get (cvt `RealT num1), num2
| `Rat _, _ -> num1, Option.get (cvt `RatT num2)
| _, `Rat _ -> Option.get (cvt `RatT num1), num2
| `Int _, _ -> num1, Option.get (cvt `IntT num2)
| _, `Int _ -> Option.get (cvt `IntT num1), num2
| `Nat _, `Nat _ -> num1, num2
let abs num : num =
match num with
| `Nat n -> `Nat n
| `Int i -> `Int (Z.abs i)
| `Rat q -> `Rat (Q.abs q)
| `Real z -> `Real (Float.abs z)
let un (op : unop) num : num option =
Util.Debug_log.(log "xl.num.un"
(fun _ -> string_of_unop op ^ " " ^ to_string num)
(function None -> "?" | Some num -> to_string num)
) @@ fun _ ->
match op, num with
| `PlusOp, `Nat n -> Some (`Nat n)
| `PlusOp, (`Int _ | `Rat _ | `Real _) -> Some num
| `MinusOp, (`Nat n | `Int n) -> Some (`Int (Z.neg n))
| `MinusOp, `Rat q -> Some (`Rat (Q.neg q))
| `MinusOp, `Real r -> Some (`Real (-. r))
let rec bin (op : binop) num1 num2 : num option =
Util.Debug_log.(log "xl.num.bin"
(fun _ -> to_string num1 ^ " " ^ string_of_binop op ^ " " ^ to_string num2)
(function None -> "?" | Some num -> to_string num)
) @@ fun _ ->
match op, num1, num2 with
| `AddOp, `Nat n1, `Nat n2 -> Some (`Nat Z.(n1 + n2))
| `AddOp, `Int i1, `Int i2 -> Some (`Int Z.(i1 + i2))
| `AddOp, `Rat q1, `Rat q2 -> Some (`Rat Q.(q1 + q2))
| `AddOp, `Real r1, `Real r2 -> Some (`Real (r1 +. r2))
| `SubOp, `Nat n1, `Nat n2 when n1 >= n2 -> Some (`Nat Z.(n1 - n2))
| `SubOp, `Int i1, `Int i2 -> Some (`Int Z.(i1 - i2))
| `SubOp, `Rat q1, `Rat q2 -> Some (`Rat Q.(q1 - q2))
| `SubOp, `Real r1, `Real r2 -> Some (`Real (r1 -. r2))
| `MulOp, `Nat n1, `Nat n2 -> Some (`Nat Z.(n1 * n2))
| `MulOp, `Int i1, `Int i2 -> Some (`Int Z.(i1 * i2))
| `MulOp, `Rat q1, `Rat q2 -> Some (`Rat Q.(q1 * q2))
| `MulOp, `Real r1, `Real r2 -> Some (`Real (r1 *. r2))
| `DivOp, `Nat n1, `Nat n2 when Z.(n2 <> zero && rem n1 n2 = zero) -> Some (`Nat Z.(n1 / n2))
| `DivOp, `Int i1, `Int i2 when Z.(i2 <> zero && rem i1 i2 = zero) -> Some (`Int Z.(i1 / i2))
| `DivOp, `Rat q1, `Rat q2 when Q.(q2 <> zero) -> Some (`Rat Q.(q1 / q2))
| `DivOp, `Real r1, `Real r2 when r2 <> 0.0 -> Some (`Real (r1 /. r2))
| `ModOp, `Nat n1, `Nat n2 when Z.(n2 <> zero) -> Some (`Nat Z.(rem n1 n2))
| `ModOp, `Int i1, `Int i2 when Z.(i2 <> zero) -> Some (`Int Z.(rem i1 i2))
| `PowOp, `Nat n1, `Nat n2 -> Some (`Nat Z.(n1 ** to_int n2))
| `PowOp, `Int i1, `Int i2 when Z.(i2 >= zero && fits_int i2) -> Some (`Int Z.(i1 ** to_int i2))
| `PowOp, `Rat q1, `Rat q2 when Q.(q2 >= zero && Z.(equal (den q2) one) && Z.fits_int (num q2)) ->
Some (`Rat Q.(of_bigint Z.(num q1 ** to_int (num q2)) / of_bigint Z.(den q1 ** to_int (num q2))))
| `PowOp, `Rat _, `Rat q2 when Q.(q2 < zero) ->
(match bin op num1 (`Rat Q.(- q2)) with
| Some (`Rat q) -> Some (`Rat Q.(one / q))
| _ -> None
)
| `PowOp, `Real r1, `Real r2 -> Some (`Real Float.(pow r1 r2))
| _, _, _ -> None
let zero = function
| `NatT -> `Nat Z.zero
| `IntT -> `Int Z.zero
| `RatT -> `Rat Q.zero
| `RealT -> `Real 0.0
let one = function
| `NatT -> `Nat Z.one
| `IntT -> `Int Z.one
| `RatT -> `Rat Q.one
| `RealT -> `Real 1.0
let is_zero num = num = zero (to_typ num)
let is_one num = num = one (to_typ num)
let is_neg = function
| `Nat _ -> false
| `Int i -> i < Z.zero
| `Rat q -> Q.(q < zero)
| `Real r -> r < 0.0
(*
let left_neutral op num =
match op with
| `AddOp -> is_zero num
| `MulOp -> is_one num
| _ -> false
let right_neutral op num =
match op with
| `AddOp | `SubOp -> is_zero num
| `MulOp | `DivOp | `PowOp -> is_one num
| _ -> false
let left_absorbing op num =
match op with
| `MulOp | `DivOp | `ModOp -> is_zero num
| `PowOp -> is_zero num || is_one num
| _ -> false
let right_absorbing op num =
match op with
| `MulOp -> is_zero num
| _ -> false
let right_zeroing op num =
match op with
| `ModOp -> is_one num
| _ -> false
let right_uniting op num =
match op with
| `PowOp -> is_zero num
| _ -> false
*)
let bin_partial (op : binop) arg1 arg2 of_ to_ : 'a option =
match op, of_ arg1, of_ arg2 with
| op, Some num1, Some num2 ->
(match bin op num1 num2 with
| Some num -> Some (to_ num)
| None -> None
)
(*
| Some num1, None ->
if left_neutral op num1 then Some arg2 else
if left_absorbing op num1 then Some arg1 else
None
| None, Some num2 ->
if right_neutral op num2 then Some arg1 else
if right_absorbing op num2 then Some arg2 else
if right_zeroing op num2 then Some (to_ (zero (to_typ num2))) else
if right_uniting op num2 then Some (to_ (one (to_typ num2))) else
None
| None, None -> None
*)
(* neutral elements *)
| `AddOp, Some num1, _ when is_zero num1 -> Some arg2
| `MulOp, Some num1, _ when is_one num1 -> Some arg2
| (`AddOp | `SubOp), _, Some num2 when is_zero num2 -> Some arg1
| (`MulOp | `DivOp | `PowOp), _, Some num2 when is_one num2 -> Some arg1
(* absorbing elements *)
| (`MulOp | `DivOp | `ModOp | `PowOp), Some num1, _ when is_zero num1 -> Some arg1
| `PowOp, Some num1, _ when is_one num1 -> Some arg1
| `MulOp, _, Some num2 when is_zero num2 -> Some arg2
(* collapsing elements *)
| `ModOp, _, Some num2 when is_one num2 -> Some (to_ (zero (to_typ num2)))
| `PowOp, _, Some num2 when is_zero num2 -> Some (to_ (one (to_typ num2)))
| _, _, _ -> None
let cmp (op : cmpop) num1 num2 : bool option =
Util.Debug_log.(log "xl.num.cmp"
(fun _ -> to_string num1 ^ " " ^ string_of_cmpop op ^ " " ^ to_string num2)
(function None -> "?" | Some b -> Bool.to_string b)
) @@ fun _ ->
match op, num1, num2 with
| `LtOp, `Nat n1, `Nat n2 -> Some (n1 < n2)
| `LtOp, `Int i1, `Int i2 -> Some (i1 < i2)
| `LtOp, `Rat q1, `Rat q2 -> Some Q.(q1 < q2)
| `LtOp, `Real r1, `Real r2 -> Some (r1 < r2)
| `GtOp, `Nat n1, `Nat n2 -> Some (n1 > n2)
| `GtOp, `Int i1, `Int i2 -> Some (i1 > i2)
| `GtOp, `Rat q1, `Rat q2 -> Some Q.(q1 > q2)
| `GtOp, `Real r1, `Real r2 -> Some (r1 > r2)
| `LeOp, `Nat n1, `Nat n2 -> Some (n1 <= n2)
| `LeOp, `Int i1, `Int i2 -> Some (i1 <= i2)
| `LeOp, `Rat q1, `Rat q2 -> Some Q.(q1 <= q2)
| `LeOp, `Real r1, `Real r2 -> Some (r1 <= r2)
| `GeOp, `Nat n1, `Nat n2 -> Some (n1 >= n2)
| `GeOp, `Int i1, `Int i2 -> Some (i1 >= i2)
| `GeOp, `Rat q1, `Rat q2 -> Some Q.(q1 >= q2)
| `GeOp, `Real r1, `Real r2 -> Some (r1 >= r2)
| _, _, _ -> None