Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameter expressions #3

Open
AmityWilder opened this issue Nov 26, 2023 · 2 comments
Open

Parameter expressions #3

AmityWilder opened this issue Nov 26, 2023 · 2 comments
Assignees
Labels
complexity is dependent on libs Complexity depends on factors belonging to one or more dependencies complexity: 3 - high Could take multiple days and likely requires planning ahead enhancement New feature or request priority: 2 - medium Should probably do this sooner rather than later

Comments

@AmityWilder
Copy link
Owner

Parameter expressions

A highly useful feature currently implemented in AlgebraBalancer is the ability to write expressions in the parameter inputs, which are evaluated before being passed to calculations.
image
In AlgebraBalancer, this feature is safe because it uses DataTable, which evaluates specifically math and not code. I'm unsure whether a similar feature exists in Node, and I definitely want to avoid giving the user access to potentially dangerous options like eval().

In order to enforce the desired safety, it may be necessary to write a custom math evaluation function which sanitizes inputs before passing them to Function().

The sanitized inputs might be checked by matching them against a regex pattern. There could also be a second pattern (breaking them up to simplify the overall pattern) for checking that parentheses are balanced, if any are detected in the pattern.

// Tests whether the expression uses only whitelisted operations, and is therefore safe to pass to Function()
const isMathExpressionSafe = (expr: string): boolean => {
    // Any instance of open or close parentheses
    const parentheses = /\(|\)/g;
    // The string is safe if it:
    // 1. Has at least one integer
    //   1.1. Which may be preceded with a + or -
    // 2. Optionally followed by exactly one of the following:
    //   a)
    //     2a1. One of the following operations:
    //       - Exponentiation
    //     2a2. Which, if an operation exists, must always be followed by another integer
    //       2a2.1. Which may be preceded with a +
    //   b)
    //     2b1. One of the following operations:
    //       - Multiplication
    //       - Addition
    //       - Subtraction
    //     2b2. Which, if an operation exists, must always be followed by another integer
    //       2b2.1. Which may be preceded with a + or -
    // 3. Repeating section 2 for as many operations as are necessary
    const whitelistedMath = /^[+-]?[0-9]+(?:(?:\^\+?[0-9]+)|(?:[*+-](?:[+-]?[0-9]+)))*$/;
    let isExpressionValid = expr.replace(parentheses, '').match(whitelistedMath);
    
    if (expr.includes(parentheses)) {
        // For the cases where parentheses are encountered, they must either
        // A) Contain no instance of parentheses within them
        // B) Contain only parentheses which also follow this pattern, recursively
        const balancedParentheses = /\((?:[^)(]+|(?R))*+\)/;
        isExpressionValid &&= expr.match(balancedParentheses);
    }

    return isExpressionValid;
}

While exponents in parameter expressions are not supported in AlgebraBalancer, I believe they should be in this project considering the evaluator will be custom and can be made to map operations to their respective functions.

Negative exponents in parameter expressions should be disallowed. At the time of writing, it is an unwritten guideline for this project that parameters may not contain fractions nor radicals; only integers and math which simplifies to integers.

Division is not currently an expectation of the feature. Testing to confirm that the result doesn't resolve to a fraction nor undefined may be more effort than it is worth. It would also be frustrating to users for rule-following expressions to simply not evaluate sometimes.

@AmityWilder AmityWilder added the enhancement New feature or request label Nov 26, 2023
@AmityWilder AmityWilder self-assigned this Nov 26, 2023
@AmityWilder AmityWilder moved this to Todo in Amity GX Mod Nov 26, 2023
@AmityWilder
Copy link
Owner Author

Problem 1: Exponent expressions

If the feature is implemented such that sub-expressions are valid arguments for exponent powers, it is possible that a user may pass an expression which, while not preceded with a minus, still evaluates to a negative.

For example: $5^{(6-8)}$, which resolves to $5^{-2}$ or $\frac{1}{25}$.
This clearly results in a fraction, which goes against the unwritten guidelines.

While disallowing exponents to contain parentheses may work, it might also reduce the overall utility of the application.

Problem 2: Inconsistent application of rules between fractions and exponents

The very fact that exponents would be permitted in parameter expressions creates some inconsistency in rules. Whether they are allowed or not, their problems mirror those of fractions.

