NML Says

Open Source Development 1

References for This Lesson

Model Solutions to Exercises

These solutions are available as FOSS from

https://codeberg.org/arosano/exercises_osd0.git

OSD.0.1 - OSD.0.2

Example 1. README.md

We have not succeded in quoting markdown inside markdown instead we refer to the live README.

OSD.0.0 FizzBuzz

Example 2. testsuite.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
'''
        testsuite.py
    
    Copyright (c) 2025 arosano/Niels Müller Larsen
    Licensed under the MIT license: https://opensource.org/license/mit

'''
import unittest
from fizzbuzz import *

class Testing(unittest.TestCase):

        def test_fizz3(self):
                self.assertEqual(fizzbuzz(3), 'Fizz')
        
        def test_fizz5(self):
                self.assertEqual(fizzbuzz(5), 'Buzz')
        
        def test_fizz15(self):
                self.assertEqual(fizzbuzz(15), 'FizzBuzz')

        def test_fizzn(self):
                self.assertEqual(fizzbuzz(31), '31')

if __name__ == '__main__':
        unittest.main()
Example 3. fizzbuzz.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
'''
    fizzbuzz.py


    Copyright (c) 2025 arosano/Niels Müller Larsen
    Licensed under the MIT license: https://opensource.org/license/mit
'''

def fizzbuzz(n):
    s = ''
    if n % 3 == 0:
        s += 'Fizz'
    if n % 5 == 0:
        s += 'Buzz'
    if s == '':
        s = f'{n}'
    return s

if __name__ == '__main__':
    for i in range (1, 101):
        print(fizzbuzz(i))

To be tested by

Example 4. Running the Test
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 $ python testsuite.py -v
test_fizz15 (__main__.Testing.test_fizz15) ... ok
test_fizz3 (__main__.Testing.test_fizz3) ... ok
test_fizz5 (__main__.Testing.test_fizz5) ... ok
test_fizzn (__main__.Testing.test_fizzn) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK
Example 5. Running the Program
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ python fizzbuzz.py
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
....
94
Buzz
Fizz
97
98
Fizz
Buzz

OSD.0.1 Palindromy

Example 6. testsuite.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
'''
        testsuite.py
        
        Copyright (c) 2025 arosano/Niels Müller Larsen
        Licensed under the MIT license: https://opensource.org/license/mit

'''
import unittest
from palindromy import *

class Testing(unittest.TestCase):

        def test_isPal1(self):
                self.assertFalse(isPalindrome('Fizz'))

        def test_isPal2(self):
                self.assertFalse(isPalindrome('1234'))

        def test_isPal3(self):
                self.assertTrue(isPalindrome('regninger'))

        def test_isPal4(self):
                self.assertTrue(isPalindrome('RegninGer'))

        def test_isPal5(self):
                self.assertTrue(isPalindrome('A man, a plan, a canal -  Panama'))

if __name__ == '__main__':
        unittest.main()
Example 7. palindromy.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
'''
        palindromy.py
        
        Copyright (c) 2025 arosano/Niels Müller Larsen
        Licensed under the MIT license: https://opensource.org/license/mit
'''
import re
import sys

def isPalindrome(txt):
        if len(txt) <= 1:
                return True
        txt = txt.lower()
        txt = re.sub(r'[^\w]', '', txt)
        return txt[0] == txt[-1] and isPalindrome(txt[1:-1])

if __name__ == '__main__':
        print(f'Is "{sys.argv[1]}" a palindrome? {isPalindrome(sys.argv[1])}!')
Example 8. Running the Testsuite
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ python testsuite.py -v
test_isPal1 (__main__.Testing.test_isPal1) ... ok
test_isPal2 (__main__.Testing.test_isPal2) ... ok
test_isPal3 (__main__.Testing.test_isPal3) ... ok
test_isPal4 (__main__.Testing.test_isPal4) ... ok
test_isPal5 (__main__.Testing.test_isPal5) ... ok

