001/* 002 * $Id$ 003 */ 004 005package edu.jas.arith; 006 007 008import java.util.Iterator; 009import java.util.Map; 010import java.util.SortedMap; 011import java.util.TreeMap; 012 013import org.apache.logging.log4j.LogManager; 014import org.apache.logging.log4j.Logger; 015 016import edu.jas.structure.NotInvertibleException; 017import edu.jas.structure.RegularRingElem; 018import edu.jas.structure.RingElem; 019import edu.jas.structure.RingFactory; 020 021 022/** 023 * Direct product element based on RingElem. Objects of this class are (nearly) 024 * immutable. 025 * @author Heinz Kredel 026 */ 027public class Product<C extends RingElem<C>> implements RegularRingElem<Product<C>> { 028 029 030 private static final Logger logger = LogManager.getLogger(Product.class); 031 032 033 //private static final boolean debug = logger.isDebugEnabled(); 034 035 036 /** 037 * Product class factory data structure. 038 */ 039 public final ProductRing<C> ring; 040 041 042 /** 043 * Value part of the element data structure. 044 */ 045 public final SortedMap<Integer, C> val; 046 047 048 /** 049 * Flag to remember if this product element is a unit in each cmponent. -1 050 * is unknown, 1 is unit, 0 not a unit. 051 */ 052 protected int isunit = -1; // initially unknown 053 054 055 /** 056 * The constructor creates a Product object from a ring factory. 057 * @param r ring factory. 058 */ 059 public Product(ProductRing<C> r) { 060 this(r, new TreeMap<Integer, C>(), 0); 061 } 062 063 064 /** 065 * The constructor creates a Product object from a ring factory and a ring 066 * element. 067 * @param r ring factory. 068 * @param a ring element. 069 */ 070 public Product(ProductRing<C> r, SortedMap<Integer, C> a) { 071 this(r, a, -1); 072 } 073 074 075 /** 076 * The constructor creates a Product object from a ring factory, a ring 077 * element and an indicator if a is a unit. 078 * @param r ring factory. 079 * @param a ring element. 080 * @param u isunit indicator, -1, 0, 1. 081 */ 082 public Product(ProductRing<C> r, SortedMap<Integer, C> a, int u) { 083 ring = r; 084 val = a; 085 isunit = u; 086 } 087 088 089 /** 090 * Get component. 091 * @param i index of component. 092 * @return val(i). 093 */ 094 public C get(int i) { 095 return val.get(i); // auto-boxing 096 } 097 098 099 /** 100 * Get the corresponding element factory. 101 * @return factory for this Element. 102 * @see edu.jas.structure.Element#factory() 103 */ 104 public ProductRing<C> factory() { 105 return ring; 106 } 107 108 109 /** 110 * Clone this. 111 * @see java.lang.Object#clone() 112 */ 113 @Override 114 public Product<C> copy() { 115 return new Product<C>(ring, val, isunit); 116 } 117 118 119 /** 120 * Is Product zero. 121 * @return If this is 0 then true is returned, else false. 122 * @see edu.jas.structure.RingElem#isZERO() 123 */ 124 public boolean isZERO() { 125 return val.size() == 0; 126 } 127 128 129 /** 130 * Is Product one. 131 * @return If this is 1 then true is returned, else false. 132 * @see edu.jas.structure.RingElem#isONE() 133 */ 134 public boolean isONE() { 135 if (val.size() != ring.length()) { 136 return false; 137 } 138 for (C e : val.values()) { 139 if (!e.isONE()) { 140 return false; 141 } 142 } 143 return true; 144 } 145 146 147 /** 148 * Is Product full. 149 * @return If every component is non-zero, then true is returned, else 150 * false. 151 */ 152 public boolean isFull() { 153 if (val.size() != ring.length()) { 154 return false; 155 } 156 return true; 157 } 158 159 160 /** 161 * Is Product unit. 162 * @return If this is a unit then true is returned, else false. 163 * @see edu.jas.structure.RingElem#isUnit() 164 */ 165 public boolean isUnit() { 166 if (isunit > 0) { 167 return true; 168 } 169 if (isunit == 0) { 170 return false; 171 } 172 if (isZERO()) { 173 isunit = 0; 174 return false; 175 } 176 for (C e : val.values()) { 177 if (!e.isUnit()) { 178 isunit = 0; 179 return false; 180 } 181 } 182 isunit = 1; 183 return true; 184 } 185 186 187 /** 188 * Is Product idempotent. 189 * @return If this is a idempotent element then true is returned, else 190 * false. 191 */ 192 public boolean isIdempotent() { 193 for (C e : val.values()) { 194 if (!e.isONE()) { 195 return false; 196 } 197 } 198 return true; 199 } 200 201 202 /** 203 * Get the String representation as RingElem. 204 * @see java.lang.Object#toString() 205 */ 206 @Override 207 public String toString() { 208 return val.toString(); 209 } 210 211 212 /** 213 * Get a scripting compatible string representation. 214 * @return script compatible representation for this Element. 215 * @see edu.jas.structure.Element#toScript() 216 */ 217 @Override 218 public String toScript() { 219 // Python case 220 StringBuffer s = new StringBuffer("( "); 221 boolean first = true; 222 for (Map.Entry<Integer, C> me : val.entrySet()) { 223 Integer i = me.getKey(); 224 C v = me.getValue(); 225 if (first) { 226 first = false; 227 } else { 228 if (v.signum() < 0) { 229 s.append(" - "); 230 v = v.negate(); 231 } else { 232 s.append(" + "); 233 } 234 } 235 if (!v.isONE()) { 236 s.append(v.toScript() + "*"); 237 } 238 s.append("pg" + i); 239 } 240 s.append(" )"); 241 return s.toString(); 242 } 243 244 245 /** 246 * Get a scripting compatible string representation of the factory. 247 * @return script compatible representation for this ElemFactory. 248 * @see edu.jas.structure.Element#toScriptFactory() 249 */ 250 @Override 251 public String toScriptFactory() { 252 // Python case 253 return factory().toScript(); 254 } 255 256 257 /** 258 * Product comparison. 259 * @param b Product. 260 * @return sign(this-b). 261 */ 262 @Override 263 public int compareTo(Product<C> b) { 264 if (!ring.equals(b.ring)) { 265 logger.info("other ring {}", b.ring); 266 throw new IllegalArgumentException("rings not comparable " + this); 267 } 268 SortedMap<Integer, C> v = b.val; 269 Iterator<Map.Entry<Integer, C>> ti = val.entrySet().iterator(); 270 Iterator<Map.Entry<Integer, C>> bi = v.entrySet().iterator(); 271 int s; 272 while (ti.hasNext() && bi.hasNext()) { 273 Map.Entry<Integer, C> te = ti.next(); 274 Map.Entry<Integer, C> be = bi.next(); 275 s = te.getKey().compareTo(be.getKey()); 276 if (s != 0) { 277 return s; 278 } 279 s = te.getValue().compareTo(be.getValue()); 280 if (s != 0) { 281 return s; 282 } 283 } 284 if (ti.hasNext()) { 285 return -1; 286 } 287 if (bi.hasNext()) { 288 return 1; 289 } 290 return 0; 291 } 292 293 294 /** 295 * Comparison with any other object. 296 * @see java.lang.Object#equals(java.lang.Object) 297 */ 298 @SuppressWarnings("unchecked") 299 @Override 300 public boolean equals(Object b) { 301 if (b == null) { 302 return false; 303 } 304 if (!(b instanceof Product)) { 305 return false; 306 } 307 Product<C> a = (Product<C>) b; 308 return (0 == compareTo(a)); 309 } 310 311 312 /** 313 * Hash code for this local. 314 * @see java.lang.Object#hashCode() 315 */ 316 @Override 317 public int hashCode() { 318 int h = ring.hashCode(); 319 h = 37 * h + val.hashCode(); 320 return h; 321 } 322 323 324 /** 325 * Product extend. Add new component j with value of component i. 326 * @param i from index. 327 * @param j to index. 328 * @return the extended value of this. 329 */ 330 public Product<C> extend(int i, int j) { 331 RingFactory<C> rf = ring.getFactory(j); 332 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); 333 C v = val.get(i); 334 C w = rf.copy(v); // valueOf 335 if (!w.isZERO()) { 336 elem.put(j, w); 337 } 338 return new Product<C>(ring, elem, isunit); 339 } 340 341 342 /** 343 * Product absolute value. 344 * @return the absolute value of this. 345 * @see edu.jas.structure.RingElem#abs() 346 */ 347 public Product<C> abs() { 348 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 349 for (Map.Entry<Integer, C> e : val.entrySet()) { 350 Integer i = e.getKey(); 351 C v = e.getValue().abs(); 352 elem.put(i, v); 353 } 354 return new Product<C>(ring, elem, isunit); 355 } 356 357 358 /** 359 * Product summation. 360 * @param S Product. 361 * @return this+S. 362 */ 363 public Product<C> sum(Product<C> S) { 364 if (S == null || S.isZERO()) { 365 return this; 366 } 367 if (this.isZERO()) { 368 return S; 369 } 370 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone 371 SortedMap<Integer, C> sel = S.val; 372 for (Map.Entry<Integer, C> is : sel.entrySet()) { 373 Integer i = is.getKey(); 374 C x = elem.get(i); 375 C y = is.getValue(); //sel.get( i ); // assert y != null 376 if (x != null) { 377 x = x.sum(y); 378 if (!x.isZERO()) { 379 elem.put(i, x); 380 } else { 381 elem.remove(i); 382 } 383 } else { 384 elem.put(i, y); 385 } 386 } 387 return new Product<C>(ring, elem); 388 } 389 390 391 /** 392 * Product negate. 393 * @return -this. 394 * @see edu.jas.structure.RingElem#negate() 395 */ 396 public Product<C> negate() { 397 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 398 for (Map.Entry<Integer, C> me : val.entrySet()) { 399 Integer i = me.getKey(); 400 C v = me.getValue().negate(); 401 elem.put(i, v); 402 } 403 return new Product<C>(ring, elem, isunit); 404 } 405 406 407 /** 408 * Product signum. 409 * @see edu.jas.structure.RingElem#signum() 410 * @return signum of first non-zero component. 411 */ 412 public int signum() { 413 if (val.size() == 0) { 414 return 0; 415 } 416 C v = val.get(val.firstKey()); 417 return v.signum(); 418 } 419 420 421 /** 422 * Product subtraction. 423 * @param S Product. 424 * @return this-S. 425 */ 426 public Product<C> subtract(Product<C> S) { 427 return sum(S.negate()); 428 } 429 430 431 /** 432 * Product quasi-inverse. 433 * @see edu.jas.structure.RingElem#inverse() 434 * @return S with S = 1/this if defined. 435 */ 436 public Product<C> inverse() { 437 if (this.isZERO()) { 438 return this; 439 } 440 int isu = 0; 441 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 442 for (Map.Entry<Integer, C> me : val.entrySet()) { 443 Integer i = me.getKey(); 444 C x = me.getValue(); // is non zero 445 try { 446 x = x.inverse(); 447 } catch (NotInvertibleException e) { 448 // could happen for e.g. ModInteger or AlgebraicNumber 449 x = null; //ring.getFactory(i).getZERO(); 450 } 451 if (x != null && !x.isZERO()) { // can happen 452 elem.put(i, x); 453 isu = 1; 454 } 455 } 456 return new Product<C>(ring, elem, isu); 457 } 458 459 460 /** 461 * Product idempotent. 462 * @return smallest S with this*S = this. 463 */ 464 public Product<C> idempotent() { 465 if (this.isZERO()) { 466 return this; 467 } 468 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 469 for (Integer i : val.keySet()) { 470 RingFactory<C> f = ring.getFactory(i); 471 C x = f.getONE(); 472 elem.put(i, x); 473 } 474 return new Product<C>(ring, elem, 1); 475 } 476 477 478 /** 479 * Product idempotent complement. 480 * @return 1-this.idempotent(). 481 */ 482 public Product<C> idemComplement() { 483 if (this.isZERO()) { 484 return ring.getONE(); 485 } 486 int isu = 0; 487 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 488 for (int i = 0; i < ring.length(); i++) { 489 C v = val.get(i); 490 if (v == null) { 491 RingFactory<C> f = ring.getFactory(i); 492 C x = f.getONE(); 493 elem.put(i, x); 494 isu = 1; 495 } 496 } 497 return new Product<C>(ring, elem, isu); 498 } 499 500 501 /** 502 * Product idempotent and. 503 * @param S Product. 504 * @return this.idempotent() and S.idempotent(). 505 */ 506 public Product<C> idempotentAnd(Product<C> S) { 507 if (this.isZERO() && S.isZERO()) { 508 return this; 509 } 510 int isu = 0; 511 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 512 for (int i = 0; i < ring.length(); i++) { 513 C v = val.get(i); 514 C w = S.val.get(i); 515 if (v != null && w != null) { 516 RingFactory<C> f = ring.getFactory(i); 517 C x = f.getONE(); 518 elem.put(i, x); 519 isu = 1; 520 } 521 } 522 return new Product<C>(ring, elem, isu); 523 } 524 525 526 /** 527 * Product idempotent or. 528 * @param S Product. 529 * @return this.idempotent() or S.idempotent(). 530 */ 531 public Product<C> idempotentOr(Product<C> S) { 532 if (this.isZERO() && S.isZERO()) { 533 return this; 534 } 535 int isu = 0; 536 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 537 for (int i = 0; i < ring.length(); i++) { 538 C v = val.get(i); 539 C w = S.val.get(i); 540 if (v != null || w != null) { 541 RingFactory<C> f = ring.getFactory(i); 542 C x = f.getONE(); 543 elem.put(i, x); 544 isu = 1; 545 } 546 } 547 return new Product<C>(ring, elem, isu); 548 } 549 550 551 /** 552 * Product fill with idempotent. 553 * @param S Product. 554 * @return fill this with S.idempotent(). 555 */ 556 public Product<C> fillIdempotent(Product<C> S) { 557 if (S.isZERO()) { 558 return this; 559 } 560 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); 561 for (int i = 0; i < ring.length(); i++) { 562 C v = elem.get(i); 563 if (v != null) { 564 continue; 565 } 566 C w = S.val.get(i); 567 if (w != null) { 568 RingFactory<C> f = ring.getFactory(i); 569 C x = f.getONE(); 570 elem.put(i, x); 571 } 572 } 573 return new Product<C>(ring, elem, isunit); 574 } 575 576 577 /** 578 * Product fill with one. 579 * @return fill this with one. 580 */ 581 public Product<C> fillOne() { 582 if (this.isFull()) { 583 return this; 584 } 585 if (this.isZERO()) { 586 return ring.getONE(); 587 } 588 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); 589 for (int i = 0; i < ring.length(); i++) { 590 C v = elem.get(i); 591 if (v != null) { 592 continue; 593 } 594 RingFactory<C> f = ring.getFactory(i); 595 C x = f.getONE(); 596 elem.put(i, x); 597 } 598 return new Product<C>(ring, elem, isunit); 599 } 600 601 602 /** 603 * Product quasi-division. 604 * @param S Product. 605 * @return this/S. 606 */ 607 public Product<C> divide(Product<C> S) { 608 if (S == null) { 609 return ring.getZERO(); 610 } 611 if (S.isZERO()) { 612 return S; 613 } 614 if (this.isZERO()) { 615 return this; 616 } 617 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 618 SortedMap<Integer, C> sel = S.val; 619 for (Map.Entry<Integer, C> me : val.entrySet()) { 620 Integer i = me.getKey(); 621 C y = sel.get(i); 622 if (y != null) { 623 C x = me.getValue(); 624 try { 625 x = x.divide(y); 626 } catch (NotInvertibleException e) { 627 // should not happen any more 628 System.out.println("product divide error: x = " + x + ", y = " + y); 629 // could happen for e.g. ModInteger or AlgebraicNumber 630 x = null; //ring.getFactory(i).getZERO(); 631 } 632 if (x != null && !x.isZERO()) { // can happen 633 elem.put(i, x); 634 } 635 } 636 } 637 return new Product<C>(ring, elem); 638 } 639 640 641 /** 642 * Product quasi-remainder. 643 * @param S Product. 644 * @return this - (this/S)*S. 645 */ 646 public Product<C> remainder(Product<C> S) { 647 if (S == null) { 648 return this; //ring.getZERO(); 649 } 650 if (S.isZERO()) { 651 return this; 652 } 653 if (this.isZERO()) { 654 return this; 655 } 656 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 657 SortedMap<Integer, C> sel = S.val; 658 for (Map.Entry<Integer, C> me : val.entrySet()) { 659 Integer i = me.getKey(); 660 C y = sel.get(i); 661 if (y != null) { 662 C x = me.getValue(); 663 x = x.remainder(y); 664 if (x != null && !x.isZERO()) { // can happen 665 elem.put(i, x); 666 } 667 } 668 } 669 return new Product<C>(ring, elem); 670 } 671 672 673 /** 674 * Quotient and remainder by division of this by S. 675 * @param S a product 676 * @return [this/S, this - (this/S)*S]. 677 */ 678 @SuppressWarnings("unchecked") 679 public Product<C>[] quotientRemainder(Product<C> S) { 680 return new Product[] { divide(S), remainder(S) }; 681 } 682 683 684 /** 685 * Product multiplication. 686 * @param S Product. 687 * @return this*S. 688 */ 689 public Product<C> multiply(Product<C> S) { 690 if (S == null) { 691 return ring.getZERO(); 692 } 693 if (S.isZERO()) { 694 return S; 695 } 696 if (this.isZERO()) { 697 return this; 698 } 699 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 700 SortedMap<Integer, C> sel = S.val; 701 for (Map.Entry<Integer, C> me : val.entrySet()) { 702 Integer i = me.getKey(); 703 C y = sel.get(i); 704 if (y != null) { 705 C x = me.getValue(); 706 x = x.multiply(y); 707 if (x != null && !x.isZERO()) { 708 elem.put(i, x); 709 } 710 } 711 } 712 return new Product<C>(ring, elem); 713 } 714 715 716 /** 717 * Product multiply by coefficient. 718 * @param c coefficient. 719 * @return this*c. 720 */ 721 public Product<C> multiply(C c) { 722 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 723 for (Map.Entry<Integer, C> me : val.entrySet()) { 724 Integer i = me.getKey(); 725 C v = me.getValue().multiply(c); 726 if (v != null && !v.isZERO()) { 727 elem.put(i, v); 728 } 729 } 730 return new Product<C>(ring, elem); 731 } 732 733 734 /** 735 * Greatest common divisor. 736 * @param S other element. 737 * @return gcd(this,S). 738 */ 739 public Product<C> gcd(Product<C> S) { 740 if (S == null || S.isZERO()) { 741 return this; 742 } 743 if (this.isZERO()) { 744 return S; 745 } 746 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone 747 SortedMap<Integer, C> sel = S.val; 748 for (Map.Entry<Integer, C> is : sel.entrySet()) { 749 Integer i = is.getKey(); 750 C x = elem.get(i); 751 C y = is.getValue(); //sel.get( i ); // assert y != null 752 if (x != null) { 753 x = x.gcd(y); 754 if (x != null && !x.isZERO()) { 755 elem.put(i, x); 756 } else { 757 elem.remove(i); 758 } 759 } else { 760 elem.put(i, y); 761 } 762 } 763 return new Product<C>(ring, elem); 764 } 765 766 767 /** 768 * Extended greatest common divisor. 769 * @param S other element. 770 * @return [ gcd(this,S), c1, c2 ] with c1*this + c2*b = gcd(this,S). 771 */ 772 @SuppressWarnings("unchecked") 773 public Product<C>[] egcd(Product<C> S) { 774 Product<C>[] ret = new Product[3]; 775 ret[0] = null; 776 ret[1] = null; 777 ret[2] = null; 778 if (S == null || S.isZERO()) { 779 ret[0] = this; 780 return ret; 781 } 782 if (this.isZERO()) { 783 ret[0] = S; 784 return ret; 785 } 786 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone 787 SortedMap<Integer, C> elem1 = this.idempotent().val; // init with 1 788 SortedMap<Integer, C> elem2 = new TreeMap<Integer, C>(); // zero 789 SortedMap<Integer, C> sel = S.val; 790 for (Map.Entry<Integer, C> is : sel.entrySet()) { 791 Integer i = is.getKey(); 792 C x = elem.get(i); 793 C y = is.getValue(); //sel.get( i ); // assert y != null 794 if (x != null) { 795 C[] g = x.egcd(y); 796 if (!g[0].isZERO()) { 797 elem.put(i, g[0]); 798 elem1.put(i, g[1]); 799 elem2.put(i, g[2]); 800 } else { 801 elem.remove(i); 802 } 803 } else { 804 elem.put(i, y); 805 elem2.put(i, ring.getFactory(i).getONE()); 806 } 807 } 808 ret[0] = new Product<C>(ring, elem); 809 ret[1] = new Product<C>(ring, elem1); 810 ret[2] = new Product<C>(ring, elem2); 811 return ret; 812 } 813 814}