package mho.qbar.objects;

import mho.wheels.math.MathUtils;
import org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.List;

import static mho.wheels.iterables.IterableUtils.*;
import static mho.wheels.testing.Testing.assertTrue;

public class Monomial implements Comparable<Monomial> {
    private static final @NotNull Monomial CONSTANT = new Monomial(Collections.emptyList());

    private final @NotNull List<Integer> variables;

    private Monomial(@NotNull List<Integer> variables) {
        this.variables = variables;
    }

    public static @NotNull Monomial of(@NotNull List<Integer> variables) {
        if (any(v -> v == null, variables)) {
            throw new NullPointerException();
        }
        if (any(v -> v < 0, variables)) {
            throw new IllegalArgumentException();
        }
        int actualSize;
        for (actualSize = variables.size(); actualSize > 0; actualSize--) {
            if (variables.get(actualSize - 1) != 0) {
                break;
            }
        }
        if (actualSize == 0) return CONSTANT;
        return new Monomial(toList(variables));
    }

    public int degree() {
        return sumInteger(variables);
    }

    @Override
    public boolean equals(Object that) {
        return this == that ||
                that != null && getClass() == that.getClass() && variables.equals(((Monomial) that).variables);

    }

    @Override
    public int hashCode() {
        return variables.hashCode();
    }

    //grevlex
    @Override
    public int compareTo(@NotNull Monomial that) {
        if (this == that) return 0;
        int thisDegree = degree();
        int thatDegree = that.degree();
        if (thisDegree > thatDegree) return 1;
        if (thisDegree < thatDegree) return -1;
        int size = that.variables.size();
        if (variables.size() > size) return -1;
        if (variables.size() < size) return 1;
        for (int i = size - 1; i >= 0; i--) {
            int thisVariable = variables.get(i);
            int thatVariable = that.variables.get(i);
            if (thisVariable > thatVariable) return -1;
            if (thisVariable < thatVariable) return 1;
        }
        return 0;
    }

    public @NotNull String toString() {
        if (this == CONSTANT) return "constant";
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (int i = 0; i < variables.size(); i++) {
            int exponent = variables.get(i);
            if (exponent != 0) {
                if (first) {
                    first = false;
                } else {
                    sb.append('*');
                }
                sb.append(MathUtils.variableIndexToString(i));
                if (exponent > 1) {
                    sb.append('^').append(exponent);
                }
            }
        }
        return sb.toString();
    }

    /**
     * Ensures that {@code this} is valid. Must return true for any {@code Polynomial} used outside this class.
     */
    public void validate() {
        assertTrue(this, all(v -> v >= 0, variables));
        if (!variables.isEmpty()) {
            assertTrue(this, last(variables) != 0);
        }
        if (equals(CONSTANT)) {
            assertTrue(this, this == CONSTANT);
        }
    }
}

@@ -448,24 +448,24 @@
 nteger%3E 
-variable
+exponent
 s;%0A%0A    
@@ -500,32 +500,32 @@
 st%3CInteger%3E 
-variable
+exponent
 s) %7B%0A       
@@ -534,28 +534,28 @@
 his.
-variables = variable
+exponents = exponent
 s;%0A 
@@ -622,24 +622,24 @@
 nteger%3E 
-variable
+exponent
 s) %7B%0A   
@@ -667,24 +667,24 @@
 = null, 
-variable
+exponent
 s)) %7B%0A  
@@ -765,24 +765,24 @@
  v %3C 0, 
-variable
+exponent
 s)) %7B%0A  
@@ -889,24 +889,24 @@
 lSize = 
-variable
+exponent
 s.size()
@@ -955,24 +955,24 @@
     if (
-variable
+exponent
 s.get(ac
@@ -1121,24 +1121,24 @@
 (toList(
-variable
+exponent
 s));%0A   
@@ -1193,24 +1193,24 @@
 Integer(
-variable
+exponent
 s);%0A    
@@ -1363,24 +1363,24 @@
 ss() && 
-variable
+exponent
 s.equals
@@ -1398,24 +1398,24 @@
 ) that).
-variable
+exponent
 s);%0A%0A   
@@ -1475,24 +1475,24 @@
  return 
-variable
+exponent
 s.hashCo
@@ -1813,24 +1813,24 @@
  = that.
-variable
+exponent
 s.size()
@@ -1839,32 +1839,32 @@
         if (
-variable
+exponent
 s.size() %3E s
@@ -1891,24 +1891,24 @@
     if (
-variable
+exponent
 s.size()
@@ -1996,27 +1996,27 @@
 this
-Variable = variable
+Exponent = exponent
 s.ge
@@ -2045,32 +2045,32 @@
 that
-Variable = that.variable
+Exponent = that.exponent
 s.ge
@@ -2099,31 +2099,31 @@
 this
-Variable %3E thatVariable
+Exponent %3E thatExponent
 ) re
@@ -2155,31 +2155,31 @@
 this
-Variable %3C thatVariable
+Exponent %3C thatExponent
 ) re
@@ -2416,24 +2416,24 @@
  0; i %3C 
-variable
+exponent
 s.size()
@@ -2468,24 +2468,24 @@
 onent = 
-variable
+exponent
 s.get(i)
@@ -3092,24 +3092,24 @@
 v %3E= 0, 
-variable
+exponent
 s));%0A   
@@ -3118,24 +3118,24 @@
    if (!
-variable
+exponent
 s.isEmpt
@@ -3175,24 +3175,24 @@
 s, last(
-variable
+exponent
 s) != 0)

rename variables to exponents