src/Tools/isac/Knowledge/GCD_Poly_ML.thy
author Walther Neuper <neuper@ist.tugraz.at>
Mon, 02 Sep 2013 14:56:57 +0200
changeset 52099 2a95fc276d0a
parent 52098 bb49dd92fa04
child 52101 c3f399ce32af
permissions -rw-r--r--
GCD_Poly_ML: generalised gcd_poly handling monomials

this also fixes bug in 6ceee9c5d6fd, which had been fixed in bb49dd92fa04 half-assed.
     1 (* rationals, i.e. fractions of multivariate polynomials over the real field
     2    author: Diana Meindl
     3    (c) Diana Meindl
     4    Use is subject to license terms. 
     5 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
     6         10        20        30        40        50        60        70        80         90      100
     7 *)
     8 
     9 theory GCD_Poly_ML imports Complex_Main begin 
    10 
    11 text {*
    12   Below we reference 
    13   F. Winkler, Polynomial algorithyms in computer algebra. Springer 1996.
    14   by page 93 and 95. This is the final version of the Isabelle theory,
    15   which conforms with Isabelle's coding standards and with requirements
    16   in terms of logics appropriate for a theorem prover.
    17 *}
    18 
    19 section {* Predicates for the specifications *}
    20 text {*
    21   This thesis provides program code to be integrated into the Isabelle 
    22   distribution.  Thus the code will be verified against specifications, and 
    23   consequently the code, as \emph{explicit} specification of how to calculate 
    24   results, is accompanied by \emph{implicit} specifications describing the 
    25   properties of the program code.
    26 
    27   For describing the properties of code the format of Haskell will be used.
    28   Haskell \cite{TODO} is front-of-the-wave in research on functional 
    29   programming and as such advances beyond SML. The advancement relevant for 
    30   this thesis is the concise combination of explicit and implicit specification
    31   of a function. The final result of the code developed within this thesis is 
    32   described as follows in Haskell format:
    33 
    34   \begin{verbatim} % TODO: find respective Isabelle/LaTeX features
    35     gcd_poly :: int poly \<Rightarrow> int poly \<Rightarrow> int poly
    36     gcd_poly p1 p2 = d
    37     assumes and p1 \<noteq> [:0:] and p2 \<noteq> [:0:]
    38     yields d dvd p1 \<and> d dvd p2 \<and> \<forall>d'. (d' dvd p1 \<and> d' dvd p2) \<longrightarrow> d' \<le> d
    39   \end{verbatim
    40   
    41   The above specification combines Haskell format with the term language of
    42   Isabelle\HOL. The latter provides a large and ever growing library of 
    43   mechanised mathematics knowledge, which this thesis re-uses in order to 
    44   prepare for integration of the code.
    45 
    46   In the sequel there is a brief account of the notions required for the 
    47   specification of the functions implemented within this thesis. *}
    48 
    49 subsection {* The Isabelle types required *}
    50 text {*
    51   bool, nat, int, option, THE, list, 'a poly
    52 *}
    53 
    54 subsection {* The connectives required *}
    55 text {*
    56   logic ~~/doc/tutorial.pdf p.203
    57   arithmetic < \<le> + - * ^ div \<bar>_\<bar>
    58 *}
    59 
    60 subsection {* The Isabelle functions required *}
    61 text {*
    62   number theory
    63     dvd, mod, gcd
    64     Primes.prime
    65     Primes.next_prime_bound
    66 
    67   lists
    68     List.hd, List.tl
    69     List.length, List.member
    70     List.fold, List.map
    71     List.nth, List.take, List.drop
    72     List.replicate
    73     
    74   others
    75     Fact.fact
    76 *}
    77 
    78 section {* gcd for univariate polynomials *}
    79 ML {*
    80 type unipoly = int list;
    81 val a = [~18, ~15, ~20, 12, 20, ~13, 2];
    82 val b = [8, 28, 22, ~11, ~14, 1, 2];
    83 *}
    84 
    85 subsection {* auxiliary functions *}
    86 ML {* 
    87   (* a variant for div: 
    88     5 div 2 = 2; ~5 div 2 = ~3; BUT WEE NEED ~5 div2 2 = ~2; *)
    89   infix div2
    90   fun a div2 b = 
    91     if a div b < 0 then (abs a) div (abs b) * ~1 else a div b
    92 
    93   (* why has "last" been removed since 2002, although "last" is in List.thy ? *)
    94   fun last ls = (hd o rev) ls
    95 
    96   (* drop tail elements equal 0 *)
    97   fun drop_hd_zeros (0 :: ps) = drop_hd_zeros ps
    98     | drop_hd_zeros p = p
    99   fun drop_tl_zeros p = (rev o drop_hd_zeros o rev) p;
   100 
   101   (* swaps the arguments of a function *)
   102   fun swapf f a b = f b a
   103 *}
   104 
   105 subsection {* modulo calculations for integers *}
   106 ML {*
   107   (* mod_inv :: int \<Rightarrow> nat 
   108      mod_inv r m = s
   109      assumes True
   110      yields r * s mod m = 1 *)
   111   fun mod_inv r m =
   112     let
   113       fun modi (r, rold, m, rinv) = 
   114         if m <= rinv then 0 else
   115           if r mod m = 1
   116           then rinv
   117           else modi (rold * (rinv + 1), rold, m, rinv + 1)
   118      in modi (r, r, m, 1) end
   119 
   120   (* mod_div :: int \<Rightarrow> int \<Rightarrow> nat \<Rightarrow> natO
   121      mod_div a b m = c
   122      assumes True
   123      yields a * b ^(-1) mod m = c   <\<Longrightarrow>  a mod m = (b * c) mod m*)
   124   fun mod_div a b m = a * (mod_inv b m) mod m
   125 
   126   (* required just for one approximation:
   127      approx_root :: nat \<Rightarrow> nat
   128      approx_root a = r
   129      assumes True
   130      yields r * r \<ge> a *)
   131   fun approx_root a =
   132     let
   133       fun root1 a n = if n * n < a then root1 a (n + 1) else n
   134     in root1 a 1 end
   135 
   136   (* chinese_remainder :: int * nat \<Rightarrow> int * nat \<Rightarrow> nat
   137      chinese_remainder (r1, m1) (r2, m2) = r
   138      assumes True
   139      yields r = r1 mod m1 \<and> r = r2 mod m2 *)
   140   fun chinese_remainder (r1, m1) (r2, m2) =
   141     (r1 mod m1) + 
   142       (((r2 - (r1 mod m1)) * (mod_inv m1 m2)) mod m2) * 
   143     m1;
   144 *}
   145 
   146 subsection {* creation of lists of primes for efficiency *}
   147 ML{*
   148   (* is_prime :: int list \<Rightarrow> int \<Rightarrow> bool
   149      is_prime ps n = b
   150      assumes max ps < n  \<and>  n \<le> (max ps)^2  \<and>  
   151        (*     FIXME: really ^^^^^^^^^^^^^^^? *)
   152        (\<forall>p. List.member ps p \<longrightarrow> Primes.prime p) \<and> (\<forall>p. p < n \<and> Primes.prime p \<longrightarrow> List.member ps p)
   153      yields Primes.prime n *)
   154   fun is_prime [] _ = true
   155     | is_prime prs n = 
   156         if n mod (List.hd prs) <> 0 then is_prime (List.tl prs) n else false
   157 
   158   (* make_primes is just local to primes_upto only:
   159      make_primes :: int list \<Rightarrow> int \<Rightarrow> int \<Rightarrow> int list
   160      make_primes ps last_p n = pps
   161      assumes last_p = maxs ps  \<and>  (\<forall>p. List.member ps p \<longrightarrow> Primes.prime p)  \<and>
   162        (\<forall>p. p < last_p \<and> Primes.prime p \<longrightarrow> List.member ps p)
   163      yields n \<le> maxs pps  \<and>  (\<forall>p. List.member pps p \<longrightarrow> Primes.prime p)  \<and>
   164        (\<forall>p. (p < maxs pps \<and> Primes.prime p) \<longrightarrow> List.member pps p)*)
   165     fun make_primes ps last_p n =
   166         if n <= last ps then ps else
   167           if is_prime ps (last_p + 2) 
   168           then make_primes (ps @ [(last_p + 2)]) (last_p + 2) n
   169           else make_primes  ps                   (last_p + 2) n
   170 
   171   (* primes_upto :: nat \<Rightarrow> nat list
   172      primes_upto n = ps
   173        assumes True
   174        yields (\<forall>p. List.member ps p \<longrightarrow> Primes.prime p) \<and>
   175          n \<le> maxs ps \<and> maxs ps \<le> Fact.fact n + 1 \<and>
   176          (\<forall>p. p \<le> maxs ps \<and> Primes.prime p \<longrightarrow> List.member ps p) *)
   177   fun primes_upto n = if n < 3 then [2] else make_primes [2, 3] 3 n
   178  
   179   (* find the next prime greater p not dividing the number n:
   180     next_prime_not_dvd :: int \<Rightarrow> int \<Rightarrow> int (infix)
   181     n1 next_prime_not_dvd n2 = p
   182       assumes True
   183       yields p is_prime  \<and>  n1 < p  \<and>  \<not> p dvd n2  \<and> (* smallest with these properties... *)
   184         (\<forall> p'. (p' is_prime  \<and>  n1 < p'  \<and>  \<not> p' dvd n2)  \<longrightarrow>  p \<le> p') *)
   185   infix next_prime_not_dvd;
   186   fun n1 next_prime_not_dvd n2 = 
   187     let
   188       val ps = primes_upto (n1 + 1)
   189       val nxt = nth ps (List.length ps - 1);
   190     in
   191       if n2 mod nxt <> 0 then nxt else nxt next_prime_not_dvd n2
   192   end
   193 *}
   194 
   195 subsection {* basic notions for univariate polynomials *}
   196 ML {*
   197   (* leading coefficient, includes drop_lc0_up ?FIXME130507 *)
   198   fun lcoeff_up (p: unipoly) = (last o drop_tl_zeros) p
   199   
   200   (* drop leading coefficients equal 0 *)
   201   fun drop_lc0_up (p: unipoly) =drop_tl_zeros p : unipoly;
   202 
   203   (* degree, includes drop_lc0_up ?FIXME130507 *)
   204   fun deg_up (p: unipoly) = ((Integer.add ~1) o length o drop_lc0_up) p;
   205 
   206   (* normalise a unipoly such that lcoeff_up mod m = 1.
   207      normalise :: unipoly \<Rightarrow> nat \<Rightarrow> unipoly
   208      normalise [p_0, .., p_k] m = [q_0, .., q_k]
   209        assumes True
   210        yields \<exists> t. 0 \<le> i \<le> k \<Rightarrow> (p_i * t) mod m = q_i  \<and>  (p_k * t) mod m = 1 *)
   211   fun normalise (p: unipoly) (m: int) =
   212     let
   213       val p = drop_lc0_up p
   214       val lcp = lcoeff_up p;
   215       fun norm nrm i =
   216         if i = 0
   217         then [mod_div (nth p i) lcp m] @ nrm
   218         else norm ([mod_div (nth p i) lcp m] @  nrm) (i - 1) ;
   219     in 
   220       if List.length p = 0 then p else norm [] (List.length p - 1)
   221   end;
   222 *}
   223 
   224 subsection {* addition, multiplication, division *}
   225 ML {*
   226   (* scalar multiplication *)
   227   infix %*
   228   fun (p: unipoly) %* n =  map (Integer.mult n) p : unipoly
   229   
   230   (* scalar divison *)
   231   infix %/ 
   232   fun (p: unipoly) %/ n = map (swapf (curry op div2) n) p : unipoly
   233 
   234   (* minus of polys *)
   235   infix %-%
   236   fun (p1: unipoly) %-% (p2: unipoly) = 
   237     map2 (curry op -) p1 (p2 @ replicate (List.length p1 - List.length p2) 0) : unipoly
   238 
   239   (* dvd for univariate polynomials *)
   240   infix %|%
   241   fun [d] %|% [p] = (p mod d = 0)
   242     | (ds: unipoly) %|% (ps: unipoly) = 
   243       let 
   244         val ds = drop_lc0_up ds;
   245         val ps = drop_lc0_up ps;
   246         val d000 = (replicate (List.length ps - List.length ds) 0) @ ds ;
   247         val quot = (lcoeff_up ps) div2 (lcoeff_up d000);
   248         val remd = drop_lc0_up (ps %-% (d000 %* quot));
   249       in
   250         if remd = [] then true else
   251           if remd <> [] andalso quot <> 0 andalso List.length ds <= List.length remd
   252           then ds %|% remd
   253           else false
   254       end
   255 *}
   256 
   257 subsection {* normalisation and Landau-Mignotte bound *}
   258 ML {* 
   259   (* normalise :: centr_up \<Rightarrow> unipoly => int \<Rightarrow> unipoly
   260      normalise [p_0, .., p_k] m = [q_0, .., q_k]
   261        assumes 
   262        yields 0 \<le> i \<le> k \<Rightarrow> |^ ~m/2 ^| <= q_i <=|^ m/2 ^| 
   263         (where |^ x ^| means round x up to the next greater integer) *)
   264   fun centr_up (p: unipoly) (m: int) =
   265     let
   266       val mi = m div2 2;
   267       val mid = if m mod mi = 0 then mi else mi + 1;
   268       fun centr m mid p_i = if mid < p_i then p_i - m else p_i;
   269     in map (centr m mid) p : unipoly end;
   270 
   271   (*** landau mignotte bound (Winkler p91)
   272     every coefficient of the gcd of a  and b in Z[x] is bounded in absolute value by
   273     2^{min(m,n)} * gcd(a_m,b_n) * min(1/|a_m| 
   274     * root(sum_{i=0}^{m}a_i^2) ,1/|b_n| * root( sum_{i=0}^{n}b_i^2 ) ***)
   275 
   276    (*  sum_lmb :: centr_up \<Rightarrow> unipoly \<Rightarrow> int \<Rightarrow> int
   277      sum_lmb [p_0, .., p_k] e = s
   278        assumes exp >= 0
   279        yields. p_0^e + p_1^e + ... + p_k^e *)
   280   fun sum_lmb (p: unipoly) exp = fold ((curry op +) o (Integer.pow exp)) p 0;
   281 
   282    (* LANDAU_MIGNOTTE_bound :: centr_up \<Rightarrow> unipoly \<Rightarrow> unipoly \<Rightarrow> int
   283       LANDAU_MIGNOTTE_bound [a_0, ..., a_m] [b_0, .., b_n] = lmb
   284         assumes True
   285         yields lmb = 2^min(m,n) * gcd(a_m,b_n) * 
   286           min( 1/|a_m| * root(sum_lmb [a_0,...a_m] 2 , 1/|b_n| * root(sum_lmb [b_0,...b_n] 2)*)
   287   fun LANDAU_MIGNOTTE_bound (p1: unipoly) (p2: unipoly) = 
   288     (Integer.pow (Integer.min (deg_up p1) (deg_up p2)) 2) * (abs (Integer.gcd (lcoeff_up p1) (lcoeff_up p2))) * 
   289     (Int.min (abs((approx_root (sum_lmb p1 2)) div2 ~(lcoeff_up p1)), 
   290               abs((approx_root (sum_lmb p2 2)) div2 ~(lcoeff_up p2))));
   291 *}
   292 
   293 subsection {* modulo calculations on polynomials *}
   294 ML{*
   295   (* chinese_remainder_up :: int * int \<Rightarrow> unipoly * unipoly \<Rightarrow> unipoly
   296      chinese_remainder_up (m1, m2) (p1, p2) = p
   297      assume m1, m2 relatively prime
   298      yields p1 = p mod m1 \<and> p2 = p mod m2 *)
   299   fun chinese_remainder_up (m1, m2) (p1: unipoly, p2: unipoly) = 
   300   let 
   301     fun chinese_rem (m1, m2) (p_1i, p_2i) = chinese_remainder (p_1i, m1) (p_2i, m2)
   302   in map (chinese_rem (m1, m2)) (p1 ~~ p2): unipoly end
   303 
   304   (* mod_up :: unipoly \<Rightarrow> int \<Rightarrow> unipoly
   305      mod_up [p1, p2, ..., pk] m = up 
   306      assume m > 0
   307      yields up = [p1 mod m, p2 mod m, ..., pk mod m]*)
   308   infix mod_up 
   309   fun (p : unipoly) mod_up m = 
   310   let 
   311     fun mod' m p = (curry op mod) p m 
   312   in map (mod' m) p : unipoly end
   313 
   314   (* euclidean algoritm in Z_p[x].
   315      mod_up_gcd :: unipoly \<Rightarrow> unipoly \<Rightarrow> int \<Rightarrow> unipoly
   316      mod_up_gcd p1 p2 m = g
   317        assumes True
   318        yields gcd ((p1 mod m), (p2 mod m)) = g *)
   319   fun mod_up_gcd (p1: unipoly) (p2: unipoly) (m: int) =
   320     let 
   321       val p1m = p1 mod_up m;
   322       val p2m = drop_lc0_up (p2 mod_up m);
   323       val p2n = (replicate (List.length p1 - List.length p2m) 0) @ p2m;
   324       val quot =  mod_div (lcoeff_up p1m) (lcoeff_up p2n) m;
   325       val remd = drop_lc0_up ((p1m %-% (p2n %* quot)) mod_up m)
   326     in
   327       if remd = [] then p2 else
   328         if length remd < List.length p2
   329         then mod_up_gcd p2 remd m 
   330         else mod_up_gcd remd p2 m
   331     end;
   332 
   333   (* primitive_up :: unipoly \<Rightarrow> unipoly
   334      primitive_up p = pp
   335      assumes True
   336      yields \<forall>p1, p2. (List.member pp p1 \<and> List.member pp p2 \<and> p1 \<noteq> p2) \<longrightarrow> gcd p1 p2 = 1 *)
   337   fun primitive_up ([c] : unipoly) = 
   338       if c = 0 then ([0] : unipoly) else ([1] : unipoly)
   339     | primitive_up p =
   340         let
   341           val d = abs (Integer.gcds p)
   342         in
   343           if d = 1 then p else p %/ d
   344         end
   345 *}
   346 
   347 subsection {* gcd_up, code from [1] p.93 *}
   348 ML {*
   349   (* try_new_prime_up :: unipoly \<Rightarrow> unipoly \<Rightarrow> int \<Rightarrow> int \<Rightarrow> int \<Rightarrow> unipoly \<Rightarrow> int  \<Rightarrow> unipoly
   350      try_new_prime_up p1 p2 d M P g p = new_g
   351        assumes d = gcd (lcoeff_up p1, lcoeff_up p2)  \<and> 
   352                M = LANDAU_MIGNOTTE_bound  \<and>  p = prime \<and>  p ~| d  \<and>  P \<ge> p  \<and>
   353                p1 is primitive  \<and>  p2 is primitive
   354        yields  new_g = [1] \<or> (new_g \<ge> g \<and> P > M)
   355 
   356     argumentns "a b d M P g p" named according to [1] p.93 *)
   357   fun try_new_prime_up a b d M P g p =
   358     if P > M then g else
   359       let
   360         val p = p next_prime_not_dvd d
   361         val g_p = centr_up (         (    (normalise (mod_up_gcd a b p) p) 
   362                                        %* (d mod p))                  
   363                               mod_up p) 
   364                             p
   365       in
   366         if deg_up g_p < deg_up g
   367         then
   368           if (deg_up g_p) = 0 then [1]: unipoly else try_new_prime_up a b d M p g_p p
   369         else
   370           if deg_up g_p <> deg_up g then try_new_prime_up a b d M P g p else
   371             let 
   372               val P = P * p
   373               val g = centr_up ((chinese_remainder_up (P, p) (g, g_p)) mod_up P) P
   374             in try_new_prime_up a b d M P g p end
   375       end
   376     
   377   (* HENSEL_lifting_up :: unipoly \<Rightarrow> unipoly \<Rightarrow> int \<Rightarrow> int \<Rightarrow> int \<Rightarrow> unipoly
   378      HENSEL_lifting_up p1 p2 d M p = gcd
   379        assumes d = gcd (lcoeff_up p1, lcoeff_up p2) \<and> 
   380                M = LANDAU_MIGNOTTE_bound \<and> p = prime  \<and>  p ~| d  \<and>
   381                p1 is primitive  \<and>  p2 is primitive
   382        yields  gcd | p1  \<and>  gcd | p2  \<and>  gcd is primitive
   383 
   384     argumentns "a b d M p" named according to [1] p.93 *)
   385   fun HENSEL_lifting_up a b d M p =
   386     let
   387       val p = p next_prime_not_dvd d 
   388       val g_p = centr_up (((normalise (mod_up_gcd a b p) p) %* (d mod p)) mod_up p) p 
   389         (*.. nesting of functions see above*)
   390     in
   391       if deg_up g_p = 0 then [1] else
   392         let 
   393           val g = primitive_up (try_new_prime_up a b d M p g_p p)
   394         in
   395           if (g %|% a) andalso (g %|% b) then g:unipoly else HENSEL_lifting_up a b d M p
   396         end
   397     end
   398 
   399   (* gcd_up :: unipoly \<Rightarrow> unipoly \<Rightarrow> unipoly
   400      gcd_up a b = c
   401      assumes not (a = [] \<or> a = [0]) \<and> not (b = []\<or> b = [0]) \<and> 
   402              a is primitive \<and> b is primitive
   403      yields c dvd a \<and> c dvd b \<and> (\<forall>c'. (c' dvd a \<and> c' dvd b) \<longrightarrow> c' \<le> c) *)
   404   fun gcd_up a b =
   405     let val d = abs (Integer.gcd (lcoeff_up a) (lcoeff_up b))
   406     in 
   407       if a = b then a else
   408         HENSEL_lifting_up a b d (2 * d * LANDAU_MIGNOTTE_bound a b) 1
   409     end;
   410 *}
   411 
   412 section {* gcd for multivariate polynomials *}
   413 ML {*
   414 type monom = (int * int list);
   415 type poly = monom list;
   416 *}
   417 
   418 subsection {* auxiliary functions *}
   419 ML {*
   420   fun land a b = a andalso b
   421   fun all_geq a b = fold land (map2 (curry op >=) a b) true
   422   fun all_geq' i es = fold land (map ((curry op <=) i) es) true
   423   fun maxs is = fold Integer.max is (hd is);
   424   fun div2_swapped d i = i div2 d
   425   fun nth_swapped i ls = nth ls i;
   426   fun drop_last ls = (rev o tl o rev) ls
   427   fun nth_ins n i xs = (*analogous to nth_drop: why not in library.ML?*)
   428     List.take (xs, n) @ [i] @ List.drop (xs, n);
   429   fun coeffs (p : poly) = map fst p
   430 *}
   431 
   432 subsection {* basic notions for multivariate polynomials *}
   433 ML {*
   434   fun lcoeff (p: poly) = (fst o hd o rev) p
   435   fun lexp (p: poly) = (snd o hd o rev) p
   436   fun lmonom (p: poly) = (hd o rev) p
   437 
   438   fun zero_poly (nro_vars: int) = [(0, replicate nro_vars 0)] : poly;
   439 
   440   (* drop leading coefficients equal 0 TODO: signature?*)
   441   fun drop_lc0 (p: poly as [(c, es)]) = if c = 0 then zero_poly (length es) else p
   442     | drop_lc0 p = if lcoeff p = 0 then drop_lc0 (drop_last p) else p
   443 
   444   (* gcd of coefficients WN find right location in file *)
   445   fun gcd_coeff (up: unipoly) = abs (Integer.gcds up)
   446 
   447  (* given p = 3x+4z = [(3,[1,0]),(4,[0,1])] with x<z
   448     add_variable p 2  =>  [(3,[1,0,0]),(4,[0,0,1])] with x<y<z *)
   449   fun add_variable pos (p: poly) = map (apsnd (nth_ins pos 0)) p : poly
   450 *}
   451 
   452 subsection {* ordering and other auxiliary funcions *}
   453 ML {*
   454   (* monomial order: lexicographic order on exponents *)
   455   fun  lex_ord ((_, a): monom) ((_, b): monom) =
   456     let val d = drop_tl_zeros (a %-% b)
   457     in
   458       if d = [] then false else last d > 0
   459     end
   460   fun  lex_ord' ((_, a): monom, (_, b): monom) =
   461     let val d = drop_tl_zeros (a %-% b)
   462     in
   463       if  d = [] then EQUAL 
   464       else if last d > 0 then GREATER else LESS
   465     end
   466 
   467   (* add monomials in ordered polynomal *)
   468   fun add_monoms (p: poly) = 
   469     let 
   470       fun add [(0, es)] [] = zero_poly (length es)
   471         | add [(0, _)] (new: poly) = new
   472         | add [m] (new: poly) = new @ [m]
   473         | add ((c1, es1) :: (c2, es2) :: ms) (new: poly) =
   474           if es1 = es2
   475           then add ([(c1 + c2, es1)] @ ms) new 
   476           else
   477             if c1 = 0 
   478             then add ((c2, es2) :: ms) new 
   479             else add ((c2, es2) :: ms) (new @ [(c1, es1)])
   480     in add p [] : poly end
   481 
   482   (* brings the polynom in correct order and adds monomials *)
   483   fun order (p: poly) = (add_monoms o (sort lex_ord')) p;
   484 
   485   (* largest exponent of variable at location var *)
   486   fun max_deg_var (p: poly) var = (maxs o (map ((nth_swapped var) o snd))) p;
   487 *}
   488 
   489 subsection {* evaluation, translation uni--multivariate, etc *}
   490 ML {*
   491   (* evaluate a polynomial in a certain variable and remove this var from the exp-list *)         
   492   fun eval_monom var value ((c, es): monom) =
   493     (c * (Integer.pow (nth es var) value), nth_drop var es) : monom
   494   fun eval (p: poly) var value = order (map (eval_monom var value) p)
   495 
   496   (* transform a multivariate to a univariate polynomial  TODO map?*)         
   497   fun multi_to_uni (p as (_, es)::_ : poly) = 
   498     if length es = 1 
   499     then 
   500       let fun transfer ([]: poly) (up: unipoly) = up 
   501             | transfer (mp as (c, es)::ps) up =
   502                 if length up = hd es
   503                 then transfer ps (up @ [c])
   504                 else transfer mp (up @ [0])    
   505          in transfer (order p) [] end
   506     else error "Polynom has more than one variable!";
   507 
   508   (* transform a univariate to a multivariate polynomial TODO map *)         
   509   fun uni_to_multi (up: unipoly) =
   510     let fun transfer ([]: unipoly) (mp: poly) (_: int) = add_monoms mp
   511           | transfer up mp var = 
   512             transfer (tl up) (mp @ [(hd up, [var])]) (var + 1)
   513     in  transfer up [] 0 end
   514 
   515   (* multiply a poly with a variable represented by a unipoly (at position var) 
   516     e.g new z: (3x + 2y)*(z - 4)  TODO mp %%*%% (uni_to_multi up) !TODO equal es ! *)
   517   fun mult_with_new_var ([]: poly) (_: unipoly) (_: int) = []
   518     | mult_with_new_var mp up var = 
   519     let 
   520       fun mult ([]: poly) (_: unipoly) (_: int) (new: poly) (_: int) = add_monoms new
   521         | mult mp up var new _ =
   522           let
   523             (* the inner loop *)
   524             fun mult' (_: poly) ([]: unipoly) (_) (new': poly) (_: int) = order new'
   525               | mult' (mp' as (c, es)::ps) up' var new' c2' =
   526                 let val (first, last) = chop var es
   527                 in mult' mp' (tl up') var
   528                   (new' @ [(c * hd up', first @ [c2'] @ last)]) (c2' + 1)
   529                 end
   530           in mult (tl mp) up var (new @ (mult' mp up var [] 0)) (0) end
   531     in mult mp up var [] 0 : poly end
   532 
   533   fun greater_deg (es1: int list) (es2: int list) =
   534     if es1 = [] andalso es2 =[] then 0
   535     else if (nth es1 (length es1 -1)) = (nth es2 (length es1 -1) ) 
   536          then greater_deg (nth_drop (length es1 -1) es1) (nth_drop (length es1 -1) es2)
   537          else if (nth es1 (length es1 -1)) > (nth es2 (length es1 -1)) 
   538               then 1 else 2
   539 
   540   (* Winkler p.95 *) 
   541   fun find_new_r (p1 as (_, es1)::_ : poly) (p2 as (_, es2)::_ : poly) old =
   542     let val p1' as (_, es1')::_ = eval p1 (length es1 - 2) (old + 1)
   543         val p2' as (_, es2')::_ = eval p2 (length es2 - 2) (old + 1);
   544     in
   545       if max_deg_var p1' (length es1' - 1) = max_deg_var p1 (length es1 - 1) orelse
   546          max_deg_var p2' (length es2' - 1) = max_deg_var p2 (length es1 - 1) 
   547       then old + 1
   548       else find_new_r p1 p2 (old + 1)
   549     end
   550 *}
   551 
   552 subsection {* addition, multiplication, division *}
   553 ML {*
   554   (* resulting 0 coefficients are removed by add_monoms *)
   555   infix %%/
   556   fun (p: poly) %%/ d = (add_monoms o (map (apfst (div2_swapped d)))) p;
   557 
   558   infix %%*
   559   fun (p: poly) %%* i = (add_monoms o (map (apfst (Integer.mult i)))) p;
   560 
   561   infix %%+%%
   562   (* the same algorithms as done by hand
   563   fun ([]: poly) %%+%% (mvp2: poly) = mvp2
   564     | p1 %%+%% [] = p1
   565     | p1 %%+%% p2 =
   566       let 
   567         fun plus [] p2 new = order (new @ p2)
   568           | plus p1 [] new = order (new @ p1)
   569           | plus (p1 as (c1, es1)::ps1) (p2 as (c2, es2)::ps2) new = 
   570             if greater_deg es1 es2 = 0
   571             then plus ps1 ps2 (new @ [((c1 + c2), es1)])
   572             else 
   573               if greater_deg es1 es2 = 1
   574               then plus p1 ps2 (new @ [(c2, es2)])
   575               else plus ps1 p2 (new @ [(c1, es1)])
   576       in plus p1 p2 [] end
   577   *)
   578   fun (p1 : poly) %%+%% (p2 : poly) = order (p1 @ p2)
   579 
   580   infix %%-%%
   581   fun p1 %%-%% (p2: poly) = p1 %%+%% (map (apfst (Integer.mult ~1)) p2)
   582 
   583   infix %%*%%
   584   fun mult_monom ((c1, es1): monom) ((c2, es2): monom) =
   585     (c1 * c2, map2 Integer.add es1 es2): monom;
   586   fun mult_monoms (ms: poly) m = map (mult_monom m) ms : poly
   587   fun p1 %%*%% (p2 : poly) = order (fold (curry op @) (map (mult_monoms p1) p2) [])
   588 
   589   infix %%|%% 
   590   (* the same algorithms as done by hand; TODO fold / map ? *)
   591   fun ([(c1, es1)]: poly) %%|%%  ([(c2, es2)]: poly) = all_geq es2 es1 andalso c2 mod c1 = 0
   592     | _  %%|%% [(0,_)] = true
   593     | p1 %%|%% p2 =
   594       if [lmonom p1] %%|%% [lmonom p2]
   595       then 
   596         p1 %%|%% 
   597           (p2 %%-%%
   598             ([((lcoeff p2) div2 (lcoeff p1), (lexp p2) %-% (lexp p1))]
   599               %%*%% p1)) 
   600       else false
   601 
   602   (* %%/%% :: poly \<Rightarrow> poly \<Rightarrow> (poly \<times> poly)
   603      p1 %%/%% p2 = (q, r)
   604      assumes p2 \<noteq> []  \<and>  deg p1 \<ge> deg p2
   605      yields THE q r. p1 = q *\<^isub>p p2  +\<^isub>p  r *)
   606   infix %%/%% 
   607   fun (p1: poly) %%/%% (p2: poly) =
   608     let 
   609       fun div_up p1 p2 quot_old = 
   610       let 
   611         val p1 as (c, es)::_ = drop_lc0 (order p1);
   612         val p2 = drop_lc0 (order p2);
   613         val [c] = zero_poly (length es);
   614         val d = (replicate (List.length p1 - List.length p2) c) @ p2 ;
   615         val quot = [(lcoeff p1 div2 lcoeff d, lexp p1 %-% lexp p2)];
   616         val remd = drop_lc0 (p1 %%-%% (d %%*%% quot));
   617       in 
   618         if remd <> [c] andalso all_geq' 0 (lexp remd %-% lexp p2)
   619         then div_up remd p2 (quot @ quot_old)
   620         else (quot @ quot_old : poly, remd)
   621       end
   622     in div_up p1 p2 [] end 
   623 *}
   624 
   625 subsection {* Newton interpolation *}
   626 ML{* (* first step *)
   627   fun newton_first (x: int list) (f: poly list) (pos: int) =
   628     let 
   629       val new_vals = [(hd x) * ~1, 1];
   630       val p = (add_variable pos (hd f)) %%+%% 
   631         ((mult_with_new_var 
   632           (((nth f 1) %%-%% (hd f)) %%/ (nth x 1) - (hd x)))   new_vals pos);
   633       val steps = [((nth f 1) %%-%% (hd f)) %%/ ((nth x 1) - (hd x))]
   634     in (p, new_vals, steps) end
   635   
   636    fun next_step ([]: poly list) (new_steps: poly list) (_: int list) = new_steps
   637      | next_step steps new_steps x =  
   638        next_step (tl steps)
   639          (new_steps @ 
   640            [((last new_steps) %%-%% (hd steps)) %%/ ((last x) - (nth x (length x - 3)))])
   641          (nth_drop (length x -2) x)
   642 
   643   fun NEWTON x f (steps: poly list) (up: unipoly) (p: poly) pos = 
   644     if length x = 2
   645     then 
   646       let val (p', new_vals, steps) = newton_first x [(hd f), (nth f 1)] pos
   647       in (p', new_vals, steps) end
   648     else 
   649       let 
   650         val new_vals = 
   651           multi_to_uni ((uni_to_multi up) %%*%% (uni_to_multi [(nth x (length x -2) ) * ~1, 1]));
   652         val new_steps = [((nth f (length f - 1)) %%-%% (nth f (length f - 2))) %%/ 
   653           ((last x) - (nth x (length x - 2)))];
   654         val steps = next_step steps new_steps x;
   655         val p' = p %%+%% (mult_with_new_var (last steps) new_vals pos);
   656       in (order p', new_vals, steps) end 
   657 *}
   658 
   659 subsection {* gcd_poly algorithm, code for [1] p.95 *}
   660 ML {*
   661   fun gcd_monom (c1, es1) (c2, es2) = (Integer.gcd c1 c2, map2 Integer.min es1 es2)
   662 
   663   (* naming of n, M, m, r, ...  follows Winkler p. 95 
   664     assumes: n = length es1 = length es2
   665     r: *)
   666   fun gcd_poly' a [monom as (c, es)] _ _ = [fold gcd_monom a monom]
   667     | gcd_poly' [monom as (c, es)] b _ _ = [fold gcd_monom b monom]
   668     | gcd_poly' (a as (_, es1)::_ : poly) (b as (_, es2)::_ : poly) n r =
   669       if lex_ord (lmonom b) (lmonom a) then gcd_poly' b a n r else
   670         if n > 1
   671         then
   672           let val M = 1 + Int.min (max_deg_var a (length es1 - 2), max_deg_var b (length es2 - 2)); 
   673           in try_new_r a b n M r [] [] end
   674         else 
   675           let val (a', b') = (multi_to_uni a, multi_to_uni b)
   676           in
   677             (* a b are made primitive and this is compensated by multiplying with gcd of divisors *)
   678             uni_to_multi ((gcd_up (primitive_up a') (primitive_up b')) %* 
   679                           (abs (Integer.gcd (gcd_coeff a') (gcd_coeff b'))))
   680           end
   681 
   682   (* fn: poly -> poly -> int -> 
   683                       int -> int -> int list -> poly list -> poly
   684      assumes length a > 1  \<and>  length b > 1
   685      yields TODO  
   686   *)
   687   and try_new_r a b n M r r_list steps = 
   688      let 
   689         val r = find_new_r a b r;
   690         val r_list = r_list @ [r];
   691         val g_r = gcd_poly' (order (eval a (n - 2) r)) 
   692                             (order (eval b (n - 2) r)) (n - 1) 0
   693         val steps = steps @ [g_r];
   694       in HENSEL_lifting a b n M 1 r r_list steps g_r ( zero_poly n ) [1] end
   695   (* fn: poly -> poly -> int -> 
   696                            int -> int -> int -> int list -> poly list -> 
   697                            |                  poly -> poly -> int list -> poly
   698      assumes length a > 1  \<and>  length b > 1
   699      yields TODO  
   700   *)
   701   and HENSEL_lifting a b n M m r r_list steps g g_n mult =
   702     if m > M then if g_n %%|%% a andalso g_n %%|%% b then g_n else
   703       try_new_r a b n M r r_list steps 
   704     else
   705       let 
   706         val r = find_new_r a b r; 
   707         val r_list = r_list @ [r];
   708         val g_r = gcd_poly' (order (eval a (n - 2) r)) 
   709                             (order (eval b (n - 2) r)) (n - 1) 0
   710       in  
   711         if lex_ord (lmonom g) (lmonom g_r)
   712         then HENSEL_lifting a b n M 1 r r_list steps g g_n mult
   713         else if (lexp g) = (lexp g_r) 
   714           then
   715             let val (g_n, new, steps) = NEWTON r_list [g, g_r] steps mult g_n (n - 2)
   716             in 
   717               if (nth steps (length steps - 1)) = (zero_poly (n - 1))
   718               then HENSEL_lifting a b n M (M + 1) r r_list steps g_r g_n new
   719               else HENSEL_lifting a b n M (m + 1) r r_list steps g_r g_n new
   720             end
   721           else (* \<not> lex_ord (lmonom g) (lmonom g_r) *)
   722             HENSEL_lifting a b n M (m + 1) r r_list steps g  g_n mult
   723       end
   724 
   725   fun majority_of_coefficients_is_negative a b c =
   726     let val cs = (coeffs a) @ (coeffs b) @ (coeffs c)
   727     in length (filter (curry op < 0) cs) < length cs div 2 end
   728 
   729   (* gcd_poly :: poly \<Rightarrow> poly \<Rightarrow> poly
   730      gcd_poly a b = ((a', b'), c)
   731      assumes a \<noteq> [] \<and> b \<noteq> [] \<and> 
   732      yields a = a' *\<^isub>p c  \<and>  b = b' *\<^isub>p c \<and> (\<forall>c'. (c' dvd\<^isub>p  a \<and> c' dvd\<^isub>p b) \<longrightarrow> c' \<le>\<^isub>p c)  \<and>
   733        \<not> gcds a' < 0 \<and> gcds a' < 0                                                      *)
   734   fun gcd_poly (a as (_, es)::_ : poly) b = 
   735     let val c = gcd_poly' a b (length es) 0
   736         val (a': poly, _) = a %%/%% c
   737         val (b': poly, _) = b %%/%% c
   738     in 
   739       if majority_of_coefficients_is_negative a' b' c
   740       then ((a' %%* ~1, b' %%* ~1), c %%* ~1) (*makes results look nicer*)
   741       else ((a', b'), c)
   742     end
   743 *}
   744 
   745 section {* example from the last mail to Tobias Nipkow *}
   746 ML {* (* test, see text below *)
   747   val a = [(1,[2, 0]), (~1,[0, 2])]; (* x^2 - y^2 *)
   748   val b = [(1,[2, 0]), (~1,[1, 1])]; (* x^2 - x*y *)
   749   val ((a', b'), c) = gcd_poly a b;
   750 
   751   a' = [(~1, [1, 0]), (~1, [0, 1])];
   752   b' = [(~1, [1, 0])];
   753   c = [(~1, [1, 0]), (1, [0, 1])];
   754   (* checking the postcondition: *)
   755   a = (a' %%*%% c) andalso b = (b' %%*%% c); (* where "%%*%%" is "*\<^isub>p" *)
   756   (* \<forall>c'. (c' dvd\<^isub>p  a \<and> c' dvd\<^isub>p b) \<longrightarrow> c' \<le>\<^isub>p c) can NOT be checked this way, of course *)
   757 *}
   758 
   759 text {* last mail to Tobias Nipkow:
   760 Eine erste Idee, wie die Integration der Diplomarbeit für einen Benutzer 
   761 von Isabelle aussehen könnte, wäre zum Beispiel im
   762 
   763    lemma cancel:
   764      assumes asm3: "x^2 - x*y \<noteq> 0" and asm4: "x \<noteq> 0"
   765      shows "(x^2 - y^2) / (x^2 - x*y) = (x + y) / (x::real)"
   766      apply (insert asm3 asm4)
   767      apply simp
   768    sorry
   769 
   770 die Assumptions
   771 
   772    asm1: "(x^2 - y^2) = (x + y) * (x - y)" and asm2: "x^2 - x*y = x * (x - y)"
   773 
   774 im Hintergrund automatisch zu erzeugen (mit der Garantie, dass "(x - y)" der GCD ist) 
   775 und sie dem Simplifier (für die Rule nonzero_mult_divide_mult_cancel_right) zur 
   776 Verfügung zu stellen, sodass anstelle von "sorry" einfach "done" stehen kann.
   777 Und weiters wäre eventuell asm3 zu "x - y \<noteq> 0" zu vereinfachen, eine 
   778 Rewriteorder zum Herstellen einer Normalform festzulegen, etc.
   779 *}
   780 
   781 section {* Preparations for Lucas-Interpretation *}
   782 text {*
   783   Lucas-Interpretation shall make "gcd_poly" transparent to the user,
   784   such that step-wise interpretation gives some intuition of the algorithm
   785   and opportunities for interactive learning by trial and error.
   786   The vision is to provide a learning environment similar to a good
   787   chess program, where the matter (chess) is selfcontained and
   788   learning happens by interaction with the system.
   789 
   790   GDC_Poly gives raise to a major case study on the above aims.
   791   Below pedagogical questions come first and respective technical
   792   requirements subsequently.
   793 *}
   794 
   795 subsection {* Questions of learners *}
   796 text {*
   797   The earlier the phase in appropaching a new topic for a student, the more
   798   divergent the questions are. So here are just some which suggest themselves.
   799 *}
   800 subsubsection {* Why does naive transfer from Z to polynomials not work? *}
   801 text {*
   802   Given the Eucliden Algorithm in Z ...
   803 *}
   804 ML {*
   805   fun euclid_int a 0 = a
   806     | euclid_int a b =
   807       if a < b then euclid_int b a else
   808       let
   809         val (q, r) = (a div b, a mod b)
   810         val _ = writeln (PolyML.makestring a ^ " = "  ^ PolyML.makestring q  ^ 
   811           " * "  ^ PolyML.makestring b ^ " + "  ^ PolyML.makestring r)
   812       in euclid_int b r : int end;
   813   euclid_int 192 162; (* ..stepwise execution is essential for comprehension *)
   814 *}
   815 text {* ... let it naively transfer to the polynomial ring Z[x]: *}
   816 ML {*
   817   fun deg p = max_deg_var (p: poly) 0 (*TODO?*)
   818   fun EUCLID_naive a [(0, [0])] = a   (*TODO: for Z[x,y] we would need [(0, [0, 0])] here*)
   819     | EUCLID_naive a b =
   820       if deg a < deg b then EUCLID_naive b a else
   821       let
   822         val (q, r) = a %%/%% b
   823         val _ = writeln (PolyML.makestring a ^ "  ==  "  ^ PolyML.makestring q  ^ 
   824           "  **  "  ^ PolyML.makestring b ^ "  ++  "  ^ PolyML.makestring r)
   825       in EUCLID_naive b r : poly end;
   826 *} 
   827 ML {*  
   828   (*The example used in the inital mail to Tobias Nipkow, simplified to univariate*)
   829   "--------------------------------";
   830   val a = uni_to_multi [0,~1,1]; (* -x + x^2 =  x   *(-1 + x) *)
   831   val b = uni_to_multi [~1,0,1]; (* -1 + x^2 = (1+x)*(-1 + x) *)
   832   "--------------------------------";
   833   EUCLID_naive a b = [(1, [0]), (~1, [1])];          (* 1 - x  .. is acceptable*)
   834 *}
   835 ML {*
   836   "--------------------------------";
   837   val a = uni_to_multi [~1,0,0,1]; (* -1 + x^3 = (1+x+x^2)*(-1 + x) *)
   838   val b = uni_to_multi [0,~1,1];   (* -x + x^2 = x        *(-1 + x) *)
   839   "--------------------------------";
   840   EUCLID_naive a b = [(~1, [0]), (1, [1])];          (*-1 + x *) 
   841 *}
   842 text {*
   843   EUCLID_naive, however, does not work in general, because division is 
   844   not as general as in Z:
   845 *}
   846 ML {*
   847 "--------------------------------";
   848 val a = uni_to_multi [0,~1,0,0,1]; (* -x + x^4 = x*(1+x+x^2)*(-1 + x) *)
   849 val b = uni_to_multi [~2,2];       (* -x + x^2 =  2         *(-1 + x) *)
   850 "--------------------------------";
   851 (*EUCLID_naive a b = [(~1, [0]), (1, [1])] happens to loop*)
   852 *} 
   853 text {*
   854 (*  (x^4 - x) : (2*x - 2) = 1/2 * x^4 ... 
   855   This division, equal to above division, is impossible in Z[x], rather, it requires Q[x]. 
   856   This problem of division with integer coefficients proceeds down from the least 
   857   coefficient to the other coefficients.
   858   Nevertheless, below we are going to generalise division such that the
   859   Euclidean Algorithm can work on Z[x].
   860 *}
   861 
   862 subsubsection {* Can division of polynomials be adapted to Euclid? *}
   863 text {*
   864   In principle it is simplest to go from Z[x] to Q[x], polynamials over rational numbers
   865   and thus, for instance, allow 
   866             (x^4 - x) : (2*x - 2) = 1/2 * x^4 ... 
   867   However, Q is computationally less efficient and the terms are more complicated.
   868   Fortunately, [1].p.83/84 tells, that division in Z[x] can be generalised for the 
   869   Euclidean Algorithm.
   870   In order to comprehend stepwise execution of the division algorithm below
   871   (which is exactly as done by hand) we adapt to our representation of (univariate)
   872   polynomials as lists and take an instructuve example:
   873 
   874     (2*x^4 + 2*x^3 + 2*x^2 + 2*x + 2) : (3*x + 4) = 2/3 * x^2 ...
   875 
   876   is translated to a reversed arrangement of monomials
   877 
   878     (2 + 2*x + 2*x^2 + 2*x^3 + 2*x^4) : (1 + 2*x + 3*x^2) = ...
   879 
   880   and further to lists:
   881 
   882         [  2,   2,   2,   2,   2] : [1, 2, 3] =
   883       3*[  2,   2,   2,   2,   2]                   *3
   884       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
   885         [  6,   6,   6,   6,   6]                   :
   886       --[  0,   0,   2,   4,   6] <--........ * [ 2]
   887       ------------------------                      :
   888         [  6,   6,   4,   2,   0]             = [ 2]*3
   889       3*[  6,   6,   4,   2]                           *3
   890       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
   891         [ 18,  18,  12,   6]                           :
   892       --[  0,   2,   4,   6] <-------........--- * [2]
   893       ------------------------                         :
   894         [ 18,  16,   8,   0]                  = [ 6, 2]
   895       3*[ 18,  16,   8]                                   *3
   896       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
   897         [ 54,  48,  24]                                    :
   898       --[  8,  16,  24] <------------........-------- * [8]
   899       ------------------------                             :
   900         [46, 32, 0]                           = [18, 6,  8]
   901       =======================================
   902      27*[  2,   2,   2,   2,   2] : [1, 2, 3] = [18, 6,  8],  remd [46, 32]
   903 
   904   We implement this algorithm below and check it with these and other values.
   905 
   906   First we need some auxiliary functions.
   907 *}
   908 ML {* (* auxiliary functions *)
   909   fun for_quot_regarding p1 p2 p1' quot remd =
   910     let 
   911        val zeros = deg_up p1' - deg_up remd - 1(*length [q]*)
   912        val max_qot_deg = deg_up p1 - deg_up p2
   913      in
   914        if zeros + 1(*length [q]*) + deg_up quot > max_qot_deg (*possible in last step of div*)
   915        then max_qot_deg - deg_up quot - 1(*length [q]*) else zeros
   916      end
   917 
   918   (* multiply polynomial p such that it has degree d with d = deg p*)
   919   fun mult_to_deg d p =
   920     let val d' = deg_up p
   921     in if d >= d' then (replicate (d - d') 0) @ p : unipoly else p end;
   922 
   923   (* find a factor n and a quotient q such that n > 0 \<and> n*c1 = q*c2 *)
   924   fun fact_quot c1 c2 =
   925     let
   926       val d = abs (Integer.gcd c1 c2)
   927       val n = c2 div2 d
   928       val q = c1 div2 d
   929     in if n > 0 then (n, q) else (~1 * n, ~1 * q) end;
   930 
   931   infix %+%
   932   fun p1 %+% p2 =
   933     if length p1 > length p2
   934     then map2 (curry op +) p1 (p2 @ (replicate (List.length p1 - List.length p2) 0))
   935     else map2 (curry op +) (p1 @ (replicate (List.length p2 - List.length p1) 0)) p2;
   936 
   937   infix %*%
   938   fun p1 %*% p2 =multi_to_uni (uni_to_multi p1 %%*%% uni_to_multi p2);
   939   ([1,2,1] %*% [1,3,3,1]) = [1, 5, 10, 10, 5, 1];
   940 
   941   replicate 3 0 = [0, 0, 0];
   942   replicate 0 0 = [];
   943 
   944   val trace_div = Unsynchronized.ref true;
   945   val trace_div_invariant = Unsynchronized.ref false; (*.. true expects !trace_div = false *)
   946   fun div_invariant2 p1 p2 n ns zeros q quot remd = 
   947     (PolyML.makestring p1 ^ " * "  ^ PolyML.makestring (n * ns) ^ " = " ^ 
   948      PolyML.makestring (mult_to_deg (deg_up p1 - deg_up p2) 
   949       ((zeros @ [q] @ (quot %* n)))) ^ 
   950      " ** " ^ PolyML.makestring p2 ^ " ++ " ^ 
   951      PolyML.makestring ((rev o (mult_to_deg (deg_up p1)) o rev) remd))
   952   fun writeln_trace p1 p1' p2 quot n q (zeros:int) remd (ns: int) = 
   953     (if (! trace_div) then
   954       ( writeln ("  div   " ^ PolyML.makestring p1' ^ " * " ^ PolyML.makestring n);
   955         writeln  "  div   ~~~~~~~~~~~~~~~~~~~~~~~~~~~";
   956         writeln ("  div   " ^ PolyML.makestring (p1' %* n));
   957         writeln ("  div-- " ^ PolyML.makestring (mult_to_deg (deg_up p1') p2 %* q) ^ 
   958           " <--- " ^ PolyML.makestring p2 ^ " * " ^ PolyML.makestring q);
   959         writeln  "  div   ---------------------------";
   960         writeln ("  div   " ^ PolyML.makestring ((p1' %* n)%-%(mult_to_deg (deg_up p1') p2 %* q)) ^ 
   961           "                            quot = " ^ 
   962           PolyML.makestring (replicate zeros 0) ^ " @ [" ^ 
   963           PolyML.makestring q ^ "] @ (" ^ PolyML.makestring n ^ 
   964           " * " ^ PolyML.makestring quot ^ ")" );
   965         if (p1' %* n) = ((mult_to_deg (deg_up p1') (p2 %* q)) %+% remd) then () 
   966         else error ("invariant 1 does not hold: " ^ PolyML.makestring (p1' %* n) ^ " = " ^ 
   967         PolyML.makestring (mult_to_deg (deg_up p1') p2) ^ " * " ^ PolyML.makestring q ^ " ++ " ^ 
   968         PolyML.makestring (remd @ []))
   969       ) else ();
   970       (*invariant 2: initial p1 *\<^isub>s ns = quot *\<^isub>p p2   +\<^isub>p  remd *)
   971       if (p1 %* (n * ns)) = 
   972         ((mult_to_deg (deg_up p1 - deg_up p2) 
   973           ((replicate zeros 0) @ [q] @ (quot %* n)) %*% p2 %+% remd))
   974       then
   975         if (! trace_div_invariant) 
   976         then writeln ("  div   " ^ div_invariant2 p1 p2 n ns (replicate zeros 0) q quot remd)
   977         else ()
   978       else error ("invariant 2 does not hold: " ^ div_invariant2 p1 p2 n ns (replicate zeros 0) q quot remd)
   979     )
   980 
   981   (* generalized division: divide in Z[x] with multiplication of the dividend 
   982      such that the quotient is again in Z[x], i.e. has integer coefficients.
   983      fact_div :: unipoly \<Rightarrow> unipoly \<Rightarrow> (unipoly, unipoly, nat)
   984      fact_div p1 p2 = (n, q, r)
   985        assumes p2 \<noteq> [0]  \<and>  deg p1 \<ge> deg p2
   986        yields p1 *\<^isub>s n = q *\<^isub>p p2   +\<^isub>p   r 
   987 
   988      note 1: p1' changes with iterations, p1 remains equal -- only required for invariant 2 
   989      note 2: test -- fun %*/% Subscript raised (line 509 of lib-- shows a bug. *)
   990   infix %*/%
   991   fun div_int_up p1 p1' p2 quot ns =
   992     let
   993       val (n, q) = fact_quot (lcoeff_up p1') (lcoeff_up p2);
   994       val remd = drop_tl_zeros ((p1' %* n) %-% (mult_to_deg (deg_up p1') p2 %* q));
   995       val zeros = for_quot_regarding p1 p2 p1' quot remd
   996       val _ = writeln_trace p1 p1' p2 quot n q zeros remd ns
   997       val quot = (replicate zeros 0) @ [q] @ (quot %* n)
   998       (*div by hand uses q; * n multiplies the previous quot*)
   999       val ns = n * ns
  1000     in
  1001       if deg_up remd >= deg_up p2
  1002       then div_int_up p1 remd p2 quot ns
  1003       else (ns, quot, remd)
  1004     end
  1005 
  1006   fun p1 %*/% p2 = 
  1007     let 
  1008       val (n, q, r) = div_int_up p1 p1 p2 [] 1
  1009       val _ = if (! trace_div) then
  1010         (writeln  "  div   .....................................................................";
  1011          writeln ("  div   " ^ PolyML.makestring n ^ " * " ^ PolyML.makestring p1 ^ " = " ^ 
  1012          PolyML.makestring q ^ " ** " ^ PolyML.makestring p2 ^ " ++ " ^ PolyML.makestring r);
  1013          writeln  "  div   ....................................................................."
  1014         ) else ()
  1015     in (n, q, r) end;
  1016 *}
  1017 text {* Here is the check of the algorithm with the values from above: *}
  1018 ML {*
  1019   trace_div := true;
  1020   trace_div_invariant := false;
  1021   val p1 = [2, 2, 2, 2, 2];
  1022   val p2 = [1, 2, 3];
  1023   val (n (* = 27*), 
  1024        q (* = [8, 6, 18]*), 
  1025        r (* = [46, 32]*)) = p1 %*/% p2;
  1026   if (p1 %* n) = ((q %*% p2) %+% r) then () else error "fun %*/% changed"
  1027 *} 
  1028 text {* However, intermediate values increase exponentially: *}
  1029 ML {* (* example from [1].p.84 *)
  1030   val p1 = [~5,2,8,~3,~3,0,1,0,1];
  1031   val p2 = [21,~9,~4,5,0,3];
  1032   val (n (* = 27*), 
  1033        q (* = [22, ~6, ~6, 9]*), 
  1034        r (* = [~597, 378, 376, ~458, 6]*)) = p1 %*/% p2;
  1035   if (p1 %* n) = ((q %*% p2) %+% r) then () else error "fun %*/% changed 1"
  1036 *} 
  1037 ML {* (* example used to develop algorithm by hand *)
  1038   val p1 = [2,2,2, 2,2,2];
  1039   val p2 = [7,8,6];
  1040   val (n (* = 162*), 
  1041        q (* = [55, 15, ~18, 54]*), 
  1042        r (* = [~61, ~221]*)) = p1 %*/% p2;
  1043   if (p1 %* n) = ((q %*% p2) %+% r) then () else error "fun %*/% changed 2"
  1044 *} 
  1045 
  1046 subsubsection {* The Euclidean Algorithm really works with polynomials? *}
  1047 text {* 
  1048  The generalized division implemented above really can replace the
  1049   division of integers in the Eucliden Algorithm:
  1050 *}
  1051 ML {*
  1052   val trace_Euclid = Unsynchronized.ref true;
  1053   fun writeln_trace_Euclid (a: int list) (b: int list) (n: int) (q: int list) (r: int list) =
  1054     if (! trace_Euclid) then
  1055       writeln ("Euclid "  ^ PolyML.makestring a ^ "  *  "  ^ PolyML.makestring n ^ 
  1056         "  ==  "  ^ PolyML.makestring q  ^ 
  1057         "  **  "  ^ PolyML.makestring b ^ "  ++  "  ^ PolyML.makestring r)
  1058     else ();
  1059 
  1060   fun EUCLID_naive_up a [] = primitive_up a
  1061     | EUCLID_naive_up a b =
  1062       if deg_up a < deg_up b then EUCLID_naive_up b a else
  1063       let
  1064         val (n, q, r) = a %*/% b
  1065         val _ = writeln_trace_Euclid a b n q r
  1066       in EUCLID_naive_up b r : unipoly end;
  1067 *}
  1068 ML {*
  1069   (*The example used in the inital mail to Tobias Nipkow made univariate: y -> 1*)
  1070   trace_div := false;
  1071   trace_div_invariant := false;
  1072   trace_Euclid := true;
  1073   "--------------------------------";
  1074   val a = [0,~1,1]: unipoly; (* -x + x^2 =  x   *(-1 + x) *)
  1075   val b = [~1,0,1]: unipoly; (* -1 + x^2 = (1+x)*(-1 + x) *)
  1076   "--------------------------------";
  1077   EUCLID_naive_up a b; (* = [1, ~1]*)          (*( 1 - x) *)
  1078 *}
  1079 ML {*
  1080   "--------------------------------";
  1081   val a = [~1,0,0,1]: unipoly; (* -1 + x^3 = (1+x+x^2)*(-1 + x) *)
  1082   val b = [0,~1,1]: unipoly;   (* -x + x^2 = x        *(-1 + x) *)
  1083   "--------------------------------";
  1084   EUCLID_naive_up a b; (*  = [~1, 1]*)      (*(-1 + x) *)
  1085 *} 
  1086 ML {* (*NOW this works with generalized division*)
  1087   "--------------------------------";
  1088   val a = [0,~1,0,0,1]: unipoly; (* -x + x^4 = x*(1+x+x^2)*(-1 + x) *)
  1089   val b = [~2,2]: unipoly;       (* -x + x^2 =  2         *(-1 + x) *)
  1090   "--------------------------------";
  1091   EUCLID_naive_up a b; (* = [~1, 1]*)
  1092 *} 
  1093 ML {* 
  1094   (* from [1].p.84; the generated coefficients are different for an unknown reason *)
  1095   "--------------------------------";
  1096   val a = [~5,2,8,~3,~3,0,1,0,1]: unipoly;
  1097   val b = [21,~9,~4,5,0,3]: unipoly;
  1098   "--------------------------------";
  1099   EUCLID_naive_up a b; (* = [1]*)
  1100 *} 
  1101 ML {*
  1102   (* from [1].p.94 *)
  1103   "--------------------------------";
  1104   val a = [~18, ~15, ~20, 12, 20, ~13, 2]: unipoly;
  1105   val b = [8, 28, 22, ~11, ~14, 1, 2]: unipoly;
  1106   "--------------------------------";
  1107   EUCLID_naive_up a b; (* = [~2, ~1, 1]*)
  1108 *}
  1109 text {*
  1110   Note: The above algorithm still needs to take into account the case, where 
  1111   constants can be factored out from polynomials, see [1].p.82-84.  
  1112 *} 
  1113 
  1114 subsubsection {* Interactivity on Euclids Algorithm *}
  1115 text {*
  1116   Euclids Algorithm in modern algebraic notation explains itself if presented
  1117   this way:
  1118 *}
  1119 ML {*
  1120   trace_div := false;
  1121   trace_div_invariant := false;
  1122   trace_Euclid := true;
  1123   "--------------------------------";
  1124   val a = [~1,0,0,1]: unipoly; (* -1 + x^3 = (1+x+x^2)*(-1 + x) *)
  1125   val b = [0,~1,1]: unipoly;   (* -x + x^2 = x        *(-1 + x) *)
  1126   "--------------------------------";
  1127   EUCLID_naive_up a b; (*  = [~1, 1]*)      (*(-1 + x) *)
  1128 (*
  1129   Euclid [~1, 0, 0, 1]  *  1  ==  [1, 1]  **  [0, ~1, 1]  ++  [~1, 1] 
  1130   Euclid [0, ~1, 1]  *  1  ==  [0, 1]  **  [~1, 1]  ++  [] 
  1131 *)
  1132 *}
  1133 ML {*
  1134   trace_Euclid := true;
  1135   (* from [1].p.94 *)
  1136   "--------------------------------";
  1137   val a = [~18, ~15, ~20, 12, 20, ~13, 2]: unipoly;
  1138   val b = [8, 28, 22, ~11, ~14, 1, 2]: unipoly;
  1139   "--------------------------------";
  1140   EUCLID_naive_up a b; (* = [~2, ~1, 1]*)
  1141 (*
  1142 Euclid [~18, ~15, ~20, 12, 20, ~13, 2]  *  1  ==  [1]  **  [8, 28, 22, ~11, ~14, 1, 2]  ++  [~26, ~43, ~42, 23, 34, ~14] 
  1143 Euclid [8, 28, 22, ~11, ~14, 1, 2]  *  98  ==  [~41, ~14]  **  [~26, ~43, ~42, 23, 34, ~14]  ++  [~282, 617, ~168, ~723, 344] 
  1144 Euclid [~26, ~43, ~42, 23, 34, ~14]  *  59168  ==  [787, ~2408]  **  [~282, 617, ~168, ~723, 344]  ++  [~1316434, ~3708859, ~867104, 1525321] 
  1145 Euclid [~282, 617, ~168, ~723, 344]  *  6783102487  ==  [~2345549, 1529768]  **  [~1316434, ~3708859, ~867104, 1525321]  ++  [~5000595353600, ~2500297676800, 2500297676800] 
  1146 Euclid [~1316434, ~3708859, ~867104, 1525321]  *  7289497600  ==  [1919, 4447]  **  [~5000595353600, ~2500297676800, 2500297676800]  ++  [] 
  1147 *)
  1148 *}
  1149 text {*
  1150   A student only needs to observe, how the second factor on the right-hand side
  1151   shifts to the left-hand side; and he or she has to observe the remainder []
  1152   in the last line and take the preceeding remainder as the gcd.
  1153 
  1154   In order to see that this recursion really computes the gcd, one needs to
  1155   know the fixpoint (or loop invariant):
  1156 *}
  1157 (*
  1158 lemma "a = (q *\<^isub>P b +\<^isub>P r) *\<^isub>s n  \<and>  degree b > degree r  \<longrightarrow>  gcd a b = gcd b r"
  1159 sorry
  1160 *)
  1161 text {*
  1162   The above lemma is analogous to 
  1163   @{thm gcd_add_mult_int} = "gcd ?m (?k * ?m + ?n) = gcd ?m ?n" from HOL/GCD.thy
  1164 *}
  1165 
  1166 subsubsection {* Interactivity and/or understanding of polynomial division *}
  1167 text {*
  1168   Division raises two requirements wrt. interactivity: 
  1169   (1) a student wants to roughly understand what happens in the algorithm
  1170   (2) a student wants to reproduce the algorithm by hand.
  1171 
  1172   Case (1) again calls for the fixpoint:
  1173 *}
  1174 ML {*
  1175   trace_div := false;
  1176   trace_div_invariant := true;
  1177   val p1 = [2, 2, 2, 2, 2];
  1178   val p2 = [1, 2, 3];
  1179   val (n (* = 27*), 
  1180        q (* = [8, 6, 18]*), 
  1181        r (* = [46, 32]*)) = p1 %*/% p2;
  1182 (*
  1183   div   [2, 2, 2, 2, 2] * 3 = [0, 0, 2] ** [1, 2, 3] ++ [6, 6, 4, 2, 0] 
  1184   div   [2, 2, 2, 2, 2] * 9 = [0, 2, 6] ** [1, 2, 3] ++ [18, 16, 8, 0, 0] 
  1185   div   [2, 2, 2, 2, 2] * 27 = [8, 6, 18] ** [1, 2, 3] ++ [46, 32, 0, 0, 0] 
  1186 *)
  1187 *}
  1188 text {*
  1189   The trace shows how the quotient, i.e. the first factor on the right-hand side,
  1190   is being increased step by step until the remainder has a degree lower than the
  1191   divisor.
  1192 
  1193   This representation, however, is inappropriate for reproduction by hand.
  1194   Dividing polynomials by hand is taught in this format:
  1195 *}
  1196 ML {*
  1197   trace_div := true;
  1198   trace_div_invariant := false;
  1199   val p1 = [2, 2, 2, 2, 2];
  1200   val p2 = [1, 2, 3];
  1201   val (n (* = 27*), 
  1202        q (* = [8, 6, 18]*), 
  1203        r (* = [46, 32]*)) = p1 %*/% p2;
  1204 (*
  1205   div   [2, 2, 2, 2, 2] * 3 
  1206   div   ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  1207   div   [6, 6, 6, 6, 6] 
  1208   div-- [0, 0, 2, 4, 6] <--- [1, 2, 3] * 2 
  1209   div   --------------------------- 
  1210   div   [6, 6, 4, 2, 0]                            quot = [] @ [2] @ (3 * []) 
  1211   div   [6, 6, 4, 2] * 3 
  1212   div   ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  1213   div   [18, 18, 12, 6] 
  1214   div-- [0, 2, 4, 6] <--- [1, 2, 3] * 2 
  1215   div   --------------------------- 
  1216   div   [18, 16, 8, 0]                            quot = [] @ [2] @ (3 * [2]) 
  1217   div   [18, 16, 8] * 3 
  1218   div   ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  1219   div   [54, 48, 24] 
  1220   div-- [8, 16, 24] <--- [1, 2, 3] * 8 
  1221   div   --------------------------- 
  1222   div   [46, 32, 0]                            quot = [] @ [8] @ (3 * [2, 6]) 
  1223   div   ..................................................................... 
  1224   div   27 * [2, 2, 2, 2, 2] = [8, 6, 18] ** [1, 2, 3] ++ [46, 32] 
  1225   div   ..................................................................... 
  1226 *)
  1227 *}
  1228 text {*
  1229   The above algorithm is unusual in that the dividend first is multiplied
  1230   such that the quotient can have integer coefficients ("generalised division"). 
  1231   Thus the quotient needs to be multiplied subsequently as well. 
  1232   The algorithm also has to handle cases, where zeros occur in the quotient: 
  1233 *}                      
  1234 ML {*
  1235   trace_div := true;
  1236   val p1 = [3,2,2,2,1,1,1,1];
  1237   val p2 = [1,1,1,1];
  1238   val (n (* = 1*), 
  1239        q (* = [2, 0, 0, 0, 1]*), 
  1240        r (* = [1]*)) = p1 %*/% p2;
  1241 (*
  1242   div   [3, 2, 2, 2, 1, 1, 1, 1] * 1 
  1243   div   ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  1244   div   [3, 2, 2, 2, 1, 1, 1, 1] 
  1245   div-- [0, 0, 0, 0, 1, 1, 1, 1] <--- [1, 1, 1, 1] * 1 
  1246   div   --------------------------- 
  1247   div   [3, 2, 2, 2, 0, 0, 0, 0]                            quot = [0, 0, 0] @ [1] @ (1 * []) 
  1248   div   [3, 2, 2, 2] * 1 
  1249   div   ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  1250   div   [3, 2, 2, 2] 
  1251   div-- [2, 2, 2, 2] <--- [1, 1, 1, 1] * 2 
  1252   div   --------------------------- 
  1253   div   [1, 0, 0, 0]                            quot = [] @ [2] @ (1 * [0, 0, 0, 1]) 
  1254   div   ..................................................................... 
  1255   div   1 * [3, 2, 2, 2, 1, 1, 1, 1] = [2, 0, 0, 0, 1] ** [1, 1, 1, 1] ++ [1] 
  1256   div   ..................................................................... 
  1257 *)
  1258 *}
  1259 
  1260 subsubsection {*  *}
  1261 text {*
  1262   
  1263 *}
  1264 ML {*
  1265 
  1266 *}
  1267 
  1268 subsection {* Technical requirements raised by pedagogical questions *}
  1269 subsubsection {* Calculations might be different from interpretation *}
  1270 text {*
  1271   Until now the demonstration cases for Lucas-Interpretation were
  1272   simplification, equation solving and ad-hoc algorithms in Signal Processing
  1273   and Structural Engineering. In these cases execution of the functional program 
  1274   coincided with the calculation generated as a side-effect of interpretation:
  1275   The tactics "Take", "Rewrite", "Rewrite_Set" and "Substitute", when executed,
  1276   immediately created respective output and handed over control to the user.
  1277   Appropriate user-input finally created a "calculation" very close to 
  1278   what would be written on paper.
  1279 
  1280   Now, in Computer Algebra, the programs contain lots of sophisticated functions.
  1281   Simple traces, for instance arguments and values of function calls, comprise too 
  1282   many data to be intuitively comprehensible.
  1283 
  1284   The examples in the case study suggest to have mechanisms for creating
  1285   steps for calculations, which are somehow abstracted from pure execution.
  1286   Interestingly these abstractions are close to the fixpoints/invariants
  1287   of the respective recursive functions --- see 
  1288   section "Interactivity on Euclids Algorithm" and section "Interactivity and/or 
  1289   understanding of polynomial division" above.
  1290 
  1291   Such mechanisms might be specific tactics, which take data from the 
  1292   local environment and the local context for presentation to the student.
  1293   Thus one such tactic is related to several lines of code in a function.
  1294   These tactics would not contribute to automated execution as done by "value",
  1295   for instance.
  1296   However, the effect of these tactics for resuming execution upon completion 
  1297   of input needs to be clarified.
  1298 *}
  1299 
  1300 subsubsection {* One function might generate various representations *}
  1301 text {*
  1302   The section "Interactivity and/or understanding of polynomial division" suggests
  1303   to have at least two kinds of representation for "calculations": 
  1304   (1) a student wants to roughly understand what happens in the algorithm
  1305   (2) a student wants to reproduce the algorithm by hand.
  1306 
  1307   A solution to this requirement of variety is to create related functions 
  1308   with different combinations of the specific tactics as mentioned above.
  1309   These functions can be selected by the dialogue module. The computational
  1310   effect of such related functions is the same, the interactive behaviour
  1311   is different (much more different as variations of "Rewrite", for instance.)
  1312 *}
  1313 
  1314 subsubsection {* One "specific tactic" might create several lines of calculation *}
  1315 text {*
  1316   The case of "division of polynomial" suggests to have one "specific tactic"
  1317   as mentioned above, which has one location in the function (e.g. "writeln_trace"
  1318   in "div_int_up"), but creates several lines of a calculation; see the example in
  1319   section "Can division of polynomials be adapted":
  1320 
  1321         [  2,   2,   2,   2,   2] : [1, 2, 3] =
  1322       3*[  2,   2,   2,   2,   2]                   *3
  1323       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
  1324         [  6,   6,   6,   6,   6]                   :
  1325       --[  0,   0,   2,   4,   6] <--........ * [ 2]
  1326       ------------------------                      :
  1327         [  6,   6,   4,   2,   0]             = [ 2]*3
  1328       :
  1329       :
  1330 
  1331   Interaction on these lines should resemble calculation with paper and pencil.
  1332 *}
  1333 
  1334 subsubsection {*  *}
  1335 text {*
  1336   
  1337 *}
  1338 ML {*
  1339 
  1340 *}
  1341 
  1342 end
  1343 
  1344