Last active
August 29, 2015 14:13
-
-
Save fbwright/a26c65c74f9283aaeccf to your computer and use it in GitHub Desktop.
ISBN Validator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## [2015-01-12] Challenge #197 [Easy] ISBN Validator | |
## 2015-01-21 15:08 - by Frankie Brogan | |
import strutils, parseopt2, version, strfmt, math, unittest | |
type | |
TISBNKind* = enum | |
ISBN10, ISBN13 | |
TISBN* = object | |
case kind*: TISBNKind | |
of ISBN10: isbn_10*: array[0..9, int] | |
of ISBN13: isbn_13*: array[0..12, int] | |
const | |
Testing: bool = false | |
ProgramName: string = "ISBN Validator" | |
ProgramVersion: TVersion = (0, 1, 7, versionAlpha) | |
Documentation: string = """ | |
ISBN Validator | |
Checks whether the given ISBNs are valid numbers or not. | |
Usage: | |
isbn check <isbn>... | |
isbn convert <isbn>... | |
isbn generate [<n>] | |
isbn (-h | --help) | |
isbn (-v | --version) | |
Options: | |
-h --help Show this screen. | |
-v --version Show version. | |
""" | |
proc parseInt(c: char): int = | |
return int(c) - int('0') | |
proc `$`*(isbn: TISBN): string = | |
case isbn.kind | |
of ISBN10: | |
isbn.isbn_10[0..8].map(proc(x: int): string = $x).join("") & | |
(if isbn.isbn_10[9] == 10: "X" else: $isbn.isbn_10[9]) | |
of ISBN13: | |
isbn.isbn_13[0..11].map(proc(x: int): string = $x).join("") & | |
(if isbn.isbn_13[12] == 10: "0" else: $isbn.isbn_13[12]) | |
proc newISBN*(code: string): TISBN = | |
let s = code. | |
toLower(). | |
replace(sub = " ", by = ""). | |
replace(sub = "-", by = "") | |
case s.len() | |
of 10: | |
result.kind = ISBN10 | |
for i in 0..8: | |
result.isbn_10[i] = parseInt(s[i]) | |
result.isbn_10[9] = if s[9] == 'x': 10 else: parseInt(s[9]) | |
of 13: | |
result.kind = ISBN13 | |
for i in 0..11: | |
result.isbn_13[i] = parseInt(s[i]) | |
result.isbn_13[12] = if s[12] == '0': 10 else: parseInt(s[12]) | |
else: | |
raise newException(ValueError, s & " is not a valid ISBN number.") | |
proc calculate_checksum*(isbn: TISBN): int = | |
case isbn.kind | |
of ISBN10: | |
let code = isbn.isbn_10 | |
for i in 0..8: | |
result += code[i] * (10 - i) | |
result = (11 - (result mod 11)) mod 11 | |
of ISBN_13: | |
let code = isbn.isbn_13 | |
for i in 0..11: | |
result += code[i] * (if i mod 2 == 0: 1 else: 3) | |
result = 10 - (result mod 10) | |
proc toISBN13*(isbn: TISBN): TISBN = | |
if isbn.kind == ISBN13: | |
return isbn | |
result.kind = ISBN13 | |
result.isbn_13[0] = 9 | |
result.isbn_13[1] = 7 | |
result.isbn_13[2] = 8 | |
for i in 0..8: | |
result.isbn_13[i+3] = isbn.isbn_10[i] | |
result.isbn_13[12] = result.calculate_checksum() | |
proc generateISBN*(kind: TISBNKind = ISBN10): TISBN = | |
result.kind = kind | |
case kind | |
of ISBN10: | |
for i in 0..8: | |
result.isbn_10[i] = random(10) | |
result.isbn_10[9] = calculate_checksum(result) | |
of ISBN13: | |
for i in 0..11: | |
result.isbn_13[i] = random(10) | |
result.isbn_13[12] = calculate_checksum(result) | |
proc checksum*(isbn: TISBN): int = | |
case isbn.kind | |
of ISBN10: isbn.isbn10[9] | |
of ISBN13: isbn.isbn13[12] | |
proc validate*(isbn: TISBN): bool = | |
isbn.calculate_checksum() == isbn.checksum() | |
proc writeHelp() = | |
echo(Documentation) | |
proc writeVersion() = | |
echo(ProgramName, " - Version ", ProgramVersion) | |
when isMainModule: | |
type | |
TCommand = enum | |
cmdNone, cmdError, cmdHelpOrVersion, cmdCheck, cmdConvert, cmdGenerate | |
var | |
isbns: seq[TISBN] = @[] | |
number: int = 0 | |
command: TCommand = cmdNone | |
for kind, key, val in getopt(): | |
case kind | |
of cmdArgument: | |
case command | |
of cmdNone: | |
command = case key | |
of "check": cmdCheck | |
of "convert": cmdConvert | |
of "generate": cmdGenerate | |
else: cmdError | |
of cmdCheck, cmdConvert: | |
try: | |
let isbn: TISBN = newISBN(key) | |
isbns.add(isbn) | |
except ValueError: | |
echo("Error: $1 is not a valid ISBN number." % [key]) | |
of cmdGenerate: | |
if number <= 0: | |
number = parseInt(key) | |
if number <= 0: | |
number = 1 | |
of cmdError, cmdHelpOrVersion: | |
discard | |
of cmdLongOption, cmdShortOption: | |
case key | |
of "help", "h": | |
command = cmdHelpOrVersion | |
writeHelp() | |
of "version", "v": | |
command = cmdHelpOrVersion | |
writeVersion() | |
of cmdEnd: | |
assert(false) | |
case command | |
of cmdNone, cmdError: | |
writeHelp() | |
of cmdHelpOrVersion: | |
discard | |
of cmdCheck: | |
for isbn in isbns: | |
printlnfmt("{0: <75}[{1}]", $isbn, if isbn.validate(): "OK" else: " ") | |
of cmdConvert: | |
for isbn in isbns: | |
echo(isbn.toISBN13()) | |
of cmdGenerate: | |
if number <= 0: | |
number = 1 | |
randomize() | |
for i in 1..number: | |
echo(generateISBN()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import unittest, isbn | |
const | |
strings_isbn_10 = [ | |
"097522980X", "0306406152", "1566199093"] | |
expected_isbn_10 = [ | |
[0, 9, 7, 5, 2, 2, 9, 8, 0, 10], [0, 3, 0, 6, 4, 0, 6, 1, 5, 2], | |
[1, 5, 6, 6, 1, 9, 9, 0, 9, 3]] | |
strings_isbn_13 = [ | |
"9781566199094"] | |
expected_isbn_13 = [ | |
[9, 7, 8, 1, 5, 6, 6, 1, 9, 9, 0, 9, 4]] | |
suite "Testing newISBN": | |
test "newISBN - ISBN10": | |
for i in 0..strings_isbn_10.high: | |
let isbn = newISBN(strings_isbn_10[i]) | |
check: | |
isbn.kind == ISBN10 | |
isbn.isbn_10 == expected_isbn_10[i] | |
test "newISBN - ISBN13": | |
for i in 0..strings_isbn_13.high: | |
let isbn = newISBN(strings_isbn_13[i]) | |
check: | |
isbn.kind == ISBN13 | |
isbn.isbn_13 == expected_isbn_13[i] | |
suite "Main suite": | |
setup: | |
var isbns_10, isbns_13: seq[TISBN] | |
isbns_10 = @[] | |
isbns_13 = @[] | |
for isbn in strings_isbn_10: | |
isbns_10.add(newISBN(isbn)) | |
for isbn in strings_isbn_13: | |
isbns_13.add(newISBN(isbn)) | |
teardown: | |
discard | |
test "`$` - ISBN10": | |
for i in 0..isbns_10.high: | |
check: $isbns_10[i] == strings_isbn_10[i] | |
test "`$` - ISBN10": | |
for i in 0..isbns_13.high: | |
check: $isbns_13[i] == strings_isbn_13[i] | |
test "validate - ISBN10": | |
for isbn in isbns_10: | |
check validate(isbn) | |
test "validate - ISBN10": | |
for isbn in isbns_13: | |
check validate(isbn) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type | |
TVersionPhase* = enum | |
versionPreAlpha, versionAlpha, versionBeta, versionReleaseCandidate | |
TVersion* = tuple[ | |
major, minor, release: int, | |
phase: TVersionPhase] | |
proc `$`*(phase: TVersionPhase): string = | |
case phase | |
of versionPreAlpha: "-pre-alpha" | |
of versionAlpha: "-alpha" | |
of versionBeta: "-beta" | |
else: "" | |
proc `$`*(version: TVersion): string = | |
result = $version.major & "." & | |
$version.minor & "." & | |
$version.release & | |
$version.phase |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment