#!/usr/bin/perl -wT #polyfactor_temp.pl ######################################################################################## #List of subroutines: # ######################################################################################## #factorial(n); return-> n!: computes the factorial of n # #comb(n,k); return->n choose k: # #SchubertKroneckerFactorization(poly); return->factors of poly: # # Factors poly over Z. Very inefficient for deg(poly)>4 # #intfactors(integer); return->array of factors: Finds all positive and negative factors# #polyprint(poly); return->N/A: print polynomial in strandard form # #polyadd(poly1,poly2); return->sum: polynomial addition # #polysub(poly1,poly2); return->difference: polynomial subtraction(poly1-poly2) # #polymulti(poly1,poly2); return->product: polynomial multiplication # #pseudopolydiv(num,denom); return->(quo,rem): # # integer poly div, first premultiplies num,then applies div algorithm. # #polyeval(arg,poly); return->value: Evals a poly at arg. # #lagrange_interp((x_0,y_0,...,x_n,y_n)); return->poly: # # Finds the Lagrange interpolation polynomial # #EisensteinIrredCrit(poly); return->0 or 1: # # Returns 1 if poly is irreducible and 0 gives no information. # #comb_next(n,k,array); return->array: # # given n choose k and a combination, returns the next combination. # # e.g.n=3,k=2: (0,1)->(0,2) # # (0,2)->(1,2) # #recinc(n,k,j,array); return->array: # # recursive increase the elts of an array used in sub comb_next # #comb_init(k); return->(0,1,...,k-1,k-1): # #diff(poly p); return->p': # #gcd(a,b); return->d: d is the greatest common divisor of integers a and b # #cont(poly); return->d: d is the greatest common divisor of the coeffs of the poly # #pp(poly); return->poly: finds the primitive part of a poly # ######################################################################################## ######################################################################################## #Bugs: # ######################################################################################## #parsing 2x+1; returns 1 # ######################################################################################## use strict; use warnings; use CGI ':standard'; #use 'my' to declare a variable my @p; my @q; my $string; my $poly=param('poly') || "x"; @p=user_enter($poly); @q=SchubertKroneckerFactorization(@p); #user_out(\@p,\@q); #$string=polyprint(@q); print header("text/html"), start_html(-title => "Polynomial Factorization over the Integers"), "\n", h1("Polynomial Factorization over the Integers"), "\n", p("The poly you entered is p=$poly."), "\n"; if($q[0]!=1) { print "

\n"; print "The factors are of p are:
\n"; for (0..$#q) { $string=polyprint(@{$q[$_]}); chomp $string; print "$string
\n"; } print "

