code: package zemberek.normalization;
  
  import java.nio.charset.StandardCharsets;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Random;
  import java.util.Set;
  import org.junit.Assert;
  import org.junit.Test;
  import zemberek.core.collections.FloatValueMap;
  
  public class SingleWordSpellCheckerTest {
  
    @Test
    public void simpleDecodeTest() {
      SingleWordSpellChecker spellChecker = new SingleWordSpellChecker(1);
      String vocabulary = "elma";
      spellChecker.addWord(vocabulary);
      Assert.assertTrue(spellChecker.decode(vocabulary).contains(vocabulary));
      check1Distance(spellChecker, "elma");
  
      spellChecker.addWord("armut");
      spellChecker.addWord("ayva");
      check1Distance(spellChecker, "armut");
    }
  
    @Test
    public void simpleDecodeTest2() {
      SingleWordSpellChecker spellChecker = new SingleWordSpellChecker(1);
      spellChecker.addWords("apple", "apples");
      FloatValueMap<String> res = spellChecker.decode("apple");
      for (String re : res) {
        System.out.println(re);
      }
    }
  
    @Test
    public void simpleDecodeTest3() {
      SingleWordSpellChecker spellChecker = new SingleWordSpellChecker(1);
      spellChecker.addWords("apple", "apples");
      List<SingleWordSpellChecker.ScoredString> res = spellChecker.getSuggestionsWithScores("apple");
      for (SingleWordSpellChecker.ScoredString re : res) {
        System.out.println(re.s);
      }
    }
  
    @Test
    public void multiWordDecodeTest() {
  
      SingleWordSpellChecker spellChecker = new SingleWordSpellChecker(1);
      spellChecker.addWords("çak", "sak", "saka", "bak", "çaka", "çakal", "sakal");
  
      FloatValueMap<String> result = spellChecker.decode("çak");
  
      Assert.assertEquals(4, result.size());
      assertContainsAll(result, "çak", "sak", "bak", "çaka");
  
      double delta = 0.0001;
      Assert.assertEquals(0, result.get("çak"), delta);
      Assert.assertEquals(1, result.get("sak"), delta);
      Assert.assertEquals(1, result.get("bak"), delta);
      Assert.assertEquals(1, result.get("çaka"), delta);
  
      result = spellChecker.decode("çaka");
  
      Assert.assertEquals(4, result.size());
      assertContainsAll(result, "çaka", "saka", "çakal", "çak");
  
      Assert.assertEquals(0, result.get("çaka"), delta);
      Assert.assertEquals(1, result.get("saka"), delta);
      Assert.assertEquals(1, result.get("çakal"), delta);
      Assert.assertEquals(1, result.get("çak"), delta);
  
    }
  
    @Test
    public void performanceTest() throws Exception {
      Path r = Paths.get(ClassLoader.getSystemResource("10000_frequent_turkish_word").toURI());
      List<String> words = Files.readAllLines(r, StandardCharsets.UTF_8);
      SingleWordSpellChecker spellChecker = new SingleWordSpellChecker();
      spellChecker.buildDictionary(words);
      long start = System.currentTimeMillis();
      int solutionCount = 0;
      for (String word : words) {
        FloatValueMap<String> result = spellChecker.decode(word);
        solutionCount += result.size();
      }
      System.out.println("Elapsed: " + (System.currentTimeMillis() - start));
      System.out.println("Solution count:" + solutionCount);
    }
  
    void assertContainsAll(FloatValueMap<String> set, String... words) {
      for (String word : words) {
        Assert.assertTrue(set.contains(word));
      }
    }
  
    private void check1Distance(SingleWordSpellChecker spellChecker, String expected) {
      Set<String> randomDeleted = randomDelete(expected, 1);
      for (String s : randomDeleted) {
        FloatValueMap<String> res = spellChecker.decode(s);
        Assert.assertEquals(s, 1, res.size());
        Assert.assertTrue(s, res.contains(expected));
      }
  
      Set<String> randomInserted = randomInsert(expected, 1);
      for (String s : randomInserted) {
        FloatValueMap<String> res = spellChecker.decode(s);
        Assert.assertEquals(s, 1, res.size());
        Assert.assertTrue(s, res.contains(expected));
      }
  
      Set<String> randomSubstitute = randomSubstitute(expected, 1);
      for (String s : randomSubstitute) {
        FloatValueMap<String> res = spellChecker.decode(s);
        Assert.assertEquals(s, 1, res.size());
        Assert.assertTrue(s, res.contains(expected));
      }
  
      Set<String> transpositions = transpositions(expected);
      for (String s : transpositions) {
        FloatValueMap<String> res = spellChecker.decode(s);
        Assert.assertEquals(s, 1, res.size());
        Assert.assertTrue(s, res.contains(expected));
      }
    }
  
    @Test
    public void nearKeyCheck() {
      SingleWordSpellChecker spellChecker = new SingleWordSpellChecker(
          1,
          SingleWordSpellChecker.TURKISH_Q_NEAR_KEY_MAP);
      String vocabulary = "elma";
      spellChecker.addWord(vocabulary);
      Assert.assertTrue(spellChecker.decode(vocabulary).contains(vocabulary));
  
      // "r" is near key "e" therefore it give a smaller penalty.
      FloatValueMap<String> res1 = spellChecker.decode("rlma");
      Assert.assertTrue(res1.contains("elma"));
      FloatValueMap<String> res2 = spellChecker.decode("ylma");
      Assert.assertTrue(res2.contains("elma"));
      Assert.assertTrue(res1.get("elma") < res2.get("elma"));
  
    }
  
    Set<String> randomDelete(String input, int d) {
      Set<String> result = new HashSet<>();
      Random r = new Random(0xbeef);
      for (int i = 0; i < 100; i++) {
        StringBuilder sb = new StringBuilder(input);
        for (int j = 0; j < d; j++) {
          sb.deleteCharAt(r.nextInt(sb.length()));
        }
        result.add(sb.toString());
      }
      return result;
    }
  
    Set<String> randomInsert(String input, int d) {
      Set<String> result = new HashSet<>();
      Random r = new Random(0xbeef);
      for (int i = 0; i < 100; i++) {
        StringBuilder sb = new StringBuilder(input);
        for (int j = 0; j < d; j++) {
          sb.insert(r.nextInt(sb.length() + 1), "x");
        }
        result.add(sb.toString());
      }
      return result;
    }
  
    Set<String> randomSubstitute(String input, int d) {
      Set<String> result = new HashSet<>();
      Random r = new Random(0xbeef);
      for (int i = 0; i < 100; i++) {
        StringBuilder sb = new StringBuilder(input);
        for (int j = 0; j < d; j++) {
          int start = r.nextInt(sb.length());
          sb.replace(start, start + 1, "x");
        }
        result.add(sb.toString());
      }
      return result;
    }
  
    Set<String> transpositions(String input) {
      Set<String> result = new HashSet<>();
      for (int i = 0; i < input.length() - 1; i++) {
        StringBuilder sb = new StringBuilder(input);
        char tmp = sb.charAt(i);
        sb.setCharAt(i, sb.charAt(i + 1));
        sb.setCharAt(i + 1, tmp);
        result.add(sb.toString());
      }
      return result;
    }
  
  
  }
  