Reputation: 394
I'm making a fractions calculator and have finally settled on the design and how things work and have written the code. However, it almost never gives out the correct answer. I don't know why this is happening?
It uses a Fraction class I made which contains only 3 variables: numerator, denominator and whole.
The process involves the following:
Taking input from user and converting to a Fraction form
Calculating
Simplifying the result
and finally displaying the result.
The code:
package com.example.fraccalcinkotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.Toast
import androidx.core.text.isDigitsOnly
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editText_number1_num.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number1_den.requestFocus()
editText_number1_num.setText(removeLastChar(editText_number1_num.text.toString()))
}
}
})
editText_number1_den.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
if (s.endsWith(" ") || s.endsWith("/")){
editText_number1_den.setText(removeLastChar(editText_number1_den.text.toString()))
}
}
})
editText_number1_whole.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number1_num.requestFocus()
editText_number1_whole.setText(removeLastChar(editText_number1_whole.text.toString()))
}
}
})
editText_number2_num.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number2_den.requestFocus()
editText_number2_num.setText(removeLastChar(editText_number2_num.text.toString()))
}
}
})
editText_number2_den.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ") || s.endsWith("/")){
editText_number2_den.setText(removeLastChar(editText_number2_den.text.toString()))
}
}
})
editText_number2_whole.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number2_num.requestFocus()
editText_number2_whole.setText(removeLastChar(editText_number2_whole.text.toString()))
}
}
})
button_add.setOnClickListener {
var sum:Fraction = AddNumbers()
var num: Int = sum.numerator
var den: Int = sum.denominator
var whole: Int = sum.wholeNo
var result_string: String = "$whole $num/$den"
textView_result.text = result_string
}
}
fun AddNumbers(): Fraction {
//convert String to Fraction for calculations
val num1: Int = editText_number1_num.text.toString().toInt()
val den1: Int = editText_number1_den.text.toString().toInt()
val whole1: Int = editText_number1_whole.text.toString().toInt()
val fraction1: List<Int> = listOf(num1, den1, whole1)
val num2: Int = editText_number2_num.text.toString().toInt()
val den2: Int = editText_number2_den.text.toString().toInt()
val whole2: Int = editText_number2_whole.text.toString().toInt()
val fraction2: List<Int> = listOf(num2, den2, whole2)
var frac_1: Fraction = fractionalize(fraction1)
var frac_2: Fraction = fractionalize(fraction2)
//extracting numerators and denominators from List<Int>
var numerator1 = frac_1.numerator
var denominator1 = frac_1.denominator
var numerator2 = frac_2.numerator
var denominator2 = frac_2.denominator
//Getting common denominator and numerators for adding
var commonDenominator: Int = 1
if (denominator1 != denominator2){
commonDenominator = getLCM(denominator1, denominator2)
numerator1 *= commonDenominator/denominator1
numerator2 *= commonDenominator/denominator2
} else if (denominator1 == denominator2){
commonDenominator = denominator1
}
//adding....
var sumOfNumerators: Int = numerator1 + numerator2
var result: Fraction = Fraction(sumOfNumerators, commonDenominator,0)
//simplifying....
result = simplify(result)
return frac_1
}
fun fractionalize(fraction: List<Int>): Fraction{
//Finds type of number (input) and converts to List<Int> with [numerator, denominator, whole number]
var num = fraction[0]
var den = fraction[1]
var whole = fraction[2]
if (whole != 0){
num += whole * den
whole = 0
}
val result: Fraction = Fraction(num, den, whole)
return result
}
fun simplify(fraction:Fraction):Fraction{
var numerator: Int = fraction.numerator
var denominator: Int = fraction.denominator
var whole_number: Int = fraction.wholeNo
if (getHCF(numerator, denominator) != 1){
numerator /= getHCF(numerator, denominator)
denominator /= getHCF(numerator, denominator)
}
if (numerator> denominator){
numerator %= denominator
whole_number = numerator / denominator
}
if (numerator == denominator){
whole_number += 1
numerator = 0
denominator = 1
}
var result: Fraction = Fraction(numerator, denominator, whole_number)
return result
}
fun getHCF(num1: Int, num2: Int):Int{
var dividend = Math.max(num1, num2)
var divisor = Math.min(num1, num2)
var remainder: Int = 1
//calculating HCF using repeated division method
do {
remainder = dividend % divisor
dividend = divisor
divisor = remainder
} while (remainder > 0)
return dividend
}
fun getLCM(num1: Int, num2: Int): Int{
//num1 * num2 = HCF * LCM
//so, LCM = (num1 * num2) / HCF
var result: Int = (num1 * num2)/getHCF(num1, num2)
return result
}
fun removeLastChar(string: String): String{
var result: String = string
if (!string.isEmpty()){
result = string.substring(0, string.length - 1);
}
return result
}
}
Please suggest me things I can do to find out which part is causing the problem.
Upvotes: 0
Views: 53
Reputation: 93609
I think I see the error here in simplify
:
if (numerator> denominator){
numerator %= denominator
whole_number = numerator / denominator
}
You have already changed the numerator
variable to be the remainder, and then you use it to find the whole number. You need to swap the order of these two lines in the if
statement.
It would be better practice to have all these operations on Fraction be part of the Fraction class itself. This is called encapsulation and is fundamental to robust software design. Bugs will be rarer and easier to resolve. Something like this (I didn't test the math):
data class Fraction(val num: Int, val den: Int, val whole: Int) {
fun toPure(): Fraction {
return Fraction(num + whole * den, den, 0)
}
fun toSimplified(withWhole: Boolean): Fraction {
with(toPure()) {
val gcm = findGcf(num, den)
val simplified = Fraction(num / gcm, den / gcm, 0)
return if (!withWhole || num < den)
simplified
else
Fraction(simplified.num % simplified.den, simplified.den, simplified.num / simplified.den)
}
}
operator fun plus(other: Fraction): Fraction {
val newDen = den * other.den
val newNum = num * other.den + other.num * den
return Fraction(newNum, newDen, whole + other.whole).toSimplified(true)
}
private fun findGcf(num1: Int, num2: Int): Int {
var dividend = max(num1, num2)
var divisor = min(num1, num2)
var remainder: Int = 1
//calculating HCF using repeated division method
do {
remainder = dividend % divisor
dividend = divisor
divisor = remainder
} while (remainder > 0)
return dividend
}
}
To avoid repeating yourself, you can create a function for converting EditText content to a Fraction:
private fun getFraction(numET: EditText, denET: EditText, wholeET: EditText): Fraction? {
val num = numET.text.toIntOrNull() ?: return null
val den = denET.text.toIntOrNull() ?: return null
val whole = wholeT.text.toIntOrNull() ?: return null
return Fraction(num, den, whole)
}
Then your addNumbers
function is a lot simpler:
fun addNumbers(): Fraction? {
val first = getFraction(editText_number1_num, editText_number1_den, editText_number1_whole) ?: return null
val second = getFraction(editText_number2_num, editText_number2_den, editText_number2_whole) ?: return null
return first + second
}
I made the return type nullable for the case of invalid input. Your click listener that uses addNumbers
can show an error message instead of the Fraction if the input is invalid.
Upvotes: 1