----------------------------------------------------------------------
Ran 5 tests in 0.001s

OK
Example 9. Running the Program
1
2
3
4
$ python palindromy.py 'Trials and Tirbulations'     
Is Trials and Tirbulations a palindrome? False!
$ python palindromy.py 'Dennis and Edna sinned'  
Is Dennis and Edna sinned a palindrome? True!

OSD.0.2 Primality

The solution to this exercise is made in JavaScript for the versatility.

Example 10. test-suite.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
        test-suite.js --        tests for primes

        Copyright (c) Niels Müller Larsen
        Licensed under the MIT license: https://opensource.org/license/mit
*/

const test = require('node:test');          // new in node as of V18
const assert = require('assert/strict');

const {                                     // the functions to be tested
        isPrime,
        isPrimeArchaic,
        isPrimeWell
} = require('./mathlib');

// test synch
test('sync 25', function() {
    return assert.equal(isPrime(25), false);
});

// test synch
test('sync 257', function() {
    return assert.ok(isPrime(257));
});

// test synch
test('sync 9007199254740991', function() {
    return assert.equal(isPrime(9007199254740991), false);
});

// test synch
test('sync 9007195909437503', function() {
    return assert.equal(isPrime(9007195909437503), false);
});

// tests asynch
test('async small no', async function(t) {
    await t.test('async 257', function() {
        assert.ok(isPrime(257));
    });
});

// test synch
test('sync 256', function() {
    return assert.equal(isPrime(256), false);
});


// test alternative isPrime functions

// test synch
test('sync archaic 257', function() {
    return assert.ok(isPrimeArchaic(257));
});

// test synch
test('sync hm 257', function() {
    return assert.ok(isPrimeWell(257));
});
Example 11. mathlib.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
        mathlib.js
        various math related functions

        Copyright (c) Niels Müller Larsen
        Licensed under the MIT license: https://opensource.org/license/mit
*/

module.exports = {
        isPrime: function (n) {
                if (n % 2 === 0 || n % 3 === 0)
                        return false;
                
                const limit = Math.sqrt(n);
                const magic = 6;
                let i = 1;
                let divm1 = magic * i - 1;
                let divp1 = magic * i + 1;

                while (divm1 <= limit || divp1 <= limit) {
                        if (n % divm1 === 0 || n % divp1 === 0)
                                return false;
                        i++;
                        divm1 = magic * i - 1;
                        divp1 = magic * i + 1;
                }
                return true;
        },
        
        isPrimeArchaic: function (n) {
                if (n % 2 === 0)
                        return false;
                for (let i = 3; i <= Math.sqrt(n); i += 2) 
                        if (n % i === 0)
                                return false;
                return true;
        },

        isPrimeWell: function(n) {
                for (let i = 2; i < n; i++)
                        if (n % i === 0)
                                return false;
                return true;
        }
}
Example 12. primes.js
1
2
3
4
5
6
7
8
const lib = require('./mathlib')

const doSomething = function (n) {
        return lib.isPrime(n);
};

let candidate = process.argv[2];
console.log(`Is ${candidate} prime: ${doSomething(candidate)}`);
Example 13. Running the Testsuite
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
$ node --test                                                                        
✔ sync 25 (1.884537ms)
✔ sync 257 (0.246666ms)
✔ sync 9007199254740991 (0.320865ms)
✔ sync 9007195909437503 (303.511543ms)
▶ async small no
  ✔ async 257 (0.3208ms)
▶ async small no (0.726882ms)
✔ sync 256 (0.226422ms)
✔ sync archaic 257 (0.224919ms)
✔ sync hm 257 (0.341712ms)
ℹ tests 9
ℹ suites 0
ℹ pass 9
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 410.47722
Example 14. Running the Program
1
2
3
4
5
6
7
8
$ node prog_osd02.js 157
Is 157 prime: true
$ node prog_osd02.js 257 
Is 257 prime: true
$ node prog_osd02.js 9007199254740991		// 6361 * 69431 * 20394401
Is 9007199254740991 prime: false
$ node prog_osd02.js 9007195909437503		// 94906249 * 94906247 
Is 9007195909437503 prime: false			// (15817708*6-1) * (15817708*6+1)

