package smol

  1. Overview
  2. Docs

Source file monomial.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
module Make (Literal : Literal.S) = struct
  module LiteralMap = Map.Make (Literal)

  type t = int LiteralMap.t

  exception Monomial_has_non_positive_exponent of (Literal.t * int)

  exception Monomial_set_negative_exponent of (Literal.t * int)

  let iter = LiteralMap.iter

  let fold = LiteralMap.fold

  let for_all = LiteralMap.for_all

  let exists = LiteralMap.exists

  let filter = LiteralMap.filter

  let partition = LiteralMap.partition

  let cardinal = LiteralMap.cardinal

  let bindings = LiteralMap.bindings

  let map = LiteralMap.map

  let mapi = LiteralMap.mapi

  let to_seq = LiteralMap.to_seq

  (** Check if exponent is non-negative, fail if negative *)
  let is_exp_nonneg var exp =
    if exp < 0 then raise (Monomial_set_negative_exponent (var, exp)) ;
    exp > 0

  let filter_seq = Seq.filter (fun (var, exp) -> is_exp_nonneg var exp)

  let add_seq s = filter_seq s |> LiteralMap.add_seq

  let of_seq s = filter_seq s |> LiteralMap.of_seq

  let of_map = LiteralMap.filter is_exp_nonneg

  let to_map m = m

  let one = LiteralMap.empty

  let is_one = LiteralMap.is_empty

  let singleton var exp =
    if is_exp_nonneg var exp then LiteralMap.singleton var exp else one

  let set_exponent var exp =
    if is_exp_nonneg var exp then LiteralMap.add var exp
    else LiteralMap.remove var

  let update var f =
    (* For convenience, we want [f] to be [int -> int], so we create a function
       [g : int option -> int option] to pass to [update]. [None] is 0 in that case.
    *)
    let g x =
      let fx = f (Option.value x ~default:0) in
      if is_exp_nonneg var fx then Some fx else None
    in
    LiteralMap.update var g

  let union f a b =
    let g var x y =
      let fx = f var x y in
      if is_exp_nonneg var fx then Some fx else None
    in
    LiteralMap.union g a b

  let remove = LiteralMap.remove

  let of_literal var = singleton var 1

  let get_support m = List.map fst (bindings m)

  let compare m1 m2 =
    let rec aux lb1 lb2 =
      match (lb1, lb2) with
      | ([], []) -> 0
      | ([], _) -> -1
      | (_, []) -> 1
      | ((k1, e1) :: t1, (k2, e2) :: t2) -> (
          match Literal.compare k1 k2 with
          | 0 -> if e1 = e2 then aux t1 t2 else e1 - e2
          | c -> c)
    in
    aux (bindings m1) (bindings m2)

  let equal = LiteralMap.equal Int.equal

  let neq a b = not (equal a b)

  let deg var m = Option.value (LiteralMap.find_opt var m) ~default:0

  let mul : t -> t -> t =
    let aux_union _ e1 e2 = Some (e1 + e2) in
    LiteralMap.union aux_union

  let deriv var m : (int * t) option =
    match deg var m with
    | 0 -> None
    | 1 -> Some (1, remove var m)
    | exp -> Some (exp, set_exponent var (exp - 1) m)

  (* Fast exponentiation for K *)
  let pow (type a) (module K : Algebra.Mul_Monoid_S with type t = a) (x : a)
      (exp : int) : a =
    let rec pow_aux x exp =
      if exp = 0 then K.one
      else if exp = 1 then x
      else
        let exp_2 = exp / 2 in
        let px = pow_aux x exp_2 in
        let px = K.mul px px in
        if exp mod 2 = 0 then px else K.mul px x
    in
    pow_aux x exp

  let apply (type a) (module K : Algebra.Mul_Monoid_S with type t = a) m spec :
      a * t =
    let apply_single m var value =
      match deg var m with
      | 0 -> (K.one, m)
      | exp -> (pow (module K) value exp, remove var m)
    in
    (* A fold on [spec]. Its bindings are applied sequentially *)
    LiteralMap.fold
      (fun var value (coef, m_acc) ->
        let (a_coef, m_acc) = apply_single m_acc var value in
        (K.mul coef a_coef, m_acc))
      spec
      (K.one, m)

  (** Pretty (?) printing of integer exponents *)
  let digit_to_string = function
    | 0 -> "⁰"
    | 1 -> "¹"
    | 2 -> "²"
    | 3 -> "³"
    | 4 -> "⁴"
    | 5 -> "⁵"
    | 6 -> "⁶"
    | 7 -> "⁷"
    | 8 -> "⁸"
    | 9 -> "⁹"
    | _ -> ""

  let rec exp_to_string n =
    if n <= 0 then "" else exp_to_string (n / 10) ^ digit_to_string (n mod 10)

  let to_string m =
    if is_one m then "1ₘ"
    else
      let aux_map (var, exp) =
        if exp < 1 then raise (Monomial_has_non_positive_exponent (var, exp))
        else if exp = 1 then Literal.to_string var
        else Printf.sprintf "%s%s" (Literal.to_string var) (exp_to_string exp)
      in
      String.concat "" (List.map aux_map (bindings m))

  module Infix = struct
    let ( * ) = mul

    let ( = ) = equal

    let ( <> ) = neq
  end
end