Specifically, consider the arguments against inclusion of fractions in parameters:

  • Relatively few fractions evaluate to clean integers.

    Examples:

    • $\frac{1}{3}$
    • $\frac{4}{8}$ (can be simplified to $\frac{1}{2}$, but no further)
    • $\pi$

    Counterexamples:

    • $\frac{3}{3} \rightarrow 1$
    • $\frac{10}{2} \rightarrow 5$
    • $\frac{\tau}{\pi} \rightarrow 2$
  • Most functionalities of the calculator explode in complexity when required to accept fractional parameters.

    Examples:

    • $\frac{4}{5}+\frac{7}{12}$
      while not requiring an unreasonable increase in complexity, still requires far more complexity (and opportunity for mistakes) than limiting to integer calculations like $2+6$.
    • $\frac{19}{3}\mod\frac{6}{7}$
      Fractional modulo is already somewhat difficult (for me) to define, and I have had trouble in previous attempts to do so. It is made even more difficult by needing to also present the result as a fraction, considering built-in tools like fmod() can only help with floating point math.
    • GCF of $(\frac{9}{14}$, $\frac{8}{19})$
      The definition of GCF actually has a different meaning when applied to fractions instead of integers, enough so that they may require an entirely separate function to calculate. This would mean that every function that calls GCF for performing their calculations may also need to be rewritten to support both fractional and integral math.
  • The very ability for parameters to be fractions would mean that fractions may need to be entirely refactored to support recursive or tree patterns in the numerators and denominators just to calculate a meaningful result.

  • Catching erroneous fractions such as $\frac{n}{0}$ requires testing the user's input, rather than preventing it ahead of time.

And then consider the arguments regarding inclusion of exponents in parameters:

  • Some exponents, but not all, cannot evaluate to clean integers.

    Examples:

    • $5^{-2}$ (coincidentally evaluates to a fraction which does not simplify to an integer: $\frac{1}{25}$)
    • $3^{\frac{1}{2}}$ (cannot be simplified beyond $\sqrt{3}$)

    Counterexamples:

    • $1^{-2}$ (simplifies to the integer $1$)
    • $4^{\frac{1}{2}}$ (simplifies to the integer $2$)
  • Exponents very quickly balloon in value to numbers expressed in scientific notation

    Examples:

    • $10^{12} = 1\mathrm{e}{+12}$
    • $25^{9} = 3.8146973\mathrm{e}{+12}$
  • If negative exponents are allowed (which is implicit if exponent expressions are added as a feature), catching erroneous exponents such as $0^{-2}$ requires testing the user's input, rather than preventing it ahead of time.

While exponents create fewer overall issues than fractions, they have the potential to directly create fractions, while simultaneously sharing (arguably) the most important reason against including fractional parameters.

@AmityWilder
Copy link
Owner Author

Problem 3: Parentheses in parameter expressions

While I am certain that parentheses in parameter expressions would be extremely useful, there are some reasons I think they should not be added.

  1. The JS-screwery parentheses could allow might be potentially dangerous, likely requiring a custom parser itself (which I really don't wanna have to write myself). Unpredictable inputs like ((()())+()(-())())()(*) may, for all I know, open users up to unsafe edge cases; or errors at the very least.

  2. Testing for validity would be excessively complex, considering cases like $(5*)+8$ which are balanced, but syntactically invalid. And persistent input lag is almost certainly a greater concern for most users than needing to copy and paste the results of their sub-expressions into the notes section.

  3. Inputs may not be able to proactively prevent invalid strings, as an open parenthesis may simply be awaiting its closing partner.

    • It is possible this could be remedied by creating both parentheses simultaneously when the user types an open parenthesis ((). However, this creates a problem when the user wants to delete the parentheses, in that the program may not be 100% accurate in selecting what parenthesis corresponds to the one being deleted.

      Not an impossible problem to solve, but if Visual Studio and Visual Studio Code themselves have trouble deleting matching parentheses pairs, it's probably safe to assume it shouldn't underestimated, and there may be more complexity to this problem than is visible at a glance. Then again, maybe it's not difficulty, but rather a mere lack of implementation.

@AmityWilder AmityWilder added complexity: 2 - medium May take an afternoon and probably some researching complexity: 3 - high Could take multiple days and likely requires planning ahead priority: 2 - medium Should probably do this sooner rather than later complexity is foggy Issue contains factors I am not experienced with; I cannot accurately predict the true complexity complexity is dependent on libs Complexity depends on factors belonging to one or more dependencies and removed complexity is foggy Issue contains factors I am not experienced with; I cannot accurately predict the true complexity labels Nov 26, 2023
@AmityWilder AmityWilder added type: quality of life and removed category: fundamental complexity: 2 - medium May take an afternoon and probably some researching labels Nov 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
complexity is dependent on libs Complexity depends on factors belonging to one or more dependencies complexity: 3 - high Could take multiple days and likely requires planning ahead enhancement New feature or request priority: 2 - medium Should probably do this sooner rather than later
Projects
Status: Todo
Development

No branches or pull requests

1 participant