risch.cc 34.5 KB
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 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
// -*- mode:C++ ; compile-command: "g++-3.4 -I.. -g -c risch.cc -DHAVE_CONFIG_H -DIN_GIAC" -*-
#include "giacPCH.h"
/*
 *  Copyright (C) 2003,14 B. Parisse, Institut Fourier, 38402 St Martin d'Heres
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
using namespace std;
#include <stdexcept>
#include "vector.h"
#include <cmath>
#include <cstdlib>
#include "sym2poly.h"
#include "usual.h"
#include "intg.h"
#include "subst.h"
#include "derive.h"
#include "lin.h"
#include "vecteur.h"
#include "gausspol.h"
#include "plot.h"
#include "prog.h"
#include "modpoly.h"
#include "series.h"
#include "tex.h"
#include "ifactor.h"
#include "risch.h"
#include "misc.h"
#include "giacintl.h"

#ifndef NO_NAMESPACE_GIAC
namespace giac {
#endif // ndef NO_NAMESPACE_GIAC

  static bool in_risch(const gen & e,const identificateur & x,const vecteur & v,const gen & allowed_lnarg,gen & prim,gen & lncoeff,gen & remains_to_integrate,GIAC_CONTEXT);
  static bool risch_poly_part(const vecteur & e,int shift,const identificateur & x,const vecteur & v,const gen & allowed_lnarg,gen & prim,gen & lncoeff,gen & remains_to_integrate,GIAC_CONTEXT);
  static bool risch_desolve(const gen & f,const gen & g,const identificateur & x,const vecteur & v,gen & y,bool f_is_derivative,GIAC_CONTEXT);

  // returns true & the tower of extension if g is elementary 
  // false otherwise
  static bool risch_tower(const identificateur & x,gen &g, vecteur & v,GIAC_CONTEXT){
    g=tsimplify(pow2expln(g,x,contextptr),contextptr);
    v=rlvarx(g,x);
    const_iterateur it=v.begin(),itend=v.end();
    for (;it!=itend;++it){
      if (*it==x)
	continue;
      if (!it->is_symb_of_sommet(at_exp) && (!it->is_symb_of_sommet(at_ln)) )
	return false;
    }
    reverse(v.begin(),v.end()); // most complex var at the beginning
    return true;
  }

  // Compute the derivative of a poly or fraction
  // The derivative wrt to x_i (i-th index) is the i-th element of v
  // The last element of v should normally be 1 (derivative of x)
  static fraction diff(const polynome & f,const vecteur & v){
    int s=int(v.size());
    if (f.dim<s)
      return fraction(gensizeerr(gettext("Risch diff dimension")));
    fraction res(zero);
    std::vector< monomial<gen> >::const_iterator it=f.coord.begin(),itend=f.coord.end();
    for (;it!=itend;++it){
      index_t i= it->index.iref();
      fraction tmp(zero);
      for (int n=0;n<s;++n){
	if (i[n]){
	  --i[n];
	  if (v[n].type==_FRAC)
	    tmp=tmp+gen(polynome(monomial<gen>(i[n]+1,i)))*(*v[n]._FRACptr);
	  else
	    tmp=tmp+fraction(gen(polynome(monomial<gen>(i[n]+1,i)))*v[n]);
	  ++i[n];
	}
      }
      res=res+it->value*tmp;
    }
    return res;
  }

  
  static polynome rothstein_trager_resultant(const polynome & num,const polynome & den,const vecteur & vl,polynome & p1,GIAC_CONTEXT){
    int s=num.dim;
    fraction denprime=diff(den,vl);
    if (is_undef(denprime.num)){
      return gen2poly(denprime.num,s);
    }
    p1=num*gen2poly(denprime.den,s);
    p1=p1.untrunc1();
    polynome p2(monomial<gen>(plus_one,1,s+1));
    p2=p2*gen2poly(denprime.num,s).untrunc1();
    p1=p1-p2;
    p2=den.untrunc1();
    // exchange var 1 (parameter t) and 2 (top tower variable)
    vector<int> i=transposition(0,1,s+1);
    p1.reorder(i);
    // Change sign of p1 if first coeff is negative
    if (is_positive(-p1.coord.front().value,context0)) // ok
      p1=-p1;
    p2.reorder(i);
    polynome pres=Tresultant<gen>(p1,p2),pcontent(s);
    p1.reorder(i);
    return pres.trunc1(); // pres top var is the parameter t
  }

  /*
  fraction diff(const fraction & f,const vecteur & v){
    if (f.num.type!=_POLY){
      if (f.den.type!=_POLY)
	return zero;
      return -diff(*f.den._POLYptr,v)*fraction(f.num,f.den*f.den);
    }
    polynome & num = *f.num._POLYptr;
    if (f.den.type!=_POLY)
      return diff(*f.num._POLYptr,v)/f.num;
    polynome & den = *f.den._POLYptr;
    return diff(num,v)*f.den-diff(den,v)*fraction(f.num,f.den*f.den);
  }
  */

  // diff(n/d*exp(a*x))
  static bool diff(const polynome & n,const polynome & d,const gen & a,const vecteur & v,polynome & resn,polynome & resd){
    int s=n.dim;
    fraction nprime(diff(n,v)),dprime(diff(d,v));
    if (is_undef(nprime.num) || is_undef(dprime.num))
      return false;
    polynome nn(gen2polynome(nprime.num,s)),nd(gen2polynome(nprime.den,s));
    polynome dn(gen2polynome(dprime.num,s)),dd(gen2polynome(dprime.den,s));
    // ((n/d)*exp(a*x))'=(n'*d-d'*n)/d^2+a*n/d = (nn/nd*d-dn/dd*n)/d^2+a*n/d
    resn = nn*d*dd-dn*n*nd;
    resd = d*d*dd*nd;
    if (!is_zero(a)){
      // resn/resd + a*n/d = (resn*aden*d+resd*anum*n)/(d*resd)
      gen num,den;
      fxnd(a,num,den);
      polynome anum=gen2poly(num,resd.dim),aden=gen2poly(den,resd.dim);
      resn = resn*aden*d + resd*anum*n;
      resd = resd*d*aden;
    }
    simplify(resn,resd);
    return true;
  }


  static bool rischde_simplify(polynome & R,polynome & S, polynome & T){
    polynome lcmdeno=gcd(gcd(R,S),T);
    R=R/lcmdeno;
    S=S/lcmdeno;
    T=T/lcmdeno;
    // if gcd(R,S) does not divide T then there is no solution
    lcmdeno=gcd(R,S);
    return lcmdeno.lexsorted_degree()==0;
  }

  static bool spde_x(const polynome & S0,const polynome & R0,const polynome & T0,const vecteur & vdiff,polynome & N1,polynome & N2){ // Solve S*N+R*N'=T, R constant poly
    gen con=gcd(Tcontent(S0),gcd(Tcontent(R0),Tcontent(T0)));
    polynome S(S0/con),R(R0/con),T(T0/con);
    int s=T.dim;
    if (T.coord.empty()){
      N1=T;
      N2=gen2poly(plus_one,s);
      return true;
    }
    if (T.lexsorted_degree()<S.lexsorted_degree())
      return false;
    polynome quo(s),rem(s),a(s);
    T.TPseudoDivRem(S,quo,rem,a); 
    // a*T=quo*S+rem -> quo/a is the quotient T/S, 
    // it has the same high degree polynomial part as N
    // We set N=quo/a+ M, where M satisfies
    // S*M+R*M'=T-S*(quo/a)-R*(quo/a)'
    // or a*dNden*(S*M+R*M')= a*dNden*(T-S*(quo/a)-R*(quo/a)')
    // Let R*(quo/a)'=dNnum/dNden
    // newS=a*dNden*S, newR=a*dNden*R, newT=a*dNden*T-S*quo*dNden-a*dNnum
    fraction quo_a=fraction(quo,a).normal();
    // fraction dN(diff(quo,vdiff)/a-(diff(a,vdiff)*quo)/(a*a));
    fraction tmp1(diff(quo,vdiff));
    tmp1.den=a*tmp1.den;
    fraction tmp2(diff(a,vdiff));
    if (is_undef(tmp1.num) || is_undef(tmp2.num))
      return false;
    tmp2.num=tmp2.num*quo;
    tmp2.den=tmp2.den*a*a;
    fraction dN((tmp1-tmp2).normal());
    polynome dNnum(R*gen2poly(dN.num,s)),dNden(gen2poly(dN.den,s));
    polynome newT(T*dNden*a-quo*S*dNden-a*dNnum);
    if (!spde_x(a*dNden*S,a*dNden*R,newT,vdiff,N1,N2))
      return false;
    // M=N1/N2 hence N=quo/a+M=quo/a+N1/N2
    fraction Nres=quo_a+fraction(N1,N2);
    N1=gen2poly(Nres.num,s);
    N2=gen2poly(Nres.den,s);
    return true;
  }

  static bool SPDE(const polynome &R0,const polynome & S0,const polynome & T0,const identificateur & x,const vecteur & v,const vecteur & vdiff,const vecteur & lv,int ydeg, gen & prim,GIAC_CONTEXT){
    // SPDE algorithm to reduce R to a constant polynomial wrt to Z
    // this will also reduce ydeg, initial equation is  Ry'+Sy=T
    // Principe: if degree(R)>0, find U and V s.t. RU+SV=T and deg(V)<deg(R)
    // Then y = V + R*z and y'=U - S*z
    // hence V'+Rz'+R'z=U-S*z -> R z' + (R' - S) z = U - V'
    if (T0.coord.empty()){
      prim=zero;
      return true;
    }
    polynome R(R0),S(S0),T(T0);
    int s=int(lv.size());
    if (ydeg<0 || !rischde_simplify(R,S,T))
      return false;
    int r=R.lexsorted_degree();
    if (R.lexsorted_degree()){
      polynome U(s),V(s),c(s);
      // U,V / R*U+S*V=c*T, c cst wrt main var, degV<degR
      Tabcuv<gen>(S,R,T,V,U,c); 
      fraction dR(diff(R,vdiff)),dV(diff(V,vdiff));
      if (is_undef(dR.num)||is_undef(dV.num))
	return false;
      dV.den=dV.den*c;
      fraction tmpfrac=fraction(V,c*c)*diff(c,vdiff);
      if (is_undef(tmpfrac.num))
	return false;
      dV=dV-tmpfrac; // now dV=(V/c)'
      polynome dRnum(gen2poly(dR.num,s)),dRden(gen2poly(dR.den,s)),dVnum(gen2poly(dV.num,s)),dVden(gen2poly(dV.den,s));
      polynome newR(R*dRden*dVden),newS((S*dRden+dRnum)*dVden),newT((((U*dVden)/c)-dVnum)*dRden);
      ydeg=ydeg-r;
      if(!SPDE(newR,newS,newT,x,v,vdiff,lv,ydeg,prim,contextptr))
	return false;
      prim=r2sym(R,lv,contextptr)*prim+r2sym(V,lv,contextptr)/r2sym(c,lv,contextptr);
      return true;
    }
    // degree(R)=0, Final resolution  
    gen Z=v.front();
    if (Z==x || S.lexsorted_degree()){ // S*N+N'=T
      polynome N1,N2;
      if (!spde_x(S,R,T,vdiff,N1,N2))
	return false;
      prim=r2sym(N1,lv,contextptr)/r2sym(N2,lv,contextptr);
      return true;
    }
    vecteur v1(v.begin()+1,v.end());
    vecteur lv1(lv.begin()+1,lv.end());
    vecteur t=polynome2poly1(T,1);
    t=*r2sym(t,lv1,contextptr)._VECTptr;
    gen rr=r2sym(R,lv,contextptr);
    t=divvecteur(t,rr);
    if (S.coord.empty()){ // solve y'=T -> polynomial part with 1 less var 
      gen lncoeff,remains;
      return risch_poly_part(t,0,x,v1,plus_one,prim,lncoeff,remains,contextptr);
    }
    // for each power solve a risch de with 1 less var
    if (ydeg<signed(t.size())-1 || Z.type!=_SYMB)
      return false;
    // t=mergevecteur(vecteur(ydeg-t.size()-1),t);
    gen z=Z._SYMBptr->feuille;
    gen dz=derive(z,x,contextptr);
    if (is_undef(dz))
      return false;
    gen b=r2sym(S,lv,contextptr)/rr;
    int tdeg=int(t.size())-1;
    gen previous,sol;
    bool ok;
    for (int k=tdeg;k>=0;--k){
      if (Z.is_symb_of_sommet(at_exp))
	ok=risch_desolve(k*dz+b,t[tdeg-k],x,v1,sol,false,contextptr);
      else
	ok=risch_desolve(b,t[tdeg-k]-(k+1)*previous*dz/z,x,v1,sol,false,contextptr);
      if (!ok)
	return false;
      prim=prim+sol*pow(Z,k);
      previous=sol;
    }
    return true;
  }

  // solve y'+f*y=g in rational fraction of v
  // returns true and y or false
  static bool risch_desolve(const gen & f,const gen & g,const identificateur & x,const vecteur & v,gen & y,bool f_is_derivative,GIAC_CONTEXT){
    gen Z=v.front();
    int s=int(v.size());
    vecteur lv(lvar(v));
    vecteur v1(v.begin()+1,v.end());
    vecteur vdiff(s);
    for (int i=0;i<s;++i){
      vdiff[i]=derive(v[i],x,contextptr);
      if (is_undef(vdiff[i]))
	return false;
    }
    lvar(vdiff,lv);
    lvar(f,lv);
    lvar(g,lv);
    vecteur lv1(lv.begin()+1,lv.end());
    int ss=int(lv.size());
    for (int i=0;i<s;++i)
      vdiff[i]=sym2r(vdiff[i],lv,contextptr);
    // identificateur Zi(" Z");
    // gen Ze(Zi);
    // gen fZ=quotesubst(f,Z,Ze);
    fraction ff(sym2r(f,lv,contextptr));
    polynome fden(gen2poly(ff.den,ss)),fnum(gen2poly(ff.num,ss)),D(gen2poly(plus_one,ss));
    polynome fdenred(fden);
    // compute denominator of y
    fraction gg(sym2r(g,lv,contextptr));
    polynome gden(gen2poly(gg.den,ss)),gnum(gen2poly(gg.num,ss));
    polynome gdenred(gden);
    if (Z.is_symb_of_sommet(at_exp)){
      // if Z is an exp, eliminate it in fden/gden and compute Z in D
      int fdenv=fden.valuation(0),fnumv=fnum.valuation(0),gdenv=gden.valuation(0),gnumv=gnum.valuation(0);
      index_t ii(ss);
      ii[0]=-fdenv;
      fdenred=fdenred.shift(ii);
      ii[0]=-gdenv;
      gdenred=gdenred.shift(ii);
      int alpha=0,beta=fnumv-fdenv,gamma=gnumv-gdenv;
      if (gamma<0){
	alpha=gamma;
	if (beta<=0){
	  if (beta!=0)
	    alpha=giacmin(0,gamma-beta);
	  else { // possible cancellation case depend of cst coeff of f
	    vecteur vtmp(polynome2poly1(fnum,1));
	    gen f0=r2sym(vtmp[0],lv1,contextptr);
	    vtmp=polynome2poly1(fdenred,1);
	    f0=f0/r2sym(vtmp[0],lv1,contextptr);
	    gen lnc,prim,remains;
	    if (in_risch(f0,x,v1,Z._SYMBptr->feuille,prim,lnc,remains,contextptr)&&lnc.type==_INT_)
	      alpha=giacmin(lnc.val,alpha);
	  }
	}
      }
      if (gamma>=0 && beta==0){
	// possible cancellation case depend of cst coeff of f
	vecteur vtmp(polynome2poly1(fnum,1));
	gen f0=r2sym(vtmp[0],lv1,contextptr);
	if (f0.type==_INT_ && f0.val>0)
	  alpha=-f0.val;
      }
      D=polynome(monomial<gen>(plus_one,-alpha,1,ss)); // Z^(-alpha)
    }
    if (!f_is_derivative){ 
      // Fixme: eliminate residues in fden -> new fdenred
      // Find degree 1 factors of fdenred
      polynome tmpy(fdenred.derivative()),tmpw(fdenred);
      polynome tmpc(simplify(tmpy,tmpw));
      tmpy=tmpy-tmpw.derivative();
      polynome f1=simplify(tmpw,tmpy);
      if (f1.lexsorted_degree()){ /// FIXME
	polynome f1cofact(fdenred/f1);
	polynome N1(ss),N2(ss),c(ss);
	Tabcuv<gen>(f1,f1cofact,fnum,N1,N2,c); // fnum/fdenred=N2/f1+...
	// find resultant_Z(N-t f1' , f1)
	polynome p1(ss);
	polynome pres=rothstein_trager_resultant(N2,f1,vdiff,p1,contextptr);
	// for each negative integer root of pres, multiply D
	// find linear factors of pres -> FIXME does not work
	factorization vden;
	gen extra_div=1;
	factor(pres,N1,vden,false,false,false,1,extra_div);
	factorization::const_iterator f_it=vden.begin(),f_itend=vden.end();
	// bool ok=true;
	for (;f_it!=f_itend;++f_it){
	  int deg=f_it->fact.lexsorted_degree();
	  if (deg!=1)
	    continue;
	  // extract the root
	  vecteur vtmp=polynome2poly1(f_it->fact,1);
	  gen root=-r2sym(vtmp.back()/vtmp.front(),lv1,contextptr);
	  if (root.type==_INT_ && root.val<0){
	    identificateur t(" t");
	    gen tmp1=r2sym(p1,mergevecteur(vecteur(1,t),lv),contextptr);
	    tmp1=subst(tmp1,t,root,false,contextptr);
	    polynome p1subst(gen2poly(sym2r(tmp1,lv,contextptr),ss));
	    p1subst=gcd(p1subst,f1);
	    D=D*pow(p1subst,-root.val);
	  }
	}
      }
    }
    polynome c(gcd(fdenred,gdenred));
    D=D*gcd(gdenred,gdenred.derivative())/gcd(c,c.derivative());
    // y'+f*y=g -> new equation is Ry'+Sy=T, compute R=D,S=fD-D',T=gD^2
    fraction dD(diff(D,vdiff));
    if (is_undef(dD.num))
      return false;
    polynome dDnum(gen2poly(dD.num,ss)),dDden(gen2poly(dD.den,ss));
    // then multiply by the lcm of denominators of S and T
    // simplify by gcd of R, S, T
    polynome lcmdeno(gden*fden/gcd(gden,fden)*dDden);
    polynome R(D*lcmdeno),S(fnum*(lcmdeno/fden)*D-dDnum*(lcmdeno/dDden)),T(D*D*gnum*(lcmdeno/gden));
    if (!rischde_simplify(R,S,T))
      return false;
    int rd=R.lexsorted_degree();
    int sd=S.lexsorted_degree();
    int td=T.lexsorted_degree();
    polynome Rr(Tfirstcoeff<gen>(R)),Ss(Tfirstcoeff<gen>(S));
    // compute max possible degree of y: it depends on Z type
    int ydeg=td-sd;
    gen expshift=plus_one; // multiplicative change of variable
    if (Z==x){
      ydeg=td-giacmax(rd-1,sd);
      if (rd-1==sd){ // test whether S_s/R_r is an integer
	gen n=Ss.coord.front().value/Rr.coord.front().value;
	if (n.type==_INT_ && n.val>ydeg && (Ss-n*Rr).coord.empty())
	  ydeg=giacmax(ydeg,n.val);
      }
    }
    else {
      if (Z.type!=_SYMB)
	return false;
      gen z=Z._SYMBptr->feuille;
      ydeg=td-giacmax(rd,sd);
      if (Z.is_symb_of_sommet(at_exp)){
	if (rd==sd){ // test whether int S_s/R_r is elementary with n*z coeff
	  gen lnc,prim,remains,tmp=r2sym(Rr,lv,contextptr)/r2sym(Ss,lv,contextptr);
	  if (in_risch(tmp,x,v1,z,prim,lnc,remains,contextptr)&&lnc.type==_INT_)
	    ydeg=giacmax(lnc.val,ydeg);
	}
      }
      else {
	if (rd==sd+1){
	  gen lnc,prim,remains,tmp=r2sym(Rr,lv,contextptr)/r2sym(Ss,lv,contextptr);
	  // test whether int S_s/R_r is elementary with exp also elementary
	  if (in_risch(tmp,x,v1,plus_one,prim,lnc,remains,contextptr)){
	    prim=tsimplify(exp(prim,contextptr),contextptr);
	    vecteur lv2(lv);
	    lvar(prim,lv2);
	    if (lv2==lv){ // the exp is elementary, change variables
	      expshift=prim;
	      fraction expshiftf(sym2r(prim,lv,contextptr));
	      polynome expnum(gen2poly(expshiftf.num,ss)),expden(gen2poly(expshiftf.den,ss));
	      R=R*Rr*expden;
	      S=(S*Rr-R*Ss)*expden;
	      // sd=S.lexsorted_degree();
	      T=(T*Rr)*expnum;
	    }
	  }
	}
      }
    }
    bool ok=SPDE(R,S,T,x,v,vdiff,lv,ydeg,y,contextptr);
    y=y*expshift/r2sym(D,lv,contextptr);
    return ok;
  }

  static pf<gen> hermite_reduce(const pf<gen> & p_cst,const gen & a,const vecteur & v_derivatives,const vecteur & lv,gen & prim,GIAC_CONTEXT){
    pf<gen> p(p_cst);
    if (p.mult<=0){
      prim=gensizeerr(gettext("risch.cc/hermite_reduce"));
      return p;
    }
    if (p.mult==1)
      return p_cst;
    gen expax=exp(r2sym(a,lv,contextptr)*lv.front(),contextptr);
    fraction pprime=diff(p.fact,v_derivatives);
    if (is_undef(pprime.num)){
      prim=pprime.num;
      return p;
    }
    int s=int(lv.size());
    polynome fprime=gen2poly(pprime.num,s),fprimeden=gen2poly(pprime.den,s);
    polynome d(s),u(s),v(s),C(s);
    polynome resnum(s),resden(plus_one,s),numtemp(s),dentemp(s);
    Tegcdpsr(fprime,p.fact,v,u,d); // f*u+f'.num*v=d
    polynome usave(u),vsave(v),pdensave(s);
    // reduce p.den to the cofactor
    p.den=p.den/pow(p.fact,p.mult);
    // now we are integrating p.num/(p.den*f^p.mult)
    while (p.mult>1){
      pdensave=p.den;
      Tegcdtoabcuv(fprime,p.fact,p.num,v,u,d,C); // f*u+f'.num*v=C*p.num
      v=v*fprimeden; // f*u+f'*v=C*p.num
      // p.num/(p.den*f^p.mult)=(f*u+f'*v)/(C*p.den*f^p.mult)
      p.mult--;
      // int(f'/f^(p.mult+1) * v/(C*p.den)*exp(a*x) ) 
      // = 1/p.mult*[-1/f^(p.mult)*v/(C*p.den)*exp(a*x) 
      //   + int(1/f^(p.mult)*(v/C*p.den*exp(a*x))')] 
      // update non integrated term
      if (!diff(v,C*p.den,a,v_derivatives,numtemp,dentemp)){
	prim=gensizeerr(gettext("risch.cc/hermite_reduce"));
	return p;
      }
      dentemp=dentemp*p.mult;
      Tfracadd<gen>(numtemp,dentemp,u,C*p.den,p.num,p.den);
      simplify(p.num,p.den);
      // update integrated term
      pdensave=-C*pdensave;
      TsimplifybyTlgcd(pdensave,v);
      pdensave=pdensave*pow(p.fact,p.mult)*p.mult;
      Tfracadd<gen>(resnum,resden,v,pdensave,numtemp,dentemp);
      resnum=numtemp;
      resden=dentemp;
      // finished?
      if (p.mult==1)
	break;
      // restore Bezout coeffs
      u=usave;
      v=vsave;
    }
    prim=prim+r2sym(resnum,lv,contextptr)/r2sym(resden,lv,contextptr)*expax;
    // restore the factor in p.den
    p.den=p.den*p.fact;
    return pf<gen>(p);
  }

  // int(exp(a*x)*coeff,x) where coeff is a rational fraction wrt x
  // polynomial part: recursive call of int
  // remaining part: reduce to square free denom by int by part
  // partial fraction decomp -> Ei
  // Ei with imaginary arguments -> Ei(i*t)=i*(-pi/2)+i*Si(x)+Ci(x)
  static bool integrate_ei(const gen & a,const gen & coeff,const identificateur & x,gen & prim,gen & remains_to_integrate,GIAC_CONTEXT){
    gen expax=exp(a*x,contextptr),ima,rea=re(a,contextptr);
    bool imaneg=false;
    if (is_zero(rea)){
      ima=im(a,contextptr);
      imaneg=is_positive(-ima,contextptr);
    }
    vecteur l(1,x);
    lvar(coeff,l);
    lvar(a,l);
    int s=int(l.size());
    vecteur l1(l.begin()+1,l.end());
    gen r=e2r(coeff,l,contextptr),ar=e2r(a,l,contextptr);
    // cout << "Int " << r << endl;
    gen r_num,r_den;
    fxnd(r,r_num,r_den);
    if (r_num.type==_EXT)
      return false;
    polynome den(gen2poly(r_den,s)),num(gen2poly(r_num,s));
    polynome p_content(lgcd(den));
    // Square-free factorization
    factorization vden(sqff(den/p_content)); 
    vector< pf<gen> > pfdecomp;
    polynome ipnum(s),ipden(s);
    gen ipshift;
    partfrac(num,den,vden,pfdecomp,ipnum,ipden);
    // int( ipnum/ipden*exp(a*x),x)
    int save=calc_mode(contextptr);
    calc_mode(0,contextptr);
    prim += _integrate(gen(makevecteur(expax*r2sym(ipnum/ipden,l,contextptr),x),_SEQ__VECT),contextptr);
    calc_mode(save,contextptr);
    if (is_undef(prim)) return false;
    // Hermite reduction 
    vector< pf<gen> >::iterator it=pfdecomp.begin();
    vector< pf<gen> >::const_iterator itend=pfdecomp.end();
    for (;it!=itend;++it){
      pf<gen> tmp(*it);
      if (it->mult>1){
	vecteur vl(1,1);
	tmp=hermite_reduce(*it,ar,vl,l,prim,contextptr);
	if (is_undef(prim))
	  return false;
      }
      if (is_zero(tmp.num))
	continue;
      // ei part for it->num/it->den
      vecteur itnum=polynome2poly1(tmp.num,1);
      vecteur itden=derivative(polynome2poly1(tmp.den,1));
      factorization vden;
      gen extra_div=1;
      factor(tmp.fact,p_content,vden,false,true,true,1,extra_div); // complex+sqrt ok
      factorization::const_iterator f_it=vden.begin(),f_itend=vden.end();
      gen add_prim;
      // bool ok=true;
      for (;f_it!=f_itend;++f_it){
	int deg=f_it->fact.lexsorted_degree();
	if (!deg)
	  continue;
	if (deg!=1){
	  remains_to_integrate=remains_to_integrate+expax*r2sym(it->num,l,contextptr)/r2sym(it->den,l,contextptr);
	  break;
	}
	// extract the root
	vecteur vtmp=polynome2poly1(f_it->fact,1);
	gen root=-vtmp.back()/vtmp.front();
	gen rootn=horner(itnum,root);
	gen rootd=horner(itden,root);
	root = r2sym(root,l1,contextptr);
	if (is_zero(im(root,contextptr)) && is_zero(rea)){
	  prim += (cos(ima*root,contextptr)+cst_i*sin(ima*root,contextptr))*(r2sym(rootn/rootd,l1,contextptr)*(symbolic(at_Ci,(imaneg?(-ima):ima)*(x-root))+(imaneg?(-cst_i):cst_i)*symbolic(at_Si,(imaneg?(-ima):ima)*(x-root))));
	}
	else
	  prim += r2sym(rootn/rootd,l1,contextptr)*exp(a*root,contextptr)*symbolic(at_Ei,a*(x-root));
      }
    }
    return true;
  }

  // e is assumed to be a (generallized) poly wrt the top var of v
  // shift is 0 for a poly or the max Laurent exponent for a generallized poly
  static bool risch_poly_part(const vecteur & e,int shift,const identificateur & x,const vecteur & v,const gen & allowed_lnarg,gen & prim,gen & lncoeff,gen & remains_to_integrate,GIAC_CONTEXT){
    if (v.size()==1){ // the shift must be 1 for integration of a polynomial
      vecteur tmp=e;
      reverse(tmp.begin(),tmp.end());
      tmp=integrate(tmp,1);
      if (is_undef(tmp))
	return false;
      reverse(tmp.begin(),tmp.end());
      tmp.push_back(zero);
      prim=prim+symb_horner(tmp,x);
      return true;
    } 
    int s=int(e.size()); // degree is s-1
    vecteur v1(v.begin()+1,v.end());
    gen X=v.front();
    if (X.is_symb_of_sommet(at_ln)){
      gen dX=ratnormal(derive(X,x,contextptr),contextptr);
      if (is_undef(dX))
	return false;
      // log extension
      vecteur eprim(s+1);
      gen lnc,remains;
      for (int j=s-1;j>0;--j){
	// eprim[s-j] ' =  e[s-1-j] - (j+1) eprim[s-j-1] * v.front()'
	if (!in_risch(e[s-1-j]-(j+1)*eprim[s-1-j]*dX,x,v1,X._SYMBptr->feuille,eprim[s-j],lnc,remains,contextptr)){
	  remains_to_integrate=remains_to_integrate+symb_horner(e,X);
	  return false;
	}
	eprim[s-1-j]=eprim[s-1-j]+rdiv(lnc,j+1,contextptr);
      }
      gen prim_add;
      bool ok=in_risch(e[s-1]-eprim[s-1]*dX,x,v1,zero,prim_add,lnc,remains,contextptr);
      prim=prim+prim_add+symb_horner(eprim,X);
      remains_to_integrate=remains_to_integrate+remains;
      return ok;
    }
    // exp extension: we have to solve a Risch diff equation for each power
    gen prim_add,remains;
    // exp extension
    vecteur eprim(s);
    if (!X.is_symb_of_sommet(at_exp))
      return false;
    gen dY=derive(X._SYMBptr->feuille,x,contextptr);
    if (is_undef(dY))
      return false;
    for (int j=s-1;j>=0;--j){
      if (j+shift==0){
	bool ok=in_risch(e[s-1-j],x,v1,allowed_lnarg,prim_add,lncoeff,remains,contextptr);
	prim=prim+prim_add;
	remains_to_integrate=remains_to_integrate+remains;
	if (!ok &&!is_zero(allowed_lnarg))
	  return ok;      
      }
      else {
	if (!risch_desolve((j+shift)*dY,e[s-1-j],x,v1,eprim[s-1-j],true,contextptr)) {
	  gen coeff=e[s-1-j],pui=j+shift;
	  gen a,b,c,d;
	  if (is_zero(allowed_lnarg)&&is_linear_wrt(X._SYMBptr->feuille,x,a,b,contextptr) && lvarx(coeff,x)==vecteur(1,x) && integrate_ei(pui*a,coeff,x,c,d,contextptr)){
	    // add to prim int(exp(pui*(a*x+b))*coeff) 
	    // expressed with Ei
	    prim += exp(pui*b,contextptr)*c;
	    remains_to_integrate += exp(pui*b,contextptr)*d;
	    continue;
	  }
	  remains_to_integrate=remains_to_integrate+coeff*pow(X,pui,contextptr);
	  eprim[s-1-j]=0;
	  if (!is_zero(allowed_lnarg))
	    return false;
	}
      }
    }
    prim=prim+symb_horner(eprim,X)*pow(X,shift);
    return true;
  }

  // Inner Risch algorithm call, v is the tower extension
  // allowed_lnarg is zero if all ln are allowed or the arg
  // of the allowed ln if only 1 ln is allowed
  // lncoeff will contain this coeff if it's the case
  // prim is the antiderivative
  // returns false if only 1 ln allowed and no elementary integral found 
  static bool in_risch(const gen & e,const identificateur & x,const vecteur & v,const gen & allowed_lnarg,gen & prim,gen & lncoeff,gen & remains_to_integrate,GIAC_CONTEXT){
    prim=zero;
    lncoeff=zero;
    remains_to_integrate= zero;
    int vs=int(v.size());
    vecteur l(v);
    // add top non-x vars
    lvar(e,l);
    gen diffv=derive(v,x,contextptr);
    if (is_undef(diffv) || diffv.type!=_VECT)
      return false;
    vecteur vx=*diffv._VECTptr;
    lvar(vx,l);
    vecteur vl;
    int s=int(l.size());
    for (int i=0;i<vs;++i)
      vl.push_back(e2r(vx[i],l,contextptr));
    vecteur l1(l.begin()+1,l.end());
    gen r=e2r(e,l,contextptr);
    // cout << "Int " << r << endl;
    gen r_num,r_den;
    fxnd(r,r_num,r_den);
    if (r_num.type==_EXT){
      remains_to_integrate= e;
      return false;
    }
    polynome den(gen2poly(r_den,s)),num(gen2poly(r_num,s));
    polynome p_content(lgcd(den));
    // Square-free factorization
    // FIXME: if ex[ extension, should always treat pole 0 apart
    factorization vden;
    int zeromult=den.coord.back().index.front();
    if (zeromult){
      index_t sh(den.dim);
      sh[0]=-zeromult;
      polynome dens=den.shift(sh);
      vden=sqff(dens/p_content); 
      vden.push_back(facteur<polynome>(polynome(monomial<gen>(1,1,den.dim)),zeromult));
    }
    else
      vden=sqff(den/p_content); 
    vector< pf<gen> > pfdecomp;
    polynome ipnum(s),ipden(s);
    gen ipshift;
    partfrac(num,den,vden,pfdecomp,ipnum,ipden);
    // Hermite/ln reduction and 0 isolation for exp extensions
    vector< pf<gen> >::iterator it=pfdecomp.begin();
    vector< pf<gen> >::const_iterator itend=pfdecomp.end();
    for (;it!=itend;++it){
      if (v.front().is_symb_of_sommet(at_exp) && it->fact.coord.size()==1){
	// generalized polynomial part, fact must be 1 [1,0,0...]
	index_t i(s);
	i.front()=-it->mult;
	vecteur tmp=polynome2poly1(it->num,1);
	tmp=*r2sym(tmp,l1,contextptr)._VECTptr;
	tmp=divvecteur(tmp,r2sym(it->den.shift(i),l,contextptr));
	if (!risch_poly_part(tmp,-it->mult,x,v,allowed_lnarg,prim,lncoeff,remains_to_integrate,contextptr) && !is_zero(allowed_lnarg))
	  return false;
      }
      else { // Hermite reduce it->num/it->den
	pf<gen> tmp(*it);
	if (it->mult>1){
	  tmp=hermite_reduce(*it,0,vl,l,prim,contextptr);
	  if (is_undef(prim))
	    return false;
	}
	if (is_zero(tmp.num))
	  continue;
	if (!is_zero(allowed_lnarg)){ // if only 1 log allowed
	  if (pfdecomp.size()>1)
	    return false;
	  // compute u'/u, must be a multiple of it->num/it->den
	  lncoeff=normal(allowed_lnarg*r2sym(it->num,l,contextptr)/(derive(allowed_lnarg,x,contextptr)*r2sym(it->den,l,contextptr)),contextptr);
	  if (!is_zero(derive(lncoeff,x,contextptr)))
	    return false;
	  continue;
	}
	// Logarithmic part: compute resultant of num - t * den
	polynome p1(s);
	polynome pres=rothstein_trager_resultant(tmp.num,tmp.den,vl,p1,contextptr);
	// Factorization, should return 1st order factor independant of
	// the tower variables
	factorization vden;
	gen extra_div=1;
	factor(pres,p_content,vden,false,withsqrt(contextptr),true,1,extra_div);
	factorization::const_iterator f_it=vden.begin(),f_itend=vden.end();
	gen add_prim;
	bool ok=true;
	for (;f_it!=f_itend;++f_it){
	  int deg=f_it->fact.lexsorted_degree();
	  if (!deg)
	    continue;
	  if (deg!=1){
	    ok=false;
	    remains_to_integrate=remains_to_integrate+r2sym(it->num,l,contextptr)/r2sym(it->den,l,contextptr);
	    break;
	  }
	  // extract the root
	  vecteur vtmp=polynome2poly1(f_it->fact,1);
	  gen root=-r2sym(vtmp.back()/vtmp.front(),l1,contextptr);
	  if (!is_zero(derive(root,x,contextptr))){
	    ok=false;
	    // remains_to_integrate=remains_to_integrate+r2sym(it->num,l,contextptr)/r2sym(it->den,l,contextptr); 
	    remains_to_integrate=remains_to_integrate+r2sym(tmp.num,l,contextptr)/r2sym(tmp.den,l,contextptr);
	    break;
	  }
	  identificateur t(" t");
	  gen tmp1=r2sym(p1,mergevecteur(vecteur(1,t),l),contextptr);
	  tmp1=subst(tmp1,t,root,false,contextptr);
	  gen tmparg=_gcd(makevecteur(recursive_normal(tmp1,contextptr),recursive_normal(r2sym(it->fact,l,contextptr),contextptr)),contextptr); 
	  add_prim=add_prim+root*ln(tmparg,contextptr);
	  // If tmparg is of maximal degree, this will change the integral
	  // part of the fraction by root*_quo(tmparg',tmparg,X)
	  ipshift=ipshift-root*_quo(makevecteur(derive(tmparg,x,contextptr),tmparg,recursive_normal(v.front(),contextptr)),contextptr);
	  if (is_undef(ipshift))
	    return false;
	}
	if (ok)
	  prim=prim+add_prim;
      } // end else case (non 0 pole, ie Hermite reduction)
    } // end for (all denominateurs)
    // ipnum/ipden = the polynomial part -> integrate it
    simplify(ipnum,ipden);
    vecteur tmp=polynome2poly1(ipnum,1);
    tmp=*r2sym(tmp,l1,contextptr)._VECTptr;
    tmp=divvecteur(tmp,r2sym(ipden,l,contextptr));
    if (tmp.empty())
      tmp=vecteur(1,ipshift);
    else
      tmp.back() += ipshift;
    if (!risch_poly_part(tmp,0,x,v,allowed_lnarg,prim,lncoeff,remains_to_integrate,contextptr) && !is_zero(allowed_lnarg))
      return false;
    return true;
  }


  static gen risch_lin(const gen & e_orig,const identificateur & x,gen & remains_to_integrate,GIAC_CONTEXT){
    vecteur v;
    gen e=e_orig;
    vecteur vatan=lop(e,at_atan);
    vecteur vln;
    for (int i=0;i<int(vatan.size());++i){
      gen ga=vatan[i];
      gen g=ga._SYMBptr->feuille;
      vln.push_back(ln(ratnormal(rdiv(cst_i+g,cst_i-g),contextptr),contextptr));
      vatan[i]=-2*ga*cst_i;
    }
    if (!risch_tower(x,e,v,contextptr)){
      remains_to_integrate=e_orig;
      return zero;
    }
    if (v.empty())
      return e*x;
    gen prim,lncoeff;
    in_risch(e,x,v,zero,prim,lncoeff,remains_to_integrate,contextptr);
    vector<const unary_function_ptr *> SiCi(1,at_Si);
    SiCi.push_back(at_Ci);
    if (!lop(prim,at_Si).empty()){
      prim=recursive_normal(prim,contextptr);
      if (has_i(prim)){
	prim=_exp2trig(prim,contextptr);
	prim=recursive_normal(prim,contextptr);
      }
    }
    if (!vatan.empty())
      prim=ratnormal(subst(recursive_ratnormal(prim,contextptr),vln,vatan,false,contextptr),contextptr);
    if (is_zero(prim))
      remains_to_integrate=e_orig;
    return prim;
  }

  gen risch(const gen & e_orig,const identificateur & x,gen & remains_to_integrate,GIAC_CONTEXT){
#if 0 // def GIAC_HAS_STO_38
    remains_to_integrate=e_orig;
    return 0;
#endif
    vecteur vexp;
    lin(trig2exp(e_orig,contextptr),vexp,contextptr);
    const_iterateur it=vexp.begin(),itend=vexp.end();
    gen rem,remsum,res;
    for (;it!=itend;){
      gen coeff=*it;
      ++it; 
      gen expo=*it;
      ++it; rem=0;
      res = res+risch_lin(coeff*exp(expo,contextptr),x,rem,contextptr);
      remsum += rem;
    }
    res = res+risch_lin(remsum,x,remains_to_integrate,contextptr);
    if (is_zero(res)){ // perhaps we should derive res and substract it from e_orig
      remains_to_integrate=e_orig;
    }
    else {
      if (!has_i(e_orig) && has_i(remains_to_integrate)){
	// ?fails for x/(2*i)/(exp(i*x)+exp(-i*x))*2*exp(i*x)+(-i)*x/(exp((-i)*x)^2+1)
	gen tmp=_exp2trig(remains_to_integrate,contextptr),r,i;
	reim(tmp,r,i,contextptr);
	if (is_zero(ratnormal(i,contextptr)))
	  remains_to_integrate=ratnormal(r,contextptr);
	else {
	  res=0;
	  remains_to_integrate=e_orig;
	}
      }
    }
    vector<const unary_function_ptr *> SiCiexp(1,at_Si);
    SiCiexp.push_back(at_Ci);
    SiCiexp.push_back(at_exp);
    if (!lop(res,SiCiexp).empty()){
      res=recursive_normal(res,contextptr);
      if (!has_i(e_orig) && has_i(res)){
	res=_exp2trig(res,contextptr);
	res=recursive_normal(res,contextptr);
	if (has_i(res))
	  res=recursive_normal(re(halftan(res,contextptr),contextptr),contextptr);
      }
    }
    return res;
  }
  
  gen _risch(const gen & g,GIAC_CONTEXT){
    if ( g.type==_STRNG && g.subtype==-1) return  g;
    if (g.type!=_VECT)
      return _risch(vecteur(1,g),contextptr);
    vecteur & v=*g._VECTptr;
    int s=int(v.size());
    if (s>2)
      return _integrate(g,contextptr);
    gen tmp;
    gen var=x__IDNT_e;
    if (s==2 && v.back().type==_IDNT)
      var=v.back();
    gen res=risch(v.front(),*var._IDNTptr,tmp,contextptr);
    if (is_zero(tmp))
      return res;
    return res+symbolic(at_integrate,makesequence(tmp,var)); 
  }
  static const char _risch_s []="risch";
  static define_unary_function_eval (__risch,&_risch,_risch_s);
  define_unary_function_ptr5( at_risch ,alias_at_risch ,&__risch,0,true);

  // integer roots of a polynomial
  vecteur iroots(const polynome & p){
    int s=p.dim;
    vecteur zerozero(s-1);
    vecteur P0(polynome2poly1(p/lgcd(p),1));
    vecteur P(P0);
    // eval every coeff at (0,...,0)
    int d=int(P.size());
    for (int i=0;i<d;++i){
      if (P[i].type==_POLY)
	P[i]=peval(*P[i]._POLYptr,zerozero,0);
    }
    // now search the integer roots of this polynomial
    polynome p0(poly12polynome(P,1,1));
    polynome p1=p0.derivative();
    p1=gcd(p0,p1);
    p0=p0/p1; // p0 is now squarefree with the same roots as initial p0
    // check that all coeffs are integer, if not call normal factorizatio
    vector< monomial<gen> >::const_iterator it=p0.coord.begin(),itend=p0.coord.end();
    vecteur res;
    for (;it!=itend;++it){
#ifndef HAVE_LIBNTL // with LIBNTL, linearfind does nothing!
      if (!is_integer(it->value)){
#endif
	factorization vden;
	gen extra_div=1;
	factor(p0,p1,vden,false,false,false,1,extra_div);
	factorization::const_iterator f_it=vden.begin(),f_itend=vden.end();
	// bool ok=true;
	for (;f_it!=f_itend;++f_it){
	  int deg=f_it->fact.lexsorted_degree();
	  if (deg!=1)
	    continue;
	  // extract the root
	  vecteur vtmp=polynome2poly1(f_it->fact,1);
	  gen root=-vtmp.back()/vtmp.front();
	  if (root.type==_INT_)
	    res.push_back(root);
	}
	return res;
#ifndef HAVE_LIBNTL
      }
#endif
    }
    environment * env=new environment;
    polynome temp(1);
    vectpoly v;
    int ithprime=1;
    if (!linearfind(p0,env,temp,v,ithprime)) // FIXME??
      res.clear();// int bound=
    delete env;
    d=int(v.size());
    for (int i=0;i<d;++i){
      vecteur tmpv=polynome2poly1(v[i]);
      if (tmpv.size()!=2)
	continue;
      gen g=-tmpv[1]/tmpv[0];
      if (g.type!=_INT_)
	continue;
      gen tmp=horner(P0,g);
      if (is_zero(tmp))
	res.push_back(g);
    }
    return res;
  }
#ifndef NO_NAMESPACE_GIAC
} // namespace giac
#endif // ndef NO_NAMESPACE_GIAC