-
Notifications
You must be signed in to change notification settings - Fork 108
[그리디] 서현진 로또 미션 3, 4, 5단계 제출합니다. #145
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
base: main
Are you sure you want to change the base?
Changes from 33 commits
8ba883a
8ae5422
8dd8277
4c65cb7
128dee6
daa8759
28b7703
f63efd3
52ef26b
af6c2f5
dfb1e77
bcdad0a
ab6a0ee
201caa7
04a10e1
165964f
70d4cbb
68ce019
1e7dc53
793e9a3
2358d22
29060b4
a472ddf
1bc5b7d
bb86d4f
4659d1a
46091b5
53d1b38
28ce4b9
ee3d545
517dcee
a8751ae
6cc626c
43b220a
418292f
166a97a
fbcd282
52e393b
dae1be8
c341fa7
acc8b99
36937b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # Java Lotto Clean Playground | ||
|
|
||
| ## 📖 개요 | ||
|
|
||
| ## ✨ 주요 기능 | ||
|
|
||
| - **로또 구매**: 입력한 금액에 맞춰 1,000원 단위로 로또를 구매합니다. | ||
| - **수동/자동 구매**: 원하는 개수만큼 로또 번호를 직접 선택하는 수동 구매와 나머지 개수를 자동으로 발급받는 기능을 지원합니다. | ||
| - **구매 내역 출력**: 구매한 모든 로또(수동, 자동)의 번호를 출력합니다. | ||
| - **당첨 통계**: 사용자가 입력한 당첨 번호 및 보너스 번호를 기반으로 당첨 내역을 계산합니다. | ||
| - 1등: 6개 번호 일치 | ||
| - 2등: 5개 번호 + 보너스 번호 일치 | ||
| - 3등: 5개 번호 일치 | ||
| - 4등: 4개 번호 일치 | ||
| - 5등: 3개 번호 일치 | ||
| - **수익률 계산**: 총 구매 금액 대비 당첨금 총액을 기반으로 한 수익률을 소수점 둘째 자리까지 계산하여 출력합니다. | ||
|
|
||
| ``` | ||
| . | ||
| └── src | ||
| └── main | ||
| └── java | ||
| ├── Application.java # 애플리케이션 시작점 | ||
| ├── Controller | ||
| │ └── LottoController.java # 게임 흐름 제어 | ||
| ├── Model | ||
| │ ├── Lotto.java # 로또 1장 | ||
| │ ├── Lottos.java # 발급된 전체 로또 | ||
| │ ├── Jackpot.java # 당첨 번호와 보너스 번호 | ||
| │ ├── LottoMatcher.java # 로또 당첨 여부 확인 및 통계 계산 | ||
| │ ├── PrizeMoney.java # 등수별 상금 Enum | ||
| │ ├── AutoLotto.java # 자동 로또 번호 생성 | ||
| │ └── ManualLotto.java # 수동 로또 번호 관리 | ||
| └── View | ||
| ├── InputView.java # 사용자 입력 처리 | ||
| └── OutView.java # 결과 출력 담당 | ||
|
|
||
| ``` | ||
| ## ⚙️ 핵심 로직 상세 | ||
|
|
||
| ### 1. 로또 생성 (`Lottos.java`) | ||
|
|
||
| - `Lottos` 클래스는 사용자가 구매한 모든 로또 티켓을 관리합니다. | ||
| - `InputView`를 통해 받은 수동 구매 개수와 번호 문자열 리스트를 `ManualLotto`를 통해 `Lotto` 객체 리스트로 변환합니다. | ||
| - 총 구매 개수에서 수동 구매 개수를 뺀 만큼 `AutoLotto`를 통해 자동으로 로또 번호를 생성합니다. | ||
| - `AutoLotto`는 `Collections.shuffle`을 활용하여 1부터 45까지의 숫자 중 6개를 무작위로 선택하여 중복 없는 번호를 생성합니다. | ||
|
|
||
| ### 2. 당첨 확인 (`LottoMatcher.java`) | ||
|
|
||
| `LottoMatcher`는 로또 게임의 당첨 결과를 판별하고 통계를 내는, 가장 중요한 비즈니스 로직을 담당하는 클래스입니다. 객체가 생성되는 시점에 모든 핵심 연산을 수행하여 내부에 결과를 저장하고, 외부에는 요청에 따라 계산된 결과를 즉시 제공하는 방식으로 설계되었습니다. | ||
|
|
||
| #### 동작 원리 | ||
|
|
||
| 1. **생성 및 초기화**: `LottoController`가 `new LottoMatcher(lottos, jackpot)` 코드로 객체를 생성하면, `LottoMatcher`의 생성자가 실행되며 모든 계산이 즉시 이루어집니다. | ||
| - **데이터 준비**: 생성자는 사용자가 구매한 모든 로또 티켓 묶음(`lottos`)과 당첨 번호 정보(`jackpot`)를 인자로 받습니다. 그리고 당첨 통계를 저장할 `matchCounts`라는 `Map`을 초기화합니다. 이 맵은 `{일치 개수: 당첨된 티켓 수}` 형태로 통계를 저장하게 됩니다. | ||
| - **개별 티켓 비교**: `for` 루프를 통해 구매한 로또 티켓을 한 장씩 순회하며 각 티켓별로 당첨 등수를 판별합니다. | ||
| - 먼저 해당 티켓의 번호 6개가 당첨 번호 6개와 몇 개나 일치하는지 계산합니다. | ||
| - 만약 일치 개수가 정확히 **5개**라면, 2등 당첨 가능성이 생깁니다. 이때, 해당 로또 티켓이 **보너스 번호를 포함하고 있는지** 추가로 확인하여 2등과 3등을 구분합니다. | ||
| - 2등(5개 일치 + 보너스 번호 포함)으로 확인되면, `matchCounts` 맵에 `7`이라는 특별한 키값으로 당첨 횟수를 1 증가시킵니다. | ||
| - 그 외의 경우(3, 4, 5, 6개 일치), 계산된 일치 개수를 키로 사용하여 `matchCounts` 맵의 카운트를 1 증가시킵니다. | ||
|
|
||
| 2. **결과 제공**: 모든 계산이 완료된 `LottoMatcher` 객체는 `LottoController`의 요청에 따라 아래 메소드들을 통해 결과를 제공합니다. | ||
| - `getMatchCounts()`: 모든 계산이 완료된 `matchCounts` 맵을 반환합니다. `OutView`는 이 맵을 받아 최종 당첨 통계를 화면에 출력합니다. | ||
| - `calculateTotalEarnings()`: `matchCounts` 맵과 `PrizeMoney` Enum을 기반으로 총상금을 계산합니다. | ||
| - `getRate()`: `calculateTotalEarnings()`로 계산된 총상금을 사용자의 총 구매 금액으로 나누어 최종 수익률을 계산합니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import Controller.LottoController; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| LottoController lottoController = new LottoController(); | ||
| lottoController.startLotto(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package Controller; | ||
|
|
||
| import Model.Jackpot; | ||
| import Model.LottoMatcher; | ||
| import Model.Lottos; | ||
| import View.InputView; | ||
| import View.OutView; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class LottoController { | ||
| public void startLotto() { | ||
| InputView inputView = new InputView(); | ||
|
|
||
| int money = inputView.getMoney(); | ||
| int manualLottoNumber = inputView.getManualLottoNumber(); | ||
|
|
||
| List<String> rawNumbersList = inputView.manualLotto(manualLottoNumber); | ||
|
|
||
| Lottos lottos = new Lottos(money, rawNumbersList); | ||
|
|
||
| OutView outView = new OutView(); | ||
|
|
||
| outView.printLottosStaus(lottos.getAutoLottoCount(money, manualLottoNumber), manualLottoNumber); | ||
|
|
||
| outView.printLottos(lottos); | ||
|
|
||
| String[] winningNumber = inputView.getJackpotNumber(); | ||
| int bonusNumber = inputView.getBonus(); | ||
|
|
||
| Jackpot jackpot = new Jackpot(winningNumber, bonusNumber); | ||
|
|
||
| LottoMatcher lottoMatcher = new LottoMatcher(lottos, jackpot); | ||
|
|
||
| outView.printLottoMatcher(lottoMatcher.getMatchCounts(), lottoMatcher.getRate(money)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package Model; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class AutoLotto extends Lotto { | ||
|
|
||
| public AutoLotto() { | ||
| List<Integer> numbers = new ArrayList<>(); | ||
|
|
||
| for (int i = 1; i <= 45; i++) { | ||
| numbers.add(i); | ||
| } | ||
|
|
||
| Collections.shuffle(numbers); | ||
| this.numbers = new ArrayList<>(numbers.subList(0, 6)); | ||
|
|
||
| Collections.sort(this.numbers); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package Model; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Jackpot { | ||
| private List<Integer> jackpot; | ||
| private int bounsNumber; | ||
|
|
||
| public Jackpot(String[] inputJackpot, int bounsNumber) { | ||
| JackpotGenerator from = JackpotGenerator.from(inputJackpot); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jackpot 생성작업을 Jackpot 안에 두지 않고 Generator로 분리하신 이유가 궁금해요!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. jackpot => jackpotgenerator + bounsNumber 를 관리하는 클래스 jackpot은 보너스 넘버까지 같이 관리하려고 나눴습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ManualLotto의 경우에는 숫자 검증, 중복 숫자 검증과 같이 객체를 만드는 데에 필요한 검증 작업과 숫자 생성 작업이 생성자 안에 들어가 있습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔔 요거 답을 안 주셨어요!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아아 죄송합니다! |
||
| this.jackpot = from.getNumber(); | ||
| validateBonusNumber(bounsNumber, this.jackpot); | ||
| this.bounsNumber = bounsNumber; | ||
| } | ||
|
|
||
| private void validateBonusNumber(int bounsNumber, List<Integer> jackpot) { | ||
| if (bounsNumber < 1 || bounsNumber > 45) { | ||
| throw new IllegalArgumentException("보너스 번호는 1과 45 사이의 숫자여야 합니다."); | ||
| } | ||
| if (jackpot.contains(bounsNumber)) { | ||
| throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| public List<Integer> getJackpot() { | ||
| return Collections.unmodifiableList(jackpot); | ||
| } | ||
|
|
||
| public int getBounsNumber() { | ||
| return bounsNumber; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return jackpot.toString(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package Model; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| public class JackpotGenerator { | ||
| private final List<Integer> number; | ||
|
|
||
| private JackpotGenerator(List<Integer> numbers) { | ||
| this.number = numbers; | ||
| } | ||
|
|
||
| public static JackpotGenerator from(String[] splits) { | ||
| if (splits.length != 6) { | ||
| throw new IllegalArgumentException("당첨 번호는 6개여야 합니다."); | ||
| } | ||
| List<Integer> numbers = parseNumbers(splits); | ||
| validateNumbers(numbers); | ||
| Collections.sort(numbers); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 자바에서 제공하는 어떤 자료구조를 활용하면 중복제거와 원소 정렬을 동시에 할 수 있습니다. 한 번 찾아보세요!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set을 이용해서 구현해보았습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set 쓰신 건 저도 압니다.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 찾아보니 treeset이 말씀하신 자료구조 인 것 같아 사용하여 구현해보았습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 잘 하셨습니다. 추가로 set의 add 메서드는 boolean을 반환합니다. 이 boolean은 무엇을 의미하고, 이를 어떻게 활용해볼 수 있을까요?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 예시코드) add 메소드는 collection에서 처음 정의가 되고 set에서 오버라이딩 하는 형태로 구성되어 있는 것 같습니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 활용해서 코드 리팩토링 해봤습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 훌륭합니다! 제가 로또 미션을 리뷰할 때마다 항상 짚고 가는 구간인데, |
||
| return new JackpotGenerator(Collections.unmodifiableList(numbers)); | ||
| } | ||
|
|
||
| private static List<Integer> parseNumbers(String[] splits) { | ||
| List<Integer> numbers = new ArrayList<>(); | ||
| for (String s : splits) { | ||
| try { | ||
| numbers.add(Integer.parseInt(s.trim())); | ||
| } catch (NumberFormatException e) { | ||
| throw new IllegalArgumentException("당첨 번호는 숫자여야 합니다."); | ||
| } | ||
| } | ||
| return numbers; | ||
| } | ||
|
|
||
| private static void validateNumbers(List<Integer> numbers) { | ||
| Set<Integer> uniqueNumbers = new HashSet<>(numbers); | ||
| if (uniqueNumbers.size() != 6) { | ||
| throw new IllegalArgumentException("당첨 번호는 중복될 수 없습니다."); | ||
| } | ||
|
|
||
| for (int number : numbers) { | ||
| if (number < 1 || number > 45) { | ||
| throw new IllegalArgumentException("당첨 번호는 1과 45 사이의 숫자여야 합니다."); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public List<Integer> getNumber() { | ||
| return number; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package Model; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Lotto { | ||
| protected List<Integer> numbers; | ||
|
|
||
| public List<Integer> getLotto() { | ||
| return Collections.unmodifiableList(numbers); | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return numbers.toString(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package Model; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public class LottoMatcher { | ||
| private final Map<Integer, Integer> matchCounts; | ||
|
|
||
| public LottoMatcher(Lottos lottos, Jackpot jackpot) { | ||
| this.matchCounts = new HashMap<>(); | ||
|
|
||
| for (Lotto lotto : lottos.getLottoList()) { | ||
| int matches = countMatches(jackpot.getJackpot(), lotto.getLotto()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LottoMatcher의 역할이 lottos의 번호들과 Jackpot 번호를 대조해보는 것이라면, 이 작업 전체를 그냥 Lottos 객체 안에서 수행해도 되지 않을까요? Matcher가 꼭 분리되어 존재해야 하는 이유가 궁금합니다.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 과제 요구사항에서 하나의 클래스 안에선 메소드가 10개 이하를 유지하기 위해서 클래스를 분리해서 만들어보았습니다!! 또한 기능 상 Lottos가 정답과 비교하는 기능 까지 가지고 있으면 너무 많은 책임을 가지고 있는 것 같아서 matcher라는 클래스를 생성해보았습니다! |
||
| checkBonus(jackpot, lotto, matches); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| private void checkBonus(Jackpot jackpot, Lotto lotto, int matches) { | ||
| if (matches == 5 && lotto.getLotto().contains(jackpot.getBounsNumber())) { | ||
| matchCounts.put(7, matchCounts.getOrDefault(7, 0) + 1); | ||
| return; | ||
| } | ||
| matchCounts.put(matches, matchCounts.getOrDefault(matches, 0) + 1); | ||
| } | ||
|
|
||
| private int countMatches(List<Integer> jackpotNumbers, List<Integer> lottoNumbers) { | ||
| int count = 0; | ||
| for (int number : lottoNumbers) { | ||
| count += getMatchValue(jackpotNumbers, number); | ||
| } | ||
| return count; | ||
| } | ||
|
|
||
|
|
||
| private int getMatchValue(List<Integer> jackpotNumbers, int number) { | ||
| if (jackpotNumbers.contains(number)) { | ||
| return 1; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| public Map<Integer, Integer> getMatchCounts() { | ||
| return matchCounts; | ||
| } | ||
|
|
||
| public double calculateTotalEarnings() { | ||
|
|
||
| return matchCounts.getOrDefault(3, 0) * PrizeMoney.THREE.getMoney() | ||
| + matchCounts.getOrDefault(4, 0) * PrizeMoney.FOUR.getMoney() | ||
| + matchCounts.getOrDefault(5, 0) * PrizeMoney.FIVE.getMoney() | ||
| + matchCounts.getOrDefault(7, 0) * PrizeMoney.BONUS.getMoney() | ||
| + matchCounts.getOrDefault(6, 0) * PrizeMoney.SIX.getMoney(); | ||
| } | ||
|
|
||
| public double getRate(int money) { | ||
| return calculateTotalEarnings() / money; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package Model; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Lottos { | ||
| private List<Lotto> lottoList; | ||
| private static final int COST_PER_TICKET = 1000; | ||
|
|
||
| public Lottos(int money, List<String> manualLottoLines) { | ||
| this.lottoList = new ArrayList<>(); | ||
| int manualLottosNumber = manualLottoLines.size(); | ||
|
|
||
| // 수동 로또 생성 | ||
| for (String line : manualLottoLines) { | ||
| lottoList.add(new ManualLotto(line)); | ||
| } | ||
|
|
||
| // 자동 로또 생성 | ||
| for (int i = 0; i < getAutoLottoCount(money,manualLottosNumber); i++) { | ||
| lottoList.add(new AutoLotto()); | ||
| } | ||
| } | ||
|
|
||
| public List<Lotto> getLottoList() { | ||
| return Collections.unmodifiableList(lottoList); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getLottoList()를 호출하는 코드들을 보면,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 너무 좋은 방식인 것 같습니다! 다만 궁금한 내용은 iterable과 읽기 전용 리스트로 반환하는 차이가 제가 보기엔 iter 구문에서 좀 더 간편해진다 밖에 없어 보이는데 어떤 장점이 또 있는지 궁금합니다 !
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. '순회'에 한정했을 때 객체의 내부 자료구조를 캡슐화할 수 있다는 장점이 있습니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 블로그 보고 공부해보겠습니다! |
||
| } | ||
|
|
||
| public int getAutoLottoCount(int money,int manualLottosNumber) { | ||
| return money / COST_PER_TICKET - manualLottosNumber; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package Model; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class ManualLotto extends Lotto { | ||
|
|
||
| public ManualLotto(String rawNumbers) { | ||
| String[] numberSplits = rawNumbers.split(","); | ||
|
|
||
| this.numbers = Arrays.stream(numberSplits) | ||
| .map(String::trim) // " 21" -> "21" | ||
| .map(Integer::parseInt) // "21" -> 21 | ||
| .collect(Collectors.toList()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package Model; | ||
|
|
||
| public enum PrizeMoney { | ||
| THREE(5000), | ||
| FOUR(50000), | ||
| FIVE(1500000), | ||
| SIX(2000000000), | ||
| BONUS(30000000); | ||
|
|
||
| private final int money; | ||
|
|
||
| private PrizeMoney(int money) { | ||
| this.money = money; | ||
| } | ||
|
|
||
| public int getMoney() { | ||
| return money; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'로또 개수'에 대한 용어로 lottoCount와 lottoNumber가 혼용되는 것 같습니다.
변수명만 보았을 때 로또 번호인지 개수인지 파악하기 어렵네요. 통일해주세요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵!! 통일했습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
418292f