diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..7bf1fb4 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,45 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*' +WarningsAsErrors: '--checks=bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-cplusplus-*,clang-analyzer-*,cppcoreguidelines-*' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +User: h3nnn4n +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: llvm-else-after-return.WarnOnConditionVariables + value: '0' + - key: llvm-else-after-return.WarnOnUnfixable + value: '0' + - key: llvm-qualified-auto.AddConstToQualified + value: '0' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' +... diff --git a/.github/workflows/clang-tidy-check.yml b/.github/workflows/clang-tidy-check.yml index a4e4368..a2e6347 100644 --- a/.github/workflows/clang-tidy-check.yml +++ b/.github/workflows/clang-tidy-check.yml @@ -19,6 +19,9 @@ jobs: sudo apt-get -y -qq install clang-tidy cmake jq clang cppcheck python -m pip install scan-build + - name: Update Path + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Update compile database run: | make clean diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml index b97e23d..ae5709b 100644 --- a/.github/workflows/cppcheck.yml +++ b/.github/workflows/cppcheck.yml @@ -19,10 +19,13 @@ jobs: sudo apt-get -y -qq install clang-tidy cmake jq clang cppcheck python -m pip install scan-build + - name: Update Path + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Update compile database run: | make clean intercept-build make - name: run cppcheck - run: cppcheck --enable=all --std=c11 --language=c --error-exitcode=1 src/*.c + run: cppcheck --enable=all --std=c11 --language=c --error-exitcode=1 src/*.c src/**/*.c diff --git a/.github/workflows/cpplint.yml b/.github/workflows/cpplint.yml index 6749407..8dc3052 100644 --- a/.github/workflows/cpplint.yml +++ b/.github/workflows/cpplint.yml @@ -17,6 +17,9 @@ jobs: python3 -m pip install cpplint python3 -m pip install scan-build + - name: Update Path + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Update compile database run: | make clean diff --git a/.github/workflows/heap-check.yml b/.github/workflows/heap-check.yml index ff102d8..9fe605b 100644 --- a/.github/workflows/heap-check.yml +++ b/.github/workflows/heap-check.yml @@ -19,13 +19,25 @@ jobs: - name: Build run: make gperftools + env: + CFLAGS: -DGRID_SIZE=2 -DN_GRIDS=2 - name: Detect Leaks on single solution - run: ./dancing-links --single-solution --quited < samples/5x8_2xAll_freePieces.dat + run: ./dancing-links --single-solution --quiet < samples/5x8_2xAll_freePieces.dat env: HEAPCHECK: normal - name: Detect Leaks on multiple solutions - run: ./dancing-links --multiple-solutions --quited < samples/5x5_5_pentoI.dat + run: ./dancing-links --multiple-solutions --quiet < samples/5x5_5_pentoI.dat + env: + HEAPCHECK: normal + + - name: Detect Leaks on generate coverset from file + run: ./dancing-links --sudoku-gen samples/sudoku/sudoku_2.in + env: + HEAPCHECK: normal + + - name: Detect Leaks on generate coverset from str + run: ./dancing-links --sudoku-gen 0100002030000004 env: HEAPCHECK: normal diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index 339627b..d3013f9 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -5,6 +5,7 @@ on: push jobs: run-benchmarks: runs-on: ${{ matrix.os }} + timeout-minutes: 5 strategy: matrix: @@ -16,8 +17,10 @@ jobs: fetch-depth: 1 submodules: true - - name: Build - run: make + - name: Build (2x2) + run: make rebuild + env: + CFLAGS: -DGRID_SIZE=2 -DN_GRIDS=2 - name: Run simple problem, default args run: ./dancing-links --multiple-solutions < samples/5x5_5_pentoI.dat @@ -27,3 +30,20 @@ jobs: - name: Run complex problem, single solution run: ./dancing-links --single-solution < samples/5x8_2xAll_freePieces.dat + + - name: Run sudoku gen and solve + run: | + ./dancing-links --sudoku-gen samples/sudoku/sudoku_2.in > sudoku_test.dat + ./dancing-links --single-solution < sudoku_test.dat + ./dancing-links --sudoku-parse output.dat + + - name: Build (3x3) + run: make rebuild + env: + CFLAGS: -DGRID_SIZE=3 -DN_GRIDS=3 + + - name: Run sudoku gen and solve + run: | + ./dancing-links --sudoku-gen samples/sudoku/sudoku.in > sudoku_test.dat + ./dancing-links --single-solution < sudoku_test.dat + ./dancing-links --sudoku-parse output.dat diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml new file mode 100644 index 0000000..bb9c2b3 --- /dev/null +++ b/.github/workflows/valgrind.yml @@ -0,0 +1,30 @@ +name: valgrind + +on: push + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@master + with: + fetch-depth: 1 + submodules: true + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get -y -qq install valgrind + + - name: Build + run: make callgrind + + - name: Run valgrind on sudoku gen + run: ./dancing-links --sudoku-gen samples/sudoku/sudoku_2.in > sudoku_test.dat + + - name: Run valgrind on sudoku solve + run: ./dancing-links --single-solution < sudoku_test.dat + + - name: Run valgrind on sudoku parse + run: ./dancing-links --sudoku-parse output.dat diff --git a/.gitignore b/.gitignore index 6b08582..5a74426 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,7 @@ build # Cache files .cache # used by clang +*.idx # used by clang + +# perf files +gmon.out diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 775cace..7711ce4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,19 +12,15 @@ repos: hooks: - id: clang-format -repos: - repo: https://github.com/pocc/pre-commit-hooks rev: v1.1.1 hooks: - id: clang-tidy - args: - - -checks=clang-diagnostic-return-type + args: ['--warnings-as-errors', '--checks=bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-cplusplus-*,clang-analyzer-*,cppcoreguidelines-*'] -repos: - repo: https://github.com/bmorcos/pre-commit-hooks-cpp rev: 9a5aa38207bf557961110d6a4f7e3a9d352911f9 hooks: - - id: cpplint - args: - - --filter=-build/include_subdir,-readability/nolint,-whitespace/line_length,-readability/casting,-build/header_guard - id: cppcheck + - id: cpplint + args: ['--filter=-build/include_subdir,-readability/nolint,-whitespace/line_length,-readability/casting,-build/header_guard'] diff --git a/Makefile b/Makefile index a83e371..e3ff8a8 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ override CFLAGS += -Wall -Wextra -pedantic -std=c11 $(OPTIMIZATION) $(OPTIONS) $ UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Linux) ECHOFLAGS = -e - LDFLAGS = -Wl,-Ldeps/Unity/build/ + UNAME_S = -Wl,-Ldeps/Unity/build/ endif ifeq ($(UNAME_S),Darwin) CFLAGS += -Wno-unused-command-line-argument @@ -27,7 +27,8 @@ endif CC = gcc -C_FILES := $(wildcard src/*.c) +C_FILES := $(wildcard src/*.c) \ + $(wildcard src/**/*.c) C_FILES_TEST := $(wildcard test/*.c) C_FILES_TEST_DEPS := $(wildcard deps/Unity/src/*.c) diff --git a/compile_commands.json b/compile_commands.json index 62c7863..9a75034 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -1,56 +1,164 @@ [ - { - "directory": "/home/h3nnn4n/progs/dancing-links", - "arguments": [ - "gcc", - "-Wall", - "-Wextra", - "-pedantic", - "-std=gnu11", - "-O3", - "-Isrc", - "-Ideps/Unity/src", - "-o", - "/home/h3nnn4n/progs/dancing-links/build/src/config.o", - "-c", - "src/config.c" - ], - "file": "src/config.c" - }, - { - "directory": "/home/h3nnn4n/progs/dancing-links", - "arguments": [ - "gcc", - "-Wall", - "-Wextra", - "-pedantic", - "-std=gnu11", - "-O3", - "-Isrc", - "-Ideps/Unity/src", - "-o", - "/home/h3nnn4n/progs/dancing-links/build/src/links.o", - "-c", - "src/links.c" - ], - "file": "src/links.c" - }, - { - "directory": "/home/h3nnn4n/progs/dancing-links", - "arguments": [ - "gcc", - "-Wall", - "-Wextra", - "-pedantic", - "-std=gnu11", - "-O3", - "-Isrc", - "-Ideps/Unity/src", - "-o", - "/home/h3nnn4n/progs/dancing-links/build/src/main.o", - "-c", - "src/main.c" - ], - "file": "src/main.c" - } + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/parsers/sudoku.o", + "src/parsers/sudoku.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/parsers/sudoku.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/generators/sudoku.o", + "src/generators/sudoku.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/generators/sudoku.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/config.o", + "src/config.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/config.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/strtok.o", + "src/strtok.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/strtok.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/solution_store.o", + "src/solution_store.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/solution_store.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/main.o", + "src/main.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/main.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/file_utils.o", + "src/file_utils.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/file_utils.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/dlx_solver.o", + "src/dlx_solver.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/dlx_solver.c" + }, + { + "arguments": [ + "cc", + "-c", + "-Wall", + "-Wextra", + "-pedantic", + "-std=c11", + "-O3", + "-Isrc", + "-Ideps/Unity/src", + "-o", + "/home/h3nnn4n/progs/dancing-links/build/src/links.o", + "src/links.c" + ], + "directory": "/home/h3nnn4n/progs/dancing-links", + "file": "src/links.c" + } ] diff --git a/data_types.jl b/julia_code/data_types.jl similarity index 100% rename from data_types.jl rename to julia_code/data_types.jl diff --git a/defs.jl b/julia_code/defs.jl similarity index 100% rename from defs.jl rename to julia_code/defs.jl diff --git a/maker.jl b/julia_code/maker.jl similarity index 100% rename from maker.jl rename to julia_code/maker.jl diff --git a/pieces_pentaminoes.jl b/julia_code/pieces_pentaminoes.jl similarity index 100% rename from pieces_pentaminoes.jl rename to julia_code/pieces_pentaminoes.jl diff --git a/pieces_tetraminoes.jl b/julia_code/pieces_tetraminoes.jl similarity index 100% rename from pieces_tetraminoes.jl rename to julia_code/pieces_tetraminoes.jl diff --git a/samples/sudoku/sudoku.in b/samples/sudoku/sudoku.in new file mode 100644 index 0000000..eae33cb --- /dev/null +++ b/samples/sudoku/sudoku.in @@ -0,0 +1 @@ +600120384008459072000006005000264030070080006940003000310000050089700000502000190 diff --git a/samples/sudoku/sudoku_2.in b/samples/sudoku/sudoku_2.in new file mode 100644 index 0000000..b40500c --- /dev/null +++ b/samples/sudoku/sudoku_2.in @@ -0,0 +1 @@ +0100002030000004 diff --git a/samples/sudoku/sudoku_2_dot.in b/samples/sudoku/sudoku_2_dot.in new file mode 100644 index 0000000..257da14 --- /dev/null +++ b/samples/sudoku/sudoku_2_dot.in @@ -0,0 +1,4 @@ +.1.. +..2. +3... +...4 diff --git a/samples/sudoku/sudoku_empty.in b/samples/sudoku/sudoku_empty.in new file mode 100644 index 0000000..caa1832 --- /dev/null +++ b/samples/sudoku/sudoku_empty.in @@ -0,0 +1 @@ +000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/src/dlx_solver.c b/src/dlx_solver.c new file mode 100644 index 0000000..974d9ef --- /dev/null +++ b/src/dlx_solver.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include "config.h" +#include "dlx_solver.h" +#include "links.h" + +void dlx_solver() { + _links *m; + int x = 0; + int y = 0; + int n = 0; + + branchs = 0; + solutions_found = 0; + + fscanf(stdin, "%d", &y); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fscanf(stdin, "%d", &x); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fscanf(stdin, "%d", &n); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + + int * column_check = (int *)malloc(sizeof(int) * y); + int **set = (int **)malloc(sizeof(int *) * y); + + m = init_torus(); + + for (int i = 0; i < y; i++) { + insert_col_header(m); + set[i] = (int *)malloc(sizeof(int) * x); + column_check[i] = 0; + } + + for (int i = 0; i < x; i++) { + for (int j = 0; j < y; j++) { + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fscanf(stdin, "%d", &set[j][i]); + + column_check[j]++; + } + } + + for (int i = 0; i < y; i++) { + assert(column_check[i] > 0); + } + + free(column_check); + + if (!quiet) { + puts("--------------------"); + printf("Building in-memory model\n"); + } + + build_links_for_dancing(m, set, x, y); + + if (!quiet) { + puts("--------------------"); + printf("Starting solve process\n"); + puts("--------------------"); + } + + _ans *O = (_ans *)malloc(sizeof(_ans)); + memset(O, 0, sizeof(_ans)); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + dancing_links(m, 0, O, n); + + if (!quiet) { + puts("--------------------"); + } + + free_set(set, y); + free_ans(O); + free_links(m); +} diff --git a/src/dlx_solver.h b/src/dlx_solver.h new file mode 100644 index 0000000..7d04f33 --- /dev/null +++ b/src/dlx_solver.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __DLX_SOLVER +#define __DLX_SOLVER + +void dlx_solver(); + +#endif /* end of include guard */ diff --git a/src/file_utils.c b/src/file_utils.c new file mode 100644 index 0000000..f6ea919 --- /dev/null +++ b/src/file_utils.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "file_utils.h" + +int file_exists(char *filepath) { + struct stat buffer = {0}; + + return (stat(filepath, &buffer) == 0); +} + +// cppcheck-suppress unusedFunction +int readall(FILE *in, char **dataptr, size_t *sizeptr) { + // https://stackoverflow.com/a/44894946 + /* This function returns one of the READALL_ constants above. + If the return value is zero == READALL_OK, then: + (*dataptr) points to a dynamically allocated buffer, with + (*sizeptr) chars read from the file. + The buffer is allocated for one extra char, which is NUL, + and automatically appended after the data. + Initial values of (*dataptr) and (*sizeptr) are ignored. + */ + char * data = NULL; + char * temp = NULL; + size_t size = 0; + size_t used = 0; + + /* None of the parameters can be NULL. */ + if (in == NULL || dataptr == NULL || sizeptr == NULL) + return READALL_INVALID; + + /* A read error already occurred? */ + if (ferror(in)) + return READALL_ERROR; + + while (1) { + if (used + READALL_CHUNK + 1 > size) { + size = used + READALL_CHUNK + 1; + + /* Overflow check. Some ANSI C compilers + may optimize this away, though. */ + if (size <= used) { + free(data); + return READALL_TOOMUCH; + } + + temp = realloc(data, size); + if (temp == NULL) { + free(data); + return READALL_NOMEM; + } + data = temp; + } + + assert(data != NULL); + size_t n = fread(data + used, 1, READALL_CHUNK, in); + if (n == 0) + break; + + used += n; + } + + if (ferror(in)) { + free(data); + return READALL_ERROR; + } + + temp = realloc(data, used + 1); + if (temp == NULL) { + free(data); + return READALL_NOMEM; + } + data = temp; + data[used] = '\0'; + + *dataptr = data; + *sizeptr = used; + + return READALL_OK; +} diff --git a/src/file_utils.h b/src/file_utils.h new file mode 100644 index 0000000..c767f96 --- /dev/null +++ b/src/file_utils.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SRC_FILE_UTILS_H_ +#define SRC_FILE_UTILS_H_ + +/* Size of each input chunk to be read and allocate for. */ +#ifndef READALL_CHUNK +#define READALL_CHUNK 262144 +#endif + +#define READALL_OK 0 /* Success */ +#define READALL_INVALID -1 /* Invalid parameters */ +#define READALL_ERROR -2 /* Stream error */ +#define READALL_TOOMUCH -3 /* Too much input */ +#define READALL_NOMEM -4 /* Out of memory */ + +void ensure_directory_exists(char *directory); +int file_exists(char *filepath); +int mkdir_p(const char *path); +int readall(FILE *in, char **dataptr, size_t *sizeptr); + +#endif /* SRC_FILE_UTILS_H_ */ diff --git a/src/generators/sudoku.c b/src/generators/sudoku.c new file mode 100644 index 0000000..f077b83 --- /dev/null +++ b/src/generators/sudoku.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include "../file_utils.h" +#include "generators/sudoku.h" + +void sudoku_generator(FILE *sudoku_output, char *sudoku_input) { + uint16_t n_clues = 0; + uint16_t **clues = load_and_parse_sudoku(sudoku_input, &n_clues); + + uint16_t n_columns = N_CELLS * 4; + uint16_t n_rows = BLOCK_SIZE * BLOCK_SIZE * BLOCK_SIZE - n_clues * (MAX_VALUE - 1); + uint16_t n_rows_check = 0; + uint16_t n_n = N_CELLS; + + sudoku_check_t sudoku_check; + + sudoku_check.column_check = (int *)malloc(sizeof(int) * n_columns); + sudoku_check.row_check = (int *)malloc(sizeof(int) * n_rows); + sudoku_check.grid_check = (int **)malloc(sizeof(int *) * n_columns); + + for (int i = 0; i < n_rows; i++) { + sudoku_check.row_check[i] = 0; + } + + for (int i = 0; i < n_columns; i++) { + sudoku_check.grid_check[i] = (int *)malloc(sizeof(int) * n_rows); + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + memset(sudoku_check.grid_check[i], 0, sizeof(int) * n_rows); + sudoku_check.column_check[i] = 0; + } + + fprintf(sudoku_output, "%d %d %d\n", n_columns, n_rows, n_n); + + /*printf("making coverset with clues\n");*/ + + for (int i = 0; i < n_clues; i++) { + uint16_t row = clues[i][0]; + uint16_t column = clues[i][1]; + uint16_t value = clues[i][2]; + + /*printf("%2d %d %d %d\n", i, row, column, value);*/ + build_cover_set(sudoku_output, row, column, value, &sudoku_check); + n_rows_check += 1; + } + + for (int value_i = 1; value_i <= MAX_VALUE; value_i++) { + for (int row_i = 0; row_i < ROW_LENGTH; row_i++) { + for (int column_i = 0; column_i < COLUMN_LENGTH; column_i++) { + int skip = 0; + + for (int i = 0; i < n_clues; i++) { + uint16_t row = clues[i][0]; + uint16_t column = clues[i][1]; + + if (row == row_i && column == column_i) { + skip = 1; + break; + } + } + + if (skip) + continue; + + build_cover_set(sudoku_output, row_i, column_i, value_i, &sudoku_check); + n_rows_check += 1; + } + } + } + + /*printf("\n");*/ + + if (n_rows != n_rows_check) { + printf("%d %d \n", n_rows, n_rows_check); + } + + assert(n_rows == n_rows_check); + + free(sudoku_check.row_check); + free(sudoku_check.column_check); + for (int i = 0; i < n_columns; i++) { + free(sudoku_check.grid_check[i]); + } + free(sudoku_check.grid_check); + + unload_clues(clues, n_clues); +} + +void build_cover_set(FILE *f, uint16_t row, uint16_t column, uint16_t value, + __attribute__((unused)) sudoku_check_t *sudoku_check) { + /*printf("%d %d %c\n", row, column, value);*/ + assert(value > 0); + assert(value <= MAX_VALUE); + + assert(row < ROW_LENGTH); + assert(column < ROW_LENGTH); + + int cell_written = 0; + // Position Constrain + // Each cell has to be occupied by exactly one number + uint16_t position = column * ROW_LENGTH + row; + for (int row_i = 0; row_i < ROW_LENGTH; row_i++) { + for (int col_i = 0; col_i < ROW_LENGTH; col_i++) { + if (position == col_i * ROW_LENGTH + row_i) { + fprintf(f, "1 "); + cell_written += 1; + } else { + fprintf(f, "0 "); + } + } + } + + fflush(f); + assert(cell_written == 1); + + fprintf(f, " "); + + // Row constraint + // Each row has to have exactly one of each number + + // Build an index where the row and the value are the coordinates + // Note that row and column are 0 indexed, value is not + uint16_t row_coordinate = row * (ROW_LENGTH) + value - 1; + for (int row_i = 0; row_i < ROW_LENGTH; row_i++) { + for (int value_i = 0; value_i < ROW_LENGTH; value_i++) { + if (row_coordinate == row_i * (ROW_LENGTH) + value_i) { + fprintf(f, "1 "); + cell_written += 1; + } else { + fprintf(f, "0 "); + } + } + } + + fflush(f); + assert(cell_written == 2); + + fprintf(f, " "); + + // Column Constraint + // Each column has to have exactly one of each number + + // Build an index where the column and the value are the coordinates + // Note that row and column are 0 indexed, value is not + uint16_t col_coordinate = column * (ROW_LENGTH) + value - 1; + for (int col_i = 0; col_i < ROW_LENGTH; col_i++) { + for (int value_i = 0; value_i < ROW_LENGTH; value_i++) { + if (col_coordinate == col_i * (ROW_LENGTH) + value_i) { + fprintf(f, "1 "); + cell_written += 1; + } else { + fprintf(f, "0 "); + } + } + } + + fflush(f); + assert(cell_written == 3); + + fprintf(f, " "); + + // Block Constraint + // Each block has to have exactly one of each number + uint16_t block_coordinate = global_to_in_block_position(row, column, value); + for (int block_i = 0; block_i < ROW_LENGTH; block_i++) { + for (int value_i = 0; value_i < ROW_LENGTH; value_i++) { + if (block_coordinate == block_i * (ROW_LENGTH) + value_i) { + fprintf(f, "1 "); + cell_written += 1; + } else { + fprintf(f, "0 "); + } + } + } + + fflush(f); + assert(cell_written == 4); + + fprintf(f, "\n"); +} + +uint16_t global_to_in_block_position(uint16_t row, uint16_t column, uint16_t value) { + // Figure out which block we are dealing with + uint16_t block_row = row / GRID_SIZE; + uint16_t block_col = column / GRID_SIZE; + uint16_t block_number = block_col * N_GRIDS + block_row; + + // Figure out where in the block we are + // uint16_t in_block_row = row % grid_size; + // uint16_t in_block_col = column % grid_size; + // uint16_t in_block_position = in_block_col * (grid_size) + in_block_row; + + // Build a global coordinate that combines the block number with the value inside + // Note that row and column are 0 indexed, value is not + uint16_t block_coordinate = block_number * ROW_LENGTH + value - 1; + return block_coordinate; +} + +uint16_t **load_and_parse_sudoku(char *input, uint16_t *n_clues) { + uint16_t loaded_from_file = 0; + char * data = NULL; + size_t data_len = 0; + uint16_t clue_count = 0; + + // A sudoku wont ever have more than N_CELLS clues + uint16_t **clues = (uint16_t **)malloc(sizeof(uint16_t *) * N_CELLS); + + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + memset(clues, 0, sizeof(uint16_t *) * N_CELLS); + + if (file_exists(input)) { + FILE *f = fopen(input, "rt"); + readall(f, &data, &data_len); + fclose(f); + loaded_from_file = 1; + } else { + data = input; + data_len = strlen(data); + } + + /*printf("parsing %s\n", data);*/ + + for (size_t i = 0; i < data_len; i++) { + char value = data[i]; + + // FIXME(h3nnn4n): Skip whitespace + // FIXME(h3nnn4n): The '9' should use MAX_VALUE as a char + // Not a clue. It is an empty cell + if (value < '1' || value > '9') { + continue; + } + + clues[clue_count] = (uint16_t *)malloc(sizeof(uint16_t) * 3); + uint16_t row = i / MAX_VALUE; + uint16_t col = i % MAX_VALUE; + clues[clue_count][0] = row; + clues[clue_count][1] = col; + clues[clue_count][2] = value - '0'; + + /*printf("%2d %d %d %d\n", clue_count, row, col, clues[clue_count][2] [> the parsed value <]);*/ + + clue_count++; + } + + *n_clues = clue_count; + + /*printf("finished parsing\n");*/ + + if (loaded_from_file) { + free(data); + } + + return clues; +} + +void unload_clues(uint16_t **clues, __attribute__((unused)) uint16_t n_clues) { + for (int i = 0; i < N_CELLS; i++) { + if (clues[i] == NULL) { + continue; + } + + free(clues[i]); + } + + free(clues); +} diff --git a/src/generators/sudoku.h b/src/generators/sudoku.h new file mode 100644 index 0000000..76629a5 --- /dev/null +++ b/src/generators/sudoku.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __GENERATORS_SUDOKU +#define __GENERATORS_SUDOKU + +#include +#include + +// TODO(h3nnn4n) These shouldnt be hardcoded. They could be either infered from +// the input size, or taken from the cli. People usually only care about the +// 9x9 sudoku, so we should be fine for a while. As a stop-gap solution we are +// setting it at compile time. +#if !defined(GRID_SIZE) +#define GRID_SIZE 3 +#define N_GRIDS 3 +#endif + +#define BLOCK_SIZE (GRID_SIZE * GRID_SIZE) +#define ROW_LENGTH (GRID_SIZE * N_GRIDS) +#define COLUMN_LENGTH (GRID_SIZE * N_GRIDS) +#define N_CELLS (GRID_SIZE * GRID_SIZE * N_GRIDS * N_GRIDS) +#define MAX_VALUE (GRID_SIZE * GRID_SIZE) + +typedef struct { + int * row_check; + int * column_check; + int **grid_check; +} sudoku_check_t; + +void sudoku_generator(FILE *sudoku_output, char *sudoku_input); +void build_cover_set(FILE *f, uint16_t row, uint16_t column, uint16_t value, sudoku_check_t *sudoku_check_t); +uint16_t global_to_in_block_position(uint16_t row, uint16_t column, uint16_t value); +uint16_t **load_and_parse_sudoku(char *input, uint16_t *n_clues); +void unload_clues(uint16_t **clues, uint16_t n_clues); + +#endif /* end of include guard */ diff --git a/src/links.c b/src/links.c index 3751d5c..eacf7ad 100644 --- a/src/links.c +++ b/src/links.c @@ -21,6 +21,7 @@ #include "config.h" #include "links.h" +#include "solution_store.h" uint64_t branchs; uint64_t solutions_found; @@ -76,6 +77,8 @@ void dancing_links(_links *h, int k, _ans *ans, int n) { printf("Solved. Took %lu steps\n", branchs); // Line 1 printf("Found %lu solutions\n", solutions_found); + store_begin(); + int w; _ans * ans_tmp; _links *p; @@ -84,9 +87,11 @@ void dancing_links(_links *h, int k, _ans *ans, int n) { } c = p; for (w = 0; p != c || w == 0; p = p->R, w++) { - printf("%2.d ", p->C->n > n ? p->C->n - n : p->C->n); + printf("%2.d ", p->C->n % n); + store_add_cell(p->C->n); } puts(""); + store_begin_new_row(); } if (ans_tmp->next == NULL) { @@ -94,11 +99,15 @@ void dancing_links(_links *h, int k, _ans *ans, int n) { } c = p; for (w = 0; p != c || w == 0; p = p->R, w++) { - printf("%2.d ", p->C->n > n ? p->C->n - n : p->C->n); + printf("%2.d ", p->C->n % n); + store_add_cell(p->C->n); } puts(""); + store_begin_new_row(); } + store_end(); + puts("--------------------"); return; // Line 1 @@ -216,9 +225,7 @@ void build_links_for_dancing(_links *h, int **m, int x, int y) { } void insert_col_header(_links *h) { - _links *new = (_links *)malloc(sizeof(_links)); - static int c = 0; - printf("%3d %p - alloc\n", c++, new); + _links *new = (_links *)malloc(sizeof(_links)); _links *a; a = h->L; @@ -251,8 +258,7 @@ void free_ans(_ans *O) { } void free_links(_links *h) { - _links * a = h->R; - static int cc = 0; + _links *a = h->R; while (a != h) { _links *d = a->D; @@ -263,7 +269,6 @@ void free_links(_links *h) { } _links *b = a->R; - printf("%3d %p - free\n", cc++, a); free(a); a = b; } diff --git a/src/main.c b/src/main.c index 627dd69..508ee1b 100644 --- a/src/main.c +++ b/src/main.c @@ -15,17 +15,24 @@ * */ +#include #include #include #include +#include #include "config.h" +#include "dlx_solver.h" +#include "generators/sudoku.h" #include "links.h" +#include "parsers/sudoku.h" struct option long_options[] = {{"quiet", no_argument, &quiet, 1}, {"single-solution", no_argument, &single_solution, 1}, {"multiple-solutions", no_argument, &single_solution, 0}, {"file", required_argument, 0, 'f'}, + {"sudoku-gen", required_argument, 0, 's'}, + {"sudoku-parse", required_argument, 0, 't'}, {0, 0, 0, 0}}; int main(int argc, char **argv) { @@ -54,65 +61,41 @@ int main(int argc, char **argv) { // TODO(h3nnn4n): Read from file break; - case '?': - /* getopt_long already printed an error message. */ - break; + case 's': { + assert(optarg != NULL); + size_t arg_len = strlen(optarg) + 2; + char * sudoku_input = malloc(sizeof(char) * arg_len); + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + memcpy(sudoku_input, optarg, sizeof(char) * (strlen(optarg) + 1)); - default: abort(); - } - } + sudoku_generator(stdout, sudoku_input); - _links *m; - int x = 0; - int y = 0; - int n = 0; + free(sudoku_input); + } + return EXIT_SUCCESS; - branchs = 0; - solutions_found = 0; + case 't': { + assert(optarg != NULL); + size_t arg_len = strlen(optarg) + 2; + char * sudoku_input = malloc(sizeof(char) * arg_len); + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + memcpy(sudoku_input, optarg, sizeof(char) * (strlen(optarg) + 1)); - fscanf(stdin, "%d", &y); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) - fscanf(stdin, "%d", &x); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) - fscanf(stdin, "%d", &n); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + sudoku_parse(sudoku_input); - int **set = (int **)malloc(sizeof(int *) * y); - - m = init_torus(); - - for (int i = 0; i < y; i++) { - insert_col_header(m); - set[i] = (int *)malloc(sizeof(int) * x); - } + free(sudoku_input); + } + return EXIT_SUCCESS; + case '?': + return EXIT_FAILURE; + /* getopt_long already printed an error message. */ + break; - for (int i = 0; i < x; i++) { - for (int j = 0; j < y; j++) { - // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) - fscanf(stdin, "%d", &set[j][i]); + default: abort(); } } - if (!quiet) { - puts("--------------------"); - printf("Building in-memory model\n"); - } - - build_links_for_dancing(m, set, x, y); - - if (!quiet) { - puts("--------------------"); - printf("Starting solve process\n"); - puts("--------------------"); - } - - _ans *O = (_ans *)malloc(sizeof(_ans)); - dancing_links(m, 0, O, n); - - if (!quiet) { - puts("--------------------"); - } - - free_set(set, y); - free_ans(O); - free_links(m); + dlx_solver(); return EXIT_SUCCESS; } diff --git a/src/parsers/sudoku.c b/src/parsers/sudoku.c new file mode 100644 index 0000000..0392a8b --- /dev/null +++ b/src/parsers/sudoku.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include + +#include "file_utils.h" +#include "generators/sudoku.h" +#include "parsers/sudoku.h" +#include "strtok.h" + +void sudoku_parse(char *input) { + const char separator[2] = " "; + uint16_t loaded_from_file = 0; + char * token_data = NULL; + char * data = NULL; + size_t data_len = 0; + + char sudoku_solution[ROW_LENGTH][COLUMN_LENGTH] = {0}; + + if (file_exists(input)) { + FILE *f = fopen(input, "rt"); + readall(f, &data, &data_len); + fclose(f); + loaded_from_file = 1; + } else { + data = input; + data_len = strlen(data); + } + + char *token = strtok_r(data, separator, &token_data); + + while (token != NULL) { + uint16_t values[4]; + + for (int i = 0; i < 4; i++) { + do { + values[i] = atoi(token); + token = strtok_r(NULL, separator, &token_data); + } while (token != NULL && values[i] == 0); + + if (token == NULL) { + break; + } + } + + if (token == NULL) { + break; + } + + uint16_t position = values[0] - 1; + uint16_t row = position % ROW_LENGTH; + uint16_t column = position / ROW_LENGTH; + uint16_t value = (values[1] - 1) % ROW_LENGTH + 1; + + assert(row < ROW_LENGTH); + assert(column < COLUMN_LENGTH); + + assert(value > 0); + assert(value <= MAX_VALUE); + + sudoku_solution[row][column] = value; + } + + for (int row = 0; row < ROW_LENGTH; row++) { + for (int col = 0; col < COLUMN_LENGTH; col++) { + printf("%d ", sudoku_solution[row][col]); + if ((col + 1) % GRID_SIZE == 0 && col > 0) { + printf(" "); + } + } + printf("\n"); + + if ((row + 1) % GRID_SIZE == 0 && row > 0) { + printf("\n"); + } + } + + sudoku_check(sudoku_solution); + + if (loaded_from_file) { + free(data); + } +} + +void sudoku_check(char sudoku_solution[ROW_LENGTH][COLUMN_LENGTH]) { + int row_check[ROW_LENGTH][COLUMN_LENGTH] = {0}; + int col_check[ROW_LENGTH][COLUMN_LENGTH] = {0}; + int value_count_check[MAX_VALUE] = {0}; + + for (int row = 0; row < ROW_LENGTH; row++) { + for (int col = 0; col < COLUMN_LENGTH; col++) { + int value = sudoku_solution[row][col]; + value_count_check[value - 1] += 1; + + if (row_check[row][value - 1] != 0) { + fprintf(stderr, "Sudoku row check failed on %d %d %d\n", row, col, value); + abort(); + } else { + row_check[row][value - 1] = value; + } + + if (col_check[col][value - 1] != 0) { + fprintf(stderr, "Sudoku column check failed on %d %d %d\n", row, col, value); + abort(); + } else { + col_check[col][value - 1] = value; + } + } + } + + // I think this might be redundant :thinking: + for (int value_i = 0; value_i < MAX_VALUE; value_i++) { + if (value_count_check[value_i] != MAX_VALUE) { + fprintf(stderr, "Sudoku value count check failed on %d\n", value_i + 1); + abort(); + } + } +} diff --git a/src/parsers/sudoku.h b/src/parsers/sudoku.h new file mode 100644 index 0000000..70a2a1c --- /dev/null +++ b/src/parsers/sudoku.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SRC_PARSERS_SUDOKU_H_ +#define SRC_PARSERS_SUDOKU_H_ + +#include "generators/sudoku.h" + +void sudoku_parse(char *input); +void sudoku_check(char sudoku_solution[ROW_LENGTH][COLUMN_LENGTH]); + +#endif /* SRC_PARSERS_SUDOKU_H_ */ diff --git a/src/solution_store.c b/src/solution_store.c new file mode 100644 index 0000000..c58b061 --- /dev/null +++ b/src/solution_store.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +#include "solution_store.h" + +FILE *f = NULL; + +void store_begin() { f = fopen("output.dat", "wt"); } + +void store_end() { fclose(f); } + +void store_begin_new_row() { + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fprintf(f, "\n"); +} + +void store_add_cell(int cell_value) { + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fprintf(f, "%2d ", cell_value); +} diff --git a/src/solution_store.h b/src/solution_store.h new file mode 100644 index 0000000..9ad4fe8 --- /dev/null +++ b/src/solution_store.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SRC_SOLUTION_STORE_H_ +#define SRC_SOLUTION_STORE_H_ + +void store_begin(); +void store_add_cell(int cell_value); +void store_begin_new_row(); +void store_end(); + +#endif // SRC_SOLUTION_STORE_H_ diff --git a/src/strtok.c b/src/strtok.c new file mode 100644 index 0000000..3fa3cd2 --- /dev/null +++ b/src/strtok.c @@ -0,0 +1,42 @@ +/* + * No Copyright needed, it is public domain + * + * public domain strtok_r() by Charlie Gordon + * + * from comp.lang.c 9/14/2007 + * + * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 + * + * (Declaration that it's public domain): + * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c + */ + +#include + +#include "strtok.h" + +char *strtok_r(char *str, const char *delim, char **nextp) { + char *ret; + + if (str == NULL) { + str = *nextp; + } + + str += strspn(str, delim); + + if (*str == '\0') { + return NULL; + } + + ret = str; + + str += strcspn(str, delim); + + if (*str) { + *str++ = '\0'; + } + + *nextp = str; + + return ret; +} diff --git a/src/strtok.h b/src/strtok.h new file mode 100644 index 0000000..d57ddc2 --- /dev/null +++ b/src/strtok.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SRC_STRTOK_H_ +#define SRC_STRTOK_H_ + +char *strtok_r(char *, const char *, char **); + +#endif /* SRC_STRTOK_H_ */ diff --git a/test/test_dancing_links.c b/test/test_dancing_links.c index 8b687d8..1cae6ac 100644 --- a/test/test_dancing_links.c +++ b/test/test_dancing_links.c @@ -1,5 +1,23 @@ +/* + * Copyright (C) 2015,2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #include #include +#include #include #include @@ -17,9 +35,9 @@ void test_single_solution_solve() { FILE *f = fopen("./samples/5x5_5_pentoI.dat", "rt"); - fscanf(f, "%d", &y); - fscanf(f, "%d", &x); - fscanf(f, "%d", &n); + fscanf(f, "%d", &y); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fscanf(f, "%d", &x); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fscanf(f, "%d", &n); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) set = (int **)malloc(sizeof(int *) * y); @@ -32,6 +50,7 @@ void test_single_solution_solve() { for (i = 0; i < x; i++) { for (j = 0; j < y; j++) { + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) fscanf(f, "%d", &set[j][i]); } } @@ -41,6 +60,8 @@ void test_single_solution_solve() { build_links_for_dancing(m, set, x, y); _ans *O = (_ans *)malloc(sizeof(_ans)); + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + memset(O, 0, sizeof(_ans)); dancing_links(m, 0, O, n); TEST_ASSERT_EQUAL(solutions_found, 1); @@ -58,9 +79,9 @@ void test_multiple_solutions_solve() { FILE *f = fopen("./samples/5x5_5_pentoI.dat", "rt"); - fscanf(f, "%d", &y); - fscanf(f, "%d", &x); - fscanf(f, "%d", &n); + fscanf(f, "%d", &y); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fscanf(f, "%d", &x); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + fscanf(f, "%d", &n); // NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) set = (int **)malloc(sizeof(int *) * y); @@ -73,6 +94,7 @@ void test_multiple_solutions_solve() { for (i = 0; i < x; i++) { for (j = 0; j < y; j++) { + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) fscanf(f, "%d", &set[j][i]); } } @@ -82,6 +104,8 @@ void test_multiple_solutions_solve() { build_links_for_dancing(m, set, x, y); _ans *O = (_ans *)malloc(sizeof(_ans)); + // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) + memset(O, 0, sizeof(_ans)); dancing_links(m, 0, O, n); TEST_ASSERT_EQUAL(solutions_found, 240); diff --git a/test/test_sudoku_gen.c b/test/test_sudoku_gen.c new file mode 100644 index 0000000..2e04e08 --- /dev/null +++ b/test/test_sudoku_gen.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015,2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include + +void test_gen() { + // The actual testing is made using built in assertions in the function + FILE *devnull = fopen("/dev/null", "wt"); + char sudoku_input[] = "000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + sudoku_generator(devnull, sudoku_input); + fclose(devnull); +} + +void setUp() {} +void tearDown() {} + +int main() { + UNITY_BEGIN(); + + RUN_TEST(test_gen); + + return UNITY_END(); +} diff --git a/test/test_test.c b/test/test_test.c index 0ecdebf..7cff34e 100644 --- a/test/test_test.c +++ b/test/test_test.c @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2015,2021 h3nnn4n, aka Renan S. Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #include void test_test() { TEST_PASS(); }