#include <quiz.h>
#include "helpers.h"

QUIZ_CASE(equation_solve) {
  assert_solves_to_error("x+y+z+a+b+c+d=0", TooManyVariables);
  assert_solves_to_error("x^2+y=0", NonLinearSystem);
  assert_solves_to_error("cos(x)=0", RequireApproximateSolution);

  assert_solves_to_no_solution("2=0");
  assert_solves_to_no_solution("x-x+2=0");

  assert_solves_to_infinite_solutions("0=0");
  assert_solves_to_infinite_solutions("x-x=0");

  assert_solves_to("2x+3=4", "x=1/2");
  assert_solves_to("3×x^2-4x+4=2", {"x=2/3-(√(2)/3)𝐢", "x=2/3+(√(2)/3)𝐢", "delta=-8"});
  assert_solves_to("2×x^2-4×x+4=3", {"x=(-√(2)+2)/2", "x=(√(2)+2)/2", "delta=8"});
  assert_solves_to("2×x^2-4×x+2=0", {"x=1", "delta=0"});
  assert_solves_to(
    "x^2+x+1=3×x^2+π×x-√(5)",
    {
      "x=(√(π^2-2π+8√(5)+9)-π+1)/4",
      "x=(-√(π^2-2π+8×√(5)+9)-π+1)/4",
      "delta=π^2-2π+8√(5)+9"
    }
  );
  assert_solves_to("(x-3)^2=0", {"x=3", "delta=0"});

  /* TODO: Cubic
   * x^3-4x^2+6x-24=0
   * x^3+x^2+1=0
   * x^3-3x-2=0 */

  // Linear System
  assert_solves_to_infinite_solutions("x+y=0");
  assert_solves_to({"x+y=0", "3x+y=-5"}, {"x=-5/2", "y=5/2"});
  assert_solves_to(
    {
      "x+y=0",
      "3x+y+z=-5",
      "4z-π=0"
    },
    {
      "x=(-π-20)/8",
      "y=(π+20)/8",
      "z=π/4"
    }
  );
  assert_solves_to(
    {
      "x+y=0",
      "3x+y+z=-5",
      "4z-π=0",
      "a+b+c=0",
      "a=3",
      "c=a+2"
    },
    {
      "x=(-π-20)/8",
      "y=(π+20)/8",
      "z=π/4",
      "a=3",
      "b=-8",
      "c=5"
    }
  );

  /* This test case needs the user defined variable. Indeed, in the equation
   * store, m_variables is just before m_userVariables, so bad fetching in
   * m_variables might fetch into m_userVariables and create problems. */
  set("x", "0");
  assert_solves_to_infinite_solutions({
    "b=0",
    "D=0",
    "e=0",
    "x+y+z+t=0"
  });
  unset("x");

  // Monovariable non-polynomial equation
  Poincare::Preferences::sharedPreferences()->setAngleUnit(Degree);
  assert_solves_numerically_to("cos(x)=0", -100, 100, {-90.0, 90.0});
  assert_solves_numerically_to("cos(x)=0", -900, 1000, {-810.0, -630.0, -450.0, -270.0, -90.0, 90.0, 270.0, 450.0, 630.0, 810.0});
  assert_solves_numerically_to("√(y)=0", -900, 1000, {0}, "y");
  assert_solves_numerically_to("ℯ^x=0", -1000, 1000, {});
  assert_solves_numerically_to("ℯ^x/1000=0", -1000, 1000, {});

  // Long variable names
  assert_solves_to("2abcde+3=4", "abcde=1/2");
  assert_solves_to({"Big1+Big2=0", "3Big1+Big2=-5"}, {"Big1=-5/2", "Big2=5/2"});

  // conj(x)*x+1 = 0
  assert_solves_to_error("conj(x)*x+1=0", RequireApproximateSolution);
  assert_solves_numerically_to("conj(x)*x+1=0", -100, 100, {});

  assert_solves_to_error("(x-10)^7=0", RequireApproximateSolution);
  assert_solves_numerically_to("(x-10)^7=0", -100, 100, {10});
}


