Closed mplekunov closed 2 years ago
Let's start from here for today
So,... The ideas...
When it comes to functions, it's important to understand that all of them are in the format of "f(x)"... From this format we can see that we have function name - "f", function parameter - "x" and two parentheses which enclose function parameter
Those parentheses aren't the same as "Operator Parentheses"... they just denote the beginning and end of the space allocated for function parameter
function parameter can be either a single number or an expression (presumably it can contain another function...)
Ideally, I want to preserve my current algo... That is, I want to still use postfix->result algo....
So, the whole "function to regular expression" transition should happen before postfix reached evaluator algo... Which means I will have to do that in Postfix class?!
Now, after taking a quick look... it looks like I wasn't too tired when I was writing Postfix class lmao... and it looks like it's not going to be too hard to implement functions there... One note, it looks like the best option for "conversion" of some functions would be the recursion...
Like, for example... if we are talking about ln(x)... Single natural log is just a natural log of x... however, when we have ln(ln(x))... this means we will have to process ln(x) first, then return value to the parent ln() and then parent ln returns value in the number format to postfix.
So, currently, I want to implement those functions:
Ln Log Sqrt x! x^2 x^y
I think power is a "function" as I plan to allow usage of power on complex expressions such as (ln (2 + ln(10 * sqrt(3))))^3
Constants are no brainer... They are just constants
As for parentheses (operator parentheses)... I think I can just "add" them to the Operators and everything should work by default as postfix->result algo already have support for parentheses
Just thought about natural log... ln(y) = x <==> log_e (y) = x <==> e^x = y which means I can translate it to the simple expression lol
But no one would want to read my shit mathematics in the code... especially when there is a library function available lmao... So it's better to stick to Kotlin API 🤣
WAIT.... I think I missed something important... If x can be an expression it means I will have to evaluate it as an expression..... Which means I will have to call ExpressionEvaluator.... Inside of the Postfix?!
Ok, on the second thought, even though it looks strange it does align with my initial plan of using recursion....
So now the idea is:
when you encounter function in the expression, find where this function ends... then take that sublist and send it back to the ExpressionEvaluator... Expression Evaluator will either do a recursion until it reaches the point when it can return value or return value immediately ... Now when I receive that value, I use log() to get a result of that value, put it on the place where sublist + function name should have been and evaluate expression further.
Seems like a plan
Sooo... The next several days will be spent on writing test cases and looking at different ways my "parenthesis" implementation can go wrong.... I need to fix all those problems.... Then I will need to implement evaluation of one function with parenthesis (like ln())... After that I need to overhaul my Editing Mode.... From what I can see, dynamically adding spans isn't a good way and it could be better to "respan" the whole input on every change.
After that I will roll out a new version with at least one function and full support of parentheses 😃
Need to remove all rounding everywhere and only add rounding at the final stage... (Maybe inside of token formatter?!)
Otherwise it's hard to have consistent rounding everywhere...
Both parentheses and function ln are "fully" implemented... at least as far as my test cases allowed me to test them.
The next step is to check viewModel class, though I'm pretty sure it's solid and there should not be much to break/change... and after that straight to edit mode.
OH JUST REMEMBERED... I haven't implemented removed logic for functions..... Need to change it so when parenthesis that is closed to function is removed, function is also getting deleted....
Now, about Edit Mode (otherwise I will forget it tomorrow lol):
The main problem is that right now, my edit mode dynamically change specifically Token that is being replaced/deleted/changed... The problem is that I have to have some pretty complex logic which figures out the position of the previous token in the span string, then it replaces it with the new string and stuff like that...
The downsides are obvious... It's pain in the ass when we try to add functions support to it... Functions are not only going to be represented as "images" (hence ImageSpan instead of StringSpan) but they also have weird quirks... like they have a body (denoted by parentheses) which complicates everything quite significantly...
I guess I can simply prohibit user replacing of functions... Like if there is a function, the only thing they will be able to change would be function body...
Or... I can just reconsider what I call a "function"
If I define a function as something that MUST have a format of f(x) then x^y, %, x!, x^2 are all not functions
hence, the only "functions" are sqrt(), log(), and ln()
In fact... if it's only those then I can have "editing mode" for functions because they all have the same format... like f(x)
Now, the question is... what are those things are then (x^y, %, x!, x^2 )
x^2 and x^2 are power operators.... technically.... but they cannot be replaced by your regular operators.... Unless....
x! (factorial) can't be replace by any other operator... it's like a percent sign...
x^2.... It can be replaced by other operators like +/-*....
x^y can technically also be replaced by other operators like +/-*
actually... I think I can differentiate between functions... I still think that % is not an operator... and calling it operator is not really correct... it's more like a function because it has an algo which helps calculating it... Same with x! factorial...
The only difference between those functions and your regular functions (f(x)) is that they have a "single digit" body on the left side...
So... I over-engineered solution to the problem....
In simple terms, the only thing I have to do is... to make it so when I call replaceSpan on my input, I replace all spans with new ones (thus avoiding a chance of messing up functions that have parentheses)...
as for imageSpan... I think it can be done with current structure... Right now, last commit contains fully working implementation of parentheses and ln function... It doesn't have "Editing logic" but it is not going to be hard to implement anyway.
Tomorrow I will need to rewrite all the logic I added because it's messy right now and I added it just for the sake of testing my "vision"...
oh, and for deletion, I just have to test for parenthesis operator... like if the current token to be deleted is an opening parenthesis... then I test if the token BEFORE that parenthesis is a function... If it is, remove both, if it's not remove only parenthesis.
So... It turned out I can attach several spans to the same substring of SpannableStringBuilder object... This means I can attach "drawable" ImageSpan and attach Clickable to int. (Which was done in the last commit in Work_In_Progress)
Stuff to do...
Fix an issue where the whole thing crashed when ln is the first thing in expression (probably input adapter)
Fix an issue where the whole thing crashed when I had 6 + ln ( ( 65 -66)) (e.g. negative inside of ln which should have returned NaN without any crashes)
Fix an issue where u can't delete imagespan without crashing (input adapter problem)
Come up with an efficient way to get "drawables" of functions to both "ExpandedClickableFunction" and "ExpandedInputAdapter".. maybe delegation? Like how u use "by Lazy()" or something like that
and the final...
Add support for replacing functions.... Everything is technically ready, I just need to add logic for another function to test it and write a bit of code in Expanded classes to differentiate between functions with body at the right side and functions with the body at the left side
Can't reproduce first issue...
Second issue was related to ln(0) not negative..
Deletion of imagespan is not crashing anymore... The problem was inside of Expression... I forgot to commit previous hotfix I did yesterday and I reset head today in the morning lol
Tomorrow:
Clean up code... It's a bit of a mess rn.
Implement Log and SquareRoot functions.
Implement Function Editing in Advanced Mode
Before adding new functions.... I want to try redo my spanning... I don't like the fact that I have to do ALL the spanning all over again on every spannable change... that's just stupid... My previous "dynamic" solution was technically O(1) and I want to go back to it... I believe it should be possible with a bit of modification.
Time for new functions...
HOOORAY 🥳 Logarithm, Natural Logarithm, and square root are all now supported
Solved in #33