diff --git a/coursera/third_course/week3/assignment/threaded_sort.go b/coursera/third_course/week3/assignment/threaded_sort.go new file mode 100644 index 0000000..126724d --- /dev/null +++ b/coursera/third_course/week3/assignment/threaded_sort.go @@ -0,0 +1,85 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "sort" + "strconv" + "strings" +) + +func stringsToInt(inputStrings []string) []int { + integers := make([]int, len(inputStrings)) + for i, v := range inputStrings { + intValue, err := strconv.Atoi(v) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %s could not be converted to integer", v) + os.Exit(1) + } + integers[i] = intValue + } + return integers +} + +func quarterArray(input_arr []int) ([4][]int, error) { + if len(input_arr) < 4 { + return [4][]int{}, fmt.Errorf("slice is too small to be divided into 4 parts") + } + + partSize := len(input_arr) / 4 + remainder := len(input_arr) % 4 + + var result [4][]int + start := 0 + for i := 0; i < 4; i++ { + end := start + partSize + if remainder > 0 { + end++ + remainder-- + } + result[i] = input_arr[start:end] + start = end + } + + return result, nil +} + +func sortArray(arr []int, c chan []int) { + fmt.Println("Will sort: ", arr) + sort.Ints(arr) + c <- arr +} + +func main() { + scanner := bufio.NewScanner(os.Stdin) + fmt.Println("Input a sequence of ints and I will sort them for you: ") + // Process text into input []int + scanner.Scan() + input_text := scanner.Text() + input_text = strings.TrimSpace(input_text) + split_input := strings.Split(input_text, " ") + user_int_slice := stringsToInt(split_input) + + // quarter input []int + quartered, err := quarterArray(user_int_slice) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + // Spawn 4 threads + c := make(chan []int, 4) + for _, quarter := range quartered { + go sortArray(quarter, c) + } + + combined := []int{} + + for i := 4; i > 0; i-- { + sorted_ints := <-c + // the ... is use the []int as a variadic argument + combined = append(combined, sorted_ints...) + } + sort.Ints(combined) + fmt.Println(combined) +} diff --git a/coursera/third_course/week3/funky-sort.go b/coursera/third_course/week3/funky-sort.go new file mode 100644 index 0000000..7d9b915 --- /dev/null +++ b/coursera/third_course/week3/funky-sort.go @@ -0,0 +1,146 @@ +/* + * Note: This program is written to demonstrate the ideas behind channels and synronization. + * It isn't written to be efficient code in term of memory use (I use copy by value, not by reference) + * or in terms of runtime (I use a waitgroup where reading from channels could provides synronization instead). + * + * Using slice.Sort in mysort() followed by a 4 way merge in main() is particularly ugly. + * + * Main() is split into 6 main steps: + * Step 1: Get the list of ints from the user + * Step 2: Split the list into four partitions of approximately equal size + * Step 3: Pass each quater to a goroutine to sort + * Step 4: Fetch results from channels + * Step 5: Merge the results. N.B. I'm taking "merge the 4 sorted subarrays into one large sorted array." literally here. + * Step 6: Print the output + * + * Some shuffled sequences to test this on: + * 2 5 3 6 1 7 10 4 8 9 + * 3 13 1 11 19 17 16 7 10 14 9 4 6 8 5 2 12 15 18 20 + * 24 18 5 15 4 12 28 30 3 27 29 14 20 6 23 21 10 8 19 9 17 16 7 25 11 2 22 1 26 13 + * + */ + +package main + +import ( + "fmt" + "os" + "bufio" + "strings" + "strconv" + "sync" + "slices" +) + +func mysort(wg *sync.WaitGroup, c chan []int, goroutineNum int, numbers []int) { + defer wg.Done() + + fmt.Printf("mysort%d got: %v\n", goroutineNum, numbers) + + slices.Sort(numbers) // Using the sort in slices as this isn't an exercise in writing a sort() + c <- numbers + fmt.Printf("mysort%d returned: %v\n", goroutineNum, numbers) +} + +func main() { + numbers := make([]int, 0) + + // Step 1: Get the list of ints from the user + scanner := bufio.NewScanner(os.Stdin) + + fmt.Printf("\nPlease enter a list of integers separated by spaces ( e.g. \"1 3 5 2\" ):\n") + + scanner.Scan() + line := scanner.Text() + fields := strings.Fields(line) + + for _, v := range(fields) { + n, err := strconv.Atoi(v) + if err != nil { + fmt.Println("Ignoring string: " + v) + continue + } + numbers = append(numbers, n) + } + + // Step 2: Split the list into four partitions of approximately equal size + quarter1 := numbers[:len(numbers)/4] + quarter2 := numbers[len(numbers)/4 : len(numbers)/2] + quarter3 := numbers[len(numbers)/2 : len(numbers)/4+len(numbers)/2] + quarter4 := numbers[len(numbers)/4+len(numbers)/2:] + + // Step 3: Pass each quater to a goroutine to sort + // N.B. the waitgroups are not needed for syncronization here as reading from unbuffered channels in Step 4 + // would perform the same function. I'm using buffered channels just so the waitgroup is needed. + // This is to demonstrate the idea of waitgroups. + c1 := make(chan []int, 1) + c2 := make(chan []int, 1) + c3 := make(chan []int, 1) + c4 := make(chan []int, 1) + var wg sync.WaitGroup + wg.Add(4) + go mysort(&wg, c1, 1, quarter1) + go mysort(&wg, c2, 2, quarter2) + go mysort(&wg, c3, 3, quarter3) + go mysort(&wg, c4, 4, quarter4) + wg.Wait() + + // Step 4: Fetch results from channels + nums1 := <- c1 + nums2 := <- c2 + nums3 := <- c3 + nums4 := <- c4 + + // Step 5: Merge the results. N.B. I'm taking "merge the 4 sorted subarrays into one large sorted array." literally here. + // It would be far easier, and more readable, to merge nums1 and nums2 into nums5, merge nums3 and nums4 into nums6, + // and then merge nums5 and nums6 into the final output. + sorted := []int{} + lowest_list := 0 + lowest_number := 0 + for len(sorted) < len(numbers) { + // Step 5a: Find which of nums1 to nums4 starts with the lowest number + if len(nums1) > 0 { + lowest_list = 1; + lowest_number = nums1[0]; + } + if len(nums2) > 0 { + if (len(nums1) == 0) || nums2[0] < lowest_number { + lowest_list = 2 + lowest_number = nums2[0] + } + } + if len(nums3) > 0 { + if (len(nums1) == 0 && len(nums2) == 0) || nums3[0] < lowest_number { + lowest_list = 3 + lowest_number = nums3[0] + } + } + if len(nums4) > 0 { + if (len(nums1) == 0 && len(nums2) == 0 && len(nums3) == 0) || nums4[0] < lowest_number { + lowest_list = 4 + lowest_number = nums4[0] + } + } + + // Step 5b: Update sorted with the lowest found and offset the []int it was found in + sorted = append(sorted, lowest_number) + switch lowest_list { + case 1: + nums1 = nums1[1:] + case 2: + nums2 = nums2[1:] + case 3: + nums3 = nums3[1:] + case 4: + nums4 = nums4[1:] + } + } + + // Step 6: Print the output + + fmt.Println("Final merged output:\n") + fmt.Println(sorted) +} + + + diff --git a/coursera/third_course/week3/gogoroutines.go b/coursera/third_course/week3/gogoroutines.go new file mode 100644 index 0000000..c5dab1f --- /dev/null +++ b/coursera/third_course/week3/gogoroutines.go @@ -0,0 +1,90 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" + "sync" +) + +func Swap(sli []int, i int) { + sli[i], sli[i+1] = sli[i+1], sli[i] +} + +func BubbleSort(sli []int, wg *sync.WaitGroup) { + fmt.Println(sli) + swapped := true + for limit := len(sli) - 1; limit > 0 && swapped == true; limit-- { + swapped = false + for i := 0; i < limit; i++ { + if sli[i] > sli[i+1] { + Swap(sli, i) + swapped = true + } + } + } + wg.Done() +} + +func Merge(sli1, sli2 []int) []int { + sorted := make([]int, 0, len(sli1)+len(sli2)) + i, j := 0, 0 + for i < len(sli1) && j < len(sli2) { + if sli1[i] < sli2[j] { + sorted = append(sorted, sli1[i]) + i++ + } else { + sorted = append(sorted, sli2[j]) + j++ + } + } + if i < len(sli1) { + sorted = append(sorted, sli1[i:]...) + } else { + sorted = append(sorted, sli2[j:]...) + } + return sorted +} + +func main() { + var seq []int +OuterLoop: + for seq == nil { + scanner := bufio.NewScanner(os.Stdin) + fmt.Println( + "Type in a sequence of space-separated integers:", + ) + scanner.Scan() + input := strings.Fields(scanner.Text()) + + for _, value := range input { + num, err := strconv.Atoi(value) + if err != nil { + fmt.Printf("Invalid input: %q\n", value) + continue OuterLoop + } else { + seq = append(seq, num) + } + } + } + a := seq[:len(seq)/2] + b := seq[len(seq)/2:] + a1 := a[:len(a)/2] + a2 := a[len(a)/2:] + b1 := b[:len(b)/2] + b2 := b[len(b)/2:] + + var wg sync.WaitGroup + wg.Add(4) + go BubbleSort(a1, &wg) + go BubbleSort(a2, &wg) + go BubbleSort(b1, &wg) + go BubbleSort(b2, &wg) + wg.Wait() + + a = Merge(a1, a2) + b = Merge(b1, b2) + fmt.Println(strings.Trim(fmt.Sprint(Merge(a, b)), "[]")) +}