Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
BUILD_DIR = ./build
EXE = $(BUILD_DIR)/data-structure-visualizer
IMGUI_DIR = ./imgui
SOURCES = ./src/main.cpp
SRC_DIR = ./src
SOURCES = $(SRC_DIR)/main.cpp
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o, $(basename $(notdir $(SOURCES)))))
UNAME_S := $(shell uname -s)
LINUX_GL_LIBS = -lGL

CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
CXXFLAGS = -std=c++14 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(SRC_DIR) -I./third_party
CXXFLAGS += -g -Wall -Wformat
LIBS =

Expand Down Expand Up @@ -71,7 +72,7 @@ endif
## BUILD RULES
##---------------------------------------------------------------------

$(BUILD_DIR)/%.o: ./src/%.cpp
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
@mkdir -p $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -c -o $@ $<

Expand Down
39 changes: 39 additions & 0 deletions src/core/DataStructure.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include "Operation.h"
#include <nlohmann/json.hpp>
#include <vector>
#include <string>
#include <algorithm>

class DataStructure {
public:
virtual ~DataStructure() = default;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot add a variable to store the element index of the last operation, and render them in a different color

virtual std::string getType() const = 0;
virtual size_t size() const = 0;
virtual bool empty() const = 0;
virtual void clear() = 0;
virtual nlohmann::json serialize() const = 0;
virtual void deserialize(const nlohmann::json& j) = 0;
virtual std::vector<Operation> getOperationHistory() const = 0;
virtual void clearOperationHistory() = 0;

// New methods for operation support
virtual std::vector<int> getSupportedOperations() const = 0; // Returns supported operation types
virtual bool supportsOperation(int operationType) const = 0; // Check if operation is supported
virtual std::string getOperationName(int operationType) const = 0; // Get human-readable name

// Initialize with data set (required by all structures)
virtual bool initialize(const std::vector<int>& data) = 0;

protected:
std::vector<Operation> operationHistory;

// Helper method to record operations
void recordOperation(BaseOperationType baseType, int specificType,
const std::vector<int>& indices, const std::vector<int>& values,
const std::string& description, bool success = true) {
operationHistory.emplace_back(baseType, specificType, getType(),
indices, values, description, success);
}
};
166 changes: 166 additions & 0 deletions src/core/DynamicArray.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#pragma once

#include "DataStructure.h"
#include <vector>

