package jscl.math.polynomial;
import jscl.math.Generic;
import jscl.math.JsclInteger;
import jscl.mathml.MathML;
import javax.annotation.Nonnull;
import java.util.Iterator;
final class GeoBucket extends Polynomial {
final Polynomial factory;
Polynomial content[];
int size;
boolean mutable = true;
boolean canonicalized = true;
GeoBucket(Polynomial factory) {
super(factory.monomialFactory, factory.coefFactory);
this.factory = factory;
}
GeoBucket(int size, Polynomial factory) {
this(factory);
init(size);
}
static int log(int n) {
int i;
for (i = 0; n > 3; n >>= 2) i++;
return i;
}
public int size() {
return size;
}
void init(int size) {
content = new Polynomial[size];
this.size = size;
}
void resize(int size) {
Polynomial content[] = new Polynomial[size];
System.arraycopy(this.content, 0, content, 0, Math.min(this.size, size));
this.content = content;
this.size = size;
}
public Iterator iterator(boolean direction, Monomial current) {
return new ContentIterator(direction, current);
}
Term behead(Term t, int n, int i) {
Monomial m = t.monomial();
Polynomial p = factory.valueOf(m).multiply(t.coef());
content[n] = content[n].subtract(p);
content[i] = content[i].add(p);
return new Term(m, content[i].coefficient(m));
}
void canonicalize() {
Polynomial s = factory.valueOf(JsclInteger.valueOf(0));
int sugar = 0;
for (int i = 0; i < size; i++) {
Polynomial p = content[i];
if (p == null) continue;
s = s.add(p);
sugar = Math.max(sugar, p.sugar());
content[i] = null;
}
resize(log(s.size()) + 1);
set(s.normalize());
canonicalized = true;
setSugar(sugar);
mutable = false;
}
Polynomial polynomial() {
if (canonicalized) return content[size - 1];
else throw new UnsupportedOperationException();
}
void set(Polynomial polynomial) {
content[size - 1] = polynomial;
}
@Nonnull
public Polynomial subtract(@Nonnull Polynomial that) {
if (mutable) {
Polynomial q = ((GeoBucket) that).polynomial();
int n = log(q.size());
if (n >= size) resize(n + 1);
Polynomial p = content[n];
Polynomial s = (p == null ? factory.valueOf(JsclInteger.valueOf(0)) : p).subtract(q);
content[n] = null;
while (n < log(s.size())) {
n++;
if (n >= size) resize(n + 1);
p = content[n];
if (p != null) s = p.add(s);
content[n] = null;
}
content[n] = s;
canonicalized = false;
normalized = false;
return this;
} else return copy().subtract(that);
}
public Polynomial multiplyAndSubtract(Generic generic, Polynomial polynomial) {
if (mutable) {
Polynomial q = ((GeoBucket) polynomial).polynomial();
int n = log(q.size());
if (n >= size) resize(n + 1);
Polynomial p = content[n];
Polynomial s = (p == null ? factory.valueOf(JsclInteger.valueOf(0)) : p).multiplyAndSubtract(generic, q);
content[n] = null;
while (n < log(s.size())) {
n++;
if (n >= size) resize(n + 1);
p = content[n];
if (p != null) s = p.add(s);
content[n] = null;
}
content[n] = s;
canonicalized = false;
normalized = false;
return this;
} else return copy().multiplyAndSubtract(generic, polynomial);
}
public Polynomial multiplyAndSubtract(Monomial monomial, Generic generic, Polynomial polynomial) {
if (mutable) {
Polynomial q = ((GeoBucket) polynomial).polynomial();
int n = log(q.size());
if (n >= size) resize(n + 1);
Polynomial p = content[n];
Polynomial s = (p == null ? factory.valueOf(JsclInteger.valueOf(0)) : p).multiplyAndSubtract(monomial, generic, q);
content[n] = null;
while (n < log(s.size())) {
n++;
if (n >= size) resize(n + 1);
p = content[n];
if (p != null) s = p.add(s);
content[n] = null;
}
content[n] = s;
canonicalized = false;
normalized = false;
return this;
} else return copy().multiplyAndSubtract(monomial, generic, polynomial);
}
public Polynomial multiply(Generic generic) {
if (mutable) {
if (canonicalized) set(polynomial().multiply(generic));
else for (int i = 0; i < size; i++) {
Polynomial p = content[i];
if (p != null) content[i] = p.multiply(generic);
}
normalized = false;
return this;
} else return copy().multiply(generic);
}
public Polynomial multiply(Monomial monomial) {
if (mutable) {
set(polynomial().multiply(monomial));
return this;
} else return copy().multiply(monomial);
}
public Polynomial divide(Generic generic) throws ArithmeticException {
if (mutable) {
if (canonicalized) set(polynomial().divide(generic));
else for (int i = 0; i < size; i++) {
Polynomial p = content[i];
if (p != null) content[i] = p.divide(generic);
}
normalized = false;
return this;
} else return copy().divide(generic);
}
public Polynomial divide(Monomial monomial) throws ArithmeticException {
if (mutable) {
set(polynomial().divide(monomial));
return this;
} else return copy().divide(monomial);
}
public Polynomial gcd(Polynomial polynomial) {
throw new UnsupportedOperationException();
}
public Generic gcd() {
if (field) return coefficient(tail());
return canonicalized ? polynomial().gcd() : coefficient(JsclInteger.valueOf(0));
}
public int degree() {
return polynomial().degree();
}
public Polynomial valueof(GeoBucket bucket) {
return valueOf(bucket.polynomial().copy());
}
public Polynomial valueOf(Polynomial polynomial) {
if (polynomial instanceof GeoBucket) {
return valueof((GeoBucket) polynomial);
} else {
GeoBucket b = new GeoBucket(log(polynomial.size()) + 1, factory);
b.set(polynomial);
return b;
}
}
public Polynomial valueOf(Generic generic) {
return valueOf(factory.valueOf(generic));
}
public Polynomial valueOf(Monomial monomial) {
return valueOf(factory.valueOf(monomial));
}
public Polynomial freeze() {
canonicalize();
return this;
}
public Term head() {
return canonicalized ? polynomial().head() : super.head();
}
public Term tail() {
return canonicalized ? polynomial().tail() : super.tail();
}
public Generic coefficient(Monomial monomial) {
return canonicalized ? polynomial().coefficient(monomial) : super.coefficient(monomial);
}
public int sugar() {
return polynomial().sugar();
}
public int index() {
return polynomial().index();
}
public void setSugar(int n) {
polynomial().setSugar(n);
}
public void setIndex(int n) {
polynomial().setIndex(n);
}
public Generic genericValue() {
return polynomial().genericValue();
}
public Generic[] elements() {
return polynomial().elements();
}
public int compareTo(GeoBucket bucket) {
return polynomial().compareTo(bucket.polynomial());
}
public int compareTo(Polynomial polynomial) {
return compareTo((GeoBucket) polynomial);
}
public String toString() {
if (canonicalized) return polynomial().toString();
else {
StringBuffer buffer = new StringBuffer();
buffer.append("{");
for (int i = 0; i < size; i++) {
Polynomial p = content[i];
buffer.append(p == null ? factory.valueOf(JsclInteger.valueOf(0)) : p).append(i < size - 1 ? ", " : "");
}
buffer.append("}");
return buffer.toString();
}
}
public void toMathML(MathML element, Object data) {
if (canonicalized) polynomial().toMathML(element, data);
else {
MathML e1 = element.element("mfenced");
MathML e2 = element.element("mtable");
for (int i = 0; i < size; i++) {
MathML e3 = element.element("mtr");
MathML e4 = element.element("mtd");
Polynomial p = content[i];
(p == null ? factory.valueOf(JsclInteger.valueOf(0)) : p).toMathML(e4, null);
e3.appendChild(e4);
e2.appendChild(e3);
}
e1.appendChild(e2);
element.appendChild(e1);
}
}
class ContentIterator implements Iterator {
final boolean direction;
Term term;
ContentIterator(boolean direction, Monomial current) {
this.direction = direction;
term = new Term(current, coefficient(JsclInteger.valueOf(0)));
seek();
}
void seek() {
while (true) {
int n = 0;
Term t = null;
for (int i = 0; i < size; i++) {
Polynomial p = content[i];
if (p == null) continue;
Iterator it = p.iterator(direction, term.monomial());
Term u = it.hasNext() ? (Term) it.next() : null;
if (u == null) continue;
if (t == null || (direction ? -1 : 1) * ordering.compare(t.monomial(), u.monomial()) > 0) {
t = u;
n = i;
} else if (ordering.compare(t.monomial(), u.monomial()) == 0) {
t = behead(t, n, i);
n = i;
}
}
if (t == null || t.coef().signum() != 0) {
term = t;
return;
}
}
}
public boolean hasNext() {
return term != null;
}
public Object next() {
Term t = term;
seek();
return t;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}