Skip to content

Commit f475718

Browse files
authored
Add ISBN Verifier Exercise (#641)
1 parent 4e1bb9c commit f475718

File tree

7 files changed

+373
-0
lines changed

7 files changed

+373
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,14 @@
11401140
"prerequisites": [],
11411141
"difficulty": 3
11421142
},
1143+
{
1144+
"slug": "isbn-verifier",
1145+
"name": "ISBN Verifier",
1146+
"uuid": "f7309216-0ba7-4990-acd7-4a47eca949fb",
1147+
"practices": [],
1148+
"prerequisites": [],
1149+
"difficulty": 2
1150+
},
11431151
{
11441152
"slug": "say",
11451153
"name": "Say",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Instructions
2+
3+
The [ISBN-10 verification process][isbn-verification] is used to validate book identification numbers.
4+
These normally contain dashes and look like: `3-598-21508-8`
5+
6+
## ISBN
7+
8+
The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only).
9+
In the case the check character is an X, this represents the value '10'.
10+
These may be communicated with or without hyphens, and can be checked for their validity by the following formula:
11+
12+
```text
13+
(d₁ * 10 + d₂ * 9 + d₃ * 8 + d₄ * 7 + d₅ * 6 + d₆ * 5 + d₇ * 4 + d₈ * 3 + d₉ * 2 + d₁₀ * 1) mod 11 == 0
14+
```
15+
16+
If the result is 0, then it is a valid ISBN-10, otherwise it is invalid.
17+
18+
## Example
19+
20+
Let's take the ISBN-10 `3-598-21508-8`.
21+
We plug it in to the formula, and get:
22+
23+
```text
24+
(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
25+
```
26+
27+
Since the result is 0, this proves that our ISBN is valid.
28+
29+
## Task
30+
31+
Given a string the program should check if the provided string is a valid ISBN-10.
32+
Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.
33+
34+
The program should be able to verify ISBN-10 both with and without separating dashes.
35+
36+
## Caveats
37+
38+
Converting from strings to numbers can be tricky in certain languages.
39+
Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10').
40+
For instance `3-598-21507-X` is a valid ISBN-10.
41+
42+
[isbn-verification]: https://en.wikipedia.org/wiki/International_Standard_Book_Number
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"tomasnorre"
4+
],
5+
"files": {
6+
"solution": [
7+
"IsbnVerifier.php"
8+
],
9+
"test": [
10+
"IsbnVerifierTest.php"
11+
],
12+
"example": [
13+
".meta/example.php"
14+
]
15+
},
16+
"blurb": "Check if a given string is a valid ISBN-10 number.",
17+
"source": "Converting a string into a number and some basic processing utilizing a relatable real world example.",
18+
"source_url": "https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation"
19+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
class IsbnVerifier
6+
{
7+
public function isValid(string $isbn): bool
8+
{
9+
$isbn = str_replace("-", "", $isbn);
10+
if (strlen($isbn) !== 10) {
11+
return false;
12+
}
13+
14+
$i = 10;
15+
$isbnSum = 0;
16+
17+
if ($isbn[strlen($isbn) - 1] === 'X') {
18+
$isbnSum = 10;
19+
$isbn = substr($isbn, 0, -1);
20+
}
21+
22+
if (!is_numeric($isbn)) {
23+
return false;
24+
}
25+
26+
for ($j = 0, $jMax = strlen($isbn); $j < $jMax; $j++) {
27+
$intV = (int) $isbn[$j];
28+
$isbnSum += $i * $intV;
29+
$i--;
30+
}
31+
32+
return $isbnSum % 11 === 0;
33+
}
34+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[0caa3eac-d2e3-4c29-8df8-b188bc8c9292]
13+
description = "valid isbn"
14+
15+
[19f76b53-7c24-45f8-87b8-4604d0ccd248]
16+
description = "invalid isbn check digit"
17+
18+
[4164bfee-fb0a-4a1c-9f70-64c6a1903dcd]
19+
description = "valid isbn with a check digit of 10"
20+
21+
[3ed50db1-8982-4423-a993-93174a20825c]
22+
description = "check digit is a character other than X"
23+
24+
[9416f4a5-fe01-4b61-a07b-eb75892ef562]
25+
description = "invalid check digit in isbn is not treated as zero"
26+
27+
[c19ba0c4-014f-4dc3-a63f-ff9aefc9b5ec]
28+
description = "invalid character in isbn is not treated as zero"
29+
30+
[28025280-2c39-4092-9719-f3234b89c627]
31+
description = "X is only valid as a check digit"
32+
33+
[f6294e61-7e79-46b3-977b-f48789a4945b]
34+
description = "valid isbn without separating dashes"
35+
36+
[185ab99b-3a1b-45f3-aeec-b80d80b07f0b]
37+
description = "isbn without separating dashes and X as check digit"
38+
39+
[7725a837-ec8e-4528-a92a-d981dd8cf3e2]
40+
description = "isbn without check digit and dashes"
41+
42+
[47e4dfba-9c20-46ed-9958-4d3190630bdf]
43+
description = "too long isbn and no dashes"
44+
45+
[737f4e91-cbba-4175-95bf-ae630b41fb60]
46+
description = "too short isbn"
47+
48+
[5458a128-a9b6-4ff8-8afb-674e74567cef]
49+
description = "isbn without check digit"
50+
51+
[70b6ad83-d0a2-4ca7-a4d5-a9ab731800f7]
52+
description = "check digit of X should not be used for 0"
53+
54+
[94610459-55ab-4c35-9b93-ff6ea1a8e562]
55+
description = "empty isbn"
56+
57+
[7bff28d4-d770-48cc-80d6-b20b3a0fb46c]
58+
description = "input is 9 characters"
59+
60+
[ed6e8d1b-382c-4081-8326-8b772c581fec]
61+
description = "invalid characters are not ignored after checking length"
62+
63+
[daad3e58-ce00-4395-8a8e-e3eded1cdc86]
64+
description = "invalid characters are not ignored before checking length"
65+
66+
[fb5e48d8-7c03-4bfb-a088-b101df16fdc3]
67+
description = "input is too long but contains a valid isbn"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class IsbnVerifier
28+
{
29+
public function isValid(string $isbn): bool
30+
{
31+
throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__));
32+
}
33+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
class IsbnVerifierTest extends PHPUnit\Framework\TestCase
6+
{
7+
private IsbnVerifier $isbnVerifier;
8+
9+
public static function setUpBeforeClass(): void
10+
{
11+
require_once 'IsbnVerifier.php';
12+
}
13+
14+
public function setUp(): void
15+
{
16+
$this->isbnVerifier = new IsbnVerifier();
17+
}
18+
19+
/**
20+
* uuid: 0caa3eac-d2e3-4c29-8df8-b188bc8c9292
21+
*/
22+
public function testValidIsbn(): void
23+
{
24+
$this->assertTrue($this->isbnVerifier->isValid('3-598-21508-8'));
25+
}
26+
27+
/**
28+
* uuid: 19f76b53-7c24-45f8-87b8-4604d0ccd248
29+
*/
30+
public function testInvalidIsbnCheckDigit(): void
31+
{
32+
$this->assertFalse($this->isbnVerifier->isValid('3-598-21508-9'));
33+
}
34+
35+
/**
36+
* uuid: 4164bfee-fb0a-4a1c-9f70-64c6a1903dcd
37+
*/
38+
public function testValidIsbnWithACheckDigitOf10(): void
39+
{
40+
$this->assertTrue($this->isbnVerifier->isValid('3-598-21507-X'));
41+
}
42+
43+
/**
44+
* uuid: 3ed50db1-8982-4423-a993-93174a20825c
45+
*/
46+
public function testCheckDigitIsACharacterOtherThanX(): void
47+
{
48+
$this->assertFalse($this->isbnVerifier->isValid('3-598-21507-A'));
49+
}
50+
51+
/**
52+
* uuid: 9416f4a5-fe01-4b61-a07b-eb75892ef562
53+
*/
54+
public function testInvalidCheckDigitInIsbnIsNotTreatedAsZero(): void
55+
{
56+
$this->assertFalse($this->isbnVerifier->isValid('4-598-21507-B'));
57+
}
58+
59+
/**
60+
* uuid: c19ba0c4-014f-4dc3-a63f-ff9aefc9b5ec
61+
*/
62+
public function testInvalidCharacterInIsbnIsNotTreatedAsZero(): void
63+
{
64+
$this->assertFalse($this->isbnVerifier->isValid('3-598-P1581-X'));
65+
}
66+
67+
/**
68+
* uuid: 28025280-2c39-4092-9719-f3234b89c627
69+
*/
70+
public function testXIsOnlyValidAsACheckDigit(): void
71+
{
72+
$this->assertFalse($this->isbnVerifier->isValid('3-598-2X507-9'));
73+
}
74+
75+
/**
76+
* uuid: f6294e61-7e79-46b3-977b-f48789a4945b
77+
*/
78+
public function testValidIsbnWithoutSeparatingDashes(): void
79+
{
80+
$this->assertTrue($this->isbnVerifier->isValid('3598215088'));
81+
}
82+
83+
/**
84+
* uuid: 185ab99b-3a1b-45f3-aeec-b80d80b07f0b
85+
*/
86+
public function testIsbnWithoutSeparatingDashesAndXAsCheckDigit(): void
87+
{
88+
$this->assertTrue($this->isbnVerifier->isValid('359821507X'));
89+
}
90+
91+
/**
92+
* uuid: 7725a837-ec8e-4528-a92a-d981dd8cf3e2
93+
*/
94+
public function testIsbnWithoutCheckDigitAndDashes(): void
95+
{
96+
$this->assertFalse($this->isbnVerifier->isValid('359821507'));
97+
}
98+
99+
/**
100+
* uuid: 47e4dfba-9c20-46ed-9958-4d3190630bdf
101+
*/
102+
public function testTooLongIsbnAndNoDashes(): void
103+
{
104+
$this->assertFalse($this->isbnVerifier->isValid('3598215078X'));
105+
}
106+
107+
/**
108+
* uuid: 737f4e91-cbba-4175-95bf-ae630b41fb60
109+
*/
110+
public function testTooShortIsbn(): void
111+
{
112+
$this->assertFalse($this->isbnVerifier->isValid('00'));
113+
}
114+
115+
/**
116+
* uuid: 5458a128-a9b6-4ff8-8afb-674e74567cef
117+
*/
118+
public function testIsbnWithoutCheckDigit(): void
119+
{
120+
$this->assertFalse($this->isbnVerifier->isValid('3-598-21507'));
121+
}
122+
123+
/**
124+
* uuid: 70b6ad83-d0a2-4ca7-a4d5-a9ab731800f7
125+
*/
126+
public function testCheckDigitOfXShouldNotBeUsedForZero(): void
127+
{
128+
$this->assertFalse($this->isbnVerifier->isValid('3-598-21515-X'));
129+
}
130+
131+
/**
132+
* uuid: 94610459-55ab-4c35-9b93-ff6ea1a8e562
133+
*/
134+
public function testEmptyIsbn(): void
135+
{
136+
$this->assertFalse($this->isbnVerifier->isValid(''));
137+
}
138+
139+
/**
140+
* uuid: 7bff28d4-d770-48cc-80d6-b20b3a0fb46c
141+
*/
142+
public function testInputIs9Characters(): void
143+
{
144+
$this->assertFalse($this->isbnVerifier->isValid('134456729'));
145+
}
146+
147+
/**
148+
* uuid: ed6e8d1b-382c-4081-8326-8b772c581fec
149+
*/
150+
public function testInvalidCharactersAreNotIgnoredAfterCheckingLength(): void
151+
{
152+
$this->assertFalse($this->isbnVerifier->isValid('3132P34035'));
153+
}
154+
155+
/**
156+
* uuid: daad3e58-ce00-4395-8a8e-e3eded1cdc86
157+
*/
158+
public function testCatchInvalidCharactersInOnOtherwiseValidIsbn(): void
159+
{
160+
$this->assertFalse($this->isbnVerifier->isValid('3598P215088'));
161+
}
162+
163+
/**
164+
* uuid: fb5e48d8-7c03-4bfb-a088-b101df16fdc3
165+
*/
166+
public function testInputIsTooLongButContainsAValidIsbn(): void
167+
{
168+
$this->assertFalse($this->isbnVerifier->isValid('98245726788'));
169+
}
170+
}

0 commit comments

Comments
 (0)