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

[로또 2단계] 공백 미션 제출합니다. #131

Open
wants to merge 42 commits into
base: junseo511
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
df07921
refactor: 함수의 재사용성을 고려해 네이밍 변경
junseo511 Feb 24, 2025
e193ea5
refactor: 캐싱된 숫자 변수 네이밍 구체화
junseo511 Feb 24, 2025
955673a
refactor: 불필요한 객체 제거
junseo511 Feb 24, 2025
4328937
refactor: 로또 번호 일치 개수 반환 로직 위임
junseo511 Feb 24, 2025
869f89a
refactor: 중복된 파라미터 이름 제거
junseo511 Feb 24, 2025
1722035
refactor: 우승 반환 객체의 네이밍 변경
junseo511 Feb 24, 2025
669939c
refactor: 분리된 LottoService를 컨트롤러로 통합
junseo511 Feb 24, 2025
0e9399f
refactor: GIVEN-WHEN-THEN 주석을 활용한 테스트 분리
junseo511 Feb 24, 2025
4c35351
refactor: GIVEN-WHEN-THEN 주석 추가
junseo511 Feb 24, 2025
7e9b21b
refactor: 객체 프로퍼티에 접근제한자 추가
junseo511 Feb 24, 2025
59495d9
docs: 수동 입력 기능 관련 리드미 추가
junseo511 Feb 24, 2025
ff31ee6
feat: 로또 머신이 수동 로또를 입력받을 수 있도록 구현
junseo511 Feb 24, 2025
0ca6a79
docs: 변경된 구현 내용에 따른 리드미 수정
junseo511 Feb 24, 2025
7fc0f1f
feat: 추가된 기능을 반영한 입출력 객체 로직 구현
junseo511 Feb 24, 2025
204952e
docs: 추가 구현에 따른 리드미 구현 예시 변경
junseo511 Feb 24, 2025
7f544ed
refactor: 수동 로또 갯수에 따른 출력 변경
junseo511 Feb 24, 2025
9f39975
docs: 수동 로또 구현 완료에 따른 리드미 업데이트
junseo511 Feb 24, 2025
f13927f
refactor: 우승 결과 반환 함수명 변경
junseo511 Feb 24, 2025
0916e12
refactor: 불필요한 변수 선언 제거
junseo511 Feb 24, 2025
b0aaff3
refactor: 중복되는 변수 재사용
junseo511 Feb 24, 2025
50da4ca
refactor: 수익률에 따른 문자열 이동
junseo511 Feb 24, 2025
abdd447
refactor: 컨트롤러에 있는 출력 로직 이동
junseo511 Feb 24, 2025
9dcf73f
refactor: 매직넘버 상수화
junseo511 Feb 24, 2025
581d5dc
refactor: 수동 로또 구매 여부에 대한 조건 이동
junseo511 Feb 27, 2025
6d6f0b3
feat: 로또 지갑 구현
junseo511 Feb 27, 2025
b6d29d2
refactor: 검증 로직 순서 변경
junseo511 Feb 27, 2025
35a9c55
refactor: 컨트롤러 함수 재분배
junseo511 Feb 27, 2025
285fd4d
refactor: 자동 로또 수량 함수명 구체화
junseo511 Feb 27, 2025
ae35853
refactor: 로또 번호 캐싱 최적화
junseo511 Feb 28, 2025
f328b98
refactor: 중복 번호 검증 로직 개선
junseo511 Feb 28, 2025
66557d3
refactor: 번호 포함 여부 판별 함수의 네이밍 변경
junseo511 Feb 28, 2025
0c0a3ad
refactor: 도메인에서 뷰에 의존적인 반환 로직 제거
junseo511 Feb 28, 2025
52e52ae
refactor: 랭크를 반환하는 분기문 가독성 개선
junseo511 Feb 28, 2025
5c7e75d
refactor: 전반적인 객체의 역할 재분배
junseo511 Feb 28, 2025
0161382
refactor: 로또 상점의 객체명 변경
junseo511 Feb 28, 2025
7811eeb
refactor: 로또 지갑을 파라미터로 주입
junseo511 Feb 28, 2025
adb6bab
refactor: 주요 로직 상단으로 이동
junseo511 Feb 28, 2025
14c6328
refactor: 보너스 번호 보유 판별 함수 네이밍 변경
junseo511 Feb 28, 2025
f842ceb
refactor: 로또 번호의 캐싱 방식 개선
junseo511 Mar 2, 2025
bee65d8
refactor: 로또 머신을 인터페이스로 이전
junseo511 Mar 2, 2025
ff9bfa2
refactor: amount 원시값 포장
junseo511 Mar 2, 2025
36b8292
refactor: 원시값을 포장한 금액 클래스 활용
junseo511 Mar 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,23 @@
- [x] 총 수익률을 반환한다.
- [x] 수익률은 소숫점 둘 째 자리까지만 출력한다.
- [x] 로또의 번호는 중복될 수 없다.
- [x] 로또는 수동으로 구매할 수 있다.

## 🖥️ 출력 예시

```
구입금액을 입력해 주세요.
14000
14개를 구매했습니다.

수동으로 구매할 로또 수를 입력해 주세요.
3

수동으로 구매할 번호를 입력해 주세요.
8, 21, 23, 41, 42, 43
3, 5, 11, 16, 32, 38
7, 11, 16, 35, 36, 44

수동으로 3장, 자동으로 11장을 구매했습니다.
[8, 21, 23, 41, 42, 43]
[3, 5, 11, 16, 32, 38]
[7, 11, 16, 35, 36, 44]
Expand Down Expand Up @@ -54,5 +64,5 @@
5개 일치 (1500000원)- 0개
5개 일치, 보너스 볼 일치(30000000원) - 0개
6개 일치 (2000000000원)- 0개
총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해(이)라는 의미임)
```
4 changes: 1 addition & 3 deletions src/main/kotlin/lotto/Application.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package lotto

import lotto.controller.LottoController
import lotto.service.LottoService
import lotto.view.InputView
import lotto.view.OutputView

fun main() {
val inputView = InputView()
val outputView = OutputView()
val lottoService = LottoService()

LottoController(inputView, outputView, lottoService).run()
LottoController(inputView, outputView).run()
}
97 changes: 71 additions & 26 deletions src/main/kotlin/lotto/controller/LottoController.kt
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1

위 처럼 입력하면 어떻게 될까요?
예상한 동작일까요? 🤔

추가로 요구사항 중 하나인 예외 처리를 통해 에러가 발생하지 않도록 한다.에 대해서도 고민해보면 좋을 것 같아요

Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1

위 처럼 입력하면 어떻게 될까요? 예상한 동작일까요? 🤔

컨트롤러를 구현하다가 보면 객체 순서를 맞추다가 하나씩 자꾸 놓치는 것 같아요 🥲
이 부분은 Amount를 포장하면서 자연스럽게 해결이 되었네요 👍
36b8292

Copy link
Author

@junseo511 junseo511 Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가로 요구사항 중 하나인 예외 처리를 통해 에러가 발생하지 않도록 한다.에 대해서도 고민해보면 좋을 것 같아요

리뷰를 읽고 ValidateResult라는 sealed class를 만들고 Success<T> 로 반환값을 감싸서 만들어보려고 했는데,
생각보다 많은 부분을 수정해야 하더라구요 🥲 이번 수정 기간 안에는 완성하지 못할 것 같아서 블랙잭에서 시도해보겠습니다 ‼️

image

조만간 방과후에서 획득당한(?) 주제이기도 하네요 🤣
다음번엔 여유가 생길때 과제 힌트의 구현사항도 하나씩 뿌셔봐야겠어요 😇

Original file line number Diff line number Diff line change
@@ -1,49 +1,94 @@
package lotto.controller

import lotto.model.Amount
import lotto.model.AutoLottoMachine
import lotto.model.LottoMarket
import lotto.model.LottoMarket.Companion.EMPTY_LOTTO_QUANTITY
import lotto.model.LottoProfitCalculator
import lotto.model.LottoWallet
import lotto.model.ManualLottoMachine
import lotto.model.ProfitStatus
import lotto.model.Rank
import lotto.service.LottoService
import lotto.model.WinningDiscriminator
import lotto.view.InputView
import lotto.view.OutputView

class LottoController(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1
수동으로 구매할 번호를 입력해 주세요.
1,2,3,4,5,6

위 처럼 입력하면 어떻게 될까요?
예상한 동작일까요? 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1
수동으로 구매할 번호를 입력해 주세요.
1,2,3,4,5,6

위 처럼 입력하면 어떻게 될까요? 예상한 동작일까요? 🤔

객체 생성의 순서를 조금 바꿔야 할 필요가 있을 것 같네요 🧐

private val inputView: InputView,
private val outputView: OutputView,
private val lottoService: LottoService,
) {
fun run() {
val purchaseAmount = getPurchaseAmount()
val manualLottoQuantity = getManualQuantity()

val lottoMarket = purchaseLottos(purchaseAmount, manualLottoQuantity)
val lottoWallet = storeLottos(lottoMarket, manualLottoQuantity)

val winningDiscriminator = getWinningInfo()
discriminateLottos(winningDiscriminator, lottoWallet, purchaseAmount)
}

private fun getPurchaseAmount(): Amount {
outputView.printPurchaseAmountGuide()
val purchaseAmount = inputView.readPurchaseAmount()
val lottos = lottoService.getPurchaseLottos(purchaseAmount)
return Amount(purchaseAmount)
}

outputView.printPurchaseLottoQuantity(lottos.lottos.size)
lottos.lottos.forEach { lotto ->
outputView.printLottoNumbers(lotto.numbers.map { it.number })
}
private fun getManualQuantity(): Int {
outputView.printManualLottoQuantityGuide()
val manualLottoQuantity = inputView.readManualLottoQuantity()
return manualLottoQuantity
}

outputView.printWinningNumbersGuide()
val winningNumbers = inputView.readWinningNumbers()
private fun purchaseLottos(
purchaseAmount: Amount,
manualLottoQuantity: Int,
): LottoMarket {
val autoLottoMachine = AutoLottoMachine()
val manualLottoMachine = ManualLottoMachine()
val lottoMarket = LottoMarket(purchaseAmount, manualLottoQuantity, manualLottoMachine, autoLottoMachine)
outputView.printManualLottoNumbersGuide(manualLottoQuantity > EMPTY_LOTTO_QUANTITY)
return lottoMarket
}

outputView.printBonusNumberGuide()
val bonusNumber = inputView.readBonusNumber()
private fun storeLottos(
lottoMarket: LottoMarket,
manualLottoQuantity: Int,
): LottoWallet {
val lottoWallet = LottoWallet()

val lottoWinningResult = lottoService.getLottosDiscriminateResult(lottos, winningNumbers, bonusNumber)
lottoMarket.buy(
List(manualLottoQuantity) { inputView.readLottoNumbers() },
lottoWallet,
)

outputView.printPurchaseLottoQuantity(manualLottoQuantity, lottoMarket.autoLottoQuantity)
outputView.printLotto(lottoWallet.lottos)
return lottoWallet
}

private fun discriminateLottos(
winningDiscriminator: WinningDiscriminator,
lottoWallet: LottoWallet,
purchaseAmount: Amount,
) {
val winningResult = winningDiscriminator.getResult(lottoWallet)
outputView.printWinningResultTitle()
lottoWinningResult.forEach { (rank, count) ->
if (rank == Rank.MISS) return@forEach

outputView.printWinningResult(
requiredMatch = rank.countOfMatch,
profit = rank.winningMoney,
matchBonus = rank == Rank.SECOND,
countOfMatch = count,
)
}

val profitRate = lottoService.getProfitRate(lottoWinningResult, purchaseAmount)
outputView.printWinningLottoResult(winningResult)

val lottoProfitCalculator = LottoProfitCalculator()
val profitRate = lottoProfitCalculator.getProfitRate(winningResult, purchaseAmount)
val profitStatus = ProfitStatus.from(profitRate)
outputView.printProfitRate(profitRate, profitStatus)
}

private fun getWinningInfo(): WinningDiscriminator {
outputView.printWinningNumbersGuide()
val winningNumbers = inputView.readLottoNumbers()

outputView.printBonusNumberGuide()
val bonusNumber = inputView.readBonusNumber()

outputView.printProfitRate(profitRate, profitStatus.krDescription)
val winningDiscriminator = WinningDiscriminator(winningNumbers, bonusNumber)
return winningDiscriminator
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
package lotto.model

class LottoCashier(
val amount: Int,
@JvmInline
value class Amount(
val value: Int,
Comment on lines +3 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스를 만들었으니 테스트도 추가하면 좋겠네요! 😊

) {
init {
validateAmountMinimumRange()
validateAmountUnit()
}

fun isAffordable(manualQuantity: Int): Boolean = value - manualQuantity * LOTTO_EACH_AMOUNT >= 0

fun getAutoLottoQuantity(manualQuantity: Int): Int = value / LOTTO_EACH_AMOUNT - manualQuantity

private fun validateAmountMinimumRange() {
require(amount > LOTTO_MIN_AMOUNT) {
"[ERROR] ${LOTTO_MIN_AMOUNT}원 이상의 금액으로 입력해 주세요. 입력값: $amount"
require(value > LOTTO_MIN_AMOUNT) {
"[ERROR] ${LOTTO_MIN_AMOUNT}원 이상의 금액으로 입력해 주세요. 입력값: $value"
}
}

private fun validateAmountUnit() {
require(amount % LOTTO_EACH_AMOUNT == 0) {
"[ERROR] ${LOTTO_EACH_AMOUNT}원 단위의 금액으로 입력해 주세요. 입력값: $amount"
require(value % LOTTO_EACH_AMOUNT == 0) {
"[ERROR] ${LOTTO_EACH_AMOUNT}원 단위의 금액으로 입력해 주세요. 입력값: $value"
}
}

fun getPurchaseQuantity(): Int = amount / LOTTO_EACH_AMOUNT

companion object {
private const val LOTTO_MIN_AMOUNT = 0
private const val LOTTO_EACH_AMOUNT = 1000
Expand Down
17 changes: 17 additions & 0 deletions src/main/kotlin/lotto/model/AutoLottoMachine.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package lotto.model

import lotto.model.Lotto.Companion.LOTTO_NUMBER_SIZE
import lotto.model.LottoNumber.Companion.cachedLottoNumbers

class AutoLottoMachine : LottoMachine {
override fun generate(
manualTicket: List<List<Int>>,
quantity: Int,
): List<Lotto> = List(quantity) { Lotto(getAutoNumbers()) }

private fun getAutoNumbers(): List<LottoNumber> =
cachedLottoNumbers
.shuffled()
.take(LOTTO_NUMBER_SIZE)
.sortedBy { number -> number.value }
}
19 changes: 10 additions & 9 deletions src/main/kotlin/lotto/model/Lotto.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
package lotto.model

class Lotto private constructor(
class Lotto(
val numbers: List<LottoNumber>,
) {
init {
validateLottoNumbersCount(numbers)
validateLottoNumbersDuplicate(numbers)
validateLottoNumbersCount()
validateLottoNumbersDuplicate()
}

private fun validateLottoNumbersCount(numbers: List<LottoNumber>) {
fun countMatchNumbers(other: Lotto): Int = numbers.count { number -> number in other.numbers }

fun contains(number: LottoNumber): Boolean = numbers.contains(number)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네이밍 👍


private fun validateLottoNumbersCount() {
require(numbers.size == LOTTO_NUMBER_SIZE) {
"[ERROR] 로또는 ${LOTTO_NUMBER_SIZE}개의 번호만 가질 수 있습니다."
}
}

private fun validateLottoNumbersDuplicate(lottoNumbers: List<LottoNumber>) {
val uniqueNumbers = lottoNumbers.map { it.number }.toSet()
require(uniqueNumbers.size == lottoNumbers.size) {
private fun validateLottoNumbersDuplicate() {
require(numbers.distinct().size == numbers.size) {
"[ERROR] 로또 번호는 중복될 수 없습니다."
}
}

fun isHaveBonus(number: LottoNumber): Boolean = numbers.contains(number)

companion object {
const val LOTTO_NUMBER_SIZE = 6

Expand Down
19 changes: 5 additions & 14 deletions src/main/kotlin/lotto/model/LottoMachine.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
package lotto.model

import lotto.model.Lotto.Companion.LOTTO_NUMBER_SIZE
import lotto.model.LottoNumber.Companion.ALL_LOTTO_NUMBERS

class LottoMachine {
fun getLottos(lottoQuantity: Int): Lottos {
val lottos = List(lottoQuantity) { Lotto.from(getLottoNumbers()) }
return Lottos(lottos)
}

private fun getLottoNumbers(): List<Int> =
ALL_LOTTO_NUMBERS
.shuffled()
.take(LOTTO_NUMBER_SIZE)
.sorted()
interface LottoMachine {
fun generate(
manualTicket: List<List<Int>> = emptyList(),
quantity: Int = manualTicket.size,
): List<Lotto>
Comment on lines +3 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

근데 자동 로또와 수동 로또를 얻는 과정에서 필요한 값이 너무 달라서
generate 함수에 필요한 변수로 아래 커밋과 같이 받았는데, 이 방법도 괜찮은지 고민이 조금 되네요 🤔

디폴트 값을 넘기면 어떤 문제가 발생할 수 있을까요?

인터페이스로 추상화하는 이유가 무엇일까에 대해서 먼저 고민해봐면 좋을 것 같은데요.
우리는 인터페이스로 추상화함으로써 역할과 구현을 구분할 수 있고, 이를 통해 유연성을 가질 수 있어요.

공백이 작성해준 방법은 manualTicket: List<List<Int>> = emptyList() 로 선언되어 있고,
구현에 대한 내용이 인터페이스에 정의되어 있다고 볼 수 있죠.

만약 반자동 발매기가 생긴다면 어떻게 해야 할까요? 이 역시도 인터페이스의 수정이 일어나야 할까요?
로또를 생성하는 구체적인 방법에 대해서는 구현체가 결정할 일입니다.

}
36 changes: 36 additions & 0 deletions src/main/kotlin/lotto/model/LottoMarket.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package lotto.model

class LottoMarket(
private val amount: Amount,
private val manualQuantity: Int,
private val manualLottoMachine: ManualLottoMachine = ManualLottoMachine(),
private val autoLottoMachine: AutoLottoMachine = AutoLottoMachine(),
) {
Comment on lines +3 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LottoMarketManualLottoMachineAutoLottoMachine를 모두 가지고 있어야 할까요?
추상화 한 LottoMachine을 잘 사용하지 못하고 있는데요, 어떤 기계를 사용할지는 사용하는 곳에서 정할 수도 있습니다.

의존성을 주입하는 방법은 대표적으로 3가지가 있으니 한 번 찾아보시고,
어떤 기계를 사용할까를 어떻게 사용하는 곳에서 결정할 수 있을지 고민해보면 좋겠어요 🤠

val autoLottoQuantity: Int = amount.getAutoLottoQuantity(manualQuantity)

init {
validateManualLottoQuantity()
}

fun buy(
manualNumbers: List<List<Int>> = emptyList(),
lottoWallet: LottoWallet,
) {
Comment on lines +15 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LottoMarket에서 로또를 구매하기 위해서는 지갑을 주고 거기에 로또를 담아주는 행위는 어색하지 않나요?
로또를 구매하니 LottoWallet을 돌려주는 게 더 명확해 보이네요 🤔

lottoWallet.addAll(
when (manualNumbers.isNotEmpty()) {
true -> manualLottoMachine.generate(manualTicket = manualNumbers)
false -> autoLottoMachine.generate(quantity = autoLottoQuantity)
},
)
}

private fun validateManualLottoQuantity() {
require(amount.isAffordable(manualQuantity)) {
"[ERROR] 낸 금액보다 많은 수동 로또를 살 수 없습니다. 금액: ${amount.value}, 수동 로또: ${manualQuantity}장"
}
}

companion object {
const val EMPTY_LOTTO_QUANTITY = 0
}
}
10 changes: 6 additions & 4 deletions src/main/kotlin/lotto/model/LottoNumber.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package lotto.model

class LottoNumber private constructor(
val number: Int,
val value: Int,
) {
companion object {
private const val LOTTO_NUMBER_MIN_RANGE = 1
private const val LOTTO_NUMBER_MAX_RANGE = 45

val ALL_LOTTO_NUMBERS = (LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE)
private val CACHE = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) }
private val CACHE_LOTTO_NUMBER =
(LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE).associateWith { LottoNumber(it) }

val cachedLottoNumbers: List<LottoNumber> by lazy { CACHE_LOTTO_NUMBER.values.toList() }

fun from(number: Int): LottoNumber =
CACHE[number]
CACHE_LOTTO_NUMBER[number]
?: throw IllegalArgumentException("[ERROR] 로또 번호의 범위는 $LOTTO_NUMBER_MIN_RANGE 이상 $LOTTO_NUMBER_MAX_RANGE 이하여야 합니다.")
}
}
10 changes: 5 additions & 5 deletions src/main/kotlin/lotto/model/LottoProfitCalculator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package lotto.model

class LottoProfitCalculator {
fun getProfitRate(
countLottoByRank: Map<Rank, Int>,
purchaseAmount: Int,
winningResult: Map<Rank, Int>,
purchaseAmount: Amount,
): Float {
val totalProfit = countLottoByRank.entries.sumOf { it.key.winningMoney * it.value }
val totalProfit = winningResult.entries.sumOf { rank -> rank.key.winningMoney * rank.value }
return formatProfitRate(totalProfit, purchaseAmount)
}

private fun formatProfitRate(
totalProfit: Int,
purchaseAmount: Int,
): Float = totalProfit.toFloat() / purchaseAmount.toFloat()
purchaseAmount: Amount,
): Float = totalProfit.toFloat() / purchaseAmount.value.toFloat()
}
Loading