src/Tools/isac/Knowledge/GCD_Poly_FP.thy
author Walther Neuper <neuper@ist.tugraz.at>
Tue, 12 Feb 2013 09:22:20 +0100
changeset 48825 69d8a0182a8d
parent 48823 913483f0582b
child 48827 44ad2e1cb06b
permissions -rw-r--r--
intermed.
     1 header {* GCD for polynomials, implemented using the function package (_FP) *}
     2 
     3 theory GCD_Poly_FP 
     4 imports "~~/src/HOL/Library/Polynomial" "~~/src/HOL/Number_Theory/Primes"
     5 begin
     6 
     7 text {* 
     8   This code has been translated from GCD_Poly.thy by Diana Meindl,
     9   who follows Franz Winkler, Polynomial algorithyms in computer algebra, Springer 1996.
    10   Winkler's original identifiers are in test/../gcd_poly_winkler.sml;
    11   test/../gcd_poly.sml documents the changes towards GCD_Poly.thy;
    12   the style of GCD_Poly.thy has been adapted to the function package.
    13 *}
    14 
    15 section {* gcd for univariate polynomials *}
    16 
    17 type_synonym unipoly = "int list"
    18 value "[0, 1, 2, 3, 4, 5] :: unipoly"
    19 
    20 subsection {* a variant for div *}
    21 (* 5 div 2 = 2; ~5 div 2 = ~3; but ~5 div2 2 = ~2; *)
    22 definition div2 :: "int => int => int" (infixl "div2" 70) where
    23 "a div2 b = (if a div b < 0 then (\<bar>a\<bar> div \<bar>b\<bar>) * -1 else a div b)"
    24 text {* div' didnt work *}
    25 
    26 value " 5 div2  2 =  2"
    27 value "-5 div2  2 = -2"
    28 value "-5 div2 -2 =  2"
    29 value " 5 div2 -2 = -2"
    30 
    31 value "gcd (15::int) (6::int) = 3"
    32 value "gcd (10::int) (3::int) = 1"
    33 value "gcd (6::int) (24::int) = 6"
    34 
    35 subsection {* modulo calculations for integers *}
    36 (* modi is just local for mod_inv *)
    37 function modi :: "int => int => int => int => int" where
    38   "modi r rold m rinv = 
    39     (if rinv < m 
    40       then 
    41         if r mod m = 1 
    42         then rinv 
    43         else modi (rold * (rinv + 1)) rold m (rinv + 1) 
    44       else 0)"
    45   by auto
    46   termination sorry
    47 (* mod_inv :: int -> nat 
    48    mod_inv r m = s
    49    assumes 
    50    yields SOME s. r * s mod m = 1 *)
    51 fun mod_inv :: "int => int => int" where
    52   "mod_inv r m = modi r r m 1"
    53 
    54 value "modi 5 5 7 1   = 3"
    55 value "modi 3 3 7 1   = 5"
    56 value "modi 4 4 339 1 = 85"
    57 
    58 value "mod_inv 5 7    = 3"
    59 value "mod_inv 3 7    = 5"
    60 value "mod_inv 4 339  = 85"
    61 
    62 (* mod_div :: int -> int -> nat -> nat
    63    mod_div a b m = c
    64    assumes 
    65    yields SOME c. a * b^(-1) mod m = c *)
    66 definition mod_div :: "int => int => int => int" where
    67   "mod_div a b m = a * (mod_inv b m) mod m"
    68 
    69 (* root1 is just local to approx_root *)
    70 function root1 :: "int => int => int" where
    71   "root1 a n = (if n * n < a then root1 a (n + 1) else n)"
    72 by auto
    73 termination sorry
    74 (* required just for one approximation:
    75    approx_root :: nat -> nat
    76    approx_root a = r
    77    assumes 
    78    yields SOME r. (r-1) * (r-1) < a  \<and>  a \<le> r * r*)
    79 definition approx_root :: "int => int" where
    80   "approx_root a = root1 a 1"
    81 
    82 (* yields SOME r. r = r1 mod m1 \<and> r = r2 mod m2 *)
    83 definition chinese_remainder :: "int => int => int => int => int" where
    84   "chinese_remainder r1 m1 r2 m2 = 
    85     (r1 mod m1) + ((r2 - (r1 mod m1)) * (mod_inv m1 m2) mod m2) * m1"
    86 
    87 value "chinese_remainder 17 9 3 4  = 35"
    88 value "chinese_remainder  7 2 6 11 = 17"
    89 
    90 subsection {* operations with primes *}
    91 
    92 (* assumes: 'primes' contains all of first primes \<and> n \<le> (max primes)^2 *)
    93 fun is_prime :: "int list \<Rightarrow> int \<Rightarrow> bool" where
    94   "is_prime primes n = 
    95     (if List.length primes > 0
    96     then 
    97       if (n mod (List.hd primes)) = 0
    98       then False
    99       else is_prime (List.tl primes) n
   100     else True)"
   101 
   102 value "is_prime [2, 3] 2  = False"
   103 value "is_prime [2, 3] 3  = False"
   104 value "is_prime [2, 3] 4  = False"
   105 value "is_prime [2, 3] 5  = True"
   106 value "is_prime [2, 3] 6  = False"
   107 value "is_prime [2, 3] 25 = True" -- "... because 5 not in list"
   108 
   109 (* make_primes is just local to primes_upto only *)
   110 function make_primes :: "int list \<Rightarrow> int \<Rightarrow> int \<Rightarrow> int list" where
   111   "make_primes ps last_p n =
   112     (if (List.nth ps (List.length ps - 1)) < n
   113     then
   114       if is_prime ps (last_p + 2) 
   115       then make_primes (ps @ [(last_p + 2)]) (last_p + 2) n
   116       else make_primes  ps (last_p + 2) n
   117     else ps)"
   118 by pat_completeness (simp del: is_prime.simps)
   119 termination sorry
   120 
   121 (* primes_upto :: int \<Rightarrow> int list
   122    primes_upto n = [2, 3, 5, 7, .., p_k]                 -- sloppy formulations
   123      assumes 0 < n
   124      yields SOME k. ([2, 3, 5, 7, .., p_k] contains all primes \<le> p_k) \<and> p_(k-1) < n \<and> n \<le> p_k *)
   125 fun primes_upto :: "int \<Rightarrow> int list" where
   126   "primes_upto n = (if n < 3 then [2] else make_primes [2, 3] 3 n)"
   127 
   128 value "primes_upto  2 = [2]"
   129 value "primes_upto  3 = [2, 3]"
   130 value "primes_upto  4 = [2, 3, 5]"
   131 value "primes_upto  5 = [2, 3, 5]"
   132 value "primes_upto  6 = [2, 3, 5, 7]"
   133 value "primes_upto  7 = [2, 3, 5, 7]"
   134 value "primes_upto  8 = [2, 3, 5, 7, 11]"
   135 value "primes_upto  9 = [2, 3, 5, 7, 11]"
   136 value "primes_upto 10 = [2, 3, 5, 7, 11]"
   137 value "primes_upto 11 = [2, 3, 5, 7, 11]"
   138 
   139 text {* FIXME next_prime_not_dvd loops with all the following:
   140 
   141 (* find a prime greater p not dividing the number n *)
   142 
   143 function next_prime_not_dvd :: "int \<Rightarrow> int \<Rightarrow> int" (infixl "next'_prime'_not'_dvd" 70) where
   144  "p next_prime_not_dvd n = 
   145   (let
   146     ps = primes_upto (p + 1);
   147     nxt = List.nth ps (List.length ps - 1)
   148   in
   149     if n mod nxt \<noteq> 0
   150     then nxt
   151     else 77)"
   152 by pat_completeness (simp del: primes_upto.simps is_prime.simps) 
   153 termination sorry
   154 
   155 function next_prime_not_dvd :: "int \<Rightarrow> int \<Rightarrow> int" (infixl "next'_prime'_not'_dvd" 70) where
   156   "p next_prime_not_dvd n = 
   157     (let
   158       ps = primes_upto (p + 1);
   159       nxt = List.nth ps (List.length ps - 1)
   160     in
   161       if n mod nxt \<noteq> 0
   162       then nxt
   163       else nxt next_prime_not_dvd n)"
   164 by pat_completeness (simp del: is_prime.simps make_primes.simps) 
   165 termination sorry
   166 
   167 ----
   168 loops                  : by pat_completeness auto
   169 loops                  : by pat_completeness simp
   170 Failed to finish proof : by pat_completeness (simp del: is_prime.simps make_primes.simps)
   171 Failed to finish proof : by pat_completeness (simp del: make_primes.simps)
   172 *}
   173 declare [[simp_trace_depth_limit=99]]
   174 declare [[simp_trace=true]]
   175 ML {*11111*}
   176 thm make_primes.simps
   177 
   178 function next_prime_not_dvd :: "int \<Rightarrow> int \<Rightarrow> int" (infixl "next'_prime'_not'_dvd" 70) where
   179   "p next_prime_not_dvd n = 
   180     (let
   181       ps = primes_upto (p + 1);
   182       nxt = List.nth ps (List.length ps - 1)
   183     in
   184       if n mod nxt \<noteq> 0
   185       then nxt
   186       else nxt next_prime_not_dvd n)"
   187 by pat_completeness (simp del: make_primes.simps)
   188 
   189 
   190 (*---------------------------------------------------------------------------------------------
   191 subsection {* auxiliary functions for univariate polynomials *}
   192 
   193 (* not in List.thy, copy from library.ML *)
   194 fun nth_drop :: "nat \<Rightarrow> 'a list \<Rightarrow> 'a list" where
   195   "nth_drop n xs = List.take n xs @ List.drop (n + 1) xs"
   196 value "nth_drop 0 []              = []"
   197 value "nth_drop 0 [1, 2, 3::int]  = [2, 3]"
   198 value "nth_drop 2 [1, 2, 3::int]  = [1, 2]"
   199 value "nth_drop 77 [1, 2, 3::int] = [1, 2, 3]"
   200 
   201 (* leading coefficient *)
   202 function lc :: "unipoly \<Rightarrow> int" where
   203   "lc uvp =
   204     (if List.nth uvp (List.length uvp - 1) \<noteq> 0
   205     then List.nth uvp (List.length uvp - 1)
   206     else lc (nth_drop (List.length uvp - 1) uvp))"
   207 by auto 
   208 termination sorry
   209 
   210 text {*"----------- fun lc -------------------------------------"*}
   211 value "lc [3, 4, 5, 6]    = 6"
   212 value "lc [3, 4, 5, 6, 0] = 6"
   213 
   214 (* degree *)
   215 function deg :: "unipoly \<Rightarrow> nat" where
   216   "deg uvp =
   217     (if nth uvp (List.length uvp - 1) \<noteq> 0
   218     then List.length uvp - 1
   219     else deg (nth_drop (List.length uvp - 1) uvp))"
   220 by auto 
   221 termination sorry
   222 (* TODO: ?let l = length uvp - 1? more efficient??? compare drop_lc0 !!! *)
   223 
   224 text {*"----------- fun deg ------------------------------------"*}
   225 value "deg [3, 4, 5, 6]    = 3"
   226 value "deg [3, 4, 5, 6, 0] = 3"
   227 
   228 (* drop leading coefficients equal 0 *)
   229 fun drop_lc0 :: "unipoly \<Rightarrow> unipoly" where 
   230   "drop_lc0 [] = []"
   231 | "drop_lc0 uvp = 
   232     (let l = List.length uvp - 1
   233     in
   234       if nth uvp l = 0
   235       then drop_lc0 (nth_drop l uvp)
   236       else uvp)"
   237 
   238 text {*"----------- fun drop_lc0 -------------------------------";*}
   239 value "drop_lc0 [0, 1, 2, 3, 4, 5, 0, 0] = [0, 1, 2, 3, 4, 5]"
   240 value "drop_lc0 [0, 1, 2, 3, 4, 5]       = [0, 1, 2, 3, 4, 5]"
   241 
   242 (* norm is just local to normalise *)
   243 fun norm :: "unipoly \<Rightarrow> unipoly \<Rightarrow> int (*nat?*) \<Rightarrow> int \<Rightarrow> nat \<Rightarrow> unipoly" where
   244   "norm pol nrm m lcp i = 
   245     (if i = 0
   246         then [mod_div (nth pol i) lcp m] @ nrm
   247         else norm pol ([mod_div (nth pol i) lcp m] @  nrm) m lcp (i - 1))"
   248 (* normalise a unipoly such that the lc mod m = 1.
   249    normalise :: unipoly \<Rightarrow> nat \<Rightarrow> unipoly
   250    normalise [p_0, .., p_k] m = [q_0, .., q_k]
   251      assumes 
   252      yields \<exists> t. 0 \<le> i \<le> k \<Rightarrow> (p_i * t) mod m = q_i  \<and>  (p_k * t) mod m = 1 *)
   253 fun normalise :: "unipoly \<Rightarrow> int (*nat?*) \<Rightarrow> unipoly" where
   254   "normalise p m = 
   255     (let
   256      p = drop_lc0 p;
   257      lcp = lc p
   258     in 
   259       if List.length p = 0 then p else norm p [] m lcp (List.length p - 1))"
   260 
   261 text {*"----------- fun normalise ------------------------------";*}
   262 value "normalise [-18, -15, -20, 12, 20, -13, 2] 5 = [1, 0, 0, 1, 0, 1, 1]"
   263 value "normalise [9, 6, 3] 10                      = [3, 2, 1]"
   264 
   265 (* scalar multiplication, %* in GCD_Poly.thy *)
   266 fun  mult_ups :: "unipoly \<Rightarrow> int \<Rightarrow> unipoly" (infixl "mult'_ups" 70) where
   267  "p mult_ups m = map (op * m) p"
   268 
   269 value "[5, 4, 7, 8, 1] mult_ups 5   = [25, 20, 35, 40, 5]"
   270 value "[5, 4, -7, 8, -1] mult_ups 5 = [25, 20, -35, 40, -5]"
   271 
   272 (* scalar divison, %/ in GCD_Poly.thy *)
   273 definition swapf :: "('a \<Rightarrow> 'b \<Rightarrow> 'c) \<Rightarrow> 'b \<Rightarrow> 'a \<Rightarrow> 'c" where
   274   "swapf f a b = f b a"
   275 definition div_ups :: "unipoly \<Rightarrow> int \<Rightarrow> unipoly" (infixl "div'_ups" 70) where
   276   "p div_ups m = map (swapf op div2 m) p"
   277 
   278 value "[4, 3, 2, 5, 6] div_ups 3 = [1, 1, 0, 1, 2]"
   279 value "[4, 3, 2, 0] div_ups 3    = [1, 1, 0, 0]"
   280 
   281 (* not in List.thy, copy from library.ML *)
   282 fun map2 :: "('a \<Rightarrow> 'b \<Rightarrow> 'c) \<Rightarrow> 'a list \<Rightarrow> 'b list \<Rightarrow> 'c list" where
   283   "map2 _ [] [] = []"
   284 | "map2 f (x # xs) (y # ys) = f x y # map2 f xs ys"
   285 | "map2 _ _ _ = []" (*raise ListPair.UnequalLengths*)
   286 
   287 (* minus of polys,  %-% in GCD_Poly.thy *)
   288 definition minus_up :: "unipoly \<Rightarrow> unipoly \<Rightarrow> unipoly" (infixl "minus'_up" 70) where
   289   "p1 minus_up p2 = map2 (op -) p1 p2"
   290 
   291 value "[8, -7, 0, 1] minus_up [-2, 2, 3, 0]     = [10, -9, -3, 1]"
   292 value "[8, 7, 6, 5, 4] minus_up [2, 2, 3, 1, 1] = [6, 5, 3, 4, 3]"
   293 
   294 (* dvd for univariate polynomials *)
   295 function dvd_up :: "unipoly \<Rightarrow> unipoly \<Rightarrow> bool" (infixl "dvd'_up" 70) where
   296 (* FIXME this makes induction proof loop .......
   297   "[d] dvd_up [i] =
   298     (if (\<bar>d\<bar> \<le> \<bar>i\<bar>) & (i mod d = 0) then True else False)"
   299 |*) "d dvd_up p =
   300     (let 
   301       d = drop_lc0 d;
   302       p = drop_lc0 p;
   303       d000 = (List.replicate (List.length p - List.length d) 0) @ d;
   304       quot = (lc p) div2 (lc d000);
   305       rest = drop_lc0 (p minus_up (d000 div_ups quot))
   306     in 
   307       if rest = [] then True
   308       else 
   309         (if quot \<noteq> 0 & List.length d \<le> List.length rest
   310         then d dvd_up rest
   311         else False))"
   312 by pat_completeness auto 
   313 termination sorry
   314 
   315 thm dvd_up.simps
   316 thm dvd_up.induct
   317 (* 
   318 value "[2, 3] dvd_up [8, 22, 15] = True"
   319 ... FIXME loops, incorrect: ALL SHOULD EVALUATE TO TRUE .......
   320 *)
   321 value "[4] dvd_up [6]                 = False"
   322 value "[8] dvd_up [16, 0]             = True"
   323 value "[3, 2] dvd_up [0, 0, 9, 12, 4] = True"
   324 value "[8, 0] dvd_up [16]             = True"
   325 
   326 (* centr is just local to poly_centr *)
   327 definition centr :: "int \<Rightarrow> int \<Rightarrow> int \<Rightarrow> int" where
   328   "centr m mid p_i = (if mid < p_i then p_i - m else p_i)"
   329 (* TODO *)
   330 definition poly_centr :: "unipoly \<Rightarrow> int \<Rightarrow> unipoly" where
   331   "poly_centr p m =
   332    (let
   333       mi = m div2 2;
   334       mid = if m mod mi = 0 then mi else mi + 1
   335    in map (centr m mid) p)"
   336 
   337 value "poly_centr [7, 3, 5, 8, 1, 3] 10 = [-3, 3, 5, -2, 1, 3]"
   338 value "poly_centr [1, 2, 3, 4, 5] 2     = [1, 0, 1, 2, 3]"
   339 value "poly_centr [1, 2, 3, 4, 5] 3     = [1, -1, 0, 1, 2]"
   340 value "poly_centr [1, 2, 3, 4, 5] 4     = [1, 2, -1, 0, 1]"
   341 value "poly_centr [1, 2, 3, 4, 5] 5     = [1, 2, 3, -1, 0]"
   342 value "poly_centr [1, 2, 3, 4, 5] 6     = [1, 2, 3, -2, -1]"
   343 value "poly_centr [1, 2, 3, 4, 5] 7     = [1, 2, 3, 4, -2]"
   344 value "poly_centr [1, 2, 3, 4, 5] 8     = [1, 2, 3, 4, -3]"
   345 value "poly_centr [1, 2, 3, 4, 5] 9     = [1, 2, 3, 4, 5]"
   346 value "poly_centr [1, 2, 3, 4, 5] 10    = [1, 2, 3, 4, 5]"
   347 value "poly_centr [1, 2, 3, 4, 5] 11    = [1, 2, 3, 4, 5]"
   348 
   349 (* TODO *)
   350 definition sum_lmb :: "unipoly \<Rightarrow> nat \<Rightarrow> int" where
   351   "sum_lmb p exp = fold ((op +) o (swapf power exp)) p 0"
   352 
   353 value "sum_lmb [-1, 2, -3, 4, -5] 1 = -3"
   354 value "sum_lmb [-1, 2, -3, 4, -5] 2 = 55"
   355 value "sum_lmb [-1, 2, -3, 4, -5] 3 = -81"
   356 value "sum_lmb [-1, 2, -3, 4, -5] 4 = 979"
   357 value "sum_lmb [-1, 2, -3, 4, -5] 5 = -2313"
   358 value "sum_lmb [-1, 2, -3, 4, -5] 6 = 20515"
   359 
   360 (* TODO *)
   361 definition landau_mignotte_bound :: "unipoly \<Rightarrow> unipoly \<Rightarrow> nat" where
   362   "landau_mignotte_bound p1 p2 = 
   363     ((power 2 (min (deg p1) (deg p2))) * (nat (gcd (lc p1) (lc p2))) * 
   364     (nat (min (abs ((approx_root (sum_lmb p1 2)) div2 -(lc p1)))
   365                 (abs ((approx_root (sum_lmb p2 2)) div2 -(lc p2))))))"
   366 
   367 value "landau_mignotte_bound [1] [4, 5]          = 1"
   368 value "landau_mignotte_bound [1, 2] [4, 5]       = 2"
   369 value "landau_mignotte_bound [1, 2, 3] [4, 5]    = 2"
   370 value "landau_mignotte_bound [1, 2, 3] [4]       = 1"
   371 value "landau_mignotte_bound [1, 2, 3] [4, 5]    = 2"
   372 value "landau_mignotte_bound [1, 2, 3] [4, 5, 6] = 12"
   373 
   374 value "landau_mignotte_bound [-1] [4, 5]            = 1"
   375 value "landau_mignotte_bound [-1, 2] [4, 5]         = 2"
   376 value "landau_mignotte_bound [-1, 2, -3] [4, -5]    = 2"
   377 value "landau_mignotte_bound [-1, 2, -3] [4]        = 1"
   378 value "landau_mignotte_bound [-1, 2, -3] [4, -5]    = 2"
   379 value "landau_mignotte_bound [-1, 2, -3] [4, -5, 6] = 12"
   380 
   381 subsection {* modulo calculations for polynomials *}
   382 
   383 (* pair is just local to chinese_remainder_poly *)
   384 fun pair :: "unipoly \<Rightarrow> unipoly \<Rightarrow> ((int * int) list)" (infix "pair" 4) where
   385   "([] pair []) = []"
   386 | "([] pair ys) = []" (*raise ListPair.UnequalLengths*)
   387 | "(xs pair []) = []" (*raise ListPair.UnequalLengths*)
   388 | "((x#xs) pair (y#ys)) = (x, y) # (xs pair ys)"
   389 
   390 (* chinese_rem is just local to chinese_remainder_poly *)
   391 fun chinese_rem :: "int * int \<Rightarrow> int * int \<Rightarrow> int" where
   392   "chinese_rem (m1, m2) (p1, p2) = (chinese_remainder p1 m1 p2 m2)"
   393 
   394 (* TODO *)
   395 fun chinese_remainder_poly :: "int * int \<Rightarrow> unipoly * unipoly \<Rightarrow> unipoly" where
   396   "chinese_remainder_poly (m1, m2) (p1, p2) = map (chinese_rem (m1, m2)) (p1 pair p2)"
   397 
   398 value "chinese_remainder_poly (5, 7) ([2, 2, 4, 3], [3, 2, 3, 5]) = [17, 2, 24, 33]"
   399 
   400 (* mod_pol is just local to mod_poly *)
   401 function mod_pol :: "unipoly \<Rightarrow> int \<Rightarrow> nat \<Rightarrow> unipoly" where
   402   "mod_pol p m n = (if n < List.length p
   403       then mod_pol ((List.tl p) @ [(List.hd p) mod m]) m (n + 1)
   404       else p)"
   405 by pat_completeness auto 
   406 termination sorry
   407 (* TODO *)
   408 definition mod_poly :: "unipoly \<Rightarrow> int \<Rightarrow> unipoly" (infixl "mod'_poly" 70) where
   409   "p mod_poly m = mod_pol p m 0"
   410 
   411 value "[5, 4, 7, 8, 1] mod_poly 5 = [0, 4, 2, 3, 1]"
   412 value "[5, 4, -7, 8, -1] mod_poly 5 = [0, 4, 3, 3, 4]"
   413 
   414 (* euclidean algoritm in Z_p[x].
   415    mod_poly_gcd :: unipoly -> unipoly -> int -> unipoly
   416    mod_poly_gcd p1 p2 m = g
   417      assumes 
   418      yields \<exists> g. gcd((p1 mod m),(p2 mod m)) = g *)
   419 function mod_poly_gcd :: "unipoly \<Rightarrow> unipoly  \<Rightarrow> int \<Rightarrow> unipoly" where
   420  "mod_poly_gcd p1 p2 m = 
   421    (let 
   422       p1m = p1 mod_poly m;
   423       p2m = drop_lc0 (p2 mod_poly m);
   424       p2n = (replicate (List.length p1 - List.length p2m) 0) @ p2m;
   425       quot =  mod_div (lc p1m) (lc p2n) m;
   426       rest = drop_lc0 ((p1m minus_up (p2n mult_ups quot)) mod_poly m)
   427     in 
   428    if rest = [] 
   429         then p2
   430         else
   431           if length rest < List.length p2
   432           then mod_poly_gcd p2 rest m 
   433           else mod_poly_gcd rest p2 m)"
   434 by pat_completeness auto 
   435 termination sorry
   436 
   437 value "mod_poly_gcd [-18, -15, -20, 12, 20, -13, 2] [8, 28, 22, -11, -14, 1, 2] 7 = [2, 6, 0, 2, 6]" 
   438 value "mod_poly_gcd [8, 28, 22, -11, -14, 1, 2] [2, 6, 0, 2, 6] 7 = [2, 6, 0, 2, 6]" 
   439 value "mod_poly_gcd [20, 15, 8, 6] [8, -2, -2, 3] 2 = [0, 1]"
   440 
   441 
   442 
   443 
   444 (*----------------------------------------------------------------------------*)
   445 value "xxx" -- "= "
   446 
   447 print_configs
   448 declare [[simp_trace_depth_limit=99]]
   449 declare [[simp_trace=true]]
   450 ---------------------------------------------------------------------------------------------*)
   451 
   452 end