Coverage Summary for Class: LuhnAlgorithm (ru.eda.plgn.bizgen.core.utils)

Class Method, % Branch, % Line, % Instruction, %
LuhnAlgorithm 100% (3/3) 100% (10/10) 100% (14/14) 100% (96/96)
LuhnAlgorithm$LuhnSumMode 100% (2/2) 100% (3/3) 100% (29/29)
Total 100% (5/5) 100% (10/10) 100% (17/17) 100% (125/125)


 package ru.eda.plgn.bizgen.core.utils
 
 /**
  * Алгоритм Луна.
  *
  * **See Also:** [Алгоритм Луна](https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D1%83%D0%BD%D0%B0)
  *
  * @author Dmitry_Emelyanenko
  */
 object LuhnAlgorithm {
 
   /**
    * Рассчитывается контрольная цифра по алгоритму Луна на основе переданного числа.
    *
    * ***ВАЖНО***: длина исходного числа должна быть нечетной
    *
    * @param source переданное число
    * @return контрольная цифра
    */
   fun calculateCheckDigit(source: String): Int {
     require(source.length % 2 == 1) { "The source: $source (length = ${source.length}) must have an odd length" }
 
     return calculateLuhnSum(source, LuhnSumMode.FIND_CHECK_DIGIT)
       .let { (10 - (it % 10)) % 10 }
   }
 
   /**
    * Проверка, что переданное число валидное.
    *
    * ***ВАЖНО***: длина исходного числа должна быть четной.
    *
    * То есть рассчитывается контрольная цифра по алгоритму Луна, если:
    * - Остаток от деления на 10 = 0 - корректное значение
    * - Остаток от деления на 10 != 0 - некорректное значение
    *
    * @param source исходное число
    * @return true - валидное число, false - невалидное число
    */
   fun isValid(source: String): Boolean {
     require(source.length % 2 == 0) { "The source: $source (length = ${source.length}) must have an even length" }
 
     return (calculateLuhnSum(source, LuhnSumMode.VALID_SOURCE) % 10) == 0
   }
 
   /**
    * Расчет суммы по алгоритму Луна.
    *
    * Алгоритм следующий:
    * - Происходит переворачивание (revert) исходного числа: "1234" --> "4321"
    * - На основе режима [calculateMode] выбирается начальный индекс с которого цифры будут умножаться на 2.
    * - Если режим [LuhnSumMode.FIND_CHECK_DIGIT] - то все цифры, которые находятся на **четных** позициях (0,2,4,6 ...) умножаются
    * - Если режим [LuhnSumMode.VALID_SOURCE] - то все цифры, которые находятся на **нечетных** позициях (1,3,5,7 ...) умножаются
    * - Если после умножения получается число больше 9, то отнимается 9
    * - Полученные значения после умножения и значения, которые не умножались суммируются
    *
    * @param source исходное число
    * @param calculateMode режим расчета (для нахождения контрольной цифры или для валидации)
    * @return рассчитанная цифра
    */
   private fun calculateLuhnSum(source: String, calculateMode: LuhnSumMode): Int {
     return source.reversed()
       .mapIndexed { index, char ->
         val digit = char.digitToInt()
 
         when {
           index % 2 == calculateMode.mod -> digit
           digit < 5 -> digit * 2
           else -> digit * 2 - 9
         }
       }.sum()
   }
 
   /**
    * Режим расчета суммы по алгоритму Луна.
    *
    * @property mod значение остатка от деления на 2, то есть 1 - нечетное число, 0 - четное число
    */
   private enum class LuhnSumMode(val mod: Int) {
     /** Режим поиска проверочной цифры. */
     FIND_CHECK_DIGIT(1),
 
     /** Режим валидации исходного числа. */
     VALID_SOURCE(0)
   }
 }