QUIZ_CASE(equation_solve_complex_real) {
  set_complex_format(Real);
  assert_solves_to("x+𝐢=0", "x=-𝐢"); // We still want complex solutions if the input has some complex value
  assert_solves_to_error("x+√(-1)=0", EquationUnreal);
  assert_solves_to("x^2+x+1=0", {"delta=-3"});
  assert_solves_to_error("x^2-√(-1)=0", EquationUnreal);
  assert_solves_to_error("x+√(-1)×√(-1)=0", EquationUnreal);
  assert_solves_to("root(-8,3)*x+3=0", "x=3/2");
  reset_complex_format();
}

QUIZ_CASE(equation_solve_complex_cartesian) {
  set_complex_format(Cartesian);
  assert_solves_to("x+𝐢=0", "x=-𝐢");
  assert_solves_to("x+√(-1)=0", "x=-𝐢");
  assert_solves_to({"x^2+x+1=0"}, {"x=-1/2-((√(3))/2)𝐢", "x=-1/2+((√(3))/2)𝐢", "delta=-3"});
  assert_solves_to("x^2-√(-1)=0", {"x=-√(2)/2-(√(2)/2)𝐢", "x=√(2)/2+(√(2)/2)𝐢", "delta=4𝐢"});
  assert_solves_to("x+√(-1)×√(-1)=0", "x=1");
  assert_solves_to("root(-8,3)*x+3=0", "x=-3/4+(3√(3)/4)*𝐢");
  reset_complex_format();
}

QUIZ_CASE(equation_solve_complex_polar) {
  set_complex_format(Polar);
  assert_solves_to("x+𝐢=0", "x=ℯ^(-(π/2)𝐢)");
  assert_solves_to("x+√(-1)=0", "x=ℯ^(-(π/2)𝐢)");
  assert_solves_to("x^2+x+1=0", {"x=ℯ^(-(2π/3)𝐢)", "x=ℯ^((2π/3)𝐢)", "delta=3ℯ^(π𝐢)"});
  assert_solves_to("x^2-√(-1)=0", {"x=ℯ^(-(3π/4)𝐢)", "x=ℯ^((π/4)𝐢)", "delta=4ℯ^((π/2)𝐢)"});
  assert_solves_to("root(-8,3)*x+3=0", "x=3/2×ℯ^((2π/3)𝐢)");
  reset_complex_format();
}

QUIZ_CASE(equation_and_symbolic_computation) {
  assert_solves_to_infinite_solutions("x+a=0");

  set("a", "-3");
  assert_solves_to("x+a=0", "x=3");

  assert_solves_to("a=0", "a=0");
  /* The equation has no solution since the user defined a = -3. So a is not
   * replaced with its context value, and the solution is a = 0. */

  set("b", "-4");
  assert_solves_to_infinite_solutions("a+b=0");
  /* The equation has no solution since the user defined a = -3 and b = -4.
   * So neither a nor b are replaced with their context values. Therefore the
   * solution is an infinity of solutions. */

  assert_solves_to("a+b+c=0", "c=7");

  assert_solves_to({"a+c=0", "a=3"}, {"a=3", "c=-3"});
  /* The system has no solution since the user defined a = -3. So a is not
   * replaced with its context value, and the solution is a = 3 and c = -3. */

  set("f(x)", "x+1");

  assert_solves_to("f(x)=0", "x=-1");

  assert_solves_to("f(a)=0", "a=-1");
  /* The equation has no solution since the user defined a = -3. So a is not
   * replaced with its context value, and the solution is a = -1. */

  set("g(x)", "a+x+2");

  assert_solves_to("g(x)=0", "x=1");

  assert_solves_to("g(a)=0", "a=-1");
  /* The equation has no solution since the user defined a = -3. So a is not
   * replaced with its context value, and the equation becomes a + a + 2 = 0.
   * The solution is therefore a = -1. */

  set("d", "5");
  set("c", "d");
  set("h(x)", "c+d+3");
  assert_solves_to({"h(x)=0", "c=-3"}, {"c=-3", "d=0"});
  // c and d context values should not be used

  assert_solves_to({"c+d=5", "c-d=1"}, {"c=3", "d=2"});

  set("e", "8_g");
  assert_solves_to({"e+1=0"}, {"e=-1"});

  unset("a");
  unset("b");
  unset("c");
  unset("d");
  unset("e");
  unset("f");
  unset("g");
  unset("h");
}
