├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── binding.gyp ├── bridge ├── addon.cc ├── bridge.c └── bridge.h ├── espresso-src ├── COPYING ├── Makefile ├── README ├── black_white.c ├── canonical.c ├── cofactor.c ├── cols.c ├── compl.c ├── contain.c ├── copyright.h ├── cpu_time.c ├── cubestr.c ├── cvrin.c ├── cvrm.c ├── cvrmisc.c ├── cvrout.c ├── dominate.c ├── equiv.c ├── espresso.c ├── espresso.h ├── essen.c ├── essentiality.c ├── exact.c ├── expand.c ├── gasp.c ├── gimpel.c ├── globals.c ├── hack.c ├── indep.c ├── irred.c ├── main.c ├── main.h ├── map.c ├── matrix.c ├── mincov.c ├── mincov.h ├── mincov_int.h ├── opo.c ├── pair.c ├── part.c ├── port.h ├── primes.c ├── prtime.c ├── reduce.c ├── rows.c ├── set.c ├── setc.c ├── sharp.c ├── sigma.c ├── signature.c ├── signature.h ├── signature_exact.c ├── sminterf.c ├── solution.c ├── sparse.c ├── sparse.h ├── sparse_int.h ├── strdup.c ├── strdup.h ├── unate.c ├── util_signature.c ├── utility.h └── verify.c ├── index.js ├── man ├── espresso.1 └── espresso.5 ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | *.log 35 | 36 | # Deployment directories 37 | build/ 38 | bin/ 39 | 40 | # Dependencies 41 | node_modules/ 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '8' 5 | - '10' 6 | - '12' 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - gcc-4.9 13 | - g++-4.9 14 | before_install: 15 | - export CC="gcc-4.9" CXX="g++-4.9" 16 | deploy: 17 | provider: npm 18 | email: seb.cottinet@gmail.com 19 | api_key: 20 | secure: ZZ+eVrRwDXnr9LpRIX8XJ+/gs7HhAEcvL08m37UEBHt5c9ejazXkJgsUZeTnQK8PJGorXjNwi3jHT7NOBgrzk78iioeNhWk4Y91XNuChybsJcbmkTVfSAeQQJFEC/XjHM+vsh8fjkRx7jUvMxQryD+JCFiUvj9sxPPgm8+J6rcpJpkYMN63iwrBFqfUIquu3edvjr2ZZbySGsw/kPZ2/eeHCgCEqJhjkHZNRGxwHCErk8dU3WFY/R3Hqe/NiEYsw3FhLD/k7wkzup3pdF1xb1lE5I8iDvj695dl7cyDssCcfboPxLYuzK8GowUwCweLGBNzRLu9vGANkO84j/AmCF0DsOP+36dNP0jIy5MmOKZ9uvz0uyyeiF4QnhoIcrSpolCQo5TSzWY0OGduo1LfT477Fr8NoOBMayHGjYeae1X9Lt2f7sAxFtEHT/C/n1TOLfkGzddZb6OOBeGO2GhPr5J7Lyka8/xEs+rBPJFXCo9p7CeS/AVRK8UcqBOGJdjIZPvCEbqcs+BBB+XibiVya2xeQ/P+CavNYW9G5ggftKXzmlXibzAENdKSFOdbwnp3XjSHNJSe4jdOZndMvuVKFUjXXwtdKwb4o6MhTNRvLbTX+gXh5DoB9e857mXuxs7NTeGKeeiIwezNgDQSHhRMn3UhiMmBnlcwWtn4RVHrvqqs= 21 | on: 22 | repo: scottinet/espresso-logic-minimizer 23 | branch: master 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Sébastien Cottinet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # espresso-logic-minimizer 2 | 3 | A NodeJS bridge to the [Espresso heuristic logic minimizer](https://en.wikipedia.org/wiki/Espresso_heuristic_logic_minimizer). 4 | 5 | The original source code comes from the [University of California, Berkeley](https://embedded.eecs.berkeley.edu/pubs/downloads/espresso/index.htm). 6 | 7 | # Install 8 | 9 | `npm install espresso-logic-minimizer` 10 | 11 | # API 12 | 13 | ## minimize(truthTable) 14 | 15 | Static method, minimize the input truth table directly. 16 | The truth table must be provided as an array of strings following the PLA format (see below). 17 | 18 | Returns an array of strings containing the minimized conditions. 19 | 20 | ## Espresso(inputSize, outputSize) 21 | 22 | Constructor allowing to input the truth table progressively. 23 | `inputSize` is the number of input conditions, and `outputSize` is the number of output conditions. 24 | They match, respectively, the `.i` and `.o` parameters of PLA files. 25 | 26 | A class instance can only process one PLA. 27 | 28 | ### push(input, output) 29 | 30 | Pushes into the current PLA a truth table entry. 31 | Both `input` and `output` are arrays of truthy/falsey values. 32 | 33 | ### minimize() 34 | 35 | Ends the current PLA and minimizes it. Subsequent calls will simple return the already computed result. 36 | Returns an array of strings containing the minimized conditions. 37 | 38 | # How to use: simple boolean minification 39 | 40 | Take the following boolean conditions: 41 | 42 | `(NOT (cond1 AND cond2) OR cond3) AND cond4` 43 | 44 | Espresso is able to simplify such conditions to [Disjunctive Normal Form (DNF)](https://en.wikipedia.org/wiki/Disjunctive_normal_form). 45 | 46 | To do so, start by creating a truth table describing the above boolean conditions: 47 | 48 | |cond1|cond2|cond3|cond4|result| 49 | |-----|-----|-----|-----|------| 50 | |0|0|0|0|0| 51 | |0|0|0|1|1| 52 | |0|0|1|0|0| 53 | |0|0|1|1|1| 54 | |0|1|0|0|0| 55 | |0|1|0|1|1| 56 | |0|1|1|0|0| 57 | |0|1|1|1|1| 58 | |1|0|0|0|0| 59 | |1|0|0|1|1| 60 | |1|0|1|0|0| 61 | |1|0|1|1|1| 62 | |1|1|0|0|0| 63 | |1|1|0|1|0| 64 | |1|1|1|0|0| 65 | |1|1|1|1|1| 66 | 67 | 68 | ## Passing the PLA data directory to espresso 69 | 70 | Creating and maintaining a truth table entirely in memory can and will quickly fill up heap space. This way of minimizing PLAs is only to be used on small numbers of input conditions. 71 | 72 | To use the in-memory PLA conversion, you first need to convert the truth table above into PLA format: 73 | 74 | ``` 75 | # there are 4 input variables 76 | .i 4 77 | 78 | # there is only 1 output result 79 | .o 1 80 | 81 | # the following is the truth table 82 | 0000 0 83 | 0001 1 84 | 0010 0 85 | 0011 1 86 | 0100 0 87 | 0101 1 88 | 0110 0 89 | 0111 1 90 | 1000 0 91 | 1001 1 92 | 1010 0 93 | 1011 1 94 | 1100 0 95 | 1101 0 96 | 1110 0 97 | 1111 1 98 | 99 | # end of the PLA data 100 | .e 101 | ``` 102 | 103 | Then simply store it into an array of strings, and feed it to espresso. 104 | 105 | ```js 106 | const espresso = require('espresso-logic-minimizer'); 107 | 108 | const pla = [ 109 | '.i 4', 110 | '.o 1', 111 | '0000 0', 112 | '0001 1', 113 | '0010 0', 114 | '0011 1', 115 | '0100 0', 116 | '0101 1', 117 | '0110 0', 118 | '0111 1', 119 | '1000 0', 120 | '1001 1', 121 | '1010 0', 122 | '1011 1', 123 | '1100 0', 124 | '1101 0', 125 | '1110 0', 126 | '1111 1', 127 | '.e' 128 | ]; 129 | 130 | console.log('result = ', espresso.minimize(pla)); 131 | ``` 132 | 133 | ## Input the truth table progressively 134 | 135 | ```js 136 | const Espresso = require('espresso-logic-minimizer').Espresso; 137 | 138 | const espresso = new Espresso(4, 1); 139 | 140 | espresso.push([0, 0, 0, 0], [0]); 141 | espresso.push([0, 0, 0, 1], [1]); 142 | espresso.push([0, 0, 1, 0], [0]); 143 | espresso.push([0, 0, 1, 1], [1]); 144 | espresso.push([0, 1, 0, 0], [0]); 145 | espresso.push([0, 1, 0, 1], [1]); 146 | espresso.push([0, 1, 1, 0], [0]); 147 | espresso.push([0, 1, 1, 1], [1]); 148 | espresso.push([1, 0, 0, 0], [0]); 149 | espresso.push([1, 0, 0, 1], [1]); 150 | espresso.push([1, 0, 1, 0], [0]); 151 | espresso.push([1, 0, 1, 1], [1]); 152 | espresso.push([1, 1, 0, 0], [0]); 153 | espresso.push([1, 1, 0, 1], [0]); 154 | espresso.push([1, 1, 1, 0], [0]); 155 | espresso.push([1, 1, 1, 1], [1]); 156 | 157 | console.log('result = ', espresso.minimize()); 158 | ``` 159 | 160 | 161 | ## Interpreting the result 162 | 163 | Both examples above output the following result: 164 | 165 | ``` 166 | result = [ '0--1 1', '-0-1 1', '--11 1' ] 167 | ``` 168 | 169 | As explained above, this result is expressed in disjunctive normal form: each array entry contains a set of AND conditions, and a OR condition must be used between entries. 170 | 171 | Here is what you need to know to understand the result: 172 | 173 | * `-` => "DC" flag => Don't Care 174 | * `0` => "OFF" flag => this condition must be FALSE for the result to be true. This means we need to perform a "NOT" on this condition 175 | * `1` => "ON" flag => this condition must be TRUE for the result to be true. 176 | 177 | So we have: 178 | 179 | * `0-–1 1` : NOT cond1 AND cond4 180 | * `-0-1 1` : NOT cond2 AND cond4 181 | * `–-11 1` : cond3 AND cond4 182 | 183 | Final result: 184 | 185 | `NOT cond2 AND cond4 OR NOT cond1 AND cond4 OR cond3 AND cond4` 186 | 187 | # Repository content 188 | 189 | The original source code is stored in the `espresso-src` folder. The only changes made to this code allow it to be compiled using C99 standards, and a few warnings have also been fixed. 190 | 191 | The original `main.c` and `main.h` are still present, even if unused in this NodeJS library. The original executable can still be compiled and executed by executing `make` in the `espresso-src` folder. 192 | This will create a `bin/` directory in the root of the repository containing the `espresso` executable. 193 | 194 | The `man` directory contains the original man pages of Espresso: 195 | 196 | * `espresso.1` contains the executable options documentation 197 | * `espresso.5` details the PLA format and contains other, more complex PLA examples 198 | 199 | The NodeJS bridge itself is contained in the `bridge` directory. 200 | 201 | # License 202 | 203 | This library is published under the [MIT License](https://opensource.org/licenses/MIT). 204 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "EspressoLogicMinimizer", 5 | "cflags": ["-std=c99", "-Wno-misleading-indentation", "-Wno-unused-result", "-Wno-format-overflow", "-Wno-implicit-fallthrough"], 6 | "sources": [ 7 | "bridge/addon.cc", 8 | "bridge/bridge.c", 9 | "espresso-src/black_white.c", 10 | "espresso-src/canonical.c", 11 | "espresso-src/cofactor.c", 12 | "espresso-src/cols.c", 13 | "espresso-src/compl.c", 14 | "espresso-src/contain.c", 15 | "espresso-src/cpu_time.c", 16 | "espresso-src/cubestr.c", 17 | "espresso-src/cvrin.c", 18 | "espresso-src/cvrm.c", 19 | "espresso-src/cvrmisc.c", 20 | "espresso-src/cvrout.c", 21 | "espresso-src/dominate.c", 22 | "espresso-src/equiv.c", 23 | "espresso-src/espresso.c", 24 | "espresso-src/essen.c", 25 | "espresso-src/essentiality.c", 26 | "espresso-src/exact.c", 27 | "espresso-src/expand.c", 28 | "espresso-src/gasp.c", 29 | "espresso-src/gimpel.c", 30 | "espresso-src/globals.c", 31 | "espresso-src/hack.c", 32 | "espresso-src/indep.c", 33 | "espresso-src/irred.c", 34 | "espresso-src/map.c", 35 | "espresso-src/matrix.c", 36 | "espresso-src/mincov.c", 37 | "espresso-src/opo.c", 38 | "espresso-src/pair.c", 39 | "espresso-src/part.c", 40 | "espresso-src/primes.c", 41 | "espresso-src/prtime.c", 42 | "espresso-src/reduce.c", 43 | "espresso-src/rows.c", 44 | "espresso-src/set.c", 45 | "espresso-src/setc.c", 46 | "espresso-src/sharp.c", 47 | "espresso-src/sigma.c", 48 | "espresso-src/signature.c", 49 | "espresso-src/signature_exact.c", 50 | "espresso-src/sminterf.c", 51 | "espresso-src/solution.c", 52 | "espresso-src/sparse.c", 53 | "espresso-src/unate.c", 54 | "espresso-src/util_signature.c", 55 | "espresso-src/verify.c" 56 | ], 57 | "include_dirs" : [ 58 | " 2 | #include 3 | #include 4 | #include 5 | 6 | extern "C" char ** run_espresso_from_data(char ** data, unsigned int length); 7 | extern "C" char ** run_espresso_from_path(char * path); 8 | 9 | NAN_METHOD(minimize_from_data) { 10 | v8::Local 11 | array = info[0].As(), 12 | returnValue = Nan::New(); 13 | unsigned int length = array->Length(); 14 | char 15 | **truthTable, 16 | **result; 17 | 18 | // returns an empty array if no input is provided 19 | if (length == 0) { 20 | info.GetReturnValue().Set(returnValue); 21 | return; 22 | } 23 | 24 | truthTable = new char*[length]; 25 | 26 | v8::Local context = info.GetIsolate()->GetCurrentContext(); 27 | for(unsigned int i = 0; i < length; ++i) { 28 | v8::Local src = array->Get(context, i) 29 | .ToLocalChecked() 30 | .As(); 31 | Nan::Utf8String val(src); 32 | truthTable[i] = new char[strlen(*val)+1]; 33 | strcpy(truthTable[i], *val); 34 | } 35 | 36 | result = run_espresso_from_data(truthTable, length); 37 | 38 | if (result != NULL) { 39 | for(unsigned int i = 0; result[i] != NULL; ++i) { 40 | Nan::Set(returnValue, i, Nan::New(result[i]).ToLocalChecked()); 41 | 42 | // since the result comes from C code, the memory was 43 | // allocated using malloc and must be freed with free 44 | free(result[i]); 45 | } 46 | 47 | free(result); 48 | } 49 | 50 | // memory clean up 51 | for(unsigned int i = 0; i < length; delete truthTable[i++]); 52 | delete truthTable; 53 | 54 | info.GetReturnValue().Set(returnValue); 55 | } 56 | 57 | NAN_METHOD(minimize_from_path) { 58 | v8::Local path = info[0].As(); 59 | v8::Local returnValue = Nan::New(); 60 | char **result; 61 | 62 | // returns an empty array if no path 63 | if (path->Length() == 0) { 64 | info.GetReturnValue().Set(returnValue); 65 | return; 66 | } 67 | 68 | Nan::Utf8String c_pathstr(path); 69 | result = run_espresso_from_path(*c_pathstr); 70 | 71 | if (result != NULL) { 72 | for(unsigned int i = 0; result[i] != NULL; ++i) { 73 | Nan::Set(returnValue, i, Nan::New(result[i]).ToLocalChecked()); 74 | 75 | // since the result comes from C code, the memory was 76 | // allocated using malloc and must be freed with free 77 | free(result[i]); 78 | } 79 | 80 | free(result); 81 | } 82 | 83 | info.GetReturnValue().Set(returnValue); 84 | } 85 | 86 | NAN_MODULE_INIT(init) { 87 | Nan::Set(target, Nan::New("minimize_from_data").ToLocalChecked(), Nan::GetFunction(Nan::New(minimize_from_data)).ToLocalChecked()); 88 | Nan::Set(target, Nan::New("minimize_from_path").ToLocalChecked(), Nan::GetFunction(Nan::New(minimize_from_path)).ToLocalChecked()); 89 | } 90 | 91 | NODE_MODULE(EspressoLogicMinimizer, init) 92 | -------------------------------------------------------------------------------- /bridge/bridge.c: -------------------------------------------------------------------------------- 1 | /* 2 | Entry point of the espresso source code. 3 | Replaces main.c 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include "bridge.h" 9 | #include "espresso.h" 10 | 11 | char ** get_solution(pPLA); 12 | void add_to_buffer(char **, unsigned int *, unsigned int*, char); 13 | 14 | char ** run_espresso(FILE * fpla) { 15 | pPLA PLA; 16 | bool error; 17 | cost_t cost; 18 | pcover fold; 19 | char ** result = NULL; 20 | 21 | if (read_pla(fpla, TRUE, TRUE, FD_type, &PLA) == EOF) { 22 | return NULL; 23 | } 24 | 25 | // makes sure free() won't crash on this variable 26 | PLA->filename = NULL; 27 | 28 | fold = sf_save(PLA->F); 29 | PLA->F = espresso(PLA->F, PLA->D, PLA->R); 30 | EXECUTE(error = verify(PLA->F, fold, PLA->D), VERIFY_TIME, PLA->F, cost); 31 | 32 | if (error) { 33 | PLA->F = fold; 34 | (void) check_consistency(PLA); 35 | } else { 36 | free_cover(fold); 37 | result = get_solution(PLA); 38 | } 39 | 40 | /* cleanup all used memory */ 41 | free_PLA(PLA); 42 | FREE(cube.part_size); 43 | setdown_cube(); /* free the cube/cdata structure data */ 44 | sf_cleanup(); /* free unused set structures */ 45 | sm_cleanup(); /* sparse matrix cleanup */ 46 | 47 | return result; 48 | } 49 | 50 | char ** run_espresso_from_data(char ** data, unsigned int length) { 51 | if (length == 0) { 52 | return NULL; 53 | } 54 | 55 | FILE * tempPLA = tmpfile(); 56 | 57 | for(unsigned int i = 0; i < length; ++i) { 58 | fputs(data[i], tempPLA); 59 | fputc('\n', tempPLA); 60 | } 61 | 62 | rewind(tempPLA); 63 | 64 | char ** result = run_espresso(tempPLA); 65 | fclose(tempPLA); 66 | 67 | return result; 68 | } 69 | 70 | char ** run_espresso_from_path(char * path) { 71 | if (strlen(path) == 0) { 72 | return NULL; 73 | } 74 | 75 | FILE * fpla = fopen(path, "r"); 76 | 77 | if (fpla == NULL) { 78 | fprintf(stderr, "[Espresso Logic Minimizer] Cannot open file %s\n", path); 79 | return NULL; 80 | } 81 | 82 | char ** result = run_espresso(fpla); 83 | 84 | fclose(fpla); 85 | return result; 86 | } 87 | 88 | char ** get_solution(pPLA PLA) { 89 | register pcube last, p; 90 | char ** solutions = malloc(((PLA->F)->count +1) * sizeof(char*)); 91 | unsigned int current_solution = 0, buffer_size = 128; 92 | char *buffer = malloc(buffer_size); // dynamic alloc to allow realloc if necessary 93 | 94 | if (solutions == NULL || buffer == NULL) { 95 | fprintf(stderr, "[espresso-logic-minimizer] FATAL: memory allocation failed\n"); 96 | 97 | if (solutions) free(solutions); 98 | if (buffer) free(buffer); 99 | return NULL; 100 | } 101 | 102 | // we set the last item +1 to NULL to let caller functions know when to stop 103 | // reading the array 104 | solutions[(PLA->F)->count] = NULL; 105 | 106 | foreach_set(PLA->F, last, p) { 107 | unsigned int cursor = 0; 108 | 109 | /* 110 | Rewrite of cvrout.c:print_cube to replace stdout printing with 111 | variable storage 112 | */ 113 | int i, var; 114 | 115 | for(var = 0; var < cube.num_binary_vars; var++) { 116 | add_to_buffer(&buffer, &buffer_size, &cursor, "?01-" [GETINPUT(p, var)]); 117 | } 118 | 119 | for(var = cube.num_binary_vars; var < cube.num_vars - 1; var++) { 120 | add_to_buffer(&buffer, &buffer_size, &cursor, ' '); 121 | 122 | for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { 123 | add_to_buffer(&buffer, &buffer_size, &cursor, "01" [is_in_set(p, i) != 0]); 124 | } 125 | } 126 | 127 | if (cube.output != -1) { 128 | int last_part = cube.last_part[cube.output]; 129 | add_to_buffer(&buffer, &buffer_size, &cursor, ' '); 130 | 131 | for(i = cube.first_part[cube.output]; i <= last_part; i++) { 132 | add_to_buffer(&buffer, &buffer_size, &cursor, "01" [is_in_set(p, i) != 0]); 133 | } 134 | } 135 | 136 | solutions[current_solution] = malloc(cursor + 1); 137 | 138 | if (solutions[current_solution] == NULL) { 139 | fprintf(stderr, "[espresso-logic-minimizer] FATAL: memory allocation failed\n"); 140 | 141 | for(unsigned int j = 0; j < current_solution; j++) free(solutions[j]); 142 | free(buffer); 143 | return NULL; 144 | } 145 | 146 | buffer[cursor] = '\0'; 147 | strcpy(solutions[current_solution], buffer); 148 | current_solution++; 149 | } 150 | 151 | free(buffer); 152 | return solutions; 153 | } 154 | 155 | void add_to_buffer(char ** buffer, unsigned int *buffer_size, unsigned int* cursor, char chr) { 156 | if (*cursor + 1 > *buffer_size) { 157 | char * new_buffer = realloc(*buffer, *buffer_size * 2); 158 | 159 | if (new_buffer == NULL) { 160 | fprintf(stderr, "[espresso-logic-minimizer] FATAL: memory allocation failed\n"); 161 | return; 162 | } 163 | 164 | *buffer_size *= 2; 165 | *buffer = new_buffer; 166 | } 167 | 168 | (*buffer)[*cursor] = chr; 169 | (*cursor)++; 170 | } 171 | -------------------------------------------------------------------------------- /bridge/bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef __ESPRESSO_BRIDGE_H 2 | #define __ESPRESSO_BRIDGE_H 3 | 4 | extern void init_runtime(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /espresso-src/COPYING: -------------------------------------------------------------------------------- 1 | /* Oct Tools Distribution 3.0 2 | * 3 | * Copyright (c) 1988, 1989, Regents of the University of California. 4 | * All rights reserved. 5 | * 6 | * Use and copying of this software and preparation of derivative works 7 | * based upon this software are permitted. However, any distribution of 8 | * this software or derivative works must include the above copyright 9 | * notice. 10 | * 11 | * This software is made available AS IS, and neither the Electronics 12 | * Research Laboratory or the University of California make any 13 | * warranty about the software, its performance or its conformity to 14 | * any specification. 15 | * 16 | * Suggestions, comments, or improvements are welcome and should be 17 | * addressed to: 18 | * 19 | * octtools@eros.berkeley.edu 20 | * ..!ucbvax!eros!octtools 21 | */ 22 | -------------------------------------------------------------------------------- /espresso-src/Makefile: -------------------------------------------------------------------------------- 1 | SRC := $(wildcard *.c) 2 | 3 | OBJ := ${SRC:.c=.o} 4 | 5 | TARGETDIR := ../bin 6 | TARGET := $(TARGETDIR)/espresso 7 | 8 | all: prepare $(TARGET) 9 | 10 | prepare: 11 | mkdir $(TARGETDIR) 12 | 13 | $(TARGET): $(OBJ) 14 | $(LINK.c) $(OBJ) -o $(TARGET) 15 | 16 | clean: 17 | @- $(RM) $(TARGET) 18 | @- $(RM) $(OBJ) 19 | 20 | -------------------------------------------------------------------------------- /espresso-src/README: -------------------------------------------------------------------------------- 1 | 2 | The original Espresso code comes from the 1989 Berkeley code (https://embedded.eecs.berkeley.edu/pubs/downloads/espresso/index.htm). See the COPYING file for original code license. 3 | 4 | This distribution is just a C99 port of the excellent work made by Andrew to simplify and port to C89 standards (https://github.com/Rupan/espresso/tree/master/jni/espresso). 5 | -------------------------------------------------------------------------------- /espresso-src/black_white.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "espresso.h" 4 | #include "signature.h" 5 | 6 | void setup_bw(pset_family R, pset c); 7 | int black_white(void); 8 | void split_list(pset_family R, int v); 9 | void merge_list(void); 10 | 11 | static void alloc_list(int size); 12 | static void free_list(void); 13 | static void init_list(int size); 14 | static void delete(int element); 15 | static void insert(int element); 16 | static void print_links(int size, int *list); 17 | void print_bw(int size); 18 | 19 | static void alloc_stack(int size); 20 | static void free_stack(void); 21 | static void clear(void); 22 | 23 | static int white_head, white_tail; 24 | static int black_head, black_tail; 25 | static int forward_link, backward_link; 26 | static int *forward, *backward; 27 | 28 | static int *stack_head, *stack_tail, stack_p; 29 | 30 | static pcover BB; 31 | 32 | static void alloc_list(int size) 33 | { 34 | forward = (int *)calloc(size, sizeof(int)); 35 | backward =(int *)calloc(size, sizeof(int)); 36 | if(!forward || !backward){ 37 | perror("alloc_list"); 38 | exit(1); 39 | } 40 | } 41 | 42 | static void free_list(void) 43 | { 44 | free(forward); 45 | free(backward); 46 | } 47 | 48 | static void init_list(int size) 49 | { 50 | int i; 51 | for(i = 0; i < size; i++){ 52 | forward[i] = i+1; 53 | backward[i] = i-1; 54 | } 55 | forward[size-1] = -1; 56 | backward[0] = -1; 57 | white_head = 0; 58 | white_tail = size - 1; 59 | black_head = -1; 60 | black_tail = -1; 61 | } 62 | 63 | static void delete(int element) 64 | { 65 | forward_link = forward[element]; 66 | backward_link = backward[element]; 67 | if(forward_link != -1){ 68 | if(backward_link != -1){ 69 | forward[backward_link] = forward_link; 70 | backward[forward_link] = backward_link; 71 | } 72 | else{ 73 | white_head = forward_link; 74 | backward[forward_link] = -1; 75 | } 76 | } 77 | else{ 78 | if(backward_link != -1){ 79 | white_tail = backward_link; 80 | forward[backward_link] = -1; 81 | } 82 | else{ 83 | white_head = white_tail = -1; 84 | } 85 | } 86 | } 87 | 88 | static void insert(int element) 89 | { 90 | if(black_head != -1){ 91 | forward[element] = black_head; 92 | backward[element] = -1; 93 | backward[black_head] = element; 94 | black_head = element; 95 | } 96 | else{ 97 | black_head = black_tail = element; 98 | forward[element] = backward[element] = -1; 99 | } 100 | } 101 | 102 | void merge_list(void) 103 | { 104 | if(white_head != -1){ 105 | if(black_head != -1){ 106 | forward[white_tail] = black_head; 107 | backward[black_head] = white_tail; 108 | white_tail = black_tail; 109 | black_head = black_tail = -1; 110 | } 111 | } 112 | else{ 113 | white_head = black_head; 114 | white_tail = black_tail; 115 | black_head = black_tail = -1; 116 | } 117 | } 118 | 119 | static void print_links(int size, int *list) 120 | { 121 | int i; 122 | for(i = 0; i < size; i++){ 123 | printf("%d%c",list[i],(i+1)%10?'\t':'\n'); 124 | } 125 | printf("\n"); 126 | } 127 | 128 | void print_bw(int size) 129 | { 130 | printf("white_head %d\twhite_tail %d\tblack_head %d\tblack_tail %d\n", 131 | white_head,white_tail,black_head,black_tail); 132 | print_links(size,forward); 133 | print_links(size,backward); 134 | } 135 | 136 | static void alloc_stack(int size) 137 | { 138 | stack_head = (int *)calloc(size,sizeof(int)); 139 | stack_tail = (int *)calloc(size,sizeof(int)); 140 | if(!stack_head || !stack_tail){ 141 | perror("alloc_stack"); 142 | exit(1); 143 | } 144 | } 145 | 146 | static void free_stack(void) 147 | { 148 | free(stack_head); 149 | free(stack_tail); 150 | } 151 | 152 | void push_black_list(void) 153 | { 154 | stack_head[stack_p] = black_head; 155 | stack_tail[stack_p++] = black_tail; 156 | } 157 | 158 | void pop_black_list(void) 159 | { 160 | black_head = stack_head[--stack_p]; 161 | black_tail = stack_tail[stack_p]; 162 | } 163 | 164 | static void clear(void) 165 | { 166 | stack_p = 0; 167 | } 168 | 169 | void setup_bw(pset_family R, pset c) 170 | { 171 | pcube out_part_r; 172 | int size = R->count; 173 | register int i; 174 | pcube b,r; 175 | register int w, last; 176 | register unsigned int x; 177 | 178 | /* Allocate memory */ 179 | alloc_list(size); 180 | alloc_stack(cube.num_binary_vars); 181 | BB = new_cover(size); 182 | BB->count = size; 183 | out_part_r = new_cube(); 184 | 185 | init_list(size); 186 | clear(); 187 | /* form BB */ 188 | /* Blocking Matrix formed here is bit-complement of that in sigma */ 189 | foreachi_set(R,i,r){ 190 | b = GETSET(BB,i); 191 | if ((last = cube.inword) != -1) { 192 | /* Check the partial word of binary variables */ 193 | x = r[last] & c[last]; 194 | x = ~(x | x >> 1) & cube.inmask; 195 | b[last] = r[last] & (x | x << 1); 196 | /* Check the full words of binary variables */ 197 | for(w = 1; w < last; w++) { 198 | x = r[w] & c[w]; 199 | x = ~(x | x >> 1) & DISJOINT; 200 | b[w] = r[w] & (x | x << 1); 201 | } 202 | } 203 | PUTLOOP(b,LOOP(r)); 204 | INLINEset_and(b,b,cube.binary_mask); 205 | INLINEset_and(out_part_r,cube.mv_mask,r); 206 | if(!setp_implies(out_part_r,c)){ 207 | INLINEset_or(b,b,out_part_r); 208 | } 209 | set_not(b); 210 | } 211 | free_cube(out_part_r); 212 | } 213 | 214 | void free_bw(void) 215 | { 216 | free_list(); 217 | free_stack(); 218 | free_cover(BB); 219 | } 220 | 221 | int black_white(void) 222 | { 223 | int b_index, w_index; 224 | int containment; 225 | for(b_index = black_head; b_index != -1; b_index = forward[b_index]){ 226 | containment = FALSE; 227 | for(w_index = white_head; w_index != -1; w_index = forward[w_index]){ 228 | if(setp_implies(GETSET(BB,b_index), GETSET(BB,w_index))){ 229 | containment = TRUE; 230 | break; 231 | } 232 | } 233 | if(containment == FALSE){ 234 | return FALSE; 235 | } 236 | } 237 | return TRUE; 238 | } 239 | 240 | void reset_black_list(void) 241 | { 242 | black_head = black_tail = -1; 243 | } 244 | 245 | 246 | void split_list(pset_family R, int v) 247 | { 248 | int index, next_index;; 249 | 250 | for(index = white_head; index != -1; index = next_index){ 251 | next_index = forward[index]; 252 | if(!is_in_set(GETSET(R, index), v)){ 253 | delete(index); 254 | insert(index); 255 | } 256 | } 257 | } 258 | 259 | /* Data Structures for ordering variables in ess_test_and_reduction */ 260 | static int variable_count; /* Number of variables currently in the list */ 261 | static int *variable_forward_chain; /* Next */ 262 | static int *variable_backward_chain; /* Previous */ 263 | static int variable_head; /* first element in the list */ 264 | static int variable_tail; /* last element in the list */ 265 | 266 | void variable_list_alloc(int size) 267 | { 268 | variable_forward_chain = (int *)calloc(size, sizeof(int)); 269 | variable_backward_chain =(int *)calloc(size, sizeof(int)); 270 | if(!variable_forward_chain || !variable_backward_chain){ 271 | perror("variable_list_alloc"); 272 | exit(1); 273 | } 274 | } 275 | 276 | void variable_list_init(int reduced_c_free_count, int *reduced_c_free_list) 277 | { 278 | int i; 279 | int v, next_v; 280 | 281 | variable_count = reduced_c_free_count; 282 | if(variable_count == 0){ 283 | variable_head = variable_tail = -1; 284 | return; 285 | } 286 | variable_head = reduced_c_free_list[0]; 287 | variable_tail = reduced_c_free_list[variable_count - 1]; 288 | variable_forward_chain[variable_tail] = -1; 289 | variable_backward_chain[variable_head] = -1; 290 | 291 | next_v = variable_head; 292 | for(i = 1; i < variable_count; i++){ 293 | v = next_v; next_v = reduced_c_free_list[i]; 294 | variable_forward_chain[v] = next_v; 295 | variable_backward_chain[next_v] = v; 296 | } 297 | 298 | } 299 | 300 | void variable_list_delete(int element) 301 | { 302 | variable_count--; 303 | forward_link = variable_forward_chain[element]; 304 | backward_link = variable_backward_chain[element]; 305 | if(forward_link != -1){ 306 | if(backward_link != -1){ 307 | variable_forward_chain[backward_link] = forward_link; 308 | variable_backward_chain[forward_link] = backward_link; 309 | } 310 | else{ 311 | variable_head = forward_link; 312 | variable_backward_chain[forward_link] = -1; 313 | } 314 | } 315 | else{ 316 | if(backward_link != -1){ 317 | variable_tail = backward_link; 318 | variable_forward_chain[backward_link] = -1; 319 | } 320 | else{ 321 | variable_head = variable_tail = -1; 322 | } 323 | } 324 | } 325 | 326 | void variable_list_insert(int element) 327 | { 328 | variable_count++; 329 | if(variable_head != -1){ 330 | variable_forward_chain[element] = variable_head; 331 | variable_backward_chain[element] = -1; 332 | variable_backward_chain[variable_head] = element; 333 | variable_head = element; 334 | } 335 | else{ 336 | variable_head = variable_tail = element; 337 | variable_forward_chain[element] = -1; 338 | variable_backward_chain[element] = -1; 339 | } 340 | } 341 | 342 | int variable_list_empty(void) 343 | { 344 | return variable_count ? FALSE : TRUE; 345 | } 346 | 347 | void get_next_variable(int *pv, int *pphase, pset_family R) 348 | { 349 | int v,e0,e1; 350 | int e0_black_count,e1_black_count; 351 | int w_index; 352 | int max_black_count,max_variable = 0, max_phase = 0; 353 | pcube r; 354 | 355 | max_black_count = -1; 356 | for(v = variable_head; v != -1; v = variable_forward_chain[v]){ 357 | e0 = v<<1; e1 = e0 + 1; 358 | e0_black_count = e1_black_count = 0; 359 | for(w_index = white_head; w_index != -1; w_index = forward[w_index]){ 360 | r = GETSET(R,w_index); 361 | if(is_in_set(r,e0)){ 362 | if(is_in_set(r,e1)){ 363 | continue; 364 | } 365 | else{ 366 | e1_black_count++; 367 | } 368 | } 369 | else{ 370 | e0_black_count++; 371 | } 372 | } 373 | if(e0_black_count > e1_black_count){ 374 | if(e0_black_count > max_black_count){ 375 | max_black_count = e0_black_count; 376 | max_variable = v; 377 | max_phase = 0; 378 | } 379 | } 380 | else{ 381 | if(e1_black_count > max_black_count){ 382 | max_black_count = e1_black_count; 383 | max_variable = v; 384 | max_phase = 1; 385 | } 386 | } 387 | } 388 | *pv = max_variable; 389 | *pphase = max_phase; 390 | } 391 | 392 | void print_variable_list(void) 393 | { 394 | printf("Variable_Forward_Chain:\n"); 395 | print_links(cube.num_binary_vars,variable_forward_chain); 396 | printf("Variable_Backward_Chain:\n"); 397 | print_links(cube.num_binary_vars,variable_backward_chain); 398 | } 399 | -------------------------------------------------------------------------------- /espresso-src/canonical.c: -------------------------------------------------------------------------------- 1 | /* Module:canonical.c 2 | * contains routines for finding the canonical cover of the 3 | * incompletely specified logic function. 4 | * Routine: 5 | * pcover find_canonical_cover(): 6 | * Finds canonical cover of the incompletely specified logic function 7 | * by iteratively calling ess_test_and_reduction for each cube in the 8 | * ON-set. 9 | */ 10 | 11 | #include 12 | #include "espresso.h" 13 | #include "signature.h" 14 | 15 | /* 16 | * find_canonical_cover 17 | * Objective: find canonical cover of the essential signature cube 18 | * Input: 19 | * F: ONSET cover; 20 | * D: DC cover; 21 | * R: OFFSET cover; 22 | * Output: 23 | * Return canonical cover of the essential signature cube 24 | */ 25 | pcover find_canonical_cover(pset_family F1, pset_family D, pset_family R) 26 | { 27 | pcover F; 28 | pcover E,ESC; 29 | pcover COVER; 30 | pcube c; 31 | pcube d, *extended_dc; 32 | 33 | F = sf_save(F1); 34 | E = new_cover(D->count); 35 | E->count = D->count; 36 | sf_copy(E,D); 37 | 38 | ESC = new_cover(F->count); 39 | 40 | while(F->count){ 41 | c = GETSET(F,--F->count); 42 | RESET(c,NONESSEN); 43 | extended_dc = cube2list(E,F); 44 | d = reduce_cube(extended_dc,c); 45 | free_cubelist(extended_dc); 46 | if(setp_empty(d)){ 47 | free_cube(d); 48 | continue; 49 | } 50 | c = get_sigma(R,d); 51 | S_EXECUTE(COVER = etr_order(F,E,R,c,d),ETR_TIME); 52 | free_cube(d); 53 | if(TESTP(c,NONESSEN)){ 54 | sf_append(F,COVER); 55 | } 56 | else{ 57 | free_cover(COVER); 58 | sf_addset(E,c); 59 | sf_addset(ESC,c); 60 | } 61 | free_cube(c); 62 | } 63 | free_cover(F); 64 | free_cover(E); 65 | 66 | return ESC; 67 | } 68 | -------------------------------------------------------------------------------- /espresso-src/cofactor.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | /* 4 | The cofactor of a cover against a cube "c" is a cover formed by the 5 | cofactor of each cube in the cover against c. The cofactor of two 6 | cubes is null if they are distance 1 or more apart. If they are 7 | distance zero apart, the cofactor is the restriction of the cube 8 | to the minterms of c. 9 | 10 | The cube list contains the following information: 11 | 12 | T[0] = pointer to a cube identifying the variables that have 13 | been cofactored against 14 | T[1] = pointer to just beyond the sentinel (i.e., T[n] in this case) 15 | T[2] 16 | . 17 | . = pointers to cubes 18 | . 19 | T[n-2] 20 | T[n-1] = NULL pointer (sentinel) 21 | 22 | 23 | Cofactoring involves repeated application of "cdist0" to check if a 24 | cube of the cover intersects the cofactored cube. This can be 25 | slow, especially for the recursive descent of the espresso 26 | routines. Therefore, a special cofactor routine "scofactor" is 27 | provided which assumes the cofactor is only in a single variable. 28 | */ 29 | 30 | 31 | /* cofactor -- compute the cofactor of a cover with respect to a cube */ 32 | pcube *cofactor(pset *T, register pset c) 33 | { 34 | pcube temp = cube.temp[0], *Tc_save, *Tc, *T1; 35 | register pcube p; 36 | int listlen; 37 | 38 | listlen = CUBELISTSIZE(T) + 5; 39 | 40 | /* Allocate a new list of cube pointers (max size is previous size) */ 41 | Tc_save = Tc = ALLOC(pcube, listlen); 42 | 43 | /* pass on which variables have been cofactored against */ 44 | *Tc++ = set_or(new_cube(), T[0], set_diff(temp, cube.fullset, c)); 45 | Tc++; 46 | 47 | /* Loop for each cube in the list, determine suitability, and save */ 48 | for(T1 = T+2; (p = *T1++) != NULL; ) { 49 | if (p != c) { 50 | 51 | #ifdef NO_INLINE 52 | if (! cdist0(p, c)) goto false; 53 | #else 54 | {register int w,last;register unsigned int x;if((last=cube.inword)!=-1) 55 | {x=p[last]&c[last];if(~(x|x>>1)&cube.inmask)goto false;for(w=1;w>1)&DISJOINT)goto false;}}}{register int w,var,last; 57 | register pcube mask;for(var=cube.num_binary_vars;var= 0; i--) 123 | count[i] = 0; 124 | } 125 | 126 | /* Count the number of zeros in each column */ 127 | { register int i, *cnt; 128 | register unsigned int val; 129 | register pcube p, cof = T[0], full = cube.fullset; 130 | for(T1 = T+2; (p = *T1++) != NULL; ) 131 | for(i = LOOP(p); i > 0; i--) 132 | if ((val = full[i] & ~ (p[i] | cof[i]))) { 133 | cnt = count + ((i-1) << LOGBPI); 134 | #if BPI == 32 135 | if (val & 0xFF000000) { 136 | if (val & 0x80000000) cnt[31]++; 137 | if (val & 0x40000000) cnt[30]++; 138 | if (val & 0x20000000) cnt[29]++; 139 | if (val & 0x10000000) cnt[28]++; 140 | if (val & 0x08000000) cnt[27]++; 141 | if (val & 0x04000000) cnt[26]++; 142 | if (val & 0x02000000) cnt[25]++; 143 | if (val & 0x01000000) cnt[24]++; 144 | } 145 | if (val & 0x00FF0000) { 146 | if (val & 0x00800000) cnt[23]++; 147 | if (val & 0x00400000) cnt[22]++; 148 | if (val & 0x00200000) cnt[21]++; 149 | if (val & 0x00100000) cnt[20]++; 150 | if (val & 0x00080000) cnt[19]++; 151 | if (val & 0x00040000) cnt[18]++; 152 | if (val & 0x00020000) cnt[17]++; 153 | if (val & 0x00010000) cnt[16]++; 154 | } 155 | #endif 156 | if (val & 0xFF00) { 157 | if (val & 0x8000) cnt[15]++; 158 | if (val & 0x4000) cnt[14]++; 159 | if (val & 0x2000) cnt[13]++; 160 | if (val & 0x1000) cnt[12]++; 161 | if (val & 0x0800) cnt[11]++; 162 | if (val & 0x0400) cnt[10]++; 163 | if (val & 0x0200) cnt[ 9]++; 164 | if (val & 0x0100) cnt[ 8]++; 165 | } 166 | if (val & 0x00FF) { 167 | if (val & 0x0080) cnt[ 7]++; 168 | if (val & 0x0040) cnt[ 6]++; 169 | if (val & 0x0020) cnt[ 5]++; 170 | if (val & 0x0010) cnt[ 4]++; 171 | if (val & 0x0008) cnt[ 3]++; 172 | if (val & 0x0004) cnt[ 2]++; 173 | if (val & 0x0002) cnt[ 1]++; 174 | if (val & 0x0001) cnt[ 0]++; 175 | } 176 | } 177 | } 178 | 179 | /* 180 | * Perform counts for each variable: 181 | * cdata.var_zeros[var] = number of zeros in the variable 182 | * cdata.parts_active[var] = number of active parts for each variable 183 | * cdata.vars_active = number of variables which are active 184 | * cdata.vars_unate = number of variables which are active and unate 185 | * 186 | * best -- the variable which is best for splitting based on: 187 | * mostactive -- most # active parts in any variable 188 | * mostzero -- most # zeros in any variable 189 | * mostbalanced -- minimum over the maximum # zeros / part / variable 190 | */ 191 | 192 | { register int var, i, lastbit, active, maxactive; 193 | int best = -1, mostactive = 0, mostzero = 0, mostbalanced = 32000; 194 | cdata.vars_unate = cdata.vars_active = 0; 195 | 196 | for(var = 0; var < cube.num_vars; var++) { 197 | if (var < cube.num_binary_vars) { /* special hack for binary vars */ 198 | i = count[var*2]; 199 | lastbit = count[var*2 + 1]; 200 | active = (i > 0) + (lastbit > 0); 201 | cdata.var_zeros[var] = i + lastbit; 202 | maxactive = MAX(i, lastbit); 203 | } else { 204 | maxactive = active = cdata.var_zeros[var] = 0; 205 | lastbit = cube.last_part[var]; 206 | for(i = cube.first_part[var]; i <= lastbit; i++) { 207 | cdata.var_zeros[var] += count[i]; 208 | active += (count[i] > 0); 209 | if (active > maxactive) maxactive = active; 210 | } 211 | } 212 | 213 | /* first priority is to maximize the number of active parts */ 214 | /* for binary case, this will usually select the output first */ 215 | if (active > mostactive) 216 | best = var, mostactive = active, mostzero = cdata.var_zeros[best], 217 | mostbalanced = maxactive; 218 | else { 219 | if (active == mostactive) 220 | { 221 | /* secondary condition is to maximize the number zeros */ 222 | /* for binary variables, this is the same as minimum # of 2's */ 223 | if (cdata.var_zeros[var] > mostzero) 224 | best = var, mostzero = cdata.var_zeros[best], 225 | mostbalanced = maxactive; 226 | else { 227 | if (cdata.var_zeros[var] == mostzero) 228 | { 229 | /* third condition is to pick a balanced variable */ 230 | /* for binary vars, this means roughly equal # 0's and 1's */ 231 | if (maxactive < mostbalanced) 232 | best = var, mostbalanced = maxactive; 233 | } 234 | } 235 | } 236 | } 237 | 238 | cdata.parts_active[var] = active; 239 | cdata.is_unate[var] = (active == 1); 240 | cdata.vars_active += (active > 0); 241 | cdata.vars_unate += (active == 1); 242 | } 243 | cdata.best = best; 244 | } 245 | } 246 | 247 | int binate_split_select(pset *T, register pset cleft, register pset cright, int debug_flag) 248 | { 249 | int best = cdata.best; 250 | register int i, lastbit = cube.last_part[best], halfbit = 0; 251 | register pcube cof=T[0]; 252 | 253 | /* Create the cubes to cofactor against */ 254 | set_diff(cleft, cube.fullset, cube.var_mask[best]); 255 | set_diff(cright, cube.fullset, cube.var_mask[best]); 256 | for(i = cube.first_part[best]; i <= lastbit; i++) 257 | if (! is_in_set(cof,i)) 258 | halfbit++; 259 | for(i = cube.first_part[best], halfbit = halfbit/2; halfbit > 0; i++) 260 | if (! is_in_set(cof,i)) 261 | halfbit--, set_insert(cleft, i); 262 | for(; i <= lastbit; i++) 263 | if (! is_in_set(cof,i)) 264 | set_insert(cright, i); 265 | 266 | if (debug & debug_flag) { 267 | printf("BINATE_SPLIT_SELECT: split against %d\n", best); 268 | if (verbose_debug) 269 | printf("cl=%s\ncr=%s\n", pc1(cleft), pc2(cright)); 270 | } 271 | return best; 272 | } 273 | 274 | 275 | pcube *cube1list(pset_family A) 276 | { 277 | register pcube last, p, *plist, *list; 278 | 279 | list = plist = ALLOC(pcube, A->count + 3); 280 | *plist++ = new_cube(); 281 | plist++; 282 | foreach_set(A, last, p) { 283 | *plist++ = p; 284 | } 285 | *plist++ = NULL; /* sentinel */ 286 | list[1] = (pcube) plist; 287 | return list; 288 | } 289 | 290 | 291 | pcube *cube2list(pset_family A, pset_family B) 292 | { 293 | register pcube last, p, *plist, *list; 294 | 295 | list = plist = ALLOC(pcube, A->count + B->count + 3); 296 | *plist++ = new_cube(); 297 | plist++; 298 | foreach_set(A, last, p) { 299 | *plist++ = p; 300 | } 301 | foreach_set(B, last, p) { 302 | *plist++ = p; 303 | } 304 | *plist++ = NULL; 305 | list[1] = (pcube) plist; 306 | return list; 307 | } 308 | 309 | 310 | pcube *cube3list(pset_family A, pset_family B, pset_family C) 311 | { 312 | register pcube last, p, *plist, *list; 313 | 314 | plist = ALLOC(pcube, A->count + B->count + C->count + 3); 315 | list = plist; 316 | *plist++ = new_cube(); 317 | plist++; 318 | foreach_set(A, last, p) { 319 | *plist++ = p; 320 | } 321 | foreach_set(B, last, p) { 322 | *plist++ = p; 323 | } 324 | foreach_set(C, last, p) { 325 | *plist++ = p; 326 | } 327 | *plist++ = NULL; 328 | list[1] = (pcube) plist; 329 | return list; 330 | } 331 | 332 | 333 | pcover cubeunlist(pset *A1) 334 | { 335 | register int i; 336 | register pcube p, pdest, cof = A1[0]; 337 | register pcover A; 338 | 339 | A = new_cover(CUBELISTSIZE(A1)); 340 | for(i = 2; (p = A1[i]) != NULL; i++) { 341 | pdest = GETSET(A, i-2); 342 | INLINEset_or(pdest, p, cof); 343 | } 344 | A->count = CUBELISTSIZE(A1); 345 | return A; 346 | } 347 | 348 | void simplify_cubelist(pset *T) 349 | { 350 | register pcube *Tdest; 351 | register int i, ncubes; 352 | 353 | set_copy(cube.temp[0], T[0]); /* retrieve cofactor */ 354 | 355 | ncubes = CUBELISTSIZE(T); 356 | qsort((char *) (T+2), ncubes, sizeof(pset), (qsort_compare_func) d1_order); 357 | 358 | Tdest = T+2; 359 | /* *Tdest++ = T[2]; */ 360 | for(i = 3; i < ncubes; i++) { 361 | if (d1_order(&T[i-1], &T[i]) != 0) { 362 | *Tdest++ = T[i]; 363 | } 364 | } 365 | 366 | *Tdest++ = NULL; /* sentinel */ 367 | Tdest[1] = (pcube) Tdest; /* save pointer to last */ 368 | } 369 | -------------------------------------------------------------------------------- /espresso-src/cols.c: -------------------------------------------------------------------------------- 1 | #include "port.h" 2 | #include "sparse_int.h" 3 | 4 | 5 | /* 6 | * allocate a new col vector 7 | */ 8 | sm_col * sm_col_alloc(void) 9 | { 10 | register sm_col *pcol; 11 | 12 | #ifdef FAST_AND_LOOSE 13 | if (sm_col_freelist == NIL(sm_col)) { 14 | pcol = ALLOC(sm_col, 1); 15 | } else { 16 | pcol = sm_col_freelist; 17 | sm_col_freelist = pcol->next_col; 18 | } 19 | #else 20 | pcol = ALLOC(sm_col, 1); 21 | #endif 22 | 23 | pcol->col_num = 0; 24 | pcol->length = 0; 25 | pcol->first_row = pcol->last_row = NIL(sm_element); 26 | pcol->next_col = pcol->prev_col = NIL(sm_col); 27 | pcol->flag = 0; 28 | pcol->user_word = NIL(char); /* for our user ... */ 29 | return pcol; 30 | } 31 | 32 | 33 | /* 34 | * free a col vector -- for FAST_AND_LOOSE, this is real cheap for cols; 35 | * however, freeing a rowumn must still walk down the rowumn discarding 36 | * the elements one-by-one; that is the only use for the extra '-DCOLS' 37 | * compile flag ... 38 | */ 39 | void sm_col_free(register sm_col *pcol) 40 | { 41 | #if defined(FAST_AND_LOOSE) && ! defined(COLS) 42 | if (pcol->first_row != NIL(sm_element)) { 43 | /* Add the linked list of col items to the free list */ 44 | pcol->last_row->next_row = sm_element_freelist; 45 | sm_element_freelist = pcol->first_row; 46 | } 47 | 48 | /* Add the col to the free list of cols */ 49 | pcol->next_col = sm_col_freelist; 50 | sm_col_freelist = pcol; 51 | #else 52 | register sm_element *p, *pnext; 53 | 54 | for(p = pcol->first_row; p != 0; p = pnext) { 55 | pnext = p->next_row; 56 | sm_element_free(p); 57 | } 58 | FREE(pcol); 59 | #endif 60 | } 61 | 62 | 63 | /* 64 | * duplicate an existing col 65 | */ 66 | sm_col * sm_col_dup(register sm_col *pcol) 67 | { 68 | register sm_col *pnew; 69 | register sm_element *p; 70 | 71 | pnew = sm_col_alloc(); 72 | for(p = pcol->first_row; p != 0; p = p->next_row) { 73 | (void) sm_col_insert(pnew, p->row_num); 74 | } 75 | return pnew; 76 | } 77 | 78 | 79 | /* 80 | * insert an element into a col vector 81 | */ 82 | sm_element * sm_col_insert(register sm_col *pcol, register int row) 83 | { 84 | register sm_element *test, *element; 85 | 86 | /* get a new item, save its address */ 87 | sm_element_alloc(element); 88 | test = element; 89 | sorted_insert(sm_element, pcol->first_row, pcol->last_row, pcol->length, 90 | next_row, prev_row, row_num, row, test); 91 | 92 | /* if item was not used, free it */ 93 | if (element != test) { 94 | sm_element_free(element); 95 | } 96 | 97 | /* either way, return the current new value */ 98 | return test; 99 | } 100 | 101 | 102 | /* 103 | * remove an element from a col vector 104 | */ 105 | void sm_col_remove(register sm_col *pcol, register int row) 106 | { 107 | register sm_element *p; 108 | 109 | for(p = pcol->first_row; p != 0 && p->row_num < row; p = p->next_row) 110 | ; 111 | if (p != 0 && p->row_num == row) { 112 | dll_unlink(p, pcol->first_row, pcol->last_row, 113 | next_row, prev_row, pcol->length); 114 | sm_element_free(p); 115 | } 116 | } 117 | 118 | 119 | /* 120 | * find an element (if it is in the col vector) 121 | */ 122 | sm_element * sm_col_find(sm_col *pcol, int row) 123 | { 124 | register sm_element *p; 125 | 126 | for(p = pcol->first_row; p != 0 && p->row_num < row; p = p->next_row) 127 | ; 128 | if (p != 0 && p->row_num == row) { 129 | return p; 130 | } else { 131 | return NIL(sm_element); 132 | } 133 | } 134 | 135 | /* 136 | * return 1 if col p2 contains col p1; 0 otherwise 137 | */ 138 | int sm_col_contains(sm_col *p1, sm_col *p2) 139 | { 140 | register sm_element *q1, *q2; 141 | 142 | q1 = p1->first_row; 143 | q2 = p2->first_row; 144 | while (q1 != 0) { 145 | if (q2 == 0 || q1->row_num < q2->row_num) { 146 | return 0; 147 | } else if (q1->row_num == q2->row_num) { 148 | q1 = q1->next_row; 149 | q2 = q2->next_row; 150 | } else { 151 | q2 = q2->next_row; 152 | } 153 | } 154 | return 1; 155 | } 156 | 157 | 158 | /* 159 | * return 1 if col p1 and col p2 share an element in common 160 | */ 161 | int sm_col_intersects(sm_col *p1, sm_col *p2) 162 | { 163 | register sm_element *q1, *q2; 164 | 165 | q1 = p1->first_row; 166 | q2 = p2->first_row; 167 | if (q1 == 0 || q2 == 0) return 0; 168 | for(;;) { 169 | if (q1->row_num < q2->row_num) { 170 | if ((q1 = q1->next_row) == 0) { 171 | return 0; 172 | } 173 | } else if (q1->row_num > q2->row_num) { 174 | if ((q2 = q2->next_row) == 0) { 175 | return 0; 176 | } 177 | } else { 178 | return 1; 179 | } 180 | } 181 | } 182 | 183 | 184 | /* 185 | * compare two cols, lexical ordering 186 | */ 187 | int sm_col_compare(sm_col *p1, sm_col *p2) 188 | { 189 | register sm_element *q1, *q2; 190 | 191 | q1 = p1->first_row; 192 | q2 = p2->first_row; 193 | while(q1 != 0 && q2 != 0) { 194 | if (q1->row_num != q2->row_num) { 195 | return q1->row_num - q2->row_num; 196 | } 197 | q1 = q1->next_row; 198 | q2 = q2->next_row; 199 | } 200 | 201 | if (q1 != 0) { 202 | return 1; 203 | } else if (q2 != 0) { 204 | return -1; 205 | } else { 206 | return 0; 207 | } 208 | } 209 | 210 | 211 | /* 212 | * return the intersection 213 | */ 214 | sm_col * sm_col_and(sm_col *p1, sm_col *p2) 215 | { 216 | register sm_element *q1, *q2; 217 | register sm_col *result; 218 | 219 | result = sm_col_alloc(); 220 | q1 = p1->first_row; 221 | q2 = p2->first_row; 222 | if (q1 == 0 || q2 == 0) return result; 223 | for(;;) { 224 | if (q1->row_num < q2->row_num) { 225 | if ((q1 = q1->next_row) == 0) { 226 | return result; 227 | } 228 | } else if (q1->row_num > q2->row_num) { 229 | if ((q2 = q2->next_row) == 0) { 230 | return result; 231 | } 232 | } else { 233 | (void) sm_col_insert(result, q1->row_num); 234 | if ((q1 = q1->next_row) == 0) { 235 | return result; 236 | } 237 | if ((q2 = q2->next_row) == 0) { 238 | return result; 239 | } 240 | } 241 | } 242 | } 243 | 244 | int sm_col_hash(sm_col *pcol, int modulus) 245 | { 246 | register int sum; 247 | register sm_element *p; 248 | 249 | sum = 0; 250 | for(p = pcol->first_row; p != 0; p = p->next_row) { 251 | sum = (sum*17 + p->row_num) % modulus; 252 | } 253 | return sum; 254 | } 255 | 256 | /* 257 | * remove an element from a col vector (given a pointer to the element) 258 | */ 259 | void sm_col_remove_element(register sm_col *pcol, register sm_element *p) 260 | { 261 | dll_unlink(p, pcol->first_row, pcol->last_row, 262 | next_row, prev_row, pcol->length); 263 | sm_element_free(p); 264 | } 265 | 266 | 267 | void sm_col_print(FILE *fp, sm_col *pcol) 268 | { 269 | sm_element *p; 270 | 271 | for(p = pcol->first_row; p != 0; p = p->next_row) { 272 | (void) fprintf(fp, " %d", p->row_num); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /espresso-src/copyright.h: -------------------------------------------------------------------------------- 1 | #ifndef OCTTOOLS_COPYRIGHT_H 2 | #define OCTTOOLS_COPYRIGHT_H 3 | /* 4 | * Oct Tools Distribution 3.0 5 | * 6 | * Copyright (c) 1988, 1989, Regents of the University of California. 7 | * All rights reserved. 8 | * 9 | * Use and copying of this software and preparation of derivative works 10 | * based upon this software are permitted. However, any distribution of 11 | * this software or derivative works must include the above copyright 12 | * notice. 13 | * 14 | * This software is made available AS IS, and neither the Electronics 15 | * Research Laboratory or the University of California make any 16 | * warranty about the software, its performance or its conformity to 17 | * any specification. 18 | * 19 | * Suggestions, comments, or improvements are welcome and should be 20 | * addressed to: 21 | * 22 | * octtools@eros.berkeley.edu 23 | * ..!ucbvax!eros!octtools 24 | */ 25 | 26 | static char octtools_copyright[] = "Copyright (c) 1988, 1989, Regents of the University of California. All rights reserved."; 27 | #endif 28 | -------------------------------------------------------------------------------- /espresso-src/cpu_time.c: -------------------------------------------------------------------------------- 1 | /* LINTLIBRARY */ 2 | #include "port.h" 3 | #include "utility.h" 4 | 5 | #ifdef IBM_WATC /* IBM Waterloo-C compiler (same as bsd 4.2) */ 6 | #ifndef BSD 7 | #define BSD 8 | #endif 9 | #ifndef void 10 | #define void int 11 | #endif 12 | #endif 13 | 14 | #ifdef ultrix 15 | #ifndef BSD 16 | #define BSD 17 | #endif 18 | #endif 19 | 20 | #ifdef aiws 21 | #ifndef UNIX10 22 | #define UNIX10 23 | #endif 24 | #endif 25 | 26 | #ifdef vms /* VAX/C compiler -- times() with 100 HZ clock */ 27 | #ifndef UNIX100 28 | #define UNIX100 29 | #endif 30 | #endif 31 | 32 | /* default */ 33 | #if !defined(BSD) && !defined(UNIX10) && !defined(UNIX60) && !defined(UNIX100) 34 | #define BSD 35 | #endif 36 | 37 | #ifdef BSD 38 | #include 39 | #include 40 | #endif 41 | 42 | #ifdef UNIX10 43 | #include 44 | #endif 45 | 46 | #ifdef UNIX60 47 | #include 48 | #endif 49 | 50 | #ifdef UNIX100 51 | #include 52 | #endif 53 | 54 | 55 | /* 56 | * util_cpu_time -- return a long which represents the elapsed processor 57 | * time in milliseconds since some constant reference 58 | */ 59 | long util_cpu_time(void) 60 | { 61 | long t = 0; 62 | 63 | #ifdef BSD 64 | struct rusage rusage; 65 | (void) getrusage(RUSAGE_SELF, &rusage); 66 | t = (long) rusage.ru_utime.tv_sec*1000 + rusage.ru_utime.tv_usec/1000; 67 | #endif 68 | 69 | #ifdef UNIX10 /* times() with 10 Hz resolution */ 70 | struct tms buffer; 71 | (void) times(&buffer); 72 | t = buffer.tms_utime * 100; 73 | #endif 74 | 75 | #ifdef UNIX60 /* times() with 60 Hz resolution */ 76 | struct tms buffer; 77 | times(&buffer); 78 | t = buffer.tms_utime * 16.6667; 79 | #endif 80 | 81 | #ifdef UNIX100 82 | struct tms buffer; /* times() with 100 Hz resolution */ 83 | times(&buffer); 84 | t = buffer.tms_utime * 10; 85 | #endif 86 | 87 | return t; 88 | } 89 | -------------------------------------------------------------------------------- /espresso-src/cubestr.c: -------------------------------------------------------------------------------- 1 | /* 2 | Module: cubestr.c -- routines for managing the global cube structure 3 | */ 4 | 5 | #include "espresso.h" 6 | 7 | /* 8 | cube_setup -- assume that the fields "num_vars", "num_binary_vars", and 9 | part_size[num_binary_vars .. num_vars-1] are setup, and initialize the 10 | rest of cube and cdata. 11 | 12 | If a part_size is < 0, then the field size is abs(part_size) and the 13 | field read from the input is symbolic. 14 | */ 15 | void cube_setup(void) 16 | { 17 | register int i, var; 18 | register pcube p; 19 | 20 | if (cube.num_binary_vars < 0 || cube.num_vars < cube.num_binary_vars) 21 | fatal("cube size is silly, error in .i/.o or .mv"); 22 | 23 | cube.num_mv_vars = cube.num_vars - cube.num_binary_vars; 24 | cube.output = cube.num_mv_vars > 0 ? cube.num_vars - 1 : -1; 25 | 26 | cube.size = 0; 27 | cube.first_part = ALLOC(int, cube.num_vars); 28 | cube.last_part = ALLOC(int, cube.num_vars); 29 | cube.first_word = ALLOC(int, cube.num_vars); 30 | cube.last_word = ALLOC(int, cube.num_vars); 31 | for(var = 0; var < cube.num_vars; var++) { 32 | if (var < cube.num_binary_vars) 33 | cube.part_size[var] = 2; 34 | cube.first_part[var] = cube.size; 35 | cube.first_word[var] = WHICH_WORD(cube.size); 36 | cube.size += ABS(cube.part_size[var]); 37 | cube.last_part[var] = cube.size - 1; 38 | cube.last_word[var] = WHICH_WORD(cube.size - 1); 39 | } 40 | 41 | cube.var_mask = ALLOC(pset, cube.num_vars); 42 | cube.sparse = ALLOC(int, cube.num_vars); 43 | cube.binary_mask = new_cube(); 44 | cube.mv_mask = new_cube(); 45 | for(var = 0; var < cube.num_vars; var++) { 46 | p = cube.var_mask[var] = new_cube(); 47 | for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) 48 | set_insert(p, i); 49 | if (var < cube.num_binary_vars) { 50 | INLINEset_or(cube.binary_mask, cube.binary_mask, p); 51 | cube.sparse[var] = 0; 52 | } else { 53 | INLINEset_or(cube.mv_mask, cube.mv_mask, p); 54 | cube.sparse[var] = 1; 55 | } 56 | } 57 | if (cube.num_binary_vars == 0) 58 | cube.inword = -1; 59 | else { 60 | cube.inword = cube.last_word[cube.num_binary_vars - 1]; 61 | cube.inmask = cube.binary_mask[cube.inword] & DISJOINT; 62 | } 63 | 64 | cube.temp = ALLOC(pset, CUBE_TEMP); 65 | for(i = 0; i < CUBE_TEMP; i++) 66 | cube.temp[i] = new_cube(); 67 | cube.fullset = set_fill(new_cube(), cube.size); 68 | cube.emptyset = new_cube(); 69 | 70 | cdata.part_zeros = ALLOC(int, cube.size); 71 | cdata.var_zeros = ALLOC(int, cube.num_vars); 72 | cdata.parts_active = ALLOC(int, cube.num_vars); 73 | cdata.is_unate = ALLOC(int, cube.num_vars); 74 | } 75 | 76 | /* 77 | setdown_cube -- free memory allocated for the cube/cdata structs 78 | (free's all but the part_size array) 79 | 80 | (I wanted to call this cube_setdown, but that violates the 8-character 81 | external routine limit on the IBM !) 82 | */ 83 | void setdown_cube(void) 84 | { 85 | register int i, var; 86 | 87 | FREE(cube.first_part); 88 | FREE(cube.last_part); 89 | FREE(cube.first_word); 90 | FREE(cube.last_word); 91 | FREE(cube.sparse); 92 | 93 | free_cube(cube.binary_mask); 94 | free_cube(cube.mv_mask); 95 | free_cube(cube.fullset); 96 | free_cube(cube.emptyset); 97 | for(var = 0; var < cube.num_vars; var++) 98 | free_cube(cube.var_mask[var]); 99 | FREE(cube.var_mask); 100 | 101 | for(i = 0; i < CUBE_TEMP; i++) 102 | free_cube(cube.temp[i]); 103 | FREE(cube.temp); 104 | 105 | FREE(cdata.part_zeros); 106 | FREE(cdata.var_zeros); 107 | FREE(cdata.parts_active); 108 | FREE(cdata.is_unate); 109 | 110 | cube.first_part = cube.last_part = (int *) NULL; 111 | cube.first_word = cube.last_word = (int *) NULL; 112 | cube.sparse = (int *) NULL; 113 | cube.binary_mask = cube.mv_mask = (pcube) NULL; 114 | cube.fullset = cube.emptyset = (pcube) NULL; 115 | cube.var_mask = cube.temp = (pcube *) NULL; 116 | 117 | cdata.part_zeros = cdata.var_zeros = cdata.parts_active = (int *) NULL; 118 | cdata.is_unate = (bool *) NULL; 119 | } 120 | 121 | 122 | void save_cube_struct(void) 123 | { 124 | temp_cube_save = cube; /* structure copy ! */ 125 | temp_cdata_save = cdata; /* "" */ 126 | 127 | cube.first_part = cube.last_part = (int *) NULL; 128 | cube.first_word = cube.last_word = (int *) NULL; 129 | cube.part_size = (int *) NULL; 130 | cube.binary_mask = cube.mv_mask = (pcube) NULL; 131 | cube.fullset = cube.emptyset = (pcube) NULL; 132 | cube.var_mask = cube.temp = (pcube *) NULL; 133 | 134 | cdata.part_zeros = cdata.var_zeros = cdata.parts_active = (int *) NULL; 135 | cdata.is_unate = (bool *) NULL; 136 | } 137 | 138 | 139 | void restore_cube_struct(void) 140 | { 141 | cube = temp_cube_save; /* structure copy ! */ 142 | cdata = temp_cdata_save; /* "" */ 143 | } 144 | -------------------------------------------------------------------------------- /espresso-src/cvrmisc.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | 4 | /* cost -- compute the cost of a cover */ 5 | void cover_cost(pset_family F, pcost cost) 6 | { 7 | register pcube p, last; 8 | pcube *T; 9 | int var; 10 | 11 | /* use the routine used by cofactor to decide splitting variables */ 12 | massive_count(T = cube1list(F)); 13 | free_cubelist(T); 14 | 15 | cost->cubes = F->count; 16 | cost->total = cost->in = cost->out = cost->mv = cost->primes = 0; 17 | 18 | /* Count transistors (zeros) for each binary variable (inputs) */ 19 | for(var = 0; var < cube.num_binary_vars; var++) 20 | cost->in += cdata.var_zeros[var]; 21 | 22 | /* Count transistors for each mv variable based on sparse/dense */ 23 | for(var = cube.num_binary_vars; var < cube.num_vars - 1; var++) 24 | if (cube.sparse[var]) 25 | cost->mv += F->count * cube.part_size[var] - cdata.var_zeros[var]; 26 | else 27 | cost->mv += cdata.var_zeros[var]; 28 | 29 | /* Count the transistors (ones) for the output variable */ 30 | if (cube.num_binary_vars != cube.num_vars) { 31 | var = cube.num_vars - 1; 32 | cost->out = F->count * cube.part_size[var] - cdata.var_zeros[var]; 33 | } 34 | 35 | /* Count the number of nonprime cubes */ 36 | foreach_set(F, last, p) 37 | cost->primes += TESTP(p, PRIME) != 0; 38 | 39 | /* Count the total number of literals */ 40 | cost->total = cost->in + cost->out + cost->mv; 41 | } 42 | 43 | 44 | /* fmt_cost -- return a string which reports the "cost" of a cover */ 45 | char *fmt_cost(pcost cost) 46 | { 47 | static char s[200]; 48 | 49 | if (cube.num_binary_vars == cube.num_vars - 1) 50 | (void) sprintf(s, "c=%d(%d) in=%d out=%d tot=%d", 51 | cost->cubes, cost->cubes - cost->primes, cost->in, 52 | cost->out, cost->total); 53 | else 54 | (void) sprintf(s, "c=%d(%d) in=%d mv=%d out=%d", 55 | cost->cubes, cost->cubes - cost->primes, cost->in, 56 | cost->mv, cost->out); 57 | return s; 58 | } 59 | 60 | 61 | char *print_cost(pset_family F) 62 | { 63 | cost_t cost; 64 | cover_cost(F, &cost); 65 | return fmt_cost(&cost); 66 | } 67 | 68 | 69 | /* copy_cost -- copy a cost function from s to d */ 70 | void copy_cost(pcost s, pcost d) 71 | { 72 | d->cubes = s->cubes; 73 | d->in = s->in; 74 | d->out = s->out; 75 | d->mv = s->mv; 76 | d->total = s->total; 77 | d->primes = s->primes; 78 | } 79 | 80 | 81 | /* size_stamp -- print single line giving the size of a cover */ 82 | void size_stamp(pset_family T, char *name) 83 | { 84 | printf("# %s\tCost is %s\n", name, print_cost(T)); 85 | (void) fflush(stdout); 86 | } 87 | 88 | 89 | /* print_trace -- print a line reporting size and time after a function */ 90 | void print_trace(pset_family T, char *name, long int time) 91 | { 92 | printf("# %s\tTime was %s, cost is %s\n", 93 | name, print_time(time), print_cost(T)); 94 | (void) fflush(stdout); 95 | } 96 | 97 | 98 | /* totals -- add time spent in the function into the totals */ 99 | void totals(long int time, int i, pset_family T, pcost cost) 100 | { 101 | time = ptime() - time; 102 | total_time[i] += time; 103 | total_calls[i]++; 104 | cover_cost(T, cost); 105 | if (trace) { 106 | printf("# %s\tTime was %s, cost is %s\n", 107 | total_name[i], print_time(time), fmt_cost(cost)); 108 | (void) fflush(stdout); 109 | } 110 | } 111 | 112 | 113 | /* fatal -- report fatal error message and take a dive */ 114 | void fatal(char *s) 115 | { 116 | fprintf(stderr, "espresso: %s\n", s); 117 | exit(1); 118 | } 119 | -------------------------------------------------------------------------------- /espresso-src/dominate.c: -------------------------------------------------------------------------------- 1 | #include "mincov_int.h" 2 | 3 | 4 | int 5 | sm_row_dominance(sm_matrix *A) 6 | { 7 | register sm_row *prow, *prow1; 8 | register sm_col *pcol, *least_col; 9 | register sm_element *p, *pnext; 10 | int rowcnt; 11 | 12 | rowcnt = A->nrows; 13 | 14 | /* Check each row against all other rows */ 15 | for(prow = A->first_row; prow != 0; prow = prow->next_row) { 16 | 17 | /* Among all columns with a 1 in this row, choose smallest */ 18 | least_col = sm_get_col(A, prow->first_col->col_num); 19 | for(p = prow->first_col->next_col; p != 0; p = p->next_col) { 20 | pcol = sm_get_col(A, p->col_num); 21 | if (pcol->length < least_col->length) { 22 | least_col = pcol; 23 | } 24 | } 25 | 26 | /* Only check for containment against rows in this column */ 27 | for(p = least_col->first_row; p != 0; p = pnext) { 28 | pnext = p->next_row; 29 | 30 | prow1 = sm_get_row(A, p->row_num); 31 | if ((prow1->length > prow->length) || 32 | (prow1->length == prow->length && 33 | prow1->row_num > prow->row_num)) { 34 | if (sm_row_contains(prow, prow1)) { 35 | sm_delrow(A, prow1->row_num); 36 | } 37 | } 38 | } 39 | } 40 | 41 | return rowcnt - A->nrows; 42 | } 43 | 44 | int 45 | sm_col_dominance(sm_matrix *A, int *weight) 46 | { 47 | register sm_row *prow; 48 | register sm_col *pcol, *pcol1; 49 | register sm_element *p; 50 | sm_row *least_row; 51 | sm_col *next_col; 52 | int colcnt; 53 | 54 | colcnt = A->ncols; 55 | 56 | /* Check each column against all other columns */ 57 | for(pcol = A->first_col; pcol != 0; pcol = next_col) { 58 | next_col = pcol->next_col; 59 | 60 | /* Check all rows to find the one with fewest elements */ 61 | least_row = sm_get_row(A, pcol->first_row->row_num); 62 | for(p = pcol->first_row->next_row; p != 0; p = p->next_row) { 63 | prow = sm_get_row(A, p->row_num); 64 | if (prow->length < least_row->length) { 65 | least_row = prow; 66 | } 67 | } 68 | 69 | /* Only check for containment against columns in this row */ 70 | for(p = least_row->first_col; p != 0; p = p->next_col) { 71 | pcol1 = sm_get_col(A, p->col_num); 72 | if (weight != 0 && weight[pcol1->col_num] > weight[pcol->col_num]) 73 | continue; 74 | if ((pcol1->length > pcol->length) || 75 | (pcol1->length == pcol->length && 76 | pcol1->col_num > pcol->col_num)) { 77 | if (sm_col_contains(pcol, pcol1)) { 78 | sm_delcol(A, pcol->col_num); 79 | break; 80 | } 81 | } 82 | } 83 | } 84 | 85 | return colcnt - A->ncols; 86 | } 87 | -------------------------------------------------------------------------------- /espresso-src/equiv.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | 4 | void find_equiv_outputs(pPLA PLA) 5 | { 6 | int i, j, ipart, jpart, some_equiv; 7 | pcover *R, *F; 8 | 9 | some_equiv = FALSE; 10 | 11 | makeup_labels(PLA); 12 | 13 | F = ALLOC(pcover, cube.part_size[cube.output]); 14 | R = ALLOC(pcover, cube.part_size[cube.output]); 15 | 16 | for(i = 0; i < cube.part_size[cube.output]; i++) { 17 | ipart = cube.first_part[cube.output] + i; 18 | R[i] = cof_output(PLA->R, ipart); 19 | F[i] = complement(cube1list(R[i])); 20 | } 21 | 22 | for(i = 0; i < cube.part_size[cube.output]-1; i++) { 23 | for(j = i+1; j < cube.part_size[cube.output]; j++) { 24 | ipart = cube.first_part[cube.output] + i; 25 | jpart = cube.first_part[cube.output] + j; 26 | 27 | if (check_equiv(F[i], F[j])) { 28 | printf("# Outputs %d and %d (%s and %s) are equivalent\n", 29 | i, j, PLA->label[ipart], PLA->label[jpart]); 30 | some_equiv = TRUE; 31 | } else if (check_equiv(F[i], R[j])) { 32 | printf("# Outputs %d and NOT %d (%s and %s) are equivalent\n", 33 | i, j, PLA->label[ipart], PLA->label[jpart]); 34 | some_equiv = TRUE; 35 | } else if (check_equiv(R[i], F[j])) { 36 | printf("# Outputs NOT %d and %d (%s and %s) are equivalent\n", 37 | i, j, PLA->label[ipart], PLA->label[jpart]); 38 | some_equiv = TRUE; 39 | } else if (check_equiv(R[i], R[j])) { 40 | printf("# Outputs NOT %d and NOT %d (%s and %s) are equivalent\n", 41 | i, j, PLA->label[ipart], PLA->label[jpart]); 42 | some_equiv = TRUE; 43 | } 44 | } 45 | } 46 | 47 | if (! some_equiv) { 48 | printf("# No outputs are equivalent\n"); 49 | } 50 | 51 | for(i = 0; i < cube.part_size[cube.output]; i++) { 52 | free_cover(F[i]); 53 | free_cover(R[i]); 54 | } 55 | FREE(F); 56 | FREE(R); 57 | } 58 | 59 | 60 | 61 | int check_equiv(pset_family f1, pset_family f2) 62 | { 63 | register pcube *f1list, *f2list; 64 | register pcube p, last; 65 | 66 | f1list = cube1list(f1); 67 | foreach_set(f2, last, p) { 68 | if (! cube_is_covered(f1list, p)) { 69 | return FALSE; 70 | } 71 | } 72 | free_cubelist(f1list); 73 | 74 | f2list = cube1list(f2); 75 | foreach_set(f1, last, p) { 76 | if (! cube_is_covered(f2list, p)) { 77 | return FALSE; 78 | } 79 | } 80 | free_cubelist(f2list); 81 | 82 | return TRUE; 83 | } 84 | -------------------------------------------------------------------------------- /espresso-src/espresso.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Module: espresso.c 3 | * Purpose: The main espresso algorithm 4 | * 5 | * Returns a minimized version of the ON-set of a function 6 | * 7 | * The following global variables affect the operation of Espresso: 8 | * 9 | * MISCELLANEOUS: 10 | * trace 11 | * print trace information as the minimization progresses 12 | * 13 | * remove_essential 14 | * remove essential primes 15 | * 16 | * single_expand 17 | * if true, stop after first expand/irredundant 18 | * 19 | * LAST_GASP or SUPER_GASP strategy: 20 | * use_super_gasp 21 | * uses the super_gasp strategy rather than last_gasp 22 | * 23 | * SETUP strategy: 24 | * recompute_onset 25 | * recompute onset using the complement before starting 26 | * 27 | * unwrap_onset 28 | * unwrap the function output part before first expand 29 | * 30 | * MAKE_SPARSE strategy: 31 | * force_irredundant 32 | * iterates make_sparse to force a minimal solution (used 33 | * indirectly by make_sparse) 34 | * 35 | * skip_make_sparse 36 | * skip the make_sparse step (used by opo only) 37 | */ 38 | 39 | #include "espresso.h" 40 | 41 | pcover espresso(pset_family F, pset_family D1, pset_family R) 42 | { 43 | pcover E, D, Fsave; 44 | pset last, p; 45 | cost_t cost, best_cost; 46 | 47 | begin: 48 | Fsave = sf_save(F); /* save original function */ 49 | D = sf_save(D1); /* make a scratch copy of D */ 50 | 51 | /* Setup has always been a problem */ 52 | if (recompute_onset) { 53 | EXEC(E = simplify(cube1list(F)), "SIMPLIFY ", E); 54 | free_cover(F); 55 | F = E; 56 | } 57 | cover_cost(F, &cost); 58 | if (unwrap_onset && (cube.part_size[cube.num_vars - 1] > 1) 59 | && (cost.out != cost.cubes*cube.part_size[cube.num_vars-1]) 60 | && (cost.out < 5000)) 61 | EXEC(F = sf_contain(unravel(F, cube.num_vars - 1)), "SETUP ", F); 62 | 63 | /* Initial expand and irredundant */ 64 | foreach_set(F, last, p) { 65 | RESET(p, PRIME); 66 | } 67 | EXECUTE(F = expand(F, R, FALSE), EXPAND_TIME, F, cost); 68 | EXECUTE(F = irredundant(F, D), IRRED_TIME, F, cost); 69 | 70 | if (! single_expand) { 71 | if (remove_essential) { 72 | EXECUTE(E = essential(&F, &D), ESSEN_TIME, E, cost); 73 | } else { 74 | E = new_cover(0); 75 | } 76 | 77 | cover_cost(F, &cost); 78 | do { 79 | 80 | /* Repeat inner loop until solution becomes "stable" */ 81 | do { 82 | copy_cost(&cost, &best_cost); 83 | EXECUTE(F = reduce(F, D), REDUCE_TIME, F, cost); 84 | EXECUTE(F = expand(F, R, FALSE), EXPAND_TIME, F, cost); 85 | EXECUTE(F = irredundant(F, D), IRRED_TIME, F, cost); 86 | } while (cost.cubes < best_cost.cubes); 87 | 88 | /* Perturb solution to see if we can continue to iterate */ 89 | copy_cost(&cost, &best_cost); 90 | if (use_super_gasp) { 91 | F = super_gasp(F, D, R, &cost); 92 | if (cost.cubes >= best_cost.cubes) 93 | break; 94 | } else { 95 | F = last_gasp(F, D, R, &cost); 96 | } 97 | 98 | } while (cost.cubes < best_cost.cubes || 99 | (cost.cubes == best_cost.cubes && cost.total < best_cost.total)); 100 | 101 | /* Append the essential cubes to F */ 102 | F = sf_append(F, E); /* disposes of E */ 103 | if (trace) size_stamp(F, "ADJUST "); 104 | } 105 | 106 | /* Free the D which we used */ 107 | free_cover(D); 108 | 109 | /* Attempt to make the PLA matrix sparse */ 110 | if (! skip_make_sparse) { 111 | F = make_sparse(F, D1, R); 112 | } 113 | 114 | /* 115 | * Check to make sure function is actually smaller !! 116 | * This can only happen because of the initial unravel. If we fail, 117 | * then run the whole thing again without the unravel. 118 | */ 119 | if (Fsave->count < F->count) { 120 | free_cover(F); 121 | F = Fsave; 122 | unwrap_onset = FALSE; 123 | goto begin; 124 | } else { 125 | free_cover(Fsave); 126 | } 127 | 128 | return F; 129 | } 130 | -------------------------------------------------------------------------------- /espresso-src/essen.c: -------------------------------------------------------------------------------- 1 | /* 2 | module: essen.c 3 | purpose: Find essential primes in a multiple-valued function 4 | */ 5 | 6 | #include "espresso.h" 7 | 8 | /* 9 | essential -- return a cover consisting of the cubes of F which are 10 | essential prime implicants (with respect to F u D); Further, remove 11 | these cubes from the ON-set F, and add them to the OFF-set D. 12 | 13 | Sometimes EXPAND can determine that a cube is not an essential prime. 14 | If so, it will set the "NONESSEN" flag in the cube. 15 | 16 | We count on IRREDUNDANT to have set the flag RELESSEN to indicate 17 | that a prime was relatively essential (i.e., covers some minterm 18 | not contained in any other prime in the current cover), or to have 19 | reset the flag to indicate that a prime was relatively redundant 20 | (i.e., all minterms covered by other primes in the current cover). 21 | Of course, after executing irredundant, all of the primes in the 22 | cover are relatively essential, but we can mark the primes which 23 | were redundant at the start of irredundant and avoid an extra check 24 | on these primes for essentiality. 25 | */ 26 | 27 | pcover essential(pset_family *Fp, pset_family *Dp) 28 | { 29 | register pcube last, p; 30 | pcover E, F = *Fp, D = *Dp; 31 | 32 | /* set all cubes in F active */ 33 | (void) sf_active(F); 34 | 35 | /* Might as well start out with some cubes in E */ 36 | E = new_cover(10); 37 | 38 | foreach_set(F, last, p) { 39 | /* don't test a prime which EXPAND says is nonessential */ 40 | if (! TESTP(p, NONESSEN)) { 41 | /* only test a prime which was relatively essential */ 42 | if (TESTP(p, RELESSEN)) { 43 | /* Check essentiality */ 44 | if (essen_cube(F, D, p)) { 45 | if (debug & ESSEN) 46 | printf("ESSENTIAL: %s\n", pc1(p)); 47 | E = sf_addset(E, p); 48 | RESET(p, ACTIVE); 49 | F->active_count--; 50 | } 51 | } 52 | } 53 | } 54 | 55 | *Fp = sf_inactive(F); /* delete the inactive cubes from F */ 56 | *Dp = sf_join(D, E); /* add the essentials to D */ 57 | sf_free(D); 58 | return E; 59 | } 60 | 61 | /* 62 | essen_cube -- check if a single cube is essential or not 63 | 64 | The prime c is essential iff 65 | 66 | consensus((F u D) # c, c) u D 67 | 68 | does not contain c. 69 | */ 70 | bool essen_cube(pset_family F, pset_family D, pset c) 71 | { 72 | pcover H, FD; 73 | pcube *H1; 74 | bool essen; 75 | 76 | /* Append F and D together, and take the sharp-consensus with c */ 77 | FD = sf_join(F, D); 78 | H = cb_consensus(FD, c); 79 | free_cover(FD); 80 | 81 | /* Add the don't care set, and see if this covers c */ 82 | H1 = cube2list(H, D); 83 | essen = ! cube_is_covered(H1, c); 84 | free_cubelist(H1); 85 | 86 | free_cover(H); 87 | return essen; 88 | } 89 | 90 | 91 | /* 92 | * cb_consensus -- compute consensus(T # c, c) 93 | */ 94 | pcover cb_consensus(register pset_family T, register pset c) 95 | { 96 | register pcube temp, last, p; 97 | register pcover R; 98 | 99 | R = new_cover(T->count*2); 100 | temp = new_cube(); 101 | foreach_set(T, last, p) { 102 | if (p != c) { 103 | switch (cdist01(p, c)) { 104 | case 0: 105 | /* distance-0 needs special care */ 106 | R = cb_consensus_dist0(R, p, c); 107 | break; 108 | 109 | case 1: 110 | /* distance-1 is easy because no sharping required */ 111 | consensus(temp, p, c); 112 | R = sf_addset(R, temp); 113 | break; 114 | } 115 | } 116 | } 117 | set_free(temp); 118 | return R; 119 | } 120 | 121 | 122 | /* 123 | * form the sharp-consensus for p and c when they intersect 124 | * What we are forming is consensus(p # c, c). 125 | */ 126 | pcover cb_consensus_dist0(pset_family R, register pset p, register pset c) 127 | { 128 | int var; 129 | bool got_one; 130 | register pcube temp, mask; 131 | register pcube p_diff_c=cube.temp[0], p_and_c=cube.temp[1]; 132 | 133 | /* If c contains p, then this gives us no information for essential test */ 134 | if (setp_implies(p, c)) { 135 | return R; 136 | } 137 | 138 | /* For the multiple-valued variables */ 139 | temp = new_cube(); 140 | got_one = FALSE; 141 | INLINEset_diff(p_diff_c, p, c); 142 | INLINEset_and(p_and_c, p, c); 143 | 144 | for(var = cube.num_binary_vars; var < cube.num_vars; var++) { 145 | /* Check if c(var) is contained in p(var) -- if so, no news */ 146 | mask = cube.var_mask[var]; 147 | if (! setp_disjoint(p_diff_c, mask)) { 148 | INLINEset_merge(temp, c, p_and_c, mask); 149 | R = sf_addset(R, temp); 150 | got_one = TRUE; 151 | } 152 | } 153 | 154 | /* if no cube so far, add one for the intersection */ 155 | if (! got_one && cube.num_binary_vars > 0) { 156 | /* Add a single cube for the intersection of p and c */ 157 | INLINEset_and(temp, p, c); 158 | R = sf_addset(R, temp); 159 | } 160 | 161 | set_free(temp); 162 | return R; 163 | } 164 | -------------------------------------------------------------------------------- /espresso-src/essentiality.c: -------------------------------------------------------------------------------- 1 | /* Module:essentiality.c 2 | * contains routines for performing essentiality test and reduction 3 | * Routines: 4 | * pcover ess_test_and_reduction(): 5 | * determines essentiality of the given signature cube and returns 6 | * cover of the signature cube (Hammered signature cube) if found 7 | * inessential. 8 | * pcover aux_ess_test_and_reduction(): 9 | * core compuation routine which determines the essentiality of 10 | * the signature cube and performs reduction of inessential signature 11 | * cubes by recursively descending down the cube-tree. 12 | */ 13 | 14 | 15 | 16 | #include 17 | #include "espresso.h" 18 | #include "signature.h" 19 | 20 | /* Yuk, Yuk, Yuk!! More Globals. It seems recursive routines have unholy 21 | alliance with globals */ 22 | 23 | static int *c_free_list; /* List of raised variables in cube c */ 24 | static int c_free_count; /* active size of the above list */ 25 | 26 | static int *r_free_list; /* List of subset of raised variables in cube c 27 | which are raised in each cube of offset R */ 28 | static int r_free_count; /* active size of the above list */ 29 | static int r_head; /* current position in the list above */ 30 | 31 | static int *reduced_c_free_list; /* c_free_list - r_free_list */ 32 | static int reduced_c_free_count; /* active size of the above list */ 33 | 34 | static VAR *unate_list; /* List of unate variables in the reduced_c_free_list */ 35 | static int unate_count; /* active size of the above list */ 36 | 37 | static VAR *binate_list; /* List of binate variables in the 38 | reduced_c_free_list */ 39 | static int binate_count; /* active size of the above list */ 40 | 41 | static int *variable_order; /* permutation of reduced c_free_count determining 42 | static ordering of variables */ 43 | static int variable_count; /* active size of the above list */ 44 | static int variable_head; /* current position in the list above */ 45 | 46 | /* The passive size allocated on the first call to etr_order is equal 47 | to the number of binary variables */ 48 | /* Clearly not the most optimized usage of memory. Not worth saving 49 | few pennies when the total memory budget is quite large */ 50 | 51 | pcover COVER; /* A global bag to collect the signature cubes in the cover 52 | of inessential signature cube */ 53 | 54 | /* etr_order: 55 | * What does it do: 56 | * Performs performs ordering of variables before calling 57 | * essentiality test and reduction routine 58 | * Inputs: 59 | * R: Cover of the Off-set. 60 | * c: cube to be reduced 61 | * Output: 62 | * COVER: signature cube cover of given inessential signature cube 63 | * Strategy: As many cubes in the cover as possible. 64 | * -> As deep a search tree recursion as possible 65 | * -> variable ordering 66 | * -> static ordering to minimize computation 67 | * 1. Free 68 | * 2. Freer Unate 69 | * 3. Freer Binate 70 | */ 71 | pcover 72 | etr_order(pset_family F, pset_family E, pset_family R, pset c, pset d) 73 | { 74 | static int num_binary_vars; 75 | int v,e0,e1; 76 | int i,free_var; 77 | pcube lastr,r; 78 | int even_count,odd_count,free_count; 79 | int odd,even; 80 | VAR *p; 81 | 82 | num_binary_vars = cube.num_binary_vars; 83 | c_free_list = (int *)calloc(num_binary_vars,sizeof(int)); 84 | r_free_list = (int *)calloc(num_binary_vars,sizeof(int)); 85 | reduced_c_free_list = (int *)calloc(num_binary_vars,sizeof(int)); 86 | unate_list = (VAR *)calloc(num_binary_vars,sizeof(VAR)); 87 | binate_list = (VAR *)calloc(num_binary_vars,sizeof(VAR)); 88 | 89 | variable_order = (int *)calloc(num_binary_vars,sizeof(int)); 90 | 91 | if(!c_free_list || !r_free_list || !reduced_c_free_list || 92 | !unate_list || !binate_list || !variable_order){ 93 | perror("etr_order:alloc"); 94 | exit(1); 95 | } 96 | 97 | /* 1.Identify free variables of cube c */ 98 | c_free_count = 0; 99 | for(v = 0; v < num_binary_vars; v++){ 100 | e0 = v<<1; 101 | e1 = e0 + 1; 102 | if(is_in_set(d,e0) && is_in_set(d,e1)){ 103 | c_free_list[c_free_count++] = v; 104 | } 105 | } 106 | 107 | /* 2.Identify corresponding free variables of R */ 108 | r_head = 0; 109 | r_free_count = 0; 110 | reduced_c_free_count = 0; 111 | for(i = 0; i < c_free_count; i++){ 112 | v = c_free_list[i]; 113 | e0 = v<<1; 114 | e1 = e0 + 1; 115 | free_var = TRUE; 116 | foreach_set(R,lastr,r){ 117 | if(!is_in_set(r,e0) || !is_in_set(r,e1)){ 118 | free_var = FALSE; 119 | break; 120 | } 121 | } 122 | if(free_var){ 123 | r_free_list[r_free_count++] = v; 124 | } 125 | else{ 126 | reduced_c_free_list[reduced_c_free_count++] = v; 127 | } 128 | } 129 | 130 | /* 3.Identify unate and binate variables and sort them in the 131 | decreasing free_count */ 132 | unate_count = 0; 133 | binate_count = 0; 134 | for(i = 0; i < reduced_c_free_count; i++){ 135 | v = reduced_c_free_list[i]; 136 | e0 = v<<1; 137 | e1 = e0 + 1; 138 | even_count = 0; 139 | odd_count = 0; 140 | free_count = 0; 141 | foreach_set(R,lastr,r){ 142 | odd = is_in_set(r,e0); 143 | even = is_in_set(r,e1); 144 | if(odd && even){ 145 | free_count++; 146 | } 147 | else if(odd){ 148 | odd_count++; 149 | } 150 | else{ 151 | even_count++; 152 | } 153 | } 154 | if(odd_count == 0 || even_count == 0){ 155 | p = &unate_list[unate_count++]; 156 | p->variable = v; 157 | p->free_count = free_count; 158 | } 159 | else{ 160 | p = &binate_list[binate_count++]; 161 | p->variable = v; 162 | p->free_count = free_count; 163 | } 164 | } 165 | 166 | qsort(unate_list,unate_count,sizeof(VAR),(qsort_compare_func) ascending); 167 | qsort(binate_list,binate_count,sizeof(VAR),(qsort_compare_func) ascending); 168 | 169 | variable_head = 0; 170 | variable_count = 0; 171 | for(i = 0; i < binate_count; i++){ 172 | variable_order[variable_count++] = binate_list[i].variable; 173 | } 174 | for(i = 0; i < unate_count; i++){ 175 | variable_order[variable_count++] = unate_list[i].variable; 176 | } 177 | 178 | /* 4.Recursively go down the tree defined by r_free_list, 179 | invoking "etr" at the leaves */ 180 | 181 | COVER = new_cover(10); 182 | setup_bw(R,c); 183 | SET(c,NONESSEN); 184 | 185 | aux_etr_order(F,E,R,c,d); 186 | 187 | free_bw(); 188 | free(c_free_list); 189 | free(r_free_list); 190 | free(reduced_c_free_list); 191 | free(unate_list); 192 | free(binate_list); 193 | free(variable_order); 194 | 195 | return COVER; 196 | } 197 | 198 | int 199 | ascending(VAR *p1, VAR *p2) 200 | { 201 | int f1 = p1->free_count; 202 | int f2 = p2->free_count; 203 | 204 | if(f1 > f2){ 205 | return 1; 206 | } 207 | else if(f1 < f2){ 208 | return -1; 209 | } 210 | else{ 211 | return 0; 212 | } 213 | } 214 | 215 | /* 216 | * aux_etr_order 217 | * Objective:main recursive routine for reducing the inessential signature cube. 218 | * Very similar to aux_ess_test_and_reduction; 219 | * Input: 220 | * c: signature cube; 221 | * d: cube contained in the signature cube; 222 | * E: Extended don't care set. DC + identified ESC; 223 | * R: OFFSET cover; 224 | */ 225 | void 226 | aux_etr_order(pset_family F, pset_family E, pset_family R, pset c, pset d) 227 | { 228 | pcover minterms; 229 | pcube d_minterm; 230 | pcube sigma_d; 231 | int v_index,e0,e1; 232 | int i; 233 | pcube *local_dc; 234 | 235 | /* Special Cases */ 236 | local_dc = cube3list(F,E,COVER); 237 | if(cube_is_covered(local_dc,d)){ 238 | free_cubelist(local_dc); 239 | return; 240 | } 241 | if(black_white() == FALSE){ 242 | free_cubelist(local_dc); 243 | sigma_d = get_sigma(R,d); 244 | sf_addset(COVER,sigma_d); 245 | free_cube(sigma_d); 246 | return; 247 | } 248 | if(variable_head == variable_count){ 249 | minterms = get_mins(d); 250 | foreachi_set(minterms,i,d_minterm){ 251 | if(cube_is_covered(local_dc,d_minterm))continue; 252 | sigma_d = get_sigma(R,d_minterm); 253 | if(setp_equal(sigma_d,c)){ 254 | RESET(c,NONESSEN); 255 | free_cube(sigma_d); 256 | break; 257 | } 258 | sf_addset(COVER,sigma_d); 259 | free_cube(sigma_d); 260 | } 261 | sf_free(minterms); 262 | free_cubelist(local_dc); 263 | return; 264 | } 265 | else{ 266 | v_index = variable_order[variable_head]; 267 | } 268 | free_cubelist(local_dc); 269 | 270 | e0 = (v_index<<1); 271 | e1 = e0 + 1; 272 | 273 | variable_head++; 274 | 275 | set_remove(d,e1); 276 | reset_black_list(); 277 | split_list(R,e0); 278 | push_black_list(); 279 | S_EXECUTE(aux_etr_order(F,E,R,c,d),ETRAUX_TIME); 280 | if(TESTP(c,NONESSEN) == FALSE)return; 281 | pop_black_list(); 282 | merge_list(); 283 | set_insert(d,e1); 284 | 285 | set_remove(d,e0); 286 | reset_black_list(); 287 | split_list(R,e1); 288 | push_black_list(); 289 | S_EXECUTE(aux_etr_order(F,E,R,c,d),ETRAUX_TIME); 290 | if(TESTP(c,NONESSEN) == FALSE)return; 291 | pop_black_list(); 292 | merge_list(); 293 | set_insert(d,e0); 294 | 295 | variable_head--; 296 | 297 | return; 298 | } 299 | 300 | pcover 301 | get_mins(pset c) 302 | { 303 | pcover minterms; 304 | pcube d_minterm; 305 | int i,j; 306 | 307 | minterms = new_cover(1); 308 | d_minterm = new_cube(); 309 | set_copy(d_minterm,c); 310 | set_and(d_minterm,d_minterm,cube.binary_mask); 311 | for(i = cube.num_binary_vars; 312 | i < cube.num_vars;i++){ 313 | for(j = cube.first_part[i]; j <= cube.last_part[i];j++){ 314 | if(is_in_set(c,j)){ 315 | set_insert(d_minterm,j); 316 | sf_addset(minterms,d_minterm); 317 | set_remove(d_minterm,j); 318 | } 319 | } 320 | } 321 | free_cube(d_minterm); 322 | return minterms; 323 | } 324 | 325 | void print_list(int n, int *x, char *name) 326 | { 327 | int i; 328 | printf("%s:\n",name); 329 | for(i = 0; i < n; i++){ 330 | printf("%d%c",x[i],(i+1)%10?'\t':'\n'); 331 | } 332 | printf("\n"); 333 | } 334 | -------------------------------------------------------------------------------- /espresso-src/exact.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | 4 | static void dump_irredundant(pset_family E, pset_family Rt, pset_family Rp, sm_matrix *table); 5 | static pcover do_minimize(pset_family F, pset_family D, pset_family R, int exact_cover, int weighted); 6 | 7 | 8 | /* 9 | * minimize_exact -- main entry point for exact minimization 10 | * 11 | * Global flags which affect this routine are: 12 | * 13 | * debug 14 | * skip_make_sparse 15 | */ 16 | 17 | pcover 18 | minimize_exact(pset_family F, pset_family D, pset_family R, int exact_cover) 19 | { 20 | return do_minimize(F, D, R, exact_cover, /*weighted*/ 0); 21 | } 22 | 23 | 24 | pcover 25 | minimize_exact_literals(pset_family F, pset_family D, pset_family R, int exact_cover) 26 | { 27 | return do_minimize(F, D, R, exact_cover, /*weighted*/ 1); 28 | } 29 | 30 | 31 | 32 | static pcover 33 | do_minimize(pset_family F, pset_family D, pset_family R, int exact_cover, int weighted) 34 | { 35 | pcover newF, E, Rt, Rp; 36 | pset p, last; 37 | int heur, level, *weights; 38 | sm_matrix *table; 39 | sm_row *cover; 40 | sm_element *pe; 41 | int debug_save = debug; 42 | 43 | if (debug & EXACT) { 44 | debug |= (IRRED | MINCOV); 45 | } 46 | #if defined(sun) || defined(bsd4_2) /* hack ... */ 47 | if (debug & MINCOV) { 48 | setlinebuf(stdout); 49 | } 50 | #endif 51 | level = (debug & MINCOV) ? 4 : 0; 52 | heur = ! exact_cover; 53 | 54 | /* Generate all prime implicants */ 55 | EXEC(F = primes_consensus(cube2list(F, D)), "PRIMES ", F); 56 | 57 | /* Setup the prime implicant table */ 58 | EXEC(irred_split_cover(F, D, &E, &Rt, &Rp), "ESSENTIALS ", E); 59 | EXEC(table = irred_derive_table(D, E, Rp), "PI-TABLE ", Rp); 60 | 61 | /* Solve either a weighted or nonweighted covering problem */ 62 | if (weighted) { 63 | /* correct only for all 2-valued variables */ 64 | weights = ALLOC(int, F->count); 65 | foreach_set(Rp, last, p) { 66 | weights[SIZE(p)] = cube.size - set_ord(p); 67 | } 68 | } else { 69 | weights = NIL(int); 70 | } 71 | EXEC(cover=sm_minimum_cover(table,weights,heur,level), "MINCOV ", F); 72 | if (weights != 0) { 73 | FREE(weights); 74 | } 75 | 76 | if (debug & EXACT) { 77 | dump_irredundant(E, Rt, Rp, table); 78 | } 79 | 80 | /* Form the result cover */ 81 | newF = new_cover(100); 82 | foreach_set(E, last, p) { 83 | newF = sf_addset(newF, p); 84 | } 85 | sm_foreach_row_element(cover, pe) { 86 | newF = sf_addset(newF, GETSET(F, pe->col_num)); 87 | } 88 | 89 | free_cover(E); 90 | free_cover(Rt); 91 | free_cover(Rp); 92 | sm_free(table); 93 | sm_row_free(cover); 94 | free_cover(F); 95 | 96 | /* Attempt to make the results more sparse */ 97 | debug &= ~ (IRRED | SHARP | MINCOV); 98 | if (! skip_make_sparse && R != 0) { 99 | newF = make_sparse(newF, D, R); 100 | } 101 | 102 | debug = debug_save; 103 | return newF; 104 | } 105 | 106 | static void 107 | dump_irredundant(pset_family E, pset_family Rt, pset_family Rp, sm_matrix *table) 108 | { 109 | FILE *fp_pi_table, *fp_primes; 110 | pPLA PLA; 111 | pset last, p; 112 | char *file; 113 | 114 | if (filename == 0 || strcmp(filename, "(stdin)") == 0) { 115 | fp_pi_table = fp_primes = stdout; 116 | } else { 117 | file = ALLOC(char, strlen(filename)+20); 118 | (void) sprintf(file, "%s.primes", filename); 119 | if ((fp_primes = fopen(file, "w")) == NULL) { 120 | fprintf(stderr, "espresso: Unable to open %s\n", file); 121 | fp_primes = stdout; 122 | } 123 | (void) sprintf(file, "%s.pi", filename); 124 | if ((fp_pi_table = fopen(file, "w")) == NULL) { 125 | fprintf(stderr, "espresso: Unable to open %s\n", file); 126 | fp_pi_table = stdout; 127 | } 128 | FREE(file); 129 | } 130 | 131 | PLA = new_PLA(); 132 | PLA_labels(PLA); 133 | 134 | fpr_header(fp_primes, PLA, F_type); 135 | free_PLA(PLA); 136 | 137 | (void) fprintf(fp_primes, "# Essential primes are\n"); 138 | foreach_set(E, last, p) { 139 | (void) fprintf(fp_primes, "%s\n", pc1(p)); 140 | } 141 | fprintf(fp_primes, "# Totally redundant primes are\n"); 142 | foreach_set(Rt, last, p) { 143 | (void) fprintf(fp_primes, "%s\n", pc1(p)); 144 | } 145 | fprintf(fp_primes, "# Partially redundant primes are\n"); 146 | foreach_set(Rp, last, p) { 147 | (void) fprintf(fp_primes, "%s\n", pc1(p)); 148 | } 149 | if (fp_primes != stdout) { 150 | (void) fclose(fp_primes); 151 | } 152 | 153 | sm_write(fp_pi_table, table); 154 | if (fp_pi_table != stdout) { 155 | (void) fclose(fp_pi_table); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /espresso-src/gasp.c: -------------------------------------------------------------------------------- 1 | /* 2 | module: gasp.c 3 | 4 | The "last_gasp" heuristic computes the reduction of each cube in 5 | the cover (without replacement) and then performs an expansion of 6 | these cubes. The cubes which expand to cover some other cube are 7 | added to the original cover and irredundant finds a minimal subset. 8 | 9 | If one of the reduced cubes expands to cover some other reduced 10 | cube, then the new prime thus generated is a candidate for reducing 11 | the size of the cover. 12 | 13 | super_gasp is a variation on this strategy which extracts a minimal 14 | subset from the set of all prime implicants which cover all 15 | maximally reduced cubes. 16 | */ 17 | 18 | #include "espresso.h" 19 | 20 | 21 | /* 22 | * reduce_gasp -- compute the maximal reduction of each cube of F 23 | * 24 | * If a cube does not reduce, it remains prime; otherwise, it is marked 25 | * as nonprime. If the cube is redundant (should NEVER happen here) we 26 | * just crap out ... 27 | * 28 | * A cover with all of the cubes of F is returned. Those that did 29 | * reduce are marked "NONPRIME"; those that reduced are marked "PRIME". 30 | * The cubes are in the same order as in F. 31 | */ 32 | static pcover reduce_gasp(pset_family F, pset_family D) 33 | { 34 | pcube p, last, cunder, *FD; 35 | pcover G; 36 | 37 | G = new_cover(F->count); 38 | FD = cube2list(F, D); 39 | 40 | /* Reduce cubes of F without replacement */ 41 | foreach_set(F, last, p) { 42 | cunder = reduce_cube(FD, p); 43 | if (setp_empty(cunder)) { 44 | fatal("empty reduction in reduce_gasp, shouldn't happen"); 45 | } else if (setp_equal(cunder, p)) { 46 | SET(cunder, PRIME); /* just to make sure */ 47 | G = sf_addset(G, p); /* it did not reduce ... */ 48 | } else { 49 | RESET(cunder, PRIME); /* it reduced ... */ 50 | G = sf_addset(G, cunder); 51 | } 52 | if (debug & GASP) { 53 | printf("REDUCE_GASP: %s reduced to %s\n", pc1(p), pc2(cunder)); 54 | } 55 | free_cube(cunder); 56 | } 57 | 58 | free_cubelist(FD); 59 | return G; 60 | } 61 | 62 | /* 63 | * expand_gasp -- expand each nonprime cube of F into a prime implicant 64 | * 65 | * The gasp strategy differs in that only those cubes which expand to 66 | * cover some other cube are saved; also, all cubes are expanded 67 | * regardless of whether they become covered or not. 68 | */ 69 | 70 | pcover expand_gasp(pset_family F, pset_family D, pset_family R, pset_family Foriginal) 71 | { 72 | int c1index; 73 | pcover G; 74 | 75 | /* Try to expand each nonprime and noncovered cube */ 76 | G = new_cover(10); 77 | for(c1index = 0; c1index < F->count; c1index++) { 78 | expand1_gasp(F, D, R, Foriginal, c1index, &G); 79 | } 80 | G = sf_dupl(G); 81 | G = expand(G, R, /*nonsparse*/ FALSE); /* Make them prime ! */ 82 | return G; 83 | } 84 | 85 | 86 | 87 | /* 88 | * expand1 -- Expand a single cube against the OFF-set, using the gasp strategy 89 | */ 90 | void expand1_gasp(pset_family F, pset_family D, pset_family R, pset_family Foriginal, int c1index, pset_family *G) 91 | /* reduced cubes of ON-set */ 92 | /* DC-set */ 93 | /* OFF-set */ 94 | /* ON-set before reduction (same order as F) */ 95 | /* which index of F (or Freduced) to be checked */ 96 | 97 | { 98 | register int c2index; 99 | register pcube p, last, c2under; 100 | pcube RAISE, FREESET, temp, *FD, c2essential; 101 | pcover F1; 102 | 103 | if (debug & EXPAND1) { 104 | printf("\nEXPAND1_GASP: \t%s\n", pc1(GETSET(F, c1index))); 105 | } 106 | 107 | RAISE = new_cube(); 108 | FREESET = new_cube(); 109 | temp = new_cube(); 110 | 111 | /* Initialize the OFF-set */ 112 | R->active_count = R->count; 113 | foreach_set(R, last, p) { 114 | SET(p, ACTIVE); 115 | } 116 | /* Initialize the reduced ON-set, all nonprime cubes become active */ 117 | F->active_count = F->count; 118 | foreachi_set(F, c2index, c2under) { 119 | if (c1index == c2index || TESTP(c2under, PRIME)) { 120 | F->active_count--; 121 | RESET(c2under, ACTIVE); 122 | } else { 123 | SET(c2under, ACTIVE); 124 | } 125 | } 126 | 127 | /* Initialize the raising and unassigned sets */ 128 | (void) set_copy(RAISE, GETSET(F, c1index)); 129 | (void) set_diff(FREESET, cube.fullset, RAISE); 130 | 131 | /* Determine parts which must be lowered */ 132 | essen_parts(R, F, RAISE, FREESET); 133 | 134 | /* Determine parts which can always be raised */ 135 | essen_raising(R, RAISE, FREESET); 136 | 137 | /* See which, if any, of the reduced cubes we can cover */ 138 | foreachi_set(F, c2index, c2under) { 139 | if (TESTP(c2under, ACTIVE)) { 140 | /* See if this cube can be covered by an expansion */ 141 | if (setp_implies(c2under, RAISE) || 142 | feasibly_covered(R, c2under, RAISE, temp)) { 143 | 144 | /* See if c1under can expanded to cover c2 reduced against 145 | * (F - c1) u c1under; if so, c2 can definitely be removed ! 146 | */ 147 | 148 | /* Copy F and replace c1 with c1under */ 149 | F1 = sf_save(Foriginal); 150 | (void) set_copy(GETSET(F1, c1index), GETSET(F, c1index)); 151 | 152 | /* Reduce c2 against ((F - c1) u c1under) */ 153 | FD = cube2list(F1, D); 154 | c2essential = reduce_cube(FD, GETSET(F1, c2index)); 155 | free_cubelist(FD); 156 | sf_free(F1); 157 | 158 | /* See if c2essential is covered by an expansion of c1under */ 159 | if (feasibly_covered(R, c2essential, RAISE, temp)) { 160 | (void) set_or(temp, RAISE, c2essential); 161 | RESET(temp, PRIME); /* cube not prime */ 162 | *G = sf_addset(*G, temp); 163 | } 164 | set_free(c2essential); 165 | } 166 | } 167 | } 168 | 169 | free_cube(RAISE); 170 | free_cube(FREESET); 171 | free_cube(temp); 172 | } 173 | 174 | /* irred_gasp -- Add new primes to F and find an irredundant subset */ 175 | pcover irred_gasp(pset_family F, pset_family D, pset_family G) 176 | /* G is disposed of */ 177 | { 178 | if (G->count != 0) 179 | F = irredundant(sf_append(F, G), D); 180 | else 181 | free_cover(G); 182 | return F; 183 | } 184 | 185 | 186 | /* last_gasp */ 187 | pcover last_gasp(pset_family F, pset_family D, pset_family R, cost_t *cost) 188 | { 189 | pcover G, G1; 190 | 191 | EXECUTE(G = reduce_gasp(F, D), GREDUCE_TIME, G, *cost); 192 | EXECUTE(G1 = expand_gasp(G, D, R, F), GEXPAND_TIME, G1, *cost); 193 | free_cover(G); 194 | EXECUTE(F = irred_gasp(F, D, G1), GIRRED_TIME, F, *cost); 195 | return F; 196 | } 197 | 198 | 199 | /* super_gasp */ 200 | pcover super_gasp(pset_family F, pset_family D, pset_family R, cost_t *cost) 201 | { 202 | pcover G, G1; 203 | 204 | EXECUTE(G = reduce_gasp(F, D), GREDUCE_TIME, G, *cost); 205 | EXECUTE(G1 = all_primes(G, R), GEXPAND_TIME, G1, *cost); 206 | free_cover(G); 207 | EXEC(G = sf_dupl(sf_append(F, G1)), "NEWPRIMES", G); 208 | EXECUTE(F = irredundant(G, D), IRRED_TIME, F, *cost); 209 | return F; 210 | } 211 | -------------------------------------------------------------------------------- /espresso-src/gimpel.c: -------------------------------------------------------------------------------- 1 | #include "mincov_int.h" 2 | 3 | 4 | /* 5 | * check for: 6 | * 7 | * c1 c2 rest 8 | * -- -- --- 9 | * 1 1 0 0 0 0 <-- primary row 10 | * 1 0 S1 <-- secondary row 11 | * 0 1 T1 12 | * 0 1 T2 13 | * 0 1 Tn 14 | * 0 0 R 15 | */ 16 | 17 | int 18 | gimpel_reduce(sm_matrix *A, solution_t *select, int *weight, int lb, int bound, int depth, stats_t *stats, solution_t **best) 19 | { 20 | register sm_row *prow, *save_sec; 21 | register sm_col *c1 = NULL, *c2 = NULL; 22 | register sm_element *p, *p1; 23 | int c1_col_num, c2_col_num, primary_row_num, secondary_row_num; 24 | int reduce_it; 25 | 26 | reduce_it = 0; 27 | for(prow = A->first_row; prow != 0; prow = prow->next_row) { 28 | if (prow->length == 2) { 29 | c1 = sm_get_col(A, prow->first_col->col_num); 30 | c2 = sm_get_col(A, prow->last_col->col_num); 31 | if (c1->length == 2) { 32 | reduce_it = 1; 33 | } else if (c2->length == 2) { 34 | c1 = sm_get_col(A, prow->last_col->col_num); 35 | c2 = sm_get_col(A, prow->first_col->col_num); 36 | reduce_it = 1; 37 | } 38 | if (reduce_it) { 39 | primary_row_num = prow->row_num; 40 | secondary_row_num = c1->first_row->row_num; 41 | if (secondary_row_num == primary_row_num) { 42 | secondary_row_num = c1->last_row->row_num; 43 | } 44 | break; 45 | } 46 | } 47 | } 48 | 49 | if (reduce_it) { 50 | c1_col_num = c1->col_num; 51 | c2_col_num = c2->col_num; 52 | save_sec = sm_row_dup(sm_get_row(A, secondary_row_num)); 53 | sm_row_remove(save_sec, c1_col_num); 54 | 55 | for(p = c2->first_row; p != 0; p = p->next_row) { 56 | if (p->row_num != primary_row_num) { 57 | /* merge rows S1 and T */ 58 | for(p1 = save_sec->first_col; p1 != 0; p1 = p1->next_col) { 59 | (void) sm_insert(A, p->row_num, p1->col_num); 60 | } 61 | } 62 | } 63 | 64 | sm_delcol(A, c1_col_num); 65 | sm_delcol(A, c2_col_num); 66 | sm_delrow(A, primary_row_num); 67 | sm_delrow(A, secondary_row_num); 68 | 69 | stats->gimpel_count++; 70 | stats->gimpel++; 71 | *best = sm_mincov(A, select, weight, lb-1, bound-1, depth, stats); 72 | stats->gimpel--; 73 | 74 | if (*best != NIL(solution_t)) { 75 | /* is secondary row covered ? */ 76 | if (sm_row_intersects(save_sec, (*best)->row)) { 77 | /* yes, actually select c2 */ 78 | solution_add(*best, weight, c2_col_num); 79 | } else { 80 | solution_add(*best, weight, c1_col_num); 81 | } 82 | } 83 | 84 | sm_row_free(save_sec); 85 | return 1; 86 | } else { 87 | return 0; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /espresso-src/globals.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | /* 4 | * Global Variable Declarations 5 | */ 6 | 7 | unsigned int debug; /* debug parameter */ 8 | bool verbose_debug; /* -v: whether to print a lot */ 9 | char *total_name[TIME_COUNT]; /* basic function names */ 10 | long total_time[TIME_COUNT]; /* time spent in basic fcts */ 11 | int total_calls[TIME_COUNT]; /* # calls to each fct */ 12 | 13 | bool echo_comments; /* turned off by -eat option */ 14 | bool echo_unknown_commands; /* always true ?? */ 15 | bool force_irredundant; /* -nirr command line option */ 16 | bool skip_make_sparse; 17 | bool kiss; /* -kiss command line option */ 18 | bool pos; /* -pos command line option */ 19 | bool print_solution; /* -x command line option */ 20 | bool recompute_onset; /* -onset command line option */ 21 | bool remove_essential; /* -ness command line option */ 22 | bool single_expand; /* -fast command line option */ 23 | bool summary; /* -s command line option */ 24 | bool trace; /* -t command line option */ 25 | bool unwrap_onset; /* -nunwrap command line option */ 26 | bool use_random_order; /* -random command line option */ 27 | bool use_super_gasp; /* -strong command line option */ 28 | char *filename; /* filename PLA was read from */ 29 | 30 | struct pla_types_struct pla_types[] = { 31 | {"-f", F_type}, 32 | {"-r", R_type}, 33 | {"-d", D_type}, 34 | {"-fd", FD_type}, 35 | {"-fr", FR_type}, 36 | {"-dr", DR_type}, 37 | {"-fdr", FDR_type}, 38 | {"-fc", F_type | CONSTRAINTS_type}, 39 | {"-rc", R_type | CONSTRAINTS_type}, 40 | {"-dc", D_type | CONSTRAINTS_type}, 41 | {"-fdc", FD_type | CONSTRAINTS_type}, 42 | {"-frc", FR_type | CONSTRAINTS_type}, 43 | {"-drc", DR_type | CONSTRAINTS_type}, 44 | {"-fdrc", FDR_type | CONSTRAINTS_type}, 45 | {"-pleasure", PLEASURE_type}, 46 | {"-eqn", EQNTOTT_type}, 47 | {"-eqntott", EQNTOTT_type}, 48 | {"-kiss", KISS_type}, 49 | {"-cons", CONSTRAINTS_type}, 50 | {"-scons", SYMBOLIC_CONSTRAINTS_type}, 51 | {0, 0} 52 | }; 53 | 54 | 55 | struct cube_struct cube, temp_cube_save; 56 | struct cdata_struct cdata, temp_cdata_save; 57 | 58 | int bit_count[256] = { 59 | 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, 60 | 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 61 | 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 62 | 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 63 | 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 64 | 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 65 | 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 66 | 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 67 | }; 68 | -------------------------------------------------------------------------------- /espresso-src/indep.c: -------------------------------------------------------------------------------- 1 | #include "mincov_int.h" 2 | 3 | static sm_matrix *build_intersection_matrix(sm_matrix *A); 4 | 5 | 6 | #if 0 7 | /* 8 | * verify that all rows in 'indep' are actually independent ! 9 | */ 10 | static int 11 | verify_indep_set(A, indep) 12 | sm_matrix *A; 13 | sm_row *indep; 14 | { 15 | register sm_row *prow, *prow1; 16 | register sm_element *p, *p1; 17 | 18 | for(p = indep->first_col; p != 0; p = p->next_col) { 19 | prow = sm_get_row(A, p->col_num); 20 | for(p1 = p->next_col; p1 != 0; p1 = p1->next_col) { 21 | prow1 = sm_get_row(A, p1->col_num); 22 | if (sm_row_intersects(prow, prow1)) { 23 | return 0; 24 | } 25 | } 26 | } 27 | return 1; 28 | } 29 | #endif 30 | 31 | solution_t * 32 | sm_maximal_independent_set(sm_matrix *A, int *weight) 33 | { 34 | register sm_row *best_row, *prow; 35 | register sm_element *p; 36 | int least_weight; 37 | sm_row *save; 38 | sm_matrix *B; 39 | solution_t *indep; 40 | 41 | indep = solution_alloc(); 42 | B = build_intersection_matrix(A); 43 | 44 | while (B->nrows > 0) { 45 | /* Find the row which is disjoint from a maximum number of rows */ 46 | best_row = B->first_row; 47 | for(prow = B->first_row->next_row; prow != 0; prow = prow->next_row) { 48 | if (prow->length < best_row->length) { 49 | best_row = prow; 50 | } 51 | } 52 | 53 | /* Find which element in this row has least weight */ 54 | if (weight == NIL(int)) { 55 | least_weight = 1; 56 | } else { 57 | prow = sm_get_row(A, best_row->row_num); 58 | least_weight = weight[prow->first_col->col_num]; 59 | for(p = prow->first_col->next_col; p != 0; p = p->next_col) { 60 | if (weight[p->col_num] < least_weight) { 61 | least_weight = weight[p->col_num]; 62 | } 63 | } 64 | } 65 | indep->cost += least_weight; 66 | (void) sm_row_insert(indep->row, best_row->row_num); 67 | 68 | /* Discard the rows which intersect this row */ 69 | save = sm_row_dup(best_row); 70 | for(p = save->first_col; p != 0; p = p->next_col) { 71 | sm_delrow(B, p->col_num); 72 | sm_delcol(B, p->col_num); 73 | } 74 | sm_row_free(save); 75 | } 76 | 77 | sm_free(B); 78 | 79 | /* 80 | if (! verify_indep_set(A, indep->row)) { 81 | fail("sm_maximal_independent_set: row set is not independent"); 82 | } 83 | */ 84 | return indep; 85 | } 86 | 87 | static sm_matrix * 88 | build_intersection_matrix(sm_matrix *A) 89 | { 90 | register sm_row *prow, *prow1; 91 | register sm_element *p, *p1; 92 | register sm_col *pcol; 93 | sm_matrix *B; 94 | 95 | /* Build row-intersection matrix */ 96 | B = sm_alloc(); 97 | for(prow = A->first_row; prow != 0; prow = prow->next_row) { 98 | 99 | /* Clear flags on all rows we can reach from row 'prow' */ 100 | for(p = prow->first_col; p != 0; p = p->next_col) { 101 | pcol = sm_get_col(A, p->col_num); 102 | for(p1 = pcol->first_row; p1 != 0; p1 = p1->next_row) { 103 | prow1 = sm_get_row(A, p1->row_num); 104 | prow1->flag = 0; 105 | } 106 | } 107 | 108 | /* Now record which rows can be reached */ 109 | for(p = prow->first_col; p != 0; p = p->next_col) { 110 | pcol = sm_get_col(A, p->col_num); 111 | for(p1 = pcol->first_row; p1 != 0; p1 = p1->next_row) { 112 | prow1 = sm_get_row(A, p1->row_num); 113 | if (! prow1->flag) { 114 | prow1->flag = 1; 115 | (void) sm_insert(B, prow->row_num, prow1->row_num); 116 | } 117 | } 118 | } 119 | } 120 | 121 | return B; 122 | } 123 | -------------------------------------------------------------------------------- /espresso-src/main.h: -------------------------------------------------------------------------------- 1 | enum keys { 2 | KEY_ESPRESSO, KEY_PLA_verify, KEY_check, KEY_contain, KEY_d1merge, 3 | KEY_disjoint, KEY_dsharp, KEY_echo, KEY_essen, KEY_exact, KEY_expand, 4 | KEY_gasp, KEY_intersect, KEY_irred, KEY_lexsort, KEY_make_sparse, 5 | KEY_map, KEY_mapdc, KEY_minterms, KEY_opo, KEY_opoall, 6 | KEY_pair, KEY_pairall, KEY_primes, KEY_qm, KEY_reduce, KEY_sharp, 7 | KEY_simplify, KEY_so, KEY_so_both, KEY_stats, KEY_super_gasp, KEY_taut, 8 | KEY_test, KEY_equiv, KEY_union, KEY_verify, KEY_MANY_ESPRESSO, 9 | KEY_separate, KEY_xor, KEY_d1merge_in, KEY_fsm, KEY_signature, 10 | KEY_unknown 11 | }; 12 | 13 | /* Lookup table for program options */ 14 | struct { 15 | char *name; 16 | enum keys key; 17 | int num_plas; 18 | bool needs_offset; 19 | bool needs_dcset; 20 | } option_table [] = { 21 | /* ways to minimize functions */ 22 | {"ESPRESSO", KEY_ESPRESSO, 1, TRUE, TRUE}, /* must be first */ 23 | {"many", KEY_MANY_ESPRESSO, 1, TRUE, TRUE}, 24 | {"exact", KEY_exact, 1, TRUE, TRUE}, 25 | {"qm", KEY_qm, 1, TRUE, TRUE}, 26 | {"single_output", KEY_so, 1, TRUE, TRUE}, 27 | {"so", KEY_so, 1, TRUE, TRUE}, 28 | {"so_both", KEY_so_both, 1, TRUE, TRUE}, 29 | {"simplify", KEY_simplify, 1, FALSE, FALSE}, 30 | {"echo", KEY_echo, 1, FALSE, FALSE}, 31 | {"signature", KEY_signature, 1, TRUE, TRUE}, 32 | 33 | /* output phase assignment and assignment of inputs to two-bit decoders */ 34 | {"opo", KEY_opo, 1, TRUE, TRUE}, 35 | {"opoall", KEY_opoall, 1, TRUE, TRUE}, 36 | {"pair", KEY_pair, 1, TRUE, TRUE}, 37 | {"pairall", KEY_pairall, 1, TRUE, TRUE}, 38 | 39 | /* Ways to check covers */ 40 | {"check", KEY_check, 1, TRUE, TRUE}, 41 | {"stats", KEY_stats, 1, FALSE, FALSE}, 42 | {"verify", KEY_verify, 2, FALSE, TRUE}, 43 | {"PLAverify", KEY_PLA_verify, 2, FALSE, TRUE}, 44 | 45 | /* hacks */ 46 | {"equiv", KEY_equiv, 1, TRUE, TRUE}, 47 | {"map", KEY_map, 1, FALSE, FALSE}, 48 | {"mapdc", KEY_mapdc, 1, FALSE, FALSE}, 49 | {"fsm", KEY_fsm, 1, FALSE, TRUE}, 50 | 51 | /* the basic boolean operations on covers */ 52 | {"contain", KEY_contain, 1, FALSE, FALSE}, 53 | {"d1merge", KEY_d1merge, 1, FALSE, FALSE}, 54 | {"d1merge_in", KEY_d1merge_in, 1, FALSE, FALSE}, 55 | {"disjoint", KEY_disjoint, 1, TRUE, FALSE}, 56 | {"dsharp", KEY_dsharp, 2, FALSE, FALSE}, 57 | {"intersect", KEY_intersect, 2, FALSE, FALSE}, 58 | {"minterms", KEY_minterms, 1, FALSE, FALSE}, 59 | {"primes", KEY_primes, 1, FALSE, TRUE}, 60 | {"separate", KEY_separate, 1, TRUE, TRUE}, 61 | {"sharp", KEY_sharp, 2, FALSE, FALSE}, 62 | {"union", KEY_union, 2, FALSE, FALSE}, 63 | {"xor", KEY_xor, 2, TRUE, TRUE}, 64 | 65 | /* debugging only -- call each step of the espresso algorithm */ 66 | {"essen", KEY_essen, 1, FALSE, TRUE}, 67 | {"expand", KEY_expand, 1, TRUE, FALSE}, 68 | {"gasp", KEY_gasp, 1, TRUE, TRUE}, 69 | {"irred", KEY_irred, 1, FALSE, TRUE}, 70 | {"make_sparse", KEY_make_sparse, 1, TRUE, TRUE}, 71 | {"reduce", KEY_reduce, 1, FALSE, TRUE}, 72 | {"taut", KEY_taut, 1, FALSE, FALSE}, 73 | {"super_gasp", KEY_super_gasp, 1, TRUE, TRUE}, 74 | {"lexsort", KEY_lexsort, 1, FALSE, FALSE}, 75 | {"test", KEY_test, 1, TRUE, TRUE}, 76 | {0, KEY_unknown, 0, FALSE, FALSE} /* must be last */ 77 | }; 78 | 79 | 80 | struct { 81 | char *name; 82 | int value; 83 | } debug_table[] = { 84 | {"", EXPAND + ESSEN + IRRED + REDUCE + SPARSE + GASP + SHARP + MINCOV}, 85 | {"compl", COMPL}, {"essen", ESSEN}, 86 | {"expand", EXPAND}, {"expand1", EXPAND1|EXPAND}, 87 | {"irred", IRRED}, {"irred1", IRRED1|IRRED}, 88 | {"reduce", REDUCE}, {"reduce1", REDUCE1|REDUCE}, 89 | {"mincov", MINCOV}, {"mincov1", MINCOV1|MINCOV}, 90 | {"sparse", SPARSE}, {"sharp", SHARP}, 91 | {"taut", TAUT}, {"gasp", GASP}, 92 | {"exact", EXACT}, 93 | {0, 0} 94 | }; 95 | 96 | 97 | struct { 98 | char *name; 99 | int *variable; 100 | int value; 101 | } esp_opt_table[] = { 102 | {"eat", &echo_comments, FALSE}, 103 | {"eatdots", &echo_unknown_commands, FALSE}, 104 | {"fast", &single_expand, TRUE}, 105 | {"kiss", &kiss, TRUE}, 106 | {"ness", &remove_essential, FALSE}, 107 | {"nirr", &force_irredundant, FALSE}, 108 | {"nunwrap", &unwrap_onset, FALSE}, 109 | {"onset", &recompute_onset, TRUE}, 110 | {"pos", &pos, TRUE}, 111 | {"random", &use_random_order, TRUE}, 112 | {"strong", &use_super_gasp, TRUE}, 113 | {0, 0, 0} 114 | }; 115 | -------------------------------------------------------------------------------- /espresso-src/map.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | static pcube Gcube; 4 | static pset Gminterm; 5 | 6 | pset minterms(pset_family T) 7 | { 8 | int size, var; 9 | register pcube last; 10 | 11 | size = 1; 12 | for(var = 0; var < cube.num_vars; var++) 13 | size *= cube.part_size[var]; 14 | Gminterm = set_new(size); 15 | 16 | foreach_set(T, last, Gcube) 17 | explode(cube.num_vars-1, 0); 18 | 19 | return Gminterm; 20 | } 21 | 22 | 23 | void explode(int var, int z) 24 | { 25 | int i, last = cube.last_part[var]; 26 | for(i=cube.first_part[var], z *= cube.part_size[var]; i<=last; i++, z++) 27 | if (is_in_set(Gcube, i)) 28 | { 29 | if (var == 0) 30 | set_insert(Gminterm, z); 31 | else 32 | explode(var-1, z); 33 | } 34 | } 35 | 36 | 37 | static int mapindex[16][16] = { 38 | {0, 1, 3, 2, 16, 17, 19, 18, 80, 81, 83, 82, 64, 65, 67, 66}, 39 | {4, 5, 7, 6, 20, 21, 23, 22, 84, 85, 87, 86, 68, 69, 71, 70}, 40 | {12, 13, 15, 14, 28, 29, 31, 30, 92, 93, 95, 94, 76, 77, 79, 78}, 41 | {8, 9, 11, 10, 24, 25, 27, 26, 88, 89, 91, 90, 72, 73, 75, 74}, 42 | 43 | {32, 33, 35, 34, 48, 49, 51, 50, 112,113,115,114, 96, 97, 99, 98}, 44 | {36, 37, 39, 38, 52, 53, 55, 54, 116,117,119,118, 100,101,103,102}, 45 | {44, 45, 47, 46, 60, 61, 63, 62, 124,125,127,126, 108,109,111,110}, 46 | {40, 41, 43, 42, 56, 57, 59, 58, 120,121,123,122, 104,105,107,106}, 47 | 48 | 49 | {160,161,163,162, 176,177,179,178, 240,241,243,242, 224,225,227,226}, 50 | {164,165,167,166, 180,181,183,182, 244,245,247,246, 228,229,231,230}, 51 | {172,173,175,174, 188,189,191,190, 252,253,255,254, 236,237,239,238}, 52 | {168,169,171,170, 184,185,187,186, 248,249,251,250, 232,233,235,234}, 53 | 54 | {128,129,131,130, 144,145,147,146, 208,209,211,210, 192,193,195,194}, 55 | {132,133,135,134, 148,149,151,150, 212,213,215,214, 196,197,199,198}, 56 | {140,141,143,142, 156,157,159,158, 220,221,223,222, 204,205,207,206}, 57 | {136,137,139,138, 152,153,155,154, 216,217,219,218, 200,201,203,202} 58 | }; 59 | 60 | #define POWER2(n) (1 << n) 61 | void map(pset_family T) 62 | { 63 | int j, k, l, other_input_offset, output_offset, outnum, ind; 64 | int largest_input_ind, numout; 65 | char c; 66 | pset m; 67 | bool some_output; 68 | 69 | m = minterms(T); 70 | largest_input_ind = POWER2(cube.num_binary_vars); 71 | numout = cube.part_size[cube.num_vars-1]; 72 | 73 | for(outnum = 0; outnum < numout; outnum++) { 74 | output_offset = outnum * largest_input_ind; 75 | printf("\n\nOutput space # %d\n", outnum); 76 | for(l = 0; l <= MAX(cube.num_binary_vars - 8, 0); l++) { 77 | other_input_offset = l * 256; 78 | for(k = 0; k < 16; k++) { 79 | some_output = FALSE; 80 | for(j = 0; j < 16; j++) { 81 | ind = mapindex[k][j] + other_input_offset; 82 | if (ind < largest_input_ind) { 83 | c = is_in_set(m, ind+output_offset) ? '1' : '.'; 84 | putchar(c); 85 | some_output = TRUE; 86 | } 87 | if ((j+1)%4 == 0) 88 | putchar(' '); 89 | if ((j+1)%8 == 0) 90 | printf(" "); 91 | } 92 | if (some_output) 93 | putchar('\n'); 94 | if ((k+1)%4 == 0) { 95 | if (k != 15 && mapindex[k+1][0] >= largest_input_ind) 96 | break; 97 | putchar('\n'); 98 | } 99 | if ((k+1)%8 == 0) 100 | putchar('\n'); 101 | } 102 | } 103 | } 104 | set_free(m); 105 | } 106 | -------------------------------------------------------------------------------- /espresso-src/mincov.c: -------------------------------------------------------------------------------- 1 | #include "mincov_int.h" 2 | 3 | /* 4 | * mincov.c 5 | */ 6 | 7 | #define USE_GIMPEL 8 | #define USE_INDEP_SET 9 | 10 | static int select_column(sm_matrix *A, int *weight, solution_t *indep); 11 | static void select_essential(sm_matrix *A, solution_t *select, int *weight, int bound); 12 | static int verify_cover(sm_matrix *A, sm_row *cover); 13 | 14 | #define fail(why) {\ 15 | (void) fprintf(stderr, "Fatal error: file %s, line %d\n%s\n",\ 16 | __FILE__, __LINE__, why);\ 17 | (void) fflush(stdout);\ 18 | abort();\ 19 | } 20 | 21 | sm_row * 22 | sm_minimum_cover(sm_matrix *A, int *weight, int heuristic, int debug_level) 23 | 24 | 25 | /* set to 1 for a heuristic covering */ 26 | /* how deep in the recursion to provide info */ 27 | { 28 | stats_t stats; 29 | solution_t *best, *select; 30 | sm_row *prow, *sol; 31 | sm_col *pcol; 32 | sm_matrix *dup_A; 33 | int nelem, bound; 34 | double sparsity; 35 | 36 | /* Avoid sillyness */ 37 | if (A->nrows <= 0) { 38 | return sm_row_alloc(); /* easy to cover */ 39 | } 40 | 41 | /* Initialize debugging structure */ 42 | stats.start_time = util_cpu_time(); 43 | stats.debug = debug_level > 0; 44 | stats.max_print_depth = debug_level; 45 | stats.max_depth = -1; 46 | stats.nodes = 0; 47 | stats.component = stats.comp_count = 0; 48 | stats.gimpel = stats.gimpel_count = 0; 49 | stats.no_branching = heuristic != 0; 50 | stats.lower_bound = -1; 51 | 52 | /* Check the matrix sparsity */ 53 | nelem = 0; 54 | sm_foreach_row(A, prow) { 55 | nelem += prow->length; 56 | } 57 | sparsity = (double) nelem / (double) (A->nrows * A->ncols); 58 | 59 | /* Determine an upper bound on the solution */ 60 | bound = 1; 61 | sm_foreach_col(A, pcol) { 62 | bound += WEIGHT(weight, pcol->col_num); 63 | } 64 | 65 | /* Perform the covering */ 66 | select = solution_alloc(); 67 | dup_A = sm_dup(A); 68 | best = sm_mincov(dup_A, select, weight, 0, bound, 0, &stats); 69 | sm_free(dup_A); 70 | solution_free(select); 71 | 72 | if (stats.debug) { 73 | if (stats.no_branching) { 74 | (void) printf("**** heuristic covering ...\n"); 75 | (void) printf("lower bound = %d\n", stats.lower_bound); 76 | } 77 | (void) printf("matrix = %d by %d with %d elements (%4.3f%%)\n", 78 | A->nrows, A->ncols, nelem, sparsity * 100.0); 79 | (void) printf("cover size = %d elements\n", best->row->length); 80 | (void) printf("cover cost = %d\n", best->cost); 81 | (void) printf("time = %s\n", 82 | util_print_time(util_cpu_time() - stats.start_time)); 83 | (void) printf("components = %d\n", stats.comp_count); 84 | (void) printf("gimpel = %d\n", stats.gimpel_count); 85 | (void) printf("nodes = %d\n", stats.nodes); 86 | (void) printf("max_depth = %d\n", stats.max_depth); 87 | } 88 | 89 | sol = sm_row_dup(best->row); 90 | if (! verify_cover(A, sol)) { 91 | fail("mincov: internal error -- cover verification failed\n"); 92 | } 93 | solution_free(best); 94 | return sol; 95 | } 96 | 97 | /* 98 | * Find the best cover for 'A' (given that 'select' already selected); 99 | * 100 | * - abort search if a solution cannot be found which beats 'bound' 101 | * 102 | * - if any solution meets 'lower_bound', then it is the optimum solution 103 | * and can be returned without further work. 104 | */ 105 | 106 | solution_t * 107 | sm_mincov(sm_matrix *A, solution_t *select, int *weight, int lb, int bound, int depth, stats_t *stats) 108 | { 109 | sm_matrix *A1, *A2, *L, *R; 110 | sm_element *p; 111 | solution_t *select1, *select2, *best, *best1, *best2, *indep; 112 | int pick, lb_new, debug; 113 | 114 | /* Start out with some debugging information */ 115 | stats->nodes++; 116 | if (depth > stats->max_depth) stats->max_depth = depth; 117 | debug = stats->debug && (depth <= stats->max_print_depth); 118 | 119 | /* Apply row dominance, column dominance, and select essentials */ 120 | select_essential(A, select, weight, bound); 121 | if (select->cost >= bound) { 122 | return NIL(solution_t); 123 | } 124 | 125 | /* See if gimpel's reduction technique applies ... */ 126 | #ifdef USE_GIMPEL 127 | if ( weight == NIL(int)) { /* hack until we fix it */ 128 | if (gimpel_reduce(A, select, weight, lb, bound, depth, stats, &best)) { 129 | return best; 130 | } 131 | } 132 | #endif 133 | 134 | #ifdef USE_INDEP_SET 135 | /* Determine bound from here to final solution using independent-set */ 136 | indep = sm_maximal_independent_set(A, weight); 137 | 138 | /* make sure the lower bound is monotonically increasing */ 139 | lb_new = MAX(select->cost + indep->cost, lb); 140 | pick = select_column(A, weight, indep); 141 | solution_free(indep); 142 | #else 143 | lb_new = select->cost + (A->nrows > 0); 144 | pick = select_column(A, weight, NIL(solution_t)); 145 | #endif 146 | 147 | if (depth == 0) { 148 | stats->lower_bound = lb_new + stats->gimpel; 149 | } 150 | 151 | if (debug) { 152 | (void) printf("ABSMIN[%2d]%s", depth, stats->component ? "*" : " "); 153 | (void) printf(" %3dx%3d sel=%3d bnd=%3d lb=%3d %12s ", 154 | A->nrows, A->ncols, select->cost + stats->gimpel, 155 | bound + stats->gimpel, lb_new + stats->gimpel, 156 | util_print_time(util_cpu_time()-stats->start_time)); 157 | } 158 | 159 | /* Check for bounding based on no better solution possible */ 160 | if (lb_new >= bound) { 161 | if (debug) (void) printf("bounded\n"); 162 | best = NIL(solution_t); 163 | 164 | 165 | /* Check for new best solution */ 166 | } else if (A->nrows == 0) { 167 | best = solution_dup(select); 168 | if (debug) (void) printf("BEST\n"); 169 | if (stats->debug && stats->component == 0) { 170 | (void) printf("new 'best' solution %d at level %d (time is %s)\n", 171 | best->cost + stats->gimpel, depth, 172 | util_print_time(util_cpu_time() - stats->start_time)); 173 | } 174 | 175 | 176 | /* Check for a partition of the problem */ 177 | } else if (sm_block_partition(A, &L, &R)) { 178 | /* Make L the smaller problem */ 179 | if (L->ncols > R->ncols) { 180 | A1 = L; 181 | L = R; 182 | R = A1; 183 | } 184 | if (debug) (void) printf("comp %d %d\n", L->nrows, R->nrows); 185 | stats->comp_count++; 186 | 187 | /* Solve problem for L */ 188 | select1 = solution_alloc(); 189 | stats->component++; 190 | best1 = sm_mincov(L, select1, weight, 0, 191 | bound-select->cost, depth+1, stats); 192 | stats->component--; 193 | solution_free(select1); 194 | sm_free(L); 195 | 196 | /* Add best solution to the selected set */ 197 | if (best1 == NIL(solution_t)) { 198 | best = NIL(solution_t); 199 | } else { 200 | for(p = best1->row->first_col; p != 0; p = p->next_col) { 201 | solution_add(select, weight, p->col_num); 202 | } 203 | solution_free(best1); 204 | 205 | /* recur for the remaining block */ 206 | best = sm_mincov(R, select, weight, lb_new, bound, depth+1, stats); 207 | } 208 | sm_free(R); 209 | 210 | /* We've tried as hard as possible, but now we must split and recur */ 211 | } else { 212 | if (debug) (void) printf("pick=%d\n", pick); 213 | 214 | /* Assume we choose this column to be in the covering set */ 215 | A1 = sm_dup(A); 216 | select1 = solution_dup(select); 217 | solution_accept(select1, A1, weight, pick); 218 | best1 = sm_mincov(A1, select1, weight, lb_new, bound, depth+1, stats); 219 | solution_free(select1); 220 | sm_free(A1); 221 | 222 | /* Update the upper bound if we found a better solution */ 223 | if (best1 != NIL(solution_t) && bound > best1->cost) { 224 | bound = best1->cost; 225 | } 226 | 227 | /* See if this is a heuristic covering (no branching) */ 228 | if (stats->no_branching) { 229 | return best1; 230 | } 231 | 232 | /* Check for reaching lower bound -- if so, don't actually branch */ 233 | if (best1 != NIL(solution_t) && best1->cost == lb_new) { 234 | return best1; 235 | } 236 | 237 | /* Now assume we cannot have that column */ 238 | A2 = sm_dup(A); 239 | select2 = solution_dup(select); 240 | solution_reject(select2, A2, weight, pick); 241 | best2 = sm_mincov(A2, select2, weight, lb_new, bound, depth+1, stats); 242 | solution_free(select2); 243 | sm_free(A2); 244 | 245 | best = solution_choose_best(best1, best2); 246 | } 247 | 248 | return best; 249 | } 250 | 251 | static int 252 | select_column(sm_matrix *A, int *weight, solution_t *indep) 253 | { 254 | register sm_col *pcol; 255 | register sm_row *prow, *indep_cols; 256 | register sm_element *p, *p1; 257 | double w, best; 258 | int best_col; 259 | 260 | indep_cols = sm_row_alloc(); 261 | if (indep != NIL(solution_t)) { 262 | /* Find which columns are in the independent sets */ 263 | for(p = indep->row->first_col; p != 0; p = p->next_col) { 264 | prow = sm_get_row(A, p->col_num); 265 | for(p1 = prow->first_col; p1 != 0; p1 = p1->next_col) { 266 | (void) sm_row_insert(indep_cols, p1->col_num); 267 | } 268 | } 269 | } else { 270 | /* select out of all columns */ 271 | sm_foreach_col(A, pcol) { 272 | (void) sm_row_insert(indep_cols, pcol->col_num); 273 | } 274 | } 275 | 276 | /* Find the best column */ 277 | best_col = -1; 278 | best = -1; 279 | 280 | /* Consider only columns which are in some independent row */ 281 | sm_foreach_row_element(indep_cols, p1) { 282 | pcol = sm_get_col(A, p1->col_num); 283 | 284 | /* Compute the total 'value' of all things covered by the column */ 285 | w = 0.0; 286 | for(p = pcol->first_row; p != 0; p = p->next_row) { 287 | prow = sm_get_row(A, p->row_num); 288 | w += 1.0 / ((double) prow->length - 1.0); 289 | } 290 | 291 | /* divide this by the relative cost of choosing this column */ 292 | w = w / (double) WEIGHT(weight, pcol->col_num); 293 | 294 | /* maximize this ratio */ 295 | if (w > best) { 296 | best_col = pcol->col_num; 297 | best = w; 298 | } 299 | } 300 | 301 | sm_row_free(indep_cols); 302 | return best_col; 303 | } 304 | 305 | static void 306 | select_essential(sm_matrix *A, solution_t *select, int *weight, int bound) 307 | 308 | 309 | 310 | /* must beat this solution */ 311 | { 312 | register sm_element *p; 313 | register sm_row *prow, *essen; 314 | int delcols, delrows, essen_count; 315 | 316 | do { 317 | /* Check for dominated columns */ 318 | delcols = sm_col_dominance(A, weight); 319 | 320 | /* Find the rows with only 1 element (the essentials) */ 321 | essen = sm_row_alloc(); 322 | sm_foreach_row(A, prow) { 323 | if (prow->length == 1) { 324 | (void) sm_row_insert(essen, prow->first_col->col_num); 325 | } 326 | } 327 | 328 | /* Select all of the elements */ 329 | sm_foreach_row_element(essen, p) { 330 | solution_accept(select, A, weight, p->col_num); 331 | /* Make sure solution still looks good */ 332 | if (select->cost >= bound) { 333 | sm_row_free(essen); 334 | return; 335 | } 336 | } 337 | essen_count = essen->length; 338 | sm_row_free(essen); 339 | 340 | /* Check for dominated rows */ 341 | delrows = sm_row_dominance(A); 342 | 343 | } while (delcols > 0 || delrows > 0 || essen_count > 0); 344 | } 345 | 346 | static int 347 | verify_cover(sm_matrix *A, sm_row *cover) 348 | { 349 | sm_row *prow; 350 | 351 | sm_foreach_row(A, prow) { 352 | if (! sm_row_intersects(prow, cover)) { 353 | return 0; 354 | } 355 | } 356 | return 1; 357 | } 358 | -------------------------------------------------------------------------------- /espresso-src/mincov.h: -------------------------------------------------------------------------------- 1 | /* exported */ 2 | extern sm_row *sm_minimum_cover(sm_matrix *A, int *weight, int heuristic, int debug_level); 3 | -------------------------------------------------------------------------------- /espresso-src/mincov_int.h: -------------------------------------------------------------------------------- 1 | #include "port.h" 2 | #include "utility.h" 3 | #include "sparse.h" 4 | #include "mincov.h" 5 | 6 | 7 | typedef struct stats_struct stats_t; 8 | struct stats_struct { 9 | int debug; /* 1 if debugging is enabled */ 10 | int max_print_depth; /* dump stats for levels up to this level */ 11 | int max_depth; /* deepest the recursion has gone */ 12 | int nodes; /* total nodes visited */ 13 | int component; /* currently solving a component */ 14 | int comp_count; /* number of components detected */ 15 | int gimpel_count; /* number of times Gimpel reduction applied */ 16 | int gimpel; /* currently inside Gimpel reduction */ 17 | long start_time; /* cpu time when the covering started */ 18 | int no_branching; 19 | int lower_bound; 20 | }; 21 | 22 | 23 | 24 | typedef struct solution_struct solution_t; 25 | struct solution_struct { 26 | sm_row *row; 27 | int cost; 28 | }; 29 | 30 | 31 | extern solution_t *solution_alloc(void); 32 | extern void solution_free(solution_t *sol); 33 | extern solution_t *solution_dup(solution_t *sol); 34 | extern void solution_accept(solution_t *sol, sm_matrix *A, int *weight, int col); 35 | extern void solution_reject(solution_t *sol, sm_matrix *A, int *weight, int col); 36 | extern void solution_add(solution_t *sol, int *weight, int col); 37 | extern solution_t *solution_choose_best(solution_t *best1, solution_t *best2); 38 | 39 | extern solution_t *sm_maximal_independent_set(sm_matrix *A, int *weight); 40 | extern solution_t *sm_mincov(sm_matrix *A, solution_t *select, int *weight, int lb, int bound, int depth, stats_t *stats); 41 | extern int gimpel_reduce(sm_matrix *A, solution_t *select, int *weight, int lb, int bound, int depth, stats_t *stats, solution_t **best); 42 | 43 | 44 | #define WEIGHT(weight, col) (weight == NIL(int) ? 1 : weight[col]) 45 | -------------------------------------------------------------------------------- /espresso-src/part.c: -------------------------------------------------------------------------------- 1 | #include "mincov_int.h" 2 | 3 | 4 | static void 5 | copy_row(register sm_matrix *A, register sm_row *prow) 6 | { 7 | register sm_element *p; 8 | 9 | for(p = prow->first_col; p != 0; p = p->next_col) { 10 | (void) sm_insert(A, p->row_num, p->col_num); 11 | } 12 | } 13 | 14 | static int visit_row(sm_matrix *A, sm_row *prow, int *rows_visited, int *cols_visited); 15 | 16 | static int 17 | visit_col(sm_matrix *A, sm_col *pcol, int *rows_visited, int *cols_visited) 18 | { 19 | sm_element *p; 20 | sm_row *prow; 21 | 22 | if (! pcol->flag) { 23 | pcol->flag = 1; 24 | (*cols_visited)++; 25 | if (*cols_visited == A->ncols) { 26 | return 1; 27 | } 28 | for(p = pcol->first_row; p != 0; p = p->next_row) { 29 | prow = sm_get_row(A, p->row_num); 30 | if (! prow->flag) { 31 | if (visit_row(A, prow, rows_visited, cols_visited)) { 32 | return 1; 33 | } 34 | } 35 | } 36 | } 37 | return 0; 38 | } 39 | 40 | static int 41 | visit_row(sm_matrix *A, sm_row *prow, int *rows_visited, int *cols_visited) 42 | { 43 | sm_element *p; 44 | sm_col *pcol; 45 | 46 | if (! prow->flag) { 47 | prow->flag = 1; 48 | (*rows_visited)++; 49 | if (*rows_visited == A->nrows) { 50 | return 1; 51 | } 52 | for(p = prow->first_col; p != 0; p = p->next_col) { 53 | pcol = sm_get_col(A, p->col_num); 54 | if (! pcol->flag) { 55 | if (visit_col(A, pcol, rows_visited, cols_visited)) { 56 | return 1; 57 | } 58 | } 59 | } 60 | } 61 | return 0; 62 | } 63 | 64 | 65 | 66 | int 67 | sm_block_partition(sm_matrix *A, sm_matrix **L, sm_matrix **R) 68 | { 69 | int cols_visited, rows_visited; 70 | register sm_row *prow; 71 | register sm_col *pcol; 72 | 73 | /* Avoid the trivial case */ 74 | if (A->nrows == 0) { 75 | return 0; 76 | } 77 | 78 | /* Reset the visited flags for each row and column */ 79 | for(prow = A->first_row; prow != 0; prow = prow->next_row) { 80 | prow->flag = 0; 81 | } 82 | for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) { 83 | pcol->flag = 0; 84 | } 85 | 86 | cols_visited = rows_visited = 0; 87 | if (visit_row(A, A->first_row, &rows_visited, &cols_visited)) { 88 | /* we found all of the rows */ 89 | return 0; 90 | } else { 91 | *L = sm_alloc(); 92 | *R = sm_alloc(); 93 | for(prow = A->first_row; prow != 0; prow = prow->next_row) { 94 | if (prow->flag) { 95 | copy_row(*L, prow); 96 | } else { 97 | copy_row(*R, prow); 98 | } 99 | } 100 | return 1; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /espresso-src/port.h: -------------------------------------------------------------------------------- 1 | #ifndef PORT_H 2 | #define PORT_H 3 | 4 | /* 5 | * int32 should be defined as the most economical sized integer capable of 6 | * holding a 32 bit quantity 7 | * int16 should be similarly defined 8 | */ 9 | 10 | /* AB Stuff */ 11 | 12 | typedef int (* qsort_compare_func)(const void *, const void *); 13 | 14 | /* XXX hack */ 15 | #ifndef MACHDEP_INCLUDED 16 | #define MACHDEP_INCLUDED 17 | #ifdef vax 18 | typedef int int32; 19 | typedef short int16; 20 | #else 21 | /* Ansi-C promises that these definitions should always work */ 22 | typedef long int32; 23 | typedef int int16; 24 | #endif /* vax */ 25 | #endif /* MACHDEP_INCLUDED */ 26 | 27 | 28 | #ifndef __STDC__ 29 | #ifndef __DATE__ 30 | #ifdef CUR_DATE 31 | #define __DATE__ CUR_DATE 32 | #else 33 | #define __DATE__ "unknown-date" 34 | #endif /* CUR_DATE */ 35 | #endif /* __DATE__ */ 36 | 37 | #ifndef __TIME__ 38 | #ifdef CUR_TIME 39 | #define __TIME__ CUR_TIME 40 | #else 41 | #define __TIME__ "unknown-time" 42 | #endif /* CUR_TIME */ 43 | #endif /* __TIME__ */ 44 | #endif /* __STDC__ */ 45 | 46 | #ifdef sun386 47 | #define PORTAR 48 | #endif 49 | 50 | #include 51 | #include 52 | #include 53 | #undef HUGE 54 | #include 55 | #include 56 | 57 | #if defined(ultrix) && defined(SIGLOST) 58 | #define ultrix3 59 | #else 60 | #define ultrix2 61 | #endif 62 | 63 | #if defined(sun) && defined(FD_SETSIZE) 64 | #define sunos4 65 | #else 66 | #define sunos3 67 | #endif 68 | 69 | #if defined(sequent) || defined(news800) 70 | #define LACK_SYS5 71 | #endif 72 | 73 | #if defined(ultrix3) || defined(sunos4) 74 | #define SIGNAL_FN void 75 | #else 76 | /* sequent, ultrix2, 4.3BSD (vax, hp), sunos3 */ 77 | #define SIGNAL_FN int 78 | #endif 79 | 80 | /* Some systems have 'fixed' certain functions which used to be int */ 81 | #if defined(ultrix) || defined(SABER) || defined(hpux) || defined(aiws) || defined(apollo) || defined(__STDC__) 82 | #define VOID_HACK void 83 | #else 84 | #define VOID_HACK int 85 | #endif 86 | 87 | #ifndef NULL 88 | #define NULL 0 89 | #endif /* NULL */ 90 | 91 | /* 92 | * CHARBITS should be defined only if the compiler lacks "unsigned char". 93 | * It should be a mask, e.g. 0377 for an 8-bit machine. 94 | */ 95 | 96 | #ifndef CHARBITS 97 | # define UNSCHAR(c) ((unsigned char)(c)) 98 | #else 99 | # define UNSCHAR(c) ((c)&CHARBITS) 100 | #endif 101 | 102 | #define SIZET int 103 | 104 | #ifdef __STDC__ 105 | #define CONST const 106 | #define VOIDSTAR void * 107 | #else 108 | #define CONST 109 | #define VOIDSTAR char * 110 | #endif /* __STDC__ */ 111 | 112 | 113 | /* Some machines fail to define some functions in stdio.h */ 114 | #ifndef __STDC__ 115 | extern FILE *popen(), *tmpfile(); 116 | extern int pclose(); 117 | #ifndef clearerr /* is a macro on many machines, but not all */ 118 | extern VOID_HACK clearerr(); 119 | #endif /* clearerr */ 120 | #ifndef rewind 121 | extern VOID_HACK rewind(); 122 | #endif /* rewind */ 123 | #endif /* __STDC__ */ 124 | 125 | 126 | /* most machines don't give us a header file for these */ 127 | #ifdef __STDC__ 128 | #include 129 | #else 130 | #ifdef hpux 131 | extern int abort(); 132 | extern void free(), exit(), perror(); 133 | #else 134 | extern VOID_HACK abort(), free(), exit(), perror(); 135 | #endif /* hpux */ 136 | extern char *getenv(), *malloc(), *realloc(), *calloc(); 137 | #ifdef aiws 138 | extern int sprintf(); 139 | #else 140 | extern char *sprintf(); 141 | #endif 142 | extern int system(); 143 | extern double atof(); 144 | extern int sscanf(); 145 | #endif /* __STDC__ */ 146 | 147 | 148 | /* some call it strings.h, some call it string.h; others, also have memory.h */ 149 | #ifdef __STDC__ 150 | #include 151 | #else 152 | /* ANSI C string.h -- 1/11/88 Draft Standard */ 153 | extern char *strcpy(), *strncpy(), *strcat(), *strncat(), *strerror(); 154 | extern char *strpbrk(), *strtok(), *strchr(), *strrchr(), *strstr(); 155 | extern int strcoll(), strxfrm(), strncmp(), strlen(), strspn(), strcspn(); 156 | extern char *memmove(), *memccpy(), *memchr(), *memcpy(), *memset(); 157 | extern int memcmp(), strcmp(); 158 | #endif /* __STDC__ */ 159 | 160 | /* assertion macro */ 161 | 162 | #ifndef assert 163 | #ifdef __STDC__ 164 | #include 165 | #else 166 | #ifndef NDEBUG 167 | #define assert(ex) {\ 168 | if (! (ex)) {\ 169 | (void) fprintf(stderr, "Assertion failed: file %s, line %d\n",\ 170 | __FILE__, __LINE__);\ 171 | (void) fflush(stdout);\ 172 | abort();\ 173 | }\ 174 | } 175 | #else 176 | #define assert(ex) {;} 177 | #endif 178 | #endif 179 | #endif 180 | 181 | /* handle the various limits */ 182 | #if defined(__STDC__) || defined(POSIX) 183 | #include 184 | #else 185 | #define USHRT_MAX (~ (unsigned short int) 0) 186 | #define UINT_MAX (~ (unsigned int) 0) 187 | #define ULONG_MAX (~ (unsigned long int) 0) 188 | #define SHRT_MAX ((short int) (USHRT_MAX >> 1)) 189 | #define INT_MAX ((int) (UINT_MAX >> 1)) 190 | #define LONG_MAX ((long int) (ULONG_MAX >> 1)) 191 | #endif 192 | 193 | #endif /* PORT_H */ 194 | 195 | -------------------------------------------------------------------------------- /espresso-src/primes.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | static bool primes_consensus_special_cases(pset *T, pset_family *Tnew); 4 | static pcover primes_consensus_merge(pset_family Tl, pset_family Tr, pset cl, pset cr); 5 | static pcover and_with_cofactor(pset_family A, register pset cof); 6 | 7 | 8 | /* primes_consensus -- generate primes using consensus */ 9 | pcover primes_consensus(pset *T) 10 | /* T will be disposed of */ 11 | { 12 | register pcube cl, cr; 13 | register int best; 14 | pcover Tnew, Tl, Tr; 15 | 16 | if (primes_consensus_special_cases(T, &Tnew) == MAYBE) { 17 | cl = new_cube(); 18 | cr = new_cube(); 19 | best = binate_split_select(T, cl, cr, COMPL); 20 | 21 | Tl = primes_consensus(scofactor(T, cl, best)); 22 | Tr = primes_consensus(scofactor(T, cr, best)); 23 | Tnew = primes_consensus_merge(Tl, Tr, cl, cr); 24 | 25 | free_cube(cl); 26 | free_cube(cr); 27 | free_cubelist(T); 28 | } 29 | 30 | return Tnew; 31 | } 32 | 33 | static bool 34 | primes_consensus_special_cases(pset *T, pset_family *Tnew) 35 | /* will be disposed if answer is determined */ 36 | /* returned only if answer determined */ 37 | { 38 | register pcube *T1, p, ceil, cof=T[0]; 39 | pcube last; 40 | pcover A; 41 | 42 | /* Check for no cubes in the cover */ 43 | if (T[2] == NULL) { 44 | *Tnew = new_cover(0); 45 | free_cubelist(T); 46 | return TRUE; 47 | } 48 | 49 | /* Check for only a single cube in the cover */ 50 | if (T[3] == NULL) { 51 | *Tnew = sf_addset(new_cover(1), set_or(cof, cof, T[2])); 52 | free_cubelist(T); 53 | return TRUE; 54 | } 55 | 56 | /* Check for a row of all 1's (implies function is a tautology) */ 57 | for(T1 = T+2; (p = *T1++) != NULL; ) { 58 | if (full_row(p, cof)) { 59 | *Tnew = sf_addset(new_cover(1), cube.fullset); 60 | free_cubelist(T); 61 | return TRUE; 62 | } 63 | } 64 | 65 | /* Check for a column of all 0's which can be factored out */ 66 | ceil = set_save(cof); 67 | for(T1 = T+2; (p = *T1++) != NULL; ) { 68 | INLINEset_or(ceil, ceil, p); 69 | } 70 | if (! setp_equal(ceil, cube.fullset)) { 71 | p = new_cube(); 72 | (void) set_diff(p, cube.fullset, ceil); 73 | (void) set_or(cof, cof, p); 74 | free_cube(p); 75 | 76 | A = primes_consensus(T); 77 | foreach_set(A, last, p) { 78 | INLINEset_and(p, p, ceil); 79 | } 80 | *Tnew = A; 81 | set_free(ceil); 82 | return TRUE; 83 | } 84 | set_free(ceil); 85 | 86 | /* Collect column counts, determine unate variables, etc. */ 87 | massive_count(T); 88 | 89 | /* If single active variable not factored out above, then tautology ! */ 90 | if (cdata.vars_active == 1) { 91 | *Tnew = sf_addset(new_cover(1), cube.fullset); 92 | free_cubelist(T); 93 | return TRUE; 94 | 95 | /* Check for unate cover */ 96 | } else if (cdata.vars_unate == cdata.vars_active) { 97 | A = cubeunlist(T); 98 | *Tnew = sf_contain(A); 99 | free_cubelist(T); 100 | return TRUE; 101 | 102 | /* Not much we can do about it */ 103 | } else { 104 | return MAYBE; 105 | } 106 | } 107 | 108 | static pcover 109 | primes_consensus_merge(pset_family Tl, pset_family Tr, pset cl, pset cr) 110 | { 111 | register pcube pl, pr, lastl, lastr, pt; 112 | pcover T, Tsave; 113 | 114 | Tl = and_with_cofactor(Tl, cl); 115 | Tr = and_with_cofactor(Tr, cr); 116 | 117 | T = sf_new(500, Tl->sf_size); 118 | pt = T->data; 119 | Tsave = sf_contain(sf_join(Tl, Tr)); 120 | 121 | foreach_set(Tl, lastl, pl) { 122 | foreach_set(Tr, lastr, pr) { 123 | if (cdist01(pl, pr) == 1) { 124 | consensus(pt, pl, pr); 125 | if (++T->count >= T->capacity) { 126 | Tsave = sf_union(Tsave, sf_contain(T)); 127 | T = sf_new(500, Tl->sf_size); 128 | pt = T->data; 129 | } else { 130 | pt += T->wsize; 131 | } 132 | } 133 | } 134 | } 135 | free_cover(Tl); 136 | free_cover(Tr); 137 | 138 | Tsave = sf_union(Tsave, sf_contain(T)); 139 | return Tsave; 140 | } 141 | 142 | 143 | static pcover 144 | and_with_cofactor(pset_family A, register pset cof) 145 | { 146 | register pset last, p; 147 | 148 | foreach_set(A, last, p) { 149 | INLINEset_and(p, p, cof); 150 | if (cdist(p, cube.fullset) > 0) { 151 | RESET(p, ACTIVE); 152 | } else { 153 | SET(p, ACTIVE); 154 | } 155 | } 156 | return sf_inactive(A); 157 | } 158 | -------------------------------------------------------------------------------- /espresso-src/prtime.c: -------------------------------------------------------------------------------- 1 | /* LINTLIBRARY */ 2 | #include "port.h" 3 | #include "utility.h" 4 | 5 | /* 6 | * util_print_time -- massage a long which represents a time interval in 7 | * milliseconds, into a string suitable for output 8 | * 9 | * Hack for IBM/PC -- avoids using floating point 10 | */ 11 | 12 | char * 13 | util_print_time(long int t) 14 | { 15 | static char s[40]; 16 | 17 | (void) sprintf(s, "%ld.%02ld sec", t/1000, (t%1000)/10); 18 | return s; 19 | } 20 | -------------------------------------------------------------------------------- /espresso-src/reduce.c: -------------------------------------------------------------------------------- 1 | /* 2 | module: reduce.c 3 | purpose: Perform the Espresso-II reduction step 4 | 5 | Reduction is a technique used to explore larger regions of the 6 | optimization space. We replace each cube of F with a smaller 7 | cube while still maintaining a cover of the same logic function. 8 | */ 9 | 10 | #include "espresso.h" 11 | 12 | static bool toggle = TRUE; 13 | 14 | 15 | /* 16 | reduce -- replace each cube in F with its reduction 17 | 18 | The reduction of a cube is the smallest cube contained in the cube 19 | which can replace the cube in the original cover without changing 20 | the cover. This is equivalent to the super cube of all of the 21 | essential points in the cube. This can be computed directly. 22 | 23 | The problem is that the order in which the cubes are reduced can 24 | greatly affect the final result. We alternate between two ordering 25 | strategies: 26 | 27 | (1) Order the cubes in ascending order of distance from the 28 | largest cube breaking ties by ordering cubes of equal distance 29 | in descending order of size (sort_reduce) 30 | 31 | (2) Order the cubes in descending order of the inner-product of 32 | the cube and the column sums (mini_sort) 33 | 34 | The real workhorse of this section is the routine SCCC which is 35 | used to find the Smallest Cube Containing the Complement of a cover. 36 | Reduction as proposed by Espresso-II takes a cube and computes its 37 | maximal reduction as the intersection between the cube and the 38 | smallest cube containing the complement of (F u D - {c}) cofactored 39 | against c. 40 | 41 | As usual, the unate-recursive paradigm is used to compute SCCC. 42 | The SCCC of a unate cover is trivial to compute, and thus we perform 43 | Shannon Cofactor expansion attempting to drive the cover to be unate 44 | as fast as possible. 45 | */ 46 | 47 | pcover reduce(pset_family F, pset_family D) 48 | { 49 | register pcube last, p, cunder, *FD; 50 | 51 | /* Order the cubes */ 52 | if (use_random_order) 53 | F = random_order(F); 54 | else { 55 | F = toggle ? sort_reduce(F) : mini_sort(F, (qsort_compare_func) descend); 56 | toggle = ! toggle; 57 | } 58 | 59 | /* Try to reduce each cube */ 60 | FD = cube2list(F, D); 61 | foreach_set(F, last, p) { 62 | cunder = reduce_cube(FD, p); /* reduce the cube */ 63 | if (setp_equal(cunder, p)) { /* see if it actually did */ 64 | SET(p, ACTIVE); /* cube remains active */ 65 | SET(p, PRIME); /* cube remains prime ? */ 66 | } else { 67 | if (debug & REDUCE) { 68 | printf("REDUCE: %s to %s %s\n", 69 | pc1(p), pc2(cunder), print_time(ptime())); 70 | } 71 | set_copy(p, cunder); /* save reduced version */ 72 | RESET(p, PRIME); /* cube is no longer prime */ 73 | if (setp_empty(cunder)) 74 | RESET(p, ACTIVE); /* if null, kill the cube */ 75 | else 76 | SET(p, ACTIVE); /* cube is active */ 77 | } 78 | free_cube(cunder); 79 | } 80 | free_cubelist(FD); 81 | 82 | /* Delete any cubes of F which reduced to the empty cube */ 83 | return sf_inactive(F); 84 | } 85 | 86 | /* reduce_cube -- find the maximal reduction of a cube */ 87 | pcube reduce_cube(pset *FD, pset p) 88 | { 89 | pcube cunder; 90 | 91 | cunder = sccc(cofactor(FD, p)); 92 | return set_and(cunder, cunder, p); 93 | } 94 | 95 | 96 | /* sccc -- find Smallest Cube Containing the Complement of a cover */ 97 | pcube sccc(pset *T) 98 | /* T will be disposed of */ 99 | { 100 | pcube r; 101 | register pcube cl, cr; 102 | register int best; 103 | static int sccc_level = 0; 104 | 105 | if (debug & REDUCE1) { 106 | debug_print(T, "SCCC", sccc_level++); 107 | } 108 | 109 | if (sccc_special_cases(T, &r) == MAYBE) { 110 | cl = new_cube(); 111 | cr = new_cube(); 112 | best = binate_split_select(T, cl, cr, REDUCE1); 113 | r = sccc_merge(sccc(scofactor(T, cl, best)), 114 | sccc(scofactor(T, cr, best)), cl, cr); 115 | free_cubelist(T); 116 | } 117 | 118 | if (debug & REDUCE1) 119 | printf("SCCC[%d]: result is %s\n", --sccc_level, pc1(r)); 120 | return r; 121 | } 122 | 123 | 124 | pcube sccc_merge(register pset left, register pset right, register pset cl, register pset cr) 125 | /* will be disposed of ... */ 126 | /* will be disposed of ... */ 127 | { 128 | INLINEset_and(left, left, cl); 129 | INLINEset_and(right, right, cr); 130 | INLINEset_or(left, left, right); 131 | free_cube(right); 132 | free_cube(cl); 133 | free_cube(cr); 134 | return left; 135 | } 136 | 137 | 138 | /* 139 | sccc_cube -- find the smallest cube containing the complement of a cube 140 | 141 | By DeMorgan's law and the fact that the smallest cube containing a 142 | cover is the "or" of the positional cubes, it is simple to see that 143 | the SCCC is the universe if the cube has more than two active 144 | variables. If there is only a single active variable, then the 145 | SCCC is merely the bitwise complement of the cube in that 146 | variable. A last special case is no active variables, in which 147 | case the SCCC is empty. 148 | 149 | This is "anded" with the incoming cube result. 150 | */ 151 | pcube sccc_cube(register pset result, register pset p) 152 | { 153 | register pcube temp=cube.temp[0], mask; 154 | int var; 155 | 156 | if ((var = cactive(p)) >= 0) { 157 | mask = cube.var_mask[var]; 158 | INLINEset_xor(temp, p, mask); 159 | INLINEset_and(result, result, temp); 160 | } 161 | return result; 162 | } 163 | 164 | /* 165 | * sccc_special_cases -- check the special cases for sccc 166 | */ 167 | 168 | bool sccc_special_cases(pset *T, pset *result) 169 | /* will be disposed if answer is determined */ 170 | /* returned only if answer determined */ 171 | { 172 | register pcube *T1, p, temp = cube.temp[1], ceil, cof = T[0]; 173 | pcube *A, *B; 174 | 175 | /* empty cover => complement is universe => SCCC is universe */ 176 | if (T[2] == NULL) { 177 | *result = set_save(cube.fullset); 178 | free_cubelist(T); 179 | return TRUE; 180 | } 181 | 182 | /* row of 1's => complement is empty => SCCC is empty */ 183 | for(T1 = T+2; (p = *T1++) != NULL; ) { 184 | if (full_row(p, cof)) { 185 | *result = new_cube(); 186 | free_cubelist(T); 187 | return TRUE; 188 | } 189 | } 190 | 191 | /* Collect column counts, determine unate variables, etc. */ 192 | massive_count(T); 193 | 194 | /* If cover is unate (or single cube), apply simple rules to find SCCCU */ 195 | if (cdata.vars_unate == cdata.vars_active || T[3] == NULL) { 196 | *result = set_save(cube.fullset); 197 | for(T1 = T+2; (p = *T1++) != NULL; ) { 198 | (void) sccc_cube(*result, set_or(temp, p, cof)); 199 | } 200 | free_cubelist(T); 201 | return TRUE; 202 | } 203 | 204 | /* Check for column of 0's (which can be easily factored( */ 205 | ceil = set_save(cof); 206 | for(T1 = T+2; (p = *T1++) != NULL; ) { 207 | INLINEset_or(ceil, ceil, p); 208 | } 209 | if (! setp_equal(ceil, cube.fullset)) { 210 | *result = sccc_cube(set_save(cube.fullset), ceil); 211 | if (setp_equal(*result, cube.fullset)) { 212 | free_cube(ceil); 213 | } else { 214 | *result = sccc_merge(sccc(cofactor(T,ceil)), 215 | set_save(cube.fullset), ceil, *result); 216 | } 217 | free_cubelist(T); 218 | return TRUE; 219 | } 220 | free_cube(ceil); 221 | 222 | /* Single active column at this point => tautology => SCCC is empty */ 223 | if (cdata.vars_active == 1) { 224 | *result = new_cube(); 225 | free_cubelist(T); 226 | return TRUE; 227 | } 228 | 229 | /* Check for components */ 230 | if (cdata.var_zeros[cdata.best] < CUBELISTSIZE(T)/2) { 231 | if (cubelist_partition(T, &A, &B, debug & REDUCE1) == 0) { 232 | return MAYBE; 233 | } else { 234 | free_cubelist(T); 235 | *result = sccc(A); 236 | ceil = sccc(B); 237 | (void) set_and(*result, *result, ceil); 238 | set_free(ceil); 239 | return TRUE; 240 | } 241 | } 242 | 243 | /* Not much we can do about it */ 244 | return MAYBE; 245 | } 246 | -------------------------------------------------------------------------------- /espresso-src/rows.c: -------------------------------------------------------------------------------- 1 | #include "port.h" 2 | #include "sparse_int.h" 3 | 4 | 5 | /* 6 | * allocate a new row vector 7 | */ 8 | sm_row * 9 | sm_row_alloc(void) 10 | { 11 | register sm_row *prow; 12 | 13 | #ifdef FAST_AND_LOOSE 14 | if (sm_row_freelist == NIL(sm_row)) { 15 | prow = ALLOC(sm_row, 1); 16 | } else { 17 | prow = sm_row_freelist; 18 | sm_row_freelist = prow->next_row; 19 | } 20 | #else 21 | prow = ALLOC(sm_row, 1); 22 | #endif 23 | 24 | prow->row_num = 0; 25 | prow->length = 0; 26 | prow->first_col = prow->last_col = NIL(sm_element); 27 | prow->next_row = prow->prev_row = NIL(sm_row); 28 | prow->flag = 0; 29 | prow->user_word = NIL(char); /* for our user ... */ 30 | return prow; 31 | } 32 | 33 | 34 | /* 35 | * free a row vector -- for FAST_AND_LOOSE, this is real cheap for rows; 36 | * however, freeing a column must still walk down the column discarding 37 | * the elements one-by-one; that is the only use for the extra '-DCOLS' 38 | * compile flag ... 39 | */ 40 | void 41 | sm_row_free(register sm_row *prow) 42 | { 43 | #if defined(FAST_AND_LOOSE) && ! defined(COLS) 44 | if (prow->first_col != NIL(sm_element)) { 45 | /* Add the linked list of row items to the free list */ 46 | prow->last_col->next_col = sm_element_freelist; 47 | sm_element_freelist = prow->first_col; 48 | } 49 | 50 | /* Add the row to the free list of rows */ 51 | prow->next_row = sm_row_freelist; 52 | sm_row_freelist = prow; 53 | #else 54 | register sm_element *p, *pnext; 55 | 56 | for(p = prow->first_col; p != 0; p = pnext) { 57 | pnext = p->next_col; 58 | sm_element_free(p); 59 | } 60 | FREE(prow); 61 | #endif 62 | } 63 | 64 | 65 | /* 66 | * duplicate an existing row 67 | */ 68 | sm_row * 69 | sm_row_dup(register sm_row *prow) 70 | { 71 | register sm_row *pnew; 72 | register sm_element *p; 73 | 74 | pnew = sm_row_alloc(); 75 | for(p = prow->first_col; p != 0; p = p->next_col) { 76 | (void) sm_row_insert(pnew, p->col_num); 77 | } 78 | return pnew; 79 | } 80 | 81 | 82 | /* 83 | * insert an element into a row vector 84 | */ 85 | sm_element * 86 | sm_row_insert(register sm_row *prow, register int col) 87 | { 88 | register sm_element *test, *element; 89 | 90 | /* get a new item, save its address */ 91 | sm_element_alloc(element); 92 | test = element; 93 | sorted_insert(sm_element, prow->first_col, prow->last_col, prow->length, 94 | next_col, prev_col, col_num, col, test); 95 | 96 | /* if item was not used, free it */ 97 | if (element != test) { 98 | sm_element_free(element); 99 | } 100 | 101 | /* either way, return the current new value */ 102 | return test; 103 | } 104 | 105 | 106 | /* 107 | * remove an element from a row vector 108 | */ 109 | void 110 | sm_row_remove(register sm_row *prow, register int col) 111 | { 112 | register sm_element *p; 113 | 114 | for(p = prow->first_col; p != 0 && p->col_num < col; p = p->next_col) 115 | ; 116 | if (p != 0 && p->col_num == col) { 117 | dll_unlink(p, prow->first_col, prow->last_col, 118 | next_col, prev_col, prow->length); 119 | sm_element_free(p); 120 | } 121 | } 122 | 123 | 124 | /* 125 | * find an element (if it is in the row vector) 126 | */ 127 | sm_element * 128 | sm_row_find(sm_row *prow, int col) 129 | { 130 | register sm_element *p; 131 | 132 | for(p = prow->first_col; p != 0 && p->col_num < col; p = p->next_col) 133 | ; 134 | if (p != 0 && p->col_num == col) { 135 | return p; 136 | } else { 137 | return NIL(sm_element); 138 | } 139 | } 140 | 141 | /* 142 | * return 1 if row p2 contains row p1; 0 otherwise 143 | */ 144 | int 145 | sm_row_contains(sm_row *p1, sm_row *p2) 146 | { 147 | register sm_element *q1, *q2; 148 | 149 | q1 = p1->first_col; 150 | q2 = p2->first_col; 151 | while (q1 != 0) { 152 | if (q2 == 0 || q1->col_num < q2->col_num) { 153 | return 0; 154 | } else if (q1->col_num == q2->col_num) { 155 | q1 = q1->next_col; 156 | q2 = q2->next_col; 157 | } else { 158 | q2 = q2->next_col; 159 | } 160 | } 161 | return 1; 162 | } 163 | 164 | 165 | /* 166 | * return 1 if row p1 and row p2 share an element in common 167 | */ 168 | int 169 | sm_row_intersects(sm_row *p1, sm_row *p2) 170 | { 171 | register sm_element *q1, *q2; 172 | 173 | q1 = p1->first_col; 174 | q2 = p2->first_col; 175 | if (q1 == 0 || q2 == 0) return 0; 176 | for(;;) { 177 | if (q1->col_num < q2->col_num) { 178 | if ((q1 = q1->next_col) == 0) { 179 | return 0; 180 | } 181 | } else if (q1->col_num > q2->col_num) { 182 | if ((q2 = q2->next_col) == 0) { 183 | return 0; 184 | } 185 | } else { 186 | return 1; 187 | } 188 | } 189 | } 190 | 191 | 192 | /* 193 | * compare two rows, lexical ordering 194 | */ 195 | int 196 | sm_row_compare(sm_row *p1, sm_row *p2) 197 | { 198 | register sm_element *q1, *q2; 199 | 200 | q1 = p1->first_col; 201 | q2 = p2->first_col; 202 | while(q1 != 0 && q2 != 0) { 203 | if (q1->col_num != q2->col_num) { 204 | return q1->col_num - q2->col_num; 205 | } 206 | q1 = q1->next_col; 207 | q2 = q2->next_col; 208 | } 209 | 210 | if (q1 != 0) { 211 | return 1; 212 | } else if (q2 != 0) { 213 | return -1; 214 | } else { 215 | return 0; 216 | } 217 | } 218 | 219 | 220 | /* 221 | * return the intersection 222 | */ 223 | sm_row * 224 | sm_row_and(sm_row *p1, sm_row *p2) 225 | { 226 | register sm_element *q1, *q2; 227 | register sm_row *result; 228 | 229 | result = sm_row_alloc(); 230 | q1 = p1->first_col; 231 | q2 = p2->first_col; 232 | if (q1 == 0 || q2 == 0) return result; 233 | for(;;) { 234 | if (q1->col_num < q2->col_num) { 235 | if ((q1 = q1->next_col) == 0) { 236 | return result; 237 | } 238 | } else if (q1->col_num > q2->col_num) { 239 | if ((q2 = q2->next_col) == 0) { 240 | return result; 241 | } 242 | } else { 243 | (void) sm_row_insert(result, q1->col_num); 244 | if ((q1 = q1->next_col) == 0) { 245 | return result; 246 | } 247 | if ((q2 = q2->next_col) == 0) { 248 | return result; 249 | } 250 | } 251 | } 252 | } 253 | 254 | int 255 | sm_row_hash(sm_row *prow, int modulus) 256 | { 257 | register int sum; 258 | register sm_element *p; 259 | 260 | sum = 0; 261 | for(p = prow->first_col; p != 0; p = p->next_col) { 262 | sum = (sum*17 + p->col_num) % modulus; 263 | } 264 | return sum; 265 | } 266 | 267 | /* 268 | * remove an element from a row vector (given a pointer to the element) 269 | */ 270 | void 271 | sm_row_remove_element(register sm_row *prow, register sm_element *p) 272 | { 273 | dll_unlink(p, prow->first_col, prow->last_col, 274 | next_col, prev_col, prow->length); 275 | sm_element_free(p); 276 | } 277 | 278 | 279 | void 280 | sm_row_print(FILE *fp, sm_row *prow) 281 | { 282 | sm_element *p; 283 | 284 | for(p = prow->first_col; p != 0; p = p->next_col) { 285 | (void) fprintf(fp, " %d", p->col_num); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /espresso-src/sharp.c: -------------------------------------------------------------------------------- 1 | /* 2 | sharp.c -- perform sharp, disjoint sharp, and intersection 3 | */ 4 | 5 | #include "espresso.h" 6 | 7 | long start_time; 8 | 9 | 10 | /* cv_sharp -- form the sharp product between two covers */ 11 | pcover cv_sharp(pset_family A, pset_family B) 12 | { 13 | pcube last, p; 14 | pcover T; 15 | 16 | T = new_cover(0); 17 | foreach_set(A, last, p) 18 | T = sf_union(T, cb_sharp(p, B)); 19 | return T; 20 | } 21 | 22 | 23 | /* cb_sharp -- form the sharp product between a cube and a cover */ 24 | pcover cb_sharp(pset c, pset_family T) 25 | { 26 | if (T->count == 0) { 27 | T = sf_addset(new_cover(1), c); 28 | } else { 29 | start_time = ptime(); 30 | T = cb_recur_sharp(c, T, 0, T->count-1, 0); 31 | } 32 | return T; 33 | } 34 | 35 | 36 | /* recursive formulation to provide balanced merging */ 37 | pcover cb_recur_sharp(pset c, pset_family T, int first, int last, int level) 38 | { 39 | pcover temp, left, right; 40 | int middle; 41 | 42 | if (first == last) { 43 | temp = sharp(c, GETSET(T, first)); 44 | } else { 45 | middle = (first + last) / 2; 46 | left = cb_recur_sharp(c, T, first, middle, level+1); 47 | right = cb_recur_sharp(c, T, middle+1, last, level+1); 48 | temp = cv_intersect(left, right); 49 | if ((debug & SHARP) && level < 4) { 50 | printf("# SHARP[%d]: %4d = %4d x %4d, time = %s\n", 51 | level, temp->count, left->count, right->count, 52 | print_time(ptime() - start_time)); 53 | (void) fflush(stdout); 54 | } 55 | free_cover(left); 56 | free_cover(right); 57 | } 58 | return temp; 59 | } 60 | 61 | 62 | /* sharp -- form the sharp product between two cubes */ 63 | pcover sharp(pset a, pset b) 64 | { 65 | register int var; 66 | register pcube d=cube.temp[0], temp=cube.temp[1], temp1=cube.temp[2]; 67 | pcover r = new_cover(cube.num_vars); 68 | 69 | if (cdist0(a, b)) { 70 | set_diff(d, a, b); 71 | for(var = 0; var < cube.num_vars; var++) { 72 | if (! setp_empty(set_and(temp, d, cube.var_mask[var]))) { 73 | set_diff(temp1, a, cube.var_mask[var]); 74 | set_or(GETSET(r, r->count++), temp, temp1); 75 | } 76 | } 77 | } else { 78 | r = sf_addset(r, a); 79 | } 80 | return r; 81 | } 82 | 83 | pcover make_disjoint(pset_family A) 84 | { 85 | pcover R, new; 86 | register pset last, p; 87 | 88 | R = new_cover(0); 89 | foreach_set(A, last, p) { 90 | new = cb_dsharp(p, R); 91 | R = sf_append(R, new); 92 | } 93 | return R; 94 | } 95 | 96 | 97 | /* cv_dsharp -- disjoint-sharp product between two covers */ 98 | pcover cv_dsharp(pset_family A, pset_family B) 99 | { 100 | register pcube last, p; 101 | pcover T; 102 | 103 | T = new_cover(0); 104 | foreach_set(A, last, p) { 105 | T = sf_union(T, cb_dsharp(p, B)); 106 | } 107 | return T; 108 | } 109 | 110 | 111 | /* cb1_dsharp -- disjoint-sharp product between a cover and a cube */ 112 | pcover cb1_dsharp(pset_family T, pset c) 113 | { 114 | pcube last, p; 115 | pcover R; 116 | 117 | R = new_cover(T->count); 118 | foreach_set(T, last, p) { 119 | R = sf_union(R, dsharp(p, c)); 120 | } 121 | return R; 122 | } 123 | 124 | 125 | /* cb_dsharp -- disjoint-sharp product between a cube and a cover */ 126 | pcover cb_dsharp(pset c, pset_family T) 127 | { 128 | pcube last, p; 129 | pcover Y, Y1; 130 | 131 | if (T->count == 0) { 132 | Y = sf_addset(new_cover(1), c); 133 | } else { 134 | Y = new_cover(T->count); 135 | set_copy(GETSET(Y,Y->count++), c); 136 | foreach_set(T, last, p) { 137 | Y1 = cb1_dsharp(Y, p); 138 | free_cover(Y); 139 | Y = Y1; 140 | } 141 | } 142 | return Y; 143 | } 144 | 145 | 146 | /* dsharp -- form the disjoint-sharp product between two cubes */ 147 | pcover dsharp(pset a, pset b) 148 | { 149 | register pcube mask, diff, and, temp, temp1 = cube.temp[0]; 150 | int var; 151 | pcover r; 152 | 153 | r = new_cover(cube.num_vars); 154 | 155 | if (cdist0(a, b)) { 156 | diff = set_diff(new_cube(), a, b); 157 | and = set_and(new_cube(), a, b); 158 | mask = new_cube(); 159 | for(var = 0; var < cube.num_vars; var++) { 160 | /* check if position var of "a and not b" is not empty */ 161 | if (! setp_disjoint(diff, cube.var_mask[var])) { 162 | 163 | /* coordinate var equals the difference between a and b */ 164 | temp = GETSET(r, r->count++); 165 | (void) set_and(temp, diff, cube.var_mask[var]); 166 | 167 | /* coordinates 0 ... var-1 equal the intersection */ 168 | INLINEset_and(temp1, and, mask); 169 | INLINEset_or(temp, temp, temp1); 170 | 171 | /* coordinates var+1 .. cube.num_vars equal a */ 172 | set_or(mask, mask, cube.var_mask[var]); 173 | INLINEset_diff(temp1, a, mask); 174 | INLINEset_or(temp, temp, temp1); 175 | } 176 | } 177 | free_cube(diff); 178 | free_cube(and); 179 | free_cube(mask); 180 | } else { 181 | r = sf_addset(r, a); 182 | } 183 | return r; 184 | } 185 | 186 | /* cv_intersect -- form the intersection of two covers */ 187 | 188 | #define MAGIC 500 /* save 500 cubes before containment */ 189 | 190 | pcover cv_intersect(pset_family A, pset_family B) 191 | { 192 | register pcube pi, pj, lasti, lastj, pt; 193 | pcover T, Tsave = NULL; 194 | 195 | /* How large should each temporary result cover be ? */ 196 | T = new_cover(MAGIC); 197 | pt = T->data; 198 | 199 | /* Form pairwise intersection of each cube of A with each cube of B */ 200 | foreach_set(A, lasti, pi) { 201 | foreach_set(B, lastj, pj) { 202 | if (cdist0(pi, pj)) { 203 | (void) set_and(pt, pi, pj); 204 | if (++T->count >= T->capacity) { 205 | if (Tsave == NULL) 206 | Tsave = sf_contain(T); 207 | else 208 | Tsave = sf_union(Tsave, sf_contain(T)); 209 | T = new_cover(MAGIC); 210 | pt = T->data; 211 | } else 212 | pt += T->wsize; 213 | } 214 | } 215 | } 216 | 217 | 218 | if (Tsave == NULL) 219 | Tsave = sf_contain(T); 220 | else 221 | Tsave = sf_union(Tsave, sf_contain(T)); 222 | return Tsave; 223 | } 224 | -------------------------------------------------------------------------------- /espresso-src/sigma.c: -------------------------------------------------------------------------------- 1 | /* Module:sigma.c 2 | * Purpose: 3 | * Contains routines for computing the signature cube of the given cube. 4 | * Routines: 5 | * pcube get_sigma(): 6 | * Computes signature cube of the given cube d. 7 | * void set_not(): 8 | */ 9 | 10 | #include 11 | #include "espresso.h" 12 | #include "signature.h" 13 | 14 | /* 15 | * get_sigma: 16 | * Objective: computes signature cube corresponding to the cube c 17 | * Input: 18 | * R: OFFSET cover; 19 | * c: ONSET cube; 20 | * Output: 21 | * signature cube 22 | */ 23 | pcube 24 | get_sigma(pset_family R, register pset c) 25 | { 26 | pcover BB; 27 | pcube out_part_r,s; 28 | register pcube r,b; 29 | register int i; 30 | register int w, last; 31 | register unsigned int x; 32 | 33 | out_part_r = new_cube(); 34 | s = new_cube(); 35 | 36 | BB = new_cover(R->count); 37 | BB->count = R->count; 38 | /* BB = get_blocking_matrix(R,c); */ 39 | foreachi_set(R,i,r){ 40 | b = GETSET(BB,i); 41 | if ((last = cube.inword) != -1) { 42 | /* Check the partial word of binary variables */ 43 | x = r[last] & c[last]; 44 | x = ~(x | x >> 1) & cube.inmask; 45 | b[last] = r[last] & (x | x << 1); 46 | /* Check the full words of binary variables */ 47 | for(w = 1; w < last; w++) { 48 | x = r[w] & c[w]; 49 | x = ~(x | x >> 1) & DISJOINT; 50 | b[w] = r[w] & (x | x << 1); 51 | } 52 | } 53 | PUTLOOP(b,LOOP(r)); 54 | INLINEset_and(b,b,cube.binary_mask); 55 | INLINEset_and(out_part_r,cube.mv_mask,r); 56 | if(!setp_implies(out_part_r,c)){ 57 | INLINEset_or(b,b,out_part_r); 58 | } 59 | } 60 | free_cube(out_part_r); 61 | BB = unate_compl(BB); 62 | 63 | INLINEset_copy(s,cube.emptyset); 64 | foreachi_set(BB, i, b) { 65 | INLINEset_or(s,s,b); 66 | } 67 | free_cover(BB); 68 | set_not(s); 69 | return s; 70 | } 71 | 72 | 73 | /* set_not: flip 0 to 1 and 1 to 0 */ 74 | void 75 | set_not(pset c) 76 | { 77 | INLINEset_diff(c,cube.fullset,c); 78 | } 79 | 80 | -------------------------------------------------------------------------------- /espresso-src/signature.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Module: signature.c 3 | * Purpose: The main signature algorithm 4 | * Routines; 5 | * pcover signature(): 6 | * Entry point for the signature cubes algorithm. 7 | * pcover generate_primes(): 8 | * Generates the set of primes corresponding to 9 | * the Essential Signature Cubes 10 | */ 11 | 12 | #include 13 | #include 14 | #include "espresso.h" 15 | #include "signature.h" 16 | 17 | static long start_time; /* yuk */ 18 | 19 | pcover 20 | signature(pset_family F1, pset_family D1, pset_family R1) 21 | { 22 | pcover ESC,ESSet,ESSENTIAL; 23 | pcover F,D,R; 24 | pcube last,p; 25 | 26 | /* make scratch copy */ 27 | F = sf_save(F1); 28 | D = sf_save(D1); 29 | R = sf_save(R1); 30 | 31 | /* unwrap offset */ 32 | R = unravel(R, cube.num_binary_vars); 33 | R = sf_contain(R); 34 | 35 | start_time = ptime(); 36 | 37 | /* Initial expand and irredundant */ 38 | foreach_set(F, last, p) { 39 | RESET(p, PRIME); 40 | } 41 | 42 | S_EXECUTE(F = expand(F, R, FALSE), ESSEN_TIME); 43 | S_EXECUTE(F = irredundant(F, D), ESSEN_TIME); 44 | S_EXECUTE(ESSENTIAL = essential(&F,&D), ESSEN_TIME); 45 | 46 | S_EXECUTE(ESC = find_canonical_cover(F,D,R), FCC_TIME); 47 | /************************************************** 48 | printf("ESCubes %d\n", ESC->count + ESSENTIAL->count); 49 | fflush(stdout); 50 | **************************************************/ 51 | 52 | S_EXECUTE(ESSet = generate_primes(ESC,R), PRIMES_TIME); 53 | /************************************************** 54 | printf("ESSet %d\n",ESSet->count + ESSENTIAL->count); 55 | fflush(stdout); 56 | **************************************************/ 57 | 58 | S_EXECUTE(F = signature_minimize_exact(ESC,ESSet), MINCOV_TIME); 59 | sf_append(F,ESSENTIAL); 60 | /************************************************** 61 | printf("Exact_Minimum %d\n",F->count); 62 | print_cover(F,"Exact Minimum"); 63 | **************************************************/ 64 | 65 | if (! skip_make_sparse && R != 0) { 66 | F = make_sparse(F, D1, R); 67 | } 68 | 69 | free_cover(D); 70 | free_cover(R); 71 | free_cover(ESC); 72 | free_cover(ESSet); 73 | return F; 74 | } 75 | 76 | 77 | pcover 78 | generate_primes(pset_family F, pset_family R) 79 | { 80 | pcube c,r,lastc,b,lastb; 81 | pcover BB,PRIMES; 82 | pcube odd,even,out_part_r; 83 | register int i; 84 | register int w, last; 85 | register unsigned int x; 86 | int count; 87 | 88 | out_part_r = new_cube(); 89 | odd = new_cube(); 90 | even = new_cube(); 91 | 92 | count = 0; 93 | PRIMES = new_cover(F->count); 94 | foreach_set(F,lastc,c){ 95 | BB = new_cover(R->count); 96 | BB->count = R->count; 97 | /* BB = get_blocking_matrix(R,c); */ 98 | foreachi_set(R,i,r){ 99 | b = GETSET(BB,i); 100 | if ((last = cube.inword) != -1) { 101 | /* Check the partial word of binary variables */ 102 | x = r[last] & c[last]; 103 | x = ~(x | x >> 1) & cube.inmask; 104 | b[last] = r[last] & (x | x << 1); 105 | /* Check the full words of binary variables */ 106 | for(w = 1; w < last; w++) { 107 | x = r[w] & c[w]; 108 | x = ~(x | x >> 1) & DISJOINT; 109 | b[w] = r[w] & (x | x << 1); 110 | } 111 | } 112 | PUTLOOP(b,LOOP(r)); 113 | INLINEset_and(b,b,cube.binary_mask); 114 | INLINEset_and(out_part_r,cube.mv_mask,r); 115 | if(!setp_implies(out_part_r,c)){ 116 | INLINEset_or(b,b,out_part_r); 117 | } 118 | } 119 | BB = unate_compl(BB); 120 | if(BB != NULL){ 121 | foreach_set(BB,lastb,b){ 122 | set_not(b); 123 | } 124 | sf_append(PRIMES,BB); 125 | } 126 | count++; 127 | if(count % 100 == 0){ 128 | PRIMES = sf_contain(PRIMES); 129 | } 130 | } 131 | PRIMES = sf_contain(PRIMES); 132 | free_cube(out_part_r); 133 | free_cube(odd); 134 | free_cube(even); 135 | return PRIMES; 136 | } 137 | 138 | void 139 | cleanup(void) 140 | { 141 | s_runtime(ptime() - start_time); 142 | printf("CPU Limit Exceeded\n"); 143 | exit(1); 144 | } 145 | -------------------------------------------------------------------------------- /espresso-src/signature.h: -------------------------------------------------------------------------------- 1 | #if BPI == 16 2 | #define ODD_MASK 0xaaaa 3 | #define EVEN_MASK 0x5555 4 | #else 5 | #define ODD_MASK 0xaaaaaaaa 6 | #define EVEN_MASK 0x55555555 7 | #endif 8 | 9 | #define POSITIVE 1 10 | #define NEGATIVE 0 11 | 12 | #define PRESENT 1 13 | #define ABSENT 0 14 | 15 | #define RAISED 2 16 | 17 | typedef struct { 18 | int variable; 19 | int free_count; 20 | } VAR; 21 | 22 | /* black_white.c */ extern void setup_bw (pset_family R, pset c); 23 | /* black_white.c */ extern void free_bw (void); 24 | /* black_white.c */ extern int black_white (void); 25 | /* black_white.c */ extern void split_list (pset_family R, int v); 26 | /* black_white.c */ extern void merge_list (void); 27 | /* black_white.c */ extern void print_bw (int size); 28 | /* black_white.c */ extern void variable_list_alloc (int size); 29 | /* black_white.c */ extern void variable_list_init (int reduced_c_free_count, int *reduced_c_free_list); 30 | /* black_white.c */ extern void variable_list_delete (int element); 31 | /* black_white.c */ extern void variable_list_insert (int element); 32 | /* black_white.c */ extern int variable_list_empty (void); 33 | /* black_white.c */ extern void get_next_variable (int *pv, int *pphase, pset_family R); 34 | /* black_white.c */ extern void print_variable_list (void); 35 | /* black_white.c */ extern void reset_black_list (void); 36 | /* black_white.c */ extern void push_black_list (void); 37 | /* black_white.c */ extern void pop_black_list (void); 38 | /* canonical.c */ extern pset_family find_canonical_cover (pset_family F1, pset_family D, pset_family R); 39 | /* essentiality.c */ extern pset_family etr_order (pset_family F, pset_family E, pset_family R, pset c, pset d); 40 | /* essentiality.c */ extern void aux_etr_order (pset_family F, pset_family E, pset_family R, pset c, pset d); 41 | /* essentiality.c */ extern pset_family get_mins (pset c); 42 | /* essentiality.c */ extern int ascending (VAR *p1, VAR *p2); 43 | /* util_signature.c */ extern void set_time_limit (int seconds); 44 | /* util_signature.c */ extern void print_cover (pset_family F, char *name); 45 | /* util_signature.c */ extern int sf_equal (pset_family F1, pset_family F2); 46 | /* util_signature.c */ extern int mem_usage (char *name); 47 | /* util_signature.c */ extern int time_usage (char *name); 48 | /* util_signature.c */ extern void s_totals (long time, int i); 49 | /* util_signature.c */ extern void s_runtime (long total); 50 | /* sigma.c */ extern pset get_sigma (pset_family R, register pset c); 51 | /* sigma.c */ extern void set_not (pset c); 52 | /* signature.c */ extern void cleanup (void); 53 | /* signature.c */ extern pset_family signature (pset_family F1, pset_family D1, pset_family R1); 54 | /* signature.c */ extern pset_family generate_primes (pset_family F, pset_family R); 55 | /* signature_exact.c */ extern pset_family signature_minimize_exact (pset_family ESCubes, pset_family ESSet); 56 | /* signature_exact.c */ extern sm_matrix * signature_form_table (pset_family ESCubes, pset_family ESSet); 57 | -------------------------------------------------------------------------------- /espresso-src/signature_exact.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | #include "signature.h" 3 | 4 | 5 | /* 6 | * signature_minimize_exact: 7 | * What does it do: forms and solves the covering table whose rows are 8 | * essential signature cubes (ESCubes) and whose columns are 9 | * union of essential signature sets (ESSet) 10 | * Input: 11 | * ESCubes: essential signature cubes 12 | * ESSet: union of essential signature sets 13 | * Output: 14 | * COVER: exact cover 15 | */ 16 | 17 | pcover 18 | signature_minimize_exact(pset_family ESCubes, pset_family ESSet) 19 | { 20 | pcube p; 21 | sm_matrix *table; 22 | sm_row *cover; 23 | sm_element *pe; 24 | pcover COVER; 25 | int index; 26 | int *weights,heur,level; 27 | 28 | /* number ESCubes, ESSet */ 29 | foreachi_set(ESCubes,index,p){ 30 | PUTSIZE(p,index); 31 | } 32 | foreachi_set(ESSet,index,p){ 33 | PUTSIZE(p,index); 34 | } 35 | 36 | /* form the covering table */ 37 | S_EXECUTE(table = signature_form_table(ESCubes, ESSet), MINCOV_TIME); 38 | 39 | /* solve the covering problem */ 40 | weights = NIL(int); heur = FALSE; level = 0; 41 | S_EXECUTE(cover=sm_minimum_cover(table,weights,heur,level), 42 | MINCOV_TIME); 43 | 44 | /* form the cover */ 45 | COVER = new_cover(100); 46 | sm_foreach_row_element(cover, pe) { 47 | COVER = sf_addset(COVER, GETSET(ESSet, pe->col_num)); 48 | } 49 | 50 | sm_free(table); 51 | sm_row_free(cover); 52 | 53 | return COVER; 54 | } 55 | 56 | sm_matrix * 57 | signature_form_table(pset_family ESCubes, pset_family ESSet) 58 | { 59 | sm_matrix *table; 60 | int row,column; 61 | pcube c,p; 62 | int col_deleted; 63 | 64 | table = sm_alloc(); 65 | 66 | col_deleted = 0; 67 | foreachi_set(ESSet,column,p){ 68 | if(column%1000 == 0){ 69 | col_deleted += sm_col_dominance(table,NULL); 70 | } 71 | foreachi_set(ESCubes,row,c){ 72 | if(setp_implies(c,p)){ 73 | sm_insert(table,row,column); 74 | } 75 | } 76 | } 77 | col_deleted += sm_col_dominance(table,NULL); 78 | 79 | return table; 80 | } 81 | -------------------------------------------------------------------------------- /espresso-src/sminterf.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | 3 | 4 | pset 5 | do_sm_minimum_cover(pset_family A) 6 | { 7 | sm_matrix *M; 8 | sm_row *sparse_cover; 9 | sm_element *pe; 10 | pset cover; 11 | register int i, base, rownum; 12 | register unsigned val; 13 | register pset last, p; 14 | 15 | M = sm_alloc(); 16 | rownum = 0; 17 | foreach_set(A, last, p) { 18 | foreach_set_element(p, i, val, base) { 19 | (void) sm_insert(M, rownum, base); 20 | } 21 | rownum++; 22 | } 23 | 24 | sparse_cover = sm_minimum_cover(M, NIL(int), 1, 0); 25 | sm_free(M); 26 | 27 | cover = set_new(A->sf_size); 28 | sm_foreach_row_element(sparse_cover, pe) { 29 | set_insert(cover, pe->col_num); 30 | } 31 | sm_row_free(sparse_cover); 32 | 33 | return cover; 34 | } 35 | -------------------------------------------------------------------------------- /espresso-src/solution.c: -------------------------------------------------------------------------------- 1 | #include "mincov_int.h" 2 | 3 | 4 | solution_t * 5 | solution_alloc(void) 6 | { 7 | solution_t *sol; 8 | 9 | sol = ALLOC(solution_t, 1); 10 | sol->cost = 0; 11 | sol->row = sm_row_alloc(); 12 | return sol; 13 | } 14 | 15 | 16 | void 17 | solution_free(solution_t *sol) 18 | { 19 | sm_row_free(sol->row); 20 | FREE(sol); 21 | } 22 | 23 | 24 | solution_t * 25 | solution_dup(solution_t *sol) 26 | { 27 | solution_t *new_sol; 28 | 29 | new_sol = ALLOC(solution_t, 1); 30 | new_sol->cost = sol->cost; 31 | new_sol->row = sm_row_dup(sol->row); 32 | return new_sol; 33 | } 34 | 35 | 36 | void 37 | solution_add(solution_t *sol, int *weight, int col) 38 | { 39 | (void) sm_row_insert(sol->row, col); 40 | sol->cost += WEIGHT(weight, col); 41 | } 42 | 43 | 44 | void 45 | solution_accept(solution_t *sol, sm_matrix *A, int *weight, int col) 46 | { 47 | register sm_element *p, *pnext; 48 | sm_col *pcol; 49 | 50 | solution_add(sol, weight, col); 51 | 52 | /* delete rows covered by this column */ 53 | pcol = sm_get_col(A, col); 54 | for(p = pcol->first_row; p != 0; p = pnext) { 55 | pnext = p->next_row; /* grab it before it disappears */ 56 | sm_delrow(A, p->row_num); 57 | } 58 | } 59 | 60 | 61 | /* ARGSUSED */ 62 | void 63 | solution_reject(solution_t *sol, sm_matrix *A, int *weight, int col) 64 | { 65 | sm_delcol(A, col); 66 | } 67 | 68 | 69 | solution_t * 70 | solution_choose_best(solution_t *best1, solution_t *best2) 71 | { 72 | if (best1 != NIL(solution_t)) { 73 | if (best2 != NIL(solution_t)) { 74 | if (best1->cost <= best2->cost) { 75 | solution_free(best2); 76 | return best1; 77 | } else { 78 | solution_free(best1); 79 | return best2; 80 | } 81 | } else { 82 | return best1; 83 | } 84 | } else { 85 | if (best2 != NIL(solution_t)) { 86 | return best2; 87 | } else { 88 | return NIL(solution_t); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /espresso-src/sparse.c: -------------------------------------------------------------------------------- 1 | /* 2 | module: sparse.c 3 | 4 | make_sparse is a last-step cleanup to reduce the total number 5 | of literals in the cover. 6 | 7 | This is done by reducing the "sparse" variables (using a modified 8 | version of irredundant rather than reduce), followed by expanding 9 | the "dense" variables (using modified version of expand). 10 | */ 11 | 12 | #include "espresso.h" 13 | 14 | pcover make_sparse(pset_family F, pset_family D, pset_family R) 15 | { 16 | cost_t cost, best_cost; 17 | 18 | cover_cost(F, &best_cost); 19 | 20 | do { 21 | EXECUTE(F = mv_reduce(F, D), MV_REDUCE_TIME, F, cost); 22 | if (cost.total == best_cost.total) 23 | break; 24 | copy_cost(&cost, &best_cost); 25 | 26 | EXECUTE(F = expand(F, R, TRUE), RAISE_IN_TIME, F, cost); 27 | if (cost.total == best_cost.total) 28 | break; 29 | copy_cost(&cost, &best_cost); 30 | } while (force_irredundant); 31 | 32 | return F; 33 | } 34 | 35 | /* 36 | mv_reduce -- perform an "optimal" reduction of the variables which 37 | we desire to be sparse 38 | 39 | This could be done using "reduce" and then saving just the desired 40 | part of the reduction. Instead, this version uses IRRED to find 41 | which cubes of an output are redundant. Note that this gets around 42 | the cube-ordering problem. 43 | 44 | In normal use, it is expected that the cover is irredundant and 45 | hence no cubes will be reduced to the empty cube (however, this is 46 | checked for and such cubes will be deleted) 47 | */ 48 | 49 | pcover 50 | mv_reduce(pset_family F, pset_family D) 51 | { 52 | register int i, var; 53 | register pcube p, p1, last; 54 | int index; 55 | pcover F1, D1; 56 | pcube *F_cube_table; 57 | 58 | /* loop for each multiple-valued variable */ 59 | for(var = 0; var < cube.num_vars; var++) { 60 | 61 | if (cube.sparse[var]) { 62 | 63 | /* loop for each part of the variable */ 64 | for(i = cube.first_part[var]; i <= cube.last_part[var]; i++) { 65 | 66 | /* remember mapping of F1 cubes back to F cubes */ 67 | F_cube_table = ALLOC(pcube, F->count); 68 | 69 | /* 'cofactor' against part #i of variable #var */ 70 | F1 = new_cover(F->count); 71 | foreach_set(F, last, p) { 72 | if (is_in_set(p, i)) { 73 | F_cube_table[F1->count] = p; 74 | p1 = GETSET(F1, F1->count++); 75 | (void) set_diff(p1, p, cube.var_mask[var]); 76 | set_insert(p1, i); 77 | } 78 | } 79 | 80 | /* 'cofactor' against part #i of variable #var */ 81 | /* not really necessary -- just more efficient ? */ 82 | D1 = new_cover(D->count); 83 | foreach_set(D, last, p) { 84 | if (is_in_set(p, i)) { 85 | p1 = GETSET(D1, D1->count++); 86 | (void) set_diff(p1, p, cube.var_mask[var]); 87 | set_insert(p1, i); 88 | } 89 | } 90 | 91 | mark_irredundant(F1, D1); 92 | 93 | /* now remove part i from cubes which are redundant */ 94 | index = 0; 95 | foreach_set(F1, last, p1) { 96 | if (! TESTP(p1, ACTIVE)) { 97 | p = F_cube_table[index]; 98 | 99 | /* don't reduce a variable which is full 100 | * (unless it is the output variable) 101 | */ 102 | if (var == cube.num_vars-1 || 103 | ! setp_implies(cube.var_mask[var], p)) { 104 | set_remove(p, i); 105 | } 106 | RESET(p, PRIME); 107 | } 108 | index++; 109 | } 110 | 111 | free_cover(F1); 112 | free_cover(D1); 113 | FREE(F_cube_table); 114 | } 115 | } 116 | } 117 | 118 | /* Check if any cubes disappeared */ 119 | (void) sf_active(F); 120 | for(var = 0; var < cube.num_vars; var++) { 121 | if (cube.sparse[var]) { 122 | foreach_active_set(F, last, p) { 123 | if (setp_disjoint(p, cube.var_mask[var])) { 124 | RESET(p, ACTIVE); 125 | F->active_count--; 126 | } 127 | } 128 | } 129 | } 130 | 131 | if (F->count != F->active_count) { 132 | F = sf_inactive(F); 133 | } 134 | return F; 135 | } 136 | -------------------------------------------------------------------------------- /espresso-src/sparse.h: -------------------------------------------------------------------------------- 1 | #ifndef SPARSE_H 2 | #define SPARSE_H 3 | 4 | /* 5 | * sparse.h -- sparse matrix package header file 6 | */ 7 | 8 | typedef struct sm_element_struct sm_element; 9 | typedef struct sm_row_struct sm_row; 10 | typedef struct sm_col_struct sm_col; 11 | typedef struct sm_matrix_struct sm_matrix; 12 | 13 | 14 | /* 15 | * sparse matrix element 16 | */ 17 | struct sm_element_struct { 18 | int row_num; /* row number of this element */ 19 | int col_num; /* column number of this element */ 20 | sm_element *next_row; /* next row in this column */ 21 | sm_element *prev_row; /* previous row in this column */ 22 | sm_element *next_col; /* next column in this row */ 23 | sm_element *prev_col; /* previous column in this row */ 24 | char *user_word; /* user-defined word */ 25 | }; 26 | 27 | 28 | /* 29 | * row header 30 | */ 31 | struct sm_row_struct { 32 | int row_num; /* the row number */ 33 | int length; /* number of elements in this row */ 34 | int flag; /* user-defined word */ 35 | sm_element *first_col; /* first element in this row */ 36 | sm_element *last_col; /* last element in this row */ 37 | sm_row *next_row; /* next row (in sm_matrix linked list) */ 38 | sm_row *prev_row; /* previous row (in sm_matrix linked list) */ 39 | char *user_word; /* user-defined word */ 40 | }; 41 | 42 | 43 | /* 44 | * column header 45 | */ 46 | struct sm_col_struct { 47 | int col_num; /* the column number */ 48 | int length; /* number of elements in this column */ 49 | int flag; /* user-defined word */ 50 | sm_element *first_row; /* first element in this column */ 51 | sm_element *last_row; /* last element in this column */ 52 | sm_col *next_col; /* next column (in sm_matrix linked list) */ 53 | sm_col *prev_col; /* prev column (in sm_matrix linked list) */ 54 | char *user_word; /* user-defined word */ 55 | }; 56 | 57 | 58 | /* 59 | * A sparse matrix 60 | */ 61 | struct sm_matrix_struct { 62 | sm_row **rows; /* pointer to row headers (by row #) */ 63 | int rows_size; /* alloc'ed size of above array */ 64 | sm_col **cols; /* pointer to column headers (by col #) */ 65 | int cols_size; /* alloc'ed size of above array */ 66 | sm_row *first_row; /* first row (linked list of all rows) */ 67 | sm_row *last_row; /* last row (linked list of all rows) */ 68 | int nrows; /* number of rows */ 69 | sm_col *first_col; /* first column (linked list of columns) */ 70 | sm_col *last_col; /* last column (linked list of columns) */ 71 | int ncols; /* number of columns */ 72 | char *user_word; /* user-defined word */ 73 | }; 74 | 75 | 76 | #define sm_get_col(A, colnum) \ 77 | (((colnum) >= 0 && (colnum) < (A)->cols_size) ? \ 78 | (A)->cols[colnum] : (sm_col *) 0) 79 | 80 | #define sm_get_row(A, rownum) \ 81 | (((rownum) >= 0 && (rownum) < (A)->rows_size) ? \ 82 | (A)->rows[rownum] : (sm_row *) 0) 83 | 84 | #define sm_foreach_row(A, prow) \ 85 | for(prow = A->first_row; prow != 0; prow = prow->next_row) 86 | 87 | #define sm_foreach_col(A, pcol) \ 88 | for(pcol = A->first_col; pcol != 0; pcol = pcol->next_col) 89 | 90 | #define sm_foreach_row_element(prow, p) \ 91 | for(p = prow->first_col; p != 0; p = p->next_col) 92 | 93 | #define sm_foreach_col_element(pcol, p) \ 94 | for(p = pcol->first_row; p != 0; p = p->next_row) 95 | 96 | #define sm_put(x, val) \ 97 | (x->user_word = (char *) val) 98 | 99 | #define sm_get(type, x) \ 100 | ((type) (x->user_word)) 101 | 102 | extern sm_matrix *sm_alloc(void), *sm_alloc_size(int row, int col), *sm_dup(sm_matrix *A); 103 | extern void sm_free(sm_matrix *A), sm_delrow(sm_matrix *A, int i), sm_delcol(sm_matrix *A, int i), sm_resize(register sm_matrix *A, int row, int col); 104 | extern void sm_write(FILE *fp, sm_matrix *A), sm_print(FILE *fp, sm_matrix *A), sm_dump(sm_matrix *A, char *s, int max), sm_cleanup(void); 105 | extern void sm_copy_row(register sm_matrix *dest, int dest_row, sm_row *prow), sm_copy_col(register sm_matrix *dest, int dest_col, sm_col *pcol); 106 | extern void sm_remove(sm_matrix *A, int rownum, int colnum), sm_remove_element(register sm_matrix *A, register sm_element *p); 107 | extern sm_element *sm_insert(register sm_matrix *A, register int row, register int col), *sm_find(sm_matrix *A, int rownum, int colnum); 108 | extern sm_row *sm_longest_row(sm_matrix *A); 109 | extern sm_col *sm_longest_col(sm_matrix *A); 110 | extern int sm_read(FILE *fp, sm_matrix **A), sm_read_compressed(FILE *fp, sm_matrix **A); 111 | 112 | extern sm_row *sm_row_alloc(void), *sm_row_dup(register sm_row *prow), *sm_row_and(sm_row *p1, sm_row *p2); 113 | extern void sm_row_free(register sm_row *prow), sm_row_remove(register sm_row *prow, register int col), sm_row_print(FILE *fp, sm_row *prow); 114 | extern sm_element *sm_row_insert(register sm_row *prow, register int col), *sm_row_find(sm_row *prow, int col); 115 | extern int sm_row_contains(sm_row *p1, sm_row *p2), sm_row_intersects(sm_row *p1, sm_row *p2); 116 | extern int sm_row_compare(sm_row *p1, sm_row *p2), sm_row_hash(sm_row *prow, int modulus); 117 | 118 | extern sm_col *sm_col_alloc(void), *sm_col_dup(register sm_col *pcol), *sm_col_and(sm_col *p1, sm_col *p2); 119 | extern void sm_col_free(register sm_col *pcol), sm_col_remove(register sm_col *pcol, register int row), sm_col_print(FILE *fp, sm_col *pcol); 120 | extern sm_element *sm_col_insert(register sm_col *pcol, register int row), *sm_col_find(sm_col *pcol, int row); 121 | extern int sm_col_contains(sm_col *p1, sm_col *p2), sm_col_intersects(sm_col *p1, sm_col *p2); 122 | extern int sm_col_compare(sm_col *p1, sm_col *p2), sm_col_hash(sm_col *pcol, int modulus); 123 | 124 | extern int sm_row_dominance(sm_matrix *A), sm_col_dominance(sm_matrix *A, int *weight), sm_block_partition(sm_matrix *A, sm_matrix **L, sm_matrix **R); 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /espresso-src/sparse_int.h: -------------------------------------------------------------------------------- 1 | #include "port.h" 2 | #include "utility.h" 3 | #include "sparse.h" 4 | 5 | 6 | 7 | /* 8 | * sorted, double-linked list insertion 9 | * 10 | * type: object type 11 | * 12 | * first, last: fields (in header) to head and tail of the list 13 | * count: field (in header) of length of the list 14 | * 15 | * next, prev: fields (in object) to link next and previous objects 16 | * value: field (in object) which controls the order 17 | * 18 | * newval: value field for new object 19 | * e: an object to use if insertion needed (set to actual value used) 20 | */ 21 | 22 | #define sorted_insert(type, first, last, count, next, prev, value, newval, e) \ 23 | if (last == 0) { \ 24 | e->value = newval; \ 25 | first = e; \ 26 | last = e; \ 27 | e->next = 0; \ 28 | e->prev = 0; \ 29 | count++; \ 30 | } else if (last->value < newval) { \ 31 | e->value = newval; \ 32 | last->next = e; \ 33 | e->prev = last; \ 34 | last = e; \ 35 | e->next = 0; \ 36 | count++; \ 37 | } else if (first->value > newval) { \ 38 | e->value = newval; \ 39 | first->prev = e; \ 40 | e->next = first; \ 41 | first = e; \ 42 | e->prev = 0; \ 43 | count++; \ 44 | } else { \ 45 | type *p; \ 46 | for(p = first; p->value < newval; p = p->next) \ 47 | ; \ 48 | if (p->value > newval) { \ 49 | e->value = newval; \ 50 | p = p->prev; \ 51 | p->next->prev = e; \ 52 | e->next = p->next; \ 53 | p->next = e; \ 54 | e->prev = p; \ 55 | count++; \ 56 | } else { \ 57 | e = p; \ 58 | } \ 59 | } 60 | 61 | 62 | /* 63 | * double linked-list deletion 64 | */ 65 | #define dll_unlink(p, first, last, next, prev, count) { \ 66 | if (p->prev == 0) { \ 67 | first = p->next; \ 68 | } else { \ 69 | p->prev->next = p->next; \ 70 | } \ 71 | if (p->next == 0) { \ 72 | last = p->prev; \ 73 | } else { \ 74 | p->next->prev = p->prev; \ 75 | } \ 76 | count--; \ 77 | } 78 | 79 | 80 | #ifdef FAST_AND_LOOSE 81 | extern sm_element *sm_element_freelist; 82 | extern sm_row *sm_row_freelist; 83 | extern sm_col *sm_col_freelist; 84 | 85 | #define sm_element_alloc(newobj) \ 86 | if (sm_element_freelist == NIL(sm_element)) { \ 87 | newobj = ALLOC(sm_element, 1); \ 88 | } else { \ 89 | newobj = sm_element_freelist; \ 90 | sm_element_freelist = sm_element_freelist->next_col; \ 91 | } \ 92 | newobj->user_word = NIL(char); \ 93 | 94 | #define sm_element_free(e) \ 95 | (e->next_col = sm_element_freelist, sm_element_freelist = e) 96 | 97 | #else 98 | 99 | #define sm_element_alloc(newobj) \ 100 | newobj = ALLOC(sm_element, 1); \ 101 | newobj->user_word = NIL(char); 102 | #define sm_element_free(e) \ 103 | FREE(e) 104 | #endif 105 | 106 | 107 | extern void sm_row_remove_element(register sm_row *prow, register sm_element *p); 108 | extern void sm_col_remove_element(register sm_col *pcol, register sm_element *p); 109 | 110 | /* LINTLIBRARY */ 111 | -------------------------------------------------------------------------------- /espresso-src/strdup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char *strdup(const char * src) { 5 | char * dup; 6 | unsigned int len; 7 | 8 | if (!src) return NULL; 9 | 10 | len = strlen(src); 11 | 12 | if (len == 0) return NULL; 13 | 14 | dup = malloc(len+1); 15 | 16 | if (dup) 17 | strcpy(dup, src); 18 | 19 | return dup; 20 | } 21 | -------------------------------------------------------------------------------- /espresso-src/strdup.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_STRDUP 2 | #define __H_STRDUP 3 | 4 | #ifndef strdup 5 | char * strdup(const char *str); 6 | #endif 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /espresso-src/unate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * unate.c -- routines for dealing with unate functions 3 | */ 4 | 5 | #include "espresso.h" 6 | 7 | static pset_family abs_covered(pset_family A, register int pick); 8 | static pset_family abs_covered_many(pset_family A, register pset pick_set); 9 | static int abs_select_restricted(pset_family A, pset restrict); 10 | 11 | pcover map_cover_to_unate(pset *T) 12 | { 13 | register unsigned int word_test, word_set, bit_test, bit_set; 14 | register pcube p, pA; 15 | pset_family A; 16 | pcube *T1; 17 | int ncol, i; 18 | 19 | A = sf_new(CUBELISTSIZE(T), cdata.vars_unate); 20 | A->count = CUBELISTSIZE(T); 21 | foreachi_set(A, i, p) { 22 | (void) set_clear(p, A->sf_size); 23 | } 24 | ncol = 0; 25 | 26 | for(i = 0; i < cube.size; i++) { 27 | if (cdata.part_zeros[i] > 0) { 28 | assert(ncol <= cdata.vars_unate); 29 | 30 | /* Copy a column from T to A */ 31 | word_test = WHICH_WORD(i); 32 | bit_test = 1 << WHICH_BIT(i); 33 | word_set = WHICH_WORD(ncol); 34 | bit_set = 1 << WHICH_BIT(ncol); 35 | 36 | pA = A->data; 37 | for(T1 = T+2; (p = *T1++) != 0; ) { 38 | if ((p[word_test] & bit_test) == 0) { 39 | pA[word_set] |= bit_set; 40 | } 41 | pA += A->wsize; 42 | } 43 | 44 | ncol++; 45 | } 46 | } 47 | 48 | return A; 49 | } 50 | 51 | pcover map_unate_to_cover(pset_family A) 52 | { 53 | register int i, ncol, lp; 54 | register pcube p, pB; 55 | int var, nunate, *unate; 56 | pcube last; 57 | pset_family B; 58 | 59 | B = sf_new(A->count, cube.size); 60 | B->count = A->count; 61 | 62 | /* Find the unate variables */ 63 | unate = ALLOC(int, cube.num_vars); 64 | nunate = 0; 65 | for(var = 0; var < cube.num_vars; var++) { 66 | if (cdata.is_unate[var]) { 67 | unate[nunate++] = var; 68 | } 69 | } 70 | 71 | /* Loop for each set of A */ 72 | pB = B->data; 73 | foreach_set(A, last, p) { 74 | 75 | /* Initialize this set of B */ 76 | INLINEset_fill(pB, cube.size); 77 | 78 | /* Now loop for the unate variables; if the part is in A, 79 | * then this variable of B should be a single 1 in the unate 80 | * part. 81 | */ 82 | for(ncol = 0; ncol < nunate; ncol++) { 83 | if (is_in_set(p, ncol)) { 84 | lp = cube.last_part[unate[ncol]]; 85 | for(i = cube.first_part[unate[ncol]]; i <= lp; i++) { 86 | if (cdata.part_zeros[i] == 0) { 87 | set_remove(pB, i); 88 | } 89 | } 90 | } 91 | } 92 | pB += B->wsize; 93 | } 94 | 95 | FREE(unate); 96 | return B; 97 | } 98 | 99 | /* 100 | * unate_compl 101 | */ 102 | 103 | pset_family unate_compl(pset_family A) 104 | { 105 | register pset p, last; 106 | 107 | /* Make sure A is single-cube containment minimal */ 108 | /* A = sf_rev_contain(A);*/ 109 | 110 | foreach_set(A, last, p) { 111 | PUTSIZE(p, set_ord(p)); 112 | } 113 | 114 | /* Recursively find the complement */ 115 | A = unate_complement(A); 116 | 117 | /* Now, we can guarantee a minimal result by containing the result */ 118 | A = sf_rev_contain(A); 119 | return A; 120 | } 121 | 122 | 123 | /* 124 | * Assume SIZE(p) records the size of each set 125 | */ 126 | pset_family unate_complement(pset_family A) 127 | /* disposes of A */ 128 | { 129 | pset_family Abar; 130 | register pset p, p1, prestrict; 131 | register int i; 132 | int max_i, j; 133 | unsigned int min_set_ord; 134 | 135 | /* Check for no sets in the matrix -- complement is the universe */ 136 | if (A->count == 0) { 137 | sf_free(A); 138 | Abar = sf_new(1, A->sf_size); 139 | (void) set_clear(GETSET(Abar, Abar->count++), A->sf_size); 140 | 141 | /* Check for a single set in the maxtrix -- compute de Morgan complement */ 142 | } else if (A->count == 1) { 143 | p = A->data; 144 | Abar = sf_new(A->sf_size, A->sf_size); 145 | for(i = 0; i < A->sf_size; i++) { 146 | if (is_in_set(p, i)) { 147 | p1 = set_clear(GETSET(Abar, Abar->count++), A->sf_size); 148 | set_insert(p1, i); 149 | } 150 | } 151 | sf_free(A); 152 | 153 | } else { 154 | 155 | /* Select splitting variable as the variable which belongs to a set 156 | * of the smallest size, and which has greatest column count 157 | */ 158 | prestrict = set_new(A->sf_size); 159 | min_set_ord = A->sf_size + 1; 160 | foreachi_set(A, i, p) { 161 | if (SIZE(p) < min_set_ord) { 162 | set_copy(prestrict, p); 163 | min_set_ord = SIZE(p); 164 | } else if (SIZE(p) == min_set_ord) { 165 | set_or(prestrict, prestrict, p); 166 | } 167 | } 168 | 169 | /* Check for no data (shouldn't happen ?) */ 170 | if (min_set_ord == 0) { 171 | A->count = 0; 172 | Abar = A; 173 | 174 | /* Check for "essential" columns */ 175 | } else if (min_set_ord == 1) { 176 | Abar = unate_complement(abs_covered_many(A, prestrict)); 177 | sf_free(A); 178 | foreachi_set(Abar, i, p) { 179 | set_or(p, p, prestrict); 180 | } 181 | 182 | /* else, recur as usual */ 183 | } else { 184 | max_i = abs_select_restricted(A, prestrict); 185 | 186 | /* Select those rows of A which are not covered by max_i, 187 | * recursively find all minimal covers of these rows, and 188 | * then add back in max_i 189 | */ 190 | Abar = unate_complement(abs_covered(A, max_i)); 191 | foreachi_set(Abar, i, p) { 192 | set_insert(p, max_i); 193 | } 194 | 195 | /* Now recur on A with all zero's on column max_i */ 196 | foreachi_set(A, i, p) { 197 | if (is_in_set(p, max_i)) { 198 | set_remove(p, max_i); 199 | j = SIZE(p) - 1; 200 | PUTSIZE(p, j); 201 | } 202 | } 203 | 204 | Abar = sf_append(Abar, unate_complement(A)); 205 | } 206 | set_free(prestrict); 207 | } 208 | 209 | return Abar; 210 | } 211 | 212 | pset_family exact_minimum_cover(pset_family T) 213 | { 214 | register pset p, last, p1; 215 | register int i, n; 216 | int lev, lvl; 217 | pset nlast; 218 | pset_family temp; 219 | long start = ptime(); 220 | struct { 221 | pset_family sf; 222 | int level; 223 | } stack[32]; /* 32 suffices for 2 ** 32 cubes ! */ 224 | 225 | if (T->count <= 0) 226 | return sf_new(1, T->sf_size); 227 | for(n = T->count, lev = 0; n != 0; n >>= 1, lev++) ; 228 | 229 | /* A simple heuristic ordering */ 230 | T = lex_sort(sf_save(T)); 231 | 232 | /* Push a full set on the stack to get things started */ 233 | n = 1; 234 | stack[0].sf = sf_new(1, T->sf_size); 235 | stack[0].level = lev; 236 | set_fill(GETSET(stack[0].sf, stack[0].sf->count++), T->sf_size); 237 | 238 | nlast = GETSET(T, T->count - 1); 239 | foreach_set(T, last, p) { 240 | 241 | /* "unstack" the set into a family */ 242 | temp = sf_new(set_ord(p), T->sf_size); 243 | for(i = 0; i < T->sf_size; i++) 244 | if (is_in_set(p, i)) { 245 | p1 = set_fill(GETSET(temp, temp->count++), T->sf_size); 246 | set_remove(p1, i); 247 | } 248 | stack[n].sf = temp; 249 | stack[n++].level = lev; 250 | 251 | /* Pop the stack and perform (leveled) intersections */ 252 | while (n > 1 && (stack[n-1].level==stack[n-2].level || p == nlast)) { 253 | temp = unate_intersect(stack[n-1].sf, stack[n-2].sf, FALSE); 254 | lvl = MIN(stack[n-1].level, stack[n-2].level) - 1; 255 | if (debug & MINCOV && lvl < 10) { 256 | printf("# EXACT_MINCOV[%d]: %4d = %4d x %4d, time = %s\n", 257 | lvl, temp->count, stack[n-1].sf->count, 258 | stack[n-2].sf->count, print_time(ptime() - start)); 259 | (void) fflush(stdout); 260 | } 261 | sf_free(stack[n-2].sf); 262 | sf_free(stack[n-1].sf); 263 | stack[n-2].sf = temp; 264 | stack[n-2].level = lvl; 265 | n--; 266 | } 267 | } 268 | 269 | temp = stack[0].sf; 270 | p1 = set_fill(set_new(T->sf_size), T->sf_size); 271 | foreach_set(temp, last, p) 272 | INLINEset_diff(p, p1, p); 273 | set_free(p1); 274 | if (debug & MINCOV1) { 275 | printf("MINCOV: family of all minimal coverings is\n"); 276 | sf_print(temp); 277 | } 278 | sf_free(T); /* this is the copy of T we made ... */ 279 | return temp; 280 | } 281 | 282 | /* 283 | * unate_intersect -- intersect two unate covers 284 | * 285 | * If largest_only is TRUE, then only the largest cube(s) are returned 286 | */ 287 | 288 | #define MAGIC 500 /* save 500 cubes before containment */ 289 | 290 | pset_family unate_intersect(pset_family A, pset_family B, int largest_only) 291 | { 292 | register pset pi, pj, lasti, lastj, pt; 293 | pset_family T, Tsave; 294 | bool save; 295 | int maxord, ord; 296 | 297 | /* How large should each temporary result cover be ? */ 298 | T = sf_new(MAGIC, A->sf_size); 299 | Tsave = NULL; 300 | maxord = 0; 301 | pt = T->data; 302 | 303 | /* Form pairwise intersection of each set of A with each cube of B */ 304 | foreach_set(A, lasti, pi) { 305 | 306 | foreach_set(B, lastj, pj) { 307 | 308 | save = set_andp(pt, pi, pj); 309 | 310 | /* Check if we want the largest only */ 311 | if (save && largest_only) { 312 | if ((ord = set_ord(pt)) > maxord) { 313 | /* discard Tsave and T */ 314 | if (Tsave != NULL) { 315 | sf_free(Tsave); 316 | Tsave = NULL; 317 | } 318 | pt = T->data; 319 | T->count = 0; 320 | /* Re-create pt (which was just thrown away) */ 321 | (void) set_and(pt, pi, pj); 322 | maxord = ord; 323 | } else if (ord < maxord) { 324 | save = FALSE; 325 | } 326 | } 327 | 328 | if (save) { 329 | if (++T->count >= T->capacity) { 330 | T = sf_contain(T); 331 | Tsave = (Tsave == NULL) ? T : sf_union(Tsave, T); 332 | T = sf_new(MAGIC, A->sf_size); 333 | pt = T->data; 334 | } else { 335 | pt += T->wsize; 336 | } 337 | } 338 | } 339 | } 340 | 341 | 342 | /* Contain the final result and merge it into Tsave */ 343 | T = sf_contain(T); 344 | Tsave = (Tsave == NULL) ? T : sf_union(Tsave, T); 345 | 346 | return Tsave; 347 | } 348 | 349 | /* 350 | * abs_covered -- after selecting a new column for the selected set, 351 | * create a new matrix which is only those rows which are still uncovered 352 | */ 353 | static pset_family 354 | abs_covered(pset_family A, register int pick) 355 | { 356 | register pset last, p, pdest; 357 | register pset_family Aprime; 358 | 359 | Aprime = sf_new(A->count, A->sf_size); 360 | pdest = Aprime->data; 361 | foreach_set(A, last, p) 362 | if (! is_in_set(p, pick)) { 363 | INLINEset_copy(pdest, p); 364 | Aprime->count++; 365 | pdest += Aprime->wsize; 366 | } 367 | return Aprime; 368 | } 369 | 370 | 371 | /* 372 | * abs_covered_many -- after selecting many columns for ther selected set, 373 | * create a new matrix which is only those rows which are still uncovered 374 | */ 375 | static pset_family 376 | abs_covered_many(pset_family A, register pset pick_set) 377 | { 378 | register pset last, p, pdest; 379 | register pset_family Aprime; 380 | 381 | Aprime = sf_new(A->count, A->sf_size); 382 | pdest = Aprime->data; 383 | foreach_set(A, last, p) 384 | if (setp_disjoint(p, pick_set)) { 385 | INLINEset_copy(pdest, p); 386 | Aprime->count++; 387 | pdest += Aprime->wsize; 388 | } 389 | return Aprime; 390 | } 391 | 392 | 393 | /* 394 | * abs_select_restricted -- select the column of maximum column count which 395 | * also belongs to the set "prestrict"; weight each column of a set as 396 | * 1 / (set_ord(p) - 1). 397 | */ 398 | static int 399 | abs_select_restricted(pset_family A, pset prestrict) 400 | { 401 | register int i, best_var, best_count, *count; 402 | 403 | /* Sum the elements in these columns */ 404 | count = sf_count_restricted(A, prestrict); 405 | 406 | /* Find which variable has maximum weight */ 407 | best_var = -1; 408 | best_count = 0; 409 | for(i = 0; i < A->sf_size; i++) { 410 | if (count[i] > best_count) { 411 | best_var = i; 412 | best_count = count[i]; 413 | } 414 | } 415 | FREE(count); 416 | 417 | if (best_var == -1) 418 | fatal("abs_select_restricted: should not have best_var == -1"); 419 | 420 | return best_var; 421 | } 422 | -------------------------------------------------------------------------------- /espresso-src/util_signature.c: -------------------------------------------------------------------------------- 1 | /* Module:util_signature.c 2 | * Purpose: 3 | * contains miscellaneous utility routines 4 | * Routines: 5 | * void set_time_limit(seconds) 6 | * sets the cpu time limit 7 | * int sf_equal(): 8 | * checks equlaity of two set families. 9 | * int print_cover(): 10 | * prints cover. 11 | * int mem_usage(): 12 | * current Memory usage. 13 | * Initialized on the first call. 14 | * int time_usage(): 15 | * current time usage. 16 | * Initialized on the first call. 17 | */ 18 | 19 | #include 20 | #include 21 | #include "espresso.h" 22 | #include "signature.h" 23 | #include 24 | #include 25 | #include 26 | 27 | void 28 | set_time_limit(int seconds) 29 | { 30 | struct rlimit rlp_st, *rlp = &rlp_st; 31 | rlp->rlim_cur = seconds; 32 | setrlimit(RLIMIT_CPU, rlp); 33 | } 34 | 35 | void 36 | print_cover(pset_family F, char *name) 37 | { 38 | pcube last, p; 39 | printf("%s:\t %d\n",name,F->count); 40 | foreach_set(F,last,p){ 41 | print_cube(stdout,p,"~0"); 42 | } 43 | /* printf("\n\n",name); AB? */ 44 | printf("\n\n"); 45 | } 46 | 47 | /* sf_equal: Check equality of two set families */ 48 | int 49 | sf_equal(pset_family F1, pset_family F2) 50 | { 51 | int i; 52 | int count = F1->count; 53 | pcube *list1,*list2; 54 | 55 | if(F1->count != F2->count){ 56 | return(FALSE); 57 | } 58 | 59 | list1 = sf_sort(F1, (qsort_compare_func) descend); 60 | list2 = sf_sort(F2, (qsort_compare_func) descend); 61 | 62 | for(i = 0; i < count; i++){ 63 | if(!setp_equal(list1[i],list2[i])){ 64 | return FALSE; 65 | } 66 | } 67 | 68 | return TRUE; 69 | } 70 | 71 | /* mem_usage: 72 | * Initialized on first call. Prints current memory usage. 73 | */ 74 | int 75 | mem_usage(char *name) 76 | { 77 | static int memory_init; 78 | int memory_current; 79 | static int flag = 1; 80 | 81 | if(flag){ 82 | memory_init = 0; 83 | /* AB, sbrk is soooo BSD sbrk(0); */ 84 | flag = 0; 85 | } 86 | 87 | memory_current = 0; 88 | /* AB, sbrk is soooo BSD sbrk(0); */ 89 | 90 | printf("Memory %s\t %d\n", name, memory_current - memory_init); 91 | 92 | return memory_current; 93 | 94 | } 95 | 96 | /* time_usage: 97 | * Initialized on first call. Prints current time usage. 98 | */ 99 | int 100 | time_usage(char *name) 101 | { 102 | static int time_init; 103 | int time_current; 104 | static int flag = 1; 105 | 106 | if(flag){ 107 | time_init = ptime(); 108 | flag = 0; 109 | return time_init; 110 | } 111 | 112 | time_current = ptime(); 113 | 114 | printf("%s\t %ld\n", name, (time_current - time_init)/1000L); 115 | 116 | return time_current; 117 | 118 | } 119 | 120 | /* s_totals : add time spent in the function and update call count */ 121 | void 122 | s_totals(long int time, int i) 123 | { 124 | time = ptime() - time; 125 | total_time[i] += time; 126 | total_calls[i]++; 127 | } 128 | 129 | void 130 | s_runtime(long int total) 131 | { 132 | int i; 133 | long temp; 134 | 135 | for(i = 0; i < TIME_COUNT; i++) { 136 | if (total_calls[i] != 0) { 137 | temp = 100 * total_time[i]; 138 | printf("# %s\t%2d call(s) for %s ( %2ld.%01ld%% )\n", 139 | total_name[i], total_calls[i], print_time(total_time[i]), 140 | temp/total, (10 * (temp%total)) / total); 141 | } 142 | } 143 | } 144 | 145 | -------------------------------------------------------------------------------- /espresso-src/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_H 2 | #define UTILITY_H 3 | 4 | /* 5 | * assumes the memory manager is libmm.a 6 | * - allows malloc(0) or realloc(obj, 0) 7 | * - catches out of memory (and calls MMout_of_memory()) 8 | * - catch free(0) and realloc(0, size) in the macros 9 | */ 10 | #define NIL(type) ((type *) 0) 11 | #define ALLOC(type, num) \ 12 | ((type *) malloc(sizeof(type) * (num))) 13 | #define REALLOC(type, obj, num) \ 14 | (obj) ? ((type *) realloc((char *) obj, sizeof(type) * (num))) : \ 15 | ((type *) malloc(sizeof(type) * (num))) 16 | #define FREE(obj) \ 17 | if ((obj)) { (void) free((char *) (obj)); (obj) = 0; } 18 | 19 | extern long util_cpu_time(void); 20 | extern char *util_print_time (long t); 21 | 22 | #ifndef NIL_FN 23 | #define NIL_FN(type) ((type (*)()) 0) 24 | #endif /* NIL_FN */ 25 | 26 | #ifndef MAX 27 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) 28 | #endif /* MAX */ 29 | #ifndef MIN 30 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) 31 | #endif /* MIN */ 32 | #ifndef ABS 33 | #define ABS(a) ((a) > 0 ? (a) : -(a)) 34 | #endif /* ABS */ 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /espresso-src/verify.c: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | #include "espresso.h" 5 | 6 | /* 7 | * verify -- check that all minterms of F are contained in (Fold u Dold) 8 | * and that all minterms of Fold are contained in (F u Dold). 9 | */ 10 | bool verify(pcover F, pcover Fold, pcover Dold) 11 | { 12 | pcube p, last, *FD; 13 | bool verify_error = FALSE; 14 | 15 | /* Make sure the function didn't grow too large */ 16 | FD = cube2list(Fold, Dold); 17 | foreach_set(F, last, p) 18 | if (! cube_is_covered(FD, p)) { 19 | printf("some minterm in F is not covered by Fold u Dold\n"); 20 | verify_error = TRUE; 21 | if (verbose_debug) printf("%s\n", pc1(p)); else break; 22 | } 23 | free_cubelist(FD); 24 | 25 | /* Make sure minimized function covers the original function */ 26 | FD = cube2list(F, Dold); 27 | foreach_set(Fold, last, p) 28 | if (! cube_is_covered(FD, p)) { 29 | printf("some minterm in Fold is not covered by F u Dold\n"); 30 | verify_error = TRUE; 31 | if (verbose_debug) printf("%s\n", pc1(p)); else break; 32 | } 33 | free_cubelist(FD); 34 | 35 | return verify_error; 36 | } 37 | 38 | 39 | 40 | /* 41 | * PLA_verify -- verify that two PLA's are identical 42 | * 43 | * If names are given, row and column permutations are done to make 44 | * the comparison meaningful. 45 | * 46 | */ 47 | bool PLA_verify(pPLA PLA1, pPLA PLA2) 48 | { 49 | /* Check if both have names given; if so, attempt to permute to 50 | * match the names 51 | */ 52 | if (PLA1->label != NULL && PLA1->label[0] != NULL && 53 | PLA2->label != NULL && PLA2->label[0] != NULL) { 54 | PLA_permute(PLA1, PLA2); 55 | } else { 56 | fprintf(stderr, "Warning: cannot permute columns without names\n"); 57 | return TRUE; 58 | } 59 | 60 | if (PLA1->F->sf_size != PLA2->F->sf_size) { 61 | fprintf(stderr, "PLA_verify: PLA's are not the same size\n"); 62 | return TRUE; 63 | } 64 | 65 | return verify(PLA2->F, PLA1->F, PLA1->D); 66 | } 67 | 68 | 69 | 70 | /* 71 | * Permute the columns of PLA1 so that they match the order of PLA2 72 | * Discard any columns of PLA1 which are not in PLA2 73 | * Association is strictly by the names of the columns of the cover. 74 | */ 75 | void PLA_permute(pPLA PLA1, pPLA PLA2) 76 | { 77 | register int i, j, *permute, npermute; 78 | register char *labi; 79 | char **label; 80 | 81 | /* determine which columns of PLA1 to save, and place these in the list 82 | * "permute"; the order in this list is the final output order 83 | */ 84 | npermute = 0; 85 | permute = ALLOC(int, PLA2->F->sf_size); 86 | for(i = 0; i < PLA2->F->sf_size; i++) { 87 | labi = PLA2->label[i]; 88 | for(j = 0; j < PLA1->F->sf_size; j++) { 89 | if (strcmp(labi, PLA1->label[j]) == 0) { 90 | permute[npermute++] = j; 91 | break; 92 | } 93 | } 94 | } 95 | 96 | /* permute columns */ 97 | if (PLA1->F != NULL) { 98 | PLA1->F = sf_permute(PLA1->F, permute, npermute); 99 | } 100 | if (PLA1->R != NULL) { 101 | PLA1->R = sf_permute(PLA1->R, permute, npermute); 102 | } 103 | if (PLA1->D != NULL) { 104 | PLA1->D = sf_permute(PLA1->D, permute, npermute); 105 | } 106 | 107 | /* permute the labels */ 108 | label = ALLOC(char *, cube.size); 109 | for(i = 0; i < npermute; i++) { 110 | label[i] = PLA1->label[permute[i]]; 111 | } 112 | for(i = npermute; i < cube.size; i++) { 113 | label[i] = NULL; 114 | } 115 | FREE(PLA1->label); 116 | PLA1->label = label; 117 | 118 | FREE(permute); 119 | } 120 | 121 | 122 | 123 | /* 124 | * check_consistency -- test that the ON-set, OFF-set and DC-set form 125 | * a partition of the boolean space. 126 | */ 127 | bool check_consistency(pPLA PLA) 128 | { 129 | bool verify_error = FALSE; 130 | pcover T; 131 | 132 | T = cv_intersect(PLA->F, PLA->D); 133 | if (T->count == 0) 134 | printf("ON-SET and DC-SET are disjoint\n"); 135 | else { 136 | printf("Some minterm(s) belong to both the ON-SET and DC-SET !\n"); 137 | if (verbose_debug) 138 | cprint(T); 139 | verify_error = TRUE; 140 | } 141 | (void) fflush(stdout); 142 | free_cover(T); 143 | 144 | T = cv_intersect(PLA->F, PLA->R); 145 | if (T->count == 0) 146 | printf("ON-SET and OFF-SET are disjoint\n"); 147 | else { 148 | printf("Some minterm(s) belong to both the ON-SET and OFF-SET !\n"); 149 | if (verbose_debug) 150 | cprint(T); 151 | verify_error = TRUE; 152 | } 153 | (void) fflush(stdout); 154 | free_cover(T); 155 | 156 | T = cv_intersect(PLA->D, PLA->R); 157 | if (T->count == 0) 158 | printf("DC-SET and OFF-SET are disjoint\n"); 159 | else { 160 | printf("Some minterm(s) belong to both the OFF-SET and DC-SET !\n"); 161 | if (verbose_debug) 162 | cprint(T); 163 | verify_error = TRUE; 164 | } 165 | (void) fflush(stdout); 166 | free_cover(T); 167 | 168 | if (tautology(cube3list(PLA->F, PLA->D, PLA->R))) 169 | printf("Union of ON-SET, OFF-SET and DC-SET is the universe\n"); 170 | else { 171 | T = complement(cube3list(PLA->F, PLA->D, PLA->R)); 172 | printf("There are minterms left unspecified !\n"); 173 | if (verbose_debug) 174 | cprint(T); 175 | verify_error = TRUE; 176 | free_cover(T); 177 | } 178 | (void) fflush(stdout); 179 | return verify_error; 180 | } 181 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const 2 | EspressoLogicMinimizer = require('bindings')('EspressoLogicMinimizer'), 3 | crypto = require('crypto'), 4 | debug = require('debug')('espresso'), 5 | fs = require('fs'), 6 | tmp = require('tmp'); 7 | 8 | /** 9 | * Applies the Espresso Heuristic Logic Minimizer algorithm to the provided 10 | * data in PLA format 11 | * 12 | * @param {Array} data 13 | * @returns {Array} 14 | */ 15 | function minimize(data) { 16 | if (!Array.isArray(data)) { 17 | throw new Error('EspressoLogicMinimizer: expected an array, got a ' + typeof data); 18 | } 19 | 20 | const badContent = data.filter((element) => typeof element != 'string'); 21 | 22 | if (badContent.length > 0) { 23 | throw new Error('EspressoLogicMinimizer: incorrect data content. Only strings are supported: ' + badContent); 24 | } 25 | 26 | return EspressoLogicMinimizer.minimize_from_data(data); 27 | } 28 | 29 | /** 30 | * @class Espresso 31 | */ 32 | class Espresso { 33 | /** 34 | * @throws 35 | * @param {number} input - number of input variables 36 | * @param {number} output - number of output variables 37 | */ 38 | constructor(inputSize, outputSize) { 39 | this.result = null; 40 | this.tmpfile = tmp.fileSync(); 41 | 42 | debug('TMP FILE CREATED: %s', this.tmpfile); 43 | fs.writeSync(this.tmpfile.fd, '.i ' + inputSize + '\n'); 44 | fs.writeSync(this.tmpfile.fd, '.o ' + outputSize + '\n'); 45 | } 46 | 47 | /** 48 | * Pushes a truth table line into the minimizer 49 | * @param {array.} input 50 | * @param {array.} output 51 | */ 52 | push(input, output) { 53 | if (this.result) { 54 | return this.result; 55 | } 56 | 57 | fs.writeSync(this.tmpfile.fd, 58 | input.reduce((p, c) => p + (c ? '1' : '0'), '') 59 | + ' ' 60 | + output.reduce((p, c) => p + (c ? '1' : '0'), '') 61 | + '\n' 62 | ); 63 | } 64 | 65 | /** 66 | * Performs the minimization 67 | */ 68 | minimize() { 69 | if (this.result) { 70 | return this.result; 71 | } 72 | 73 | fs.closeSync(this.tmpfile.fd); 74 | debug('Providing file to espresso: ', this.tmpfile); 75 | debug('EXISTS: ', fs.existsSync(this.tmpfile.name)); 76 | this.result = EspressoLogicMinimizer.minimize_from_path(this.tmpfile.name); 77 | this.tmpfile.removeCallback(); 78 | 79 | return this.result; 80 | } 81 | } 82 | 83 | module.exports = { 84 | minimize, 85 | Espresso 86 | }; 87 | -------------------------------------------------------------------------------- /man/espresso.1: -------------------------------------------------------------------------------- 1 | .TH ESPRESSO 1OCTTOOLS "31 January 1988" 2 | .SH NAME 3 | espresso \- Boolean Minimization 4 | .SH SYNOPSIS 5 | .B espresso 6 | [\fIoptions\fR] [\fIfile\fR] 7 | .SH DESCRIPTION 8 | .PP 9 | .I Espresso 10 | takes as input a two-level representation of a 11 | two-valued (or multiple-valued) Boolean function, and produces a 12 | minimal equivalent representation. The algorithms used are new and 13 | represent an advance in both speed and optimality of solution in 14 | heuristic Boolean minimization. 15 | .PP 16 | .I Espresso 17 | reads the \fIfile\fR provided (or standard input if no files 18 | are specified), performs the minimization, and writes the minimized result to 19 | standard output. 20 | .I Espresso 21 | automatically verifies that the minimized function 22 | is equivalent to the original function. 23 | Options allow for using an exact minimization algorithm, for 24 | choosing an optimal phase assignment for the output functions, and 25 | for choosing an optimal assignment of the inputs to input decoders. 26 | .PP 27 | The default input and output file formats are compatible with the 28 | Berkeley standard format for the physical description of a \s-1PLA\s0. The 29 | input format is described in detail in espresso(5). Note that the 30 | input file is a \fIlogical\fR representation of 31 | a set of Boolean equations, and hence the input format differs 32 | slightly from that described in pla(5) (which provides for the \fIphysical\fR 33 | representation of a \s-1PLA\s0). 34 | The input and output formats 35 | have been expanded to allow for multiple-valued logic 36 | functions, and to allow for the specification of the 37 | don't-care set which will be used in the minimization. 38 | .PP 39 | A complete list of the command line options is given below. 40 | Be warned that many of the command line options are not intended 41 | for general use. 42 | .TP 10 43 | .B -d 44 | Enables debugging. 45 | Useful only for those familiar with the algorithms used. 46 | .TP 10 47 | .B -Dcheck 48 | Checks that the function is a partition of the 49 | entire space (i.e., that the \s-1ON\s0-set, \s-1OFF\s0-set 50 | and \s-1DC\s0-set are 51 | pairwise disjoint, and that their union is the Universe). 52 | .ne 4 53 | .TP 10 54 | .B -Dd1merge 55 | Performs a quick distance-1 merge on the input 56 | file. This is useful when the input file 57 | is very large (e.g., a truth table with more than 1000 terms) because 58 | distance-1 merge is O(n log n) rather than the \s-1EXPAND\s0 59 | step of Espresso which is 60 | O(n * n). The output should then be run through 61 | Espresso to complete the minimization. A range of variables to 62 | be merged can also be specified using 63 | .B -rn-m 64 | (the default is to merge over all variables). 65 | .ne 4 66 | .TP 10 67 | .B -Decho 68 | Echoes the function to standard output. 69 | This can be used to get the complement of a function when 70 | combined with \fB-o\fP. 71 | .ne 4 72 | .TP 10 73 | .B -Dequiv 74 | Identify output variables which are equivalent. Takes into account 75 | the don't-care set and checks for equivalence of both the \s-1ON\s0-set 76 | and \s-1OFF\s0-set. 77 | .TP 10 78 | .B -Dexact 79 | Exact minimization algorithm (guarantees minimum number of 80 | product terms, and heuristically minimizes number of literals). 81 | Potentially expensive. 82 | .ne 4 83 | .TP 10 84 | .B -Dmany 85 | Reads and minimizes PLA's until end-of-file is detected. PLA's in 86 | the same file are separated by \fI.e\fP. 87 | .ne 4 88 | .TP 10 89 | .B -Dmap 90 | Draw the Karnaugh maps for a binary-valued function. 91 | .ne 4 92 | .TP 10 93 | .B -Dmapdc 94 | Derive from the binary-valued variable \fIDONT_\|CARE\fP a don't-care set, 95 | and then delete this variable. 96 | All input conditions for which an output changes when \fIDONT_\|CARE\fP 97 | changes define the don't-care conditions for that output. 98 | This is a hack to support don't-cares from high-level languages without 99 | a notion of don't-cares. 100 | .ne 4 101 | .TP 10 102 | .B -Dopo 103 | Perform output phase optimization (i.e., determine which 104 | functions to complement to reduce the number of 105 | terms needed to implement the function). After choosing an 106 | assignment of phases for the outputs, the function is minimized. 107 | A simple algorithm is used which may become very expensive for 108 | a large number of outputs (e.g., more than 40). 109 | .TP 10 110 | .B -Dopoall 111 | Minimize the function with all possible phase assignments. 112 | A range of outputs to cycle through can be given with 113 | .B -rn-m 114 | (the default is to use all outputs). 115 | The option 116 | .B -S1 117 | will perform an exact minimization for each phase assignment. 118 | Be warned that opoall requires an exponential number of minimizations ! 119 | .TP 10 120 | .B -Dpair 121 | Choose an assignment of the inputs to two-bit decoders, and 122 | minimize the function. The function MUST be minimized first to 123 | achieve good results. There are actually 4 different algorithms, 124 | of increasing cost, which may be selected with 125 | .BR -S1 , 126 | .BR -S2 , 127 | or 128 | .BR -S3 . 129 | The default is 130 | .B -S0 131 | which seems to give the best results for the cost. 132 | .TP 10 133 | .B -Dpairall 134 | Minimize the function with all possible assignments of inputs to 135 | two-bit decoders. 136 | The option 137 | .B -S1 138 | will perform an exact minimization for each assignment of inputs 139 | to decoders, and the option 140 | .B -S2 141 | will perform an output-phase assignment for each assignment of 142 | inputs to decoders. 143 | Be warned that pairall requires an exponential number of minimizations ! 144 | .TP 10 145 | .B -Dseparate 146 | Remove the don't-care set from the \s-1ON\s0-set of the function. 147 | .TP 10 148 | .B -Dso 149 | Minimize each function one at a time as a single-output function. 150 | Terms will not be shared among the functions. 151 | The option 152 | .B -S1 153 | will perform an exact minimization for each single-output function. 154 | .TP 10 155 | .B -Dso_both 156 | Minimize each function one at a time as a single-output function, but 157 | choose the function or its complement based on which has fewer terms. 158 | The option 159 | .B -S1 160 | will perform an exact minimization for each single-output function and 161 | its complement to determine which has fewer terms. 162 | .TP 10 163 | .B -Dstats 164 | Provide simple statistics on the size of the function. 165 | .TP 10 166 | .B -Dverify 167 | Checks for Boolean equivalence of two PLA's. Reads two filenames 168 | from the command line, each containing a single PLA. 169 | .TP 10 170 | .B -DPLAverify 171 | Checks for Boolean equivalence of two PLA's by first permuting the 172 | columns based on the user supplied variable names. Reads two 173 | filenames from the command line. 174 | .TP 10 175 | .B -eeat 176 | Normally comments are echoed from the input file to the output file. 177 | This options discards any comments in the input file. 178 | .TP 10 179 | .B -efast 180 | Stop after the first \s-1EXPAND\s0 and \s-1IRREDUNDANT\s0 operations 181 | (i.e., do not iterate over the solution). 182 | .TP 10 183 | .B -ekiss 184 | Sets up a \fIkiss\fR-style minimization problem. This is a hack. 185 | .TP 10 186 | .B -eness 187 | Essential primes will not be detected. 188 | .TP 10 189 | .B -enirr 190 | The result will not necessarily be made irredundant in the final step 191 | which removes redundant literals. 192 | .TP 10 193 | .B -enunwrap 194 | The \s-1ON\s0-set will not be unwrapped before beginning the minimization. 195 | .TP 10 196 | .B -eonset 197 | Recompute the \s-1ON\s0-set before the minimization. Useful when the 198 | \s-1PLA\s0 has a large number of product terms (e.g., an exhaustive 199 | list of minterms). 200 | .TP 10 201 | .B -epos 202 | Swaps the \s-1ON\s0-set and \s-1OFF\s0-set of the function after 203 | reading the function. This can be used to minimize the \s-1OFF\s0-set 204 | of a function. \fI.phase\fR (see espresso(5)) 205 | in the input file can also specify an arbitrary choice of output phases. 206 | .TP 10 207 | .B -estrong 208 | Uses the alternate strategy \s-1SUPER_\|GASP\s0 (as a replacement 209 | for \s-1LAST_\ GASP\s0) which is 210 | more expensive, but occasionally provides better results. 211 | .TP 10 212 | .B -o[type] 213 | Selects the output format. By default, only the \s-1ON\s0-set (i.e., 214 | type f) is output after the minimization. [type] can be one of \fBf\fR, 215 | \fBd\fR, \fBr\fR, \fBfd\fR, \fBdr\fR, \fBfr\fR, or \fBfdr\fR to select 216 | any combination of the \s-1ON\s0-set (f), the \s-1OFF\s0-set (r) or the 217 | \s-1DC\s0-set (d). [type] may also be \fBeqntott\fR to output algebraic 218 | equations acceptable to 219 | .IR eqntott (1OCTTOOLS), 220 | or \fBpleasure\fR to output an 221 | unmerged \s-1PLA\s0 (with the \fI.label\fR and \fI.group\fR keywords) 222 | acceptable to 223 | .IR pleasure (1OCTTOOLS). 224 | .TP 10 225 | .B -s 226 | Will provide a short summary of the execution of the program including 227 | the initial cost of the function, the final cost, and the computer 228 | resources used. 229 | .TP 10 230 | .B -t 231 | Will produce a trace showing the execution of the program. After each 232 | main step of the algorithm, a single line is printed which reports the 233 | processor time used, and the current cost of the function. 234 | .TP 10 235 | .B -x 236 | Suppress printing of the solution. 237 | .TP 10 238 | .B -v [type] 239 | Specifies verbose debugging detail. Not generally useful. 240 | .SH DIAGNOSTICS 241 | Espresso will issue a warning message 242 | if a product term spans more than one line. Usually this is an 243 | indication that the number of inputs or outputs of the function 244 | is specified incorrectly. 245 | .SH "SEE ALSO" 246 | kiss(1OCTTOOLS), pleasure(1OCTTOOLS), pla(5OCTTOOLS), espresso(5OCTTOOLS) 247 | .LP 248 | R. Brayton, G. Hachtel, C. McMullen, and A. Sangiovanni-Vincentelli, 249 | \fILogic Minimization Algorithms for VLSI Synthesis\fR, 250 | Kluwer Academic Publishers, 1984. 251 | .LP 252 | R. Rudell, A. Sangiovanni-Vincentelli, 253 | "Espresso-MV: Algorithms for Multiple-Valued Logic Minimization," 254 | \fIProc. Cust. Int. Circ. Conf.\fR, Portland, May 1985. 255 | .LP 256 | R. Rudell, "Multiple-Valued Minimization for PLA Synthesis," 257 | Master's Report, University of California, Berkeley, June 1986. 258 | .LP 259 | R. Rudell, A. Sangiovanni-Vincentelli, 260 | "Exact Minimization of Multiple-Valued Functions for PLA Optimization", 261 | \fIInt. Conf. Comp. Aid. Des.\fP, Santa Clara, November 1986. 262 | .SH AUTHOR 263 | Please direct any questions or comments to: 264 | .nf 265 | Richard Rudell 266 | 205 Cory Hall 267 | Dept. of EECS 268 | University of California 269 | Berkeley, California 94720 270 | .fi 271 | .LP 272 | Arpanet mail address is rudell@ic.Berkeley.EDU. 273 | .SH COMMENTS 274 | Default is to pass comments and unrecognized options from the input file 275 | to standard output (sometimes this isn't what you want). 276 | .LP 277 | It is no longer possible to specify the type on the command line. 278 | .LP 279 | There are a lot of options, but typical use doesn't need them. 280 | .LP 281 | This manual page refers to Version 2.3 of Espresso. The major change from 282 | Version 2.2 to Version 2.3 is the addition of a fast sparse-matrix 283 | covering algorithm for the \fB-Dexact\fP mode. 284 | .LP 285 | The -Dopo option becomes very slow for many outputs (> 20). 286 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "espresso-logic-minimizer", 3 | "main": "index.js", 4 | "version": "2.0.3", 5 | "author": "Sebastien Cottinet (https://github.com/scottinet)", 6 | "description": "A NodeJS bridge to the Espresso heuristic logic minimizer", 7 | "keywords": [ 8 | "espresso", 9 | "logic", 10 | "heuristic", 11 | "minimizer", 12 | "canonical", 13 | "normal form", 14 | "canonicalization" 15 | ], 16 | "scripts": { 17 | "configure": "node-gyp configure", 18 | "build": "node-gyp build" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/scottinet/espresso-logic-minimizer.git" 23 | }, 24 | "dependencies": { 25 | "bindings": "^1.5.0", 26 | "debug": "^4.1.1", 27 | "nan": "^2.14.0", 28 | "tmp": "0.1.0" 29 | }, 30 | "devDependencies": { 31 | "node-gyp": "^6.0.0" 32 | }, 33 | "engines": { 34 | "node": ">= 4.4.5", 35 | "npm": ">= 2.14.4" 36 | }, 37 | "license": "MIT" 38 | } 39 | --------------------------------------------------------------------------------