001/* 002 * $Id: PartialFraction.java 5810 2018-04-23 20:41:35Z kredel $ 003 */ 004 005package edu.jas.ufd; 006 007 008import java.io.Serializable; 009import java.util.ArrayList; 010import java.util.HashMap; 011import java.util.HashSet; 012import java.util.List; 013import java.util.Map; 014import java.util.Set; 015 016import org.apache.log4j.Logger; 017 018import edu.jas.poly.AlgebraicNumber; 019import edu.jas.poly.AlgebraicNumberRing; 020import edu.jas.poly.GenPolynomial; 021import edu.jas.poly.GenPolynomialRing; 022import edu.jas.poly.PolyUtil; 023import edu.jas.structure.GcdRingElem; 024 025 026/** 027 * Container for the partial fraction decomposition of a squarefree denominator. 028 * num/den = sum( a_i / d_i ) 029 * @author Heinz Kredel 030 * @param <C> coefficient type 031 */ 032 033public class PartialFraction<C extends GcdRingElem<C>> implements Serializable { 034 035 036 private static final Logger logger = Logger.getLogger(PartialFraction.class); 037 038 039 /** 040 * Original numerator polynomial coefficients from C and deg(num) < 041 * deg(den). 042 */ 043 public final GenPolynomial<C> num; 044 045 046 /** 047 * Original (irreducible) denominator polynomial coefficients from C. 048 */ 049 public final GenPolynomial<C> den; 050 051 052 /** 053 * List of numbers from C. 054 */ 055 public final List<C> cfactors; 056 057 058 /** 059 * List of linear factors of the denominator with coefficients from C. 060 */ 061 public final List<GenPolynomial<C>> cdenom; 062 063 064 /** 065 * List of algebraic numbers of an algebraic field extension over C. 066 */ 067 public final List<AlgebraicNumber<C>> afactors; 068 069 070 /** 071 * List of factors of the denominator with coefficients from an 072 * AlgebraicNumberRing<C>. 073 */ 074 public final List<GenPolynomial<AlgebraicNumber<C>>> adenom; 075 076 077 /** 078 * Constructor. 079 * @param n numerator GenPolynomial over C. 080 * @param d irreducible denominator GenPolynomial over C. 081 * @param cf list of elements a_i. 082 * @param cd list of linear factors d_i of d. 083 * @param af list of algebraic elements a_i. 084 * @param ad list of linear (irreducible) factors d_i of d with algebraic 085 * coefficients. n/d = sum( a_i / d_i ) 086 */ 087 public PartialFraction(GenPolynomial<C> n, GenPolynomial<C> d, List<C> cf, List<GenPolynomial<C>> cd, 088 List<AlgebraicNumber<C>> af, List<GenPolynomial<AlgebraicNumber<C>>> ad) { 089 num = n; 090 den = d; 091 cfactors = cf; 092 cdenom = cd; 093 afactors = af; 094 adenom = ad; 095 for (GenPolynomial<C> p : cdenom) { 096 if (p.degree(0) > 1) { 097 throw new IllegalArgumentException("polynomial not linear, p = " + p); 098 } 099 } 100 for (GenPolynomial<AlgebraicNumber<C>> a : adenom) { 101 if (a.degree(0) > 1) { 102 throw new IllegalArgumentException("polynomial not linear, a = " + a); 103 } 104 } 105 } 106 107 108 /** 109 * Get the String representation. 110 * @see java.lang.Object#toString() 111 */ 112 @Override 113 public String toString() { 114 StringBuffer sb = new StringBuffer(); 115 sb.append("(" + num.toString() + ")"); 116 sb.append(" / "); 117 sb.append("(" + den.toString() + ")"); 118 sb.append(" =\n"); 119 boolean first = true; 120 for (int i = 0; i < cfactors.size(); i++) { 121 C cp = cfactors.get(i); 122 if (first) { 123 first = false; 124 } else { 125 sb.append(" + "); 126 } 127 sb.append("(" + cp.toString() + ")"); 128 GenPolynomial<C> p = cdenom.get(i); 129 sb.append(" / (" + p.toString() + ")"); 130 } 131 if (!first && afactors.size() > 0) { 132 sb.append(" + "); 133 } 134 first = true; 135 for (int i = 0; i < afactors.size(); i++) { 136 if (first) { 137 first = false; 138 } else { 139 sb.append(" + "); 140 } 141 AlgebraicNumber<C> ap = afactors.get(i); 142 AlgebraicNumberRing<C> ar = ap.factory(); 143 GenPolynomial<AlgebraicNumber<C>> p = adenom.get(i); 144 if (p.degree(0) < ar.modul.degree(0) && ar.modul.degree(0) > 2) { 145 sb.append("sum_(" + ar.getGenerator() + " in "); 146 sb.append("rootOf(" + ar.modul + ") ) "); 147 } else { 148 //sb.append("sum_("+ar+") "); 149 } 150 sb.append("(" + ap.toString() + ")"); 151 sb.append(" / (" + p.toString() + ")"); 152 //sb.append(" ## over " + ap.factory() + "\n"); 153 } 154 return sb.toString(); 155 } 156 157 158 /** 159 * Get a scripting compatible string representation. 160 * @return script compatible representation for this container. 161 * @see edu.jas.structure.ElemFactory#toScript() 162 */ 163 public String toScript() { 164 // Python case 165 StringBuffer sb = new StringBuffer(); 166 sb.append(num.toScript()); 167 sb.append(" / "); 168 sb.append(den.toScript()); 169 sb.append(" = "); 170 boolean first = true; 171 int i = 0; 172 for (C cp : cfactors) { 173 if (first) { 174 first = false; 175 } else { 176 sb.append(" + "); 177 } 178 sb.append(cp.toScript()); 179 GenPolynomial<C> p = cdenom.get(i); 180 sb.append(" / " + p.toScript()); 181 } 182 if (!first) { 183 sb.append(" + "); 184 } 185 first = true; 186 i = 0; 187 for (AlgebraicNumber<C> ap : afactors) { 188 if (first) { 189 first = false; 190 } else { 191 sb.append(" + "); 192 } 193 AlgebraicNumberRing<C> ar = ap.factory(); 194 GenPolynomial<AlgebraicNumber<C>> p = adenom.get(i); 195 if (p.degree(0) < ar.modul.degree(0) && ar.modul.degree(0) > 2) { 196 sb.append("sum_(" + ar.getGenerator().toScript() + " in "); 197 sb.append("rootOf(" + ar.modul.toScript() + ") ) "); 198 } else { 199 //sb.append("sum_("+ar+") "); 200 } 201 sb.append(ap.toScript()); 202 sb.append(" / " + p.toScript()); 203 //sb.append(" ## over " + ap.toScriptFactory() + "\n"); 204 } 205 return sb.toString(); 206 } 207 208 209 /** 210 * Hash code for this Factors. 211 * @see java.lang.Object#hashCode() 212 */ 213 @Override 214 public int hashCode() { 215 int h = num.hashCode(); 216 h = h * 37 + den.hashCode(); 217 h = h * 37 + cfactors.hashCode(); 218 h = h * 37 + cdenom.hashCode(); 219 h = h * 37 + afactors.hashCode(); 220 h = h * 37 + adenom.hashCode(); 221 return h; 222 } 223 224 225 /** 226 * Comparison with any other object. 227 * @see java.lang.Object#equals(java.lang.Object) 228 */ 229 @Override 230 @SuppressWarnings("unchecked") 231 public boolean equals(Object B) { 232 if (B == null) { 233 return false; 234 } 235 if (!(B instanceof PartialFraction)) { 236 return false; 237 } 238 PartialFraction<C> a = (PartialFraction<C>) B; 239 boolean t = num.equals(a.num) && den.equals(a.den); 240 if (!t) { 241 return t; 242 } 243 t = cfactors.equals(a.cfactors); 244 if (!t) { 245 return t; 246 } 247 t = cdenom.equals(a.cdenom); 248 if (!t) { 249 return t; 250 } 251 t = afactors.equals(a.afactors); 252 if (!t) { 253 return t; 254 } 255 t = adenom.equals(a.adenom); 256 return t; 257 } 258 259 260 /** 261 * Test if correct partial fraction. num/den = sum( a_i / d_i ) 262 */ 263 @SuppressWarnings("unchecked") 264 public boolean isPartialFraction() { 265 QuotientRing<C> qfac = new QuotientRing<C>(num.ring); 266 // num / den 267 Quotient<C> q = new Quotient<C>(qfac, num, den); 268 //System.out.println("q = " + q); 269 Quotient<C> qs = qfac.getZERO(); 270 int i = 0; 271 for (C c : cfactors) { 272 GenPolynomial<C> cp = cdenom.get(i++); 273 // plus c / cp 274 GenPolynomial<C> cd = num.ring.getONE().multiply(c); 275 Quotient<C> qq = new Quotient<C>(qfac, cd, cp); 276 qs = qs.sum(qq); 277 } 278 //System.out.println("qs = " + qs); 279 if (afactors.isEmpty()) { 280 return q.compareTo(qs) == 0; 281 } 282 283 // sort by extension field 284 Set<AlgebraicNumberRing<C>> fields = new HashSet<AlgebraicNumberRing<C>>(); 285 for (AlgebraicNumber<C> ap : afactors) { 286 if (ap.ring.depth() > 1) { 287 logger.warn("extension field depth to high"); // todo 288 } 289 fields.add(ap.ring); 290 } 291 //System.out.println("fields = " + fields); 292 Map<AlgebraicNumberRing<C>, List<AlgebraicNumber<C>>> facs = new HashMap<AlgebraicNumberRing<C>, List<AlgebraicNumber<C>>>(); 293 for (AlgebraicNumber<C> ap : afactors) { 294 List<AlgebraicNumber<C>> cf = facs.get(ap.ring); 295 if (cf == null) { 296 cf = new ArrayList<AlgebraicNumber<C>>(); 297 } 298 cf.add(ap); 299 facs.put(ap.ring, cf); 300 } 301 //System.out.println("facs = " + facs); 302 Map<AlgebraicNumberRing<C>, List<GenPolynomial<AlgebraicNumber<C>>>> pfacs = new HashMap<AlgebraicNumberRing<C>, List<GenPolynomial<AlgebraicNumber<C>>>>(); 303 for (GenPolynomial<AlgebraicNumber<C>> ap : adenom) { 304 AlgebraicNumberRing<C> ar = (AlgebraicNumberRing<C>) ap.ring.coFac; 305 List<GenPolynomial<AlgebraicNumber<C>>> cf = pfacs.get(ar); 306 if (cf == null) { 307 cf = new ArrayList<GenPolynomial<AlgebraicNumber<C>>>(); 308 } 309 cf.add(ap); 310 pfacs.put(ar, cf); 311 } 312 //System.out.println("pfacs = " + pfacs); 313 314 // check algebraic parts 315 boolean sumMissing = false; 316 for (AlgebraicNumberRing<C> ar : fields) { 317 if (ar.modul.degree(0) > 2) { //&& p.degree(0) < ar.modul.degree(0) ? 318 sumMissing = true; 319 } 320 List<AlgebraicNumber<C>> cf = facs.get(ar); 321 List<GenPolynomial<AlgebraicNumber<C>>> cfp = pfacs.get(ar); 322 GenPolynomialRing<AlgebraicNumber<C>> apfac = cfp.get(0).ring; 323 QuotientRing<AlgebraicNumber<C>> aqfac = new QuotientRing<AlgebraicNumber<C>>(apfac); 324 Quotient<AlgebraicNumber<C>> aq = aqfac.getZERO(); 325 i = 0; 326 for (AlgebraicNumber<C> c : cf) { 327 GenPolynomial<AlgebraicNumber<C>> cp = cfp.get(i++); 328 // plus c / cp 329 GenPolynomial<AlgebraicNumber<C>> cd = apfac.getONE().multiply(c); 330 Quotient<AlgebraicNumber<C>> qq = new Quotient<AlgebraicNumber<C>>(aqfac, cd, cp); 331 //System.out.println("qq = " + qq); 332 aq = aq.sum(qq); 333 } 334 //System.out.println("aq = " + aq); 335 GenPolynomialRing<C> cfac = ar.ring; 336 GenPolynomialRing<GenPolynomial<C>> prfac = new GenPolynomialRing<GenPolynomial<C>>(cfac, apfac); 337 GenPolynomial<GenPolynomial<C>> pqnum = PolyUtil.<C> fromAlgebraicCoefficients(prfac, aq.num); 338 GenPolynomial<GenPolynomial<C>> pqden = PolyUtil.<C> fromAlgebraicCoefficients(prfac, aq.den); 339 //System.out.println("pq = (" + pqnum + ") / (" + pqden + ")"); 340 341 C one = cfac.coFac.getONE(); // varaible should no more occur in coefficient 342 GenPolynomialRing<C> pfac = new GenPolynomialRing<C>(cfac.coFac, prfac); 343 GenPolynomial<C> pnum = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqnum, one); 344 GenPolynomial<C> pden = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqden, one); 345 //System.out.println("p = (" + pnum + ") / (" + pden + ")"); 346 347 // iterate if multiple field extensions 348 while (cfac.coFac instanceof AlgebraicNumberRing) { 349 //System.out.println("cfac.coFac = " + cfac.coFac.toScript()); 350 AlgebraicNumberRing<C> ar2 = (AlgebraicNumberRing<C>) cfac.coFac; 351 cfac = ar2.ring; 352 prfac = new GenPolynomialRing<GenPolynomial<C>>(cfac, apfac); 353 GenPolynomial<AlgebraicNumber<C>> prnum = (GenPolynomial<AlgebraicNumber<C>>) pnum; 354 GenPolynomial<AlgebraicNumber<C>> prden = (GenPolynomial<AlgebraicNumber<C>>) pden; 355 pqnum = PolyUtil.<C> fromAlgebraicCoefficients(prfac, prnum); 356 pqden = PolyUtil.<C> fromAlgebraicCoefficients(prfac, prden); 357 one = cfac.coFac.getONE(); // varaible should no more occur in coefficient 358 pfac = new GenPolynomialRing<C>(cfac.coFac, prfac); 359 pnum = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqnum, one); 360 pden = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqden, one); 361 } 362 363 Quotient<C> qq = new Quotient<C>(qfac, pnum, pden); 364 //System.out.println("qq = " + qq); 365 qs = qs.sum(qq); 366 } 367 boolean cmp = q.compareTo(qs) == 0; 368 if (!cmp) { 369 System.out.println("q != qs: " + q + " != " + qs); 370 } 371 return cmp || sumMissing; 372 } 373 374}