From the Previous Lesson

Some words about use cases, in particular the diagrams.

Some Flashbacks Before the Exercises

This is from the most recent lesson. Important to remember when writing tests.

SitePoint advocates what they call the AAA pattern.

The Arrange, Act and Assert pattern is a common strategy used to write and organize unit tests. It works in the following way:

  • During the Arrange phase, all the objects and variables needed for the test are set.
  • Next, during the Act phase, the function/method/class under test is called.
  • In the end, during the Assert phase, we verify the outcome of the test.

This strategy provides a clean approach to organizing unit tests by separating all the main parts of a test: setup, execution and verification. Plus, unit tests are easier to read, because they all follow the same structure.

Coding Memories

Example 15. Creating a Class in JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
    * Student.js
    *
    * Copyright (c) 2025, Niels Müller Larsen
    * Licensed under the MIT license: https://opensource.org/license/mit
*/

module.exports = class Student {
    constructor(first, last, city, country) {
        if (!
            (typeof(first) == 'string'
                && typeof(last) === 'string'
                && typeof(city) === 'string'
                && typeof(country) === 'string'
            )
		) {
       		return false;
		}
        this.first = first;
        this.last = last;
        this.city = city;
        this.country = country;
    }
}
Example 16. Using a Class in JavaScript
1
2
3
4
5
6
7
8
9
const Stu = require('./Student.js');

let a = 'Niels';
let b = 'Larsen';
let c = 'Aarhus';
let d = 'Denmark';

let s = new Stu(a, b, c, d);
console.log(`Student ${s.first} is from ${s.city}, ${s.country}`);
Example 17. Creating a Class in Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
'''
    Student.py

    Copyright (c) 2025, Niels Müller Larsen
    Licensed under the MIT license: https://opensource.org/license/mit
'''

class Student:
    def __init__(self, first, last, city, country):
        self.first = first
        self.last = last
        self.city = city
        self.country = country
Example n. Using a Class in Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
'''
test.py
'''

from Student import Student

first = 'Niels'
last = 'Larsen'
city = 'Aarhus'
ctry = 'Denmark' 

s = Student(first, last, city, ctry)
print(s)
print(f'Student {s.first} is from {s.city}, {s.country}')

Exercises

The rules for handing in assignments may be found in the README

Todays submissions MUST be in a repo called exercises_osd1. You MAY solve them in Python or JavaScript.

Exercise OSD.1.0

Given the following specification, first write a testsuite containing tests for the methods of the class that fulfill the specification below. Then write a program that demonstrates the results. Remember to test frequently. The tests help you.

Create a class Rational. The constructor should have two properties: a numerator, and a denominator. Both MUST be integers.

The constructor may take zero, one or two arguments.

The class MUST contain the following methods:

reduce()
This method must reduce the fraction as much as possible
gcd(a,b)
For the reduce I will give you the following implementation of Euclid’s algorithm:
const gcd = function (a, b) {
    if (b == 0) {
       return a;
    } else {
        return gcd(b, a % b);
 	}i
}

This must be a static method.

negate()
This method must change the sign of the fraction.
invert()
This method must swap numerator and denominator.
cmp(rational)
This take another object of the class Rational as input. Then it compares the value of the object with that of the input fraction. It must return -1 if the value af the instance is smaller, 0 if they are equal, and +1 if the value of the instance is greater than that of the input fraction.
toString()
Returns a string representation af the fraction eg 3/7. If the value is 0, 0 must be returned, if the value is an integer, the integer, eg 7, must be returned.
add(rational)
Adds the value of the input fraction to that of the instance.
sub(rational)
Subtracts the value of the input fraction form that of the instance.
mul(rational)
Multiplies the value of the input fraction with that of the instance.
div(rational)
Divides the value of the input fraction into that of the instance.