diff --git a/DroidSudSolve.iml b/DroidSudSolve.iml new file mode 100644 index 0000000..4df0d25 --- /dev/null +++ b/DroidSudSolve.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 51cfdae..f04b4cd 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# droidsudsolve \ No newline at end of file +Sudoku solver for Android (open source) + +This sudoku solver is written by Ashik (ashik@ashikslab.in) +using the libexact software library for solving exact cover problems. + +libexact was developed by Petteri Kaski and Olli Pottonen +at the Helsinki University of technology. diff --git a/app/app.iml b/app/app.iml new file mode 100644 index 0000000..650b8e7 --- /dev/null +++ b/app/app.iml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..0500367 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 17 + buildToolsVersion "25.0.2" + + defaultConfig { + applicationId "com.oldestmonk.droidsudsolve" + minSdkVersion 8 + targetSdkVersion 17 + + ndk { + moduleName "exactcaller" + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } + externalNativeBuild { + ndkBuild { + path '../../../Dropbox/workspace/DroidSudSolve/jni/Android.mk' + } + } +} + +dependencies { + compile 'com.android.support:gridlayout-v7:18.0.0' + compile 'com.android.support:support-v4:18.0.0' +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..be62e1f --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/oldestmonk/droidsudsolve/MainActivity.java b/app/src/main/java/com/oldestmonk/droidsudsolve/MainActivity.java new file mode 100644 index 0000000..87257de --- /dev/null +++ b/app/src/main/java/com/oldestmonk/droidsudsolve/MainActivity.java @@ -0,0 +1,495 @@ +package com.oldestmonk.droidsudsolve; + +import android.R.color; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.graphics.Color; +import android.text.style.EasyEditSpan; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Gravity; +import android.widget.Button; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; +import android.view.ViewGroup.LayoutParams; +import java.util.TreeSet; +import java.util.TreeSet; + +import com.oldestmonk.droidsudsolve.R; + +public class MainActivity extends Activity { + private static final int MENU_CLEARALL = 1; + private static final int MENU_EXITAPP = 2; + private TextView[][] tvarray; + private Button[] numbtns; + int curposinmatrix; + protected static final int[] bresids={ + R.drawable.back1, + R.drawable.back2, + R.drawable.back3, + R.drawable.back8, + R.drawable.back9, + R.drawable.back4, + R.drawable.back7, + R.drawable.back6, + R.drawable.back5 + }; + public MainActivity() { + tvarray = new TextView[9][9]; + numbtns = new Button[10]; + curposinmatrix = -1; + + } + public native int[] exactcaller(int[] jproblem); + static { + System.loadLibrary("exactcaller"); + } + protected void setTvBack(int i, int j) { + + int ii = i%3; + int jj = j%3; + int cresid = bresids[ii+jj*3]; + tvarray[i][j].setBackgroundResource(cresid); + } + protected void deactivatePreviousTv(int cpinmat) { + int i = cpinmat/9; + int j = cpinmat%9; + tvarray[i][j].setBackgroundColor(Color.WHITE); + curposinmatrix = -1; + } + protected boolean alContainsVal(TreeSet al, int val) { + for( int x : al ) + { + if(x==val) + return true; + } + return false; + } + protected void activateTv(int i, int j) { + if(curposinmatrix!=-1) { + deactivatePreviousTv(curposinmatrix); + } + tvarray[i][j].setBackgroundColor(Color.YELLOW); + curposinmatrix = i*9+j; + for(int u=0; u<9; u++) { + for(int v=0; v<9; v++) { + setTvBack(u,v); + } + } + tvarray[i][j].setBackgroundColor(Color.YELLOW); + } + int getValAt(int i, int j) { + String str = tvarray[i][j].getText().toString(); + if(str.equals("")) { + return 0; + } + else { + return Integer.parseInt(str); + } + } + TreeSet getErrors() { + TreeSet retval = new TreeSet(); + int[][] curvalues = new int[9][9]; + for(int i=0; i<9; i++) { + for(int j=0; j<9; j++) { + curvalues[i][j] = getValAt(i, j); + } + } + // row and column checks are done by the following for loop + for(int i=0; i<9; i++) { + //check for row i and column i + for(int j=0; j<9; j++) { + for(int k=j+1; k<9; k++) { + if(curvalues[i][j]!=0 && (curvalues[i][j]== curvalues[i][k])){ + retval.add(i*9+j); + retval.add(i*9+k); + } + if(curvalues[j][i]!=0 && (curvalues[j][i]== curvalues[k][i])){ + retval.add(j*9+i); + retval.add(k*9+i); + } + } + } + } + for(int ci=0; ci<3; ci++) { + for(int cj=0; cj<3; cj++) { + int cbegini = ci*3; + int cbeginj = cj*3; + for(int iti=cbegini; iti al = getErrors(); + for (Integer val : al) + { + int i = val/9; + int j = val%9; + tvarray[i][j].setTextColor(Color.RED); + } + if(alContainsVal(al, curi*9+curj)) { + activateTv(curi, curj); + } + } + protected void changeValueAt(int curposi, int curposj, int val) { + if(val==0) { + tvarray[curposi][curposj].setText(""); + } + else { + tvarray[curposi][curposj].setText(String.valueOf(val)); + } + curposinmatrix = -1; + checkValuesAndWarn(curposi, curposj); + } + protected void handleBtnPress(int i) { + if(curposinmatrix==-1) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage("Please click on a cell to select it, then click on a value to enter that value to the cell!") + .setCancelable(false) + .setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return; + } + else { + int curposi = curposinmatrix/9; + int curposj = curposinmatrix%9; + changeValueAt(curposi, curposj, i); + } + } + void fillSolution(int[] solution, boolean[] wasempty) { + for(int n=0; n<81; n++) { + int i = n/9; + int j = n%9; + tvarray[i][j].setText(String.valueOf(solution[n])); + if(wasempty[n]) { + tvarray[i][j].setTextColor(Color.GREEN); + } + setTvBack(i, j); + } + } + void fillCell(int i, int j, int val) { + tvarray[i][j].setText(String.valueOf(val)); + tvarray[i][j].setBackgroundColor(Color.GREEN); + } + boolean isGoodSol(int[] sol) { + if(sol[0]==100) + return false; + for(int i=0; i<81; i++) { + if(sol[i]==0) + return false; + } + return true; + } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + setContentView(R.layout.activity_main); + + TableLayout tl = (TableLayout) findViewById(R.id.sudokutable); + + for (int j = 0; j < 9; j++) { + TableRow tr = new TableRow(this); +// tr.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, +// LayoutParams.WRAP_CONTENT)); + tr.setLayoutParams(new TableRow.LayoutParams( + LayoutParams.MATCH_PARENT,0,0.1f)); + for (int i = 0; i < 9; i++) { + TextView tView = new TextView(this); + + tView.setLayoutParams(new TableRow.LayoutParams(0, + LayoutParams.WRAP_CONTENT, 1f)); + //tView.setBackgroundResource(R.drawable.back); + tView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL); +// tView.setLayoutParams(new LayoutParams( +// LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); + //set it's height = width + + tr.addView(tView); + tvarray[i][j] = tView; + + int cellht = getResources().getDisplayMetrics().widthPixels/11; + tView.setHeight(cellht); + setTvBack(i,j); + final int finali = i; + final int finalj = j; + tvarray[i][j].setOnClickListener(new TextView.OnClickListener() { + @Override + public void onClick(View v) { + activateTv(finali,finalj); + } + }); + + } + tl.addView(tr); + } + TableLayout tnt = (TableLayout) findViewById(R.id.numtable); + TableRow tr2 = new TableRow(this); + tr2.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.WRAP_CONTENT)); + int bside = getResources().getDisplayMetrics().widthPixels/10; + for(int i=1; i<6; i++) { + Button numbtn = new Button(this); + + numbtn.setText(String.valueOf(i)); + tr2.addView(numbtn); + numbtns[i] = numbtn; + } + tnt.addView(tr2); + TableRow tr3 = new TableRow(this); + tr3.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.WRAP_CONTENT)); + for(int i=6; i<10; i++) { + Button numbtn = new Button(this); + + numbtn.setText(String.valueOf(i)); + tr3.addView(numbtn); + numbtns[i] = numbtn; + } + + Button numbtn = new Button(this); + + numbtn.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL); + numbtn.setText("X"); + numbtn.setBackgroundColor(Color.RED); + tr3.addView(numbtn); + numbtns[0] = numbtn; + tnt.addView(tr3); + + for(int i=0; i<10; i++) { + final int finali = i; + numbtns[i].setOnClickListener(new TextView.OnClickListener() { + @Override + public void onClick(View v) { + handleBtnPress(finali); + } + }); + } + TableRow tr4 = new TableRow(this); + tr4.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.MATCH_PARENT)); + Button hintbtn = new Button(this); + hintbtn.setText("Hint"); + final Activity factivity = this; + hintbtn.setOnClickListener(new Button.OnClickListener() { + @Override + public void onClick(View v) { + TreeSet el = getErrors(); + if(!el.isEmpty()) { + //show alertdialog + AlertDialog.Builder builder = new AlertDialog.Builder(factivity); + builder.setMessage("Error in your input. Please correct values marked in red!") + .setCancelable(false) + .setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return; + } + if(curposinmatrix==-1 || getValAt(curposinmatrix/9, curposinmatrix%9)!=0) { + AlertDialog.Builder builder = new AlertDialog.Builder(factivity); + builder.setMessage( + "First click on an empty cell to select it," + + " then click on the Hint Button." + + " I will fill that cell with a correct value!") + .setCancelable(false) + .setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return; + } + int ci = curposinmatrix/9; + int cj = curposinmatrix%9; + final int[] jproblem = new int[81]; + for(int i=0; i<9; i++) { + + for(int j=0; j<9; j++) { + jproblem[i*9+j] = getValAt(i, j); + } + } + + int[] solution = exactcaller(jproblem); + if(isGoodSol(solution)) { + fillCell(ci, cj, solution[ci*9+cj]); + } + else { + AlertDialog.Builder builder = new AlertDialog.Builder(factivity); + builder.setMessage("The puzzle you entered has no solutions!") + .setCancelable(false) + .setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return; + } + } + }); + tr4.addView(hintbtn); + + Button solvebtn = new Button(this); + solvebtn.setText("SOLVE!"); + //final Activity factivity = this; + solvebtn.setOnClickListener(new Button.OnClickListener() { + @Override + public void onClick(View v) { + TreeSet el = getErrors(); + if(!el.isEmpty()) { + //show alertdialog + AlertDialog.Builder builder = new AlertDialog.Builder(factivity); + builder.setMessage("Error in your input. Please correct values marked in red!") + .setCancelable(false) + .setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return; + } + final int[] jproblem = new int[81]; + for(int i=0; i<9; i++) { + + for(int j=0; j<9; j++) { + jproblem[i*9+j] = getValAt(i, j); + } + } + final boolean[] wasempty = new boolean[81]; + for(int i=0; i<81; i++) { + if(jproblem[i]==0) { + wasempty[i] = true; + } + else { + wasempty[i] = false; + } + } + int[] solution = exactcaller(jproblem); + if(isGoodSol(solution)) { + fillSolution(solution, wasempty); + } + else { + AlertDialog.Builder builder = new AlertDialog.Builder(factivity); + builder.setMessage("The puzzle you entered has no solutions!") + .setCancelable(false) + .setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return; + } + } + }); + tr4.addView(solvebtn); + TableLayout abtnt = (TableLayout) findViewById(R.id.actionbtntable); + abtnt.addView(tr4); + + } + protected void clearAllCells() { + for(int i=0; i<9; i++) { + for(int j=0; j<9;j++) { + tvarray[i][j].setTextColor(Color.BLACK); + tvarray[i][j].setText(""); + //tvarray[i][j].setBackgroundResource(R.drawable.back); + setTvBack(i, j); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + menu.add(0, MENU_CLEARALL, 0, "Clear All"); + menu.add(0, MENU_EXITAPP, 0, "Exit App"); + //getMenuInflater().inflate(R.menu.main, menu); + return true; + } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case MENU_CLEARALL: + clearAllCells(); + return true; + + case MENU_EXITAPP: + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder( + this); + // set title + alertDialogBuilder.setTitle("Quit?!"); + + alertDialogBuilder + .setMessage("Do you really want to quit?") + .setCancelable(false) + .setPositiveButton("Yes", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // if this button is clicked, close + // current activity + MainActivity.this.finish(); + } + }) + .setNegativeButton("No", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + // create alert dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + + // show it + alertDialog.show(); + + return true; + } + return false; + } + + +} diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..675e07f --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,22 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := exactcaller +LOCAL_SRC_FILES := exactcaller.c exact.c util.c + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/exact.c b/app/src/main/jni/exact.c new file mode 100644 index 0000000..fb6cbaf --- /dev/null +++ b/app/src/main/jni/exact.c @@ -0,0 +1,1013 @@ +/* + * libexact v1.0 --- a software library for solving combinatorial + * exact covering problems + * + * Copyright (C) 2008 Petteri Kaski + * Olli Pottonen + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +/*************************************************** Library implementation. */ + +#include +#include +#include "exact.h" +#include "util.h" + +/****************************************************** Internal structures. */ + +struct exact_rchead_struct; + +typedef struct exact_lrudrc_struct +{ + struct exact_lrudrc_struct *left; + struct exact_lrudrc_struct *right; + struct exact_lrudrc_struct *up; + struct exact_lrudrc_struct *down; + struct exact_rchead_struct *row; + struct exact_rchead_struct *col; +} exact_lrudrc_t; + +typedef struct exact_rchead_struct +{ + exact_lrudrc_t links; + int id; + int count; + int b; +} exact_rchead_t; + +struct exact_struct +{ + /* Root element of the dancing link data structure. */ + exact_rchead_t root; + + /* Memory management for the dancing link data structure. */ + exact_chunk_t * lrudrc_chunk; + exact_chunk_t * rchead_chunk; + + /* Key--value maps for elements of the data structure. */ + exact_map_t * rowid_to_rowhead; + exact_map_t * colid_to_colhead; + exact_map_t * entry_to_lrudrc; + + /* Counts etc. */ + int num_cols; + int num_rows; + int sum_b; /* Sum of the entries b_i in the vector b. + * Used as an upper bound for + * the size of any solution. */ + int simple; /* Nonzero if b_i == 1 for all i, + * or equivalently, every row must + * be covered exactly once. */ + + /* Modes of operation. */ + int can_declare; + int iteration_in_progress; + int lock; + + /* The search stack. */ + int level; + int * soln_stack; + int soln_stack_capacity; + int num_push; + exact_lrudrc_t ** col_stack; + int * filter_pos; + exact_lrudrc_t ** filter_stack; + int filter_stack_capacity; + + /* Cache for a row with the minimum branching factor. */ + exact_rchead_t * cache; + int cache_branching_factor; + + /* Search control functions and their user parameters. */ + exact_level_t * level_f; + void * level_p; + exact_filter_t * filter_f; + void * filter_p; +}; + +/************************************************ Link/unlink/relink macros. */ + +#define lrreset(entry) \ +{ (entry)->left = entry; \ + (entry)->right = entry; } + +/* Unused macro +#define lrlinkright(to, entry) \ +{ (entry)->right = (to)->right; \ + (entry)->left = to; \ + (to)->right->left = entry; \ + (to)->right = entry; } +*/ + +#define lrlinkleft(to, entry) \ +{ (entry)->left = (to)->left; \ + (entry)->right = to; \ + (to)->left->right = entry; \ + (to)->left = entry; } + +#define lrunlink(entry) \ +{ (entry)->left->right = (entry)->right; \ + (entry)->right->left = (entry)->left; } + +#define lrrelink(entry) \ +{ (entry)->left->right = entry; \ + (entry)->right->left = entry; } + +#define udreset(entry) \ +{ (entry)->up = entry; \ + (entry)->down = entry; } + +/* Unused macro +#define udlinkdown(to, entry) \ +{ (entry)->down = (to)->down; \ + (entry)->up = to; \ + (to)->down->up = entry; \ + (to)->down = entry; } +*/ + +#define udlinkup(to, entry) \ +{ (entry)->up = (to)->up; \ + (entry)->down = to; \ + (to)->up->down = entry; \ + (to)->up = entry; } + +#define udunlink(entry) \ +{ (entry)->up->down = (entry)->down; \ + (entry)->down->up = (entry)->up; } + +#define udrelink(entry) \ +{ (entry)->up->down = entry; \ + (entry)->down->up = entry; } + +/******************** Structures & comparison functions for key--value maps. */ + +static int int_cmp(const void *a, const void *b) +{ + const int *aa = (const int *) a; + const int *bb = (const int *) b; + return (*aa > *bb) - (*aa < *bb); +} + +typedef struct exact_rcid_struct +{ + int row; + int col; +} exact_rcid_t; + +static int rcid_cmp(const void *a, const void *b) +{ + const exact_rcid_t *aa = (const exact_rcid_t *) a; + const exact_rcid_t *bb = (const exact_rcid_t *) b; + return 2*((aa->row > bb->row) - (aa->row < bb->row)) + + (aa->col > bb->col) - (aa->col < bb->col); +} + +/************************************* Initialization and release functions. */ + +static void enlarge_soln_stack(exact_t *e, int size) +{ + EXACT_FREE(e->soln_stack); + EXACT_FREE(e->col_stack); + EXACT_FREE(e->filter_pos); + + e->soln_stack = EXACT_ALLOC(sizeof(int)*size); + e->col_stack = EXACT_ALLOC(sizeof(exact_lrudrc_t *)*size); + e->filter_pos = EXACT_ALLOC(sizeof(int)*(size+1)); + e->soln_stack_capacity = size; +} + +static void enlarge_filter_stack(exact_t *e, int size) +{ + EXACT_FREE(e->filter_stack); + e->filter_stack = EXACT_ALLOC(sizeof(exact_lrudrc_t *)*size); + e->filter_stack_capacity = size; +} + +#define SOLN_STACK_START_SIZE 128 +#define FILTER_STACK_START_SIZE 128 +#define EXACT_CHUNK_SIZE 256 + +static void exact_init(exact_t *e) +{ + e->num_cols = 0; + e->num_rows = 0; + e->num_push = 0; + e->sum_b = 0; + e->simple = 1; + e->soln_stack_capacity = SOLN_STACK_START_SIZE; + e->soln_stack = EXACT_ALLOC(sizeof(int)*e->soln_stack_capacity); + e->col_stack = EXACT_ALLOC(sizeof(exact_lrudrc_t *)* + e->soln_stack_capacity); + e->filter_pos = EXACT_ALLOC(sizeof(int)* + (e->soln_stack_capacity+1)); + e->filter_stack_capacity = FILTER_STACK_START_SIZE; + e->filter_stack = EXACT_ALLOC(sizeof(exact_lrudrc_t *)* + e->filter_stack_capacity); + e->lrudrc_chunk = exact_chunk_alloc(sizeof(exact_lrudrc_t), + EXACT_CHUNK_SIZE); + e->rchead_chunk = exact_chunk_alloc(sizeof(exact_rchead_t), + EXACT_CHUNK_SIZE); + e->rowid_to_rowhead = exact_map_alloc(sizeof(int), &int_cmp); + e->colid_to_colhead = exact_map_alloc(sizeof(int), &int_cmp); + e->entry_to_lrudrc = exact_map_alloc(sizeof(exact_rcid_t), + &rcid_cmp); + e->can_declare = 1; + e->iteration_in_progress = 0; + e->lock = 0; + + e->level_f = NULL; + e->level_p = NULL; + e->filter_f = NULL; + e->filter_p = NULL; + + exact_lrudrc_t *l = &e->root.links; + l->row = &e->root; + l->col = &e->root; + lrreset(l); + udreset(l); +} + +static void exact_release(exact_t *e) +{ + exact_map_free(e->rowid_to_rowhead); + exact_map_free(e->colid_to_colhead); + exact_map_free(e->entry_to_lrudrc); + exact_chunk_free(e->lrudrc_chunk); + exact_chunk_free(e->rchead_chunk); + EXACT_FREE(e->filter_stack); + EXACT_FREE(e->filter_pos); + EXACT_FREE(e->col_stack); + EXACT_FREE(e->soln_stack); +} + +exact_t *exact_alloc(void) +{ + exact_t *e = (exact_t *) EXACT_ALLOC(sizeof(exact_t)); + exact_init(e); + return e; +} + +void exact_free(exact_t *e) +{ + exact_release(e); + EXACT_FREE(e); +} + +/*********************************************************** Number of rows. */ + +int exact_num_rows(exact_t *e) +{ + return e->num_rows; +} + +/******************************************************** Number of columns. */ + +int exact_num_cols(exact_t *e) +{ + return e->num_cols; +} + +/*********************************** Check whether declarations are allowed. */ + +int exact_can_declare(exact_t *e) +{ + return e->can_declare; +} + +/************************************************************ Declare a row. */ + +void exact_declare_row(exact_t *e, int i, int b) +{ + if(!e->can_declare) + EXACT_ERROR("not in DECLARE mode, operation not permitted"); + if(exact_map_find(e->rowid_to_rowhead, &i)) + EXACT_ERROR("row already exists (i = %d)", i); + if(b <= 0) + EXACT_ERROR("parameter b must be positive (b = %d)", b); + + exact_rchead_t *rh = exact_chunk_get(e->rchead_chunk); + rh->id = i; + rh->count = 0; + rh->b = b; + exact_map_associate(e->rowid_to_rowhead, rh); + + exact_lrudrc_t *r = &rh->links; + r->row = rh; + r->col = &e->root; + lrreset(r); + + exact_lrudrc_t *rl = &e->root.links; + udlinkup(rl, r); + + e->num_rows++; + e->sum_b = e->sum_b + b; + e->simple = e->simple && (b == 1); + if(e->sum_b > e->soln_stack_capacity) + enlarge_soln_stack(e, 2*e->sum_b); +} + +/*********************************************** Check whether a row exists. */ + +int exact_is_row(exact_t *e, int i) +{ + return exact_map_find(e->rowid_to_rowhead, &i); +} + +/********************************************************* Declare a column. */ + +void exact_declare_col(exact_t *e, int j, int u) +{ + if(!e->can_declare) + EXACT_ERROR("not in DECLARE mode, operation not permitted"); + if(exact_map_find(e->colid_to_colhead, &j)) + EXACT_ERROR("column already exists (j = %d)", j); + if(u <= 0) + EXACT_ERROR("parameter u must be positive (u = %d)", u); + + exact_rchead_t *ch = exact_chunk_get(e->rchead_chunk); + ch->id = j; + ch->count = 0; + ch->b = u; + exact_map_associate(e->colid_to_colhead, ch); + + exact_lrudrc_t *c = &ch->links; + c->row = &e->root; + c->col = ch; + udreset(c); + + exact_lrudrc_t *rl = &e->root.links; + lrlinkleft(rl, c); + + e->num_cols++; + if(e->num_cols > e->filter_stack_capacity) + enlarge_filter_stack(e, 2*e->num_cols); +} + +/******************************************** Check whether a column exists. */ + +int exact_is_col(exact_t *e, int j) +{ + return exact_map_find(e->colid_to_colhead, &j); +} + +/****************************** Declare a 1-entry to the coefficient matrix. */ + +void exact_declare_entry(exact_t *e, int i, int j) +{ + if(!e->can_declare) + EXACT_ERROR("not in DECLARE mode, operation not permitted"); + + if(!exact_map_find(e->rowid_to_rowhead, &i)) + EXACT_ERROR("nonexistent row (i = %d)", i); + exact_rchead_t *rh = exact_map_value(e->rowid_to_rowhead); + + if(!exact_map_find(e->colid_to_colhead, &j)) + EXACT_ERROR("nonexistent column (j = %d)", j); + exact_rchead_t *ch = exact_map_value(e->colid_to_colhead); + + exact_rcid_t rcid = { i, j }; + if(exact_map_find(e->entry_to_lrudrc, &rcid)) + EXACT_ERROR("entry already exists (i = %d, j = %d)", i, j); + + rh->count++; + ch->count++; + exact_lrudrc_t *r = &rh->links; + exact_lrudrc_t *c = &ch->links; + exact_lrudrc_t *t = exact_chunk_get(e->lrudrc_chunk); + t->row = rh; + t->col = ch; + lrlinkleft(r, t); + udlinkup(c, t); + + exact_map_associate(e->entry_to_lrudrc, t); +} + +/********************************************* Return the value of an entry. */ + +int exact_is_entry(exact_t *e, int i, int j) +{ + if(!exact_map_find(e->rowid_to_rowhead, &i)) + EXACT_ERROR("nonexistent row (i = %d)", i); + if(!exact_map_find(e->colid_to_colhead, &j)) + EXACT_ERROR("nonexistent column (j = %d)", j); + int k[2] = { i, j }; + if(exact_map_find(e->entry_to_lrudrc, k)) + return 1; + return 0; +} + +/*********************************** Output identifiers of unsatisfied rows. */ + +int exact_get_rows(exact_t *e, int *id) +{ + int n = 0; + exact_lrudrc_t *r; + for(r = e->root.links.down; + r != &e->root.links; + r = r->down) + id[n++] = r->row->id; + return n; +} + +/********************************** Output identifiers of available columns. */ + +int exact_get_cols(exact_t *e, int *id) +{ + int n = 0; + exact_lrudrc_t *c; + for(c = e->root.links.right; + c != &e->root.links; + c = c->right) + id[n++] = c->col->id; + return n; +} + +/*********** Subroutines for manipulating the "dancing link" data structure. */ + +/* + + | | | | | | + | | | | | | + | | | | | | + up | up | up | +-------###########right------###########right------###########right-- COLUMN + ## ROOT ## ## col ## ## col ## LIST +---left###########-------left###########-------left###########------- + | down | down | down + | | | | | | + | | | | | | + | | | | | | + up | | | up | +-------###########right-------+-------+------------###########right-- + ## row ## | | ## entry ## +---left###########------------+-------+--------left###########------- + | down | | | down + | | | | | | + | | | | | | + up | up | | | +-------###########right------###########right-------+-------+-------- + ## row ## ## entry ## | | +---left###########-------left###########------------+-------+-------- + | down | down | | + | | | | | | + + ROW LIST + +*/ + +/* Left--right unlink routine for a vertical interval. */ + +static int lrunlink_interval(exact_t *e, + exact_lrudrc_t *a, + const exact_lrudrc_t *b, + int skip) +{ + /* Start from a, go down until (but not including) b. */ + exact_lrudrc_t *d; + for(d = a; d != b; d = d->down) { + if(--d->row->count == 0) { + /* The row of d is not covered, and + * has no intersecting columns available for covering it. + * We can backtrack immediately. */ + skip = 1; + } + /* Update minimum branching row cache. */ + if(d->row->count < e->cache_branching_factor) { + e->cache_branching_factor = d->row->count; + e->cache = d->row; + } + lrunlink(d); + } + return skip; +} + + +/* Left--right relink routine for a vertical interval. */ + +static void lrrelink_interval(exact_lrudrc_t *a, + const exact_lrudrc_t *b) +{ + /* Start from a, go up until (but not including) b. */ + exact_lrudrc_t *d; + for(d = a; d != b; d = d->up) { + lrrelink(d); + d->row->count++; + } +} + + +/* Updates the data structure to reflect the fact that + * the column of c was just pushed to the solution stack. */ + +static int push_col_update(exact_t *e, exact_lrudrc_t *c, int use_cache) +{ + /* Initialize minimum branching row cache. */ + e->cache_branching_factor = use_cache ? (c->row->count + 1) : 0; + e->cache = NULL; + + /* Initialize control variables. */ + int skip = 0; /* Force backtrack upon return? */ + int unlink_c = (--c->col->b == 0); /* Unlink the column of c if + * the column bound is met. */ + + /* Check whether any rows intersecting the column of c + * got covered as a result of the push. */ + + /* Ensure that c is a column header. */ + c = &c->col->links; + + /* Traverse down the list at the column of c, + * starting from the entry below the column header. + * All entries except the column header are traversed. */ + exact_lrudrc_t *d; + for(d = c->down; d != c ; d = d->down) { + if(--d->row->b == 0) { + /* The row of d just got covered. + * Proceed to unlink all columns intersecting the row of d. */ + + /* In particular, remember to unlink the column of c + * once this loop is done. */ + unlink_c = 1; + + /* Unlink the row header of the row of d from the row list. */ + exact_lrudrc_t *rhd = &d->row->links; + udunlink(rhd); + /* Reminder: + * Do not decrease d->row->count in this loop, but do so + * in the following loop (which will be executed since + * unlink_c == 1); otherwise the calls to lrunlink_interval + * in this loop can incorrectly force a backtrack. */ + + /* Traverse right the list at the row of d. + * All entries except the entry d are traversed, + * including the row header (rhd). */ + exact_lrudrc_t *dr; + for(dr = d->right; dr != d; dr = dr->right) { + if(dr != rhd) { /* Ignore the row header. */ + /* Unlink the column of dr. */ + exact_lrudrc_t *a = dr->down; + exact_lrudrc_t *b = &dr->col->links; + skip = lrunlink_interval(e, a, b, skip); + lrunlink(b); /* Unlink the column header. */ + a = b->down; + b = dr; + skip = lrunlink_interval(e, a, b, skip); + } + } + } + } + if(unlink_c) { + /* Unlink the column of c. + * The entry c must be a column header. */ + lrunlink(c); /* Unlink header. */ + exact_lrudrc_t *d; + for(d = c->down; d != c; d = d->down) { + /* Caching is used only if all rows have b == 1. In that case, + * the row d considered here is covered and should not be cached + * even id d->row->count < e->cache_branching_factor. + */ + d->row->count--; + /* Reminder: + * Cannot backtrack here on d->row->count == 0 alone + * because the row of d may be covered + * (that is, d->row->b == 0). + */ + lrunlink(d); + } + } + return skip; +} + + +/* Rewinds the data structure to reflect the fact that + * the column of c was just popped off the solution stack. */ + +static void pop_col_update(exact_lrudrc_t *c) +{ + + /* Ensure that c is a column header. */ + c = &c->col->links; + + /* Relink the column of c if it was unlinked. */ + if(c->right->left != c) { + /* Relink the column of c. + * The entry c must be a column header. */ + exact_lrudrc_t *d; + for(d = c->up; d != c; d = d->up) { + lrrelink(d); + d->row->count++; + } + lrrelink(c); /* Relink header. */ + } + + /* Check whether any rows intersecting the column of c are uncovered + * as a result of the pop. */ + + /* Traverse up the list at the column of c, + * starting from the entry above the column header. + * All entries except the column header are traversed. */ + exact_lrudrc_t *d; + for(d = c->up ; d != c ; d = d->up) { + if(d->row->b == 0) { + /* The row of d just got uncovered. + * Proceed to relink all columns intersecting the row of d. */ + + /* Traverse left the list at the row of d. + * All entries except the entry d are traversed, + * including the row header (rhd). */ + exact_lrudrc_t *rhd = &d->row->links; + exact_lrudrc_t *dr; + for(dr = d->left; dr != d; dr = dr->left) { + if(dr != rhd) { /* Ignore row header. */ + /* Relink the column of dr. */ + exact_lrudrc_t *a = dr->up; + exact_lrudrc_t *b = &dr->col->links; + lrrelink_interval(a, b); + lrrelink(b); /* Relink the column header. */ + a = b->up; + b = dr; + lrrelink_interval(a, b); + } + } + /* Relink the row header of the row of d to the row list. */ + udrelink(rhd); + } + d->row->b++; + } + c->col->b++; +} + +static int filter(exact_t *e, int lvl) +{ + /* Evaluate filter function on candidate columns and + unlink accordingly. */ + int skip = 0; + int i = e->filter_pos[lvl-1]; + + /* Traverse right the column list. + * All entries except the root are traversed. */ + exact_lrudrc_t *c; + for(c = e->root.links.right; + c != &e->root.links; + c = c->right) { + if(!e->filter_f(e->filter_p, + lvl, + e->soln_stack, + c->col->id)) { + /* Unlink the column of c. */ + exact_lrudrc_t *a = c->down; + exact_lrudrc_t *b = c; + skip = lrunlink_interval(e, a, b, skip); + lrunlink(c); /* Unlink the column header. */ + /* Save data for unfiltering. */ + e->filter_stack[i++] = c; + } + } + /* Record the position for unfiltering. */ + e->filter_pos[lvl] = i; + + return skip; +} + +static void unfilter(exact_t *e, int lvl) +{ + /* Relink any columns unlinked by the filter function. */ + int i = e->filter_pos[lvl]; + while(i > e->filter_pos[lvl-1]) { + exact_lrudrc_t *c = e->filter_stack[--i]; + /* Relink the column of c. */ + lrrelink(c); /* Relink the column header. */ + exact_lrudrc_t *a = c->up; + exact_lrudrc_t *b = c; + lrrelink_interval(a, b); + } +} + +/********************** Check whether forcing a partial solution is allowed. */ + +int exact_can_push(exact_t *e) +{ + if(e->iteration_in_progress) + return 0; + return 1; +} + +/************************************************************ Push a column. */ + +void exact_push(exact_t *e, int j) +{ + if(e->iteration_in_progress) + EXACT_ERROR("operation not permitted in ITERATE mode"); + + /* Find the column. */ + if(!exact_map_find(e->colid_to_colhead, &j)) + EXACT_ERROR("nonexistent column (j = %d)", j); + exact_rchead_t *ch = exact_map_value(e->colid_to_colhead); + exact_lrudrc_t *c = &ch->links; + + /* Check that column has not been unlinked. */ + if(c->left->right != c) + EXACT_ERROR("cannot push an unlinked column (j = %d)", j); + + /* Check that the column is not empty. */ + if(ch->count == 0) + EXACT_ERROR("pushing a column of zeroes (j = %d)", j); + + e->soln_stack[e->num_push++] = j; + push_col_update(e, c, 0); + e->can_declare = 0; +} + +/************************************************************* Pop a column. */ + +void exact_pop(exact_t *e) +{ + if(e->iteration_in_progress) + EXACT_ERROR("operation not permitted in ITERATE mode"); + if(e->num_push == 0) + EXACT_ERROR("nothing to pop"); + int j = e->soln_stack[--e->num_push]; + + /* Find the column. */ + exact_map_find(e->colid_to_colhead, &j); + exact_rchead_t *ch = exact_map_value(e->colid_to_colhead); + exact_lrudrc_t *c = &ch->links; + + pop_col_update(c); + if(e->num_push == 0) + e->can_declare = 1; +} + +/************************************************* Check validity of a push. */ + +int exact_pushable(exact_t *e, int j) +{ + if(e->iteration_in_progress) + EXACT_ERROR("operation not permitted in ITERATE mode"); + + /* Find the column. */ + if(!exact_map_find(e->colid_to_colhead, &j)) + EXACT_ERROR("nonexistent column (j = %d)", j); + exact_rchead_t *ch = exact_map_value(e->colid_to_colhead); + exact_lrudrc_t *c = &ch->links; + + /* Check that column has not been unlinked. */ + if(c->left->right != c) + return 0; + + /* Check that the column is not empty. */ + if(ch->count == 0) + return 0; + return 1; +} + +/************************************************* Number of pushed columns. */ + +int exact_num_push(exact_t *e) +{ + return e->num_push; +} + +/************************************* Output identifiers of pushed columns. */ + +int exact_get_push(exact_t *e, int *id) +{ + int n = e->num_push; + int i; + for(i = 0; i < n; i++) + id[i] = e->soln_stack[i]; + return n; +} + +/******************************************************* Set level function. */ + +void exact_level(exact_t *e, exact_level_t *l, void *p) +{ + if(e->iteration_in_progress) + EXACT_ERROR("operation not permitted in ITERATE mode"); + e->level_f = l; + e->level_p = p; +} + +/****************************************************** Set filter function. */ + +void exact_filter(exact_t *e, exact_filter_t *f, void *p) +{ + if(e->iteration_in_progress) + EXACT_ERROR("operation not permitted in ITERATE mode"); + e->filter_f = f; + e->filter_p = p; +} + +/******************** Data structure consistency check (for test code only). */ + +void exact_check(exact_t *e); + +void exact_check(exact_t *e) +{ + exact_lrudrc_t *d = &e->root.links; + do { + int s = 0; + exact_lrudrc_t *r = d; + do { + if(r->left->right != r || + r->right->left != r) + EXACT_INTERNAL_ERROR("invalid left/right link"); + if(r->row != d->row) + EXACT_INTERNAL_ERROR("invalid row link"); + s++; + int t = 0; + exact_lrudrc_t *dr = r; + do { + if(dr->down->up != dr || + dr->up->down != dr) + EXACT_INTERNAL_ERROR("invalid up/down link"); + if(dr->col != r->col) + EXACT_INTERNAL_ERROR("invalid column link"); + if(dr->row == &e->root) + t++; + dr = dr->down; + } while(dr != r); + if(t != 1) + EXACT_INTERNAL_ERROR("missing/repeated entry in column list"); + r = r->right; + } while(r != d); + if(d->row != &e->root && s != d->row->count + 1) { + EXACT_INTERNAL_ERROR("invalid row count"); + } + d = d->down; + } while(d != &e->root.links); +} + +/************************************************** Reset solution iterator. */ + +void exact_reset_solve(exact_t *e) +{ + if(e->lock) + EXACT_ERROR("re-entry while call on instance in progress"); + if(e->iteration_in_progress) { + int lvl = e->level; + while(lvl > e->num_push) { + if(e->filter_f != NULL) + unfilter(e, lvl); + lvl--; + exact_lrudrc_t *c = e->col_stack[lvl]; + pop_col_update(c); + } + e->iteration_in_progress = 0; + if(e->num_push == 0) + e->can_declare = 1; + } +} + +/******************************************************** Solution iterator. */ + +const int *exact_solve(exact_t *e, int *size) +{ + int lvl; + + if(e->lock) + EXACT_ERROR("re-entry while call on instance in progress"); + e->lock = 1; + + if(e->iteration_in_progress) { + lvl = e->level; + goto lvl_down; + } + e->can_declare = 0; + e->iteration_in_progress = 1; + lvl = e->num_push; + e->filter_pos[lvl] = 0; + e->cache = NULL; +lvl_up: + /* Call level function. */ + if(e->level_f != NULL) + if(!e->level_f(e->level_p, lvl, e->soln_stack)) + goto lvl_down; + + /* Report if solution found. */ + if(e->root.links.down == &e->root.links) { + /* Solution found. Return to caller for reporting. */ + e->level = lvl; + *size = lvl; + e->lock = 0; + return e->soln_stack; + /* Will backtrack upon next entry to exact_solve. */ + } + + { + exact_lrudrc_t *r, *c; + if(lvl <= e->num_push || + e->col_stack[lvl-1]->row->b == 0) { + /* Select a new row to cover. */ + + /* Selection heuristic: + * Select a row that leads to the minimum branching factor + * for the search. Put otherwise, select a row with the + * minimum number of columns currently intersecting it. + */ + + /* See if we have such a row cached. + * Note: must check that the cached row has not been unlinked + * after it has been cached; an unlinked row has + * e->cache->b == 0. + */ + if(e->cache != NULL && e->cache->b != 0) { + /* Yes. Use the cached row. */ + r = &e->cache->links; + } else { + /* No. Find a row with the minimum branching factor. */ + int record_count = e->num_cols + 1; + r = NULL; + exact_lrudrc_t *t; + for(t = e->root.links.down; + t != &e->root.links; + t = t->down) { + + int count = t->row->count; + if(count < record_count) { + r = t; + record_count = count; + } + } + } + + /* Start with the first column at this row. */ + c = r->right; + } else { + /* The row selected at a preceding level is not yet covered, + * proceed to cover it further. */ + + /* Start with the first available column that is + * not to the left of the column pushed at the previous level. + * Note: any columns to the left will be considered as the search + * branches at the preceding level(s). + */ + c = e->col_stack[lvl-1]; + while(c->right->left != c) { + /* This is an unlinked column, + * proceed to the next column on the right. */ + c = c->right; /* Left--right links in an unlinked column + * can still be traversed. This however + * may lead to traversing repeated + * unlinked columns. */ + if(c == &c->row->links) { + /* Row header encountered. + * This row has no available columns, but + * the row is not yet covered; can backtrack. */ + goto lvl_down; + } + } + } + + /* Branch the search: + * Push one column at a time until the row header is encountered. */ + for(; c != &c->row->links; c = c->right) { + /* Push the column of c and save backtrack data. */ + e->soln_stack[lvl] = c->col->id; + e->col_stack[lvl] = c; + /* Update data structure. */ + if(push_col_update(e, c, e->simple)) + goto lvl_skip; /* Update signals that we can backtrack. */ + lvl++; + if(e->filter_f != NULL) + if(filter(e, lvl)) + goto lvl_down; /* Filtering signals that we can backtrack. */ + goto lvl_up; + /* Simulated recursive invocation here. */ + lvl_down: + /* Backtrack and rewind data structure. */ + if(lvl == e->num_push) + goto finished; + if(e->filter_f != NULL) + unfilter(e, lvl); + lvl--; + c = e->col_stack[lvl]; + lvl_skip: + pop_col_update(c); + } + goto lvl_down; + } + finished: + e->iteration_in_progress = 0; + if(e->num_push == 0) + e->can_declare = 1; + e->lock = 0; + return NULL; +} diff --git a/app/src/main/jni/exact.h b/app/src/main/jni/exact.h new file mode 100644 index 0000000..a6df5c0 --- /dev/null +++ b/app/src/main/jni/exact.h @@ -0,0 +1,76 @@ +/* + * libexact v1.0 --- a software library for solving combinatorial + * exact covering problems + * + * Copyright (C) 2008 Petteri Kaski + * Olli Pottonen + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +/******************************************************** Library interface. */ + +#ifndef EXACT_READ +#define EXACT_READ + +struct exact_struct; +typedef struct exact_struct exact_t; +typedef int exact_level_t (void *, int, const int *); +typedef int exact_filter_t (void *, int, const int *, int); + +/* Initializing and releasing an instance. */ + +exact_t * exact_alloc (void); +void exact_free (exact_t *e); + +/* Declaring an instance. */ + +void exact_declare_row (exact_t *e, int i, int b); +void exact_declare_col (exact_t *e, int j, int u); +void exact_declare_entry (exact_t *e, int i, int j); +int exact_can_declare (exact_t *e); + +/* Iterating through the solutions. */ + +const int * exact_solve (exact_t *e, int *n); +void exact_reset_solve (exact_t *e); + +/* Examining an instance. */ + +int exact_is_row (exact_t *e, int i); +int exact_is_col (exact_t *e, int j); +int exact_is_entry (exact_t *e, int i, int j); +int exact_num_rows (exact_t *e); +int exact_num_cols (exact_t *e); +int exact_get_rows (exact_t *e, int *i); +int exact_get_cols (exact_t *e, int *j); + +/* Forcing a partial solution. */ + +void exact_push (exact_t *e, int j); +void exact_pop (exact_t *e); +int exact_pushable (exact_t *e, int j); +int exact_can_push (exact_t *e); +int exact_num_push (exact_t *e); +int exact_get_push (exact_t *e, int *j); + +/* Controlling the search. */ + +void exact_level (exact_t *e, exact_level_t *l, void *p); +void exact_filter (exact_t *e, exact_filter_t *f, void *p); + +#endif diff --git a/app/src/main/jni/exactcaller.c b/app/src/main/jni/exactcaller.c new file mode 100644 index 0000000..61b4e52 --- /dev/null +++ b/app/src/main/jni/exactcaller.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include +#include "exact.h" + +/* This is a trivial JNI example where we use a native method + * to return a new VM String. See the corresponding Java source + * file located at: + * + * apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java + */ + +#define UNDEF -1 +static void solve_sudoku(int *a) +{ + exact_t *e = exact_alloc(); + + /* Row--column */ + int r, v, c, s; + for(r = 0; r < 9; r++) + for(c = 0; c < 9; c++) + exact_declare_row(e, 9*r+c, 1); + /* Row--value */ + for(r = 0; r < 9; r++) + for(v = 0; v < 9; v++) + exact_declare_row(e, 81+9*r+v, 1); + /* Column--value */ + for(c = 0; c < 9; c++) + for(v = 0; v < 9; v++) + exact_declare_row(e, 162+9*c+v, 1); + /* Subsquare--value */ + for(s = 0; s < 9; s++) + for(v = 0; v < 9; v++) + exact_declare_row(e, 243+9*s+v, 1); + /* Row--column--value */ + for(r = 0; r < 9; r++) { + for(c = 0; c < 9; c++) { + s = 3*(r/3)+c/3; + for(v = 0; v < 9; v++) { + exact_declare_col(e, 9*9*r+9*c+v, 1); + + /* Row--column */ + exact_declare_entry(e, 9*r+c, 9*9*r+9*c+v); + + /* Row--value */ + exact_declare_entry(e, 81+9*r+v, 9*9*r+9*c+v); + + /* Column--value */ + exact_declare_entry(e, 162+9*c+v, 9*9*r+9*c+v); + + /* Subsquare--value */ + exact_declare_entry(e, 243+9*s+v, 9*9*r+9*c+v); + } + } + } + + /* Push the columns corresponding to the given partial solution. */ + for(r = 0; r < 9; r++) { + for(c = 0; c < 9; c++) { + if(a[9*r+c] != UNDEF) { + int j = 9*9*r+9*c+a[9*r+c]; + if(!exact_pushable(e, j)) { + /* The partial solution is conflicting. */ + a[0]=100; + goto solve_done; + } + exact_push(e, j); + } + } + } + + /* List all complete solutions. */ + int n, i; + const int *b; + while((b = exact_solve(e, &n)) != NULL) { + /* Put the solution into matrix form. */ + for(i = 0; i < n; i++) { + int r = b[i]/81; + int c = b[i]/9; c = c%9; + int v = b[i]%9; + a[9*r+c] = v; + } + return; /*added by ashik- since i need only one soltion */ + /* Rewind the matrix. */ + /* for(int i = exact_num_push(e); i < n; i++) { */ + /* int r = b[i]/81; */ + /* int c = b[i]/9; c = c%9; */ + /* a[9*r+c] = UNDEF; */ + /* } */ + } +solve_done: + exact_free(e); +} + +jintArray +Java_com_oldestmonk_droidsudsolve_MainActivity_exactcaller( JNIEnv* env, + jobject thiz, + jintArray jproblem ) +{ + jintArray result; + int size = 81; + jint cproblem[81]; + (*env)->GetIntArrayRegion(env, jproblem, 0, size, cproblem); + result = (*env)->NewIntArray(env, size); + if (result == NULL) { + return NULL; /* out of memory error thrown */ + } + + int a[81]; + int i; + for(i=0; i<81; i++) { + a[i] = cproblem[i]-1; + } + solve_sudoku(a); + for(i=0; i<81; i++) { + cproblem[i] = a[i]+1; + } + + // fill a temp structure to use to populate the java int array + jint fill[81]; + for (i = 0; i < size; i++) { + fill[i] = cproblem[i]; // put whatever logic you want. + } + // move from the temp structure to the java structure + (*env)->SetIntArrayRegion(env, result, 0, size, fill); + return result; +} diff --git a/app/src/main/jni/util.c b/app/src/main/jni/util.c new file mode 100644 index 0000000..88ea9ae --- /dev/null +++ b/app/src/main/jni/util.c @@ -0,0 +1,377 @@ +/* + * libexact v1.0 --- a software library for solving combinatorial + * exact covering problems + * + * Copyright (C) 2008 Petteri Kaski + * Olli Pottonen + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +/*********************************************** Internal utility functions. */ + +#include +#include +#include +#include "util.h" + +int exact_mem_alloc_balance = 0; + +void *exact_mem_alloc(size_t size) +{ + exact_mem_alloc_balance++; + void *p = malloc(size); + if(p == NULL) + EXACT_ERROR("malloc fails"); + return p; +} + +void exact_mem_free(void *p) +{ + exact_mem_alloc_balance--; + free(p); +} + +struct exact_chunk_struct +{ + int num_entries; + int num_alloc; + size_t entry_size; + void *data; + struct exact_chunk_struct *next; +}; + +exact_chunk_t *exact_chunk_alloc(size_t entry, int entries) +{ + exact_chunk_t *c = (exact_chunk_t *) EXACT_ALLOC(sizeof(exact_chunk_t)); + c->num_entries = entries; + c->num_alloc = 0; + c->entry_size = entry; + c->data = EXACT_ALLOC(c->entry_size * c->num_entries); + c->next = NULL; + return c; +} + +void exact_chunk_free(exact_chunk_t *c) +{ + while(c != NULL) { + exact_chunk_t *t = c->next; + EXACT_FREE(c->data); + EXACT_FREE(c); + c = t; + } +} + +void *exact_chunk_get(exact_chunk_t *c) +{ + if(c->num_alloc >= c->num_entries) { + exact_chunk_t *n = + (exact_chunk_t *) EXACT_ALLOC(sizeof(exact_chunk_t)); + n->num_entries = c->num_entries; + n->num_alloc = c->num_alloc; + n->entry_size = c->entry_size; + n->data = c->data; + n->next = c->next; + c->next = n; + c->num_entries = 2*c->num_entries; + c->data = EXACT_ALLOC(c->entry_size * c->num_entries); + c->num_alloc = 0; + } + return ((char *) c->data) + c->entry_size*c->num_alloc++; +} + +void exact_error(const char *fn, int line, const char *func, + const char *format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, + "ERROR [detected by libexact interface; file = %s, line = %d]\n" + "%s: ", + fn, + line, + func); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); + abort(); +} + +void exact_internal_error(const char *fn, int line, const char *func, + const char *format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, + "ERROR [libexact internal error; file = %s, line = %d]\n" + "%s: ", + fn, + line, + func); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); + abort(); +} + +/******************** Map from keys to values -- implemented as an AVL tree. */ + +struct avl_node_struct; + +typedef struct avl_node_struct +{ + void * key; + void * value; + struct avl_node_struct * left; + struct avl_node_struct * right; + int height; +} avl_node_t; + +struct exact_map_struct +{ + avl_node_t * root; + exact_chunk_t * node_chunk; + exact_chunk_t * key_chunk; + size_t key_size; + exact_map_cmp_t * cmp_f; + + void * active_key; + void ** active_value_p; + int path_length; + int path_capacity; + avl_node_t *** path; +}; + +#define AVL_CHUNK_SIZE 128 +#define PATH_CAPACITY_START 32 + +static void enlarge_path(exact_map_t *m, int capacity) +{ + EXACT_FREE(m->path); + m->path_capacity = capacity; + m->path = EXACT_ALLOC(sizeof(avl_node_t **)*m->path_capacity); +} + +static void exact_map_init(exact_map_t *m, size_t key, exact_map_cmp_t *f) +{ + m->key_size = key; + m->root = NULL; + m->active_value_p = NULL; + m->node_chunk = exact_chunk_alloc(sizeof(avl_node_t), AVL_CHUNK_SIZE); + m->key_chunk = exact_chunk_alloc(m->key_size, AVL_CHUNK_SIZE); + m->path_capacity = PATH_CAPACITY_START; + m->path = EXACT_ALLOC(sizeof(avl_node_t **)*m->path_capacity); + m->path_length = 0; + m->active_key = EXACT_ALLOC(m->key_size); + m->cmp_f = f; +} + +static void exact_map_release(exact_map_t *m) +{ + EXACT_FREE(m->active_key); + EXACT_FREE(m->path); + exact_chunk_free(m->key_chunk); + exact_chunk_free(m->node_chunk); +} + +exact_map_t *exact_map_alloc(size_t key, exact_map_cmp_t *f) +{ + exact_map_t *m = (exact_map_t *) EXACT_ALLOC(sizeof(exact_map_t)); + exact_map_init(m, key, f); + return m; +} + +void exact_map_free(exact_map_t *m) +{ + exact_map_release(m); + EXACT_FREE(m); +} + +static int height(avl_node_t *n) +{ + if(n == NULL) + return -1; + return n->height; +} + +int exact_map_find(exact_map_t *m, void *key) +{ + memcpy(m->active_key, key, m->key_size); + avl_node_t *n = m->root; + int t = height(n)+2; + if(t > m->path_capacity) + enlarge_path(m, 2*t); + m->path_length = 1; + m->path[0] = &m->root; + while(n != NULL) { + t = m->cmp_f(n->key, m->active_key); + if(t == 0) { + m->active_value_p = &n->value; + return 1; + } + if(t > 0) { + m->path[m->path_length++] = &n->left; + n = n->left; + } else { + m->path[m->path_length++] = &n->right; + n = n->right; + } + } + m->active_value_p = NULL; + return 0; +} + +void *exact_map_value(exact_map_t *m) +{ + void **av = m->active_value_p; + if(av == NULL) + EXACT_INTERNAL_ERROR("no value available"); + return *av; +} + +static int max(int a, int b) +{ + return a > b ? a : b; +} + +/* AVL outer rotation: + * + * d b + * / \ <--- left --- / \ + * b E A d + * / \ --- right --> / \ + * A C C E + * + */ + +static avl_node_t *rotate_outer_right(avl_node_t *d) +{ + avl_node_t *b = d->left; + d->left = b->right; + b->right = d; + + d->height = max(height(d->left), height(d->right)) + 1; + b->height = max(height(b->left), height(b->right)) + 1; + + return b; +} + +static avl_node_t *rotate_outer_left(avl_node_t *b) +{ + avl_node_t *d = b->right; + b->right = d->left; + d->left = b; + + b->height = max(height(b->left), height(b->right)) + 1; + d->height = max(height(d->left), height(d->right)) + 1; + + return d; +} + +/* AVL inner rotation right: + * + * f f d + * / \ / \ / \ + * b G --- b left --> d G -- f right --> / \ + * / \ / \ b f + * A d b E / \ / \ + * / \ / \ A C E G + * C E A C + * + */ + +static avl_node_t *rotate_inner_right(avl_node_t *f) +{ + f->left = rotate_outer_left(f->left); + return rotate_outer_right(f); +} + +/* AVL inner rotation left: + * + * b b d + * / \ / \ / \ + * A f -- f right --> A d --- b left --> / \ + * / \ / \ b f + * d G C f / \ / \ + * / \ / \ A C E G + * C E E G + * + */ + +static avl_node_t *rotate_inner_left(avl_node_t *b) +{ + b->right = rotate_outer_right(b->right); + return rotate_outer_left(b); +} + +void exact_map_associate(exact_map_t *m, void *v) +{ + void **av = m->active_value_p; + if(av != NULL) { + *av = v; + return; + } + if(m->path_length == 0) + EXACT_INTERNAL_ERROR("no query issued"); + + avl_node_t *n = exact_chunk_get(m->node_chunk); + n->left = NULL; + n->right = NULL; + n->height = 0; + n->key = exact_chunk_get(m->key_chunk); + n->value = v; + memcpy(n->key, m->active_key, m->key_size); + + m->active_value_p = &n->value; + avl_node_t **p = m->path[--m->path_length]; + *p = n; + + while(m->path_length > 0) { + p = m->path[--m->path_length]; + avl_node_t *nn = n; + n = *p; + /* A child of n was updated to the value nn. */ + if(n->left == nn) { + /* Left child of n was updated. */ + if(height(n->left) - height(n->right) == 2) { + /* Left subtree is too high after update, must rebalance. */ + if(height(n->left->left) + 1 == n->left->height) { + /* The cause is the left--left subtree. */ + n = rotate_outer_right(n); + } else { + /* The cause is the left--right subtree. */ + n = rotate_inner_right(n); + } + } + } else { + /* Right child of n was updated. */ + if(height(n->right) - height(n->left) == 2) { + /* Right child is too high after update, must rebalance. */ + if(height(n->right->right) + 1 == n->right->height) { + /* The cause is the right--right subtree. */ + n = rotate_outer_left(n); + } else { + /* The cause is the right--left subtree. */ + n = rotate_inner_left(n); + } + } + } + n->height = max(height(n->left), + height(n->right)) + 1; + *p = n; + } +} diff --git a/app/src/main/jni/util.h b/app/src/main/jni/util.h new file mode 100644 index 0000000..9b39a6e --- /dev/null +++ b/app/src/main/jni/util.h @@ -0,0 +1,70 @@ +/* + * libexact v1.0 --- a software library for solving combinatorial + * exact covering problems + * + * Copyright (C) 2008 Petteri Kaski + * Olli Pottonen + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +/************************************ Internal utility functions and macros. */ + +#ifndef EXACT_UTIL_READ +#define EXACT_UTIL_READ + +#include +#include + +void * exact_mem_alloc (size_t size); +void exact_mem_free (void *p); + +#define EXACT_ALLOC(s) exact_mem_alloc(s) +#define EXACT_FREE(p) exact_mem_free(p) + +struct exact_chunk_struct; +typedef struct exact_chunk_struct exact_chunk_t; + +exact_chunk_t * exact_chunk_alloc (size_t entry, int entries); +void exact_chunk_free (exact_chunk_t *c); +void * exact_chunk_get (exact_chunk_t *c); + +struct exact_map_struct; +typedef struct exact_map_struct exact_map_t; +typedef int exact_map_cmp_t (const void *, const void *); + +exact_map_t * exact_map_alloc (size_t key, exact_map_cmp_t *f); +void exact_map_free (exact_map_t *m); +int exact_map_find (exact_map_t *m, void *k); +void * exact_map_value (exact_map_t *m); +void exact_map_associate (exact_map_t *m, void *v); + +void exact_error (const char *fn, + int line, + const char *func, + const char *format, ...); + +void exact_internal_error (const char *fn, + int line, + const char *func, + const char *format, ...); + +#define EXACT_ERROR(...) exact_error(__FILE__,__LINE__,__func__,__VA_ARGS__); +#define EXACT_INTERNAL_ERROR(...) \ + exact_internal_error(__FILE__,__LINE__,__func__,__VA_ARGS__); + +#endif diff --git a/app/src/main/res/drawable/back1.xml b/app/src/main/res/drawable/back1.xml new file mode 100644 index 0000000..acc2b23 --- /dev/null +++ b/app/src/main/res/drawable/back1.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back2.xml b/app/src/main/res/drawable/back2.xml new file mode 100644 index 0000000..f8062e2 --- /dev/null +++ b/app/src/main/res/drawable/back2.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back3.xml b/app/src/main/res/drawable/back3.xml new file mode 100644 index 0000000..72ae7fd --- /dev/null +++ b/app/src/main/res/drawable/back3.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back4.xml b/app/src/main/res/drawable/back4.xml new file mode 100644 index 0000000..0e04558 --- /dev/null +++ b/app/src/main/res/drawable/back4.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back5.xml b/app/src/main/res/drawable/back5.xml new file mode 100644 index 0000000..20970a1 --- /dev/null +++ b/app/src/main/res/drawable/back5.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back6.xml b/app/src/main/res/drawable/back6.xml new file mode 100644 index 0000000..e06a52e --- /dev/null +++ b/app/src/main/res/drawable/back6.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back7.xml b/app/src/main/res/drawable/back7.xml new file mode 100644 index 0000000..bbaea51 --- /dev/null +++ b/app/src/main/res/drawable/back7.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back8.xml b/app/src/main/res/drawable/back8.xml new file mode 100644 index 0000000..78e6d1b --- /dev/null +++ b/app/src/main/res/drawable/back8.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/back9.xml b/app/src/main/res/drawable/back9.xml new file mode 100644 index 0000000..1db2979 --- /dev/null +++ b/app/src/main/res/drawable/back9.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher.png b/app/src/main/res/drawable/ic_launcher.png new file mode 100644 index 0000000..5966d4e Binary files /dev/null and b/app/src/main/res/drawable/ic_launcher.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..f245b9e --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,31 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml new file mode 100644 index 0000000..d227c49 --- /dev/null +++ b/app/src/main/res/menu/main.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml new file mode 100644 index 0000000..1ba777d --- /dev/null +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-sw720dp-land/dimens.xml b/app/src/main/res/values-sw720dp-land/dimens.xml new file mode 100644 index 0000000..eee741a --- /dev/null +++ b/app/src/main/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,9 @@ + + + + 128dp + + \ No newline at end of file diff --git a/app/src/main/res/values-v11/styles.xml b/app/src/main/res/values-v11/styles.xml new file mode 100644 index 0000000..541752f --- /dev/null +++ b/app/src/main/res/values-v11/styles.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-v14/styles.xml b/app/src/main/res/values-v14/styles.xml new file mode 100644 index 0000000..f20e015 --- /dev/null +++ b/app/src/main/res/values-v14/styles.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..a6dd140 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ + + + + 16dp + 16dp + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..4c81f50 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + Monk\'s Sudoku Solver + Settings + Hello India! + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..4a10ca4 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..f6627fc --- /dev/null +++ b/build.gradle @@ -0,0 +1,15 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.2' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app'