001/* 002 * $Id: Product.java 5872 2018-07-20 16:01:46Z kredel $ 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.Logger; 014import org.apache.logging.log4j.LogManager; 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 public Product<C>[] quotientRemainder(Product<C> S) { 679 return new Product[] { divide(S), remainder(S) }; 680 } 681 682 683 /** 684 * Product multiplication. 685 * @param S Product. 686 * @return this*S. 687 */ 688 public Product<C> multiply(Product<C> S) { 689 if (S == null) { 690 return ring.getZERO(); 691 } 692 if (S.isZERO()) { 693 return S; 694 } 695 if (this.isZERO()) { 696 return this; 697 } 698 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 699 SortedMap<Integer, C> sel = S.val; 700 for (Map.Entry<Integer,C> me : val.entrySet()) { 701 Integer i = me.getKey(); 702 C y = sel.get(i); 703 if (y != null) { 704 C x = me.getValue(); 705 x = x.multiply(y); 706 if (x != null && !x.isZERO()) { 707 elem.put(i, x); 708 } 709 } 710 } 711 return new Product<C>(ring, elem); 712 } 713 714 715 /** 716 * Product multiply by coefficient. 717 * @param c coefficient. 718 * @return this*c. 719 */ 720 public Product<C> multiply(C c) { 721 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(); 722 for (Map.Entry<Integer,C> me : val.entrySet()) { 723 Integer i = me.getKey(); 724 C v = me.getValue().multiply(c); 725 if (v != null && !v.isZERO()) { 726 elem.put(i, v); 727 } 728 } 729 return new Product<C>(ring, elem); 730 } 731 732 733 /** 734 * Greatest common divisor. 735 * @param S other element. 736 * @return gcd(this,S). 737 */ 738 public Product<C> gcd(Product<C> S) { 739 if (S == null || S.isZERO()) { 740 return this; 741 } 742 if (this.isZERO()) { 743 return S; 744 } 745 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone 746 SortedMap<Integer, C> sel = S.val; 747 for (Map.Entry<Integer, C> is : sel.entrySet()) { 748 Integer i = is.getKey(); 749 C x = elem.get(i); 750 C y = is.getValue(); //sel.get( i ); // assert y != null 751 if (x != null) { 752 x = x.gcd(y); 753 if (x != null && !x.isZERO()) { 754 elem.put(i, x); 755 } else { 756 elem.remove(i); 757 } 758 } else { 759 elem.put(i, y); 760 } 761 } 762 return new Product<C>(ring, elem); 763 } 764 765 766 /** 767 * Extended greatest common divisor. 768 * @param S other element. 769 * @return [ gcd(this,S), c1, c2 ] with c1*this + c2*b = gcd(this,S). 770 */ 771 @SuppressWarnings("cast") 772 public Product<C>[] egcd(Product<C> S) { 773 Product<C>[] ret = (Product<C>[]) new Product[3]; 774 ret[0] = null; 775 ret[1] = null; 776 ret[2] = null; 777 if (S == null || S.isZERO()) { 778 ret[0] = this; 779 return ret; 780 } 781 if (this.isZERO()) { 782 ret[0] = S; 783 return ret; 784 } 785 SortedMap<Integer, C> elem = new TreeMap<Integer, C>(val); // clone 786 SortedMap<Integer, C> elem1 = this.idempotent().val; // init with 1 787 SortedMap<Integer, C> elem2 = new TreeMap<Integer, C>(); // zero 788 SortedMap<Integer, C> sel = S.val; 789 for (Map.Entry<Integer, C> is : sel.entrySet()) { 790 Integer i = is.getKey(); 791 C x = elem.get(i); 792 C y = is.getValue(); //sel.get( i ); // assert y != null 793 if (x != null) { 794 C[] g = x.egcd(y); 795 if (!g[0].isZERO()) { 796 elem.put(i, g[0]); 797 elem1.put(i, g[1]); 798 elem2.put(i, g[2]); 799 } else { 800 elem.remove(i); 801 } 802 } else { 803 elem.put(i, y); 804 elem2.put(i, ring.getFactory(i).getONE()); 805 } 806 } 807 ret[0] = new Product<C>(ring, elem); 808 ret[1] = new Product<C>(ring, elem1); 809 ret[2] = new Product<C>(ring, elem2); 810 return ret; 811 } 812 813}