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(); }