From 5a670d6cae41d77280a98936f2baddf31f069656 Mon Sep 17 00:00:00 2001 From: A404M Date: Wed, 18 Dec 2024 11:15:23 +0330 Subject: fixed bugs in multiple operators updated launcher icon --- .../main/java/com/a404m/calculator/MathHelper.kt | 309 -------------------- .../com/a404m/calculator/model/CalcButtonModel.kt | 2 +- .../java/com/a404m/calculator/util/MathHelper.kt | 314 +++++++++++++++++++++ 3 files changed, 315 insertions(+), 310 deletions(-) delete mode 100644 app/src/main/java/com/a404m/calculator/MathHelper.kt create mode 100644 app/src/main/java/com/a404m/calculator/util/MathHelper.kt (limited to 'app/src/main/java') diff --git a/app/src/main/java/com/a404m/calculator/MathHelper.kt b/app/src/main/java/com/a404m/calculator/MathHelper.kt deleted file mode 100644 index fa6cfc7..0000000 --- a/app/src/main/java/com/a404m/calculator/MathHelper.kt +++ /dev/null @@ -1,309 +0,0 @@ -package com.a404m.calculator - -import android.util.Log -import com.a404m.calculator.MathHelper.Operator.Kind -import java.nio.charset.UnsupportedCharsetException - -object MathHelper { - private val operatorStrings = arrayOf( - '+', - '-', - '×', - '÷', - '%', - ) - private val numberStrings = arrayOf( - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '.', - ) - - private val plus = Operator( - '+', - Kind.PREFIX, - { - Operand(it.first().value) - } - ) - private val minus = Operator( - '-', - Kind.PREFIX, - { - Operand(-it.first().value) - } - ) - private val add = Operator( - '+', - Kind.INFIX, - { - Operand(it.first().value+it.last().value) - } - ) - private val sub = Operator( - '-', - Kind.INFIX, - { - Operand(it.first().value-it.last().value) - } - ) - private val mul = Operator( - '×', - Kind.INFIX, - { - Operand(it.first().value*it.last().value) - } - ) - private val div = Operator( - '÷', - Kind.INFIX, - { - Operand(it.first().value/it.last().value) - } - ) - private val rem = Operator( - '%', - Kind.INFIX, - { - Operand(it.first().value%it.last().value) - } - ) - private val operatorOrders = arrayOf( - arrayOf( - plus, - minus, - ), - arrayOf( - mul, - div, - rem, - ), - arrayOf( - add, - sub, - ) - ) - private val operators = arrayOf( - plus, - minus, - add, - sub, - mul, - div, - rem, - ) - - fun eval(expression: String): String { - return try { - parse(lex(expression)).eval().toString() - } catch (e: Exception) { - e.message ?: "Exception" - } - } - - private fun parse(lexed: ArrayList): Operator { - for (order in operatorOrders) { - if (lexed.size == 1) { - break - } - for (i in lexed.indices) { - val item = lexed[i] - if (item is BasicOperator) { - var op = item.toOperator( - lexed, - i - ) - if (op in order) { - op = op.cloneWithoutOperands() - Log.d( - "A404M", - "parse: $op" - ) - lexed[i] = op - when (op.kind) { - Kind.NONE -> {} - Kind.INFIX -> { - op.operands.add(lexed.removeAt(i - 1)) - op.operands.add(lexed.removeAt(i)) - } - - Kind.PREFIX -> { - op.operands.add(lexed.removeAt(i + 1)) - } - - Kind.POSTFIX -> { - op.operands.add(lexed.removeAt(i - 1)) - } - } - break - } - } - } - } - return if (lexed.size != 1) { - throw UnsupportedOperationException("lexed.size = ${lexed.size}: $lexed") - } else if (lexed.first() is Operator) { - lexed.first() as Operator - } else if (lexed.first() is Operand) { - plus.cloneWithoutOperands().cloneWithOperator(arrayListOf(lexed.first())) - } else { - throw UnsupportedOperationException("unsupported type ${lexed.first().javaClass}") - } - } - - private fun lex(expression: String): ArrayList { - val items = arrayListOf() - - var i = 0 - while (i < expression.length) { - when (val c = expression[i]) { - in numberStrings -> { - val start = i - while (++i < expression.length && expression[i] in numberStrings); - items.add( - Operand( - value = expression.substring( - start, - i - ).toDouble() - ) - ) - --i - } - - in operatorStrings -> { - items.add( - BasicOperator(c) - ) - } - - else -> throw UnsupportedCharsetException(c.toString()) - } - ++i - } - - return items - } - - private class Operator( - val operator: Char, - val kind: Kind, - val operate: (operands:List)->Operand, - val operands: ArrayList = arrayListOf(), - ) { - fun cloneWithoutOperands():Operator{ - return Operator( - operator, - kind, - operate, - ) - } - - fun cloneWithOperator(operands: ArrayList): Operator { - return Operator( - operator, - kind, - operate, - operands, - ) - } - - fun eval(): Operand { - Log.d( - "A404M", - "eval: $operator $operands" - ) - if(operandSize() != operands.size){ - throw UnsupportedOperationException("Not enough operands") - } - - val evaledOperands = operands.map { - if(it is Operator){ - it.eval() - }else{ - it as Operand - } - } - - return operate(evaledOperands) - } - - fun operandSize(): Int { - return when (kind) { - Kind.INFIX -> 2 - Kind.PREFIX, Kind.POSTFIX -> 1 - Kind.NONE -> 0 - } - } - - override fun toString(): String { - return "$operator $kind $operands" - } - - enum class Kind { - NONE, - INFIX, - PREFIX, - POSTFIX, - } - } - - private class BasicOperator( - val operator: Char, - ) { - fun toOperator( - items: List, - i: Int - ): Operator { - val left = items.getOrElse( - i - 1, - { Any() }, - ) is Operand - - val right = items.getOrElse( - i + 1, - { Any() }, - ) is Operand - - val k:Kind - - if (left) { - if (right) { - k = Kind.INFIX - } else { - k = Kind.POSTFIX - } - } else if (right) { - k = Kind.PREFIX - } else { - k = Kind.NONE - } - - return operators.first { - it.operator == operator && it.kind == k - } - } - - override fun toString(): String { - return operator.toString(); - } - } - - private class Operand( - val value: Double, - ){ - override fun toString(): String { - if(value % 1 == 0.0){ - return "%.0f".format(value) - } - return value.toString() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt b/app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt index 435acef..c6a10da 100644 --- a/app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt +++ b/app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt @@ -1,6 +1,6 @@ package com.a404m.calculator.model -import com.a404m.calculator.MathHelper +import com.a404m.calculator.util.MathHelper data class CalcButtonModel( val text: String, diff --git a/app/src/main/java/com/a404m/calculator/util/MathHelper.kt b/app/src/main/java/com/a404m/calculator/util/MathHelper.kt new file mode 100644 index 0000000..d0c3ee3 --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/util/MathHelper.kt @@ -0,0 +1,314 @@ +package com.a404m.calculator.util + +import android.util.Log +import com.a404m.calculator.util.MathHelper.Operator.Kind +import java.nio.charset.UnsupportedCharsetException + +object MathHelper { + private val operatorStrings = arrayOf( + '+', + '-', + '×', + '÷', + '%', + ) + private val numberStrings = arrayOf( + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '.', + ) + + private val plus = Operator( + '+', + Kind.PREFIX, + { + Operand(it.first().value) + } + ) + private val minus = Operator( + '-', + Kind.PREFIX, + { + Operand(-it.first().value) + } + ) + private val add = Operator( + '+', + Kind.INFIX, + { + Operand(it.first().value+it.last().value) + } + ) + private val sub = Operator( + '-', + Kind.INFIX, + { + Operand(it.first().value-it.last().value) + } + ) + private val mul = Operator( + '×', + Kind.INFIX, + { + Operand(it.first().value*it.last().value) + } + ) + private val div = Operator( + '÷', + Kind.INFIX, + { + Operand(it.first().value/it.last().value) + } + ) + private val rem = Operator( + '%', + Kind.INFIX, + { + Operand(it.first().value%it.last().value) + } + ) + private val operatorOrders = arrayOf( + arrayOf( + plus, + minus, + ), + arrayOf( + mul, + div, + rem, + ), + arrayOf( + add, + sub, + ) + ) + private val operators = arrayOf( + plus, + minus, + add, + sub, + mul, + div, + rem, + ) + + fun eval(expression: String): String { + return try { + parse(lex(expression)).eval().toString() + } catch (e: Exception) { + e.message ?: "Exception" + } + } + + private fun parse(lexed: ArrayList): Operator { + for (order in operatorOrders) { + if (lexed.size == 1) { + break + } + var i = -1 + while(++i < lexed.size) { + val item = lexed[i] + if (item is BasicOperator) { + var op = item.toOperator( + lexed, + i + ) + if (op in order) { + op = op.cloneWithoutOperands() + Log.d( + "A404M", + "parse: $op" + ) + lexed[i] = op + when (op.kind) { + Kind.NONE -> {} + Kind.INFIX -> { + op.operands.add(lexed.removeAt(i - 1)) + op.operands.add(lexed.removeAt(i)) + i -= 2 + } + + Kind.PREFIX -> { + op.operands.add(lexed.removeAt(i + 1)) + --i + } + + Kind.POSTFIX -> { + op.operands.add(lexed.removeAt(i - 1)) + --i + } + } + } + } + } + } + return if (lexed.size != 1) { + throw UnsupportedOperationException("lexed.size = ${lexed.size}: $lexed") + } else if (lexed.first() is Operator) { + lexed.first() as Operator + } else if (lexed.first() is Operand) { + plus.cloneWithoutOperands().cloneWithOperator(arrayListOf(lexed.first())) + } else { + throw UnsupportedOperationException("unsupported type ${lexed.first().javaClass}") + } + } + + private fun lex(expression: String): ArrayList { + val items = arrayListOf() + + var i = 0 + while (i < expression.length) { + when (val c = expression[i]) { + in numberStrings -> { + val start = i + while (++i < expression.length && expression[i] in numberStrings); + items.add( + Operand( + value = expression.substring( + start, + i + ).toDouble() + ) + ) + --i + } + + in operatorStrings -> { + items.add( + BasicOperator(c) + ) + } + + else -> throw UnsupportedCharsetException(c.toString()) + } + ++i + } + + return items + } + + private class Operator( + val operator: Char, + val kind: Kind, + val operate: (operands:List)-> Operand, + val operands: ArrayList = arrayListOf(), + ) { + fun cloneWithoutOperands(): Operator { + return Operator( + operator, + kind, + operate, + ) + } + + fun cloneWithOperator(operands: ArrayList): Operator { + return Operator( + operator, + kind, + operate, + operands, + ) + } + + fun eval(): Operand { + Log.d( + "A404M", + "eval: $operator $operands" + ) + if(operandSize() != operands.size){ + throw UnsupportedOperationException("Not enough operands") + } + + val evaledOperands = operands.map { + if(it is Operator){ + it.eval() + }else{ + it as Operand + } + } + + return operate(evaledOperands) + } + + fun operandSize(): Int { + return when (kind) { + Kind.INFIX -> 2 + Kind.PREFIX, Kind.POSTFIX -> 1 + Kind.NONE -> 0 + } + } + + override fun toString(): String { + return "$operator $kind $operands" + } + + enum class Kind { + NONE, + INFIX, + PREFIX, + POSTFIX, + } + } + + private class BasicOperator( + val operator: Char, + ) { + fun toOperator( + items: List, + i: Int + ): Operator { + val leftItem = items.getOrElse( + i - 1, + { Any() }, + ) + val left = leftItem is Operand || leftItem is Operator + + val rightItem = items.getOrElse( + i + 1, + { Any() }, + ) + val right = rightItem is Operand || rightItem is Operator + + val k:Kind + + if (left) { + if (right) { + k = Kind.INFIX + } else { + k = Kind.POSTFIX + } + } else if (right) { + k = Kind.PREFIX + } else { + k = Kind.NONE + } + + return operators.first { + it.operator == operator && it.kind == k + } + } + + override fun toString(): String { + return operator.toString(); + } + } + + private class Operand( + val value: Double, + ){ + override fun toString(): String { + if(value % 1 == 0.0){ + return "%.0f".format(value) + } + return value.toString() + } + } +} \ No newline at end of file -- cgit v1.2.3