class DynamicArray : public DataStructure {
private:
std::vector<int> elements;

public:
DynamicArray() = default;
virtual ~DynamicArray() = default;

// DataStructure interface implementation
std::string getType() const override {
return "DynamicArray";
}

size_t size() const override {
return elements.size();
}

bool empty() const override {
return elements.empty();
}

void clear() override {
elements.clear();
recordOperation(BaseOperationType::CUSTOM, static_cast<int>(LinearOps::Type::DELETE),
{}, {}, "Clear all elements");
}

std::vector<int> getSupportedOperations() const override {
return {
static_cast<int>(LinearOps::Type::INIT),
static_cast<int>(LinearOps::Type::INSERT),
static_cast<int>(LinearOps::Type::DELETE)
};
}

bool supportsOperation(int operationType) const override {
auto supported = getSupportedOperations();
return std::find(supported.begin(), supported.end(), operationType) != supported.end();
}

std::string getOperationName(int operationType) const override {
switch(static_cast<LinearOps::Type>(operationType)) {
case LinearOps::Type::INIT: return "Initialize";
case LinearOps::Type::INSERT: return "Insert";
case LinearOps::Type::DELETE: return "Delete";
default: return "Unknown";
}
}

bool initialize(const std::vector<int>& data) override {
elements = data;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot we want to initialize the array element by element, so we can visualize them

recordOperation(BaseOperationType::INIT, static_cast<int>(LinearOps::Type::INIT),
{}, data, "Initialize array with data");
return true;
}

std::vector<Operation> getOperationHistory() const override {
return operationHistory;
}

void clearOperationHistory() override {
operationHistory.clear();
}

// Array-specific operations
bool insert(size_t index, int value) {
if (index > elements.size()) {
recordOperation(BaseOperationType::INSERT, static_cast<int>(LinearOps::Type::INSERT),
{static_cast<int>(index)}, {value},
"Insert failed: index out of bounds", false);
return false;
}

elements.insert(elements.begin() + index, value);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please write a insert function that moves elements, as we want to visualise these operations as well.

recordOperation(BaseOperationType::INSERT, static_cast<int>(LinearOps::Type::INSERT),
{static_cast<int>(index)}, {value},
"Insert " + std::to_string(value) + " at position " + std::to_string(index));
return true;
}

bool deleteAt(size_t index) {
if (index >= elements.size()) {
recordOperation(BaseOperationType::DELETE, static_cast<int>(LinearOps::Type::DELETE),
{static_cast<int>(index)}, {},
"Delete failed: index out of bounds", false);
return false;
}

int deletedValue = elements[index];
elements.erase(elements.begin() + index);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot just like the insert function, we want to write the erase function that moves element for visualization, not the provided one in cpp vector

recordOperation(BaseOperationType::DELETE, static_cast<int>(LinearOps::Type::DELETE),
{static_cast<int>(index)}, {deletedValue},
"Delete element at position " + std::to_string(index));
return true;
}

void pushBack(int value) {
insert(elements.size(), value);
}

// Getters for visualization
const std::vector<int>& getElements() const {
return elements;
}

int at(size_t index) const {
if (index >= elements.size()) {
throw std::out_of_range("Index out of bounds");
}
return elements[index];
}

// Serialization
nlohmann::json serialize() const override {
nlohmann::json j;
j["metadata"]["type"] = getType();
j["metadata"]["version"] = "1.0";
j["data"]["elements"] = elements;
j["data"]["size"] = elements.size();

// Serialize operations
j["operations"] = nlohmann::json::array();
for (const auto& op : operationHistory) {
nlohmann::json opJson;
opJson["baseType"] = static_cast<int>(op.baseType);
opJson["specificType"] = op.specificType;
opJson["dataStructureType"] = op.dataStructureType;
opJson["indices"] = op.indices;
opJson["values"] = op.values;
opJson["description"] = op.description;
opJson["success"] = op.success;
j["operations"].push_back(opJson);
}

return j;
}

void deserialize(const nlohmann::json& j) override {
if (j["metadata"]["type"] != getType()) {
throw std::invalid_argument("Invalid data structure type for deserialization");
}

elements = j["data"]["elements"].get<std::vector<int>>();

// Deserialize operations if present
if (j.contains("operations")) {
operationHistory.clear();
for (const auto& opJson : j["operations"]) {
operationHistory.emplace_back(
static_cast<BaseOperationType>(opJson["baseType"].get<int>()),
opJson["specificType"].get<int>(),
opJson["dataStructureType"].get<std::string>(),
opJson["indices"].get<std::vector<int>>(),
opJson["values"].get<std::vector<int>>(),
opJson["description"].get<std::string>(),
opJson["success"].get<bool>()
);
}
}
}
};
67 changes: 67 additions & 0 deletions src/core/Operation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include <vector>
#include <string>
#include <chrono>

// Base operation types - extensible design
enum class BaseOperationType {
INIT, // Initialize with data set
INSERT, // Generic insertion
DELETE, // Generic deletion
SEARCH, // Search operation
CUSTOM // Custom operation (defined by subclass)
};

// Data structure specific operations
namespace LinearOps {
enum class Type {
INIT = static_cast<int>(BaseOperationType::INIT),
INSERT = static_cast<int>(BaseOperationType::INSERT),
DELETE = static_cast<int>(BaseOperationType::DELETE),
// List-specific operations can be added here
};
}

namespace StackOps {
enum class Type {
INIT = static_cast<int>(BaseOperationType::INIT),
PUSH = static_cast<int>(BaseOperationType::INSERT), // Push maps to insert
POP = static_cast<int>(BaseOperationType::DELETE), // Pop maps to delete
};
}

namespace TreeOps {
enum class Type {
INIT = static_cast<int>(BaseOperationType::INIT),
// Basic trees only support initialization
};
}

namespace BSTOps {
enum class Type {
INIT = static_cast<int>(BaseOperationType::INIT),
SEARCH = static_cast<int>(BaseOperationType::SEARCH),
INSERT = static_cast<int>(BaseOperationType::INSERT),
DELETE = static_cast<int>(BaseOperationType::DELETE),
};
}

struct Operation {
BaseOperationType baseType; // Base operation type
int specificType; // Data structure specific type
std::string dataStructureType; // Which data structure this applies to
std::vector<int> indices; // Affected positions
std::vector<int> values; // Values involved
std::string description; // Human-readable description
std::chrono::steady_clock::time_point time; // When operation occurred
bool success; // Whether operation succeeded

// Constructor for convenience
Operation(BaseOperationType base, int specific, const std::string& dsType,
const std::vector<int>& idx, const std::vector<int>& vals,
const std::string& desc, bool succeeded = true)
: baseType(base), specificType(specific), dataStructureType(dsType),
indices(idx), values(vals), description(desc),
time(std::chrono::steady_clock::now()), success(succeeded) {}
};
Loading