Add 'ods/' from commit 'cbb1f78f27b88978de401a0f9d90fbbd396c001f'
git-subtree-dir: ods git-subtree-mainline:97402604dfgit-subtree-split:cbb1f78f27
This commit is contained in:
27
ods/src/Makefile.am
Normal file
27
ods/src/Makefile.am
Normal file
@@ -0,0 +1,27 @@
|
||||
AM_CPPFLAGS = -Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align
|
||||
AM_CPPFLAGS += -Wwrite-strings -Wmissing-declarations -Wno-long-long -Werror
|
||||
AM_CPPFLAGS += -Wunused-variable -std=c++17 -D_XOPEN_SOURCE -O0 -g -I.
|
||||
AM_CPPFLAGS += -fno-elide-constructors -Weffc++ -fPIC
|
||||
|
||||
bin_PROGRAMS := ch01ex01 ch01ex03 ch01ex04 ch01ex05 ch01ex06 \
|
||||
list_bench uset_bench sset_bench
|
||||
ch01ex01_SOURCES := ch01ex01.cc
|
||||
ch01ex03_SOURCES := ch01ex03.cc
|
||||
ch01ex04_SOURCES := ch01ex04.cc
|
||||
ch01ex05_SOURCES := ch01ex05.cc
|
||||
ch01ex06_SOURCES := ch01ex06.cc
|
||||
list_bench_SOURCES := list_bench.cc
|
||||
uset_bench_SOURCES := uset_bench.cc
|
||||
sset_bench_SOURCES := sset_bench.cc
|
||||
|
||||
BENCH_OPS ?= 1000000
|
||||
|
||||
benchmarks: list_bench uset_bench sset_bench
|
||||
@echo "LIST BENCHMARKS"
|
||||
@./list_bench $(BENCH_OPS) > /dev/null
|
||||
@echo
|
||||
@echo "USET BENCHMARKS"
|
||||
@./uset_bench $(BENCH_OPS) > /dev/null
|
||||
@echo
|
||||
@echo "SSET BENCHMARKS"
|
||||
@./sset_bench $(BENCH_OPS) > /dev/null
|
||||
234
ods/src/ch01ex01.cc
Normal file
234
ods/src/ch01ex01.cc
Normal file
@@ -0,0 +1,234 @@
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Solve the following problems by reading a text file one line at a time
|
||||
// and performing operations on each line in the appropriate data structure(s).
|
||||
// Your implementations should be fast enough that even files containing
|
||||
// a million lines can be processed in a few seconds.
|
||||
|
||||
|
||||
// Read the input one line at a time and then write the lines out in
|
||||
// reverse order, so that the last input line is printed first, then the
|
||||
// second last input line, and so on.
|
||||
static void
|
||||
problem1(const char *path)
|
||||
{
|
||||
ifstream ifs(path);
|
||||
string line;
|
||||
stack<string> s;
|
||||
|
||||
if (!ifs) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (getline(ifs, line)) {
|
||||
s.push(line);
|
||||
}
|
||||
|
||||
while (!s.empty()) {
|
||||
cout << s.top() << endl;
|
||||
s.pop();
|
||||
}
|
||||
|
||||
ifs.close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Read the first 50 lines of input and then write them out in reverse
|
||||
// order. Read the next 50 lines and then write them out in reverse
|
||||
// order. Do this until there are no more lines left to read, at which
|
||||
// point any remaining lines should be output in reverse order.
|
||||
//
|
||||
// In other words, your output will start with the 50th line, then the
|
||||
// 49th, then the 48th, and so on down to the first line. This will be
|
||||
// followed by the 100th line, followed by the 99th, and so on down to
|
||||
// the 51st line. And so on.
|
||||
//
|
||||
// Your code should never have to store more than 50 lines at any given
|
||||
// time.
|
||||
static void
|
||||
problem2(const char *path)
|
||||
{
|
||||
ifstream ifs(path);
|
||||
string line;
|
||||
stack<string> s;
|
||||
bool stop = false;
|
||||
|
||||
if (!ifs) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!stop) {
|
||||
while (s.size() < 50) {
|
||||
if (getline(ifs, line)) {
|
||||
s.push(line);
|
||||
} else {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (!s.empty()) {
|
||||
cout << s.top() << endl;
|
||||
s.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read the input one line at a time. At any point after reading the
|
||||
// first 42 lines, if some line is blank (i.e., a string of length 0), then
|
||||
// output the line that occured 42 lines prior to that one. For example,
|
||||
// if Line 242 is blank, then your program should output line 200.
|
||||
// This program should be implemented so that it never stores more
|
||||
// than 43 lines of the input at any given time.
|
||||
static void
|
||||
problem3(const char *path)
|
||||
{
|
||||
ifstream ifs(path);
|
||||
string line;
|
||||
deque<string> q;
|
||||
|
||||
if (!ifs) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (getline(ifs, line)) {
|
||||
if (q.size() > 42) {
|
||||
q.pop_back();
|
||||
}
|
||||
q.push_front(line);
|
||||
|
||||
if (line == "") {
|
||||
cout << q.back() << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the input one line at a time and write each line to the output
|
||||
// if it is not a duplicate of some previous input line. Take special care
|
||||
// so that a file with a lot of duplicate lines does not use more memory
|
||||
// than what is required for the number of unique lines.
|
||||
static void
|
||||
problem4(const char *path)
|
||||
{
|
||||
ifstream ifs(path);
|
||||
string line;
|
||||
set<string> set;
|
||||
|
||||
if (!ifs) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (getline(ifs, line)) {
|
||||
if (set.count(line) == 0) {
|
||||
cout << line << endl;
|
||||
set.insert(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read the input one line at a time and write each line to the output
|
||||
// only if you have already read this line before. (The end result is that
|
||||
// you remove the first occurrence of each line.) Take special care so
|
||||
// that a file with a lot of duplicate lines does not use more memory
|
||||
// than what is required for the number of unique lines.
|
||||
static void
|
||||
problem5(const char *path)
|
||||
{
|
||||
ifstream ifs(path);
|
||||
string line;
|
||||
set<string> set;
|
||||
|
||||
if (!ifs) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (getline(ifs, line)) {
|
||||
if (set.count(line) == 1) {
|
||||
cout << line << endl;
|
||||
} else {
|
||||
set.insert(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// main should just execute the problems in sequence.
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int problem = -1;
|
||||
|
||||
if (argc < 2) {
|
||||
cerr << "No input file specified, exiting." << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (argc == 3) {
|
||||
problem = stoi(string(argv[2]));
|
||||
}
|
||||
|
||||
if (problem > 0) {
|
||||
switch (problem) {
|
||||
case 1:
|
||||
cout << "*** PROBLEM 1 ***" << endl;
|
||||
problem1(argv[1]);
|
||||
cout << endl << endl;
|
||||
break;
|
||||
case 2:
|
||||
cout << "*** PROBLEM 2 ***" << endl;
|
||||
problem2(argv[1]);
|
||||
cout << endl << endl;
|
||||
break;
|
||||
case 3:
|
||||
cout << "*** PROBLEM 3 ***" << endl;
|
||||
problem3(argv[1]);
|
||||
cout << endl << endl;
|
||||
break;
|
||||
case 4:
|
||||
cout << "*** PROBLEM 4 ***" << endl;
|
||||
problem4(argv[1]);
|
||||
cout << endl << endl;
|
||||
break;
|
||||
case 5:
|
||||
cout << "*** PROBLEM 5 ***" << endl;
|
||||
problem5(argv[1]);
|
||||
cout << endl << endl;
|
||||
break;
|
||||
default:
|
||||
cerr << "Unknown problem " << problem << endl;
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
cout << "*** PROBLEM 1 ***" << endl;
|
||||
problem1(argv[1]);
|
||||
cout << endl << endl;
|
||||
|
||||
cout << "*** PROBLEM 2 ***" << endl;
|
||||
problem2(argv[1]);
|
||||
cout << endl << endl;
|
||||
|
||||
cout << "*** PROBLEM 3 ***" << endl;
|
||||
problem3(argv[1]);
|
||||
cout << endl << endl;
|
||||
|
||||
cout << "*** PROBLEM 4 ***" << endl;
|
||||
problem4(argv[1]);
|
||||
cout << endl << endl;
|
||||
|
||||
cout << "*** PROBLEM 5 ***" << endl;
|
||||
problem5(argv[1]);
|
||||
cout << endl << endl;
|
||||
}
|
||||
67
ods/src/ch01ex03.cc
Normal file
67
ods/src/ch01ex03.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <iostream>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
stack<char> s;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string ms(argv[i]);
|
||||
bool matched = true;
|
||||
|
||||
for (auto c : ms) {
|
||||
switch (c) {
|
||||
case '{':
|
||||
case '(':
|
||||
case '[':
|
||||
s.push(c);
|
||||
break;
|
||||
case '}':
|
||||
if (s.top() != '{') {
|
||||
cerr << "Saw '" << s.top() << "' but expected '{'." << endl;
|
||||
matched = false;
|
||||
}
|
||||
else {
|
||||
s.pop();
|
||||
}
|
||||
break;
|
||||
case ']':
|
||||
if (s.top() != '[') {
|
||||
cerr << "Saw '" << s.top() << "' but expected '['." << endl;
|
||||
matched = false;
|
||||
}
|
||||
else {
|
||||
s.pop();
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (s.top() != '(') {
|
||||
cerr << "Saw '" << s.top() << "' but expected '('." << endl;
|
||||
matched = false;
|
||||
}
|
||||
else {
|
||||
s.pop();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cerr << "Invalid character in string: " << c << endl;
|
||||
matched = false;
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
cerr << "'" << ms << "' is not a matched string." << endl;
|
||||
}
|
||||
else {
|
||||
cout << "'" << ms << "' is a matched string." << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
ods/src/ch01ex04.cc
Normal file
43
ods/src/ch01ex04.cc
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
static void
|
||||
print_queue(queue<int>& q)
|
||||
{
|
||||
while (!q.empty()) {
|
||||
cout << q.front() << " ";
|
||||
q.pop();
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
stack<int> s;
|
||||
queue<int> q;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string line(argv[i]);
|
||||
int arg = stoi(line);
|
||||
|
||||
s.push(arg);
|
||||
}
|
||||
|
||||
while (!s.empty()) {
|
||||
// NB: the pop() interface in the book returns the topmost
|
||||
// element while removing it; the C++ STL interface does
|
||||
// not, requiring the separate top() and pop() invocations.
|
||||
int arg = s.top();
|
||||
s.pop();
|
||||
q.push(arg);
|
||||
}
|
||||
|
||||
print_queue(q);
|
||||
}
|
||||
22
ods/src/ch01ex05.cc
Normal file
22
ods/src/ch01ex05.cc
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
// Using a USet, implement a Bag. A Bag is like a USet — it supports the add(x),
|
||||
// remove(x), and find (x) methods — but it allows duplicate elements to be
|
||||
// stored. The find(x) operation in a Bag returns some element (if any) that is
|
||||
// equal to x. In addition, a Bag supports the findAll(x) operation that returns
|
||||
// a list of all elements in the Bag that are equal to x.
|
||||
|
||||
// template<typename T>
|
||||
// class Bag {
|
||||
// public:
|
||||
//
|
||||
// private:
|
||||
// vector<T> v;
|
||||
// };
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
147
ods/src/ch01ex06.cc
Normal file
147
ods/src/ch01ex06.cc
Normal file
@@ -0,0 +1,147 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <ods/simplist.h>
|
||||
#include <ods/simpuset.h>
|
||||
#include <ods/simpsset.h>
|
||||
#include <ods/linked_list.h>
|
||||
using namespace std;
|
||||
|
||||
|
||||
static void
|
||||
check_simplist(void)
|
||||
{
|
||||
ods::SimpList<int> sl;
|
||||
|
||||
sl.add(0, 1);
|
||||
sl.add(1, 2);
|
||||
sl.add(2, 3);
|
||||
assert(sl.size() == 3);
|
||||
|
||||
sl.add(0, 4);
|
||||
sl.add(1, 5);
|
||||
sl.add(2, 6);
|
||||
assert(sl.size() == 6);
|
||||
|
||||
int expected[6] = {4, 5, 6, 1, 2, 3};
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
assert(sl.get(i) == expected[i]);
|
||||
}
|
||||
|
||||
bool caught = false;
|
||||
try {
|
||||
sl.add(8, 8);
|
||||
} catch (std::invalid_argument) {
|
||||
caught = true;
|
||||
}
|
||||
assert(caught);
|
||||
|
||||
assert(sl.get(2) == 6);
|
||||
sl.remove(2);
|
||||
assert(sl.get(2) == 1);
|
||||
assert(sl.size() == 5);
|
||||
sl.set(2, 6);
|
||||
assert(sl.get(2) == 6);
|
||||
|
||||
// expected = {1, 2, 3, 4, 5, 6};
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
expected[i] = i+1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
sl.set(i, i+1);
|
||||
}
|
||||
sl.add(5, 6);
|
||||
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
assert(sl.get(i) == expected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
check_simpuset(void)
|
||||
{
|
||||
ods::SimpUSet<int> us;
|
||||
|
||||
assert(us.add(1));
|
||||
assert(us.size() == 1);
|
||||
assert(us.find(1));
|
||||
assert(!us.add(1));
|
||||
assert(us.size() == 1);
|
||||
assert(us.add(2));
|
||||
assert(us.find(2));
|
||||
assert(us.add(3));
|
||||
assert(us.size() == 3);
|
||||
assert(us.find(3));
|
||||
|
||||
auto removed = us.remove(2);
|
||||
assert(removed == 2);
|
||||
assert(us.size() == 2);
|
||||
assert(!us.find(2));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
check_simpsset(void)
|
||||
{
|
||||
ods::SimpSSet<int> ss;
|
||||
|
||||
assert(ss.add(2));
|
||||
assert(ss.size() == 1);
|
||||
assert(ss.find(2));
|
||||
assert(!ss.add(2));
|
||||
assert(ss.size() == 1);
|
||||
assert(ss.add(1));
|
||||
assert(ss.find(1));
|
||||
assert(ss.size() == 2);
|
||||
assert(ss.add(3));
|
||||
assert(ss.size() == 3);
|
||||
assert(ss.find(3));
|
||||
|
||||
auto removed = ss.remove(2);
|
||||
assert(removed == 2);
|
||||
assert(ss.size() == 2);
|
||||
assert(!ss.find(2));
|
||||
}
|
||||
|
||||
void check_linkedlist(void);
|
||||
|
||||
void
|
||||
check_linkedlist()
|
||||
{
|
||||
ods::LinkedList<int> ll;
|
||||
|
||||
ll.add(0, 1);
|
||||
assert(ll.size() == 1);
|
||||
assert(ll.get(0) == 1);
|
||||
ll.add(0, 2);
|
||||
assert(ll.size() == 2);
|
||||
assert(ll.get(0) == 2);
|
||||
ll.add(2, 4);
|
||||
assert(ll.get(2) == 4);
|
||||
assert(ll.size() == 3);
|
||||
|
||||
ll.set(1, 5);
|
||||
assert(ll.get(1) == 5);
|
||||
|
||||
ll.remove(1);
|
||||
assert(ll.size() == 2);
|
||||
assert(ll.get(1) == 4);
|
||||
ll.remove(1);
|
||||
assert(ll.size() == 1);
|
||||
ll.remove(0);
|
||||
assert(ll.size() == 0);
|
||||
|
||||
ll.add(0, 1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
check_simplist();
|
||||
check_simpuset();
|
||||
check_simpsset();
|
||||
check_linkedlist();
|
||||
cout << "OK" << endl;
|
||||
}
|
||||
135
ods/src/list_bench.cc
Normal file
135
ods/src/list_bench.cc
Normal file
@@ -0,0 +1,135 @@
|
||||
// list_bench runs benchmarks against List implementations.
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <ods/list.h>
|
||||
#include <ods/simplist.h>
|
||||
#include <ods/vlist.h>
|
||||
#include <ods/linked_list.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static mt19937 rng;
|
||||
static random_device devrand;
|
||||
|
||||
// reseed picks a new seed for the RNG using the system random device.
|
||||
static void
|
||||
reseed()
|
||||
{
|
||||
rng.seed(devrand());
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
randint(int low, int high)
|
||||
{
|
||||
uniform_int_distribution<int> dist(low, high);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
static int
|
||||
randsizet(size_t low, size_t high)
|
||||
{
|
||||
uniform_int_distribution<size_t> dist(low, high);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
static void
|
||||
benchmark(ods::List<int>& list, int ops)
|
||||
{
|
||||
for (int i = 0; i < ops; i++) {
|
||||
auto op = randint(0, 3);
|
||||
size_t j = 0;
|
||||
int newval, oldval;
|
||||
|
||||
if (list.size() > 0) {
|
||||
j = randsizet(0, list.size()-1);
|
||||
}
|
||||
switch (op) {
|
||||
// get
|
||||
case 0:
|
||||
if (list.size() == 0) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
cout << "\tget " << j << endl;
|
||||
assert(list.get(j) != -1);
|
||||
break;
|
||||
// set
|
||||
case 1:
|
||||
if (list.size() == 0) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
oldval = list.get(j);
|
||||
newval = oldval;
|
||||
while (newval == oldval) {
|
||||
newval = randint(1, 1000000);
|
||||
}
|
||||
|
||||
cout << "\tset " << j << " from " << oldval << " to " << newval << endl;
|
||||
assert(list.set(j, newval) == oldval);
|
||||
assert(list.get(j) == newval);
|
||||
break;
|
||||
// add
|
||||
case 2:
|
||||
newval = randint(1, 1000000);
|
||||
list.add(j, newval);
|
||||
cout << "\tadd " << j << " " << newval << endl;
|
||||
assert(list.get(j) == newval);
|
||||
break;
|
||||
// remove
|
||||
case 3:
|
||||
if (list.size() == 0) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto old_size = list.size();
|
||||
cout << "\tremove " << j << endl;
|
||||
list.remove(j);
|
||||
assert(list.size() == (old_size-1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run(string label, ods::List<int>& list, int ops)
|
||||
{
|
||||
std::chrono::steady_clock clock;
|
||||
|
||||
auto start = clock.now();
|
||||
benchmark(list, ops);
|
||||
auto stop = clock.now();
|
||||
|
||||
std::chrono::duration<double> delta = stop - start;
|
||||
cerr << label << " @ " << ops << " ops: " << delta.count() << "s" << endl;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int ops = 1000;
|
||||
|
||||
if (argc == 2) {
|
||||
ops = stoi(argv[1]);
|
||||
}
|
||||
|
||||
reseed();
|
||||
|
||||
ods::SimpList<int> sl;
|
||||
run("SimpList", sl, ops);
|
||||
|
||||
ods::VList<int> vl;
|
||||
run("VList", vl, ops);
|
||||
|
||||
ods::LinkedList<int> ll;
|
||||
run("LinkedList", ll, ops);
|
||||
}
|
||||
151
ods/src/ods/linked_list.h
Normal file
151
ods/src/ods/linked_list.h
Normal file
@@ -0,0 +1,151 @@
|
||||
#ifndef __ODS_ODS_LINKED_LIST__
|
||||
#define __ODS_ODS_LINKED_LIST__
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <ods/list.h>
|
||||
|
||||
|
||||
namespace ods {
|
||||
|
||||
template<typename T>
|
||||
struct Node {
|
||||
struct Node *next;
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class LinkedList : public List<T> {
|
||||
public:
|
||||
LinkedList();
|
||||
~LinkedList();
|
||||
std::size_t size(void);
|
||||
T get(std::size_t);
|
||||
T set(std::size_t, T);
|
||||
void add(std::size_t, T);
|
||||
T remove(std::size_t);
|
||||
private:
|
||||
struct Node<T> *head;
|
||||
std::size_t len;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>::LinkedList() : head(nullptr), len(0) {}
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>::~LinkedList()
|
||||
{
|
||||
struct Node<T> *cursor = this->head;
|
||||
while (cursor != nullptr) {
|
||||
if (this->head != nullptr) {
|
||||
cursor = this->head->next;
|
||||
}
|
||||
delete this->head;
|
||||
this->head = cursor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
std::size_t
|
||||
LinkedList<T>::size()
|
||||
{
|
||||
return this->len;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
LinkedList<T>::add(std::size_t i, T value)
|
||||
{
|
||||
assert(i <= this->size());
|
||||
struct Node<T> *node = new struct Node<T>;
|
||||
node->value = value;
|
||||
node->next = nullptr;
|
||||
|
||||
auto cursor = this->head;
|
||||
if (i == 0) {
|
||||
node->next = this->head;
|
||||
this->head = node;
|
||||
}
|
||||
else {
|
||||
for (size_t j = 0; j < (i-1); j++) {
|
||||
cursor = cursor->next;
|
||||
}
|
||||
|
||||
if (cursor != nullptr) {
|
||||
node->next = cursor->next;
|
||||
cursor->next = node;
|
||||
}
|
||||
else {
|
||||
this->head = node;
|
||||
}
|
||||
}
|
||||
|
||||
this->len++;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
LinkedList<T>::get(std::size_t i)
|
||||
{
|
||||
assert(i < this->size());
|
||||
if (i == 0) {
|
||||
return this->head->value;
|
||||
}
|
||||
|
||||
auto cursor = this->head;
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
cursor = cursor->next;
|
||||
}
|
||||
|
||||
return cursor->value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
LinkedList<T>::set(std::size_t i, T value)
|
||||
{
|
||||
auto cursor = this->head;
|
||||
for (std::size_t j = 0; j < i; j++) {
|
||||
cursor = cursor->next;
|
||||
}
|
||||
|
||||
T prev = cursor->value;
|
||||
cursor->value = value;
|
||||
return prev;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
LinkedList<T>::remove(std::size_t i)
|
||||
{
|
||||
if (i == 0) {
|
||||
auto old = this->head->value;
|
||||
auto target = this->head;
|
||||
this->head = this->head->next;
|
||||
delete target;
|
||||
this->len--;
|
||||
return old;
|
||||
}
|
||||
|
||||
auto cursor = this->head;
|
||||
for (std::size_t j = 0; j < (i-1); j++) {
|
||||
cursor = cursor->next;
|
||||
}
|
||||
|
||||
auto target = cursor->next;
|
||||
cursor->next = target->next;
|
||||
auto old = target->value;
|
||||
delete target;
|
||||
this->len--;
|
||||
return old;
|
||||
}
|
||||
|
||||
} // namespace ods
|
||||
|
||||
|
||||
#endif // __ODS_ODS_LINKED_LIST__
|
||||
22
ods/src/ods/list.h
Normal file
22
ods/src/ods/list.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef __ODS_ODS_LIST__
|
||||
#define __ODS_ODS_LIST__
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace ods {
|
||||
|
||||
// Lists are sequences of values.
|
||||
template<typename T>
|
||||
class List {
|
||||
public:
|
||||
virtual ~List(void) {};
|
||||
virtual std::size_t size(void) =0;
|
||||
virtual T get(std::size_t) =0;
|
||||
virtual T set(std::size_t, T) =0;
|
||||
virtual void add(std::size_t, T) =0;
|
||||
virtual T remove(std::size_t) =0;
|
||||
};
|
||||
|
||||
} // end namespace ods
|
||||
|
||||
#endif
|
||||
16
ods/src/ods/queue.h
Normal file
16
ods/src/ods/queue.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef __ODS_QUEUE__
|
||||
#define __ODS_QUEUE__
|
||||
|
||||
|
||||
// Dequeue represents a collection of elements to which we can add elements
|
||||
// and remove the next element.
|
||||
template<typename T>
|
||||
class Dequeue {
|
||||
public:
|
||||
virtual void add_first(T);
|
||||
virtual void add_last(T);
|
||||
virtual T remove_first(void);
|
||||
virtual T remove_last(void);
|
||||
virtual bool empty(void);
|
||||
};
|
||||
#endif
|
||||
153
ods/src/ods/simplist.h
Normal file
153
ods/src/ods/simplist.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#ifndef __ODS_ODS_SIMPLIST__
|
||||
#define __ODS_ODS_SIMPLIST__
|
||||
|
||||
#include <ods/list.h>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ods {
|
||||
|
||||
template<typename T>
|
||||
class SimpList : public List<T> {
|
||||
public:
|
||||
SimpList();
|
||||
~SimpList(void);
|
||||
std::size_t size(void);
|
||||
T get(std::size_t);
|
||||
T set(std::size_t, T);
|
||||
void add(std::size_t, T);
|
||||
T remove(std::size_t);
|
||||
private:
|
||||
T *arr;
|
||||
std::size_t cap;
|
||||
std::size_t len;
|
||||
void grow();
|
||||
void shrink();
|
||||
|
||||
const std::size_t DEFAULT_SIZE = 8;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
SimpList<T>::SimpList()
|
||||
{
|
||||
arr = new T[DEFAULT_SIZE];
|
||||
cap = DEFAULT_SIZE;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SimpList<T>::~SimpList()
|
||||
{
|
||||
delete this->arr;
|
||||
this->cap = 0;
|
||||
this->len = 0;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
std::size_t
|
||||
SimpList<T>::size(void)
|
||||
{
|
||||
assert(len <= cap);
|
||||
return this->len;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
SimpList<T>::get(std::size_t i)
|
||||
{
|
||||
if (i >= this->len) {
|
||||
throw std::invalid_argument("index out of range");
|
||||
}
|
||||
|
||||
return this->arr[i];
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
SimpList<T>::set(std::size_t i, T value)
|
||||
{
|
||||
if (i >= this->len) {
|
||||
throw std::invalid_argument("index out of range");
|
||||
}
|
||||
|
||||
T old = this->arr[i];
|
||||
this->arr[i] = value;
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
SimpList<T>::add(std::size_t i, T value)
|
||||
{
|
||||
if (i > this->len) {
|
||||
throw std::invalid_argument("index out of range");
|
||||
}
|
||||
assert(value);
|
||||
|
||||
// check size, grow as needed
|
||||
if (len == (cap - 1)) {
|
||||
this->grow();
|
||||
}
|
||||
|
||||
// simple case: check append
|
||||
if (i == len) {
|
||||
this->arr[len++] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
// complex case: insertion
|
||||
for (std::size_t j = this->len; j > i; j--) {
|
||||
this->arr[j] = this->arr[j-1];
|
||||
}
|
||||
this->arr[i] = value;
|
||||
this->len++;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
SimpList<T>::remove(std::size_t i)
|
||||
{
|
||||
if (i > this->len) {
|
||||
throw std::invalid_argument("index out of range");
|
||||
}
|
||||
|
||||
T old = this->arr[i];
|
||||
|
||||
if (i == this->len) {
|
||||
this->len--;
|
||||
return old;
|
||||
}
|
||||
|
||||
for (std::size_t j = i; j < this->len; j++) {
|
||||
this->arr[j] = this->arr[j+1];
|
||||
}
|
||||
this->len--;
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
SimpList<T>::grow()
|
||||
{
|
||||
std::size_t new_cap = this->cap * 2;
|
||||
T *new_arr = new T[new_cap];
|
||||
|
||||
for (std::size_t i = 0; i < this->len; i++) {
|
||||
new_arr[i] = this->arr[i];
|
||||
}
|
||||
|
||||
delete this->arr;
|
||||
this->arr = new_arr;
|
||||
this->cap = new_cap;
|
||||
}
|
||||
|
||||
} // end namespace ods
|
||||
|
||||
#endif
|
||||
95
ods/src/ods/simpsset.h
Normal file
95
ods/src/ods/simpsset.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef __ODS_ODS_SIMPSSET__
|
||||
#define __ODS_ODS_SIMPSSET__
|
||||
|
||||
|
||||
#include <optional>
|
||||
#include <ods/simplist.h>
|
||||
#include <ods/sset.h>
|
||||
|
||||
namespace ods {
|
||||
template<typename T>
|
||||
class SimpSSet : public SSet<T> {
|
||||
public:
|
||||
SimpSSet(void);
|
||||
~SimpSSet(void);
|
||||
std::size_t size(void);
|
||||
bool add(T);
|
||||
std::optional<T> remove(T);
|
||||
std::optional<T> find(T);
|
||||
private:
|
||||
SimpList<T> list;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
SimpSSet<T>::SimpSSet()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
SimpSSet<T>::~SimpSSet()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::size_t
|
||||
SimpSSet<T>::size()
|
||||
{
|
||||
return this->list.size();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
SimpSSet<T>::add(T value)
|
||||
{
|
||||
for (std::size_t i = 0; i < this->list.size(); i++) {
|
||||
if (this->list.get(i) == value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < this->list.size(); i++) {
|
||||
if (this->list.get(i) < value) {
|
||||
continue;
|
||||
}
|
||||
this->list.add(i, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
this->list.add(this->list.size(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<T>
|
||||
SimpSSet<T>::remove(T value)
|
||||
{
|
||||
for (std::size_t i = 0; i < this->list.size(); i++) {
|
||||
if (this->list.get(i) == value) {
|
||||
auto removed = this->list.get(i);
|
||||
this->list.remove(i);
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<T>
|
||||
SimpSSet<T>::find(T value)
|
||||
{
|
||||
for (std::size_t i = 0; i < this->list.size(); i++) {
|
||||
if (this->list.get(i) == value) {
|
||||
return this->list.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ods
|
||||
|
||||
|
||||
#endif // __ODS_ODS_SIMPSSET__
|
||||
87
ods/src/ods/simpuset.h
Normal file
87
ods/src/ods/simpuset.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef __ODS_ODS_SIMPUSET__
|
||||
#define __ODS_ODS_SIMPUSET__
|
||||
|
||||
|
||||
#include <optional>
|
||||
#include <ods/simplist.h>
|
||||
#include <ods/uset.h>
|
||||
|
||||
namespace ods {
|
||||
template<typename T>
|
||||
class SimpUSet : public USet<T> {
|
||||
public:
|
||||
SimpUSet(void);
|
||||
~SimpUSet(void);
|
||||
std::size_t size(void);
|
||||
bool add(T);
|
||||
std::optional<T> remove(T);
|
||||
std::optional<T> find(T);
|
||||
private:
|
||||
SimpList<T> list;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
SimpUSet<T>::SimpUSet()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
SimpUSet<T>::~SimpUSet()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::size_t
|
||||
SimpUSet<T>::size()
|
||||
{
|
||||
return this->list.size();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
SimpUSet<T>::add(T value)
|
||||
{
|
||||
for (std::size_t i = 0; i < this->list.size(); i++) {
|
||||
if (this->list.get(i) == value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->list.add(this->list.size(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<T>
|
||||
SimpUSet<T>::remove(T value)
|
||||
{
|
||||
for (std::size_t i = 0; i < this->list.size(); i++) {
|
||||
if (this->list.get(i) == value) {
|
||||
auto removed = this->list.get(i);
|
||||
this->list.remove(i);
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<T>
|
||||
SimpUSet<T>::find(T value)
|
||||
{
|
||||
for (std::size_t i = 0; i < this->list.size(); i++) {
|
||||
if (this->list.get(i) == value) {
|
||||
return this->list.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ods
|
||||
|
||||
|
||||
#endif // __ODS_ODS_SIMPUSET__
|
||||
20
ods/src/ods/sset.h
Normal file
20
ods/src/ods/sset.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef __ODS_ODS_SSET__
|
||||
#define __ODS_ODS_SSET__
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
|
||||
namespace ods {
|
||||
template<typename T>
|
||||
class SSet {
|
||||
public:
|
||||
virtual ~SSet(void) {};
|
||||
virtual std::size_t size(void) =0;
|
||||
virtual bool add(T) = 0;
|
||||
virtual std::optional<T> remove(T) = 0;
|
||||
virtual std::optional<T> find(T) = 0;
|
||||
};
|
||||
} // namespace ods
|
||||
|
||||
#endif // __ODS_ODS_SSET__
|
||||
20
ods/src/ods/uset.h
Normal file
20
ods/src/ods/uset.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef __ODS_ODS_USET__
|
||||
#define __ODS_ODS_USET__
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
|
||||
namespace ods {
|
||||
template<typename T>
|
||||
class USet {
|
||||
public:
|
||||
virtual ~USet(void) {};
|
||||
virtual std::size_t size(void) =0;
|
||||
virtual bool add(T) = 0;
|
||||
virtual std::optional<T> remove(T) = 0;
|
||||
virtual std::optional<T> find(T) = 0;
|
||||
};
|
||||
} // namespace ods
|
||||
|
||||
#endif // __ODS_ODS_USET__
|
||||
78
ods/src/ods/vlist.h
Normal file
78
ods/src/ods/vlist.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef __ODS_ODS_VLIST__
|
||||
#define __ODS_ODS_VLIST__
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <ods/list.h>
|
||||
|
||||
namespace ods {
|
||||
|
||||
// VList implements a vector-based list interface.
|
||||
template<typename T>
|
||||
class VList : public List<T> {
|
||||
public:
|
||||
~VList(void);
|
||||
std::size_t size(void);
|
||||
T get(std::size_t);
|
||||
T set(std::size_t, T);
|
||||
void add(std::size_t, T);
|
||||
T remove(std::size_t);
|
||||
private:
|
||||
std::vector<T> vec;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
VList<T>::~VList()
|
||||
{
|
||||
this->vec.clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::size_t
|
||||
VList<T>::size()
|
||||
{
|
||||
return this->vec.size();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
VList<T>::get(std::size_t i)
|
||||
{
|
||||
return this->vec.at(i);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
VList<T>::set(size_t i, T value)
|
||||
{
|
||||
auto p = this->vec.begin() + i;
|
||||
T old = this->vec.at(i);
|
||||
this->vec.erase(p);
|
||||
this->vec.insert(p, value);
|
||||
return old;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
VList<T>::add(std::size_t i, T value)
|
||||
{
|
||||
auto p = this->vec.begin() + i;
|
||||
this->vec.insert(p, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
VList<T>::remove(std::size_t i)
|
||||
{
|
||||
auto removed = this->vec.at(i);
|
||||
auto p = this->vec.begin() + i;
|
||||
this->vec.erase(p);
|
||||
return removed;
|
||||
}
|
||||
|
||||
} // namespace ods
|
||||
|
||||
|
||||
#endif // __ODS_ODS_VLIST__
|
||||
86
ods/src/sset_bench.cc
Normal file
86
ods/src/sset_bench.cc
Normal file
@@ -0,0 +1,86 @@
|
||||
// list_bench runs benchmarks against List implementations.
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <ods/sset.h>
|
||||
#include <ods/simpsset.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static mt19937 rng;
|
||||
static random_device devrand;
|
||||
|
||||
// reseed picks a new seed for the RNG using the system random device.
|
||||
static void
|
||||
reseed()
|
||||
{
|
||||
rng.seed(devrand());
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
randint(int low, int high)
|
||||
{
|
||||
uniform_int_distribution<int> dist(low, high);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
static void
|
||||
benchmark(ods::SSet<int>& set, int ops)
|
||||
{
|
||||
for (int i = 0; i < ops; i++) {
|
||||
auto op = randint(0, 3);
|
||||
int val = randint(1, 100);
|
||||
|
||||
switch (op) {
|
||||
// add
|
||||
case 0:
|
||||
case 1:
|
||||
cout << "\tadd " << val << endl;
|
||||
set.add(val);
|
||||
break;
|
||||
// remove
|
||||
case 2:
|
||||
set.remove(val);
|
||||
break;
|
||||
// find
|
||||
case 3:
|
||||
set.find(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run(string label, ods::SSet<int>& list, int ops)
|
||||
{
|
||||
std::chrono::steady_clock clock;
|
||||
|
||||
auto start = clock.now();
|
||||
benchmark(list, ops);
|
||||
auto stop = clock.now();
|
||||
|
||||
std::chrono::duration<double> delta = stop - start;
|
||||
cerr << label << " @ " << ops << " ops: " << delta.count() << "s" << endl;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int ops = 1000;
|
||||
|
||||
if (argc == 2) {
|
||||
ops = stoi(argv[1]);
|
||||
}
|
||||
|
||||
reseed();
|
||||
|
||||
ods::SimpSSet<int> us;
|
||||
run("SimpSSet", us, ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
86
ods/src/uset_bench.cc
Normal file
86
ods/src/uset_bench.cc
Normal file
@@ -0,0 +1,86 @@
|
||||
// list_bench runs benchmarks against List implementations.
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <ods/uset.h>
|
||||
#include <ods/simpuset.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static mt19937 rng;
|
||||
static random_device devrand;
|
||||
|
||||
// reseed picks a new seed for the RNG using the system random device.
|
||||
static void
|
||||
reseed()
|
||||
{
|
||||
rng.seed(devrand());
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
randint(int low, int high)
|
||||
{
|
||||
uniform_int_distribution<int> dist(low, high);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
static void
|
||||
benchmark(ods::USet<int>& set, int ops)
|
||||
{
|
||||
for (int i = 0; i < ops; i++) {
|
||||
auto op = randint(0, 3);
|
||||
int val = randint(1, 100);
|
||||
|
||||
switch (op) {
|
||||
// add
|
||||
case 0:
|
||||
case 1:
|
||||
cout << "\tadd " << val << endl;
|
||||
set.add(val);
|
||||
break;
|
||||
// remove
|
||||
case 2:
|
||||
set.remove(val);
|
||||
break;
|
||||
// find
|
||||
case 3:
|
||||
set.find(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run(string label, ods::USet<int>& list, int ops)
|
||||
{
|
||||
std::chrono::steady_clock clock;
|
||||
|
||||
auto start = clock.now();
|
||||
benchmark(list, ops);
|
||||
auto stop = clock.now();
|
||||
|
||||
std::chrono::duration<double> delta = stop - start;
|
||||
cerr << label << " @ " << ops << " ops: " << delta.count() << "s" << endl;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int ops = 1000;
|
||||
|
||||
if (argc == 2) {
|
||||
ops = stoi(argv[1]);
|
||||
}
|
||||
|
||||
reseed();
|
||||
|
||||
ods::SimpUSet<int> us;
|
||||
run("SimpUSet", us, ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user