Guest User

C++ code for algebraic parser

a guest
Feb 26th, 2023
208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 36.43 KB | Source Code | 0 0
  1. #pragma once
  2.  
  3. #include "basic_math_operations.h" // https://github.com/avighnac/basic_math_operations/blob/main/src/library/basic_math_operations.h
  4. #include "gcd.hpp"
  5. #include "lcm.hpp"
  6. #include <string>
  7. #include <algorithm>
  8. #include <map>
  9. #include <vector>
  10.  
  11. class rnumber {
  12. public:
  13.   std::string number;
  14.   size_t division_accuracy = 20;
  15.  
  16.   rnumber(std::string s) { number = s; }
  17.   rnumber(const char *s) { number = s; }
  18.   rnumber() {}
  19.  
  20.   std::string to_string() { return number; }
  21.  
  22.   void operator=(std::string s) { number = s; }
  23.   void operator=(rnumber n2) { number = n2.number; }
  24.   bool operator==(std::string s) { return number == s; }
  25.   bool operator==(rnumber n2) { return number == n2.number; }
  26.   friend std::ostream &operator<<(std::ostream &os, const rnumber n);
  27.  
  28.   rnumber operator+(rnumber n) {
  29.     rnumber answer;
  30.     char *buf =
  31.         (char *)calloc(std::max(number.length(), n.number.length()) + 2, 1);
  32.     add(n.number.c_str(), number.c_str(), buf);
  33.     answer.number = buf;
  34.     return answer;
  35.   }
  36.   rnumber operator-(rnumber n) {
  37.     rnumber answer;
  38.     char *buf =
  39.         (char *)calloc(std::max(number.length(), n.number.length()) + 2, 1);
  40.     subtract(number.c_str(), n.number.c_str(), buf);
  41.     answer.number = buf;
  42.     return answer;
  43.   }
  44.   rnumber operator*(rnumber n) {
  45.     rnumber answer;
  46.     char *buf = (char *)calloc(number.length() + n.number.length() + 2, 1);
  47.     multiply(number.c_str(), n.number.c_str(), buf);
  48.     answer.number = buf;
  49.     return answer;
  50.   }
  51.   rnumber operator/(rnumber n) {
  52.     rnumber answer;
  53.     char *buf =
  54.         (char *)calloc(std::max(number.length(), n.number.length()) +
  55.                            std::max(division_accuracy, n.division_accuracy) + 3,
  56.                        1);
  57.     divide(number.c_str(), n.number.c_str(), buf,
  58.            std::max(division_accuracy, n.division_accuracy));
  59.     answer.number = buf;
  60.     return answer;
  61.   }
  62.   rnumber operator%(rnumber n) {
  63.     rnumber answer;
  64.     char *buf =
  65.         (char *)calloc(std::max(number.length(), n.number.length()) + 2, 1);
  66.     char *rem =
  67.         (char *)calloc(std::max(number.length(), n.number.length()) + 2, 1);
  68.     divide_whole_with_remainder(number.c_str(), n.number.c_str(), buf, rem);
  69.     answer.number = rem;
  70.     return answer;
  71.   }
  72. };
  73.  
  74. std::ostream &operator<<(std::ostream &os, const rnumber n) {
  75.   os << n.number;
  76.   return os;
  77. }
  78.  
  79. class rfraction {
  80. public:
  81.   rnumber numerator, denominator;
  82.  
  83.   rfraction simplify_fraction(rfraction frac) {
  84.     bool negative = false;
  85.     if (frac.numerator.number[0] == '-' && frac.denominator.number[0] == '-') {
  86.       frac.numerator.number =
  87.           frac.numerator.number.substr(1, frac.numerator.number.length());
  88.       frac.denominator.number =
  89.           frac.denominator.number.substr(1, frac.denominator.number.length());
  90.     }
  91.     if (frac.numerator.number[0] == '-' && frac.denominator.number[0] != '-') {
  92.       negative = true;
  93.       frac.numerator.number =
  94.           frac.numerator.number.substr(1, frac.numerator.number.length());
  95.     }
  96.     if (frac.numerator.number[0] != '-' && frac.denominator.number[0] == '-') {
  97.       negative = true;
  98.       frac.denominator.number =
  99.           frac.denominator.number.substr(1, frac.denominator.number.length());
  100.     }
  101.  
  102.     rnumber GCD = gcd(frac.numerator, frac.denominator);
  103.     size_t t1 = frac.numerator.division_accuracy,
  104.            t2 = frac.denominator.division_accuracy;
  105.     frac.numerator.division_accuracy = 0;
  106.     frac.denominator.division_accuracy = 0;
  107.     frac.numerator = frac.numerator / GCD;
  108.     frac.denominator = frac.denominator / GCD;
  109.     frac.numerator.division_accuracy = t1;
  110.     frac.denominator.division_accuracy = t2;
  111.  
  112.     if (negative)
  113.       frac.numerator.number = "-" + frac.numerator.number;
  114.     return frac;
  115.   }
  116.  
  117.   rfraction(std::pair<std::string, std::string> p) {
  118.     numerator = p.first;
  119.     denominator = p.second;
  120.   }
  121.   rfraction(const char *s) {
  122.     std::string str = std::string(s);
  123.     if (str.find('\\') == std::string::npos) {
  124.       if (str.find('/') == std::string::npos) {
  125.         numerator = str;
  126.         denominator = std::string("1");
  127.       } else {
  128.         numerator = str.substr(0, str.find('/'));
  129.         denominator = str.substr(str.find('/') + 1, str.length());
  130.       }
  131.     } else if (str.find("\\frac{") != std::string::npos) {
  132.       numerator = str.substr(str.find("\\frac{") + 6,
  133.                              str.find('}') - str.find("\\frac{") - 6);
  134.       denominator = str.substr(str.find("}{") + 2, str.length());
  135.       denominator.number =
  136.           denominator.number.substr(0, denominator.number.length() - 1);
  137.     } else {
  138.       numerator = str;
  139.       denominator = std::string("1");
  140.     }
  141.   }
  142.   rfraction(rnumber r1, rnumber r2) {
  143.     numerator = r1.number;
  144.     denominator = r2.number;
  145.   }
  146.  
  147.   rfraction() {}
  148.  
  149.   std::string latex() {
  150.     if (denominator.number != "1")
  151.       return "\\frac{" + numerator.to_string() + "}{" +
  152.              denominator.to_string() + "}";
  153.     else
  154.       return numerator.number;
  155.   }
  156.  
  157.   std::string to_string() {
  158.     return numerator.to_string() + "/" + denominator.to_string();
  159.   }
  160.  
  161.   void operator=(std::pair<std::string, std::string> p) {
  162.     numerator = p.first;
  163.     denominator = p.second;
  164.   }
  165.   void operator=(std::string str) {
  166.     if (str.find('\\') == std::string::npos) {
  167.       if (str.find('/') == std::string::npos) {
  168.         numerator = str;
  169.         denominator = std::string("1");
  170.       } else {
  171.         numerator = str.substr(0, str.find('/'));
  172.         denominator = str.substr(str.find('/') + 1, str.length());
  173.       }
  174.     } else if (str.find("\\frac{") != std::string::npos) {
  175.       numerator = str.substr(str.find("\\frac{") + 6,
  176.                              str.find('}') - str.find("\\frac{") - 6);
  177.       denominator = str.substr(str.find("}{") + 2, str.length());
  178.       denominator.number =
  179.           denominator.number.substr(0, denominator.number.length() - 1);
  180.     } else {
  181.       numerator = str;
  182.       denominator = std::string("1");
  183.     }
  184.   }
  185.   void operator=(const char *s) {
  186.     std::string str = std::string(s);
  187.     operator=(str);
  188.   }
  189.   void operator=(std::pair<rnumber, rnumber> p) {
  190.     numerator = p.first.number;
  191.     denominator = p.second.number;
  192.   }
  193.   bool operator==(rfraction f2) {
  194.     rfraction new_1 = simplify_fraction(*this);
  195.     rfraction new_2 = simplify_fraction(f2);
  196.     return new_1.numerator == new_2.numerator &&
  197.            new_1.denominator == new_2.denominator;
  198.   }
  199.   friend std::ostream &operator<<(std::ostream &os, const rfraction f);
  200.   rfraction operator+(rfraction f2) {
  201.     rfraction answer = {(numerator * f2.denominator) +
  202.                             (denominator * f2.numerator),
  203.                         denominator * f2.denominator};
  204.     return simplify_fraction(answer);
  205.   }
  206.   rfraction operator-(rfraction f2) {
  207.     rfraction answer = {(numerator * f2.denominator) -
  208.                             (denominator * f2.numerator),
  209.                         denominator * f2.denominator};
  210.     return simplify_fraction(answer);
  211.   }
  212.   rfraction operator*(rfraction f2) {
  213.     rfraction answer = {numerator * f2.numerator, denominator * f2.denominator};
  214.     return simplify_fraction(answer);
  215.   }
  216.   rfraction operator/(rfraction f2) {
  217.     std::swap(f2.denominator, f2.numerator);
  218.     rfraction answer = *this * f2;
  219.     return simplify_fraction(answer);
  220.   }
  221. };
  222.  
  223. std::ostream &operator<<(std::ostream &os, const rfraction f) {
  224.   os << f.numerator.number;
  225.   if (f.denominator.number != "1")
  226.     os << std::string("/") << f.denominator.number;
  227.   return os;
  228. }
  229. class variable {
  230. public:
  231.   std::string var;
  232.   rfraction power;
  233.   bool constant = false; // for stuff like sqrt(2)
  234.   bool function = false; // for sin(), log(), arctan(), etc.
  235.  
  236.   std::string functionName;
  237.   std::string functionValue;
  238.  
  239.   variable(std::string v, rfraction p) {
  240.     var = v;
  241.     power = p;
  242.   }
  243.   variable(const char *cstr) {
  244.     std::string s = std::string(cstr);
  245.     if (s.find('^') != std::string::npos) {
  246.       var = s.substr(0, s.find('^'));
  247.       std::string rest = s.substr(s.find('^') + 1, s.length());
  248.       if (!rest.empty() && rest[0] == '(')
  249.         power = rest.substr(1, rest.length() - 2);
  250.       else
  251.         power = rest;
  252.     } else {
  253.       var = s;
  254.       power = "1";
  255.     }
  256.   }
  257.   variable() {}
  258.  
  259.   bool operator==(variable v2) { return var == v2.var && power == v2.power; }
  260. };
  261.  
  262. static std::vector<std::string> split_string(std::string s, char ch) {
  263.   std::vector<std::string> answer;
  264.   std::string part;
  265.   for (auto &i : s) {
  266.     if (i != ch)
  267.       part.push_back(i);
  268.     else {
  269.       answer.push_back(part);
  270.       part.clear();
  271.     }
  272.   }
  273.   answer.push_back(part);
  274.   return answer;
  275. }
  276.  
  277. static void replace_all(std::string &str, const std::string &from,
  278.                         const std::string &to) {
  279.   if (from.empty())
  280.     return;
  281.   size_t start_pos = 0;
  282.   while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
  283.     str.replace(start_pos, from.length(), to);
  284.     start_pos += to.length();
  285.   }
  286. }
  287.  
  288. static bool is_letter(char ch) {
  289.   return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');
  290. }
  291.  
  292. static size_t get_matching_brace(std::string str, size_t index) {
  293.   if (str[index] != '(')
  294.     return -1;
  295.   int count = 0;
  296.   for (size_t i = index; i < str.length(); i++) {
  297.     if (str[i] == '(')
  298.       count++;
  299.     if (str[i] == ')')
  300.       count--;
  301.  
  302.     if (!count)
  303.       return i;
  304.   }
  305.   return -1;
  306. }
  307.  
  308. class algnum {
  309. private:
  310.   static size_t get_matching_open_brace(std::string str, size_t index) {
  311.     if (str[index] != ')')
  312.       return -1;
  313.     int count = 0;
  314.     for (size_t i = index; i + 1 > 0; i--) {
  315.       if (str[i] == '(')
  316.         count--;
  317.       if (str[i] == ')')
  318.         count++;
  319.  
  320.       if (!count)
  321.         return i;
  322.     }
  323.     return -1;
  324.   }
  325.   static bool
  326.   check_for_functions_behind(std::string input, size_t start_pos,
  327.                              std::vector<std::string> &supportedFunctions) {
  328.     for (auto &func : supportedFunctions) {
  329.       if ((long long)start_pos - (long long)func.length() >= 0) {
  330.         if (input.substr(start_pos - func.length(), func.length()) == func) {
  331.           return true;
  332.         }
  333.       }
  334.     }
  335.     return false;
  336.   }
  337.   static size_t prev_index(std::string s, size_t start) {
  338.     if (start == 0)
  339.       return 0;
  340.     for (auto i = start - 1; i + 1 > 0; i--)
  341.       if (s[i] != ' ')
  342.         return i;
  343.     return 0;
  344.   }
  345.   static std::string process_brackets(std::string in, size_t exp_loc,
  346.                                       size_t &i) {
  347.     if (exp_loc + 1 >= in.length())
  348.       return "";
  349.     // if we have brackets
  350.     if (in[exp_loc + 1] == '(') {
  351.       size_t first_brace_location = exp_loc + 1;
  352.       size_t matching_brace = get_matching_brace(in, first_brace_location);
  353.       i = matching_brace;
  354.       return in.substr(first_brace_location + 1,
  355.                        matching_brace - first_brace_location - 1);
  356.     } else {
  357.       if (is_letter(in[exp_loc + 1]))
  358.         return in.substr(exp_loc + 1, 1);
  359.       std::string answer;
  360.       for (auto j = exp_loc + 1; j < in.length(); j++) {
  361.         if (!('0' <= in[j] && in[j] <= '9')) {
  362.           i = j - 1;
  363.           break;
  364.         }
  365.         answer.push_back(in[j]);
  366.       }
  367.       return answer;
  368.     }
  369.   }
  370.  
  371. public:
  372.   rfraction constant;
  373.   std::vector<variable> variables;
  374.  
  375.   void add_variable(variable v) {
  376.     for (auto &i : variables) {
  377.       if (i.var == v.var) {
  378.         i.power = i.power + v.power;
  379.         return;
  380.       }
  381.     }
  382.     variables.push_back(v);
  383.   }
  384.  
  385.   std::string latex() {
  386.     if (constant.numerator.number == "0")
  387.       return "";
  388.     std::string answer;
  389.     if (constant.numerator.number != constant.denominator.number) {
  390.       if (constant.numerator.number == "-1")
  391.         answer += "-";
  392.       else
  393.         answer += constant.latex();
  394.       if (!variables.empty()) {
  395.         if (!(variables[0].power.denominator.number == "1" ||
  396.               (variables[0].power.numerator.number == "1" &&
  397.                variables[0].power.denominator.number != "1")))
  398.           answer += "\\times";
  399.       }
  400.     }
  401.     for (auto &var : variables) {
  402.       if (var.function) {
  403.         answer += "\\" + var.functionName;
  404.         if (var.power.numerator.number != var.power.denominator.number) {
  405.           if (var.power.denominator.number == "1" &&
  406.               var.power.numerator.number[0] != '-')
  407.             answer += "^" + var.power.latex();
  408.           else
  409.             answer += "^{" + var.power.latex() + "}";
  410.         }
  411.         answer += "{" + var.functionValue + "}";
  412.       } else {
  413.         if (var.power.numerator.number != "1") {
  414.           if (var.var.length() > 1)
  415.             answer += "{" + var.var + "}";
  416.           else
  417.             answer += var.var;
  418.  
  419.           if (var.power.numerator.number != var.power.denominator.number) {
  420.             if (var.power.denominator.number == "1" &&
  421.                 var.power.numerator.number[0] != '-')
  422.               answer += "^" + var.power.latex();
  423.             else
  424.               answer += "^{" + var.power.latex() + "}";
  425.           }
  426.         } else {
  427.           if (var.power.denominator.number == "1")
  428.             answer += var.var;
  429.           else {
  430.             if (var.power.denominator.number == "2")
  431.               answer += "\\sqrt{";
  432.             else
  433.               answer += "\\sqrt[" + var.power.denominator.number + "]{";
  434.  
  435.             answer += var.var + "}";
  436.           }
  437.         }
  438.       }
  439.  
  440.       answer += " ";
  441.     }
  442.  
  443.     if (!variables.empty())
  444.       answer.pop_back();
  445.  
  446.     return answer;
  447.   }
  448.  
  449.   algnum(const char *s) {
  450.     std::string input = std::string(s);
  451.  
  452.     /*
  453.     Functional requirements:
  454.  
  455.       1. Identify any brackets ('{}', '()', '[]')
  456.  
  457.       2. Detect multiplication indicated by either (x)(y), (x)y, x(y), x*y, or
  458.       just xy.
  459.  
  460.       3. Detect some special functions such as sqrt(), cbrt(), sin(), cos(),
  461.       tan(), sec(), cot(), csc() / cosec(), all trig inverse functions such as
  462.       sin^(-1)() which is the same thing as arcsin() which shouldn't be mistaken
  463.       for multiplication, ln(), log_b() should be identified as log to the base
  464.       b, log() (assume base e).
  465.  
  466.       4. Detect constants such as 7^(2/5) and assign them as a variable (so they
  467.       only get added with other like constants).
  468.  
  469.       5. Detect rational constants in the middle of variables (such as x^2(2)y,
  470.       where 2 is a constant which should be multiplied w/ the already existing
  471.       constant part).
  472.  
  473.       6. Detect like terms that have already been made (such as x^2yx, should
  474.       detect the repeated x and not make repeated variables.
  475.  
  476.       7. sqrt^2(x), cbrt^2(x), and other obscure function notations should be
  477.       supported.
  478.  
  479.       Note: tanx should be parsed as tan(x), tanx+2 should be parsed as tan(x) +
  480.       2, tanx2 should be parsed as 2*tan(x) but tan2x should be parsed as
  481.       tan(2x).
  482.     */
  483.  
  484.     // Deal with alternative brackets (i.e. anything other than '()')
  485.     replace_all(input, "{", "(");
  486.     replace_all(input, "}", ")");
  487.     replace_all(input, "[", "(");
  488.     replace_all(input, "]", ")");
  489.  
  490.     // cosec and csc mean the same thing. Also, ln and log without an explicit
  491.     // base also mean the same thing.
  492.     replace_all(input, "cosec", "csc");
  493.     replace_all(input, "ln", "log");
  494.  
  495.     // Deal with inverse trig. functions
  496.     for (std::string &i :
  497.          std::vector<std::string>{"sin", "cos", "tan", "csc", "sec", "cot"}) {
  498.       replace_all(input, i + "^(-1)", "arc" + i);
  499.       replace_all(input, i + "h^(-1)", "arc" + i + "h"); // Hyperbolic
  500.     }
  501.  
  502.     // I'm also choosing to ignore all empty brackets (i.e. '()').
  503.     replace_all(input, "()", "");
  504.  
  505.     // List of supported functions
  506.     std::vector<std::string> supportedFunctions = {"sqrt", "cbrt", "log"};
  507.     for (std::string &i :
  508.          std::vector<std::string>{"sin", "cos", "tan", "csc", "sec", "cot"}) {
  509.       supportedFunctions.push_back(i);
  510.       supportedFunctions.push_back(i + "h");
  511.       supportedFunctions.push_back("arc" + i);
  512.       supportedFunctions.push_back("arc" + i + "h");
  513.     }
  514.  
  515.     // Now, let's eliminate the problem of variables followed by '-' such as
  516.     // '-y'
  517.     for (auto i = 0; i < input.length() - 1; i++) {
  518.       if (input[i] == '-' && is_letter(input[i + 1])) {
  519.         input = "-1" + input.substr(1, input.length());
  520.       }
  521.     }
  522.  
  523.     // To deal with the second condition, we can eliminate all brackets that
  524.     // don't enclose the parameters of a function
  525.     size_t start_pos = 0;
  526.     while ((start_pos = input.find('(', start_pos)) != std::string::npos) {
  527.       if (start_pos == 0) {
  528.         input[get_matching_brace(input, 0)] = ' ';
  529.         input[0] = ' ';
  530.         start_pos++;
  531.         continue;
  532.       }
  533.       size_t index_prev_non_space = prev_index(input, start_pos);
  534.       bool can_remove_brackets = input[index_prev_non_space] != '^';
  535.       if (can_remove_brackets) { // only perform this additional
  536.         // check if first one is true
  537.         // check_for_functions() returns true if a function immediately preceeds
  538.         // the bracket start_pos is an index to
  539.         can_remove_brackets =
  540.             !check_for_functions_behind(input, start_pos, supportedFunctions);
  541.       }
  542.       // finally, if it's still true, perform a check for functions' weird power
  543.       // notations i.e. how tan(x)^2 is written as tan^2(x) or x^(2/3) *can* be
  544.       // written as cbrt^2(x)
  545.       if (can_remove_brackets) {
  546.         // this variable contains the index of the first non-space character
  547.         // or 0 if there are no non space characters
  548.         if (start_pos != 0 && input[index_prev_non_space] == ')') {
  549.           size_t matching_brace_pos =
  550.               get_matching_open_brace(input, index_prev_non_space);
  551.           if (input[prev_index(input, matching_brace_pos)] == '_') {
  552.             can_remove_brackets = false;
  553.           }
  554.           if (input[prev_index(input, matching_brace_pos)] == '^') {
  555.             can_remove_brackets = !check_for_functions_behind(
  556.                 input, prev_index(input, matching_brace_pos),
  557.                 supportedFunctions);
  558.           }
  559.         } else {
  560.           // not ')', check gets a little more complicated
  561.           bool prev_enc_let = false, prev_enc_num = false;
  562.           bool encountering_number = false, encountered_number = false,
  563.                encountered_letter = false;
  564.           for (size_t i = index_prev_non_space; i + 1 > 0; i--) {
  565.             // if two adjacent characters are letters and numbers and stricly
  566.             // that (i.e. num w/ num or letter w/ letter won't work), then no
  567.             // function can possibly be inserted with reference to the bracket
  568.             // pair we're considering, so it's safe to replace it with spaces.
  569.             bool is_current_let = is_letter(input[i]),
  570.                  is_current_num = '0' <= input[i] && input[i] <= '9';
  571.             if ((prev_enc_let && is_current_num) ||
  572.                 (prev_enc_num && is_current_let))
  573.               break;
  574.  
  575.             if (is_current_num)
  576.               encountering_number = true;
  577.             if (is_current_let) {
  578.               encountered_letter = true;
  579.               if (encountering_number) {
  580.                 encountering_number = false;
  581.                 encountered_number = true;
  582.               }
  583.             }
  584.  
  585.             if (!(encountered_letter && encountered_number)) {
  586.               // can_remove_brackets depends on whether the thing before the
  587.               // exponent is a function in the case of an exponent. you can't
  588.               // remove the brackets in the case of an underscore
  589.               if (input[i] == '_') {
  590.                 can_remove_brackets = false;
  591.                 break;
  592.               }
  593.               if (input[i] == '^') {
  594.                 can_remove_brackets =
  595.                     !check_for_functions_behind(input, i, supportedFunctions);
  596.                 if (!can_remove_brackets)
  597.                   break;
  598.               }
  599.             }
  600.  
  601.             prev_enc_let = is_current_let;
  602.             prev_enc_num = is_current_num;
  603.           }
  604.         }
  605.       }
  606.  
  607.       if (can_remove_brackets) {
  608.         input[get_matching_brace(input, start_pos)] =
  609.             ' '; // separate it into parts
  610.         input[start_pos] = ' ';
  611.       }
  612.  
  613.       start_pos++;
  614.       if (start_pos >= input.length() - 1)
  615.         break;
  616.     }
  617.  
  618.     replace_all(input, "*", " "); // for 2.
  619.  
  620.     // Now, remove all subsequent spaces, for example "  hell  o " -> " hell o "
  621.     while (input.find("  ") != std::string::npos)
  622.       replace_all(input, "  ", " ");
  623.  
  624.     // Deal with each part of the input individually.
  625.     constant = "1"; // since no constant (i.e. x^2y) implies the constant is 1
  626.     auto temp = split_string(input, ' ');
  627.     for (auto &in : temp) {
  628.       for (size_t i = 0; i < in.length(); i++) {
  629.         // check for functions first
  630.         bool functionExists = false;
  631.         std::string functionName;
  632.         for (auto &func : supportedFunctions) {
  633.           if (in.substr(i, func.length()) == func) {
  634.             functionExists = true;
  635.             functionName = func;
  636.             break;
  637.           }
  638.         }
  639.  
  640.         if (functionExists) {
  641.           std::string varparam;
  642.           rfraction varpower = "1"; // default power
  643.  
  644.           // deal with functions
  645.           // first, let's deal with the obscure function power notation
  646.           if (in[i + functionName.length()] == '^') {
  647.             size_t powerLocation = i + functionName.length();
  648.             // case with braces is really easy
  649.             if (in[powerLocation + 1] == '(') {
  650.               size_t matching_brace = get_matching_brace(in, powerLocation + 1);
  651.               varpower = in.substr(powerLocation + 2,
  652.                                    matching_brace - powerLocation - 2);
  653.               size_t next_matching_brace =
  654.                   get_matching_brace(in, matching_brace + 1);
  655.               varparam = in.substr(matching_brace + 2,
  656.                                    get_matching_brace(in, matching_brace + 1) -
  657.                                        matching_brace - 2);
  658.               i = next_matching_brace;
  659.             } else {
  660.               // this case is also not too bad, we just look ahead for the first
  661.               // '(', that's where our function's parameter is, and will be
  662.               // where the power part ends
  663.               size_t param_brac_begin = in.find('(', i);
  664.               size_t matching_brace = get_matching_brace(in, param_brac_begin);
  665.               varpower = in.substr(powerLocation + 1,
  666.                                    param_brac_begin - powerLocation - 1);
  667.               varparam = in.substr(param_brac_begin + 1,
  668.                                    matching_brace - param_brac_begin - 1);
  669.               i = matching_brace;
  670.             }
  671.           } else {
  672.             // we don't need to set the power, default is already 1
  673.             size_t bracket_location = in.find('(', i);
  674.             size_t matching_brace = get_matching_brace(in, bracket_location);
  675.             varparam = in.substr(bracket_location + 1,
  676.                                  matching_brace - bracket_location - 1);
  677.             i = matching_brace;
  678.           }
  679.  
  680.           // special functions
  681.           // if varparam doesn't contain any letters, it's a constant
  682.           bool contains_letters = false;
  683.           for (auto &i : varparam) {
  684.             if (is_letter(i)) {
  685.               contains_letters = true;
  686.               break;
  687.             }
  688.           }
  689.           if (functionName == "sqrt") {
  690.             varpower = varpower * "1/2";
  691.             variable v = variable(varparam, varpower);
  692.             v.constant = !contains_letters;
  693.             add_variable(v);
  694.           } else if (functionName == "cbrt") {
  695.             varpower = varpower * "1/3";
  696.             variable v = variable(varparam, varpower);
  697.             v.constant = !contains_letters;
  698.             add_variable(v);
  699.           } else {
  700.             variable v =
  701.                 variable(functionName + "(" + varparam + ")", varpower);
  702.             v.function = true;
  703.             v.functionName = functionName;
  704.             v.functionValue = varparam;
  705.             add_variable(v);
  706.           }
  707.         }
  708.  
  709.         else if (is_letter(in[i])) {
  710.           std::string varname;
  711.           rfraction varpower = "1";
  712.  
  713.           // we've come across a variable
  714.           size_t variable_begin = i;
  715.           // a variable can either have only a single letter, or a letter
  716.           // followed by a subscript '_' with the subscript either having or not
  717.           // having brackets around it
  718.  
  719.           // first, let's deal with the subscript variables
  720.           // currently, this code breaks if spaces are used with the
  721.           // subscript variables
  722.           if (variable_begin + 1 < in.length() &&
  723.               in[variable_begin + 1] == '_') {
  724.             size_t subscript_location = variable_begin + 1;
  725.  
  726.             // if the subscript is enclosed by brackets
  727.             if (subscript_location + 1 < in.length() &&
  728.                 in[subscript_location + 1] == '(') {
  729.               size_t first_brace_location = subscript_location + 1;
  730.               size_t matching_brace =
  731.                   get_matching_brace(in, first_brace_location);
  732.               varname = in.substr(i, matching_brace - i + 1);
  733.               i = matching_brace + 1;
  734.             } else {
  735.               // the subscript is not enclosed in brackets
  736.               // if the first character of the subscript is a letter, we know
  737.               // that that's where the variable name ends
  738.               if (subscript_location + 1 < in.length() &&
  739.                   is_letter(subscript_location + 1)) {
  740.                 varname = in.substr(i, subscript_location - i + 2);
  741.                 i = subscript_location + 2;
  742.               } else {
  743.                 // keep going ahead till a non-numerical character is
  744.                 // encountered
  745.                 varname = in.substr(i, 2);
  746.                 for (auto j = subscript_location + 1; j < in.length(); j++) {
  747.                   if (!('0' <= in[j] && in[j] <= '9')) {
  748.                     i = j;
  749.                     break;
  750.                   }
  751.                   varname.push_back(in[j]);
  752.                 }
  753.               }
  754.             }
  755.           } else {
  756.             // this means we don't have a subscript variable, the variable here
  757.             // is just this singular letter
  758.             varname = in.substr(i, 1);
  759.             i++;
  760.           }
  761.  
  762.           // now, let's deal with the variables' powers
  763.           // a variable only has a power if the current character (since we
  764.           // incremented i while setting the variable's name) is '^'
  765.           if (i < in.length() && in[i] == '^') {
  766.             size_t powerLocation = i;
  767.             // check for brackets
  768.             if (powerLocation + 1 < in.length() &&
  769.                 in[powerLocation + 1] == '(') {
  770.               size_t first_brace_location = powerLocation + 1;
  771.               size_t matching_brace =
  772.                   get_matching_brace(in, first_brace_location);
  773.  
  774.               varpower = in.substr(first_brace_location + 1,
  775.                                    matching_brace - first_brace_location - 1);
  776.               i = matching_brace;
  777.             } else {
  778.               // we don't have brackets
  779.               // again, only a letter means we're done and that letter is the
  780.               // power
  781.               if (powerLocation + 1 < in.length() &&
  782.                   is_letter(in[powerLocation + 1])) {
  783.                 varpower = in.substr(powerLocation + 1, 1);
  784.               } else {
  785.                 std::string temp;
  786.                 // keep going ahead till we encounter the next non-numerical
  787.                 // character
  788.                 bool did_break = false;
  789.                 for (auto j = powerLocation + 1; j < in.length(); j++) {
  790.                   if (!('0' <= in[j] && in[j] <= '9')) {
  791.                     i = j - 1; // since it's going to be incremented when it
  792.                     // loops we have to sub 1
  793.                     did_break = true;
  794.                     break;
  795.                   }
  796.                   temp.push_back(in[j]);
  797.                 }
  798.                 varpower = temp;
  799.                 if (!did_break)
  800.                   i = powerLocation + temp.length();
  801.               }
  802.             }
  803.           } else {
  804.             if (varname.length() == 1)
  805.               i--; // since we incremented previously for a single letter
  806.                    // variable
  807.           }
  808.  
  809.           add_variable(variable(varname, varpower));
  810.         }
  811.         /*
  812.         221^(1/3), this is only a variable if it's raised to a power. keep
  813.         checking forward, if you encounter a letter then it's a constant. if you
  814.         encounter ^ before a letter, it's a variable.
  815.         */
  816.         else if (('0' <= in[i] && in[i] <= '9') || in[i] == '-') {
  817.           std::string num;
  818.           // this could either be a constant or a power raised variable
  819.           // (221^(1/3))
  820.           bool did_break = false;
  821.           for (auto j = i; j < in.length(); j++) {
  822.             if (is_letter(in[j])) {
  823.               // is a constant
  824.               i = j - 1;
  825.               constant = constant * rfraction(num.c_str());
  826.               did_break = true;
  827.               break;
  828.             }
  829.             if (in[j] == '^') {
  830.               // is a variable
  831.               std::string power = process_brackets(in, j, i);
  832.               variable v = variable(num, rfraction(power.c_str()));
  833.               v.constant = true;
  834.               add_variable(v);
  835.               did_break = true;
  836.               break;
  837.             }
  838.             num.push_back(in[j]);
  839.           }
  840.           if (!did_break) {
  841.             constant = constant * rfraction(num.c_str());
  842.             i += num.length() - 1;
  843.           }
  844.         }
  845.       }
  846.     }
  847.  
  848.     // Finally, simplify the constants which have mixed fraction powers
  849.     std::vector<variable> newVariables;
  850.     for (auto i = 0; i < variables.size(); i++) {
  851.       variable &var = variables[i];
  852.       if (var.constant) {
  853.         if ((var.power.numerator - var.power.denominator).to_string()[0] !=
  854.             '-') {
  855.           // if the numerator is greater than or equal to the denominator
  856.           size_t temp1 = var.power.numerator.division_accuracy,
  857.                  temp2 = var.power.denominator.division_accuracy;
  858.           var.power.numerator.division_accuracy = 0;
  859.           var.power.denominator.division_accuracy = 0;
  860.           rnumber integerPart = var.power.numerator / var.power.denominator;
  861.           var.power.numerator.division_accuracy = temp1;
  862.           var.power.denominator.division_accuracy = temp2;
  863.           rfraction multiplier = "1";
  864.           rfraction toMultiplyWith = rfraction(var.var.c_str());
  865.           var.power = var.power - rfraction(integerPart, rnumber("1"));
  866.           if (var.power.numerator.number != "0")
  867.             newVariables.push_back(var);
  868.           while (integerPart.number != "0") {
  869.             multiplier = multiplier * toMultiplyWith;
  870.             integerPart = integerPart - "1";
  871.           }
  872.           constant = constant * multiplier;
  873.         } else
  874.           newVariables.push_back(var);
  875.       } else
  876.         newVariables.push_back(var);
  877.     }
  878.  
  879.     // Remove the raised to 0 variables.
  880.     variables = newVariables;
  881.   }
  882.   algnum() {}
  883.  
  884.   algnum operator+(algnum a2) {
  885.     // this function only works if both are like
  886.     // it doesn't check this and adds them anyway, be warned.
  887.  
  888.     algnum answer;
  889.     answer.constant = constant + a2.constant;
  890.     answer.variables = variables;
  891.  
  892.     return answer;
  893.   }
  894.  
  895.   algnum operator*(algnum a2) {
  896.     algnum answer;
  897.     answer.constant = constant * a2.constant;
  898.     answer.variables = variables;
  899.  
  900.     for (auto i = 0; i < a2.variables.size(); i++) {
  901.       long long index = -1;
  902.       for (auto j = 0; j < answer.variables.size(); j++) {
  903.         if (answer.variables[j].var == a2.variables[i].var) {
  904.           index = j;
  905.           break;
  906.         }
  907.       }
  908.       if (index == -1) {
  909.         answer.variables.push_back(a2.variables[i]);
  910.       } else {
  911.         answer.variables[index].power =
  912.             answer.variables[index].power + a2.variables[i].power;
  913.       }
  914.     }
  915.  
  916.     return answer;
  917.   }
  918.   friend std::ostream &operator<<(std::ostream &os, const algnum n);
  919. };
  920.  
  921. std::ostream &operator<<(std::ostream &os, const algnum n) {
  922.   if (n.variables.empty())
  923.     os << n.constant;
  924.   if (n.constant.numerator.number != n.constant.denominator.number) {
  925.     if (!n.variables.empty())
  926.       os << std::string(" ");
  927.   }
  928.   for (auto i = 0; i < n.variables.size(); i++) {
  929.     os << n.variables[i].var;
  930.     if (n.variables[i].power.numerator.number !=
  931.         n.variables[i].power.denominator.number) {
  932.       os << std::string("^");
  933.       if (n.variables[i].power.denominator.number != "1") {
  934.         os << std::string("(") << n.variables[i].power << std::string(")");
  935.       } else {
  936.         os << n.variables[i].power;
  937.       }
  938.     }
  939.     if (i != n.variables.size() - 1)
  940.       os << std::string(" ");
  941.   }
  942.   return os;
  943. }
  944.  
  945. bool is_like(algnum a, algnum b) {
  946.   for (auto i = 0; i < a.variables.size(); i++) {
  947.     bool found = false;
  948.     for (auto j = 0; j < b.variables.size(); j++) {
  949.       if (a.variables[i] == b.variables[j]) {
  950.         found = true;
  951.         break;
  952.       }
  953.     }
  954.     if (!found)
  955.       return false;
  956.   }
  957.   return true;
  958. }
  959. class algexpr {
  960. private:
  961.   static void clean_double_signs(std::string &expression) {
  962.     while ((expression.find("--") != std::string::npos) ||
  963.            (expression.find("++") != std::string::npos) ||
  964.            (expression.find("-+") != std::string::npos) ||
  965.            (expression.find("+-") != std::string::npos)) {
  966.       replace_all(expression, "--", "+");
  967.       replace_all(expression, "-+", "-");
  968.       replace_all(expression, "+-", "-");
  969.       replace_all(expression, "++", "+");
  970.     }
  971.     replace_all(expression, "*+", "*");
  972.     replace_all(expression, "/+", "/");
  973.   }
  974.  
  975. public:
  976.   std::vector<algnum> expr;
  977.  
  978.   algexpr(const char *s) {
  979.     std::string input = std::string(s);
  980.     clean_double_signs(input);
  981.  
  982.     replace_all(input, "-", "+-");
  983.  
  984.     std::vector<std::string> numbers = split_string(input, '+');
  985.     for (auto &i : numbers)
  986.       expr.emplace_back(i.c_str());
  987.   }
  988.   algexpr() {}
  989.  
  990.   std::string latex() {
  991.     std::string answer;
  992.     for (auto i = 0; i < expr.size(); i++) {
  993.       answer += expr[i].latex();
  994.       if (i + 1 < expr.size()) {
  995.         std::string temp = expr[i + 1].latex();
  996.         if (!temp.empty() && temp[0] != '-')
  997.           answer += "+";
  998.       }
  999.     }
  1000.     return answer;
  1001.   }
  1002.  
  1003.   algnum element(size_t index) { return expr[index]; }
  1004.   size_t size() { return expr.size(); }
  1005.   void insert(algnum n) { expr.push_back(n); }
  1006.  
  1007.   algexpr combine_like_terms(algexpr e) {
  1008.     algexpr answer;
  1009.     std::vector<size_t> added;
  1010.     for (auto i = 0; i < e.size(); i++) {
  1011.       if (std::find(added.begin(), added.end(), i) == added.end()) {
  1012.         algnum temp = e.element(i);
  1013.         for (auto j = i + 1; j < e.size(); j++) {
  1014.           if (is_like(temp, e.element(j))) {
  1015.             if (std::find(added.begin(), added.end(), j) == added.end()) {
  1016.               temp = temp + e.element(j);
  1017.               added.push_back(j);
  1018.             }
  1019.           }
  1020.         }
  1021.         answer.insert(temp);
  1022.       }
  1023.     }
  1024.     return answer;
  1025.   }
  1026.  
  1027.   algexpr operator+(algexpr e2) {
  1028.     algexpr answer;
  1029.     answer.expr = expr;
  1030.  
  1031.     for (auto i = 0; i < e2.size(); i++) {
  1032.       answer.insert(e2.element(i));
  1033.     }
  1034.     return combine_like_terms(answer);
  1035.   }
  1036.   algexpr operator*(algexpr e2) {
  1037.     algexpr answer;
  1038.     for (auto i = 0; i < e2.size(); i++) {
  1039.       for (auto j = 0; j < expr.size(); j++) {
  1040.         answer.insert(e2.element(i) * expr[j]);
  1041.       }
  1042.     }
  1043.  
  1044.     return combine_like_terms(answer);
  1045.   }
  1046.  
  1047.   friend std::ostream &operator<<(std::ostream &os, const algexpr n);
  1048. };
  1049.  
  1050. std::ostream &operator<<(std::ostream &os, const algexpr n) {
  1051.   std::string temp;
  1052.   if (n.expr.empty())
  1053.     return os;
  1054.   for (auto i = 0; i < n.expr.size(); i++) {
  1055.     if (n.expr[i].constant.numerator.number[0] != '-')
  1056.       os << n.expr[i];
  1057.     else {
  1058.       algnum n1;
  1059.       n1.constant = n.expr[i].constant.numerator.number.substr(
  1060.           1, n.expr[i].constant.numerator.number.length());
  1061.       n1.variables = n.expr[i].variables;
  1062.       os << n1;
  1063.     }
  1064.     if (i + 1 < n.expr.size()) {
  1065.       if (n.expr[i + 1].constant.numerator.number[0] != '-')
  1066.         os << std::string(" + ");
  1067.       else
  1068.         os << std::string(" - ");
  1069.     }
  1070.   }
  1071.   return os;
  1072. }
Advertisement
Add Comment
Please, Sign In to add comment