├── .gitignore ├── code ├── lecture1 │ ├── concepts.h │ ├── count_operations.cpp │ ├── count_operations.h │ ├── count_operations_normalized.cpp │ ├── functorized.h │ ├── iota.h │ ├── setsort.h │ └── table_util.h ├── lecture10 │ ├── algorithm.h │ ├── binary_counter.h │ ├── list_pool.h │ ├── min.cpp │ └── min_element1_2.h ├── lecture10v2 │ ├── algorithm.h │ ├── binary_counter.h │ ├── list_pool.h │ ├── min.cpp │ └── min_element1_2.h ├── lecture11 │ ├── algorithm.h │ ├── binary_counter.h │ ├── lisp │ │ ├── binary-counter.lisp │ │ ├── min.lisp │ │ └── util.lisp │ ├── list_pool.h │ ├── min.cpp │ ├── min_element1_2.h │ └── python │ │ └── min.py ├── lecture12 │ ├── algorithm.h │ ├── binary_counter.h │ ├── list_algorithm.h │ ├── list_pool.h │ ├── merge_linked.h │ └── sort.cpp ├── lecture13 │ └── search.h ├── lecture14 │ ├── algorithm.h │ ├── merge_inplace.h │ ├── search.h │ └── sort.cpp ├── lecture15 │ ├── algorithm.h │ ├── ideas.txt │ ├── merge.h │ ├── merge_inplace.h │ ├── search.h │ ├── sort.cpp │ ├── test_sort.cpp │ ├── test_sort.h │ ├── timer.h │ └── type_description.h ├── lecture16 │ ├── concepts A9.pdf │ ├── concepts A9.pptx │ ├── concepts design for stl.pdf │ └── concepts lite.pdf ├── lecture17 │ ├── algorithm.h │ ├── ideas.txt │ ├── insertion_sort.h │ ├── merge.h │ ├── merge_inplace.h │ ├── search.h │ ├── sort.cpp │ ├── test_insertion_sort.cpp │ ├── test_sort.cpp │ ├── test_sort.h │ ├── timer.h │ └── type_description.h ├── lecture18 │ ├── algorithm.h │ ├── ideas.txt │ ├── insertion_sort.h │ ├── merge.h │ ├── merge_inplace.h │ ├── search.h │ ├── sort.cpp │ ├── test_insertion_sort.cpp │ ├── test_sort.cpp │ ├── test_sort.h │ ├── timer.h │ └── type_description.h ├── lecture19 │ ├── algorithm.h │ ├── ideas.txt │ ├── insertion_sort.h │ ├── merge.h │ ├── merge_inplace.h │ ├── search.h │ ├── sort.cpp │ ├── test_insertion_sort.cpp │ ├── test_sort.cpp │ ├── test_sort.h │ ├── timer.h │ └── type_description.h ├── lecture2 │ ├── singleton.cpp │ └── singleton.h ├── lecture20 │ ├── algorithm.h │ ├── ideas.txt │ ├── insertion_sort.h │ ├── merge.h │ ├── merge_inplace.h │ ├── search.h │ ├── sort.cpp │ ├── sort_akraft.h │ ├── sort_bert.h │ ├── sort_ph.h │ ├── sort_rjernst.h │ ├── test.cpp │ ├── test_insertion_sort.cpp │ ├── test_sort.cpp │ ├── test_sort.h │ ├── timer.h │ └── type_description.h ├── lecture3 │ ├── concepts.h │ ├── count_operations.cpp │ ├── count_operations.h │ ├── count_operations_normalized.cpp │ ├── functorized.h │ ├── instrumented.cpp │ ├── instrumented.h │ ├── iota.h │ ├── setsort.h │ ├── singleton.h │ └── table_util.h ├── lecture4 │ ├── example.cpp │ ├── min.h │ └── swap.h ├── lecture5 │ ├── algorithm.h │ ├── concepts.h │ ├── minmax.cpp │ ├── minmax.h │ └── timed.h ├── lecture6 │ ├── binary_counter.h │ └── min.cpp ├── lecture7 │ ├── binary_counter.h │ └── min.cpp ├── lecture8 │ ├── binary_counter.h │ ├── list_pool.h │ └── min.cpp └── lecture9 │ ├── binary_counter.h │ ├── list_pool.h │ ├── min.cpp │ └── min_element1_2.h └── papers ├── knuth-computer-programming-as-art.pdf ├── mcilroy-components.txt ├── quicksort.pdf └── trusting-trust-thompson.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | a.out 3 | -------------------------------------------------------------------------------- /code/lecture1/concepts.h: -------------------------------------------------------------------------------- 1 | #ifndef CONCEPTS_H 2 | #define CONCEPTS_H 3 | 4 | #define TotallyOrdered typename 5 | #define Pointer typename 6 | #define Number typename 7 | #define Unsigned typename 8 | #define Integral typename 9 | #define InputIterator typename 10 | #define OutputIterator typename 11 | #define ForwardIterator typename 12 | #define BidirectionalIterator typename 13 | #define RandomAccessIterator typename 14 | #define Sorter typename 15 | #define Incrementable typename 16 | #define StrictWeakOrdering typename 17 | #define Generator typename 18 | #define BinaryOperation typename 19 | #define Sequence typename 20 | 21 | #define ValueType(I) typename std::iterator_traits::value_type 22 | #define DifferenceType(I) typename std::iterator_traits::difference_type 23 | #define IteratorCategory(I) typename std::iterator_traits::iterator_category 24 | 25 | struct less { 26 | template 27 | bool operator()(const T& x, const T& y) { 28 | return x < y; 29 | } 30 | }; 31 | 32 | template 33 | inline 34 | I successor(I x) { return ++x; } 35 | 36 | template 37 | inline 38 | I predecessor(I x) { return ++x; } 39 | 40 | template 41 | inline 42 | I successor(I x, N n, std::random_access_iterator_tag) { 43 | return x + n; 44 | } 45 | 46 | template 47 | inline 48 | I successor(I x, N n, std::input_iterator_tag) { 49 | while (n != N(0)) { 50 | ++x; 51 | --n; 52 | } 53 | return x; 54 | } 55 | 56 | template 57 | inline 58 | I successor(I x, N n) { 59 | return successor(x, n, IteratorCategory(I)()); 60 | } 61 | 62 | template 63 | inline 64 | I successor_guarded(I first, I last, N n, std::random_access_iterator_tag) { 65 | return first + std::min(n, last - first); 66 | } 67 | 68 | template 69 | inline 70 | I successor_guarded(I first, I last, N n, std::input_iterator_tag) { 71 | while (n != N(0) && first != last) { 72 | ++first; 73 | --n; 74 | } 75 | return first; 76 | } 77 | 78 | template 79 | inline 80 | I successor_guarded(I first, I last, N n) { 81 | return successor_guarded(first, last, n, IteratorCategory(I)()); 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /code/lecture1/count_operations.cpp: -------------------------------------------------------------------------------- 1 | #include "count_operations.h" 2 | #include "functorized.h" 3 | 4 | int main() { 5 | count_operations(16, 16 * 1024 * 1024, sort_functor()); 6 | } 7 | -------------------------------------------------------------------------------- /code/lecture1/count_operations.h: -------------------------------------------------------------------------------- 1 | #ifndef COUNT_OPERATIONS_H 2 | #define COUNT_OPERATIONS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "iota.h" 9 | #include "instrumented.h" 10 | #include "table_util.h" 11 | 12 | double normalized_by_n(double x, double n) { return x / n; } 13 | double normalized_by_nlogn(double x, double n) { 14 | return x / (n * (log(n) / log(2))); 15 | } 16 | double dont_normalize(double x, double) { return x; } 17 | 18 | template 19 | void count_operations(size_t i, size_t j, Function fun, double (*norm)(double, double) = dont_normalize) { 20 | // measure operations on an interval of a given length 21 | // ranging from i to j and going through i, 2i, 4i, ... up to and including j 22 | 23 | size_t cols = instrumented::number_ops; 24 | 25 | size_t decimals[cols]; 26 | size_t normalized((norm == dont_normalize) ? 0 : 2); 27 | *decimals = 0; 28 | std::fill(decimals + 1, decimals + cols, normalized); 29 | 30 | table_util table; 31 | table.print_headers(instrumented::counter_names, instrumented::number_ops, 12); 32 | 33 | while (i <= j) { 34 | 35 | std::vector > vec(i); 36 | course::iota(vec.begin(), vec.end(), 0.0); 37 | std::random_shuffle(vec.begin(), vec.end()); 38 | 39 | instrumented::initialize(i); 40 | fun(vec.begin(), vec.end()); 41 | 42 | double* count_p = instrumented::counts; 43 | 44 | for (size_t k(1); k < cols; ++k) count_p[k] = norm(count_p[k], count_p[0]); 45 | 46 | table.print_row(count_p, decimals); 47 | 48 | i <<= 1; 49 | } 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /code/lecture1/count_operations_normalized.cpp: -------------------------------------------------------------------------------- 1 | #include "count_operations.h" 2 | #include "functorized.h" 3 | 4 | int main() { 5 | count_operations(16, 16 * 1028 * 1028, sort_functor(), 6 | normalized_by_nlogn); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /code/lecture1/functorized.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTORIZED_H 2 | #define FUNCTORIZED_H 3 | 4 | #include 5 | 6 | #include "setsort.h" 7 | 8 | /* 9 | strict functorization: 10 | 11 | template 12 | R function_name(args) { code } 13 | 14 | struct function_name_functor 15 | { 16 | template 17 | R operator()(args) const { code } 18 | }; 19 | 20 | partial functorization: 21 | 22 | template 23 | R function_name(args) { code } 24 | 25 | template // could be done for more than 1 type argument 26 | struct function_name_functor 27 | { 28 | template // does not include Ti 29 | R operator()(args) const { code } 30 | }; 31 | 32 | */ 33 | 34 | template 35 | // multiplicative semigroup 36 | inline 37 | T square(const T& x) { return x * x; } 38 | 39 | struct square_functor { 40 | template 41 | T operator()(const T& x) const { 42 | return square(x); 43 | } 44 | }; 45 | 46 | struct sort_functor 47 | { 48 | template 49 | // I is random-access iterator 50 | void operator()(I first, I last) const { 51 | std::sort(first, last); 52 | } 53 | }; 54 | 55 | struct sort_unique_functor 56 | { 57 | template 58 | // I is random-access iterator 59 | I operator()(I first, I last) const { 60 | std::sort(first, last); 61 | return std::unique(first, last); 62 | } 63 | }; 64 | 65 | struct setsort_functor 66 | { 67 | template 68 | // I is forward iterator 69 | void operator()(I first, I last) const { 70 | setsort(first, last); 71 | } 72 | }; 73 | 74 | struct setsort_unique_functor 75 | { 76 | template 77 | // I is forward iterator 78 | I operator()(I first, I last) const { 79 | return setsort_unique(first, last); 80 | } 81 | }; 82 | 83 | 84 | struct stable_sort_functor 85 | { 86 | template 87 | // I is random-access iterator 88 | void operator()(I first, I last) const { std::stable_sort(first, last); } 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /code/lecture1/iota.h: -------------------------------------------------------------------------------- 1 | #ifndef IOTA_H 2 | #define IOTA_H 3 | 4 | #include 5 | 6 | namespace course { 7 | 8 | template 9 | N iota(I first, I last, N start = N(0), N step = N(1)) { 10 | typedef typename std::iterator_traits::value_type T; 11 | while (first != last) { 12 | *first = T(start); 13 | start += step; 14 | ++first; 15 | } 16 | return start; 17 | } 18 | 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /code/lecture1/setsort.h: -------------------------------------------------------------------------------- 1 | #ifndef SETSORT_H 2 | #define SETSORT_H 3 | 4 | #include 5 | #include 6 | #include "concepts.h" 7 | 8 | template 9 | // I is forward iterator 10 | void setsort(I first, I last) { 11 | std::multiset tmp(first, last); 12 | std::copy(tmp.begin(), tmp.end(), first); 13 | } 14 | 15 | template 16 | // I is forward iterator 17 | I setsort_unique(I first, I last) { 18 | std::set tmp(first, last); 19 | return std::copy(tmp.begin(), tmp.end(), 20 | first); 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /code/lecture10/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define ForwardIterator typename 5 | #define RandomAccessIterator typename 6 | #define BidirectionalIterator typename 7 | #define Integral typename 8 | 9 | 10 | template 11 | N iota(I first, I last, N start = N(0), N step = N(1)) { 12 | typedef typename std::iterator_traits::value_type T; 13 | while (first != last) { 14 | *first = T(start); 15 | start += step; 16 | ++first; 17 | } 18 | return start; 19 | } 20 | 21 | template 22 | inline 23 | void iota(I first, I last) { 24 | typedef typename std::iterator_traits::value_type T; 25 | iota(first, last, T(0), T(1)); 26 | } 27 | 28 | template 29 | void random_iota(I first, I last) { 30 | iota(first, last); 31 | std::random_shuffle(first, last); 32 | } 33 | 34 | template 35 | void reverse_iota(I first, I last) { 36 | iota(first, last); 37 | std::reverse(first, last); 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /code/lecture10/binary_counter.h: -------------------------------------------------------------------------------- 1 | #ifndef BINARY_COUNTER_H 2 | #define BINARY_COUNTER_H 3 | 4 | #include 5 | 6 | template 7 | class binary_counter 8 | { 9 | private: 10 | std::vector counter; 11 | Op op; 12 | T zero; 13 | 14 | public: 15 | binary_counter(const Op& op, const T& zero) : 16 | op(op), zero(zero) {} 17 | 18 | void reserve(size_t n) { counter.reserve(n); } 19 | 20 | void add(T x) { 21 | x = add_to_counter(counter.begin(), counter.end(), op, zero, x); 22 | if (x != zero) counter.push_back(x); 23 | } 24 | 25 | // returns: value of the counter 26 | T reduce() { 27 | return reduce_counter(counter.begin(), counter.end(), op, zero); 28 | } 29 | ~binary_counter() { std::cout << "counter size: " << counter.size() << std::endl; } 30 | }; 31 | 32 | template 33 | // requires Op is BinaryOperation(T) 34 | // and Op is associative 35 | // and I is ForwardIterator and ValueType(I) == T 36 | T add_to_counter(I first, I last, Op op, const T& zero, T carry) { 37 | // precondition: carry != zero 38 | while (first != last) { 39 | if (*first == zero) { 40 | *first = carry; 41 | return zero; 42 | } 43 | carry = op(*first, carry); 44 | *first = zero; 45 | ++first; 46 | } 47 | return carry; 48 | } 49 | 50 | template 51 | // requires Op is BinaryOperation(T) 52 | // and Op is associative 53 | // and I is ForwardIterator and ValueType(I) == T 54 | T reduce_counter(I first, I last, Op op, const T& zero) { 55 | while (first != last && *first == zero) { 56 | ++first; 57 | } 58 | if (first == last) return zero; 59 | 60 | T result = *first; 61 | while (++first != last) { 62 | if (*first != zero) { 63 | result = op(*first, result); 64 | } 65 | } 66 | return result; 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /code/lecture10/list_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_POOL_H 2 | #define LIST_POOL_H 3 | 4 | #include 5 | #include 6 | 7 | // Requirements on T: semiregular. 8 | // Requirements on N: integral 9 | template 10 | class list_pool { 11 | public: 12 | typedef N list_type; 13 | 14 | private: 15 | 16 | struct node_t { 17 | T value; 18 | list_type next; 19 | }; 20 | 21 | std::vector pool; 22 | 23 | node_t& node(list_type x) { 24 | return pool[x - 1]; 25 | } 26 | const node_t& node(list_type x) const { 27 | return pool[x - 1]; 28 | } 29 | 30 | list_type new_list() { 31 | pool.push_back(node_t()); 32 | return list_type(pool.size()); 33 | } 34 | 35 | list_type free_list; 36 | 37 | public: 38 | typedef typename std::vector::size_type size_type; 39 | 40 | list_type end() const { 41 | return list_type(0); 42 | } 43 | 44 | bool is_end(list_type x) const { 45 | return x == end(); 46 | } 47 | 48 | bool empty() const { 49 | return pool.empty(); 50 | } 51 | 52 | size_type size() const { 53 | return pool.size(); 54 | } 55 | 56 | size_type capacity() const { 57 | return pool.capacity(); 58 | } 59 | 60 | void reserve(size_type n) { 61 | pool.reserve(n); 62 | } 63 | 64 | list_pool() { 65 | free_list = end(); 66 | } 67 | 68 | list_pool(size_type n) { 69 | free_list = end(); 70 | reserve(n); 71 | } 72 | 73 | T& value(list_type x) { 74 | return node(x).value; 75 | } 76 | 77 | const T& value(list_type x) const { 78 | return node(x).value; 79 | } 80 | 81 | list_type& next(list_type x) { 82 | return node(x).next; 83 | } 84 | const list_type& next(list_type x) const { 85 | return node(x).next; 86 | } 87 | 88 | list_type free(list_type x) { 89 | list_type cdr = next(x); 90 | next(x) = free_list; 91 | free_list = x; 92 | return cdr; 93 | } 94 | 95 | list_type allocate(const T& val, list_type tail) { 96 | list_type list = free_list; 97 | if (is_end(free_list)) { 98 | list = new_list(); 99 | } else { 100 | free_list = next(free_list); 101 | } 102 | value(list) = val; 103 | next(list) = tail; 104 | return list; 105 | } 106 | ~list_pool() { std::cout << "pool size: " << size() << std::endl; } 107 | }; 108 | 109 | template 110 | void free_list(list_pool& pool, 111 | typename list_pool::list_type x) 112 | { 113 | while (!pool.is_end(x)) x = pool.free(x); 114 | } 115 | 116 | template 117 | typename list_pool::list_type 118 | min_element_list(const list_pool& pool, 119 | typename list_pool::list_type list, 120 | Compare cmp) { 121 | if (pool.is_end(list)) return list; 122 | typename list_pool::list_type current_min = list; 123 | list = pool.next(list); 124 | while (!pool.is_end(list)) { 125 | if (cmp(pool.value(list), pool.value(current_min))) { 126 | current_min = list; 127 | } 128 | list = pool.next(list); 129 | } 130 | return current_min; 131 | } 132 | 133 | template 134 | typename list_pool::list_type 135 | min_element_last_list(const list_pool& pool, 136 | typename list_pool::list_type list, 137 | Compare cmp) { 138 | if (pool.is_end(list)) return list; 139 | typename list_pool::list_type current_min = list; 140 | list = pool.next(list); 141 | while (!pool.is_end(list)) { 142 | if (!cmp(pool.value(current_min), pool.value(list))) { 143 | current_min = list; 144 | } 145 | list = pool.next(list); 146 | } 147 | return current_min; 148 | } 149 | #endif 150 | -------------------------------------------------------------------------------- /code/lecture10/min.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "min_element1_2.h" 5 | #include "algorithm.h" 6 | 7 | template 8 | class counted_compare 9 | { 10 | private: 11 | Compare cmp; 12 | size_t* counter_p; 13 | public: 14 | counted_compare(const Compare& cmp, size_t& counter) : cmp(cmp), counter_p(&counter) {} 15 | counted_compare(size_t& counter) : cmp(), counter_p(&counter) {} 16 | template 17 | bool operator()(const T& x, const T& y) const { 18 | ++*counter_p; 19 | return cmp(x & 7, y & 7); 20 | } 21 | }; 22 | 23 | template 24 | void count_comparisons(I first, I last, 25 | std::pair (*algorithm)(I, I, counted_compare::value_type> >), 26 | const char* algorithm_name) { 27 | typedef typename std::iterator_traits::value_type T; 28 | size_t counter(0); 29 | counted_compare > cmp(counter); 30 | std::pair result = algorithm(first, last, cmp); 31 | std::cout << algorithm_name << " "; 32 | std::cout << "results " << *result.first << " " << *result.second << " "; 33 | std::cout << "number of comparisons " << counter << std::endl; 34 | } 35 | 36 | 37 | int main() { 38 | std::vector vec(1000 * 1000); 39 | typedef std::vector::iterator I; 40 | random_iota(vec.begin(), vec.end()); 41 | count_comparisons(vec.begin(), vec.end(), min_element1_2, "min_element1_2"); 42 | count_comparisons(vec.begin(), vec.end(), min_element1_2_stable0, "min_element1_2_stable0"); 43 | count_comparisons(vec.begin(), vec.end(), min_element1_2_stable, "min_element1_2_stable"); 44 | count_comparisons(vec.begin(), vec.end(), min_element1_2_stable2, "min_element1_2_stable2"); 45 | count_comparisons(vec.begin(), vec.end(), min_element1_2_practical, "min_element1_2_practical"); 46 | count_comparisons(vec.begin(), vec.end(), min_element1_2_practical_lecture, "min_element1_2_practical_lecture"); 47 | } 48 | -------------------------------------------------------------------------------- /code/lecture10v2/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | template 11 | inline 12 | I successor(I x) { return ++x; } 13 | 14 | template 15 | N iota(I first, I last, N start = N(0), N step = N(1)) { 16 | typedef typename std::iterator_traits::value_type T; 17 | while (first != last) { 18 | *first = T(start); 19 | start += step; 20 | ++first; 21 | } 22 | return start; 23 | } 24 | 25 | template 26 | inline 27 | void iota(I first, I last) { 28 | typedef typename std::iterator_traits::value_type T; 29 | iota(first, last, T(0), T(1)); 30 | } 31 | 32 | template 33 | void random_iota(I first, I last) { 34 | iota(first, last); 35 | std::random_shuffle(first, last); 36 | } 37 | 38 | template 39 | void reverse_iota(I first, I last) { 40 | iota(first, last); 41 | std::reverse(first, last); 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /code/lecture10v2/binary_counter.h: -------------------------------------------------------------------------------- 1 | #ifndef BINARY_COUNTER_H 2 | #define BINARY_COUNTER_H 3 | 4 | #include 5 | 6 | template 7 | class binary_counter 8 | { 9 | private: 10 | std::vector counter; 11 | Op op; 12 | T zero; 13 | 14 | public: 15 | binary_counter(const Op& op, const T& zero) : 16 | op(op), zero(zero) {} 17 | 18 | void reserve(size_t n) { counter.reserve(n); } 19 | 20 | void add(T x) { 21 | x = add_to_counter(counter.begin(), counter.end(), op, zero, x); 22 | if (x != zero) counter.push_back(x); 23 | } 24 | 25 | // returns: value of the counter 26 | T reduce() { 27 | return reduce_counter(counter.begin(), counter.end(), op, zero); 28 | } 29 | ~binary_counter() { std::cout << "counter size: " << counter.size() << std::endl; } 30 | }; 31 | 32 | template 33 | // requires Op is BinaryOperation(T) 34 | // and Op is associative 35 | // and I is ForwardIterator and ValueType(I) == T 36 | T add_to_counter(I first, I last, Op op, const T& zero, T carry) { 37 | // precondition: carry != zero 38 | while (first != last) { 39 | if (*first == zero) { 40 | *first = carry; 41 | return zero; 42 | } 43 | carry = op(*first, carry); 44 | *first = zero; 45 | ++first; 46 | } 47 | return carry; 48 | } 49 | 50 | template 51 | // requires Op is BinaryOperation(T) 52 | // and Op is associative 53 | // and I is ForwardIterator and ValueType(I) == T 54 | T reduce_counter(I first, I last, Op op, const T& zero) { 55 | while (first != last && *first == zero) { 56 | ++first; 57 | } 58 | if (first == last) return zero; 59 | 60 | T result = *first; 61 | while (++first != last) { 62 | if (*first != zero) { 63 | result = op(*first, result); 64 | } 65 | } 66 | return result; 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /code/lecture10v2/list_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_POOL_H 2 | #define LIST_POOL_H 3 | 4 | #include 5 | #include 6 | 7 | // Requirements on T: semiregular. 8 | // Requirements on N: integral 9 | template 10 | class list_pool { 11 | public: 12 | typedef N list_type; 13 | 14 | private: 15 | 16 | struct node_t { 17 | T value; 18 | list_type next; 19 | }; 20 | 21 | std::vector pool; 22 | 23 | node_t& node(list_type x) { 24 | return pool[x - 1]; 25 | } 26 | const node_t& node(list_type x) const { 27 | return pool[x - 1]; 28 | } 29 | 30 | list_type new_list() { 31 | pool.push_back(node_t()); 32 | return list_type(pool.size()); 33 | } 34 | 35 | list_type free_list; 36 | 37 | public: 38 | typedef typename std::vector::size_type size_type; 39 | 40 | list_type end() const { 41 | return list_type(0); 42 | } 43 | 44 | bool is_end(list_type x) const { 45 | return x == end(); 46 | } 47 | 48 | bool empty() const { 49 | return pool.empty(); 50 | } 51 | 52 | size_type size() const { 53 | return pool.size(); 54 | } 55 | 56 | size_type capacity() const { 57 | return pool.capacity(); 58 | } 59 | 60 | void reserve(size_type n) { 61 | pool.reserve(n); 62 | } 63 | 64 | list_pool() { 65 | free_list = end(); 66 | } 67 | 68 | list_pool(size_type n) { 69 | free_list = end(); 70 | reserve(n); 71 | } 72 | 73 | T& value(list_type x) { 74 | return node(x).value; 75 | } 76 | 77 | const T& value(list_type x) const { 78 | return node(x).value; 79 | } 80 | 81 | list_type& next(list_type x) { 82 | return node(x).next; 83 | } 84 | const list_type& next(list_type x) const { 85 | return node(x).next; 86 | } 87 | 88 | list_type free(list_type x) { 89 | list_type tail = next(x); 90 | next(x) = free_list; 91 | free_list = x; 92 | return tail; 93 | } 94 | 95 | list_type free(list_type front, list_type back) { 96 | if (is_end(front)) return end(); 97 | list_type tail = next(back); 98 | next(back) = free_list; 99 | free_list = front; 100 | return tail; 101 | } 102 | 103 | list_type allocate(const T& val, list_type tail) { 104 | list_type list = free_list; 105 | if (is_end(free_list)) { 106 | list = new_list(); 107 | } else { 108 | free_list = next(free_list); 109 | } 110 | value(list) = val; 111 | next(list) = tail; 112 | return list; 113 | } 114 | ~list_pool() { std::cout << "pool size: " << size() << std::endl; } 115 | }; 116 | 117 | template 118 | inline 119 | std::pair::list_type, 120 | typename list_pool::list_type> 121 | push_front(list_pool& pool, 122 | typename list_pool::list_type front, 123 | typename list_pool::list_type back, 124 | const T& value) { 125 | typename list_pool::list_type new_node = pool.allocate(value, front); 126 | if (pool.is_end(front)) return std::make_pair(new_node, new_node); 127 | return std::make_pair(new_node, back); 128 | } 129 | 130 | template 131 | inline 132 | std::pair::list_type, 133 | typename list_pool::list_type> 134 | push_back(list_pool& pool, 135 | typename list_pool::list_type front, 136 | typename list_pool::list_type back, 137 | const T& value) { 138 | typename list_pool::list_type new_node = pool.allocate(value, pool.end()); 139 | if (pool.is_end(front)) return std::make_pair(new_node, new_node); 140 | pool.next(back) = new_node; 141 | return std::make_pair(front, new_node); 142 | } 143 | 144 | template 145 | void free_list(list_pool& pool, 146 | typename list_pool::list_type x) { 147 | while (!pool.is_end(x)) x = pool.free(x); 148 | } 149 | 150 | template 151 | typename list_pool::list_type 152 | min_element_list(const list_pool& pool, 153 | typename list_pool::list_type list, 154 | Compare cmp) { 155 | if (pool.is_end(list)) return list; 156 | typename list_pool::list_type current_min = list; 157 | list = pool.next(list); 158 | while (!pool.is_end(list)) { 159 | if (cmp(pool.value(list), pool.value(current_min))) { 160 | current_min = list; 161 | } 162 | list = pool.next(list); 163 | } 164 | return current_min; 165 | } 166 | 167 | template 168 | typename list_pool::list_type 169 | min_element_last_list(const list_pool& pool, 170 | typename list_pool::list_type list, 171 | Compare cmp) { 172 | if (pool.is_end(list)) return list; 173 | typename list_pool::list_type current_min = list; 174 | list = pool.next(list); 175 | while (!pool.is_end(list)) { 176 | if (!cmp(pool.value(current_min), pool.value(list))) { 177 | current_min = list; 178 | } 179 | list = pool.next(list); 180 | } 181 | return current_min; 182 | } 183 | #endif 184 | -------------------------------------------------------------------------------- /code/lecture10v2/min.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "min_element1_2.h" 5 | #include "algorithm.h" 6 | 7 | template 8 | class counted_compare 9 | { 10 | private: 11 | Compare cmp; 12 | size_t* counter_p; 13 | public: 14 | counted_compare(const Compare& cmp, size_t& counter) : cmp(cmp), counter_p(&counter) {} 15 | counted_compare(size_t& counter) : cmp(), counter_p(&counter) {} 16 | template 17 | bool operator()(const T& x, const T& y) const { 18 | ++*counter_p; 19 | return cmp(x & 63, y & 63); 20 | } 21 | }; 22 | 23 | template 24 | void count_comparisons(I first, I last, 25 | std::pair (*algorithm)(I, I, counted_compare::value_type> >), 26 | const char* algorithm_name) { 27 | typedef typename std::iterator_traits::value_type T; 28 | size_t counter(0); 29 | counted_compare > cmp(counter); 30 | std::pair result = algorithm(first, last, cmp); 31 | std::cout << algorithm_name << " "; 32 | std::cout << "results " << *result.first << " " << *result.second << " "; 33 | std::cout << "number of comparisons " << counter << std::endl; 34 | } 35 | 36 | 37 | int main() { 38 | std::vector vec(1000 * 1000); 39 | typedef std::vector::iterator I; 40 | random_iota(vec.begin(), vec.end()); 41 | count_comparisons(vec.begin(), vec.end(), min_element1_2, "min_element1_2"); 42 | count_comparisons(vec.begin(), vec.end(), min_element1_2_stable_random_access, "min_element1_2_stable_random_access"); 43 | count_comparisons(vec.begin(), vec.end(), min_element1_2_stable, "min_element1_2_stable"); 44 | count_comparisons(vec.begin(), vec.end(), min_element1_2_stable_indexed, "min_element1_2_stable_indexed"); 45 | count_comparisons(vec.begin(), vec.end(), min_element1_2_practical, "min_element1_2_practical"); 46 | } 47 | -------------------------------------------------------------------------------- /code/lecture11/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | template 11 | inline 12 | I successor(I x) { return ++x; } 13 | 14 | template 15 | N iota(I first, I last, N start = N(0), N step = N(1)) { 16 | typedef typename std::iterator_traits::value_type T; 17 | while (first != last) { 18 | *first = T(start); 19 | start += step; 20 | ++first; 21 | } 22 | return start; 23 | } 24 | 25 | template 26 | inline 27 | void iota(I first, I last) { 28 | typedef typename std::iterator_traits::value_type T; 29 | iota(first, last, T(0), T(1)); 30 | } 31 | 32 | template 33 | void random_iota(I first, I last) { 34 | iota(first, last); 35 | std::random_shuffle(first, last); 36 | } 37 | 38 | template 39 | void reverse_iota(I first, I last) { 40 | iota(first, last); 41 | std::reverse(first, last); 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /code/lecture11/binary_counter.h: -------------------------------------------------------------------------------- 1 | #ifndef BINARY_COUNTER_H 2 | #define BINARY_COUNTER_H 3 | 4 | #include 5 | 6 | template 7 | class binary_counter 8 | { 9 | private: 10 | std::vector counter; 11 | Op op; 12 | T zero; 13 | 14 | public: 15 | binary_counter(const Op& op, const T& zero) : 16 | op(op), zero(zero) {} 17 | 18 | void reserve(size_t n) { counter.reserve(n); } 19 | 20 | void add(T x) { 21 | x = add_to_counter(counter.begin(), counter.end(), op, zero, x); 22 | if (x != zero) counter.push_back(x); 23 | } 24 | 25 | // returns: value of the counter 26 | T reduce() { 27 | return reduce_counter(counter.begin(), counter.end(), op, zero); 28 | } 29 | ~binary_counter() { std::cout << "counter size: " << counter.size() << std::endl; } 30 | }; 31 | 32 | template 33 | // requires Op is BinaryOperation(T) 34 | // and Op is associative 35 | // and I is ForwardIterator and ValueType(I) == T 36 | T add_to_counter(I first, I last, Op op, const T& zero, T carry) { 37 | // precondition: carry != zero 38 | while (first != last) { 39 | if (*first == zero) { 40 | *first = carry; 41 | return zero; 42 | } 43 | carry = op(*first, carry); 44 | *first = zero; 45 | ++first; 46 | } 47 | return carry; 48 | } 49 | 50 | template 51 | // requires Op is BinaryOperation(T) 52 | // and Op is associative 53 | // and I is ForwardIterator and ValueType(I) == T 54 | T reduce_counter(I first, I last, Op op, const T& zero) { 55 | while (first != last && *first == zero) { 56 | ++first; 57 | } 58 | if (first == last) return zero; 59 | 60 | T result = *first; 61 | while (++first != last) { 62 | if (*first != zero) { 63 | result = op(*first, result); 64 | } 65 | } 66 | return result; 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /code/lecture11/lisp/binary-counter.lisp: -------------------------------------------------------------------------------- 1 | (provide 'binary-counter) 2 | 3 | (defun add-to-counter (counter op zero carry) 4 | (cond ((null counter) carry) 5 | ((eq (car counter) zero) (progn (setf (car counter) carry) 6 | zero)) 7 | (t (let ((new-carry (funcall op (car counter) carry))) 8 | (setf (car counter) zero) 9 | (add-to-counter (cdr counter) op zero new-carry))))) 10 | 11 | (defun skip-zeros (counter zero) 12 | (cond ((or (null counter) (not (eq (car counter) zero))) counter) 13 | (t (skip-zeros (cdr counter) zero)))) 14 | 15 | (defun reduce-counter (counter op zero result) 16 | (cond ((null counter) result) 17 | ((eq zero (car counter)) (reduce-counter (cdr counter) op zero result)) 18 | (t (reduce-counter (cdr counter) op zero (funcall op (car counter) result))))) 19 | 20 | ;;; Creates a new binary counter and returns 3 functions 21 | ;;; add, reduce and dump. 22 | (defun binary-counter (initial-capacity op zero) 23 | (let ((counter (make-list initial-capacity)) 24 | (op op) 25 | (zero zero)) 26 | (values 27 | ;; Function to add to counter. 28 | (function (lambda (x) 29 | (let ((result (add-to-counter counter op zero x))) 30 | (when result 31 | (setf counter (append counter (list result))))))) 32 | ;; Function to reduce the counter. 33 | (function (lambda () 34 | (let ((counter (skip-zeros counter zero))) 35 | (if (null counter) 36 | nil 37 | (reduce-counter (cdr counter) op zero (car counter)))))) 38 | ;; Function to dump the counter. 39 | (function (lambda () counter))))) 40 | 41 | ;;; Creates a combine function that can be used to apply the operator 42 | ;;; to items in the binary counter. 43 | (defun make-combine (op) 44 | (let ((op op)) 45 | (function (lambda (x y) (if (funcall op y x) y x))))) 46 | 47 | #| 48 | 49 | Test: 50 | 51 | (multiple-value-setq (add reduce dump) (binary-counter 8 (make-combine '<) nil)) 52 | (mapcar add '(1 2 3 4 5 6 7 8 9 10)) 53 | (funcall reduce) 54 | 55 | |# 56 | 57 | -------------------------------------------------------------------------------- /code/lecture11/lisp/util.lisp: -------------------------------------------------------------------------------- 1 | (provide 'util) 2 | 3 | ;; --------------------------------------------------------------- 4 | ;; Instrumented operations using Lexical Closures... 5 | ;; --------------------------------------------------------------- 6 | 7 | ;; Makes two function: an instrumented operation and a get-counts 8 | ;; that can be called to see the total number of calls. To reset 9 | ;; the counts, just make a new set. 10 | (defun make-instrumented (op) 11 | (let ((op-count 0)) 12 | (values (function (lambda (x y) 13 | (progn 14 | (setq op-count (+ op-count 1)) 15 | (funcall op x y)))) 16 | (function (lambda () op-count))))) 17 | 18 | #| 19 | (multiple-value-bind (cmp get-counts) (make-instrumented '<) 20 | (funcall cmp 10 20) 21 | (funcall cmp 11 34) 22 | (funcall get-counts)) 23 | |# 24 | 25 | ;; --------------------------------------------------------------- 26 | ;; Generating Data Sets for Testing 27 | ;; --------------------------------------------------------------- 28 | 29 | (defun set-car-sequential (list start step) 30 | (cond ((null list) nil) 31 | (t (setf (car list) start) 32 | (set-car-sequential (cdr list) (+ start step) step)))) 33 | 34 | (defun gen-sequential-list (size start step) 35 | (let ((result (make-list size))) 36 | (set-car-sequential result start step) 37 | result)) 38 | 39 | ;; --------------------------------------------------------------- 40 | ;; Timing 41 | ;; --------------------------------------------------------------- 42 | 43 | (defmacro timing (&body forms) 44 | (let ((real1 (gensym)) 45 | (real2 (gensym)) 46 | (run1 (gensym)) 47 | (run2 (gensym)) 48 | (result (gensym))) 49 | `(let* ((,real1 (get-internal-real-time)) 50 | (,run1 (get-internal-run-time)) 51 | (,result (progn ,@forms)) 52 | (,run2 (get-internal-run-time)) 53 | (,real2 (get-internal-real-time))) 54 | (format *debug-io* ";;; Computation took:~%") 55 | (format *debug-io* ";;; ~f seconds of real time~%" 56 | (/ (- ,real2 ,real1) internal-time-units-per-second)) 57 | (format t ";;; ~f seconds of run time~%" 58 | (/ (- ,run2 ,run1) internal-time-units-per-second)) 59 | ,result))) 60 | -------------------------------------------------------------------------------- /code/lecture11/list_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_POOL_H 2 | #define LIST_POOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Requirements on T: semiregular. 9 | // Requirements on N: integral 10 | template 11 | class list_pool { 12 | public: 13 | typedef N list_type; 14 | typedef T value_type; 15 | 16 | private: 17 | 18 | struct node_t { 19 | T value; 20 | list_type next; 21 | }; 22 | 23 | std::vector pool; 24 | 25 | node_t& node(list_type x) { 26 | return pool[x - 1]; 27 | } 28 | const node_t& node(list_type x) const { 29 | return pool[x - 1]; 30 | } 31 | 32 | list_type new_list() { 33 | pool.push_back(node_t()); 34 | return list_type(pool.size()); 35 | } 36 | 37 | list_type free_list; 38 | 39 | public: 40 | typedef typename std::vector::size_type size_type; 41 | 42 | list_type end() const { 43 | return list_type(0); 44 | } 45 | 46 | bool is_end(list_type x) const { 47 | return x == end(); 48 | } 49 | 50 | bool empty() const { 51 | return pool.empty(); 52 | } 53 | 54 | size_type size() const { 55 | return pool.size(); 56 | } 57 | 58 | size_type capacity() const { 59 | return pool.capacity(); 60 | } 61 | 62 | void reserve(size_type n) { 63 | pool.reserve(n); 64 | } 65 | 66 | list_pool() { 67 | free_list = end(); 68 | } 69 | 70 | list_pool(size_type n) { 71 | free_list = end(); 72 | reserve(n); 73 | } 74 | 75 | T& value(list_type x) { 76 | return node(x).value; 77 | } 78 | 79 | const T& value(list_type x) const { 80 | return node(x).value; 81 | } 82 | 83 | list_type& next(list_type x) { 84 | return node(x).next; 85 | } 86 | const list_type& next(list_type x) const { 87 | return node(x).next; 88 | } 89 | 90 | list_type free(list_type x) { 91 | list_type tail = next(x); 92 | next(x) = free_list; 93 | free_list = x; 94 | return tail; 95 | } 96 | 97 | list_type free(list_type front, list_type back) { 98 | if (is_end(front)) return end(); 99 | list_type tail = next(back); 100 | next(back) = free_list; 101 | free_list = front; 102 | return tail; 103 | } 104 | 105 | list_type allocate(const T& val, list_type tail) { 106 | list_type list = free_list; 107 | if (is_end(free_list)) { 108 | list = new_list(); 109 | } else { 110 | free_list = next(free_list); 111 | } 112 | value(list) = val; 113 | next(list) = tail; 114 | return list; 115 | } 116 | 117 | // operations on queues: 118 | // pop_front, push_front, push_back and free, etc 119 | 120 | typedef std::pair pair_type; 121 | 122 | bool empty(const pair_type& p) { return is_end(p.first); } 123 | 124 | pair_type empty_queue() { return pair_type(end(), end()); } 125 | 126 | pair_type pop_front(const pair_type& p) { 127 | if (empty(p)) return p; 128 | return pair_type(next(p.first), p.second); 129 | } 130 | 131 | pair_type push_front(const pair_type& p, const T& value) { 132 | list_type new_node = allocate(value, p.first); 133 | if (empty(p)) return pair_type(new_node, new_node); 134 | return pair_type(new_node, p.second); 135 | } 136 | 137 | pair_type push_back(const pair_type& p, const T& value) { 138 | list_type new_node = allocate(value, end()); 139 | if (empty(p)) return pair_type(new_node, new_node); 140 | next(p.second) = new_node; 141 | return pair_type(p.first, new_node); 142 | } 143 | 144 | void free(const pair_type& p) { free(p.first, p.second); } 145 | 146 | struct iterator { 147 | typedef list_pool::value_type value_type; 148 | typedef list_pool::list_type difference_type; 149 | typedef std::forward_iterator_tag iterator_category; 150 | 151 | typedef value_type& reference; 152 | typedef value_type* pointer; 153 | 154 | list_pool* pool; 155 | list_pool::list_type node; 156 | 157 | iterator() {} // creates a partially formed value 158 | iterator(list_pool& p, list_pool::list_type node) : 159 | pool(&p), node(node) {} 160 | iterator(list_pool& p) : pool(&p), node(p.empty()) {} 161 | 162 | reference operator*() const { 163 | return pool->value(node); 164 | } 165 | 166 | pointer operator->() const { 167 | return &**this; 168 | } 169 | 170 | iterator& operator++() { 171 | node = pool->next(node); 172 | return *this; 173 | } 174 | 175 | iterator operator++(int) { 176 | iterator tmp(*this); 177 | ++*this; 178 | return tmp; 179 | } 180 | 181 | friend 182 | bool operator==(const iterator& x, const iterator& y) { 183 | // assert(x.pool == y.pool); 184 | return x.node == y.node; 185 | } 186 | 187 | friend 188 | bool operator!=(const iterator& x, const iterator& y) { 189 | return !(x == y); 190 | } 191 | }; 192 | }; 193 | 194 | 195 | #endif 196 | -------------------------------------------------------------------------------- /code/lecture11/min.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "min_element1_2.h" 5 | #include "algorithm.h" 6 | 7 | template 8 | class counted_compare 9 | { 10 | private: 11 | Compare cmp; 12 | size_t* counter_p; 13 | public: 14 | counted_compare(const Compare& cmp, size_t& counter) : cmp(cmp), counter_p(&counter) {} 15 | counted_compare(size_t& counter) : cmp(), counter_p(&counter) {} 16 | template 17 | bool operator()(const T& x, const T& y) const { 18 | ++*counter_p; 19 | return cmp(x & 63, y & 63); 20 | } 21 | }; 22 | 23 | template 24 | void count_comparisons(I first, I last, 25 | std::pair (*algorithm)(I, I, counted_compare::value_type> >), 26 | const char* algorithm_name) { 27 | typedef typename std::iterator_traits::value_type T; 28 | size_t counter(0); 29 | counted_compare > cmp(counter); 30 | std::pair result = algorithm(first, last, cmp); 31 | std::cout << algorithm_name << " "; 32 | std::cout << "results " << *result.first << " " << *result.second << " "; 33 | std::cout << "number of comparisons " << counter << std::endl; 34 | } 35 | 36 | 37 | int main() { 38 | std::vector vec(1000 * 1000); 39 | typedef std::vector::iterator I; 40 | random_iota(vec.begin(), vec.end()); 41 | count_comparisons(vec.begin(), vec.end(), min_element1_2, "min_element1_2"); 42 | count_comparisons(vec.begin(), vec.end(), min_element1_2_practical, "min_element1_2_practical"); 43 | } 44 | -------------------------------------------------------------------------------- /code/lecture11/min_element1_2.h: -------------------------------------------------------------------------------- 1 | #ifndef MIN_ELEMENT1_2_H 2 | #define MIN_ELEMENT1_2_H 3 | 4 | #include 5 | #include "algorithm.h" 6 | #include "binary_counter.h" 7 | #include "list_pool.h" 8 | 9 | template 10 | class compare_dereference 11 | { 12 | private: 13 | Compare cmp; 14 | public: 15 | compare_dereference(const Compare& cmp) : cmp(cmp) {} 16 | template 17 | bool operator() (const I& x, const I& y) const { 18 | return cmp(*x, *y); 19 | } 20 | }; 21 | 22 | /****************** optimal algorithm (stable) ****************************/ 23 | 24 | template 25 | class op_min1_2 26 | { 27 | private: 28 | Compare cmp; 29 | list_pool* p; 30 | public: 31 | typedef typename list_pool::list_type list_type; 32 | typedef std::pair > argument_type; 33 | 34 | op_min1_2(const Compare& cmp, list_pool& pool) : cmp(cmp), p(&pool) {} 35 | 36 | argument_type operator()(const argument_type& x, 37 | const argument_type& y) { 38 | if (!cmp(y.first, x.first)) { 39 | p->free(y.second); 40 | return std::make_pair(x.first, p->push_back(x.second, y.first)); 41 | } else { 42 | p->free(x.second); 43 | return std::make_pair(y.first, p->push_front(y.second, x.first)); 44 | } 45 | } 46 | }; 47 | 48 | template 49 | // requires I is a ForwardIterator 50 | // and Compare is a StrictWeakOrdering on ValueType(I) 51 | std::pair min_element1_2(I first, I last, Compare cmp) { 52 | if (first == last || successor(first) == last) { 53 | return std::make_pair(first, last); 54 | } 55 | 56 | typedef op_min1_2 > op_type; 57 | typedef typename list_pool::iterator iterator; 58 | 59 | list_pool pool; 60 | pool.reserve(256); 61 | compare_dereference cmp_deref(cmp); 62 | op_type op(cmp_deref, pool); 63 | binary_counter counter(op, std::make_pair(last, pool.empty_queue())); 64 | counter.reserve(32); 65 | 66 | while (first != last) counter.add(std::make_pair(first++, pool.empty_queue())); 67 | typename op_type::argument_type min1_list = counter.reduce(); 68 | I min1 = min1_list.first; 69 | I min2 = *std::min_element(iterator(pool, min1_list.second.first), iterator(pool), cmp_deref); 70 | return std::make_pair(min1, min2); 71 | } 72 | 73 | //********************** practical algorithm 74 | 75 | template 76 | // Compare is a StrictWeakOrdering on T 77 | inline 78 | void insert_2(T& first, T& second, const T& candidate, Compare cmp) { 79 | if (cmp(candidate, second)) { 80 | if (cmp(candidate, first)) { 81 | second = first; 82 | first = candidate; 83 | } else { 84 | second = candidate; 85 | } 86 | } 87 | } 88 | 89 | template 90 | // requires I is a ForwardIterator 91 | // and Compare is a StrictWeakOrdering on ValueType(I) 92 | std::pair min_element1_2_practical(I first, I last, Compare cmp) { 93 | if (first == last || successor(first) == last) { 94 | return std::make_pair(first, last); 95 | } 96 | 97 | compare_dereference cmp_deref(cmp); 98 | 99 | I first_place = first++; 100 | I second_place = first++; 101 | if (cmp_deref(second_place, first_place)) std::swap(first_place, second_place); 102 | 103 | while (first != last) insert_2(first_place, second_place, first++, cmp_deref); 104 | return std::make_pair(first_place, second_place); 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /code/lecture11/python/min.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import operator 4 | 5 | def min1(seq, op=operator.lt): 6 | seq = iter(seq) 7 | try: 8 | result = next(seq) 9 | except StopIteration: 10 | return None 11 | for x in seq: 12 | if op(x, result): 13 | result = x 14 | return result 15 | 16 | def add_to_counter(counter, op, zero, carry): 17 | for i, c in enumerate(counter): 18 | if c == zero: 19 | counter[i] = carry 20 | return zero 21 | carry = op(c, carry) 22 | counter[i] = zero 23 | return carry 24 | 25 | def reduce_counter(counter, op, zero): 26 | it = iter(counter) 27 | result = None 28 | for c in it: 29 | if c != zero: 30 | result = c 31 | break 32 | 33 | if result is None: 34 | return zero 35 | 36 | for c in it: 37 | if c != zero: 38 | result = op(c, result) 39 | return result 40 | 41 | class BinaryCounter: 42 | def __init__(self, op, zero): 43 | self.op = op 44 | self.zero = zero 45 | self.counter = [zero]*16 46 | 47 | def add(self, x): 48 | x = add_to_counter(self.counter, self.op, self.zero, x) 49 | if x != self.zero: self.counter.append(x) 50 | 51 | def reduce(self): 52 | return reduce_counter(self.counter, self.op, self.zero) 53 | 54 | class Min2Op: 55 | def __init__(self, op=operator.lt): 56 | self.op = op 57 | 58 | def __call__(self, x, y): 59 | if self.op(y[0], x[0]): 60 | y[1].insert(0, x[0]) 61 | return y 62 | else: 63 | x[1].append(y[0]) 64 | return x 65 | 66 | def min2(seq): 67 | op = Min2Op() 68 | counter = BinaryCounter(op, (None, [])) 69 | for x in seq: 70 | counter.add((x, [])) 71 | m = counter.reduce() 72 | return m[0], min1(m[1]) 73 | 74 | def main(): 75 | # data = [7, 15, 45, 3, 14, 12, 9] 76 | data = range(1000*1000*10) 77 | print min2(data) 78 | 79 | if __name__ == '__main__': 80 | main() -------------------------------------------------------------------------------- /code/lecture12/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | #include 11 | 12 | template 13 | inline 14 | I successor(I x) { return ++x; } 15 | 16 | template 17 | N iota(I first, I last, N start = N(0), N step = N(1)) { 18 | typedef typename std::iterator_traits::value_type T; 19 | while (first != last) { 20 | *first = T(start); 21 | start += step; 22 | ++first; 23 | } 24 | return start; 25 | } 26 | 27 | template 28 | inline 29 | void iota(I first, I last) { 30 | typedef typename std::iterator_traits::value_type T; 31 | iota(first, last, T(0), T(1)); 32 | } 33 | 34 | template 35 | void random_iota(I first, I last) { 36 | iota(first, last); 37 | std::random_shuffle(first, last); 38 | } 39 | 40 | template 41 | void reverse_iota(I first, I last) { 42 | iota(first, last); 43 | std::reverse(first, last); 44 | } 45 | 46 | template 47 | void print_range(I first, I last) { 48 | while (first != last) { 49 | std::cout << *first << " "; 50 | ++first; 51 | } 52 | std::cout << std::endl; 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /code/lecture12/binary_counter.h: -------------------------------------------------------------------------------- 1 | #ifndef BINARY_COUNTER_H 2 | #define BINARY_COUNTER_H 3 | 4 | #include 5 | 6 | template 7 | class binary_counter 8 | { 9 | private: 10 | std::vector counter; 11 | Op op; 12 | T zero; 13 | 14 | public: 15 | binary_counter(const Op& op, const T& zero) : 16 | op(op), zero(zero) {} 17 | 18 | void reserve(size_t n) { counter.reserve(n); } 19 | 20 | void add(T x) { 21 | x = add_to_counter(counter.begin(), counter.end(), op, zero, x); 22 | if (x != zero) counter.push_back(x); 23 | } 24 | 25 | // returns: value of the counter 26 | T reduce() { 27 | return reduce_counter(counter.begin(), counter.end(), op, zero); 28 | } 29 | }; 30 | 31 | template 32 | // requires Op is BinaryOperation(T) 33 | // and Op is associative 34 | // and I is ForwardIterator and ValueType(I) == T 35 | T add_to_counter(I first, I last, Op op, const T& zero, T carry) { 36 | // precondition: carry != zero 37 | while (first != last) { 38 | if (*first == zero) { 39 | *first = carry; 40 | return zero; 41 | } 42 | carry = op(*first, carry); 43 | *first = zero; 44 | ++first; 45 | } 46 | return carry; 47 | } 48 | 49 | template 50 | // requires Op is BinaryOperation(T) 51 | // and Op is associative 52 | // and I is ForwardIterator and ValueType(I) == T 53 | T reduce_counter(I first, I last, Op op, const T& zero) { 54 | while (first != last && *first == zero) { 55 | ++first; 56 | } 57 | if (first == last) return zero; 58 | 59 | T result = *first; 60 | while (++first != last) { 61 | if (*first != zero) { 62 | result = op(*first, result); 63 | } 64 | } 65 | return result; 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /code/lecture12/list_algorithm.h: -------------------------------------------------------------------------------- 1 | 2 | #include "binary_counter.h" 3 | 4 | #include "merge_linked.h" 5 | 6 | template 7 | // requires I is Linked Iterator 8 | I reverse_linked(I first, I last, I tail) { 9 | while (first != last) { 10 | I next = first; 11 | ++next; 12 | set_successor(first, tail); 13 | tail = first; 14 | first = next; 15 | } 16 | return tail; 17 | } 18 | 19 | template 20 | // requires I is Linked Iterator 21 | I merge_linked_simple(I first1, I last1, I first2, I last2, Compare cmp) { 22 | I result = last1; 23 | while (first1 != last1 && first2 != last2) { 24 | I tmp = cmp(*first2, *first1) ? first2++ : first1++; 25 | set_successor(tmp, result); 26 | result = tmp; 27 | } 28 | return reverse_linked(result, last1, first1 == last1 ? first2 : first1); 29 | } 30 | 31 | template 32 | // I is Linked Iterator 33 | struct mergesort_linked_operation 34 | { 35 | typedef I argument_type; 36 | I nil; 37 | Compare cmp; 38 | mergesort_linked_operation(I nil, const Compare& cmp) : nil(nil), cmp(cmp) {} 39 | I operator()(I x, I y) { return merge_linked_simple(x, nil, y, nil, cmp); } 40 | }; 41 | 42 | template 43 | // I is Linked Iterator 44 | I mergesort_linked(I first, I last, Compare cmp) { 45 | mergesort_linked_operation op(last, cmp); 46 | binary_counter > counter(op, last); 47 | counter.reserve(16); 48 | while (first != last) { 49 | I tmp = first++; 50 | set_successor(tmp, last); 51 | counter.add(tmp); 52 | } 53 | return counter.reduce(); 54 | } 55 | 56 | template 57 | // requires I0 is Input Iterator 58 | // requires I1 is Singly Linked List Iterator 59 | I1 generate_list(I0 first, I0 last, I1 tail) { 60 | if (first == last) return tail; 61 | push_front(tail, *first++); 62 | I1 front = tail; 63 | while (first != last) { 64 | push_back(tail, *first++); 65 | ++tail; 66 | } 67 | return front; 68 | } 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /code/lecture12/merge_linked.h: -------------------------------------------------------------------------------- 1 | 2 | template 3 | // I is a linked forward iterator 4 | std::pair > 5 | merge_linked_non_empty(I first1, I last1, I first2, I last2, Compare cmp) { 6 | I head, tail; 7 | if (cmp(*first2, *first1)) { 8 | head = first2; 9 | tail = first2; 10 | ++first2; 11 | goto winner2; 12 | } else { 13 | head = first1; 14 | tail = first1; 15 | ++first1; 16 | // goto winner1; 17 | } 18 | winner1: 19 | if (first1 == last1) goto empty1; 20 | if (!cmp(*first2, *first1)) { 21 | tail = first1; 22 | ++first1; 23 | goto winner1; 24 | } else { 25 | set_successor(tail, first2); 26 | tail = first2; 27 | ++first2; 28 | // goto winner2; 29 | } 30 | winner2: 31 | if (first2 == last2) goto empty2; 32 | if (cmp(*first2, *first1)) { 33 | tail = first2; 34 | ++first2; 35 | goto winner2; 36 | } else { 37 | set_successor(tail, first1); 38 | tail = first1; 39 | ++first1; 40 | goto winner1; 41 | } 42 | empty1: 43 | set_successor(tail, first2); 44 | return std::make_pair(head, std::make_pair(first2, last2)); 45 | empty2: 46 | set_successor(tail, first1); 47 | return std::make_pair(head, std::make_pair(first1, last1)); 48 | } 49 | -------------------------------------------------------------------------------- /code/lecture12/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "algorithm.h" 5 | #include "list_pool.h" 6 | #include "list_algorithm.h" 7 | 8 | int main() { 9 | std::vector vec(100); 10 | random_iota(vec.begin(), vec.end()); 11 | list_pool pool; 12 | list_pool::iterator nil(pool); 13 | list_pool::iterator list = generate_list(vec.begin(), vec.end(), nil); 14 | print_range(list, nil); 15 | list = mergesort_linked(list, nil, std::less()); 16 | print_range(list, nil); 17 | } 18 | -------------------------------------------------------------------------------- /code/lecture14/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | #include 11 | 12 | template 13 | inline 14 | I successor(I x) { return ++x; } 15 | 16 | template 17 | N iota(I first, I last, N start = N(0), N step = N(1)) { 18 | typedef typename std::iterator_traits::value_type T; 19 | while (first != last) { 20 | *first = T(start); 21 | start += step; 22 | ++first; 23 | } 24 | return start; 25 | } 26 | 27 | template 28 | inline 29 | void iota(I first, I last) { 30 | typedef typename std::iterator_traits::value_type T; 31 | iota(first, last, T(0), T(1)); 32 | } 33 | 34 | template 35 | void random_iota(I first, I last) { 36 | iota(first, last); 37 | std::random_shuffle(first, last); 38 | } 39 | 40 | template 41 | void reverse_iota(I first, I last) { 42 | iota(first, last); 43 | std::reverse(first, last); 44 | } 45 | 46 | template 47 | void print_range(I first, I last) { 48 | while (first != last) { 49 | std::cout << *first << " "; 50 | ++first; 51 | } 52 | std::cout << std::endl; 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /code/lecture14/merge_inplace.h: -------------------------------------------------------------------------------- 1 | #ifndef MERGE_INPLACE_H 2 | #define MERGE_INPLACE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "search.h" 8 | 9 | // NOTE: this file will only compile with c++11 std::rotate 10 | 11 | /************************************************************ 12 | First published by Dudzin'sky and Dydek in 1981 IPL 12(1):5-8 13 | *************************************************************/ 14 | 15 | template 16 | // I is ForwardIterator 17 | // N is Integral 18 | // R is WeakStrictOrdering on the value type of I 19 | inline 20 | void merge_inplace_left_subproblem(I f0, N n0, 21 | I f1, N n1, 22 | I& f0_0, N& n0_0, 23 | I& f0_1, N& n0_1, 24 | I& f1_0, N& n1_0, 25 | I& f1_1, N& n1_1, 26 | R r) { 27 | // precondition std::distance(f0, f1) == n0 28 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 29 | // precondition n0 > 0 30 | // precondition n1 > 0 31 | f0_0 = f0; 32 | n0_0 = n0 >> 1; 33 | f0_1 = f0; 34 | std::advance(f0_1, n0_0); 35 | f1_1 = lower_bound_n(f1, n1, *f0_1, r); 36 | f1_0 = std::rotate(f0_1, f1, f1_1); 37 | n0_1 = std::distance(f0_1, f1_0); 38 | ++f1_0; 39 | n1_0 = (n0 - n0_0) - 1; 40 | n1_1 = n1 - n0_1; 41 | } 42 | 43 | template 44 | // I is ForwardIterator 45 | // N is Integral 46 | // R is WeakStrictOrdering on the value type of I 47 | inline 48 | void merge_inplace_right_subproblem(I f0, N n0, 49 | I f1, N n1, 50 | I& f0_0, N& n0_0, 51 | I& f0_1, N& n0_1, 52 | I& f1_0, N& n1_0, 53 | I& f1_1, N& n1_1, 54 | R r) { 55 | // precondition std::distance(f0, f1) == n0 56 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 57 | // precondition n0 > 0 58 | // precondition n1 > 0 59 | f0_0 = f0; 60 | n0_1 = n1 >> 1; 61 | f1_1 = f1; 62 | std::advance(f1_1, n0_1); 63 | f0_1 = upper_bound_n(f0, n0, *f1_1, r); 64 | ++f1_1; 65 | f1_0 = std::rotate(f0_1, f1, f1_1); 66 | n0_0 = std::distance(f0_0, f0_1); 67 | n1_0 = n0 - n0_0; 68 | n1_1 = (n1 - n0_1) - 1; 69 | } 70 | 71 | template 72 | // I is ForwardIterator 73 | // N is Integral 74 | // R is WeakStrictOrdering on the value type of I 75 | void merge_inplace_n(I f0, N n0, 76 | I f1, N n1, R r) { 77 | // precondition std::distance(f0, f1) == n0 78 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 79 | if (!n0 || !n1) return; 80 | I f0_0, f0_1, f1_0, f1_1; 81 | N n0_0, n0_1, n1_0, n1_1; 82 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 83 | f1, n1, 84 | f0_0, n0_0, 85 | f0_1, n0_1, 86 | f1_0, n1_0, 87 | f1_1, n1_1, 88 | r); 89 | else merge_inplace_right_subproblem(f0, n0, 90 | f1, n1, 91 | f0_0, n0_0, 92 | f0_1, n0_1, 93 | f1_0, n1_0, 94 | f1_1, n1_1, 95 | r); 96 | 97 | merge_inplace_n(f0_0, n0_0, f0_1, n0_1, r); 98 | merge_inplace_n(f1_0, n1_0, f1_1, n1_1, r); 99 | } 100 | 101 | template 102 | // I is ForwardIterator 103 | // N is Integral 104 | // R is WeakStrictOrdering on the value type of I 105 | I sort_inplace_n(I first, N n, R r) { 106 | if (!n) return first; 107 | N half = n >> 1; 108 | if (!half) return ++first; 109 | I middle = sort_inplace_n(first, half, r); 110 | I last = sort_inplace_n(middle, n - half, r); 111 | merge_inplace_n(first, half, middle, n - half, r); 112 | return last; 113 | } 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /code/lecture14/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "algorithm.h" 6 | #include "merge_inplace.h" 7 | 8 | 9 | int main() { 10 | std::vector vec(111); 11 | random_iota(vec.begin(), vec.end()); 12 | print_range(vec.begin(), vec.end()); 13 | sort_inplace_n(vec.begin(), vec.size(), std::less()); 14 | print_range(vec.begin(), vec.end()); 15 | } 16 | -------------------------------------------------------------------------------- /code/lecture15/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | #include 11 | 12 | template 13 | N iota(I first, I last, N start = N(0), N step = N(1)) { 14 | typedef typename std::iterator_traits::value_type T; 15 | while (first != last) { 16 | *first = T(start); 17 | start += step; 18 | ++first; 19 | } 20 | return start; 21 | } 22 | 23 | template 24 | inline 25 | void iota(I first, I last) { 26 | typedef typename std::iterator_traits::value_type T; 27 | iota(first, last, T(0), T(1)); 28 | } 29 | 30 | template 31 | void random_iota(I first, I last) { 32 | iota(first, last); 33 | std::random_shuffle(first, last); 34 | } 35 | 36 | template 37 | void reverse_iota(I first, I last) { 38 | iota(first, last); 39 | std::reverse(first, last); 40 | } 41 | 42 | template 43 | void print_range(I first, I last) { 44 | while (first != last) { 45 | std::cout << *first << " "; 46 | ++first; 47 | } 48 | std::cout << std::endl; 49 | } 50 | 51 | template 52 | // requires I is InputIterator 53 | inline 54 | I successor(I x) { return ++x; } 55 | 56 | template 57 | // requires I is BidirectionalIterator 58 | inline 59 | I predecessor(I x) { return ++x; } 60 | 61 | template 62 | // requires I is RandomAccessIterator 63 | // requires N is Integral 64 | inline 65 | I successor(I x, N n, std::random_access_iterator_tag) { 66 | return x + n; 67 | } 68 | 69 | template 70 | // requires I is InputIterator 71 | // requires N is Integral 72 | inline 73 | I successor(I x, N n, std::input_iterator_tag) { 74 | while (n != N(0)) { 75 | ++x; 76 | --n; 77 | } 78 | return x; 79 | } 80 | 81 | template 82 | // requires I is InputIterator 83 | // requires N is Integral 84 | inline 85 | I successor(I x, N n) { 86 | typedef typename std::iterator_traits::iterator_category C; 87 | return successor(x, n, C()); 88 | } 89 | 90 | template 91 | // requires I is BidirectionalIterator 92 | void hill(I first, I last) 93 | { 94 | I middle = successor(first, std::distance(first, last)/2); 95 | iota(first, middle); 96 | reverse_iota(middle, last); 97 | } 98 | 99 | template 100 | // requires I is BidirectionalIterator 101 | void valley(I first, I last) 102 | { 103 | I middle = successor(first, std::distance(first, last)/2); 104 | reverse_iota(first, middle); 105 | iota(middle, last); 106 | } 107 | 108 | template 109 | // requires I is BidirectionalIterator 110 | inline 111 | const char* function_name(void (*gen)(I, I)) { 112 | void (*generator[])(I, I) = 113 | { 114 | iota, 115 | hill, 116 | valley, 117 | reverse_iota, 118 | random_iota 119 | }; 120 | const char* names[] = 121 | { 122 | "iota", 123 | "hill", 124 | "valley", 125 | "reverse_iota", 126 | "random_iota" 127 | }; 128 | size_t number_of_generators = sizeof(generator)/sizeof(generator[0]); 129 | size_t index = std::find(generator, generator + number_of_generators, gen) - generator; 130 | if (index >= number_of_generators) return "unknown data generation "; 131 | return names[index]; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /code/lecture15/ideas.txt: -------------------------------------------------------------------------------- 1 | ideas 2 | ----- 3 | 1. use extra storage when the size is small 4 | a. use the extra storage as temp, copying on next merge? 5 | 2. use insertion sort when n < 16? 6 | a. use binary insertion sort? for n less than k 7 | 3. get rid of recursion (using binary counter) 8 | 4. write a very good merge (adaptive) 9 | 5. use rotate_with_buffer in merge_inplace_n left/right 10 | 11 | 12 | requirements 13 | ------------ 14 | 1. maintain stability 15 | 2. accepts ForwardIterator 16 | 3. use limited/reasonable extra memory 17 | 4. fast! (good=within 5% of std::stable_sort, 18 | great=faster than std::stable_sort, 19 | optimal=as fast as std::sort) 20 | 5. implement good insertion sort using ForwardIterator 21 | -------------------------------------------------------------------------------- /code/lecture15/merge.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "merge_inplace.h" 8 | 9 | template 10 | // requires I is ForwardIterator 11 | // requires R is StrictWeakOrdering 12 | // requires B is ForwardIterator 13 | void merge_with_buffer(I first, I middle, I last, R r, B buffer) { 14 | B buffer_last = std::copy(first, middle, buffer); 15 | std::merge(buffer, buffer_last, middle, last, first, r); 16 | } 17 | 18 | template 19 | // I is ForwardIterator 20 | // N is Integral 21 | // R is WeakStrictOrdering on the value type of I 22 | void merge_adaptive_n(I f0, N n0, 23 | I f1, N n1, R r, B buffer, N buffer_size) { 24 | // precondition std::distance(f0, f1) == n0 25 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 26 | if (!n0 || !n1) return; 27 | if (n0 <= buffer_size) { 28 | I last = f1; 29 | std::advance(last, n1); 30 | merge_with_buffer(f0, f1, last, r, buffer); 31 | return; 32 | } 33 | I f0_0, f0_1, f1_0, f1_1; 34 | N n0_0, n0_1, n1_0, n1_1; 35 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 36 | f1, n1, 37 | f0_0, n0_0, 38 | f0_1, n0_1, 39 | f1_0, n1_0, 40 | f1_1, n1_1, 41 | r); 42 | else merge_inplace_right_subproblem(f0, n0, 43 | f1, n1, 44 | f0_0, n0_0, 45 | f0_1, n0_1, 46 | f1_0, n1_0, 47 | f1_1, n1_1, 48 | r); 49 | 50 | merge_adaptive_n(f0_0, n0_0, f0_1, n0_1, r, buffer, buffer_size); 51 | merge_adaptive_n(f1_0, n1_0, f1_1, n1_1, r, buffer, buffer_size); 52 | } 53 | 54 | template 55 | // I is ForwardIterator 56 | // N is Integral 57 | // R is WeakStrictOrdering on the value type of I 58 | I sort_inplace_n_with_buffer(I first, N n, R r, B buffer) { 59 | if (!n) return first; 60 | N half = n >> 1; 61 | if (!half) return ++first; 62 | I middle = sort_inplace_n_with_buffer(first, half, r, buffer); 63 | I last = sort_inplace_n_with_buffer(middle, n - half, r, buffer); 64 | merge_with_buffer(first, middle, last, r, buffer); 65 | return last; 66 | } 67 | 68 | template 69 | // I is ForwardIterator 70 | inline 71 | void sort_inplace_with_buffer(I first, I last) { 72 | typedef typename std::iterator_traits::value_type T; 73 | typedef typename std::iterator_traits::difference_type N; 74 | N n = std::distance(first, last); 75 | std::vector buffer(n >> 1); 76 | sort_inplace_n_with_buffer(first, n, std::less(), buffer.begin()); 77 | } 78 | 79 | template 80 | // I is ForwardIterator 81 | // N is Integral 82 | // R is WeakStrictOrdering on the value type of I 83 | I sort_adaptive_n(I first, N n, R r, B buffer, N buffer_size) { 84 | if (!n) return first; 85 | N half = n >> 1; 86 | if (!half) return ++first; 87 | I middle = sort_adaptive_n(first, half, r, buffer, buffer_size); 88 | I last = sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 89 | merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 90 | return last; 91 | } 92 | 93 | template 94 | // I is ForwardIterator 95 | inline 96 | void sort_bert(I first, I last) { 97 | typedef typename std::iterator_traits::value_type T; 98 | typedef typename std::iterator_traits::difference_type N; 99 | N n = std::distance(first, last); 100 | std::vector buffer(n >> 3); 101 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 102 | } 103 | 104 | -------------------------------------------------------------------------------- /code/lecture15/merge_inplace.h: -------------------------------------------------------------------------------- 1 | #ifndef MERGE_INPLACE_H 2 | #define MERGE_INPLACE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "search.h" 8 | 9 | // NOTE: this file will only compile with c++11 std::rotate 10 | 11 | /************************************************************ 12 | First published by Dudzin'sky and Dydek in 1981 IPL 12(1):5-8 13 | *************************************************************/ 14 | 15 | template 16 | // I is ForwardIterator 17 | // N is Integral 18 | // R is WeakStrictOrdering on the value type of I 19 | inline 20 | void merge_inplace_left_subproblem(I f0, N n0, 21 | I f1, N n1, 22 | I& f0_0, N& n0_0, 23 | I& f0_1, N& n0_1, 24 | I& f1_0, N& n1_0, 25 | I& f1_1, N& n1_1, 26 | R r) { 27 | // precondition std::distance(f0, f1) == n0 28 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 29 | // precondition n0 > 0 30 | // precondition n1 > 0 31 | f0_0 = f0; 32 | n0_0 = n0 >> 1; 33 | f0_1 = f0; 34 | std::advance(f0_1, n0_0); 35 | f1_1 = lower_bound_n(f1, n1, *f0_1, r); 36 | f1_0 = std::rotate(f0_1, f1, f1_1); 37 | n0_1 = std::distance(f0_1, f1_0); 38 | ++f1_0; 39 | n1_0 = (n0 - n0_0) - 1; 40 | n1_1 = n1 - n0_1; 41 | } 42 | 43 | template 44 | // I is ForwardIterator 45 | // N is Integral 46 | // R is WeakStrictOrdering on the value type of I 47 | inline 48 | void merge_inplace_right_subproblem(I f0, N n0, 49 | I f1, N n1, 50 | I& f0_0, N& n0_0, 51 | I& f0_1, N& n0_1, 52 | I& f1_0, N& n1_0, 53 | I& f1_1, N& n1_1, 54 | R r) { 55 | // precondition std::distance(f0, f1) == n0 56 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 57 | // precondition n0 > 0 58 | // precondition n1 > 0 59 | f0_0 = f0; 60 | n0_1 = n1 >> 1; 61 | f1_1 = f1; 62 | std::advance(f1_1, n0_1); 63 | f0_1 = upper_bound_n(f0, n0, *f1_1, r); 64 | ++f1_1; 65 | f1_0 = std::rotate(f0_1, f1, f1_1); 66 | n0_0 = std::distance(f0_0, f0_1); 67 | n1_0 = n0 - n0_0; 68 | n1_1 = (n1 - n0_1) - 1; 69 | } 70 | 71 | template 72 | // I is ForwardIterator 73 | // N is Integral 74 | // R is WeakStrictOrdering on the value type of I 75 | void merge_inplace_n(I f0, N n0, 76 | I f1, N n1, R r) { 77 | // precondition std::distance(f0, f1) == n0 78 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 79 | if (!n0 || !n1) return; 80 | I f0_0, f0_1, f1_0, f1_1; 81 | N n0_0, n0_1, n1_0, n1_1; 82 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 83 | f1, n1, 84 | f0_0, n0_0, 85 | f0_1, n0_1, 86 | f1_0, n1_0, 87 | f1_1, n1_1, 88 | r); 89 | else merge_inplace_right_subproblem(f0, n0, 90 | f1, n1, 91 | f0_0, n0_0, 92 | f0_1, n0_1, 93 | f1_0, n1_0, 94 | f1_1, n1_1, 95 | r); 96 | 97 | merge_inplace_n(f0_0, n0_0, f0_1, n0_1, r); 98 | merge_inplace_n(f1_0, n1_0, f1_1, n1_1, r); 99 | } 100 | 101 | template 102 | // I is ForwardIterator 103 | // N is Integral 104 | // R is WeakStrictOrdering on the value type of I 105 | I sort_inplace_n(I first, N n, R r) { 106 | if (!n) return first; 107 | N half = n >> 1; 108 | if (!half) return ++first; 109 | I middle = sort_inplace_n(first, half, r); 110 | I last = sort_inplace_n(middle, n - half, r); 111 | merge_inplace_n(first, half, middle, n - half, r); 112 | return last; 113 | } 114 | 115 | template 116 | // I is ForwardIterator 117 | inline 118 | void sort_inplace(I first, I last) { 119 | typedef typename std::iterator_traits::value_type T; 120 | sort_inplace_n(first, std::distance(first, last), std::less()); 121 | } 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /code/lecture15/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "algorithm.h" 6 | #include "merge_inplace.h" 7 | 8 | 9 | int main() { 10 | std::vector vec(111); 11 | random_iota(vec.begin(), vec.end()); 12 | print_range(vec.begin(), vec.end()); 13 | sort_inplace_n(vec.begin(), vec.size(), std::less()); 14 | print_range(vec.begin(), vec.end()); 15 | } 16 | -------------------------------------------------------------------------------- /code/lecture15/test_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test_sort.h" 4 | #include "algorithm.h" 5 | 6 | const size_t min_size(8); 7 | const size_t max_size(2 * 1024 * 1024); 8 | 9 | int main() { 10 | test_sort(min_size, max_size, random_iota); 11 | } 12 | -------------------------------------------------------------------------------- /code/lecture15/test_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_SORT_H 2 | #define TEST_SORT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "timer.h" 10 | #include "type_description.h" 11 | #include "algorithm.h" 12 | 13 | #include "merge_inplace.h" 14 | #include "merge.h" 15 | 16 | template 17 | // requires T is TotallyOrdered 18 | double time_sort(T* first, T* last, void (*sort)(T*, T*), T* buffer, size_t count) { 19 | timer t; 20 | t.start(); 21 | size_t i = count; 22 | while (i--) { 23 | std::copy(buffer, buffer + std::distance(first, last), first); 24 | sort(first, last); 25 | if (!std::is_sorted(first, last)) { 26 | std::cerr << "*** SORT FAILED! ***" << std::endl; 27 | return 0; 28 | } 29 | } 30 | return t.stop(); 31 | } 32 | 33 | template 34 | // requires T is TotallyOrdered 35 | // requires G is a Generator 36 | void test_sort(size_t min_size, size_t max_size, G gen) { 37 | 38 | time_t now = time(0); 39 | 40 | std::cout << "Sorting " << type_description(T(0)) 41 | << " from " << min_size << " up to " << max_size 42 | << " elements" << " generated with " << function_name(gen) 43 | <<" at: " << asctime(localtime(&now)); 44 | 45 | 46 | void (*f_pointers[])(T*, T*) = 47 | { 48 | std::stable_sort 49 | ,sort_inplace 50 | ,sort_inplace_with_buffer 51 | ,sort_bert 52 | }; 53 | 54 | size_t number_of_sorts = sizeof(f_pointers) / sizeof(f_pointers[0]); 55 | 56 | std::cout << " size\t" 57 | << "stable\t" 58 | << "inplace\t" 59 | << "sort_merge\t" 60 | << "sort_bert\t" 61 | << std::endl; 62 | 63 | for (size_t array_size(min_size); array_size <= max_size; array_size *= 2) { 64 | const size_t n = max_size / array_size; 65 | std::vector vec(array_size); 66 | gen(&*vec.begin(), (&*vec.begin()) + vec.size()); 67 | std::vector tmp(vec.begin(), vec.end()); 68 | std::cout << std::setw(12) << array_size << "\t"; 69 | for (size_t i = 0; i < number_of_sorts; ++i) { 70 | size_t bound = max_size; 71 | double time = time_sort(&*tmp.begin(), (&*tmp.begin()) + tmp.size(), f_pointers[i], &*vec.begin(), n); 72 | time /= double(array_size * n); 73 | std::cout << std::setw(6) << std::fixed << std::setprecision(0) << time << "\t"; 74 | } 75 | std::cout << std::endl; 76 | } 77 | } 78 | 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /code/lecture15/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include 5 | 6 | class timer { 7 | private: 8 | clock_t start_time; 9 | public: 10 | typedef double result_type; 11 | 12 | void start() { 13 | start_time = clock(); 14 | } 15 | 16 | result_type stop() { 17 | return 1000000000. * ((clock() - start_time) / double(CLOCKS_PER_SEC)); 18 | } 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /code/lecture15/type_description.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPE_DESCRIPTION_H 2 | #define TYPE_DESCRIPTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::string type_description(double) { return std::string("double"); } 10 | std::string type_description(long double) { return std::string("long double"); } 11 | std::string type_description(float) { return std::string("float"); } 12 | std::string type_description(uint8_t) { return std::string("uint8_t"); } 13 | std::string type_description(uint16_t) { return std::string("uint16_t"); } 14 | std::string type_description(uint32_t) { return std::string("uint32_t"); } 15 | std::string type_description(uint64_t) { return std::string("uint64_t"); } 16 | std::string type_description(int8_t) { return std::string("int8_t"); } 17 | std::string type_description(int16_t) { return std::string("int16_t"); } 18 | std::string type_description(int32_t) { return std::string("int32_t"); } 19 | std::string type_description(int64_t) { return std::string("int64_t"); } 20 | 21 | template 22 | std::string type_description(const std::vector&) { return std::string("vector<") + type_description(T()) + std::string(">"); } 23 | 24 | template 25 | std::string type_description(const std::deque&) { return std::string("deque<") + type_description(T()) + std::string(">"); } 26 | 27 | template 28 | std::string type_description(const std::list&) { return std::string("list<") + type_description(T()) + std::string(">"); } 29 | #endif 30 | -------------------------------------------------------------------------------- /code/lecture16/concepts A9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjernst/stepanov-components-course/375bcb790ee40020ff639e0b8ddec0cfe58ba27a/code/lecture16/concepts A9.pdf -------------------------------------------------------------------------------- /code/lecture16/concepts A9.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjernst/stepanov-components-course/375bcb790ee40020ff639e0b8ddec0cfe58ba27a/code/lecture16/concepts A9.pptx -------------------------------------------------------------------------------- /code/lecture16/concepts design for stl.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjernst/stepanov-components-course/375bcb790ee40020ff639e0b8ddec0cfe58ba27a/code/lecture16/concepts design for stl.pdf -------------------------------------------------------------------------------- /code/lecture16/concepts lite.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rjernst/stepanov-components-course/375bcb790ee40020ff639e0b8ddec0cfe58ba27a/code/lecture16/concepts lite.pdf -------------------------------------------------------------------------------- /code/lecture17/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | #include 11 | 12 | template 13 | N iota(I first, I last, N start = N(0), N step = N(1)) { 14 | typedef typename std::iterator_traits::value_type T; 15 | while (first != last) { 16 | *first = T(start); 17 | start += step; 18 | ++first; 19 | } 20 | return start; 21 | } 22 | 23 | template 24 | inline 25 | void iota(I first, I last) { 26 | typedef typename std::iterator_traits::value_type T; 27 | iota(first, last, T(0), T(1)); 28 | } 29 | 30 | template 31 | void random_iota(I first, I last) { 32 | iota(first, last); 33 | std::random_shuffle(first, last); 34 | } 35 | 36 | template 37 | void reverse_iota(I first, I last) { 38 | iota(first, last); 39 | std::reverse(first, last); 40 | } 41 | 42 | template 43 | void print_range(I first, I last) { 44 | while (first != last) { 45 | std::cout << *first << " "; 46 | ++first; 47 | } 48 | std::cout << std::endl; 49 | } 50 | 51 | template 52 | // requires I is InputIterator 53 | inline 54 | I successor(I x) { return ++x; } 55 | 56 | template 57 | // requires I is BidirectionalIterator 58 | inline 59 | I predecessor(I x) { return ++x; } 60 | 61 | template 62 | // requires I is RandomAccessIterator 63 | // requires N is Integral 64 | inline 65 | I successor(I x, N n, std::random_access_iterator_tag) { 66 | return x + n; 67 | } 68 | 69 | template 70 | // requires I is InputIterator 71 | // requires N is Integral 72 | inline 73 | I successor(I x, N n, std::input_iterator_tag) { 74 | while (n != N(0)) { 75 | ++x; 76 | --n; 77 | } 78 | return x; 79 | } 80 | 81 | template 82 | // requires I is InputIterator 83 | // requires N is Integral 84 | inline 85 | I successor(I x, N n) { 86 | typedef typename std::iterator_traits::iterator_category C; 87 | return successor(x, n, C()); 88 | } 89 | 90 | template 91 | // requires I is BidirectionalIterator 92 | void hill(I first, I last) 93 | { 94 | I middle = successor(first, std::distance(first, last)/2); 95 | iota(first, middle); 96 | reverse_iota(middle, last); 97 | } 98 | 99 | template 100 | // requires I is BidirectionalIterator 101 | void valley(I first, I last) 102 | { 103 | I middle = successor(first, std::distance(first, last)/2); 104 | reverse_iota(first, middle); 105 | iota(middle, last); 106 | } 107 | 108 | template 109 | // requires I is BidirectionalIterator 110 | inline 111 | const char* function_name(void (*gen)(I, I)) { 112 | void (*generator[])(I, I) = 113 | { 114 | iota, 115 | hill, 116 | valley, 117 | reverse_iota, 118 | random_iota 119 | }; 120 | const char* names[] = 121 | { 122 | "iota", 123 | "hill", 124 | "valley", 125 | "reverse_iota", 126 | "random_iota" 127 | }; 128 | size_t number_of_generators = sizeof(generator)/sizeof(generator[0]); 129 | size_t index = std::find(generator, generator + number_of_generators, gen) - generator; 130 | if (index >= number_of_generators) return "unknown data generation "; 131 | return names[index]; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /code/lecture17/ideas.txt: -------------------------------------------------------------------------------- 1 | ideas 2 | ----- 3 | 1. use extra storage when the size is small 4 | a. use the extra storage as temp, copying on next merge? 5 | 2. use insertion sort when n < 16? 6 | a. use binary insertion sort? for n less than k 7 | b. optimize insertion sort for 1 element 8 | 3. get rid of recursion (using binary counter) 9 | 4. write a very good merge (adaptive) 10 | 5. use rotate_with_buffer in merge_inplace_n left/right 11 | 12 | 13 | requirements 14 | ------------ 15 | 1. maintain stability 16 | 2. accepts ForwardIterator 17 | 3. use limited/reasonable extra memory 18 | 4. fast! (good=within 5% of std::stable_sort, 19 | great=faster than std::stable_sort, 20 | optimal=as fast as std::sort) 21 | 5. implement good insertion sort using ForwardIterator 22 | -------------------------------------------------------------------------------- /code/lecture17/insertion_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef INSERTION_SORT_H 2 | #define INSERTION_SORT_H 3 | 4 | #include 5 | #include 6 | #include "algorithm.h" 7 | #include "search.h" 8 | 9 | template 10 | // I is ForwardIterator 11 | void rotate_right_by_one(I first, I butlast, I last, std::forward_iterator_tag) { 12 | // 1 2 3 4 13 | // 4 1 2 3 14 | /* 15 | typedef typename std::iterator_traits::value_type T; 16 | I current = first; 17 | if (current == last) return; 18 | I prev = current; 19 | ++current; 20 | while (current != last) { 21 | *current = prev_value; 22 | ++current; 23 | prev_value = *current; 24 | } 25 | */ 26 | std::rotate(first, butlast, last); 27 | } 28 | 29 | template 30 | // I is BidirectionalIterator 31 | void rotate_right_by_one(I first, I butlast, I last, std::bidirectional_iterator_tag) { 32 | typedef typename std::iterator_traits::value_type T; 33 | T x = *butlast; 34 | std::copy_backward(first, butlast, last); 35 | *first = x; 36 | } 37 | 38 | template 39 | inline 40 | void rotate_right_by_one(I first, I butlast, I last) { 41 | rotate_right_by_one(first, butlast, last, typename std::iterator_traits::iterator_category()); 42 | } 43 | 44 | 45 | template 46 | // I is ForwardIterator 47 | // N is Integral 48 | // R is WeakStrictOrdering on the value type of I 49 | I binary_insertion_sort_n(I first, N n, R r) { 50 | I last = first; 51 | N i(0); 52 | while (i < n) { 53 | I insertion_point = upper_bound_n(first, i, *last, r); 54 | rotate_right_by_one(insertion_point, last, successor(last)); 55 | ++last; 56 | ++i; 57 | } 58 | return last; 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /code/lecture17/merge.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "merge_inplace.h" 8 | #include "insertion_sort.h" 9 | 10 | template 11 | // requires I is ForwardIterator 12 | // requires R is StrictWeakOrdering 13 | // requires B is ForwardIterator 14 | void merge_with_buffer(I first, I middle, I last, R r, B buffer) { 15 | B buffer_last = std::copy(first, middle, buffer); 16 | std::merge(buffer, buffer_last, middle, last, first, r); 17 | } 18 | 19 | template 20 | // I is ForwardIterator 21 | // N is Integral 22 | // R is WeakStrictOrdering on the value type of I 23 | void merge_adaptive_n(I f0, N n0, 24 | I f1, N n1, R r, B buffer, N buffer_size) { 25 | // precondition std::distance(f0, f1) == n0 26 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 27 | if (!n0 || !n1) return; 28 | if (n0 <= buffer_size) { 29 | I last = f1; 30 | std::advance(last, n1); 31 | merge_with_buffer(f0, f1, last, r, buffer); 32 | return; 33 | } 34 | I f0_0, f0_1, f1_0, f1_1; 35 | N n0_0, n0_1, n1_0, n1_1; 36 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 37 | f1, n1, 38 | f0_0, n0_0, 39 | f0_1, n0_1, 40 | f1_0, n1_0, 41 | f1_1, n1_1, 42 | r); 43 | else merge_inplace_right_subproblem(f0, n0, 44 | f1, n1, 45 | f0_0, n0_0, 46 | f0_1, n0_1, 47 | f1_0, n1_0, 48 | f1_1, n1_1, 49 | r); 50 | 51 | merge_adaptive_n(f0_0, n0_0, f0_1, n0_1, r, buffer, buffer_size); 52 | merge_adaptive_n(f1_0, n1_0, f1_1, n1_1, r, buffer, buffer_size); 53 | } 54 | 55 | template 56 | // I is ForwardIterator 57 | // N is Integral 58 | // R is WeakStrictOrdering on the value type of I 59 | I sort_inplace_n_with_buffer(I first, N n, R r, B buffer) { 60 | if (!n) return first; 61 | N half = n >> 1; 62 | if (!half) return ++first; 63 | I middle = sort_inplace_n_with_buffer(first, half, r, buffer); 64 | I last = sort_inplace_n_with_buffer(middle, n - half, r, buffer); 65 | merge_with_buffer(first, middle, last, r, buffer); 66 | return last; 67 | } 68 | 69 | template 70 | // I is ForwardIterator 71 | inline 72 | void sort_inplace_with_buffer(I first, I last) { 73 | typedef typename std::iterator_traits::value_type T; 74 | typedef typename std::iterator_traits::difference_type N; 75 | N n = std::distance(first, last); 76 | std::vector buffer(n >> 1); 77 | sort_inplace_n_with_buffer(first, n, std::less(), buffer.begin()); 78 | } 79 | 80 | const size_t INSERTION_SORT_CUTOFF = 16; 81 | 82 | template 83 | // I is ForwardIterator 84 | // N is Integral 85 | // R is WeakStrictOrdering on the value type of I 86 | I sort_adaptive_n(I first, N n, R r, B buffer, N buffer_size) { 87 | if (!n) return first; 88 | if (n < INSERTION_SORT_CUTOFF) return binary_insertion_sort_n(first, n, r); 89 | N half = n >> 1; 90 | if (!half) return ++first; 91 | I middle = sort_adaptive_n(first, half, r, buffer, buffer_size); 92 | I last = sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 93 | merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 94 | return last; 95 | } 96 | 97 | template 98 | // I is ForwardIterator 99 | inline 100 | void sort_1_8th(I first, I last) { 101 | typedef typename std::iterator_traits::value_type T; 102 | typedef typename std::iterator_traits::difference_type N; 103 | N n = std::distance(first, last); 104 | std::vector buffer(n >> 3); 105 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 106 | } 107 | 108 | template 109 | // I is ForwardIterator 110 | inline 111 | void sort_1_64th(I first, I last) { 112 | typedef typename std::iterator_traits::value_type T; 113 | typedef typename std::iterator_traits::difference_type N; 114 | N n = std::distance(first, last); 115 | std::vector buffer(n >> 6); 116 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /code/lecture17/merge_inplace.h: -------------------------------------------------------------------------------- 1 | #ifndef MERGE_INPLACE_H 2 | #define MERGE_INPLACE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "search.h" 8 | 9 | // NOTE: this file will only compile with c++11 std::rotate 10 | 11 | /************************************************************ 12 | First published by Dudzin'sky and Dydek in 1981 IPL 12(1):5-8 13 | *************************************************************/ 14 | 15 | template 16 | // I is ForwardIterator 17 | // N is Integral 18 | // R is WeakStrictOrdering on the value type of I 19 | inline 20 | void merge_inplace_left_subproblem(I f0, N n0, 21 | I f1, N n1, 22 | I& f0_0, N& n0_0, 23 | I& f0_1, N& n0_1, 24 | I& f1_0, N& n1_0, 25 | I& f1_1, N& n1_1, 26 | R r) { 27 | // precondition std::distance(f0, f1) == n0 28 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 29 | // precondition n0 > 0 30 | // precondition n1 > 0 31 | f0_0 = f0; 32 | n0_0 = n0 >> 1; 33 | f0_1 = f0; 34 | std::advance(f0_1, n0_0); 35 | f1_1 = lower_bound_n(f1, n1, *f0_1, r); 36 | f1_0 = std::rotate(f0_1, f1, f1_1); 37 | n0_1 = std::distance(f0_1, f1_0); 38 | ++f1_0; 39 | n1_0 = (n0 - n0_0) - 1; 40 | n1_1 = n1 - n0_1; 41 | } 42 | 43 | template 44 | // I is ForwardIterator 45 | // N is Integral 46 | // R is WeakStrictOrdering on the value type of I 47 | inline 48 | void merge_inplace_right_subproblem(I f0, N n0, 49 | I f1, N n1, 50 | I& f0_0, N& n0_0, 51 | I& f0_1, N& n0_1, 52 | I& f1_0, N& n1_0, 53 | I& f1_1, N& n1_1, 54 | R r) { 55 | // precondition std::distance(f0, f1) == n0 56 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 57 | // precondition n0 > 0 58 | // precondition n1 > 0 59 | f0_0 = f0; 60 | n0_1 = n1 >> 1; 61 | f1_1 = f1; 62 | std::advance(f1_1, n0_1); 63 | f0_1 = upper_bound_n(f0, n0, *f1_1, r); 64 | ++f1_1; 65 | f1_0 = std::rotate(f0_1, f1, f1_1); 66 | n0_0 = std::distance(f0_0, f0_1); 67 | n1_0 = n0 - n0_0; 68 | n1_1 = (n1 - n0_1) - 1; 69 | } 70 | 71 | template 72 | // I is ForwardIterator 73 | // N is Integral 74 | // R is WeakStrictOrdering on the value type of I 75 | void merge_inplace_n(I f0, N n0, 76 | I f1, N n1, R r) { 77 | // precondition std::distance(f0, f1) == n0 78 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 79 | if (!n0 || !n1) return; 80 | I f0_0, f0_1, f1_0, f1_1; 81 | N n0_0, n0_1, n1_0, n1_1; 82 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 83 | f1, n1, 84 | f0_0, n0_0, 85 | f0_1, n0_1, 86 | f1_0, n1_0, 87 | f1_1, n1_1, 88 | r); 89 | else merge_inplace_right_subproblem(f0, n0, 90 | f1, n1, 91 | f0_0, n0_0, 92 | f0_1, n0_1, 93 | f1_0, n1_0, 94 | f1_1, n1_1, 95 | r); 96 | 97 | merge_inplace_n(f0_0, n0_0, f0_1, n0_1, r); 98 | merge_inplace_n(f1_0, n1_0, f1_1, n1_1, r); 99 | } 100 | 101 | template 102 | // I is ForwardIterator 103 | // N is Integral 104 | // R is WeakStrictOrdering on the value type of I 105 | I sort_inplace_n(I first, N n, R r) { 106 | if (!n) return first; 107 | N half = n >> 1; 108 | if (!half) return ++first; 109 | I middle = sort_inplace_n(first, half, r); 110 | I last = sort_inplace_n(middle, n - half, r); 111 | merge_inplace_n(first, half, middle, n - half, r); 112 | return last; 113 | } 114 | 115 | template 116 | // I is ForwardIterator 117 | inline 118 | void sort_inplace(I first, I last) { 119 | typedef typename std::iterator_traits::value_type T; 120 | sort_inplace_n(first, std::distance(first, last), std::less()); 121 | } 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /code/lecture17/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "algorithm.h" 6 | #include "merge_inplace.h" 7 | 8 | 9 | int main() { 10 | std::vector vec(111); 11 | random_iota(vec.begin(), vec.end()); 12 | print_range(vec.begin(), vec.end()); 13 | sort_inplace_n(vec.begin(), vec.size(), std::less()); 14 | print_range(vec.begin(), vec.end()); 15 | } 16 | -------------------------------------------------------------------------------- /code/lecture17/test_insertion_sort.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "insertion_sort.h" 4 | #include "algorithm.h" 5 | 6 | 7 | int main() { 8 | int nums[] = {5, 3, 10, 1, 2}; 9 | print_range(nums, nums + 5); 10 | binary_insertion_sort_n(nums, 5, std::less()); 11 | print_range(nums, nums + 5); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /code/lecture17/test_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test_sort.h" 4 | #include "algorithm.h" 5 | 6 | const size_t min_size(8); 7 | const size_t max_size(2 * 1024 * 1024); 8 | 9 | int main() { 10 | test_sort(min_size, max_size, random_iota); 11 | } 12 | -------------------------------------------------------------------------------- /code/lecture17/test_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_SORT_H 2 | #define TEST_SORT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "timer.h" 10 | #include "type_description.h" 11 | #include "algorithm.h" 12 | 13 | #include "merge_inplace.h" 14 | #include "merge.h" 15 | 16 | template 17 | // requires T is TotallyOrdered 18 | double time_sort(T* first, T* last, void (*sort)(T*, T*), T* buffer, size_t count) { 19 | timer t; 20 | t.start(); 21 | size_t i = count; 22 | while (i--) { 23 | std::copy(buffer, buffer + std::distance(first, last), first); 24 | sort(first, last); 25 | if (!std::is_sorted(first, last)) { 26 | std::cerr << "*** SORT FAILED! ***" << std::endl; 27 | return 0; 28 | } 29 | } 30 | return t.stop(); 31 | } 32 | 33 | template 34 | // requires T is TotallyOrdered 35 | // requires G is a Generator 36 | void test_sort(size_t min_size, size_t max_size, G gen) { 37 | 38 | time_t now = time(0); 39 | 40 | std::cout << "Sorting " << type_description(T(0)) 41 | << " from " << min_size << " up to " << max_size 42 | << " elements" << " generated with " << function_name(gen) 43 | <<" at: " << asctime(localtime(&now)); 44 | 45 | 46 | void (*f_pointers[])(T*, T*) = 47 | { 48 | std::stable_sort 49 | ,sort_inplace 50 | ,sort_inplace_with_buffer 51 | ,sort_1_8th 52 | ,sort_1_64th 53 | }; 54 | 55 | size_t number_of_sorts = sizeof(f_pointers) / sizeof(f_pointers[0]); 56 | 57 | std::cout << " size\t" 58 | << " stable\t" 59 | << "inplace\t" 60 | << " merge\t" 61 | << " 1_8th\t" 62 | << " 1_64th\t" 63 | << std::endl; 64 | 65 | for (size_t array_size(min_size); array_size <= max_size; array_size *= 2) { 66 | const size_t n = max_size / array_size; 67 | std::vector vec(array_size); 68 | gen(&*vec.begin(), (&*vec.begin()) + vec.size()); 69 | std::vector tmp(vec.begin(), vec.end()); 70 | std::cout << std::setw(12) << array_size << "\t"; 71 | for (size_t i = 0; i < number_of_sorts; ++i) { 72 | size_t bound = max_size; 73 | double time = time_sort(&*tmp.begin(), (&*tmp.begin()) + tmp.size(), f_pointers[i], &*vec.begin(), n); 74 | time /= double(array_size * n); 75 | std::cout << std::setw(6) << std::fixed << std::setprecision(0) << time << "\t"; 76 | } 77 | std::cout << std::endl; 78 | } 79 | } 80 | 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /code/lecture17/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include 5 | 6 | class timer { 7 | private: 8 | clock_t start_time; 9 | public: 10 | typedef double result_type; 11 | 12 | void start() { 13 | start_time = clock(); 14 | } 15 | 16 | result_type stop() { 17 | return 1000000000. * ((clock() - start_time) / double(CLOCKS_PER_SEC)); 18 | } 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /code/lecture17/type_description.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPE_DESCRIPTION_H 2 | #define TYPE_DESCRIPTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::string type_description(double) { return std::string("double"); } 10 | std::string type_description(long double) { return std::string("long double"); } 11 | std::string type_description(float) { return std::string("float"); } 12 | std::string type_description(uint8_t) { return std::string("uint8_t"); } 13 | std::string type_description(uint16_t) { return std::string("uint16_t"); } 14 | std::string type_description(uint32_t) { return std::string("uint32_t"); } 15 | std::string type_description(uint64_t) { return std::string("uint64_t"); } 16 | std::string type_description(int8_t) { return std::string("int8_t"); } 17 | std::string type_description(int16_t) { return std::string("int16_t"); } 18 | std::string type_description(int32_t) { return std::string("int32_t"); } 19 | std::string type_description(int64_t) { return std::string("int64_t"); } 20 | 21 | template 22 | std::string type_description(const std::vector&) { return std::string("vector<") + type_description(T()) + std::string(">"); } 23 | 24 | template 25 | std::string type_description(const std::deque&) { return std::string("deque<") + type_description(T()) + std::string(">"); } 26 | 27 | template 28 | std::string type_description(const std::list&) { return std::string("list<") + type_description(T()) + std::string(">"); } 29 | #endif 30 | -------------------------------------------------------------------------------- /code/lecture18/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | #include 11 | 12 | template 13 | N iota(I first, I last, N start = N(0), N step = N(1)) { 14 | typedef typename std::iterator_traits::value_type T; 15 | while (first != last) { 16 | *first = T(start); 17 | start += step; 18 | ++first; 19 | } 20 | return start; 21 | } 22 | 23 | template 24 | inline 25 | void iota(I first, I last) { 26 | typedef typename std::iterator_traits::value_type T; 27 | iota(first, last, T(0), T(1)); 28 | } 29 | 30 | template 31 | void random_iota(I first, I last) { 32 | iota(first, last); 33 | std::random_shuffle(first, last); 34 | } 35 | 36 | template 37 | void reverse_iota(I first, I last) { 38 | iota(first, last); 39 | std::reverse(first, last); 40 | } 41 | 42 | template 43 | void print_range(I first, I last) { 44 | while (first != last) { 45 | std::cout << *first << " "; 46 | ++first; 47 | } 48 | std::cout << std::endl; 49 | } 50 | 51 | template 52 | // requires I is InputIterator 53 | inline 54 | I successor(I x) { return ++x; } 55 | 56 | template 57 | // requires I is BidirectionalIterator 58 | inline 59 | I predecessor(I x) { return ++x; } 60 | 61 | template 62 | // requires I is RandomAccessIterator 63 | // requires N is Integral 64 | inline 65 | I successor(I x, N n, std::random_access_iterator_tag) { 66 | return x + n; 67 | } 68 | 69 | template 70 | // requires I is InputIterator 71 | // requires N is Integral 72 | inline 73 | I successor(I x, N n, std::input_iterator_tag) { 74 | while (n != N(0)) { 75 | ++x; 76 | --n; 77 | } 78 | return x; 79 | } 80 | 81 | template 82 | // requires I is InputIterator 83 | // requires N is Integral 84 | inline 85 | I successor(I x, N n) { 86 | typedef typename std::iterator_traits::iterator_category C; 87 | return successor(x, n, C()); 88 | } 89 | 90 | template 91 | // requires I is BidirectionalIterator 92 | void hill(I first, I last) 93 | { 94 | I middle = successor(first, std::distance(first, last)/2); 95 | iota(first, middle); 96 | reverse_iota(middle, last); 97 | } 98 | 99 | template 100 | // requires I is BidirectionalIterator 101 | void valley(I first, I last) 102 | { 103 | I middle = successor(first, std::distance(first, last)/2); 104 | reverse_iota(first, middle); 105 | iota(middle, last); 106 | } 107 | 108 | template 109 | // requires I is BidirectionalIterator 110 | inline 111 | const char* function_name(void (*gen)(I, I)) { 112 | void (*generator[])(I, I) = 113 | { 114 | iota, 115 | hill, 116 | valley, 117 | reverse_iota, 118 | random_iota 119 | }; 120 | const char* names[] = 121 | { 122 | "iota", 123 | "hill", 124 | "valley", 125 | "reverse_iota", 126 | "random_iota" 127 | }; 128 | size_t number_of_generators = sizeof(generator)/sizeof(generator[0]); 129 | size_t index = std::find(generator, generator + number_of_generators, gen) - generator; 130 | if (index >= number_of_generators) return "unknown data generation "; 131 | return names[index]; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /code/lecture18/ideas.txt: -------------------------------------------------------------------------------- 1 | ideas 2 | ----- 3 | 1. use extra storage when the size is small 4 | a. use the extra storage as temp, copying on next merge? 5 | 2. use insertion sort when n < 16? 6 | a. use binary insertion sort? for n less than k 7 | b. optimize insertion sort for 1 element 8 | 3. get rid of recursion (using binary counter) 9 | 4. write a very good merge (adaptive) 10 | 5. use rotate_with_buffer in merge_inplace_n left/right 11 | 12 | 13 | requirements 14 | ------------ 15 | 1. maintain stability 16 | 2. accepts ForwardIterator 17 | 3. use limited/reasonable extra memory 18 | 4. fast! (good=within 5% of std::stable_sort, 19 | great=faster than std::stable_sort, 20 | optimal=as fast as std::sort) 21 | 5. implement good insertion sort using ForwardIterator 22 | -------------------------------------------------------------------------------- /code/lecture18/insertion_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef INSERTION_SORT_H 2 | #define INSERTION_SORT_H 3 | 4 | #include 5 | #include 6 | #include "algorithm.h" 7 | #include "search.h" 8 | 9 | template 10 | // I is ForwardIterator 11 | void rotate_right_by_one(I first, I last, std::forward_iterator_tag) { 12 | if (first == last) return; 13 | I current = first; 14 | while (++current != last) std::swap(first, current); 15 | } 16 | 17 | template 18 | // I is BidirectionalIterator 19 | void rotate_right_by_one(I first, I last, std::bidirectional_iterator_tag) { 20 | typedef typename std::iterator_traits::value_type T; 21 | I butlast = last; 22 | --butlast; 23 | T x = *butlast; 24 | std::copy_backward(first, butlast, last); 25 | *first = x; 26 | } 27 | 28 | template 29 | inline 30 | void rotate_right_by_one(I first, I last) { 31 | rotate_right_by_one(first, last, typename std::iterator_traits::iterator_category()); 32 | } 33 | 34 | template 35 | // I is ForwardIterator 36 | // N is Integral 37 | // R is WeakStrictOrdering on the value type of I 38 | I binary_insert_n(I first, N n, I current, R r) { 39 | // precondition: is_sorted(first, current, r) && current is a valid iterator 40 | // && std::distance(first, current) == n 41 | I insertion_point = upper_bound_n(first, n, *current, r); 42 | rotate_right_by_one(insertion_point, ++current); 43 | return insertion_point; 44 | } 45 | 46 | template 47 | // I is BidirectionalIterator 48 | // R is WeakStrictOrdering on the value type of I 49 | I linear_insert(I first, I current, R r) { 50 | // precondition: is_sorted(first, current, r) && current is a valid iterator 51 | typedef typename std::iterator_traits::value_type T; 52 | if (first == current) return first; 53 | T value = *current; 54 | --current; 55 | while (r(value, *current)) { 56 | // invariant: 57 | *successor(current) = *current; 58 | if (first == current) { 59 | *current = value; 60 | return current; 61 | } 62 | --current; 63 | } 64 | ++current; 65 | *current = value; 66 | return current; 67 | } 68 | 69 | template 70 | // I is ForwardIterator 71 | // N is Integral 72 | // R is WeakStrictOrdering on the value type of I 73 | I binary_insertion_sort_n(I first, N n, R r) { 74 | if (n == N(0)) return first; 75 | I current = first; 76 | ++current; 77 | N i(1); 78 | while (i < n) { 79 | // invariant: is_sorted_n(first, i, r) && std::distance(first, current) == i 80 | binary_insert_n(first, i++, current++, r); 81 | } 82 | return current; 83 | } 84 | 85 | template 86 | // I is BidirectionalIterator 87 | // N is Integral 88 | // R is WeakStrictOrdering on the value type of I 89 | I linear_insertion_sort_n(I first, N n, R r) { 90 | if (n == N(0)) return first; 91 | I current = first; 92 | ++current; 93 | N i(1); 94 | while (i < n) { 95 | // invariant: is_sorted_n(first, i, r) && std::distance(first, current) == i 96 | linear_insert(first, current++, r); 97 | ++i; 98 | } 99 | return current; 100 | } 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /code/lecture18/merge.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "merge_inplace.h" 8 | #include "insertion_sort.h" 9 | 10 | template 11 | // requires I is ForwardIterator 12 | // requires R is StrictWeakOrdering 13 | // requires B is ForwardIterator 14 | void merge_with_buffer(I first, I middle, I last, R r, B buffer) { 15 | B buffer_last = std::copy(first, middle, buffer); 16 | std::merge(buffer, buffer_last, middle, last, first, r); 17 | } 18 | 19 | template 20 | // I is ForwardIterator 21 | // N is Integral 22 | // R is WeakStrictOrdering on the value type of I 23 | void merge_adaptive_n(I f0, N n0, 24 | I f1, N n1, R r, B buffer, N buffer_size) { 25 | // precondition std::distance(f0, f1) == n0 26 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 27 | if (!n0 || !n1) return; 28 | if (n0 <= buffer_size) { 29 | I last = f1; 30 | std::advance(last, n1); 31 | merge_with_buffer(f0, f1, last, r, buffer); 32 | return; 33 | } 34 | I f0_0, f0_1, f1_0, f1_1; 35 | N n0_0, n0_1, n1_0, n1_1; 36 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 37 | f1, n1, 38 | f0_0, n0_0, 39 | f0_1, n0_1, 40 | f1_0, n1_0, 41 | f1_1, n1_1, 42 | r); 43 | else merge_inplace_right_subproblem(f0, n0, 44 | f1, n1, 45 | f0_0, n0_0, 46 | f0_1, n0_1, 47 | f1_0, n1_0, 48 | f1_1, n1_1, 49 | r); 50 | 51 | merge_adaptive_n(f0_0, n0_0, f0_1, n0_1, r, buffer, buffer_size); 52 | merge_adaptive_n(f1_0, n1_0, f1_1, n1_1, r, buffer, buffer_size); 53 | } 54 | 55 | template 56 | // I is ForwardIterator 57 | // N is Integral 58 | // R is WeakStrictOrdering on the value type of I 59 | I sort_inplace_n_with_buffer(I first, N n, R r, B buffer) { 60 | if (!n) return first; 61 | N half = n >> 1; 62 | if (!half) return ++first; 63 | I middle = sort_inplace_n_with_buffer(first, half, r, buffer); 64 | I last = sort_inplace_n_with_buffer(middle, n - half, r, buffer); 65 | merge_with_buffer(first, middle, last, r, buffer); 66 | return last; 67 | } 68 | 69 | template 70 | // I is ForwardIterator 71 | inline 72 | void sort_inplace_with_buffer(I first, I last) { 73 | typedef typename std::iterator_traits::value_type T; 74 | typedef typename std::iterator_traits::difference_type N; 75 | N n = std::distance(first, last); 76 | std::vector buffer(n >> 1); 77 | sort_inplace_n_with_buffer(first, n, std::less(), buffer.begin()); 78 | } 79 | 80 | const size_t INSERTION_SORT_CUTOFF = 16; 81 | 82 | template 83 | // I is ForwardIterator 84 | // N is Integral 85 | // R is WeakStrictOrdering on the value type of I 86 | I sort_adaptive_n(I first, N n, R r, B buffer, N buffer_size) { 87 | if (!n) return first; 88 | if (n < INSERTION_SORT_CUTOFF) return binary_insertion_sort_n(first, n, r); 89 | N half = n >> 1; 90 | if (!half) return ++first; 91 | I middle = sort_adaptive_n(first, half, r, buffer, buffer_size); 92 | I last = sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 93 | merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 94 | return last; 95 | } 96 | 97 | template 98 | // I is ForwardIterator 99 | inline 100 | void sort_1_8th(I first, I last) { 101 | typedef typename std::iterator_traits::value_type T; 102 | typedef typename std::iterator_traits::difference_type N; 103 | N n = std::distance(first, last); 104 | std::vector buffer(n >> 3); 105 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 106 | } 107 | 108 | template 109 | // I is ForwardIterator 110 | inline 111 | void sort_1_64th(I first, I last) { 112 | typedef typename std::iterator_traits::value_type T; 113 | typedef typename std::iterator_traits::difference_type N; 114 | N n = std::distance(first, last); 115 | std::vector buffer(n >> 6); 116 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /code/lecture18/merge_inplace.h: -------------------------------------------------------------------------------- 1 | #ifndef MERGE_INPLACE_H 2 | #define MERGE_INPLACE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "search.h" 8 | 9 | // NOTE: this file will only compile with c++11 std::rotate 10 | 11 | /************************************************************ 12 | First published by Dudzin'sky and Dydek in 1981 IPL 12(1):5-8 13 | *************************************************************/ 14 | 15 | template 16 | // I is ForwardIterator 17 | // N is Integral 18 | // R is WeakStrictOrdering on the value type of I 19 | inline 20 | void merge_inplace_left_subproblem(I f0, N n0, 21 | I f1, N n1, 22 | I& f0_0, N& n0_0, 23 | I& f0_1, N& n0_1, 24 | I& f1_0, N& n1_0, 25 | I& f1_1, N& n1_1, 26 | R r) { 27 | // precondition std::distance(f0, f1) == n0 28 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 29 | // precondition n0 > 0 30 | // precondition n1 > 0 31 | f0_0 = f0; 32 | n0_0 = n0 >> 1; 33 | f0_1 = f0; 34 | std::advance(f0_1, n0_0); 35 | f1_1 = lower_bound_n(f1, n1, *f0_1, r); 36 | f1_0 = std::rotate(f0_1, f1, f1_1); 37 | n0_1 = std::distance(f0_1, f1_0); 38 | ++f1_0; 39 | n1_0 = (n0 - n0_0) - 1; 40 | n1_1 = n1 - n0_1; 41 | } 42 | 43 | template 44 | // I is ForwardIterator 45 | // N is Integral 46 | // R is WeakStrictOrdering on the value type of I 47 | inline 48 | void merge_inplace_right_subproblem(I f0, N n0, 49 | I f1, N n1, 50 | I& f0_0, N& n0_0, 51 | I& f0_1, N& n0_1, 52 | I& f1_0, N& n1_0, 53 | I& f1_1, N& n1_1, 54 | R r) { 55 | // precondition std::distance(f0, f1) == n0 56 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 57 | // precondition n0 > 0 58 | // precondition n1 > 0 59 | f0_0 = f0; 60 | n0_1 = n1 >> 1; 61 | f1_1 = f1; 62 | std::advance(f1_1, n0_1); 63 | f0_1 = upper_bound_n(f0, n0, *f1_1, r); 64 | ++f1_1; 65 | f1_0 = std::rotate(f0_1, f1, f1_1); 66 | n0_0 = std::distance(f0_0, f0_1); 67 | n1_0 = n0 - n0_0; 68 | n1_1 = (n1 - n0_1) - 1; 69 | } 70 | 71 | template 72 | // I is ForwardIterator 73 | // N is Integral 74 | // R is WeakStrictOrdering on the value type of I 75 | void merge_inplace_n(I f0, N n0, 76 | I f1, N n1, R r) { 77 | // precondition std::distance(f0, f1) == n0 78 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 79 | if (!n0 || !n1) return; 80 | I f0_0, f0_1, f1_0, f1_1; 81 | N n0_0, n0_1, n1_0, n1_1; 82 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 83 | f1, n1, 84 | f0_0, n0_0, 85 | f0_1, n0_1, 86 | f1_0, n1_0, 87 | f1_1, n1_1, 88 | r); 89 | else merge_inplace_right_subproblem(f0, n0, 90 | f1, n1, 91 | f0_0, n0_0, 92 | f0_1, n0_1, 93 | f1_0, n1_0, 94 | f1_1, n1_1, 95 | r); 96 | 97 | merge_inplace_n(f0_0, n0_0, f0_1, n0_1, r); 98 | merge_inplace_n(f1_0, n1_0, f1_1, n1_1, r); 99 | } 100 | 101 | template 102 | // I is ForwardIterator 103 | // N is Integral 104 | // R is WeakStrictOrdering on the value type of I 105 | I sort_inplace_n(I first, N n, R r) { 106 | if (!n) return first; 107 | N half = n >> 1; 108 | if (!half) return ++first; 109 | I middle = sort_inplace_n(first, half, r); 110 | I last = sort_inplace_n(middle, n - half, r); 111 | merge_inplace_n(first, half, middle, n - half, r); 112 | return last; 113 | } 114 | 115 | template 116 | // I is ForwardIterator 117 | inline 118 | void sort_inplace(I first, I last) { 119 | typedef typename std::iterator_traits::value_type T; 120 | sort_inplace_n(first, std::distance(first, last), std::less()); 121 | } 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /code/lecture18/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "algorithm.h" 6 | #include "merge_inplace.h" 7 | 8 | 9 | int main() { 10 | std::vector vec(111); 11 | random_iota(vec.begin(), vec.end()); 12 | print_range(vec.begin(), vec.end()); 13 | sort_inplace_n(vec.begin(), vec.size(), std::less()); 14 | print_range(vec.begin(), vec.end()); 15 | } 16 | -------------------------------------------------------------------------------- /code/lecture18/test_insertion_sort.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "insertion_sort.h" 4 | #include "algorithm.h" 5 | 6 | 7 | int main() { 8 | int nums[] = {5, 3, 10, 1, 2}; 9 | print_range(nums, nums + 5); 10 | linear_insertion_sort_n(nums, 5, std::less()); 11 | print_range(nums, nums + 5); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /code/lecture18/test_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test_sort.h" 4 | #include "algorithm.h" 5 | 6 | const size_t min_size(8); 7 | const size_t max_size(2 * 1024 * 1024); 8 | 9 | int main() { 10 | test_sort(min_size, max_size, random_iota); 11 | } 12 | -------------------------------------------------------------------------------- /code/lecture18/test_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_SORT_H 2 | #define TEST_SORT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "timer.h" 10 | #include "type_description.h" 11 | #include "algorithm.h" 12 | 13 | #include "merge_inplace.h" 14 | #include "merge.h" 15 | 16 | template 17 | // requires T is TotallyOrdered 18 | double time_sort(T* first, T* last, void (*sort)(T*, T*), T* buffer, size_t count) { 19 | timer t; 20 | t.start(); 21 | size_t i = count; 22 | while (i--) { 23 | std::copy(buffer, buffer + std::distance(first, last), first); 24 | sort(first, last); 25 | if (!std::is_sorted(first, last)) { 26 | std::cerr << "*** SORT FAILED! ***" << std::endl; 27 | return 0; 28 | } 29 | } 30 | return t.stop(); 31 | } 32 | 33 | template 34 | // requires T is TotallyOrdered 35 | // requires G is a Generator 36 | void test_sort(size_t min_size, size_t max_size, G gen) { 37 | 38 | time_t now = time(0); 39 | 40 | std::cout << "Sorting " << type_description(T(0)) 41 | << " from " << min_size << " up to " << max_size 42 | << " elements" << " generated with " << function_name(gen) 43 | <<" at: " << asctime(localtime(&now)); 44 | 45 | 46 | void (*f_pointers[])(T*, T*) = 47 | { 48 | std::stable_sort 49 | ,sort_inplace 50 | ,sort_inplace_with_buffer 51 | ,sort_1_8th 52 | ,sort_1_64th 53 | }; 54 | 55 | size_t number_of_sorts = sizeof(f_pointers) / sizeof(f_pointers[0]); 56 | 57 | std::cout << " size\t" 58 | << " stable\t" 59 | << "inplace\t" 60 | << " merge\t" 61 | << " 1_8th\t" 62 | << " 1_64th\t" 63 | << std::endl; 64 | 65 | for (size_t array_size(min_size); array_size <= max_size; array_size *= 2) { 66 | const size_t n = max_size / array_size; 67 | std::vector vec(array_size); 68 | gen(&*vec.begin(), (&*vec.begin()) + vec.size()); 69 | std::vector tmp(vec.begin(), vec.end()); 70 | std::cout << std::setw(12) << array_size << "\t"; 71 | for (size_t i = 0; i < number_of_sorts; ++i) { 72 | size_t bound = max_size; 73 | double time = time_sort(&*tmp.begin(), (&*tmp.begin()) + tmp.size(), f_pointers[i], &*vec.begin(), n); 74 | time /= double(array_size * n); 75 | std::cout << std::setw(6) << std::fixed << std::setprecision(0) << time << "\t"; 76 | } 77 | std::cout << std::endl; 78 | } 79 | } 80 | 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /code/lecture18/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include 5 | 6 | class timer { 7 | private: 8 | clock_t start_time; 9 | public: 10 | typedef double result_type; 11 | 12 | void start() { 13 | start_time = clock(); 14 | } 15 | 16 | result_type stop() { 17 | return 1000000000. * ((clock() - start_time) / double(CLOCKS_PER_SEC)); 18 | } 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /code/lecture18/type_description.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPE_DESCRIPTION_H 2 | #define TYPE_DESCRIPTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::string type_description(double) { return std::string("double"); } 10 | std::string type_description(long double) { return std::string("long double"); } 11 | std::string type_description(float) { return std::string("float"); } 12 | std::string type_description(uint8_t) { return std::string("uint8_t"); } 13 | std::string type_description(uint16_t) { return std::string("uint16_t"); } 14 | std::string type_description(uint32_t) { return std::string("uint32_t"); } 15 | std::string type_description(uint64_t) { return std::string("uint64_t"); } 16 | std::string type_description(int8_t) { return std::string("int8_t"); } 17 | std::string type_description(int16_t) { return std::string("int16_t"); } 18 | std::string type_description(int32_t) { return std::string("int32_t"); } 19 | std::string type_description(int64_t) { return std::string("int64_t"); } 20 | 21 | template 22 | std::string type_description(const std::vector&) { return std::string("vector<") + type_description(T()) + std::string(">"); } 23 | 24 | template 25 | std::string type_description(const std::deque&) { return std::string("deque<") + type_description(T()) + std::string(">"); } 26 | 27 | template 28 | std::string type_description(const std::list&) { return std::string("list<") + type_description(T()) + std::string(">"); } 29 | #endif 30 | -------------------------------------------------------------------------------- /code/lecture19/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | #include 11 | 12 | template 13 | N iota(I first, I last, N start = N(0), N step = N(1)) { 14 | typedef typename std::iterator_traits::value_type T; 15 | while (first != last) { 16 | *first = T(start); 17 | start += step; 18 | ++first; 19 | } 20 | return start; 21 | } 22 | 23 | template 24 | inline 25 | void iota(I first, I last) { 26 | typedef typename std::iterator_traits::value_type T; 27 | iota(first, last, T(0), T(1)); 28 | } 29 | 30 | template 31 | void random_iota(I first, I last) { 32 | iota(first, last); 33 | std::random_shuffle(first, last); 34 | } 35 | 36 | template 37 | void reverse_iota(I first, I last) { 38 | iota(first, last); 39 | std::reverse(first, last); 40 | } 41 | 42 | template 43 | void print_range(I first, I last) { 44 | while (first != last) { 45 | std::cout << *first << " "; 46 | ++first; 47 | } 48 | std::cout << std::endl; 49 | } 50 | 51 | template 52 | // requires I is InputIterator 53 | inline 54 | I successor(I x) { return ++x; } 55 | 56 | template 57 | // requires I is BidirectionalIterator 58 | inline 59 | I predecessor(I x) { return --x; } 60 | 61 | template 62 | // requires I is RandomAccessIterator 63 | // requires N is Integral 64 | inline 65 | I successor(I x, N n, std::random_access_iterator_tag) { 66 | return x + n; 67 | } 68 | 69 | template 70 | // requires I is InputIterator 71 | // requires N is Integral 72 | inline 73 | I successor(I x, N n, std::input_iterator_tag) { 74 | while (n != N(0)) { 75 | ++x; 76 | --n; 77 | } 78 | return x; 79 | } 80 | 81 | template 82 | // requires I is InputIterator 83 | // requires N is Integral 84 | inline 85 | I successor(I x, N n) { 86 | typedef typename std::iterator_traits::iterator_category C; 87 | return successor(x, n, C()); 88 | } 89 | 90 | template 91 | // requires I is BidirectionalIterator 92 | void hill(I first, I last) 93 | { 94 | I middle = successor(first, std::distance(first, last)/2); 95 | iota(first, middle); 96 | reverse_iota(middle, last); 97 | } 98 | 99 | template 100 | // requires I is BidirectionalIterator 101 | void valley(I first, I last) 102 | { 103 | I middle = successor(first, std::distance(first, last)/2); 104 | reverse_iota(first, middle); 105 | iota(middle, last); 106 | } 107 | 108 | template 109 | // requires I is BidirectionalIterator 110 | inline 111 | const char* function_name(void (*gen)(I, I)) { 112 | void (*generator[])(I, I) = 113 | { 114 | iota, 115 | hill, 116 | valley, 117 | reverse_iota, 118 | random_iota 119 | }; 120 | const char* names[] = 121 | { 122 | "iota", 123 | "hill", 124 | "valley", 125 | "reverse_iota", 126 | "random_iota" 127 | }; 128 | size_t number_of_generators = sizeof(generator)/sizeof(generator[0]); 129 | size_t index = std::find(generator, generator + number_of_generators, gen) - generator; 130 | if (index >= number_of_generators) return "unknown data generation "; 131 | return names[index]; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /code/lecture19/ideas.txt: -------------------------------------------------------------------------------- 1 | ideas 2 | ----- 3 | 1. use extra storage when the size is small 4 | a. use the extra storage as temp, copying on next merge? 5 | 2. use insertion sort when n < 16? 6 | a. use binary insertion sort? for n less than k 7 | b. optimize insertion sort for 1 element 8 | 3. get rid of recursion (using binary counter) 9 | 4. write a very good merge (adaptive) 10 | a. could we write a better merge than std::merge? 11 | 5. use rotate_with_buffer in merge_inplace_n left/right 12 | 13 | 14 | requirements 15 | ------------ 16 | 1. maintain stability 17 | 2. accepts ForwardIterator 18 | 3. use limited/reasonable extra memory 19 | 4. fast! (good=within 5% of std::stable_sort, 20 | great=faster than std::stable_sort, 21 | optimal=as fast as std::sort) 22 | 5. implement good insertion sort using ForwardIterator 23 | -------------------------------------------------------------------------------- /code/lecture19/merge.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "merge_inplace.h" 8 | #include "insertion_sort.h" 9 | 10 | template 11 | // requires I is ForwardIterator 12 | // requires R is StrictWeakOrdering 13 | // requires B is ForwardIterator 14 | void merge_with_buffer(I first, I middle, I last, R r, B buffer) { 15 | B buffer_last = std::copy(first, middle, buffer); 16 | std::merge(buffer, buffer_last, middle, last, first, r); 17 | } 18 | 19 | template 20 | // I is ForwardIterator 21 | // N is Integral 22 | // R is WeakStrictOrdering on the value type of I 23 | void merge_adaptive_n(I f0, N n0, 24 | I f1, N n1, R r, B buffer, N buffer_size) { 25 | // precondition std::distance(f0, f1) == n0 26 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 27 | if (!n0 || !n1) return; 28 | if (n0 <= buffer_size) { 29 | I last = f1; 30 | std::advance(last, n1); 31 | merge_with_buffer(f0, f1, last, r, buffer); 32 | return; 33 | } 34 | I f0_0, f0_1, f1_0, f1_1; 35 | N n0_0, n0_1, n1_0, n1_1; 36 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 37 | f1, n1, 38 | f0_0, n0_0, 39 | f0_1, n0_1, 40 | f1_0, n1_0, 41 | f1_1, n1_1, 42 | r); 43 | else merge_inplace_right_subproblem(f0, n0, 44 | f1, n1, 45 | f0_0, n0_0, 46 | f0_1, n0_1, 47 | f1_0, n1_0, 48 | f1_1, n1_1, 49 | r); 50 | 51 | merge_adaptive_n(f0_0, n0_0, f0_1, n0_1, r, buffer, buffer_size); 52 | merge_adaptive_n(f1_0, n1_0, f1_1, n1_1, r, buffer, buffer_size); 53 | } 54 | 55 | template 56 | // I is ForwardIterator 57 | // N is Integral 58 | // R is WeakStrictOrdering on the value type of I 59 | I sort_inplace_n_with_buffer(I first, N n, R r, B buffer) { 60 | if (!n) return first; 61 | N half = n >> 1; 62 | if (!half) return ++first; 63 | I middle = sort_inplace_n_with_buffer(first, half, r, buffer); 64 | I last = sort_inplace_n_with_buffer(middle, n - half, r, buffer); 65 | merge_with_buffer(first, middle, last, r, buffer); 66 | return last; 67 | } 68 | 69 | template 70 | // I is ForwardIterator 71 | inline 72 | void sort_inplace_with_buffer(I first, I last) { 73 | typedef typename std::iterator_traits::value_type T; 74 | typedef typename std::iterator_traits::difference_type N; 75 | N n = std::distance(first, last); 76 | std::vector buffer(n >> 1); 77 | sort_inplace_n_with_buffer(first, n, std::less(), buffer.begin()); 78 | } 79 | 80 | const size_t INSERTION_SORT_CUTOFF = 16; 81 | 82 | template 83 | // I is ForwardIterator 84 | // N is Integral 85 | // R is WeakStrictOrdering on the value type of I 86 | I sort_adaptive_n(I first, N n, R r, B buffer, N buffer_size) { 87 | if (!n) return first; 88 | if (n < INSERTION_SORT_CUTOFF) return binary_insertion_sort_n(first, n, r); 89 | N half = n >> 1; 90 | if (!half) return ++first; 91 | I middle = sort_adaptive_n(first, half, r, buffer, buffer_size); 92 | I last = sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 93 | merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 94 | return last; 95 | } 96 | 97 | template 98 | // I is ForwardIterator 99 | inline 100 | void sort_1_8th(I first, I last) { 101 | typedef typename std::iterator_traits::value_type T; 102 | typedef typename std::iterator_traits::difference_type N; 103 | N n = std::distance(first, last); 104 | std::vector buffer(n >> 3); 105 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 106 | } 107 | 108 | template 109 | // I is ForwardIterator 110 | inline 111 | void sort_1_64th(I first, I last) { 112 | typedef typename std::iterator_traits::value_type T; 113 | typedef typename std::iterator_traits::difference_type N; 114 | N n = std::distance(first, last); 115 | std::vector buffer(n >> 6); 116 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /code/lecture19/merge_inplace.h: -------------------------------------------------------------------------------- 1 | #ifndef MERGE_INPLACE_H 2 | #define MERGE_INPLACE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "search.h" 8 | 9 | // NOTE: this file will only compile with c++11 std::rotate 10 | 11 | /************************************************************ 12 | First published by Dudzin'sky and Dydek in 1981 IPL 12(1):5-8 13 | *************************************************************/ 14 | 15 | template 16 | // I is ForwardIterator 17 | // N is Integral 18 | // R is WeakStrictOrdering on the value type of I 19 | inline 20 | void merge_inplace_left_subproblem(I f0, N n0, 21 | I f1, N n1, 22 | I& f0_0, N& n0_0, 23 | I& f0_1, N& n0_1, 24 | I& f1_0, N& n1_0, 25 | I& f1_1, N& n1_1, 26 | R r) { 27 | // precondition std::distance(f0, f1) == n0 28 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 29 | // precondition n0 > 0 30 | // precondition n1 > 0 31 | f0_0 = f0; 32 | n0_0 = n0 >> 1; 33 | f0_1 = f0; 34 | std::advance(f0_1, n0_0); 35 | f1_1 = lower_bound_n(f1, n1, *f0_1, r); 36 | f1_0 = std::rotate(f0_1, f1, f1_1); 37 | n0_1 = std::distance(f0_1, f1_0); 38 | ++f1_0; 39 | n1_0 = (n0 - n0_0) - 1; 40 | n1_1 = n1 - n0_1; 41 | } 42 | 43 | template 44 | // I is ForwardIterator 45 | // N is Integral 46 | // R is WeakStrictOrdering on the value type of I 47 | inline 48 | void merge_inplace_right_subproblem(I f0, N n0, 49 | I f1, N n1, 50 | I& f0_0, N& n0_0, 51 | I& f0_1, N& n0_1, 52 | I& f1_0, N& n1_0, 53 | I& f1_1, N& n1_1, 54 | R r) { 55 | // precondition std::distance(f0, f1) == n0 56 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 57 | // precondition n0 > 0 58 | // precondition n1 > 0 59 | f0_0 = f0; 60 | n0_1 = n1 >> 1; 61 | f1_1 = f1; 62 | std::advance(f1_1, n0_1); 63 | f0_1 = upper_bound_n(f0, n0, *f1_1, r); 64 | ++f1_1; 65 | f1_0 = std::rotate(f0_1, f1, f1_1); 66 | n0_0 = std::distance(f0_0, f0_1); 67 | n1_0 = n0 - n0_0; 68 | n1_1 = (n1 - n0_1) - 1; 69 | } 70 | 71 | template 72 | // I is ForwardIterator 73 | // N is Integral 74 | // R is WeakStrictOrdering on the value type of I 75 | void merge_inplace_n(I f0, N n0, 76 | I f1, N n1, R r) { 77 | // precondition std::distance(f0, f1) == n0 78 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 79 | if (!n0 || !n1) return; 80 | I f0_0, f0_1, f1_0, f1_1; 81 | N n0_0, n0_1, n1_0, n1_1; 82 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 83 | f1, n1, 84 | f0_0, n0_0, 85 | f0_1, n0_1, 86 | f1_0, n1_0, 87 | f1_1, n1_1, 88 | r); 89 | else merge_inplace_right_subproblem(f0, n0, 90 | f1, n1, 91 | f0_0, n0_0, 92 | f0_1, n0_1, 93 | f1_0, n1_0, 94 | f1_1, n1_1, 95 | r); 96 | 97 | merge_inplace_n(f0_0, n0_0, f0_1, n0_1, r); 98 | merge_inplace_n(f1_0, n1_0, f1_1, n1_1, r); 99 | } 100 | 101 | template 102 | // I is ForwardIterator 103 | // N is Integral 104 | // R is WeakStrictOrdering on the value type of I 105 | I sort_inplace_n(I first, N n, R r) { 106 | if (!n) return first; 107 | N half = n >> 1; 108 | if (!half) return ++first; 109 | I middle = sort_inplace_n(first, half, r); 110 | I last = sort_inplace_n(middle, n - half, r); 111 | merge_inplace_n(first, half, middle, n - half, r); 112 | return last; 113 | } 114 | 115 | template 116 | // I is ForwardIterator 117 | inline 118 | void sort_inplace(I first, I last) { 119 | typedef typename std::iterator_traits::value_type T; 120 | sort_inplace_n(first, std::distance(first, last), std::less()); 121 | } 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /code/lecture19/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "algorithm.h" 6 | #include "merge_inplace.h" 7 | 8 | 9 | int main() { 10 | std::vector vec(111); 11 | random_iota(vec.begin(), vec.end()); 12 | print_range(vec.begin(), vec.end()); 13 | sort_inplace_n(vec.begin(), vec.size(), std::less()); 14 | print_range(vec.begin(), vec.end()); 15 | } 16 | -------------------------------------------------------------------------------- /code/lecture19/test_insertion_sort.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "insertion_sort.h" 4 | #include "algorithm.h" 5 | 6 | 7 | int main() { 8 | int nums[] = {5, 3, 10, 1, 2}; 9 | print_range(nums, nums + 5); 10 | linear_insertion_sort_n(nums, 5, std::less()); 11 | print_range(nums, nums + 5); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /code/lecture19/test_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test_sort.h" 4 | #include "algorithm.h" 5 | 6 | const size_t min_size(8); 7 | const size_t max_size(2 * 1024 * 1024); 8 | 9 | int main() { 10 | test_sort(min_size, max_size, random_iota); 11 | } 12 | -------------------------------------------------------------------------------- /code/lecture19/test_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_SORT_H 2 | #define TEST_SORT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "timer.h" 10 | #include "type_description.h" 11 | #include "algorithm.h" 12 | 13 | #include "merge_inplace.h" 14 | #include "merge.h" 15 | 16 | template 17 | // requires T is TotallyOrdered 18 | double time_sort(T* first, T* last, void (*sort)(T*, T*), T* buffer, size_t count) { 19 | timer t; 20 | t.start(); 21 | size_t i = count; 22 | while (i--) { 23 | std::copy(buffer, buffer + std::distance(first, last), first); 24 | sort(first, last); 25 | if (!std::is_sorted(first, last)) { 26 | std::cerr << "*** SORT FAILED! ***" << std::endl; 27 | return 0; 28 | } 29 | } 30 | return t.stop(); 31 | } 32 | 33 | template 34 | // requires T is TotallyOrdered 35 | // requires G is a Generator 36 | void test_sort(size_t min_size, size_t max_size, G gen) { 37 | 38 | time_t now = time(0); 39 | 40 | std::cout << "Sorting " << type_description(T(0)) 41 | << " from " << min_size << " up to " << max_size 42 | << " elements" << " generated with " << function_name(gen) 43 | <<" at: " << asctime(localtime(&now)); 44 | 45 | 46 | void (*f_pointers[])(T*, T*) = 47 | { 48 | std::stable_sort 49 | ,sort_inplace 50 | ,sort_inplace_with_buffer 51 | ,sort_1_8th 52 | ,sort_1_64th 53 | }; 54 | 55 | size_t number_of_sorts = sizeof(f_pointers) / sizeof(f_pointers[0]); 56 | 57 | std::cout << " size\t" 58 | << " stable\t" 59 | << "inplace\t" 60 | << " merge\t" 61 | << " 1_8th\t" 62 | << " 1_64th\t" 63 | << std::endl; 64 | 65 | for (size_t array_size(min_size); array_size <= max_size; array_size *= 2) { 66 | const size_t n = max_size / array_size; 67 | std::vector vec(array_size); 68 | gen(&*vec.begin(), (&*vec.begin()) + vec.size()); 69 | std::vector tmp(vec.begin(), vec.end()); 70 | std::cout << std::setw(12) << array_size << "\t"; 71 | for (size_t i = 0; i < number_of_sorts; ++i) { 72 | size_t bound = max_size; 73 | double time = time_sort(&*tmp.begin(), (&*tmp.begin()) + tmp.size(), f_pointers[i], &*vec.begin(), n); 74 | time /= double(array_size * n); 75 | std::cout << std::setw(6) << std::fixed << std::setprecision(0) << time << "\t"; 76 | } 77 | std::cout << std::endl; 78 | } 79 | } 80 | 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /code/lecture19/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include 5 | 6 | class timer { 7 | private: 8 | clock_t start_time; 9 | public: 10 | typedef double result_type; 11 | 12 | void start() { 13 | start_time = clock(); 14 | } 15 | 16 | result_type stop() { 17 | return 1000000000. * ((clock() - start_time) / double(CLOCKS_PER_SEC)); 18 | } 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /code/lecture19/type_description.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPE_DESCRIPTION_H 2 | #define TYPE_DESCRIPTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::string type_description(double) { return std::string("double"); } 10 | std::string type_description(long double) { return std::string("long double"); } 11 | std::string type_description(float) { return std::string("float"); } 12 | std::string type_description(uint8_t) { return std::string("uint8_t"); } 13 | std::string type_description(uint16_t) { return std::string("uint16_t"); } 14 | std::string type_description(uint32_t) { return std::string("uint32_t"); } 15 | std::string type_description(uint64_t) { return std::string("uint64_t"); } 16 | std::string type_description(int8_t) { return std::string("int8_t"); } 17 | std::string type_description(int16_t) { return std::string("int16_t"); } 18 | std::string type_description(int32_t) { return std::string("int32_t"); } 19 | std::string type_description(int64_t) { return std::string("int64_t"); } 20 | 21 | template 22 | std::string type_description(const std::vector&) { return std::string("vector<") + type_description(T()) + std::string(">"); } 23 | 24 | template 25 | std::string type_description(const std::deque&) { return std::string("deque<") + type_description(T()) + std::string(">"); } 26 | 27 | template 28 | std::string type_description(const std::list&) { return std::string("list<") + type_description(T()) + std::string(">"); } 29 | #endif 30 | -------------------------------------------------------------------------------- /code/lecture2/singleton.cpp: -------------------------------------------------------------------------------- 1 | #include "singleton.h" 2 | #include 3 | 4 | typedef singleton sd; 5 | typedef singleton si; 6 | 7 | int main() { 8 | sd x(1.5); 9 | sd y(2.5); 10 | sd z(1); 11 | std::cout << x + x << std::endl; 12 | std::cout << y + y << std::endl; 13 | std::cout << x + z << std::endl; 14 | 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /code/lecture2/singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLETON_H 2 | #define SINGLETON_H 3 | 4 | template 5 | // T is Semiregualr or Regular or TotallyOrdered 6 | struct singleton 7 | { 8 | typedef T value_type; 9 | T value; 10 | // Conversions from T and to T: 11 | explicit singleton(const T& x) : value(x) {} 12 | explicit operator T() const { return value; } 13 | template 14 | singleton(const singleton& x) : value(x.value) {} 15 | // Write conversions from T to singleton and singleton to T. 16 | 17 | // Semiregular: 18 | singleton(const singleton& x) : value(x.value) {} // could be implicitly declared 19 | singleton() {} // could be implicitly declared sometimes 20 | ~singleton() {} // could be implicitly declared 21 | singleton& operator=(const singleton& x) { // could be implicitly declared 22 | value = x.value; 23 | return *this; 24 | } 25 | // Regular 26 | friend 27 | bool operator==(const singleton& x, const singleton& y) { 28 | return x.value == y.value; 29 | } 30 | friend 31 | bool operator!=(const singleton& x, const singleton& y) { 32 | return !(x == y); 33 | } 34 | // TotallyOrdered 35 | friend 36 | bool operator<(const singleton& x, const singleton& y) { 37 | return x.value < y.value; 38 | } 39 | friend 40 | bool operator>(const singleton& x, const singleton& y) { 41 | return y < x; 42 | } 43 | friend 44 | bool operator<=(const singleton& x, const singleton& y) { 45 | return !(y < x); 46 | } 47 | friend 48 | bool operator>=(const singleton& x, const singleton& y) { 49 | return !(x < y); 50 | } 51 | 52 | 53 | }; 54 | 55 | #endif 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /code/lecture20/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #define InputIterator typename 5 | #define ForwardIterator typename 6 | #define RandomAccessIterator typename 7 | #define BidirectionalIterator typename 8 | #define Integral typename 9 | 10 | #include 11 | 12 | template 13 | N iota(I first, I last, N start = N(0), N step = N(1)) { 14 | typedef typename std::iterator_traits::value_type T; 15 | while (first != last) { 16 | *first = T(start); 17 | start += step; 18 | ++first; 19 | } 20 | return start; 21 | } 22 | 23 | template 24 | inline 25 | void iota(I first, I last) { 26 | typedef typename std::iterator_traits::value_type T; 27 | iota(first, last, T(0), T(1)); 28 | } 29 | 30 | template 31 | void random_iota(I first, I last) { 32 | iota(first, last); 33 | std::random_shuffle(first, last); 34 | } 35 | 36 | template 37 | void reverse_iota(I first, I last) { 38 | iota(first, last); 39 | std::reverse(first, last); 40 | } 41 | 42 | template 43 | void print_range(I first, I last) { 44 | while (first != last) { 45 | std::cout << *first << " "; 46 | ++first; 47 | } 48 | std::cout << std::endl; 49 | } 50 | 51 | template 52 | // requires I is InputIterator 53 | inline 54 | I successor(I x) { return ++x; } 55 | 56 | template 57 | // requires I is BidirectionalIterator 58 | inline 59 | I predecessor(I x) { return --x; } 60 | 61 | template 62 | // requires I is RandomAccessIterator 63 | // requires N is Integral 64 | inline 65 | I successor(I x, N n, std::random_access_iterator_tag) { 66 | return x + n; 67 | } 68 | 69 | template 70 | // requires I is InputIterator 71 | // requires N is Integral 72 | inline 73 | I successor(I x, N n, std::input_iterator_tag) { 74 | while (n != N(0)) { 75 | ++x; 76 | --n; 77 | } 78 | return x; 79 | } 80 | 81 | template 82 | // requires I is InputIterator 83 | // requires N is Integral 84 | inline 85 | I successor(I x, N n) { 86 | typedef typename std::iterator_traits::iterator_category C; 87 | return successor(x, n, C()); 88 | } 89 | 90 | template 91 | // requires I is BidirectionalIterator 92 | void hill(I first, I last) 93 | { 94 | I middle = successor(first, std::distance(first, last)/2); 95 | iota(first, middle); 96 | reverse_iota(middle, last); 97 | } 98 | 99 | template 100 | // requires I is BidirectionalIterator 101 | void valley(I first, I last) 102 | { 103 | I middle = successor(first, std::distance(first, last)/2); 104 | reverse_iota(first, middle); 105 | iota(middle, last); 106 | } 107 | 108 | template 109 | // requires I is BidirectionalIterator 110 | inline 111 | const char* function_name(void (*gen)(I, I)) { 112 | void (*generator[])(I, I) = 113 | { 114 | iota, 115 | hill, 116 | valley, 117 | reverse_iota, 118 | random_iota 119 | }; 120 | const char* names[] = 121 | { 122 | "iota", 123 | "hill", 124 | "valley", 125 | "reverse_iota", 126 | "random_iota" 127 | }; 128 | size_t number_of_generators = sizeof(generator)/sizeof(generator[0]); 129 | size_t index = std::find(generator, generator + number_of_generators, gen) - generator; 130 | if (index >= number_of_generators) return "unknown data generation "; 131 | return names[index]; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /code/lecture20/ideas.txt: -------------------------------------------------------------------------------- 1 | ideas 2 | ----- 3 | 1. use extra storage when the size is small 4 | a. use the extra storage as temp, copying on next merge? 5 | 2. use insertion sort when n < 16? 6 | a. use binary insertion sort? for n less than k 7 | b. optimize insertion sort for 1 element 8 | 3. get rid of recursion (using binary counter) 9 | 4. write a very good merge (adaptive) 10 | a. could we write a better merge than std::merge? 11 | 5. use rotate_with_buffer in merge_inplace_n left/right 12 | 13 | 14 | requirements 15 | ------------ 16 | 1. maintain stability 17 | 2. accepts ForwardIterator 18 | 3. use limited/reasonable extra memory 19 | 4. fast! (good=within 5% of std::stable_sort, 20 | great=faster than std::stable_sort, 21 | optimal=as fast as std::sort) 22 | 5. implement good insertion sort using ForwardIterator 23 | -------------------------------------------------------------------------------- /code/lecture20/merge.h: -------------------------------------------------------------------------------- 1 | #ifndef MERGE_H 2 | #define MERGE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "merge_inplace.h" 10 | #include "insertion_sort.h" 11 | 12 | template 13 | // requires I is ForwardIterator 14 | // requires R is StrictWeakOrdering 15 | // requires B is ForwardIterator 16 | void merge_with_buffer(I first, I middle, I last, R r, B buffer) { 17 | B buffer_last = std::copy(first, middle, buffer); 18 | std::merge(buffer, buffer_last, middle, last, first, r); 19 | } 20 | 21 | template 22 | // I is ForwardIterator 23 | // N is Integral 24 | // R is WeakStrictOrdering on the value type of I 25 | void merge_adaptive_n(I f0, N n0, 26 | I f1, N n1, R r, B buffer, N buffer_size) { 27 | // precondition std::distance(f0, f1) == n0 28 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 29 | if (!n0 || !n1) return; 30 | if (n0 <= buffer_size) { 31 | I last = f1; 32 | std::advance(last, n1); 33 | merge_with_buffer(f0, f1, last, r, buffer); 34 | return; 35 | } 36 | I f0_0, f0_1, f1_0, f1_1; 37 | N n0_0, n0_1, n1_0, n1_1; 38 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 39 | f1, n1, 40 | f0_0, n0_0, 41 | f0_1, n0_1, 42 | f1_0, n1_0, 43 | f1_1, n1_1, 44 | r); 45 | else merge_inplace_right_subproblem(f0, n0, 46 | f1, n1, 47 | f0_0, n0_0, 48 | f0_1, n0_1, 49 | f1_0, n1_0, 50 | f1_1, n1_1, 51 | r); 52 | 53 | merge_adaptive_n(f0_0, n0_0, f0_1, n0_1, r, buffer, buffer_size); 54 | merge_adaptive_n(f1_0, n1_0, f1_1, n1_1, r, buffer, buffer_size); 55 | } 56 | 57 | template 58 | // I is ForwardIterator 59 | // N is Integral 60 | // R is WeakStrictOrdering on the value type of I 61 | I sort_inplace_n_with_buffer(I first, N n, R r, B buffer) { 62 | if (!n) return first; 63 | N half = n >> 1; 64 | if (!half) return ++first; 65 | I middle = sort_inplace_n_with_buffer(first, half, r, buffer); 66 | I last = sort_inplace_n_with_buffer(middle, n - half, r, buffer); 67 | merge_with_buffer(first, middle, last, r, buffer); 68 | return last; 69 | } 70 | 71 | template 72 | // I is ForwardIterator 73 | inline 74 | void sort_inplace_with_buffer(I first, I last) { 75 | typedef typename std::iterator_traits::value_type T; 76 | typedef typename std::iterator_traits::difference_type N; 77 | N n = std::distance(first, last); 78 | std::vector buffer(n >> 1); 79 | sort_inplace_n_with_buffer(first, n, std::less(), buffer.begin()); 80 | } 81 | 82 | const size_t INSERTION_SORT_CUTOFF = 16; 83 | 84 | template 85 | // I is ForwardIterator 86 | // N is Integral 87 | // R is WeakStrictOrdering on the value type of I 88 | I sort_adaptive_n(I first, N n, R r, B buffer, N buffer_size) { 89 | if (!n) return first; 90 | if (n < INSERTION_SORT_CUTOFF) return binary_insertion_sort_n(first, n, r); 91 | N half = n >> 1; 92 | if (!half) return ++first; 93 | I middle = sort_adaptive_n(first, half, r, buffer, buffer_size); 94 | I last = sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 95 | merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 96 | return last; 97 | } 98 | 99 | template 100 | // I is ForwardIterator 101 | inline 102 | void sort_1_8th(I first, I last) { 103 | typedef typename std::iterator_traits::value_type T; 104 | typedef typename std::iterator_traits::difference_type N; 105 | N n = std::distance(first, last); 106 | std::vector buffer(n >> 3); 107 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 108 | } 109 | 110 | template 111 | // I is ForwardIterator 112 | inline 113 | void sort_1_64th(I first, I last) { 114 | typedef typename std::iterator_traits::value_type T; 115 | typedef typename std::iterator_traits::difference_type N; 116 | N n = std::distance(first, last); 117 | std::vector buffer(n >> 6); 118 | sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 119 | } 120 | 121 | #endif // MERGE_H 122 | -------------------------------------------------------------------------------- /code/lecture20/merge_inplace.h: -------------------------------------------------------------------------------- 1 | #ifndef MERGE_INPLACE_H 2 | #define MERGE_INPLACE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "search.h" 8 | 9 | // NOTE: this file will only compile with c++11 std::rotate 10 | 11 | /************************************************************ 12 | First published by Dudzin'sky and Dydek in 1981 IPL 12(1):5-8 13 | *************************************************************/ 14 | 15 | template 16 | // I is ForwardIterator 17 | // N is Integral 18 | // R is WeakStrictOrdering on the value type of I 19 | inline 20 | void merge_inplace_left_subproblem(I f0, N n0, 21 | I f1, N n1, 22 | I& f0_0, N& n0_0, 23 | I& f0_1, N& n0_1, 24 | I& f1_0, N& n1_0, 25 | I& f1_1, N& n1_1, 26 | R r) { 27 | // precondition std::distance(f0, f1) == n0 28 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 29 | // precondition n0 > 0 30 | // precondition n1 > 0 31 | f0_0 = f0; 32 | n0_0 = n0 >> 1; 33 | f0_1 = f0; 34 | std::advance(f0_1, n0_0); 35 | f1_1 = lower_bound_n(f1, n1, *f0_1, r); 36 | f1_0 = std::rotate(f0_1, f1, f1_1); 37 | n0_1 = std::distance(f0_1, f1_0); 38 | ++f1_0; 39 | n1_0 = (n0 - n0_0) - 1; 40 | n1_1 = n1 - n0_1; 41 | } 42 | 43 | template 44 | // I is ForwardIterator 45 | // N is Integral 46 | // R is WeakStrictOrdering on the value type of I 47 | inline 48 | void merge_inplace_right_subproblem(I f0, N n0, 49 | I f1, N n1, 50 | I& f0_0, N& n0_0, 51 | I& f0_1, N& n0_1, 52 | I& f1_0, N& n1_0, 53 | I& f1_1, N& n1_1, 54 | R r) { 55 | // precondition std::distance(f0, f1) == n0 56 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 57 | // precondition n0 > 0 58 | // precondition n1 > 0 59 | f0_0 = f0; 60 | n0_1 = n1 >> 1; 61 | f1_1 = f1; 62 | std::advance(f1_1, n0_1); 63 | f0_1 = upper_bound_n(f0, n0, *f1_1, r); 64 | ++f1_1; 65 | f1_0 = std::rotate(f0_1, f1, f1_1); 66 | n0_0 = std::distance(f0_0, f0_1); 67 | n1_0 = n0 - n0_0; 68 | n1_1 = (n1 - n0_1) - 1; 69 | } 70 | 71 | template 72 | // I is ForwardIterator 73 | // N is Integral 74 | // R is WeakStrictOrdering on the value type of I 75 | void merge_inplace_n(I f0, N n0, 76 | I f1, N n1, R r) { 77 | // precondition std::distance(f0, f1) == n0 78 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 79 | if (!n0 || !n1) return; 80 | I f0_0, f0_1, f1_0, f1_1; 81 | N n0_0, n0_1, n1_0, n1_1; 82 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 83 | f1, n1, 84 | f0_0, n0_0, 85 | f0_1, n0_1, 86 | f1_0, n1_0, 87 | f1_1, n1_1, 88 | r); 89 | else merge_inplace_right_subproblem(f0, n0, 90 | f1, n1, 91 | f0_0, n0_0, 92 | f0_1, n0_1, 93 | f1_0, n1_0, 94 | f1_1, n1_1, 95 | r); 96 | 97 | merge_inplace_n(f0_0, n0_0, f0_1, n0_1, r); 98 | merge_inplace_n(f1_0, n1_0, f1_1, n1_1, r); 99 | } 100 | 101 | template 102 | // I is ForwardIterator 103 | // N is Integral 104 | // R is WeakStrictOrdering on the value type of I 105 | I sort_inplace_n(I first, N n, R r) { 106 | if (!n) return first; 107 | N half = n >> 1; 108 | if (!half) return ++first; 109 | I middle = sort_inplace_n(first, half, r); 110 | I last = sort_inplace_n(middle, n - half, r); 111 | merge_inplace_n(first, half, middle, n - half, r); 112 | return last; 113 | } 114 | 115 | template 116 | // I is ForwardIterator 117 | inline 118 | void sort_inplace(I first, I last) { 119 | typedef typename std::iterator_traits::value_type T; 120 | sort_inplace_n(first, std::distance(first, last), std::less()); 121 | } 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /code/lecture20/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "algorithm.h" 6 | #include "merge_inplace.h" 7 | 8 | 9 | int main() { 10 | std::vector vec(111); 11 | random_iota(vec.begin(), vec.end()); 12 | print_range(vec.begin(), vec.end()); 13 | sort_inplace_n(vec.begin(), vec.size(), std::less()); 14 | print_range(vec.begin(), vec.end()); 15 | } 16 | -------------------------------------------------------------------------------- /code/lecture20/sort_akraft.h: -------------------------------------------------------------------------------- 1 | #ifndef _SORT_AKRAFT 2 | #define _SORT_AKRAFT 3 | // Include Everything!!! 4 | #include "algorithm.h" 5 | #include "search.h" 6 | #include "insertion_sort.h" 7 | 8 | template 9 | // I is ForwardIterator 10 | // N is Integral 11 | // R is WeakStrictOrdering on the value type of I 12 | inline 13 | I sort_adaptive_n_akraft(I first, N n, R r, B buffer, N buffer_size) { 14 | if (!n) return first; 15 | if (n < INSERTION_SORT_CUTOFF) return insertion_sort_n(first, n, r); 16 | //if (n < INSERTION_SORT_CUTOFF) return linear_insert_with_sentinel_n(first, n, r); 17 | N half = n >> 1; 18 | if (!half) return ++first; 19 | I middle = sort_adaptive_n(first, half, r, buffer, buffer_size); 20 | I last = sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 21 | merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 22 | return last; 23 | } 24 | 25 | template 26 | // I is ForwardIterator 27 | // R is WeakStrictOrdering on the value type of I 28 | inline 29 | void sort_akraft(I first, I last) { 30 | // Attempt 1, just use merge sort 31 | typedef typename std::iterator_traits::value_type T; 32 | typedef typename std::iterator_traits::difference_type N; 33 | N n = std::distance(first, last); 34 | std::vector buffer(n >> 1); 35 | sort_adaptive_n_akraft(first, n, std::less(), buffer.begin(), N(buffer.size())); 36 | 37 | //std::stable_sort(first, last, std::less()); 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /code/lecture20/sort_bert.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "merge_inplace.h" 7 | #include "insertion_sort.h" 8 | 9 | const size_t BERT_INSERTION_SORT_CUTOFF = 64; 10 | 11 | template 12 | // requires I is ForwardIterator 13 | // requires R is StrictWeakOrdering 14 | // requires B is ForwardIterator 15 | void bert_merge_with_buffer(I first, I middle, I last, R r, B buffer) { 16 | B buffer_last = std::copy(first, middle, buffer); 17 | std::merge(buffer, buffer_last, middle, last, first, r); 18 | } 19 | 20 | template 21 | // I is ForwardIterator 22 | // N is Integral 23 | // R is WeakStrictOrdering on the value type of I 24 | I bert_sort_inplace_n_with_buffer(I first, N n, R r, B buffer) { 25 | if (!n) return first; 26 | N half = n >> 1; 27 | if (!half) return ++first; 28 | if (n < BERT_INSERTION_SORT_CUTOFF) return insertion_sort_n(first, n, r); 29 | // if (n < BERT_INSERTION_SORT_CUTOFF) return linear_insertion_sort_n(first, n, r); 30 | // if (n < BERT_INSERTION_SORT_CUTOFF) return binary_insertion_sort_n(first, n, r); 31 | I middle = bert_sort_inplace_n_with_buffer(first, half, r, buffer); 32 | I last = bert_sort_inplace_n_with_buffer(middle, n - half, r, buffer); 33 | bert_merge_with_buffer(first, middle, last, r, buffer); 34 | return last; 35 | } 36 | 37 | template 38 | // I is ForwardIterator 39 | // R is WeakStrictOrdering on the value type of I 40 | inline 41 | void sort_bert(I first, I last, R r) { 42 | typedef typename std::iterator_traits::value_type T; 43 | typedef typename std::iterator_traits::difference_type N; 44 | N n = std::distance(first, last); 45 | std::vector buffer(n >> 1); 46 | bert_sort_inplace_n_with_buffer(first, n, r, buffer.begin()); 47 | } 48 | 49 | template 50 | // I is ForwardIterator 51 | inline 52 | void sort_bert(I first, I last) { 53 | typedef typename std::iterator_traits::value_type T; 54 | // sort_bert(first, last, std::less()); 55 | typedef typename std::iterator_traits::difference_type N; 56 | N n = std::distance(first, last); 57 | std::vector buffer(n >> 1); 58 | bert_sort_inplace_n_with_buffer(first, n, std::less(), buffer.begin()); 59 | } 60 | -------------------------------------------------------------------------------- /code/lecture20/sort_ph.h: -------------------------------------------------------------------------------- 1 | #ifndef SORT_PH_H 2 | #define SORT_PH_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "timer.h" 10 | #include "type_description.h" 11 | #include "algorithm.h" 12 | 13 | #include "merge_inplace.h" 14 | #include "merge.h" 15 | 16 | template 17 | // I is ForwardIterator 18 | // N is Integral 19 | // R is WeakStrictOrdering on the value type of I 20 | inline 21 | void ph_merge_adaptive_n(I f0, N n0, 22 | I f1, N n1, 23 | R r, B buffer, N buffer_size) { 24 | // precondition std::distance(f0, f1) == n0 25 | // precondition is_sorted_n(f0, n0, r) && is_sorted(f1, n1, r) 26 | if (!n0 || !n1) return; 27 | if (n0 <= buffer_size) { 28 | I last = f1; 29 | std::advance(last, n1); 30 | merge_with_buffer(f0, f1, last, r, buffer); 31 | return; 32 | } 33 | I f0_0, f0_1, f1_0, f1_1; 34 | N n0_0, n0_1, n1_0, n1_1; 35 | if (n0 < n1) merge_inplace_left_subproblem(f0, n0, 36 | f1, n1, 37 | f0_0, n0_0, 38 | f0_1, n0_1, 39 | f1_0, n1_0, 40 | f1_1, n1_1, 41 | r); 42 | else merge_inplace_right_subproblem(f0, n0, 43 | f1, n1, 44 | f0_0, n0_0, 45 | f0_1, n0_1, 46 | f1_0, n1_0, 47 | f1_1, n1_1, 48 | r); 49 | 50 | ph_merge_adaptive_n(f0_0, n0_0, f0_1, n0_1, r, buffer, buffer_size); 51 | ph_merge_adaptive_n(f1_0, n1_0, f1_1, n1_1, r, buffer, buffer_size); 52 | } 53 | 54 | template 55 | // I is BidirectionalIterator 56 | // R is WeakStrictOrdering on the value type of I 57 | inline 58 | I ph_insertion_sort_n(I first, N n, R r) { 59 | I last = first + n; 60 | if (first == last) return last; 61 | I current = first; 62 | ++current; 63 | if (current == last) return last; 64 | // create a sentinel 65 | I min = std::min_element(first, last, r); 66 | rotate_right_by_one(first, ++min); 67 | insertion_sort_suffix(current, last, r); 68 | return last; 69 | } 70 | 71 | const size_t PH_INSERTION_SORT_CUTOFF = 64; 72 | 73 | template 74 | // I is ForwardIterator 75 | // N is Integral 76 | // R is WeakStrictOrdering on the value type of I 77 | inline 78 | I ph_sort_adaptive_n(I first, N n, R r, B buffer, N buffer_size) { 79 | if (!n) return first; 80 | if (n < PH_INSERTION_SORT_CUTOFF) return ph_insertion_sort_n(first, n, r); 81 | N half = n >> 1; 82 | if (!half) return ++first; 83 | I middle = ph_sort_adaptive_n(first, half, r, buffer, buffer_size); 84 | I last = ph_sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 85 | ph_merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 86 | return last; 87 | } 88 | 89 | template 90 | inline 91 | void sort_ph(I first, I last) { 92 | typedef typename std::iterator_traits::value_type T; 93 | typedef typename std::iterator_traits::difference_type N; 94 | N n = std::distance(first, last); 95 | // n : 117 96 | // n >> 1: 115 97 | // n >> 2: 122 98 | // n >> 3: 130 99 | std::vector buffer(n >> 1); 100 | ph_sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 101 | } 102 | 103 | #endif 104 | 105 | 106 | -------------------------------------------------------------------------------- /code/lecture20/sort_rjernst.h: -------------------------------------------------------------------------------- 1 | #ifndef SORT_RJERNST_H 2 | #define SORT_RJERNST_H 3 | 4 | #include 5 | #include 6 | #include "algorithm.h" 7 | #include "search.h" 8 | #include "merge.h" 9 | #include "merge_inplace.h" 10 | #include "insertion_sort.h" 11 | 12 | const size_t RJERNST_INSERTION_SORT_CUTOFF = 64; 13 | 14 | template 15 | // I is ForwardIterator 16 | // N is Integral 17 | // R is WeakStrictOrdering on the value type of I 18 | I rjernst_sort_adaptive_n(I first, N n, R r, B buffer, N buffer_size) { 19 | if (!n) return first; 20 | if (n < RJERNST_INSERTION_SORT_CUTOFF) return insertion_sort_n(first, n, r); 21 | N half = n >> 1; 22 | if (!half) return ++first; 23 | I middle = rjernst_sort_adaptive_n(first, half, r, buffer, buffer_size); 24 | I last = rjernst_sort_adaptive_n(middle, n - half, r, buffer, buffer_size); 25 | merge_adaptive_n(first, half, middle, n - half, r, buffer, buffer_size); 26 | return last; 27 | } 28 | 29 | template 30 | // I is ForwardIterator 31 | inline 32 | void sort_rjernst(I first, I last) { 33 | typedef typename std::iterator_traits::value_type T; 34 | typedef typename std::iterator_traits::difference_type N; 35 | N n = std::distance(first, last); 36 | 37 | if (n < RJERNST_INSERTION_SORT_CUTOFF) { 38 | insertion_sort(first, last, std::less()); 39 | } else { 40 | std::vector buffer(n >> 2); 41 | rjernst_sort_adaptive_n(first, n, std::less(), buffer.begin(), N(buffer.size())); 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /code/lecture20/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | void print_range(I first, I last) { 5 | while (first != last) { 6 | std::cout << *first << " "; 7 | ++first; 8 | } 9 | std::cout << std::endl; 10 | } 11 | 12 | template 13 | // I is ForwardIterator 14 | void rotate_right_by_one(I first, I last) { 15 | if (first == last) return; 16 | I current = first; 17 | while (++current != last) std::swap(*first, *current); 18 | } 19 | 20 | int main () { 21 | int a[] = {1, 2, 3, 4}; 22 | print_range(a, a + 4); 23 | rotate_right_by_one(a, a + 4); 24 | print_range(a, a + 4); 25 | } 26 | -------------------------------------------------------------------------------- /code/lecture20/test_insertion_sort.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "insertion_sort.h" 4 | #include "algorithm.h" 5 | 6 | 7 | int main() { 8 | int nums[] = {5, 3, 10, 1, 2}; 9 | print_range(nums, nums + 5); 10 | insertion_sort_n(nums, 5, std::less()); 11 | print_range(nums, nums + 5); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /code/lecture20/test_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test_sort.h" 4 | #include "algorithm.h" 5 | 6 | const size_t min_size(8); 7 | const size_t max_size(2 * 1024 * 1024); 8 | 9 | int main() { 10 | test_sort(min_size, max_size, random_iota); 11 | } 12 | -------------------------------------------------------------------------------- /code/lecture20/test_sort.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_SORT_H 2 | #define TEST_SORT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "timer.h" 10 | #include "type_description.h" 11 | #include "algorithm.h" 12 | 13 | #include "merge_inplace.h" 14 | #include "merge.h" 15 | #include "sort_ph.h" 16 | #include "sort_akraft.h" 17 | #include "sort_bert.h" 18 | #include "sort_rjernst.h" 19 | 20 | template 21 | // requires T is TotallyOrdered 22 | double time_sort(T* first, T* last, void (*sort)(T*, T*), T* buffer, size_t count) { 23 | timer t; 24 | t.start(); 25 | size_t i = count; 26 | while (i--) { 27 | std::copy(buffer, buffer + std::distance(first, last), first); 28 | sort(first, last); 29 | if (!std::is_sorted(first, last)) { 30 | std::cerr << "*** SORT FAILED! ***" << std::endl; 31 | return 0; 32 | } 33 | } 34 | return t.stop(); 35 | } 36 | 37 | template 38 | // requires T is TotallyOrdered 39 | // requires G is a Generator 40 | void test_sort(size_t min_size, size_t max_size, G gen) { 41 | 42 | time_t now = time(0); 43 | 44 | std::cout << "Sorting " << type_description(T(0)) 45 | << " from " << min_size << " up to " << max_size 46 | << " elements" << " generated with " << function_name(gen) 47 | <<" at: " << asctime(localtime(&now)); 48 | 49 | 50 | void (*f_pointers[])(T*, T*) = 51 | { 52 | std::stable_sort 53 | ,sort_inplace_with_buffer 54 | ,sort_1_64th 55 | ,sort_ph 56 | ,sort_akraft 57 | ,sort_bert 58 | ,sort_rjernst 59 | }; 60 | 61 | size_t number_of_sorts = sizeof(f_pointers) / sizeof(f_pointers[0]); 62 | 63 | int colwidth = 10; 64 | 65 | std::cout << std::right << std::setw(12) << " size" 66 | << std::setw(colwidth) << "stable" 67 | << std::setw(colwidth) << "merge" 68 | << std::setw(colwidth) << "1_64th" 69 | << std::setw(colwidth) << "ph" 70 | << std::setw(colwidth) << "akraft" 71 | << std::setw(colwidth) << "bert" 72 | << std::setw(colwidth) << "rjernst" 73 | << std::endl; 74 | 75 | for (size_t array_size(min_size); array_size <= max_size; array_size *= 2) { 76 | const size_t n = max_size / array_size; 77 | std::vector vec(array_size); 78 | gen(&*vec.begin(), (&*vec.begin()) + vec.size()); 79 | std::vector tmp(vec.begin(), vec.end()); 80 | std::cout << std::setw(12) << array_size; 81 | for (size_t i = 0; i < number_of_sorts; ++i) { 82 | size_t bound = max_size; 83 | double time = time_sort(&*tmp.begin(), (&*tmp.begin()) + tmp.size(), f_pointers[i], &*vec.begin(), n); 84 | time /= double(array_size * n); 85 | std::cout << std::setw(colwidth) << std::fixed << std::setprecision(0) << time; 86 | } 87 | std::cout << std::endl; 88 | } 89 | } 90 | 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /code/lecture20/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include 5 | 6 | class timer { 7 | private: 8 | clock_t start_time; 9 | public: 10 | typedef double result_type; 11 | 12 | void start() { 13 | start_time = clock(); 14 | } 15 | 16 | result_type stop() { 17 | return 1000000000. * ((clock() - start_time) / double(CLOCKS_PER_SEC)); 18 | } 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /code/lecture20/type_description.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPE_DESCRIPTION_H 2 | #define TYPE_DESCRIPTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::string type_description(double) { return std::string("double"); } 10 | std::string type_description(long double) { return std::string("long double"); } 11 | std::string type_description(float) { return std::string("float"); } 12 | std::string type_description(uint8_t) { return std::string("uint8_t"); } 13 | std::string type_description(uint16_t) { return std::string("uint16_t"); } 14 | std::string type_description(uint32_t) { return std::string("uint32_t"); } 15 | std::string type_description(uint64_t) { return std::string("uint64_t"); } 16 | std::string type_description(int8_t) { return std::string("int8_t"); } 17 | std::string type_description(int16_t) { return std::string("int16_t"); } 18 | std::string type_description(int32_t) { return std::string("int32_t"); } 19 | std::string type_description(int64_t) { return std::string("int64_t"); } 20 | 21 | template 22 | std::string type_description(const std::vector&) { return std::string("vector<") + type_description(T()) + std::string(">"); } 23 | 24 | template 25 | std::string type_description(const std::deque&) { return std::string("deque<") + type_description(T()) + std::string(">"); } 26 | 27 | template 28 | std::string type_description(const std::list&) { return std::string("list<") + type_description(T()) + std::string(">"); } 29 | #endif 30 | -------------------------------------------------------------------------------- /code/lecture3/concepts.h: -------------------------------------------------------------------------------- 1 | #ifndef CONCEPTS_H 2 | #define CONCEPTS_H 3 | 4 | #define TotallyOrdered typename 5 | #define Pointer typename 6 | #define Number typename 7 | #define Unsigned typename 8 | #define Integral typename 9 | #define InputIterator typename 10 | #define OutputIterator typename 11 | #define ForwardIterator typename 12 | #define BidirectionalIterator typename 13 | #define RandomAccessIterator typename 14 | #define Sorter typename 15 | #define Incrementable typename 16 | #define StrictWeakOrdering typename 17 | #define Generator typename 18 | #define BinaryOperation typename 19 | #define Sequence typename 20 | 21 | #define ValueType(I) typename std::iterator_traits::value_type 22 | #define DifferenceType(I) typename std::iterator_traits::difference_type 23 | #define IteratorCategory(I) typename std::iterator_traits::iterator_category 24 | 25 | struct less { 26 | template 27 | bool operator()(const T& x, const T& y) { 28 | return x < y; 29 | } 30 | }; 31 | 32 | template 33 | inline 34 | I successor(I x) { return ++x; } 35 | 36 | template 37 | inline 38 | I predecessor(I x) { return ++x; } 39 | 40 | template 41 | inline 42 | I successor(I x, N n, std::random_access_iterator_tag) { 43 | return x + n; 44 | } 45 | 46 | template 47 | inline 48 | I successor(I x, N n, std::input_iterator_tag) { 49 | while (n != N(0)) { 50 | ++x; 51 | --n; 52 | } 53 | return x; 54 | } 55 | 56 | template 57 | inline 58 | I successor(I x, N n) { 59 | return successor(x, n, IteratorCategory(I)()); 60 | } 61 | 62 | template 63 | inline 64 | I successor_guarded(I first, I last, N n, std::random_access_iterator_tag) { 65 | return first + std::min(n, last - first); 66 | } 67 | 68 | template 69 | inline 70 | I successor_guarded(I first, I last, N n, std::input_iterator_tag) { 71 | while (n != N(0) && first != last) { 72 | ++first; 73 | --n; 74 | } 75 | return first; 76 | } 77 | 78 | template 79 | inline 80 | I successor_guarded(I first, I last, N n) { 81 | return successor_guarded(first, last, n, IteratorCategory(I)()); 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /code/lecture3/count_operations.cpp: -------------------------------------------------------------------------------- 1 | #include "count_operations.h" 2 | #include "functorized.h" 3 | 4 | int main() { 5 | count_operations(16, 16 * 1024 * 1024, heap_sort_functor()); 6 | } 7 | -------------------------------------------------------------------------------- /code/lecture3/count_operations.h: -------------------------------------------------------------------------------- 1 | #ifndef COUNT_OPERATIONS_H 2 | #define COUNT_OPERATIONS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "iota.h" 9 | #include "instrumented.h" 10 | #include "table_util.h" 11 | 12 | double normalized_by_n(double x, double n) { return x / n; } 13 | double normalized_by_nlogn(double x, double n) { 14 | return x / (n * (log(n) / log(2))); 15 | } 16 | double normalized_by_nlogn1(double x, double n) { 17 | return x / (n * log(n) - n); 18 | } 19 | double dont_normalize(double x, double) { return x; } 20 | 21 | template 22 | void count_operations(size_t i, size_t j, Function fun, double (*norm)(double, double) = dont_normalize) { 23 | // measure operations on an interval of a given length 24 | // ranging from i to j and going through i, 2i, 4i, ... up to and including j 25 | 26 | size_t cols = instrumented::number_ops; 27 | 28 | size_t decimals[cols]; 29 | size_t normalized((norm == dont_normalize) ? 0 : 2); 30 | *decimals = 0; 31 | std::fill(decimals + 1, decimals + cols, normalized); 32 | 33 | table_util table; 34 | table.print_headers(instrumented::counter_names, instrumented::number_ops, 12); 35 | 36 | while (i <= j) { 37 | 38 | std::vector > vec(i); 39 | course::iota(vec.begin(), vec.end(), 0.0); 40 | std::random_shuffle(vec.begin(), vec.end()); 41 | 42 | instrumented::initialize(i); 43 | fun(vec.begin(), vec.end()); 44 | 45 | double* count_p = instrumented::counts; 46 | 47 | for (size_t k(1); k < cols; ++k) count_p[k] = norm(count_p[k], count_p[0]); 48 | 49 | table.print_row(count_p, decimals); 50 | 51 | i <<= 1; 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /code/lecture3/count_operations_normalized.cpp: -------------------------------------------------------------------------------- 1 | #include "count_operations.h" 2 | #include "functorized.h" 3 | 4 | int main() { 5 | count_operations(16, 16 * 1028 * 1028, heap_sort_functor(), 6 | normalized_by_nlogn1); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /code/lecture3/functorized.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTORIZED_H 2 | #define FUNCTORIZED_H 3 | 4 | #include 5 | 6 | #include "setsort.h" 7 | 8 | /* 9 | strict functorization: 10 | 11 | template 12 | R function_name(args) { code } 13 | 14 | struct function_name_functor 15 | { 16 | template 17 | R operator()(args) const { code } 18 | }; 19 | 20 | partial functorization: 21 | 22 | template 23 | R function_name(args) { code } 24 | 25 | template // could be done for more than 1 type argument 26 | struct function_name_functor 27 | { 28 | template // does not include Ti 29 | R operator()(args) const { code } 30 | }; 31 | 32 | */ 33 | 34 | template 35 | // multiplicative semigroup 36 | inline 37 | T square(const T& x) { return x * x; } 38 | 39 | struct square_functor { 40 | template 41 | T operator()(const T& x) const { 42 | return square(x); 43 | } 44 | }; 45 | 46 | struct sort_functor 47 | { 48 | template 49 | // I is random-access iterator 50 | void operator()(I first, I last) const { 51 | std::sort(first, last); 52 | } 53 | }; 54 | 55 | struct sort_unique_functor 56 | { 57 | template 58 | // I is random-access iterator 59 | I operator()(I first, I last) const { 60 | std::sort(first, last); 61 | return std::unique(first, last); 62 | } 63 | }; 64 | 65 | struct setsort_functor 66 | { 67 | template 68 | // I is forward iterator 69 | void operator()(I first, I last) const { 70 | setsort(first, last); 71 | } 72 | }; 73 | 74 | struct setsort_unique_functor 75 | { 76 | template 77 | // I is forward iterator 78 | I operator()(I first, I last) const { 79 | return setsort_unique(first, last); 80 | } 81 | }; 82 | 83 | 84 | struct stable_sort_functor 85 | { 86 | template 87 | // I is random-access iterator 88 | void operator()(I first, I last) const { std::stable_sort(first, last); } 89 | }; 90 | 91 | struct heap_sort_functor 92 | { 93 | template 94 | // I is random-access iterator 95 | void operator()(I first, I last) const { std::partial_sort(first, last, last); } 96 | }; 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /code/lecture3/instrumented.cpp: -------------------------------------------------------------------------------- 1 | #include "instrumented.h" 2 | #include 3 | 4 | double instrumented_base::counts[]; 5 | const char* instrumented_base::counter_names[number_ops] = {"n", "copy", "assign", "destruct", "default", "equal", "less", "construct"}; 6 | void instrumented_base::initialize(size_t m) { 7 | std::fill(counts, counts + number_ops, 0.0); 8 | counts[n] = double(m); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /code/lecture3/instrumented.h: -------------------------------------------------------------------------------- 1 | #ifndef INSTRUMENTED_H 2 | #define INSTRUMENTED_H 3 | 4 | #include 5 | 6 | struct instrumented_base 7 | { 8 | enum operations { 9 | n, copy, assignment, destructor, default_constructor, equality, comparison, construction 10 | }; 11 | static const size_t number_ops = 8; 12 | static double counts[number_ops]; 13 | static const char* counter_names[number_ops]; 14 | static void initialize(size_t); 15 | }; 16 | 17 | 18 | template 19 | // T is Semiregualr or Regular or TotallyOrdered 20 | struct instrumented : instrumented_base 21 | { 22 | typedef T value_type; 23 | T value; 24 | // Conversions from T and to T: 25 | explicit instrumented(const T& x) : value(x) { ++counts[construction]; } 26 | 27 | 28 | // Semiregular: 29 | instrumented(const instrumented& x) : value(x.value) { 30 | ++counts[copy]; 31 | } 32 | instrumented() { ++counts[default_constructor]; } 33 | ~instrumented() { ++counts[destructor]; } 34 | instrumented& operator=(const instrumented& x) { 35 | ++counts[assignment]; 36 | value = x.value; 37 | return *this; 38 | } 39 | // Regular 40 | friend 41 | bool operator==(const instrumented& x, const instrumented& y) { 42 | ++counts[equality]; 43 | return x.value == y.value; 44 | } 45 | friend 46 | bool operator!=(const instrumented& x, const instrumented& y) { 47 | return !(x == y); 48 | } 49 | // TotallyOrdered 50 | friend 51 | bool operator<(const instrumented& x, const instrumented& y) { 52 | ++counts[comparison]; 53 | return x.value < y.value; 54 | } 55 | friend 56 | bool operator>(const instrumented& x, const instrumented& y) { 57 | return y < x; 58 | } 59 | friend 60 | bool operator<=(const instrumented& x, const instrumented& y) { 61 | return !(y < x); 62 | } 63 | friend 64 | bool operator>=(const instrumented& x, const instrumented& y) { 65 | return !(x < y); 66 | } 67 | 68 | 69 | }; 70 | 71 | #endif 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /code/lecture3/iota.h: -------------------------------------------------------------------------------- 1 | #ifndef IOTA_H 2 | #define IOTA_H 3 | 4 | #include 5 | 6 | namespace course { 7 | 8 | template 9 | N iota(I first, I last, N start = N(0), N step = N(1)) { 10 | typedef typename std::iterator_traits::value_type T; 11 | while (first != last) { 12 | *first = T(start); 13 | start += step; 14 | ++first; 15 | } 16 | return start; 17 | } 18 | 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /code/lecture3/setsort.h: -------------------------------------------------------------------------------- 1 | #ifndef SETSORT_H 2 | #define SETSORT_H 3 | 4 | #include 5 | #include 6 | #include "concepts.h" 7 | 8 | template 9 | // I is forward iterator 10 | void setsort(I first, I last) { 11 | std::multiset tmp(first, last); 12 | std::copy(tmp.begin(), tmp.end(), first); 13 | } 14 | 15 | template 16 | // I is forward iterator 17 | I setsort_unique(I first, I last) { 18 | std::set tmp(first, last); 19 | return std::copy(tmp.begin(), tmp.end(), 20 | first); 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /code/lecture3/singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLETON_H 2 | #define SINGLETON_H 3 | 4 | template 5 | // T is Semiregualr or Regular or TotallyOrdered 6 | struct singleton 7 | { 8 | T value; 9 | 10 | // Semiregular: 11 | singleton(const singleton& x) : value(x.value) {} // could be implicitly declared 12 | singleton() {} // could be implicitly declared sometimes 13 | ~singleton() {} // could be implicitly declared 14 | singleton& operator=(const singleton& x) { // could be implicitly declared 15 | value = x.value; 16 | return *this; 17 | } 18 | // Regular 19 | friend 20 | bool operator==(const singleton& x, const singleton& y) { 21 | return x.value == y.value; 22 | } 23 | friend 24 | bool operator!=(const singleton& x, const singleton& y) { 25 | return !(x == y); 26 | } 27 | // TotallyOrdered 28 | friend 29 | bool operator<(const singleton& x, const singleton& y) { 30 | return x.value < y.value; 31 | } 32 | friend 33 | bool operator>(const singleton& x, const singleton& y) { 34 | return y < x; 35 | } 36 | friend 37 | bool operator<=(const singleton& x, const singleton& y) { 38 | return !(y < x); 39 | } 40 | friend 41 | bool operator>=(const singleton& x, const singleton& y) { 42 | return !(x < y); 43 | } 44 | 45 | // Conversions from T and to T: 46 | // Homework - 47 | // Write conversions from T to singleton and singleton to T. 48 | 49 | }; 50 | 51 | #endif 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /code/lecture4/example.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | template 5 | // requires T is Regular 6 | void check_equality1(T& a) { 7 | std::cout << a << " == " << a << std::endl; 8 | std::cout << (a == a ? "yes" : "no") << std::endl; 9 | } 10 | 11 | template 12 | // requires T is Regular 13 | void check_equality2(T& a) { 14 | std::cout << a << " < " << a << " || " << a << " >= " << a << std::endl; 15 | std::cout << (a < a || a >= a? "yes" : "no") << std::endl; 16 | } 17 | 18 | int main() { 19 | double x(0.0/0.0); 20 | check_equality1(x); 21 | check_equality2(x); 22 | } 23 | -------------------------------------------------------------------------------- /code/lecture4/min.h: -------------------------------------------------------------------------------- 1 | 2 | template 3 | // requires Compare defines a StrictWeakOrdering on T 4 | inline 5 | const T& min(const T& a, const T& b, Compare cmp) { 6 | if (cmp(b, a)) { 7 | return b; 8 | } else { 9 | return a; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /code/lecture4/swap.h: -------------------------------------------------------------------------------- 1 | 2 | template 3 | // requires T is Semiregular 4 | inline 5 | void swap(T& a, T& b) { 6 | T tmp(a); 7 | a = b; 8 | b = tmp; 9 | } 10 | 11 | // partial specialization (won't actually work here, needs to be friend of vector) 12 | template 13 | inline 14 | void swap(std::vector& a, std::vector& b) { 15 | // swap headers of a and b 16 | // fix back pointers (if they exist) 17 | } 18 | 19 | /* 20 | std::vector a(1000000); 21 | std::vector b(1000000); 22 | swap(a, b); 23 | */ 24 | 25 | template 26 | // requires T is UnsignedIntegral 27 | inline 28 | void swap_xor(T& a, T& b) { 29 | if (&a != &b) { 30 | a = a ^ b; // a ^ b, b 31 | b = a ^ b; // a ^ b, a 32 | a = a ^ b; // b, a 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /code/lecture5/algorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGORITHM_H 2 | #define ALGORITHM_H 3 | 4 | #include 5 | 6 | #include "concepts.h" 7 | 8 | namespace course { 9 | 10 | template 11 | O copy_n(I first, N n, O result) { // the function should really return pair, but the new standard decides otherwise 12 | while (n--) *result++ = *first++; 13 | return result; 14 | } 15 | 16 | template 17 | // I is forward iterator 18 | // C is strict weak ordering on value type of I 19 | bool is_sorted(I first, I last, C comp) { 20 | if (first == last) return true; 21 | I next = first; 22 | while (++next != last) { 23 | if (comp(*next, *first)) return false; 24 | ++first; 25 | } 26 | return true; 27 | } 28 | 29 | template 30 | // I is forward iterator 31 | // value type of I is totally ordered 32 | inline 33 | bool is_sorted(I first, I last) { 34 | std::less::value_type> comp; 35 | return is_sorted(first, last, comp); 36 | } 37 | 38 | template 39 | void print_range(I first, I last, const char* msg = NULL) { 40 | if (msg) std::cout << msg << ": "; 41 | while (first != last) std::cout << *first++ << " "; 42 | std::cout << std::endl; 43 | } 44 | 45 | template 46 | N iota(I first, I last, N start = N(0), N step = N(1)) { 47 | typedef typename std::iterator_traits::value_type T; 48 | while (first != last) { 49 | *first = T(start); 50 | start += step; 51 | ++first; 52 | } 53 | return start; 54 | } 55 | 56 | template 57 | I iota_n(I first, N n, N start = N(0), N step = N(1)) { 58 | while (n-- > 0) { 59 | *first = start; 60 | start += step; 61 | ++first; 62 | } 63 | return first; 64 | } 65 | 66 | 67 | template 68 | inline 69 | void constant(I first, I last) { 70 | std::fill(first, last, ValueType(I)(0)); 71 | } 72 | 73 | 74 | template 75 | inline 76 | void iota(I first, I last) { 77 | iota(first, last, ValueType(I)(0), ValueType(I)(1)); 78 | } 79 | 80 | template 81 | // I is forward iterator, N is integral 82 | void segmented_iota(I first, I last, N segment_size) { 83 | typedef typename std::iterator_traits::value_type T; 84 | while (last - first >= segment_size) { 85 | first = course::iota_n(first, segment_size, N(0)); 86 | } 87 | course::iota(first, last, N(0)); 88 | } 89 | 90 | template 91 | inline 92 | void reverse_iota(I first, I last) { 93 | iota(first, last); 94 | std::reverse(first, last); 95 | } 96 | 97 | template 98 | void random_iota(I first, I last) { 99 | iota(first, last); 100 | std::random_shuffle(first, last); 101 | } 102 | 103 | template 104 | void hill(I first, I last) 105 | { 106 | I middle = successor(first, std::distance(first, last)/2); 107 | iota(first, middle); 108 | reverse_iota(middle, last); 109 | } 110 | 111 | template 112 | void valley(I first, I last) 113 | { 114 | I middle = successor(first, std::distance(first, last)/2); 115 | reverse_iota(first, middle); 116 | iota(middle, last); 117 | } 118 | 119 | template 120 | std::pair 121 | repeating_iota(I first, I last, N start = N(0), N step = N(1), N repetitions = N(1)) { 122 | // assert(repetitions > 0) 123 | N i = repetitions; 124 | while (first != last) { 125 | *first++ = start; 126 | if (--i == 0) { 127 | i = repetitions; 128 | start += step; 129 | } 130 | } 131 | return std::pair(start, i); 132 | } 133 | 134 | template 135 | inline 136 | const char* function_name(void (*gen)(I, I)) { 137 | void (*generator[])(I, I) = 138 | { 139 | iota, 140 | hill, 141 | valley, 142 | reverse_iota, 143 | random_iota 144 | }; 145 | const char* names[] = 146 | { 147 | "iota", 148 | "hill", 149 | "valley", 150 | "reverse_iota", 151 | "random_iota" 152 | }; 153 | size_t number_of_generators = sizeof(generator)/sizeof(generator[0]); 154 | size_t index = std::find(generator, generator + number_of_generators, gen) - generator; 155 | if (index >= number_of_generators) return "unknown data generation "; 156 | return names[index]; 157 | } 158 | } // end of namespace course 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /code/lecture5/concepts.h: -------------------------------------------------------------------------------- 1 | #ifndef CONCEPTS_H 2 | #define CONCEPTS_H 3 | 4 | #define TotallyOrdered typename 5 | #define Pointer typename 6 | #define Number typename 7 | #define Unsigned typename 8 | #define Integral typename 9 | #define InputIterator typename 10 | #define OutputIterator typename 11 | #define ForwardIterator typename 12 | #define BidirectionalIterator typename 13 | #define RandomAccessIterator typename 14 | #define Sorter typename 15 | #define Incrementable typename 16 | #define StrictWeakOrdering typename 17 | #define Generator typename 18 | #define BinaryOperation typename 19 | #define Sequence typename 20 | 21 | #define ValueType(I) typename std::iterator_traits::value_type 22 | #define DifferenceType(I) typename std::iterator_traits::difference_type 23 | #define IteratorCategory(I) typename std::iterator_traits::iterator_category 24 | 25 | struct less { 26 | template 27 | bool operator()(const T& x, const T& y) { 28 | return x < y; 29 | } 30 | }; 31 | 32 | template 33 | inline 34 | I successor(I x) { return ++x; } 35 | 36 | template 37 | inline 38 | I predecessor(I x) { return ++x; } 39 | 40 | template 41 | inline 42 | I successor(I x, N n, std::random_access_iterator_tag) { 43 | return x + n; 44 | } 45 | 46 | template 47 | inline 48 | I successor(I x, N n, std::input_iterator_tag) { 49 | while (n != N(0)) { 50 | ++x; 51 | --n; 52 | } 53 | return x; 54 | } 55 | 56 | template 57 | inline 58 | I successor(I x, N n) { 59 | return successor(x, n, IteratorCategory(I)()); 60 | } 61 | 62 | template 63 | inline 64 | I successor_guarded(I first, I last, N n, std::random_access_iterator_tag) { 65 | return first + std::min(n, last - first); 66 | } 67 | 68 | template 69 | inline 70 | I successor_guarded(I first, I last, N n, std::input_iterator_tag) { 71 | while (n != N(0) && first != last) { 72 | ++first; 73 | --n; 74 | } 75 | return first; 76 | } 77 | 78 | template 79 | inline 80 | I successor_guarded(I first, I last, N n) { 81 | return successor_guarded(first, last, n, IteratorCategory(I)()); 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /code/lecture5/timed.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | * Utilities for timing functions 3 | *----------------------------------------------------------------------------*/ 4 | 5 | #ifndef TIMED_H 6 | #define TIMED_H 7 | 8 | #include 9 | #include 10 | 11 | class timer { 12 | private: 13 | clock_t start_time; 14 | public: 15 | typedef double result_type; 16 | 17 | void start() { 18 | start_time = clock(); 19 | } 20 | 21 | result_type seconds() { 22 | return (clock() - start_time) / double(CLOCKS_PER_SEC); 23 | } 24 | 25 | result_type nanoseconds() { 26 | return 1000000000.0 * seconds(); 27 | } 28 | }; 29 | 30 | 31 | // Function object for applying unary function to all elements of a range. 32 | 33 | template 34 | struct transform_all { 35 | Function fun; 36 | 37 | transform_all() {}; 38 | transform_all(const Function& fun) : fun(fun) {}; 39 | 40 | template // I is forward iterator 41 | void operator()(I first, I last) { std::transform(first, last, first, fun); } 42 | }; 43 | 44 | // Copy data from [source_first, source_last), then 45 | // apply function fun to buffer. Repeat iters times. 46 | // Subtracts time to copy from total time 47 | 48 | template