I have a question from a company, the question says I have to implement z-score and minMaxScaling to read a csv file and do some calculations with BigDecimal so, I create a new class that implements Normalizer interface to deal with z-score and minMaxScaling so I will add the code with the test cases
I write my code and there was some failure in some test cases and I spent 4 days trying to solve them so I will but everything and hope I can get the right help
result was:
Quote:
org.opentest4j.AssertionFailedError: invalid mean ==> expected: <66.00> but was: <66>
Quote:
org.opentest4j.AssertionFailedError: invalid mean ==> expected: <1702.00> but was: <1701.51>
Quote:
org.opentest4j.AssertionFailedError: invalid standard deviation ==> expected: <16.73> but was: <16.72>
Quote:
org.opentest4j.AssertionFailedError: invalid mean ==> expected: <1702.00> but was: <1702>
What I have tried:
NormalizerTest class:
@Test
public void givenMarksCSVFileToScale_whenMarkColumnIsZScored_thenNewCSVFileIsGeneratedWithAdditionalZScoreColumn() throws IOException {
String filename = "marks.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "mark";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("marks_scaled.csv");
copyFile("/marks.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.zscore(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("66.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("16.73"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("280.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("65.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("40.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("95.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/marks_z.csv", induction.resolve("marks_z.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(generatedLines, expectedLines);
}
@Test
public void givenEmployeesCSVFileToScale_whenSalaryColumnIsZScored_thenNewCSVFileIsGeneratedWithAdditionalZScoreColumn() throws IOException {
String filename = "employees.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "salary";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("employees_scaled.csv");
copyFile("/employees.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.zscore(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("1702.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("785.19"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("616523.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("1758.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("299.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("2965.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/employees_z.csv", induction.resolve("employees_z.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(generatedLines, expectedLines);
}
@Test
public void givenMarksCSVFileToScale_whenMarkColumnIsMinMaxScaled_thenNewCSVFileIsGeneratedWithAdditionalMinMaxScoreColumn() throws IOException {
String filename = "marks.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "mark";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("marks_scaled.csv");
copyFile("/marks.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.minMaxScaling(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("66.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("16.73"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("280.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("65.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("40.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("95.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/marks_mm.csv", induction.resolve("marks_mm.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(expectedLines, generatedLines);
}
@Test
public void givenEmployeesCSVFileToScale_whenSalaryColumnIsMinMaxScaled_thenNewCSVFileIsGeneratedWithAdditionalMinMaxScoreColumn() throws IOException {
String filename = "employees.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "salary";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("employees_scaled.csv");
copyFile("/employees.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.minMaxScaling(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("1702.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("785.19"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("616523.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("1758.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("299.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("2965.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/employees_mm.csv", induction.resolve("employees_mm.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(expectedLines, generatedLines);
}
private final Path copyFile(String resource, Path path) throws IOException {
try (InputStream is = this.getClass().getResourceAsStream(resource)) {
try (OutputStream os = Files.newOutputStream(path)) {
int b;
while ((b = is.read()) != -1) {
os.write(b);
}
}
}
return path;
}
private void assertLines(List<String> generatedLines, List<String> expectedLines) {
Assertions.assertTrue(generatedLines.size() == expectedLines.size(), "lines are not identical");
for (int i = 0; i < generatedLines.size(); i++) {
Assertions.assertEquals(expectedLines.get(i), generatedLines.get(i));
}
}
}
ScoringSummary interface:
public interface ScoringSummary {
public BigDecimal mean();
public BigDecimal standardDeviation();
public BigDecimal variance();
public BigDecimal median();
public BigDecimal min();
public BigDecimal max();
}
Normalizer test:
package com.progressoft.tools;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class NormalizerTest {
private Normalizer normalizer;
@BeforeEach
public void beforeEach() {
normalizer = new Interviewtask();
}
public Normalizer normalizer() {
if (normalizer == null)
Assertions.fail("normalizer is not initialized");
return normalizer;
}
@Test
public void givenInvalidInput_whenZscore_thenThrowException() throws IOException {
Normalizer normalizer = normalizer();
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class,
() -> normalizer.zscore(Paths.get("no_exists"), null, null));
Assertions.assertEquals("source file not found", exception.getMessage());
Path source = copyFile("/marks.csv", Files.createTempFile("marks", ".csv"));
exception = Assertions.assertThrows(IllegalArgumentException.class,
() -> normalizer.zscore(source,
Files.createTempFile("target", ".csv"), "Salary"));
Assertions.assertEquals("column Salary not found", exception.getMessage());
exception = Assertions.assertThrows(IllegalArgumentException.class,
() -> normalizer.zscore(source,
Files.createTempFile("target", ".csv"), "TESt"));
Assertions.assertEquals("column TESt not found", exception.getMessage());
}
@Test
public void givenMarksCSVFileToScale_whenMarkColumnIsZScored_thenNewCSVFileIsGeneratedWithAdditionalZScoreColumn() throws IOException {
String filename = "marks.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "mark";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("marks_scaled.csv");
copyFile("/marks.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.zscore(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("66.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("16.73"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("280.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("65.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("40.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("95.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/marks_z.csv", induction.resolve("marks_z.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(generatedLines, expectedLines);
}
@Test
public void givenEmployeesCSVFileToScale_whenSalaryColumnIsZScored_thenNewCSVFileIsGeneratedWithAdditionalZScoreColumn() throws IOException {
String filename = "employees.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "salary";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("employees_scaled.csv");
copyFile("/employees.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.zscore(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("1702.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("785.19"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("616523.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("1758.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("299.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("2965.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/employees_z.csv", induction.resolve("employees_z.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(generatedLines, expectedLines);
}
@Test
public void givenInvalidInput_whenMinMaxScale_thenThrowException() throws IOException {
Normalizer normalizer = normalizer();
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class,
() -> normalizer.minMaxScaling(Paths.get("no_exists"), null, null));
Assertions.assertEquals("source file not found", exception.getMessage());
Path source = copyFile("/marks.csv", Files.createTempFile("marks", ".csv"));
exception = Assertions.assertThrows(IllegalArgumentException.class,
() -> normalizer.minMaxScaling(source,
Files.createTempFile("target", ".csv"), "Kalven"));
Assertions.assertEquals("column Kalven not found", exception.getMessage());
exception = Assertions.assertThrows(IllegalArgumentException.class,
() -> normalizer.minMaxScaling(source,
Files.createTempFile("target", ".csv"), "TESt2"));
Assertions.assertEquals("column TESt2 not found", exception.getMessage());
}
@Test
public void givenMarksCSVFileToScale_whenMarkColumnIsMinMaxScaled_thenNewCSVFileIsGeneratedWithAdditionalMinMaxScoreColumn() throws IOException {
String filename = "marks.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "mark";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("marks_scaled.csv");
copyFile("/marks.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.minMaxScaling(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("66.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("16.73"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("280.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("65.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("40.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("95.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/marks_mm.csv", induction.resolve("marks_mm.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(expectedLines, generatedLines);
}
@Test
public void givenEmployeesCSVFileToScale_whenSalaryColumnIsMinMaxScaled_thenNewCSVFileIsGeneratedWithAdditionalMinMaxScoreColumn() throws IOException {
String filename = "employees.csv";
Path induction = Files.createTempDirectory("induction");
String columnName = "salary";
Path csvPath = induction.resolve(filename);
Path destPath = induction.resolve("employees_scaled.csv");
copyFile("/employees.csv", csvPath);
Assertions.assertTrue(Files.exists(csvPath));
Normalizer normalizer = normalizer();
ScoringSummary summary = normalizer.minMaxScaling(csvPath, destPath, columnName);
Assertions.assertNotNull(summary, "the returned summary is null");
Assertions.assertEquals(new BigDecimal("1702.00"), summary.mean(), "invalid mean");
Assertions.assertEquals(new BigDecimal("785.19"), summary.standardDeviation(), "invalid standard deviation");
Assertions.assertEquals(new BigDecimal("616523.00"), summary.variance(), "invalid variance");
Assertions.assertEquals(new BigDecimal("1758.00"), summary.median(), "invalid median");
Assertions.assertEquals(new BigDecimal("299.00"), summary.min(), "invalid min value");
Assertions.assertEquals(new BigDecimal("2965.00"), summary.max(), "invalid maximum value");
Assertions.assertTrue(Files.exists(destPath), "the destination file does not exists");
Assertions.assertFalse(Files.isDirectory(destPath), "the destination is not a file");
List<String> generatedLines = Files.readAllLines(destPath);
Path assertionPath = copyFile("/employees_mm.csv", induction.resolve("employees_mm.csv"));
List<String> expectedLines = Files.readAllLines(assertionPath);
assertLines(expectedLines, generatedLines);
}
private final Path copyFile(String resource, Path path) throws IOException {
try (InputStream is = this.getClass().getResourceAsStream(resource)) {
try (OutputStream os = Files.newOutputStream(path)) {
int b;
while ((b = is.read()) != -1) {
os.write(b);
}
}
}
return path;
}
private void assertLines(List<String> generatedLines, List<String> expectedLines) {
Assertions.assertTrue(generatedLines.size() == expectedLines.size(), "lines are not identical");
for (int i = 0; i < generatedLines.size(); i++) {
Assertions.assertEquals(expectedLines.get(i), generatedLines.get(i));
}
}
}
my code:
package com.progressoft.tools;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import org.junit.jupiter.api.Assertions;
public class Interviewtask implements Normalizer {
@Override
public ScoringSummary zscore(Path csvPath, Path destPath, String colToStandardize) {
DoubleSummaryStatistics stat = getStat(fileReader(csvPath, destPath, colToStandardize));
double[] column = fileReader(csvPath, destPath, colToStandardize).stream().mapToDouble(Double::valueOf).sorted()
.toArray();
ScoringSummary summary = new ScoringSummary() {
DoubleSummaryStatistics stat = getStat(fileReader(csvPath, destPath, colToStandardize));
@Override
public BigDecimal mean() {
BigDecimal bigdecimlaStore = new BigDecimal(stat.getAverage());
MathContext math = new MathContext(0);
BigDecimal bigdecimlaStoreVTow =bigdecimlaStore.round(math);
BigDecimal bigdecimlaStoreVThree= bigdecimlaStoreVTow.setScale(2, RoundingMode.HALF_EVEN);
return bigdecimlaStoreVThree;
}
@Override
public BigDecimal standardDeviation() {
BigDecimal stdDeviation = BigDecimal.ZERO;
for (String num : fileReader(csvPath, destPath, colToStandardize)) {
stdDeviation = stdDeviation.add((new BigDecimal(Double.parseDouble(num)).subtract(mean())).pow(2))
.setScale(2, RoundingMode.HALF_EVEN);
}
BigDecimal val = stdDeviation.divide(BigDecimal.valueOf(stat.getCount()));
double d = Math.sqrt(val.doubleValue());
return BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_EVEN);
}
@Override
public BigDecimal variance() {
int i;
BigDecimal variance = BigDecimal.ZERO;
for (i = 0; i < column.length; i++) {
variance = variance.add((BigDecimal.valueOf((column[i])).subtract(mean())).pow(2));
}
MathContext mc = new MathContext(3, RoundingMode.UP);
variance = variance.divide(BigDecimal.valueOf(column.length));
return variance.round(mc).setScale(0);
}
@Override
public BigDecimal median() {
double median;
if (column.length % 2 == 0)
median = (column[column.length / 2] + column[column.length / 2 - 1]) / 2;
else
median = column[column.length / 2];
return BigDecimal.valueOf(median).setScale(0, RoundingMode.HALF_EVEN);
}
@Override
public BigDecimal min() {
return BigDecimal.valueOf(stat.getMin());
}
@Override
public BigDecimal max() {
return BigDecimal.valueOf(stat.getMax());
}
};
if (stat == null) {
summary = null;
return summary;
}
return summary;
}
@Override
public ScoringSummary minMaxScaling(Path csvPath, Path destPath, String colToNormalize) {
DoubleSummaryStatistics stat = getStat(fileReader(csvPath, destPath, colToNormalize));
double[] column = fileReader(csvPath, destPath, colToNormalize).stream().mapToDouble(Double::valueOf).sorted()
.toArray();
ScoringSummary summary = new ScoringSummary() {
@Override
public BigDecimal mean() {
return BigDecimal.valueOf(stat.getAverage()).setScale(0, RoundingMode.HALF_EVEN);
}
@Override
public BigDecimal standardDeviation() {
BigDecimal stdDeviation = BigDecimal.ZERO;
for (String num : fileReader(csvPath, destPath, colToNormalize)) {
stdDeviation = stdDeviation.add((new BigDecimal(Double.parseDouble(num)).subtract(mean())).pow(2))
.setScale(2, RoundingMode.HALF_EVEN);
}
BigDecimal val = stdDeviation.divide(BigDecimal.valueOf(stat.getCount()));
double d = Math.sqrt(val.doubleValue());
return BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_EVEN);
}
@Override
public BigDecimal variance() {
int i;
BigDecimal variance = BigDecimal.ZERO;
for (i = 0; i < column.length; i++) {
variance = variance.add((BigDecimal.valueOf((column[i])).subtract(mean())).pow(2));
}
MathContext mc = new MathContext(3, RoundingMode.UP);
variance = variance.divide(BigDecimal.valueOf(column.length));
return variance.round(mc).setScale(0);
}
@Override
public BigDecimal median() {
double median;
if (column.length % 2 == 0)
median = (column[column.length / 2] + column[column.length / 2 - 1]) / 2;
else
median = column[column.length / 2];
return BigDecimal.valueOf(median).setScale(0, RoundingMode.HALF_EVEN);
}
@Override
public BigDecimal min() {
return BigDecimal.valueOf(stat.getMin()).setScale(0);
}
@Override
public BigDecimal max() {
return BigDecimal.valueOf(stat.getMax()).setScale(0);
}
};
return summary;
}
public static List<String> fileReader(Path csvPath, Path destPath, String columnNS) {
List<String> result = new ArrayList<>();
if (csvPath == null || Files.exists(csvPath) == false)
throw new IllegalArgumentException("source file not found");
if (destPath == null)
new IllegalArgumentException("source file not found");
try (BufferedReader reader = Files.newBufferedReader(csvPath);) {
int index = -1;
String line;
while ((line = reader.readLine()) != null) {
String[] arr = line.split(",");
if (index == -1) {
index = getIndex(arr, columnNS);
continue;
}
result.add(arr[index]);
}
return result;
} catch (IOException e) {
e.printStackTrace();
}
if (Files.isDirectory(csvPath)) {
throw new IllegalArgumentException("is a directory");
} else if (Files.exists(csvPath)) {
} else {
String[] arr = String.valueOf(csvPath).split("\\\\");
int i = 0;
for (i = 0; i < arr.length; i++) {
}
String csvFile = arr[i];
File newFile = new File(csvFile);
try {
newFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static int getIndex(String[] arr, String columnNS) {
int index = Arrays.asList(arr).indexOf(columnNS);
if (index == -1) {
throw new IllegalArgumentException(
"column " + columnNS.substring(0, 1).toUpperCase() + columnNS.substring(1) + " not found");
}
return index;
}
public static DoubleSummaryStatistics getStat(List<String> list) {
return list.stream().mapToDouble(Double::valueOf).summaryStatistics();
}
}