(* Polynômes à une variable, avec coefficients dans un anneau quelconque *)

module type Ring = sig
  type t
  val zero : t
  val one : t
  val add : t -> t -> t
  val mul : t -> t -> t
  val equal : t -> t -> bool
end

module Poly(R: Ring) = struct

  type monomial = { coef : R.t; degree : int }   (* coef <> R.zero *)
  type t = monomial list          (* triée par degrés décroissants *)

  (* construit un monôme, en assurant que le coefficient n'est pas nul *)
  let monomial c d =
    if R.equal c R.zero then [] else [{ coef = c; degree = d }]

  (* faire la somme de deux polynômes revient à fusionner deux listes triées *)
  let rec add p1 p2 = match p1, p2 with
    | [], _ ->
        p2
    | _, [] ->
        p1
    | m1 :: r1, m2 :: r2 ->
        if m1.degree > m2.degree then
          m1 :: add r1 p2
        else if m1.degree < m2.degree then
          m2 :: add p1 r2
        else
          let c = R.add m1.coef m2.coef in
          if R.equal c R.zero then
            add r1 r2
          else
            { coef = c; degree = m1.degree } :: add r1 r2

  (* pour assurer que les monômes sont bien triés par degrés décroissants,
     on les ajoute un par un avec la fonction add *)
  let create ml =
    List.fold_left (fun p (c, d) -> add (monomial c d) p) [] ml

  (* exponentiation efficace *)
  let rec power x n =
    if n = 0 then
      R.one
    else
      let y = power x (n / 2) in
      if n mod 2 = 0 then R.mul y y else R.mul x (R.mul y y)

  (* l'évaluation parcourt la liste en partant des degrés les plus faibles *)
  let eval p x =
    (* v = valeur courante, xk = x^k *)
    let rec loop v k xk = function
      | [] -> v
      | { coef = c; degree = d } :: l ->
          assert (d >= k);
          let xd = R.mul xk (power x (d - k)) in
          loop (R.add v (R.mul c xd)) d xd l
    in
    loop R.zero 0 R.one (List.rev p)

end


(* Test *)

module Int : Ring with type t = int = struct
  type t = int
  let zero  = 0
  let one = 1
  let add = ( + )
  let mul = ( * )
  let equal = ( = )
end

module P = Poly(Int)

let p = P.create [1,1; 1,3; 1,5]
let () = assert (P.eval p 2 = 42)


This document was generated using caml2html