\n\n"; } else { polyprint(@p); print "The poly p is irreducible.\n"; } print end_html(); ################################################################################ #SchubertKroneckerFactorization(poly); #return->(irred,factors of poly): #Factors poly over Z. Very inefficient for deg(poly)>4 sub SchubertKroneckerFactorization{ my $m; #floor of half of the degree of p my $c; #$m+1 choose $outer (n choose k) my $n; #number of times to loop over multifor my $irredn; #=1 if poly is irreducible, 0 if reducible my $bool; #if irred poly passed to SKF and fails Eisenstein set to 1 my $tf_int; #if coeffs of Lagrange poly are integers = 1 my @p; #poly to be factored my @a; #abcissa support my @b; #ordinate support my @f; #family of factors my @e; #choice of values from f my @support; #interpolation points (x_0,y_0,...,x_n,y_n) my @lip; #lagrange interpolation poly my @q; #quo my @r; #rem my @junk; #holds (quo,rem) after division my @temp; #intermediate result my @temp2; #intermediate result my @comb; #combination list my @i; #index array for main loop my @li; #array of last indicies in the array f my @irred; #holds the irreducable factors #put array passed to sub in p @p=pp(@_); $bool=1; #print "Tring to factor:"; #polyprint(@p); #check for irreducablity if(EisensteinIrredCrit(@p)) { #print "poly is irreducable\n"; return (1,\@p); } #floor(deg(p)/2) $m=int($#p/2); #m+1 values to eval p at -> (0,1,-1,...) @a=(0); for my $i (2..$m+1) { push @a,(-1)**$i*int($i/2); } #eval p at a_i and store in b_i for my $i (0..$#a) { $b[$i]=polyeval($a[$i],@p); } #build family of factors for the b_i for my $i (0..$#b) { push @f,[intfactors($b[$i])]; #print "f[$i]=@{$f[$i]}\n"; } for my $i (0..$#a) { $li[$i]=$#{$f[$i]}; } #print "Trying to factor:"; polyprint(@p); #main loop: outer= number of points to try MAIN: for my $outer (2..$m+1) { $c=comb($m+1,$outer); #print "Try $outer out of ",$m+1," points.\n"; #print "There are $c combination(s) to try.\n"; #Initializes combination list @comb=comb_init($outer); for my $k (0..$c-1) { #print "\nk=$k\n"; @comb=comb_next($m+1,$outer,@comb); #print "comb=@comb\n"; @i=initforindex($outer); # $#{$f[$comb[0]]} index of last elt in row $comb[0] # to get the second value from the array @a=(0,1,2) use $a[1]->1 # since it is a scalar #reset array support $n=1; for (0..$outer-1) { $n*=($li[$comb[$_]]+1) } # print "It will require $n loops to go through this combination of them.\n\n"; for (0..$n-1) { @i=multifor(\@i,\@li,\@comb); @support=(); #get the b_i chosen and put into array e for my $j (0..$outer-1) { #print "j=$j\n"; #print "f[$comb[$j]][$i[$j]]\n"; $e[$j]=$f[$comb[$j]][$i[$j]]; } #print "e=@e:\n"; #form the support to interpolate over for my $i (0..$outer-1) { push @support,($a[$i],$e[$i]) } @lip=lagrange_interp(@support); #if the last index of lip is not 0 (i.e. deg(lip)>=1) $tf_int=1; for (0..$#lip) { if($lip[$_]-int($lip[$_]) != 0) { $tf_int=0; last; } } if($#lip !=0 && $tf_int) { #print "lip="; #polyprint(@lip); #\@p is the reference to @p #pseudopolydiv will return an array consisting of two refrences @junk=pseudopolydiv(\@p,\@lip); #@{$junk[0]} derefs first elt in array @junk @q=@{$junk[0]}; @r=@{$junk[1]}; if($#r==0 && $r[0]==(0)) { $bool=0; @temp=SchubertKroneckerFactorization(pp(@lip)); #$irredn=shift @temp; #if($irredn) if($temp[0]!=1) { @irred=(@irred,@temp); } else { @temp2=pp(@{$temp[1]}); @temp=SchubertKroneckerFactorization(@temp2); shift @temp; @irred=(@irred,@temp); } @temp=SchubertKroneckerFactorization(pp(@q)); #$irredn=shift @temp; #if($irredn) if($temp[0]!=1) { @irred=(@irred,@temp); } else { @temp2=pp(@{$temp[1]}); @temp=SchubertKroneckerFactorization(@temp2); shift @temp; @irred=(@irred,@temp); } last MAIN; } } } } } if($bool) { my $ref=\@p; push @irred,$ref; unshift @irred,1; } return @irred; } #factorial(n); #return-> n!: #computes the factorial of n sub factorial{ my $n; my $v; #move arg passed to sub factorial into n $n=shift; $v=1; if($n==0) { return 1; } else { for my $i (1..$n) { $v*=$i; } } #returns v $v; } #comb(n,k); #return->n choose k: # sub comb{ my $n; my $k; my $c; $n=shift; $k=shift; die "n must be larger than k" if($n < $k); $c=factorial($n)/(factorial($k)*factorial($n-$k)); } #intfactors(integer); #return->array of factors: #Finds all positive and negative factors sub intfactors { my $n=shift; my $m=sqrt abs($n); my @f; if($n==1) { @f =(-1,1); return @f; } @f=(-1,1,-1*$n,$n); for my$i (2..$m) { if(($n % $i) == 0) { if($i == ($n/$i)) { push @f,($i,-1*$i); } else { push @f,($i, -1*$i, $n/$i, -1*$n/$i); } } } @f=sort {$a <=> $b} @f; @f; } #polyprint(poly); #return->string: #print polynomial in strandard form sub polyprint{ my @a; my $TF=0; my $string=""; @a=@_; if($#a > 1) { $string .= "$a[$#a]x^$#a"; $TF=1; } foreach my $i (reverse(2..$#a-1)) { if(($a[$i] != 0) && ($a[$i] > 0)) { $string .= "+$a[$i]x^$i"; $TF=1; } elsif(($a[$i] != 0) && ($a[$i] < 0)) { $string .= "$a[$i]x^$i"; $TF=1; } } if($#a>0) { if(($a[1] > 0) && $TF) { $string .= "+$a[1]x"; $TF=1; } elsif(($a[1] > 0) && !$TF) { $string .= "$a[1]x"; $TF=1; } elsif($a[1] < 0) { $string .= "$a[1]x"; $TF=1; } } $string .= "+$a[0]\n" if(($a[0] > 0) && $TF); $string .= "$a[0]\n" if(($a[0] > 0) && !$TF); $string .= "$a[0]\n" if(($a[0] < 0)); $string; } # polyadd(poly1,poly2); #return->sum: #polynomial addition sub polyadd{ my @a; my @b; my @c; my $compared; @a=@{$_[0]}; #gets poly a @b=@{$_[1]}; #gets poly b @c=(); #init poly c $compared=@a <=> @b; #compares the sizes of a and b if($compared == 0) { for my $i (0..$#a) { $c[$i]=$a[$i]+$b[$i]; } my $j=$#c; while(($c[$j] == 0) && ($j > 0)) { pop @c; $j--; } } elsif($compared == -1) { for my $i (0..$#a) { $c[$i]=$a[$i]+$b[$i]; } for my $i ($#a+1..$#b) { $c[$i]=$b[$i]; } } else { for my $i (0..$#b) { $c[$i]=$a[$i]+$b[$i]; } for my $i ($#b+1..$#a) { $c[$i]=$a[$i]; } } @c; } # polysub(poly1,poly2); #return->difference: #polynomial subtraction(poly1-poly2) sub polysub{ my @a; my @b; my @c; my $compared; @a=@{$_[0]}; @b=@{$_[1]}; @c=(); $compared=@a <=> @b; if($compared == 0) { for my $i (0..$#a) { $c[$i]=$a[$i]-$b[$i]; } my $j=$#c; while(($c[$j] == 0) && ($j > 0)) { pop @c; $j--; } } elsif($compared == -1) { for my $i (0..$#a) { $c[$i]=$a[$i]-$b[$i]; } for my $i ($#a+1..$#b) { $c[$i]=-1*$b[$i]; } } else { for my $i (0..$#b) { $c[$i]=$a[$i]-$b[$i]; } for my $i ($#b+1..$#a) { $c[$i]=$a[$i]; } } @c; } #polymulti(poly1,poly2); #return->product: #polynomial multiplication sub polymulti{ my @a; my @b; my @c; @a=@{$_[0]}; @b=@{$_[1]}; for my $i (0..$#a+$#b) { push @c,0; } for(my $i=0;$i<=$#a; $i++) { for(my $j=0;$j<=$#b;$j++) { $c[$i+$j]=$a[$i]*$b[$j]+$c[$i+$j]; } } @c; } #pseudopolydiv(num,denom); #return->(,quo,rem): #integer poly div, first premultiplies num,then applies div algorithm. sub pseudopolydiv{ my @a; my @b; my @c; my $m; my $n; my @q; my $pm; @a=@{$_[0]}; #gets first poly @b=@{$_[1]}; #gets second poly $m=$#a; #deg of poly a $n=$#b; #deg of poly b @q=(); #init poly q #premultiply @a $pm=$b[$#b]**($m-$n+1); @a=polymulti(\@a,[$pm]); #div algor for my $k (reverse(0..$m-$n)) { $q[$k]=$a[$n+$k]/$b[$n]; for my $j (reverse($k..$n+$k-1)) { $a[$j]=$a[$j]-$q[$k]*$b[$j-$k]; } pop @a; } my $j=$#a; while(($a[$j] == 0) && ($j > 0)) { pop @a; $j--; } (\@q,\@a); } #polyeval(arg,poly); #return->value: #Evals a poly at arg. sub polyeval { my $x=shift; my @p=@_; my $sum=0; for my $i (reverse(1..$#p)) { $sum = ($sum + $p[$i])*$x; } $sum+=$p[0]; $sum } #lagrange_interp((x_0,y_0,...,x_n,y_n)); #return->poly: #Finds the Lagrange interpolation polynomial sub lagrange_interp{ my @x; my @y; my @c; my @r; my @p; my @temp; my @junk; #Get support for my $i (0..($#_-1)/2) { $x[$i]= shift; $y[$i]= shift; } #Initialize divided difference matrix for my $i (0..$#y) { $c[$i][0]=$y[$i]; } #Calculate interp poly coefficients for my $j (1..$#x) { #for my $i (0..$#x-$j-1)# for my $i (0..$#x-$j) { $c[$i][$j]=($c[$i+1][$j-1]-$c[$i][$j-1])/($x[$i+$j]-$x[$i]); } } for my $i (0..$#x) { $r[$i]=$c[0][$i]; } #start calculating poly $p[0]=$r[0]; $temp[0]=1; for my $i (0..$#x-1) { @temp=polymulti([-1*$x[$i], 1],\@temp); @junk=polymulti([$r[$i+1]],\@temp); @p=polyadd(\@p,\@junk); } my $j=$#p; while(($p[$j] == 0) && ($j > 0)) { pop @p; $j--; } @p; } #EisensteinIrredCrit(poly); #return->0 or 1: #Returns 1 if poly is irreducible and 0 gives no information. sub EisensteinIrredCrit{ my @p; my $tf; my $tf_big; my $min_coeff; open(PRIMES,'< listoprimes1000.dat') or die $!; @p=@_; return 1 if($#p < 2 ); $tf=1; $tf_big=0; $min_coeff=abs($p[0]); for my $i (1..$#p-1) { $min_coeff=abs($p[$i]) if((abs($p[$i]) < $min_coeff) && ($p[$i] != 0)); } for (0..$#p-1) { if($p[$_]>=2) { $tf_big=1; last; } return 0 if($_==($#p-1)); } while() { $tf=0 if($tf_big); last if($_ > $min_coeff); if(($p[$#p] % $_ != 0) && ($p[0]%($_**2) != 0)) { for my $i (0..$#p-1) { if($p[$i] % $_ == 0) { $tf=1; } else { $tf=0; last; } } } } close PRIMES; $tf; } #comb_next(n,k,array); #return->array: #given n choose k and a combination, returns the next combination #e.g.n=3,k=2: #(0,1)->(0,2) #(0,2)->(1,2) sub comb_next{ my $n; my $k; my @c; my $j; $n=shift; $k=shift; @c=@_; $j=$#c; if($c[$j]<$n-1) { $c[$j]++; } else { @c=recinc($n,$k,$j,@c); } @c; } #recinc(n,k,j,array); #return->array: #recursive increase the elts of an array #used in sub comb_next sub recinc{ my $n; my $k; my $j; my @c; $n=shift; $k=shift; $j=shift; @c=@_; --$j; --$n; die "j=$j: to small in sub recinc" if($j<0); if($c[$j]<$n-1) { $c[$j]++; for my $i ($j+1..$k-1) { $c[$i]=$c[$i-1]+1; } } else { @c=recinc($n,$k,$j,@c); } @c; } #comb_init(k); #return->(0,1,...,k-1,k-1): sub comb_init{ my $k; my @c; $k=shift; for my $i (0..$k-1) { $c[$i]=$i; } $c[$k-1]=$c[$k-1]-1; @c; } #diff(poly p); #return->p': sub diff{ my @p; @p=@_; for my $i (2..$#p) { $p[$i]*=$i; } shift @p; @p; } #gcd(a,b); #return->d: #d is the greatest common divisor of integers a and b sub gcd{ my $a; my $b; ($a,$b)=@_; while($b != 0) { ($a,$b)=($b,$a % $b); } $a; } #cont(poly); #return->d: #d is the greatest common divisor of the coeffs of the poly sub cont{ my @p; my $d; @p=@_; $d=$p[0]; for my $i (1..$#p) { $d=gcd($d,$p[$i]); } $d; } #pp(poly); #return->poly: #finds the primitive part of a poly sub pp{ my @p; my $c; @p=@_; $c=cont(@p); for my $i (0..$#p) { $p[$i]/=$c; } @p; } ################################################################## #initforindex(k); #return->(0, 0,...,0,-1): #returns an array of size k with every entry = 0 except the last. sub initforindex{ my $k; my @i; $k=shift; push @i,0 for (0..$k-1); $i[$k-1]-=1; @i; } #multifor(ref index array i, ref last index value li); #return->index array i: #increments i as if you had called sizeof(i) for loops sub multifor{ my @i; my @li; my @comb; my @temp; @i=@{$_[0]}; @li=@{$_[1]}; @comb=@{$_[2]}; if($i[$#i] < $li[$comb[$#i]]) { $i[$#i]++; } else { @temp=@i; #need since errors occur if use \@i instead of \@temp @i=recfor(\@temp,\@li,\@comb,--$#i); } @i; } #recfor(ref array index i, ref last index value li, last index of i); #return->index array: #used in sub multifor sub recfor{ my @i; my @li; my @comb; my $j; @i=@{$_[0]}; @li=@{$_[1]}; @comb=@{$_[2]}; $j=pop; die "j=$j: too small in sub recfor" if($j<0); if($i[$j] < $li[$comb[$j]]) { $i[$j]++; for my $l ($j+1..$#i) { $i[$l]=0; } } else { @i=recfor(\@i,\@li,\@comb,--$j); } @i; } sub user_enter{ my $input; my @p; $input =$_[0]; while($input =~ /(\+|-)?(\d+)?x\^(\d+)\w*/gc) { if(defined $2) { if(defined $p[$3]) { if(defined $1) { if($1 eq "-") { $p[$3]+=-1*$2; } else { $p[$3]+=$2; } } } else { if(defined $1) { if($1 eq "-") { $p[$3]=-1*$2; } } else { $p[$3]=$2; } } } else { if(defined $p[$3]) { if(defined $1) { if($1 eq "-") { $p[$3]+=-1; } else { $p[$3]+=1; } } } else { if(defined $1) { if($1 eq "-") { $p[$3]=-1; } } else { $p[$3]=1; } } } } while($input =~ /\G(\+|-)?(\d+)?x\w*/gc) { if(defined $2) { if(defined $p[1]) { if(defined $1) { if($1 eq "-") { $p[1]+=-1*$2; } else { $p[1]+=$2; } } } else { if(defined $1) { $p[1]=-1*$2 if($1 eq "-"); $p[1]=$2 if($1 eq "+"); } } } else { if(defined $p[1]) { if(defined $1) { if($1 eq "-") { $p[1]+=-1; } else { $p[1]+=1; } } } else { if(defined $1) { $p[1]=-1 if($1 eq "-"); $p[1]=1 if($1 eq "+"); } } } } while($input =~ /\G(\+|-)?(\d+)/gc) { if(defined $p[0]) { if(defined $1) { $p[0]+=-1*$2 if($1 eq "-"); $p[0]+=$2 if($1 eq "+"); } } else { if(defined $1) { $p[0]=-1*$2 if($1 eq "-"); $p[0]=$2 if($1 eq "+"); } else { $p[0]=$2; } } } for (0..$#p) { $p[$_]=0 unless(defined $p[$_]); } @p; } sub user_out{ my @p; my @q; @p=@{$_[0]}; @q=@{$_[1]}; if($q[0]!=1) { print "are:\n"; for (0..$#q) { polyprint(@{$q[$_]}); } } else { polyprint(@p); print "is irreducible.\n"; } }