From 37415ba032e8c707ff4f28a2977f21d59e95d451 Mon Sep 17 00:00:00 2001 From: christian-byrne Date: Sun, 13 Oct 2024 21:14:37 -0700 Subject: [PATCH] Functions 12-18 LA01 --- .../large_assignment_01.sml | 213 ++++++++++++++---- test-la-01.sh | 3 +- .../test_dec2BaseN.sml | 53 +++++ .../test_dropNth.sml | 63 ++++++ .../test_indexOf.sml | 43 ++++ .../test_insertionSort.sml | 54 +++++ .../test_subList.sml | 4 + .../test_substring.sml | 67 ++++++ 8 files changed, 453 insertions(+), 47 deletions(-) create mode 100644 tests/tests-large_assignment_01/test_dec2BaseN.sml create mode 100644 tests/tests-large_assignment_01/test_dropNth.sml create mode 100644 tests/tests-large_assignment_01/test_indexOf.sml create mode 100644 tests/tests-large_assignment_01/test_substring.sml diff --git a/src/large_assignment_01/large_assignment_01.sml b/src/large_assignment_01/large_assignment_01.sml index b9120f4..48acf90 100644 --- a/src/large_assignment_01/large_assignment_01.sml +++ b/src/large_assignment_01/large_assignment_01.sml @@ -1,13 +1,14 @@ (* * Author: Christian Byrne * Date: 9/27/24 - * Large Assignment #1 - * Desc: Practice functions in SML, - * focusing on pattern matching - * and recursion. + * Large Assignment #01 + * Desc: Practice functions in SML, focusing on features of the + * language such as pattern matching, currying, partial + * application, and higher-order functions. *) + (* * Type: `int * int * int -> bool` * Desc: The triangle inequality theorem states that the sum of any @@ -91,21 +92,21 @@ fun suffix([], _) = true | suffixEqual(x::a, y::b) = if x = y then suffixEqual(a, b) else false val truncateCount = listLength(li2) - listLength(li1) in + (* Handle case: li1 longer than li2 *) if truncateCount < 0 then false else suffixEqual(li1, truncatePrefix(truncateCount, li2)) end; - (* * Type: `'a list * int → 'a` * Desc: This function takes a list and an integer i and * returns the ith element in the list. Start the * indexing at 0 *) -fun get(cur::li, 0) = cur - | get(cur::li, index) = get(li, index - 1); - +fun get([], _) = raise Empty + | get(cur::li, 0) = cur + | get(cur::li, index) = get(li, index - 1); (* @@ -117,7 +118,21 @@ fun get(cur::li, 0) = cur * required for #7, it does not need to be included in a * let block here.) *) -fun subList(li, startIndex, endIndex) = if startIndex = endIndex then [get(li, startIndex)] else [get(li, startIndex)] @ subList(li, startIndex + 1, endIndex); +fun subList([], _, _) = [] + | subList(li, startIndex, endIndex) = + let + fun listLength([]) = 0 + | listLength(x::li) = 1 + listLength(li) + (* Clamp the end index to the length of the list *) + val clampedEndIndex = if endIndex > listLength(li) then listLength(li) else endIndex + in + (* Handle case: start index greater than end index *) + if startIndex > clampedEndIndex then [] + (* Base case: start index equals end index *) + else if startIndex = clampedEndIndex then [get(li, startIndex)] + else [get(li, startIndex)] @ subList(li, startIndex + 1, clampedEndIndex) + end; + (* * Type: `'a list → 'a list` @@ -125,19 +140,19 @@ fun subList(li, startIndex, endIndex) = if startIndex = endIndex then [get(li, s *) fun reverse li = foldl (fn(cur, acc) => cur::acc) [] li; + (* * Type: `'a list * ('a → 'b) → 'b list` * Desc: Apply a function to a list of elements. You may not * use map, foldr, or foldl. * Example: * apply([(1,2),(3,4),(5,6)],(op +)) → [3,7,11] - * apply((explode “hello”),ord) → [104,101,108,108,111 + * apply((explode “hello”),ord) → [104,101,108,108,111] *) fun apply([], f) = [] | apply(x::li, f) = [f(x)] @ apply(li, f); - (* * Type: `'a list * 'b * ('a * 'b → 'b) → 'b` * Desc: Take a list, a starting value, and a function and @@ -147,27 +162,96 @@ fun apply([], f) = [] * collapse([1,2,3,4,5],0,(op +)) → 15 * collapse([1.1,2.2,3.3],10.0,(op -)) → ~7.8 *) -fun collapse([], startVal, func) = startVal - | collapse(cur::li, startVal, func) = func(cur, collapse(li, startVal, func)); +fun collapse([], startVal, f) = startVal + (* Associativity: right-to-left with (current, accumulator) *) + | collapse(cur::li, startVal, f) = f(cur, collapse(li, startVal, f)); + + +(* + * Type: `('a * 'a → bool) → 'a list → 'a list` + * Desc: Sort a list using the quicksort method, but make it + * general enough so that you can pass in a function and + * sort in either direction and/or sort various types of + * data. + *) +fun quicksort f [] = [] + | quicksort f (pivot::li) = + let + fun partition([], left, right) = (left, right) + | partition(cur::li, left, right) = + if f(cur, pivot) then partition(li, cur::left, right) + else partition(li, left, cur::right) + val (left, right) = partition(li, [], []) + in + quicksort f left @ [pivot] @ quicksort f right + end; + + +(* + * Type: `('a * 'a → bool) → 'a list → 'a list` + * Desc: Sort a list using the bubble sort method, but make it + * general enough so that you can pass in a function and + * sort in either direction and/or sort various types of + * data. + *) +fun bubbleSort f [] = [] + | bubbleSort f li = + let + fun swap([]) = [] + | swap(x::[]) = [x] + | swap(x::y::li) = if f(x, y) then x::swap(y::li) else y::swap(x::li) + fun listLength([]) = 0 + | listLength(x::li) = 1 + listLength(li) + (* Perform swap sort on the list *) + val sorted = swap(li) + (* Get the last element in the sorted list *) + val lastInSorted = hd(reverse(sorted)) + in + (* Keep the last item, recurse on the rest of the list *) + bubbleSort f (subList(sorted, 0, listLength(sorted) - 2)) @ [lastInSorted] + end; + -(* TODO: fun quicksort = fun *) +(* + * Type: `('a * 'a → bool) → 'a list → 'a list` + * Desc: Sort a list using the insertion sort method, but make it + * general enough so that you can pass in a function and + * sort in either direction and/or sort various types of + * data. + *) +fun insertionSort f [] = [] + | insertionSort f (pivot::li) = + let + fun insert([], pivot) = [pivot] + | insert(cur::li, pivot) = if f(pivot, cur) then pivot::cur::li else cur::insert(li, pivot) + in + insert(insertionSort f li, pivot) + end; -(* TODO: fun bubbleSort = fun *) -(* TODO: fun insertionSort = fun *) (* * Type: `string → string → bool` * Desc: Determine if a string is a substring of another * string. *) -fun substring(str, "") = false - | substring(str, isIn) = if String.size(str) = String.size(isIn) then false else if str = String.substring(isIn, 0, String.size(str)) then true else substring(str, - String.substring( - isIn, - 1, - String.size(isIn) - ) - ); +fun substring "" "" = true + | substring str "" = false + | substring "" str = true + | substring str isIn = + let + fun listLength([]) = 0 + | listLength(x::xs) = 1 + listLength(xs) + fun stringLength(st) = listLength(explode st) + fun spliceString(startIndex, endIndex, st) = + implode(subList(explode(st), startIndex, endIndex)) + val strLen = stringLength(str) + val isInLen = stringLength(isIn) + in + (* Handle case: not a substring *) + if strLen > isInLen then false + else if str = spliceString(0, strLen - 1, isIn) then true + else substring str (spliceString(1, isInLen - 1, isIn)) + end; (* @@ -175,8 +259,19 @@ fun substring(str, "") = false * Desc: Determine the index of the first occurrence of a value in a list. * Indexing should start at 0. *) -fun indexOf(_, []) = ~1 - | indexOf(x, y::ys) = if x = y then 0 else 1 + indexOf(x, ys); +fun indexOf target li = + let + fun listLength([]) = 0 + | listLength(x::xs) = 1 + listLength(xs) + fun indexOf(_, []) = 1 + | indexOf(target, x::li) = if x = target then 0 else 1 + indexOf(target, li) + val result = indexOf(target, li) + val liLen = listLength(li) + in + (* Handle case: target not found -> return -1 *) + if result > liLen then ~1 + else result + end; (* @@ -184,22 +279,48 @@ fun indexOf(_, []) = ~1 * Desc: Turn a decimal number into a string representation of base N. * The input for N will be an integer between 2 and 10 (inclusive). *) -fun dec2BaseN(num, base) = (* Implementation pending *); +fun dec2BaseN base n = + let + fun dec2BaseN base 0 = "" + | dec2BaseN base n = + if n < base then Int.toString(n) (* Base case: n is less than base *) + else (dec2BaseN base (n div base)) ^ Int.toString(n mod base) + fun absVal(n) = if n < 0 then ~n else n + val wasNegative = n < 0 + val result = dec2BaseN base (absVal n) + in + (* Handle case where n is exactly 0, return "0" instead of empty string *) + if result = "" then "0" + (* Add back the negative sign if n was negative *) + else if wasNegative then "~" ^ result + else result + end; (* * Type: `int → 'a list → 'a list` * Desc: Drop every nth element in a list. *) -fun dropNth(_, []) = [] - | dropNth(n, xs) = (* Implementation pending *); - +fun dropNth _ [] = [] + | dropNth 1 _ = [] + | dropNth n li = + let + fun listLength([]) = 0 + | listLength(x::xs) = 1 + listLength(xs) + val liLen = listLength(li) + in + (* Handle bad input: n less than 1 *) + if n < 1 then li + (* Base case: next nth beyond list bounds *) + else if n > liLen then li + else subList(li, 0, n - 2) @ dropNth n (subList(li, n, liLen - 1)) + end; (* * Type: `'a list list → 'a list` * Desc: Flatten a list of lists into a single list of elements. *) -fun flatten(li) = List.concat(li); +(* fun flatten(li) = List.concat(li); *) (* @@ -207,36 +328,36 @@ fun flatten(li) = List.concat(li); * Desc: Take a list of lists, a function, and a starting value. * Apply the function recursively to each list, condensing it to a single value. *) -fun condenseLists(f, startVal, lists) = List.map (fn xs => List.foldl f startVal xs) lists; +(* fun condenseLists(f, startVal, lists) = List.map (fn xs => List.foldl f startVal xs) lists; *) (* * Type: `('a → bool) → 'a list → 'a list` * Desc: Remove all elements from a list for which the given function returns true. *) -fun remove(f, xs) = List.filter (fn x => not (f x)) xs; +(* fun remove(f, xs) = List.filter (fn x => not (f x)) xs; *) (* * Type: `'a list → 'a list` * Desc: Take a list and create a new list where every element is repeated three times. *) -fun triplist(xs) = List.concat (List.map (fn x => [x, x, x]) xs); +(* fun triplist(xs) = List.concat (List.map (fn x => [x, x, x]) xs); *) (* * Type: `'a list → int → 'a list` * Desc: Take a list and an integer n and create a list that repeats the original list n times. *) -fun repeat(_, 0) = [] - | repeat(xs, n) = xs @ repeat(xs, n - 1); +(* fun repeat(_, 0) = [] + | repeat(xs, n) = xs @ repeat(xs, n - 1); *) (* * Type: `'a list → ('a → bool) → ('a → 'a) → 'a list` * Desc: Apply function g to all elements in the list for which function f returns true. *) -fun filterApply(xs, f, g) = List.map (fn x => if f x then g x else x) xs; +(* fun filterApply(xs, f, g) = List.map (fn x => if f x then g x else x) xs; *) (* @@ -244,60 +365,60 @@ fun filterApply(xs, f, g) = List.map (fn x => if f x then g x else x) xs; * Desc: Create an arithmetic sequence starting at a with a common difference of d * and a length of l. *) -fun arithSeq(a, d, l) = List.tabulate(l, fn i => a + i * d); +(* fun arithSeq(a, d, l) = List.tabulate(l, fn i => a + i * d); *) (* * Type: `'a → 'a list → bool` * Desc: Return true if the element is in the list, false otherwise. *) -fun element(x, xs) = List.exists (fn y => y = x) xs; +(* fun element(x, xs) = List.exists (fn y => y = x) xs; *) (* * Type: `'a list → bool` * Desc: Return true if the list is a set (no duplicates), false otherwise. *) -fun isSet(xs) = xs = List.tabulate(List.length xs, fn i => List.nth(xs, i)); +(* fun isSet(xs) = xs = List.tabulate(List.length xs, fn i => List.nth(xs, i)); *) (* * Type: `'a list * 'a list → 'a list` * Desc: Combine two lists to produce the union of the two sets. *) -fun union(xs, ys) = List.concat [xs, ys]; +(* fun union(xs, ys) = List.concat [xs, ys]; *) (* * Type: `'a list * 'a list → 'a list` * Desc: Combine two lists to produce the intersection of the two sets. *) -fun intersection(xs, ys) = List.filter (fn x => List.exists (fn y => x = y) ys) xs; +(* fun intersection(xs, ys) = List.filter (fn x => List.exists (fn y => x = y) ys) xs; *) (* * Type: `'a list * 'a list → 'a list` * Desc: Produce the difference of two sets (items in the first but not in the second). *) -fun difference(xs, ys) = List.filter (fn x => not (List.exists (fn y => y = x) ys)) xs; +(* fun difference(xs, ys) = List.filter (fn x => not (List.exists (fn y => y = x) ys)) xs; *) (* * Type: `'a list * 'a list → 'a list` * Desc: Produce the xor of two sets (items in one set but not both). *) -fun xor(xs, ys) = difference(union(xs, ys), intersection(xs, ys)); +(* fun xor(xs, ys) = difference(union(xs, ys), intersection(xs, ys)); *) (* * Type: `'a list → 'a list list` * Desc: Return the powerset of a set. *) -fun powerset([]) = [[]] +(* fun powerset([]) = [[]] | powerset(x::xs) = let val rest = powerset(xs) in rest @ List.map (fn ys => x::ys) rest - end; + end; *) diff --git a/test-la-01.sh b/test-la-01.sh index 9481da0..57ab315 100755 --- a/test-la-01.sh +++ b/test-la-01.sh @@ -1,3 +1,4 @@ #!/bin/bash # -./test --rootdir tests/tests-large_assignment_01 --failed-first --log-cli-level WARNING $@ +#./test --rootdir tests/tests-large_assignment_01 --failed-first --log-cli-level WARNING $@ +./test --rootdir tests/tests-large_assignment_01 --nl --log-cli-level INFO diff --git a/tests/tests-large_assignment_01/test_dec2BaseN.sml b/tests/tests-large_assignment_01/test_dec2BaseN.sml new file mode 100644 index 0000000..64488c3 --- /dev/null +++ b/tests/tests-large_assignment_01/test_dec2BaseN.sml @@ -0,0 +1,53 @@ + + +(* Large Assignment 01 Tests *) + +use "src/large_assignment_01/large_assignment_01.sml"; +use "tests/utils.sml"; + +(* Test cases for the dec2BaseN function *) +val testCasesToBaseN = [ + (* 1. Convert 0 to any base (should return "0") *) + (fn (base, num) => dec2BaseN base num, (2, 0), "0"), + (fn (base, num) => dec2BaseN base num, (10, 0), "0"), + + (* 2. Convert 1 to various bases *) + (fn (base, num) => dec2BaseN base num, (2, 1), "1"), + (fn (base, num) => dec2BaseN base num, (10, 1), "1"), + + (* 3. Convert a number to binary (base 2) *) + (fn (base, num) => dec2BaseN base num, (2, 5), "101"), + (fn (base, num) => dec2BaseN base num, (2, 13), "1101"), + (fn (num, base) => dec2BaseN num base, (2, 10), "1010"), + + (* 4. Convert a number to octal (base 8) *) + (fn (base, num) => dec2BaseN base num, (8, 8), "10"), + (fn (base, num) => dec2BaseN base num, (8, 17), "21"), + + (* 5. Convert a number to decimal (base 10) *) + (fn (base, num) => dec2BaseN base num, (10, 42), "42"), + + (* 6. Edge case: Smallest and largest bases *) + (fn (base, num) => dec2BaseN base num, (2, 255), "11111111"), + (fn (base, num) => dec2BaseN base num, (10, 255), "255"), + + (* 7. Large number in a smaller base (binary) *) + (fn (base, num) => dec2BaseN base num, (2, 1023), "1111111111"), + + (* 8. Large number in octal *) + (fn (base, num) => dec2BaseN base num, (8, 1023), "1777"), + + (* 9. Check base boundaries (valid: 2 and 10) *) + (fn (base, num) => dec2BaseN base num, (2, 42), "101010"), + (fn (base, num) => dec2BaseN base num, (10, 42), "42"), + + (* 10. Test for negative input *) + (fn (base, num) => dec2BaseN base num, (2, ~5), "~101"), + (fn (base, num) => dec2BaseN base num, (2, ~13), "~1101") +]; + +runTests( + testCasesToBaseN, + fn (base, num) => "dec2BaseN(" ^ Int.toString base ^ ", " ^ Int.toString num ^ ")", + (fn s => s) (* Identity function for string output *) +); diff --git a/tests/tests-large_assignment_01/test_dropNth.sml b/tests/tests-large_assignment_01/test_dropNth.sml new file mode 100644 index 0000000..50e135f --- /dev/null +++ b/tests/tests-large_assignment_01/test_dropNth.sml @@ -0,0 +1,63 @@ +(* Large Assignment 01 Tests *) + +use "src/large_assignment_01/large_assignment_01.sml"; +use "tests/utils.sml"; + +(* Integer List Test Cases *) +val testCasesDropNthInt = [ + (* 1. Drop every 1st element (should return an empty list) *) + (fn (n, li) => dropNth n li, (1, [1, 2, 3, 4, 5]), []), + + (* 2. Drop every 2nd element *) + (fn (n, li) => dropNth n li, (2, [1, 2, 3, 4, 5]), [1, 3, 5]), + + (* 3. Drop every 3rd element *) + (fn (n, li) => dropNth n li, (3, [1, 2, 3, 4, 5, 6, 7, 8, 9]), [1, 2, 4, 5, 7, 8]), + + (* 4. Drop every nth element where n > length of list (should return the same list) *) + (fn (n, li) => dropNth n li, (10, [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]), + + (* 5. Drop every nth element for an empty list *) + (fn (n, li) => dropNth n li, (3, []), []), + + (* 6. Drop every nth element in a single-element list *) + (fn (n, li) => dropNth n li, (1, [42]), []), + (fn (n, li) => dropNth n li, (2, [42]), [42]), + + (* 7. Drop every nth element in a list with repeated elements *) + (fn (n, li) => dropNth n li, (2, [1, 1, 1, 1, 1]), [1, 1, 1]), + + (* 8. Large list without List.tabulate *) + (fn (n, li) => dropNth n li, (5, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), [1, 2, 3, 4, 6, 7, 8, 9]) +]; + +runTests( + testCasesDropNthInt, + fn (n, li) => "dropNth(" ^ Int.toString n ^ ", " ^ valueToStringIntList li ^ ")", + valueToStringIntList +); + +(* String List Test Cases *) +val testCasesDropNthString = [ + (* 1. Drop every 1st element from a list of strings *) + (fn (n, li) => dropNth n li, (1, ["a", "b", "c", "d"]), []), + + (* 2. Drop every 2nd element from a list of strings *) + (fn (n, li) => dropNth n li, (2, ["a", "b", "c", "d"]), ["a", "c"]), + + (* 3. Drop every 3rd element from a list of strings *) + (fn (n, li) => dropNth n li, (3, ["a", "b", "c", "d", "e", "f"]), ["a", "b", "d", "e"]), + + (* 4. Drop every nth element where n > length of list (should return the same list) *) + (fn (n, li) => dropNth n li, (10, ["a", "b", "c"]), ["a", "b", "c"]), + + (* 5. Drop every nth element for a single-element list of strings *) + (fn (n, li) => dropNth n li, (1, ["hello"]), []), + (fn (n, li) => dropNth n li, (2, ["hello"]), ["hello"]) +]; + +runTests( + testCasesDropNthString, + fn (n, li) => "dropNth(" ^ Int.toString n ^ ", " ^ valueToStringStringList li ^ ")", + valueToStringStringList +); diff --git a/tests/tests-large_assignment_01/test_indexOf.sml b/tests/tests-large_assignment_01/test_indexOf.sml new file mode 100644 index 0000000..1bbb1eb --- /dev/null +++ b/tests/tests-large_assignment_01/test_indexOf.sml @@ -0,0 +1,43 @@ +(* Large Assignment 01 Tests *) + +use "src/large_assignment_01/large_assignment_01.sml"; +use "tests/utils.sml"; + +(* Test cases for the indexOf function *) +val testCasesIndexOf = [ + (* 1. Single-element list with matching value *) + (fn (x, li) => indexOf x li, (42, [42]), 0), + + (* 2. Single-element list without matching value *) + (fn (x, li) => indexOf x li, (99, [42]), ~1), + + (* 3. Value at the beginning of the list *) + (fn (x, li) => indexOf x li, (1, [1, 2, 3, 4, 5]), 0), + + (* 4. Value at the end of the list *) + (fn (x, li) => indexOf x li, (5, [1, 2, 3, 4, 5]), 4), + + (* 5. Value in the middle of the list *) + (fn (x, li) => indexOf x li, (3, [1, 2, 3, 4, 5]), 2), + + (* 6. List with multiple occurrences (should return the first index) *) + (fn (x, li) => indexOf x li, (2, [1, 2, 2, 2, 3]), 1), + + (* 7. List with all identical elements (should return index 0) *) + (fn (x, li) => indexOf x li, (1, [1, 1, 1, 1]), 0), + + (* 8. Value not found in the list *) + (fn (x, li) => indexOf x li, (99, [1, 2, 3, 4, 5]), ~1), + + (* 9. Empty list *) + (fn (x, li) => indexOf x li, (42, []), ~1), + + (* 10. List with negative numbers *) + (fn (x, li) => indexOf x li, (~2, [1, ~2, 3, ~2, 5]), 1) +]; + +runTests( + testCasesIndexOf, + fn (x, li) => "indexOf(" ^ Int.toString x ^ ", " ^ valueToStringIntList li ^ ")", + Int.toString +); diff --git a/tests/tests-large_assignment_01/test_insertionSort.sml b/tests/tests-large_assignment_01/test_insertionSort.sml index e69de29..1aa7860 100644 --- a/tests/tests-large_assignment_01/test_insertionSort.sml +++ b/tests/tests-large_assignment_01/test_insertionSort.sml @@ -0,0 +1,54 @@ +(* Large Assignment 01 Tests *) + +use "src/large_assignment_01/large_assignment_01.sml"; +use "tests/utils.sml"; + +(* Integer Test Cases *) +val testCasesInsertionSortInt = [ + (fn li => insertionSort (fn (a, b) => a < b) li, [42], [42]), + (fn li => insertionSort (fn (a, b) => a < b) li, [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]), + (fn li => insertionSort (fn (a, b) => a < b) li, [5, 4, 3, 2, 1], [1, 2, 3, 4, 5]), + (fn li => insertionSort (fn (a, b) => a < b) li, [3, 1, 4, 1, 5], [1, 1, 3, 4, 5]), + (fn li => insertionSort (fn (a, b) => a > b) li, [1, 2, 3, 4, 5], [5, 4, 3, 2, 1]), + (fn li => insertionSort (fn (a, b) => a < b) li, [3, ~1, ~2, 5, 0], [~2, ~1, 0, 3, 5]), + (fn li => insertionSort (fn (a, b) => a < b) li, [], []), + (fn li => insertionSort (fn (a, b) => a < b) li, [1, 1, 1, 1], [1, 1, 1, 1]) +]; + +runTestsCustomComparator( + testCasesInsertionSortInt, + fn li => "insertionSort on " ^ valueToStringIntList(li), + valueToStringIntList, + isEqualList +); + +(* String Test Cases *) +val testCasesInsertionSortString = [ + (fn li => insertionSort (fn (a, b) => String.compare(a, b) = LESS) li, [], []), + (fn li => insertionSort (fn (a, b) => String.compare(a, b) = LESS) li, ["hello"], ["hello"]), + (fn li => insertionSort (fn (a, b) => String.compare(a, b) = LESS) li, ["apple", "banana", "cherry"], ["apple", "banana", "cherry"]), + (fn li => insertionSort (fn (a, b) => String.compare(a, b) = GREATER) li, ["apple", "banana", "cherry"], ["cherry", "banana", "apple"]), + (fn li => insertionSort (fn (a, b) => String.compare(a, b) = LESS) li, ["a", "b", "a", "c"], ["a", "a", "b", "c"]) +]; + +runTestsCustomComparator( + testCasesInsertionSortString, + fn li => "insertionSort on " ^ valueToStringStringList(li), + valueToStringStringList, + isEqualList +); + +(* Boolean Test Cases *) +val testCasesInsertionSortBool = [ + (fn li => insertionSort (fn (a, b) => a = false andalso b = true) li, [true, false, true], [false, true, true]), + (fn li => insertionSort (fn (a, b) => a = true andalso b = false) li, [false, true, false], [true, false, false]), + (fn li => insertionSort (fn (a, b) => a = false andalso b = true) li, [false, false, false], [false, false, false]), + (fn li => insertionSort (fn (a, b) => a = false andalso b = true) li, [true, true, true], [true, true, true]) +]; + +runTestsCustomComparator( + testCasesInsertionSortBool, + fn li => "insertionSort on " ^ valueToStringBoolList(li), + valueToStringBoolList, + isEqualList +); diff --git a/tests/tests-large_assignment_01/test_subList.sml b/tests/tests-large_assignment_01/test_subList.sml index be4e609..c466c43 100644 --- a/tests/tests-large_assignment_01/test_subList.sml +++ b/tests/tests-large_assignment_01/test_subList.sml @@ -30,7 +30,11 @@ val testCasesSubListInt = [ (* 9. List with duplicate elements (sublist with repetitions) *) (subList, ([1, 2, 2, 3], 1, 3), [2, 2, 3]), + + (subList, ([1, 2, 3, 4, 5], 1, 3), [2, 3, 4]), + (subList, ([1, 2, 2, 3], 0, 1), [1, 2]), + (* 10. Start index out of range (empty result) *) (subList, ([1, 2, 3], 5, 6), []) ]; diff --git a/tests/tests-large_assignment_01/test_substring.sml b/tests/tests-large_assignment_01/test_substring.sml new file mode 100644 index 0000000..b71e6d1 --- /dev/null +++ b/tests/tests-large_assignment_01/test_substring.sml @@ -0,0 +1,67 @@ +(* Large Assignment 01 Tests *) + +use "src/large_assignment_01/large_assignment_01.sml"; +use "tests/utils.sml"; + +(* Test cases for the substring function *) +val testCasesSubstring = [ + (* 1. Empty substring (should always return true) *) + (fn (s1, s2) => substring s1 s2, ("", "hello"), true), + + (* 2. Empty string to search within (should return false) *) + (fn (s1, s2) => substring s1 s2, ("hello", ""), false), + + (* 3. Substring at the beginning *) + (fn (s1, s2) => substring s1 s2, ("he", "hello"), true), + + (* 4. Substring at the end *) + (fn (s1, s2) => substring s1 s2, ("lo", "hello"), true), + + (* 5. Substring in the middle *) + (fn (s1, s2) => substring s1 s2, ("ell", "hello"), true), + + (* 6. Full string match *) + (fn (s1, s2) => substring s1 s2, ("hello", "hello"), true), + + (* 7. Substring not found *) + (fn (s1, s2) => substring s1 s2, ("world", "hello"), false), + + (* 8. Substring longer than the string *) + (fn (s1, s2) => substring s1 s2, ("hello world", "hello"), false), + + (* 9. Case sensitivity check *) + (fn (s1, s2) => substring s1 s2, ("Hello", "hello"), false), + + (* 10. Special characters *) + (fn (s1, s2) => substring s1 s2, ("@", "email@example.com"), true), + + (* 11. Empty string is in Empty string *) + (fn (s1, s2) => substring s1 s2, ("", ""), true), + + (* 12. Empty string is in one-character string *) + (fn (s1, s2) => substring s1 s2, ("", "a"), true), + + (* 13. One-character string is in one-character string *) + (fn (s1, s2) => substring s1 s2, ("a", "a"), true), + + (* 14. One-character string is not in one-character string *) + (fn (s1, s2) => substring s1 s2, ("a", "b"), false), + + (* 15.a One-character string is not in string *) + (fn (s1, s2) => substring s1 s2, ("a", "zcedfgfjklergioegmgelwk"), false), + + (* 15.b One-character string is last char in long string *) + (fn (s1, s2) => substring s1 s2, ("a", "acedfgfjklergioegmgelwk"), true), + + (* 16. Two-character string is in two-character string *) + (fn (s1, s2) => substring s1 s2, ("ab", "ab"), true), + + (* 17. Right arg is in left arg - supposedly not supposed to be covered; i.e., should be subtring, string *) + (fn (s1, s2) => substring s1 s2, ("abc", "ab"), false) +]; + +runTests( + testCasesSubstring, + fn (s1, s2) => "substring(\"" ^ s1 ^ "\", \"" ^ s2 ^ "\")", + valueToStringBool +);