diff --git a/CMakeLists.txt b/CMakeLists.txt index cc58e27..e68a470 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,3 +8,4 @@ add_compile_options(-Wall -Wextra) add_subdirectory(homework_2) add_subdirectory(homework_3) +add_subdirectory(homework_4) diff --git a/homework_4/CMakeLists.txt b/homework_4/CMakeLists.txt new file mode 100644 index 0000000..4d4fd12 --- /dev/null +++ b/homework_4/CMakeLists.txt @@ -0,0 +1,5 @@ +project(homework_4) + +set(homeworkName "${PROJECT_NAME}") + +add_subdirectory(task_1) diff --git a/homework_4/README.md b/homework_4/README.md new file mode 100644 index 0000000..33f4ec2 --- /dev/null +++ b/homework_4/README.md @@ -0,0 +1,3 @@ +# Homework 4 + +[Task 1. Binary representation](/homework_4/task_1) diff --git a/homework_4/task_1/CMakeLists.txt b/homework_4/task_1/CMakeLists.txt new file mode 100644 index 0000000..909778a --- /dev/null +++ b/homework_4/task_1/CMakeLists.txt @@ -0,0 +1,9 @@ +project("${homeworkName}_task_1") + +add_library(binaryAddition binaryAddition.c) + +add_executable(${PROJECT_NAME} main.c) +target_link_libraries(${PROJECT_NAME} binaryAddition) + +add_executable(${PROJECT_NAME}_test test.c) +target_link_libraries(${PROJECT_NAME}_test binaryAddition) diff --git a/homework_4/task_1/binaryAddition.c b/homework_4/task_1/binaryAddition.c new file mode 100644 index 0000000..3a969ac --- /dev/null +++ b/homework_4/task_1/binaryAddition.c @@ -0,0 +1,34 @@ +#include "binaryAddition.h" + +#include +#include + +int8_t addTwoNumbers(int8_t left, int8_t right, AdditionCallback callback) { + const int bitWidth = sizeof(int8_t) * 8; + + int8_t result = 0; + int8_t accumulatedCarry = 0; + bool carryBit = 0; + + if (callback != NULL) { + callback(left, right, result, accumulatedCarry, false, false, false, false, false, 0); + } + + for (int i = 0; i < bitWidth; ++i) { + bool leftBit = ((left >> i) & 1) == 1; + bool rightBit = ((right >> i) & 1) == 1; + bool resultBit = ((leftBit + rightBit + carryBit) % 2) == 1; + + bool newCarryBit = (leftBit + rightBit + carryBit) >= 2; + + result |= resultBit << i; + accumulatedCarry |= newCarryBit << i; + + if (callback != NULL) { + callback(left, right, result, accumulatedCarry, leftBit, rightBit, resultBit, carryBit, newCarryBit, i + 1); + } + carryBit = newCarryBit; + } + + return result; +} diff --git a/homework_4/task_1/binaryAddition.h b/homework_4/task_1/binaryAddition.h new file mode 100644 index 0000000..8ff2de5 --- /dev/null +++ b/homework_4/task_1/binaryAddition.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +/// Callback that is called after performing every step of addition. +/// Arguments in order: left, right, accumulatedResult, accumulatedCarryBits, +/// leftBit, rightBit, resultBit, oldCarryBit, newCarryBit, steps (0 before first step, incrementing after every step) +typedef void (*AdditionCallback)(int8_t, int8_t, int8_t, int8_t, bool, bool, bool, bool, bool, int); + +/// @brief Adds two numbers with pre-step callback +/// @param left Left operand +/// @param right Right operand +/// @param callback Function to call after every step, can be `NULL`; it will be called once before first step +/// @return Result of the addition +int8_t addTwoNumbers(int8_t left, int8_t right, AdditionCallback callback); diff --git a/homework_4/task_1/main.c b/homework_4/task_1/main.c new file mode 100644 index 0000000..8995414 --- /dev/null +++ b/homework_4/task_1/main.c @@ -0,0 +1,153 @@ +#include +#include +#include +#include + +#include "binaryAddition.h" + +// set to false to disable color and line overrides +#define PRINT_ANSI true + +#if PRINT_ANSI + +#define ANSI_BG_YELLOW "\e[43m" +#define ANSI_FG_GREEN "\e[32m" +#define ANSI_RESET "\e[0m" + +#else + +#define ANSI_BG_YELLOW "" +#define ANSI_FG_GREEN "" +#define ANSI_RESET "" + +#endif + +void printBinary(int8_t number, int bits, int bitToHighlight) { + for (int i = bits - 1; i >= 0; --i) { + int bit = (number >> i) & 1; + if (i == bitToHighlight) { + printf(ANSI_FG_GREEN "%d" ANSI_RESET, bit); + } else { + printf("%d", bit); + } + } +} + +void printStep(int8_t left, int8_t right, int8_t accumulatedResult, int8_t accumulatedCarry, + bool leftBit, bool rightBit, bool resultBit, bool oldCarryBit, bool newCarryBit, int steps) { + + if (steps != 0) { +#if PRINT_ANSI + // 0th step prints 4 lines, 1st step and further - 6, and user inputs one more + const int clearLines = (steps == 1 ? 4 : 6) + 1; + + // move cursor back, clear all lines and move cursor again + printf("\e[%dA", clearLines); + for (int i = 0; i < clearLines; ++i) { + printf("\e[K\n"); + } + printf("\e[%dA", clearLines); +#endif + } + + const int bitWidth = sizeof(int8_t) * 8; + + // 1 space here because carry can overflow + printf(" "); + // print all carry bits + for (int i = bitWidth - 1; i >= 0; --i) { + if (((accumulatedCarry >> i) & 1) == 1) { + if (i + 1 == steps) { + printf(ANSI_BG_YELLOW "." ANSI_RESET); + } else { + printf("."); + } + } else { + printf(" "); + } + } + printf("\n"); + + // print left + printf(" "); + printBinary(left, bitWidth, steps - 1); + printf("\n"); + + // print right + printf("+ "); + printBinary(right, bitWidth, steps - 1); + printf("\n"); + + // print delimiter + printf(" "); + for (int i = 0; i < bitWidth; ++i) { + printf("-"); + } + printf("\n"); + + if (steps > 0) { + + const int stepsNotCompletedYet = bitWidth - steps; + + // print result + printf("= "); + for (int i = 0; i < stepsNotCompletedYet; ++i) { + printf(" "); + } + printBinary(accumulatedResult, steps, steps - 1); + printf("\n"); + + // print head + printf(" "); + for (int i = 0; i < stepsNotCompletedYet; ++i) { + printf(" "); + } + printf("^ <--> %d + %d", leftBit, rightBit); + + if (oldCarryBit != 0) { + printf(" + 1"); + } + + printf(" = %d", resultBit); + + if (newCarryBit != 0) { + printf(" (1 в уме)"); + } + + printf("\n"); + } + + // wait for enter + while (getchar() != '\n') {} +} + +int8_t readValue(const char *prompt, void (*printIncorrectValue)(long long, long long), long long lowLimit, long long highLimit) { + long long value; + printf("%s", prompt); + while ((scanf("%lld", &value) != 1) || value < lowLimit || value > highLimit) { + while (getchar() != '\n') {} + printIncorrectValue(lowLimit, highLimit); + } + return (int8_t)value; +} + +void printError(long long lowLimit, long long highLimit) { + printf("введено неверное значение: число должно быть больше %lld и меньше %lld; попробуйте ещё раз: ", lowLimit, highLimit); +} + +int main(void) { + setlocale(LC_ALL, "ru_RU.UTF-8"); + + int8_t left = readValue("введите первое число: ", printError, INT8_MIN, INT8_MAX); + int8_t right = readValue("введите второе число: ", printError, INT8_MIN, INT8_MAX); + + // reset input + while (getchar() != '\n') {} + + printf("для следующего шага нажмите Enter\n"); + + int8_t result = addTwoNumbers(left, right, printStep); + + printf("результат: \n"); + printf("%d + %d = %d\n", left, right, result); +} diff --git a/homework_4/task_1/test.c b/homework_4/task_1/test.c new file mode 100644 index 0000000..c2b8ba1 --- /dev/null +++ b/homework_4/task_1/test.c @@ -0,0 +1,41 @@ +#define CTEST_MAIN +#define CTEST_SEGFAULT +#include "../../ctest/ctest.h" + +#include + +#include "binaryAddition.h" + +int main(int argc, const char *argv[]) { + return ctest_main(argc, argv); +} + +CTEST(additionTests, zeroTest) { + ASSERT_EQUAL(addTwoNumbers(0, 0, NULL), 0); +} + +CTEST(additionTests, zeroPlusAnyTest) { + ASSERT_EQUAL(addTwoNumbers(0, 1, NULL), 1); + ASSERT_EQUAL(addTwoNumbers(0, 2, NULL), 2); + ASSERT_EQUAL(addTwoNumbers(0, 4, NULL), 4); + ASSERT_EQUAL(addTwoNumbers(0, 45, NULL), 45); + ASSERT_EQUAL(addTwoNumbers(0, 98, NULL), 98); + ASSERT_EQUAL(addTwoNumbers(0, 121, NULL), 121); + ASSERT_EQUAL(addTwoNumbers(0, -1, NULL), -1); + ASSERT_EQUAL(addTwoNumbers(0, -3, NULL), -3); + ASSERT_EQUAL(addTwoNumbers(0, -11, NULL), -11); +} + +CTEST(additionTests, oneNegativeTest) { + ASSERT_EQUAL(addTwoNumbers(31, -29, NULL), 2); + ASSERT_EQUAL(addTwoNumbers(-16, 4, NULL), -12); + ASSERT_EQUAL(addTwoNumbers(-1, 1, NULL), 0); + ASSERT_EQUAL(addTwoNumbers(-100, 100, NULL), 0); +} + +CTEST(additionTests, twoNegativeTest) { + ASSERT_EQUAL(addTwoNumbers(-31, -29, NULL), -60); + ASSERT_EQUAL(addTwoNumbers(-1, -2, NULL), -3); + ASSERT_EQUAL(addTwoNumbers(-10, -24, NULL), -34); + ASSERT_EQUAL(addTwoNumbers(-110, -10, NULL), -120); +}