Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
44
Task/IBAN/Kotlin/iban-1.kotlin
Normal file
44
Task/IBAN/Kotlin/iban-1.kotlin
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// version 1.0.6
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
object IBAN {
|
||||
/* List updated to release 73, January 2017, of IBAN Registry (75 countries) */
|
||||
private const val countryCodes = "" +
|
||||
"AD24 AE23 AL28 AT20 AZ28 BA20 BE16 BG22 BH22 BR29 " +
|
||||
"BY28 CH21 CR22 CY28 CZ24 DE22 DK18 DO28 EE20 ES24 " +
|
||||
"FI18 FO18 FR27 GB22 GE22 GI23 GL18 GR27 GT28 HR21 " +
|
||||
"HU28 IE22 IL23 IQ23 IS26 IT27 JO30 KW30 KZ20 LB28 " +
|
||||
"LC32 LI21 LT20 LU20 LV21 MC27 MD24 ME22 MK19 MR27 " +
|
||||
"MT31 MU30 NL18 NO15 PK24 PL28 PS29 PT25 QA29 RO24 " +
|
||||
"RS22 SA24 SC31 SE24 SI19 SK24 SM27 ST25 SV28 TL23 " +
|
||||
"TN24 TR26 UA29 VG24 XK20"
|
||||
|
||||
fun isValid(iban: String): Boolean {
|
||||
// remove spaces from IBAN
|
||||
var s = iban.replace(" ", "")
|
||||
|
||||
// check country code and length
|
||||
s.substring(0, 2) + s.length in countryCodes || return false
|
||||
|
||||
// move first 4 characters to the end
|
||||
s = s.substring(4) + s.substring(0, 4)
|
||||
|
||||
// replace A to Z with numbers 10 To 35
|
||||
s = s.replace(Regex("[A-Z]")) { (10 + (it.value[0] - 'A')).toString() }
|
||||
|
||||
// check whether mod 97 calculation gives a remainder of 1
|
||||
return BigInteger(s) % BigInteger.valueOf(97L) == BigInteger.ONE
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val ibans = arrayOf(
|
||||
"GB82 WEST 1234 5698 7654 32",
|
||||
"GB82 TEST 1234 5698 7654 32"
|
||||
)
|
||||
for (iban in ibans) {
|
||||
val valid = IBAN.isValid(iban)
|
||||
println(iban + if (valid) " may be valid" else " is not valid")
|
||||
}
|
||||
}
|
||||
111
Task/IBAN/Kotlin/iban-2.kotlin
Normal file
111
Task/IBAN/Kotlin/iban-2.kotlin
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package rosettacode
|
||||
|
||||
import rosettacode.Outcome.Fail
|
||||
import rosettacode.Outcome.Ok
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.math.BigInteger
|
||||
|
||||
sealed class Outcome {
|
||||
|
||||
object Ok : Outcome() {
|
||||
override fun toString(): String = "Ok"
|
||||
}
|
||||
|
||||
data class Fail(val error: String) : Outcome()
|
||||
}
|
||||
|
||||
fun Boolean.asOutcome(error: String = "") = when (this) {
|
||||
true -> Ok
|
||||
false -> Fail(error)
|
||||
}
|
||||
|
||||
/**
|
||||
* A validator based on a pattern (e.g GB2!n4!a6!n8!n) as defined in the IBAN REGISTRY
|
||||
* Doesn't handle variable length IBANs - sufficient for Rosetta
|
||||
*
|
||||
* IBAN REGISTRY: https://www.swift.com/swift_resource/9606
|
||||
*/
|
||||
class Validator(private val pattern: String) {
|
||||
private val triplet: Regex = """(\d+)!([n,a,c])""".toRegex() // e.g: 10!n
|
||||
private val rules: List<Regex>
|
||||
|
||||
init {
|
||||
val countTypePairs = triplet
|
||||
.findAll(pattern)
|
||||
.map { Pair(it.groups[1]!!.value.toInt(), it.groups[2]!!.value) }
|
||||
|
||||
rules = countTypePairs.fold(emptyList()) { acc, p ->
|
||||
acc + when (p.second) {
|
||||
"n" -> """[0-9]{${p.first}}""".toRegex()
|
||||
"a" -> """[A-Z]{${p.first}}""".toRegex()
|
||||
"c" -> """[A-Z,0-9]{${p.first}}""".toRegex()
|
||||
else -> throw IllegalArgumentException("Unexpected pattern: $pattern")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isValid(ccbban: String): Outcome {
|
||||
var k = 0
|
||||
|
||||
for (rule in rules) {
|
||||
when (val match = rule.matchAt(ccbban, k)) {
|
||||
null -> return Fail("failed rule: $rule")
|
||||
else -> {
|
||||
k = match.range.last + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return (k == ccbban.length).asOutcome("IBAN is too long")
|
||||
}
|
||||
}
|
||||
|
||||
object Registry : MutableMap<String, Validator> by mutableMapOf()
|
||||
|
||||
fun String.split(i: Int) = Pair(this.substring(0, i), this.substring(i))
|
||||
fun String.toDigits() = this.fold("") { acc, ch -> acc + Character.getNumericValue(ch) }
|
||||
fun Pair<String, String>.swap() = this.second + this.first
|
||||
|
||||
object CDCheck {
|
||||
private val _97 = BigInteger.valueOf(97)
|
||||
private val _1 = BigInteger.ONE
|
||||
|
||||
operator fun invoke(digits: String) = (BigInteger(digits).mod(_97) == _1).asOutcome("Invalid Check Digits")
|
||||
}
|
||||
|
||||
fun validate(iban: String): Outcome {
|
||||
val normalized = iban.replace(" ", "").uppercase().also {
|
||||
if (it.length < 5) {
|
||||
return Fail("IBAN is too short")
|
||||
}
|
||||
}
|
||||
|
||||
val (cc, tail) = normalized.split(2)
|
||||
val validator = Registry[cc] ?: return Fail("Unknown Country Code: $cc")
|
||||
|
||||
return when (val outcome = validator.isValid(tail)) {
|
||||
is Fail -> outcome
|
||||
is Ok -> {
|
||||
val allDigits = normalized
|
||||
.split(4).swap() // (CC + CD) -> end
|
||||
.toDigits() // letters -> digits
|
||||
CDCheck(allDigits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Registry["GB"] = Validator("GB2!n4!a6!n8!n")
|
||||
Registry["DE"] = Validator("DE2!n8!n10!n")
|
||||
|
||||
validate("GB82WEST12345698765432").also { println(it) } // Ok
|
||||
validate("GB82 WEST 1234 5698 7654 32").also { println(it) } // Ok
|
||||
|
||||
validate("DE89370400440532013000").also { println(it) } // Ok
|
||||
|
||||
validate("XX82WEST1234569A765432").also { println(it) } // Unknown Country Code: XX
|
||||
validate("GB82").also { println(it) } // IBAN is too short
|
||||
validate("GB82WEST1234569A765432").also { println(it) } // failed rule: [0-9]{8}
|
||||
validate("GB82WE5T1234569A765432").also { println(it) } // failed rule: [A-Z]{4}
|
||||
validate("GB82WEST123456987654329").also { println(it) } // IBAN is too long
|
||||
validate("GB80WEST12345698765432").also { println(it) } // Invalid Check Digits
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue